summaryrefslogtreecommitdiffstats
path: root/plug-ins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
commit5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch)
treecbffb45144febf451e54061db2b21395faf94bfe /plug-ins
parentInitial commit. (diff)
downloadgimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz
gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins')
-rw-r--r--plug-ins/Makefile.am62
-rw-r--r--plug-ins/Makefile.in974
-rw-r--r--plug-ins/common/Makefile.am1883
-rw-r--r--plug-ins/common/Makefile.in9492
-rw-r--r--plug-ins/common/align-layers.c748
-rw-r--r--plug-ins/common/animation-optimize.c1340
-rw-r--r--plug-ins/common/animation-play.c1763
-rw-r--r--plug-ins/common/blinds.c730
-rw-r--r--plug-ins/common/blur.c375
-rw-r--r--plug-ins/common/border-average.c468
-rw-r--r--plug-ins/common/busy-dialog.c309
-rw-r--r--plug-ins/common/cartoon.c880
-rw-r--r--plug-ins/common/checkerboard.c525
-rw-r--r--plug-ins/common/cml-explorer.c2569
-rw-r--r--plug-ins/common/color-cube-analyze.c502
-rw-r--r--plug-ins/common/color-enhance.c289
-rw-r--r--plug-ins/common/colorify.c398
-rw-r--r--plug-ins/common/colormap-remap.c752
-rw-r--r--plug-ins/common/compose.c1402
-rw-r--r--plug-ins/common/contrast-retinex.c840
-rw-r--r--plug-ins/common/crop-zealous.c326
-rw-r--r--plug-ins/common/curve-bend.c3402
-rw-r--r--plug-ins/common/decompose.c973
-rw-r--r--plug-ins/common/depth-merge.c1050
-rw-r--r--plug-ins/common/despeckle.c901
-rw-r--r--plug-ins/common/destripe.c530
-rw-r--r--plug-ins/common/edge-dog.c1029
-rw-r--r--plug-ins/common/emboss.c550
-rw-r--r--plug-ins/common/file-aa.c412
-rw-r--r--plug-ins/common/file-cel.c975
-rw-r--r--plug-ins/common/file-compressor.c1017
-rw-r--r--plug-ins/common/file-csource.c1045
-rw-r--r--plug-ins/common/file-desktop-link.c185
-rw-r--r--plug-ins/common/file-dicom.c1796
-rw-r--r--plug-ins/common/file-gbr.c367
-rw-r--r--plug-ins/common/file-gegl.c492
-rw-r--r--plug-ins/common/file-gif-load.c1252
-rw-r--r--plug-ins/common/file-gif-save.c2574
-rw-r--r--plug-ins/common/file-gih.c819
-rw-r--r--plug-ins/common/file-glob.c450
-rw-r--r--plug-ins/common/file-header.c439
-rw-r--r--plug-ins/common/file-heif.c2173
-rw-r--r--plug-ins/common/file-html-table.c750
-rw-r--r--plug-ins/common/file-jp2-load.c1344
-rw-r--r--plug-ins/common/file-jpegxl.c1174
-rw-r--r--plug-ins/common/file-mng.c1791
-rw-r--r--plug-ins/common/file-pat.c320
-rw-r--r--plug-ins/common/file-pcx.c1070
-rw-r--r--plug-ins/common/file-pdf-load.c2058
-rw-r--r--plug-ins/common/file-pdf-save.c1882
-rw-r--r--plug-ins/common/file-pix.c736
-rw-r--r--plug-ins/common/file-png.c2654
-rw-r--r--plug-ins/common/file-pnm.c1820
-rw-r--r--plug-ins/common/file-ps.c3940
-rw-r--r--plug-ins/common/file-psp.c2571
-rw-r--r--plug-ins/common/file-raw-data.c2261
-rw-r--r--plug-ins/common/file-sunras.c1784
-rw-r--r--plug-ins/common/file-svg.c914
-rw-r--r--plug-ins/common/file-tga.c1486
-rw-r--r--plug-ins/common/file-wmf.c1049
-rw-r--r--plug-ins/common/file-xbm.c1445
-rw-r--r--plug-ins/common/file-xmc.c2492
-rw-r--r--plug-ins/common/file-xpm.c881
-rw-r--r--plug-ins/common/file-xwd.c2587
-rw-r--r--plug-ins/common/film.c1287
-rw-r--r--plug-ins/common/filter-pack.c2178
-rw-r--r--plug-ins/common/fractal-trace.c829
-rw-r--r--plug-ins/common/gimprc.common90
-rw-r--r--plug-ins/common/goat-exercise.c119
-rw-r--r--plug-ins/common/gradient-map.c412
-rw-r--r--plug-ins/common/grid.c1011
-rw-r--r--plug-ins/common/guillotine.c305
-rw-r--r--plug-ins/common/hot.c782
-rw-r--r--plug-ins/common/jigsaw.c2563
-rw-r--r--plug-ins/common/mail.c830
-rw-r--r--plug-ins/common/max-rgb.c373
-rwxr-xr-xplug-ins/common/mkgen.pl234
-rw-r--r--plug-ins/common/nl-filter.c1149
-rw-r--r--plug-ins/common/photocopy.c935
-rw-r--r--plug-ins/common/plugin-browser.c918
-rw-r--r--plug-ins/common/plugin-defs.pl92
-rw-r--r--plug-ins/common/procedure-browser.c147
-rw-r--r--plug-ins/common/qbist.c912
-rw-r--r--plug-ins/common/sample-colorize.c3113
-rw-r--r--plug-ins/common/sharpen.c800
-rw-r--r--plug-ins/common/smooth-palette.c499
-rw-r--r--plug-ins/common/softglow.c713
-rw-r--r--plug-ins/common/sparkle.c1189
-rw-r--r--plug-ins/common/sphere-designer.c3166
-rw-r--r--plug-ins/common/tile-small.c1130
-rw-r--r--plug-ins/common/tile.c505
-rw-r--r--plug-ins/common/unit-editor.c700
-rw-r--r--plug-ins/common/van-gogh-lic.c904
-rw-r--r--plug-ins/common/warp.c1793
-rw-r--r--plug-ins/common/wavelet-decompose.c422
-rw-r--r--plug-ins/common/web-browser.c214
-rw-r--r--plug-ins/common/web-page.c551
-rw-r--r--plug-ins/file-bmp/Makefile.am52
-rw-r--r--plug-ins/file-bmp/Makefile.in1012
-rw-r--r--plug-ins/file-bmp/bmp-load.c1026
-rw-r--r--plug-ins/file-bmp/bmp-load.h27
-rw-r--r--plug-ins/file-bmp/bmp-save.c1104
-rw-r--r--plug-ins/file-bmp/bmp-save.h39
-rw-r--r--plug-ins/file-bmp/bmp.c306
-rw-r--r--plug-ins/file-bmp/bmp.h71
-rw-r--r--plug-ins/file-dds/COPYING339
-rw-r--r--plug-ins/file-dds/Makefile.am68
-rw-r--r--plug-ins/file-dds/Makefile.in1046
-rw-r--r--plug-ins/file-dds/README29
-rw-r--r--plug-ins/file-dds/TODO7
-rw-r--r--plug-ins/file-dds/color.c58
-rw-r--r--plug-ins/file-dds/color.h96
-rw-r--r--plug-ins/file-dds/dds.c482
-rw-r--r--plug-ins/file-dds/dds.h326
-rw-r--r--plug-ins/file-dds/ddsplugin.h79
-rw-r--r--plug-ins/file-dds/ddsread.c1401
-rw-r--r--plug-ins/file-dds/ddswrite.c2278
-rw-r--r--plug-ins/file-dds/dxt.c1526
-rw-r--r--plug-ins/file-dds/dxt.h49
-rw-r--r--plug-ins/file-dds/dxt_tables.h216
-rw-r--r--plug-ins/file-dds/endian_rw.h69
-rw-r--r--plug-ins/file-dds/imath.h77
-rw-r--r--plug-ins/file-dds/mipmap.c1132
-rw-r--r--plug-ins/file-dds/mipmap.h75
-rw-r--r--plug-ins/file-dds/misc.c261
-rw-r--r--plug-ins/file-dds/misc.h31
-rw-r--r--plug-ins/file-dds/mktables.c130
-rw-r--r--plug-ins/file-dds/vec.h245
-rw-r--r--plug-ins/file-exr/Makefile.am54
-rw-r--r--plug-ins/file-exr/Makefile.in1036
-rw-r--r--plug-ins/file-exr/exr-attribute-blob.h97
-rw-r--r--plug-ins/file-exr/file-exr.c393
-rw-r--r--plug-ins/file-exr/openexr-wrapper.cc518
-rw-r--r--plug-ins/file-exr/openexr-wrapper.h68
-rw-r--r--plug-ins/file-faxg3/Makefile.am49
-rw-r--r--plug-ins/file-faxg3/Makefile.in1004
-rw-r--r--plug-ins/file-faxg3/faxg3.c626
-rw-r--r--plug-ins/file-faxg3/g3.c277
-rw-r--r--plug-ins/file-faxg3/g3.h55
-rw-r--r--plug-ins/file-fits/Makefile.am50
-rw-r--r--plug-ins/file-fits/Makefile.in1005
-rw-r--r--plug-ins/file-fits/fits-io.c2506
-rw-r--r--plug-ins/file-fits/fits-io.h199
-rw-r--r--plug-ins/file-fits/fits.c1222
-rw-r--r--plug-ins/file-fli/Makefile.am49
-rw-r--r--plug-ins/file-fli/Makefile.in1004
-rw-r--r--plug-ins/file-fli/fli-gimp.c984
-rw-r--r--plug-ins/file-fli/fli.c1033
-rw-r--r--plug-ins/file-fli/fli.h153
-rw-r--r--plug-ins/file-ico/Makefile.am57
-rw-r--r--plug-ins/file-ico/Makefile.in1081
-rw-r--r--plug-ins/file-ico/ico-dialog.c531
-rw-r--r--plug-ins/file-ico/ico-dialog.h30
-rw-r--r--plug-ins/file-ico/ico-load.c813
-rw-r--r--plug-ins/file-ico/ico-load.h43
-rw-r--r--plug-ins/file-ico/ico-save.c1176
-rw-r--r--plug-ins/file-ico/ico-save.h34
-rw-r--r--plug-ins/file-ico/ico.c345
-rw-r--r--plug-ins/file-ico/ico.h110
-rw-r--r--plug-ins/file-jpeg/Makefile.am77
-rw-r--r--plug-ins/file-jpeg/Makefile.in1069
-rw-r--r--plug-ins/file-jpeg/jpeg-icc.c263
-rw-r--r--plug-ins/file-jpeg/jpeg-icc.h59
-rw-r--r--plug-ins/file-jpeg/jpeg-load.c719
-rw-r--r--plug-ins/file-jpeg/jpeg-load.h33
-rw-r--r--plug-ins/file-jpeg/jpeg-quality.c148
-rw-r--r--plug-ins/file-jpeg/jpeg-quality.h26
-rw-r--r--plug-ins/file-jpeg/jpeg-save.c1524
-rw-r--r--plug-ins/file-jpeg/jpeg-save.h57
-rw-r--r--plug-ins/file-jpeg/jpeg-settings.c397
-rw-r--r--plug-ins/file-jpeg/jpeg-settings.h37
-rw-r--r--plug-ins/file-jpeg/jpeg.c643
-rw-r--r--plug-ins/file-jpeg/jpeg.h73
-rw-r--r--plug-ins/file-jpeg/jpegqual.c1082
-rw-r--r--plug-ins/file-psd/Makefile.am74
-rw-r--r--plug-ins/file-psd/Makefile.in1048
-rw-r--r--plug-ins/file-psd/TODO.txt54
-rw-r--r--plug-ins/file-psd/new-resource-ids.txt23
-rw-r--r--plug-ins/file-psd/psd-image-res-load.c1611
-rw-r--r--plug-ins/file-psd/psd-image-res-load.h43
-rw-r--r--plug-ins/file-psd/psd-layer-res-load.c944
-rw-r--r--plug-ins/file-psd/psd-layer-res-load.h35
-rw-r--r--plug-ins/file-psd/psd-load.c2807
-rw-r--r--plug-ins/file-psd/psd-load.h32
-rw-r--r--plug-ins/file-psd/psd-save.c2097
-rw-r--r--plug-ins/file-psd/psd-save.h27
-rw-r--r--plug-ins/file-psd/psd-thumb-load.c309
-rw-r--r--plug-ins/file-psd/psd-thumb-load.h31
-rw-r--r--plug-ins/file-psd/psd-util.c933
-rw-r--r--plug-ins/file-psd/psd-util.h87
-rw-r--r--plug-ins/file-psd/psd.c385
-rw-r--r--plug-ins/file-psd/psd.h688
-rw-r--r--plug-ins/file-raw/Makefile.am90
-rw-r--r--plug-ins/file-raw/Makefile.in1220
-rw-r--r--plug-ins/file-raw/file-darktable-export-on-exit.lua88
-rw-r--r--plug-ins/file-raw/file-darktable-get-size.lua21
-rw-r--r--plug-ins/file-raw/file-darktable.c550
-rw-r--r--plug-ins/file-raw/file-raw-formats.h320
-rw-r--r--plug-ins/file-raw/file-raw-placeholder.c163
-rw-r--r--plug-ins/file-raw/file-raw-utils.c153
-rw-r--r--plug-ins/file-raw/file-raw-utils.h33
-rw-r--r--plug-ins/file-raw/file-rawtherapee.c476
-rw-r--r--plug-ins/file-sgi/Makefile.am49
-rw-r--r--plug-ins/file-sgi/Makefile.in1004
-rw-r--r--plug-ins/file-sgi/sgi-lib.c960
-rw-r--r--plug-ins/file-sgi/sgi-lib.h93
-rw-r--r--plug-ins/file-sgi/sgi.c702
-rw-r--r--plug-ins/file-tiff/Makefile.am58
-rw-r--r--plug-ins/file-tiff/Makefile.in1022
-rw-r--r--plug-ins/file-tiff/file-tiff-io.c585
-rw-r--r--plug-ins/file-tiff/file-tiff-io.h50
-rw-r--r--plug-ins/file-tiff/file-tiff-load.c2815
-rw-r--r--plug-ins/file-tiff/file-tiff-load.h57
-rw-r--r--plug-ins/file-tiff/file-tiff-save.c1387
-rw-r--r--plug-ins/file-tiff/file-tiff-save.h64
-rw-r--r--plug-ins/file-tiff/file-tiff.c587
-rw-r--r--plug-ins/file-webp/Makefile.am64
-rw-r--r--plug-ins/file-webp/Makefile.in1029
-rw-r--r--plug-ins/file-webp/file-webp-dialog.c430
-rw-r--r--plug-ins/file-webp/file-webp-dialog.h33
-rw-r--r--plug-ins/file-webp/file-webp-load.c294
-rw-r--r--plug-ins/file-webp/file-webp-load.h31
-rw-r--r--plug-ins/file-webp/file-webp-save.c903
-rw-r--r--plug-ins/file-webp/file-webp-save.h55
-rw-r--r--plug-ins/file-webp/file-webp.c384
-rw-r--r--plug-ins/file-webp/file-webp.h33
-rw-r--r--plug-ins/flame/Makefile.am59
-rw-r--r--plug-ins/flame/Makefile.in1021
-rw-r--r--plug-ins/flame/README64
-rw-r--r--plug-ins/flame/cmap.c5776
-rw-r--r--plug-ins/flame/cmap.h35
-rw-r--r--plug-ins/flame/flame.c1294
-rw-r--r--plug-ins/flame/flame.h21
-rw-r--r--plug-ins/flame/libifs.c1487
-rw-r--r--plug-ins/flame/libifs.h87
-rw-r--r--plug-ins/flame/rect.c398
-rw-r--r--plug-ins/flame/rect.h41
-rw-r--r--plug-ins/fractal-explorer/Makefile.am55
-rw-r--r--plug-ins/fractal-explorer/Makefile.in1127
-rw-r--r--plug-ins/fractal-explorer/examples/Asteroid_Field26
-rw-r--r--plug-ins/fractal-explorer/examples/Bar_Code_Label26
-rw-r--r--plug-ins/fractal-explorer/examples/Beauty_of_Nature26
-rw-r--r--plug-ins/fractal-explorer/examples/Blue_Curtain26
-rw-r--r--plug-ins/fractal-explorer/examples/Car_Track26
-rw-r--r--plug-ins/fractal-explorer/examples/Energetic_Diamond26
-rw-r--r--plug-ins/fractal-explorer/examples/Explosive26
-rw-r--r--plug-ins/fractal-explorer/examples/Flower26
-rw-r--r--plug-ins/fractal-explorer/examples/Fragments26
-rw-r--r--plug-ins/fractal-explorer/examples/Hemp26
-rw-r--r--plug-ins/fractal-explorer/examples/High_Voltage26
-rw-r--r--plug-ins/fractal-explorer/examples/Hoops26
-rw-r--r--plug-ins/fractal-explorer/examples/Ice_Crystal26
-rw-r--r--plug-ins/fractal-explorer/examples/Leaves26
-rw-r--r--plug-ins/fractal-explorer/examples/Lightning26
-rw-r--r--plug-ins/fractal-explorer/examples/Makefile.am40
-rw-r--r--plug-ins/fractal-explorer/examples/Makefile.in838
-rw-r--r--plug-ins/fractal-explorer/examples/Mandelbrot26
-rw-r--r--plug-ins/fractal-explorer/examples/Marble26
-rw-r--r--plug-ins/fractal-explorer/examples/Marble226
-rw-r--r--plug-ins/fractal-explorer/examples/Medusa26
-rw-r--r--plug-ins/fractal-explorer/examples/Nautilus26
-rw-r--r--plug-ins/fractal-explorer/examples/Nebula26
-rw-r--r--plug-ins/fractal-explorer/examples/Plant26
-rw-r--r--plug-ins/fractal-explorer/examples/Rose26
-rw-r--r--plug-ins/fractal-explorer/examples/Saturn26
-rw-r--r--plug-ins/fractal-explorer/examples/Snow_Crystal26
-rw-r--r--plug-ins/fractal-explorer/examples/Soma26
-rw-r--r--plug-ins/fractal-explorer/examples/Spark26
-rw-r--r--plug-ins/fractal-explorer/examples/Suns26
-rw-r--r--plug-ins/fractal-explorer/examples/Tentacles26
-rw-r--r--plug-ins/fractal-explorer/examples/The_Green_Place26
-rw-r--r--plug-ins/fractal-explorer/examples/Wave26
-rw-r--r--plug-ins/fractal-explorer/examples/Wood26
-rw-r--r--plug-ins/fractal-explorer/examples/Zooming_Circle26
-rw-r--r--plug-ins/fractal-explorer/fractal-explorer-dialogs.c1889
-rw-r--r--plug-ins/fractal-explorer/fractal-explorer-dialogs.h19
-rw-r--r--plug-ins/fractal-explorer/fractal-explorer.c1201
-rw-r--r--plug-ins/fractal-explorer/fractal-explorer.h216
-rw-r--r--plug-ins/gfig/Makefile.am85
-rw-r--r--plug-ins/gfig/Makefile.in1209
-rw-r--r--plug-ins/gfig/README313
-rw-r--r--plug-ins/gfig/gfig-arc.c735
-rw-r--r--plug-ins/gfig/gfig-arc.h36
-rw-r--r--plug-ins/gfig/gfig-bezier.c422
-rw-r--r--plug-ins/gfig/gfig-bezier.h42
-rw-r--r--plug-ins/gfig/gfig-circle.c234
-rw-r--r--plug-ins/gfig/gfig-circle.h35
-rw-r--r--plug-ins/gfig/gfig-dialog.c2193
-rw-r--r--plug-ins/gfig/gfig-dialog.h39
-rw-r--r--plug-ins/gfig/gfig-dobject.c1063
-rw-r--r--plug-ins/gfig/gfig-dobject.h114
-rw-r--r--plug-ins/gfig/gfig-ellipse.c235
-rw-r--r--plug-ins/gfig/gfig-ellipse.h35
-rw-r--r--plug-ins/gfig/gfig-examples/A_star84
-rw-r--r--plug-ins/gfig/gfig-examples/Makefile.am17
-rw-r--r--plug-ins/gfig/gfig-examples/Makefile.in815
-rw-r--r--plug-ins/gfig/gfig-examples/curves57
-rw-r--r--plug-ins/gfig/gfig-examples/polys40
-rw-r--r--plug-ins/gfig/gfig-examples/ring44
-rw-r--r--plug-ins/gfig/gfig-examples/ring+star123
-rw-r--r--plug-ins/gfig/gfig-examples/simily47
-rw-r--r--plug-ins/gfig/gfig-examples/spirals_and_stars55
-rw-r--r--plug-ins/gfig/gfig-examples/sprial38
-rw-r--r--plug-ins/gfig/gfig-examples/star261
-rw-r--r--plug-ins/gfig/gfig-examples/stars44
-rw-r--r--plug-ins/gfig/gfig-grid.c540
-rw-r--r--plug-ins/gfig/gfig-grid.h47
-rw-r--r--plug-ins/gfig/gfig-line.c225
-rw-r--r--plug-ins/gfig/gfig-line.h35
-rw-r--r--plug-ins/gfig/gfig-poly.c533
-rw-r--r--plug-ins/gfig/gfig-poly.h42
-rw-r--r--plug-ins/gfig/gfig-preview.c423
-rw-r--r--plug-ins/gfig/gfig-preview.h35
-rw-r--r--plug-ins/gfig/gfig-rectangle.c218
-rw-r--r--plug-ins/gfig/gfig-rectangle.h35
-rw-r--r--plug-ins/gfig/gfig-spiral.c316
-rw-r--r--plug-ins/gfig/gfig-spiral.h37
-rw-r--r--plug-ins/gfig/gfig-star.c406
-rw-r--r--plug-ins/gfig/gfig-star.h38
-rw-r--r--plug-ins/gfig/gfig-stock.c117
-rw-r--r--plug-ins/gfig/gfig-stock.h46
-rw-r--r--plug-ins/gfig/gfig-style.c787
-rw-r--r--plug-ins/gfig/gfig-style.h126
-rw-r--r--plug-ins/gfig/gfig-types.h115
-rw-r--r--plug-ins/gfig/gfig.c809
-rw-r--r--plug-ins/gfig/gfig.h245
-rw-r--r--plug-ins/gfig/images/Makefile.am35
-rw-r--r--plug-ins/gfig/images/Makefile.in781
-rw-r--r--plug-ins/gfig/images/stock-bezier.pngbin0 -> 258 bytes
-rw-r--r--plug-ins/gfig/images/stock-circle.pngbin0 -> 354 bytes
-rw-r--r--plug-ins/gfig/images/stock-copy-object.pngbin0 -> 268 bytes
-rw-r--r--plug-ins/gfig/images/stock-curve.pngbin0 -> 284 bytes
-rw-r--r--plug-ins/gfig/images/stock-delete-object.pngbin0 -> 461 bytes
-rw-r--r--plug-ins/gfig/images/stock-ellipse.pngbin0 -> 446 bytes
-rw-r--r--plug-ins/gfig/images/stock-line.pngbin0 -> 185 bytes
-rw-r--r--plug-ins/gfig/images/stock-logo.pngbin0 -> 6472 bytes
-rw-r--r--plug-ins/gfig/images/stock-move-object.pngbin0 -> 500 bytes
-rw-r--r--plug-ins/gfig/images/stock-move-point.pngbin0 -> 508 bytes
-rw-r--r--plug-ins/gfig/images/stock-polygon.pngbin0 -> 251 bytes
-rw-r--r--plug-ins/gfig/images/stock-rectangle.pngbin0 -> 230 bytes
-rw-r--r--plug-ins/gfig/images/stock-select-object.pngbin0 -> 552 bytes
-rw-r--r--plug-ins/gfig/images/stock-show-all.pngbin0 -> 690 bytes
-rw-r--r--plug-ins/gfig/images/stock-spiral.pngbin0 -> 203 bytes
-rw-r--r--plug-ins/gfig/images/stock-star.pngbin0 -> 365 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/Makefile.am66
-rw-r--r--plug-ins/gimpressionist/Brushes/Makefile.in864
-rw-r--r--plug-ins/gimpressionist/Brushes/arrow01.pgmbin0 -> 23160 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/ball.ppmbin0 -> 4858 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/blob.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/box.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/chalk01.pgmbin0 -> 11245 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/cone.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon01.pgmbin0 -> 9259 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon02.pgmbin0 -> 9302 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon03.pgmbin0 -> 8502 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon04.pgmbin0 -> 13059 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon05.pgmbin0 -> 70503 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon06.pgmbin0 -> 9327 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon07.pgmbin0 -> 11534 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/crayon08.pgmbin0 -> 5059 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/defaultbrush.pgmbin0 -> 11859 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/dribble.pgmbin0 -> 9222 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/fabric.pgmbin0 -> 40060 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/fabric01.pgmbin0 -> 40060 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/fabric02.pgmbin0 -> 27615 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/fabric03.pgmbin0 -> 20780 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/flower01.pgmbin0 -> 40060 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/flower02.pgmbin0 -> 5158 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/flower03.pgmbin0 -> 4933 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/flower04.pgmbin0 -> 30438 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/grad01.pgm205
-rw-r--r--plug-ins/gimpressionist/Brushes/grad02.pgmbin0 -> 40060 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/grad03.pgmbin0 -> 31502 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/heart.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/leaf01.pgmbin0 -> 68180 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paintbrush01.pgmbin0 -> 12347 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paintbrush02.pgmbin0 -> 16847 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paintbrush03.pgmbin0 -> 7059 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paintbrush04.pgmbin0 -> 35260 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paper01.pgmbin0 -> 24440 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paper02.pgmbin0 -> 26053 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paper03.pgmbin0 -> 38436 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/paper04.pgmbin0 -> 52788 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/pentagram.pgmbin0 -> 42060 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/scribble.pgmbin0 -> 18974 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/shape01.pgm5
-rw-r--r--plug-ins/gimpressionist/Brushes/shape02.pgmbin0 -> 34285 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/shape03.pgmbin0 -> 34285 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/shape04.pgmbin0 -> 31303 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/snow1.pgmbin0 -> 2360 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/sphere.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/splat1.pgmbin0 -> 10101 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/splat2.pgmbin0 -> 10103 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/splat3.pgmbin0 -> 6755 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/spunge01.pgmbin0 -> 12859 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/spunge02.pgmbin0 -> 10058 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/spunge03.pgmbin0 -> 14059 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/spunge04.pgmbin0 -> 8057 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/spunge05.pgmbin0 -> 14655 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/strange01.pgmbin0 -> 39703 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/thegimp.pgmbin0 -> 19860 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/torus.ppmbin0 -> 10813 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/wavy.pgmbin0 -> 17807 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/weave.pgmbin0 -> 16399 bytes
-rw-r--r--plug-ins/gimpressionist/Brushes/worm.pgmbin0 -> 10060 bytes
-rw-r--r--plug-ins/gimpressionist/Makefile.am87
-rw-r--r--plug-ins/gimpressionist/Makefile.in1217
-rw-r--r--plug-ins/gimpressionist/Paper/Makefile.am16
-rw-r--r--plug-ins/gimpressionist/Paper/Makefile.in814
-rw-r--r--plug-ins/gimpressionist/Paper/bricks.pgm43
-rw-r--r--plug-ins/gimpressionist/Paper/bricks2.pgmbin0 -> 9662 bytes
-rw-r--r--plug-ins/gimpressionist/Paper/burlap.pgmbin0 -> 9274 bytes
-rw-r--r--plug-ins/gimpressionist/Paper/canvas2.pgmbin0 -> 40058 bytes
-rw-r--r--plug-ins/gimpressionist/Paper/defaultpaper.pgm5
-rw-r--r--plug-ins/gimpressionist/Paper/marble.pgmbin0 -> 40187 bytes
-rw-r--r--plug-ins/gimpressionist/Paper/marble2.pgmbin0 -> 16444 bytes
-rw-r--r--plug-ins/gimpressionist/Paper/stone.pgm6
-rw-r--r--plug-ins/gimpressionist/Paper/struc.pgmbin0 -> 16399 bytes
-rw-r--r--plug-ins/gimpressionist/Presets/ApplyCanvas43
-rw-r--r--plug-ins/gimpressionist/Presets/Ballpark40
-rw-r--r--plug-ins/gimpressionist/Presets/Canvas28
-rw-r--r--plug-ins/gimpressionist/Presets/Crosshatch28
-rw-r--r--plug-ins/gimpressionist/Presets/Cubism40
-rw-r--r--plug-ins/gimpressionist/Presets/Dotify40
-rw-r--r--plug-ins/gimpressionist/Presets/Embroidery28
-rw-r--r--plug-ins/gimpressionist/Presets/Feathers28
-rw-r--r--plug-ins/gimpressionist/Presets/Felt-marker28
-rw-r--r--plug-ins/gimpressionist/Presets/Flowerbed40
-rw-r--r--plug-ins/gimpressionist/Presets/Furry40
-rw-r--r--plug-ins/gimpressionist/Presets/Line-art28
-rw-r--r--plug-ins/gimpressionist/Presets/Line-art-228
-rw-r--r--plug-ins/gimpressionist/Presets/Maggot-invasion40
-rw-r--r--plug-ins/gimpressionist/Presets/Makefile.am32
-rw-r--r--plug-ins/gimpressionist/Presets/Makefile.in830
-rw-r--r--plug-ins/gimpressionist/Presets/MarbleMadness39
-rw-r--r--plug-ins/gimpressionist/Presets/Mossy40
-rw-r--r--plug-ins/gimpressionist/Presets/Painted_Rock40
-rw-r--r--plug-ins/gimpressionist/Presets/Parquette30
-rw-r--r--plug-ins/gimpressionist/Presets/Patchwork28
-rw-r--r--plug-ins/gimpressionist/Presets/Ringworks28
-rw-r--r--plug-ins/gimpressionist/Presets/Sample40
-rw-r--r--plug-ins/gimpressionist/Presets/Smash40
-rw-r--r--plug-ins/gimpressionist/Presets/Straws41
-rw-r--r--plug-ins/gimpressionist/Presets/Weave40
-rw-r--r--plug-ins/gimpressionist/Presets/Wormcan28
-rw-r--r--plug-ins/gimpressionist/README102
-rw-r--r--plug-ins/gimpressionist/brush.c660
-rw-r--r--plug-ins/gimpressionist/brush.h29
-rw-r--r--plug-ins/gimpressionist/color.c106
-rw-r--r--plug-ins/gimpressionist/color.h32
-rw-r--r--plug-ins/gimpressionist/general.c283
-rw-r--r--plug-ins/gimpressionist/general.h26
-rw-r--r--plug-ins/gimpressionist/gimp.c535
-rw-r--r--plug-ins/gimpressionist/gimpressionist.c207
-rw-r--r--plug-ins/gimpressionist/gimpressionist.h181
-rw-r--r--plug-ins/gimpressionist/globals.c86
-rw-r--r--plug-ins/gimpressionist/infile.h32
-rw-r--r--plug-ins/gimpressionist/orientation.c206
-rw-r--r--plug-ins/gimpressionist/orientation.h39
-rw-r--r--plug-ins/gimpressionist/orientmap.c717
-rw-r--r--plug-ins/gimpressionist/orientmap.h25
-rw-r--r--plug-ins/gimpressionist/paper.c221
-rw-r--r--plug-ins/gimpressionist/paper.h25
-rw-r--r--plug-ins/gimpressionist/placement.c130
-rw-r--r--plug-ins/gimpressionist/placement.h32
-rw-r--r--plug-ins/gimpressionist/plasma.c122
-rw-r--r--plug-ins/gimpressionist/ppmtool.c904
-rw-r--r--plug-ins/gimpressionist/ppmtool.h60
-rw-r--r--plug-ins/gimpressionist/presets.c1098
-rw-r--r--plug-ins/gimpressionist/presets.h34
-rw-r--r--plug-ins/gimpressionist/preview.c191
-rw-r--r--plug-ins/gimpressionist/preview.h26
-rw-r--r--plug-ins/gimpressionist/random.h24
-rw-r--r--plug-ins/gimpressionist/repaint.c1201
-rw-r--r--plug-ins/gimpressionist/size.c210
-rw-r--r--plug-ins/gimpressionist/size.h40
-rw-r--r--plug-ins/gimpressionist/sizemap.c553
-rw-r--r--plug-ins/gimpressionist/utils.c411
-rw-r--r--plug-ins/gradient-flare/Makefile.am51
-rw-r--r--plug-ins/gradient-flare/Makefile.in1117
-rw-r--r--plug-ins/gradient-flare/README99
-rw-r--r--plug-ins/gradient-flare/flares/Bright_Star18
-rw-r--r--plug-ins/gradient-flare/flares/Classic18
-rw-r--r--plug-ins/gradient-flare/flares/Default18
-rw-r--r--plug-ins/gradient-flare/flares/Distant_Sun18
-rw-r--r--plug-ins/gradient-flare/flares/GFlare_10118
-rw-r--r--plug-ins/gradient-flare/flares/GFlare_10218
-rw-r--r--plug-ins/gradient-flare/flares/Hidden_Planet18
-rw-r--r--plug-ins/gradient-flare/flares/Makefile.am14
-rw-r--r--plug-ins/gradient-flare/flares/Makefile.in811
-rw-r--r--plug-ins/gradient-flare/gradient-flare.c5123
-rw-r--r--plug-ins/help-browser/Makefile.am59
-rw-r--r--plug-ins/help-browser/Makefile.in1027
-rw-r--r--plug-ins/help-browser/dialog.c1255
-rw-r--r--plug-ins/help-browser/dialog.h35
-rw-r--r--plug-ins/help-browser/gimpthrobber.c366
-rw-r--r--plug-ins/help-browser/gimpthrobber.h68
-rw-r--r--plug-ins/help-browser/gimpthrobberaction.c133
-rw-r--r--plug-ins/help-browser/gimpthrobberaction.h48
-rw-r--r--plug-ins/help-browser/help-browser.c322
-rw-r--r--plug-ins/help-browser/uri.c386
-rw-r--r--plug-ins/help-browser/uri.h22
-rw-r--r--plug-ins/help/Makefile.am90
-rw-r--r--plug-ins/help/Makefile.in1098
-rw-r--r--plug-ins/help/gimp-help-lookup.c195
-rw-r--r--plug-ins/help/gimphelp.c151
-rw-r--r--plug-ins/help/gimphelp.h58
-rw-r--r--plug-ins/help/gimphelpdomain.c253
-rw-r--r--plug-ins/help/gimphelpdomain.h51
-rw-r--r--plug-ins/help/gimphelpitem.c73
-rw-r--r--plug-ins/help/gimphelpitem.h47
-rw-r--r--plug-ins/help/gimphelplocale.c581
-rw-r--r--plug-ins/help/gimphelplocale.h51
-rw-r--r--plug-ins/help/gimphelpprogress-private.h39
-rw-r--r--plug-ins/help/gimphelpprogress.c150
-rw-r--r--plug-ins/help/gimphelpprogress.h51
-rw-r--r--plug-ins/help/gimphelptypes.h33
-rw-r--r--plug-ins/help/help.c352
-rw-r--r--plug-ins/ifs-compose/Makefile.am55
-rw-r--r--plug-ins/ifs-compose/Makefile.in1014
-rw-r--r--plug-ins/ifs-compose/README.ifscompose83
-rw-r--r--plug-ins/ifs-compose/ifs-compose-storage.c551
-rw-r--r--plug-ins/ifs-compose/ifs-compose-utils.c1092
-rw-r--r--plug-ins/ifs-compose/ifs-compose.c2799
-rw-r--r--plug-ins/ifs-compose/ifs-compose.h180
-rw-r--r--plug-ins/imagemap/Makefile.am216
-rw-r--r--plug-ins/imagemap/Makefile.in1531
-rw-r--r--plug-ins/imagemap/images/Makefile.am27
-rw-r--r--plug-ins/imagemap/images/Makefile.in773
-rw-r--r--plug-ins/imagemap/images/stock-circle.pngbin0 -> 355 bytes
-rw-r--r--plug-ins/imagemap/images/stock-coord.pngbin0 -> 147 bytes
-rw-r--r--plug-ins/imagemap/images/stock-dimension.pngbin0 -> 179 bytes
-rw-r--r--plug-ins/imagemap/images/stock-java.pngbin0 -> 1017 bytes
-rw-r--r--plug-ins/imagemap/images/stock-polygon.pngbin0 -> 291 bytes
-rw-r--r--plug-ins/imagemap/images/stock-rectangle.pngbin0 -> 221 bytes
-rw-r--r--plug-ins/imagemap/images/stock-to-back.pngbin0 -> 305 bytes
-rw-r--r--plug-ins/imagemap/images/stock-to-front.pngbin0 -> 261 bytes
-rw-r--r--plug-ins/imagemap/imap_about.c62
-rw-r--r--plug-ins/imagemap/imap_about.h28
-rw-r--r--plug-ins/imagemap/imap_browse.c172
-rw-r--r--plug-ins/imagemap/imap_browse.h46
-rw-r--r--plug-ins/imagemap/imap_cern.l93
-rw-r--r--plug-ins/imagemap/imap_cern.y184
-rw-r--r--plug-ins/imagemap/imap_cern_lex.c1939
-rw-r--r--plug-ins/imagemap/imap_cern_parse.c1822
-rw-r--r--plug-ins/imagemap/imap_cern_parse.h110
-rw-r--r--plug-ins/imagemap/imap_circle.c405
-rw-r--r--plug-ins/imagemap/imap_circle.h40
-rw-r--r--plug-ins/imagemap/imap_cmd_clear.c72
-rw-r--r--plug-ins/imagemap/imap_cmd_copy.c71
-rw-r--r--plug-ins/imagemap/imap_cmd_copy_object.c84
-rw-r--r--plug-ins/imagemap/imap_cmd_create.c82
-rw-r--r--plug-ins/imagemap/imap_cmd_cut.c92
-rw-r--r--plug-ins/imagemap/imap_cmd_cut_object.c62
-rw-r--r--plug-ins/imagemap/imap_cmd_delete.c83
-rw-r--r--plug-ins/imagemap/imap_cmd_delete_point.c86
-rw-r--r--plug-ins/imagemap/imap_cmd_edit_object.c73
-rw-r--r--plug-ins/imagemap/imap_cmd_gimp_guides.c260
-rw-r--r--plug-ins/imagemap/imap_cmd_guides.c279
-rw-r--r--plug-ins/imagemap/imap_cmd_insert_point.c96
-rw-r--r--plug-ins/imagemap/imap_cmd_move.c166
-rw-r--r--plug-ins/imagemap/imap_cmd_move_down.c82
-rw-r--r--plug-ins/imagemap/imap_cmd_move_sash.c150
-rw-r--r--plug-ins/imagemap/imap_cmd_move_selected.c72
-rw-r--r--plug-ins/imagemap/imap_cmd_move_to_front.c83
-rw-r--r--plug-ins/imagemap/imap_cmd_move_up.c81
-rw-r--r--plug-ins/imagemap/imap_cmd_object_down.c78
-rw-r--r--plug-ins/imagemap/imap_cmd_object_move.c80
-rw-r--r--plug-ins/imagemap/imap_cmd_object_up.c78
-rw-r--r--plug-ins/imagemap/imap_cmd_paste.c71
-rw-r--r--plug-ins/imagemap/imap_cmd_select.c75
-rw-r--r--plug-ins/imagemap/imap_cmd_select_all.c74
-rw-r--r--plug-ins/imagemap/imap_cmd_select_next.c76
-rw-r--r--plug-ins/imagemap/imap_cmd_select_prev.c76
-rw-r--r--plug-ins/imagemap/imap_cmd_select_region.c142
-rw-r--r--plug-ins/imagemap/imap_cmd_send_to_back.c83
-rw-r--r--plug-ins/imagemap/imap_cmd_unselect.c76
-rw-r--r--plug-ins/imagemap/imap_cmd_unselect_all.c90
-rw-r--r--plug-ins/imagemap/imap_command.c384
-rw-r--r--plug-ins/imagemap/imap_command.h103
-rw-r--r--plug-ins/imagemap/imap_commands.h64
-rw-r--r--plug-ins/imagemap/imap_csim.l137
-rw-r--r--plug-ins/imagemap/imap_csim.y396
-rw-r--r--plug-ins/imagemap/imap_csim_lex.c2054
-rw-r--r--plug-ins/imagemap/imap_csim_parse.c2131
-rw-r--r--plug-ins/imagemap/imap_csim_parse.h142
-rw-r--r--plug-ins/imagemap/imap_default_dialog.c184
-rw-r--r--plug-ins/imagemap/imap_default_dialog.h59
-rw-r--r--plug-ins/imagemap/imap_edit_area_info.c515
-rw-r--r--plug-ins/imagemap/imap_edit_area_info.h66
-rw-r--r--plug-ins/imagemap/imap_file.c180
-rw-r--r--plug-ins/imagemap/imap_file.h34
-rw-r--r--plug-ins/imagemap/imap_grid.c414
-rw-r--r--plug-ins/imagemap/imap_grid.h34
-rw-r--r--plug-ins/imagemap/imap_main.c1306
-rw-r--r--plug-ins/imagemap/imap_main.h125
-rw-r--r--plug-ins/imagemap/imap_menu.c551
-rw-r--r--plug-ins/imagemap/imap_menu.h65
-rw-r--r--plug-ins/imagemap/imap_menu_funcs.c58
-rw-r--r--plug-ins/imagemap/imap_menu_funcs.h39
-rw-r--r--plug-ins/imagemap/imap_misc.c52
-rw-r--r--plug-ins/imagemap/imap_misc.h31
-rw-r--r--plug-ins/imagemap/imap_mru.c105
-rw-r--r--plug-ins/imagemap/imap_mru.h45
-rw-r--r--plug-ins/imagemap/imap_ncsa.l112
-rw-r--r--plug-ins/imagemap/imap_ncsa.y195
-rw-r--r--plug-ins/imagemap/imap_ncsa_lex.c1917
-rw-r--r--plug-ins/imagemap/imap_ncsa_parse.c1830
-rw-r--r--plug-ins/imagemap/imap_ncsa_parse.h112
-rw-r--r--plug-ins/imagemap/imap_object.c1040
-rw-r--r--plug-ins/imagemap/imap_object.h253
-rw-r--r--plug-ins/imagemap/imap_object_popup.c60
-rw-r--r--plug-ins/imagemap/imap_object_popup.h39
-rw-r--r--plug-ins/imagemap/imap_polygon.c854
-rw-r--r--plug-ins/imagemap/imap_polygon.h45
-rw-r--r--plug-ins/imagemap/imap_preferences.c527
-rw-r--r--plug-ins/imagemap/imap_preferences.h56
-rw-r--r--plug-ins/imagemap/imap_preview.c402
-rw-r--r--plug-ins/imagemap/imap_preview.h55
-rw-r--r--plug-ins/imagemap/imap_rectangle.c538
-rw-r--r--plug-ins/imagemap/imap_rectangle.h41
-rw-r--r--plug-ins/imagemap/imap_selection.c452
-rw-r--r--plug-ins/imagemap/imap_selection.h64
-rw-r--r--plug-ins/imagemap/imap_settings.c188
-rw-r--r--plug-ins/imagemap/imap_settings.h28
-rw-r--r--plug-ins/imagemap/imap_source.c91
-rw-r--r--plug-ins/imagemap/imap_source.h28
-rw-r--r--plug-ins/imagemap/imap_statusbar.c153
-rw-r--r--plug-ins/imagemap/imap_statusbar.h47
-rw-r--r--plug-ins/imagemap/imap_stock.c89
-rw-r--r--plug-ins/imagemap/imap_stock.h39
-rw-r--r--plug-ins/imagemap/imap_string.c37
-rw-r--r--plug-ins/imagemap/imap_string.h28
-rw-r--r--plug-ins/imagemap/imap_table.c81
-rw-r--r--plug-ins/imagemap/imap_table.h39
-rw-r--r--plug-ins/imagemap/imap_taglist.c126
-rw-r--r--plug-ins/imagemap/imap_taglist.h44
-rw-r--r--plug-ins/lighting/Makefile.am65
-rw-r--r--plug-ins/lighting/Makefile.in1156
-rw-r--r--plug-ins/lighting/README39
-rw-r--r--plug-ins/lighting/TODO15
-rw-r--r--plug-ins/lighting/images/Makefile.am29
-rw-r--r--plug-ins/lighting/images/Makefile.in775
-rw-r--r--plug-ins/lighting/images/stock-intensity-ambient-high.pngbin0 -> 725 bytes
-rw-r--r--plug-ins/lighting/images/stock-intensity-ambient-low.pngbin0 -> 918 bytes
-rw-r--r--plug-ins/lighting/images/stock-intensity-diffuse-high.pngbin0 -> 829 bytes
-rw-r--r--plug-ins/lighting/images/stock-intensity-diffuse-low.pngbin0 -> 504 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-diffuse-high.pngbin0 -> 805 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-diffuse-low.pngbin0 -> 441 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-highlight-high.pngbin0 -> 822 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-highlight-low.pngbin0 -> 805 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-specular-high.pngbin0 -> 842 bytes
-rw-r--r--plug-ins/lighting/images/stock-reflectivity-specular-low.pngbin0 -> 800 bytes
-rw-r--r--plug-ins/lighting/lighting-apply.c154
-rw-r--r--plug-ins/lighting/lighting-apply.h7
-rw-r--r--plug-ins/lighting/lighting-image.c410
-rw-r--r--plug-ins/lighting/lighting-image.h69
-rw-r--r--plug-ins/lighting/lighting-main.c330
-rw-r--r--plug-ins/lighting/lighting-main.h104
-rw-r--r--plug-ins/lighting/lighting-preview.c496
-rw-r--r--plug-ins/lighting/lighting-preview.h38
-rw-r--r--plug-ins/lighting/lighting-shade.c838
-rw-r--r--plug-ins/lighting/lighting-shade.h20
-rw-r--r--plug-ins/lighting/lighting-stock.c110
-rw-r--r--plug-ins/lighting/lighting-stock.h37
-rw-r--r--plug-ins/lighting/lighting-ui.c1557
-rw-r--r--plug-ins/lighting/lighting-ui.h21
-rw-r--r--plug-ins/map-object/Makefile.am65
-rw-r--r--plug-ins/map-object/Makefile.in1046
-rw-r--r--plug-ins/map-object/README61
-rw-r--r--plug-ins/map-object/TODO18
-rw-r--r--plug-ins/map-object/arcball.c515
-rw-r--r--plug-ins/map-object/arcball.h50
-rw-r--r--plug-ins/map-object/map-object-apply.c328
-rw-r--r--plug-ins/map-object/map-object-apply.h10
-rw-r--r--plug-ins/map-object/map-object-image.c352
-rw-r--r--plug-ins/map-object/map-object-image.h62
-rw-r--r--plug-ins/map-object/map-object-main.c334
-rw-r--r--plug-ins/map-object/map-object-main.h90
-rw-r--r--plug-ins/map-object/map-object-preview.c745
-rw-r--r--plug-ins/map-object/map-object-preview.h26
-rw-r--r--plug-ins/map-object/map-object-shade.c1254
-rw-r--r--plug-ins/map-object/map-object-shade.h26
-rw-r--r--plug-ins/map-object/map-object-stock.c110
-rw-r--r--plug-ins/map-object/map-object-stock.h37
-rw-r--r--plug-ins/map-object/map-object-ui.c1462
-rw-r--r--plug-ins/map-object/map-object-ui.h14
-rw-r--r--plug-ins/metadata/Makefile.am82
-rw-r--r--plug-ins/metadata/Makefile.in1114
-rw-r--r--plug-ins/metadata/metadata-editor.c4757
-rw-r--r--plug-ins/metadata/metadata-editor.h27
-rw-r--r--plug-ins/metadata/metadata-impexp.c244
-rw-r--r--plug-ins/metadata/metadata-impexp.h30
-rw-r--r--plug-ins/metadata/metadata-misc.h70
-rw-r--r--plug-ins/metadata/metadata-tags.c506
-rw-r--r--plug-ins/metadata/metadata-tags.h253
-rw-r--r--plug-ins/metadata/metadata-viewer.c683
-rw-r--r--plug-ins/metadata/metadata-xml.c1147
-rw-r--r--plug-ins/metadata/metadata-xml.h98
-rw-r--r--plug-ins/pagecurl/Makefile.am82
-rw-r--r--plug-ins/pagecurl/Makefile.in1035
-rw-r--r--plug-ins/pagecurl/curl0.pngbin0 -> 445 bytes
-rw-r--r--plug-ins/pagecurl/curl1.pngbin0 -> 487 bytes
-rw-r--r--plug-ins/pagecurl/curl2.pngbin0 -> 476 bytes
-rw-r--r--plug-ins/pagecurl/curl3.pngbin0 -> 450 bytes
-rw-r--r--plug-ins/pagecurl/curl4.pngbin0 -> 431 bytes
-rw-r--r--plug-ins/pagecurl/curl5.pngbin0 -> 474 bytes
-rw-r--r--plug-ins/pagecurl/curl6.pngbin0 -> 488 bytes
-rw-r--r--plug-ins/pagecurl/curl7.pngbin0 -> 448 bytes
-rw-r--r--plug-ins/pagecurl/pagecurl.c1028
-rw-r--r--plug-ins/print/Makefile.am62
-rw-r--r--plug-ins/print/Makefile.in1038
-rw-r--r--plug-ins/print/print-draw-page.c222
-rw-r--r--plug-ins/print/print-draw-page.h20
-rw-r--r--plug-ins/print/print-page-layout.c948
-rw-r--r--plug-ins/print/print-page-layout.h20
-rw-r--r--plug-ins/print/print-page-setup.c101
-rw-r--r--plug-ins/print/print-page-setup.h27
-rw-r--r--plug-ins/print/print-preview.c880
-rw-r--r--plug-ins/print/print-preview.h60
-rw-r--r--plug-ins/print/print-settings.c320
-rw-r--r--plug-ins/print/print-settings.h19
-rw-r--r--plug-ins/print/print-utils.c145
-rw-r--r--plug-ins/print/print-utils.h26
-rw-r--r--plug-ins/print/print.c503
-rw-r--r--plug-ins/print/print.h56
-rw-r--r--plug-ins/pygimp/Makefile.am188
-rw-r--r--plug-ins/pygimp/Makefile.in1431
-rw-r--r--plug-ins/pygimp/gimp-types.defs9
-rw-r--r--plug-ins/pygimp/gimpcolor-types.defs25
-rw-r--r--plug-ins/pygimp/gimpcolormodule.c433
-rw-r--r--plug-ins/pygimp/gimpenums-types.defs338
-rw-r--r--plug-ins/pygimp/gimpenums.py50
-rw-r--r--plug-ins/pygimp/gimpenumsmodule.c174
-rw-r--r--plug-ins/pygimp/gimpfu.py876
-rw-r--r--plug-ins/pygimp/gimpmodule.c2071
-rw-r--r--plug-ins/pygimp/gimpplugin.py81
-rw-r--r--plug-ins/pygimp/gimpshelf.py92
-rw-r--r--plug-ins/pygimp/gimpthumb.c694
-rw-r--r--plug-ins/pygimp/gimpthumb.defs312
-rw-r--r--plug-ins/pygimp/gimpthumb.override102
-rw-r--r--plug-ins/pygimp/gimpthumbmodule.c60
-rw-r--r--plug-ins/pygimp/gimpui.c9387
-rw-r--r--plug-ins/pygimp/gimpui.defs3411
-rw-r--r--plug-ins/pygimp/gimpui.override2075
-rw-r--r--plug-ins/pygimp/gimpui.py227
-rw-r--r--plug-ins/pygimp/gimpuimodule.c97
-rw-r--r--plug-ins/pygimp/plug-ins/Makefile.am64
-rw-r--r--plug-ins/pygimp/plug-ins/Makefile.in946
-rwxr-xr-xplug-ins/pygimp/plug-ins/benchmark-foreground-extract.py197
-rwxr-xr-xplug-ins/pygimp/plug-ins/clothify.py76
-rwxr-xr-xplug-ins/pygimp/plug-ins/colorxhtml.py212
-rwxr-xr-xplug-ins/pygimp/plug-ins/file-openraster.py404
-rwxr-xr-xplug-ins/pygimp/plug-ins/foggify.py77
-rwxr-xr-xplug-ins/pygimp/plug-ins/gradients-save-as-css.py104
-rwxr-xr-xplug-ins/pygimp/plug-ins/histogram-export.py114
-rw-r--r--plug-ins/pygimp/plug-ins/palette-offset.py61
-rw-r--r--plug-ins/pygimp/plug-ins/palette-sort.py357
-rw-r--r--plug-ins/pygimp/plug-ins/palette-to-gradient.py88
-rwxr-xr-xplug-ins/pygimp/plug-ins/py-slice.py457
-rw-r--r--plug-ins/pygimp/plug-ins/pyconsole.py749
-rwxr-xr-xplug-ins/pygimp/plug-ins/python-console.py245
-rwxr-xr-xplug-ins/pygimp/plug-ins/python-eval.py42
-rwxr-xr-xplug-ins/pygimp/plug-ins/shadow_bevel.py82
-rwxr-xr-xplug-ins/pygimp/plug-ins/sphere.py111
-rw-r--r--plug-ins/pygimp/plug-ins/spyro_plus.py2212
-rwxr-xr-xplug-ins/pygimp/plug-ins/whirlpinch.py227
-rwxr-xr-xplug-ins/pygimp/py-compile72
-rw-r--r--plug-ins/pygimp/pygimp-api.h122
-rw-r--r--plug-ins/pygimp/pygimp-colors.c2407
-rw-r--r--plug-ins/pygimp/pygimp-display.c140
-rw-r--r--plug-ins/pygimp/pygimp-drawable.c2459
-rw-r--r--plug-ins/pygimp/pygimp-image.c1564
-rw-r--r--plug-ins/pygimp/pygimp-intl.h48
-rw-r--r--plug-ins/pygimp/pygimp-item.c181
-rw-r--r--plug-ins/pygimp/pygimp-logo.pngbin0 -> 19823 bytes
-rw-r--r--plug-ins/pygimp/pygimp-parasite.c216
-rw-r--r--plug-ins/pygimp/pygimp-pdb.c1110
-rw-r--r--plug-ins/pygimp/pygimp-tile.c1046
-rw-r--r--plug-ins/pygimp/pygimp-util.h53
-rw-r--r--plug-ins/pygimp/pygimp-vectors.c992
-rw-r--r--plug-ins/pygimp/pygimp.h139
-rw-r--r--plug-ins/pygimp/pygimpcolor-api.h84
-rw-r--r--plug-ins/pygimp/pygimpcolor.h50
-rw-r--r--plug-ins/screenshot/Makefile.am73
-rw-r--r--plug-ins/screenshot/Makefile.in1031
-rw-r--r--plug-ins/screenshot/screenshot-freedesktop.c199
-rw-r--r--plug-ins/screenshot/screenshot-freedesktop.h32
-rw-r--r--plug-ins/screenshot/screenshot-icon.h80
-rw-r--r--plug-ins/screenshot/screenshot-kwin.c207
-rw-r--r--plug-ins/screenshot/screenshot-kwin.h32
-rw-r--r--plug-ins/screenshot/screenshot-osx.c159
-rw-r--r--plug-ins/screenshot/screenshot-osx.h36
-rw-r--r--plug-ins/screenshot/screenshot-win32-dwm-api.h76
-rw-r--r--plug-ins/screenshot/screenshot-win32-magnification-api.h154
-rw-r--r--plug-ins/screenshot/screenshot-win32-resource.h26
-rw-r--r--plug-ins/screenshot/screenshot-win32-select.curbin0 -> 326 bytes
-rw-r--r--plug-ins/screenshot/screenshot-win32-small.icobin0 -> 318 bytes
-rw-r--r--plug-ins/screenshot/screenshot-win32.c1317
-rw-r--r--plug-ins/screenshot/screenshot-win32.h44
-rw-r--r--plug-ins/screenshot/screenshot-win32.icobin0 -> 1590 bytes
-rw-r--r--plug-ins/screenshot/screenshot-win32.rc189
-rw-r--r--plug-ins/screenshot/screenshot-x11.c697
-rw-r--r--plug-ins/screenshot/screenshot-x11.h36
-rw-r--r--plug-ins/screenshot/screenshot.c873
-rw-r--r--plug-ins/screenshot/screenshot.h80
-rw-r--r--plug-ins/script-fu/Makefile.am117
-rw-r--r--plug-ins/script-fu/Makefile.in1225
-rw-r--r--plug-ins/script-fu/ftx/LICENSE31
-rw-r--r--plug-ins/script-fu/ftx/Makefile.am16
-rw-r--r--plug-ins/script-fu/ftx/Makefile.in912
-rw-r--r--plug-ins/script-fu/ftx/README99
-rw-r--r--plug-ins/script-fu/ftx/ftx-functions.txt119
-rw-r--r--plug-ins/script-fu/ftx/ftx.c415
-rw-r--r--plug-ins/script-fu/ftx/ftx.h2
-rw-r--r--plug-ins/script-fu/ftx/listhome.scm58
-rw-r--r--plug-ins/script-fu/scheme-wrapper.c1671
-rw-r--r--plug-ins/script-fu/scheme-wrapper.h47
-rw-r--r--plug-ins/script-fu/script-fu-console.c703
-rw-r--r--plug-ins/script-fu/script-fu-console.h29
-rw-r--r--plug-ins/script-fu/script-fu-enums.h54
-rw-r--r--plug-ins/script-fu/script-fu-eval.c80
-rw-r--r--plug-ins/script-fu/script-fu-eval.h29
-rw-r--r--plug-ins/script-fu/script-fu-interface.c1070
-rw-r--r--plug-ins/script-fu/script-fu-interface.h28
-rw-r--r--plug-ins/script-fu/script-fu-intl.h43
-rw-r--r--plug-ins/script-fu/script-fu-regex.c179
-rw-r--r--plug-ins/script-fu/script-fu-regex.h25
-rw-r--r--plug-ins/script-fu/script-fu-script.c780
-rw-r--r--plug-ins/script-fu/script-fu-script.h49
-rw-r--r--plug-ins/script-fu/script-fu-scripts.c934
-rw-r--r--plug-ins/script-fu/script-fu-scripts.h29
-rw-r--r--plug-ins/script-fu/script-fu-server.c1113
-rw-r--r--plug-ins/script-fu/script-fu-server.h32
-rw-r--r--plug-ins/script-fu/script-fu-text-console.c60
-rw-r--r--plug-ins/script-fu/script-fu-text-console.h29
-rw-r--r--plug-ins/script-fu/script-fu-types.h107
-rw-r--r--plug-ins/script-fu/script-fu-utils.c69
-rw-r--r--plug-ins/script-fu/script-fu-utils.h25
-rw-r--r--plug-ins/script-fu/script-fu.c385
-rw-r--r--plug-ins/script-fu/scripts/Makefile.am70
-rw-r--r--plug-ins/script-fu/scripts/Makefile.in1044
-rw-r--r--plug-ins/script-fu/scripts/add-bevel.scm202
-rw-r--r--plug-ins/script-fu/scripts/addborder.scm177
-rw-r--r--plug-ins/script-fu/scripts/blend-anim.scm242
-rw-r--r--plug-ins/script-fu/scripts/burn-in-anim.scm243
-rw-r--r--plug-ins/script-fu/scripts/carve-it.scm205
-rw-r--r--plug-ins/script-fu/scripts/chrome-it.scm252
-rw-r--r--plug-ins/script-fu/scripts/circuit.scm143
-rw-r--r--plug-ins/script-fu/scripts/clothify.scm68
-rw-r--r--plug-ins/script-fu/scripts/coffee.scm94
-rw-r--r--plug-ins/script-fu/scripts/contactsheet.scm337
-rw-r--r--plug-ins/script-fu/scripts/copy-visible.scm49
-rw-r--r--plug-ins/script-fu/scripts/difference-clouds.scm80
-rw-r--r--plug-ins/script-fu/scripts/distress-selection.scm122
-rw-r--r--plug-ins/script-fu/scripts/drop-shadow.scm187
-rw-r--r--plug-ins/script-fu/scripts/erase-rows.scm71
-rw-r--r--plug-ins/script-fu/scripts/font-map.scm168
-rw-r--r--plug-ins/script-fu/scripts/fuzzyborder.scm168
-rw-r--r--plug-ins/script-fu/scripts/gimp-online.scm277
-rw-r--r--plug-ins/script-fu/scripts/gradient-example.scm81
-rw-r--r--plug-ins/script-fu/scripts/grid-system.scm95
-rw-r--r--plug-ins/script-fu/scripts/guides-from-selection.scm43
-rw-r--r--plug-ins/script-fu/scripts/guides-new-percent.scm41
-rw-r--r--plug-ins/script-fu/scripts/guides-new.scm40
-rw-r--r--plug-ins/script-fu/scripts/guides-remove-all.scm30
-rw-r--r--plug-ins/script-fu/scripts/images/Makefile.am12
-rw-r--r--plug-ins/script-fu/scripts/images/Makefile.in810
-rw-r--r--plug-ins/script-fu/scripts/images/beavis.jpgbin0 -> 20688 bytes
-rw-r--r--plug-ins/script-fu/scripts/images/texture.jpgbin0 -> 22622 bytes
-rw-r--r--plug-ins/script-fu/scripts/images/texture1.jpgbin0 -> 4256 bytes
-rw-r--r--plug-ins/script-fu/scripts/images/texture2.jpgbin0 -> 4983 bytes
-rw-r--r--plug-ins/script-fu/scripts/images/texture3.jpgbin0 -> 3245 bytes
-rw-r--r--plug-ins/script-fu/scripts/lava.scm135
-rw-r--r--plug-ins/script-fu/scripts/line-nova.scm123
-rw-r--r--plug-ins/script-fu/scripts/mkbrush.scm272
-rw-r--r--plug-ins/script-fu/scripts/old-photo.scm108
-rw-r--r--plug-ins/script-fu/scripts/palette-export.scm402
-rw-r--r--plug-ins/script-fu/scripts/paste-as-brush.scm74
-rw-r--r--plug-ins/script-fu/scripts/paste-as-pattern.scm61
-rw-r--r--plug-ins/script-fu/scripts/perspective-shadow.scm217
-rw-r--r--plug-ins/script-fu/scripts/plug-in-compat.init24
-rw-r--r--plug-ins/script-fu/scripts/predator.scm137
-rw-r--r--plug-ins/script-fu/scripts/reverse-layers.scm53
-rw-r--r--plug-ins/script-fu/scripts/ripply-anim.scm83
-rw-r--r--plug-ins/script-fu/scripts/round-corners.scm149
-rw-r--r--plug-ins/script-fu/scripts/script-fu-compat.init457
-rw-r--r--plug-ins/script-fu/scripts/script-fu-set-cmap.scm64
-rw-r--r--plug-ins/script-fu/scripts/script-fu-util.scm94
-rw-r--r--plug-ins/script-fu/scripts/script-fu.init716
-rw-r--r--plug-ins/script-fu/scripts/select-to-brush.scm144
-rw-r--r--plug-ins/script-fu/scripts/select-to-image.scm89
-rw-r--r--plug-ins/script-fu/scripts/select-to-pattern.scm103
-rw-r--r--plug-ins/script-fu/scripts/selection-round.scm164
-rw-r--r--plug-ins/script-fu/scripts/slide.scm261
-rw-r--r--plug-ins/script-fu/scripts/spinning-globe.scm110
-rw-r--r--plug-ins/script-fu/scripts/spyrogimp.scm352
-rw-r--r--plug-ins/script-fu/scripts/test-sphere.scm307
-rw-r--r--plug-ins/script-fu/scripts/tileblur.scm83
-rw-r--r--plug-ins/script-fu/scripts/ts-helloworld.scm65
-rw-r--r--plug-ins/script-fu/scripts/unsharp-mask.scm84
-rw-r--r--plug-ins/script-fu/scripts/waves-anim.scm110
-rw-r--r--plug-ins/script-fu/scripts/weave.scm415
-rw-r--r--plug-ins/script-fu/scripts/xach-effect.scm142
-rw-r--r--plug-ins/script-fu/tinyscheme/BUILDING139
-rw-r--r--plug-ins/script-fu/tinyscheme/CHANGES326
-rw-r--r--plug-ins/script-fu/tinyscheme/COPYING31
-rw-r--r--plug-ins/script-fu/tinyscheme/Makefile.am26
-rw-r--r--plug-ins/script-fu/tinyscheme/Makefile.in924
-rw-r--r--plug-ins/script-fu/tinyscheme/Manual.txt452
-rw-r--r--plug-ins/script-fu/tinyscheme/MiniSCHEMETribute.txt88
-rw-r--r--plug-ins/script-fu/tinyscheme/README14
-rw-r--r--plug-ins/script-fu/tinyscheme/hack.txt233
-rw-r--r--plug-ins/script-fu/tinyscheme/opdefines.h195
-rw-r--r--plug-ins/script-fu/tinyscheme/scheme-private.h227
-rw-r--r--plug-ins/script-fu/tinyscheme/scheme.c5352
-rw-r--r--plug-ins/script-fu/tinyscheme/scheme.h273
-rw-r--r--plug-ins/selection-to-path/Makefile.am73
-rw-r--r--plug-ins/selection-to-path/Makefile.in1057
-rw-r--r--plug-ins/selection-to-path/README51
-rw-r--r--plug-ins/selection-to-path/README.limn56
-rw-r--r--plug-ins/selection-to-path/bitmap.h112
-rw-r--r--plug-ins/selection-to-path/bounding-box.h63
-rw-r--r--plug-ins/selection-to-path/curve.c184
-rw-r--r--plug-ins/selection-to-path/curve.h157
-rw-r--r--plug-ins/selection-to-path/edge.c268
-rw-r--r--plug-ins/selection-to-path/edge.h59
-rw-r--r--plug-ins/selection-to-path/fit.c1967
-rw-r--r--plug-ins/selection-to-path/fit.h54
-rw-r--r--plug-ins/selection-to-path/global.h213
-rw-r--r--plug-ins/selection-to-path/math.c177
-rw-r--r--plug-ins/selection-to-path/pxl-outline.c254
-rw-r--r--plug-ins/selection-to-path/pxl-outline.h68
-rw-r--r--plug-ins/selection-to-path/selection-to-path-dialog.c403
-rw-r--r--plug-ins/selection-to-path/selection-to-path.c536
-rw-r--r--plug-ins/selection-to-path/selection-to-path.h37
-rw-r--r--plug-ins/selection-to-path/spline.c233
-rw-r--r--plug-ins/selection-to-path/spline.h124
-rw-r--r--plug-ins/selection-to-path/types.h147
-rw-r--r--plug-ins/selection-to-path/vector.c249
-rw-r--r--plug-ins/selection-to-path/vector.h95
-rw-r--r--plug-ins/twain/Makefile.am61
-rw-r--r--plug-ins/twain/Makefile.in1027
-rw-r--r--plug-ins/twain/README92
-rw-r--r--plug-ins/twain/gimp-twain.pngbin0 -> 13859 bytes
-rw-r--r--plug-ins/twain/tw_dump.c273
-rw-r--r--plug-ins/twain/tw_dump.h70
-rw-r--r--plug-ins/twain/tw_func.c874
-rw-r--r--plug-ins/twain/tw_func.h243
-rw-r--r--plug-ins/twain/tw_local.h48
-rw-r--r--plug-ins/twain/tw_platform.h37
-rw-r--r--plug-ins/twain/tw_util.c170
-rw-r--r--plug-ins/twain/tw_util.h73
-rw-r--r--plug-ins/twain/tw_win.c452
-rw-r--r--plug-ins/twain/twain.c962
-rw-r--r--plug-ins/twain/twain.h1819
-rw-r--r--plug-ins/ui/Makefile.am12
-rw-r--r--plug-ins/ui/Makefile.in811
-rw-r--r--plug-ins/ui/plug-in-file-gif.ui306
-rw-r--r--plug-ins/ui/plug-in-file-png.ui350
-rw-r--r--plug-ins/ui/plug-in-file-raw.ui111
-rw-r--r--plug-ins/ui/plug-in-file-tiff.ui306
-rw-r--r--plug-ins/ui/plug-in-metadata-editor-calendar.ui25
-rw-r--r--plug-ins/ui/plug-in-metadata-editor.ui4953
-rw-r--r--plug-ins/ui/plug-in-metadata-viewer.ui226
965 files changed, 397377 insertions, 0 deletions
diff --git a/plug-ins/Makefile.am b/plug-ins/Makefile.am
new file mode 100644
index 0000000..5fe4384
--- /dev/null
+++ b/plug-ins/Makefile.am
@@ -0,0 +1,62 @@
+## Process this file with automake to produce Makefile.in
+
+if HAVE_WEBKIT
+help_browser = help-browser
+endif
+
+if HAVE_OPENEXR
+file_exr = file-exr
+endif
+
+if BUILD_PRINT
+print = print
+endif
+
+if BUILD_PYTHON
+pygimp = pygimp
+endif
+
+if OS_WIN32
+twain = twain
+endif
+
+if HAVE_WEBP
+file_webp = file-webp
+endif
+
+SUBDIRS = \
+ script-fu \
+ $(pygimp) \
+ file-bmp \
+ $(file_darktable) \
+ file-dds \
+ $(file_exr) \
+ file-faxg3 \
+ file-fits \
+ file-fli \
+ file-ico \
+ file-jpeg \
+ file-psd \
+ file-raw \
+ file-sgi \
+ file-tiff \
+ $(file_webp) \
+ flame \
+ fractal-explorer \
+ gfig \
+ gimpressionist \
+ gradient-flare \
+ help \
+ $(help_browser) \
+ ifs-compose \
+ imagemap \
+ lighting \
+ map-object \
+ metadata \
+ pagecurl \
+ $(print) \
+ screenshot \
+ selection-to-path \
+ $(twain) \
+ ui \
+ common
diff --git a/plug-ins/Makefile.in b/plug-ins/Makefile.in
new file mode 100644
index 0000000..5b9db73
--- /dev/null
+++ b/plug-ins/Makefile.in
@@ -0,0 +1,974 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = script-fu pygimp file-bmp file-dds file-exr file-faxg3 \
+ file-fits file-fli file-ico file-jpeg file-psd file-raw \
+ file-sgi file-tiff file-webp flame fractal-explorer gfig \
+ gimpressionist gradient-flare help help-browser ifs-compose \
+ imagemap lighting map-object metadata pagecurl print \
+ screenshot selection-to-path twain ui common
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@HAVE_WEBKIT_TRUE@help_browser = help-browser
+@HAVE_OPENEXR_TRUE@file_exr = file-exr
+@BUILD_PRINT_TRUE@print = print
+@BUILD_PYTHON_TRUE@pygimp = pygimp
+@OS_WIN32_TRUE@twain = twain
+@HAVE_WEBP_TRUE@file_webp = file-webp
+SUBDIRS = \
+ script-fu \
+ $(pygimp) \
+ file-bmp \
+ $(file_darktable) \
+ file-dds \
+ $(file_exr) \
+ file-faxg3 \
+ file-fits \
+ file-fli \
+ file-ico \
+ file-jpeg \
+ file-psd \
+ file-raw \
+ file-sgi \
+ file-tiff \
+ $(file_webp) \
+ flame \
+ fractal-explorer \
+ gfig \
+ gimpressionist \
+ gradient-flare \
+ help \
+ $(help_browser) \
+ ifs-compose \
+ imagemap \
+ lighting \
+ map-object \
+ metadata \
+ pagecurl \
+ $(print) \
+ screenshot \
+ selection-to-path \
+ $(twain) \
+ ui \
+ common
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/common/Makefile.am b/plug-ins/common/Makefile.am
new file mode 100644
index 0000000..75fbd61
--- /dev/null
+++ b/plug-ins/common/Makefile.am
@@ -0,0 +1,1883 @@
+
+
+## ---------------------------------------------------------
+## This file is autogenerated by mkgen.pl and plugin-defs.pl
+## ---------------------------------------------------------
+
+## Modify those two files instead of this one; for most
+## plug-ins you should only need to modify plugin-defs.pl.
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+framework_cocoa = -framework Cocoa
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+include gimprc.common
+endif
+
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la $(libm)
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+
+AM_LDFLAGS = $(mwindows)
+
+EXTRA_DIST = \
+ mkgen.pl \
+ plugin-defs.pl \
+ gimprc.common
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+align_layers_libexecdir = $(gimpplugindir)/plug-ins/align-layers
+animation_optimize_libexecdir = $(gimpplugindir)/plug-ins/animation-optimize
+animation_play_libexecdir = $(gimpplugindir)/plug-ins/animation-play
+blinds_libexecdir = $(gimpplugindir)/plug-ins/blinds
+blur_libexecdir = $(gimpplugindir)/plug-ins/blur
+border_average_libexecdir = $(gimpplugindir)/plug-ins/border-average
+busy_dialog_libexecdir = $(gimpplugindir)/plug-ins/busy-dialog
+cartoon_libexecdir = $(gimpplugindir)/plug-ins/cartoon
+checkerboard_libexecdir = $(gimpplugindir)/plug-ins/checkerboard
+cml_explorer_libexecdir = $(gimpplugindir)/plug-ins/cml-explorer
+color_cube_analyze_libexecdir = $(gimpplugindir)/plug-ins/color-cube-analyze
+color_enhance_libexecdir = $(gimpplugindir)/plug-ins/color-enhance
+colorify_libexecdir = $(gimpplugindir)/plug-ins/colorify
+colormap_remap_libexecdir = $(gimpplugindir)/plug-ins/colormap-remap
+compose_libexecdir = $(gimpplugindir)/plug-ins/compose
+contrast_retinex_libexecdir = $(gimpplugindir)/plug-ins/contrast-retinex
+crop_zealous_libexecdir = $(gimpplugindir)/plug-ins/crop-zealous
+curve_bend_libexecdir = $(gimpplugindir)/plug-ins/curve-bend
+decompose_libexecdir = $(gimpplugindir)/plug-ins/decompose
+depth_merge_libexecdir = $(gimpplugindir)/plug-ins/depth-merge
+despeckle_libexecdir = $(gimpplugindir)/plug-ins/despeckle
+destripe_libexecdir = $(gimpplugindir)/plug-ins/destripe
+edge_dog_libexecdir = $(gimpplugindir)/plug-ins/edge-dog
+emboss_libexecdir = $(gimpplugindir)/plug-ins/emboss
+file_aa_libexecdir = $(gimpplugindir)/plug-ins/file-aa
+file_cel_libexecdir = $(gimpplugindir)/plug-ins/file-cel
+file_compressor_libexecdir = $(gimpplugindir)/plug-ins/file-compressor
+file_csource_libexecdir = $(gimpplugindir)/plug-ins/file-csource
+file_desktop_link_libexecdir = $(gimpplugindir)/plug-ins/file-desktop-link
+file_dicom_libexecdir = $(gimpplugindir)/plug-ins/file-dicom
+file_gbr_libexecdir = $(gimpplugindir)/plug-ins/file-gbr
+file_gegl_libexecdir = $(gimpplugindir)/plug-ins/file-gegl
+file_gif_load_libexecdir = $(gimpplugindir)/plug-ins/file-gif-load
+file_gif_save_libexecdir = $(gimpplugindir)/plug-ins/file-gif-save
+file_gih_libexecdir = $(gimpplugindir)/plug-ins/file-gih
+file_glob_libexecdir = $(gimpplugindir)/plug-ins/file-glob
+file_header_libexecdir = $(gimpplugindir)/plug-ins/file-header
+file_heif_libexecdir = $(gimpplugindir)/plug-ins/file-heif
+file_html_table_libexecdir = $(gimpplugindir)/plug-ins/file-html-table
+file_jp2_load_libexecdir = $(gimpplugindir)/plug-ins/file-jp2-load
+file_jpegxl_libexecdir = $(gimpplugindir)/plug-ins/file-jpegxl
+file_mng_libexecdir = $(gimpplugindir)/plug-ins/file-mng
+file_pat_libexecdir = $(gimpplugindir)/plug-ins/file-pat
+file_pcx_libexecdir = $(gimpplugindir)/plug-ins/file-pcx
+file_pdf_load_libexecdir = $(gimpplugindir)/plug-ins/file-pdf-load
+file_pdf_save_libexecdir = $(gimpplugindir)/plug-ins/file-pdf-save
+file_pix_libexecdir = $(gimpplugindir)/plug-ins/file-pix
+file_png_libexecdir = $(gimpplugindir)/plug-ins/file-png
+file_pnm_libexecdir = $(gimpplugindir)/plug-ins/file-pnm
+file_ps_libexecdir = $(gimpplugindir)/plug-ins/file-ps
+file_psp_libexecdir = $(gimpplugindir)/plug-ins/file-psp
+file_raw_data_libexecdir = $(gimpplugindir)/plug-ins/file-raw-data
+file_sunras_libexecdir = $(gimpplugindir)/plug-ins/file-sunras
+file_svg_libexecdir = $(gimpplugindir)/plug-ins/file-svg
+file_tga_libexecdir = $(gimpplugindir)/plug-ins/file-tga
+file_wmf_libexecdir = $(gimpplugindir)/plug-ins/file-wmf
+file_xbm_libexecdir = $(gimpplugindir)/plug-ins/file-xbm
+file_xmc_libexecdir = $(gimpplugindir)/plug-ins/file-xmc
+file_xpm_libexecdir = $(gimpplugindir)/plug-ins/file-xpm
+file_xwd_libexecdir = $(gimpplugindir)/plug-ins/file-xwd
+film_libexecdir = $(gimpplugindir)/plug-ins/film
+filter_pack_libexecdir = $(gimpplugindir)/plug-ins/filter-pack
+fractal_trace_libexecdir = $(gimpplugindir)/plug-ins/fractal-trace
+goat_exercise_libexecdir = $(gimpplugindir)/plug-ins/goat-exercise
+gradient_map_libexecdir = $(gimpplugindir)/plug-ins/gradient-map
+grid_libexecdir = $(gimpplugindir)/plug-ins/grid
+guillotine_libexecdir = $(gimpplugindir)/plug-ins/guillotine
+hot_libexecdir = $(gimpplugindir)/plug-ins/hot
+jigsaw_libexecdir = $(gimpplugindir)/plug-ins/jigsaw
+mail_libexecdir = $(gimpplugindir)/plug-ins/mail
+max_rgb_libexecdir = $(gimpplugindir)/plug-ins/max-rgb
+nl_filter_libexecdir = $(gimpplugindir)/plug-ins/nl-filter
+photocopy_libexecdir = $(gimpplugindir)/plug-ins/photocopy
+plugin_browser_libexecdir = $(gimpplugindir)/plug-ins/plugin-browser
+procedure_browser_libexecdir = $(gimpplugindir)/plug-ins/procedure-browser
+qbist_libexecdir = $(gimpplugindir)/plug-ins/qbist
+sample_colorize_libexecdir = $(gimpplugindir)/plug-ins/sample-colorize
+sharpen_libexecdir = $(gimpplugindir)/plug-ins/sharpen
+smooth_palette_libexecdir = $(gimpplugindir)/plug-ins/smooth-palette
+softglow_libexecdir = $(gimpplugindir)/plug-ins/softglow
+sparkle_libexecdir = $(gimpplugindir)/plug-ins/sparkle
+sphere_designer_libexecdir = $(gimpplugindir)/plug-ins/sphere-designer
+tile_libexecdir = $(gimpplugindir)/plug-ins/tile
+tile_small_libexecdir = $(gimpplugindir)/plug-ins/tile-small
+unit_editor_libexecdir = $(gimpplugindir)/plug-ins/unit-editor
+van_gogh_lic_libexecdir = $(gimpplugindir)/plug-ins/van-gogh-lic
+warp_libexecdir = $(gimpplugindir)/plug-ins/warp
+wavelet_decompose_libexecdir = $(gimpplugindir)/plug-ins/wavelet-decompose
+web_browser_libexecdir = $(gimpplugindir)/plug-ins/web-browser
+web_page_libexecdir = $(gimpplugindir)/plug-ins/web-page
+
+
+align_layers_libexec_PROGRAMS = align-layers
+animation_optimize_libexec_PROGRAMS = animation-optimize
+animation_play_libexec_PROGRAMS = animation-play
+blinds_libexec_PROGRAMS = blinds
+blur_libexec_PROGRAMS = blur
+border_average_libexec_PROGRAMS = border-average
+busy_dialog_libexec_PROGRAMS = busy-dialog
+cartoon_libexec_PROGRAMS = cartoon
+checkerboard_libexec_PROGRAMS = checkerboard
+cml_explorer_libexec_PROGRAMS = cml-explorer
+color_cube_analyze_libexec_PROGRAMS = color-cube-analyze
+color_enhance_libexec_PROGRAMS = color-enhance
+colorify_libexec_PROGRAMS = colorify
+colormap_remap_libexec_PROGRAMS = colormap-remap
+compose_libexec_PROGRAMS = compose
+contrast_retinex_libexec_PROGRAMS = contrast-retinex
+crop_zealous_libexec_PROGRAMS = crop-zealous
+curve_bend_libexec_PROGRAMS = curve-bend
+decompose_libexec_PROGRAMS = decompose
+depth_merge_libexec_PROGRAMS = depth-merge
+despeckle_libexec_PROGRAMS = despeckle
+destripe_libexec_PROGRAMS = destripe
+edge_dog_libexec_PROGRAMS = edge-dog
+emboss_libexec_PROGRAMS = emboss
+file_aa_libexec_PROGRAMS = $(FILE_AA)
+file_cel_libexec_PROGRAMS = file-cel
+file_compressor_libexec_PROGRAMS = file-compressor
+file_csource_libexec_PROGRAMS = file-csource
+file_desktop_link_libexec_PROGRAMS = file-desktop-link
+file_dicom_libexec_PROGRAMS = file-dicom
+file_gbr_libexec_PROGRAMS = file-gbr
+file_gegl_libexec_PROGRAMS = file-gegl
+file_gif_load_libexec_PROGRAMS = file-gif-load
+file_gif_save_libexec_PROGRAMS = file-gif-save
+file_gih_libexec_PROGRAMS = file-gih
+file_glob_libexec_PROGRAMS = file-glob
+file_header_libexec_PROGRAMS = file-header
+file_heif_libexec_PROGRAMS = $(FILE_HEIF)
+file_html_table_libexec_PROGRAMS = file-html-table
+file_jp2_load_libexec_PROGRAMS = $(FILE_JP2_LOAD)
+file_jpegxl_libexec_PROGRAMS = $(FILE_JPEGXL)
+file_mng_libexec_PROGRAMS = $(FILE_MNG)
+file_pat_libexec_PROGRAMS = file-pat
+file_pcx_libexec_PROGRAMS = file-pcx
+file_pdf_load_libexec_PROGRAMS = file-pdf-load
+file_pdf_save_libexec_PROGRAMS = $(FILE_PDF_SAVE)
+file_pix_libexec_PROGRAMS = file-pix
+file_png_libexec_PROGRAMS = file-png
+file_pnm_libexec_PROGRAMS = file-pnm
+file_ps_libexec_PROGRAMS = $(FILE_PS)
+file_psp_libexec_PROGRAMS = file-psp
+file_raw_data_libexec_PROGRAMS = file-raw-data
+file_sunras_libexec_PROGRAMS = file-sunras
+file_svg_libexec_PROGRAMS = file-svg
+file_tga_libexec_PROGRAMS = file-tga
+file_wmf_libexec_PROGRAMS = $(FILE_WMF)
+file_xbm_libexec_PROGRAMS = file-xbm
+file_xmc_libexec_PROGRAMS = $(FILE_XMC)
+file_xpm_libexec_PROGRAMS = $(FILE_XPM)
+file_xwd_libexec_PROGRAMS = file-xwd
+film_libexec_PROGRAMS = film
+filter_pack_libexec_PROGRAMS = filter-pack
+fractal_trace_libexec_PROGRAMS = fractal-trace
+goat_exercise_libexec_PROGRAMS = goat-exercise
+gradient_map_libexec_PROGRAMS = gradient-map
+grid_libexec_PROGRAMS = grid
+guillotine_libexec_PROGRAMS = guillotine
+hot_libexec_PROGRAMS = hot
+jigsaw_libexec_PROGRAMS = jigsaw
+mail_libexec_PROGRAMS = $(MAIL)
+max_rgb_libexec_PROGRAMS = max-rgb
+nl_filter_libexec_PROGRAMS = nl-filter
+photocopy_libexec_PROGRAMS = photocopy
+plugin_browser_libexec_PROGRAMS = plugin-browser
+procedure_browser_libexec_PROGRAMS = procedure-browser
+qbist_libexec_PROGRAMS = qbist
+sample_colorize_libexec_PROGRAMS = sample-colorize
+sharpen_libexec_PROGRAMS = sharpen
+smooth_palette_libexec_PROGRAMS = smooth-palette
+softglow_libexec_PROGRAMS = softglow
+sparkle_libexec_PROGRAMS = sparkle
+sphere_designer_libexec_PROGRAMS = sphere-designer
+tile_libexec_PROGRAMS = tile
+tile_small_libexec_PROGRAMS = tile-small
+unit_editor_libexec_PROGRAMS = unit-editor
+van_gogh_lic_libexec_PROGRAMS = van-gogh-lic
+warp_libexec_PROGRAMS = warp
+wavelet_decompose_libexec_PROGRAMS = wavelet-decompose
+web_browser_libexec_PROGRAMS = web-browser
+web_page_libexec_PROGRAMS = $(WEB_PAGE)
+
+
+EXTRA_PROGRAMS = \
+ file-aa \
+ file-heif \
+ file-jp2-load \
+ file-jpegxl \
+ file-mng \
+ file-pdf-save \
+ file-ps \
+ file-wmf \
+ file-xmc \
+ file-xpm \
+ mail \
+ web-page
+
+install-%: %
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(gimpplugindir)/plug-ins/$<
+ @p=$<; p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(gimpplugindir)/plug-ins/$$p/$$f"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(gimpplugindir)/plug-ins/$$p/$$f || exit 1; \
+ else :; fi
+
+align_layers_SOURCES = \
+ align-layers.c
+
+align_layers_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(align_layers_RC)
+
+animation_optimize_SOURCES = \
+ animation-optimize.c
+
+animation_optimize_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(animation_optimize_RC)
+
+animation_play_SOURCES = \
+ animation-play.c
+
+animation_play_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(animation_play_RC)
+
+blinds_SOURCES = \
+ blinds.c
+
+blinds_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(blinds_RC)
+
+blur_SOURCES = \
+ blur.c
+
+blur_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(blur_RC)
+
+border_average_SOURCES = \
+ border-average.c
+
+border_average_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(border_average_RC)
+
+busy_dialog_SOURCES = \
+ busy-dialog.c
+
+busy_dialog_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(busy_dialog_RC)
+
+cartoon_SOURCES = \
+ cartoon.c
+
+cartoon_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(cartoon_RC)
+
+checkerboard_SOURCES = \
+ checkerboard.c
+
+checkerboard_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(checkerboard_RC)
+
+cml_explorer_SOURCES = \
+ cml-explorer.c
+
+cml_explorer_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(cml_explorer_RC)
+
+color_cube_analyze_SOURCES = \
+ color-cube-analyze.c
+
+color_cube_analyze_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(color_cube_analyze_RC)
+
+color_enhance_SOURCES = \
+ color-enhance.c
+
+color_enhance_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(color_enhance_RC)
+
+colorify_SOURCES = \
+ colorify.c
+
+colorify_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(colorify_RC)
+
+colormap_remap_SOURCES = \
+ colormap-remap.c
+
+colormap_remap_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(colormap_remap_RC)
+
+compose_SOURCES = \
+ compose.c
+
+compose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(compose_RC)
+
+contrast_retinex_SOURCES = \
+ contrast-retinex.c
+
+contrast_retinex_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(contrast_retinex_RC)
+
+crop_zealous_SOURCES = \
+ crop-zealous.c
+
+crop_zealous_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(crop_zealous_RC)
+
+curve_bend_SOURCES = \
+ curve-bend.c
+
+curve_bend_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(curve_bend_RC)
+
+decompose_SOURCES = \
+ decompose.c
+
+decompose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(decompose_RC)
+
+depth_merge_SOURCES = \
+ depth-merge.c
+
+depth_merge_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(depth_merge_RC)
+
+despeckle_SOURCES = \
+ despeckle.c
+
+despeckle_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(despeckle_RC)
+
+destripe_SOURCES = \
+ destripe.c
+
+destripe_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(destripe_RC)
+
+edge_dog_SOURCES = \
+ edge-dog.c
+
+edge_dog_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(edge_dog_RC)
+
+emboss_SOURCES = \
+ emboss.c
+
+emboss_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(emboss_RC)
+
+file_aa_SOURCES = \
+ file-aa.c
+
+file_aa_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(AA_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_aa_RC)
+
+file_cel_SOURCES = \
+ file-cel.c
+
+file_cel_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_cel_RC)
+
+file_compressor_CFLAGS = $(LZMA_CFLAGS)
+
+file_compressor_SOURCES = \
+ file-compressor.c
+
+file_compressor_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS) \
+ $(LZMA_LIBS) \
+ $(BZIP2_LIBS) \
+ $(Z_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_compressor_RC)
+
+file_csource_SOURCES = \
+ file-csource.c
+
+file_csource_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_csource_RC)
+
+file_desktop_link_SOURCES = \
+ file-desktop-link.c
+
+file_desktop_link_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_desktop_link_RC)
+
+file_dicom_CFLAGS = -fno-strict-aliasing
+
+file_dicom_SOURCES = \
+ file-dicom.c
+
+file_dicom_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_dicom_RC)
+
+file_gbr_SOURCES = \
+ file-gbr.c
+
+file_gbr_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gbr_RC)
+
+file_gegl_SOURCES = \
+ file-gegl.c
+
+file_gegl_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gegl_RC)
+
+file_gif_load_SOURCES = \
+ file-gif-load.c
+
+file_gif_load_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gif_load_RC)
+
+file_gif_save_SOURCES = \
+ file-gif-save.c
+
+file_gif_save_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gif_save_RC)
+
+file_gih_SOURCES = \
+ file-gih.c
+
+file_gih_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gih_RC)
+
+file_glob_SOURCES = \
+ file-glob.c
+
+file_glob_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_glob_RC)
+
+file_header_SOURCES = \
+ file-header.c
+
+file_header_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_header_RC)
+
+file_heif_CFLAGS = $(LIBHEIF_CFLAGS)
+
+file_heif_SOURCES = \
+ file-heif.c
+
+file_heif_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(LIBHEIF_LIBS) \
+ $(LCMS_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_heif_RC)
+
+file_html_table_SOURCES = \
+ file-html-table.c
+
+file_html_table_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_html_table_RC)
+
+file_jp2_load_CFLAGS = $(OPENJPEG_CFLAGS)
+
+file_jp2_load_SOURCES = \
+ file-jp2-load.c
+
+file_jp2_load_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(OPENJPEG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jp2_load_RC)
+
+file_jpegxl_CFLAGS = $(JXL_CFLAGS)
+
+file_jpegxl_SOURCES = \
+ file-jpegxl.c
+
+file_jpegxl_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(JXL_THREADS_LIBS) \
+ $(JXL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpegxl_RC)
+
+file_mng_CFLAGS = $(MNG_CFLAGS)
+
+file_mng_SOURCES = \
+ file-mng.c
+
+file_mng_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(MNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_mng_RC)
+
+file_pat_SOURCES = \
+ file-pat.c
+
+file_pat_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pat_RC)
+
+file_pcx_SOURCES = \
+ file-pcx.c
+
+file_pcx_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pcx_RC)
+
+file_pdf_load_CFLAGS = $(POPPLER_CFLAGS)
+
+file_pdf_load_SOURCES = \
+ file-pdf-load.c
+
+file_pdf_load_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(POPPLER_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pdf_load_RC)
+
+file_pdf_save_CFLAGS = $(CAIRO_PDF_CFLAGS)
+
+file_pdf_save_SOURCES = \
+ file-pdf-save.c
+
+file_pdf_save_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(CAIRO_PDF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pdf_save_RC)
+
+file_pix_SOURCES = \
+ file-pix.c
+
+file_pix_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pix_RC)
+
+file_png_CFLAGS = $(PNG_CFLAGS)
+
+file_png_SOURCES = \
+ file-png.c
+
+file_png_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(PNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_png_RC)
+
+file_pnm_SOURCES = \
+ file-pnm.c
+
+file_pnm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pnm_RC)
+
+file_ps_SOURCES = \
+ file-ps.c
+
+file_ps_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GS_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_ps_RC)
+
+file_psp_SOURCES = \
+ file-psp.c
+
+file_psp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(Z_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_psp_RC)
+
+file_raw_data_SOURCES = \
+ file-raw-data.c
+
+file_raw_data_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_raw_data_RC)
+
+file_sunras_SOURCES = \
+ file-sunras.c
+
+file_sunras_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_sunras_RC)
+
+file_svg_CFLAGS = $(SVG_CFLAGS)
+
+file_svg_SOURCES = \
+ file-svg.c
+
+file_svg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(SVG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_svg_RC)
+
+file_tga_SOURCES = \
+ file-tga.c
+
+file_tga_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tga_RC)
+
+file_wmf_CFLAGS = $(WMF_CFLAGS)
+
+file_wmf_SOURCES = \
+ file-wmf.c
+
+file_wmf_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(WMF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_wmf_RC)
+
+file_xbm_SOURCES = \
+ file-xbm.c
+
+file_xbm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xbm_RC)
+
+file_xmc_SOURCES = \
+ file-xmc.c
+
+file_xmc_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(XMC_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xmc_RC)
+
+file_xpm_SOURCES = \
+ file-xpm.c
+
+file_xpm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(XPM_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xpm_RC)
+
+file_xwd_SOURCES = \
+ file-xwd.c
+
+file_xwd_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xwd_RC)
+
+film_SOURCES = \
+ film.c
+
+film_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(film_RC)
+
+filter_pack_SOURCES = \
+ filter-pack.c
+
+filter_pack_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(filter_pack_RC)
+
+fractal_trace_SOURCES = \
+ fractal-trace.c
+
+fractal_trace_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(fractal_trace_RC)
+
+goat_exercise_SOURCES = \
+ goat-exercise.c
+
+goat_exercise_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(goat_exercise_RC)
+
+gradient_map_SOURCES = \
+ gradient-map.c
+
+gradient_map_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gradient_map_RC)
+
+grid_SOURCES = \
+ grid.c
+
+grid_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(grid_RC)
+
+guillotine_SOURCES = \
+ guillotine.c
+
+guillotine_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(guillotine_RC)
+
+hot_SOURCES = \
+ hot.c
+
+hot_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(hot_RC)
+
+jigsaw_SOURCES = \
+ jigsaw.c
+
+jigsaw_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(jigsaw_RC)
+
+mail_SOURCES = \
+ mail.c
+
+mail_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(mail_RC)
+
+max_rgb_SOURCES = \
+ max-rgb.c
+
+max_rgb_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(max_rgb_RC)
+
+nl_filter_SOURCES = \
+ nl-filter.c
+
+nl_filter_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(nl_filter_RC)
+
+photocopy_SOURCES = \
+ photocopy.c
+
+photocopy_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(photocopy_RC)
+
+plugin_browser_SOURCES = \
+ plugin-browser.c
+
+plugin_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(plugin_browser_RC)
+
+procedure_browser_SOURCES = \
+ procedure-browser.c
+
+procedure_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(procedure_browser_RC)
+
+qbist_SOURCES = \
+ qbist.c
+
+qbist_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(qbist_RC)
+
+sample_colorize_SOURCES = \
+ sample-colorize.c
+
+sample_colorize_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sample_colorize_RC)
+
+sharpen_SOURCES = \
+ sharpen.c
+
+sharpen_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sharpen_RC)
+
+smooth_palette_SOURCES = \
+ smooth-palette.c
+
+smooth_palette_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(smooth_palette_RC)
+
+softglow_SOURCES = \
+ softglow.c
+
+softglow_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(softglow_RC)
+
+sparkle_SOURCES = \
+ sparkle.c
+
+sparkle_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sparkle_RC)
+
+sphere_designer_SOURCES = \
+ sphere-designer.c
+
+sphere_designer_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sphere_designer_RC)
+
+tile_SOURCES = \
+ tile.c
+
+tile_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(tile_RC)
+
+tile_small_SOURCES = \
+ tile-small.c
+
+tile_small_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(tile_small_RC)
+
+unit_editor_SOURCES = \
+ unit-editor.c
+
+unit_editor_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(unit_editor_RC)
+
+van_gogh_lic_SOURCES = \
+ van-gogh-lic.c
+
+van_gogh_lic_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(van_gogh_lic_RC)
+
+warp_SOURCES = \
+ warp.c
+
+warp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(warp_RC)
+
+wavelet_decompose_SOURCES = \
+ wavelet-decompose.c
+
+wavelet_decompose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(wavelet_decompose_RC)
+
+web_browser_LDFLAGS = $(framework_cocoa)
+
+web_browser_CPPFLAGS = $(AM_CPPFLAGS) $(xobjective_c)
+
+web_browser_SOURCES = \
+ web-browser.c
+
+web_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(web_browser_RC)
+
+web_page_CFLAGS = $(WEBKIT_CFLAGS)
+
+web_page_SOURCES = \
+ web-page.c
+
+web_page_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(WEBKIT_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(web_page_RC)
diff --git a/plug-ins/common/Makefile.in b/plug-ins/common/Makefile.in
new file mode 100644
index 0000000..11f7613
--- /dev/null
+++ b/plug-ins/common/Makefile.in
@@ -0,0 +1,9492 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+align_layers_libexec_PROGRAMS = align-layers$(EXEEXT)
+animation_optimize_libexec_PROGRAMS = animation-optimize$(EXEEXT)
+animation_play_libexec_PROGRAMS = animation-play$(EXEEXT)
+blinds_libexec_PROGRAMS = blinds$(EXEEXT)
+blur_libexec_PROGRAMS = blur$(EXEEXT)
+border_average_libexec_PROGRAMS = border-average$(EXEEXT)
+busy_dialog_libexec_PROGRAMS = busy-dialog$(EXEEXT)
+cartoon_libexec_PROGRAMS = cartoon$(EXEEXT)
+checkerboard_libexec_PROGRAMS = checkerboard$(EXEEXT)
+cml_explorer_libexec_PROGRAMS = cml-explorer$(EXEEXT)
+color_cube_analyze_libexec_PROGRAMS = color-cube-analyze$(EXEEXT)
+color_enhance_libexec_PROGRAMS = color-enhance$(EXEEXT)
+colorify_libexec_PROGRAMS = colorify$(EXEEXT)
+colormap_remap_libexec_PROGRAMS = colormap-remap$(EXEEXT)
+compose_libexec_PROGRAMS = compose$(EXEEXT)
+contrast_retinex_libexec_PROGRAMS = contrast-retinex$(EXEEXT)
+crop_zealous_libexec_PROGRAMS = crop-zealous$(EXEEXT)
+curve_bend_libexec_PROGRAMS = curve-bend$(EXEEXT)
+decompose_libexec_PROGRAMS = decompose$(EXEEXT)
+depth_merge_libexec_PROGRAMS = depth-merge$(EXEEXT)
+despeckle_libexec_PROGRAMS = despeckle$(EXEEXT)
+destripe_libexec_PROGRAMS = destripe$(EXEEXT)
+edge_dog_libexec_PROGRAMS = edge-dog$(EXEEXT)
+emboss_libexec_PROGRAMS = emboss$(EXEEXT)
+file_cel_libexec_PROGRAMS = file-cel$(EXEEXT)
+file_compressor_libexec_PROGRAMS = file-compressor$(EXEEXT)
+file_csource_libexec_PROGRAMS = file-csource$(EXEEXT)
+file_desktop_link_libexec_PROGRAMS = file-desktop-link$(EXEEXT)
+file_dicom_libexec_PROGRAMS = file-dicom$(EXEEXT)
+file_gbr_libexec_PROGRAMS = file-gbr$(EXEEXT)
+file_gegl_libexec_PROGRAMS = file-gegl$(EXEEXT)
+file_gif_load_libexec_PROGRAMS = file-gif-load$(EXEEXT)
+file_gif_save_libexec_PROGRAMS = file-gif-save$(EXEEXT)
+file_gih_libexec_PROGRAMS = file-gih$(EXEEXT)
+file_glob_libexec_PROGRAMS = file-glob$(EXEEXT)
+file_header_libexec_PROGRAMS = file-header$(EXEEXT)
+file_html_table_libexec_PROGRAMS = file-html-table$(EXEEXT)
+file_pat_libexec_PROGRAMS = file-pat$(EXEEXT)
+file_pcx_libexec_PROGRAMS = file-pcx$(EXEEXT)
+file_pdf_load_libexec_PROGRAMS = file-pdf-load$(EXEEXT)
+file_pix_libexec_PROGRAMS = file-pix$(EXEEXT)
+file_png_libexec_PROGRAMS = file-png$(EXEEXT)
+file_pnm_libexec_PROGRAMS = file-pnm$(EXEEXT)
+file_psp_libexec_PROGRAMS = file-psp$(EXEEXT)
+file_raw_data_libexec_PROGRAMS = file-raw-data$(EXEEXT)
+file_sunras_libexec_PROGRAMS = file-sunras$(EXEEXT)
+file_svg_libexec_PROGRAMS = file-svg$(EXEEXT)
+file_tga_libexec_PROGRAMS = file-tga$(EXEEXT)
+file_xbm_libexec_PROGRAMS = file-xbm$(EXEEXT)
+file_xwd_libexec_PROGRAMS = file-xwd$(EXEEXT)
+film_libexec_PROGRAMS = film$(EXEEXT)
+filter_pack_libexec_PROGRAMS = filter-pack$(EXEEXT)
+fractal_trace_libexec_PROGRAMS = fractal-trace$(EXEEXT)
+goat_exercise_libexec_PROGRAMS = goat-exercise$(EXEEXT)
+gradient_map_libexec_PROGRAMS = gradient-map$(EXEEXT)
+grid_libexec_PROGRAMS = grid$(EXEEXT)
+guillotine_libexec_PROGRAMS = guillotine$(EXEEXT)
+hot_libexec_PROGRAMS = hot$(EXEEXT)
+jigsaw_libexec_PROGRAMS = jigsaw$(EXEEXT)
+max_rgb_libexec_PROGRAMS = max-rgb$(EXEEXT)
+nl_filter_libexec_PROGRAMS = nl-filter$(EXEEXT)
+photocopy_libexec_PROGRAMS = photocopy$(EXEEXT)
+plugin_browser_libexec_PROGRAMS = plugin-browser$(EXEEXT)
+procedure_browser_libexec_PROGRAMS = procedure-browser$(EXEEXT)
+qbist_libexec_PROGRAMS = qbist$(EXEEXT)
+sample_colorize_libexec_PROGRAMS = sample-colorize$(EXEEXT)
+sharpen_libexec_PROGRAMS = sharpen$(EXEEXT)
+smooth_palette_libexec_PROGRAMS = smooth-palette$(EXEEXT)
+softglow_libexec_PROGRAMS = softglow$(EXEEXT)
+sparkle_libexec_PROGRAMS = sparkle$(EXEEXT)
+sphere_designer_libexec_PROGRAMS = sphere-designer$(EXEEXT)
+tile_libexec_PROGRAMS = tile$(EXEEXT)
+tile_small_libexec_PROGRAMS = tile-small$(EXEEXT)
+unit_editor_libexec_PROGRAMS = unit-editor$(EXEEXT)
+van_gogh_lic_libexec_PROGRAMS = van-gogh-lic$(EXEEXT)
+warp_libexec_PROGRAMS = warp$(EXEEXT)
+wavelet_decompose_libexec_PROGRAMS = wavelet-decompose$(EXEEXT)
+web_browser_libexec_PROGRAMS = web-browser$(EXEEXT)
+EXTRA_PROGRAMS = file-aa$(EXEEXT) file-heif$(EXEEXT) \
+ file-jp2-load$(EXEEXT) file-jpegxl$(EXEEXT) file-mng$(EXEEXT) \
+ file-pdf-save$(EXEEXT) file-ps$(EXEEXT) file-wmf$(EXEEXT) \
+ file-xmc$(EXEEXT) file-xpm$(EXEEXT) mail$(EXEEXT) \
+ web-page$(EXEEXT)
+subdir = plug-ins/common
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(align_layers_libexecdir)" \
+ "$(DESTDIR)$(animation_optimize_libexecdir)" \
+ "$(DESTDIR)$(animation_play_libexecdir)" \
+ "$(DESTDIR)$(blinds_libexecdir)" \
+ "$(DESTDIR)$(blur_libexecdir)" \
+ "$(DESTDIR)$(border_average_libexecdir)" \
+ "$(DESTDIR)$(busy_dialog_libexecdir)" \
+ "$(DESTDIR)$(cartoon_libexecdir)" \
+ "$(DESTDIR)$(checkerboard_libexecdir)" \
+ "$(DESTDIR)$(cml_explorer_libexecdir)" \
+ "$(DESTDIR)$(color_cube_analyze_libexecdir)" \
+ "$(DESTDIR)$(color_enhance_libexecdir)" \
+ "$(DESTDIR)$(colorify_libexecdir)" \
+ "$(DESTDIR)$(colormap_remap_libexecdir)" \
+ "$(DESTDIR)$(compose_libexecdir)" \
+ "$(DESTDIR)$(contrast_retinex_libexecdir)" \
+ "$(DESTDIR)$(crop_zealous_libexecdir)" \
+ "$(DESTDIR)$(curve_bend_libexecdir)" \
+ "$(DESTDIR)$(decompose_libexecdir)" \
+ "$(DESTDIR)$(depth_merge_libexecdir)" \
+ "$(DESTDIR)$(despeckle_libexecdir)" \
+ "$(DESTDIR)$(destripe_libexecdir)" \
+ "$(DESTDIR)$(edge_dog_libexecdir)" \
+ "$(DESTDIR)$(emboss_libexecdir)" \
+ "$(DESTDIR)$(file_aa_libexecdir)" \
+ "$(DESTDIR)$(file_cel_libexecdir)" \
+ "$(DESTDIR)$(file_compressor_libexecdir)" \
+ "$(DESTDIR)$(file_csource_libexecdir)" \
+ "$(DESTDIR)$(file_desktop_link_libexecdir)" \
+ "$(DESTDIR)$(file_dicom_libexecdir)" \
+ "$(DESTDIR)$(file_gbr_libexecdir)" \
+ "$(DESTDIR)$(file_gegl_libexecdir)" \
+ "$(DESTDIR)$(file_gif_load_libexecdir)" \
+ "$(DESTDIR)$(file_gif_save_libexecdir)" \
+ "$(DESTDIR)$(file_gih_libexecdir)" \
+ "$(DESTDIR)$(file_glob_libexecdir)" \
+ "$(DESTDIR)$(file_header_libexecdir)" \
+ "$(DESTDIR)$(file_heif_libexecdir)" \
+ "$(DESTDIR)$(file_html_table_libexecdir)" \
+ "$(DESTDIR)$(file_jp2_load_libexecdir)" \
+ "$(DESTDIR)$(file_jpegxl_libexecdir)" \
+ "$(DESTDIR)$(file_mng_libexecdir)" \
+ "$(DESTDIR)$(file_pat_libexecdir)" \
+ "$(DESTDIR)$(file_pcx_libexecdir)" \
+ "$(DESTDIR)$(file_pdf_load_libexecdir)" \
+ "$(DESTDIR)$(file_pdf_save_libexecdir)" \
+ "$(DESTDIR)$(file_pix_libexecdir)" \
+ "$(DESTDIR)$(file_png_libexecdir)" \
+ "$(DESTDIR)$(file_pnm_libexecdir)" \
+ "$(DESTDIR)$(file_ps_libexecdir)" \
+ "$(DESTDIR)$(file_psp_libexecdir)" \
+ "$(DESTDIR)$(file_raw_data_libexecdir)" \
+ "$(DESTDIR)$(file_sunras_libexecdir)" \
+ "$(DESTDIR)$(file_svg_libexecdir)" \
+ "$(DESTDIR)$(file_tga_libexecdir)" \
+ "$(DESTDIR)$(file_wmf_libexecdir)" \
+ "$(DESTDIR)$(file_xbm_libexecdir)" \
+ "$(DESTDIR)$(file_xmc_libexecdir)" \
+ "$(DESTDIR)$(file_xpm_libexecdir)" \
+ "$(DESTDIR)$(file_xwd_libexecdir)" \
+ "$(DESTDIR)$(film_libexecdir)" \
+ "$(DESTDIR)$(filter_pack_libexecdir)" \
+ "$(DESTDIR)$(fractal_trace_libexecdir)" \
+ "$(DESTDIR)$(goat_exercise_libexecdir)" \
+ "$(DESTDIR)$(gradient_map_libexecdir)" \
+ "$(DESTDIR)$(grid_libexecdir)" \
+ "$(DESTDIR)$(guillotine_libexecdir)" \
+ "$(DESTDIR)$(hot_libexecdir)" "$(DESTDIR)$(jigsaw_libexecdir)" \
+ "$(DESTDIR)$(mail_libexecdir)" \
+ "$(DESTDIR)$(max_rgb_libexecdir)" \
+ "$(DESTDIR)$(nl_filter_libexecdir)" \
+ "$(DESTDIR)$(photocopy_libexecdir)" \
+ "$(DESTDIR)$(plugin_browser_libexecdir)" \
+ "$(DESTDIR)$(procedure_browser_libexecdir)" \
+ "$(DESTDIR)$(qbist_libexecdir)" \
+ "$(DESTDIR)$(sample_colorize_libexecdir)" \
+ "$(DESTDIR)$(sharpen_libexecdir)" \
+ "$(DESTDIR)$(smooth_palette_libexecdir)" \
+ "$(DESTDIR)$(softglow_libexecdir)" \
+ "$(DESTDIR)$(sparkle_libexecdir)" \
+ "$(DESTDIR)$(sphere_designer_libexecdir)" \
+ "$(DESTDIR)$(tile_libexecdir)" \
+ "$(DESTDIR)$(tile_small_libexecdir)" \
+ "$(DESTDIR)$(unit_editor_libexecdir)" \
+ "$(DESTDIR)$(van_gogh_lic_libexecdir)" \
+ "$(DESTDIR)$(warp_libexecdir)" \
+ "$(DESTDIR)$(wavelet_decompose_libexecdir)" \
+ "$(DESTDIR)$(web_browser_libexecdir)" \
+ "$(DESTDIR)$(web_page_libexecdir)"
+PROGRAMS = $(align_layers_libexec_PROGRAMS) \
+ $(animation_optimize_libexec_PROGRAMS) \
+ $(animation_play_libexec_PROGRAMS) $(blinds_libexec_PROGRAMS) \
+ $(blur_libexec_PROGRAMS) $(border_average_libexec_PROGRAMS) \
+ $(busy_dialog_libexec_PROGRAMS) $(cartoon_libexec_PROGRAMS) \
+ $(checkerboard_libexec_PROGRAMS) \
+ $(cml_explorer_libexec_PROGRAMS) \
+ $(color_cube_analyze_libexec_PROGRAMS) \
+ $(color_enhance_libexec_PROGRAMS) $(colorify_libexec_PROGRAMS) \
+ $(colormap_remap_libexec_PROGRAMS) $(compose_libexec_PROGRAMS) \
+ $(contrast_retinex_libexec_PROGRAMS) \
+ $(crop_zealous_libexec_PROGRAMS) \
+ $(curve_bend_libexec_PROGRAMS) $(decompose_libexec_PROGRAMS) \
+ $(depth_merge_libexec_PROGRAMS) $(despeckle_libexec_PROGRAMS) \
+ $(destripe_libexec_PROGRAMS) $(edge_dog_libexec_PROGRAMS) \
+ $(emboss_libexec_PROGRAMS) $(file_aa_libexec_PROGRAMS) \
+ $(file_cel_libexec_PROGRAMS) \
+ $(file_compressor_libexec_PROGRAMS) \
+ $(file_csource_libexec_PROGRAMS) \
+ $(file_desktop_link_libexec_PROGRAMS) \
+ $(file_dicom_libexec_PROGRAMS) $(file_gbr_libexec_PROGRAMS) \
+ $(file_gegl_libexec_PROGRAMS) \
+ $(file_gif_load_libexec_PROGRAMS) \
+ $(file_gif_save_libexec_PROGRAMS) $(file_gih_libexec_PROGRAMS) \
+ $(file_glob_libexec_PROGRAMS) $(file_header_libexec_PROGRAMS) \
+ $(file_heif_libexec_PROGRAMS) \
+ $(file_html_table_libexec_PROGRAMS) \
+ $(file_jp2_load_libexec_PROGRAMS) \
+ $(file_jpegxl_libexec_PROGRAMS) $(file_mng_libexec_PROGRAMS) \
+ $(file_pat_libexec_PROGRAMS) $(file_pcx_libexec_PROGRAMS) \
+ $(file_pdf_load_libexec_PROGRAMS) \
+ $(file_pdf_save_libexec_PROGRAMS) $(file_pix_libexec_PROGRAMS) \
+ $(file_png_libexec_PROGRAMS) $(file_pnm_libexec_PROGRAMS) \
+ $(file_ps_libexec_PROGRAMS) $(file_psp_libexec_PROGRAMS) \
+ $(file_raw_data_libexec_PROGRAMS) \
+ $(file_sunras_libexec_PROGRAMS) $(file_svg_libexec_PROGRAMS) \
+ $(file_tga_libexec_PROGRAMS) $(file_wmf_libexec_PROGRAMS) \
+ $(file_xbm_libexec_PROGRAMS) $(file_xmc_libexec_PROGRAMS) \
+ $(file_xpm_libexec_PROGRAMS) $(file_xwd_libexec_PROGRAMS) \
+ $(film_libexec_PROGRAMS) $(filter_pack_libexec_PROGRAMS) \
+ $(fractal_trace_libexec_PROGRAMS) \
+ $(goat_exercise_libexec_PROGRAMS) \
+ $(gradient_map_libexec_PROGRAMS) $(grid_libexec_PROGRAMS) \
+ $(guillotine_libexec_PROGRAMS) $(hot_libexec_PROGRAMS) \
+ $(jigsaw_libexec_PROGRAMS) $(mail_libexec_PROGRAMS) \
+ $(max_rgb_libexec_PROGRAMS) $(nl_filter_libexec_PROGRAMS) \
+ $(photocopy_libexec_PROGRAMS) \
+ $(plugin_browser_libexec_PROGRAMS) \
+ $(procedure_browser_libexec_PROGRAMS) \
+ $(qbist_libexec_PROGRAMS) $(sample_colorize_libexec_PROGRAMS) \
+ $(sharpen_libexec_PROGRAMS) $(smooth_palette_libexec_PROGRAMS) \
+ $(softglow_libexec_PROGRAMS) $(sparkle_libexec_PROGRAMS) \
+ $(sphere_designer_libexec_PROGRAMS) $(tile_libexec_PROGRAMS) \
+ $(tile_small_libexec_PROGRAMS) $(unit_editor_libexec_PROGRAMS) \
+ $(van_gogh_lic_libexec_PROGRAMS) $(warp_libexec_PROGRAMS) \
+ $(wavelet_decompose_libexec_PROGRAMS) \
+ $(web_browser_libexec_PROGRAMS) $(web_page_libexec_PROGRAMS)
+am_align_layers_OBJECTS = align-layers.$(OBJEXT)
+align_layers_OBJECTS = $(am_align_layers_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la \
+ $(am__DEPENDENCIES_1)
+align_layers_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(align_layers_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_animation_optimize_OBJECTS = animation-optimize.$(OBJEXT)
+animation_optimize_OBJECTS = $(am_animation_optimize_OBJECTS)
+animation_optimize_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(animation_optimize_RC)
+am_animation_play_OBJECTS = animation-play.$(OBJEXT)
+animation_play_OBJECTS = $(am_animation_play_OBJECTS)
+animation_play_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(animation_play_RC)
+am_blinds_OBJECTS = blinds.$(OBJEXT)
+blinds_OBJECTS = $(am_blinds_OBJECTS)
+blinds_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(blinds_RC)
+am_blur_OBJECTS = blur.$(OBJEXT)
+blur_OBJECTS = $(am_blur_OBJECTS)
+blur_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(blur_RC)
+am_border_average_OBJECTS = border-average.$(OBJEXT)
+border_average_OBJECTS = $(am_border_average_OBJECTS)
+border_average_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(border_average_RC)
+am_busy_dialog_OBJECTS = busy-dialog.$(OBJEXT)
+busy_dialog_OBJECTS = $(am_busy_dialog_OBJECTS)
+busy_dialog_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(busy_dialog_RC)
+am_cartoon_OBJECTS = cartoon.$(OBJEXT)
+cartoon_OBJECTS = $(am_cartoon_OBJECTS)
+cartoon_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(cartoon_RC)
+am_checkerboard_OBJECTS = checkerboard.$(OBJEXT)
+checkerboard_OBJECTS = $(am_checkerboard_OBJECTS)
+checkerboard_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(checkerboard_RC)
+am_cml_explorer_OBJECTS = cml-explorer.$(OBJEXT)
+cml_explorer_OBJECTS = $(am_cml_explorer_OBJECTS)
+cml_explorer_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(cml_explorer_RC)
+am_color_cube_analyze_OBJECTS = color-cube-analyze.$(OBJEXT)
+color_cube_analyze_OBJECTS = $(am_color_cube_analyze_OBJECTS)
+color_cube_analyze_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(color_cube_analyze_RC)
+am_color_enhance_OBJECTS = color-enhance.$(OBJEXT)
+color_enhance_OBJECTS = $(am_color_enhance_OBJECTS)
+color_enhance_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(color_enhance_RC)
+am_colorify_OBJECTS = colorify.$(OBJEXT)
+colorify_OBJECTS = $(am_colorify_OBJECTS)
+colorify_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(colorify_RC)
+am_colormap_remap_OBJECTS = colormap-remap.$(OBJEXT)
+colormap_remap_OBJECTS = $(am_colormap_remap_OBJECTS)
+colormap_remap_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(colormap_remap_RC)
+am_compose_OBJECTS = compose.$(OBJEXT)
+compose_OBJECTS = $(am_compose_OBJECTS)
+compose_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(compose_RC)
+am_contrast_retinex_OBJECTS = contrast-retinex.$(OBJEXT)
+contrast_retinex_OBJECTS = $(am_contrast_retinex_OBJECTS)
+contrast_retinex_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(contrast_retinex_RC)
+am_crop_zealous_OBJECTS = crop-zealous.$(OBJEXT)
+crop_zealous_OBJECTS = $(am_crop_zealous_OBJECTS)
+crop_zealous_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(crop_zealous_RC)
+am_curve_bend_OBJECTS = curve-bend.$(OBJEXT)
+curve_bend_OBJECTS = $(am_curve_bend_OBJECTS)
+curve_bend_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(curve_bend_RC)
+am_decompose_OBJECTS = decompose.$(OBJEXT)
+decompose_OBJECTS = $(am_decompose_OBJECTS)
+decompose_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(decompose_RC)
+am_depth_merge_OBJECTS = depth-merge.$(OBJEXT)
+depth_merge_OBJECTS = $(am_depth_merge_OBJECTS)
+depth_merge_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(depth_merge_RC)
+am_despeckle_OBJECTS = despeckle.$(OBJEXT)
+despeckle_OBJECTS = $(am_despeckle_OBJECTS)
+despeckle_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(despeckle_RC)
+am_destripe_OBJECTS = destripe.$(OBJEXT)
+destripe_OBJECTS = $(am_destripe_OBJECTS)
+destripe_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(destripe_RC)
+am_edge_dog_OBJECTS = edge-dog.$(OBJEXT)
+edge_dog_OBJECTS = $(am_edge_dog_OBJECTS)
+edge_dog_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(edge_dog_RC)
+am_emboss_OBJECTS = emboss.$(OBJEXT)
+emboss_OBJECTS = $(am_emboss_OBJECTS)
+emboss_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(emboss_RC)
+am_file_aa_OBJECTS = file-aa.$(OBJEXT)
+file_aa_OBJECTS = $(am_file_aa_OBJECTS)
+file_aa_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_aa_RC)
+am_file_cel_OBJECTS = file-cel.$(OBJEXT)
+file_cel_OBJECTS = $(am_file_cel_OBJECTS)
+file_cel_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_cel_RC)
+am_file_compressor_OBJECTS = \
+ file_compressor-file-compressor.$(OBJEXT)
+file_compressor_OBJECTS = $(am_file_compressor_OBJECTS)
+file_compressor_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(file_compressor_RC)
+file_compressor_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(file_compressor_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+ -o $@
+am_file_csource_OBJECTS = file-csource.$(OBJEXT)
+file_csource_OBJECTS = $(am_file_csource_OBJECTS)
+file_csource_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_csource_RC)
+am_file_desktop_link_OBJECTS = file-desktop-link.$(OBJEXT)
+file_desktop_link_OBJECTS = $(am_file_desktop_link_OBJECTS)
+file_desktop_link_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(file_desktop_link_RC)
+am_file_dicom_OBJECTS = file_dicom-file-dicom.$(OBJEXT)
+file_dicom_OBJECTS = $(am_file_dicom_OBJECTS)
+file_dicom_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_dicom_RC)
+file_dicom_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_dicom_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_gbr_OBJECTS = file-gbr.$(OBJEXT)
+file_gbr_OBJECTS = $(am_file_gbr_OBJECTS)
+file_gbr_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_gbr_RC)
+am_file_gegl_OBJECTS = file-gegl.$(OBJEXT)
+file_gegl_OBJECTS = $(am_file_gegl_OBJECTS)
+file_gegl_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_gegl_RC)
+am_file_gif_load_OBJECTS = file-gif-load.$(OBJEXT)
+file_gif_load_OBJECTS = $(am_file_gif_load_OBJECTS)
+file_gif_load_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_gif_load_RC)
+am_file_gif_save_OBJECTS = file-gif-save.$(OBJEXT)
+file_gif_save_OBJECTS = $(am_file_gif_save_OBJECTS)
+file_gif_save_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(file_gif_save_RC)
+am_file_gih_OBJECTS = file-gih.$(OBJEXT)
+file_gih_OBJECTS = $(am_file_gih_OBJECTS)
+file_gih_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_gih_RC)
+am_file_glob_OBJECTS = file-glob.$(OBJEXT)
+file_glob_OBJECTS = $(am_file_glob_OBJECTS)
+file_glob_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_glob_RC)
+am_file_header_OBJECTS = file-header.$(OBJEXT)
+file_header_OBJECTS = $(am_file_header_OBJECTS)
+file_header_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_header_RC)
+am_file_heif_OBJECTS = file_heif-file-heif.$(OBJEXT)
+file_heif_OBJECTS = $(am_file_heif_OBJECTS)
+file_heif_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_heif_RC)
+file_heif_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_heif_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_html_table_OBJECTS = file-html-table.$(OBJEXT)
+file_html_table_OBJECTS = $(am_file_html_table_OBJECTS)
+file_html_table_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(file_html_table_RC)
+am_file_jp2_load_OBJECTS = file_jp2_load-file-jp2-load.$(OBJEXT)
+file_jp2_load_OBJECTS = $(am_file_jp2_load_OBJECTS)
+file_jp2_load_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_jp2_load_RC)
+file_jp2_load_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_jp2_load_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_jpegxl_OBJECTS = file_jpegxl-file-jpegxl.$(OBJEXT)
+file_jpegxl_OBJECTS = $(am_file_jpegxl_OBJECTS)
+file_jpegxl_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_jpegxl_RC)
+file_jpegxl_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_jpegxl_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_mng_OBJECTS = file_mng-file-mng.$(OBJEXT)
+file_mng_OBJECTS = $(am_file_mng_OBJECTS)
+file_mng_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_mng_RC)
+file_mng_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_mng_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_pat_OBJECTS = file-pat.$(OBJEXT)
+file_pat_OBJECTS = $(am_file_pat_OBJECTS)
+file_pat_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_pat_RC)
+am_file_pcx_OBJECTS = file-pcx.$(OBJEXT)
+file_pcx_OBJECTS = $(am_file_pcx_OBJECTS)
+file_pcx_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_pcx_RC)
+am_file_pdf_load_OBJECTS = file_pdf_load-file-pdf-load.$(OBJEXT)
+file_pdf_load_OBJECTS = $(am_file_pdf_load_OBJECTS)
+file_pdf_load_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_pdf_load_RC)
+file_pdf_load_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_pdf_load_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_pdf_save_OBJECTS = file_pdf_save-file-pdf-save.$(OBJEXT)
+file_pdf_save_OBJECTS = $(am_file_pdf_save_OBJECTS)
+file_pdf_save_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_pdf_save_RC)
+file_pdf_save_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_pdf_save_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_pix_OBJECTS = file-pix.$(OBJEXT)
+file_pix_OBJECTS = $(am_file_pix_OBJECTS)
+file_pix_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_pix_RC)
+am_file_png_OBJECTS = file_png-file-png.$(OBJEXT)
+file_png_OBJECTS = $(am_file_png_OBJECTS)
+file_png_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_png_RC)
+file_png_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_png_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_pnm_OBJECTS = file-pnm.$(OBJEXT)
+file_pnm_OBJECTS = $(am_file_pnm_OBJECTS)
+file_pnm_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_pnm_RC)
+am_file_ps_OBJECTS = file-ps.$(OBJEXT)
+file_ps_OBJECTS = $(am_file_ps_OBJECTS)
+file_ps_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_ps_RC)
+am_file_psp_OBJECTS = file-psp.$(OBJEXT)
+file_psp_OBJECTS = $(am_file_psp_OBJECTS)
+file_psp_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_psp_RC)
+am_file_raw_data_OBJECTS = file-raw-data.$(OBJEXT)
+file_raw_data_OBJECTS = $(am_file_raw_data_OBJECTS)
+file_raw_data_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(file_raw_data_RC)
+am_file_sunras_OBJECTS = file-sunras.$(OBJEXT)
+file_sunras_OBJECTS = $(am_file_sunras_OBJECTS)
+file_sunras_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_sunras_RC)
+am_file_svg_OBJECTS = file_svg-file-svg.$(OBJEXT)
+file_svg_OBJECTS = $(am_file_svg_OBJECTS)
+file_svg_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_svg_RC)
+file_svg_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_svg_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_tga_OBJECTS = file-tga.$(OBJEXT)
+file_tga_OBJECTS = $(am_file_tga_OBJECTS)
+file_tga_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_tga_RC)
+am_file_wmf_OBJECTS = file_wmf-file-wmf.$(OBJEXT)
+file_wmf_OBJECTS = $(am_file_wmf_OBJECTS)
+file_wmf_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_wmf_RC)
+file_wmf_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_wmf_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am_file_xbm_OBJECTS = file-xbm.$(OBJEXT)
+file_xbm_OBJECTS = $(am_file_xbm_OBJECTS)
+file_xbm_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_xbm_RC)
+am_file_xmc_OBJECTS = file-xmc.$(OBJEXT)
+file_xmc_OBJECTS = $(am_file_xmc_OBJECTS)
+file_xmc_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_xmc_RC)
+am_file_xpm_OBJECTS = file-xpm.$(OBJEXT)
+file_xpm_OBJECTS = $(am_file_xpm_OBJECTS)
+file_xpm_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_xpm_RC)
+am_file_xwd_OBJECTS = file-xwd.$(OBJEXT)
+file_xwd_OBJECTS = $(am_file_xwd_OBJECTS)
+file_xwd_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_xwd_RC)
+am_film_OBJECTS = film.$(OBJEXT)
+film_OBJECTS = $(am_film_OBJECTS)
+film_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(film_RC)
+am_filter_pack_OBJECTS = filter-pack.$(OBJEXT)
+filter_pack_OBJECTS = $(am_filter_pack_OBJECTS)
+filter_pack_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(filter_pack_RC)
+am_fractal_trace_OBJECTS = fractal-trace.$(OBJEXT)
+fractal_trace_OBJECTS = $(am_fractal_trace_OBJECTS)
+fractal_trace_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(fractal_trace_RC)
+am_goat_exercise_OBJECTS = goat-exercise.$(OBJEXT)
+goat_exercise_OBJECTS = $(am_goat_exercise_OBJECTS)
+goat_exercise_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(goat_exercise_RC)
+am_gradient_map_OBJECTS = gradient-map.$(OBJEXT)
+gradient_map_OBJECTS = $(am_gradient_map_OBJECTS)
+gradient_map_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(gradient_map_RC)
+am_grid_OBJECTS = grid.$(OBJEXT)
+grid_OBJECTS = $(am_grid_OBJECTS)
+grid_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(grid_RC)
+am_guillotine_OBJECTS = guillotine.$(OBJEXT)
+guillotine_OBJECTS = $(am_guillotine_OBJECTS)
+guillotine_DEPENDENCIES = $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(guillotine_RC)
+am_hot_OBJECTS = hot.$(OBJEXT)
+hot_OBJECTS = $(am_hot_OBJECTS)
+hot_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(hot_RC)
+am_jigsaw_OBJECTS = jigsaw.$(OBJEXT)
+jigsaw_OBJECTS = $(am_jigsaw_OBJECTS)
+jigsaw_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(jigsaw_RC)
+am_mail_OBJECTS = mail.$(OBJEXT)
+mail_OBJECTS = $(am_mail_OBJECTS)
+mail_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(mail_RC)
+am_max_rgb_OBJECTS = max-rgb.$(OBJEXT)
+max_rgb_OBJECTS = $(am_max_rgb_OBJECTS)
+max_rgb_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(max_rgb_RC)
+am_nl_filter_OBJECTS = nl-filter.$(OBJEXT)
+nl_filter_OBJECTS = $(am_nl_filter_OBJECTS)
+nl_filter_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(nl_filter_RC)
+am_photocopy_OBJECTS = photocopy.$(OBJEXT)
+photocopy_OBJECTS = $(am_photocopy_OBJECTS)
+photocopy_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(photocopy_RC)
+am_plugin_browser_OBJECTS = plugin-browser.$(OBJEXT)
+plugin_browser_OBJECTS = $(am_plugin_browser_OBJECTS)
+plugin_browser_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(plugin_browser_RC)
+am_procedure_browser_OBJECTS = procedure-browser.$(OBJEXT)
+procedure_browser_OBJECTS = $(am_procedure_browser_OBJECTS)
+procedure_browser_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(procedure_browser_RC)
+am_qbist_OBJECTS = qbist.$(OBJEXT)
+qbist_OBJECTS = $(am_qbist_OBJECTS)
+qbist_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(qbist_RC)
+am_sample_colorize_OBJECTS = sample-colorize.$(OBJEXT)
+sample_colorize_OBJECTS = $(am_sample_colorize_OBJECTS)
+sample_colorize_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(sample_colorize_RC)
+am_sharpen_OBJECTS = sharpen.$(OBJEXT)
+sharpen_OBJECTS = $(am_sharpen_OBJECTS)
+sharpen_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(sharpen_RC)
+am_smooth_palette_OBJECTS = smooth-palette.$(OBJEXT)
+smooth_palette_OBJECTS = $(am_smooth_palette_OBJECTS)
+smooth_palette_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(smooth_palette_RC)
+am_softglow_OBJECTS = softglow.$(OBJEXT)
+softglow_OBJECTS = $(am_softglow_OBJECTS)
+softglow_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(softglow_RC)
+am_sparkle_OBJECTS = sparkle.$(OBJEXT)
+sparkle_OBJECTS = $(am_sparkle_OBJECTS)
+sparkle_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(sparkle_RC)
+am_sphere_designer_OBJECTS = sphere-designer.$(OBJEXT)
+sphere_designer_OBJECTS = $(am_sphere_designer_OBJECTS)
+sphere_designer_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(sphere_designer_RC)
+am_tile_OBJECTS = tile.$(OBJEXT)
+tile_OBJECTS = $(am_tile_OBJECTS)
+tile_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(tile_RC)
+am_tile_small_OBJECTS = tile-small.$(OBJEXT)
+tile_small_OBJECTS = $(am_tile_small_OBJECTS)
+tile_small_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(tile_small_RC)
+am_unit_editor_OBJECTS = unit-editor.$(OBJEXT)
+unit_editor_OBJECTS = $(am_unit_editor_OBJECTS)
+unit_editor_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(unit_editor_RC)
+am_van_gogh_lic_OBJECTS = van-gogh-lic.$(OBJEXT)
+van_gogh_lic_OBJECTS = $(am_van_gogh_lic_OBJECTS)
+van_gogh_lic_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(van_gogh_lic_RC)
+am_warp_OBJECTS = warp.$(OBJEXT)
+warp_OBJECTS = $(am_warp_OBJECTS)
+warp_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpmodule) \
+ $(libgimp) $(am__DEPENDENCIES_2) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(warp_RC)
+am_wavelet_decompose_OBJECTS = wavelet-decompose.$(OBJEXT)
+wavelet_decompose_OBJECTS = $(am_wavelet_decompose_OBJECTS)
+wavelet_decompose_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(wavelet_decompose_RC)
+am_web_browser_OBJECTS = web_browser-web-browser.$(OBJEXT)
+web_browser_OBJECTS = $(am_web_browser_OBJECTS)
+web_browser_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(web_browser_RC)
+web_browser_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(web_browser_LDFLAGS) $(LDFLAGS) -o $@
+am_web_page_OBJECTS = web_page-web-page.$(OBJEXT)
+web_page_OBJECTS = $(am_web_page_OBJECTS)
+web_page_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpmodule) $(libgimp) $(am__DEPENDENCIES_2) \
+ $(libgimpconfig) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(web_page_RC)
+web_page_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(web_page_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/align-layers.Po \
+ ./$(DEPDIR)/animation-optimize.Po \
+ ./$(DEPDIR)/animation-play.Po ./$(DEPDIR)/blinds.Po \
+ ./$(DEPDIR)/blur.Po ./$(DEPDIR)/border-average.Po \
+ ./$(DEPDIR)/busy-dialog.Po ./$(DEPDIR)/cartoon.Po \
+ ./$(DEPDIR)/checkerboard.Po ./$(DEPDIR)/cml-explorer.Po \
+ ./$(DEPDIR)/color-cube-analyze.Po ./$(DEPDIR)/color-enhance.Po \
+ ./$(DEPDIR)/colorify.Po ./$(DEPDIR)/colormap-remap.Po \
+ ./$(DEPDIR)/compose.Po ./$(DEPDIR)/contrast-retinex.Po \
+ ./$(DEPDIR)/crop-zealous.Po ./$(DEPDIR)/curve-bend.Po \
+ ./$(DEPDIR)/decompose.Po ./$(DEPDIR)/depth-merge.Po \
+ ./$(DEPDIR)/despeckle.Po ./$(DEPDIR)/destripe.Po \
+ ./$(DEPDIR)/edge-dog.Po ./$(DEPDIR)/emboss.Po \
+ ./$(DEPDIR)/file-aa.Po ./$(DEPDIR)/file-cel.Po \
+ ./$(DEPDIR)/file-csource.Po ./$(DEPDIR)/file-desktop-link.Po \
+ ./$(DEPDIR)/file-gbr.Po ./$(DEPDIR)/file-gegl.Po \
+ ./$(DEPDIR)/file-gif-load.Po ./$(DEPDIR)/file-gif-save.Po \
+ ./$(DEPDIR)/file-gih.Po ./$(DEPDIR)/file-glob.Po \
+ ./$(DEPDIR)/file-header.Po ./$(DEPDIR)/file-html-table.Po \
+ ./$(DEPDIR)/file-pat.Po ./$(DEPDIR)/file-pcx.Po \
+ ./$(DEPDIR)/file-pix.Po ./$(DEPDIR)/file-pnm.Po \
+ ./$(DEPDIR)/file-ps.Po ./$(DEPDIR)/file-psp.Po \
+ ./$(DEPDIR)/file-raw-data.Po ./$(DEPDIR)/file-sunras.Po \
+ ./$(DEPDIR)/file-tga.Po ./$(DEPDIR)/file-xbm.Po \
+ ./$(DEPDIR)/file-xmc.Po ./$(DEPDIR)/file-xpm.Po \
+ ./$(DEPDIR)/file-xwd.Po \
+ ./$(DEPDIR)/file_compressor-file-compressor.Po \
+ ./$(DEPDIR)/file_dicom-file-dicom.Po \
+ ./$(DEPDIR)/file_heif-file-heif.Po \
+ ./$(DEPDIR)/file_jp2_load-file-jp2-load.Po \
+ ./$(DEPDIR)/file_jpegxl-file-jpegxl.Po \
+ ./$(DEPDIR)/file_mng-file-mng.Po \
+ ./$(DEPDIR)/file_pdf_load-file-pdf-load.Po \
+ ./$(DEPDIR)/file_pdf_save-file-pdf-save.Po \
+ ./$(DEPDIR)/file_png-file-png.Po \
+ ./$(DEPDIR)/file_svg-file-svg.Po \
+ ./$(DEPDIR)/file_wmf-file-wmf.Po ./$(DEPDIR)/film.Po \
+ ./$(DEPDIR)/filter-pack.Po ./$(DEPDIR)/fractal-trace.Po \
+ ./$(DEPDIR)/goat-exercise.Po ./$(DEPDIR)/gradient-map.Po \
+ ./$(DEPDIR)/grid.Po ./$(DEPDIR)/guillotine.Po \
+ ./$(DEPDIR)/hot.Po ./$(DEPDIR)/jigsaw.Po ./$(DEPDIR)/mail.Po \
+ ./$(DEPDIR)/max-rgb.Po ./$(DEPDIR)/nl-filter.Po \
+ ./$(DEPDIR)/photocopy.Po ./$(DEPDIR)/plugin-browser.Po \
+ ./$(DEPDIR)/procedure-browser.Po ./$(DEPDIR)/qbist.Po \
+ ./$(DEPDIR)/sample-colorize.Po ./$(DEPDIR)/sharpen.Po \
+ ./$(DEPDIR)/smooth-palette.Po ./$(DEPDIR)/softglow.Po \
+ ./$(DEPDIR)/sparkle.Po ./$(DEPDIR)/sphere-designer.Po \
+ ./$(DEPDIR)/tile-small.Po ./$(DEPDIR)/tile.Po \
+ ./$(DEPDIR)/unit-editor.Po ./$(DEPDIR)/van-gogh-lic.Po \
+ ./$(DEPDIR)/warp.Po ./$(DEPDIR)/wavelet-decompose.Po \
+ ./$(DEPDIR)/web_browser-web-browser.Po \
+ ./$(DEPDIR)/web_page-web-page.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(align_layers_SOURCES) $(animation_optimize_SOURCES) \
+ $(animation_play_SOURCES) $(blinds_SOURCES) $(blur_SOURCES) \
+ $(border_average_SOURCES) $(busy_dialog_SOURCES) \
+ $(cartoon_SOURCES) $(checkerboard_SOURCES) \
+ $(cml_explorer_SOURCES) $(color_cube_analyze_SOURCES) \
+ $(color_enhance_SOURCES) $(colorify_SOURCES) \
+ $(colormap_remap_SOURCES) $(compose_SOURCES) \
+ $(contrast_retinex_SOURCES) $(crop_zealous_SOURCES) \
+ $(curve_bend_SOURCES) $(decompose_SOURCES) \
+ $(depth_merge_SOURCES) $(despeckle_SOURCES) \
+ $(destripe_SOURCES) $(edge_dog_SOURCES) $(emboss_SOURCES) \
+ $(file_aa_SOURCES) $(file_cel_SOURCES) \
+ $(file_compressor_SOURCES) $(file_csource_SOURCES) \
+ $(file_desktop_link_SOURCES) $(file_dicom_SOURCES) \
+ $(file_gbr_SOURCES) $(file_gegl_SOURCES) \
+ $(file_gif_load_SOURCES) $(file_gif_save_SOURCES) \
+ $(file_gih_SOURCES) $(file_glob_SOURCES) \
+ $(file_header_SOURCES) $(file_heif_SOURCES) \
+ $(file_html_table_SOURCES) $(file_jp2_load_SOURCES) \
+ $(file_jpegxl_SOURCES) $(file_mng_SOURCES) $(file_pat_SOURCES) \
+ $(file_pcx_SOURCES) $(file_pdf_load_SOURCES) \
+ $(file_pdf_save_SOURCES) $(file_pix_SOURCES) \
+ $(file_png_SOURCES) $(file_pnm_SOURCES) $(file_ps_SOURCES) \
+ $(file_psp_SOURCES) $(file_raw_data_SOURCES) \
+ $(file_sunras_SOURCES) $(file_svg_SOURCES) $(file_tga_SOURCES) \
+ $(file_wmf_SOURCES) $(file_xbm_SOURCES) $(file_xmc_SOURCES) \
+ $(file_xpm_SOURCES) $(file_xwd_SOURCES) $(film_SOURCES) \
+ $(filter_pack_SOURCES) $(fractal_trace_SOURCES) \
+ $(goat_exercise_SOURCES) $(gradient_map_SOURCES) \
+ $(grid_SOURCES) $(guillotine_SOURCES) $(hot_SOURCES) \
+ $(jigsaw_SOURCES) $(mail_SOURCES) $(max_rgb_SOURCES) \
+ $(nl_filter_SOURCES) $(photocopy_SOURCES) \
+ $(plugin_browser_SOURCES) $(procedure_browser_SOURCES) \
+ $(qbist_SOURCES) $(sample_colorize_SOURCES) $(sharpen_SOURCES) \
+ $(smooth_palette_SOURCES) $(softglow_SOURCES) \
+ $(sparkle_SOURCES) $(sphere_designer_SOURCES) $(tile_SOURCES) \
+ $(tile_small_SOURCES) $(unit_editor_SOURCES) \
+ $(van_gogh_lic_SOURCES) $(warp_SOURCES) \
+ $(wavelet_decompose_SOURCES) $(web_browser_SOURCES) \
+ $(web_page_SOURCES)
+DIST_SOURCES = $(align_layers_SOURCES) $(animation_optimize_SOURCES) \
+ $(animation_play_SOURCES) $(blinds_SOURCES) $(blur_SOURCES) \
+ $(border_average_SOURCES) $(busy_dialog_SOURCES) \
+ $(cartoon_SOURCES) $(checkerboard_SOURCES) \
+ $(cml_explorer_SOURCES) $(color_cube_analyze_SOURCES) \
+ $(color_enhance_SOURCES) $(colorify_SOURCES) \
+ $(colormap_remap_SOURCES) $(compose_SOURCES) \
+ $(contrast_retinex_SOURCES) $(crop_zealous_SOURCES) \
+ $(curve_bend_SOURCES) $(decompose_SOURCES) \
+ $(depth_merge_SOURCES) $(despeckle_SOURCES) \
+ $(destripe_SOURCES) $(edge_dog_SOURCES) $(emboss_SOURCES) \
+ $(file_aa_SOURCES) $(file_cel_SOURCES) \
+ $(file_compressor_SOURCES) $(file_csource_SOURCES) \
+ $(file_desktop_link_SOURCES) $(file_dicom_SOURCES) \
+ $(file_gbr_SOURCES) $(file_gegl_SOURCES) \
+ $(file_gif_load_SOURCES) $(file_gif_save_SOURCES) \
+ $(file_gih_SOURCES) $(file_glob_SOURCES) \
+ $(file_header_SOURCES) $(file_heif_SOURCES) \
+ $(file_html_table_SOURCES) $(file_jp2_load_SOURCES) \
+ $(file_jpegxl_SOURCES) $(file_mng_SOURCES) $(file_pat_SOURCES) \
+ $(file_pcx_SOURCES) $(file_pdf_load_SOURCES) \
+ $(file_pdf_save_SOURCES) $(file_pix_SOURCES) \
+ $(file_png_SOURCES) $(file_pnm_SOURCES) $(file_ps_SOURCES) \
+ $(file_psp_SOURCES) $(file_raw_data_SOURCES) \
+ $(file_sunras_SOURCES) $(file_svg_SOURCES) $(file_tga_SOURCES) \
+ $(file_wmf_SOURCES) $(file_xbm_SOURCES) $(file_xmc_SOURCES) \
+ $(file_xpm_SOURCES) $(file_xwd_SOURCES) $(film_SOURCES) \
+ $(filter_pack_SOURCES) $(fractal_trace_SOURCES) \
+ $(goat_exercise_SOURCES) $(gradient_map_SOURCES) \
+ $(grid_SOURCES) $(guillotine_SOURCES) $(hot_SOURCES) \
+ $(jigsaw_SOURCES) $(mail_SOURCES) $(max_rgb_SOURCES) \
+ $(nl_filter_SOURCES) $(photocopy_SOURCES) \
+ $(plugin_browser_SOURCES) $(procedure_browser_SOURCES) \
+ $(qbist_SOURCES) $(sample_colorize_SOURCES) $(sharpen_SOURCES) \
+ $(smooth_palette_SOURCES) $(softglow_SOURCES) \
+ $(sparkle_SOURCES) $(sphere_designer_SOURCES) $(tile_SOURCES) \
+ $(tile_small_SOURCES) $(unit_editor_SOURCES) \
+ $(van_gogh_lic_SOURCES) $(warp_SOURCES) \
+ $(wavelet_decompose_SOURCES) $(web_browser_SOURCES) \
+ $(web_page_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/gimprc.common \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@align_layers_RC = align-layers.rc.o
+@HAVE_WINDRES_TRUE@animation_optimize_RC = animation-optimize.rc.o
+@HAVE_WINDRES_TRUE@animation_play_RC = animation-play.rc.o
+@HAVE_WINDRES_TRUE@blinds_RC = blinds.rc.o
+@HAVE_WINDRES_TRUE@blur_RC = blur.rc.o
+@HAVE_WINDRES_TRUE@border_average_RC = border-average.rc.o
+@HAVE_WINDRES_TRUE@busy_dialog_RC = busy-dialog.rc.o
+@HAVE_WINDRES_TRUE@cartoon_RC = cartoon.rc.o
+@HAVE_WINDRES_TRUE@checkerboard_RC = checkerboard.rc.o
+@HAVE_WINDRES_TRUE@cml_explorer_RC = cml-explorer.rc.o
+@HAVE_WINDRES_TRUE@color_cube_analyze_RC = color-cube-analyze.rc.o
+@HAVE_WINDRES_TRUE@color_enhance_RC = color-enhance.rc.o
+@HAVE_WINDRES_TRUE@colorify_RC = colorify.rc.o
+@HAVE_WINDRES_TRUE@colormap_remap_RC = colormap-remap.rc.o
+@HAVE_WINDRES_TRUE@compose_RC = compose.rc.o
+@HAVE_WINDRES_TRUE@contrast_retinex_RC = contrast-retinex.rc.o
+@HAVE_WINDRES_TRUE@crop_zealous_RC = crop-zealous.rc.o
+@HAVE_WINDRES_TRUE@curve_bend_RC = curve-bend.rc.o
+@HAVE_WINDRES_TRUE@decompose_RC = decompose.rc.o
+@HAVE_WINDRES_TRUE@depth_merge_RC = depth-merge.rc.o
+@HAVE_WINDRES_TRUE@despeckle_RC = despeckle.rc.o
+@HAVE_WINDRES_TRUE@destripe_RC = destripe.rc.o
+@HAVE_WINDRES_TRUE@edge_dog_RC = edge-dog.rc.o
+@HAVE_WINDRES_TRUE@emboss_RC = emboss.rc.o
+@HAVE_WINDRES_TRUE@file_aa_RC = file-aa.rc.o
+@HAVE_WINDRES_TRUE@file_cel_RC = file-cel.rc.o
+@HAVE_WINDRES_TRUE@file_compressor_RC = file-compressor.rc.o
+@HAVE_WINDRES_TRUE@file_csource_RC = file-csource.rc.o
+@HAVE_WINDRES_TRUE@file_desktop_link_RC = file-desktop-link.rc.o
+@HAVE_WINDRES_TRUE@file_dicom_RC = file-dicom.rc.o
+@HAVE_WINDRES_TRUE@file_gbr_RC = file-gbr.rc.o
+@HAVE_WINDRES_TRUE@file_gegl_RC = file-gegl.rc.o
+@HAVE_WINDRES_TRUE@file_gif_load_RC = file-gif-load.rc.o
+@HAVE_WINDRES_TRUE@file_gif_save_RC = file-gif-save.rc.o
+@HAVE_WINDRES_TRUE@file_gih_RC = file-gih.rc.o
+@HAVE_WINDRES_TRUE@file_glob_RC = file-glob.rc.o
+@HAVE_WINDRES_TRUE@file_header_RC = file-header.rc.o
+@HAVE_WINDRES_TRUE@file_heif_RC = file-heif.rc.o
+@HAVE_WINDRES_TRUE@file_html_table_RC = file-html-table.rc.o
+@HAVE_WINDRES_TRUE@file_jp2_load_RC = file-jp2-load.rc.o
+@HAVE_WINDRES_TRUE@file_jpegxl_RC = file-jpegxl.rc.o
+@HAVE_WINDRES_TRUE@file_mng_RC = file-mng.rc.o
+@HAVE_WINDRES_TRUE@file_pat_RC = file-pat.rc.o
+@HAVE_WINDRES_TRUE@file_pcx_RC = file-pcx.rc.o
+@HAVE_WINDRES_TRUE@file_pdf_load_RC = file-pdf-load.rc.o
+@HAVE_WINDRES_TRUE@file_pdf_save_RC = file-pdf-save.rc.o
+@HAVE_WINDRES_TRUE@file_pix_RC = file-pix.rc.o
+@HAVE_WINDRES_TRUE@file_png_RC = file-png.rc.o
+@HAVE_WINDRES_TRUE@file_pnm_RC = file-pnm.rc.o
+@HAVE_WINDRES_TRUE@file_ps_RC = file-ps.rc.o
+@HAVE_WINDRES_TRUE@file_psp_RC = file-psp.rc.o
+@HAVE_WINDRES_TRUE@file_raw_data_RC = file-raw-data.rc.o
+@HAVE_WINDRES_TRUE@file_sunras_RC = file-sunras.rc.o
+@HAVE_WINDRES_TRUE@file_svg_RC = file-svg.rc.o
+@HAVE_WINDRES_TRUE@file_tga_RC = file-tga.rc.o
+@HAVE_WINDRES_TRUE@file_wmf_RC = file-wmf.rc.o
+@HAVE_WINDRES_TRUE@file_xbm_RC = file-xbm.rc.o
+@HAVE_WINDRES_TRUE@file_xmc_RC = file-xmc.rc.o
+@HAVE_WINDRES_TRUE@file_xpm_RC = file-xpm.rc.o
+@HAVE_WINDRES_TRUE@file_xwd_RC = file-xwd.rc.o
+@HAVE_WINDRES_TRUE@film_RC = film.rc.o
+@HAVE_WINDRES_TRUE@filter_pack_RC = filter-pack.rc.o
+@HAVE_WINDRES_TRUE@fractal_trace_RC = fractal-trace.rc.o
+@HAVE_WINDRES_TRUE@goat_exercise_RC = goat-exercise.rc.o
+@HAVE_WINDRES_TRUE@gradient_map_RC = gradient-map.rc.o
+@HAVE_WINDRES_TRUE@grid_RC = grid.rc.o
+@HAVE_WINDRES_TRUE@guillotine_RC = guillotine.rc.o
+@HAVE_WINDRES_TRUE@hot_RC = hot.rc.o
+@HAVE_WINDRES_TRUE@jigsaw_RC = jigsaw.rc.o
+@HAVE_WINDRES_TRUE@mail_RC = mail.rc.o
+@HAVE_WINDRES_TRUE@max_rgb_RC = max-rgb.rc.o
+@HAVE_WINDRES_TRUE@nl_filter_RC = nl-filter.rc.o
+@HAVE_WINDRES_TRUE@photocopy_RC = photocopy.rc.o
+@HAVE_WINDRES_TRUE@plugin_browser_RC = plugin-browser.rc.o
+@HAVE_WINDRES_TRUE@procedure_browser_RC = procedure-browser.rc.o
+@HAVE_WINDRES_TRUE@qbist_RC = qbist.rc.o
+@HAVE_WINDRES_TRUE@sample_colorize_RC = sample-colorize.rc.o
+@HAVE_WINDRES_TRUE@sharpen_RC = sharpen.rc.o
+@HAVE_WINDRES_TRUE@smooth_palette_RC = smooth-palette.rc.o
+@HAVE_WINDRES_TRUE@softglow_RC = softglow.rc.o
+@HAVE_WINDRES_TRUE@sparkle_RC = sparkle.rc.o
+@HAVE_WINDRES_TRUE@sphere_designer_RC = sphere-designer.rc.o
+@HAVE_WINDRES_TRUE@tile_RC = tile.rc.o
+@HAVE_WINDRES_TRUE@tile_small_RC = tile-small.rc.o
+@HAVE_WINDRES_TRUE@unit_editor_RC = unit-editor.rc.o
+@HAVE_WINDRES_TRUE@van_gogh_lic_RC = van-gogh-lic.rc.o
+@HAVE_WINDRES_TRUE@warp_RC = warp.rc.o
+@HAVE_WINDRES_TRUE@wavelet_decompose_RC = wavelet-decompose.rc.o
+@HAVE_WINDRES_TRUE@web_browser_RC = web-browser.rc.o
+@HAVE_WINDRES_TRUE@web_page_RC = web-page.rc.o
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la $(libm)
+libgimpmodule = $(top_builddir)/libgimpmodule/libgimpmodule-$(GIMP_API_VERSION).la
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+AM_LDFLAGS = $(mwindows)
+EXTRA_DIST = \
+ mkgen.pl \
+ plugin-defs.pl \
+ gimprc.common
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+align_layers_libexecdir = $(gimpplugindir)/plug-ins/align-layers
+animation_optimize_libexecdir = $(gimpplugindir)/plug-ins/animation-optimize
+animation_play_libexecdir = $(gimpplugindir)/plug-ins/animation-play
+blinds_libexecdir = $(gimpplugindir)/plug-ins/blinds
+blur_libexecdir = $(gimpplugindir)/plug-ins/blur
+border_average_libexecdir = $(gimpplugindir)/plug-ins/border-average
+busy_dialog_libexecdir = $(gimpplugindir)/plug-ins/busy-dialog
+cartoon_libexecdir = $(gimpplugindir)/plug-ins/cartoon
+checkerboard_libexecdir = $(gimpplugindir)/plug-ins/checkerboard
+cml_explorer_libexecdir = $(gimpplugindir)/plug-ins/cml-explorer
+color_cube_analyze_libexecdir = $(gimpplugindir)/plug-ins/color-cube-analyze
+color_enhance_libexecdir = $(gimpplugindir)/plug-ins/color-enhance
+colorify_libexecdir = $(gimpplugindir)/plug-ins/colorify
+colormap_remap_libexecdir = $(gimpplugindir)/plug-ins/colormap-remap
+compose_libexecdir = $(gimpplugindir)/plug-ins/compose
+contrast_retinex_libexecdir = $(gimpplugindir)/plug-ins/contrast-retinex
+crop_zealous_libexecdir = $(gimpplugindir)/plug-ins/crop-zealous
+curve_bend_libexecdir = $(gimpplugindir)/plug-ins/curve-bend
+decompose_libexecdir = $(gimpplugindir)/plug-ins/decompose
+depth_merge_libexecdir = $(gimpplugindir)/plug-ins/depth-merge
+despeckle_libexecdir = $(gimpplugindir)/plug-ins/despeckle
+destripe_libexecdir = $(gimpplugindir)/plug-ins/destripe
+edge_dog_libexecdir = $(gimpplugindir)/plug-ins/edge-dog
+emboss_libexecdir = $(gimpplugindir)/plug-ins/emboss
+file_aa_libexecdir = $(gimpplugindir)/plug-ins/file-aa
+file_cel_libexecdir = $(gimpplugindir)/plug-ins/file-cel
+file_compressor_libexecdir = $(gimpplugindir)/plug-ins/file-compressor
+file_csource_libexecdir = $(gimpplugindir)/plug-ins/file-csource
+file_desktop_link_libexecdir = $(gimpplugindir)/plug-ins/file-desktop-link
+file_dicom_libexecdir = $(gimpplugindir)/plug-ins/file-dicom
+file_gbr_libexecdir = $(gimpplugindir)/plug-ins/file-gbr
+file_gegl_libexecdir = $(gimpplugindir)/plug-ins/file-gegl
+file_gif_load_libexecdir = $(gimpplugindir)/plug-ins/file-gif-load
+file_gif_save_libexecdir = $(gimpplugindir)/plug-ins/file-gif-save
+file_gih_libexecdir = $(gimpplugindir)/plug-ins/file-gih
+file_glob_libexecdir = $(gimpplugindir)/plug-ins/file-glob
+file_header_libexecdir = $(gimpplugindir)/plug-ins/file-header
+file_heif_libexecdir = $(gimpplugindir)/plug-ins/file-heif
+file_html_table_libexecdir = $(gimpplugindir)/plug-ins/file-html-table
+file_jp2_load_libexecdir = $(gimpplugindir)/plug-ins/file-jp2-load
+file_jpegxl_libexecdir = $(gimpplugindir)/plug-ins/file-jpegxl
+file_mng_libexecdir = $(gimpplugindir)/plug-ins/file-mng
+file_pat_libexecdir = $(gimpplugindir)/plug-ins/file-pat
+file_pcx_libexecdir = $(gimpplugindir)/plug-ins/file-pcx
+file_pdf_load_libexecdir = $(gimpplugindir)/plug-ins/file-pdf-load
+file_pdf_save_libexecdir = $(gimpplugindir)/plug-ins/file-pdf-save
+file_pix_libexecdir = $(gimpplugindir)/plug-ins/file-pix
+file_png_libexecdir = $(gimpplugindir)/plug-ins/file-png
+file_pnm_libexecdir = $(gimpplugindir)/plug-ins/file-pnm
+file_ps_libexecdir = $(gimpplugindir)/plug-ins/file-ps
+file_psp_libexecdir = $(gimpplugindir)/plug-ins/file-psp
+file_raw_data_libexecdir = $(gimpplugindir)/plug-ins/file-raw-data
+file_sunras_libexecdir = $(gimpplugindir)/plug-ins/file-sunras
+file_svg_libexecdir = $(gimpplugindir)/plug-ins/file-svg
+file_tga_libexecdir = $(gimpplugindir)/plug-ins/file-tga
+file_wmf_libexecdir = $(gimpplugindir)/plug-ins/file-wmf
+file_xbm_libexecdir = $(gimpplugindir)/plug-ins/file-xbm
+file_xmc_libexecdir = $(gimpplugindir)/plug-ins/file-xmc
+file_xpm_libexecdir = $(gimpplugindir)/plug-ins/file-xpm
+file_xwd_libexecdir = $(gimpplugindir)/plug-ins/file-xwd
+film_libexecdir = $(gimpplugindir)/plug-ins/film
+filter_pack_libexecdir = $(gimpplugindir)/plug-ins/filter-pack
+fractal_trace_libexecdir = $(gimpplugindir)/plug-ins/fractal-trace
+goat_exercise_libexecdir = $(gimpplugindir)/plug-ins/goat-exercise
+gradient_map_libexecdir = $(gimpplugindir)/plug-ins/gradient-map
+grid_libexecdir = $(gimpplugindir)/plug-ins/grid
+guillotine_libexecdir = $(gimpplugindir)/plug-ins/guillotine
+hot_libexecdir = $(gimpplugindir)/plug-ins/hot
+jigsaw_libexecdir = $(gimpplugindir)/plug-ins/jigsaw
+mail_libexecdir = $(gimpplugindir)/plug-ins/mail
+max_rgb_libexecdir = $(gimpplugindir)/plug-ins/max-rgb
+nl_filter_libexecdir = $(gimpplugindir)/plug-ins/nl-filter
+photocopy_libexecdir = $(gimpplugindir)/plug-ins/photocopy
+plugin_browser_libexecdir = $(gimpplugindir)/plug-ins/plugin-browser
+procedure_browser_libexecdir = $(gimpplugindir)/plug-ins/procedure-browser
+qbist_libexecdir = $(gimpplugindir)/plug-ins/qbist
+sample_colorize_libexecdir = $(gimpplugindir)/plug-ins/sample-colorize
+sharpen_libexecdir = $(gimpplugindir)/plug-ins/sharpen
+smooth_palette_libexecdir = $(gimpplugindir)/plug-ins/smooth-palette
+softglow_libexecdir = $(gimpplugindir)/plug-ins/softglow
+sparkle_libexecdir = $(gimpplugindir)/plug-ins/sparkle
+sphere_designer_libexecdir = $(gimpplugindir)/plug-ins/sphere-designer
+tile_libexecdir = $(gimpplugindir)/plug-ins/tile
+tile_small_libexecdir = $(gimpplugindir)/plug-ins/tile-small
+unit_editor_libexecdir = $(gimpplugindir)/plug-ins/unit-editor
+van_gogh_lic_libexecdir = $(gimpplugindir)/plug-ins/van-gogh-lic
+warp_libexecdir = $(gimpplugindir)/plug-ins/warp
+wavelet_decompose_libexecdir = $(gimpplugindir)/plug-ins/wavelet-decompose
+web_browser_libexecdir = $(gimpplugindir)/plug-ins/web-browser
+web_page_libexecdir = $(gimpplugindir)/plug-ins/web-page
+file_aa_libexec_PROGRAMS = $(FILE_AA)
+file_heif_libexec_PROGRAMS = $(FILE_HEIF)
+file_jp2_load_libexec_PROGRAMS = $(FILE_JP2_LOAD)
+file_jpegxl_libexec_PROGRAMS = $(FILE_JPEGXL)
+file_mng_libexec_PROGRAMS = $(FILE_MNG)
+file_pdf_save_libexec_PROGRAMS = $(FILE_PDF_SAVE)
+file_ps_libexec_PROGRAMS = $(FILE_PS)
+file_wmf_libexec_PROGRAMS = $(FILE_WMF)
+file_xmc_libexec_PROGRAMS = $(FILE_XMC)
+file_xpm_libexec_PROGRAMS = $(FILE_XPM)
+mail_libexec_PROGRAMS = $(MAIL)
+web_page_libexec_PROGRAMS = $(WEB_PAGE)
+align_layers_SOURCES = \
+ align-layers.c
+
+align_layers_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(align_layers_RC)
+
+animation_optimize_SOURCES = \
+ animation-optimize.c
+
+animation_optimize_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(animation_optimize_RC)
+
+animation_play_SOURCES = \
+ animation-play.c
+
+animation_play_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(animation_play_RC)
+
+blinds_SOURCES = \
+ blinds.c
+
+blinds_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(blinds_RC)
+
+blur_SOURCES = \
+ blur.c
+
+blur_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(blur_RC)
+
+border_average_SOURCES = \
+ border-average.c
+
+border_average_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(border_average_RC)
+
+busy_dialog_SOURCES = \
+ busy-dialog.c
+
+busy_dialog_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(busy_dialog_RC)
+
+cartoon_SOURCES = \
+ cartoon.c
+
+cartoon_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(cartoon_RC)
+
+checkerboard_SOURCES = \
+ checkerboard.c
+
+checkerboard_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(checkerboard_RC)
+
+cml_explorer_SOURCES = \
+ cml-explorer.c
+
+cml_explorer_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(cml_explorer_RC)
+
+color_cube_analyze_SOURCES = \
+ color-cube-analyze.c
+
+color_cube_analyze_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(color_cube_analyze_RC)
+
+color_enhance_SOURCES = \
+ color-enhance.c
+
+color_enhance_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(color_enhance_RC)
+
+colorify_SOURCES = \
+ colorify.c
+
+colorify_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(colorify_RC)
+
+colormap_remap_SOURCES = \
+ colormap-remap.c
+
+colormap_remap_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(colormap_remap_RC)
+
+compose_SOURCES = \
+ compose.c
+
+compose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(compose_RC)
+
+contrast_retinex_SOURCES = \
+ contrast-retinex.c
+
+contrast_retinex_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(contrast_retinex_RC)
+
+crop_zealous_SOURCES = \
+ crop-zealous.c
+
+crop_zealous_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(crop_zealous_RC)
+
+curve_bend_SOURCES = \
+ curve-bend.c
+
+curve_bend_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(curve_bend_RC)
+
+decompose_SOURCES = \
+ decompose.c
+
+decompose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(decompose_RC)
+
+depth_merge_SOURCES = \
+ depth-merge.c
+
+depth_merge_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(depth_merge_RC)
+
+despeckle_SOURCES = \
+ despeckle.c
+
+despeckle_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(despeckle_RC)
+
+destripe_SOURCES = \
+ destripe.c
+
+destripe_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(destripe_RC)
+
+edge_dog_SOURCES = \
+ edge-dog.c
+
+edge_dog_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(edge_dog_RC)
+
+emboss_SOURCES = \
+ emboss.c
+
+emboss_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(emboss_RC)
+
+file_aa_SOURCES = \
+ file-aa.c
+
+file_aa_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(AA_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_aa_RC)
+
+file_cel_SOURCES = \
+ file-cel.c
+
+file_cel_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_cel_RC)
+
+file_compressor_CFLAGS = $(LZMA_CFLAGS)
+file_compressor_SOURCES = \
+ file-compressor.c
+
+file_compressor_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GIO_LIBS) \
+ $(LZMA_LIBS) \
+ $(BZIP2_LIBS) \
+ $(Z_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_compressor_RC)
+
+file_csource_SOURCES = \
+ file-csource.c
+
+file_csource_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_csource_RC)
+
+file_desktop_link_SOURCES = \
+ file-desktop-link.c
+
+file_desktop_link_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_desktop_link_RC)
+
+file_dicom_CFLAGS = -fno-strict-aliasing
+file_dicom_SOURCES = \
+ file-dicom.c
+
+file_dicom_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_dicom_RC)
+
+file_gbr_SOURCES = \
+ file-gbr.c
+
+file_gbr_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gbr_RC)
+
+file_gegl_SOURCES = \
+ file-gegl.c
+
+file_gegl_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gegl_RC)
+
+file_gif_load_SOURCES = \
+ file-gif-load.c
+
+file_gif_load_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gif_load_RC)
+
+file_gif_save_SOURCES = \
+ file-gif-save.c
+
+file_gif_save_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gif_save_RC)
+
+file_gih_SOURCES = \
+ file-gih.c
+
+file_gih_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_gih_RC)
+
+file_glob_SOURCES = \
+ file-glob.c
+
+file_glob_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_glob_RC)
+
+file_header_SOURCES = \
+ file-header.c
+
+file_header_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_header_RC)
+
+file_heif_CFLAGS = $(LIBHEIF_CFLAGS)
+file_heif_SOURCES = \
+ file-heif.c
+
+file_heif_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(LIBHEIF_LIBS) \
+ $(LCMS_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_heif_RC)
+
+file_html_table_SOURCES = \
+ file-html-table.c
+
+file_html_table_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_html_table_RC)
+
+file_jp2_load_CFLAGS = $(OPENJPEG_CFLAGS)
+file_jp2_load_SOURCES = \
+ file-jp2-load.c
+
+file_jp2_load_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(OPENJPEG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jp2_load_RC)
+
+file_jpegxl_CFLAGS = $(JXL_CFLAGS)
+file_jpegxl_SOURCES = \
+ file-jpegxl.c
+
+file_jpegxl_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(JXL_THREADS_LIBS) \
+ $(JXL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpegxl_RC)
+
+file_mng_CFLAGS = $(MNG_CFLAGS)
+file_mng_SOURCES = \
+ file-mng.c
+
+file_mng_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(MNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_mng_RC)
+
+file_pat_SOURCES = \
+ file-pat.c
+
+file_pat_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pat_RC)
+
+file_pcx_SOURCES = \
+ file-pcx.c
+
+file_pcx_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pcx_RC)
+
+file_pdf_load_CFLAGS = $(POPPLER_CFLAGS)
+file_pdf_load_SOURCES = \
+ file-pdf-load.c
+
+file_pdf_load_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(POPPLER_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pdf_load_RC)
+
+file_pdf_save_CFLAGS = $(CAIRO_PDF_CFLAGS)
+file_pdf_save_SOURCES = \
+ file-pdf-save.c
+
+file_pdf_save_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(CAIRO_PDF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pdf_save_RC)
+
+file_pix_SOURCES = \
+ file-pix.c
+
+file_pix_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pix_RC)
+
+file_png_CFLAGS = $(PNG_CFLAGS)
+file_png_SOURCES = \
+ file-png.c
+
+file_png_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(PNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_png_RC)
+
+file_pnm_SOURCES = \
+ file-pnm.c
+
+file_pnm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_pnm_RC)
+
+file_ps_SOURCES = \
+ file-ps.c
+
+file_ps_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GS_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_ps_RC)
+
+file_psp_SOURCES = \
+ file-psp.c
+
+file_psp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(Z_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_psp_RC)
+
+file_raw_data_SOURCES = \
+ file-raw-data.c
+
+file_raw_data_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_raw_data_RC)
+
+file_sunras_SOURCES = \
+ file-sunras.c
+
+file_sunras_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_sunras_RC)
+
+file_svg_CFLAGS = $(SVG_CFLAGS)
+file_svg_SOURCES = \
+ file-svg.c
+
+file_svg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(SVG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_svg_RC)
+
+file_tga_SOURCES = \
+ file-tga.c
+
+file_tga_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tga_RC)
+
+file_wmf_CFLAGS = $(WMF_CFLAGS)
+file_wmf_SOURCES = \
+ file-wmf.c
+
+file_wmf_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(WMF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_wmf_RC)
+
+file_xbm_SOURCES = \
+ file-xbm.c
+
+file_xbm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xbm_RC)
+
+file_xmc_SOURCES = \
+ file-xmc.c
+
+file_xmc_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(XMC_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xmc_RC)
+
+file_xpm_SOURCES = \
+ file-xpm.c
+
+file_xpm_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(XPM_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xpm_RC)
+
+file_xwd_SOURCES = \
+ file-xwd.c
+
+file_xwd_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_xwd_RC)
+
+film_SOURCES = \
+ film.c
+
+film_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(film_RC)
+
+filter_pack_SOURCES = \
+ filter-pack.c
+
+filter_pack_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(filter_pack_RC)
+
+fractal_trace_SOURCES = \
+ fractal-trace.c
+
+fractal_trace_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(fractal_trace_RC)
+
+goat_exercise_SOURCES = \
+ goat-exercise.c
+
+goat_exercise_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(goat_exercise_RC)
+
+gradient_map_SOURCES = \
+ gradient-map.c
+
+gradient_map_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gradient_map_RC)
+
+grid_SOURCES = \
+ grid.c
+
+grid_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(grid_RC)
+
+guillotine_SOURCES = \
+ guillotine.c
+
+guillotine_LDADD = \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(guillotine_RC)
+
+hot_SOURCES = \
+ hot.c
+
+hot_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(hot_RC)
+
+jigsaw_SOURCES = \
+ jigsaw.c
+
+jigsaw_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(jigsaw_RC)
+
+mail_SOURCES = \
+ mail.c
+
+mail_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(mail_RC)
+
+max_rgb_SOURCES = \
+ max-rgb.c
+
+max_rgb_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(max_rgb_RC)
+
+nl_filter_SOURCES = \
+ nl-filter.c
+
+nl_filter_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(nl_filter_RC)
+
+photocopy_SOURCES = \
+ photocopy.c
+
+photocopy_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(photocopy_RC)
+
+plugin_browser_SOURCES = \
+ plugin-browser.c
+
+plugin_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(plugin_browser_RC)
+
+procedure_browser_SOURCES = \
+ procedure-browser.c
+
+procedure_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(procedure_browser_RC)
+
+qbist_SOURCES = \
+ qbist.c
+
+qbist_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(qbist_RC)
+
+sample_colorize_SOURCES = \
+ sample-colorize.c
+
+sample_colorize_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sample_colorize_RC)
+
+sharpen_SOURCES = \
+ sharpen.c
+
+sharpen_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sharpen_RC)
+
+smooth_palette_SOURCES = \
+ smooth-palette.c
+
+smooth_palette_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(smooth_palette_RC)
+
+softglow_SOURCES = \
+ softglow.c
+
+softglow_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(softglow_RC)
+
+sparkle_SOURCES = \
+ sparkle.c
+
+sparkle_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sparkle_RC)
+
+sphere_designer_SOURCES = \
+ sphere-designer.c
+
+sphere_designer_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(sphere_designer_RC)
+
+tile_SOURCES = \
+ tile.c
+
+tile_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(tile_RC)
+
+tile_small_SOURCES = \
+ tile-small.c
+
+tile_small_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(tile_small_RC)
+
+unit_editor_SOURCES = \
+ unit-editor.c
+
+unit_editor_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(unit_editor_RC)
+
+van_gogh_lic_SOURCES = \
+ van-gogh-lic.c
+
+van_gogh_lic_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(van_gogh_lic_RC)
+
+warp_SOURCES = \
+ warp.c
+
+warp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(warp_RC)
+
+wavelet_decompose_SOURCES = \
+ wavelet-decompose.c
+
+wavelet_decompose_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(wavelet_decompose_RC)
+
+web_browser_LDFLAGS = $(framework_cocoa)
+web_browser_CPPFLAGS = $(AM_CPPFLAGS) $(xobjective_c)
+web_browser_SOURCES = \
+ web-browser.c
+
+web_browser_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(web_browser_RC)
+
+web_page_CFLAGS = $(WEBKIT_CFLAGS)
+web_page_SOURCES = \
+ web-page.c
+
+web_page_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpmodule) \
+ $(libgimp) \
+ $(libgimpmath) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(WEBKIT_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(web_page_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(srcdir)/gimprc.common $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/common/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/common/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(srcdir)/gimprc.common $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-align_layers_libexecPROGRAMS: $(align_layers_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(align_layers_libexec_PROGRAMS)'; test -n "$(align_layers_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(align_layers_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(align_layers_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(align_layers_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(align_layers_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-align_layers_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(align_layers_libexec_PROGRAMS)'; test -n "$(align_layers_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(align_layers_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(align_layers_libexecdir)" && rm -f $$files
+
+clean-align_layers_libexecPROGRAMS:
+ @list='$(align_layers_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-animation_optimize_libexecPROGRAMS: $(animation_optimize_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(animation_optimize_libexec_PROGRAMS)'; test -n "$(animation_optimize_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(animation_optimize_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(animation_optimize_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(animation_optimize_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(animation_optimize_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-animation_optimize_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(animation_optimize_libexec_PROGRAMS)'; test -n "$(animation_optimize_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(animation_optimize_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(animation_optimize_libexecdir)" && rm -f $$files
+
+clean-animation_optimize_libexecPROGRAMS:
+ @list='$(animation_optimize_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-animation_play_libexecPROGRAMS: $(animation_play_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(animation_play_libexec_PROGRAMS)'; test -n "$(animation_play_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(animation_play_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(animation_play_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(animation_play_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(animation_play_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-animation_play_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(animation_play_libexec_PROGRAMS)'; test -n "$(animation_play_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(animation_play_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(animation_play_libexecdir)" && rm -f $$files
+
+clean-animation_play_libexecPROGRAMS:
+ @list='$(animation_play_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-blinds_libexecPROGRAMS: $(blinds_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(blinds_libexec_PROGRAMS)'; test -n "$(blinds_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(blinds_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(blinds_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(blinds_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(blinds_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-blinds_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(blinds_libexec_PROGRAMS)'; test -n "$(blinds_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(blinds_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(blinds_libexecdir)" && rm -f $$files
+
+clean-blinds_libexecPROGRAMS:
+ @list='$(blinds_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-blur_libexecPROGRAMS: $(blur_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(blur_libexec_PROGRAMS)'; test -n "$(blur_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(blur_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(blur_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(blur_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(blur_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-blur_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(blur_libexec_PROGRAMS)'; test -n "$(blur_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(blur_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(blur_libexecdir)" && rm -f $$files
+
+clean-blur_libexecPROGRAMS:
+ @list='$(blur_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-border_average_libexecPROGRAMS: $(border_average_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(border_average_libexec_PROGRAMS)'; test -n "$(border_average_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(border_average_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(border_average_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(border_average_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(border_average_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-border_average_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(border_average_libexec_PROGRAMS)'; test -n "$(border_average_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(border_average_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(border_average_libexecdir)" && rm -f $$files
+
+clean-border_average_libexecPROGRAMS:
+ @list='$(border_average_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-busy_dialog_libexecPROGRAMS: $(busy_dialog_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(busy_dialog_libexec_PROGRAMS)'; test -n "$(busy_dialog_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(busy_dialog_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(busy_dialog_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(busy_dialog_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(busy_dialog_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-busy_dialog_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(busy_dialog_libexec_PROGRAMS)'; test -n "$(busy_dialog_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(busy_dialog_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(busy_dialog_libexecdir)" && rm -f $$files
+
+clean-busy_dialog_libexecPROGRAMS:
+ @list='$(busy_dialog_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-cartoon_libexecPROGRAMS: $(cartoon_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(cartoon_libexec_PROGRAMS)'; test -n "$(cartoon_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(cartoon_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(cartoon_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cartoon_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cartoon_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-cartoon_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(cartoon_libexec_PROGRAMS)'; test -n "$(cartoon_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(cartoon_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(cartoon_libexecdir)" && rm -f $$files
+
+clean-cartoon_libexecPROGRAMS:
+ @list='$(cartoon_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-checkerboard_libexecPROGRAMS: $(checkerboard_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(checkerboard_libexec_PROGRAMS)'; test -n "$(checkerboard_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(checkerboard_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(checkerboard_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(checkerboard_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(checkerboard_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-checkerboard_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(checkerboard_libexec_PROGRAMS)'; test -n "$(checkerboard_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(checkerboard_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(checkerboard_libexecdir)" && rm -f $$files
+
+clean-checkerboard_libexecPROGRAMS:
+ @list='$(checkerboard_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-cml_explorer_libexecPROGRAMS: $(cml_explorer_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(cml_explorer_libexec_PROGRAMS)'; test -n "$(cml_explorer_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(cml_explorer_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(cml_explorer_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cml_explorer_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cml_explorer_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-cml_explorer_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(cml_explorer_libexec_PROGRAMS)'; test -n "$(cml_explorer_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(cml_explorer_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(cml_explorer_libexecdir)" && rm -f $$files
+
+clean-cml_explorer_libexecPROGRAMS:
+ @list='$(cml_explorer_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-color_cube_analyze_libexecPROGRAMS: $(color_cube_analyze_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(color_cube_analyze_libexec_PROGRAMS)'; test -n "$(color_cube_analyze_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(color_cube_analyze_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(color_cube_analyze_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(color_cube_analyze_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(color_cube_analyze_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-color_cube_analyze_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(color_cube_analyze_libexec_PROGRAMS)'; test -n "$(color_cube_analyze_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(color_cube_analyze_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(color_cube_analyze_libexecdir)" && rm -f $$files
+
+clean-color_cube_analyze_libexecPROGRAMS:
+ @list='$(color_cube_analyze_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-color_enhance_libexecPROGRAMS: $(color_enhance_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(color_enhance_libexec_PROGRAMS)'; test -n "$(color_enhance_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(color_enhance_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(color_enhance_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(color_enhance_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(color_enhance_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-color_enhance_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(color_enhance_libexec_PROGRAMS)'; test -n "$(color_enhance_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(color_enhance_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(color_enhance_libexecdir)" && rm -f $$files
+
+clean-color_enhance_libexecPROGRAMS:
+ @list='$(color_enhance_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-colorify_libexecPROGRAMS: $(colorify_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(colorify_libexec_PROGRAMS)'; test -n "$(colorify_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(colorify_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(colorify_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(colorify_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(colorify_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-colorify_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(colorify_libexec_PROGRAMS)'; test -n "$(colorify_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(colorify_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(colorify_libexecdir)" && rm -f $$files
+
+clean-colorify_libexecPROGRAMS:
+ @list='$(colorify_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-colormap_remap_libexecPROGRAMS: $(colormap_remap_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(colormap_remap_libexec_PROGRAMS)'; test -n "$(colormap_remap_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(colormap_remap_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(colormap_remap_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(colormap_remap_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(colormap_remap_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-colormap_remap_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(colormap_remap_libexec_PROGRAMS)'; test -n "$(colormap_remap_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(colormap_remap_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(colormap_remap_libexecdir)" && rm -f $$files
+
+clean-colormap_remap_libexecPROGRAMS:
+ @list='$(colormap_remap_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-compose_libexecPROGRAMS: $(compose_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(compose_libexec_PROGRAMS)'; test -n "$(compose_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(compose_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(compose_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(compose_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(compose_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-compose_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(compose_libexec_PROGRAMS)'; test -n "$(compose_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(compose_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(compose_libexecdir)" && rm -f $$files
+
+clean-compose_libexecPROGRAMS:
+ @list='$(compose_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-contrast_retinex_libexecPROGRAMS: $(contrast_retinex_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(contrast_retinex_libexec_PROGRAMS)'; test -n "$(contrast_retinex_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(contrast_retinex_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(contrast_retinex_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(contrast_retinex_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(contrast_retinex_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-contrast_retinex_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(contrast_retinex_libexec_PROGRAMS)'; test -n "$(contrast_retinex_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(contrast_retinex_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(contrast_retinex_libexecdir)" && rm -f $$files
+
+clean-contrast_retinex_libexecPROGRAMS:
+ @list='$(contrast_retinex_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-crop_zealous_libexecPROGRAMS: $(crop_zealous_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(crop_zealous_libexec_PROGRAMS)'; test -n "$(crop_zealous_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(crop_zealous_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(crop_zealous_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(crop_zealous_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(crop_zealous_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-crop_zealous_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(crop_zealous_libexec_PROGRAMS)'; test -n "$(crop_zealous_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(crop_zealous_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(crop_zealous_libexecdir)" && rm -f $$files
+
+clean-crop_zealous_libexecPROGRAMS:
+ @list='$(crop_zealous_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-curve_bend_libexecPROGRAMS: $(curve_bend_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(curve_bend_libexec_PROGRAMS)'; test -n "$(curve_bend_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(curve_bend_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(curve_bend_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(curve_bend_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(curve_bend_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-curve_bend_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(curve_bend_libexec_PROGRAMS)'; test -n "$(curve_bend_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(curve_bend_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(curve_bend_libexecdir)" && rm -f $$files
+
+clean-curve_bend_libexecPROGRAMS:
+ @list='$(curve_bend_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-decompose_libexecPROGRAMS: $(decompose_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(decompose_libexec_PROGRAMS)'; test -n "$(decompose_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(decompose_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(decompose_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(decompose_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(decompose_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-decompose_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(decompose_libexec_PROGRAMS)'; test -n "$(decompose_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(decompose_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(decompose_libexecdir)" && rm -f $$files
+
+clean-decompose_libexecPROGRAMS:
+ @list='$(decompose_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-depth_merge_libexecPROGRAMS: $(depth_merge_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(depth_merge_libexec_PROGRAMS)'; test -n "$(depth_merge_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(depth_merge_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(depth_merge_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(depth_merge_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(depth_merge_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-depth_merge_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(depth_merge_libexec_PROGRAMS)'; test -n "$(depth_merge_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(depth_merge_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(depth_merge_libexecdir)" && rm -f $$files
+
+clean-depth_merge_libexecPROGRAMS:
+ @list='$(depth_merge_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-despeckle_libexecPROGRAMS: $(despeckle_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(despeckle_libexec_PROGRAMS)'; test -n "$(despeckle_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(despeckle_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(despeckle_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(despeckle_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(despeckle_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-despeckle_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(despeckle_libexec_PROGRAMS)'; test -n "$(despeckle_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(despeckle_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(despeckle_libexecdir)" && rm -f $$files
+
+clean-despeckle_libexecPROGRAMS:
+ @list='$(despeckle_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-destripe_libexecPROGRAMS: $(destripe_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(destripe_libexec_PROGRAMS)'; test -n "$(destripe_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(destripe_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(destripe_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(destripe_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(destripe_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-destripe_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(destripe_libexec_PROGRAMS)'; test -n "$(destripe_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(destripe_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(destripe_libexecdir)" && rm -f $$files
+
+clean-destripe_libexecPROGRAMS:
+ @list='$(destripe_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-edge_dog_libexecPROGRAMS: $(edge_dog_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(edge_dog_libexec_PROGRAMS)'; test -n "$(edge_dog_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(edge_dog_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(edge_dog_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(edge_dog_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(edge_dog_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-edge_dog_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(edge_dog_libexec_PROGRAMS)'; test -n "$(edge_dog_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(edge_dog_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(edge_dog_libexecdir)" && rm -f $$files
+
+clean-edge_dog_libexecPROGRAMS:
+ @list='$(edge_dog_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-emboss_libexecPROGRAMS: $(emboss_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(emboss_libexec_PROGRAMS)'; test -n "$(emboss_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(emboss_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(emboss_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(emboss_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(emboss_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-emboss_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(emboss_libexec_PROGRAMS)'; test -n "$(emboss_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(emboss_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(emboss_libexecdir)" && rm -f $$files
+
+clean-emboss_libexecPROGRAMS:
+ @list='$(emboss_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_aa_libexecPROGRAMS: $(file_aa_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_aa_libexec_PROGRAMS)'; test -n "$(file_aa_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_aa_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_aa_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_aa_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_aa_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_aa_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_aa_libexec_PROGRAMS)'; test -n "$(file_aa_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_aa_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_aa_libexecdir)" && rm -f $$files
+
+clean-file_aa_libexecPROGRAMS:
+ @list='$(file_aa_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_cel_libexecPROGRAMS: $(file_cel_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_cel_libexec_PROGRAMS)'; test -n "$(file_cel_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_cel_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_cel_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_cel_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_cel_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_cel_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_cel_libexec_PROGRAMS)'; test -n "$(file_cel_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_cel_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_cel_libexecdir)" && rm -f $$files
+
+clean-file_cel_libexecPROGRAMS:
+ @list='$(file_cel_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_compressor_libexecPROGRAMS: $(file_compressor_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_compressor_libexec_PROGRAMS)'; test -n "$(file_compressor_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_compressor_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_compressor_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_compressor_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_compressor_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_compressor_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_compressor_libexec_PROGRAMS)'; test -n "$(file_compressor_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_compressor_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_compressor_libexecdir)" && rm -f $$files
+
+clean-file_compressor_libexecPROGRAMS:
+ @list='$(file_compressor_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_csource_libexecPROGRAMS: $(file_csource_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_csource_libexec_PROGRAMS)'; test -n "$(file_csource_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_csource_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_csource_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_csource_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_csource_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_csource_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_csource_libexec_PROGRAMS)'; test -n "$(file_csource_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_csource_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_csource_libexecdir)" && rm -f $$files
+
+clean-file_csource_libexecPROGRAMS:
+ @list='$(file_csource_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_desktop_link_libexecPROGRAMS: $(file_desktop_link_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_desktop_link_libexec_PROGRAMS)'; test -n "$(file_desktop_link_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_desktop_link_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_desktop_link_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_desktop_link_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_desktop_link_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_desktop_link_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_desktop_link_libexec_PROGRAMS)'; test -n "$(file_desktop_link_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_desktop_link_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_desktop_link_libexecdir)" && rm -f $$files
+
+clean-file_desktop_link_libexecPROGRAMS:
+ @list='$(file_desktop_link_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_dicom_libexecPROGRAMS: $(file_dicom_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_dicom_libexec_PROGRAMS)'; test -n "$(file_dicom_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_dicom_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_dicom_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_dicom_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_dicom_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_dicom_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_dicom_libexec_PROGRAMS)'; test -n "$(file_dicom_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_dicom_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_dicom_libexecdir)" && rm -f $$files
+
+clean-file_dicom_libexecPROGRAMS:
+ @list='$(file_dicom_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_gbr_libexecPROGRAMS: $(file_gbr_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_gbr_libexec_PROGRAMS)'; test -n "$(file_gbr_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_gbr_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_gbr_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_gbr_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_gbr_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_gbr_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_gbr_libexec_PROGRAMS)'; test -n "$(file_gbr_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_gbr_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_gbr_libexecdir)" && rm -f $$files
+
+clean-file_gbr_libexecPROGRAMS:
+ @list='$(file_gbr_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_gegl_libexecPROGRAMS: $(file_gegl_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_gegl_libexec_PROGRAMS)'; test -n "$(file_gegl_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_gegl_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_gegl_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_gegl_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_gegl_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_gegl_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_gegl_libexec_PROGRAMS)'; test -n "$(file_gegl_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_gegl_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_gegl_libexecdir)" && rm -f $$files
+
+clean-file_gegl_libexecPROGRAMS:
+ @list='$(file_gegl_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_gif_load_libexecPROGRAMS: $(file_gif_load_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_gif_load_libexec_PROGRAMS)'; test -n "$(file_gif_load_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_gif_load_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_gif_load_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_gif_load_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_gif_load_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_gif_load_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_gif_load_libexec_PROGRAMS)'; test -n "$(file_gif_load_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_gif_load_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_gif_load_libexecdir)" && rm -f $$files
+
+clean-file_gif_load_libexecPROGRAMS:
+ @list='$(file_gif_load_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_gif_save_libexecPROGRAMS: $(file_gif_save_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_gif_save_libexec_PROGRAMS)'; test -n "$(file_gif_save_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_gif_save_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_gif_save_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_gif_save_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_gif_save_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_gif_save_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_gif_save_libexec_PROGRAMS)'; test -n "$(file_gif_save_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_gif_save_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_gif_save_libexecdir)" && rm -f $$files
+
+clean-file_gif_save_libexecPROGRAMS:
+ @list='$(file_gif_save_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_gih_libexecPROGRAMS: $(file_gih_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_gih_libexec_PROGRAMS)'; test -n "$(file_gih_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_gih_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_gih_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_gih_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_gih_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_gih_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_gih_libexec_PROGRAMS)'; test -n "$(file_gih_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_gih_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_gih_libexecdir)" && rm -f $$files
+
+clean-file_gih_libexecPROGRAMS:
+ @list='$(file_gih_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_glob_libexecPROGRAMS: $(file_glob_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_glob_libexec_PROGRAMS)'; test -n "$(file_glob_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_glob_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_glob_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_glob_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_glob_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_glob_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_glob_libexec_PROGRAMS)'; test -n "$(file_glob_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_glob_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_glob_libexecdir)" && rm -f $$files
+
+clean-file_glob_libexecPROGRAMS:
+ @list='$(file_glob_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_header_libexecPROGRAMS: $(file_header_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_header_libexec_PROGRAMS)'; test -n "$(file_header_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_header_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_header_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_header_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_header_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_header_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_header_libexec_PROGRAMS)'; test -n "$(file_header_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_header_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_header_libexecdir)" && rm -f $$files
+
+clean-file_header_libexecPROGRAMS:
+ @list='$(file_header_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_heif_libexecPROGRAMS: $(file_heif_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_heif_libexec_PROGRAMS)'; test -n "$(file_heif_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_heif_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_heif_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_heif_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_heif_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_heif_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_heif_libexec_PROGRAMS)'; test -n "$(file_heif_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_heif_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_heif_libexecdir)" && rm -f $$files
+
+clean-file_heif_libexecPROGRAMS:
+ @list='$(file_heif_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_html_table_libexecPROGRAMS: $(file_html_table_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_html_table_libexec_PROGRAMS)'; test -n "$(file_html_table_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_html_table_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_html_table_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_html_table_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_html_table_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_html_table_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_html_table_libexec_PROGRAMS)'; test -n "$(file_html_table_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_html_table_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_html_table_libexecdir)" && rm -f $$files
+
+clean-file_html_table_libexecPROGRAMS:
+ @list='$(file_html_table_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_jp2_load_libexecPROGRAMS: $(file_jp2_load_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_jp2_load_libexec_PROGRAMS)'; test -n "$(file_jp2_load_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_jp2_load_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_jp2_load_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_jp2_load_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_jp2_load_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_jp2_load_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_jp2_load_libexec_PROGRAMS)'; test -n "$(file_jp2_load_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_jp2_load_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_jp2_load_libexecdir)" && rm -f $$files
+
+clean-file_jp2_load_libexecPROGRAMS:
+ @list='$(file_jp2_load_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_jpegxl_libexecPROGRAMS: $(file_jpegxl_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_jpegxl_libexec_PROGRAMS)'; test -n "$(file_jpegxl_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_jpegxl_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_jpegxl_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_jpegxl_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_jpegxl_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_jpegxl_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_jpegxl_libexec_PROGRAMS)'; test -n "$(file_jpegxl_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_jpegxl_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_jpegxl_libexecdir)" && rm -f $$files
+
+clean-file_jpegxl_libexecPROGRAMS:
+ @list='$(file_jpegxl_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_mng_libexecPROGRAMS: $(file_mng_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_mng_libexec_PROGRAMS)'; test -n "$(file_mng_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_mng_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_mng_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_mng_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_mng_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_mng_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_mng_libexec_PROGRAMS)'; test -n "$(file_mng_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_mng_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_mng_libexecdir)" && rm -f $$files
+
+clean-file_mng_libexecPROGRAMS:
+ @list='$(file_mng_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pat_libexecPROGRAMS: $(file_pat_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pat_libexec_PROGRAMS)'; test -n "$(file_pat_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pat_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pat_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pat_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pat_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pat_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pat_libexec_PROGRAMS)'; test -n "$(file_pat_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pat_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pat_libexecdir)" && rm -f $$files
+
+clean-file_pat_libexecPROGRAMS:
+ @list='$(file_pat_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pcx_libexecPROGRAMS: $(file_pcx_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pcx_libexec_PROGRAMS)'; test -n "$(file_pcx_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pcx_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pcx_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pcx_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pcx_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pcx_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pcx_libexec_PROGRAMS)'; test -n "$(file_pcx_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pcx_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pcx_libexecdir)" && rm -f $$files
+
+clean-file_pcx_libexecPROGRAMS:
+ @list='$(file_pcx_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pdf_load_libexecPROGRAMS: $(file_pdf_load_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pdf_load_libexec_PROGRAMS)'; test -n "$(file_pdf_load_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pdf_load_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pdf_load_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pdf_load_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pdf_load_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pdf_load_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pdf_load_libexec_PROGRAMS)'; test -n "$(file_pdf_load_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pdf_load_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pdf_load_libexecdir)" && rm -f $$files
+
+clean-file_pdf_load_libexecPROGRAMS:
+ @list='$(file_pdf_load_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pdf_save_libexecPROGRAMS: $(file_pdf_save_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pdf_save_libexec_PROGRAMS)'; test -n "$(file_pdf_save_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pdf_save_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pdf_save_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pdf_save_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pdf_save_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pdf_save_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pdf_save_libexec_PROGRAMS)'; test -n "$(file_pdf_save_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pdf_save_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pdf_save_libexecdir)" && rm -f $$files
+
+clean-file_pdf_save_libexecPROGRAMS:
+ @list='$(file_pdf_save_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pix_libexecPROGRAMS: $(file_pix_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pix_libexec_PROGRAMS)'; test -n "$(file_pix_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pix_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pix_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pix_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pix_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pix_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pix_libexec_PROGRAMS)'; test -n "$(file_pix_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pix_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pix_libexecdir)" && rm -f $$files
+
+clean-file_pix_libexecPROGRAMS:
+ @list='$(file_pix_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_png_libexecPROGRAMS: $(file_png_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_png_libexec_PROGRAMS)'; test -n "$(file_png_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_png_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_png_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_png_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_png_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_png_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_png_libexec_PROGRAMS)'; test -n "$(file_png_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_png_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_png_libexecdir)" && rm -f $$files
+
+clean-file_png_libexecPROGRAMS:
+ @list='$(file_png_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_pnm_libexecPROGRAMS: $(file_pnm_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_pnm_libexec_PROGRAMS)'; test -n "$(file_pnm_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_pnm_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_pnm_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_pnm_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_pnm_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_pnm_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_pnm_libexec_PROGRAMS)'; test -n "$(file_pnm_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_pnm_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_pnm_libexecdir)" && rm -f $$files
+
+clean-file_pnm_libexecPROGRAMS:
+ @list='$(file_pnm_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_ps_libexecPROGRAMS: $(file_ps_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_ps_libexec_PROGRAMS)'; test -n "$(file_ps_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_ps_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_ps_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_ps_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_ps_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_ps_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_ps_libexec_PROGRAMS)'; test -n "$(file_ps_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_ps_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_ps_libexecdir)" && rm -f $$files
+
+clean-file_ps_libexecPROGRAMS:
+ @list='$(file_ps_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_psp_libexecPROGRAMS: $(file_psp_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_psp_libexec_PROGRAMS)'; test -n "$(file_psp_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_psp_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_psp_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_psp_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_psp_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_psp_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_psp_libexec_PROGRAMS)'; test -n "$(file_psp_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_psp_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_psp_libexecdir)" && rm -f $$files
+
+clean-file_psp_libexecPROGRAMS:
+ @list='$(file_psp_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_raw_data_libexecPROGRAMS: $(file_raw_data_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_raw_data_libexec_PROGRAMS)'; test -n "$(file_raw_data_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_raw_data_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_raw_data_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_raw_data_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_raw_data_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_raw_data_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_raw_data_libexec_PROGRAMS)'; test -n "$(file_raw_data_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_raw_data_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_raw_data_libexecdir)" && rm -f $$files
+
+clean-file_raw_data_libexecPROGRAMS:
+ @list='$(file_raw_data_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_sunras_libexecPROGRAMS: $(file_sunras_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_sunras_libexec_PROGRAMS)'; test -n "$(file_sunras_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_sunras_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_sunras_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_sunras_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_sunras_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_sunras_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_sunras_libexec_PROGRAMS)'; test -n "$(file_sunras_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_sunras_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_sunras_libexecdir)" && rm -f $$files
+
+clean-file_sunras_libexecPROGRAMS:
+ @list='$(file_sunras_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_svg_libexecPROGRAMS: $(file_svg_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_svg_libexec_PROGRAMS)'; test -n "$(file_svg_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_svg_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_svg_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_svg_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_svg_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_svg_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_svg_libexec_PROGRAMS)'; test -n "$(file_svg_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_svg_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_svg_libexecdir)" && rm -f $$files
+
+clean-file_svg_libexecPROGRAMS:
+ @list='$(file_svg_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_tga_libexecPROGRAMS: $(file_tga_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_tga_libexec_PROGRAMS)'; test -n "$(file_tga_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_tga_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_tga_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_tga_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_tga_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_tga_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_tga_libexec_PROGRAMS)'; test -n "$(file_tga_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_tga_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_tga_libexecdir)" && rm -f $$files
+
+clean-file_tga_libexecPROGRAMS:
+ @list='$(file_tga_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_wmf_libexecPROGRAMS: $(file_wmf_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_wmf_libexec_PROGRAMS)'; test -n "$(file_wmf_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_wmf_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_wmf_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_wmf_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_wmf_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_wmf_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_wmf_libexec_PROGRAMS)'; test -n "$(file_wmf_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_wmf_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_wmf_libexecdir)" && rm -f $$files
+
+clean-file_wmf_libexecPROGRAMS:
+ @list='$(file_wmf_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_xbm_libexecPROGRAMS: $(file_xbm_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_xbm_libexec_PROGRAMS)'; test -n "$(file_xbm_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_xbm_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_xbm_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_xbm_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_xbm_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_xbm_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_xbm_libexec_PROGRAMS)'; test -n "$(file_xbm_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_xbm_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_xbm_libexecdir)" && rm -f $$files
+
+clean-file_xbm_libexecPROGRAMS:
+ @list='$(file_xbm_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_xmc_libexecPROGRAMS: $(file_xmc_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_xmc_libexec_PROGRAMS)'; test -n "$(file_xmc_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_xmc_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_xmc_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_xmc_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_xmc_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_xmc_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_xmc_libexec_PROGRAMS)'; test -n "$(file_xmc_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_xmc_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_xmc_libexecdir)" && rm -f $$files
+
+clean-file_xmc_libexecPROGRAMS:
+ @list='$(file_xmc_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_xpm_libexecPROGRAMS: $(file_xpm_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_xpm_libexec_PROGRAMS)'; test -n "$(file_xpm_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_xpm_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_xpm_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_xpm_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_xpm_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_xpm_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_xpm_libexec_PROGRAMS)'; test -n "$(file_xpm_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_xpm_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_xpm_libexecdir)" && rm -f $$files
+
+clean-file_xpm_libexecPROGRAMS:
+ @list='$(file_xpm_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-file_xwd_libexecPROGRAMS: $(file_xwd_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(file_xwd_libexec_PROGRAMS)'; test -n "$(file_xwd_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(file_xwd_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(file_xwd_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(file_xwd_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(file_xwd_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-file_xwd_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(file_xwd_libexec_PROGRAMS)'; test -n "$(file_xwd_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(file_xwd_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(file_xwd_libexecdir)" && rm -f $$files
+
+clean-file_xwd_libexecPROGRAMS:
+ @list='$(file_xwd_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-film_libexecPROGRAMS: $(film_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(film_libexec_PROGRAMS)'; test -n "$(film_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(film_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(film_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(film_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(film_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-film_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(film_libexec_PROGRAMS)'; test -n "$(film_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(film_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(film_libexecdir)" && rm -f $$files
+
+clean-film_libexecPROGRAMS:
+ @list='$(film_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-filter_pack_libexecPROGRAMS: $(filter_pack_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(filter_pack_libexec_PROGRAMS)'; test -n "$(filter_pack_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(filter_pack_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(filter_pack_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(filter_pack_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(filter_pack_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-filter_pack_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(filter_pack_libexec_PROGRAMS)'; test -n "$(filter_pack_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(filter_pack_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(filter_pack_libexecdir)" && rm -f $$files
+
+clean-filter_pack_libexecPROGRAMS:
+ @list='$(filter_pack_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-fractal_trace_libexecPROGRAMS: $(fractal_trace_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(fractal_trace_libexec_PROGRAMS)'; test -n "$(fractal_trace_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(fractal_trace_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(fractal_trace_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(fractal_trace_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(fractal_trace_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-fractal_trace_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(fractal_trace_libexec_PROGRAMS)'; test -n "$(fractal_trace_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(fractal_trace_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(fractal_trace_libexecdir)" && rm -f $$files
+
+clean-fractal_trace_libexecPROGRAMS:
+ @list='$(fractal_trace_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-goat_exercise_libexecPROGRAMS: $(goat_exercise_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(goat_exercise_libexec_PROGRAMS)'; test -n "$(goat_exercise_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(goat_exercise_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(goat_exercise_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(goat_exercise_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(goat_exercise_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-goat_exercise_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(goat_exercise_libexec_PROGRAMS)'; test -n "$(goat_exercise_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(goat_exercise_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(goat_exercise_libexecdir)" && rm -f $$files
+
+clean-goat_exercise_libexecPROGRAMS:
+ @list='$(goat_exercise_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-gradient_map_libexecPROGRAMS: $(gradient_map_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(gradient_map_libexec_PROGRAMS)'; test -n "$(gradient_map_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(gradient_map_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(gradient_map_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(gradient_map_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(gradient_map_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-gradient_map_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(gradient_map_libexec_PROGRAMS)'; test -n "$(gradient_map_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(gradient_map_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(gradient_map_libexecdir)" && rm -f $$files
+
+clean-gradient_map_libexecPROGRAMS:
+ @list='$(gradient_map_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-grid_libexecPROGRAMS: $(grid_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(grid_libexec_PROGRAMS)'; test -n "$(grid_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(grid_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(grid_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(grid_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(grid_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-grid_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(grid_libexec_PROGRAMS)'; test -n "$(grid_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(grid_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(grid_libexecdir)" && rm -f $$files
+
+clean-grid_libexecPROGRAMS:
+ @list='$(grid_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-guillotine_libexecPROGRAMS: $(guillotine_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(guillotine_libexec_PROGRAMS)'; test -n "$(guillotine_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(guillotine_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(guillotine_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(guillotine_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(guillotine_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-guillotine_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(guillotine_libexec_PROGRAMS)'; test -n "$(guillotine_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(guillotine_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(guillotine_libexecdir)" && rm -f $$files
+
+clean-guillotine_libexecPROGRAMS:
+ @list='$(guillotine_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-hot_libexecPROGRAMS: $(hot_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(hot_libexec_PROGRAMS)'; test -n "$(hot_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(hot_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(hot_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(hot_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(hot_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-hot_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(hot_libexec_PROGRAMS)'; test -n "$(hot_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(hot_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(hot_libexecdir)" && rm -f $$files
+
+clean-hot_libexecPROGRAMS:
+ @list='$(hot_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-jigsaw_libexecPROGRAMS: $(jigsaw_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(jigsaw_libexec_PROGRAMS)'; test -n "$(jigsaw_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(jigsaw_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(jigsaw_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(jigsaw_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(jigsaw_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-jigsaw_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(jigsaw_libexec_PROGRAMS)'; test -n "$(jigsaw_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(jigsaw_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(jigsaw_libexecdir)" && rm -f $$files
+
+clean-jigsaw_libexecPROGRAMS:
+ @list='$(jigsaw_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-mail_libexecPROGRAMS: $(mail_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(mail_libexec_PROGRAMS)'; test -n "$(mail_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(mail_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(mail_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(mail_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(mail_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-mail_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(mail_libexec_PROGRAMS)'; test -n "$(mail_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(mail_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(mail_libexecdir)" && rm -f $$files
+
+clean-mail_libexecPROGRAMS:
+ @list='$(mail_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-max_rgb_libexecPROGRAMS: $(max_rgb_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(max_rgb_libexec_PROGRAMS)'; test -n "$(max_rgb_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(max_rgb_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(max_rgb_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(max_rgb_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(max_rgb_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-max_rgb_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(max_rgb_libexec_PROGRAMS)'; test -n "$(max_rgb_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(max_rgb_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(max_rgb_libexecdir)" && rm -f $$files
+
+clean-max_rgb_libexecPROGRAMS:
+ @list='$(max_rgb_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-nl_filter_libexecPROGRAMS: $(nl_filter_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(nl_filter_libexec_PROGRAMS)'; test -n "$(nl_filter_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(nl_filter_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(nl_filter_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(nl_filter_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(nl_filter_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-nl_filter_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nl_filter_libexec_PROGRAMS)'; test -n "$(nl_filter_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(nl_filter_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(nl_filter_libexecdir)" && rm -f $$files
+
+clean-nl_filter_libexecPROGRAMS:
+ @list='$(nl_filter_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-photocopy_libexecPROGRAMS: $(photocopy_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(photocopy_libexec_PROGRAMS)'; test -n "$(photocopy_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(photocopy_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(photocopy_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(photocopy_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(photocopy_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-photocopy_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(photocopy_libexec_PROGRAMS)'; test -n "$(photocopy_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(photocopy_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(photocopy_libexecdir)" && rm -f $$files
+
+clean-photocopy_libexecPROGRAMS:
+ @list='$(photocopy_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-plugin_browser_libexecPROGRAMS: $(plugin_browser_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(plugin_browser_libexec_PROGRAMS)'; test -n "$(plugin_browser_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(plugin_browser_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(plugin_browser_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(plugin_browser_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(plugin_browser_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-plugin_browser_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(plugin_browser_libexec_PROGRAMS)'; test -n "$(plugin_browser_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(plugin_browser_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(plugin_browser_libexecdir)" && rm -f $$files
+
+clean-plugin_browser_libexecPROGRAMS:
+ @list='$(plugin_browser_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-procedure_browser_libexecPROGRAMS: $(procedure_browser_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(procedure_browser_libexec_PROGRAMS)'; test -n "$(procedure_browser_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(procedure_browser_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(procedure_browser_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(procedure_browser_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(procedure_browser_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-procedure_browser_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(procedure_browser_libexec_PROGRAMS)'; test -n "$(procedure_browser_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(procedure_browser_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(procedure_browser_libexecdir)" && rm -f $$files
+
+clean-procedure_browser_libexecPROGRAMS:
+ @list='$(procedure_browser_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-qbist_libexecPROGRAMS: $(qbist_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(qbist_libexec_PROGRAMS)'; test -n "$(qbist_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(qbist_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(qbist_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(qbist_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(qbist_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-qbist_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(qbist_libexec_PROGRAMS)'; test -n "$(qbist_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(qbist_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(qbist_libexecdir)" && rm -f $$files
+
+clean-qbist_libexecPROGRAMS:
+ @list='$(qbist_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sample_colorize_libexecPROGRAMS: $(sample_colorize_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sample_colorize_libexec_PROGRAMS)'; test -n "$(sample_colorize_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sample_colorize_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sample_colorize_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sample_colorize_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sample_colorize_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sample_colorize_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sample_colorize_libexec_PROGRAMS)'; test -n "$(sample_colorize_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sample_colorize_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sample_colorize_libexecdir)" && rm -f $$files
+
+clean-sample_colorize_libexecPROGRAMS:
+ @list='$(sample_colorize_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sharpen_libexecPROGRAMS: $(sharpen_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sharpen_libexec_PROGRAMS)'; test -n "$(sharpen_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sharpen_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sharpen_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sharpen_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sharpen_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sharpen_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sharpen_libexec_PROGRAMS)'; test -n "$(sharpen_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sharpen_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sharpen_libexecdir)" && rm -f $$files
+
+clean-sharpen_libexecPROGRAMS:
+ @list='$(sharpen_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-smooth_palette_libexecPROGRAMS: $(smooth_palette_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(smooth_palette_libexec_PROGRAMS)'; test -n "$(smooth_palette_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(smooth_palette_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(smooth_palette_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(smooth_palette_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(smooth_palette_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-smooth_palette_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(smooth_palette_libexec_PROGRAMS)'; test -n "$(smooth_palette_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(smooth_palette_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(smooth_palette_libexecdir)" && rm -f $$files
+
+clean-smooth_palette_libexecPROGRAMS:
+ @list='$(smooth_palette_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-softglow_libexecPROGRAMS: $(softglow_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(softglow_libexec_PROGRAMS)'; test -n "$(softglow_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(softglow_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(softglow_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(softglow_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(softglow_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-softglow_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(softglow_libexec_PROGRAMS)'; test -n "$(softglow_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(softglow_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(softglow_libexecdir)" && rm -f $$files
+
+clean-softglow_libexecPROGRAMS:
+ @list='$(softglow_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sparkle_libexecPROGRAMS: $(sparkle_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sparkle_libexec_PROGRAMS)'; test -n "$(sparkle_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sparkle_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sparkle_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sparkle_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sparkle_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sparkle_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sparkle_libexec_PROGRAMS)'; test -n "$(sparkle_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sparkle_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sparkle_libexecdir)" && rm -f $$files
+
+clean-sparkle_libexecPROGRAMS:
+ @list='$(sparkle_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-sphere_designer_libexecPROGRAMS: $(sphere_designer_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sphere_designer_libexec_PROGRAMS)'; test -n "$(sphere_designer_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sphere_designer_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sphere_designer_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sphere_designer_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sphere_designer_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sphere_designer_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sphere_designer_libexec_PROGRAMS)'; test -n "$(sphere_designer_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sphere_designer_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sphere_designer_libexecdir)" && rm -f $$files
+
+clean-sphere_designer_libexecPROGRAMS:
+ @list='$(sphere_designer_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-tile_libexecPROGRAMS: $(tile_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(tile_libexec_PROGRAMS)'; test -n "$(tile_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(tile_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(tile_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(tile_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(tile_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-tile_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(tile_libexec_PROGRAMS)'; test -n "$(tile_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(tile_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(tile_libexecdir)" && rm -f $$files
+
+clean-tile_libexecPROGRAMS:
+ @list='$(tile_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-tile_small_libexecPROGRAMS: $(tile_small_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(tile_small_libexec_PROGRAMS)'; test -n "$(tile_small_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(tile_small_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(tile_small_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(tile_small_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(tile_small_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-tile_small_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(tile_small_libexec_PROGRAMS)'; test -n "$(tile_small_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(tile_small_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(tile_small_libexecdir)" && rm -f $$files
+
+clean-tile_small_libexecPROGRAMS:
+ @list='$(tile_small_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-unit_editor_libexecPROGRAMS: $(unit_editor_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(unit_editor_libexec_PROGRAMS)'; test -n "$(unit_editor_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(unit_editor_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(unit_editor_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(unit_editor_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(unit_editor_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-unit_editor_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(unit_editor_libexec_PROGRAMS)'; test -n "$(unit_editor_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(unit_editor_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(unit_editor_libexecdir)" && rm -f $$files
+
+clean-unit_editor_libexecPROGRAMS:
+ @list='$(unit_editor_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-van_gogh_lic_libexecPROGRAMS: $(van_gogh_lic_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(van_gogh_lic_libexec_PROGRAMS)'; test -n "$(van_gogh_lic_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(van_gogh_lic_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(van_gogh_lic_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(van_gogh_lic_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(van_gogh_lic_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-van_gogh_lic_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(van_gogh_lic_libexec_PROGRAMS)'; test -n "$(van_gogh_lic_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(van_gogh_lic_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(van_gogh_lic_libexecdir)" && rm -f $$files
+
+clean-van_gogh_lic_libexecPROGRAMS:
+ @list='$(van_gogh_lic_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-warp_libexecPROGRAMS: $(warp_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(warp_libexec_PROGRAMS)'; test -n "$(warp_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(warp_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(warp_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(warp_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(warp_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-warp_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(warp_libexec_PROGRAMS)'; test -n "$(warp_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(warp_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(warp_libexecdir)" && rm -f $$files
+
+clean-warp_libexecPROGRAMS:
+ @list='$(warp_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-wavelet_decompose_libexecPROGRAMS: $(wavelet_decompose_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(wavelet_decompose_libexec_PROGRAMS)'; test -n "$(wavelet_decompose_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(wavelet_decompose_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(wavelet_decompose_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(wavelet_decompose_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(wavelet_decompose_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-wavelet_decompose_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(wavelet_decompose_libexec_PROGRAMS)'; test -n "$(wavelet_decompose_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(wavelet_decompose_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(wavelet_decompose_libexecdir)" && rm -f $$files
+
+clean-wavelet_decompose_libexecPROGRAMS:
+ @list='$(wavelet_decompose_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-web_browser_libexecPROGRAMS: $(web_browser_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(web_browser_libexec_PROGRAMS)'; test -n "$(web_browser_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(web_browser_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(web_browser_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(web_browser_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(web_browser_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-web_browser_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(web_browser_libexec_PROGRAMS)'; test -n "$(web_browser_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(web_browser_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(web_browser_libexecdir)" && rm -f $$files
+
+clean-web_browser_libexecPROGRAMS:
+ @list='$(web_browser_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-web_page_libexecPROGRAMS: $(web_page_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(web_page_libexec_PROGRAMS)'; test -n "$(web_page_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(web_page_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(web_page_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(web_page_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(web_page_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-web_page_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(web_page_libexec_PROGRAMS)'; test -n "$(web_page_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(web_page_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(web_page_libexecdir)" && rm -f $$files
+
+clean-web_page_libexecPROGRAMS:
+ @list='$(web_page_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+align-layers$(EXEEXT): $(align_layers_OBJECTS) $(align_layers_DEPENDENCIES) $(EXTRA_align_layers_DEPENDENCIES)
+ @rm -f align-layers$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(align_layers_OBJECTS) $(align_layers_LDADD) $(LIBS)
+
+animation-optimize$(EXEEXT): $(animation_optimize_OBJECTS) $(animation_optimize_DEPENDENCIES) $(EXTRA_animation_optimize_DEPENDENCIES)
+ @rm -f animation-optimize$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(animation_optimize_OBJECTS) $(animation_optimize_LDADD) $(LIBS)
+
+animation-play$(EXEEXT): $(animation_play_OBJECTS) $(animation_play_DEPENDENCIES) $(EXTRA_animation_play_DEPENDENCIES)
+ @rm -f animation-play$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(animation_play_OBJECTS) $(animation_play_LDADD) $(LIBS)
+
+blinds$(EXEEXT): $(blinds_OBJECTS) $(blinds_DEPENDENCIES) $(EXTRA_blinds_DEPENDENCIES)
+ @rm -f blinds$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(blinds_OBJECTS) $(blinds_LDADD) $(LIBS)
+
+blur$(EXEEXT): $(blur_OBJECTS) $(blur_DEPENDENCIES) $(EXTRA_blur_DEPENDENCIES)
+ @rm -f blur$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(blur_OBJECTS) $(blur_LDADD) $(LIBS)
+
+border-average$(EXEEXT): $(border_average_OBJECTS) $(border_average_DEPENDENCIES) $(EXTRA_border_average_DEPENDENCIES)
+ @rm -f border-average$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(border_average_OBJECTS) $(border_average_LDADD) $(LIBS)
+
+busy-dialog$(EXEEXT): $(busy_dialog_OBJECTS) $(busy_dialog_DEPENDENCIES) $(EXTRA_busy_dialog_DEPENDENCIES)
+ @rm -f busy-dialog$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(busy_dialog_OBJECTS) $(busy_dialog_LDADD) $(LIBS)
+
+cartoon$(EXEEXT): $(cartoon_OBJECTS) $(cartoon_DEPENDENCIES) $(EXTRA_cartoon_DEPENDENCIES)
+ @rm -f cartoon$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cartoon_OBJECTS) $(cartoon_LDADD) $(LIBS)
+
+checkerboard$(EXEEXT): $(checkerboard_OBJECTS) $(checkerboard_DEPENDENCIES) $(EXTRA_checkerboard_DEPENDENCIES)
+ @rm -f checkerboard$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(checkerboard_OBJECTS) $(checkerboard_LDADD) $(LIBS)
+
+cml-explorer$(EXEEXT): $(cml_explorer_OBJECTS) $(cml_explorer_DEPENDENCIES) $(EXTRA_cml_explorer_DEPENDENCIES)
+ @rm -f cml-explorer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(cml_explorer_OBJECTS) $(cml_explorer_LDADD) $(LIBS)
+
+color-cube-analyze$(EXEEXT): $(color_cube_analyze_OBJECTS) $(color_cube_analyze_DEPENDENCIES) $(EXTRA_color_cube_analyze_DEPENDENCIES)
+ @rm -f color-cube-analyze$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(color_cube_analyze_OBJECTS) $(color_cube_analyze_LDADD) $(LIBS)
+
+color-enhance$(EXEEXT): $(color_enhance_OBJECTS) $(color_enhance_DEPENDENCIES) $(EXTRA_color_enhance_DEPENDENCIES)
+ @rm -f color-enhance$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(color_enhance_OBJECTS) $(color_enhance_LDADD) $(LIBS)
+
+colorify$(EXEEXT): $(colorify_OBJECTS) $(colorify_DEPENDENCIES) $(EXTRA_colorify_DEPENDENCIES)
+ @rm -f colorify$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(colorify_OBJECTS) $(colorify_LDADD) $(LIBS)
+
+colormap-remap$(EXEEXT): $(colormap_remap_OBJECTS) $(colormap_remap_DEPENDENCIES) $(EXTRA_colormap_remap_DEPENDENCIES)
+ @rm -f colormap-remap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(colormap_remap_OBJECTS) $(colormap_remap_LDADD) $(LIBS)
+
+compose$(EXEEXT): $(compose_OBJECTS) $(compose_DEPENDENCIES) $(EXTRA_compose_DEPENDENCIES)
+ @rm -f compose$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(compose_OBJECTS) $(compose_LDADD) $(LIBS)
+
+contrast-retinex$(EXEEXT): $(contrast_retinex_OBJECTS) $(contrast_retinex_DEPENDENCIES) $(EXTRA_contrast_retinex_DEPENDENCIES)
+ @rm -f contrast-retinex$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrast_retinex_OBJECTS) $(contrast_retinex_LDADD) $(LIBS)
+
+crop-zealous$(EXEEXT): $(crop_zealous_OBJECTS) $(crop_zealous_DEPENDENCIES) $(EXTRA_crop_zealous_DEPENDENCIES)
+ @rm -f crop-zealous$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(crop_zealous_OBJECTS) $(crop_zealous_LDADD) $(LIBS)
+
+curve-bend$(EXEEXT): $(curve_bend_OBJECTS) $(curve_bend_DEPENDENCIES) $(EXTRA_curve_bend_DEPENDENCIES)
+ @rm -f curve-bend$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(curve_bend_OBJECTS) $(curve_bend_LDADD) $(LIBS)
+
+decompose$(EXEEXT): $(decompose_OBJECTS) $(decompose_DEPENDENCIES) $(EXTRA_decompose_DEPENDENCIES)
+ @rm -f decompose$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(decompose_OBJECTS) $(decompose_LDADD) $(LIBS)
+
+depth-merge$(EXEEXT): $(depth_merge_OBJECTS) $(depth_merge_DEPENDENCIES) $(EXTRA_depth_merge_DEPENDENCIES)
+ @rm -f depth-merge$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(depth_merge_OBJECTS) $(depth_merge_LDADD) $(LIBS)
+
+despeckle$(EXEEXT): $(despeckle_OBJECTS) $(despeckle_DEPENDENCIES) $(EXTRA_despeckle_DEPENDENCIES)
+ @rm -f despeckle$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(despeckle_OBJECTS) $(despeckle_LDADD) $(LIBS)
+
+destripe$(EXEEXT): $(destripe_OBJECTS) $(destripe_DEPENDENCIES) $(EXTRA_destripe_DEPENDENCIES)
+ @rm -f destripe$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(destripe_OBJECTS) $(destripe_LDADD) $(LIBS)
+
+edge-dog$(EXEEXT): $(edge_dog_OBJECTS) $(edge_dog_DEPENDENCIES) $(EXTRA_edge_dog_DEPENDENCIES)
+ @rm -f edge-dog$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(edge_dog_OBJECTS) $(edge_dog_LDADD) $(LIBS)
+
+emboss$(EXEEXT): $(emboss_OBJECTS) $(emboss_DEPENDENCIES) $(EXTRA_emboss_DEPENDENCIES)
+ @rm -f emboss$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(emboss_OBJECTS) $(emboss_LDADD) $(LIBS)
+
+file-aa$(EXEEXT): $(file_aa_OBJECTS) $(file_aa_DEPENDENCIES) $(EXTRA_file_aa_DEPENDENCIES)
+ @rm -f file-aa$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_aa_OBJECTS) $(file_aa_LDADD) $(LIBS)
+
+file-cel$(EXEEXT): $(file_cel_OBJECTS) $(file_cel_DEPENDENCIES) $(EXTRA_file_cel_DEPENDENCIES)
+ @rm -f file-cel$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_cel_OBJECTS) $(file_cel_LDADD) $(LIBS)
+
+file-compressor$(EXEEXT): $(file_compressor_OBJECTS) $(file_compressor_DEPENDENCIES) $(EXTRA_file_compressor_DEPENDENCIES)
+ @rm -f file-compressor$(EXEEXT)
+ $(AM_V_CCLD)$(file_compressor_LINK) $(file_compressor_OBJECTS) $(file_compressor_LDADD) $(LIBS)
+
+file-csource$(EXEEXT): $(file_csource_OBJECTS) $(file_csource_DEPENDENCIES) $(EXTRA_file_csource_DEPENDENCIES)
+ @rm -f file-csource$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_csource_OBJECTS) $(file_csource_LDADD) $(LIBS)
+
+file-desktop-link$(EXEEXT): $(file_desktop_link_OBJECTS) $(file_desktop_link_DEPENDENCIES) $(EXTRA_file_desktop_link_DEPENDENCIES)
+ @rm -f file-desktop-link$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_desktop_link_OBJECTS) $(file_desktop_link_LDADD) $(LIBS)
+
+file-dicom$(EXEEXT): $(file_dicom_OBJECTS) $(file_dicom_DEPENDENCIES) $(EXTRA_file_dicom_DEPENDENCIES)
+ @rm -f file-dicom$(EXEEXT)
+ $(AM_V_CCLD)$(file_dicom_LINK) $(file_dicom_OBJECTS) $(file_dicom_LDADD) $(LIBS)
+
+file-gbr$(EXEEXT): $(file_gbr_OBJECTS) $(file_gbr_DEPENDENCIES) $(EXTRA_file_gbr_DEPENDENCIES)
+ @rm -f file-gbr$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_gbr_OBJECTS) $(file_gbr_LDADD) $(LIBS)
+
+file-gegl$(EXEEXT): $(file_gegl_OBJECTS) $(file_gegl_DEPENDENCIES) $(EXTRA_file_gegl_DEPENDENCIES)
+ @rm -f file-gegl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_gegl_OBJECTS) $(file_gegl_LDADD) $(LIBS)
+
+file-gif-load$(EXEEXT): $(file_gif_load_OBJECTS) $(file_gif_load_DEPENDENCIES) $(EXTRA_file_gif_load_DEPENDENCIES)
+ @rm -f file-gif-load$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_gif_load_OBJECTS) $(file_gif_load_LDADD) $(LIBS)
+
+file-gif-save$(EXEEXT): $(file_gif_save_OBJECTS) $(file_gif_save_DEPENDENCIES) $(EXTRA_file_gif_save_DEPENDENCIES)
+ @rm -f file-gif-save$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_gif_save_OBJECTS) $(file_gif_save_LDADD) $(LIBS)
+
+file-gih$(EXEEXT): $(file_gih_OBJECTS) $(file_gih_DEPENDENCIES) $(EXTRA_file_gih_DEPENDENCIES)
+ @rm -f file-gih$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_gih_OBJECTS) $(file_gih_LDADD) $(LIBS)
+
+file-glob$(EXEEXT): $(file_glob_OBJECTS) $(file_glob_DEPENDENCIES) $(EXTRA_file_glob_DEPENDENCIES)
+ @rm -f file-glob$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_glob_OBJECTS) $(file_glob_LDADD) $(LIBS)
+
+file-header$(EXEEXT): $(file_header_OBJECTS) $(file_header_DEPENDENCIES) $(EXTRA_file_header_DEPENDENCIES)
+ @rm -f file-header$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_header_OBJECTS) $(file_header_LDADD) $(LIBS)
+
+file-heif$(EXEEXT): $(file_heif_OBJECTS) $(file_heif_DEPENDENCIES) $(EXTRA_file_heif_DEPENDENCIES)
+ @rm -f file-heif$(EXEEXT)
+ $(AM_V_CCLD)$(file_heif_LINK) $(file_heif_OBJECTS) $(file_heif_LDADD) $(LIBS)
+
+file-html-table$(EXEEXT): $(file_html_table_OBJECTS) $(file_html_table_DEPENDENCIES) $(EXTRA_file_html_table_DEPENDENCIES)
+ @rm -f file-html-table$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_html_table_OBJECTS) $(file_html_table_LDADD) $(LIBS)
+
+file-jp2-load$(EXEEXT): $(file_jp2_load_OBJECTS) $(file_jp2_load_DEPENDENCIES) $(EXTRA_file_jp2_load_DEPENDENCIES)
+ @rm -f file-jp2-load$(EXEEXT)
+ $(AM_V_CCLD)$(file_jp2_load_LINK) $(file_jp2_load_OBJECTS) $(file_jp2_load_LDADD) $(LIBS)
+
+file-jpegxl$(EXEEXT): $(file_jpegxl_OBJECTS) $(file_jpegxl_DEPENDENCIES) $(EXTRA_file_jpegxl_DEPENDENCIES)
+ @rm -f file-jpegxl$(EXEEXT)
+ $(AM_V_CCLD)$(file_jpegxl_LINK) $(file_jpegxl_OBJECTS) $(file_jpegxl_LDADD) $(LIBS)
+
+file-mng$(EXEEXT): $(file_mng_OBJECTS) $(file_mng_DEPENDENCIES) $(EXTRA_file_mng_DEPENDENCIES)
+ @rm -f file-mng$(EXEEXT)
+ $(AM_V_CCLD)$(file_mng_LINK) $(file_mng_OBJECTS) $(file_mng_LDADD) $(LIBS)
+
+file-pat$(EXEEXT): $(file_pat_OBJECTS) $(file_pat_DEPENDENCIES) $(EXTRA_file_pat_DEPENDENCIES)
+ @rm -f file-pat$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_pat_OBJECTS) $(file_pat_LDADD) $(LIBS)
+
+file-pcx$(EXEEXT): $(file_pcx_OBJECTS) $(file_pcx_DEPENDENCIES) $(EXTRA_file_pcx_DEPENDENCIES)
+ @rm -f file-pcx$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_pcx_OBJECTS) $(file_pcx_LDADD) $(LIBS)
+
+file-pdf-load$(EXEEXT): $(file_pdf_load_OBJECTS) $(file_pdf_load_DEPENDENCIES) $(EXTRA_file_pdf_load_DEPENDENCIES)
+ @rm -f file-pdf-load$(EXEEXT)
+ $(AM_V_CCLD)$(file_pdf_load_LINK) $(file_pdf_load_OBJECTS) $(file_pdf_load_LDADD) $(LIBS)
+
+file-pdf-save$(EXEEXT): $(file_pdf_save_OBJECTS) $(file_pdf_save_DEPENDENCIES) $(EXTRA_file_pdf_save_DEPENDENCIES)
+ @rm -f file-pdf-save$(EXEEXT)
+ $(AM_V_CCLD)$(file_pdf_save_LINK) $(file_pdf_save_OBJECTS) $(file_pdf_save_LDADD) $(LIBS)
+
+file-pix$(EXEEXT): $(file_pix_OBJECTS) $(file_pix_DEPENDENCIES) $(EXTRA_file_pix_DEPENDENCIES)
+ @rm -f file-pix$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_pix_OBJECTS) $(file_pix_LDADD) $(LIBS)
+
+file-png$(EXEEXT): $(file_png_OBJECTS) $(file_png_DEPENDENCIES) $(EXTRA_file_png_DEPENDENCIES)
+ @rm -f file-png$(EXEEXT)
+ $(AM_V_CCLD)$(file_png_LINK) $(file_png_OBJECTS) $(file_png_LDADD) $(LIBS)
+
+file-pnm$(EXEEXT): $(file_pnm_OBJECTS) $(file_pnm_DEPENDENCIES) $(EXTRA_file_pnm_DEPENDENCIES)
+ @rm -f file-pnm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_pnm_OBJECTS) $(file_pnm_LDADD) $(LIBS)
+
+file-ps$(EXEEXT): $(file_ps_OBJECTS) $(file_ps_DEPENDENCIES) $(EXTRA_file_ps_DEPENDENCIES)
+ @rm -f file-ps$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_ps_OBJECTS) $(file_ps_LDADD) $(LIBS)
+
+file-psp$(EXEEXT): $(file_psp_OBJECTS) $(file_psp_DEPENDENCIES) $(EXTRA_file_psp_DEPENDENCIES)
+ @rm -f file-psp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_psp_OBJECTS) $(file_psp_LDADD) $(LIBS)
+
+file-raw-data$(EXEEXT): $(file_raw_data_OBJECTS) $(file_raw_data_DEPENDENCIES) $(EXTRA_file_raw_data_DEPENDENCIES)
+ @rm -f file-raw-data$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_raw_data_OBJECTS) $(file_raw_data_LDADD) $(LIBS)
+
+file-sunras$(EXEEXT): $(file_sunras_OBJECTS) $(file_sunras_DEPENDENCIES) $(EXTRA_file_sunras_DEPENDENCIES)
+ @rm -f file-sunras$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_sunras_OBJECTS) $(file_sunras_LDADD) $(LIBS)
+
+file-svg$(EXEEXT): $(file_svg_OBJECTS) $(file_svg_DEPENDENCIES) $(EXTRA_file_svg_DEPENDENCIES)
+ @rm -f file-svg$(EXEEXT)
+ $(AM_V_CCLD)$(file_svg_LINK) $(file_svg_OBJECTS) $(file_svg_LDADD) $(LIBS)
+
+file-tga$(EXEEXT): $(file_tga_OBJECTS) $(file_tga_DEPENDENCIES) $(EXTRA_file_tga_DEPENDENCIES)
+ @rm -f file-tga$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_tga_OBJECTS) $(file_tga_LDADD) $(LIBS)
+
+file-wmf$(EXEEXT): $(file_wmf_OBJECTS) $(file_wmf_DEPENDENCIES) $(EXTRA_file_wmf_DEPENDENCIES)
+ @rm -f file-wmf$(EXEEXT)
+ $(AM_V_CCLD)$(file_wmf_LINK) $(file_wmf_OBJECTS) $(file_wmf_LDADD) $(LIBS)
+
+file-xbm$(EXEEXT): $(file_xbm_OBJECTS) $(file_xbm_DEPENDENCIES) $(EXTRA_file_xbm_DEPENDENCIES)
+ @rm -f file-xbm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_xbm_OBJECTS) $(file_xbm_LDADD) $(LIBS)
+
+file-xmc$(EXEEXT): $(file_xmc_OBJECTS) $(file_xmc_DEPENDENCIES) $(EXTRA_file_xmc_DEPENDENCIES)
+ @rm -f file-xmc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_xmc_OBJECTS) $(file_xmc_LDADD) $(LIBS)
+
+file-xpm$(EXEEXT): $(file_xpm_OBJECTS) $(file_xpm_DEPENDENCIES) $(EXTRA_file_xpm_DEPENDENCIES)
+ @rm -f file-xpm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_xpm_OBJECTS) $(file_xpm_LDADD) $(LIBS)
+
+file-xwd$(EXEEXT): $(file_xwd_OBJECTS) $(file_xwd_DEPENDENCIES) $(EXTRA_file_xwd_DEPENDENCIES)
+ @rm -f file-xwd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_xwd_OBJECTS) $(file_xwd_LDADD) $(LIBS)
+
+film$(EXEEXT): $(film_OBJECTS) $(film_DEPENDENCIES) $(EXTRA_film_DEPENDENCIES)
+ @rm -f film$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(film_OBJECTS) $(film_LDADD) $(LIBS)
+
+filter-pack$(EXEEXT): $(filter_pack_OBJECTS) $(filter_pack_DEPENDENCIES) $(EXTRA_filter_pack_DEPENDENCIES)
+ @rm -f filter-pack$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(filter_pack_OBJECTS) $(filter_pack_LDADD) $(LIBS)
+
+fractal-trace$(EXEEXT): $(fractal_trace_OBJECTS) $(fractal_trace_DEPENDENCIES) $(EXTRA_fractal_trace_DEPENDENCIES)
+ @rm -f fractal-trace$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(fractal_trace_OBJECTS) $(fractal_trace_LDADD) $(LIBS)
+
+goat-exercise$(EXEEXT): $(goat_exercise_OBJECTS) $(goat_exercise_DEPENDENCIES) $(EXTRA_goat_exercise_DEPENDENCIES)
+ @rm -f goat-exercise$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(goat_exercise_OBJECTS) $(goat_exercise_LDADD) $(LIBS)
+
+gradient-map$(EXEEXT): $(gradient_map_OBJECTS) $(gradient_map_DEPENDENCIES) $(EXTRA_gradient_map_DEPENDENCIES)
+ @rm -f gradient-map$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gradient_map_OBJECTS) $(gradient_map_LDADD) $(LIBS)
+
+grid$(EXEEXT): $(grid_OBJECTS) $(grid_DEPENDENCIES) $(EXTRA_grid_DEPENDENCIES)
+ @rm -f grid$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(grid_OBJECTS) $(grid_LDADD) $(LIBS)
+
+guillotine$(EXEEXT): $(guillotine_OBJECTS) $(guillotine_DEPENDENCIES) $(EXTRA_guillotine_DEPENDENCIES)
+ @rm -f guillotine$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(guillotine_OBJECTS) $(guillotine_LDADD) $(LIBS)
+
+hot$(EXEEXT): $(hot_OBJECTS) $(hot_DEPENDENCIES) $(EXTRA_hot_DEPENDENCIES)
+ @rm -f hot$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(hot_OBJECTS) $(hot_LDADD) $(LIBS)
+
+jigsaw$(EXEEXT): $(jigsaw_OBJECTS) $(jigsaw_DEPENDENCIES) $(EXTRA_jigsaw_DEPENDENCIES)
+ @rm -f jigsaw$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(jigsaw_OBJECTS) $(jigsaw_LDADD) $(LIBS)
+
+mail$(EXEEXT): $(mail_OBJECTS) $(mail_DEPENDENCIES) $(EXTRA_mail_DEPENDENCIES)
+ @rm -f mail$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(mail_OBJECTS) $(mail_LDADD) $(LIBS)
+
+max-rgb$(EXEEXT): $(max_rgb_OBJECTS) $(max_rgb_DEPENDENCIES) $(EXTRA_max_rgb_DEPENDENCIES)
+ @rm -f max-rgb$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(max_rgb_OBJECTS) $(max_rgb_LDADD) $(LIBS)
+
+nl-filter$(EXEEXT): $(nl_filter_OBJECTS) $(nl_filter_DEPENDENCIES) $(EXTRA_nl_filter_DEPENDENCIES)
+ @rm -f nl-filter$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(nl_filter_OBJECTS) $(nl_filter_LDADD) $(LIBS)
+
+photocopy$(EXEEXT): $(photocopy_OBJECTS) $(photocopy_DEPENDENCIES) $(EXTRA_photocopy_DEPENDENCIES)
+ @rm -f photocopy$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(photocopy_OBJECTS) $(photocopy_LDADD) $(LIBS)
+
+plugin-browser$(EXEEXT): $(plugin_browser_OBJECTS) $(plugin_browser_DEPENDENCIES) $(EXTRA_plugin_browser_DEPENDENCIES)
+ @rm -f plugin-browser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(plugin_browser_OBJECTS) $(plugin_browser_LDADD) $(LIBS)
+
+procedure-browser$(EXEEXT): $(procedure_browser_OBJECTS) $(procedure_browser_DEPENDENCIES) $(EXTRA_procedure_browser_DEPENDENCIES)
+ @rm -f procedure-browser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(procedure_browser_OBJECTS) $(procedure_browser_LDADD) $(LIBS)
+
+qbist$(EXEEXT): $(qbist_OBJECTS) $(qbist_DEPENDENCIES) $(EXTRA_qbist_DEPENDENCIES)
+ @rm -f qbist$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(qbist_OBJECTS) $(qbist_LDADD) $(LIBS)
+
+sample-colorize$(EXEEXT): $(sample_colorize_OBJECTS) $(sample_colorize_DEPENDENCIES) $(EXTRA_sample_colorize_DEPENDENCIES)
+ @rm -f sample-colorize$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sample_colorize_OBJECTS) $(sample_colorize_LDADD) $(LIBS)
+
+sharpen$(EXEEXT): $(sharpen_OBJECTS) $(sharpen_DEPENDENCIES) $(EXTRA_sharpen_DEPENDENCIES)
+ @rm -f sharpen$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sharpen_OBJECTS) $(sharpen_LDADD) $(LIBS)
+
+smooth-palette$(EXEEXT): $(smooth_palette_OBJECTS) $(smooth_palette_DEPENDENCIES) $(EXTRA_smooth_palette_DEPENDENCIES)
+ @rm -f smooth-palette$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(smooth_palette_OBJECTS) $(smooth_palette_LDADD) $(LIBS)
+
+softglow$(EXEEXT): $(softglow_OBJECTS) $(softglow_DEPENDENCIES) $(EXTRA_softglow_DEPENDENCIES)
+ @rm -f softglow$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(softglow_OBJECTS) $(softglow_LDADD) $(LIBS)
+
+sparkle$(EXEEXT): $(sparkle_OBJECTS) $(sparkle_DEPENDENCIES) $(EXTRA_sparkle_DEPENDENCIES)
+ @rm -f sparkle$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sparkle_OBJECTS) $(sparkle_LDADD) $(LIBS)
+
+sphere-designer$(EXEEXT): $(sphere_designer_OBJECTS) $(sphere_designer_DEPENDENCIES) $(EXTRA_sphere_designer_DEPENDENCIES)
+ @rm -f sphere-designer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sphere_designer_OBJECTS) $(sphere_designer_LDADD) $(LIBS)
+
+tile$(EXEEXT): $(tile_OBJECTS) $(tile_DEPENDENCIES) $(EXTRA_tile_DEPENDENCIES)
+ @rm -f tile$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tile_OBJECTS) $(tile_LDADD) $(LIBS)
+
+tile-small$(EXEEXT): $(tile_small_OBJECTS) $(tile_small_DEPENDENCIES) $(EXTRA_tile_small_DEPENDENCIES)
+ @rm -f tile-small$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tile_small_OBJECTS) $(tile_small_LDADD) $(LIBS)
+
+unit-editor$(EXEEXT): $(unit_editor_OBJECTS) $(unit_editor_DEPENDENCIES) $(EXTRA_unit_editor_DEPENDENCIES)
+ @rm -f unit-editor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(unit_editor_OBJECTS) $(unit_editor_LDADD) $(LIBS)
+
+van-gogh-lic$(EXEEXT): $(van_gogh_lic_OBJECTS) $(van_gogh_lic_DEPENDENCIES) $(EXTRA_van_gogh_lic_DEPENDENCIES)
+ @rm -f van-gogh-lic$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(van_gogh_lic_OBJECTS) $(van_gogh_lic_LDADD) $(LIBS)
+
+warp$(EXEEXT): $(warp_OBJECTS) $(warp_DEPENDENCIES) $(EXTRA_warp_DEPENDENCIES)
+ @rm -f warp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(warp_OBJECTS) $(warp_LDADD) $(LIBS)
+
+wavelet-decompose$(EXEEXT): $(wavelet_decompose_OBJECTS) $(wavelet_decompose_DEPENDENCIES) $(EXTRA_wavelet_decompose_DEPENDENCIES)
+ @rm -f wavelet-decompose$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(wavelet_decompose_OBJECTS) $(wavelet_decompose_LDADD) $(LIBS)
+
+web-browser$(EXEEXT): $(web_browser_OBJECTS) $(web_browser_DEPENDENCIES) $(EXTRA_web_browser_DEPENDENCIES)
+ @rm -f web-browser$(EXEEXT)
+ $(AM_V_CCLD)$(web_browser_LINK) $(web_browser_OBJECTS) $(web_browser_LDADD) $(LIBS)
+
+web-page$(EXEEXT): $(web_page_OBJECTS) $(web_page_DEPENDENCIES) $(EXTRA_web_page_DEPENDENCIES)
+ @rm -f web-page$(EXEEXT)
+ $(AM_V_CCLD)$(web_page_LINK) $(web_page_OBJECTS) $(web_page_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/align-layers.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/animation-optimize.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/animation-play.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blinds.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blur.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/border-average.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/busy-dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cartoon.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checkerboard.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cml-explorer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-cube-analyze.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color-enhance.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colorify.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colormap-remap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compose.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/contrast-retinex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crop-zealous.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/curve-bend.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decompose.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/depth-merge.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/despeckle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/destripe.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge-dog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emboss.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-aa.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-cel.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-csource.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-desktop-link.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-gbr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-gegl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-gif-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-gif-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-gih.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-glob.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-header.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-html-table.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-pat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-pcx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-pix.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-pnm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-ps.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-psp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-raw-data.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-sunras.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tga.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-xbm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-xmc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-xpm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-xwd.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_compressor-file-compressor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_dicom-file-dicom.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_heif-file-heif.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_jp2_load-file-jp2-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_jpegxl-file-jpegxl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_mng-file-mng.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_pdf_load-file-pdf-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_pdf_save-file-pdf-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_png-file-png.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_svg-file-svg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_wmf-file-wmf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/film.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter-pack.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fractal-trace.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/goat-exercise.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradient-map.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/grid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/guillotine.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hot.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jigsaw.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/max-rgb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nl-filter.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/photocopy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin-browser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procedure-browser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qbist.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sample-colorize.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sharpen.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smooth-palette.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/softglow.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sparkle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sphere-designer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tile-small.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tile.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit-editor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/van-gogh-lic.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/warp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wavelet-decompose.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_browser-web-browser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/web_page-web-page.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+file_compressor-file-compressor.o: file-compressor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_compressor_CFLAGS) $(CFLAGS) -MT file_compressor-file-compressor.o -MD -MP -MF $(DEPDIR)/file_compressor-file-compressor.Tpo -c -o file_compressor-file-compressor.o `test -f 'file-compressor.c' || echo '$(srcdir)/'`file-compressor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_compressor-file-compressor.Tpo $(DEPDIR)/file_compressor-file-compressor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-compressor.c' object='file_compressor-file-compressor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_compressor_CFLAGS) $(CFLAGS) -c -o file_compressor-file-compressor.o `test -f 'file-compressor.c' || echo '$(srcdir)/'`file-compressor.c
+
+file_compressor-file-compressor.obj: file-compressor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_compressor_CFLAGS) $(CFLAGS) -MT file_compressor-file-compressor.obj -MD -MP -MF $(DEPDIR)/file_compressor-file-compressor.Tpo -c -o file_compressor-file-compressor.obj `if test -f 'file-compressor.c'; then $(CYGPATH_W) 'file-compressor.c'; else $(CYGPATH_W) '$(srcdir)/file-compressor.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_compressor-file-compressor.Tpo $(DEPDIR)/file_compressor-file-compressor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-compressor.c' object='file_compressor-file-compressor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_compressor_CFLAGS) $(CFLAGS) -c -o file_compressor-file-compressor.obj `if test -f 'file-compressor.c'; then $(CYGPATH_W) 'file-compressor.c'; else $(CYGPATH_W) '$(srcdir)/file-compressor.c'; fi`
+
+file_dicom-file-dicom.o: file-dicom.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_dicom_CFLAGS) $(CFLAGS) -MT file_dicom-file-dicom.o -MD -MP -MF $(DEPDIR)/file_dicom-file-dicom.Tpo -c -o file_dicom-file-dicom.o `test -f 'file-dicom.c' || echo '$(srcdir)/'`file-dicom.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_dicom-file-dicom.Tpo $(DEPDIR)/file_dicom-file-dicom.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-dicom.c' object='file_dicom-file-dicom.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_dicom_CFLAGS) $(CFLAGS) -c -o file_dicom-file-dicom.o `test -f 'file-dicom.c' || echo '$(srcdir)/'`file-dicom.c
+
+file_dicom-file-dicom.obj: file-dicom.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_dicom_CFLAGS) $(CFLAGS) -MT file_dicom-file-dicom.obj -MD -MP -MF $(DEPDIR)/file_dicom-file-dicom.Tpo -c -o file_dicom-file-dicom.obj `if test -f 'file-dicom.c'; then $(CYGPATH_W) 'file-dicom.c'; else $(CYGPATH_W) '$(srcdir)/file-dicom.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_dicom-file-dicom.Tpo $(DEPDIR)/file_dicom-file-dicom.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-dicom.c' object='file_dicom-file-dicom.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_dicom_CFLAGS) $(CFLAGS) -c -o file_dicom-file-dicom.obj `if test -f 'file-dicom.c'; then $(CYGPATH_W) 'file-dicom.c'; else $(CYGPATH_W) '$(srcdir)/file-dicom.c'; fi`
+
+file_heif-file-heif.o: file-heif.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_heif_CFLAGS) $(CFLAGS) -MT file_heif-file-heif.o -MD -MP -MF $(DEPDIR)/file_heif-file-heif.Tpo -c -o file_heif-file-heif.o `test -f 'file-heif.c' || echo '$(srcdir)/'`file-heif.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_heif-file-heif.Tpo $(DEPDIR)/file_heif-file-heif.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-heif.c' object='file_heif-file-heif.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_heif_CFLAGS) $(CFLAGS) -c -o file_heif-file-heif.o `test -f 'file-heif.c' || echo '$(srcdir)/'`file-heif.c
+
+file_heif-file-heif.obj: file-heif.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_heif_CFLAGS) $(CFLAGS) -MT file_heif-file-heif.obj -MD -MP -MF $(DEPDIR)/file_heif-file-heif.Tpo -c -o file_heif-file-heif.obj `if test -f 'file-heif.c'; then $(CYGPATH_W) 'file-heif.c'; else $(CYGPATH_W) '$(srcdir)/file-heif.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_heif-file-heif.Tpo $(DEPDIR)/file_heif-file-heif.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-heif.c' object='file_heif-file-heif.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_heif_CFLAGS) $(CFLAGS) -c -o file_heif-file-heif.obj `if test -f 'file-heif.c'; then $(CYGPATH_W) 'file-heif.c'; else $(CYGPATH_W) '$(srcdir)/file-heif.c'; fi`
+
+file_jp2_load-file-jp2-load.o: file-jp2-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jp2_load_CFLAGS) $(CFLAGS) -MT file_jp2_load-file-jp2-load.o -MD -MP -MF $(DEPDIR)/file_jp2_load-file-jp2-load.Tpo -c -o file_jp2_load-file-jp2-load.o `test -f 'file-jp2-load.c' || echo '$(srcdir)/'`file-jp2-load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_jp2_load-file-jp2-load.Tpo $(DEPDIR)/file_jp2_load-file-jp2-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-jp2-load.c' object='file_jp2_load-file-jp2-load.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jp2_load_CFLAGS) $(CFLAGS) -c -o file_jp2_load-file-jp2-load.o `test -f 'file-jp2-load.c' || echo '$(srcdir)/'`file-jp2-load.c
+
+file_jp2_load-file-jp2-load.obj: file-jp2-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jp2_load_CFLAGS) $(CFLAGS) -MT file_jp2_load-file-jp2-load.obj -MD -MP -MF $(DEPDIR)/file_jp2_load-file-jp2-load.Tpo -c -o file_jp2_load-file-jp2-load.obj `if test -f 'file-jp2-load.c'; then $(CYGPATH_W) 'file-jp2-load.c'; else $(CYGPATH_W) '$(srcdir)/file-jp2-load.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_jp2_load-file-jp2-load.Tpo $(DEPDIR)/file_jp2_load-file-jp2-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-jp2-load.c' object='file_jp2_load-file-jp2-load.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jp2_load_CFLAGS) $(CFLAGS) -c -o file_jp2_load-file-jp2-load.obj `if test -f 'file-jp2-load.c'; then $(CYGPATH_W) 'file-jp2-load.c'; else $(CYGPATH_W) '$(srcdir)/file-jp2-load.c'; fi`
+
+file_jpegxl-file-jpegxl.o: file-jpegxl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jpegxl_CFLAGS) $(CFLAGS) -MT file_jpegxl-file-jpegxl.o -MD -MP -MF $(DEPDIR)/file_jpegxl-file-jpegxl.Tpo -c -o file_jpegxl-file-jpegxl.o `test -f 'file-jpegxl.c' || echo '$(srcdir)/'`file-jpegxl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_jpegxl-file-jpegxl.Tpo $(DEPDIR)/file_jpegxl-file-jpegxl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-jpegxl.c' object='file_jpegxl-file-jpegxl.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jpegxl_CFLAGS) $(CFLAGS) -c -o file_jpegxl-file-jpegxl.o `test -f 'file-jpegxl.c' || echo '$(srcdir)/'`file-jpegxl.c
+
+file_jpegxl-file-jpegxl.obj: file-jpegxl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jpegxl_CFLAGS) $(CFLAGS) -MT file_jpegxl-file-jpegxl.obj -MD -MP -MF $(DEPDIR)/file_jpegxl-file-jpegxl.Tpo -c -o file_jpegxl-file-jpegxl.obj `if test -f 'file-jpegxl.c'; then $(CYGPATH_W) 'file-jpegxl.c'; else $(CYGPATH_W) '$(srcdir)/file-jpegxl.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_jpegxl-file-jpegxl.Tpo $(DEPDIR)/file_jpegxl-file-jpegxl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-jpegxl.c' object='file_jpegxl-file-jpegxl.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_jpegxl_CFLAGS) $(CFLAGS) -c -o file_jpegxl-file-jpegxl.obj `if test -f 'file-jpegxl.c'; then $(CYGPATH_W) 'file-jpegxl.c'; else $(CYGPATH_W) '$(srcdir)/file-jpegxl.c'; fi`
+
+file_mng-file-mng.o: file-mng.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_mng_CFLAGS) $(CFLAGS) -MT file_mng-file-mng.o -MD -MP -MF $(DEPDIR)/file_mng-file-mng.Tpo -c -o file_mng-file-mng.o `test -f 'file-mng.c' || echo '$(srcdir)/'`file-mng.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_mng-file-mng.Tpo $(DEPDIR)/file_mng-file-mng.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-mng.c' object='file_mng-file-mng.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_mng_CFLAGS) $(CFLAGS) -c -o file_mng-file-mng.o `test -f 'file-mng.c' || echo '$(srcdir)/'`file-mng.c
+
+file_mng-file-mng.obj: file-mng.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_mng_CFLAGS) $(CFLAGS) -MT file_mng-file-mng.obj -MD -MP -MF $(DEPDIR)/file_mng-file-mng.Tpo -c -o file_mng-file-mng.obj `if test -f 'file-mng.c'; then $(CYGPATH_W) 'file-mng.c'; else $(CYGPATH_W) '$(srcdir)/file-mng.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_mng-file-mng.Tpo $(DEPDIR)/file_mng-file-mng.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-mng.c' object='file_mng-file-mng.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_mng_CFLAGS) $(CFLAGS) -c -o file_mng-file-mng.obj `if test -f 'file-mng.c'; then $(CYGPATH_W) 'file-mng.c'; else $(CYGPATH_W) '$(srcdir)/file-mng.c'; fi`
+
+file_pdf_load-file-pdf-load.o: file-pdf-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_load_CFLAGS) $(CFLAGS) -MT file_pdf_load-file-pdf-load.o -MD -MP -MF $(DEPDIR)/file_pdf_load-file-pdf-load.Tpo -c -o file_pdf_load-file-pdf-load.o `test -f 'file-pdf-load.c' || echo '$(srcdir)/'`file-pdf-load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_pdf_load-file-pdf-load.Tpo $(DEPDIR)/file_pdf_load-file-pdf-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-pdf-load.c' object='file_pdf_load-file-pdf-load.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_load_CFLAGS) $(CFLAGS) -c -o file_pdf_load-file-pdf-load.o `test -f 'file-pdf-load.c' || echo '$(srcdir)/'`file-pdf-load.c
+
+file_pdf_load-file-pdf-load.obj: file-pdf-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_load_CFLAGS) $(CFLAGS) -MT file_pdf_load-file-pdf-load.obj -MD -MP -MF $(DEPDIR)/file_pdf_load-file-pdf-load.Tpo -c -o file_pdf_load-file-pdf-load.obj `if test -f 'file-pdf-load.c'; then $(CYGPATH_W) 'file-pdf-load.c'; else $(CYGPATH_W) '$(srcdir)/file-pdf-load.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_pdf_load-file-pdf-load.Tpo $(DEPDIR)/file_pdf_load-file-pdf-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-pdf-load.c' object='file_pdf_load-file-pdf-load.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_load_CFLAGS) $(CFLAGS) -c -o file_pdf_load-file-pdf-load.obj `if test -f 'file-pdf-load.c'; then $(CYGPATH_W) 'file-pdf-load.c'; else $(CYGPATH_W) '$(srcdir)/file-pdf-load.c'; fi`
+
+file_pdf_save-file-pdf-save.o: file-pdf-save.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_save_CFLAGS) $(CFLAGS) -MT file_pdf_save-file-pdf-save.o -MD -MP -MF $(DEPDIR)/file_pdf_save-file-pdf-save.Tpo -c -o file_pdf_save-file-pdf-save.o `test -f 'file-pdf-save.c' || echo '$(srcdir)/'`file-pdf-save.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_pdf_save-file-pdf-save.Tpo $(DEPDIR)/file_pdf_save-file-pdf-save.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-pdf-save.c' object='file_pdf_save-file-pdf-save.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_save_CFLAGS) $(CFLAGS) -c -o file_pdf_save-file-pdf-save.o `test -f 'file-pdf-save.c' || echo '$(srcdir)/'`file-pdf-save.c
+
+file_pdf_save-file-pdf-save.obj: file-pdf-save.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_save_CFLAGS) $(CFLAGS) -MT file_pdf_save-file-pdf-save.obj -MD -MP -MF $(DEPDIR)/file_pdf_save-file-pdf-save.Tpo -c -o file_pdf_save-file-pdf-save.obj `if test -f 'file-pdf-save.c'; then $(CYGPATH_W) 'file-pdf-save.c'; else $(CYGPATH_W) '$(srcdir)/file-pdf-save.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_pdf_save-file-pdf-save.Tpo $(DEPDIR)/file_pdf_save-file-pdf-save.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-pdf-save.c' object='file_pdf_save-file-pdf-save.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_pdf_save_CFLAGS) $(CFLAGS) -c -o file_pdf_save-file-pdf-save.obj `if test -f 'file-pdf-save.c'; then $(CYGPATH_W) 'file-pdf-save.c'; else $(CYGPATH_W) '$(srcdir)/file-pdf-save.c'; fi`
+
+file_png-file-png.o: file-png.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_png_CFLAGS) $(CFLAGS) -MT file_png-file-png.o -MD -MP -MF $(DEPDIR)/file_png-file-png.Tpo -c -o file_png-file-png.o `test -f 'file-png.c' || echo '$(srcdir)/'`file-png.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_png-file-png.Tpo $(DEPDIR)/file_png-file-png.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-png.c' object='file_png-file-png.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_png_CFLAGS) $(CFLAGS) -c -o file_png-file-png.o `test -f 'file-png.c' || echo '$(srcdir)/'`file-png.c
+
+file_png-file-png.obj: file-png.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_png_CFLAGS) $(CFLAGS) -MT file_png-file-png.obj -MD -MP -MF $(DEPDIR)/file_png-file-png.Tpo -c -o file_png-file-png.obj `if test -f 'file-png.c'; then $(CYGPATH_W) 'file-png.c'; else $(CYGPATH_W) '$(srcdir)/file-png.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_png-file-png.Tpo $(DEPDIR)/file_png-file-png.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-png.c' object='file_png-file-png.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_png_CFLAGS) $(CFLAGS) -c -o file_png-file-png.obj `if test -f 'file-png.c'; then $(CYGPATH_W) 'file-png.c'; else $(CYGPATH_W) '$(srcdir)/file-png.c'; fi`
+
+file_svg-file-svg.o: file-svg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_svg_CFLAGS) $(CFLAGS) -MT file_svg-file-svg.o -MD -MP -MF $(DEPDIR)/file_svg-file-svg.Tpo -c -o file_svg-file-svg.o `test -f 'file-svg.c' || echo '$(srcdir)/'`file-svg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_svg-file-svg.Tpo $(DEPDIR)/file_svg-file-svg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-svg.c' object='file_svg-file-svg.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_svg_CFLAGS) $(CFLAGS) -c -o file_svg-file-svg.o `test -f 'file-svg.c' || echo '$(srcdir)/'`file-svg.c
+
+file_svg-file-svg.obj: file-svg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_svg_CFLAGS) $(CFLAGS) -MT file_svg-file-svg.obj -MD -MP -MF $(DEPDIR)/file_svg-file-svg.Tpo -c -o file_svg-file-svg.obj `if test -f 'file-svg.c'; then $(CYGPATH_W) 'file-svg.c'; else $(CYGPATH_W) '$(srcdir)/file-svg.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_svg-file-svg.Tpo $(DEPDIR)/file_svg-file-svg.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-svg.c' object='file_svg-file-svg.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_svg_CFLAGS) $(CFLAGS) -c -o file_svg-file-svg.obj `if test -f 'file-svg.c'; then $(CYGPATH_W) 'file-svg.c'; else $(CYGPATH_W) '$(srcdir)/file-svg.c'; fi`
+
+file_wmf-file-wmf.o: file-wmf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_wmf_CFLAGS) $(CFLAGS) -MT file_wmf-file-wmf.o -MD -MP -MF $(DEPDIR)/file_wmf-file-wmf.Tpo -c -o file_wmf-file-wmf.o `test -f 'file-wmf.c' || echo '$(srcdir)/'`file-wmf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_wmf-file-wmf.Tpo $(DEPDIR)/file_wmf-file-wmf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-wmf.c' object='file_wmf-file-wmf.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_wmf_CFLAGS) $(CFLAGS) -c -o file_wmf-file-wmf.o `test -f 'file-wmf.c' || echo '$(srcdir)/'`file-wmf.c
+
+file_wmf-file-wmf.obj: file-wmf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_wmf_CFLAGS) $(CFLAGS) -MT file_wmf-file-wmf.obj -MD -MP -MF $(DEPDIR)/file_wmf-file-wmf.Tpo -c -o file_wmf-file-wmf.obj `if test -f 'file-wmf.c'; then $(CYGPATH_W) 'file-wmf.c'; else $(CYGPATH_W) '$(srcdir)/file-wmf.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_wmf-file-wmf.Tpo $(DEPDIR)/file_wmf-file-wmf.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='file-wmf.c' object='file_wmf-file-wmf.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_wmf_CFLAGS) $(CFLAGS) -c -o file_wmf-file-wmf.obj `if test -f 'file-wmf.c'; then $(CYGPATH_W) 'file-wmf.c'; else $(CYGPATH_W) '$(srcdir)/file-wmf.c'; fi`
+
+web_browser-web-browser.o: web-browser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(web_browser_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT web_browser-web-browser.o -MD -MP -MF $(DEPDIR)/web_browser-web-browser.Tpo -c -o web_browser-web-browser.o `test -f 'web-browser.c' || echo '$(srcdir)/'`web-browser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/web_browser-web-browser.Tpo $(DEPDIR)/web_browser-web-browser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='web-browser.c' object='web_browser-web-browser.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(web_browser_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o web_browser-web-browser.o `test -f 'web-browser.c' || echo '$(srcdir)/'`web-browser.c
+
+web_browser-web-browser.obj: web-browser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(web_browser_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT web_browser-web-browser.obj -MD -MP -MF $(DEPDIR)/web_browser-web-browser.Tpo -c -o web_browser-web-browser.obj `if test -f 'web-browser.c'; then $(CYGPATH_W) 'web-browser.c'; else $(CYGPATH_W) '$(srcdir)/web-browser.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/web_browser-web-browser.Tpo $(DEPDIR)/web_browser-web-browser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='web-browser.c' object='web_browser-web-browser.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(web_browser_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o web_browser-web-browser.obj `if test -f 'web-browser.c'; then $(CYGPATH_W) 'web-browser.c'; else $(CYGPATH_W) '$(srcdir)/web-browser.c'; fi`
+
+web_page-web-page.o: web-page.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(web_page_CFLAGS) $(CFLAGS) -MT web_page-web-page.o -MD -MP -MF $(DEPDIR)/web_page-web-page.Tpo -c -o web_page-web-page.o `test -f 'web-page.c' || echo '$(srcdir)/'`web-page.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/web_page-web-page.Tpo $(DEPDIR)/web_page-web-page.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='web-page.c' object='web_page-web-page.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(web_page_CFLAGS) $(CFLAGS) -c -o web_page-web-page.o `test -f 'web-page.c' || echo '$(srcdir)/'`web-page.c
+
+web_page-web-page.obj: web-page.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(web_page_CFLAGS) $(CFLAGS) -MT web_page-web-page.obj -MD -MP -MF $(DEPDIR)/web_page-web-page.Tpo -c -o web_page-web-page.obj `if test -f 'web-page.c'; then $(CYGPATH_W) 'web-page.c'; else $(CYGPATH_W) '$(srcdir)/web-page.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/web_page-web-page.Tpo $(DEPDIR)/web_page-web-page.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='web-page.c' object='web_page-web-page.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(web_page_CFLAGS) $(CFLAGS) -c -o web_page-web-page.obj `if test -f 'web-page.c'; then $(CYGPATH_W) 'web-page.c'; else $(CYGPATH_W) '$(srcdir)/web-page.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(align_layers_libexecdir)" "$(DESTDIR)$(animation_optimize_libexecdir)" "$(DESTDIR)$(animation_play_libexecdir)" "$(DESTDIR)$(blinds_libexecdir)" "$(DESTDIR)$(blur_libexecdir)" "$(DESTDIR)$(border_average_libexecdir)" "$(DESTDIR)$(busy_dialog_libexecdir)" "$(DESTDIR)$(cartoon_libexecdir)" "$(DESTDIR)$(checkerboard_libexecdir)" "$(DESTDIR)$(cml_explorer_libexecdir)" "$(DESTDIR)$(color_cube_analyze_libexecdir)" "$(DESTDIR)$(color_enhance_libexecdir)" "$(DESTDIR)$(colorify_libexecdir)" "$(DESTDIR)$(colormap_remap_libexecdir)" "$(DESTDIR)$(compose_libexecdir)" "$(DESTDIR)$(contrast_retinex_libexecdir)" "$(DESTDIR)$(crop_zealous_libexecdir)" "$(DESTDIR)$(curve_bend_libexecdir)" "$(DESTDIR)$(decompose_libexecdir)" "$(DESTDIR)$(depth_merge_libexecdir)" "$(DESTDIR)$(despeckle_libexecdir)" "$(DESTDIR)$(destripe_libexecdir)" "$(DESTDIR)$(edge_dog_libexecdir)" "$(DESTDIR)$(emboss_libexecdir)" "$(DESTDIR)$(file_aa_libexecdir)" "$(DESTDIR)$(file_cel_libexecdir)" "$(DESTDIR)$(file_compressor_libexecdir)" "$(DESTDIR)$(file_csource_libexecdir)" "$(DESTDIR)$(file_desktop_link_libexecdir)" "$(DESTDIR)$(file_dicom_libexecdir)" "$(DESTDIR)$(file_gbr_libexecdir)" "$(DESTDIR)$(file_gegl_libexecdir)" "$(DESTDIR)$(file_gif_load_libexecdir)" "$(DESTDIR)$(file_gif_save_libexecdir)" "$(DESTDIR)$(file_gih_libexecdir)" "$(DESTDIR)$(file_glob_libexecdir)" "$(DESTDIR)$(file_header_libexecdir)" "$(DESTDIR)$(file_heif_libexecdir)" "$(DESTDIR)$(file_html_table_libexecdir)" "$(DESTDIR)$(file_jp2_load_libexecdir)" "$(DESTDIR)$(file_jpegxl_libexecdir)" "$(DESTDIR)$(file_mng_libexecdir)" "$(DESTDIR)$(file_pat_libexecdir)" "$(DESTDIR)$(file_pcx_libexecdir)" "$(DESTDIR)$(file_pdf_load_libexecdir)" "$(DESTDIR)$(file_pdf_save_libexecdir)" "$(DESTDIR)$(file_pix_libexecdir)" "$(DESTDIR)$(file_png_libexecdir)" "$(DESTDIR)$(file_pnm_libexecdir)" "$(DESTDIR)$(file_ps_libexecdir)" "$(DESTDIR)$(file_psp_libexecdir)" "$(DESTDIR)$(file_raw_data_libexecdir)" "$(DESTDIR)$(file_sunras_libexecdir)" "$(DESTDIR)$(file_svg_libexecdir)" "$(DESTDIR)$(file_tga_libexecdir)" "$(DESTDIR)$(file_wmf_libexecdir)" "$(DESTDIR)$(file_xbm_libexecdir)" "$(DESTDIR)$(file_xmc_libexecdir)" "$(DESTDIR)$(file_xpm_libexecdir)" "$(DESTDIR)$(file_xwd_libexecdir)" "$(DESTDIR)$(film_libexecdir)" "$(DESTDIR)$(filter_pack_libexecdir)" "$(DESTDIR)$(fractal_trace_libexecdir)" "$(DESTDIR)$(goat_exercise_libexecdir)" "$(DESTDIR)$(gradient_map_libexecdir)" "$(DESTDIR)$(grid_libexecdir)" "$(DESTDIR)$(guillotine_libexecdir)" "$(DESTDIR)$(hot_libexecdir)" "$(DESTDIR)$(jigsaw_libexecdir)" "$(DESTDIR)$(mail_libexecdir)" "$(DESTDIR)$(max_rgb_libexecdir)" "$(DESTDIR)$(nl_filter_libexecdir)" "$(DESTDIR)$(photocopy_libexecdir)" "$(DESTDIR)$(plugin_browser_libexecdir)" "$(DESTDIR)$(procedure_browser_libexecdir)" "$(DESTDIR)$(qbist_libexecdir)" "$(DESTDIR)$(sample_colorize_libexecdir)" "$(DESTDIR)$(sharpen_libexecdir)" "$(DESTDIR)$(smooth_palette_libexecdir)" "$(DESTDIR)$(softglow_libexecdir)" "$(DESTDIR)$(sparkle_libexecdir)" "$(DESTDIR)$(sphere_designer_libexecdir)" "$(DESTDIR)$(tile_libexecdir)" "$(DESTDIR)$(tile_small_libexecdir)" "$(DESTDIR)$(unit_editor_libexecdir)" "$(DESTDIR)$(van_gogh_lic_libexecdir)" "$(DESTDIR)$(warp_libexecdir)" "$(DESTDIR)$(wavelet_decompose_libexecdir)" "$(DESTDIR)$(web_browser_libexecdir)" "$(DESTDIR)$(web_page_libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-align_layers_libexecPROGRAMS \
+ clean-animation_optimize_libexecPROGRAMS \
+ clean-animation_play_libexecPROGRAMS \
+ clean-blinds_libexecPROGRAMS clean-blur_libexecPROGRAMS \
+ clean-border_average_libexecPROGRAMS \
+ clean-busy_dialog_libexecPROGRAMS \
+ clean-cartoon_libexecPROGRAMS \
+ clean-checkerboard_libexecPROGRAMS \
+ clean-cml_explorer_libexecPROGRAMS \
+ clean-color_cube_analyze_libexecPROGRAMS \
+ clean-color_enhance_libexecPROGRAMS \
+ clean-colorify_libexecPROGRAMS \
+ clean-colormap_remap_libexecPROGRAMS \
+ clean-compose_libexecPROGRAMS \
+ clean-contrast_retinex_libexecPROGRAMS \
+ clean-crop_zealous_libexecPROGRAMS \
+ clean-curve_bend_libexecPROGRAMS \
+ clean-decompose_libexecPROGRAMS \
+ clean-depth_merge_libexecPROGRAMS \
+ clean-despeckle_libexecPROGRAMS clean-destripe_libexecPROGRAMS \
+ clean-edge_dog_libexecPROGRAMS clean-emboss_libexecPROGRAMS \
+ clean-file_aa_libexecPROGRAMS clean-file_cel_libexecPROGRAMS \
+ clean-file_compressor_libexecPROGRAMS \
+ clean-file_csource_libexecPROGRAMS \
+ clean-file_desktop_link_libexecPROGRAMS \
+ clean-file_dicom_libexecPROGRAMS \
+ clean-file_gbr_libexecPROGRAMS clean-file_gegl_libexecPROGRAMS \
+ clean-file_gif_load_libexecPROGRAMS \
+ clean-file_gif_save_libexecPROGRAMS \
+ clean-file_gih_libexecPROGRAMS clean-file_glob_libexecPROGRAMS \
+ clean-file_header_libexecPROGRAMS \
+ clean-file_heif_libexecPROGRAMS \
+ clean-file_html_table_libexecPROGRAMS \
+ clean-file_jp2_load_libexecPROGRAMS \
+ clean-file_jpegxl_libexecPROGRAMS \
+ clean-file_mng_libexecPROGRAMS clean-file_pat_libexecPROGRAMS \
+ clean-file_pcx_libexecPROGRAMS \
+ clean-file_pdf_load_libexecPROGRAMS \
+ clean-file_pdf_save_libexecPROGRAMS \
+ clean-file_pix_libexecPROGRAMS clean-file_png_libexecPROGRAMS \
+ clean-file_pnm_libexecPROGRAMS clean-file_ps_libexecPROGRAMS \
+ clean-file_psp_libexecPROGRAMS \
+ clean-file_raw_data_libexecPROGRAMS \
+ clean-file_sunras_libexecPROGRAMS \
+ clean-file_svg_libexecPROGRAMS clean-file_tga_libexecPROGRAMS \
+ clean-file_wmf_libexecPROGRAMS clean-file_xbm_libexecPROGRAMS \
+ clean-file_xmc_libexecPROGRAMS clean-file_xpm_libexecPROGRAMS \
+ clean-file_xwd_libexecPROGRAMS clean-film_libexecPROGRAMS \
+ clean-filter_pack_libexecPROGRAMS \
+ clean-fractal_trace_libexecPROGRAMS clean-generic \
+ clean-goat_exercise_libexecPROGRAMS \
+ clean-gradient_map_libexecPROGRAMS clean-grid_libexecPROGRAMS \
+ clean-guillotine_libexecPROGRAMS clean-hot_libexecPROGRAMS \
+ clean-jigsaw_libexecPROGRAMS clean-libtool \
+ clean-mail_libexecPROGRAMS clean-max_rgb_libexecPROGRAMS \
+ clean-nl_filter_libexecPROGRAMS \
+ clean-photocopy_libexecPROGRAMS \
+ clean-plugin_browser_libexecPROGRAMS \
+ clean-procedure_browser_libexecPROGRAMS \
+ clean-qbist_libexecPROGRAMS \
+ clean-sample_colorize_libexecPROGRAMS \
+ clean-sharpen_libexecPROGRAMS \
+ clean-smooth_palette_libexecPROGRAMS \
+ clean-softglow_libexecPROGRAMS clean-sparkle_libexecPROGRAMS \
+ clean-sphere_designer_libexecPROGRAMS \
+ clean-tile_libexecPROGRAMS clean-tile_small_libexecPROGRAMS \
+ clean-unit_editor_libexecPROGRAMS \
+ clean-van_gogh_lic_libexecPROGRAMS clean-warp_libexecPROGRAMS \
+ clean-wavelet_decompose_libexecPROGRAMS \
+ clean-web_browser_libexecPROGRAMS \
+ clean-web_page_libexecPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/align-layers.Po
+ -rm -f ./$(DEPDIR)/animation-optimize.Po
+ -rm -f ./$(DEPDIR)/animation-play.Po
+ -rm -f ./$(DEPDIR)/blinds.Po
+ -rm -f ./$(DEPDIR)/blur.Po
+ -rm -f ./$(DEPDIR)/border-average.Po
+ -rm -f ./$(DEPDIR)/busy-dialog.Po
+ -rm -f ./$(DEPDIR)/cartoon.Po
+ -rm -f ./$(DEPDIR)/checkerboard.Po
+ -rm -f ./$(DEPDIR)/cml-explorer.Po
+ -rm -f ./$(DEPDIR)/color-cube-analyze.Po
+ -rm -f ./$(DEPDIR)/color-enhance.Po
+ -rm -f ./$(DEPDIR)/colorify.Po
+ -rm -f ./$(DEPDIR)/colormap-remap.Po
+ -rm -f ./$(DEPDIR)/compose.Po
+ -rm -f ./$(DEPDIR)/contrast-retinex.Po
+ -rm -f ./$(DEPDIR)/crop-zealous.Po
+ -rm -f ./$(DEPDIR)/curve-bend.Po
+ -rm -f ./$(DEPDIR)/decompose.Po
+ -rm -f ./$(DEPDIR)/depth-merge.Po
+ -rm -f ./$(DEPDIR)/despeckle.Po
+ -rm -f ./$(DEPDIR)/destripe.Po
+ -rm -f ./$(DEPDIR)/edge-dog.Po
+ -rm -f ./$(DEPDIR)/emboss.Po
+ -rm -f ./$(DEPDIR)/file-aa.Po
+ -rm -f ./$(DEPDIR)/file-cel.Po
+ -rm -f ./$(DEPDIR)/file-csource.Po
+ -rm -f ./$(DEPDIR)/file-desktop-link.Po
+ -rm -f ./$(DEPDIR)/file-gbr.Po
+ -rm -f ./$(DEPDIR)/file-gegl.Po
+ -rm -f ./$(DEPDIR)/file-gif-load.Po
+ -rm -f ./$(DEPDIR)/file-gif-save.Po
+ -rm -f ./$(DEPDIR)/file-gih.Po
+ -rm -f ./$(DEPDIR)/file-glob.Po
+ -rm -f ./$(DEPDIR)/file-header.Po
+ -rm -f ./$(DEPDIR)/file-html-table.Po
+ -rm -f ./$(DEPDIR)/file-pat.Po
+ -rm -f ./$(DEPDIR)/file-pcx.Po
+ -rm -f ./$(DEPDIR)/file-pix.Po
+ -rm -f ./$(DEPDIR)/file-pnm.Po
+ -rm -f ./$(DEPDIR)/file-ps.Po
+ -rm -f ./$(DEPDIR)/file-psp.Po
+ -rm -f ./$(DEPDIR)/file-raw-data.Po
+ -rm -f ./$(DEPDIR)/file-sunras.Po
+ -rm -f ./$(DEPDIR)/file-tga.Po
+ -rm -f ./$(DEPDIR)/file-xbm.Po
+ -rm -f ./$(DEPDIR)/file-xmc.Po
+ -rm -f ./$(DEPDIR)/file-xpm.Po
+ -rm -f ./$(DEPDIR)/file-xwd.Po
+ -rm -f ./$(DEPDIR)/file_compressor-file-compressor.Po
+ -rm -f ./$(DEPDIR)/file_dicom-file-dicom.Po
+ -rm -f ./$(DEPDIR)/file_heif-file-heif.Po
+ -rm -f ./$(DEPDIR)/file_jp2_load-file-jp2-load.Po
+ -rm -f ./$(DEPDIR)/file_jpegxl-file-jpegxl.Po
+ -rm -f ./$(DEPDIR)/file_mng-file-mng.Po
+ -rm -f ./$(DEPDIR)/file_pdf_load-file-pdf-load.Po
+ -rm -f ./$(DEPDIR)/file_pdf_save-file-pdf-save.Po
+ -rm -f ./$(DEPDIR)/file_png-file-png.Po
+ -rm -f ./$(DEPDIR)/file_svg-file-svg.Po
+ -rm -f ./$(DEPDIR)/file_wmf-file-wmf.Po
+ -rm -f ./$(DEPDIR)/film.Po
+ -rm -f ./$(DEPDIR)/filter-pack.Po
+ -rm -f ./$(DEPDIR)/fractal-trace.Po
+ -rm -f ./$(DEPDIR)/goat-exercise.Po
+ -rm -f ./$(DEPDIR)/gradient-map.Po
+ -rm -f ./$(DEPDIR)/grid.Po
+ -rm -f ./$(DEPDIR)/guillotine.Po
+ -rm -f ./$(DEPDIR)/hot.Po
+ -rm -f ./$(DEPDIR)/jigsaw.Po
+ -rm -f ./$(DEPDIR)/mail.Po
+ -rm -f ./$(DEPDIR)/max-rgb.Po
+ -rm -f ./$(DEPDIR)/nl-filter.Po
+ -rm -f ./$(DEPDIR)/photocopy.Po
+ -rm -f ./$(DEPDIR)/plugin-browser.Po
+ -rm -f ./$(DEPDIR)/procedure-browser.Po
+ -rm -f ./$(DEPDIR)/qbist.Po
+ -rm -f ./$(DEPDIR)/sample-colorize.Po
+ -rm -f ./$(DEPDIR)/sharpen.Po
+ -rm -f ./$(DEPDIR)/smooth-palette.Po
+ -rm -f ./$(DEPDIR)/softglow.Po
+ -rm -f ./$(DEPDIR)/sparkle.Po
+ -rm -f ./$(DEPDIR)/sphere-designer.Po
+ -rm -f ./$(DEPDIR)/tile-small.Po
+ -rm -f ./$(DEPDIR)/tile.Po
+ -rm -f ./$(DEPDIR)/unit-editor.Po
+ -rm -f ./$(DEPDIR)/van-gogh-lic.Po
+ -rm -f ./$(DEPDIR)/warp.Po
+ -rm -f ./$(DEPDIR)/wavelet-decompose.Po
+ -rm -f ./$(DEPDIR)/web_browser-web-browser.Po
+ -rm -f ./$(DEPDIR)/web_page-web-page.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-align_layers_libexecPROGRAMS \
+ install-animation_optimize_libexecPROGRAMS \
+ install-animation_play_libexecPROGRAMS \
+ install-blinds_libexecPROGRAMS install-blur_libexecPROGRAMS \
+ install-border_average_libexecPROGRAMS \
+ install-busy_dialog_libexecPROGRAMS \
+ install-cartoon_libexecPROGRAMS \
+ install-checkerboard_libexecPROGRAMS \
+ install-cml_explorer_libexecPROGRAMS \
+ install-color_cube_analyze_libexecPROGRAMS \
+ install-color_enhance_libexecPROGRAMS \
+ install-colorify_libexecPROGRAMS \
+ install-colormap_remap_libexecPROGRAMS \
+ install-compose_libexecPROGRAMS \
+ install-contrast_retinex_libexecPROGRAMS \
+ install-crop_zealous_libexecPROGRAMS \
+ install-curve_bend_libexecPROGRAMS \
+ install-decompose_libexecPROGRAMS \
+ install-depth_merge_libexecPROGRAMS \
+ install-despeckle_libexecPROGRAMS \
+ install-destripe_libexecPROGRAMS \
+ install-edge_dog_libexecPROGRAMS \
+ install-emboss_libexecPROGRAMS install-file_aa_libexecPROGRAMS \
+ install-file_cel_libexecPROGRAMS \
+ install-file_compressor_libexecPROGRAMS \
+ install-file_csource_libexecPROGRAMS \
+ install-file_desktop_link_libexecPROGRAMS \
+ install-file_dicom_libexecPROGRAMS \
+ install-file_gbr_libexecPROGRAMS \
+ install-file_gegl_libexecPROGRAMS \
+ install-file_gif_load_libexecPROGRAMS \
+ install-file_gif_save_libexecPROGRAMS \
+ install-file_gih_libexecPROGRAMS \
+ install-file_glob_libexecPROGRAMS \
+ install-file_header_libexecPROGRAMS \
+ install-file_heif_libexecPROGRAMS \
+ install-file_html_table_libexecPROGRAMS \
+ install-file_jp2_load_libexecPROGRAMS \
+ install-file_jpegxl_libexecPROGRAMS \
+ install-file_mng_libexecPROGRAMS \
+ install-file_pat_libexecPROGRAMS \
+ install-file_pcx_libexecPROGRAMS \
+ install-file_pdf_load_libexecPROGRAMS \
+ install-file_pdf_save_libexecPROGRAMS \
+ install-file_pix_libexecPROGRAMS \
+ install-file_png_libexecPROGRAMS \
+ install-file_pnm_libexecPROGRAMS \
+ install-file_ps_libexecPROGRAMS \
+ install-file_psp_libexecPROGRAMS \
+ install-file_raw_data_libexecPROGRAMS \
+ install-file_sunras_libexecPROGRAMS \
+ install-file_svg_libexecPROGRAMS \
+ install-file_tga_libexecPROGRAMS \
+ install-file_wmf_libexecPROGRAMS \
+ install-file_xbm_libexecPROGRAMS \
+ install-file_xmc_libexecPROGRAMS \
+ install-file_xpm_libexecPROGRAMS \
+ install-file_xwd_libexecPROGRAMS install-film_libexecPROGRAMS \
+ install-filter_pack_libexecPROGRAMS \
+ install-fractal_trace_libexecPROGRAMS \
+ install-goat_exercise_libexecPROGRAMS \
+ install-gradient_map_libexecPROGRAMS \
+ install-grid_libexecPROGRAMS \
+ install-guillotine_libexecPROGRAMS install-hot_libexecPROGRAMS \
+ install-jigsaw_libexecPROGRAMS install-mail_libexecPROGRAMS \
+ install-max_rgb_libexecPROGRAMS \
+ install-nl_filter_libexecPROGRAMS \
+ install-photocopy_libexecPROGRAMS \
+ install-plugin_browser_libexecPROGRAMS \
+ install-procedure_browser_libexecPROGRAMS \
+ install-qbist_libexecPROGRAMS \
+ install-sample_colorize_libexecPROGRAMS \
+ install-sharpen_libexecPROGRAMS \
+ install-smooth_palette_libexecPROGRAMS \
+ install-softglow_libexecPROGRAMS \
+ install-sparkle_libexecPROGRAMS \
+ install-sphere_designer_libexecPROGRAMS \
+ install-tile_libexecPROGRAMS \
+ install-tile_small_libexecPROGRAMS \
+ install-unit_editor_libexecPROGRAMS \
+ install-van_gogh_lic_libexecPROGRAMS \
+ install-warp_libexecPROGRAMS \
+ install-wavelet_decompose_libexecPROGRAMS \
+ install-web_browser_libexecPROGRAMS \
+ install-web_page_libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/align-layers.Po
+ -rm -f ./$(DEPDIR)/animation-optimize.Po
+ -rm -f ./$(DEPDIR)/animation-play.Po
+ -rm -f ./$(DEPDIR)/blinds.Po
+ -rm -f ./$(DEPDIR)/blur.Po
+ -rm -f ./$(DEPDIR)/border-average.Po
+ -rm -f ./$(DEPDIR)/busy-dialog.Po
+ -rm -f ./$(DEPDIR)/cartoon.Po
+ -rm -f ./$(DEPDIR)/checkerboard.Po
+ -rm -f ./$(DEPDIR)/cml-explorer.Po
+ -rm -f ./$(DEPDIR)/color-cube-analyze.Po
+ -rm -f ./$(DEPDIR)/color-enhance.Po
+ -rm -f ./$(DEPDIR)/colorify.Po
+ -rm -f ./$(DEPDIR)/colormap-remap.Po
+ -rm -f ./$(DEPDIR)/compose.Po
+ -rm -f ./$(DEPDIR)/contrast-retinex.Po
+ -rm -f ./$(DEPDIR)/crop-zealous.Po
+ -rm -f ./$(DEPDIR)/curve-bend.Po
+ -rm -f ./$(DEPDIR)/decompose.Po
+ -rm -f ./$(DEPDIR)/depth-merge.Po
+ -rm -f ./$(DEPDIR)/despeckle.Po
+ -rm -f ./$(DEPDIR)/destripe.Po
+ -rm -f ./$(DEPDIR)/edge-dog.Po
+ -rm -f ./$(DEPDIR)/emboss.Po
+ -rm -f ./$(DEPDIR)/file-aa.Po
+ -rm -f ./$(DEPDIR)/file-cel.Po
+ -rm -f ./$(DEPDIR)/file-csource.Po
+ -rm -f ./$(DEPDIR)/file-desktop-link.Po
+ -rm -f ./$(DEPDIR)/file-gbr.Po
+ -rm -f ./$(DEPDIR)/file-gegl.Po
+ -rm -f ./$(DEPDIR)/file-gif-load.Po
+ -rm -f ./$(DEPDIR)/file-gif-save.Po
+ -rm -f ./$(DEPDIR)/file-gih.Po
+ -rm -f ./$(DEPDIR)/file-glob.Po
+ -rm -f ./$(DEPDIR)/file-header.Po
+ -rm -f ./$(DEPDIR)/file-html-table.Po
+ -rm -f ./$(DEPDIR)/file-pat.Po
+ -rm -f ./$(DEPDIR)/file-pcx.Po
+ -rm -f ./$(DEPDIR)/file-pix.Po
+ -rm -f ./$(DEPDIR)/file-pnm.Po
+ -rm -f ./$(DEPDIR)/file-ps.Po
+ -rm -f ./$(DEPDIR)/file-psp.Po
+ -rm -f ./$(DEPDIR)/file-raw-data.Po
+ -rm -f ./$(DEPDIR)/file-sunras.Po
+ -rm -f ./$(DEPDIR)/file-tga.Po
+ -rm -f ./$(DEPDIR)/file-xbm.Po
+ -rm -f ./$(DEPDIR)/file-xmc.Po
+ -rm -f ./$(DEPDIR)/file-xpm.Po
+ -rm -f ./$(DEPDIR)/file-xwd.Po
+ -rm -f ./$(DEPDIR)/file_compressor-file-compressor.Po
+ -rm -f ./$(DEPDIR)/file_dicom-file-dicom.Po
+ -rm -f ./$(DEPDIR)/file_heif-file-heif.Po
+ -rm -f ./$(DEPDIR)/file_jp2_load-file-jp2-load.Po
+ -rm -f ./$(DEPDIR)/file_jpegxl-file-jpegxl.Po
+ -rm -f ./$(DEPDIR)/file_mng-file-mng.Po
+ -rm -f ./$(DEPDIR)/file_pdf_load-file-pdf-load.Po
+ -rm -f ./$(DEPDIR)/file_pdf_save-file-pdf-save.Po
+ -rm -f ./$(DEPDIR)/file_png-file-png.Po
+ -rm -f ./$(DEPDIR)/file_svg-file-svg.Po
+ -rm -f ./$(DEPDIR)/file_wmf-file-wmf.Po
+ -rm -f ./$(DEPDIR)/film.Po
+ -rm -f ./$(DEPDIR)/filter-pack.Po
+ -rm -f ./$(DEPDIR)/fractal-trace.Po
+ -rm -f ./$(DEPDIR)/goat-exercise.Po
+ -rm -f ./$(DEPDIR)/gradient-map.Po
+ -rm -f ./$(DEPDIR)/grid.Po
+ -rm -f ./$(DEPDIR)/guillotine.Po
+ -rm -f ./$(DEPDIR)/hot.Po
+ -rm -f ./$(DEPDIR)/jigsaw.Po
+ -rm -f ./$(DEPDIR)/mail.Po
+ -rm -f ./$(DEPDIR)/max-rgb.Po
+ -rm -f ./$(DEPDIR)/nl-filter.Po
+ -rm -f ./$(DEPDIR)/photocopy.Po
+ -rm -f ./$(DEPDIR)/plugin-browser.Po
+ -rm -f ./$(DEPDIR)/procedure-browser.Po
+ -rm -f ./$(DEPDIR)/qbist.Po
+ -rm -f ./$(DEPDIR)/sample-colorize.Po
+ -rm -f ./$(DEPDIR)/sharpen.Po
+ -rm -f ./$(DEPDIR)/smooth-palette.Po
+ -rm -f ./$(DEPDIR)/softglow.Po
+ -rm -f ./$(DEPDIR)/sparkle.Po
+ -rm -f ./$(DEPDIR)/sphere-designer.Po
+ -rm -f ./$(DEPDIR)/tile-small.Po
+ -rm -f ./$(DEPDIR)/tile.Po
+ -rm -f ./$(DEPDIR)/unit-editor.Po
+ -rm -f ./$(DEPDIR)/van-gogh-lic.Po
+ -rm -f ./$(DEPDIR)/warp.Po
+ -rm -f ./$(DEPDIR)/wavelet-decompose.Po
+ -rm -f ./$(DEPDIR)/web_browser-web-browser.Po
+ -rm -f ./$(DEPDIR)/web_page-web-page.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-align_layers_libexecPROGRAMS \
+ uninstall-animation_optimize_libexecPROGRAMS \
+ uninstall-animation_play_libexecPROGRAMS \
+ uninstall-blinds_libexecPROGRAMS \
+ uninstall-blur_libexecPROGRAMS \
+ uninstall-border_average_libexecPROGRAMS \
+ uninstall-busy_dialog_libexecPROGRAMS \
+ uninstall-cartoon_libexecPROGRAMS \
+ uninstall-checkerboard_libexecPROGRAMS \
+ uninstall-cml_explorer_libexecPROGRAMS \
+ uninstall-color_cube_analyze_libexecPROGRAMS \
+ uninstall-color_enhance_libexecPROGRAMS \
+ uninstall-colorify_libexecPROGRAMS \
+ uninstall-colormap_remap_libexecPROGRAMS \
+ uninstall-compose_libexecPROGRAMS \
+ uninstall-contrast_retinex_libexecPROGRAMS \
+ uninstall-crop_zealous_libexecPROGRAMS \
+ uninstall-curve_bend_libexecPROGRAMS \
+ uninstall-decompose_libexecPROGRAMS \
+ uninstall-depth_merge_libexecPROGRAMS \
+ uninstall-despeckle_libexecPROGRAMS \
+ uninstall-destripe_libexecPROGRAMS \
+ uninstall-edge_dog_libexecPROGRAMS \
+ uninstall-emboss_libexecPROGRAMS \
+ uninstall-file_aa_libexecPROGRAMS \
+ uninstall-file_cel_libexecPROGRAMS \
+ uninstall-file_compressor_libexecPROGRAMS \
+ uninstall-file_csource_libexecPROGRAMS \
+ uninstall-file_desktop_link_libexecPROGRAMS \
+ uninstall-file_dicom_libexecPROGRAMS \
+ uninstall-file_gbr_libexecPROGRAMS \
+ uninstall-file_gegl_libexecPROGRAMS \
+ uninstall-file_gif_load_libexecPROGRAMS \
+ uninstall-file_gif_save_libexecPROGRAMS \
+ uninstall-file_gih_libexecPROGRAMS \
+ uninstall-file_glob_libexecPROGRAMS \
+ uninstall-file_header_libexecPROGRAMS \
+ uninstall-file_heif_libexecPROGRAMS \
+ uninstall-file_html_table_libexecPROGRAMS \
+ uninstall-file_jp2_load_libexecPROGRAMS \
+ uninstall-file_jpegxl_libexecPROGRAMS \
+ uninstall-file_mng_libexecPROGRAMS \
+ uninstall-file_pat_libexecPROGRAMS \
+ uninstall-file_pcx_libexecPROGRAMS \
+ uninstall-file_pdf_load_libexecPROGRAMS \
+ uninstall-file_pdf_save_libexecPROGRAMS \
+ uninstall-file_pix_libexecPROGRAMS \
+ uninstall-file_png_libexecPROGRAMS \
+ uninstall-file_pnm_libexecPROGRAMS \
+ uninstall-file_ps_libexecPROGRAMS \
+ uninstall-file_psp_libexecPROGRAMS \
+ uninstall-file_raw_data_libexecPROGRAMS \
+ uninstall-file_sunras_libexecPROGRAMS \
+ uninstall-file_svg_libexecPROGRAMS \
+ uninstall-file_tga_libexecPROGRAMS \
+ uninstall-file_wmf_libexecPROGRAMS \
+ uninstall-file_xbm_libexecPROGRAMS \
+ uninstall-file_xmc_libexecPROGRAMS \
+ uninstall-file_xpm_libexecPROGRAMS \
+ uninstall-file_xwd_libexecPROGRAMS \
+ uninstall-film_libexecPROGRAMS \
+ uninstall-filter_pack_libexecPROGRAMS \
+ uninstall-fractal_trace_libexecPROGRAMS \
+ uninstall-goat_exercise_libexecPROGRAMS \
+ uninstall-gradient_map_libexecPROGRAMS \
+ uninstall-grid_libexecPROGRAMS \
+ uninstall-guillotine_libexecPROGRAMS \
+ uninstall-hot_libexecPROGRAMS uninstall-jigsaw_libexecPROGRAMS \
+ uninstall-mail_libexecPROGRAMS \
+ uninstall-max_rgb_libexecPROGRAMS \
+ uninstall-nl_filter_libexecPROGRAMS \
+ uninstall-photocopy_libexecPROGRAMS \
+ uninstall-plugin_browser_libexecPROGRAMS \
+ uninstall-procedure_browser_libexecPROGRAMS \
+ uninstall-qbist_libexecPROGRAMS \
+ uninstall-sample_colorize_libexecPROGRAMS \
+ uninstall-sharpen_libexecPROGRAMS \
+ uninstall-smooth_palette_libexecPROGRAMS \
+ uninstall-softglow_libexecPROGRAMS \
+ uninstall-sparkle_libexecPROGRAMS \
+ uninstall-sphere_designer_libexecPROGRAMS \
+ uninstall-tile_libexecPROGRAMS \
+ uninstall-tile_small_libexecPROGRAMS \
+ uninstall-unit_editor_libexecPROGRAMS \
+ uninstall-van_gogh_lic_libexecPROGRAMS \
+ uninstall-warp_libexecPROGRAMS \
+ uninstall-wavelet_decompose_libexecPROGRAMS \
+ uninstall-web_browser_libexecPROGRAMS \
+ uninstall-web_page_libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-align_layers_libexecPROGRAMS \
+ clean-animation_optimize_libexecPROGRAMS \
+ clean-animation_play_libexecPROGRAMS \
+ clean-blinds_libexecPROGRAMS clean-blur_libexecPROGRAMS \
+ clean-border_average_libexecPROGRAMS \
+ clean-busy_dialog_libexecPROGRAMS \
+ clean-cartoon_libexecPROGRAMS \
+ clean-checkerboard_libexecPROGRAMS \
+ clean-cml_explorer_libexecPROGRAMS \
+ clean-color_cube_analyze_libexecPROGRAMS \
+ clean-color_enhance_libexecPROGRAMS \
+ clean-colorify_libexecPROGRAMS \
+ clean-colormap_remap_libexecPROGRAMS \
+ clean-compose_libexecPROGRAMS \
+ clean-contrast_retinex_libexecPROGRAMS \
+ clean-crop_zealous_libexecPROGRAMS \
+ clean-curve_bend_libexecPROGRAMS \
+ clean-decompose_libexecPROGRAMS \
+ clean-depth_merge_libexecPROGRAMS \
+ clean-despeckle_libexecPROGRAMS clean-destripe_libexecPROGRAMS \
+ clean-edge_dog_libexecPROGRAMS clean-emboss_libexecPROGRAMS \
+ clean-file_aa_libexecPROGRAMS clean-file_cel_libexecPROGRAMS \
+ clean-file_compressor_libexecPROGRAMS \
+ clean-file_csource_libexecPROGRAMS \
+ clean-file_desktop_link_libexecPROGRAMS \
+ clean-file_dicom_libexecPROGRAMS \
+ clean-file_gbr_libexecPROGRAMS clean-file_gegl_libexecPROGRAMS \
+ clean-file_gif_load_libexecPROGRAMS \
+ clean-file_gif_save_libexecPROGRAMS \
+ clean-file_gih_libexecPROGRAMS clean-file_glob_libexecPROGRAMS \
+ clean-file_header_libexecPROGRAMS \
+ clean-file_heif_libexecPROGRAMS \
+ clean-file_html_table_libexecPROGRAMS \
+ clean-file_jp2_load_libexecPROGRAMS \
+ clean-file_jpegxl_libexecPROGRAMS \
+ clean-file_mng_libexecPROGRAMS clean-file_pat_libexecPROGRAMS \
+ clean-file_pcx_libexecPROGRAMS \
+ clean-file_pdf_load_libexecPROGRAMS \
+ clean-file_pdf_save_libexecPROGRAMS \
+ clean-file_pix_libexecPROGRAMS clean-file_png_libexecPROGRAMS \
+ clean-file_pnm_libexecPROGRAMS clean-file_ps_libexecPROGRAMS \
+ clean-file_psp_libexecPROGRAMS \
+ clean-file_raw_data_libexecPROGRAMS \
+ clean-file_sunras_libexecPROGRAMS \
+ clean-file_svg_libexecPROGRAMS clean-file_tga_libexecPROGRAMS \
+ clean-file_wmf_libexecPROGRAMS clean-file_xbm_libexecPROGRAMS \
+ clean-file_xmc_libexecPROGRAMS clean-file_xpm_libexecPROGRAMS \
+ clean-file_xwd_libexecPROGRAMS clean-film_libexecPROGRAMS \
+ clean-filter_pack_libexecPROGRAMS \
+ clean-fractal_trace_libexecPROGRAMS clean-generic \
+ clean-goat_exercise_libexecPROGRAMS \
+ clean-gradient_map_libexecPROGRAMS clean-grid_libexecPROGRAMS \
+ clean-guillotine_libexecPROGRAMS clean-hot_libexecPROGRAMS \
+ clean-jigsaw_libexecPROGRAMS clean-libtool \
+ clean-mail_libexecPROGRAMS clean-max_rgb_libexecPROGRAMS \
+ clean-nl_filter_libexecPROGRAMS \
+ clean-photocopy_libexecPROGRAMS \
+ clean-plugin_browser_libexecPROGRAMS \
+ clean-procedure_browser_libexecPROGRAMS \
+ clean-qbist_libexecPROGRAMS \
+ clean-sample_colorize_libexecPROGRAMS \
+ clean-sharpen_libexecPROGRAMS \
+ clean-smooth_palette_libexecPROGRAMS \
+ clean-softglow_libexecPROGRAMS clean-sparkle_libexecPROGRAMS \
+ clean-sphere_designer_libexecPROGRAMS \
+ clean-tile_libexecPROGRAMS clean-tile_small_libexecPROGRAMS \
+ clean-unit_editor_libexecPROGRAMS \
+ clean-van_gogh_lic_libexecPROGRAMS clean-warp_libexecPROGRAMS \
+ clean-wavelet_decompose_libexecPROGRAMS \
+ clean-web_browser_libexecPROGRAMS \
+ clean-web_page_libexecPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install \
+ install-align_layers_libexecPROGRAMS install-am \
+ install-animation_optimize_libexecPROGRAMS \
+ install-animation_play_libexecPROGRAMS \
+ install-blinds_libexecPROGRAMS install-blur_libexecPROGRAMS \
+ install-border_average_libexecPROGRAMS \
+ install-busy_dialog_libexecPROGRAMS \
+ install-cartoon_libexecPROGRAMS \
+ install-checkerboard_libexecPROGRAMS \
+ install-cml_explorer_libexecPROGRAMS \
+ install-color_cube_analyze_libexecPROGRAMS \
+ install-color_enhance_libexecPROGRAMS \
+ install-colorify_libexecPROGRAMS \
+ install-colormap_remap_libexecPROGRAMS \
+ install-compose_libexecPROGRAMS \
+ install-contrast_retinex_libexecPROGRAMS \
+ install-crop_zealous_libexecPROGRAMS \
+ install-curve_bend_libexecPROGRAMS install-data \
+ install-data-am install-decompose_libexecPROGRAMS \
+ install-depth_merge_libexecPROGRAMS \
+ install-despeckle_libexecPROGRAMS \
+ install-destripe_libexecPROGRAMS install-dvi install-dvi-am \
+ install-edge_dog_libexecPROGRAMS \
+ install-emboss_libexecPROGRAMS install-exec install-exec-am \
+ install-file_aa_libexecPROGRAMS \
+ install-file_cel_libexecPROGRAMS \
+ install-file_compressor_libexecPROGRAMS \
+ install-file_csource_libexecPROGRAMS \
+ install-file_desktop_link_libexecPROGRAMS \
+ install-file_dicom_libexecPROGRAMS \
+ install-file_gbr_libexecPROGRAMS \
+ install-file_gegl_libexecPROGRAMS \
+ install-file_gif_load_libexecPROGRAMS \
+ install-file_gif_save_libexecPROGRAMS \
+ install-file_gih_libexecPROGRAMS \
+ install-file_glob_libexecPROGRAMS \
+ install-file_header_libexecPROGRAMS \
+ install-file_heif_libexecPROGRAMS \
+ install-file_html_table_libexecPROGRAMS \
+ install-file_jp2_load_libexecPROGRAMS \
+ install-file_jpegxl_libexecPROGRAMS \
+ install-file_mng_libexecPROGRAMS \
+ install-file_pat_libexecPROGRAMS \
+ install-file_pcx_libexecPROGRAMS \
+ install-file_pdf_load_libexecPROGRAMS \
+ install-file_pdf_save_libexecPROGRAMS \
+ install-file_pix_libexecPROGRAMS \
+ install-file_png_libexecPROGRAMS \
+ install-file_pnm_libexecPROGRAMS \
+ install-file_ps_libexecPROGRAMS \
+ install-file_psp_libexecPROGRAMS \
+ install-file_raw_data_libexecPROGRAMS \
+ install-file_sunras_libexecPROGRAMS \
+ install-file_svg_libexecPROGRAMS \
+ install-file_tga_libexecPROGRAMS \
+ install-file_wmf_libexecPROGRAMS \
+ install-file_xbm_libexecPROGRAMS \
+ install-file_xmc_libexecPROGRAMS \
+ install-file_xpm_libexecPROGRAMS \
+ install-file_xwd_libexecPROGRAMS install-film_libexecPROGRAMS \
+ install-filter_pack_libexecPROGRAMS \
+ install-fractal_trace_libexecPROGRAMS \
+ install-goat_exercise_libexecPROGRAMS \
+ install-gradient_map_libexecPROGRAMS \
+ install-grid_libexecPROGRAMS \
+ install-guillotine_libexecPROGRAMS install-hot_libexecPROGRAMS \
+ install-html install-html-am install-info install-info-am \
+ install-jigsaw_libexecPROGRAMS install-mail_libexecPROGRAMS \
+ install-man install-max_rgb_libexecPROGRAMS \
+ install-nl_filter_libexecPROGRAMS install-pdf install-pdf-am \
+ install-photocopy_libexecPROGRAMS \
+ install-plugin_browser_libexecPROGRAMS \
+ install-procedure_browser_libexecPROGRAMS install-ps \
+ install-ps-am install-qbist_libexecPROGRAMS \
+ install-sample_colorize_libexecPROGRAMS \
+ install-sharpen_libexecPROGRAMS \
+ install-smooth_palette_libexecPROGRAMS \
+ install-softglow_libexecPROGRAMS \
+ install-sparkle_libexecPROGRAMS \
+ install-sphere_designer_libexecPROGRAMS install-strip \
+ install-tile_libexecPROGRAMS \
+ install-tile_small_libexecPROGRAMS \
+ install-unit_editor_libexecPROGRAMS \
+ install-van_gogh_lic_libexecPROGRAMS \
+ install-warp_libexecPROGRAMS \
+ install-wavelet_decompose_libexecPROGRAMS \
+ install-web_browser_libexecPROGRAMS \
+ install-web_page_libexecPROGRAMS installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-align_layers_libexecPROGRAMS uninstall-am \
+ uninstall-animation_optimize_libexecPROGRAMS \
+ uninstall-animation_play_libexecPROGRAMS \
+ uninstall-blinds_libexecPROGRAMS \
+ uninstall-blur_libexecPROGRAMS \
+ uninstall-border_average_libexecPROGRAMS \
+ uninstall-busy_dialog_libexecPROGRAMS \
+ uninstall-cartoon_libexecPROGRAMS \
+ uninstall-checkerboard_libexecPROGRAMS \
+ uninstall-cml_explorer_libexecPROGRAMS \
+ uninstall-color_cube_analyze_libexecPROGRAMS \
+ uninstall-color_enhance_libexecPROGRAMS \
+ uninstall-colorify_libexecPROGRAMS \
+ uninstall-colormap_remap_libexecPROGRAMS \
+ uninstall-compose_libexecPROGRAMS \
+ uninstall-contrast_retinex_libexecPROGRAMS \
+ uninstall-crop_zealous_libexecPROGRAMS \
+ uninstall-curve_bend_libexecPROGRAMS \
+ uninstall-decompose_libexecPROGRAMS \
+ uninstall-depth_merge_libexecPROGRAMS \
+ uninstall-despeckle_libexecPROGRAMS \
+ uninstall-destripe_libexecPROGRAMS \
+ uninstall-edge_dog_libexecPROGRAMS \
+ uninstall-emboss_libexecPROGRAMS \
+ uninstall-file_aa_libexecPROGRAMS \
+ uninstall-file_cel_libexecPROGRAMS \
+ uninstall-file_compressor_libexecPROGRAMS \
+ uninstall-file_csource_libexecPROGRAMS \
+ uninstall-file_desktop_link_libexecPROGRAMS \
+ uninstall-file_dicom_libexecPROGRAMS \
+ uninstall-file_gbr_libexecPROGRAMS \
+ uninstall-file_gegl_libexecPROGRAMS \
+ uninstall-file_gif_load_libexecPROGRAMS \
+ uninstall-file_gif_save_libexecPROGRAMS \
+ uninstall-file_gih_libexecPROGRAMS \
+ uninstall-file_glob_libexecPROGRAMS \
+ uninstall-file_header_libexecPROGRAMS \
+ uninstall-file_heif_libexecPROGRAMS \
+ uninstall-file_html_table_libexecPROGRAMS \
+ uninstall-file_jp2_load_libexecPROGRAMS \
+ uninstall-file_jpegxl_libexecPROGRAMS \
+ uninstall-file_mng_libexecPROGRAMS \
+ uninstall-file_pat_libexecPROGRAMS \
+ uninstall-file_pcx_libexecPROGRAMS \
+ uninstall-file_pdf_load_libexecPROGRAMS \
+ uninstall-file_pdf_save_libexecPROGRAMS \
+ uninstall-file_pix_libexecPROGRAMS \
+ uninstall-file_png_libexecPROGRAMS \
+ uninstall-file_pnm_libexecPROGRAMS \
+ uninstall-file_ps_libexecPROGRAMS \
+ uninstall-file_psp_libexecPROGRAMS \
+ uninstall-file_raw_data_libexecPROGRAMS \
+ uninstall-file_sunras_libexecPROGRAMS \
+ uninstall-file_svg_libexecPROGRAMS \
+ uninstall-file_tga_libexecPROGRAMS \
+ uninstall-file_wmf_libexecPROGRAMS \
+ uninstall-file_xbm_libexecPROGRAMS \
+ uninstall-file_xmc_libexecPROGRAMS \
+ uninstall-file_xpm_libexecPROGRAMS \
+ uninstall-file_xwd_libexecPROGRAMS \
+ uninstall-film_libexecPROGRAMS \
+ uninstall-filter_pack_libexecPROGRAMS \
+ uninstall-fractal_trace_libexecPROGRAMS \
+ uninstall-goat_exercise_libexecPROGRAMS \
+ uninstall-gradient_map_libexecPROGRAMS \
+ uninstall-grid_libexecPROGRAMS \
+ uninstall-guillotine_libexecPROGRAMS \
+ uninstall-hot_libexecPROGRAMS uninstall-jigsaw_libexecPROGRAMS \
+ uninstall-mail_libexecPROGRAMS \
+ uninstall-max_rgb_libexecPROGRAMS \
+ uninstall-nl_filter_libexecPROGRAMS \
+ uninstall-photocopy_libexecPROGRAMS \
+ uninstall-plugin_browser_libexecPROGRAMS \
+ uninstall-procedure_browser_libexecPROGRAMS \
+ uninstall-qbist_libexecPROGRAMS \
+ uninstall-sample_colorize_libexecPROGRAMS \
+ uninstall-sharpen_libexecPROGRAMS \
+ uninstall-smooth_palette_libexecPROGRAMS \
+ uninstall-softglow_libexecPROGRAMS \
+ uninstall-sparkle_libexecPROGRAMS \
+ uninstall-sphere_designer_libexecPROGRAMS \
+ uninstall-tile_libexecPROGRAMS \
+ uninstall-tile_small_libexecPROGRAMS \
+ uninstall-unit_editor_libexecPROGRAMS \
+ uninstall-van_gogh_lic_libexecPROGRAMS \
+ uninstall-warp_libexecPROGRAMS \
+ uninstall-wavelet_decompose_libexecPROGRAMS \
+ uninstall-web_browser_libexecPROGRAMS \
+ uninstall-web_page_libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+install-%: %
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(gimpplugindir)/plug-ins/$<
+ @p=$<; p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(gimpplugindir)/plug-ins/$$p/$$f"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(gimpplugindir)/plug-ins/$$p/$$f || exit 1; \
+ else :; fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/common/align-layers.c b/plug-ins/common/align-layers.c
new file mode 100644
index 0000000..9cddc3b
--- /dev/null
+++ b/plug-ins/common/align-layers.c
@@ -0,0 +1,748 @@
+/* align_layers.c
+ * Author: Shuji Narazaki <narazaki@InetQ.or.jp>
+ * Version: 0.26
+ *
+ * Copyright (C) 1997-1998 Shuji Narazaki <narazaki@InetQ.or.jp>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PLUG_IN_PROC "plug-in-align-layers"
+#define PLUG_IN_BINARY "align-layers"
+#define PLUG_IN_ROLE "gimp-align-layers"
+#define SCALE_WIDTH 150
+
+enum
+{
+ H_NONE,
+ H_COLLECT,
+ LEFT2RIGHT,
+ RIGHT2LEFT,
+ SNAP2HGRID
+};
+
+enum
+{
+ H_BASE_LEFT,
+ H_BASE_CENTER,
+ H_BASE_RIGHT
+};
+
+enum
+{
+ V_NONE,
+ V_COLLECT,
+ TOP2BOTTOM,
+ BOTTOM2TOP,
+ SNAP2VGRID
+};
+
+enum
+{
+ V_BASE_TOP,
+ V_BASE_CENTER,
+ V_BASE_BOTTOM
+};
+
+
+typedef struct
+{
+ gint step_x;
+ gint step_y;
+ gint base_x;
+ gint base_y;
+} AlignData;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* Main function */
+static GimpPDBStatusType align_layers (gint32 image_id);
+
+/* Helpers and internal functions */
+static gint align_layers_count_visibles_layers (gint *layers,
+ gint length);
+static gint align_layers_find_last_layer (gint *layers,
+ gint layers_num,
+ gboolean *found);
+static gint align_layers_spread_visibles_layers (gint *layers,
+ gint layers_num,
+ gint *layers_array);
+static gint * align_layers_spread_image (gint32 image_id,
+ gint *layer_num);
+static gint align_layers_find_background (gint32 image_id);
+static AlignData align_layers_gather_data (gint *layers,
+ gint layer_num,
+ gint background);
+static void align_layers_perform_alignment (gint *layers,
+ gint layer_num,
+ AlignData data);
+static void align_layers_get_align_offsets (gint32 drawable_id,
+ gint *x,
+ gint *y);
+static gint align_layers_dialog (void);
+
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* dialog variables */
+typedef struct
+{
+ gint h_style;
+ gint h_base;
+ gint v_style;
+ gint v_base;
+ gboolean ignore_bottom;
+ gboolean base_is_bottom_layer;
+ gint grid_size;
+} ValueType;
+
+static ValueType VALS =
+{
+ H_NONE,
+ H_BASE_LEFT,
+ V_NONE,
+ V_BASE_TOP,
+ TRUE,
+ FALSE,
+ 10
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args [] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
+ { GIMP_PDB_IMAGE, "image", "Input image"},
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (not used)"},
+ { GIMP_PDB_INT32, "link-after-alignment", "Link the visible layers after alignment { TRUE, FALSE }"},
+ { GIMP_PDB_INT32, "use-bottom", "use the bottom layer as the base of alignment { TRUE, FALSE }"}
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Align all visible layers of the image"),
+ "Align visible layers",
+ "Shuji Narazaki <narazaki@InetQ.or.jp>",
+ "Shuji Narazaki",
+ "1997",
+ N_("Align Visi_ble Layers..."),
+ "RGB*,GRAY*,INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Arrange");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ GimpRunMode run_mode;
+ gint image_id, layer_num;
+ gint *layers;
+
+ run_mode = param[0].data.d_int32;
+ image_id = param[1].data.d_int32;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ switch ( run_mode )
+ {
+ case GIMP_RUN_INTERACTIVE:
+ layers = gimp_image_get_layers (image_id, &layer_num);
+ layer_num = align_layers_count_visibles_layers (layers,
+ layer_num);
+ g_free (layers);
+ if (layer_num < 2)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("There are not enough layers to align.");
+ return;
+ }
+ gimp_get_data (PLUG_IN_PROC, &VALS);
+ VALS.grid_size = MAX (VALS.grid_size, 1);
+ if (! align_layers_dialog ())
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &VALS);
+ break;
+ }
+
+ status = align_layers (image_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS)
+ gimp_set_data (PLUG_IN_PROC, &VALS, sizeof (ValueType));
+
+ values[0].data.d_status = status;
+}
+
+/*
+ * Main function
+ */
+static GimpPDBStatusType
+align_layers (gint32 image_id)
+{
+ gint layer_num = 0;
+ gint *layers = NULL;
+ gint background = 0;
+ AlignData data;
+
+ layers = align_layers_spread_image (image_id, &layer_num);
+ if (layer_num < 2)
+ {
+ g_free (layers);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ background = align_layers_find_background (image_id);
+
+ /* If we want to ignore the bottom layer and if it's visible */
+ if (VALS.ignore_bottom && background == layers[layer_num - 1])
+ {
+ layer_num--;
+ }
+
+ data = align_layers_gather_data (layers,
+ layer_num,
+ background);
+
+ gimp_image_undo_group_start (image_id);
+
+ align_layers_perform_alignment (layers,
+ layer_num,
+ data);
+
+ gimp_image_undo_group_end (image_id);
+
+ g_free (layers);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+/*
+ * Find the bottommost layer, visible or not
+ * The image must contain at least one layer.
+ */
+static gint
+align_layers_find_last_layer (gint *layers,
+ gint layers_num,
+ gboolean *found)
+{
+ gint i;
+
+ for (i = layers_num - 1; i >= 0; i--)
+ {
+ gint item = layers[i];
+
+ if (gimp_item_is_group (item))
+ {
+ gint *children;
+ gint children_num;
+ gint last_layer;
+
+ children = gimp_item_get_children (item, &children_num);
+ last_layer = align_layers_find_last_layer (children,
+ children_num,
+ found);
+ g_free (children);
+ if (*found)
+ return last_layer;
+ }
+ else if (gimp_item_is_layer (item))
+ {
+ *found = TRUE;
+ return item;
+ }
+ }
+
+ /* should never happen */
+ return -1;
+}
+
+/*
+ * Return the bottom layer.
+ */
+static gint
+align_layers_find_background (gint32 image_id)
+{
+ gint *layers;
+ gint layers_num;
+ gint background;
+ gboolean found = FALSE;
+
+ layers = gimp_image_get_layers (image_id, &layers_num);
+ background = align_layers_find_last_layer (layers,
+ layers_num,
+ &found);
+ g_free (layers);
+
+ return background;
+}
+
+/*
+ * Fill layers_array with all visible layers.
+ * layers_array needs to be allocated before the call
+ */
+static gint
+align_layers_spread_visibles_layers (gint *layers,
+ gint layers_num,
+ gint *layers_array)
+{
+ gint i;
+ gint index = 0;
+
+ for (i = 0; i < layers_num; i++)
+ {
+ gint item = layers[i];
+
+ if (gimp_item_get_visible (item))
+ {
+ if (gimp_item_is_group (item))
+ {
+ gint *children;
+ gint children_num;
+
+ children = gimp_item_get_children (item, &children_num);
+ index += align_layers_spread_visibles_layers (children,
+ children_num,
+ &(layers_array[index]));
+ g_free (children);
+ }
+ else if (gimp_item_is_layer (item))
+ {
+ layers_array[index] = item;
+ index++;
+ }
+ }
+ }
+
+ return index;
+}
+
+/*
+ * Return a contiguous array of all visible layers
+ */
+static gint *
+align_layers_spread_image (gint32 image_id,
+ gint *layer_num)
+{
+ gint *layers;
+ gint *layers_array;
+ gint layer_num_loc;
+
+ layers = gimp_image_get_layers (image_id, &layer_num_loc);
+ *layer_num = align_layers_count_visibles_layers (layers,
+ layer_num_loc);
+
+ layers_array = g_malloc (sizeof (gint) * *layer_num);
+
+ align_layers_spread_visibles_layers (layers,
+ layer_num_loc,
+ layers_array);
+ g_free (layers);
+
+ return layers_array;
+}
+
+static gint
+align_layers_count_visibles_layers (gint *layers,
+ gint length)
+{
+ gint i;
+ gint count = 0;
+
+ for (i = 0; i<length; i++)
+ {
+ gint item = layers[i];
+
+ if (gimp_item_get_visible (item))
+ {
+ if (gimp_item_is_group (item))
+ {
+ gint *children;
+ gint children_num;
+
+ children = gimp_item_get_children (item, &children_num);
+ count += align_layers_count_visibles_layers (children,
+ children_num);
+ g_free (children);
+ }
+ else if (gimp_item_is_layer (item))
+ {
+ count += 1;
+ }
+ }
+ }
+
+ return count;
+}
+
+static AlignData
+align_layers_gather_data (gint *layers,
+ gint layer_num,
+ gint background)
+{
+ AlignData data;
+ gint min_x = G_MAXINT;
+ gint min_y = G_MAXINT;
+ gint max_x = G_MININT;
+ gint max_y = G_MININT;
+ gint index;
+ gint orig_x = 0;
+ gint orig_y = 0;
+ gint offset_x = 0;
+ gint offset_y = 0;
+
+ data.step_x = 0;
+ data.step_y = 0;
+ data.base_x = 0;
+ data.base_y = 0;
+
+ /* 0 is the top layer */
+ for (index = 0; index < layer_num; index++)
+ {
+ gimp_drawable_offsets (layers[index], &orig_x, &orig_y);
+
+ align_layers_get_align_offsets (layers[index],
+ &offset_x,
+ &offset_y);
+ orig_x += offset_x;
+ orig_y += offset_y;
+
+ min_x = MIN (min_x, orig_x);
+ max_x = MAX (max_x, orig_x);
+ min_y = MIN (min_y, orig_y);
+ max_y = MAX (max_y, orig_y);
+ }
+
+ if (VALS.base_is_bottom_layer)
+ {
+ gimp_drawable_offsets (background, &orig_x, &orig_y);
+
+ align_layers_get_align_offsets (background,
+ &offset_x,
+ &offset_y);
+ orig_x += offset_x;
+ orig_y += offset_y;
+ data.base_x = min_x = orig_x;
+ data.base_y = min_y = orig_y;
+ }
+
+ if (layer_num > 1)
+ {
+ data.step_x = (max_x - min_x) / (layer_num - 1);
+ data.step_y = (max_y - min_y) / (layer_num - 1);
+ }
+
+ if ( (VALS.h_style == LEFT2RIGHT) || (VALS.h_style == RIGHT2LEFT))
+ data.base_x = min_x;
+
+ if ( (VALS.v_style == TOP2BOTTOM) || (VALS.v_style == BOTTOM2TOP))
+ data.base_y = min_y;
+
+ return data;
+}
+
+/*
+ * Modifies position of each visible layers
+ * according to data.
+ */
+static void
+align_layers_perform_alignment (gint *layers,
+ gint layer_num,
+ AlignData data)
+{
+ gint index;
+
+ for (index = 0; index < layer_num; index++)
+ {
+ gint x = 0;
+ gint y = 0;
+ gint orig_x;
+ gint orig_y;
+ gint offset_x;
+ gint offset_y;
+
+ gimp_drawable_offsets (layers[index], &orig_x, &orig_y);
+
+ align_layers_get_align_offsets (layers[index],
+ &offset_x,
+ &offset_y);
+ switch (VALS.h_style)
+ {
+ case H_NONE:
+ x = orig_x;
+ break;
+ case H_COLLECT:
+ x = data.base_x - offset_x;
+ break;
+ case LEFT2RIGHT:
+ x = (data.base_x + index * data.step_x) - offset_x;
+ break;
+ case RIGHT2LEFT:
+ x = (data.base_x + (layer_num - index - 1) * data.step_x) - offset_x;
+ break;
+ case SNAP2HGRID:
+ x = VALS.grid_size
+ * (int) ((orig_x + offset_x + VALS.grid_size /2) / VALS.grid_size)
+ - offset_x;
+ break;
+ }
+
+ switch (VALS.v_style)
+ {
+ case V_NONE:
+ y = orig_y;
+ break;
+ case V_COLLECT:
+ y = data.base_y - offset_y;
+ break;
+ case TOP2BOTTOM:
+ y = (data.base_y + index * data.step_y) - offset_y;
+ break;
+ case BOTTOM2TOP:
+ y = (data.base_y + (layer_num - index - 1) * data.step_y) - offset_y;
+ break;
+ case SNAP2VGRID:
+ y = VALS.grid_size
+ * (int) ((orig_y + offset_y + VALS.grid_size / 2) / VALS.grid_size)
+ - offset_y;
+ break;
+ }
+
+ gimp_layer_set_offsets (layers[index], x, y);
+ }
+}
+
+static void
+align_layers_get_align_offsets (gint32 drawable_id,
+ gint *x,
+ gint *y)
+{
+ gint width = gimp_drawable_width (drawable_id);
+ gint height = gimp_drawable_height (drawable_id);
+
+ switch (VALS.h_base)
+ {
+ case H_BASE_LEFT:
+ *x = 0;
+ break;
+ case H_BASE_CENTER:
+ *x = width / 2;
+ break;
+ case H_BASE_RIGHT:
+ *x = width;
+ break;
+ default:
+ *x = 0;
+ break;
+ }
+
+ switch (VALS.v_base)
+ {
+ case V_BASE_TOP:
+ *y = 0;
+ break;
+ case V_BASE_CENTER:
+ *y = height / 2;
+ break;
+ case V_BASE_BOTTOM:
+ *y = height;
+ break;
+ default:
+ *y = 0;
+ break;
+ }
+}
+
+static int
+align_layers_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *toggle;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Align Visible Layers"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new (C_("align-style", "None"), H_NONE,
+ _("Collect"), H_COLLECT,
+ _("Fill (left to right)"), LEFT2RIGHT,
+ _("Fill (right to left)"), RIGHT2LEFT,
+ _("Snap to grid"), SNAP2HGRID,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), VALS.h_style);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &VALS.h_style);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Horizontal style:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+
+ combo = gimp_int_combo_box_new (_("Left edge"), H_BASE_LEFT,
+ _("Center"), H_BASE_CENTER,
+ _("Right edge"), H_BASE_RIGHT,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), VALS.h_base);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &VALS.h_base);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Ho_rizontal base:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ combo = gimp_int_combo_box_new (C_("align-style", "None"), V_NONE,
+ _("Collect"), V_COLLECT,
+ _("Fill (top to bottom)"), TOP2BOTTOM,
+ _("Fill (bottom to top)"), BOTTOM2TOP,
+ _("Snap to grid"), SNAP2VGRID,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), VALS.v_style);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &VALS.v_style);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Vertical style:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ combo = gimp_int_combo_box_new (_("Top edge"), V_BASE_TOP,
+ _("Center"), V_BASE_CENTER,
+ _("Bottom edge"), V_BASE_BOTTOM,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), VALS.v_base);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &VALS.v_base);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("Ver_tical base:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("_Grid size:"), SCALE_WIDTH, 0,
+ VALS.grid_size, 1, 200, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &VALS.grid_size);
+
+ toggle = gtk_check_button_new_with_mnemonic
+ (_("_Ignore the bottom layer even if visible"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), VALS.ignore_bottom);
+ gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, 5, 6);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &VALS.ignore_bottom);
+
+ toggle = gtk_check_button_new_with_mnemonic
+ (_("_Use the (invisible) bottom layer as the base"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ VALS.base_is_bottom_layer);
+ gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, 6, 7);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &VALS.base_is_bottom_layer);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/animation-optimize.c b/plug-ins/common/animation-optimize.c
new file mode 100644
index 0000000..e110c13
--- /dev/null
+++ b/plug-ins/common/animation-optimize.c
@@ -0,0 +1,1340 @@
+/*
+ * Animation Optimizer plug-in version 1.1.2
+ *
+ * (c) Adam D. Moss, 1997-2003
+ * adam@gimp.org
+ * adam@foxbox.org
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+#define EXPERIMENTAL_BACKDROP_CODE
+*/
+
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define OPTIMIZE_PROC "plug-in-animationoptimize"
+#define OPTIMIZE_DIFF_PROC "plug-in-animationoptimize-diff"
+#define UNOPTIMIZE_PROC "plug-in-animationunoptimize"
+#define REMOVE_BACKDROP_PROC "plug-in-animation-remove-backdrop"
+#define FIND_BACKDROP_PROC "plug-in-animation-find-backdrop"
+
+
+typedef enum
+{
+ DISPOSE_UNDEFINED = 0x00,
+ DISPOSE_COMBINE = 0x01,
+ DISPOSE_REPLACE = 0x02
+} DisposeType;
+
+
+typedef enum
+{
+ OPOPTIMIZE = 0L,
+ OPUNOPTIMIZE = 1L,
+ OPFOREGROUND = 2L,
+ OPBACKGROUND = 3L
+} operatingMode;
+
+
+/* Declare local functions. */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 do_optimizations (GimpRunMode run_mode,
+ gboolean diff_only);
+
+/* tag util functions*/
+static gint parse_ms_tag (const gchar *str);
+static DisposeType parse_disposal_tag (const gchar *str);
+static DisposeType get_frame_disposal (guint whichframe);
+static guint32 get_frame_duration (guint whichframe);
+static void remove_disposal_tag (gchar *dest,
+ gchar *src);
+static void remove_ms_tag (gchar *dest,
+ gchar *src);
+static gboolean is_disposal_tag (const gchar *str,
+ DisposeType *disposal,
+ gint *taglength);
+static gboolean is_ms_tag (const gchar *str,
+ gint *duration,
+ gint *taglength);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* Global widgets'n'stuff */
+static guint width, height;
+static gint32 image_id;
+static gint32 new_image_id;
+static gint32 total_frames;
+static gint32 *layers;
+static GimpImageBaseType imagetype;
+static GimpImageType drawabletype_alpha;
+static guchar pixelstep;
+static guchar *palette;
+static gint ncolors;
+static operatingMode opmode;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
+ };
+ static const GimpParamDef return_args[] =
+ {
+ { GIMP_PDB_IMAGE, "result", "Resulting image" }
+ };
+
+ gimp_install_procedure (OPTIMIZE_PROC,
+ N_("Modify image to reduce size when saved as GIF animation"),
+ "This procedure applies various optimizations to"
+ " a GIMP layer-based animation in an attempt to"
+ " reduce the final file size. If a frame of the"
+ " animation can use the 'combine' mode, this"
+ " procedure attempts to maximize the number of"
+ " ajdacent pixels having the same color, which"
+ " improves the compression for some image formats"
+ " such as GIF or MNG.",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "1997-2003",
+ N_("Optimize (for _GIF)"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_args),
+ args, return_args);
+
+ gimp_install_procedure (OPTIMIZE_DIFF_PROC,
+ N_("Reduce file size where combining layers is possible"),
+ "This procedure applies various optimizations to"
+ " a GIMP layer-based animation in an attempt to"
+ " reduce the final file size. If a frame of the"
+ " animation can use the 'combine' mode, this"
+ " procedure uses a simple difference between the"
+ " frames.",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "1997-2001",
+ N_("_Optimize (Difference)"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_args),
+ args, return_args);
+
+ gimp_install_procedure (UNOPTIMIZE_PROC,
+ N_("Remove optimization to make editing easier"),
+ "This procedure 'simplifies' a GIMP layer-based"
+ " animation that has been optimized for animation. "
+ "This makes editing the animation much easier.",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "1997-2001",
+ N_("_Unoptimize"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_args),
+ args, return_args);
+
+ gimp_plugin_menu_register (OPTIMIZE_PROC, "<Image>/Filters/Animation");
+ gimp_plugin_menu_register (OPTIMIZE_DIFF_PROC, "<Image>/Filters/Animation");
+ gimp_plugin_menu_register (UNOPTIMIZE_PROC, "<Image>/Filters/Animation");
+
+#ifdef EXPERIMENTAL_BACKDROP_CODE
+ gimp_install_procedure (REMOVE_BACKDROP_PROC,
+ "This procedure attempts to remove the backdrop"
+ " from a GIMP layer-based animation, leaving"
+ " the foreground animation over transparency.",
+ "",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "2001",
+ N_("_Remove Backdrop"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_args),
+ args, return_args);
+
+ gimp_install_procedure (FIND_BACKDROP_PROC,
+ "This procedure attempts to remove the foreground"
+ " from a GIMP layer-based animation, leaving"
+ " a one-layered image containing only the"
+ " constant backdrop image.",
+ "",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "2001",
+ N_("_Find Backdrop"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_args),
+ args, return_args);
+
+ gimp_plugin_menu_register (REMOVE_BACKDROP_PROC, "<Image>/Filters/Animation");
+ gimp_plugin_menu_register (FIND_BACKDROP_PROC, "<Image>/Filters/Animation");
+#endif
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gboolean diff_only = FALSE;
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE && n_params != 3)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ /* Check the procedure name we were called with, to decide
+ what needs to be done. */
+ if (strcmp (name, OPTIMIZE_PROC) == 0)
+ opmode = OPOPTIMIZE;
+ else if (strcmp (name, OPTIMIZE_DIFF_PROC) == 0)
+ {
+ opmode = OPOPTIMIZE;
+ diff_only = TRUE;
+ }
+ else if (strcmp (name, UNOPTIMIZE_PROC) == 0)
+ opmode = OPUNOPTIMIZE;
+ else if (strcmp (name, FIND_BACKDROP_PROC) == 0)
+ opmode = OPBACKGROUND;
+ else if (strcmp (name, REMOVE_BACKDROP_PROC) == 0)
+ opmode = OPFOREGROUND;
+ else
+ g_error("GAH!!!");
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ image_id = param[1].data.d_image;
+
+ new_image_id = do_optimizations (run_mode, diff_only);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush();
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = new_image_id;
+}
+
+
+
+/* Rendering Functions */
+
+static void
+total_alpha (guchar *imdata,
+ guint32 numpix,
+ guchar bytespp)
+{
+ /* Set image to total-transparency w/black
+ */
+
+ memset (imdata, 0, numpix * bytespp);
+}
+
+static const Babl *
+get_format (gint32 drawable_ID)
+{
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ return babl_format ("R'G'B'A u8");
+ else
+ return babl_format ("R'G'B' u8");
+ }
+ else if (gimp_drawable_is_gray (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ return babl_format ("Y'A u8");
+ else
+ return babl_format ("Y' u8");
+ }
+
+ return gimp_drawable_get_format (drawable_ID);
+}
+
+static void
+compose_row (gint frame_num,
+ DisposeType dispose,
+ gint row_num,
+ guchar *dest,
+ gint dest_width,
+ gint32 drawable_ID,
+ gboolean cleanup)
+{
+ static guchar *line_buf = NULL;
+ GeglBuffer *src_buffer;
+ const Babl *format;
+ guchar *srcptr;
+ gint rawx, rawy, rawbpp, rawwidth, rawheight;
+ gint i;
+ gboolean has_alpha;
+
+ if (cleanup)
+ {
+ if (line_buf)
+ {
+ g_free (line_buf);
+ line_buf = NULL;
+ }
+
+ return;
+ }
+
+ if (dispose == DISPOSE_REPLACE)
+ {
+ total_alpha (dest, dest_width, pixelstep);
+ }
+
+ gimp_drawable_offsets (drawable_ID, &rawx, &rawy);
+
+ rawwidth = gimp_drawable_width (drawable_ID);
+ rawheight = gimp_drawable_height (drawable_ID);
+
+ /* this frame has nothing to give us for this row; return */
+ if (row_num >= rawheight + rawy ||
+ row_num < rawy)
+ return;
+
+ format = get_format (drawable_ID);
+
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+ rawbpp = babl_format_get_bytes_per_pixel (format);
+
+ if (line_buf)
+ {
+ g_free (line_buf);
+ line_buf = NULL;
+ }
+ line_buf = g_malloc (rawwidth * rawbpp);
+
+ /* Initialise and fetch the raw new frame row */
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row_num - rawy,
+ rawwidth, 1), 1.0,
+ format, line_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (src_buffer);
+
+ /* render... */
+
+ srcptr = line_buf;
+
+ for (i=rawx; i<rawwidth+rawx; i++)
+ {
+ if (i>=0 && i<dest_width)
+ {
+ if ((!has_alpha) || ((*(srcptr+rawbpp-1))&128))
+ {
+ gint pi;
+
+ for (pi = 0; pi < pixelstep-1; pi++)
+ {
+ dest[i*pixelstep +pi] = *(srcptr + pi);
+ }
+
+ dest[i*pixelstep + pixelstep - 1] = 255;
+ }
+ }
+
+ srcptr += rawbpp;
+ }
+}
+
+
+static gint32
+do_optimizations (GimpRunMode run_mode,
+ gboolean diff_only)
+{
+ static guchar *rawframe = NULL;
+ guchar *srcptr;
+ guchar *destptr;
+ gint row, this_frame_num;
+ guint32 frame_sizebytes;
+ gint32 new_layer_id;
+ DisposeType dispose;
+ guchar *this_frame = NULL;
+ guchar *last_frame = NULL;
+ guchar *opti_frame = NULL;
+ guchar *back_frame = NULL;
+
+ gint this_delay;
+ gint cumulated_delay = 0;
+ gint last_true_frame = -1;
+ gint buflen;
+
+ gchar *oldlayer_name;
+ gchar *newlayer_name;
+
+ gboolean can_combine;
+
+ gint32 bbox_top, bbox_bottom, bbox_left, bbox_right;
+ gint32 rbox_top, rbox_bottom, rbox_left, rbox_right;
+
+ switch (opmode)
+ {
+ case OPUNOPTIMIZE:
+ gimp_progress_init (_("Unoptimizing animation"));
+ break;
+ case OPFOREGROUND:
+ gimp_progress_init (_("Removing animation background"));
+ break;
+ case OPBACKGROUND:
+ gimp_progress_init (_("Finding animation background"));
+ break;
+ case OPOPTIMIZE:
+ default:
+ gimp_progress_init (_("Optimizing animation"));
+ break;
+ }
+
+ width = gimp_image_width (image_id);
+ height = gimp_image_height (image_id);
+ layers = gimp_image_get_layers (image_id, &total_frames);
+ imagetype = gimp_image_base_type (image_id);
+ pixelstep = (imagetype == GIMP_RGB) ? 4 : 2;
+
+ drawabletype_alpha = (imagetype == GIMP_RGB) ? GIMP_RGBA_IMAGE :
+ ((imagetype == GIMP_INDEXED) ? GIMP_INDEXEDA_IMAGE : GIMP_GRAYA_IMAGE);
+
+ frame_sizebytes = width * height * pixelstep;
+
+ this_frame = g_malloc (frame_sizebytes);
+ last_frame = g_malloc (frame_sizebytes);
+ opti_frame = g_malloc (frame_sizebytes);
+
+ if (opmode == OPBACKGROUND ||
+ opmode == OPFOREGROUND)
+ back_frame = g_malloc (frame_sizebytes);
+
+ total_alpha (this_frame, width*height, pixelstep);
+ total_alpha (last_frame, width*height, pixelstep);
+
+ new_image_id = gimp_image_new(width, height, imagetype);
+ gimp_image_undo_disable (new_image_id);
+
+ if (imagetype == GIMP_INDEXED)
+ {
+ palette = gimp_image_get_colormap (image_id, &ncolors);
+ gimp_image_set_colormap (new_image_id, palette, ncolors);
+ }
+
+#if 1
+ if (opmode == OPBACKGROUND ||
+ opmode == OPFOREGROUND)
+ {
+ /* iterate through all rows of all frames, find statistical
+ mode for each pixel position. */
+ gint i,j;
+ guchar **these_rows;
+ guchar **red;
+ guchar **green;
+ guchar **blue;
+ guint **count;
+ guint *num_colors;
+
+ these_rows = g_new (guchar *, total_frames);
+ red = g_new (guchar *, total_frames);
+ green = g_new (guchar *, total_frames);
+ blue = g_new (guchar *, total_frames);
+ count = g_new (guint *, total_frames);
+
+ num_colors = g_new (guint, width);
+
+ for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
+ {
+ these_rows[this_frame_num] = g_malloc(width * pixelstep);
+
+ red[this_frame_num] = g_new (guchar, width);
+ green[this_frame_num] = g_new (guchar, width);
+ blue[this_frame_num] = g_new (guchar, width);
+
+ count[this_frame_num] = g_new0(guint, width);
+ }
+
+ for (row = 0; row < height; row++)
+ {
+ memset(num_colors, 0, width * sizeof(guint));
+
+ for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
+ {
+ gint32 drawable_ID = layers[total_frames-(this_frame_num+1)];
+
+ dispose = get_frame_disposal (this_frame_num);
+
+ compose_row (this_frame_num,
+ dispose,
+ row,
+ these_rows[this_frame_num],
+ width,
+ drawable_ID,
+ FALSE);
+ }
+
+ for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
+ {
+ for (i=0; i<width; i++)
+ {
+ if (these_rows[this_frame_num][i * pixelstep + pixelstep -1]
+ >= 128)
+ {
+ for (j=0; j<num_colors[i]; j++)
+ {
+
+ switch (pixelstep)
+ {
+ case 4:
+ if (these_rows[this_frame_num][i * 4 +0] ==
+ red[j][i] &&
+ these_rows[this_frame_num][i * 4 +1] ==
+ green[j][i] &&
+ these_rows[this_frame_num][i * 4 +2] ==
+ blue[j][i])
+ {
+ (count[j][i])++;
+ goto same;
+ }
+ break;
+ case 2:
+ if (these_rows[this_frame_num][i * 2 +0] ==
+ red[j][i])
+ {
+ (count[j][i])++;
+ goto same;
+ }
+ break;
+ default:
+ g_error ("Eeep!");
+ break;
+ }
+ }
+
+ count[num_colors[i]][i] = 1;
+ red[num_colors[i]][i] =
+ these_rows[this_frame_num][i * pixelstep];
+ if (pixelstep == 4)
+ {
+ green[num_colors[i]][i] =
+ these_rows[this_frame_num][i * 4 +1];
+ blue[num_colors[i]][i] =
+ these_rows[this_frame_num][i * 4 +2];
+ }
+ num_colors[i]++;
+ }
+ same:
+ /* nop */;
+ }
+ }
+
+ for (i=0; i<width; i++)
+ {
+ guint best_count = 0;
+ guchar best_r = 255, best_g = 0, best_b = 255;
+
+ for (j=0; j<num_colors[i]; j++)
+ {
+ if (count[j][i] > best_count)
+ {
+ best_count = count[j][i];
+ best_r = red[j][i];
+ best_g = green[j][i];
+ best_b = blue[j][i];
+ }
+ }
+
+ back_frame[width * pixelstep * row +i*pixelstep + 0] = best_r;
+ if (pixelstep == 4)
+ {
+ back_frame[width * pixelstep * row +i*pixelstep + 1] =
+ best_g;
+ back_frame[width * pixelstep * row +i*pixelstep + 2] =
+ best_b;
+ }
+ back_frame[width * pixelstep * row +i*pixelstep +pixelstep-1] =
+ (best_count == 0) ? 0 : 255;
+
+ if (best_count == 0)
+ g_warning("yayyyy!");
+ }
+ /* memcpy(&back_frame[width * pixelstep * row],
+ these_rows[0],
+ width * pixelstep);*/
+ }
+
+ for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
+ {
+ g_free (these_rows[this_frame_num]);
+ g_free (red[this_frame_num]);
+ g_free (green[this_frame_num]);
+ g_free (blue[this_frame_num]);
+ g_free (count[this_frame_num]);
+ }
+
+ g_free (these_rows);
+ g_free (red);
+ g_free (green);
+ g_free (blue);
+ g_free (count);
+ g_free (num_colors);
+ }
+#endif
+
+ if (opmode == OPBACKGROUND)
+ {
+ GeglBuffer *buffer;
+ const Babl *format;
+
+ new_layer_id = gimp_layer_new (new_image_id,
+ "Backgroundx",
+ width, height,
+ drawabletype_alpha,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+
+ gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (new_layer_id);
+
+ format = get_format (new_layer_id);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ format, back_frame,
+ GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+ }
+ else
+ {
+ for (this_frame_num=0; this_frame_num<total_frames; this_frame_num++)
+ {
+ /*
+ * BUILD THIS FRAME into our 'this_frame' buffer.
+ */
+
+ gint32 drawable_ID = layers[total_frames-(this_frame_num+1)];
+
+ /* Image has been closed/etc since we got the layer list? */
+ /* FIXME - How do we tell if a gimp_drawable_get() fails? */
+ if (gimp_drawable_width (drawable_ID) == 0)
+ {
+ gimp_quit ();
+ }
+
+ this_delay = get_frame_duration (this_frame_num);
+ dispose = get_frame_disposal (this_frame_num);
+
+ for (row = 0; row < height; row++)
+ {
+ compose_row (this_frame_num,
+ dispose,
+ row,
+ &this_frame[pixelstep*width * row],
+ width,
+ drawable_ID,
+ FALSE
+ );
+ }
+
+ if (opmode == OPFOREGROUND)
+ {
+ gint xit, yit, byteit;
+
+ for (yit=0; yit<height; yit++)
+ {
+ for (xit=0; xit<width; xit++)
+ {
+ for (byteit=0; byteit<pixelstep-1; byteit++)
+ {
+ if (back_frame[yit*width*pixelstep + xit*pixelstep
+ + byteit]
+ !=
+ this_frame[yit*width*pixelstep + xit*pixelstep
+ + byteit])
+ {
+ goto enough;
+ }
+ }
+ this_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep - 1] = 0;
+ enough:
+ /* nop */;
+ }
+ }
+ }
+
+ can_combine = FALSE;
+ bbox_left = 0;
+ bbox_top = 0;
+ bbox_right = width;
+ bbox_bottom = height;
+ rbox_left = 0;
+ rbox_top = 0;
+ rbox_right = width;
+ rbox_bottom = height;
+
+ /* copy 'this' frame into a buffer which we can safely molest */
+ memcpy (opti_frame, this_frame, frame_sizebytes);
+ /*
+ *
+ * OPTIMIZE HERE!
+ *
+ */
+ if (
+ (this_frame_num != 0) /* Can't delta bottom frame! */
+ && (opmode == OPOPTIMIZE)
+ )
+ {
+ gint xit, yit, byteit;
+
+ can_combine = TRUE;
+
+ /*
+ * SEARCH FOR BOUNDING BOX
+ */
+ bbox_left = width;
+ bbox_top = height;
+ bbox_right = 0;
+ bbox_bottom = 0;
+ rbox_left = width;
+ rbox_top = height;
+ rbox_right = 0;
+ rbox_bottom = 0;
+
+ for (yit=0; yit<height; yit++)
+ {
+ for (xit=0; xit<width; xit++)
+ {
+ gboolean keep_pix;
+ gboolean opaq_pix;
+
+ /* Check if 'this' and 'last' are transparent */
+ if (!(this_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128)
+ &&
+ !(last_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128))
+ {
+ keep_pix = FALSE;
+ opaq_pix = FALSE;
+ goto decided;
+ }
+ /* Check if just 'this' is transparent */
+ if ((last_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128)
+ &&
+ !(this_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128))
+ {
+ keep_pix = TRUE;
+ opaq_pix = FALSE;
+ can_combine = FALSE;
+ goto decided;
+ }
+ /* Check if just 'last' is transparent */
+ if (!(last_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128)
+ &&
+ (this_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1]&128))
+ {
+ keep_pix = TRUE;
+ opaq_pix = TRUE;
+ goto decided;
+ }
+ /* If 'last' and 'this' are opaque, we have
+ * to check if they're the same color - we
+ * only have to keep the pixel if 'last' or
+ * 'this' are opaque and different.
+ */
+ keep_pix = FALSE;
+ opaq_pix = TRUE;
+ for (byteit=0; byteit<pixelstep-1; byteit++)
+ {
+ if ((last_frame[yit*width*pixelstep + xit*pixelstep
+ + byteit]
+ !=
+ this_frame[yit*width*pixelstep + xit*pixelstep
+ + byteit])
+ )
+ {
+ keep_pix = TRUE;
+ goto decided;
+ }
+ }
+ decided:
+ if (opaq_pix)
+ {
+ if (xit<rbox_left) rbox_left=xit;
+ if (xit>rbox_right) rbox_right=xit;
+ if (yit<rbox_top) rbox_top=yit;
+ if (yit>rbox_bottom) rbox_bottom=yit;
+ }
+ if (keep_pix)
+ {
+ if (xit<bbox_left) bbox_left=xit;
+ if (xit>bbox_right) bbox_right=xit;
+ if (yit<bbox_top) bbox_top=yit;
+ if (yit>bbox_bottom) bbox_bottom=yit;
+ }
+ else
+ {
+ /* pixel didn't change this frame - make
+ * it transparent in our optimized buffer!
+ */
+ opti_frame[yit*width*pixelstep + xit*pixelstep
+ + pixelstep-1] = 0;
+ }
+ } /* xit */
+ } /* yit */
+
+ if (!can_combine)
+ {
+ bbox_left = rbox_left;
+ bbox_top = rbox_top;
+ bbox_right = rbox_right;
+ bbox_bottom = rbox_bottom;
+ }
+
+ bbox_right++;
+ bbox_bottom++;
+
+ if (can_combine && !diff_only)
+ {
+ /* Try to optimize the pixel data for RLE or LZW compression
+ * by making some transparent pixels non-transparent if they
+ * would have the same color as the adjacent pixels. This
+ * gives a better compression if the algorithm compresses
+ * the image line by line.
+ * See: http://bugzilla.gnome.org/show_bug.cgi?id=66367
+ * It may not be very efficient to add two additional passes
+ * over the pixels, but this hopefully makes the code easier
+ * to maintain and less error-prone.
+ */
+ for (yit = bbox_top; yit < bbox_bottom; yit++)
+ {
+ /* Compare with previous pixels from left to right */
+ for (xit = bbox_left + 1; xit < bbox_right; xit++)
+ {
+ if (!(opti_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + pixelstep-1]&128)
+ && (opti_frame[yit*width*pixelstep
+ + (xit-1)*pixelstep
+ + pixelstep-1]&128)
+ && (last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + pixelstep-1]&128))
+ {
+ for (byteit=0; byteit<pixelstep-1; byteit++)
+ {
+ if (opti_frame[yit*width*pixelstep
+ + (xit-1)*pixelstep
+ + byteit]
+ !=
+ last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit])
+ {
+ goto skip_right;
+ }
+ }
+ /* copy the color and alpha */
+ for (byteit=0; byteit<pixelstep; byteit++)
+ {
+ opti_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit]
+ = last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit];
+ }
+ }
+ skip_right:
+ /* nop */;
+ } /* xit */
+
+ /* Compare with next pixels from right to left */
+ for (xit = bbox_right - 2; xit >= bbox_left; xit--)
+ {
+ if (!(opti_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + pixelstep-1]&128)
+ && (opti_frame[yit*width*pixelstep
+ + (xit+1)*pixelstep
+ + pixelstep-1]&128)
+ && (last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + pixelstep-1]&128))
+ {
+ for (byteit=0; byteit<pixelstep-1; byteit++)
+ {
+ if (opti_frame[yit*width*pixelstep
+ + (xit+1)*pixelstep
+ + byteit]
+ !=
+ last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit])
+ {
+ goto skip_left;
+ }
+ }
+ /* copy the color and alpha */
+ for (byteit=0; byteit<pixelstep; byteit++)
+ {
+ opti_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit]
+ = last_frame[yit*width*pixelstep
+ + xit*pixelstep
+ + byteit];
+ }
+ }
+ skip_left:
+ /* nop */;
+ } /* xit */
+ } /* yit */
+ }
+
+ /*
+ * Collapse opti_frame data down such that the data
+ * which occupies the bounding box sits at the start
+ * of the data (for convenience with ..set_rect()).
+ */
+ destptr = opti_frame;
+ /*
+ * If can_combine, then it's safe to use our optimized
+ * alpha information. Otherwise, an opaque pixel became
+ * transparent this frame, and we'll have to use the
+ * actual true frame's alpha.
+ */
+ if (can_combine)
+ srcptr = opti_frame;
+ else
+ srcptr = this_frame;
+ for (yit=bbox_top; yit<bbox_bottom; yit++)
+ {
+ for (xit=bbox_left; xit<bbox_right; xit++)
+ {
+ for (byteit=0; byteit<pixelstep; byteit++)
+ {
+ *(destptr++) = srcptr[yit*pixelstep*width +
+ pixelstep*xit + byteit];
+ }
+ }
+ }
+ } /* !bot frame? */
+ else
+ {
+ memcpy (opti_frame, this_frame, frame_sizebytes);
+ }
+
+ /*
+ *
+ * REMEMBER THE ANIMATION STATUS TO DELTA AGAINST NEXT TIME
+ *
+ */
+ memcpy (last_frame, this_frame, frame_sizebytes);
+
+
+ /*
+ *
+ * PUT THIS FRAME INTO A NEW LAYER IN THE NEW IMAGE
+ *
+ */
+
+ oldlayer_name =
+ gimp_item_get_name(layers[total_frames-(this_frame_num+1)]);
+
+ buflen = strlen(oldlayer_name) + 40;
+
+ newlayer_name = g_malloc(buflen);
+
+ remove_disposal_tag(newlayer_name, oldlayer_name);
+ g_free(oldlayer_name);
+
+ oldlayer_name = g_malloc(buflen);
+
+ remove_ms_tag(oldlayer_name, newlayer_name);
+
+ g_snprintf(newlayer_name, buflen, "%s(%dms)%s",
+ oldlayer_name, this_delay,
+ (this_frame_num == 0) ? "" :
+ can_combine ? "(combine)" : "(replace)");
+
+ g_free(oldlayer_name);
+
+ /* Empty frame! */
+ if (bbox_right <= bbox_left ||
+ bbox_bottom <= bbox_top)
+ {
+ cumulated_delay += this_delay;
+
+ g_free (newlayer_name);
+
+ oldlayer_name = gimp_item_get_name (last_true_frame);
+
+ buflen = strlen (oldlayer_name) + 40;
+
+ newlayer_name = g_malloc (buflen);
+
+ remove_disposal_tag (newlayer_name, oldlayer_name);
+ g_free (oldlayer_name);
+
+ oldlayer_name = g_malloc (buflen);
+
+ remove_ms_tag (oldlayer_name, newlayer_name);
+
+ g_snprintf (newlayer_name, buflen, "%s(%dms)%s",
+ oldlayer_name, cumulated_delay,
+ (this_frame_num == 0) ? "" :
+ can_combine ? "(combine)" : "(replace)");
+
+ gimp_item_set_name (last_true_frame, newlayer_name);
+
+ g_free (newlayer_name);
+ }
+ else
+ {
+ GeglBuffer *buffer;
+ const Babl *format;
+
+ cumulated_delay = this_delay;
+
+ last_true_frame =
+ new_layer_id = gimp_layer_new (new_image_id,
+ newlayer_name,
+ bbox_right-bbox_left,
+ bbox_bottom-bbox_top,
+ drawabletype_alpha,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+ g_free (newlayer_name);
+
+ gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (new_layer_id);
+
+ format = get_format (new_layer_id);
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0,
+ bbox_right-bbox_left,
+ bbox_bottom-bbox_top), 0,
+ format, opti_frame,
+ GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+ gimp_item_transform_translate (new_layer_id, bbox_left, bbox_top);
+ }
+
+ gimp_progress_update (((gdouble) this_frame_num + 1.0) /
+ ((gdouble) total_frames));
+ }
+
+ gimp_progress_update (1.0);
+ }
+
+ gimp_image_undo_enable (new_image_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_display_new (new_image_id);
+
+ g_free (rawframe);
+ rawframe = NULL;
+
+ g_free (last_frame);
+ last_frame = NULL;
+
+ g_free (this_frame);
+ this_frame = NULL;
+
+ g_free (opti_frame);
+ opti_frame = NULL;
+
+ g_free (back_frame);
+ back_frame = NULL;
+
+ return new_image_id;
+}
+
+/* Util. */
+
+static DisposeType
+get_frame_disposal (guint whichframe)
+{
+ gchar *layer_name;
+ DisposeType disposal;
+
+ layer_name = gimp_item_get_name(layers[total_frames-(whichframe+1)]);
+ disposal = parse_disposal_tag(layer_name);
+ g_free(layer_name);
+
+ return disposal;
+}
+
+static guint32
+get_frame_duration (guint whichframe)
+{
+ gchar* layer_name;
+ gint duration = 0;
+
+ layer_name = gimp_item_get_name(layers[total_frames-(whichframe+1)]);
+ if (layer_name)
+ {
+ duration = parse_ms_tag(layer_name);
+ g_free(layer_name);
+ }
+
+ if (duration < 0) duration = 100; /* FIXME for default-if-not-said */
+ if (duration == 0) duration = 100; /* FIXME - 0-wait is nasty */
+
+ return (guint32) duration;
+}
+
+static gboolean
+is_ms_tag (const gchar *str,
+ gint *duration,
+ gint *taglength)
+{
+ gint sum = 0;
+ gint offset;
+ gint length;
+
+ length = strlen(str);
+
+ if (str[0] != '(')
+ return FALSE;
+
+ offset = 1;
+
+ /* eat any spaces between open-parenthesis and number */
+ while ((offset<length) && (str[offset] == ' '))
+ offset++;
+
+ if ((offset>=length) || (!g_ascii_isdigit (str[offset])))
+ return 0;
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset<length) && (g_ascii_isdigit (str[offset])));
+
+ if (length-offset <= 2)
+ return FALSE;
+
+ /* eat any spaces between number and 'ms' */
+ while ((offset<length) && (str[offset] == ' '))
+ offset++;
+
+ if ((length-offset <= 2) ||
+ (g_ascii_toupper (str[offset]) != 'M') ||
+ (g_ascii_toupper (str[offset+1]) != 'S'))
+ return FALSE;
+
+ offset += 2;
+
+ /* eat any spaces between 'ms' and close-parenthesis */
+ while ((offset<length) && (str[offset] == ' '))
+ offset++;
+
+ if ((length-offset < 1) || (str[offset] != ')'))
+ return FALSE;
+
+ offset++;
+
+ *duration = sum;
+ *taglength = offset;
+
+ return TRUE;
+}
+
+static int
+parse_ms_tag (const char *str)
+{
+ gint i;
+ gint rtn;
+ gint dummy;
+ gint length;
+
+ length = strlen (str);
+
+ for (i = 0; i < length; i++)
+ {
+ if (is_ms_tag (&str[i], &rtn, &dummy))
+ return rtn;
+ }
+
+ return -1;
+}
+
+static gboolean
+is_disposal_tag (const gchar *str,
+ DisposeType *disposal,
+ gint *taglength)
+{
+ if (strlen (str) != 9)
+ return FALSE;
+
+ if (strncmp (str, "(combine)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_COMBINE;
+ return TRUE;
+ }
+ else if (strncmp (str, "(replace)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_REPLACE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static DisposeType
+parse_disposal_tag (const gchar *str)
+{
+ DisposeType rtn;
+ gint i, dummy;
+ gint length;
+
+ length = strlen(str);
+
+ for (i=0; i<length; i++)
+ {
+ if (is_disposal_tag (&str[i], &rtn, &dummy))
+ {
+ return rtn;
+ }
+ }
+
+ return DISPOSE_UNDEFINED; /* FIXME */
+}
+
+static void
+remove_disposal_tag (gchar *dest,
+ gchar *src)
+{
+ gint offset = 0;
+ gint destoffset = 0;
+ gint length;
+ int taglength;
+ DisposeType dummy;
+
+ length = strlen(src);
+
+ strcpy(dest, src);
+
+ while (offset<=length)
+ {
+ if (is_disposal_tag(&src[offset], &dummy, &taglength))
+ {
+ offset += taglength;
+ }
+ dest[destoffset] = src[offset];
+ destoffset++;
+ offset++;
+ }
+
+ dest[offset] = '\0';
+}
+
+static void
+remove_ms_tag (gchar *dest,
+ gchar *src)
+{
+ gint offset = 0;
+ gint destoffset = 0;
+ gint length;
+ gint taglength;
+ gint dummy;
+
+ length = strlen(src);
+
+ strcpy(dest, src);
+
+ while (offset<=length)
+ {
+ if (is_ms_tag(&src[offset], &dummy, &taglength))
+ {
+ offset += taglength;
+ }
+ dest[destoffset] = src[offset];
+ destoffset++;
+ offset++;
+ }
+
+ dest[offset] = '\0';
+}
diff --git a/plug-ins/common/animation-play.c b/plug-ins/common/animation-play.c
new file mode 100644
index 0000000..7e0ab26
--- /dev/null
+++ b/plug-ins/common/animation-play.c
@@ -0,0 +1,1763 @@
+/*
+ * Animation Playback plug-in version 0.99.1
+ *
+ * (c) Adam D. Moss : 1997-2000 : adam@gimp.org : adam@foxbox.org
+ * (c) Mircea Purdea : 2009 : someone_else@exhalus.net
+ * (c) Jehan : 2012 : jehan at girinstud.io
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * TODO:
+ * pdb interface - should we bother?
+ *
+ * speedups (caching? most bottlenecks seem to be in pixelrgns)
+ * -> do pixelrgns properly!
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#undef GDK_DISABLE_DEPRECATED
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-animationplay"
+#define PLUG_IN_BINARY "animation-play"
+#define PLUG_IN_ROLE "gimp-animation-play"
+#define DITHERTYPE GDK_RGB_DITHER_NORMAL
+
+
+typedef enum
+{
+ DISPOSE_COMBINE = 0x00,
+ DISPOSE_REPLACE = 0x01
+} DisposeType;
+
+typedef struct
+{
+ gint duration_index;
+ DisposeType default_frame_disposal;
+ guint32 default_frame_duration;
+}
+AnimationSettings;
+
+/* for shaping */
+typedef struct
+{
+ gint x, y;
+} CursorOffset;
+
+/* Declare local functions. */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void initialize (void);
+static void build_dialog (gchar *imagename);
+static void refresh_dialog (gchar *imagename);
+
+static void da_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data);
+static void sda_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data);
+
+static void window_destroy (GtkWidget *widget);
+static void play_callback (GtkToggleAction *action);
+static void step_back_callback (GtkAction *action);
+static void step_callback (GtkAction *action);
+static void refresh_callback (GtkAction *action);
+static void rewind_callback (GtkAction *action);
+static void speed_up_callback (GtkAction *action);
+static void speed_down_callback (GtkAction *action);
+static void speed_reset_callback (GtkAction *action);
+static void framecombo_changed (GtkWidget *combo,
+ gpointer data);
+static void speedcombo_changed (GtkWidget *combo,
+ gpointer data);
+static void fpscombo_changed (GtkWidget *combo,
+ gpointer data);
+static void zoomcombo_activated (GtkEntry *combo,
+ gpointer data);
+static void zoomcombo_changed (GtkWidget *combo,
+ gpointer data);
+static gboolean repaint_sda (GtkWidget *darea,
+ GdkEventExpose *event,
+ gpointer data);
+static gboolean repaint_da (GtkWidget *darea,
+ GdkEventExpose *event,
+ gpointer data);
+
+static void init_frames (void);
+static void render_frame (gint32 whichframe);
+static void show_frame (void);
+static void total_alpha_preview (void);
+static void update_alpha_preview (void);
+static void update_combobox (void);
+static gdouble get_duration_factor (gint index);
+static gint get_fps (gint index);
+static gdouble get_scale (gint index);
+static void update_scale (gdouble scale);
+
+
+/* tag util functions*/
+static gint parse_ms_tag (const gchar *str);
+static DisposeType parse_disposal_tag (const gchar *str);
+static gboolean is_disposal_tag (const gchar *str,
+ DisposeType *disposal,
+ gint *taglength);
+static gboolean is_ms_tag (const gchar *str,
+ gint *duration,
+ gint *taglength);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* Global widgets'n'stuff */
+static GtkWidget *window = NULL;
+static GdkWindow *root_win = NULL;
+static GtkUIManager *ui_manager = NULL;
+static GtkWidget *progress;
+static GtkWidget *speedcombo = NULL;
+static GtkWidget *fpscombo = NULL;
+static GtkWidget *zoomcombo = NULL;
+static GtkWidget *frame_disposal_combo = NULL;
+
+static gint32 image_id;
+static guint width = -1,
+ height = -1;
+static gint32 *layers = NULL;
+static gint32 total_layers = 0;
+
+static GtkWidget *drawing_area = NULL;
+static guchar *drawing_area_data = NULL;
+static guint drawing_area_width = -1,
+ drawing_area_height = -1;
+static guchar *preview_alpha1_data = NULL;
+static guchar *preview_alpha2_data = NULL;
+
+static GtkWidget *shape_window = NULL;
+static GtkWidget *shape_drawing_area = NULL;
+static guchar *shape_drawing_area_data = NULL;
+static guint shape_drawing_area_width = -1,
+ shape_drawing_area_height = -1;
+static gchar *shape_preview_mask = NULL;
+
+
+static gint32 total_frames = 0;
+static gint32 *frames = NULL;
+static guchar *rawframe = NULL;
+static guint32 *frame_durations = NULL;
+static guint frame_number = 0;
+
+static gboolean playing = FALSE;
+static guint timer = 0;
+static gboolean detached = FALSE;
+static gdouble scale, shape_scale;
+
+/* Default settings. */
+static AnimationSettings settings =
+{
+ 3,
+ DISPOSE_COMBINE,
+ 100 /* ms */
+};
+
+static gint32 frames_image_id = 0;
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Preview a GIMP layer-based animation"),
+ "",
+ "Adam D. Moss <adam@gimp.org>",
+ "Adam D. Moss <adam@gimp.org>",
+ "1997, 1998...",
+ N_("_Playback..."),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Animation");
+ gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) "media-playback-start");
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE && n_params != 3)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_get_data (PLUG_IN_PROC, &settings);
+ image_id = param[1].data.d_image;
+
+ initialize ();
+ gtk_main ();
+ gimp_set_data (PLUG_IN_PROC, &settings, sizeof (settings));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ gimp_image_delete (frames_image_id);
+ gegl_exit ();
+}
+
+static void
+reshape_from_bitmap (const gchar *bitmap)
+{
+ static gchar *prev_bitmap = NULL;
+ static guint prev_width = -1;
+ static guint prev_height = -1;
+
+ if ((!prev_bitmap) ||
+ prev_width != shape_drawing_area_width || prev_height != shape_drawing_area_height ||
+ (memcmp (prev_bitmap, bitmap, (shape_drawing_area_width * shape_drawing_area_height) / 8 + shape_drawing_area_height)))
+ {
+ GdkBitmap *shape_mask;
+
+ shape_mask = gdk_bitmap_create_from_data (gtk_widget_get_window (shape_window),
+ bitmap,
+ shape_drawing_area_width, shape_drawing_area_height);
+ gtk_widget_shape_combine_mask (shape_window, shape_mask, 0, 0);
+ g_object_unref (shape_mask);
+
+ if (!prev_bitmap || prev_width != shape_drawing_area_width || prev_height != shape_drawing_area_height)
+ {
+ g_free(prev_bitmap);
+ prev_bitmap = g_malloc ((shape_drawing_area_width * shape_drawing_area_height) / 8 + shape_drawing_area_height);
+ prev_width = shape_drawing_area_width;
+ prev_height = shape_drawing_area_height;
+ }
+
+ memcpy (prev_bitmap, bitmap, (shape_drawing_area_width * shape_drawing_area_height) / 8 + shape_drawing_area_height);
+ }
+}
+
+static gboolean
+popup_menu (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkWidget *menu = gtk_ui_manager_get_widget (ui_manager, "/anim-play-popup");
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ event ? event->time : gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static gboolean
+button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ return popup_menu (widget, event);
+
+ return FALSE;
+}
+
+/*
+ * Update the actual drawing area metrics, which may be different as requested,
+ * because there is no full control of the WM.
+ * data is always NULL. */
+static void
+da_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data)
+{
+ if (allocation->width == drawing_area_width && allocation->height == drawing_area_height)
+ return;
+
+ drawing_area_width = allocation->width;
+ drawing_area_height = allocation->height;
+ scale = MIN ((gdouble) drawing_area_width / (gdouble) width, (gdouble) drawing_area_height / (gdouble) height);
+
+ g_free (drawing_area_data);
+ drawing_area_data = g_malloc (drawing_area_width * drawing_area_height * 3);
+
+ update_alpha_preview ();
+
+ if (! detached)
+ {
+ /* Update the zoom information. */
+ GtkEntry *zoomcombo_text_child;
+
+ zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo)));
+ if (zoomcombo_text_child)
+ {
+ char* new_entry_text = g_strdup_printf (_("%.1f %%"), scale * 100.0);
+
+ gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
+ g_free (new_entry_text);
+ }
+
+ /* Update the rawframe. */
+ g_free (rawframe);
+ rawframe = g_malloc ((unsigned long) drawing_area_width * drawing_area_height * 4);
+
+ /* As we re-allocated the drawn data, let's render it again. */
+ if (frame_number < total_frames)
+ render_frame (frame_number);
+ }
+ else
+ {
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ repaint_da(drawing_area, NULL, NULL);
+ }
+}
+
+/*
+ * Update the actual shape drawing area metrics, which may be different as requested,
+ * They *should* be the same as the drawing area, but the safe way is to make sure
+ * and process it separately.
+ * data is always NULL. */
+static void
+sda_size_callback (GtkWidget *widget,
+ GtkAllocation *allocation, void *data)
+{
+ if (allocation->width == shape_drawing_area_width && allocation->height == shape_drawing_area_height)
+ return;
+
+ shape_drawing_area_width = allocation->width;
+ shape_drawing_area_height = allocation->height;
+ shape_scale = MIN ((gdouble) shape_drawing_area_width / (gdouble) width, (gdouble) shape_drawing_area_height / (gdouble) height);
+
+ g_free (shape_drawing_area_data);
+ g_free (shape_preview_mask);
+
+ shape_drawing_area_data = g_malloc (shape_drawing_area_width * shape_drawing_area_height * 3);
+ shape_preview_mask = g_malloc ((shape_drawing_area_width * shape_drawing_area_height) / 8 + 1 + shape_drawing_area_height);
+
+ if (detached)
+ {
+ /* Update the zoom information. */
+ GtkEntry *zoomcombo_text_child;
+
+ zoomcombo_text_child = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo)));
+ if (zoomcombo_text_child)
+ {
+ char* new_entry_text = g_strdup_printf (_("%.1f %%"), shape_scale * 100.0);
+
+ gtk_entry_set_text (zoomcombo_text_child, new_entry_text);
+ g_free (new_entry_text);
+ }
+
+ /* Update the rawframe. */
+ g_free (rawframe);
+ rawframe = g_malloc ((unsigned long) shape_drawing_area_width * shape_drawing_area_height * 4);
+
+ if (frame_number < total_frames)
+ render_frame (frame_number);
+ }
+}
+
+static gboolean
+shape_pressed (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (button_press (widget, event))
+ return TRUE;
+
+ /* ignore double and triple click */
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ CursorOffset *p = g_object_get_data (G_OBJECT(widget), "cursor-offset");
+
+ if (!p)
+ return FALSE;
+
+ p->x = (gint) event->x;
+ p->y = (gint) event->y;
+
+ gtk_grab_add (widget);
+ gdk_pointer_grab (gtk_widget_get_window (widget), TRUE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK,
+ NULL, NULL, 0);
+ gdk_window_raise (gtk_widget_get_window (widget));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+shape_released (GtkWidget *widget)
+{
+ gtk_grab_remove (widget);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget), 0);
+ gdk_flush ();
+
+ return FALSE;
+}
+
+static gboolean
+shape_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GdkModifierType mask;
+ gint xp, yp;
+
+ gdk_window_get_pointer (root_win, &xp, &yp, &mask);
+
+ /* if a button is still held by the time we process this event... */
+ if (mask & GDK_BUTTON1_MASK)
+ {
+ CursorOffset *p = g_object_get_data (G_OBJECT (widget), "cursor-offset");
+
+ if (!p)
+ return FALSE;
+
+ gtk_window_move (GTK_WINDOW (widget), xp - p->x, yp - p->y);
+ }
+ else /* the user has released all buttons */
+ {
+ shape_released (widget);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+repaint_da (GtkWidget *darea,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ GtkStyle *style = gtk_widget_get_style (darea);
+
+ gdk_draw_rgb_image (gtk_widget_get_window (darea),
+ style->white_gc,
+ (gint) ((drawing_area_width - scale * width) / 2),
+ (gint) ((drawing_area_height - scale * height) / 2),
+ drawing_area_width, drawing_area_height,
+ (total_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
+ drawing_area_data, drawing_area_width * 3);
+
+ return TRUE;
+}
+
+static gboolean
+repaint_sda (GtkWidget *darea,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ GtkStyle *style = gtk_widget_get_style (darea);
+
+ gdk_draw_rgb_image (gtk_widget_get_window (darea),
+ style->white_gc,
+ (gint) ((shape_drawing_area_width - shape_scale * width) / 2),
+ (gint) ((shape_drawing_area_height - shape_scale * height) / 2),
+ shape_drawing_area_width, shape_drawing_area_height,
+ (total_frames == 1) ? GDK_RGB_DITHER_MAX : DITHERTYPE,
+ shape_drawing_area_data, shape_drawing_area_width * 3);
+
+ return TRUE;
+}
+
+static void
+close_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_widget_destroy (GTK_WIDGET (data));
+}
+
+static void
+help_callback (GtkAction *action,
+ gpointer data)
+{
+ gimp_standard_help_func (PLUG_IN_PROC, data);
+}
+
+
+static void
+detach_callback (GtkToggleAction *action)
+{
+ gboolean active = gtk_toggle_action_get_active (action);
+
+ if (active == detached)
+ {
+ g_warning ("detached state and toggle action got out of sync");
+ return;
+ }
+
+ detached = active;
+
+ if (detached)
+ {
+ gint x, y;
+
+ /* Create a total-alpha buffer merely for the not-shaped
+ drawing area to now display. */
+
+ gtk_window_set_screen (GTK_WINDOW (shape_window),
+ gtk_widget_get_screen (drawing_area));
+
+ gtk_widget_show (shape_window);
+
+ if (!gtk_widget_get_realized (drawing_area))
+ gtk_widget_realize (drawing_area);
+ if (!gtk_widget_get_realized (shape_drawing_area))
+ gtk_widget_realize (shape_drawing_area);
+
+ gdk_window_get_origin (gtk_widget_get_window (drawing_area), &x, &y);
+
+ gtk_window_move (GTK_WINDOW (shape_window), x + 6, y + 6);
+
+ gdk_window_set_back_pixmap (gtk_widget_get_window (shape_drawing_area), NULL, TRUE);
+
+
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ repaint_da(drawing_area, NULL, NULL);
+ }
+ else
+ gtk_widget_hide (shape_window);
+
+ render_frame (frame_number);
+}
+
+static GtkUIManager *
+ui_manager_new (GtkWidget *window)
+{
+ static GtkActionEntry actions[] =
+ {
+ { "step-back", "media-skip-backward",
+ N_("Step _back"), "d", N_("Step back to previous frame"),
+ G_CALLBACK (step_back_callback) },
+
+ { "step", "media-skip-forward",
+ N_("_Step"), "f", N_("Step to next frame"),
+ G_CALLBACK (step_callback) },
+
+ { "rewind", "media-seek-backward",
+ NULL, NULL, N_("Rewind the animation"),
+ G_CALLBACK (rewind_callback) },
+
+ { "refresh", GIMP_ICON_VIEW_REFRESH,
+ NULL, "<control>R", N_("Reload the image"),
+ G_CALLBACK (refresh_callback) },
+
+ { "help", "help-browser",
+ NULL, NULL, NULL,
+ G_CALLBACK (help_callback) },
+
+ { "close", "window-close",
+ NULL, "<control>W", NULL,
+ G_CALLBACK (close_callback)
+ },
+ {
+ "quit", "application-quit",
+ NULL, "<control>Q", NULL,
+ G_CALLBACK (close_callback)
+ },
+ {
+ "speed-up", NULL,
+ N_("Faster"), "<control>L", N_("Increase the speed of the animation"),
+ G_CALLBACK (speed_up_callback)
+ },
+ {
+ "speed-down", NULL,
+ N_("Slower"), "<control>J", N_("Decrease the speed of the animation"),
+ G_CALLBACK (speed_down_callback)
+ },
+ {
+ "speed-reset", NULL,
+ N_("Reset speed"), "<control>K", N_("Reset the speed of the animation"),
+ G_CALLBACK (speed_reset_callback)
+ }
+ };
+
+ static GtkToggleActionEntry toggle_actions[] =
+ {
+ { "play", "media-playback-start",
+ NULL, "space", N_("Start playback"),
+ G_CALLBACK (play_callback), FALSE },
+
+ { "detach", GIMP_ICON_DETACH,
+ N_("Detach"), NULL,
+ N_("Detach the animation from the dialog window"),
+ G_CALLBACK (detach_callback), FALSE }
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GtkActionGroup *group = gtk_action_group_new ("Actions");
+ GError *error = NULL;
+
+ gtk_action_group_set_translation_domain (group, NULL);
+
+ gtk_action_group_add_actions (group,
+ actions,
+ G_N_ELEMENTS (actions),
+ window);
+ gtk_action_group_add_toggle_actions (group,
+ toggle_actions,
+ G_N_ELEMENTS (toggle_actions),
+ NULL);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <toolbar name=\"anim-play-toolbar\">"
+ " <toolitem action=\"play\" />"
+ " <toolitem action=\"step-back\" />"
+ " <toolitem action=\"step\" />"
+ " <toolitem action=\"rewind\" />"
+ " <separator />"
+ " <toolitem action=\"detach\" />"
+ " <toolitem action=\"refresh\" />"
+ " <separator name=\"space\" />"
+ " <toolitem action=\"help\" />"
+ " </toolbar>"
+ " <accelerator action=\"close\" />"
+ " <accelerator action=\"quit\" />"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"anim-play-popup\">"
+ " <menuitem action=\"play\" />"
+ " <menuitem action=\"step-back\" />"
+ " <menuitem action=\"step\" />"
+ " <menuitem action=\"rewind\" />"
+ " <separator />"
+ " <menuitem action=\"speed-down\" />"
+ " <menuitem action=\"speed-up\" />"
+ " <menuitem action=\"speed-reset\" />"
+ " <separator />"
+ " <menuitem action=\"detach\" />"
+ " <menuitem action=\"refresh\" />"
+ " <menuitem action=\"close\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return ui_manager;
+}
+
+static void
+refresh_dialog (gchar *imagename)
+{
+ gchar *name;
+ GdkScreen *screen;
+ guint screen_width, screen_height;
+ gint window_width, window_height;
+
+ /* Image Name */
+ name = g_strconcat (_("Animation Playback:"), " ", imagename, NULL);
+ gtk_window_set_title (GTK_WINDOW (window), name);
+ g_free (name);
+
+ /* Update GUI size. */
+ screen = gtk_widget_get_screen (window);
+ screen_height = gdk_screen_get_height (screen);
+ screen_width = gdk_screen_get_width (screen);
+ gtk_window_get_size (GTK_WINDOW (window), &window_width, &window_height);
+
+ /* if the *window* size is bigger than the screen size,
+ * diminish the drawing area by as much, then compute the corresponding scale. */
+ if (window_width + 50 > screen_width || window_height + 50 > screen_height)
+ {
+ guint expected_drawing_area_width = MAX (1, width - window_width + screen_width);
+ guint expected_drawing_area_height = MAX (1, height - window_height + screen_height);
+ gdouble expected_scale = MIN ((gdouble) expected_drawing_area_width / (gdouble) width,
+ (gdouble) expected_drawing_area_height / (gdouble) height);
+ update_scale (expected_scale);
+
+ /* There is unfortunately no good way to know the size of the decorations, taskbars, etc.
+ * So we take a wild guess by making the window slightly smaller to fit into any case. */
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ MIN (expected_drawing_area_width + 20, screen_width - 60),
+ MIN (expected_drawing_area_height + 90, screen_height - 60));
+
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (window));
+ }
+}
+
+static void
+build_dialog (gchar *imagename)
+{
+ GtkWidget *toolbar;
+ GtkWidget *frame;
+ GtkWidget *viewport;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *abox;
+ GtkToolItem *item;
+ GtkAction *action;
+ GdkCursor *cursor;
+ gint index;
+ gchar *text;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_role (GTK_WINDOW (window), "animation-playback");
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (window_destroy),
+ NULL);
+ g_signal_connect (window, "popup-menu",
+ G_CALLBACK (popup_menu),
+ NULL);
+
+ gimp_help_connect (window, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ ui_manager = ui_manager_new (window);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (window), main_vbox);
+ gtk_widget_show (main_vbox);
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/anim-play-toolbar");
+ gtk_box_pack_start (GTK_BOX (main_vbox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ item =
+ GTK_TOOL_ITEM (gtk_ui_manager_get_widget (ui_manager,
+ "/anim-play-toolbar/space"));
+ gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
+ gtk_tool_item_set_expand (item, TRUE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* Alignment for the scrolling window, which can be resized by the user. */
+ abox = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, TRUE, TRUE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (frame),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_show (frame);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (frame), viewport);
+ gtk_widget_show (viewport);
+
+ /* I add the drawing area inside an alignment box to prevent it from being resized. */
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_container_add (GTK_CONTAINER (viewport), abox);
+ gtk_widget_show (abox);
+
+ /* Build a drawing area, with a default size same as the image */
+ drawing_area = gtk_drawing_area_new ();
+ gtk_widget_add_events (drawing_area, GDK_BUTTON_PRESS_MASK);
+ gtk_container_add (GTK_CONTAINER (abox), drawing_area);
+ gtk_widget_show (drawing_area);
+
+ g_signal_connect (drawing_area, "size-allocate",
+ G_CALLBACK(da_size_callback),
+ NULL);
+ g_signal_connect (drawing_area, "button-press-event",
+ G_CALLBACK (button_press),
+ NULL);
+
+ /* Lower option bar. */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Progress bar. */
+
+ progress = gtk_progress_bar_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), progress, TRUE, TRUE, 0);
+ gtk_widget_show (progress);
+
+ /* Zoom */
+ zoomcombo = gtk_combo_box_text_new_with_entry ();
+ gtk_box_pack_end (GTK_BOX (hbox), zoomcombo, FALSE, FALSE, 0);
+ gtk_widget_show (zoomcombo);
+ for (index = 0; index < 5; index++)
+ {
+ /* list is given in "fps" - frames per second */
+ text = g_strdup_printf (_("%.1f %%"), get_scale (index) * 100.0);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (zoomcombo), text);
+ g_free (text);
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (zoomcombo), 2); /* 1.0 by default. */
+
+ g_signal_connect (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (zoomcombo))),
+ "activate",
+ G_CALLBACK (zoomcombo_activated),
+ NULL);
+ g_signal_connect (zoomcombo, "changed",
+ G_CALLBACK (zoomcombo_changed),
+ NULL);
+
+ gimp_help_set_help_data (zoomcombo, _("Zoom"), NULL);
+
+ /* fps combo */
+ fpscombo = gtk_combo_box_text_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), fpscombo, FALSE, FALSE, 0);
+ gtk_widget_show (fpscombo);
+
+ for (index = 0; index < 9; index++)
+ {
+ /* list is given in "fps" - frames per second */
+ text = g_strdup_printf (_("%d fps"), get_fps (index));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (fpscombo), text);
+ g_free (text);
+ if (settings.default_frame_duration == 1000 / get_fps(index))
+ gtk_combo_box_set_active (GTK_COMBO_BOX (fpscombo), index);
+ }
+
+ g_signal_connect (fpscombo, "changed",
+ G_CALLBACK (fpscombo_changed),
+ NULL);
+
+ gimp_help_set_help_data (fpscombo, _("Default framerate"), NULL);
+
+ /* Speed Combo */
+ speedcombo = gtk_combo_box_text_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), speedcombo, FALSE, FALSE, 0);
+ gtk_widget_show (speedcombo);
+
+ for (index = 0; index < 7; index++)
+ {
+ text = g_strdup_printf ("%g\303\227", (100 / get_duration_factor (index)) / 100);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (speedcombo), text);
+ g_free (text);
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (speedcombo), settings.duration_index);
+
+ g_signal_connect (speedcombo, "changed",
+ G_CALLBACK (speedcombo_changed),
+ NULL);
+
+ gimp_help_set_help_data (speedcombo, _("Playback speed"), NULL);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-reset");
+ gtk_action_set_sensitive (action, FALSE);
+
+ /* Set up the frame disposal combo. */
+ frame_disposal_combo = gtk_combo_box_text_new ();
+
+ /* 2 styles of default frame disposals: cumulative layers and one frame per layer. */
+ text = g_strdup (_("Cumulative layers (combine)"));
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (frame_disposal_combo), DISPOSE_COMBINE, text);
+ g_free (text);
+
+ text = g_strdup (_("One frame per layer (replace)"));
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (frame_disposal_combo), DISPOSE_REPLACE, text);
+ g_free (text);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (frame_disposal_combo), settings.default_frame_disposal);
+
+ g_signal_connect (frame_disposal_combo, "changed",
+ G_CALLBACK (framecombo_changed),
+ NULL);
+
+ gtk_box_pack_end (GTK_BOX (hbox), frame_disposal_combo, FALSE, FALSE, 0);
+ gtk_widget_show (frame_disposal_combo);
+
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (window), width + 20, height + 90);
+ gtk_widget_show (window);
+
+ /* shape_drawing_area for detached feature. */
+ shape_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_window_set_resizable (GTK_WINDOW (shape_window), FALSE);
+
+ shape_drawing_area = gtk_drawing_area_new ();
+ gtk_container_add (GTK_CONTAINER (shape_window), shape_drawing_area);
+ gtk_widget_show (shape_drawing_area);
+ gtk_widget_add_events (shape_drawing_area, GDK_BUTTON_PRESS_MASK);
+ gtk_widget_realize (shape_drawing_area);
+
+ gdk_window_set_back_pixmap (gtk_widget_get_window (shape_window), NULL, FALSE);
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (shape_window),
+ GDK_HAND2);
+ gdk_window_set_cursor (gtk_widget_get_window (shape_window), cursor);
+ gdk_cursor_unref (cursor);
+
+ g_signal_connect(shape_drawing_area, "size-allocate",
+ G_CALLBACK(sda_size_callback),
+ NULL);
+ g_signal_connect (shape_window, "button-press-event",
+ G_CALLBACK (shape_pressed),
+ NULL);
+ g_signal_connect (shape_window, "button-release-event",
+ G_CALLBACK (shape_released),
+ NULL);
+ g_signal_connect (shape_window, "motion-notify-event",
+ G_CALLBACK (shape_motion),
+ NULL);
+
+ g_object_set_data (G_OBJECT (shape_window),
+ "cursor-offset", g_new0 (CursorOffset, 1));
+
+ g_signal_connect (drawing_area, "expose-event",
+ G_CALLBACK (repaint_da),
+ NULL);
+
+ g_signal_connect (shape_drawing_area, "expose-event",
+ G_CALLBACK (repaint_sda),
+ NULL);
+
+ /* We request a minimum size *after* having connecting the
+ * size-allocate signal for correct initialization. */
+ gtk_widget_set_size_request (drawing_area, width, height);
+ gtk_widget_set_size_request (shape_drawing_area, width, height);
+
+ root_win = gdk_get_default_root_window ();
+}
+
+static void
+init_frames (void)
+{
+ /* Frames are associated to an unused image. */
+ gint i;
+ gint32 new_frame, previous_frame, new_layer;
+ gboolean animated;
+ GtkAction *action;
+ gint duration = 0;
+ DisposeType disposal = settings.default_frame_disposal;
+ gchar *layer_name;
+
+ total_frames = total_layers;
+
+ /* Cleanup before re-generation. */
+ if (frames)
+ {
+ gimp_image_delete (frames_image_id);
+ g_free (frames);
+ g_free (frame_durations);
+ }
+ frames = g_try_malloc0_n (total_frames, sizeof (gint32));
+ frame_durations = g_try_malloc0_n (total_frames, sizeof (guint32));
+ if (! frames || ! frame_durations)
+ {
+ gimp_message (_("Memory could not be allocated to the frame container."));
+ gtk_main_quit ();
+ gimp_quit ();
+ return;
+ }
+ /* We only use RGB images for display because indexed images would somehow
+ render terrible colors. Layers from other types will be automatically
+ converted. */
+ frames_image_id = gimp_image_new (width, height, GIMP_RGB);
+ /* Save processing time and memory by not saving history and merged frames. */
+ gimp_image_undo_disable (frames_image_id);
+
+ for (i = 0; i < total_frames; i++)
+ {
+ layer_name = gimp_item_get_name (layers[total_layers - (i + 1)]);
+ if (layer_name)
+ {
+ duration = parse_ms_tag (layer_name);
+ disposal = parse_disposal_tag (layer_name);
+ g_free (layer_name);
+ }
+
+ if (i > 0 && disposal != DISPOSE_REPLACE)
+ {
+ previous_frame = gimp_layer_copy (frames[i - 1]);
+ gimp_image_insert_layer (frames_image_id, previous_frame, 0, -1);
+ gimp_item_set_visible (previous_frame, TRUE);
+ }
+ new_layer = gimp_layer_new_from_drawable (layers[total_layers - (i + 1)], frames_image_id);
+ gimp_image_insert_layer (frames_image_id, new_layer, 0, -1);
+ gimp_item_set_visible (new_layer, TRUE);
+ new_frame = gimp_image_merge_visible_layers (frames_image_id, GIMP_CLIP_TO_IMAGE);
+ frames[i] = new_frame;
+ gimp_item_set_visible (new_frame, FALSE);
+
+ if (duration <= 0)
+ duration = settings.default_frame_duration;
+ frame_durations[i] = (guint32) duration;
+ }
+
+ /* Update the UI. */
+ animated = total_frames >= 2;
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/play");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/step-back");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/step");
+ gtk_action_set_sensitive (action, animated);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/anim-play-toolbar/rewind");
+ gtk_action_set_sensitive (action, animated);
+
+ /* Keep the same frame number, unless it is now invalid. */
+ if (frame_number >= total_frames)
+ frame_number = 0;
+}
+
+static void
+initialize (void)
+{
+ /* Freeing existing data after a refresh. */
+ g_free (layers);
+
+ /* Catch the case when the user has closed the image in the meantime. */
+ if (! gimp_image_is_valid (image_id))
+ {
+ gimp_message (_("Invalid image. Did you close it?"));
+ gtk_main_quit ();
+ return;
+ }
+
+ width = gimp_image_width (image_id);
+ height = gimp_image_height (image_id);
+ layers = gimp_image_get_layers (image_id, &total_layers);
+
+ if (!window)
+ build_dialog (gimp_image_get_name (image_id));
+ refresh_dialog (gimp_image_get_name (image_id));
+
+ init_frames ();
+ render_frame (frame_number);
+ show_frame ();
+}
+
+/* Rendering Functions */
+
+static void
+render_frame (gint32 whichframe)
+{
+ GeglBuffer *buffer;
+ gint i, j, k;
+ guchar *srcptr;
+ guchar *destptr;
+ GtkWidget *da;
+ guint drawing_width, drawing_height;
+ gdouble drawing_scale;
+ guchar *preview_data;
+
+ g_assert (whichframe < total_frames);
+
+ if (detached)
+ {
+ da = shape_drawing_area;
+ preview_data = shape_drawing_area_data;
+ drawing_width = shape_drawing_area_width;
+ drawing_height = shape_drawing_area_height;
+ drawing_scale = shape_scale;
+ }
+ else
+ {
+ da = drawing_area;
+ preview_data = drawing_area_data;
+ drawing_width = drawing_area_width;
+ drawing_height = drawing_area_height;
+ drawing_scale = scale;
+
+ /* Set "alpha grid" background. */
+ total_alpha_preview ();
+ }
+
+ buffer = gimp_drawable_get_buffer (frames[whichframe]);
+
+ /* Fetch and scale the whole raw new frame */
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, drawing_width, drawing_height),
+ drawing_scale, babl_format ("R'G'B'A u8"),
+ rawframe, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_CLAMP);
+
+ /* Number of pixels. */
+ i = drawing_width * drawing_height;
+ destptr = preview_data;
+ srcptr = rawframe;
+ while (i--)
+ {
+ if (! (srcptr[3] & 128))
+ {
+ srcptr += 4;
+ destptr += 3;
+ continue;
+ }
+
+ *(destptr++) = *(srcptr++);
+ *(destptr++) = *(srcptr++);
+ *(destptr++) = *(srcptr++);
+
+ srcptr++;
+ }
+
+ /* calculate the shape mask */
+ if (detached)
+ {
+ memset (shape_preview_mask, 0, (drawing_width * drawing_height) / 8 + drawing_height);
+ srcptr = rawframe + 3;
+
+ for (j = 0; j < drawing_height; j++)
+ {
+ k = j * ((7 + drawing_width) / 8);
+
+ for (i = 0; i < drawing_width; i++)
+ {
+ if ((*srcptr) & 128)
+ shape_preview_mask[k + i/8] |= (1 << (i&7));
+
+ srcptr += 4;
+ }
+ }
+ reshape_from_bitmap (shape_preview_mask);
+ }
+
+ /* Display the preview buffer. */
+ if (gtk_widget_get_realized (da))
+#ifndef PLATFORM_OSX
+ gdk_draw_rgb_image (gtk_widget_get_window (da),
+ (gtk_widget_get_style (da))->white_gc,
+ (gint) ((drawing_width - drawing_scale * width) / 2),
+ (gint) ((drawing_height - drawing_scale * height) / 2),
+ drawing_width, drawing_height,
+ (total_frames == 1 ?
+ GDK_RGB_DITHER_MAX : DITHERTYPE),
+ preview_data, drawing_width * 3);
+#else
+ gtk_widget_queue_draw (da);
+#endif /* PLATFORM_OSX */
+
+ /* clean up */
+ g_object_unref (buffer);
+}
+
+static void
+show_frame (void)
+{
+ gchar *text;
+
+ /* update the dialog's progress bar */
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
+ ((gfloat) frame_number /
+ (gfloat) (total_frames - 0.999)));
+
+ text = g_strdup_printf (_("Frame %d of %d"), frame_number + 1, total_frames);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (progress), text);
+ g_free (text);
+}
+
+static void
+update_alpha_preview (void)
+{
+ gint i;
+
+ g_free (preview_alpha1_data);
+ g_free (preview_alpha2_data);
+
+ preview_alpha1_data = g_malloc (drawing_area_width * 3);
+ preview_alpha2_data = g_malloc (drawing_area_width * 3);
+
+ for (i = 0; i < drawing_area_width; i++)
+ {
+ if (i & 8)
+ {
+ preview_alpha1_data[i*3 + 0] =
+ preview_alpha1_data[i*3 + 1] =
+ preview_alpha1_data[i*3 + 2] = 102;
+ preview_alpha2_data[i*3 + 0] =
+ preview_alpha2_data[i*3 + 1] =
+ preview_alpha2_data[i*3 + 2] = 154;
+ }
+ else
+ {
+ preview_alpha1_data[i*3 + 0] =
+ preview_alpha1_data[i*3 + 1] =
+ preview_alpha1_data[i*3 + 2] = 154;
+ preview_alpha2_data[i*3 + 0] =
+ preview_alpha2_data[i*3 + 1] =
+ preview_alpha2_data[i*3 + 2] = 102;
+ }
+ }
+}
+
+static void
+total_alpha_preview (void)
+{
+ gint i;
+
+ for (i = 0; i < drawing_area_height; i++)
+ {
+ if (i & 8)
+ memcpy (&drawing_area_data[i * 3 * drawing_area_width], preview_alpha1_data, 3 * drawing_area_width);
+ else
+ memcpy (&drawing_area_data[i * 3 * drawing_area_width], preview_alpha2_data, 3 * drawing_area_width);
+ }
+}
+
+/* Util. */
+
+static void
+remove_timer (void)
+{
+ if (timer)
+ {
+ g_source_remove (timer);
+ timer = 0;
+ }
+}
+
+static void
+do_back_step (void)
+{
+ if (frame_number == 0)
+ frame_number = total_frames - 1;
+ else
+ frame_number = (frame_number - 1) % total_frames;
+ render_frame (frame_number);
+}
+
+static void
+do_step (void)
+{
+ frame_number = (frame_number + 1) % total_frames;
+ render_frame (frame_number);
+}
+
+
+/* Callbacks */
+
+static void
+window_destroy (GtkWidget *widget)
+{
+ if (playing)
+ remove_timer ();
+
+ if (shape_window)
+ gtk_widget_destroy (GTK_WIDGET (shape_window));
+
+ gtk_main_quit ();
+}
+
+
+static gint
+advance_frame_callback (gpointer data)
+{
+ gdouble duration;
+
+ remove_timer();
+
+ duration = frame_durations[(frame_number + 1) % total_frames];
+
+ timer = g_timeout_add (duration * get_duration_factor (settings.duration_index),
+ advance_frame_callback, NULL);
+
+ do_step ();
+ show_frame ();
+
+ return FALSE;
+}
+
+
+static void
+play_callback (GtkToggleAction *action)
+{
+ if (playing)
+ remove_timer ();
+
+ playing = gtk_toggle_action_get_active (action);
+
+ if (playing)
+ {
+ timer = g_timeout_add ((gdouble) frame_durations[frame_number] *
+ get_duration_factor (settings.duration_index),
+ advance_frame_callback, NULL);
+
+ gtk_action_set_icon_name (GTK_ACTION (action), "media-playback-pause");
+ }
+ else
+ {
+ gtk_action_set_icon_name (GTK_ACTION (action), "media-playback-start");
+ }
+
+ g_object_set (action,
+ "tooltip", playing ? _("Stop playback") : _("Start playback"),
+ NULL);
+}
+
+static gdouble
+get_duration_factor (gint index)
+{
+ switch (index)
+ {
+ case 0:
+ return 0.125;
+ case 1:
+ return 0.25;
+ case 2:
+ return 0.5;
+ case 3:
+ return 1.0;
+ case 4:
+ return 2.0;
+ case 5:
+ return 4.0;
+ case 6:
+ return 8.0;
+ default:
+ return 1.0;
+ }
+}
+
+static gint
+get_fps (gint index)
+{
+ switch (index)
+ {
+ case 0:
+ return 10;
+ case 1:
+ return 12;
+ case 2:
+ return 15;
+ case 3:
+ return 24;
+ case 4:
+ return 25;
+ case 5:
+ return 30;
+ case 6:
+ return 50;
+ case 7:
+ return 60;
+ case 8:
+ return 72;
+ default:
+ return 10;
+ }
+}
+
+static gdouble
+get_scale (gint index)
+{
+ switch (index)
+ {
+ case 0:
+ return 0.51;
+ case 1:
+ return 1.0;
+ case 2:
+ return 1.25;
+ case 3:
+ return 1.5;
+ case 4:
+ return 2.0;
+ default:
+ {
+ /* likely -1 returned if there is no active item from the list.
+ * Try a text conversion, locale-aware in such a case, assuming people write in percent. */
+ gchar* active_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (zoomcombo));
+ gdouble zoom = g_strtod (active_text, NULL);
+
+ /* Negative scales are inconsistent. And we want to avoid huge scaling. */
+ if (zoom > 400.0)
+ zoom = 400.0;
+ else if (zoom <= 50.0)
+ /* FIXME: scales under 0.5 are broken. See bug 690265. */
+ zoom = 50.1;
+ g_free (active_text);
+ return zoom / 100.0;
+ }
+ }
+}
+
+static void
+step_back_callback (GtkAction *action)
+{
+ if (playing)
+ gtk_action_activate (gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-toolbar/play"));
+ do_back_step();
+ show_frame();
+}
+
+static void
+step_callback (GtkAction *action)
+{
+ if (playing)
+ gtk_action_activate (gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-toolbar/play"));
+ do_step();
+ show_frame();
+}
+
+static void
+refresh_callback (GtkAction *action)
+{
+ initialize ();
+}
+
+static void
+rewind_callback (GtkAction *action)
+{
+ if (playing)
+ gtk_action_activate (gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-toolbar/play"));
+ frame_number = 0;
+ render_frame (frame_number);
+ show_frame ();
+}
+
+static void
+speed_up_callback (GtkAction *action)
+{
+ if (settings.duration_index > 0)
+ --settings.duration_index;
+
+ gtk_action_set_sensitive (action, settings.duration_index > 0);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-reset");
+ gtk_action_set_sensitive (action, settings.duration_index != 3);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-down");
+ gtk_action_set_sensitive (action, TRUE);
+
+ update_combobox ();
+}
+
+static void
+speed_down_callback (GtkAction *action)
+{
+ if (settings.duration_index < 6)
+ ++settings.duration_index;
+
+ gtk_action_set_sensitive (action, settings.duration_index < 6);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-reset");
+ gtk_action_set_sensitive (action, settings.duration_index != 3);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-up");
+ gtk_action_set_sensitive (action, TRUE);
+
+ update_combobox ();
+}
+
+static void
+speed_reset_callback (GtkAction *action)
+{
+ settings.duration_index = 3;
+
+ gtk_action_set_sensitive (action, FALSE);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-down");
+ gtk_action_set_sensitive (action, TRUE);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-up");
+ gtk_action_set_sensitive (action, TRUE);
+
+ update_combobox ();
+}
+
+static void
+framecombo_changed (GtkWidget *combo, gpointer data)
+{
+ settings.default_frame_disposal = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+ init_frames ();
+ render_frame (frame_number);
+}
+
+static void
+speedcombo_changed (GtkWidget *combo, gpointer data)
+{
+ GtkAction * action;
+
+ settings.duration_index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-reset");
+ gtk_action_set_sensitive (action, settings.duration_index != 3);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-down");
+ gtk_action_set_sensitive (action, settings.duration_index < 6);
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/anim-play-popup/speed-up");
+ gtk_action_set_sensitive (action, settings.duration_index > 0);
+}
+
+static void
+update_scale (gdouble scale)
+{
+ guint expected_drawing_area_width;
+ guint expected_drawing_area_height;
+
+ /* FIXME: scales under 0.5 are broken. See bug 690265. */
+ if (scale <= 0.5)
+ scale = 0.501;
+
+ expected_drawing_area_width = width * scale;
+ expected_drawing_area_height = height * scale;
+
+ gtk_widget_set_size_request (drawing_area, expected_drawing_area_width, expected_drawing_area_height);
+ gtk_widget_set_size_request (shape_drawing_area, expected_drawing_area_width, expected_drawing_area_height);
+ /* I force the shape window to a smaller size if we scale down. */
+ if (detached)
+ {
+ gint x, y;
+
+ gdk_window_get_origin (gtk_widget_get_window (shape_window), &x, &y);
+ gtk_window_reshow_with_initial_size (GTK_WINDOW (shape_window));
+ gtk_window_move (GTK_WINDOW (shape_window), x, y);
+ }
+}
+
+/*
+ * Callback emitted when the user hits the Enter key of the zoom combo.
+ */
+static void
+zoomcombo_activated (GtkEntry *combo, gpointer data)
+{
+ update_scale (get_scale (-1));
+}
+
+/*
+ * Callback emitted when the user selects a zoom in the dropdown,
+ * or edits the text entry.
+ * We don't want to process manual edits because it greedily emits
+ * signals after each character deleted or added.
+ */
+static void
+zoomcombo_changed (GtkWidget *combo, gpointer data)
+{
+ gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ /* If no index, user is probably editing by hand. We wait for him to click "Enter". */
+ if (index != -1)
+ update_scale (get_scale (index));
+}
+
+static void
+fpscombo_changed (GtkWidget *combo, gpointer data)
+{
+ settings.default_frame_duration = 1000 / get_fps (gtk_combo_box_get_active (GTK_COMBO_BOX (combo)));
+}
+
+static void
+update_combobox (void)
+{
+ gtk_combo_box_set_active (GTK_COMBO_BOX (speedcombo), settings.duration_index);
+}
+
+/* tag util. */
+
+static gboolean
+is_ms_tag (const gchar *str,
+ gint *duration,
+ gint *taglength)
+{
+ gint sum = 0;
+ gint offset;
+ gint length;
+
+ length = strlen(str);
+
+ if (str[0] != '(')
+ return FALSE;
+
+ offset = 1;
+
+ /* eat any spaces between open-parenthesis and number */
+ while ((offset < length) && (str[offset] == ' '))
+ offset++;
+
+ if ((offset>=length) || (!g_ascii_isdigit (str[offset])))
+ return FALSE;
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset<length) && (g_ascii_isdigit (str[offset])));
+
+ if (length - offset <= 2)
+ return FALSE;
+
+ /* eat any spaces between number and 'ms' */
+ while ((offset < length) && (str[offset] == ' '))
+ offset++;
+
+ if (length - offset <= 2 ||
+ g_ascii_toupper (str[offset]) != 'M' ||
+ g_ascii_toupper (str[offset + 1]) != 'S')
+ return FALSE;
+
+ offset += 2;
+
+ /* eat any spaces between 'ms' and close-parenthesis */
+ while ((offset < length) && (str[offset] == ' '))
+ offset++;
+
+ if ((length - offset < 1) || (str[offset] != ')'))
+ return FALSE;
+
+ offset++;
+
+ *duration = sum;
+ *taglength = offset;
+
+ return TRUE;
+}
+
+static gint
+parse_ms_tag (const gchar *str)
+{
+ gint i;
+ gint length = strlen (str);
+
+ for (i = 0; i < length; i++)
+ {
+ gint rtn;
+ gint dummy;
+
+ if (is_ms_tag (&str[i], &rtn, &dummy))
+ return rtn;
+ }
+
+ return -1;
+}
+
+static gboolean
+is_disposal_tag (const gchar *str,
+ DisposeType *disposal,
+ gint *taglength)
+{
+ if (strlen (str) != 9)
+ return FALSE;
+
+ if (strncmp (str, "(combine)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_COMBINE;
+ return TRUE;
+ }
+ else if (strncmp (str, "(replace)", 9) == 0)
+ {
+ *taglength = 9;
+ *disposal = DISPOSE_REPLACE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static DisposeType
+parse_disposal_tag (const gchar *str)
+{
+ gint i;
+ gint length = strlen (str);
+
+ for (i = 0; i < length; i++)
+ {
+ DisposeType rtn;
+ gint dummy;
+
+ if (is_disposal_tag (&str[i], &rtn, &dummy))
+ return rtn;
+ }
+
+ return settings.default_frame_disposal;
+}
+
diff --git a/plug-ins/common/blinds.c b/plug-ins/common/blinds.c
new file mode 100644
index 0000000..a0ceeea
--- /dev/null
+++ b/plug-ins/common/blinds.c
@@ -0,0 +1,730 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Blinds plug-in. Distort an image as though it was stuck to
+ * window blinds and the blinds where opened/closed.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * A fair proprotion of this code was taken from the Whirl plug-in
+ * which was copyrighted by Federico Mena Quintero (as below).
+ *
+ * Whirl plug-in --- distort an image into a whirlpool
+ * Copyright (C) 1997 Federico Mena Quintero
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/***** Magic numbers *****/
+
+#define PLUG_IN_PROC "plug-in-blinds"
+#define PLUG_IN_BINARY "blinds"
+#define PLUG_IN_ROLE "gimp-blinds"
+
+#define SCALE_WIDTH 150
+
+#define MAX_FANS 100
+
+/* Variables set in dialog box */
+typedef struct data
+{
+ gint angledsp;
+ gint numsegs;
+ GimpOrientationType orientation;
+ gboolean bg_trans;
+} BlindVals;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean blinds_dialog (gint32 drawable_id);
+
+static void dialog_update_preview (gpointer drawable_id,
+ GimpPreview *preview);
+static void apply_blinds (gint32 drawable_id);
+
+
+/* Array to hold each size of fans. And no there are not each the
+ * same size (rounding errors...)
+ */
+
+static gint fanwidths[MAX_FANS];
+
+/* Values when first invoked */
+static BlindVals bvals =
+{
+ 30,
+ 3,
+ GIMP_ORIENTATION_HORIZONTAL,
+ FALSE
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "angle-dsp", "Angle of Displacement" },
+ { GIMP_PDB_INT32, "num-segments", "Number of segments in blinds" },
+ { GIMP_PDB_INT32, "orientation", "The orientation { ORIENTATION-HORIZONTAL (0), ORIENTATION-VERTICAL (1) }" },
+ { GIMP_PDB_INT32, "bg-transparent", "Background transparent { FALSE, TRUE }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simulate an image painted on window blinds"),
+ "More here later",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1997",
+ N_("_Blinds..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 drawable_id;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &bvals);
+ if (! blinds_dialog (drawable_id))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 7)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ bvals.angledsp = param[3].data.d_int32;
+ bvals.numsegs = param[4].data.d_int32;
+ bvals.orientation = param[5].data.d_int32;
+ bvals.bg_trans = param[6].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &bvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id))
+ {
+ gimp_progress_init (_("Adding blinds"));
+
+ apply_blinds (drawable_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &bvals, sizeof (BlindVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gboolean
+blinds_dialog (gint32 drawable_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkObject *size_data;
+ GtkWidget *toggle;
+ GtkWidget *horizontal;
+ GtkWidget *vertical;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Blinds"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_aspect_preview_new_from_drawable_id (drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (dialog_update_preview),
+ GINT_TO_POINTER (drawable_id));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame =
+ gimp_int_radio_group_new (TRUE, _("Orientation"),
+ G_CALLBACK (gimp_radio_button_update),
+ &bvals.orientation, bvals.orientation,
+
+ _("_Horizontal"), GIMP_ORIENTATION_HORIZONTAL,
+ &horizontal,
+
+ _("_Vertical"), GIMP_ORIENTATION_VERTICAL,
+ &vertical,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ g_signal_connect_swapped (horizontal, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (vertical, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ frame = gimp_frame_new (_("Background"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Transparent"));
+ gtk_container_add (GTK_CONTAINER (frame), toggle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &bvals.bg_trans);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bvals.bg_trans);
+
+ if (! gimp_drawable_has_alpha (drawable_id))
+ {
+ gtk_widget_set_sensitive (toggle, FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Displacement:"), SCALE_WIDTH, 0,
+ bvals.angledsp, 1, 90, 1, 15, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &bvals.angledsp);
+ g_signal_connect_swapped (size_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Number of segments:"), SCALE_WIDTH, 0,
+ bvals.numsegs, 1, MAX_FANS, 1, 2, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &bvals.numsegs);
+ g_signal_connect_swapped (size_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+blindsapply (guchar *srow,
+ guchar *drow,
+ gint width,
+ gint bpp,
+ guchar *bg)
+{
+ guchar *src;
+ guchar *dst;
+ gint i,j,k;
+ gdouble ang;
+ gint available;
+
+ /* Make the row 'shrink' around points along its length */
+ /* The bvals.numsegs determines how many segments to slip it in to */
+ /* The angle is the conceptual 'rotation' of each of these segments */
+
+ /* Note the row is considered to be made up of a two dim array actual
+ * pixel locations and the RGB color at these locations.
+ */
+
+ /* In the process copy the src row to the destination row */
+
+ /* Fill in with background color ? */
+ for (i = 0 ; i < width ; i++)
+ {
+ dst = &drow[i*bpp];
+
+ for (j = 0 ; j < bpp; j++)
+ {
+ dst[j] = bg[j];
+ }
+ }
+
+ /* Apply it */
+
+ available = width;
+ for (i = 0; i < bvals.numsegs; i++)
+ {
+ /* Width of segs are variable */
+ fanwidths[i] = available / (bvals.numsegs - i);
+ available -= fanwidths[i];
+ }
+
+ /* do center points first - just for fun...*/
+ available = 0;
+ for (k = 1; k <= bvals.numsegs; k++)
+ {
+ int point;
+
+ point = available + fanwidths[k - 1] / 2;
+
+ available += fanwidths[k - 1];
+
+ src = &srow[point * bpp];
+ dst = &drow[point * bpp];
+
+ /* Copy pixels across */
+ for (j = 0 ; j < bpp; j++)
+ {
+ dst[j] = src[j];
+ }
+ }
+
+ /* Disp for each point */
+ ang = (bvals.angledsp * 2 * G_PI) / 360; /* Angle in rads */
+ ang = (1 - fabs (cos (ang)));
+
+ available = 0;
+ for (k = 0 ; k < bvals.numsegs; k++)
+ {
+ int dx; /* Amount to move by */
+ int fw;
+
+ for (i = 0 ; i < (fanwidths[k]/2) ; i++)
+ {
+ /* Copy pixels across of left half of fan */
+ fw = fanwidths[k] / 2;
+ dx = (int) (ang * ((double) (fw - (double)(i % fw))));
+
+ src = &srow[(available + i) * bpp];
+ dst = &drow[(available + i + dx) * bpp];
+
+ for (j = 0; j < bpp; j++)
+ {
+ dst[j] = src[j];
+ }
+
+ /* Right side */
+ j = i + 1;
+ src = &srow[(available + fanwidths[k] - j
+ - (fanwidths[k] % 2)) * bpp];
+ dst = &drow[(available + fanwidths[k] - j
+ - (fanwidths[k] % 2) - dx) * bpp];
+
+ for (j = 0; j < bpp; j++)
+ {
+ dst[j] = src[j];
+ }
+ }
+
+ available += fanwidths[k];
+ }
+}
+
+static void
+dialog_update_preview (gpointer drawable_ptr,
+ GimpPreview *preview)
+{
+ gint32 drawable_id = GPOINTER_TO_INT (drawable_ptr);
+ gint y;
+ guchar *p, *buffer, *cache;
+ GimpRGB background;
+ guchar bg[4];
+ gint width, height, bpp;
+
+ gimp_preview_get_size (preview, &width, &height);
+ cache = gimp_drawable_get_thumbnail_data (drawable_id,
+ &width, &height, &bpp);
+ p = cache;
+
+ gimp_context_get_background (&background);
+
+ if (bvals.bg_trans)
+ gimp_rgb_set_alpha (&background, 0.0);
+
+ if (gimp_drawable_is_gray (drawable_id))
+ {
+ bg[0] = gimp_rgb_luminance_uchar (&background);
+ gimp_rgba_get_uchar (&background, NULL, NULL, NULL, bg + 3);
+ }
+ else
+ {
+ gimp_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
+ }
+
+ buffer = g_new (guchar, width * height * bpp);
+
+ if (bvals.orientation == GIMP_ORIENTATION_VERTICAL)
+ {
+ for (y = 0; y < height; y++)
+ {
+ blindsapply (p,
+ buffer + y * width * bpp,
+ width,
+ bpp, bg);
+ p += width * bpp;
+ }
+ }
+ else
+ {
+ /* Horizontal blinds */
+ /* Apply the blinds algo to a single column -
+ * this act as a transfomation matrix for the
+ * rows. Make row 0 invalid so we can find it again!
+ */
+ gint i;
+ guchar *sr = g_new (guchar, height * 4);
+ guchar *dr = g_new0 (guchar, height * 4);
+ guchar dummybg[4] = {0, 0, 0, 0};
+
+ /* Fill in with background color ? */
+ for (i = 0 ; i < width ; i++)
+ {
+ gint j;
+ gint bd = bpp;
+ guchar *dst;
+ dst = &buffer[i * bd];
+
+ for (j = 0 ; j < bd; j++)
+ {
+ dst[j] = bg[j];
+ }
+ }
+
+ for ( y = 0 ; y < height; y++)
+ {
+ sr[y] = y+1;
+ }
+
+ /* Bit of a fiddle since blindsapply really works on an image
+ * row not a set of bytes. - preview can't be > 255
+ * or must make dr sr int rows.
+ */
+ blindsapply (sr, dr, height, 1, dummybg);
+
+ for (y = 0; y < height; y++)
+ {
+ if (dr[y] == 0)
+ {
+ /* Draw background line */
+ p = buffer;
+ }
+ else
+ {
+ /* Draw line from src */
+ p = cache +
+ (width * bpp * (dr[y] - 1));
+ }
+ memcpy (buffer + y * width * bpp,
+ p,
+ width * bpp);
+ }
+ g_free (sr);
+ g_free (dr);
+ }
+
+ gimp_preview_draw_buffer (preview, buffer, width * bpp);
+
+ g_free (buffer);
+ g_free (cache);
+}
+
+/* STEP tells us how many rows/columns to gulp down in one go... */
+/* Note all the "4" literals around here are to do with the depths
+ * of the images. Makes it easier to deal with for my small brain.
+ */
+
+#define STEP 40
+
+static void
+apply_blinds (gint32 drawable_id)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ guchar *src_rows, *des_rows;
+ gint bytes;
+ gint x, y;
+ GimpRGB background;
+ guchar bg[4];
+ gint sel_x1, sel_y1;
+ gint sel_width, sel_height;
+
+ gimp_context_get_background (&background);
+
+ if (bvals.bg_trans)
+ gimp_rgb_set_alpha (&background, 0.0);
+
+ gimp_rgba_get_uchar (&background, bg, bg + 1, bg + 2, bg + 3);
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &sel_x1, &sel_y1,
+ &sel_width, &sel_height))
+ return;
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ src_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
+ des_rows = g_new (guchar, MAX (sel_width, sel_height) * bytes * STEP);
+
+ if (bvals.orientation == GIMP_ORIENTATION_VERTICAL)
+ {
+ for (y = 0; y < sel_height; y += STEP)
+ {
+ gint rr;
+ gint step;
+
+ if ((y + STEP) > sel_height)
+ step = sel_height - y;
+ else
+ step = STEP;
+
+ gegl_buffer_get (src_buffer,
+ GEGL_RECTANGLE (sel_x1, sel_y1 + y,
+ sel_width, step), 1.0,
+ format, src_rows,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* OK I could make this better */
+ for (rr = 0; rr < STEP; rr++)
+ blindsapply (src_rows + (sel_width * rr * bytes),
+ des_rows + (sel_width * rr * bytes),
+ sel_width, bytes, bg);
+
+ gegl_buffer_set (dest_buffer,
+ GEGL_RECTANGLE (sel_x1, sel_y1 + y,
+ sel_width, step), 0,
+ format, des_rows,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) y / (double) sel_height);
+ }
+ }
+ else
+ {
+ /* Horizontal blinds */
+ /* Apply the blinds algo to a single column -
+ * this act as a transfomation matrix for the
+ * rows. Make row 0 invalid so we can find it again!
+ */
+ gint i;
+ gint *sr = g_new (gint, sel_height * bytes);
+ gint *dr = g_new (gint, sel_height * bytes);
+ guchar *dst = g_new (guchar, STEP * bytes);
+ guchar dummybg[4];
+
+ memset (dummybg, 0, 4);
+ memset (dr, 0, sel_height * bytes); /* all dr rows are background rows */
+ for (y = 0; y < sel_height; y++)
+ {
+ sr[y] = y+1;
+ }
+
+ /* Hmmm. does this work portably? */
+ /* This "swaps" the integers around that are held in in the
+ * sr & dr arrays.
+ */
+ blindsapply ((guchar *) sr, (guchar *) dr,
+ sel_height, sizeof (gint), dummybg);
+
+ /* Fill in with background color ? */
+ for (i = 0 ; i < STEP ; i++)
+ {
+ int j;
+ guchar *bgdst;
+ bgdst = &dst[i * bytes];
+
+ for (j = 0 ; j < bytes; j++)
+ {
+ bgdst[j] = bg[j];
+ }
+ }
+
+ for (x = 0; x < sel_width; x += STEP)
+ {
+ int rr;
+ int step;
+ guchar *p;
+
+ if((x + STEP) > sel_width)
+ step = sel_width - x;
+ else
+ step = STEP;
+
+ gegl_buffer_get (src_buffer,
+ GEGL_RECTANGLE (sel_x1 + x, sel_y1,
+ step, sel_height), 1.0,
+ format, src_rows,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* OK I could make this better */
+ for (rr = 0; rr < sel_height; rr++)
+ {
+ if(dr[rr] == 0)
+ {
+ /* Draw background line */
+ p = dst;
+ }
+ else
+ {
+ /* Draw line from src */
+ p = src_rows + (step * bytes * (dr[rr] - 1));
+ }
+ memcpy (des_rows + (rr * step * bytes), p,
+ step * bytes);
+ }
+
+ gegl_buffer_set (dest_buffer,
+ GEGL_RECTANGLE (sel_x1 + x, sel_y1,
+ step, sel_height), 0,
+ format, des_rows,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) x / (double) sel_width);
+ }
+
+ g_free (dst);
+ g_free (sr);
+ g_free (dr);
+ }
+
+ g_free (src_rows);
+ g_free (des_rows);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id,
+ sel_x1, sel_y1, sel_width, sel_height);
+}
diff --git a/plug-ins/common/blur.c b/plug-ins/common/blur.c
new file mode 100644
index 0000000..82aa86c
--- /dev/null
+++ b/plug-ins/common/blur.c
@@ -0,0 +1,375 @@
+/****************************************************************************
+ * This is a plugin for GIMP v 0.99.8 or later. Documentation is
+ * available at http://www.rru.com/~meo/gimp/ .
+ *
+ * Copyright (C) 1997-98 Miles O'Neal <meo@rru.com> http://www.rru.com/~meo/
+ * Blur code Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * GUI based on GTK code from:
+ * alienmap (Copyright (C) 1996, 1997 Daniel Cotting)
+ * plasma (Copyright (C) 1996 Stephen Norris),
+ * oilify (Copyright (C) 1996 Torsten Martinsen),
+ * ripple (Copyright (C) 1997 Brian Degenhardt) and
+ * whirl (Copyright (C) 1997 Federico Mena Quintero).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Blur:
+ *
+ * blur version 2.1 (10 June 2004 WES)
+ * history
+ * 2.1 - 10 June 2004 WES
+ * removed dialog along with randomization and repeat options
+ * 2.0 - 1 May 1998 MEO
+ * based on randomize 1.7
+ *
+ * Please send any patches or suggestions to the author: meo@rru.com .
+ *
+ * Blur applies a 3x3 blurring convolution kernel to the specified drawable.
+ *
+ * For each pixel in the selection or image,
+ * whether to change the pixel is decided by picking a
+ * random number, weighted by the user's "randomization" percentage.
+ * If the random number is in range, the pixel is modified. For
+ * blurring, an average is determined from the current and adjacent
+ * pixels. *(Except for the random factor, the blur code came
+ * straight from the original S&P blur plug-in.)*
+ *
+ * This works only with RGB and grayscale images.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*********************************
+ *
+ * PLUGIN-SPECIFIC CONSTANTS
+ *
+ ********************************/
+
+#define PLUG_IN_PROC "plug-in-blur"
+
+/*********************************
+ *
+ * LOCAL FUNCTIONS
+ *
+ ********************************/
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static void blur (GimpDrawable *drawable);
+
+static inline void blur_prepare_row (GimpPixelRgn *pixel_rgn,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+
+/************************************ Guts ***********************************/
+
+MAIN ()
+
+/*********************************
+ *
+ * query() - query_proc
+ *
+ * called by GIMP to learn about this plug-in
+ *
+ ********************************/
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simple blur, fast but not very strong"),
+ "This plug-in blurs the specified drawable, using "
+ "a 3x3 blur. Indexed images are not supported.",
+ "Miles O'Neal <meo@rru.com>",
+ "Miles O'Neal, Spencer Kimball, Peter Mattis, "
+ "Torsten Martinsen, Brian Degenhardt, "
+ "Federico Mena Quintero, Stephen Norris, "
+ "Daniel Cotting",
+ "1995-1998",
+ N_("_Blur"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpDrawable *drawable;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ static GimpParam values[1];
+
+ INIT_I18N ();
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ if (strcmp (name, PLUG_IN_PROC) != 0 || nparams < 3)
+ {
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ return;
+ }
+
+ run_mode = param[0].data.d_int32;
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ /*
+ * Make sure the drawable type is appropriate.
+ */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init (_("Blurring"));
+ gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
+
+ blur (drawable);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ gimp_displays_flush ();
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+ gimp_drawable_detach (drawable);
+}
+
+
+/*********************************
+ *
+ * blur_prepare_row()
+ *
+ * Get a row of pixels. If the requested row
+ * is off the edge, clone the edge row.
+ *
+ ********************************/
+
+static inline void
+blur_prepare_row (GimpPixelRgn *pixel_rgn,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w)
+{
+ gint b;
+
+ y = CLAMP (y, 0, pixel_rgn->h - 1);
+
+ gimp_pixel_rgn_get_row (pixel_rgn, data, x, y, w);
+
+ /*
+ * Fill in edge pixels
+ */
+ for (b = 0; b < pixel_rgn->bpp; b++)
+ data[-(gint)pixel_rgn->bpp + b] = data[b];
+
+ for (b = 0; b < pixel_rgn->bpp; b++)
+ data[w * pixel_rgn->bpp + b] = data[(w - 1) * pixel_rgn->bpp + b];
+}
+
+/*********************************
+ *
+ * blur()
+ *
+ * Actually mess with the image.
+ *
+ ********************************/
+
+static void
+blur (GimpDrawable *drawable)
+{
+ GimpPixelRgn srcPR, destPR;
+ gint width, height;
+ gint bytes;
+ guchar *dest, *d;
+ guchar *prev_row, *pr;
+ guchar *cur_row, *cr;
+ guchar *next_row, *nr;
+ guchar *tmp;
+ gint row, col;
+ gint x1, y1, x2, y2;
+ gint ind;
+ gboolean has_alpha;
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ /*
+ * Get the size of the input image. (This will/must be the same
+ * as the size of the output image. Also get alpha info.
+ */
+ width = drawable->width;
+ height = drawable->height;
+ bytes = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+ /*
+ * allocate row buffers
+ */
+ prev_row = g_new (guchar, (x2 - x1 + 2) * bytes);
+ cur_row = g_new (guchar, (x2 - x1 + 2) * bytes);
+ next_row = g_new (guchar, (x2 - x1 + 2) * bytes);
+ dest = g_new (guchar, (x2 - x1) * bytes);
+
+ /*
+ * initialize the pixel regions
+ */
+ gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, drawable, 0, 0, width, height, TRUE, TRUE);
+
+ pr = prev_row + bytes;
+ cr = cur_row + bytes;
+ nr = next_row + bytes;
+
+ /*
+ * prepare the first row and previous row
+ */
+ blur_prepare_row (&srcPR, pr, x1, y1 - 1, (x2 - x1));
+ blur_prepare_row (&srcPR, cr, x1, y1, (x2 - x1));
+
+ /*
+ * loop through the rows, applying the selected convolution
+ */
+ for (row = y1; row < y2; row++)
+ {
+ /* prepare the next row */
+ blur_prepare_row (&srcPR, nr, x1, row + 1, (x2 - x1));
+
+ d = dest;
+ ind = 0;
+ for (col = 0; col < (x2 - x1) * bytes; col++)
+ {
+ ind++;
+ if (ind == bytes || !has_alpha)
+ {
+ /*
+ * If no alpha channel,
+ * or if there is one and this is it...
+ */
+ *d++ = ((gint) pr[col - bytes] + (gint) pr[col] +
+ (gint) pr[col + bytes] +
+ (gint) cr[col - bytes] + (gint) cr[col] +
+ (gint) cr[col + bytes] +
+ (gint) nr[col - bytes] + (gint) nr[col] +
+ (gint) nr[col + bytes] + 4) / 9;
+ ind = 0;
+ }
+ else
+ {
+ /*
+ * otherwise we have an alpha channel,
+ * but this is a color channel
+ */
+ *d++ = ROUND(
+ ((gdouble) (pr[col - bytes] * pr[col - ind])
+ + (gdouble) (pr[col] * pr[col + bytes - ind])
+ + (gdouble) (pr[col + bytes] * pr[col + 2*bytes - ind])
+ + (gdouble) (cr[col - bytes] * cr[col - ind])
+ + (gdouble) (cr[col] * cr[col + bytes - ind])
+ + (gdouble) (cr[col + bytes] * cr[col + 2*bytes - ind])
+ + (gdouble) (nr[col - bytes] * nr[col - ind])
+ + (gdouble) (nr[col] * nr[col + bytes - ind])
+ + (gdouble) (nr[col + bytes] * nr[col + 2*bytes - ind]))
+ / ((gdouble) pr[col - ind]
+ + (gdouble) pr[col + bytes - ind]
+ + (gdouble) pr[col + 2*bytes - ind]
+ + (gdouble) cr[col - ind]
+ + (gdouble) cr[col + bytes - ind]
+ + (gdouble) cr[col + 2*bytes - ind]
+ + (gdouble) nr[col - ind]
+ + (gdouble) nr[col + bytes - ind]
+ + (gdouble) nr[col + 2*bytes - ind]));
+ }
+ }
+
+ /*
+ * Save the modified row, shuffle the row pointers, and every
+ * so often, update the progress meter.
+ */
+ gimp_pixel_rgn_set_row (&destPR, dest, x1, row, (x2 - x1));
+
+ tmp = pr;
+ pr = cr;
+ cr = nr;
+ nr = tmp;
+
+ if ((row % 32) == 0)
+ gimp_progress_update ((gdouble) row / (gdouble) (y2 - y1));
+ }
+
+ gimp_progress_update (1.0);
+
+ /*
+ * update the blurred region
+ */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+ /*
+ * clean up after ourselves.
+ */
+ g_free (prev_row);
+ g_free (cur_row);
+ g_free (next_row);
+ g_free (dest);
+}
diff --git a/plug-ins/common/border-average.c b/plug-ins/common/border-average.c
new file mode 100644
index 0000000..70b7d53
--- /dev/null
+++ b/plug-ins/common/border-average.c
@@ -0,0 +1,468 @@
+/* borderaverage 0.01 - image processing plug-in for GIMP.
+ *
+ * Copyright (C) 1998 Philipp Klaus (webmaster@access.ch)
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-borderaverage"
+#define PLUG_IN_BINARY "border-average"
+#define PLUG_IN_ROLE "gimp-border-average"
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+static void borderaverage (GeglBuffer *buffer,
+ gint32 drawable_id,
+ GimpRGB *result);
+
+static gboolean borderaverage_dialog (gint32 image_ID,
+ gint32 drawable_id);
+
+static void add_new_color (const guchar *buffer,
+ gint *cube,
+ gint bucket_expo);
+
+static void thickness_callback (GtkWidget *widget,
+ gpointer data);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run, /* run */
+};
+
+static gint borderaverage_thickness = 3;
+static gint borderaverage_bucket_exponent = 4;
+
+struct borderaverage_data
+{
+ gint thickness;
+ gint bucket_exponent;
+}
+
+static borderaverage_data =
+{
+ 3,
+ 4
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "thickness", "Border size to take in count" },
+ { GIMP_PDB_INT32, "bucket-exponent", "Bits for bucket size (default=4: 16 Levels)" },
+ };
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_COLOR, "borderaverage", "The average color of the specified border." },
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Set foreground to the average color of the image border"),
+ "",
+ "Philipp Klaus",
+ "Internet Access AG",
+ "1998",
+ N_("_Border Average..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Info");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+ gint32 image_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRGB result_color = { 0.0, };
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GeglBuffer *buffer;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &borderaverage_data);
+ borderaverage_thickness = borderaverage_data.thickness;
+ borderaverage_bucket_exponent = borderaverage_data.bucket_exponent;
+ if (! borderaverage_dialog (image_ID, drawable_id))
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ borderaverage_thickness = param[3].data.d_int32;
+ borderaverage_bucket_exponent = param[4].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &borderaverage_data);
+ borderaverage_thickness = borderaverage_data.thickness;
+ borderaverage_bucket_exponent = borderaverage_data.bucket_exponent;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGB color */
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ gimp_progress_init ( _("Border Average"));
+ borderaverage (buffer, drawable_id, &result_color);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ gimp_context_set_foreground (&result_color);
+ }
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ borderaverage_data.thickness = borderaverage_thickness;
+ borderaverage_data.bucket_exponent = borderaverage_bucket_exponent;
+ gimp_set_data (PLUG_IN_PROC,
+ &borderaverage_data, sizeof (borderaverage_data));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ *nreturn_vals = 3;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ values[1].type = GIMP_PDB_COLOR;
+ values[1].data.d_color = result_color;
+
+ g_object_unref (buffer);
+}
+
+
+static void
+borderaverage (GeglBuffer *buffer,
+ gint32 drawable_id,
+ GimpRGB *result)
+{
+ gint x, y, width, height;
+ gint max;
+ guchar r, g, b;
+ gint bucket_num, bucket_expo, bucket_rexpo;
+ gint *cube;
+ gint i, j, k;
+ GeglRectangle border[4];
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &x, &y, &width, &height))
+ {
+ gimp_rgba_set_uchar (result, 0, 0, 0, 255);
+ return;
+ }
+
+ /* allocate and clear the cube before */
+ bucket_expo = borderaverage_bucket_exponent;
+ bucket_rexpo = 8 - bucket_expo;
+ cube = g_new (gint, 1 << (bucket_rexpo * 3));
+ bucket_num = 1 << bucket_rexpo;
+
+ for (i = 0; i < bucket_num; i++)
+ {
+ for (j = 0; j < bucket_num; j++)
+ {
+ for (k = 0; k < bucket_num; k++)
+ {
+ cube[(i << (bucket_rexpo << 1)) + (j << bucket_rexpo) + k] = 0;
+ }
+ }
+ }
+
+ /* Top */
+ border[0].x = x;
+ border[0].y = y;
+ border[0].width = width;
+ border[0].height = borderaverage_thickness;
+
+ /* Bottom */
+ border[1].x = x;
+ border[1].y = y + height - borderaverage_thickness;
+ border[1].width = width;
+ border[1].height = borderaverage_thickness;
+
+ /* Left */
+ border[2].x = x;
+ border[2].y = y + borderaverage_thickness;
+ border[2].width = borderaverage_thickness;
+ border[2].height = height - 2 * borderaverage_thickness;
+
+ /* Right */
+ border[3].x = x + width - borderaverage_thickness;
+ border[3].y = y + borderaverage_thickness;
+ border[3].width = borderaverage_thickness;
+ border[3].height = height - 2 * borderaverage_thickness;
+
+ /* Fill the cube */
+ for (i = 0; i < 4; i++)
+ {
+ if (border[i].width > 0 && border[i].height > 0)
+ {
+ GeglBufferIterator *gi;
+
+ gi = gegl_buffer_iterator_new (buffer, &border[i], 0, babl_format ("R'G'B' u8"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ guint k;
+ guchar *data;
+
+ data = (guchar*) gi->items[0].data;
+
+ for (k = 0; k < gi->length; k++)
+ {
+ add_new_color (data + k * 3,
+ cube,
+ bucket_expo);
+ }
+ }
+ }
+ }
+
+ max = 0; r = 0; g = 0; b = 0;
+
+ /* get max of cube */
+ for (i = 0; i < bucket_num; i++)
+ {
+ for (j = 0; j < bucket_num; j++)
+ {
+ for (k = 0; k < bucket_num; k++)
+ {
+ if (cube[(i << (bucket_rexpo << 1)) +
+ (j << bucket_rexpo) + k] > max)
+ {
+ max = cube[(i << (bucket_rexpo << 1)) +
+ (j << bucket_rexpo) + k];
+ r = (i<<bucket_expo) + (1<<(bucket_expo - 1));
+ g = (j<<bucket_expo) + (1<<(bucket_expo - 1));
+ b = (k<<bucket_expo) + (1<<(bucket_expo - 1));
+ }
+ }
+ }
+ }
+
+ /* return the color */
+ gimp_rgba_set_uchar (result, r, g, b, 255);
+
+ g_free (cube);
+}
+
+static void
+add_new_color (const guchar *buffer,
+ gint *cube,
+ gint bucket_expo)
+{
+ guchar r, g, b;
+ gint bucket_rexpo;
+
+ bucket_rexpo = 8 - bucket_expo;
+ r = buffer[0] >> bucket_expo;
+ g = buffer[1] >> bucket_expo;
+ b = buffer[2] >> bucket_expo;
+ cube[(r << (bucket_rexpo << 1)) + (g << bucket_rexpo) + b]++;
+}
+
+static gboolean
+borderaverage_dialog (gint32 image_ID,
+ gint32 drawable_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *size_entry;
+ GimpUnit unit;
+ GtkWidget *combo;
+ GtkSizeGroup *group;
+ gboolean run;
+ gdouble xres, yres;
+ GeglBuffer *buffer = NULL;
+
+ const gchar *labels[] =
+ { "1", "2", "4", "8", "16", "32", "64", "128", "256" };
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Border Average"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ frame = gimp_frame_new (_("Border Size"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("_Thickness:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ /* Get the image resolution and unit */
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ unit = gimp_image_get_unit (image_ID);
+
+ size_entry = gimp_size_entry_new (1, unit, "%a", TRUE, TRUE, FALSE, 4,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE);
+ gtk_box_pack_start (GTK_BOX (hbox), size_entry, FALSE, FALSE, 0);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (size_entry), GIMP_UNIT_PIXEL);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (size_entry), 0, xres, TRUE);
+
+ /* set the size (in pixels) that will be treated as 0% and 100% */
+ buffer = gimp_drawable_get_buffer (drawable_id);
+ if (buffer)
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (size_entry), 0, 0.0,
+ MIN (gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)));
+
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (size_entry), 0,
+ 1.0, 256.0);
+ gtk_table_set_col_spacing (GTK_TABLE (size_entry), 0, 4);
+ gtk_table_set_col_spacing (GTK_TABLE (size_entry), 2, 12);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (size_entry), 0,
+ (gdouble) borderaverage_thickness);
+ g_signal_connect (size_entry, "value-changed",
+ G_CALLBACK (thickness_callback),
+ NULL);
+ gtk_widget_show (size_entry);
+
+ frame = gimp_frame_new (_("Number of Colors"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("_Bucket size:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_size_group_add_widget (group, label);
+
+ combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (labels), labels);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ borderaverage_bucket_exponent);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &borderaverage_bucket_exponent);
+
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+thickness_callback (GtkWidget *widget,
+ gpointer data)
+{
+ borderaverage_thickness =
+ gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+}
diff --git a/plug-ins/common/busy-dialog.c b/plug-ins/common/busy-dialog.c
new file mode 100644
index 0000000..ac16792
--- /dev/null
+++ b/plug-ins/common/busy-dialog.c
@@ -0,0 +1,309 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * busy-dialog.c
+ * Copyright (C) 2018 Ell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-busy-dialog"
+#define PLUG_IN_BINARY "busy-dialog"
+#define PLUG_IN_ROLE "gimp-busy-dialog"
+
+
+typedef struct
+{
+ GIOChannel *read_channel;
+ GIOChannel *write_channel;
+} Context;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType busy_dialog (gint read_fd,
+ gint write_fd,
+ const gchar *message,
+ gboolean cancelable);
+
+static gboolean busy_dialog_read_channel_notify (GIOChannel *source,
+ GIOCondition condition,
+ Context *context);
+
+static gboolean busy_dialog_delete_event (GtkDialog *dialog,
+ GdkEvent *event,
+ Context *context);
+static void busy_dialog_response (GtkDialog *dialog,
+ gint response_id,
+ Context *context);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args [] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_INT32, "read-fd", "The read file descriptor" },
+ { GIMP_PDB_INT32, "write-fd", "The write file descriptor" },
+ { GIMP_PDB_STRING, "message", "The message" },
+ { GIMP_PDB_INT32, "cancelable", "Whether the dialog is cancelable (TRUE or FALSE)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ "Show a dialog while waiting for an operation to finish",
+ "Used by GIMP to display a dialog, containing a "
+ "spinner and a custom message, while waiting for an "
+ "ongoing operation to finish. Optionally, the dialog "
+ "may provide a \"Cancel\" button, which can be used "
+ "to cancel the operation.",
+ "Ell",
+ "Ell",
+ "2018",
+ NULL,
+ "",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *params,
+ gint *n_return_vals,
+ GimpParam **return_vals)
+{
+ GimpRunMode run_mode = params[0].data.d_int32;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ static GimpParam values[1];
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *n_return_vals = 1;
+ *return_vals = values;
+
+ INIT_I18N ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_NONINTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ if (n_params != 5)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ status = busy_dialog (params[1].data.d_int32,
+ params[2].data.d_int32,
+ params[3].data.d_string,
+ params[4].data.d_int32);
+ }
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static GimpPDBStatusType
+busy_dialog (gint read_fd,
+ gint write_fd,
+ const gchar *message,
+ gboolean cancelable)
+{
+ Context context;
+ GtkWidget *window;
+ GtkWidget *content_area;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *box;
+
+#ifdef G_OS_WIN32
+ context.read_channel = g_io_channel_win32_new_fd (read_fd);
+ context.write_channel = g_io_channel_win32_new_fd (write_fd);
+#else
+ context.read_channel = g_io_channel_unix_new (read_fd);
+ context.write_channel = g_io_channel_unix_new (write_fd);
+#endif
+
+ g_io_channel_set_close_on_unref (context.read_channel, TRUE);
+ g_io_channel_set_close_on_unref (context.write_channel, TRUE);
+
+ /* triggered when the operation is finished in the main app, and we should
+ * quit.
+ */
+ g_io_add_watch (context.read_channel, G_IO_IN | G_IO_ERR | G_IO_HUP,
+ (GIOFunc) busy_dialog_read_channel_notify,
+ &context);
+
+ /* call gtk_init() before gimp_ui_init(), to avoid DESKTOP_STARTUP_ID from
+ * taking effect -- we want the dialog to be prominently displayed above
+ * other plug-in windows.
+ */
+ gtk_init (NULL, NULL);
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ /* the main window */
+ if (! cancelable)
+ {
+ window = g_object_new (GTK_TYPE_WINDOW,
+ "title", _("Please Wait"),
+ "skip-taskbar-hint", TRUE,
+ "deletable", FALSE,
+ "resizable", FALSE,
+ "role", "gimp-busy-dialog",
+ "type-hint", GDK_WINDOW_TYPE_HINT_DIALOG,
+ "window-position", GTK_WIN_POS_CENTER,
+ NULL);
+
+ g_signal_connect (window, "delete-event", G_CALLBACK (gtk_true), NULL);
+
+ content_area = window;
+ }
+ else
+ {
+ window = g_object_new (GTK_TYPE_DIALOG,
+ "title", _("Please Wait"),
+ "skip-taskbar-hint", TRUE,
+ "resizable", FALSE,
+ "role", "gimp-busy-dialog",
+ "window-position", GTK_WIN_POS_CENTER,
+ NULL);
+
+ gtk_dialog_add_button (GTK_DIALOG (window),
+ _("_Cancel"), GTK_RESPONSE_CANCEL);
+
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (busy_dialog_delete_event),
+ &context);
+
+ g_signal_connect (window, "response",
+ G_CALLBACK (busy_dialog_response),
+ &context);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (window));
+ }
+
+ /* the main vbox */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 16);
+ gtk_container_add (GTK_CONTAINER (content_area), vbox);
+ gtk_widget_show (vbox);
+
+ /* the title label */
+ label = gtk_label_new (_("Please wait for the operation to complete"));
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* the busy box */
+ box = gimp_busy_box_new (message);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 8);
+ gtk_box_pack_start (GTK_BOX (vbox), box, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+
+ gtk_window_present (GTK_WINDOW (window));
+
+ gtk_main ();
+
+ gtk_widget_destroy (window);
+
+ g_clear_pointer (&context.read_channel, g_io_channel_unref);
+ g_clear_pointer (&context.write_channel, g_io_channel_unref);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static gboolean
+busy_dialog_read_channel_notify (GIOChannel *source,
+ GIOCondition condition,
+ Context *context)
+{
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+static gboolean
+busy_dialog_delete_event (GtkDialog *dialog,
+ GdkEvent *event,
+ Context *context)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+
+ return TRUE;
+}
+
+static void
+busy_dialog_response (GtkDialog *dialog,
+ gint response_id,
+ Context *context)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_CANCEL:
+ {
+ GtkWidget *button;
+
+ gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, FALSE);
+
+ button = gtk_dialog_get_widget_for_response (dialog,
+ GTK_RESPONSE_CANCEL);
+ gtk_button_set_label (GTK_BUTTON (button), _("Canceling..."));
+
+ /* signal the cancellation request to the main app */
+ g_clear_pointer (&context->write_channel, g_io_channel_unref);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/plug-ins/common/cartoon.c b/plug-ins/common/cartoon.c
new file mode 100644
index 0000000..beebe7d
--- /dev/null
+++ b/plug-ins/common/cartoon.c
@@ -0,0 +1,880 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Cartoon filter for GIMP for BIPS
+ * -Spencer Kimball
+ *
+ * This filter propagates dark values in an image based on
+ * each pixel's relative darkness to a neighboring average
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+
+#define PLUG_IN_PROC "plug-in-cartoon"
+#define PLUG_IN_BINARY "cartoon"
+#define PLUG_IN_ROLE "gimp-cartoon"
+#define TILE_CACHE_SIZE 48
+
+typedef struct
+{
+ gdouble mask_radius;
+ gdouble threshold;
+ gdouble pct_black;
+} CartoonVals;
+
+
+/*
+ * Function prototypes.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void cartoon (GimpDrawable *drawable,
+ GimpPreview *preview);
+static gboolean cartoon_dialog (GimpDrawable *drawable);
+
+static gdouble compute_ramp (guchar *dest1,
+ guchar *dest2,
+ gint length,
+ gdouble pct_black);
+
+/*
+ * Gaussian blur helper functions
+ */
+static void find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev);
+static void transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint bytes,
+ gint width);
+
+
+/***** Local vars *****/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run, /* run */
+};
+
+static CartoonVals cvals =
+{
+ 7.0, /* mask_radius */
+ 1.0, /* threshold */
+ 0.2 /* pct_black */
+};
+
+
+/***** Functions *****/
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "mask-radius", "Cartoon mask radius (radius of pixel neighborhood)" },
+ { GIMP_PDB_FLOAT, "pct-black", "Percentage of darkened pixels to set to black (0.0 - 1.0)" }
+ };
+
+ gchar *help_string =
+ "Propagates dark values in an image based on "
+ "each pixel's relative darkness to a neighboring average. The idea behind "
+ "this filter is to give the look of a black felt pen drawing subsequently "
+ "shaded with color. This is achieved by darkening areas of the image which "
+ "are measured to be darker than a neighborhood average. In this way, "
+ "sufficiently large shifts in intensity are darkened to black. The rate "
+ "at which they are darkened to black is determined by the second pct_black "
+ "parameter. The mask_radius parameter controls the size of the pixel "
+ "neighborhood over which the average intensity is computed and then "
+ "compared to each pixel in the neighborhood to decide whether or not to "
+ "darken it to black. Large values for mask_radius result in very thick "
+ "black areas bordering the shaded regions of color and much less detail "
+ "for black areas everywhere including inside regions of color. Small "
+ "values result in more subtle pen strokes and detail everywhere. Small "
+ "values for the pct_black make the blend from the color regions to the "
+ "black border lines smoother and the lines themselves thinner and less "
+ "noticeable; larger values achieve the opposite effect.";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simulate a cartoon by enhancing edges"),
+ help_string,
+ "Spencer Kimball",
+ "Bit Specialists, Inc.",
+ "2001",
+ N_("Ca_rtoon (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpDrawable *drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ run_mode = param[0].data.d_int32;
+
+ /* Get the specified drawable */
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ /* set the tile cache size */
+ gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ INIT_I18N();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+
+ /* First acquire information with a dialog */
+ if (! cartoon_dialog (drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ cvals.mask_radius = param[3].data.d_float;
+ cvals.pct_black = param[4].data.d_float;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGB or GRAY color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init ("Cartoon");
+
+
+ cartoon (drawable, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (CartoonVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("Cannot operate on indexed color images.");
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ gimp_drawable_detach (drawable);
+}
+
+/*
+ * Cartoon algorithm
+ * -----------------
+ * Mask radius = radius of pixel neighborhood for intensity comparison
+ * Threshold = relative intensity difference which will result in darkening
+ * Ramp = amount of relative intensity difference before total black
+ * Blur radius = mask radius / 3.0
+ *
+ * Algorithm:
+ * For each pixel, calculate pixel intensity value to be: avg (blur radius)
+ * relative diff = pixel intensity / avg (mask radius)
+ * If relative diff < Threshold
+ * intensity mult = (Ramp - MIN (Ramp, (Threshold - relative diff))) / Ramp
+ * pixel intensity *= intensity mult
+ */
+static void
+cartoon (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ GimpPixelRgn src_rgn, dest_rgn;
+ GimpPixelRgn *pr;
+ gint x, y, width, height;
+ gint bytes;
+ gboolean has_alpha;
+ guchar *dest1;
+ guchar *dest2;
+ guchar *src;
+ guchar *src1, *sp_p1, *sp_m1;
+ guchar *src2, *sp_p2, *sp_m2;
+ gdouble n_p1[5], n_m1[5];
+ gdouble n_p2[5], n_m2[5];
+ gdouble d_p1[5], d_m1[5];
+ gdouble d_p2[5], d_m2[5];
+ gdouble bd_p1[5], bd_m1[5];
+ gdouble bd_p2[5], bd_m2[5];
+ gdouble *val_p1, *val_m1, *vp1, *vm1;
+ gdouble *val_p2, *val_m2, *vp2, *vm2;
+ gint i, j;
+ gint row, col, b;
+ gint terms;
+ gint progress, max_progress;
+ gint initial_p1[4];
+ gint initial_p2[4];
+ gint initial_m1[4];
+ gint initial_m2[4];
+ gdouble radius;
+ gdouble std_dev1;
+ gdouble std_dev2;
+ gdouble ramp;
+ guchar *preview_buffer = NULL;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x, &y);
+ gimp_preview_get_size (preview, &width, &height);
+ }
+ else if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x, &y, &width, &height))
+ {
+ return;
+ }
+
+ bytes = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ val_p1 = g_new (gdouble, MAX (width, height) * bytes);
+ val_p2 = g_new (gdouble, MAX (width, height) * bytes);
+ val_m1 = g_new (gdouble, MAX (width, height) * bytes);
+ val_m2 = g_new (gdouble, MAX (width, height) * bytes);
+
+ src = g_new (guchar, MAX (width, height) * bytes);
+ dest1 = g_new0 (guchar, width * height);
+ dest2 = g_new0 (guchar, width * height);
+
+ gimp_pixel_rgn_init (&src_rgn, drawable,
+ 0, 0, drawable->width, drawable->height, FALSE, FALSE);
+
+ progress = 0;
+ max_progress = width * height * 2;
+
+ /* Calculate the standard deviations */
+ radius = 1.0; /* blur radius */
+ radius = fabs (radius) + 1.0;
+ std_dev1 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ radius = cvals.mask_radius;
+ radius = fabs (radius) + 1.0;
+ std_dev2 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ /* derive the constants for calculating the gaussian from the std dev */
+ find_constants (n_p1, n_m1, d_p1, d_m1, bd_p1, bd_m1, std_dev1);
+ find_constants (n_p2, n_m2, d_p2, d_m2, bd_p2, bd_m2, std_dev2);
+
+ /* First the vertical pass */
+ for (col = 0; col < width; col++)
+ {
+ memset (val_p1, 0, height * bytes * sizeof (gdouble));
+ memset (val_p2, 0, height * bytes * sizeof (gdouble));
+ memset (val_m1, 0, height * bytes * sizeof (gdouble));
+ memset (val_m2, 0, height * bytes * sizeof (gdouble));
+
+ gimp_pixel_rgn_get_col (&src_rgn, src, col + x, y, height);
+
+ src1 = src;
+ sp_p1 = src1;
+ sp_m1 = src1 + (height - 1) * bytes;
+ vp1 = val_p1;
+ vp2 = val_p2;
+ vm1 = val_m1 + (height - 1) * bytes;
+ vm2 = val_m2 + (height - 1) * bytes;
+
+ /* Set up the first vals */
+ for (i = 0; i < bytes; i++)
+ {
+ initial_p1[i] = sp_p1[i];
+ initial_m1[i] = sp_m1[i];
+ }
+
+ for (row = 0; row < height; row++)
+ {
+ gdouble *vpptr1, *vmptr1;
+ gdouble *vpptr2, *vmptr2;
+
+ terms = (row < 4) ? row : 4;
+
+ for (b = 0; b < bytes; b++)
+ {
+ vpptr1 = vp1 + b; vmptr1 = vm1 + b;
+ vpptr2 = vp2 + b; vmptr2 = vm2 + b;
+
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr1 += n_p1[i] * sp_p1[(-i * bytes) + b] -
+ d_p1[i] * vp1[(-i * bytes) + b];
+ *vmptr1 += n_m1[i] * sp_m1[(i * bytes) + b] -
+ d_m1[i] * vm1[(i * bytes) + b];
+
+ *vpptr2 += n_p2[i] * sp_p1[(-i * bytes) + b] -
+ d_p2[i] * vp2[(-i * bytes) + b];
+ *vmptr2 += n_m2[i] * sp_m1[(i * bytes) + b] -
+ d_m2[i] * vm2[(i * bytes) + b];
+ }
+
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[b];
+ *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[b];
+
+ *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p1[b];
+ *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m1[b];
+ }
+ }
+
+ sp_p1 += bytes;
+ sp_m1 -= bytes;
+ vp1 += bytes;
+ vp2 += bytes;
+ vm1 -= bytes;
+ vm2 -= bytes;
+ }
+
+ transfer_pixels (val_p1, val_m1, dest1 + col, width, bytes, height);
+ transfer_pixels (val_p2, val_m2, dest2 + col, width, bytes, height);
+
+ if (!preview)
+ {
+ progress += height;
+ if ((col % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ for (row = 0; row < height; row++)
+ {
+ memset (val_p1, 0, width * sizeof (gdouble));
+ memset (val_p2, 0, width * sizeof (gdouble));
+ memset (val_m1, 0, width * sizeof (gdouble));
+ memset (val_m2, 0, width * sizeof (gdouble));
+
+ src1 = dest1 + row * width;
+ src2 = dest2 + row * width;
+
+ sp_p1 = src1;
+ sp_p2 = src2;
+ sp_m1 = src1 + width - 1;
+ sp_m2 = src2 + width - 1;
+ vp1 = val_p1;
+ vp2 = val_p2;
+ vm1 = val_m1 + width - 1;
+ vm2 = val_m2 + width - 1;
+
+ /* Set up the first vals */
+ initial_p1[0] = sp_p1[0];
+ initial_p2[0] = sp_p2[0];
+ initial_m1[0] = sp_m1[0];
+ initial_m2[0] = sp_m2[0];
+
+ for (col = 0; col < width; col++)
+ {
+ gdouble *vpptr1, *vmptr1;
+ gdouble *vpptr2, *vmptr2;
+
+ terms = (col < 4) ? col : 4;
+
+ vpptr1 = vp1; vmptr1 = vm1;
+ vpptr2 = vp2; vmptr2 = vm2;
+
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr1 += n_p1[i] * sp_p1[-i] - d_p1[i] * vp1[-i];
+ *vmptr1 += n_m1[i] * sp_m1[i] - d_m1[i] * vm1[i];
+
+ *vpptr2 += n_p2[i] * sp_p2[-i] - d_p2[i] * vp2[-i];
+ *vmptr2 += n_m2[i] * sp_m2[i] - d_m2[i] * vm2[i];
+ }
+
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0];
+ *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0];
+
+ *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p2[0];
+ *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m2[0];
+ }
+
+ sp_p1 ++;
+ sp_p2 ++;
+ sp_m1 --;
+ sp_m2 --;
+ vp1 ++;
+ vp2 ++;
+ vm1 --;
+ vm2 --;
+ }
+
+ transfer_pixels (val_p1, val_m1, dest1 + row * width, 1, 1, width);
+ transfer_pixels (val_p2, val_m2, dest2 + row * width, 1, 1, width);
+
+ if (!preview)
+ {
+ progress += width;
+ if ((row % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ /* Compute the ramp value which sets 'pct_black' % of the darkened pixels black */
+ ramp = compute_ramp (dest1, dest2, width * height, cvals.pct_black);
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&src_rgn, drawable, x, y, width, height, FALSE, FALSE);
+
+ if (preview)
+ {
+ pr = gimp_pixel_rgns_register (1, &src_rgn);
+ preview_buffer = g_new (guchar, width * height * bytes);
+ }
+ else
+ {
+ gimp_pixel_rgn_init (&dest_rgn, drawable,
+ x, y, width, height, TRUE, TRUE);
+ pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
+ }
+
+ while (pr)
+ {
+ guchar *src_ptr = src_rgn.data;
+ guchar *dest_ptr;
+ guchar *blur_ptr = dest1 + (src_rgn.y - y) * width + (src_rgn.x - x);
+ guchar *avg_ptr = dest2 + (src_rgn.y - y) * width + (src_rgn.x - x);
+ gdouble diff;
+ gdouble mult = 0.0;
+ gdouble lightness;
+
+ if (preview)
+ dest_ptr =
+ preview_buffer +
+ ((src_rgn.y - y) * width + (src_rgn.x - x)) * bytes;
+ else
+ dest_ptr = dest_rgn.data;
+
+ for (row = 0; row < src_rgn.h; row++)
+ {
+ for (col = 0; col < src_rgn.w; col++)
+ {
+ if (avg_ptr[col] != 0)
+ {
+ diff = (gdouble) blur_ptr[col] / (gdouble) avg_ptr[col];
+ if (diff < cvals.threshold)
+ {
+ if (ramp == 0.0)
+ mult = 0.0;
+ else
+ mult = (ramp - MIN (ramp, (cvals.threshold - diff))) / ramp;
+ }
+ else
+ mult = 1.0;
+ }
+
+ lightness = CLAMP (blur_ptr[col] * mult, 0, 255);
+
+ if (bytes < 3)
+ {
+ dest_ptr[col * bytes] = (guchar) lightness;
+ if (has_alpha)
+ dest_ptr[col * bytes + 1] = src_ptr[col * src_rgn.bpp + 1];
+ }
+ else
+ {
+ /* Convert to HLS, set lightness and convert back */
+ gint r, g, b;
+
+ r = src_ptr[col * src_rgn.bpp + 0];
+ g = src_ptr[col * src_rgn.bpp + 1];
+ b = src_ptr[col * src_rgn.bpp + 2];
+
+ gimp_rgb_to_hsl_int (&r, &g, &b);
+ b = lightness;
+ gimp_hsl_to_rgb_int (&r, &g, &b);
+
+ dest_ptr[col * bytes + 0] = r;
+ dest_ptr[col * bytes + 1] = g;
+ dest_ptr[col * bytes + 2] = b;
+
+ if (has_alpha)
+ dest_ptr[col * bytes + 3] = src_ptr[col * src_rgn.bpp + 3];
+ }
+ }
+
+ src_ptr += src_rgn.rowstride;
+ if (preview)
+ dest_ptr += width * bytes;
+ else
+ dest_ptr += dest_rgn.rowstride;
+ blur_ptr += width;
+ avg_ptr += width;
+ }
+
+ if (!preview)
+ {
+ progress += src_rgn.w * src_rgn.h;
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+
+ pr = gimp_pixel_rgns_process (pr);
+ }
+
+ if (preview)
+ {
+ gimp_preview_draw_buffer (preview, preview_buffer, width * bytes);
+ g_free (preview_buffer);
+ }
+ else
+ {
+ gimp_progress_update (1.0);
+ /* merge the shadow, update the drawable */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x, y, width, height);
+ }
+
+ /* free up buffers */
+ g_free (val_p1);
+ g_free (val_p2);
+ g_free (val_m1);
+ g_free (val_m2);
+ g_free (src);
+ g_free (dest1);
+ g_free (dest2);
+}
+
+static gdouble
+compute_ramp (guchar *dest1,
+ guchar *dest2,
+ gint length,
+ gdouble pct_black)
+{
+ gint hist[100];
+ gdouble diff;
+ gint count;
+ gint i;
+ gint sum;
+
+ memset (hist, 0, sizeof (int) * 100);
+ count = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ if (*dest2 != 0)
+ {
+ diff = (gdouble) *dest1 / (gdouble) *dest2;
+ if (diff < 1.0)
+ {
+ hist[(int) (diff * 100)] += 1;
+ count += 1;
+ }
+ }
+
+ dest1++;
+ dest2++;
+ }
+
+ if (pct_black == 0.0 || count == 0)
+ return 1.0;
+
+ sum = 0;
+ for (i = 0; i < 100; i++)
+ {
+ sum += hist[i];
+ if (((gdouble) sum / (gdouble) count) > pct_black)
+ return (1.0 - (gdouble) i / 100.0);
+ }
+
+ return 0.0;
+}
+
+
+/*
+ * Gaussian blur helper functions
+ */
+
+static void
+transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint bytes,
+ gint width)
+{
+ gint i, b;
+ gdouble sum[4];
+
+ for(i = 0; i < width; i++)
+ {
+ for (b = 0; b < bytes; b++)
+ {
+ sum[b] = src1[b] + src2[b];
+ if (sum[b] > 255) sum[b] = 255;
+ else if(sum[b] < 0) sum[b] = 0;
+ }
+
+ /* Convert to lightness if RGB */
+ if (bytes > 2)
+ *dest = (guchar) gimp_rgb_to_l_int (sum[0], sum[1], sum[2]);
+ else
+ *dest = (guchar) sum[0];
+
+ src1 += bytes;
+ src2 += bytes;
+ dest += jump;
+ }
+}
+
+static void
+find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev)
+{
+ gint i;
+ gdouble constants [8];
+ gdouble div;
+
+ /* The constants used in the implementation of a casual sequence
+ * using a 4th order approximation of the gaussian operator
+ */
+
+ div = sqrt (2 * G_PI) * std_dev;
+
+ constants [0] = -1.783 / std_dev;
+ constants [1] = -1.723 / std_dev;
+ constants [2] = 0.6318 / std_dev;
+ constants [3] = 1.997 / std_dev;
+ constants [4] = 1.6803 / div;
+ constants [5] = 3.735 / div;
+ constants [6] = -0.6803 / div;
+ constants [7] = -0.2598 / div;
+
+ n_p [0] = constants[4] + constants[6];
+ n_p [1] = exp (constants[1]) *
+ (constants[7] * sin (constants[3]) -
+ (constants[6] + 2 * constants[4]) * cos (constants[3])) +
+ exp (constants[0]) *
+ (constants[5] * sin (constants[2]) -
+ (2 * constants[6] + constants[4]) * cos (constants[2]));
+ n_p [2] = 2 * exp (constants[0] + constants[1]) *
+ ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
+ constants[5] * cos (constants[3]) * sin (constants[2]) -
+ constants[7] * cos (constants[2]) * sin (constants[3])) +
+ constants[6] * exp (2 * constants[0]) +
+ constants[4] * exp (2 * constants[1]);
+ n_p [3] = exp (constants[1] + 2 * constants[0]) *
+ (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
+ exp (constants[0] + 2 * constants[1]) *
+ (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
+ n_p [4] = 0.0;
+
+ d_p [0] = 0.0;
+ d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
+ 2 * exp (constants[0]) * cos (constants[2]);
+ d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
+ exp (2 * constants[1]) + exp (2 * constants[0]);
+ d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
+ 2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
+ d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
+
+#ifndef ORIGINAL_READABLE_CODE
+ memcpy(d_m, d_p, 5 * sizeof(gdouble));
+#else
+ for (i = 0; i <= 4; i++)
+ d_m [i] = d_p [i];
+#endif
+
+ n_m[0] = 0.0;
+ for (i = 1; i <= 4; i++)
+ n_m [i] = n_p[i] - d_p[i] * n_p[0];
+
+ {
+ gdouble sum_n_p, sum_n_m, sum_d;
+ gdouble a, b;
+
+ sum_n_p = 0.0;
+ sum_n_m = 0.0;
+ sum_d = 0.0;
+
+ for (i = 0; i <= 4; i++)
+ {
+ sum_n_p += n_p[i];
+ sum_n_m += n_m[i];
+ sum_d += d_p[i];
+ }
+
+#ifndef ORIGINAL_READABLE_CODE
+ sum_d++;
+ a = sum_n_p / sum_d;
+ b = sum_n_m / sum_d;
+#else
+ a = sum_n_p / (1 + sum_d);
+ b = sum_n_m / (1 + sum_d);
+#endif
+
+ for (i = 0; i <= 4; i++)
+ {
+ bd_p[i] = d_p[i] * a;
+ bd_m[i] = d_m[i] * b;
+ }
+ }
+}
+
+/*******************************************************/
+/* Dialog */
+/*******************************************************/
+
+static gboolean
+cartoon_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkObject *scale_data;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Cartoon"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (cartoon),
+ drawable);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Label, scale, entry for cvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Mask radius:"), 100, 5,
+ cvals.mask_radius, 1.0, 50.0, 1, 5.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cvals.mask_radius);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for cvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Percent black:"), 50, 5,
+ cvals.pct_black, 0.0, 1.0, 0.01, 0.1, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &cvals.pct_black);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/checkerboard.c b/plug-ins/common/checkerboard.c
new file mode 100644
index 0000000..47fcd96
--- /dev/null
+++ b/plug-ins/common/checkerboard.c
@@ -0,0 +1,525 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Copyright (C) 1997 Brent Burton & the Edward Blevins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-checkerboard"
+#define PLUG_IN_BINARY "checkerboard"
+#define PLUG_IN_ROLE "gimp-checkerboard"
+#define SPIN_BUTTON_WIDTH 8
+
+
+/* Variables set in dialog box */
+typedef struct data
+{
+ gboolean mode;
+ gint size;
+} CheckVals;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void do_checkerboard_pattern (gint32 drawable_ID,
+ GimpPreview *preview);
+static void do_checkerboard_preview (gpointer drawable_ID,
+ GimpPreview *preview);
+static gint inblock (gint pos,
+ gint size);
+
+static gboolean checkerboard_dialog (gint32 image_ID,
+ gint32 drawable_ID);
+static void check_size_update_callback (GtkWidget *widget);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static CheckVals cvals =
+{
+ FALSE, /* mode */
+ 10 /* size */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "check-mode", "Check mode { REGULAR (0), PSYCHOBILY (1) }" },
+ { GIMP_PDB_INT32, "check-size", "Size of the checks" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create a checkerboard pattern"),
+ "More here later",
+ "Brent Burton & the Edward Blevins",
+ "Brent Burton & the Edward Blevins",
+ "1997",
+ N_("_Checkerboard (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+ if (! checkerboard_dialog (image_ID, drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ cvals.mode = param[3].data.d_int32;
+ cvals.size = param[4].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ do_checkerboard_pattern (drawable_ID, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (CheckVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+typedef struct
+{
+ guchar fg[4];
+ guchar bg[4];
+} CheckerboardParam_t;
+
+static void
+checkerboard_func (gint x,
+ gint y,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ CheckerboardParam_t *param = (CheckerboardParam_t*) data;
+
+ gint val, xp, yp;
+ gint b;
+
+ if (cvals.mode)
+ {
+ /* Psychobilly Mode */
+ val = (inblock (x, cvals.size) != inblock (y, cvals.size));
+ }
+ else
+ {
+ /* Normal, regular checkerboard mode.
+ * Determine base factor (even or odd) of block
+ * this x/y position is in.
+ */
+ xp = x / cvals.size;
+ yp = y / cvals.size;
+
+ /* if both even or odd, color sqr */
+ val = ( (xp & 1) != (yp & 1) );
+ }
+
+ for (b = 0; b < bpp; b++)
+ dest[b] = val ? param->fg[b] : param->bg[b];
+}
+
+static void
+do_checkerboard_pattern (gint32 drawable_ID,
+ GimpPreview *preview)
+{
+ CheckerboardParam_t param;
+ GimpRGB fg, bg;
+ const Babl *format;
+ gint bpp;
+
+ gimp_context_get_background (&bg);
+ gimp_context_get_foreground (&fg);
+
+ if (gimp_drawable_is_gray (drawable_ID))
+ {
+ param.bg[0] = gimp_rgb_luminance_uchar (&bg);
+ gimp_rgba_get_uchar (&bg, NULL, NULL, NULL, param.bg + 1);
+
+ param.fg[0] = gimp_rgb_luminance_uchar (&fg);
+ gimp_rgba_get_uchar (&fg, NULL, NULL, NULL, param.fg + 3);
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ gimp_rgba_get_uchar (&bg,
+ param.bg, param.bg + 1, param.bg + 2, param.bg + 1);
+
+ gimp_rgba_get_uchar (&fg,
+ param.fg, param.fg + 1, param.fg + 2, param.fg + 3);
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ if (cvals.size < 1)
+ {
+ /* make size 1 to prevent division by zero */
+ cvals.size = 1;
+ }
+
+ if (preview)
+ {
+ gint x1, y1;
+ gint width, height;
+ gint i;
+ guchar *buffer;
+
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+ bpp = gimp_drawable_bpp (drawable_ID);
+ buffer = g_new (guchar, width * height * bpp);
+
+ for (i = 0; i < width * height; i++)
+ {
+ checkerboard_func (x1 + i % width,
+ y1 + i / width,
+ buffer + i * bpp,
+ bpp, &param);
+ }
+
+ gimp_preview_draw_buffer (preview, buffer, width * bpp);
+ g_free (buffer);
+ }
+ else
+ {
+ GeglBuffer *buffer;
+ GeglBufferIterator *iter;
+ gint x, y, w, h;
+ gint progress_total;
+ gint progress_done = 0;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID, &x, &y, &w, &h))
+ return;
+
+ progress_total = w * h;
+
+ gimp_progress_init (_("Checkerboard"));
+
+ buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (x, y, w, h), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[0].roi;
+ guchar *dest = iter->items[0].data;
+ guchar *d;
+ gint y1, x1;
+
+ d = dest;
+
+ for (y1 = 0; y1 < roi.height; y1++)
+ {
+ for (x1 = 0; x1 < roi.width; x1++)
+ {
+ checkerboard_func (roi.x + x1,
+ roi.y + y1,
+ d + x1 * bpp,
+ bpp, &param);
+ }
+
+ d += roi.width * bpp;
+ }
+
+ progress_done += roi.width * roi.height;
+ gimp_progress_update ((gdouble) progress_done /
+ (gdouble) progress_total);
+ }
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, x, y, w, h);
+ }
+}
+
+static void
+do_checkerboard_preview (gpointer drawable_ID,
+ GimpPreview *preview)
+{
+ do_checkerboard_pattern (GPOINTER_TO_INT (drawable_ID), preview);
+}
+
+static gint
+inblock (gint pos,
+ gint size)
+{
+ static gint *in = NULL; /* initialized first time */
+ static gint len = -1;
+
+ /* avoid a FP exception */
+ if (size == 1)
+ size = 2;
+
+ if (in && len != size * size)
+ {
+ g_free (in);
+ in = NULL;
+ }
+ len = size * size;
+
+ /* Initialize the array; since we'll be called thousands of
+ * times with the same size value, precompute the array.
+ */
+ if (in == NULL)
+ {
+ gint cell = 1; /* cell value */
+ gint i, j, k;
+
+ in = g_new (gint, len);
+
+ /* i is absolute index into in[]
+ * j is current number of blocks to fill in with a 1 or 0.
+ * k is just counter for the j cells.
+ */
+ i = 0;
+ for (j = 1; j <= size; j++)
+ { /* first half */
+ for (k = 0; k < j; k++)
+ {
+ in[i++] = cell;
+ }
+ cell = !cell;
+ }
+ for (j = size - 1; j >= 1; j--)
+ { /* second half */
+ for (k = 0; k < j; k++)
+ {
+ in[i++] = cell;
+ }
+ cell = !cell;
+ }
+ }
+
+ /* place pos within 0..(len-1) grid and return the value. */
+ return in[pos % (len - 1)];
+}
+
+static gboolean
+checkerboard_dialog (gint32 image_ID,
+ gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *preview;
+ GtkWidget *toggle;
+ GtkWidget *size_entry;
+ gint size, width, height;
+ GimpUnit unit;
+ gdouble xres;
+ gdouble yres;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Checkerboard"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (do_checkerboard_preview),
+ GINT_TO_POINTER (drawable_ID));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Get the image resolution and unit */
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ unit = gimp_image_get_unit (image_ID);
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+ size = MIN (width, height);
+
+ size_entry = gimp_size_entry_new (1, unit, "%a",
+ TRUE, TRUE, FALSE, SPIN_BUTTON_WIDTH,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE);
+ gtk_table_set_col_spacing (GTK_TABLE (size_entry), 0, 4);
+ gtk_table_set_col_spacing (GTK_TABLE (size_entry), 1, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), size_entry, FALSE, FALSE, 0);
+ gtk_widget_show (size_entry);
+
+ /* set the unit back to pixels, since most times we will want pixels */
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (size_entry), GIMP_UNIT_PIXEL);
+
+ /* set the resolution to the image resolution */
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (size_entry), 0, xres, TRUE);
+
+ /* set the size (in pixels) that will be treated as 0% and 100% */
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (size_entry), 0, 0.0, size);
+
+ /* set upper and lower limits (in pixels) */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (size_entry), 0,
+ 1.0, size);
+
+ /* initialize the values */
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (size_entry), 0, cvals.size);
+
+ /* attach labels */
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (size_entry),
+ _("_Size:"), 1, 0, 0.0);
+
+ g_signal_connect (size_entry, "value-changed",
+ G_CALLBACK (check_size_update_callback),
+ &cvals.size);
+ g_signal_connect_swapped (size_entry, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Psychobilly"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cvals.mode);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &cvals.mode);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+check_size_update_callback (GtkWidget *widget)
+{
+ cvals.size = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+}
diff --git a/plug-ins/common/cml-explorer.c b/plug-ins/common/cml-explorer.c
new file mode 100644
index 0000000..b09fc66
--- /dev/null
+++ b/plug-ins/common/cml-explorer.c
@@ -0,0 +1,2569 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * CML_explorer.c
+ * Time-stamp: <2000-02-13 18:18:37 yasuhiro>
+ * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
+ * Version: 1.0.11
+ * URL: http://www.inetq.or.jp/~narazaki/TheGIMP/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Comment:
+ * CML is the abbreviation of Coupled-Map Lattice that is a model of
+ * complex systems, proposed by a physicist[1,2].
+ *
+ * Similar models are summaried as follows:
+ *
+ * Value Time Space
+ * Coupled-Map Lattice cont. discrete discrete
+ * Celluar Automata discrete discrete discrete
+ * Differential Eq. cont. cont. cont.
+ *
+ * (But this program uses a parameter: hold-rate to avoid very fast changes.
+ * Thus time is rather continuous than discrete.
+ * Yes, this change to model changes the output completely.)
+ *
+ * References:
+ * 1. Kunihiko Kaneko, Period-doubling of kind-antikink patterns,
+ * quasi-periodicity in antiferro-like structures and spatial
+ * intermittency in coupled map lattices -- Toward a prelude to a
+ * "field theory of chaos", Prog. Theor. Phys. 72 (1984) 480.
+ *
+ * 2. Kunihiko Kaneko ed., Theory and Applications of Coupled Map
+ * Lattices (Wiley, 1993).
+ *
+ * About Parameter File:
+ * I assume that the possible longest line in CMP parameter file is 1023.
+ * Please read CML_save_to_file_callback if you want know details of syntax.
+ *
+ * Format version 1.0 starts with:
+ * ; This is a parameter file for CML_explorer
+ * ; File format version: 1.0
+ * ;
+ * Hue
+ *
+ * The old format for CML_explorer included in gimp-0.99.[89] is:
+ * ; CML parameter file (version: 1.0)
+ * ; Hue
+ *
+ * (This file format is interpreted as format version 0.99 now.)
+ *
+ * Thanks:
+ * This version contains patches from:
+ * Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu>
+ * Sean P Cier <scier@andrew.cmu.edu>
+ * David Mosberger-Tang <davidm@azstarnet.com>
+ * Michael Sweet <mike@easysw.com>
+ *
+ */
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PARAM_FILE_FORMAT_VERSION 1.0
+#define PLUG_IN_PROC "plug-in-cml-explorer"
+#define PLUG_IN_BINARY "cml-explorer"
+#define PLUG_IN_ROLE "gimp-cml-explorer"
+#define VALS CML_explorer_vals
+#define PROGRESS_UPDATE_NUM 100
+#define CML_LINE_SIZE 1024
+#define TILE_CACHE_SIZE 32
+#define SCALE_WIDTH 130
+#define PREVIEW_WIDTH 64
+#define PREVIEW_HEIGHT 220
+
+#define CANNONIZE(p, x) (255*(((p).range_h - (p).range_l)*(x) + (p).range_l))
+#define HCANNONIZE(p, x) (254*(((p).range_h - (p).range_l)*(x) + (p).range_l))
+#define POS_IN_TORUS(i,size) ((i < 0) ? size + i : ((size <= i) ? i - size : i))
+
+typedef struct _WidgetEntry WidgetEntry;
+
+struct _WidgetEntry
+{
+ GtkWidget *widget;
+ gpointer value;
+ void (*updater) (WidgetEntry *);
+};
+
+enum
+{
+ CML_KEEP_VALUES,
+ CML_KEEP_FIRST,
+ CML_FILL,
+ CML_LOGIST,
+ CML_LOGIST_STEP,
+ CML_POWER,
+ CML_POWER_STEP,
+ CML_REV_POWER,
+ CML_REV_POWER_STEP,
+ CML_DELTA,
+ CML_DELTA_STEP,
+ CML_SIN_CURVE,
+ CML_SIN_CURVE_STEP,
+ CML_NUM_VALUES
+};
+
+static const gchar *function_names[CML_NUM_VALUES] =
+{
+ N_("Keep image's values"),
+ N_("Keep the first value"),
+ N_("Fill with parameter k"),
+ N_("k{x(1-x)}^p"),
+ N_("k{x(1-x)}^p stepped"),
+ N_("kx^p"),
+ N_("kx^p stepped"),
+ N_("k(1-x^p)"),
+ N_("k(1-x^p) stepped"),
+ N_("Delta function"),
+ N_("Delta function stepped"),
+ N_("sin^p-based function"),
+ N_("sin^p, stepped")
+};
+
+enum
+{
+ COMP_NONE,
+ COMP_MAX_LINEAR,
+ COMP_MAX_LINEAR_P1,
+ COMP_MAX_LINEAR_M1,
+ COMP_MIN_LINEAR,
+ COMP_MIN_LINEAR_P1,
+ COMP_MIN_LINEAR_M1,
+ COMP_MAX_LINEAR_P1L,
+ COMP_MAX_LINEAR_P1U,
+ COMP_MAX_LINEAR_M1L,
+ COMP_MAX_LINEAR_M1U,
+ COMP_MIN_LINEAR_P1L,
+ COMP_MIN_LINEAR_P1U,
+ COMP_MIN_LINEAR_M1L,
+ COMP_MIN_LINEAR_M1U,
+ COMP_NUM_VALUES
+};
+
+static const gchar *composition_names[COMP_NUM_VALUES] =
+{
+ NC_("cml-composition", "None"),
+ N_("Max (x, -)"),
+ N_("Max (x+d, -)"),
+ N_("Max (x-d, -)"),
+ N_("Min (x, -)"),
+ N_("Min (x+d, -)"),
+ N_("Min (x-d, -)"),
+ N_("Max (x+d, -), (x < 0.5)"),
+ N_("Max (x+d, -), (0.5 < x)"),
+ N_("Max (x-d, -), (x < 0.5)"),
+ N_("Max (x-d, -), (0.5 < x)"),
+ N_("Min (x+d, -), (x < 0.5)"),
+ N_("Min (x+d, -), (0.5 < x)"),
+ N_("Min (x-d, -), (x < 0.5)"),
+ N_("Min (x-d, -), (0.5 < x)")
+};
+
+enum
+{
+ STANDARD,
+ AVERAGE,
+ ANTILOG,
+ RAND_POWER0,
+ RAND_POWER1,
+ RAND_POWER2,
+ MULTIPLY_RANDOM0,
+ MULTIPLY_RANDOM1,
+ MULTIPLY_GRADIENT,
+ RAND_AND_P,
+ ARRANGE_NUM_VALUES
+};
+
+static const gchar *arrange_names[ARRANGE_NUM_VALUES] =
+{
+ N_("Standard"),
+ N_("Use average value"),
+ N_("Use reverse value"),
+ N_("With random power (0,10)"),
+ N_("With random power (0,1)"),
+ N_("With gradient power (0,1)"),
+ N_("Multiply rand. value (0,1)"),
+ N_("Multiply rand. value (0,2)"),
+ N_("Multiply gradient (0,1)"),
+ N_("With p and random (0,1)"),
+};
+
+enum
+{
+ CML_INITIAL_RANDOM_INDEPENDENT = 6,
+ CML_INITIAL_RANDOM_SHARED,
+ CML_INITIAL_RANDOM_FROM_SEED,
+ CML_INITIAL_RANDOM_FROM_SEED_SHARED,
+ CML_INITIAL_NUM_VALUES
+};
+
+static const gchar *initial_value_names[CML_INITIAL_NUM_VALUES] =
+{
+ N_("All black"),
+ N_("All gray"),
+ N_("All white"),
+ N_("The first row of the image"),
+ N_("Continuous gradient"),
+ N_("Continuous grad. w/o gap"),
+ N_("Random, ch. independent"),
+ N_("Random shared"),
+ N_("Randoms from seed"),
+ N_("Randoms from seed (shared)")
+};
+
+#define CML_PARAM_NUM 15
+
+typedef struct
+{
+ gint function;
+ gint composition;
+ gint arrange;
+ gint cyclic_range;
+ gdouble mod_rate; /* diff / old-value */
+ gdouble env_sensitivity; /* self-diff : env-diff */
+ gint diffusion_dist;
+ gdouble ch_sensitivity;
+ gint range_num;
+ gdouble power;
+ gdouble parameter_k;
+ gdouble range_l;
+ gdouble range_h;
+ gdouble mutation_rate;
+ gdouble mutation_dist;
+} CML_PARAM;
+
+typedef struct
+{
+ CML_PARAM hue;
+ CML_PARAM sat;
+ CML_PARAM val;
+ gint initial_value;
+ gint scale;
+ gint start_offset;
+ gint seed;
+ gchar last_file_name[256];
+} ValueType;
+
+static ValueType VALS =
+{
+ /* function composition arra
+ cyc chng sens diff cor n pow k (l,h) rnd dist */
+ {
+ CML_SIN_CURVE, COMP_NONE, STANDARD,
+ 1, 0.5, 0.7, 2, 0.0, 1, 1.0, 1.0, 0, 1, 0.0, 0.1
+ },
+ {
+ CML_FILL, COMP_NONE, STANDARD,
+ 0, 0.6, 0.1, 2, 0.0, 1, 1.4, 0.9, 0, 0.9, 0.0, 0.1
+ },
+ {
+ CML_FILL, COMP_NONE, STANDARD,
+ 0, 0.5, 0.2, 2, 0.0, 1, 2.0, 1.0, 0, 0.9, 0.0, 0.1
+ },
+ 6, /* random value 1 */
+ 1, /* scale */
+ 0, /* start_offset */
+ 0, /* seed */
+ "" /* last filename */
+};
+
+static CML_PARAM *channel_params[] =
+{
+ &VALS.hue,
+ &VALS.sat,
+ &VALS.val
+};
+
+static const gchar *channel_names[] =
+{
+ N_("Hue"),
+ N_("Saturation"),
+ N_("Value")
+};
+
+static const gchar *load_channel_names[] =
+{
+ N_("(None)"),
+ N_("Hue"),
+ N_("Saturation"),
+ N_("Value")
+};
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType CML_main_function (gboolean preview_p);
+static void CML_compute_next_step (gint size,
+ gdouble **h,
+ gdouble **s,
+ gdouble **v,
+ gdouble **hn,
+ gdouble **sn,
+ gdouble **vn,
+ gdouble **haux,
+ gdouble **saux,
+ gdouble **vaux);
+static gdouble CML_next_value (gdouble *vec,
+ gint pos,
+ gint size,
+ gdouble c1,
+ gdouble c2,
+ CML_PARAM *param,
+ gdouble aux);
+static gdouble logistic_function (CML_PARAM *param,
+ gdouble x,
+ gdouble power);
+
+
+static gint CML_explorer_dialog (void);
+static GtkWidget * CML_dialog_channel_panel_new (CML_PARAM *param,
+ gint channel_id);
+static GtkWidget * CML_dialog_advanced_panel_new (void);
+
+static void CML_explorer_toggle_entry_init (WidgetEntry *widget_entry,
+ GtkWidget *widget,
+ gpointer value_ptr);
+
+static void CML_explorer_int_entry_init (WidgetEntry *widget_entry,
+ GtkObject *object,
+ gpointer value_ptr);
+
+static void CML_explorer_double_entry_init (WidgetEntry *widget_entry,
+ GtkObject *object,
+ gpointer value_ptr);
+
+static void CML_explorer_menu_update (GtkWidget *widget,
+ gpointer data);
+static void CML_initial_value_menu_update (GtkWidget *widget,
+ gpointer data);
+static void CML_explorer_menu_entry_init (WidgetEntry *widget_entry,
+ GtkWidget *widget,
+ gpointer value_ptr);
+
+static void preview_update (void);
+static void function_graph_new (GtkWidget *widget,
+ gpointer *data);
+static void CML_set_or_randomize_seed_callback (GtkWidget *widget,
+ gpointer data);
+static void CML_copy_parameters_callback (GtkWidget *widget,
+ gpointer data);
+static void CML_initial_value_sensitives_update (void);
+
+static void CML_save_to_file_callback (GtkWidget *widget,
+ gpointer data);
+static void CML_save_to_file_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data);
+
+static void CML_preview_update_callback (GtkWidget *widget,
+ gpointer data);
+static void CML_load_from_file_callback (GtkWidget *widget,
+ gpointer data);
+static gboolean CML_load_parameter_file (const gchar *filename,
+ gboolean interactive_mode);
+static void CML_load_from_file_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data);
+static gint parse_line_to_gint (FILE *file,
+ gboolean *flag);
+static gdouble parse_line_to_gdouble (FILE *file,
+ gboolean *flag);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static GtkWidget *preview;
+static WidgetEntry widget_pointers[4][CML_PARAM_NUM];
+
+static guchar *img;
+static gint img_stride;
+static cairo_surface_t *buffer;
+
+typedef struct
+{
+ GtkWidget *widget;
+ gint logic;
+} CML_sensitive_widget_table;
+
+#define RANDOM_SENSITIVES_NUM 5
+#define GRAPHSIZE 256
+
+static CML_sensitive_widget_table random_sensitives[RANDOM_SENSITIVES_NUM] =
+{
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 }
+};
+
+static GRand *gr;
+static gint drawable_id = 0;
+static gint copy_source = 0;
+static gint copy_destination = 0;
+static gint selective_load_source = 0;
+static gint selective_load_destination = 0;
+static gboolean CML_preview_defer = FALSE;
+
+static gdouble *mem_chank0 = NULL;
+static gint mem_chank0_size = 0;
+static guchar *mem_chank1 = NULL;
+static gint mem_chank1_size = 0;
+static guchar *mem_chank2 = NULL;
+static gint mem_chank2_size = 0;
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args [] =
+ {
+ { GIMP_PDB_INT32, "ru-_mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (not used)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "parameter-filename", "The name of parameter file. CML_explorer makes an image with its settings." }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create abstract Coupled-Map Lattice patterns"),
+ "Make an image of Coupled-Map Lattice (CML). CML is "
+ "a kind of Cellula Automata on continuous (value) "
+ "domain. In GIMP_RUN_NONINTERACTIVE, the name of a "
+ "parameter file is passed as the 4th arg. You can "
+ "control CML_explorer via parameter file.",
+ /* Or do you want to call me with over 50 args? */
+ "Shuji Narazaki (narazaki@InetQ.or.jp); "
+ "http://www.inetq.or.jp/~narazaki/TheGIMP/",
+ "Shuji Narazaki",
+ "1997",
+ N_("CML _Explorer..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &VALS);
+ if (! CML_explorer_dialog ())
+ return;
+ break;
+ case GIMP_RUN_NONINTERACTIVE:
+ {
+ gchar *filename = param[3].data.d_string;
+
+ if (! CML_load_parameter_file (filename, FALSE))
+ return;
+ break;
+ }
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &VALS);
+ break;
+ }
+
+ status = CML_main_function (FALSE);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush();
+ if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS)
+ gimp_set_data (PLUG_IN_PROC, &VALS, sizeof (ValueType));
+
+ g_free (mem_chank0);
+ g_free (mem_chank1);
+ g_free (mem_chank2);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static GimpPDBStatusType
+CML_main_function (gboolean preview_p)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *src_format;
+ const Babl *dest_format;
+ guchar *dest_buf = NULL;
+ guchar *src_buf = NULL;
+ gint x, y;
+ gint dx, dy;
+ gboolean dest_has_alpha = FALSE;
+ gboolean dest_is_gray = FALSE;
+ gboolean src_has_alpha = FALSE;
+ gboolean src_is_gray = FALSE;
+ gint total, processed = 0;
+ gint keep_height = 1;
+ gint cell_num, width_by_pixel, height_by_pixel;
+ gint index;
+ gint src_bpp, src_bpl;
+ gint dest_bpp, dest_bpl;
+ gdouble *hues, *sats, *vals;
+ gdouble *newh, *news, *newv;
+ gdouble *haux, *saux, *vaux;
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x, &y,
+ &width_by_pixel, &height_by_pixel))
+ return GIMP_PDB_SUCCESS;
+
+ src_has_alpha = dest_has_alpha = gimp_drawable_has_alpha (drawable_id);
+ src_is_gray = dest_is_gray = gimp_drawable_is_gray (drawable_id);
+
+ if (src_is_gray)
+ {
+ if (src_has_alpha)
+ src_format = babl_format ("Y'A u8");
+ else
+ src_format = babl_format ("Y' u8");
+ }
+ else
+ {
+ if (src_has_alpha)
+ src_format = babl_format ("R'G'B'A u8");
+ else
+ src_format = babl_format ("R'G'B' u8");
+ }
+
+ dest_format = src_format;
+
+ src_bpp = dest_bpp = babl_format_get_bytes_per_pixel (src_format);
+
+ if (preview_p)
+ {
+ dest_format = babl_format ("R'G'B' u8");
+
+ dest_has_alpha = FALSE;
+ dest_bpp = 3;
+
+ if (width_by_pixel > PREVIEW_WIDTH) /* preview < drawable (selection) */
+ width_by_pixel = PREVIEW_WIDTH;
+ if (height_by_pixel > PREVIEW_HEIGHT)
+ height_by_pixel = PREVIEW_HEIGHT;
+ }
+
+ dest_bpl = width_by_pixel * dest_bpp;
+ src_bpl = width_by_pixel * src_bpp;
+ cell_num = (width_by_pixel - 1)/ VALS.scale + 1;
+ total = height_by_pixel * width_by_pixel;
+
+ if (total < 1)
+ return GIMP_PDB_EXECUTION_ERROR;
+
+ keep_height = VALS.scale;
+
+ /* configure reusable memories */
+ if (mem_chank0_size < 9 * cell_num * sizeof (gdouble))
+ {
+ g_free (mem_chank0);
+ mem_chank0_size = 9 * cell_num * sizeof (gdouble);
+ mem_chank0 = (gdouble *) g_malloc (mem_chank0_size);
+ }
+
+ hues = mem_chank0;
+ sats = mem_chank0 + cell_num;
+ vals = mem_chank0 + 2 * cell_num;
+ newh = mem_chank0 + 3 * cell_num;
+ news = mem_chank0 + 4 * cell_num;
+ newv = mem_chank0 + 5 * cell_num;
+ haux = mem_chank0 + 6 * cell_num;
+ saux = mem_chank0 + 7 * cell_num;
+ vaux = mem_chank0 + 8 * cell_num;
+
+ if (mem_chank1_size < src_bpl * keep_height)
+ {
+ g_free (mem_chank1);
+ mem_chank1_size = src_bpl * keep_height;
+ mem_chank1 = (guchar *) g_malloc (mem_chank1_size);
+ }
+ src_buf = mem_chank1;
+
+ if (mem_chank2_size < dest_bpl * keep_height)
+ {
+ g_free (mem_chank2);
+ mem_chank2_size = dest_bpl * keep_height;
+ mem_chank2 = (guchar *) g_malloc (mem_chank2_size);
+ }
+ dest_buf = mem_chank2;
+
+ if (! preview_p)
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gr = g_rand_new ();
+ if (VALS.initial_value == CML_INITIAL_RANDOM_FROM_SEED)
+ g_rand_set_seed (gr, VALS.seed);
+
+ for (index = 0; index < cell_num; index++)
+ {
+ switch (VALS.hue.arrange)
+ {
+ case RAND_POWER0:
+ haux [index] = g_rand_double_range (gr, 0, 10);
+ break;
+ case RAND_POWER2:
+ case MULTIPLY_GRADIENT:
+ haux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
+ break;
+ case RAND_POWER1:
+ case MULTIPLY_RANDOM0:
+ haux [index] = g_rand_double (gr);
+ break;
+ case MULTIPLY_RANDOM1:
+ haux [index] = g_rand_double_range (gr, 0, 2);
+ break;
+ case RAND_AND_P:
+ haux [index] = ((index % (2 * VALS.hue.diffusion_dist) == 0) ?
+ g_rand_double (gr) : VALS.hue.power);
+ break;
+ default:
+ haux [index] = VALS.hue.power;
+ break;
+ }
+
+ switch (VALS.sat.arrange)
+ {
+ case RAND_POWER0:
+ saux [index] = g_rand_double_range (gr, 0, 10);
+ break;
+ case RAND_POWER2:
+ case MULTIPLY_GRADIENT:
+ saux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
+ break;
+ case RAND_POWER1:
+ case MULTIPLY_RANDOM0:
+ saux [index] = g_rand_double (gr);
+ break;
+ case MULTIPLY_RANDOM1:
+ saux [index] = g_rand_double_range (gr, 0, 2);
+ break;
+ case RAND_AND_P:
+ saux [index] = ((index % (2 * VALS.sat.diffusion_dist) == 0) ?
+ g_rand_double (gr) : VALS.sat.power);
+ break;
+ default:
+ saux [index] = VALS.sat.power;
+ break;
+ }
+
+ switch (VALS.val.arrange)
+ {
+ case RAND_POWER0:
+ vaux [index] = g_rand_double_range (gr, 0, 10);
+ break;
+ case RAND_POWER2:
+ case MULTIPLY_GRADIENT:
+ vaux [index] = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
+ break;
+ case RAND_POWER1:
+ case MULTIPLY_RANDOM0:
+ vaux [index] = g_rand_double (gr);
+ break;
+ case MULTIPLY_RANDOM1:
+ vaux [index] = g_rand_double_range (gr, 0, 2);
+ break;
+ case RAND_AND_P:
+ vaux [index] = ((index % (2 * VALS.val.diffusion_dist) == 0) ?
+ g_rand_double (gr) : VALS.val.power);
+ break;
+ default:
+ vaux [index] = VALS.val.power;
+ break;
+ }
+
+ switch (VALS.initial_value)
+ {
+ case 0:
+ case 1:
+ case 2:
+ hues[index] = sats[index] = vals[index] = 0.5 * (VALS.initial_value);
+ break;
+ case 3: /* use the values of the image (drawable) */
+ break; /* copy from the drawable after this loop */
+ case 4: /* grandient 1 */
+ hues[index] = sats[index] = vals[index]
+ = (gdouble) (index % 256) / (gdouble) 256;
+ break; /* gradinet 2 */
+ case 5:
+ hues[index] = sats[index] = vals[index]
+ = (gdouble) abs ((index % 511) - 255) / (gdouble) 256;
+ break;
+ case CML_INITIAL_RANDOM_INDEPENDENT:
+ case CML_INITIAL_RANDOM_FROM_SEED:
+ hues[index] = g_rand_double (gr);
+ sats[index] = g_rand_double (gr);
+ vals[index] = g_rand_double (gr);
+ break;
+ case CML_INITIAL_RANDOM_SHARED:
+ case CML_INITIAL_RANDOM_FROM_SEED_SHARED:
+ hues[index] = sats[index] = vals[index] = g_rand_double (gr);
+ break;
+ }
+ }
+
+ if (VALS.initial_value == 3)
+ {
+ int index;
+
+ for (index = 0;
+ index < MIN (cell_num, width_by_pixel / VALS.scale);
+ index++)
+ {
+ guchar buffer[4];
+ int rgbi[3];
+ int i;
+
+ gegl_buffer_sample (src_buffer, x + (index * VALS.scale), y, NULL,
+ buffer, src_format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ for (i = 0; i < 3; i++) rgbi[i] = buffer[i];
+ gimp_rgb_to_hsv_int (rgbi, rgbi + 1, rgbi + 2);
+ hues[index] = (gdouble) rgbi[0] / (gdouble) 255;
+ sats[index] = (gdouble) rgbi[1] / (gdouble) 255;
+ vals[index] = (gdouble) rgbi[2] / (gdouble) 255;
+ }
+ }
+
+ if (! preview_p)
+ gimp_progress_init (_("CML Explorer: evoluting"));
+
+ /* rolling start */
+ for (index = 0; index < VALS.start_offset; index++)
+ CML_compute_next_step (cell_num, &hues, &sats, &vals, &newh, &news, &newv,
+ &haux, &saux, &vaux);
+
+ /* rendering */
+ for (dy = 0; dy < height_by_pixel; dy += VALS.scale)
+ {
+ gint r, g, b, h, s, v;
+ gint offset_x, offset_y, dest_offset;
+
+ if (height_by_pixel < dy + keep_height)
+ keep_height = height_by_pixel - dy;
+
+ if ((VALS.hue.function == CML_KEEP_VALUES) ||
+ (VALS.sat.function == CML_KEEP_VALUES) ||
+ (VALS.val.function == CML_KEEP_VALUES))
+ {
+ gegl_buffer_get (src_buffer,
+ GEGL_RECTANGLE (x, y + dy,
+ width_by_pixel, keep_height), 1.0,
+ src_format, src_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ CML_compute_next_step (cell_num,
+ &hues, &sats, &vals,
+ &newh, &news, &newv,
+ &haux, &saux, &vaux);
+
+ for (dx = 0; dx < cell_num; dx++)
+ {
+ h = r = HCANNONIZE (VALS.hue, hues[dx]);
+ s = g = CANNONIZE (VALS.sat, sats[dx]);
+ v = b = CANNONIZE (VALS.val, vals[dx]);
+
+ if (! dest_is_gray)
+ gimp_hsv_to_rgb_int (&r, &g, &b);
+
+ /* render destination */
+ for (offset_y = 0;
+ (offset_y < VALS.scale) && (dy + offset_y < height_by_pixel);
+ offset_y++)
+ for (offset_x = 0;
+ (offset_x < VALS.scale) && (dx * VALS.scale + offset_x < width_by_pixel);
+ offset_x++)
+ {
+ if ((VALS.hue.function == CML_KEEP_VALUES) ||
+ (VALS.sat.function == CML_KEEP_VALUES) ||
+ (VALS.val.function == CML_KEEP_VALUES))
+ {
+ int rgbi[3];
+ int i;
+
+ for (i = 0; i < src_bpp; i++)
+ rgbi[i] = src_buf[offset_y * src_bpl
+ + (dx * VALS.scale + offset_x) * src_bpp + i];
+ if (src_is_gray && (VALS.val.function == CML_KEEP_VALUES))
+ {
+ b = rgbi[0];
+ }
+ else
+ {
+ gimp_rgb_to_hsv_int (rgbi, rgbi + 1, rgbi + 2);
+
+ r = (VALS.hue.function == CML_KEEP_VALUES) ? rgbi[0] : h;
+ g = (VALS.sat.function == CML_KEEP_VALUES) ? rgbi[1] : s;
+ b = (VALS.val.function == CML_KEEP_VALUES) ? rgbi[2] : v;
+ gimp_hsv_to_rgb_int (&r, &g, &b);
+ }
+ }
+
+ dest_offset = (offset_y * dest_bpl +
+ (dx * VALS.scale + offset_x) * dest_bpp);
+
+ if (dest_is_gray)
+ {
+ dest_buf[dest_offset++] = b;
+ if (preview_p)
+ {
+ dest_buf[dest_offset++] = b;
+ dest_buf[dest_offset++] = b;
+ }
+ }
+ else
+ {
+ dest_buf[dest_offset++] = r;
+ dest_buf[dest_offset++] = g;
+ dest_buf[dest_offset++] = b;
+ }
+ if (dest_has_alpha)
+ dest_buf[dest_offset] = 255;
+
+ if ((!preview_p) &&
+ (++processed % (total / PROGRESS_UPDATE_NUM + 1)) == 0)
+ gimp_progress_update ((gdouble) processed / (gdouble) total);
+ }
+ }
+
+ if (preview_p)
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, dy,
+ width_by_pixel, keep_height,
+ GIMP_RGB_IMAGE,
+ dest_buf,
+ dest_bpl);
+ }
+ else
+ {
+ gegl_buffer_set (dest_buffer,
+ GEGL_RECTANGLE (x, y + dy,
+ width_by_pixel, keep_height), 0,
+ dest_format, dest_buf,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+ }
+
+ g_object_unref (src_buffer);
+
+ if (preview_p)
+ {
+ gtk_widget_queue_draw (preview);
+ }
+ else
+ {
+ gimp_progress_update (1.0);
+
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id,
+ x, y, width_by_pixel, height_by_pixel);
+ }
+
+ g_rand_free (gr);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static void
+CML_compute_next_step (gint size,
+ gdouble **h,
+ gdouble **s,
+ gdouble **v,
+ gdouble **hn,
+ gdouble **sn,
+ gdouble **vn,
+ gdouble **haux,
+ gdouble **saux,
+ gdouble **vaux)
+{
+ gint index;
+
+ for (index = 0; index < size; index++)
+ (*hn)[index] = CML_next_value (*h, index, size,
+ (*s)[POS_IN_TORUS (index, size)],
+ (*v)[POS_IN_TORUS (index, size)],
+ &VALS.hue,
+ (*haux)[POS_IN_TORUS (index , size)]);
+ for (index = 0; index < size; index++)
+ (*sn)[index] = CML_next_value (*s, index, size,
+ (*v)[POS_IN_TORUS (index , size)],
+ (*h)[POS_IN_TORUS (index , size)],
+ &VALS.sat,
+ (*saux)[POS_IN_TORUS (index , size)]);
+ for (index = 0; index < size; index++)
+ (*vn)[index] = CML_next_value (*v, index, size,
+ (*h)[POS_IN_TORUS (index , size)],
+ (*s)[POS_IN_TORUS (index , size)],
+ &VALS.val,
+ (*vaux)[POS_IN_TORUS (index , size)]);
+
+#define GD_SWAP(x, y) { gdouble *tmp = *x; *x = *y; *y = tmp; }
+ GD_SWAP (h, hn);
+ GD_SWAP (s, sn);
+ GD_SWAP (v, vn);
+#undef SWAP
+}
+
+#define LOGISTICS(x) logistic_function (param, x, power)
+#define ENV_FACTOR(x) (param->env_sensitivity * LOGISTICS (x))
+#define CHN_FACTOR(x) (param->ch_sensitivity * LOGISTICS (x))
+
+static gdouble
+CML_next_value (gdouble *vec,
+ gint pos,
+ gint size,
+ gdouble c1,
+ gdouble c2,
+ CML_PARAM *param,
+ gdouble power)
+{
+ gdouble val = vec[pos];
+ gdouble diff = 0;
+ gdouble self_diff = 0;
+ gdouble by_env = 0;
+ gdouble self_mod_rate = 0;
+ gdouble hold_rate = 1 - param->mod_rate;
+ gdouble env_factor = 0;
+ gint index;
+
+ self_mod_rate = (1 - param->env_sensitivity - param->ch_sensitivity);
+
+ switch (param->arrange)
+ {
+ case ANTILOG:
+ self_diff = self_mod_rate * LOGISTICS (1 - vec[pos]);
+ for (index = 1; index <= param->diffusion_dist / 2; index++)
+ env_factor += ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)]);
+ if ((param->diffusion_dist % 2) == 1)
+ env_factor += (ENV_FACTOR (1 - vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (1 - vec[POS_IN_TORUS (pos - index, size)])) / 2;
+ env_factor /= (gdouble) param->diffusion_dist;
+ by_env = env_factor + (CHN_FACTOR (1 - c1) + CHN_FACTOR (1 - c2)) / 2;
+ diff = param->mod_rate * (self_diff + by_env);
+ val = hold_rate * vec[pos] + diff;
+ break;
+ case AVERAGE:
+ self_diff = self_mod_rate * LOGISTICS (vec[pos]);
+ for (index = 1; index <= param->diffusion_dist / 2; index++)
+ env_factor += vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)];
+ if ((param->diffusion_dist % 2) == 1)
+ env_factor += (vec[POS_IN_TORUS (pos + index, size)] + vec[POS_IN_TORUS (pos - index, size)]) / 2;
+ env_factor /= (gdouble) param->diffusion_dist;
+ by_env = ENV_FACTOR (env_factor) + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
+ diff = param->mod_rate * (self_diff + by_env);
+ val = hold_rate * vec[pos] + diff;
+ break;
+ case MULTIPLY_RANDOM0:
+ case MULTIPLY_RANDOM1:
+ case MULTIPLY_GRADIENT:
+ {
+ gdouble tmp;
+
+ tmp = power;
+ power = param->power;
+ self_diff = self_mod_rate * LOGISTICS (vec[pos]);
+ for (index = 1; index <= param->diffusion_dist / 2; index++)
+ env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]);
+ if ((param->diffusion_dist % 2) == 1)
+ env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2;
+ env_factor /= (gdouble) param->diffusion_dist;
+ by_env = (env_factor + CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
+ diff = pow (param->mod_rate * (self_diff + by_env), tmp);
+ val = hold_rate * vec[pos] + diff;
+ break;
+ }
+ case STANDARD:
+ case RAND_POWER0:
+ case RAND_POWER1:
+ case RAND_POWER2:
+ case RAND_AND_P:
+ default:
+ self_diff = self_mod_rate * LOGISTICS (vec[pos]);
+
+ for (index = 1; index <= param->diffusion_dist / 2; index++)
+ env_factor += ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)]);
+ if ((param->diffusion_dist % 2) == 1)
+ env_factor += (ENV_FACTOR (vec[POS_IN_TORUS (pos + index, size)])
+ + ENV_FACTOR (vec[POS_IN_TORUS (pos - index, size)])) / 2;
+ env_factor /= (gdouble) param->diffusion_dist;
+ by_env = env_factor + (CHN_FACTOR (c1) + CHN_FACTOR (c2)) / 2;
+ diff = param->mod_rate * (self_diff + by_env);
+ val = hold_rate * vec[pos] + diff;
+ break;
+ }
+ /* finalize */
+ if (g_rand_double (gr) < param->mutation_rate)
+ {
+ val += ((g_rand_double (gr) < 0.5) ? -1.0 : 1.0) * param->mutation_dist * g_rand_double (gr);
+ }
+ if (param->cyclic_range)
+ {
+ if (1.0 < val)
+ val = val - (int) val;
+ else if (val < 0.0)
+ val = val - floor (val);
+ }
+ else
+ /* The range of val should be [0,1], not [0,1).
+ Cannonization shuold be done in color mapping phase. */
+ val = CLAMP (val, 0.0, 1);
+
+ return val;
+}
+#undef LOGISTICS
+#undef ENV_FACTOR
+#undef CHN_FACTOR
+
+
+static gdouble
+logistic_function (CML_PARAM *param,
+ gdouble x,
+ gdouble power)
+{
+ gdouble x1 = x;
+ gdouble result = 0;
+ gint n = param->range_num;
+ gint step;
+
+ step = (int) (x * (gdouble) n);
+ x1 = (x - ((gdouble) step / (gdouble) n)) * n;
+ switch (param->function)
+ {
+ case CML_KEEP_VALUES:
+ case CML_KEEP_FIRST:
+ result = x;
+ return result;
+ break;
+ case CML_FILL:
+ result = CLAMP (param->parameter_k, 0.0, 1.0);
+ return result;
+ break;
+ case CML_LOGIST:
+ result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power);
+ break;
+ case CML_LOGIST_STEP:
+ result = param->parameter_k * pow (4 * x1 * (1.0 - x1), power);
+ result = (result + step) / (gdouble) n;
+ break;
+ case CML_POWER:
+ result = param->parameter_k * pow (x1, power);
+ break;
+ case CML_POWER_STEP:
+ result = param->parameter_k * pow (x1, power);
+ result = (result + step) / (gdouble) n;
+ break;
+ case CML_REV_POWER:
+ result = param->parameter_k * (1 - pow (x1, power));
+ break;
+ case CML_REV_POWER_STEP:
+ result = param->parameter_k * (1 - pow (x1, power));
+ result = (result + step) / (gdouble) n;
+ break;
+ case CML_DELTA:
+ result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1));
+ break;
+ case CML_DELTA_STEP:
+ result = param->parameter_k * 2 * ((x1 < 0.5) ? x1 : (1.0 - x1));
+ result = (result + step) / (gdouble) n;
+ break;
+ case CML_SIN_CURVE:
+ if (1.0 < power)
+ result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1);
+ else
+ result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1);
+ if (x1 < 0.5) result = 1 - result;
+ break;
+ case CML_SIN_CURVE_STEP:
+ if (1.0 < power)
+ result = 0.5 * (sin (G_PI * ABS (x1 - 0.5) / power) / sin (G_PI * 0.5 / power) + 1);
+ else
+ result = 0.5 * (pow (sin (G_PI * ABS (x1 - 0.5)), power) + 1);
+ if (x1 < 0.5) result = 1 - result;
+ result = (result + step) / (gdouble) n;
+ break;
+ }
+ switch (param->composition)
+ {
+ case COMP_NONE:
+ break;
+ case COMP_MAX_LINEAR:
+ result = MAX ((gdouble) x, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_P1:
+ result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_P1L:
+ if (x < 0.5)
+ result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_P1U:
+ if (0.5 < x)
+ result = MAX ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_M1:
+ result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_M1L:
+ if (x < 0.5)
+ result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MAX_LINEAR_M1U:
+ if (0.5 < x)
+ result = MAX ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR:
+ result = MIN ((gdouble) x, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_P1:
+ result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_P1L:
+ if (x < 0.5)
+ result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_P1U:
+ if (0.5 < x)
+ result = MIN ((gdouble) x + (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_M1:
+ result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_M1L:
+ if (x < 0.5)
+ result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ case COMP_MIN_LINEAR_M1U:
+ if (0.5 < x)
+ result = MIN ((gdouble) x - (gdouble) 1 / (gdouble) 256, (gdouble) result);
+ break;
+ }
+ return result;
+}
+
+/* dialog stuff */
+static gint
+CML_explorer_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *abox;
+ GtkWidget *bbox;
+ GtkWidget *button;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Coupled-Map-Lattice Explorer"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ CML_preview_defer = TRUE;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview,
+ PREVIEW_WIDTH, PREVIEW_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+ gtk_widget_show (preview);
+
+ bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_label (_("New Seed"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_preview_update_callback),
+ &VALS);
+
+ random_sensitives[0].widget = button;
+ random_sensitives[0].logic = TRUE;
+
+ button = gtk_button_new_with_label (_("Fix Seed"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_set_or_randomize_seed_callback),
+ &VALS);
+
+ random_sensitives[1].widget = button;
+ random_sensitives[1].logic = TRUE;
+
+ button = gtk_button_new_with_label (_("Random Seed"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_set_or_randomize_seed_callback),
+ &VALS);
+
+ random_sensitives[2].widget = button;
+ random_sensitives[2].logic = FALSE;
+
+ bbox = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_load_from_file_callback),
+ &VALS);
+
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_save_to_file_callback),
+ &VALS);
+
+ {
+ GtkWidget *notebook;
+ GtkWidget *page;
+
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
+ gtk_widget_show (notebook);
+
+ page = CML_dialog_channel_panel_new (&VALS.hue, 0);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
+ gtk_label_new_with_mnemonic (_("_Hue")));
+
+ page = CML_dialog_channel_panel_new (&VALS.sat, 1);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
+ gtk_label_new_with_mnemonic (_("Sat_uration")));
+
+ page = CML_dialog_channel_panel_new (&VALS.val, 2);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
+ gtk_label_new_with_mnemonic (_("_Value")));
+
+ page = CML_dialog_advanced_panel_new ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page,
+ gtk_label_new_with_mnemonic (_("_Advanced")));
+
+ {
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkObject *adj;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Channel Independent Parameters"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new_array (CML_INITIAL_NUM_VALUES,
+ initial_value_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ VALS.initial_value);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (CML_initial_value_menu_update),
+ &VALS.initial_value);
+
+ CML_explorer_menu_entry_init (&widget_pointers[3][0],
+ combo, &VALS.initial_value);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Initial value:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Zoom scale:"), SCALE_WIDTH, 3,
+ VALS.scale, 1, 10, 1, 2, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ CML_explorer_int_entry_init (&widget_pointers[3][1],
+ adj, &VALS.scale);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Start offset:"), SCALE_WIDTH, 3,
+ VALS.start_offset, 0, 100, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ CML_explorer_int_entry_init (&widget_pointers[3][2],
+ adj, &VALS.start_offset);
+
+ frame =
+ gimp_frame_new (_("Seed of Random (only for \"From Seed\" Modes)"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Seed:"), SCALE_WIDTH, 0,
+ VALS.seed, 0, (guint32) -1, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ CML_explorer_int_entry_init (&widget_pointers[3][3],
+ adj, &VALS.seed);
+
+ random_sensitives[3].widget = table;
+ random_sensitives[3].logic = FALSE;
+
+ button =
+ gtk_button_new_with_label
+ (_("Switch to \"From seed\" With the Last Seed"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 3, 1, 2);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_set_or_randomize_seed_callback),
+ &VALS);
+
+ random_sensitives[4].widget = button;
+ random_sensitives[4].logic = TRUE;
+
+ gimp_help_set_help_data (button,
+ _("\"Fix seed\" button is an alias of me.\n"
+ "The same seed produces the same image, "
+ "if (1) the widths of images are same "
+ "(this is the reason why image on drawable "
+ "is different from preview), and (2) all "
+ "mutation rates equal to zero."), NULL);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("O_thers")));
+ }
+
+ {
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *vbox;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Copy Settings"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names),
+ channel_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), copy_source);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &copy_source);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Source channel:"), 0.0, 0.5,
+ combo, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (channel_names),
+ channel_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ copy_destination);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &copy_destination);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Destination channel:"), 0.0, 0.5,
+ combo, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ button = gtk_button_new_with_label (_("Copy Parameters"));
+ gtk_table_attach (GTK_TABLE (table), button, 0, 2, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (CML_copy_parameters_callback),
+ &VALS);
+
+ frame = gimp_frame_new (_("Selective Load Settings"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names),
+ load_channel_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ selective_load_source);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &selective_load_source);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Source channel in file:"),
+ 0.0, 0.5,
+ combo, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ combo = gimp_int_combo_box_new_array (G_N_ELEMENTS (load_channel_names),
+ load_channel_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ selective_load_destination);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &selective_load_destination);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Destination channel:"),
+ 0.0, 0.5,
+ combo, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("_Misc Ops.")));
+ }
+ }
+
+ CML_initial_value_sensitives_update ();
+
+ gtk_widget_show (dialog);
+
+ img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, GRAPHSIZE);
+ img = g_malloc0 (img_stride * GRAPHSIZE);
+
+ buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24,
+ GRAPHSIZE,
+ GRAPHSIZE,
+ img_stride);
+
+ /* Displaying preview might takes a long time. Thus, first, dialog itself
+ * should be shown before making preview in it.
+ */
+ CML_preview_defer = FALSE;
+ preview_update ();
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+ g_free (img);
+ cairo_surface_destroy (buffer);
+
+ return run;
+}
+
+static GtkWidget *
+CML_dialog_channel_panel_new (CML_PARAM *param,
+ gint channel_id)
+{
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *toggle;
+ GtkWidget *button;
+ GtkObject *adj;
+ gpointer *chank;
+ gint index = 0;
+
+ table = gtk_table_new (13, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new_array (CML_NUM_VALUES, function_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->function);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (CML_explorer_menu_update),
+ &param->function);
+
+ CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
+ combo, &param->function);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, index,
+ _("Function type:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ index++;
+
+ combo = gimp_int_combo_box_new_array (COMP_NUM_VALUES, composition_names);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ param->composition);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (CML_explorer_menu_update),
+ &param->composition);
+
+ CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
+ combo, &param->composition);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, index,
+ _("Composition:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ index++;
+
+ combo = gimp_int_combo_box_new_array (ARRANGE_NUM_VALUES, arrange_names);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), param->arrange);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (CML_explorer_menu_update),
+ &param->arrange);
+
+ CML_explorer_menu_entry_init (&widget_pointers[channel_id][index],
+ combo, &param->arrange);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, index,
+ _("Misc arrange:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ index++;
+
+ toggle = gtk_check_button_new_with_label (_("Use cyclic range"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ param->cyclic_range);
+ gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, index, index + 1);
+ CML_explorer_toggle_entry_init (&widget_pointers[channel_id][index],
+ toggle, &param->cyclic_range);
+ gtk_widget_show (toggle);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Mod. rate:"), SCALE_WIDTH, 5,
+ param->mod_rate, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->mod_rate);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Env. sensitivity:"), SCALE_WIDTH, 5,
+ param->env_sensitivity, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->env_sensitivity);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Diffusion dist.:"), SCALE_WIDTH, 5,
+ param->diffusion_dist, 2, 10, 1, 2, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_int_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->diffusion_dist);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("# of subranges:"), SCALE_WIDTH, 5,
+ param->range_num, 1, 10, 1, 2, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_int_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->range_num);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("P(ower factor):"), SCALE_WIDTH, 5,
+ param->power, 0.0, 10.0, 0.1, 1.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->power);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Parameter k:"), SCALE_WIDTH, 5,
+ param->parameter_k, 0.0, 10.0, 0.1, 1.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->parameter_k);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Range low:"), SCALE_WIDTH, 5,
+ param->range_l, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->range_l);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Range high:"), SCALE_WIDTH, 5,
+ param->range_h, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index],
+ adj, &param->range_h);
+ index++;
+
+ chank = g_new (gpointer, 2);
+ chank[0] = GINT_TO_POINTER (channel_id);
+ chank[1] = param;
+
+ button = gtk_button_new_with_label (_("Plot a Graph of the Settings"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button,
+ 0, 3, index, index + 1);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (function_graph_new),
+ chank);
+ return table;
+}
+
+static GtkWidget *
+CML_dialog_advanced_panel_new (void)
+{
+ GtkWidget *vbox;
+ GtkWidget *subframe;
+ GtkWidget *table;
+ GtkObject *adj;
+
+ gint index = 0;
+ gint widget_offset = 12;
+ gint channel_id;
+ CML_PARAM *param;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+
+ for (channel_id = 0; channel_id < 3; channel_id++)
+ {
+ param = (CML_PARAM *)&VALS + channel_id;
+
+ subframe = gimp_frame_new (gettext (channel_names[channel_id]));
+ gtk_box_pack_start (GTK_BOX (vbox), subframe, FALSE, FALSE, 0);
+ gtk_widget_show (subframe);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (subframe), table);
+ gtk_widget_show (table);
+
+ index = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Ch. sensitivity:"), SCALE_WIDTH, 0,
+ param->ch_sensitivity, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
+ widget_offset],
+ adj, &param->ch_sensitivity);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Mutation rate:"), SCALE_WIDTH, 0,
+ param->mutation_rate, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
+ widget_offset],
+ adj, &param->mutation_rate);
+ index++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, index,
+ _("Mutation dist.:"), SCALE_WIDTH, 0,
+ param->mutation_dist, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ CML_explorer_double_entry_init (&widget_pointers[channel_id][index +
+ widget_offset],
+ adj, &param->mutation_dist);
+ }
+ return vbox;
+}
+
+static void
+preview_update (void)
+{
+ if (! CML_preview_defer)
+ CML_main_function (TRUE);
+}
+
+static gboolean
+function_graph_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer *data)
+{
+ GtkStyle *style = gtk_widget_get_style (widget);
+ gint x, y;
+ gint rgbi[3];
+ gint channel_id = GPOINTER_TO_INT (data[0]);
+ CML_PARAM *param = data[1];
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ cairo_set_line_width (cr, 1.0);
+
+ cairo_surface_flush (buffer);
+
+ for (x = 0; x < GRAPHSIZE; x++)
+ {
+ /* hue */
+ rgbi[0] = rgbi[1] = rgbi[2] = 127;
+ if ((0 <= channel_id) && (channel_id <= 2))
+ rgbi[channel_id] = CANNONIZE ((*param), ((gdouble) x / (gdouble) 255));
+ gimp_hsv_to_rgb_int (rgbi, rgbi+1, rgbi+2);
+ for (y = 0; y < GRAPHSIZE; y++)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL((img+(y*img_stride+x*4)),
+ rgbi[0],
+ rgbi[1],
+ rgbi[2]);
+ }
+ }
+
+ cairo_surface_mark_dirty (buffer);
+
+ cairo_set_source_surface (cr, buffer, 0.0, 0.0);
+
+ cairo_paint (cr);
+ cairo_translate (cr, 0.5, 0.5);
+
+ cairo_move_to (cr, 0, 255);
+ cairo_line_to (cr, 255, 0);
+ gdk_cairo_set_source_color (cr, &style->white);
+ cairo_stroke (cr);
+
+ y = 255 * CLAMP (logistic_function (param, 0, param->power),
+ 0, 1.0);
+ cairo_move_to (cr, 0, 255-y);
+ for (x = 0; x < GRAPHSIZE; x++)
+ {
+ /* curve */
+ y = 255 * CLAMP (logistic_function (param, x/(gdouble)255, param->power),
+ 0, 1.0);
+ cairo_line_to (cr, x, 255-y);
+ }
+
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static void
+function_graph_new (GtkWidget *widget,
+ gpointer *data)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *preview;
+
+ dialog = gimp_dialog_new (_("Graph of the Current Settings"), PLUG_IN_ROLE,
+ gtk_widget_get_toplevel (widget), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ preview = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (preview, GRAPHSIZE, GRAPHSIZE);
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+ gtk_widget_show (preview);
+ g_signal_connect (preview, "expose-event",
+ G_CALLBACK (function_graph_expose), data);
+
+ gtk_widget_show (dialog);
+
+ gimp_dialog_run (GIMP_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+CML_set_or_randomize_seed_callback (GtkWidget *widget,
+ gpointer data)
+{
+ CML_preview_defer = TRUE;
+
+ switch (VALS.initial_value)
+ {
+ case CML_INITIAL_RANDOM_INDEPENDENT:
+ VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED;
+ break;
+ case CML_INITIAL_RANDOM_SHARED:
+ VALS.initial_value = CML_INITIAL_RANDOM_FROM_SEED_SHARED;
+ break;
+ case CML_INITIAL_RANDOM_FROM_SEED:
+ VALS.initial_value = CML_INITIAL_RANDOM_INDEPENDENT;
+ break;
+ case CML_INITIAL_RANDOM_FROM_SEED_SHARED:
+ VALS.initial_value = CML_INITIAL_RANDOM_SHARED;
+ break;
+ default:
+ break;
+ }
+ if (widget_pointers[3][3].widget && widget_pointers[3][3].updater)
+ (widget_pointers[3][3].updater) (widget_pointers[3]+3);
+ if (widget_pointers[3][0].widget && widget_pointers[3][0].updater)
+ (widget_pointers[3][0].updater) (widget_pointers[3]);
+
+ CML_initial_value_sensitives_update ();
+
+ CML_preview_defer = FALSE;
+}
+
+static void
+CML_copy_parameters_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint index;
+ WidgetEntry *widgets;
+
+ if (copy_source == copy_destination)
+ {
+ g_message (_("Warning: the source and the destination are the same channel."));
+ return;
+ }
+ *channel_params[copy_destination] = *channel_params[copy_source];
+ CML_preview_defer = TRUE;
+ widgets = widget_pointers[copy_destination];
+
+ for (index = 0; index < CML_PARAM_NUM; index++)
+ if (widgets[index].widget && widgets[index].updater)
+ (widgets[index].updater) (widgets + index);
+
+ CML_preview_defer = FALSE;
+ preview_update ();
+}
+
+static void
+CML_initial_value_sensitives_update (void)
+{
+ gint i = 0;
+ gint flag1, flag2;
+
+ flag1 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value)
+ & (VALS.initial_value <= CML_INITIAL_RANDOM_FROM_SEED_SHARED);
+ flag2 = (CML_INITIAL_RANDOM_INDEPENDENT <= VALS.initial_value)
+ & (VALS.initial_value <= CML_INITIAL_RANDOM_SHARED);
+
+ for (; i < G_N_ELEMENTS (random_sensitives) ; i++)
+ if (random_sensitives[i].widget)
+ gtk_widget_set_sensitive (random_sensitives[i].widget,
+ flag1 & (random_sensitives[i].logic == flag2));
+}
+
+static void
+CML_preview_update_callback (GtkWidget *widget,
+ gpointer data)
+{
+ WidgetEntry seed_widget = widget_pointers[3][3];
+
+ preview_update ();
+
+ CML_preview_defer = TRUE;
+
+ if (seed_widget.widget && seed_widget.updater)
+ seed_widget.updater (&seed_widget);
+
+ CML_preview_defer = FALSE;
+}
+
+/* parameter file saving functions */
+
+static void
+CML_save_to_file_callback (GtkWidget *widget,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (! dialog)
+ {
+ dialog =
+ gtk_file_chooser_dialog_new (_("Save CML Explorer Parameters"),
+ GTK_WINDOW (gtk_widget_get_toplevel (widget)),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (CML_save_to_file_response),
+ NULL);
+ g_signal_connect (dialog, "delete-event",
+ G_CALLBACK (gtk_true),
+ NULL);
+ }
+
+ if (strlen (VALS.last_file_name))
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog),
+ VALS.last_file_name);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+CML_save_to_file_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ gchar *filename;
+ FILE *file;
+ gint channel_id;
+
+ if (response_id != GTK_RESPONSE_OK)
+ {
+ gtk_widget_hide (dialog);
+ return;
+ }
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ if (! filename)
+ return;
+
+ file = g_fopen (filename, "wb");
+
+ if (! file)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ g_free (filename);
+ return;
+ }
+
+ fprintf (file, "; This is a parameter file for CML_explorer\n");
+ fprintf (file, "; File format version: %1.1f\n", PARAM_FILE_FORMAT_VERSION);
+ fprintf (file, ";\n");
+
+ for (channel_id = 0; channel_id < 3; channel_id++)
+ {
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ CML_PARAM param = *(CML_PARAM *)(channel_params[channel_id]);
+
+ fprintf (file, "\t%s\n", channel_names[channel_id]);
+ fprintf (file, "Function_type : %d (%s)\n",
+ param.function, function_names[param.function]);
+ fprintf (file, "Compostion_type : %d (%s)\n",
+ param.composition, composition_names[param.composition]);
+ fprintf (file, "Arrange : %d (%s)\n",
+ param.arrange, arrange_names[param.arrange]);
+ fprintf (file, "Cyclic_range : %d (%s)\n",
+ param.cyclic_range, (param.cyclic_range ? "TRUE" : "FALSE"));
+ fprintf (file, "Mod. rate : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.mod_rate));
+ fprintf (file, "Env_sensitivtiy : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.env_sensitivity));
+ fprintf (file, "Diffusion dist. : %d\n", param.diffusion_dist);
+ fprintf (file, "Ch. sensitivity : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.ch_sensitivity));
+ fprintf (file, "Num. of Subranges: %d\n", param.range_num);
+ fprintf (file, "Power_factor : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.power));
+ fprintf (file, "Parameter_k : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.parameter_k));
+ fprintf (file, "Range_low : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.range_l));
+ fprintf (file, "Range_high : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.range_h));
+ fprintf (file, "Mutation_rate : %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.mutation_rate));
+ fprintf (file, "Mutation_distance: %s\n",
+ g_ascii_dtostr (buf, sizeof (buf), param.mutation_dist));
+ }
+
+ fprintf (file, "\n");
+ fprintf (file, "Initial value : %d (%s)\n",
+ VALS.initial_value, initial_value_names[VALS.initial_value]);
+ fprintf (file, "Zoom scale : %d\n", VALS.scale);
+ fprintf (file, "Start offset : %d\n", VALS.start_offset);
+ fprintf (file, "Random seed : %d\n", VALS.seed);
+ fclose(file);
+
+ g_message (_("Parameters were saved to '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ strncpy (VALS.last_file_name, filename,
+ sizeof (VALS.last_file_name) - 1);
+
+ g_free (filename);
+
+ gtk_widget_hide (dialog);
+}
+
+/* parameter file loading functions */
+
+static void
+CML_load_from_file_callback (GtkWidget *widget,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (! dialog)
+ {
+ dialog =
+ gtk_file_chooser_dialog_new (_("Load CML Explorer Parameters"),
+ GTK_WINDOW (gtk_widget_get_toplevel (widget)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (CML_load_from_file_response),
+ NULL);
+ g_signal_connect (dialog, "delete-event",
+ G_CALLBACK (gtk_true),
+ NULL);
+ }
+
+ if (strlen (VALS.last_file_name) > 0)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog),
+ VALS.last_file_name);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+CML_load_from_file_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+ gint channel_id;
+ gboolean flag = TRUE;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ gtk_widget_set_sensitive (dialog, FALSE);
+ flag = CML_load_parameter_file (filename, TRUE);
+
+ g_free (filename);
+
+ if (flag)
+ {
+ WidgetEntry *widgets;
+ gint index;
+
+ CML_preview_defer = TRUE;
+
+ for (channel_id = 0; channel_id < 3; channel_id++)
+ {
+ widgets = widget_pointers[channel_id];
+ for (index = 0; index < CML_PARAM_NUM; index++)
+ if (widgets[index].widget && widgets[index].updater)
+ (widgets[index].updater) (widgets + index);
+ }
+ /* channel independent parameters */
+ widgets = widget_pointers[3];
+ for (index = 0; index < 4; index++)
+ if (widgets[index].widget && widgets[index].updater)
+ (widgets[index].updater) (widgets + index);
+
+ CML_preview_defer = FALSE;
+
+ preview_update ();
+ }
+ }
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+static gboolean
+CML_load_parameter_file (const gchar *filename,
+ gboolean interactive_mode)
+{
+ FILE *file;
+ gint channel_id;
+ gboolean flag = TRUE;
+ CML_PARAM ch[3];
+ gint initial_value = 0;
+ gint scale = 1;
+ gint start_offset = 0;
+ gint seed = 0;
+ gint old2new_function_id[] = { 3, 4, 5, 6, 7, 9, 10, 11, 1, 2 };
+
+ file = g_fopen (filename, "rb");
+
+ if (!file)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+ else
+ {
+ gchar line[CML_LINE_SIZE];
+ gdouble version = 0.99;
+
+ version = parse_line_to_gdouble (file, &flag); /* old format returns 1 */
+ if (version == 1.0)
+ version = 0.99;
+ else if (! flag)
+ {
+ flag = TRUE;
+ version = parse_line_to_gdouble (file, &flag); /* maybe new format */
+ if (flag)
+ fgets (line, CML_LINE_SIZE - 1, file); /* one more comment line */
+ }
+ if (version == 0)
+ {
+ if (interactive_mode)
+ gimp_message (_("Error: it's not CML parameter file."));
+ fclose(file);
+ return FALSE;
+ }
+ if (interactive_mode)
+ {
+ if (version < PARAM_FILE_FORMAT_VERSION)
+ g_message (_("Warning: '%s' is an old format file."),
+ gimp_filename_to_utf8 (filename));
+
+ if (PARAM_FILE_FORMAT_VERSION < version)
+ g_message (_("Warning: '%s' is a parameter file for a newer "
+ "version of CML Explorer."),
+ gimp_filename_to_utf8 (filename));
+ }
+ for (channel_id = 0; flag && (channel_id < 3); channel_id++)
+ {
+ /* patched by Tim Mooney <mooney@dogbert.cc.ndsu.NoDak.edu> */
+ if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) /* skip channel name */
+ {
+ flag = FALSE;
+ break;
+ }
+ ch[channel_id].function = parse_line_to_gint (file, &flag);
+ if (version < 1.0)
+ ch[channel_id].function = old2new_function_id [ch[channel_id].function];
+ if (1.0 <= version)
+ ch[channel_id].composition = parse_line_to_gint (file, &flag);
+ else
+ ch[channel_id].composition = COMP_NONE;
+ ch[channel_id].arrange = parse_line_to_gint (file, &flag);
+ ch[channel_id].cyclic_range = parse_line_to_gint (file, &flag);
+ ch[channel_id].mod_rate = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].env_sensitivity = parse_line_to_gdouble (file, &flag);
+ if (1.0 <= version)
+ ch[channel_id].diffusion_dist = parse_line_to_gint (file, &flag);
+ else
+ ch[channel_id].diffusion_dist = 2;
+ ch[channel_id].ch_sensitivity = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].range_num = parse_line_to_gint (file, &flag);
+ ch[channel_id].power = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].parameter_k = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].range_l = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].range_h = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].mutation_rate = parse_line_to_gdouble (file, &flag);
+ ch[channel_id].mutation_dist = parse_line_to_gdouble (file, &flag);
+ }
+ if (flag)
+ {
+ gint dummy;
+
+ if (fgets (line, CML_LINE_SIZE - 1, file) == NULL) /* skip a line */
+ dummy = 1;
+ else
+ {
+ initial_value = parse_line_to_gint (file, &dummy);
+ scale = parse_line_to_gint (file, &dummy);
+ start_offset = parse_line_to_gint (file, &dummy);
+ seed = parse_line_to_gint (file, &dummy);
+ }
+ if (! dummy)
+ {
+ initial_value = 0;
+ scale = 1;
+ start_offset = 0;
+ seed = 0;
+ }
+ }
+ fclose (file);
+ }
+
+ if (! flag)
+ {
+ if (interactive_mode)
+ gimp_message (_("Error: failed to load parameters"));
+ }
+ else
+ {
+ if ((selective_load_source == 0) || (selective_load_destination == 0))
+ {
+ VALS.hue = ch[0];
+ VALS.sat = ch[1];
+ VALS.val = ch[2];
+
+ VALS.initial_value = initial_value;
+ VALS.scale = scale;
+ VALS.start_offset = start_offset;
+ VALS.seed = seed;
+ }
+ else
+ {
+ memcpy ((CML_PARAM *)&VALS + (selective_load_destination - 1),
+ (void *)&ch[selective_load_source - 1],
+ sizeof (CML_PARAM));
+ }
+
+ strncpy (VALS.last_file_name, filename,
+ sizeof (VALS.last_file_name) - 1);
+ }
+ return flag;
+}
+
+static gint
+parse_line_to_gint (FILE *file,
+ gboolean *flag)
+{
+ gchar line[CML_LINE_SIZE];
+ gchar *str;
+
+ if (! *flag)
+ return 0;
+ if (fgets (line, CML_LINE_SIZE - 1, file) == NULL)
+ {
+ *flag = FALSE; /* set FALSE if fail to parse */
+ return 0;
+ }
+ str = &line[0];
+ while (*str != ':')
+ if (*str == '\000')
+ {
+ *flag = FALSE;
+ return 0;
+ }
+ else
+ {
+ str++;
+ }
+
+ return atoi (str + 1);
+}
+
+static gdouble
+parse_line_to_gdouble (FILE *file,
+ gboolean *flag)
+{
+ gchar line[CML_LINE_SIZE];
+ gchar *str;
+
+ if (! *flag)
+ return 0.0;
+
+ if (fgets (line, CML_LINE_SIZE - 1, file) == NULL)
+ {
+ *flag = FALSE; /* set FALSE if fail to parse */
+ return 0.0;
+ }
+ str = &line[0];
+ while (*str != ':')
+ if (*str == '\000')
+ {
+ *flag = FALSE;
+ return 0.0;
+ }
+ else
+ {
+ str++;
+ }
+
+ return g_ascii_strtod (str + 1, NULL);
+}
+
+
+/* toggle button functions */
+
+static void
+CML_explorer_toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ preview_update ();
+}
+
+static void
+CML_explorer_toggle_entry_change_value (WidgetEntry *widget_entry)
+{
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget_entry->widget),
+ *(gint *) (widget_entry->value));
+}
+
+static void
+CML_explorer_toggle_entry_init (WidgetEntry *widget_entry,
+ GtkWidget *widget,
+ gpointer value_ptr)
+{
+ g_signal_connect (widget, "toggled",
+ G_CALLBACK (CML_explorer_toggle_update),
+ value_ptr);
+
+ widget_entry->widget = widget;
+ widget_entry->value = value_ptr;
+ widget_entry->updater = CML_explorer_toggle_entry_change_value;
+}
+
+/* int adjustment functions */
+
+static void
+CML_explorer_int_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_int_adjustment_update (adjustment, data);
+
+ preview_update ();
+}
+
+static void
+CML_explorer_int_entry_change_value (WidgetEntry *widget_entry)
+{
+ GtkAdjustment *adjustment = (GtkAdjustment *) (widget_entry->widget);
+
+ gtk_adjustment_set_value (adjustment, *(gint *) (widget_entry->value));
+}
+
+static void
+CML_explorer_int_entry_init (WidgetEntry *widget_entry,
+ GtkObject *adjustment,
+ gpointer value_ptr)
+{
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (CML_explorer_int_adjustment_update),
+ value_ptr);
+
+ widget_entry->widget = (GtkWidget *) adjustment;
+ widget_entry->value = value_ptr;
+ widget_entry->updater = CML_explorer_int_entry_change_value;
+}
+
+/* double adjustment functions */
+
+static void
+CML_explorer_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_double_adjustment_update (adjustment, data);
+
+ preview_update ();
+}
+
+static void
+CML_explorer_double_entry_change_value (WidgetEntry *widget_entry)
+{
+ GtkAdjustment *adjustment = (GtkAdjustment *) (widget_entry->widget);
+
+ gtk_adjustment_set_value (adjustment, *(gdouble *) (widget_entry->value));
+}
+
+static void
+CML_explorer_double_entry_init (WidgetEntry *widget_entry,
+ GtkObject *adjustment,
+ gpointer value_ptr)
+{
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (CML_explorer_double_adjustment_update),
+ value_ptr);
+
+ widget_entry->widget = (GtkWidget *) adjustment;
+ widget_entry->value = value_ptr;
+ widget_entry->updater = CML_explorer_double_entry_change_value;
+}
+
+/* menu functions */
+
+static void
+CML_explorer_menu_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
+
+ preview_update ();
+}
+
+static void
+CML_initial_value_menu_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
+
+ CML_initial_value_sensitives_update ();
+ preview_update ();
+}
+
+static void
+CML_explorer_menu_entry_change_value (WidgetEntry *widget_entry)
+{
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget_entry->widget),
+ *(gint *) (widget_entry->value));
+}
+
+static void
+CML_explorer_menu_entry_init (WidgetEntry *widget_entry,
+ GtkWidget *widget,
+ gpointer value_ptr)
+{
+ widget_entry->widget = widget;
+ widget_entry->value = value_ptr;
+ widget_entry->updater = CML_explorer_menu_entry_change_value;
+}
diff --git a/plug-ins/common/color-cube-analyze.c b/plug-ins/common/color-cube-analyze.c
new file mode 100644
index 0000000..c58c177
--- /dev/null
+++ b/plug-ins/common/color-cube-analyze.c
@@ -0,0 +1,502 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * Analyze colorcube.
+ *
+ * Author: robert@experimental.net
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-ccanalyze"
+#define PLUG_IN_BINARY "color-cube-analyze"
+#define PLUG_IN_ROLE "gimp-color-cube-analyze"
+
+/* size of histogram image */
+#define PREWIDTH 256
+#define PREHEIGHT 150
+
+/* lets prototype */
+static void query (void);
+static void run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void doDialog (void);
+static void analyze (GimpDrawable *drawable);
+
+static void histogram (guchar r,
+ guchar g,
+ guchar b,
+ gdouble a);
+static void fillPreview (GtkWidget *preview);
+static void insertcolor (guchar r,
+ guchar g,
+ guchar b,
+ gdouble a);
+
+static void doLabel (GtkWidget *table,
+ const char *format,
+ ...) G_GNUC_PRINTF (2, 3);
+
+/* some global variables */
+static gint width, height, bpp;
+static gdouble hist_red[256], hist_green[256], hist_blue[256];
+static gdouble maxred = 0.0, maxgreen = 0.0, maxblue = 0.0;
+static gint uniques = 0;
+static gint32 imageID;
+
+/* lets declare what we want to do */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* run program */
+MAIN ()
+
+/* tell GIMP who we are */
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_INT32, "num-colors", "Number of colors in the image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Analyze the set of colors in the image"),
+ "Analyze colorcube and print some information about "
+ "the current image (also displays a color-histogram)",
+ "robert@experimental.net",
+ "robert@experimental.net",
+ "June 20th, 1997",
+ N_("Colorcube A_nalysis..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Info");
+}
+
+/* main function */
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpDrawable *drawable;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ if (n_params != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+ imageID = param[1].data.d_image;
+
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id) ||
+ gimp_drawable_is_indexed (drawable->drawable_id))
+ {
+ memset (hist_red, 0, sizeof (hist_red));
+ memset (hist_green, 0, sizeof (hist_green));
+ memset (hist_blue, 0, sizeof (hist_blue));
+
+ gimp_tile_cache_ntiles (2 *
+ (drawable->width / gimp_tile_width () + 1));
+
+ analyze (drawable);
+
+ /* show dialog after we analyzed image */
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ doDialog ();
+ }
+ else
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ gimp_drawable_detach (drawable);
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = uniques;
+}
+
+/* do the analyzing */
+static void
+analyze (GimpDrawable *drawable)
+{
+ GimpPixelRgn srcPR;
+ guchar *src_row, *cmap;
+ gint x, y, numcol;
+ gint x1, y1, x2, y2, w, h;
+ guchar r, g, b;
+ gint a;
+ guchar idx;
+ gboolean gray;
+ gboolean has_alpha;
+ gboolean has_sel;
+ guchar *sel;
+ GimpPixelRgn selPR;
+ gint ofsx, ofsy;
+ GimpDrawable *selDrawable;
+
+ gimp_progress_init (_("Colorcube Analysis"));
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id, &x1, &y1, &w, &h))
+ return;
+
+ x2 = x1 + w;
+ y2 = y1 + h;
+
+ /*
+ * Get the size of the input image (this will/must be the same
+ * as the size of the output image).
+ */
+ width = drawable->width;
+ height = drawable->height;
+ bpp = drawable->bpp;
+
+ has_sel = !gimp_selection_is_empty (imageID);
+ gimp_drawable_offsets (drawable->drawable_id, &ofsx, &ofsy);
+
+ /* initialize the pixel region */
+ gimp_pixel_rgn_init (&srcPR, drawable, 0, 0, width, height, FALSE, FALSE);
+
+ cmap = gimp_image_get_colormap (imageID, &numcol);
+ gray = (gimp_drawable_is_gray (drawable->drawable_id) ||
+ gimp_item_is_channel (drawable->drawable_id));
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ selDrawable = gimp_drawable_get (gimp_image_get_selection (imageID));
+ gimp_pixel_rgn_init (&selPR,
+ selDrawable,
+ 0, 0, width, height, FALSE, FALSE);
+
+ /* allocate row buffer */
+ src_row = g_new (guchar, (x2 - x1) * bpp);
+ sel = g_new (guchar, x2 - x1);
+
+ for (y = y1; y < y2; y++)
+ {
+ gimp_pixel_rgn_get_row (&srcPR, src_row, x1, y, (x2 - x1));
+ if (has_sel)
+ gimp_pixel_rgn_get_row (&selPR, sel, x1 + ofsx, y + ofsy, (x2 - x1));
+
+ for (x = 0; x < w; x++)
+ {
+ /* Start with full opacity. */
+ a = 255;
+
+ /*
+ * If the image is indexed, fetch RGB values
+ * from colormap.
+ */
+ if (cmap)
+ {
+ idx = src_row[x * bpp];
+
+ r = cmap[idx * 3];
+ g = cmap[idx * 3 + 1];
+ b = cmap[idx * 3 + 2];
+ if (has_alpha)
+ a = src_row[x * bpp + 1];
+ }
+ else if (gray)
+ {
+ r = g = b = src_row[x * bpp];
+ if (has_alpha)
+ a = src_row[x * bpp + 1];
+ }
+ else
+ {
+ r = src_row[x * bpp];
+ g = src_row[x * bpp + 1];
+ b = src_row[x * bpp + 2];
+ if (has_alpha)
+ a = src_row[x * bpp + 3];
+ }
+
+ if (has_sel)
+ a *= sel[x];
+ else
+ a *= 255;
+
+ if (a != 0)
+ insertcolor (r, g, b, (gdouble) a * (1.0 / (255.0 * 255.0)));
+ }
+
+ /* tell the user what we're doing */
+ if ((y % 10) == 0)
+ gimp_progress_update ((gdouble) y / (gdouble) (y2 - y1));
+ }
+
+ gimp_progress_update (1.0);
+
+ /* clean up */
+ gimp_drawable_detach (selDrawable);
+ g_free (src_row);
+ g_free (sel);
+}
+
+static void
+insertcolor (guchar r,
+ guchar g,
+ guchar b,
+ gdouble a)
+{
+ static GHashTable *hash_table;
+ guint key;
+
+ if (!hash_table)
+ hash_table = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ histogram (r, g, b, a);
+
+ key = r + 256 * (g + 256 * b);
+ if (g_hash_table_lookup (hash_table, GINT_TO_POINTER (key)))
+ {
+ return;
+ }
+
+ g_hash_table_insert (hash_table, GINT_TO_POINTER (key),
+ GINT_TO_POINTER (1));
+
+ uniques++;
+}
+
+/*
+ * Update RGB count, and keep track of maximum values (which aren't used
+ * anywhere as of yet, but they might be useful sometime).
+ */
+static void
+histogram (guchar r,
+ guchar g,
+ guchar b,
+ gdouble a)
+{
+ hist_red[r] += a;
+ hist_green[g] += a;
+ hist_blue[b] += a;
+
+ if (hist_red[r] > maxred)
+ maxred = hist_red[r];
+
+ if (hist_green[g] > maxgreen)
+ maxgreen = hist_green[g];
+
+ if (hist_blue[b] > maxblue)
+ maxblue = hist_blue[b];
+}
+
+/* show our results */
+static void
+doDialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *preview;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Colorcube Analysis"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+
+ /* use preview for histogram window */
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview, PREWIDTH, PREHEIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+
+ /* output results */
+ doLabel (vbox, _("Image dimensions: %d × %d"), width, height);
+
+ if (uniques == 0)
+ doLabel (vbox, _("No colors"));
+ else if (uniques == 1)
+ doLabel (vbox, _("Only one unique color"));
+ else
+ doLabel (vbox, _("Number of unique colors: %d"), uniques);
+
+ /* show stuff */
+ gtk_widget_show_all (dialog);
+
+ fillPreview (preview);
+
+ gimp_dialog_run (GIMP_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+}
+
+/* shortcut */
+static void
+doLabel (GtkWidget *vbox,
+ const gchar *format,
+ ...)
+{
+ GtkWidget *label;
+ gchar *text;
+ va_list args;
+
+ va_start (args, format);
+ text = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ label = gtk_label_new (text);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_free (text);
+}
+
+/* fill our preview image with the color-histogram */
+static void
+fillPreview (GtkWidget *preview)
+{
+ guchar *image, *column, *pixel;
+ gint x, y, rowstride;
+ gdouble histcount, val;
+
+ rowstride = PREWIDTH * 3;
+
+ image = g_new0 (guchar, PREWIDTH * rowstride);
+
+ for (x = 0, column = image; x < PREWIDTH; x++, column += 3)
+ {
+ /*
+ * For every channel, calculate a logarithmic value, scale it,
+ * and build a one-pixel bar.
+ * ... in the respective channel, preserving the other ones. --hb
+ */
+ histcount = hist_red[x] > 1.0 ? hist_red[x] : 1.0;
+
+ val = log (histcount) * (PREHEIGHT / 12);
+
+ if (val > PREHEIGHT)
+ val = PREHEIGHT;
+
+ y = PREHEIGHT - 1;
+ pixel = column + (y * rowstride);
+ for (; y > (PREHEIGHT - val); y--)
+ {
+ pixel[0] = 255;
+ pixel -= rowstride;
+ }
+
+ histcount = hist_green[x] > 1.0 ? hist_green[x] : 1.0;
+
+ val = log (histcount) * (PREHEIGHT / 12);
+
+ if (val > PREHEIGHT)
+ val = PREHEIGHT;
+
+ y = PREHEIGHT - 1;
+ pixel = column + (y * rowstride);
+ for (; y > (PREHEIGHT - val); y--)
+ {
+ pixel[1] = 255;
+ pixel -= rowstride;
+ }
+
+ histcount = hist_blue[x] > 1.0 ? hist_blue[x] : 1.0;
+
+ val = log (histcount) * (PREHEIGHT / 12);
+
+ if (val > PREHEIGHT)
+ val = PREHEIGHT;
+
+ y = PREHEIGHT - 1;
+ pixel = column + (y * rowstride);
+ for (; y > (PREHEIGHT - val); y--)
+ {
+ pixel[2] = 255;
+ pixel -= rowstride;
+ }
+ }
+
+ /* move our data into the preview image */
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREWIDTH, PREHEIGHT,
+ GIMP_RGB_IMAGE,
+ image,
+ 3 * PREWIDTH);
+
+ g_free (image);
+}
diff --git a/plug-ins/common/color-enhance.c b/plug-ins/common/color-enhance.c
new file mode 100644
index 0000000..66fc29f
--- /dev/null
+++ b/plug-ins/common/color-enhance.c
@@ -0,0 +1,289 @@
+/* Color Enhance 0.10 --- image filter plug-in for GIMP
+ *
+ * Copyright (C) 1999 Martin Weber
+ * Copyright (C) 1996 Federico Mena Quintero
+ *
+ * You can contact me at martweb@gmx.net
+ * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-color-enhance"
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void Color_Enhance (GimpDrawable *drawable);
+static void indexed_Color_Enhance (gint32 image_ID);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Stretch color saturation to cover maximum possible range"),
+ "This simple plug-in does an automatic saturation "
+ "stretch. For each channel in the image, it finds "
+ "the minimum and maximum values... it uses those "
+ "values to stretch the individual histograms to the "
+ "full range. For some images it may do just what "
+ "you want; for others it may not work that well. "
+ "This version differs from Contrast Autostretch in "
+ "that it works in HSV space, and preserves hue.",
+ "Martin Weber",
+ "Martin Weber",
+ "1997",
+ N_("_Color Enhance (legacy)"),
+ "RGB*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Auto");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpDrawable *drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 image_ID;
+
+ INIT_I18N();
+
+ run_mode = param[0].data.d_int32;
+
+ /* Get the specified drawable */
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+ image_ID = param[1].data.d_image;
+
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init (_("Color Enhance"));
+ gimp_tile_cache_ntiles (2 * (drawable->width / gimp_tile_width () + 1));
+ Color_Enhance (drawable);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else if (gimp_drawable_is_indexed (drawable->drawable_id))
+ {
+ indexed_Color_Enhance (image_ID);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ gimp_drawable_detach (drawable);
+}
+
+static gdouble
+get_v (const guchar *src)
+{
+ gdouble h, z, v;
+ gint c, m, y;
+ gint k;
+ guchar map[3];
+
+ c = 255 - src[0];
+ m = 255 - src[1];
+ y = 255 - src[2];
+
+ k = c;
+ if (m < k) k = m;
+ if (y < k) k = y;
+
+ map[0] = c - k;
+ map[1] = m - k;
+ map[2] = y - k;
+
+ gimp_rgb_to_hsv4(map, &h, &z, &v);
+
+ return v;
+}
+
+static void
+enhance_it (const guchar *src, guchar *dest, gdouble vlo, gdouble vhi)
+{
+ gdouble h, z, v;
+ gint c, m, y;
+ gint k;
+ guchar map[3];
+
+ c = 255 - src[0];
+ m = 255 - src[1];
+ y = 255 - src[2];
+
+ k = c;
+ if (m < k) k = m;
+ if (y < k) k = y;
+
+ map[0] = c - k;
+ map[1] = m - k;
+ map[2] = y - k;
+
+ gimp_rgb_to_hsv4 (map, &h, &z, &v);
+
+ if (vhi != vlo)
+ v = (v - vlo) / (vhi - vlo);
+
+ gimp_hsv_to_rgb4 (map, h, z, v);
+
+ c = map[0];
+ m = map[1];
+ y = map[2];
+
+ c += k;
+ if (c > 255) c = 255;
+ m += k;
+ if (m > 255) m = 255;
+ y += k;
+ if (y > 255) y = 255;
+
+ dest[0] = 255 - c;
+ dest[1] = 255 - m;
+ dest[2] = 255 - y;
+}
+
+static void
+indexed_Color_Enhance (gint32 image_ID)
+{
+ guchar *cmap;
+ gint ncols,i;
+ gdouble vhi = 0.0, vlo = 1.0;
+
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ if (!cmap)
+ {
+ g_message ("colormap was NULL! Quitting.");
+ gimp_quit();
+ }
+
+ for (i = 0; i < ncols; i++)
+ {
+ gdouble v = get_v (&cmap[3 * i]);
+
+ if (v > vhi) vhi = v;
+ if (v < vlo) vlo = v;
+ }
+
+ for (i = 0; i < ncols; i++)
+ {
+ enhance_it (&cmap[3 * i], &cmap[3 * i], vlo, vhi);
+ }
+
+ gimp_image_set_colormap (image_ID, cmap, ncols);
+}
+
+typedef struct
+{
+ gdouble vhi;
+ gdouble vlo;
+ gboolean has_alpha;
+} ColorEnhanceParam_t;
+
+static void
+find_vhi_vlo (const guchar *src,
+ gint bpp,
+ gpointer data)
+{
+ ColorEnhanceParam_t *param = (ColorEnhanceParam_t*) data;
+
+ if (!param->has_alpha || src[3])
+ {
+ gdouble v = get_v (src);
+
+ if (v > param->vhi) param->vhi = v;
+ if (v < param->vlo) param->vlo = v;
+ }
+}
+
+static void
+color_enhance_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ ColorEnhanceParam_t *param = (ColorEnhanceParam_t*) data;
+
+ enhance_it (src, dest, param->vlo, param->vhi);
+
+ if (param->has_alpha)
+ dest[3] = src[3];
+}
+
+static void
+Color_Enhance (GimpDrawable *drawable)
+{
+ ColorEnhanceParam_t param;
+
+ param.has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+ param.vhi = 0.0;
+ param.vlo = 1.0;
+
+ gimp_rgn_iterate1 (drawable, 0 /* unused */, find_vhi_vlo, &param);
+ gimp_rgn_iterate2 (drawable, 0 /* unused */, color_enhance_func, &param);
+}
diff --git a/plug-ins/common/colorify.c b/plug-ins/common/colorify.c
new file mode 100644
index 0000000..5bd0a88
--- /dev/null
+++ b/plug-ins/common/colorify.c
@@ -0,0 +1,398 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Colorify. Changes the pixel's luminosity to a specified color
+ * Copyright (C) 1997 Francisco Bustamante
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-colorify"
+#define PLUG_IN_BINARY "colorify"
+#define PLUG_IN_ROLE "gimp-colorify"
+#define PLUG_IN_VERSION "1.1"
+
+#define COLOR_SIZE 30
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void colorify (GimpDrawable *drawable,
+ GimpPreview *preview);
+static gboolean colorify_dialog (GimpDrawable *drawable);
+static void predefined_color_callback (GtkWidget *widget,
+ gpointer data);
+
+typedef struct
+{
+ GimpRGB color;
+} ColorifyVals;
+
+static ColorifyVals cvals =
+{
+ { 1.0, 1.0, 1.0, 1.0 }
+};
+
+static GimpRGB button_color[] =
+{
+ { 1.0, 0.0, 0.0, 1.0 },
+ { 1.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 0.0, 1.0 },
+ { 0.0, 1.0, 1.0, 1.0 },
+ { 0.0, 0.0, 1.0, 1.0 },
+ { 1.0, 0.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 },
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run,
+};
+
+static GtkWidget *custom_color_button = NULL;
+
+static gint lum_red_lookup[256];
+static gint lum_green_lookup[256];
+static gint lum_blue_lookup[256];
+static gint final_red_lookup[256];
+static gint final_green_lookup[256];
+static gint final_blue_lookup[256];
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_COLOR, "color", "Color to apply" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Replace all colors with shades of a specified color"),
+ "Makes an average of the RGB channels and uses it "
+ "to set the color",
+ "Francisco Bustamante",
+ "Francisco Bustamante",
+ PLUG_IN_VERSION,
+ N_("Colorif_y..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpPDBStatusType status;
+ static GimpParam values[1];
+ GimpDrawable *drawable;
+ GimpRunMode run_mode;
+
+ INIT_I18N ();
+
+ status = GIMP_PDB_SUCCESS;
+ run_mode = param[0].data.d_int32;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+ if (!colorify_dialog (drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 4)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ cvals.color = param[3].data.d_color;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &cvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Colorifying"));
+
+ colorify (drawable, NULL);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &cvals, sizeof (ColorifyVals));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+
+ gimp_drawable_detach (drawable);
+
+ values[0].data.d_status = status;
+}
+
+static void
+colorify_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ gint lum;
+
+ lum = lum_red_lookup[src[0]] +
+ lum_green_lookup[src[1]] +
+ lum_blue_lookup[src[2]];
+
+ dest[0] = final_red_lookup[lum];
+ dest[1] = final_green_lookup[lum];
+ dest[2] = final_blue_lookup[lum];
+
+ if (bpp == 4)
+ dest[3] = src[3];
+}
+
+static void
+colorify (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ gint i;
+
+ for (i = 0; i < 256; i ++)
+ {
+ lum_red_lookup[i] = i * GIMP_RGB_LUMINANCE_RED;
+ lum_green_lookup[i] = i * GIMP_RGB_LUMINANCE_GREEN;
+ lum_blue_lookup[i] = i * GIMP_RGB_LUMINANCE_BLUE;
+ final_red_lookup[i] = i * cvals.color.r;
+ final_green_lookup[i] = i * cvals.color.g;
+ final_blue_lookup[i] = i * cvals.color.b;
+ }
+
+ if (preview)
+ {
+ gint width, height, bytes;
+ guchar *src;
+
+ src = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
+ &width, &height, &bytes);
+ for (i = 0; i < width * height; i++)
+ colorify_func (src + i * bytes, src + i * bytes, bytes, NULL);
+
+ gimp_preview_draw_buffer (preview, src, width * bytes);
+ g_free (src);
+ }
+ else
+ {
+ GimpPixelRgn srcPR, destPR;
+ gint x1, y1, x2, y2;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ return;
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR.data;
+ guchar *dest = destPR.data;
+ gint row;
+
+ for (row = 0; row < srcPR.h; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint pixels = srcPR.w;
+
+ while (pixels--)
+ {
+ colorify_func (s, d, srcPR.bpp, NULL);
+
+ s += srcPR.bpp;
+ d += destPR.bpp;
+ }
+
+ src += srcPR.rowstride;
+ dest += destPR.rowstride;
+ }
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ /* update the processed region */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+ }
+}
+
+static gboolean
+colorify_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *label;
+ GtkWidget *button;
+ GtkWidget *table;
+ GtkWidget *color_area;
+ gint i;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Colorify"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_zoom_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (colorify),
+ drawable);
+
+ table = gtk_table_new (2, 7, TRUE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label = gtk_label_new (_("Custom color:"));
+ gtk_table_attach (GTK_TABLE (table), label, 4, 6, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ custom_color_button = gimp_color_button_new (_("Colorify Custom Color"),
+ COLOR_SIZE, COLOR_SIZE,
+ &cvals.color,
+ GIMP_COLOR_AREA_FLAT);
+ g_signal_connect (custom_color_button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &cvals.color);
+ g_signal_connect_swapped (custom_color_button, "color-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_table_attach (GTK_TABLE (table), custom_color_button, 6, 7, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (custom_color_button);
+
+ for (i = 0; i < 7; i++)
+ {
+ button = gtk_button_new ();
+ color_area = gimp_color_area_new (&button_color[i],
+ GIMP_COLOR_AREA_FLAT,
+ GDK_BUTTON2_MASK);
+ gtk_widget_set_size_request (GTK_WIDGET (color_area),
+ COLOR_SIZE, COLOR_SIZE);
+ gtk_container_add (GTK_CONTAINER (button), color_area);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (predefined_color_callback),
+ &button_color[i]);
+ gtk_widget_show (color_area);
+
+ gtk_table_attach (GTK_TABLE (table), button, i, i + 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+ }
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+predefined_color_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (custom_color_button),
+ (GimpRGB *) data);
+}
diff --git a/plug-ins/common/colormap-remap.c b/plug-ins/common/colormap-remap.c
new file mode 100644
index 0000000..6980ec8
--- /dev/null
+++ b/plug-ins/common/colormap-remap.c
@@ -0,0 +1,752 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Colormap remapping plug-in
+ * Copyright (C) 2006 Mukund Sivaraman <muks@mukund.org>
+ *
+ * This plug-in takes the colormap and lets you move colors from one index
+ * to another while keeping the original image visually unmodified.
+ *
+ * Such functionality is useful for creating graphics files for applications
+ * which expect certain indices to contain some specific colors.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC_REMAP "plug-in-colormap-remap"
+#define PLUG_IN_PROC_SWAP "plug-in-colormap-swap"
+#define PLUG_IN_BINARY "colormap-remap"
+#define PLUG_IN_ROLE "gimp-colormap-remap"
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean remap (gint32 image_ID,
+ gint num_colors,
+ guchar *map);
+
+static gboolean remap_dialog (gint32 image_ID,
+ guchar *map);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef remap_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "num-colors", "Length of 'map' argument "
+ "(should be equal to colormap size)" },
+ { GIMP_PDB_INT8ARRAY, "map", "Remap array for the colormap" }
+ };
+
+ static const GimpParamDef swap_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT8, "index1", "First index in the colormap" },
+ { GIMP_PDB_INT8, "index2", "Second (other) index in the colormap" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC_REMAP,
+ N_("Rearrange the colormap"),
+ "This procedure takes an indexed image and lets you "
+ "alter the positions of colors in the colormap "
+ "without visually changing the image.",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "June 2006",
+ N_("R_earrange Colormap..."),
+ "INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (remap_args), 0,
+ remap_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC_REMAP, "<Image>/Colors/Map/Colormap");
+ gimp_plugin_menu_register (PLUG_IN_PROC_REMAP, "<Colormap>");
+ gimp_plugin_icon_register (PLUG_IN_PROC_REMAP, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_COLORMAP);
+
+ gimp_install_procedure (PLUG_IN_PROC_SWAP,
+ N_("Swap two colors in the colormap"),
+ "This procedure takes an indexed image and lets you "
+ "swap the positions of two colors in the colormap "
+ "without visually changing the image.",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "June 2006",
+ N_("_Swap Colors"),
+ "INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (swap_args), 0,
+ swap_args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 image_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ guchar map[256];
+ gint i;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ image_ID = param[1].data.d_image;
+
+ for (i = 0; i < 256; i++)
+ map[i] = i;
+
+ if (strcmp (name, PLUG_IN_PROC_REMAP) == 0)
+ {
+ /* Make sure that the image is indexed */
+ if (gimp_image_base_type (image_ID) != GIMP_INDEXED)
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint n_cols;
+
+ g_free (gimp_image_get_colormap (image_ID, &n_cols));
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (! remap_dialog (image_ID, map))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (n_cols != param[3].data.d_int32)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ for (i = 0; i < n_cols; i++)
+ map[i] = param[4].data.d_int8array[i];
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC_REMAP, map);
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! remap (image_ID, n_cols, map))
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC_REMAP, map, sizeof (map));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ }
+ }
+ }
+ else if (strcmp (name, PLUG_IN_PROC_SWAP) == 0)
+ {
+ /* Make sure that the image is indexed */
+ if (gimp_image_base_type (image_ID) != GIMP_INDEXED)
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (run_mode == GIMP_RUN_NONINTERACTIVE && nparams == 5)
+ {
+ guchar index1 = param[3].data.d_int8;
+ guchar index2 = param[4].data.d_int8;
+ gint n_cols;
+
+ g_free (gimp_image_get_colormap (image_ID, &n_cols));
+
+ if (index1 >= n_cols || index2 >= n_cols)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ guchar tmp;
+
+ tmp = map[index1];
+ map[index1] = map[index2];
+ map[index2] = tmp;
+
+ if (! remap (image_ID, n_cols, map))
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gboolean
+remap (gint32 image_ID,
+ gint num_colors,
+ guchar *map)
+{
+ guchar *cmap;
+ guchar *new_cmap;
+ guchar *new_cmap_i;
+ gint ncols;
+ gint num_layers;
+ gint32 *layers;
+ glong pixels = 0;
+ glong processed = 0;
+ guchar pixel_map[256];
+ gboolean valid[256];
+ gint i;
+
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ g_return_val_if_fail (cmap != NULL, FALSE);
+ g_return_val_if_fail (ncols > 0, FALSE);
+
+ if (num_colors != ncols)
+ {
+ g_message (_("Invalid remap array was passed to remap function"));
+ return FALSE;
+ }
+
+ for (i = 0; i < ncols; i++)
+ valid[i] = FALSE;
+
+ for (i = 0; i < ncols; i++)
+ {
+ if (map[i] >= ncols)
+ {
+ g_message (_("Invalid remap array was passed to remap function"));
+ return FALSE;
+ }
+
+ valid[map[i]] = TRUE;
+ pixel_map[map[i]] = i;
+ }
+
+ for (i = 0; i < ncols; i++)
+ if (valid[i] == FALSE)
+ {
+ g_message (_("Invalid remap array was passed to remap function"));
+ return FALSE;
+ }
+
+ new_cmap = g_new (guchar, ncols * 3);
+
+ new_cmap_i = new_cmap;
+
+ for (i = 0; i < ncols; i++)
+ {
+ gint j = map[i] * 3;
+
+ *new_cmap_i++ = cmap[j];
+ *new_cmap_i++ = cmap[j + 1];
+ *new_cmap_i++ = cmap[j + 2];
+ }
+
+ gimp_image_undo_group_start (image_ID);
+
+ gimp_image_set_colormap (image_ID, new_cmap, ncols);
+
+ g_free (cmap);
+ g_free (new_cmap);
+
+ gimp_progress_init (_("Rearranging the colormap"));
+
+ /* There is no needs to process the layers recursively, because
+ * indexed images cannot have layer groups.
+ */
+ layers = gimp_image_get_layers (image_ID, &num_layers);
+
+ for (i = 0; i < num_layers; i++)
+ pixels +=
+ gimp_drawable_width (layers[i]) * gimp_drawable_height (layers[i]);
+
+ for (i = 0; i < num_layers; i++)
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *shadow;
+ const Babl *format;
+ GeglBufferIterator *iter;
+ GeglRectangle *src_roi;
+ GeglRectangle *dest_roi;
+ gint width, height, bpp;
+ gint update = 0;
+
+ buffer = gimp_drawable_get_buffer (layers[i]);
+ shadow = gimp_drawable_get_shadow_buffer (layers[i]);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ format = gegl_buffer_get_format (buffer);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, 0, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+ src_roi = &iter->items[0].roi;
+
+ gegl_buffer_iterator_add (iter, shadow,
+ GEGL_RECTANGLE (0, 0, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+ dest_roi = &iter->items[1].roi;
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src_row = iter->items[0].data;
+ guchar *dest_row = iter->items[1].data;
+ gint y;
+
+ for (y = 0; y < src_roi->height; y++)
+ {
+ const guchar *src = src_row;
+ guchar *dest = dest_row;
+ gint x;
+
+ if (bpp == 1)
+ {
+ for (x = 0; x < src_roi->width; x++)
+ *dest++ = pixel_map[*src++];
+ }
+ else
+ {
+ for (x = 0; x < src_roi->width; x++)
+ {
+ *dest++ = pixel_map[*src++];
+ *dest++ = *src++;
+ }
+ }
+
+ src_row += src_roi->width * bpp;
+ dest_row += dest_roi->width * bpp;
+ }
+
+ processed += src_roi->width * src_roi->height;
+ update %= 16;
+
+ if (update == 0)
+ gimp_progress_update ((gdouble) processed / pixels);
+
+ update++;
+ }
+
+ g_object_unref (buffer);
+ g_object_unref (shadow);
+
+ gimp_drawable_merge_shadow (layers[i], TRUE);
+ gimp_drawable_update (layers[i], 0, 0, width, height);
+ }
+
+ g_free (layers);
+
+ gimp_progress_update (1.0);
+
+ gimp_image_undo_group_end (image_ID);
+
+ return TRUE;
+}
+
+
+/* dialog */
+
+#define RESPONSE_RESET 1
+
+enum
+{
+ COLOR_INDEX,
+ COLOR_INDEX_TEXT,
+ COLOR_RGB,
+ COLOR_H,
+ COLOR_S,
+ COLOR_V,
+ NUM_COLS
+};
+
+static GtkUIManager *remap_ui = NULL;
+static gboolean remap_run = FALSE;
+static gint reverse_order[256];
+
+
+static void
+remap_sort (GtkTreeSortable *store,
+ gint column,
+ GtkSortType order)
+{
+ gtk_tree_sortable_set_sort_column_id (store, column, order);
+ gtk_tree_sortable_set_sort_column_id (store,
+ GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
+}
+
+static void
+remap_sort_callback (GtkAction *action,
+ GtkTreeSortable *store)
+{
+ const gchar *name = gtk_action_get_name (action);
+ gint column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
+
+ g_return_if_fail (g_str_has_prefix (name, "sort-"));
+
+ if (strncmp (name + 5, "hue", 3) == 0)
+ column = COLOR_H;
+ else if (strncmp (name + 5, "sat", 3) == 0)
+ column = COLOR_S;
+ else if (strncmp (name + 5, "val", 3) == 0)
+ column = COLOR_V;
+
+ remap_sort (store, column, GTK_SORT_ASCENDING);
+}
+
+static void
+remap_reset_callback (GtkAction *action,
+ GtkTreeSortable *store)
+{
+ remap_sort (store, COLOR_INDEX, GTK_SORT_ASCENDING);
+}
+
+static void
+remap_reverse_callback (GtkAction *action,
+ GtkListStore *store)
+{
+ gtk_list_store_reorder (store, reverse_order);
+}
+
+static GtkUIManager *
+remap_ui_manager_new (GtkWidget *window,
+ GtkListStore *store)
+{
+ static const GtkActionEntry actions[] =
+ {
+ {
+ "sort-hue", NULL, N_("Sort on Hue"), NULL, NULL,
+ G_CALLBACK (remap_sort_callback)
+ },
+ {
+ "sort-sat", NULL, N_("Sort on Saturation"), NULL, NULL,
+ G_CALLBACK (remap_sort_callback)
+ },
+ {
+ "sort-val", NULL, N_("Sort on Value"), NULL, NULL,
+ G_CALLBACK (remap_sort_callback)
+ },
+ {
+ "reverse", NULL, N_("Reverse Order"), NULL, NULL,
+ G_CALLBACK (remap_reverse_callback)
+ },
+ {
+ "reset", GIMP_ICON_RESET, N_("Reset Order"), NULL, NULL,
+ G_CALLBACK (remap_reset_callback)
+ },
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GtkActionGroup *group = gtk_action_group_new ("Actions");
+ GError *error = NULL;
+
+ gtk_action_group_set_translation_domain (group, NULL);
+ gtk_action_group_add_actions (group, actions, G_N_ELEMENTS (actions), store);
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"remap-popup\">"
+ " <menuitem action=\"sort-hue\" />"
+ " <menuitem action=\"sort-sat\" />"
+ " <menuitem action=\"sort-val\" />"
+ " <separator />"
+ " <menuitem action=\"reverse\" />"
+ " <menuitem action=\"reset\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return ui_manager;
+}
+
+static gboolean
+remap_popup_menu (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkWidget *menu = gtk_ui_manager_get_widget (remap_ui, "/remap-popup");
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ event ? event->time : gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static gboolean
+remap_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ return remap_popup_menu (widget, event);
+
+ return FALSE;
+}
+
+static void
+remap_response (GtkWidget *dialog,
+ gint response_id,
+ GtkTreeSortable *store)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ remap_reset_callback (NULL, store);
+ break;
+
+ case GTK_RESPONSE_OK:
+ remap_run = TRUE;
+ /* fallthrough */
+
+ default:
+ gtk_main_quit ();
+ break;
+ }
+}
+
+static gboolean
+remap_dialog (gint32 image_ID,
+ guchar *map)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *box;
+ GtkWidget *iconview;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeIter iter;
+ guchar *cmap;
+ gint ncols, i;
+ gboolean valid;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Rearrange Colormap"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC_REMAP,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ g_return_val_if_fail ((ncols > 0) && (ncols <= 256), FALSE);
+
+ store = gtk_list_store_new (NUM_COLS,
+ G_TYPE_INT, G_TYPE_STRING, GIMP_TYPE_RGB,
+ G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
+
+ for (i = 0; i < ncols; i++)
+ {
+ GimpRGB rgb;
+ GimpHSV hsv;
+ gint index = map[i];
+ gchar *text = g_strdup_printf ("%d", index);
+
+ gimp_rgb_set_uchar (&rgb,
+ cmap[index * 3],
+ cmap[index * 3 + 1],
+ cmap[index * 3 + 2]);
+ gimp_rgb_to_hsv (&rgb, &hsv);
+
+ reverse_order[i] = ncols - i - 1;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ COLOR_INDEX, index,
+ COLOR_INDEX_TEXT, text,
+ COLOR_RGB, &rgb,
+ COLOR_H, hsv.h,
+ COLOR_S, hsv.s,
+ COLOR_V, hsv.v,
+ -1);
+ g_free (text);
+ }
+
+ g_free (cmap);
+
+ remap_ui = remap_ui_manager_new (dialog, store);
+
+ iconview = gtk_icon_view_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gtk_box_pack_start (GTK_BOX (vbox), iconview, TRUE, TRUE, 0);
+
+ gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (iconview),
+ GTK_SELECTION_SINGLE);
+ gtk_icon_view_set_orientation (GTK_ICON_VIEW (iconview),
+ GTK_ORIENTATION_VERTICAL);
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (iconview), 16);
+ gtk_icon_view_set_row_spacing (GTK_ICON_VIEW (iconview), 0);
+ gtk_icon_view_set_column_spacing (GTK_ICON_VIEW (iconview), 0);
+ gtk_icon_view_set_reorderable (GTK_ICON_VIEW (iconview), TRUE);
+
+ renderer = gimp_cell_renderer_color_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (iconview), renderer, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (iconview), renderer,
+ "color", COLOR_RGB,
+ NULL);
+ g_object_set (renderer,
+ "width", 24,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (iconview), renderer, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (iconview), renderer,
+ "text", COLOR_INDEX_TEXT,
+ NULL);
+ g_object_set (renderer,
+ "size-points", 6.0,
+ "xalign", 0.5,
+ "ypad", 0,
+ NULL);
+
+ g_signal_connect (iconview, "popup-menu",
+ G_CALLBACK (remap_popup_menu),
+ NULL);
+
+ g_signal_connect (iconview, "button-press-event",
+ G_CALLBACK (remap_button_press),
+ NULL);
+
+ box = gimp_hint_box_new (_("Drag and drop colors to rearrange the colormap. "
+ "The numbers shown are the original indices. "
+ "Right-click for a menu with sort options."));
+
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 0);
+ gtk_widget_show (box);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (remap_response),
+ store);
+
+ gtk_widget_show_all (dialog);
+
+ gtk_main ();
+
+ i = 0;
+
+ for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter))
+ {
+ gint index;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+ COLOR_INDEX, &index,
+ -1);
+ map[i++] = index;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return remap_run;
+}
diff --git a/plug-ins/common/compose.c b/plug-ins/common/compose.c
new file mode 100644
index 0000000..2f70579
--- /dev/null
+++ b/plug-ins/common/compose.c
@@ -0,0 +1,1402 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Compose plug-in (C) 1997,1999 Peter Kirchgessner
+ * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This plug-in composes RGB-images from several types of channels
+ */
+
+/* Lab colorspace support originally written by Alexey Dyachenko,
+ * merged into the official plug-in by Sven Neumann.
+ *
+ * Support for channels empty or filled with a single mask value
+ * added by Sylvain FORET.
+ */
+
+/*
+ * All redundant _256 versions of YCbCr* are here only for compatibility .
+ * They can be dropped for GIMP 3.0
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define COMPOSE_PROC "plug-in-compose"
+#define DRAWABLE_COMPOSE_PROC "plug-in-drawable-compose"
+#define RECOMPOSE_PROC "plug-in-recompose"
+#define PLUG_IN_BINARY "compose"
+#define PLUG_IN_ROLE "gimp-compose"
+
+/* Maximum number of images to compose */
+#define MAX_COMPOSE_IMAGES 4
+
+typedef struct
+{
+ union
+ {
+ gint32 ID; /* Image ID of input images or drawable */
+ guchar val; /* Mask value to compose with */
+ } comp;
+ gboolean is_ID;
+} ComposeInput;
+
+/* Description of a component */
+typedef struct
+{
+ const gchar *babl_name;
+ const gchar *name;
+ const gchar *icon;
+ const float range_min; /* val min of the component */
+ const float range_max; /* val max of the component */
+ const gboolean is_perceptual; /* Take the componenent from an Y' or Y buffer */
+} COMPONENT_DSC;
+
+/* Description of a composition */
+typedef struct
+{
+ const gchar *babl_model;
+ const gchar *compose_type; /* Type of composition ("RGB", "RGBA",...) */
+ gint num_images; /* Number of input images needed */
+ /* Channel information */
+ const COMPONENT_DSC components[MAX_COMPOSE_IMAGES];
+ const gchar *filename; /* Name of new image */
+
+} COMPOSE_DSC;
+
+
+/* Declare local functions
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void cpn_affine_transform (GeglBuffer *buffer,
+ gdouble min,
+ gdouble max);
+
+static void fill_buffer_from_components (GeglBuffer *temp[MAX_COMPOSE_IMAGES],
+ GeglBuffer *dst,
+ gint num_cpn,
+ ComposeInput *inputs,
+ gdouble mask_vals[MAX_COMPOSE_IMAGES]);
+
+static void perform_composition (COMPOSE_DSC curr_compose_dsc,
+ GeglBuffer *buffer_src[MAX_COMPOSE_IMAGES],
+ GeglBuffer *buffer_dst,
+ ComposeInput *inputs,
+ gint num_images);
+
+static gint32 compose (const gchar *compose_type,
+ ComposeInput *inputs,
+ gboolean compose_by_drawable);
+
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ GimpPrecision precision,
+ gint32 *layer_ID,
+ GeglBuffer **drawable);
+
+static gboolean compose_dialog (const gchar *compose_type,
+ gint32 drawable_ID);
+
+static gboolean check_gray (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+
+static void combo_callback (GimpIntComboBox *cbox,
+ gpointer data);
+
+static void scale_callback (GtkAdjustment *adj,
+ ComposeInput *input);
+
+static void check_response (GtkWidget *dialog,
+ gint response,
+ gpointer data);
+
+static void type_combo_callback (GimpIntComboBox *combo,
+ gpointer data);
+
+
+
+/* Decompositions availables.
+ * All the following values have to be kept in sync with those of decompose.c
+ */
+
+#define CPN_RGBA_R { "R", N_("_Red:"), GIMP_ICON_CHANNEL_RED, 0.0, 1.0, FALSE}
+#define CPN_RGBA_G { "G", N_("_Green:"), GIMP_ICON_CHANNEL_GREEN, 0.0, 1.0, FALSE}
+#define CPN_RGBA_B { "B", N_("_Blue:"), GIMP_ICON_CHANNEL_BLUE, 0.0, 1.0, FALSE}
+#define CPN_RGBA_A { "A", N_("_Alpha:"), GIMP_ICON_CHANNEL_ALPHA, 0.0, 1.0, TRUE}
+
+#define CPN_HSV_H { "hue", N_("_Hue:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_HSV_S { "saturation", N_("_Saturation:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_HSV_V { "value", N_("_Value:"), NULL, 0.0, 1.0, TRUE}
+
+#define CPN_HSL_H { "hue", N_("_Hue:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_HSL_S { "saturation", N_("_Saturation:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_HSL_L { "lightness", N_("_Lightness:"), NULL, 0.0, 1.0, TRUE}
+
+#define CPN_CMYK_C { "Cyan", N_("_Cyan:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_CMYK_M { "Magenta", N_("_Magenta:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_CMYK_Y { "Yellow", N_("_Yellow:"), NULL, 0.0, 1.0, TRUE}
+#define CPN_CMYK_K { "Key", N_("_Black:"), NULL, 0.0, 1.0, TRUE}
+
+#define CPN_LAB_L { "CIE L", N_("_L:"), NULL, 0.0, 100.0, TRUE}
+#define CPN_LAB_A { "CIE a", N_("_A:"), NULL, -127.5, 127.5, TRUE}
+#define CPN_LAB_B { "CIE b", N_("_B:"), NULL, -127.5, 127.5, TRUE}
+
+#define CPN_LCH_L { "CIE L", N_("_L"), NULL, 0.0, 100.0, TRUE}
+#define CPN_LCH_C { "CIE C(ab)", N_("_C"), NULL, 0.0, 200.0, TRUE}
+#define CPN_LCH_H { "CIE H(ab)", N_("_H"), NULL, 0.0, 360.0, TRUE}
+
+#define CPN_YCBCR_Y { "Y'", N_("_Luma y470:"), NULL, 0.0, 1.0, TRUE }
+#define CPN_YCBCR_CB { "Cb", N_("_Blueness cb470:"), NULL, -0.5, 0.5, TRUE }
+#define CPN_YCBCR_CR { "Cr", N_("_Redness cr470:"), NULL, -0.5, 0.5, TRUE }
+
+#define CPN_YCBCR709_Y { "Y'", N_("_Luma y709:"), NULL, 0.0, 1.0, TRUE }
+#define CPN_YCBCR709_CB { "Cb", N_("_Blueness cb709:"), NULL, -0.5, 0.5, TRUE }
+#define CPN_YCBCR709_CR { "Cr", N_("_Redness cr709:"), NULL, -0.5, 0.5, TRUE }
+
+
+static COMPOSE_DSC compose_dsc[] =
+{
+ { "RGB",
+ N_("RGB"), 3,
+ { CPN_RGBA_R,
+ CPN_RGBA_G,
+ CPN_RGBA_B },
+ "rgb-compose" },
+
+ { "RGBA",
+ N_("RGBA"), 4,
+ { CPN_RGBA_R,
+ CPN_RGBA_G,
+ CPN_RGBA_B,
+ CPN_RGBA_A },
+ "rgba-compose" },
+
+ { "HSV",
+ N_("HSV"), 3,
+ { CPN_HSV_H,
+ CPN_HSV_S,
+ CPN_HSV_V },
+ "hsv-compose" },
+
+ { "HSL",
+ N_("HSL"), 3,
+ { CPN_HSL_H,
+ CPN_HSL_S,
+ CPN_HSL_L },
+ "hsl-compose" },
+
+ { "CMYK",
+ N_("CMYK"), 4,
+ { CPN_CMYK_C,
+ CPN_CMYK_M,
+ CPN_CMYK_Y,
+ CPN_CMYK_K },
+ "cmyk-compose" },
+
+ { "CIE Lab",
+ N_("LAB"), 3,
+ { CPN_LAB_L,
+ CPN_LAB_A,
+ CPN_LAB_B },
+ "lab-compose" },
+
+ { "CIE LCH(ab)",
+ N_("LCH"), 3,
+ { CPN_LCH_L,
+ CPN_LCH_C,
+ CPN_LCH_H },
+ "lch-compose" },
+
+ { "Y'CbCr",
+ N_("YCbCr_ITU_R470"), 3,
+ { CPN_YCBCR_Y,
+ CPN_YCBCR_CB,
+ CPN_YCBCR_CR },
+ "ycbcr470-compose" },
+
+ { "Y'CbCr709",
+ N_("YCbCr_ITU_R709"), 3,
+ { CPN_YCBCR709_Y,
+ CPN_YCBCR709_CB,
+ CPN_YCBCR709_CR },
+ "ycbcr709-compose" },
+
+ { "Y'CbCr",
+ N_("YCbCr_ITU_R470_256"), 3,
+ { CPN_YCBCR_Y,
+ CPN_YCBCR_CB,
+ CPN_YCBCR_CR },
+ "ycbcr470F-compose" },
+
+ { "Y'CbCr709",
+ N_("YCbCr_ITU_R709_256"), 3,
+ { CPN_YCBCR709_Y,
+ CPN_YCBCR709_CB,
+ CPN_YCBCR709_CR },
+ "ycbcr709F-compose" }
+};
+
+
+typedef struct
+{
+ ComposeInput inputs[MAX_COMPOSE_IMAGES]; /* Image IDs or mask value of input */
+ gchar compose_type[32]; /* type of composition */
+ gboolean do_recompose;
+ gint32 source_layer_ID; /* for recomposing */
+} ComposeVals;
+
+/* Dialog structure */
+typedef struct
+{
+ gint width, height; /* Size of selected image */
+
+ GtkWidget *channel_label[MAX_COMPOSE_IMAGES]; /* The labels to change */
+ GtkWidget *channel_icon[MAX_COMPOSE_IMAGES]; /* The icons */
+ GtkWidget *channel_menu[MAX_COMPOSE_IMAGES]; /* The menus */
+ GtkWidget *color_scales[MAX_COMPOSE_IMAGES]; /* The values color scales */
+ GtkWidget *color_spins[MAX_COMPOSE_IMAGES]; /* The values spin buttons */
+
+ ComposeInput selected[MAX_COMPOSE_IMAGES]; /* Image Ids or mask values from menus */
+
+ gint compose_idx; /* Compose type */
+} ComposeInterface;
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static ComposeVals composevals =
+{
+ {{{ 0 }}}, /* Image IDs of images to compose or mask values */
+ "rgb", /* Type of composition */
+ FALSE, /* Do recompose */
+ -1 /* source layer ID */
+};
+
+static ComposeInterface composeint =
+{
+ 0, 0, /* width, height */
+ { NULL }, /* Label Widgets */
+ { NULL }, /* Icon Widgets */
+ { NULL }, /* Menu Widgets */
+ { NULL }, /* Color Scale Widgets */
+ { NULL }, /* Color Spin Widgets */
+ {{{ 0 }}}, /* Image Ids or mask values from menus */
+ 0 /* Compose type */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image1", "First input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (not used)" },
+ { GIMP_PDB_IMAGE, "image2", "Second input image" },
+ { GIMP_PDB_IMAGE, "image3", "Third input image" },
+ { GIMP_PDB_IMAGE, "image4", "Fourth input image" },
+ { GIMP_PDB_STRING, "compose-type", NULL }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new_image", "Output image" }
+ };
+
+ static GimpParamDef drw_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image1", "First input image (not used)" },
+ { GIMP_PDB_DRAWABLE, "drawable1", "First input drawable" },
+ { GIMP_PDB_DRAWABLE, "drawable2", "Second input drawable" },
+ { GIMP_PDB_DRAWABLE, "drawable3", "Third input drawable" },
+ { GIMP_PDB_DRAWABLE, "drawable4", "Fourth input drawable" },
+ { GIMP_PDB_STRING, "compose-type", NULL }
+ };
+
+ static const GimpParamDef drw_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new_image", "Output image" }
+ };
+
+ static const GimpParamDef recompose_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Image to recompose from" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Not used" },
+ };
+
+ GString *type_desc;
+ gint i;
+
+ type_desc = g_string_new ("What to compose: ");
+ g_string_append_c (type_desc, '"');
+ g_string_append (type_desc, compose_dsc[0].compose_type);
+ g_string_append_c (type_desc, '"');
+
+ for (i = 1; i < G_N_ELEMENTS (compose_dsc); i++)
+ {
+ g_string_append (type_desc, ", ");
+ g_string_append_c (type_desc, '"');
+ g_string_append (type_desc, compose_dsc[i].compose_type);
+ g_string_append_c (type_desc, '"');
+ }
+
+ args[6].description = type_desc->str;
+ drw_args[6].description = type_desc->str;
+
+ gimp_install_procedure (COMPOSE_PROC,
+ N_("Create an image using multiple gray images as color channels"),
+ "This function creates a new image from "
+ "multiple gray images",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1997",
+ N_("C_ompose..."),
+ "GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (COMPOSE_PROC, "<Image>/Colors/Components");
+
+ gimp_install_procedure (DRAWABLE_COMPOSE_PROC,
+ "Compose an image from multiple drawables of gray images",
+ "This function creates a new image from "
+ "multiple drawables of gray images",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1998",
+ NULL, /* It is not available in interactive mode */
+ "GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (drw_args),
+ G_N_ELEMENTS (drw_return_vals),
+ drw_args, drw_return_vals);
+
+ gimp_install_procedure (RECOMPOSE_PROC,
+ N_("Recompose an image that was previously decomposed"),
+ "This function recombines the grayscale layers produced "
+ "by Decompose into a single RGB or RGBA layer, and "
+ "replaces the originally decomposed layer with the "
+ "result.",
+ "Bill Skaggs",
+ "Bill Skaggs",
+ "2004",
+ N_("R_ecompose"),
+ "GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (recompose_args), 0,
+ recompose_args, NULL);
+
+ gimp_plugin_menu_register (RECOMPOSE_PROC, "<Image>/Colors/Components");
+
+ g_string_free (type_desc, TRUE);
+}
+
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 image_ID;
+ gint32 drawable_ID = -1;
+ gint compose_by_drawable;
+ gint i;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ compose_by_drawable = (strcmp (name, DRAWABLE_COMPOSE_PROC) == 0);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_int32 = -1;
+
+ if (strcmp (name, RECOMPOSE_PROC) == 0)
+ {
+ GimpParasite *parasite = gimp_image_get_parasite (param[1].data.d_image,
+ "decompose-data");
+
+ if (! parasite)
+ {
+ g_message (_("You can only run 'Recompose' if the active image "
+ "was originally produced by 'Decompose'."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ gint nret;
+
+ nret = sscanf (gimp_parasite_data (parasite),
+ "source=%d type=%31s %d %d %d %d",
+ &composevals.source_layer_ID,
+ composevals.compose_type,
+ &composevals.inputs[0].comp.ID,
+ &composevals.inputs[1].comp.ID,
+ &composevals.inputs[2].comp.ID,
+ &composevals.inputs[3].comp.ID);
+
+ gimp_parasite_free (parasite);
+
+ for (i = 0; i < MAX_COMPOSE_IMAGES; i++)
+ composevals.inputs[i].is_ID = TRUE;
+
+ if (nret < 5)
+ {
+ g_message (_("Error scanning 'decompose-data' parasite: "
+ "too few layers found"));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ composevals.do_recompose = TRUE;
+ compose_by_drawable = TRUE;
+ }
+ }
+ }
+ else
+ {
+ composevals.do_recompose = FALSE;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &composevals);
+
+ compose_by_drawable = TRUE;
+
+ /* The dialog is now drawable based. Get a drawable-ID of the image */
+ if (strcmp (name, COMPOSE_PROC) == 0)
+ {
+ gint32 *layer_list;
+ gint nlayers;
+
+ layer_list = gimp_image_get_layers (param[1].data.d_int32,
+ &nlayers);
+ if ((layer_list == NULL) || (nlayers <= 0))
+ {
+ g_message (_("Could not get layers for image %d"),
+ (gint) param[1].data.d_int32);
+ return;
+ }
+
+ drawable_ID = layer_list[0];
+ g_free (layer_list);
+ }
+ else
+ {
+ drawable_ID = param[2].data.d_int32;
+ }
+
+ /* First acquire information with a dialog */
+ if (! compose_dialog (composevals.compose_type, drawable_ID))
+ return;
+
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams < 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ composevals.inputs[0].comp.ID = (compose_by_drawable ?
+ param[2].data.d_int32 :
+ param[1].data.d_int32);
+ composevals.inputs[1].comp.ID = param[3].data.d_int32;
+ composevals.inputs[2].comp.ID = param[4].data.d_int32;
+ composevals.inputs[3].comp.ID = param[5].data.d_int32;
+
+ strncpy (composevals.compose_type, param[6].data.d_string,
+ sizeof (composevals.compose_type));
+ composevals.compose_type[sizeof (composevals.compose_type)-1] = '\0';
+
+ for (i = 0; i < MAX_COMPOSE_IMAGES; i++)
+ {
+ if (composevals.inputs[i].comp.ID == -1)
+ {
+ composevals.inputs[i].is_ID = FALSE;
+ composevals.inputs[i].comp.val = 0;
+ }
+ else
+ {
+ composevals.inputs[i].is_ID = TRUE;
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &composevals);
+
+ compose_by_drawable = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Composing"));
+
+ image_ID = compose (composevals.compose_type,
+ composevals.inputs,
+ compose_by_drawable);
+
+ if (image_ID < 0)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ values[1].data.d_int32 = image_ID;
+
+ if (composevals.do_recompose)
+ {
+ gimp_displays_flush ();
+ }
+ else
+ {
+ gimp_image_undo_enable (image_ID);
+ gimp_image_clean_all (image_ID);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_display_new (image_ID);
+ }
+ }
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (name, &composevals, sizeof (ComposeVals));
+ }
+
+ *nreturn_vals = composevals.do_recompose ? 1 : 2;
+
+ values[0].data.d_status = status;
+}
+
+static void
+cpn_affine_transform (GeglBuffer *buffer,
+ gdouble min,
+ gdouble max)
+{
+ GeglBufferIterator *gi;
+ const gdouble scale = max - min;
+ const gdouble offset = min;
+
+ /* We want to scale values linearly, regardless of the format of the buffer */
+ gegl_buffer_set_format (buffer, babl_format ("Y double"));
+
+ gi = gegl_buffer_iterator_new (buffer, NULL, 0, NULL,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ gdouble *data = gi->items[0].data;
+ guint k;
+
+ for (k = 0; k < gi->length; k++)
+ {
+ data[k] = data[k] * scale + offset;
+ }
+ }
+}
+
+static void
+fill_buffer_from_components (GeglBuffer *temp[MAX_COMPOSE_IMAGES],
+ GeglBuffer *dst,
+ gint num_cpn,
+ ComposeInput *inputs,
+ gdouble mask_vals[MAX_COMPOSE_IMAGES])
+{
+ GeglBufferIterator *gi;
+ gint j;
+
+ gi = gegl_buffer_iterator_new (dst, NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 10);
+
+ for (j = 0; j < num_cpn; j++)
+ {
+ if (inputs[j].is_ID)
+ gegl_buffer_iterator_add (gi, temp[j], NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ }
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ gdouble *src_data[MAX_COMPOSE_IMAGES];
+ gdouble *dst_data = (gdouble*) gi->items[0].data;
+ gulong k, count;
+
+ count = 1;
+ for (j = 0; j < num_cpn; j++)
+ if (inputs[j].is_ID)
+ src_data[j] = (gdouble*) gi->items[count++].data;
+
+ for (k = 0; k < gi->length; k++)
+ {
+ gulong pos = k * num_cpn;
+
+ for (j = 0; j < num_cpn; j++)
+ {
+ if (inputs[j].is_ID)
+ dst_data[pos+j] = src_data[j][k];
+ else
+ dst_data[pos+j] = mask_vals[j];
+ }
+ }
+ }
+}
+
+static void
+perform_composition (COMPOSE_DSC curr_compose_dsc,
+ GeglBuffer *buffer_src[MAX_COMPOSE_IMAGES],
+ GeglBuffer *buffer_dst,
+ ComposeInput *inputs,
+ gint num_images)
+{
+ const Babl *dst_format;
+ GeglBuffer *temp[MAX_COMPOSE_IMAGES];
+ GeglBuffer *dst_temp;
+ const GeglRectangle *extent = NULL;
+
+ const COMPONENT_DSC *components;
+ gdouble mask_vals[MAX_COMPOSE_IMAGES];
+ gint i;
+
+ components = curr_compose_dsc.components;
+
+ /* Get all individual components in gray buffers */
+ for (i = 0; i < num_images; i++)
+ {
+ COMPONENT_DSC cpn_dsc = components[i];
+ const Babl *gray_format;
+
+ if (cpn_dsc.is_perceptual)
+ gray_format = babl_format ("Y' double");
+ else
+ gray_format = babl_format ("Y double");
+
+ if (! inputs[i].is_ID)
+ {
+ const Babl *fish = babl_fish (babl_format ("Y' u8"), gray_format);
+
+ babl_process (fish, &inputs[i].comp.val, &mask_vals[i], 1);
+
+ mask_vals[i] = mask_vals[i] * (cpn_dsc.range_max - cpn_dsc.range_min) + cpn_dsc.range_min;
+ }
+ else
+ {
+ extent = gegl_buffer_get_extent (buffer_src[i]);
+
+ temp[i] = gegl_buffer_new (extent, gray_format);
+
+ gegl_buffer_copy (buffer_src[i], NULL, GEGL_ABYSS_NONE, temp[i], NULL);
+
+ if (cpn_dsc.range_min != 0.0 || cpn_dsc.range_max != 1.0)
+ cpn_affine_transform (temp[i], cpn_dsc.range_min, cpn_dsc.range_max);
+ }
+
+ gimp_progress_update ((gdouble) i / (gdouble) (num_images + 1.0));
+ }
+
+ dst_format = babl_format_new (babl_model (curr_compose_dsc.babl_model),
+ babl_type ("double"),
+ babl_component (components[0].babl_name),
+ num_images > 1 ? babl_component (components[1].babl_name) : NULL,
+ num_images > 2 ? babl_component (components[2].babl_name) : NULL,
+ num_images > 3 ? babl_component (components[3].babl_name) : NULL,
+ NULL);
+
+ /* extent is not NULL because there is at least one drawable */
+ dst_temp = gegl_buffer_new (extent, dst_format);
+
+ /* Gather all individual components in the dst_format buffer */
+ fill_buffer_from_components (temp, dst_temp, num_images, inputs, mask_vals);
+
+ gimp_progress_update ((gdouble) num_images / (gdouble) (num_images + 1.0));
+
+ /* Copy back to the format GIMP wants (and perform the conversion in itself) */
+ gegl_buffer_copy (dst_temp, NULL, GEGL_ABYSS_NONE, buffer_dst, NULL);
+
+ for (i = 0; i< num_images; i++)
+ if( inputs[i].is_ID)
+ g_object_unref (temp[i]);
+
+ g_object_unref (dst_temp);
+}
+
+/* Compose an image from several gray-images */
+static gint32
+compose (const gchar *compose_type,
+ ComposeInput *inputs,
+ gboolean compose_by_drawable)
+{
+ gint width, height;
+ gint num_images, compose_idx;
+ gint i, j;
+ gint num_layers;
+ gint32 layer_ID_dst, image_ID_dst;
+ gint first_ID;
+ GimpImageType gdtype_dst;
+ GeglBuffer *buffer_src[MAX_COMPOSE_IMAGES];
+ GeglBuffer *buffer_dst;
+ GimpPrecision precision;
+
+ /* Search type of composing */
+ compose_idx = -1;
+ for (j = 0; j < G_N_ELEMENTS (compose_dsc); j++)
+ {
+ if (g_ascii_strcasecmp (compose_type, compose_dsc[j].compose_type) == 0)
+ {
+ compose_idx = j;
+ break;
+ }
+ }
+
+ if (compose_idx < 0)
+ return -1;
+
+ num_images = compose_dsc[compose_idx].num_images;
+
+ /* Check that at least one image or one drawable is provided */
+ first_ID = -1;
+ for (i = 0; i < num_images; i++)
+ {
+ if (inputs[i].is_ID)
+ {
+ first_ID = i;
+ break;
+ }
+ }
+
+ if (-1 == first_ID)
+ {
+ g_message (_("At least one image is needed to compose"));
+ return -1;
+ }
+
+ /* Check image sizes */
+ if (compose_by_drawable)
+ {
+ gint32 first_image = gimp_item_get_image (inputs[first_ID].comp.ID);
+
+ if (! gimp_item_is_valid (inputs[first_ID].comp.ID))
+ {
+ g_message (_("Specified layer %d not found"),
+ inputs[first_ID].comp.ID);
+ return -1;
+ }
+
+ width = gimp_drawable_width (inputs[first_ID].comp.ID);
+ height = gimp_drawable_height (inputs[first_ID].comp.ID);
+
+ precision = gimp_image_get_precision (first_image);
+
+ for (j = first_ID + 1; j < num_images; j++)
+ {
+ if (inputs[j].is_ID)
+ {
+ if (! gimp_item_is_valid (inputs[j].comp.ID))
+ {
+ g_message (_("Specified layer %d not found"),
+ inputs[j].comp.ID);
+ return -1;
+ }
+
+ if ((width != gimp_drawable_width (inputs[j].comp.ID)) ||
+ (height != gimp_drawable_height (inputs[j].comp.ID)))
+ {
+ g_message (_("Drawables have different size"));
+ return -1;
+ }
+ }
+ }
+
+ for (j = 0; j < num_images; j++)
+ {
+ if (inputs[j].is_ID)
+ buffer_src[j] = gimp_drawable_get_buffer (inputs[j].comp.ID);
+ else
+ buffer_src[j] = NULL;
+ }
+ }
+ else /* Compose by image ID */
+ {
+ width = gimp_image_width (inputs[first_ID].comp.ID);
+ height = gimp_image_height (inputs[first_ID].comp.ID);
+
+ precision = gimp_image_get_precision (inputs[first_ID].comp.ID);
+
+ for (j = first_ID + 1; j < num_images; j++)
+ {
+ if (inputs[j].is_ID)
+ {
+ if ((width != gimp_image_width (inputs[j].comp.ID)) ||
+ (height != gimp_image_height (inputs[j].comp.ID)))
+ {
+ g_message (_("Images have different size"));
+ return -1;
+ }
+ }
+ }
+
+ /* Get first layer/drawable for all input images */
+ for (j = 0; j < num_images; j++)
+ {
+ if (inputs[j].is_ID)
+ {
+ gint32 *layers;
+
+ /* Get first layer of image */
+ layers = gimp_image_get_layers (inputs[j].comp.ID, &num_layers);
+
+ if (! layers || (num_layers <= 0))
+ {
+ g_message (_("Error in getting layer IDs"));
+ return -1;
+ }
+
+ /* Get drawable for layer */
+ buffer_src[j] = gimp_drawable_get_buffer (layers[0]);
+ g_free (layers);
+ }
+ }
+ }
+
+ /* Unless recomposing, create new image */
+ if (composevals.do_recompose)
+ {
+ layer_ID_dst = composevals.source_layer_ID;
+
+ if (! gimp_item_is_valid (layer_ID_dst))
+ {
+ g_message (_("Unable to recompose, source layer not found"));
+ return -1;
+ }
+
+ image_ID_dst = gimp_item_get_image (layer_ID_dst);
+
+ buffer_dst = gimp_drawable_get_shadow_buffer (layer_ID_dst);
+ }
+ else
+ {
+ gdtype_dst = ((babl_model (compose_dsc[compose_idx].babl_model) == babl_model ("RGBA")) ?
+ GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE);
+
+ image_ID_dst = create_new_image (compose_dsc[compose_idx].filename,
+ width, height, gdtype_dst, precision,
+ &layer_ID_dst, &buffer_dst);
+ }
+
+ if (! compose_by_drawable)
+ {
+ gdouble xres, yres;
+
+ gimp_image_get_resolution (inputs[first_ID].comp.ID, &xres, &yres);
+ gimp_image_set_resolution (image_ID_dst, xres, yres);
+ }
+
+ perform_composition (compose_dsc[compose_idx],
+ buffer_src,
+ buffer_dst,
+ inputs,
+ num_images);
+
+ gimp_progress_update (1.0);
+
+ for (j = 0; j < num_images; j++)
+ {
+ if (inputs[j].is_ID)
+ g_object_unref (buffer_src[j]);
+ }
+
+ g_object_unref (buffer_dst);
+
+ if (composevals.do_recompose)
+ gimp_drawable_merge_shadow (layer_ID_dst, TRUE);
+
+ gimp_drawable_update (layer_ID_dst, 0, 0,
+ gimp_drawable_width (layer_ID_dst),
+ gimp_drawable_height (layer_ID_dst));
+
+ return image_ID_dst;
+}
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ GimpPrecision precision,
+ gint32 *layer_ID,
+ GeglBuffer **buffer)
+{
+ gint32 image_ID;
+ GimpImageBaseType gitype;
+
+ if ((gdtype == GIMP_GRAY_IMAGE) || (gdtype == GIMP_GRAYA_IMAGE))
+ gitype = GIMP_GRAY;
+ else if ((gdtype == GIMP_INDEXED_IMAGE) || (gdtype == GIMP_INDEXEDA_IMAGE))
+ gitype = GIMP_INDEXED;
+ else
+ gitype = GIMP_RGB;
+
+ image_ID = gimp_image_new_with_precision (width, height, gitype, precision);
+
+ gimp_image_undo_disable (image_ID);
+ gimp_image_set_filename (image_ID, filename);
+
+ *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ *buffer = gimp_drawable_get_buffer (*layer_ID);
+
+ return image_ID;
+}
+
+
+static gboolean
+compose_dialog (const gchar *compose_type,
+ gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *table;
+ GtkSizeGroup *size_group;
+ gint32 *layer_list;
+ gint nlayers;
+ gint j;
+ gboolean run;
+
+ /* Check default compose type */
+ composeint.compose_idx = 0;
+ for (j = 0; j < G_N_ELEMENTS (compose_dsc); j++)
+ {
+ if (g_ascii_strcasecmp (compose_type, compose_dsc[j].compose_type) == 0)
+ {
+ composeint.compose_idx = j;
+ break;
+ }
+ }
+
+ /* Save original image width/height */
+ composeint.width = gimp_drawable_width (drawable_ID);
+ composeint.height = gimp_drawable_height (drawable_ID);
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ layer_list = gimp_image_get_layers (gimp_item_get_image (drawable_ID),
+ &nlayers);
+
+ dialog = gimp_dialog_new (_("Compose"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, COMPOSE_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (check_response),
+ NULL);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* Compose type combo */
+
+ frame = gimp_frame_new (_("Compose Channels"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ label = gtk_label_new_with_mnemonic (_("Color _model:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_size_group_add_widget (size_group, label);
+ g_object_unref (size_group);
+
+ combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+ for (j = 0; j < G_N_ELEMENTS (compose_dsc); j++)
+ {
+ gchar *label = g_strdup (gettext (compose_dsc[j].compose_type));
+ gchar *l;
+
+ for (l = label; *l; l++)
+ if (*l == '-' || *l == '_')
+ *l = ' ';
+
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_LABEL, label,
+ GIMP_INT_STORE_VALUE, j,
+ -1);
+ g_free (label);
+ }
+
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ /* Channel representation table */
+
+ frame = gimp_frame_new (_("Channel Representations"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (MAX_COMPOSE_IMAGES, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ for (j = 0; j < MAX_COMPOSE_IMAGES; j++)
+ {
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkObject *scale;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GdkPixbuf *ico;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, j, j + 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ gtk_size_group_add_widget (size_group, hbox);
+
+ composeint.channel_icon[j] = image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ composeint.channel_label[j] = label = gtk_label_new_with_mnemonic ("");
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ if (composeint.compose_idx >= 0 &&
+ nlayers >= compose_dsc[composeint.compose_idx].num_images &&
+ j < nlayers)
+ {
+ composeint.selected[j].comp.ID = layer_list[j];
+ }
+ else
+ {
+ composeint.selected[j].comp.ID = drawable_ID;
+ }
+
+ composeint.selected[j].is_ID = TRUE;
+
+ combo = gimp_drawable_combo_box_new (check_gray, NULL);
+ composeint.channel_menu[j] = combo;
+
+ ico = gtk_widget_render_icon (dialog,
+ GIMP_ICON_CHANNEL_GRAY,
+ GTK_ICON_SIZE_BUTTON, NULL);
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ GIMP_INT_STORE_VALUE, -1,
+ GIMP_INT_STORE_LABEL, _("Mask value"),
+ GIMP_INT_STORE_PIXBUF, ico,
+ -1);
+ g_object_unref (ico);
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, j, j + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ scale = gimp_color_scale_entry_new (GTK_TABLE (table), 2, j, NULL,
+ 100, 4,
+ 255.0, 0.0, 255.0, 1.0, 10.0, 0,
+ NULL, NULL);
+ composeint.color_scales[j] = GIMP_SCALE_ENTRY_SCALE (scale);
+ composeint.color_spins[j] = GIMP_SCALE_ENTRY_SPINBUTTON (scale);
+
+ gtk_widget_set_sensitive (composeint.color_scales[j], FALSE);
+ gtk_widget_set_sensitive (composeint.color_spins[j], FALSE);
+
+ g_signal_connect (scale, "value-changed",
+ G_CALLBACK (scale_callback),
+ &composeint.selected[j]);
+
+ /* This has to be connected last otherwise it will emit before
+ * combo_callback has any scale and spinbutton to work with
+ */
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ composeint.selected[j].comp.ID,
+ G_CALLBACK (combo_callback),
+ GINT_TO_POINTER (j));
+ }
+
+ g_free (layer_list);
+
+ /* Calls the combo callback and sets icons, labels and sensitivity */
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ composeint.compose_idx,
+ G_CALLBACK (type_combo_callback),
+ NULL);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ if (run)
+ {
+ gint j;
+
+ for (j = 0; j < MAX_COMPOSE_IMAGES; j++)
+ {
+ composevals.inputs[j].is_ID = composeint.selected[j].is_ID;
+
+ if (composevals.inputs[j].is_ID)
+ composevals.inputs[j].comp.ID = composeint.selected[j].comp.ID;
+ else
+ composevals.inputs[j].comp.val = composeint.selected[j].comp.val;
+ }
+
+ strcpy (composevals.compose_type,
+ compose_dsc[composeint.compose_idx].compose_type);
+ }
+
+ return run;
+}
+
+/* Compose interface functions */
+
+static gboolean
+check_gray (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+
+{
+ return ((gimp_image_base_type (image_id) == GIMP_GRAY) &&
+ (gimp_image_width (image_id) == composeint.width) &&
+ (gimp_image_height (image_id) == composeint.height));
+}
+
+static void
+check_response (GtkWidget *dialog,
+ gint response,
+ gpointer data)
+{
+ switch (response)
+ {
+ case GTK_RESPONSE_OK:
+ {
+ gint i;
+ gint nb = 0;
+ gboolean has_image = FALSE;
+
+ nb = compose_dsc[composeint.compose_idx].num_images;
+
+ for (i = 0; i < nb; i++)
+ {
+ if (composeint.selected[i].is_ID)
+ {
+ has_image = TRUE;
+ break;
+ }
+ }
+
+ if (! has_image)
+ {
+ GtkWidget *d;
+
+ g_signal_stop_emission_by_name (dialog, "response");
+ d = gtk_message_dialog_new (GTK_WINDOW (dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT |
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("At least one image is needed to compose"));
+
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (d);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+combo_callback (GimpIntComboBox *widget,
+ gpointer data)
+{
+ gint id;
+ gint n;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &id);
+ n = GPOINTER_TO_INT (data);
+
+ if (id == -1)
+ {
+ gtk_widget_set_sensitive (composeint.color_scales[n], TRUE);
+ gtk_widget_set_sensitive (composeint.color_spins[n], TRUE);
+
+ composeint.selected[n].is_ID = FALSE;
+ composeint.selected[n].comp.val =
+ gtk_range_get_value (GTK_RANGE (composeint.color_scales[n]));
+ }
+ else
+ {
+ gtk_widget_set_sensitive (composeint.color_scales[n], FALSE);
+ gtk_widget_set_sensitive (composeint.color_spins[n], FALSE);
+
+ composeint.selected[n].is_ID = TRUE;
+ composeint.selected[n].comp.ID = id;
+ }
+}
+
+static void
+scale_callback (GtkAdjustment *adj,
+ ComposeInput *input)
+{
+ input->comp.val = gtk_adjustment_get_value (adj);
+}
+
+static void
+type_combo_callback (GimpIntComboBox *combo,
+ gpointer data)
+{
+ if (gimp_int_combo_box_get_active (combo, &composeint.compose_idx))
+ {
+ gboolean combo4;
+ gboolean scale4;
+ gint compose_idx;
+ gint j;
+
+ compose_idx = composeint.compose_idx;
+
+ for (j = 0; j < MAX_COMPOSE_IMAGES; j++)
+ {
+ GtkWidget *label = composeint.channel_label[j];
+ GtkWidget *image = composeint.channel_icon[j];
+ const gchar *text = compose_dsc[compose_idx].components[j].name;
+ const gchar *icon = compose_dsc[compose_idx].components[j].icon;
+
+ gtk_label_set_text_with_mnemonic (GTK_LABEL (label),
+ text ? gettext (text) : "");
+
+ if (icon)
+ {
+ gtk_image_set_from_icon_name (GTK_IMAGE (image),
+ icon, GTK_ICON_SIZE_BUTTON);
+ gtk_widget_show (image);
+ }
+ else
+ {
+ gtk_image_clear (GTK_IMAGE (image));
+ gtk_widget_hide (image);
+ }
+ }
+
+ /* Set sensitivity of last menu */
+ combo4 = (compose_dsc[compose_idx].num_images == 4);
+ gtk_widget_set_sensitive (composeint.channel_menu[3], combo4);
+
+ scale4 = combo4 && !composeint.selected[3].is_ID;
+ gtk_widget_set_sensitive (composeint.color_scales[3], scale4);
+ gtk_widget_set_sensitive (composeint.color_spins[3], scale4);
+ }
+}
diff --git a/plug-ins/common/contrast-retinex.c b/plug-ins/common/contrast-retinex.c
new file mode 100644
index 0000000..f48e5c9
--- /dev/null
+++ b/plug-ins/common/contrast-retinex.c
@@ -0,0 +1,840 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-retinex"
+#define PLUG_IN_BINARY "contrast-retinex"
+#define PLUG_IN_ROLE "gimp-contrast-retinex"
+#define MAX_RETINEX_SCALES 8
+#define MIN_GAUSSIAN_SCALE 16
+#define MAX_GAUSSIAN_SCALE 250
+#define SCALE_WIDTH 150
+#define ENTRY_WIDTH 4
+
+
+typedef struct
+{
+ gint scale;
+ gint nscales;
+ gint scales_mode;
+ gfloat cvar;
+} RetinexParams;
+
+typedef enum
+{
+ filter_uniform,
+ filter_low,
+ filter_high
+} FilterMode;
+
+/*
+ Definit comment sont repartis les
+ differents filtres en fonction de
+ l'echelle (~= ecart type de la gaussienne)
+ */
+#define RETINEX_UNIFORM 0
+#define RETINEX_LOW 1
+#define RETINEX_HIGH 2
+
+static gfloat RetinexScales[MAX_RETINEX_SCALES];
+
+typedef struct
+{
+ gint N;
+ gfloat sigma;
+ gdouble B;
+ gdouble b[4];
+} gauss3_coefs;
+
+
+/*
+ * Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* Gimp */
+static gboolean retinex_dialog (gint32 drawable_ID);
+static void retinex (gint32 drawable_ID,
+ GimpPreview *preview);
+static void retinex_preview (gpointer drawable_ID,
+ GimpPreview *preview);
+
+static void retinex_scales_distribution (gfloat *scales,
+ gint nscales,
+ gint mode,
+ gint s);
+
+static void compute_mean_var (gfloat *src,
+ gfloat *mean,
+ gfloat *var,
+ gint size,
+ gint bytes);
+/*
+ * Gauss
+ */
+static void compute_coefs3 (gauss3_coefs *c,
+ gfloat sigma);
+
+static void gausssmooth (gfloat *in,
+ gfloat *out,
+ gint size,
+ gint rowtride,
+ gauss3_coefs *c);
+
+/*
+ * MSRCR = MultiScale Retinex with Color Restoration
+ */
+static void MSRCR (guchar *src,
+ gint width,
+ gint height,
+ gint bytes,
+ gboolean preview_mode);
+
+
+/*
+ * Private variables.
+ */
+static RetinexParams rvals =
+{
+ 240, /* Scale */
+ 3, /* Scales */
+ RETINEX_UNIFORM, /* Echelles reparties uniformement */
+ 1.2 /* A voir */
+};
+
+static GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "scale", "Biggest scale value" },
+ { GIMP_PDB_INT32, "nscales", "Number of scales" },
+ { GIMP_PDB_INT32, "scales-mode", "Retinex distribution through scales" },
+ { GIMP_PDB_FLOAT, "cvar", "Variance value" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Enhance contrast using the Retinex method"),
+ "The Retinex Image Enhancement Algorithm is an "
+ "automatic image enhancement method that enhances "
+ "a digital image in terms of dynamic range "
+ "compression, color independence from the spectral "
+ "distribution of the scene illuminant, and "
+ "color/lightness rendition.",
+ "Fabien Pelisson",
+ "Fabien Pelisson",
+ "2003",
+ N_("Retine_x..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Tone Mapping");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint x, y, width, height;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x, &y, &width, &height) ||
+ width < MIN_GAUSSIAN_SCALE ||
+ height < MIN_GAUSSIAN_SCALE)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ values[0].data.d_status = status;
+ return;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &rvals);
+
+ /* First acquire information with a dialog */
+ if (! retinex_dialog (drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ rvals.scale = (param[3].data.d_int32);
+ rvals.nscales = (param[4].data.d_int32);
+ rvals.scales_mode = (param[5].data.d_int32);
+ rvals.cvar = (param[6].data.d_float);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &rvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS &&
+ (gimp_drawable_is_rgb (drawable_ID)))
+ {
+ gimp_progress_init (_("Retinex"));
+
+ retinex (drawable_ID, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &rvals, sizeof (RetinexParams));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gboolean
+retinex_dialog (gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Retinex Image Enhancement"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_zoom_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (retinex_preview),
+ GINT_TO_POINTER (drawable_ID));
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new (_("Uniform"), filter_uniform,
+ _("Low"), filter_low,
+ _("High"), filter_high,
+ NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), rvals.scales_mode,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &rvals.scales_mode);
+ g_signal_connect_swapped (combo, "changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Level:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ gtk_widget_show (combo);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Scale:"), SCALE_WIDTH, ENTRY_WIDTH,
+ rvals.scale,
+ MIN_GAUSSIAN_SCALE, MAX_GAUSSIAN_SCALE, 1, 1, 0,
+ TRUE, 0, 0, NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &rvals.scale);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Scale _division:"), SCALE_WIDTH, ENTRY_WIDTH,
+ rvals.nscales,
+ 0, MAX_RETINEX_SCALES, 1, 1, 0,
+ TRUE, 0, 0, NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &rvals.nscales);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Dy_namic:"), SCALE_WIDTH, ENTRY_WIDTH,
+ rvals.cvar, 0, 4, 0.1, 0.1, 1,
+ TRUE, 0, 0, NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_float_adjustment_update),
+ &rvals.cvar);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+/*
+ * Applies the algorithm
+ */
+static void
+retinex (gint32 drawable_ID,
+ GimpPreview *preview)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ guchar *src = NULL;
+ guchar *psrc = NULL;
+ gint x, y, width, height;
+ gint size, bytes;
+
+ /*
+ * Get the size of the current image or its selection.
+ */
+ if (preview)
+ {
+ src = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
+ &width, &height, &bytes);
+ }
+ else
+ {
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x, &y, &width, &height))
+ return;
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ /* Allocate memory */
+ size = width * height * bytes;
+ src = g_try_malloc (sizeof (guchar) * size);
+
+ if (src == NULL)
+ {
+ g_warning ("Failed to allocate memory");
+ return;
+ }
+
+ memset (src, 0, sizeof (guchar) * size);
+
+ /* Fill allocated memory with pixel data */
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ /*
+ Algorithm for Multi-scale Retinex with color Restoration (MSRCR).
+ */
+ psrc = src;
+ MSRCR (psrc, width, height, bytes, preview != NULL);
+
+ if (preview)
+ {
+ gimp_preview_draw_buffer (preview, psrc, width * bytes);
+ }
+ else
+ {
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
+ format, psrc,
+ GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, x, y, width, height);
+ }
+
+ g_free (src);
+}
+
+static void
+retinex_preview (gpointer drawable_ID,
+ GimpPreview *preview)
+{
+ retinex (GPOINTER_TO_INT (drawable_ID), preview);
+}
+
+/*
+ * calculate scale values for desired distribution.
+ */
+static void
+retinex_scales_distribution (gfloat *scales,
+ gint nscales,
+ gint mode,
+ gint s)
+{
+ if (nscales == 1)
+ { /* For one filter we choose the median scale */
+ scales[0] = (gint) s / 2;
+ }
+ else if (nscales == 2)
+ { /* For two filters we choose the median and maximum scale */
+ scales[0] = (gint) s / 2;
+ scales[1] = (gint) s;
+ }
+ else
+ {
+ gfloat size_step = (gfloat) s / (gfloat) nscales;
+ gint i;
+
+ switch(mode)
+ {
+ case RETINEX_UNIFORM:
+ for(i = 0; i < nscales; ++i)
+ scales[i] = 2. + (gfloat) i * size_step;
+ break;
+
+ case RETINEX_LOW:
+ size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
+ for (i = 0; i < nscales; ++i)
+ scales[i] = 2. + pow (10, (i * size_step) / log (10));
+ break;
+
+ case RETINEX_HIGH:
+ size_step = (gfloat) log(s - 2.0) / (gfloat) nscales;
+ for (i = 0; i < nscales; ++i)
+ scales[i] = s - pow (10, (i * size_step) / log (10));
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Calculate the coefficients for the recursive filter algorithm
+ * Fast Computation of gaussian blurring.
+ */
+static void
+compute_coefs3 (gauss3_coefs *c, gfloat sigma)
+{
+ /*
+ * Papers: "Recursive Implementation of the gaussian filter.",
+ * Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
+ * formula: 11b computation of q
+ * 8c computation of b0..b1
+ * 10 alpha is normalization constant B
+ */
+ gfloat q, q2, q3;
+
+ if (sigma >= 2.5)
+ {
+ q = 0.98711 * sigma - 0.96330;
+ }
+ else if ((sigma >= 0.5) && (sigma < 2.5))
+ {
+ q = 3.97156 - 4.14554 * (gfloat) sqrt ((double) 1 - 0.26891 * sigma);
+ }
+ else
+ {
+ q = 0.1147705018520355224609375;
+ }
+
+ q2 = q * q;
+ q3 = q * q2;
+ c->b[0] = (1.57825+(2.44413*q)+(1.4281 *q2)+(0.422205*q3));
+ c->b[1] = ( (2.44413*q)+(2.85619*q2)+(1.26661 *q3));
+ c->b[2] = ( -((1.4281*q2)+(1.26661 *q3)));
+ c->b[3] = ( (0.422205*q3));
+ c->B = 1.0-((c->b[1]+c->b[2]+c->b[3])/c->b[0]);
+ c->sigma = sigma;
+ c->N = 3;
+
+/*
+ g_printerr ("q %f\n", q);
+ g_printerr ("q2 %f\n", q2);
+ g_printerr ("q3 %f\n", q3);
+ g_printerr ("c->b[0] %f\n", c->b[0]);
+ g_printerr ("c->b[1] %f\n", c->b[1]);
+ g_printerr ("c->b[2] %f\n", c->b[2]);
+ g_printerr ("c->b[3] %f\n", c->b[3]);
+ g_printerr ("c->B %f\n", c->B);
+ g_printerr ("c->sigma %f\n", c->sigma);
+ g_printerr ("c->N %d\n", c->N);
+*/
+}
+
+static void
+gausssmooth (gfloat *in, gfloat *out, gint size, gint rowstride, gauss3_coefs *c)
+{
+ /*
+ * Papers: "Recursive Implementation of the gaussian filter.",
+ * Ian T. Young , Lucas J. Van Vliet, Signal Processing 44, Elsevier 1995.
+ * formula: 9a forward filter
+ * 9b backward filter
+ * fig7 algorithm
+ */
+ gint i,n, bufsize;
+ gfloat *w1,*w2;
+
+ /* forward pass */
+ bufsize = size+3;
+ size -= 1;
+ w1 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
+ w2 = (gfloat *) g_try_malloc (bufsize * sizeof (gfloat));
+ w1[0] = in[0];
+ w1[1] = in[0];
+ w1[2] = in[0];
+ for ( i = 0 , n=3; i <= size ; i++, n++)
+ {
+ w1[n] = (gfloat)(c->B*in[i*rowstride] +
+ ((c->b[1]*w1[n-1] +
+ c->b[2]*w1[n-2] +
+ c->b[3]*w1[n-3] ) / c->b[0]));
+ }
+
+ /* backward pass */
+ w2[size+1]= w1[size+3];
+ w2[size+2]= w1[size+3];
+ w2[size+3]= w1[size+3];
+ for (i = size, n = i; i >= 0; i--, n--)
+ {
+ w2[n]= out[i * rowstride] = (gfloat)(c->B*w1[n+3] +
+ ((c->b[1]*w2[n+1] +
+ c->b[2]*w2[n+2] +
+ c->b[3]*w2[n+3] ) / c->b[0]));
+ }
+
+ g_free (w1);
+ g_free (w2);
+}
+
+/*
+ * This function is the heart of the algo.
+ * (a) Filterings at several scales and sumarize the results.
+ * (b) Calculation of the final values.
+ */
+static void
+MSRCR (guchar *src, gint width, gint height, gint bytes, gboolean preview_mode)
+{
+
+ gint scale,row,col;
+ gint i,j;
+ gint size;
+ gint channel;
+ guchar *psrc = NULL; /* backup pointer for src buffer */
+ gfloat *dst = NULL; /* float buffer for algorithm */
+ gfloat *pdst = NULL; /* backup pointer for float buffer */
+ gfloat *in, *out;
+ gint channelsize; /* Float memory cache for one channel */
+ gfloat weight;
+ gauss3_coefs coef;
+ gfloat mean, var;
+ gfloat mini, range, maxi;
+ gfloat alpha;
+ gfloat gain;
+ gfloat offset;
+ gdouble max_preview = 0.0;
+
+ if (!preview_mode)
+ {
+ gimp_progress_init (_("Retinex: filtering"));
+ max_preview = 3 * rvals.nscales;
+ }
+
+ /* Allocate all the memory needed for algorithm*/
+ size = width * height * bytes;
+ dst = g_try_malloc (size * sizeof (gfloat));
+ if (dst == NULL)
+ {
+ g_warning ("Failed to allocate memory");
+ return;
+ }
+ memset (dst, 0, size * sizeof (gfloat));
+
+ channelsize = (width * height);
+ in = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
+ if (in == NULL)
+ {
+ g_free (dst);
+ g_warning ("Failed to allocate memory");
+ return; /* do some clever stuff */
+ }
+
+ out = (gfloat *) g_try_malloc (channelsize * sizeof (gfloat));
+ if (out == NULL)
+ {
+ g_free (in);
+ g_free (dst);
+ g_warning ("Failed to allocate memory");
+ return; /* do some clever stuff */
+ }
+
+
+ /*
+ Calculate the scales of filtering according to the
+ number of filter and their distribution.
+ */
+
+ retinex_scales_distribution (RetinexScales,
+ rvals.nscales, rvals.scales_mode, rvals.scale);
+
+ /*
+ Filtering according to the various scales.
+ Summerize the results of the various filters according to a
+ specific weight(here equivalent for all).
+ */
+ weight = 1./ (gfloat) rvals.nscales;
+
+ /*
+ The recursive filtering algorithm needs different coefficients according
+ to the selected scale (~ = standard deviation of Gaussian).
+ */
+ for (channel = 0; channel < 3; channel++)
+ {
+ gint pos;
+
+ for (i = 0, pos = channel; i < channelsize ; i++, pos += bytes)
+ {
+ /* 0-255 => 1-256 */
+ in[i] = (gfloat)(src[pos] + 1.0);
+ }
+ for (scale = 0; scale < rvals.nscales; scale++)
+ {
+ compute_coefs3 (&coef, RetinexScales[scale]);
+ /*
+ * Filtering (smoothing) Gaussian recursive.
+ *
+ * Filter rows first
+ */
+ for (row=0 ;row < height; row++)
+ {
+ pos = row * width;
+ gausssmooth (in + pos, out + pos, width, 1, &coef);
+ }
+
+ memcpy(in, out, channelsize * sizeof(gfloat));
+ memset(out, 0 , channelsize * sizeof(gfloat));
+
+ /*
+ * Filtering (smoothing) Gaussian recursive.
+ *
+ * Second columns
+ */
+ for (col=0; col < width; col++)
+ {
+ pos = col;
+ gausssmooth(in + pos, out + pos, height, width, &coef);
+ }
+
+ /*
+ Summarize the filtered values.
+ In fact one calculates a ratio between the original values and the filtered values.
+ */
+ for (i = 0, pos = channel; i < channelsize; i++, pos += bytes)
+ {
+ dst[pos] += weight * (log (src[pos] + 1.) - log (out[i]));
+ }
+
+ if (!preview_mode)
+ gimp_progress_update ((channel * rvals.nscales + scale) /
+ max_preview);
+ }
+ }
+ g_free(in);
+ g_free(out);
+
+ /*
+ Final calculation with original value and cumulated filter values.
+ The parameters gain, alpha and offset are constants.
+ */
+ /* Ci(x,y)=log[a Ii(x,y)]-log[ Ei=1-s Ii(x,y)] */
+
+ alpha = 128.;
+ gain = 1.;
+ offset = 0.;
+
+ for (i = 0; i < size; i += bytes)
+ {
+ gfloat logl;
+
+ psrc = src+i;
+ pdst = dst+i;
+
+ logl = log((gfloat)psrc[0] + (gfloat)psrc[1] + (gfloat)psrc[2] + 3.);
+
+ pdst[0] = gain * ((log(alpha * (psrc[0]+1.)) - logl) * pdst[0]) + offset;
+ pdst[1] = gain * ((log(alpha * (psrc[1]+1.)) - logl) * pdst[1]) + offset;
+ pdst[2] = gain * ((log(alpha * (psrc[2]+1.)) - logl) * pdst[2]) + offset;
+ }
+
+/* if (!preview_mode)
+ gimp_progress_update ((2.0 + (rvals.nscales * 3)) /
+ ((rvals.nscales * 3) + 3));*/
+
+ /*
+ Adapt the dynamics of the colors according to the statistics of the first and second order.
+ The use of the variance makes it possible to control the degree of saturation of the colors.
+ */
+ pdst = dst;
+
+ compute_mean_var (pdst, &mean, &var, size, bytes);
+ mini = mean - rvals.cvar*var;
+ maxi = mean + rvals.cvar*var;
+ range = maxi - mini;
+
+ if (!range)
+ range = 1.0;
+
+ for (i = 0; i < size; i+= bytes)
+ {
+ psrc = src + i;
+ pdst = dst + i;
+
+ for (j = 0 ; j < 3 ; j++)
+ {
+ gfloat c = 255 * ( pdst[j] - mini ) / range;
+
+ psrc[j] = (guchar) CLAMP (c, 0, 255);
+ }
+ }
+
+ g_free (dst);
+}
+
+/*
+ * Calculate the average and variance in one go.
+ */
+static void
+compute_mean_var (gfloat *src, gfloat *mean, gfloat *var, gint size, gint bytes)
+{
+ gfloat vsquared;
+ gint i,j;
+ gfloat *psrc;
+
+ vsquared = 0;
+ *mean = 0;
+ for (i = 0; i < size; i+= bytes)
+ {
+ psrc = src+i;
+ for (j = 0 ; j < 3 ; j++)
+ {
+ *mean += psrc[j];
+ vsquared += psrc[j] * psrc[j];
+ }
+ }
+
+ *mean /= (gfloat) size; /* mean */
+ vsquared /= (gfloat) size; /* mean (x^2) */
+ *var = ( vsquared - (*mean * *mean) );
+ *var = sqrt(*var); /* var */
+}
diff --git a/plug-ins/common/crop-zealous.c b/plug-ins/common/crop-zealous.c
new file mode 100644
index 0000000..6efc34b
--- /dev/null
+++ b/plug-ins/common/crop-zealous.c
@@ -0,0 +1,326 @@
+/*
+ * ZealousCrop plug-in version 1.00
+ * by Adam D. Moss <adam@foxbox.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PLUG_IN_PROC "plug-in-zealouscrop"
+
+#define EPSILON (1e-5)
+#define FLOAT_IS_ZERO(value) (value > -EPSILON && value < EPSILON)
+#define FLOAT_EQUAL(v1, v2) ((v1 - v2) > -EPSILON && (v1 - v2) < EPSILON)
+
+/* Declare local functions. */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static inline gboolean colors_equal (const gfloat *col1,
+ const gfloat *col2,
+ gint components,
+ gboolean has_alpha);
+static void do_zcrop (gint32 drawable_id,
+ gint32 image_id);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Autocrop unused space from edges and middle"),
+ "",
+ "Adam D. Moss",
+ "Adam D. Moss",
+ "1997",
+ N_("_Zealous Crop"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Crop");
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 drawable_id;
+ gint32 image_id;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ if (n_params != 3)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Get the specified drawable */
+ image_id = param[1].data.d_int32;
+ drawable_id = param[2].data.d_int32;
+
+ /* Make sure that the drawable is gray or RGB or indexed */
+ if (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id) ||
+ gimp_drawable_is_indexed (drawable_id))
+ {
+ gimp_progress_init (_("Zealous cropping"));
+
+ do_zcrop (drawable_id, image_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ gegl_exit ();
+}
+
+static inline gboolean
+colors_equal (const gfloat *col1,
+ const gfloat *col2,
+ gint components,
+ gboolean has_alpha)
+{
+ if (has_alpha &&
+ FLOAT_IS_ZERO (col1[components - 1]) &&
+ FLOAT_IS_ZERO (col2[components - 1]))
+ {
+ return TRUE;
+ }
+ else
+ {
+ gint b;
+
+ for (b = 0; b < components; b++)
+ {
+ if (! FLOAT_EQUAL (col1[b], col2[b]))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+static void
+do_zcrop (gint32 drawable_id,
+ gint32 image_id)
+{
+ GeglBuffer *drawable_buffer;
+ GeglBuffer *shadow_buffer;
+ gfloat *linear_buf;
+ const Babl *format;
+
+ gint x, y, width, height;
+ gint components;
+ gint8 *killrows;
+ gint8 *killcols;
+ gint32 livingrows, livingcols, destrow, destcol;
+ gint32 selection_copy_id;
+ gboolean has_alpha;
+
+ drawable_buffer = gimp_drawable_get_buffer (drawable_id);
+ shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ width = gegl_buffer_get_width (drawable_buffer);
+ height = gegl_buffer_get_height (drawable_buffer);
+ has_alpha = gimp_drawable_has_alpha (drawable_id);
+
+ if (has_alpha)
+ format = babl_format ("R'G'B'A float");
+ else
+ format = babl_format ("R'G'B' float");
+
+ components = babl_format_get_n_components (format);
+
+ killrows = g_new (gint8, height);
+ killcols = g_new (gint8, width);
+
+ linear_buf = g_new (gfloat, (width > height ? width : height) * components);
+
+ /* search which rows to remove */
+
+ livingrows = 0;
+ for (y = 0; y < height; y++)
+ {
+ gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (0, y, width, 1),
+ 1.0, format, linear_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ killrows[y] = TRUE;
+
+ for (x = components; x < width * components; x += components)
+ {
+ if (! colors_equal (linear_buf, &linear_buf[x], components, has_alpha))
+ {
+ livingrows++;
+ killrows[y] = FALSE;
+ break;
+ }
+ }
+ }
+
+ gimp_progress_update (0.25);
+
+ /* search which columns to remove */
+
+ livingcols = 0;
+ for (x = 0; x < width; x++)
+ {
+ gegl_buffer_get (drawable_buffer, GEGL_RECTANGLE (x, 0, 1, height),
+ 1.0, format, linear_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ killcols[x] = TRUE;
+
+ for (y = components; y < height * components; y += components)
+ {
+ if (! colors_equal (linear_buf, &linear_buf[y], components, has_alpha))
+ {
+ livingcols++;
+ killcols[x] = FALSE;
+ break;
+ }
+ }
+ }
+
+ gimp_progress_update (0.5);
+
+ if ((livingcols == 0 || livingrows == 0) ||
+ (livingcols == width && livingrows == height))
+ {
+ g_message (_("Nothing to crop."));
+
+ g_object_unref (shadow_buffer);
+ g_object_unref (drawable_buffer);
+
+ g_free (linear_buf);
+ g_free (killrows);
+ g_free (killcols);
+ return;
+ }
+
+ /* restitute living rows */
+
+ destrow = 0;
+ for (y = 0; y < height; y++)
+ {
+ if (!killrows[y])
+ {
+ gegl_buffer_copy (drawable_buffer,
+ GEGL_RECTANGLE (0, y, width, 1),
+ GEGL_ABYSS_NONE,
+ shadow_buffer,
+ GEGL_RECTANGLE (0, destrow, width, 1));
+
+ destrow++;
+ }
+ }
+
+ gimp_progress_update (0.75);
+
+ /* restitute living columns */
+
+ destcol = 0;
+ for (x = 0; x < width; x++)
+ {
+ if (!killcols[x])
+ {
+ gegl_buffer_copy (shadow_buffer,
+ GEGL_RECTANGLE (x, 0, 1, height),
+ GEGL_ABYSS_NONE,
+ shadow_buffer,
+ GEGL_RECTANGLE (destcol, 0, 1, height));
+
+ destcol++;
+ }
+ }
+
+ gimp_progress_update (1.00);
+
+ gimp_image_undo_group_start (image_id);
+
+ selection_copy_id = gimp_selection_save (image_id);
+ gimp_selection_none (image_id);
+
+ gegl_buffer_flush (shadow_buffer);
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gegl_buffer_flush (drawable_buffer);
+
+ gimp_image_select_item (image_id, GIMP_CHANNEL_OP_REPLACE, selection_copy_id);
+ gimp_image_remove_channel (image_id, selection_copy_id);
+
+ gimp_image_crop (image_id, livingcols, livingrows, 0, 0);
+
+ gimp_image_undo_group_end (image_id);
+
+ g_object_unref (shadow_buffer);
+ g_object_unref (drawable_buffer);
+
+ g_free (linear_buf);
+ g_free (killrows);
+ g_free (killcols);
+}
diff --git a/plug-ins/common/curve-bend.c b/plug-ins/common/curve-bend.c
new file mode 100644
index 0000000..dfa9ff7
--- /dev/null
+++ b/plug-ins/common/curve-bend.c
@@ -0,0 +1,3402 @@
+/* curve_bend plugin for GIMP */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Revision history
+ * (2004/02/08) v1.3.18 hof: #133244 exit with execution error if there is
+ * an empty selection
+ * (2003/08/24) v1.3.18 hof: #119937 show busy cursor when recalculating
+ * preview
+ * (2002/09/xx) v1.1.18 mitch and gsr: clean interface
+ * (2000/02/16) v1.1.17b hof: added spinbuttons for rotate entry
+ * (2000/02/16) v1.1.17 hof: undo bugfix (#6012)
+ * don't call gimp_undo_push_group_end
+ * after gimp_displays_flush
+ * (1999/09/13) v1.01 hof: PDB-calls updated for gimp 1.1.9
+ * (1999/05/10) v1.0 hof: first public release
+ * (1999/04/23) v0.0 hof: coding started,
+ * splines and dialog parts are similar to curves.c
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Defines */
+#define PLUG_IN_PROC "plug-in-curve-bend"
+#define PLUG_IN_BINARY "curve-bend"
+#define PLUG_IN_ROLE "gimp-curve-bend"
+#define PLUG_IN_VERSION "v1.3.18 (2003/08/26)"
+#define PLUG_IN_IMAGE_TYPES "RGB*, GRAY*"
+#define PLUG_IN_AUTHOR "Wolfgang Hofer (hof@hotbot.com)"
+#define PLUG_IN_COPYRIGHT "Wolfgang Hofer"
+
+#define PLUG_IN_ITER_NAME "plug_in_curve_bend_Iterator"
+#define PLUG_IN_DATA_ITER_FROM "plug_in_curve_bend_ITER_FROM"
+#define PLUG_IN_DATA_ITER_TO "plug_in_curve_bend_ITER_TO"
+
+#define KEY_POINTFILE "POINTFILE_CURVE_BEND"
+#define KEY_POINTS "POINTS"
+#define KEY_VAL_Y "VAL_Y"
+
+#define MIDDLE 127
+
+#define MIX_CHANNEL(a, b, m) (((a * m) + (b * (255 - m))) / 255)
+
+#define UP_GRAPH 0x1
+#define UP_PREVIEW_EXPOSE 0x4
+#define UP_PREVIEW 0x8
+#define UP_DRAW 0x10
+#define UP_ALL 0xFF
+
+#define GRAPH_WIDTH 256
+#define GRAPH_HEIGHT 256
+#define PREVIEW_SIZE_X 256
+#define PREVIEW_SIZE_Y 256
+#define PV_IMG_WIDTH 128
+#define PV_IMG_HEIGHT 128
+#define RADIUS 3
+#define MIN_DISTANCE 8
+#define PREVIEW_BPP 4
+
+#define SMOOTH 0
+#define GFREE 1
+
+#define GRAPH_MASK GDK_EXPOSURE_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK
+
+
+#define OUTLINE_UPPER 0
+#define OUTLINE_LOWER 1
+
+typedef struct _BenderValues BenderValues;
+struct _BenderValues
+{
+ guchar curve[2][256]; /* for curve_type freehand mode 0 <= curve <= 255 */
+ gdouble points[2][17][2]; /* for curve_type smooth mode 0.0 <= points <= 1.0 */
+
+ gint curve_type;
+
+ gint smoothing;
+ gint antialias;
+ gint work_on_copy;
+ gdouble rotation;
+
+ gint32 total_steps;
+ gdouble current_step;
+};
+
+typedef struct _Curves Curves;
+
+struct _Curves
+{
+ int x, y; /* coords for last mouse click */
+};
+
+typedef struct _BenderDialog BenderDialog;
+
+struct _BenderDialog
+{
+ GtkWidget *shell;
+ GtkWidget *outline_menu;
+ GtkWidget *pv_widget;
+ GtkWidget *graph;
+ GtkAdjustment *rotate_data;
+ GdkPixmap *pixmap;
+ GtkWidget *filechooser;
+
+ GdkCursor *cursor_busy;
+
+ gint32 drawable_id;
+ int color;
+ int outline;
+ gint preview;
+
+ int grab_point;
+ int last;
+ int leftmost;
+ int rightmost;
+ int curve_type;
+ gdouble points[2][17][2]; /* 0.0 <= points <= 1.0 */
+ guchar curve[2][256]; /* 0 <= curve <= 255 */
+ gint32 *curve_ptr[2]; /* 0 <= curve_ptr <= src_drawable_width
+ * both arrays are allocated dynamic,
+ * depending on drawable width
+ */
+ gint32 min2[2];
+ gint32 max2[2];
+ gint32 zero2[2];
+
+ gboolean show_progress;
+ gboolean smoothing;
+ gboolean antialias;
+ gboolean work_on_copy;
+ gdouble rotation;
+
+ gint32 preview_image_id;
+ gint32 preview_layer_id1;
+ gint32 preview_layer_id2;
+
+ BenderValues *bval_from;
+ BenderValues *bval_to;
+ BenderValues *bval_curr;
+
+ gboolean run;
+};
+
+/* points Coords:
+ *
+ * 1.0 +----+----+----+----+
+ * | . | | |
+ * +--.-+--.-+----+----+
+ * . | . | |
+ * 0.5 +----+----+-.--+----+
+ * | | | . .
+ * +----+----+----+-.-.+
+ * | | | | |
+ * 0.0 +----+----+----+----+
+ * 0.0 0.5 1.0
+ *
+ * curve Coords:
+ *
+ * OUTLINE_UPPER OUTLINE_LOWER
+ *
+ * 255 +----+----+----+----+ 255 +----+----+----+----+
+ * | . | | | --- max | . | | | --- max
+ * +--.-+--.-+----+----+ +--.-+--.-+----+----+
+ * . | . | | zero ___ . | . | |
+ * +----+----+-.--+----+ +----+----+-.--+----+
+ * | | | . . --- zero | | | . .
+ * +----+----+----+-.-.+ ___ min +----+----+----+-.-.+ ___ min
+ * | | | | | | | | | |
+ * 0 +----+----+----+----+ 0 +----+----+----+----+
+ * 0 255 0 255
+ */
+
+typedef double CRMatrix[4][4];
+
+typedef struct
+{
+ guint32 drawable_id;
+ gint width;
+ gint height;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */
+ gint bpp;
+ gint tile_width;
+ gint tile_height;
+} t_GDRW;
+
+typedef struct
+{
+ gint32 y;
+ guchar color[4];
+} t_Last;
+
+
+/* curves action functions */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static BenderDialog * bender_new_dialog (gint32 drawable_id);
+static void bender_update (BenderDialog *,
+ int);
+static void bender_plot_curve (BenderDialog *,
+ int,
+ int,
+ int,
+ int,
+ gint32,
+ gint32,
+ gint);
+static void bender_calculate_curve (BenderDialog *,
+ gint32,
+ gint32,
+ gint);
+static void bender_rotate_adj_callback (GtkAdjustment *,
+ gpointer);
+static void bender_border_callback (GtkWidget *,
+ gpointer);
+static void bender_type_callback (GtkWidget *,
+ gpointer);
+static void bender_reset_callback (GtkWidget *,
+ gpointer);
+static void bender_copy_callback (GtkWidget *,
+ gpointer);
+static void bender_copy_inv_callback (GtkWidget *,
+ gpointer);
+static void bender_swap_callback (GtkWidget *,
+ gpointer);
+static void bender_response (GtkWidget *,
+ gint,
+ BenderDialog *);
+static void bender_smoothing_callback (GtkWidget *,
+ gpointer);
+static void bender_antialias_callback (GtkWidget *,
+ gpointer);
+static void bender_work_on_copy_callback (GtkWidget *,
+ gpointer);
+static void bender_preview_update (GtkWidget *,
+ gpointer);
+static void bender_preview_update_once (GtkWidget *,
+ gpointer);
+static void bender_load_callback (GtkWidget *,
+ BenderDialog *);
+static void bender_save_callback (GtkWidget *,
+ BenderDialog *);
+static gint bender_graph_events (GtkWidget *,
+ GdkEvent *,
+ BenderDialog *);
+static void bender_CR_compose (CRMatrix,
+ CRMatrix,
+ CRMatrix);
+static void bender_init_min_max (BenderDialog *,
+ gint32);
+static BenderDialog * do_dialog (gint32 drawable_id);
+static void p_init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ int shadow);
+static void p_end_gdrw (t_GDRW *gdrw);
+static gint32 p_main_bend (BenderDialog *cd,
+ guint32,
+ gint);
+static gint32 p_create_pv_image (gint32 src_drawable_id,
+ gint32 *layer_id);
+static void p_render_preview (BenderDialog *cd,
+ gint32 layer_id);
+static void p_get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void p_put_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void p_put_mix_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *color,
+ gint32 nb_curvy,
+ gint32 nb2_curvy,
+ gint32 curvy);
+static void p_stretch_curves (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax);
+static void p_cd_to_bval (BenderDialog *cd,
+ BenderValues *bval);
+static void p_cd_from_bval (BenderDialog *cd,
+ BenderValues *bval);
+static void p_store_values (BenderDialog *cd);
+static void p_retrieve_values (BenderDialog *cd);
+static void p_bender_calculate_iter_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax);
+static void p_delta_gdouble (double *val,
+ double val_from,
+ double val_to,
+ gint32 total_steps,
+ gdouble current_step);
+static void p_delta_gint32 (gint32 *val,
+ gint32 val_from,
+ gint32 val_to,
+ gint32 total_steps,
+ gdouble current_step);
+static void p_copy_points (BenderDialog *cd,
+ int outline,
+ int xy,
+ int argc,
+ gdouble *floatarray);
+static void p_copy_yval (BenderDialog *cd,
+ int outline,
+ int argc,
+ guint8 *int8array);
+static int p_save_pointfile (BenderDialog *cd,
+ const gchar *filename);
+
+
+/* Global Variables */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+static CRMatrix CR_basis =
+{
+ { -0.5, 1.5, -1.5, 0.5 },
+ { 1.0, -2.5, 2.0, -0.5 },
+ { -0.5, 0.0, 0.5, 0.0 },
+ { 0.0, 1.0, 0.0, 0.0 },
+};
+
+static int gb_debug = FALSE;
+
+/* Functions */
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX PDB_STUFF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+#ifdef ROTATE_OPTIMIZE
+
+/* ============================================================================
+ * p_pdb_procedure_available
+ * if requested procedure is available in the PDB return the number of args
+ * (0 upto n) that are needed to call the procedure.
+ * if not available return -1
+ * ============================================================================
+ */
+
+static gint
+p_pdb_procedure_available (const gchar *proc_name)
+{
+ gint nparams;
+ gint nreturn_vals;
+ GimpPDBProcType proc_type;
+ gchar *proc_blurb;
+ gchar *proc_help;
+ gchar *proc_author;
+ gchar *proc_copyright;
+ gchar *proc_date;
+ GimpParamDef *params;
+ GimpParamDef *return_vals;
+ gint rc;
+
+ rc = 0;
+
+ /* Query the gimp application's procedural database
+ * regarding a particular procedure.
+ */
+ if (gimp_procedural_db_proc_info (proc_name,
+ &proc_blurb,
+ &proc_help,
+ &proc_author,
+ &proc_copyright,
+ &proc_date,
+ &proc_type,
+ &nparams, &nreturn_vals,
+ &params, &return_vals))
+ {
+ /* procedure found in PDB */
+ return nparams;
+ }
+
+ g_printerr ("Warning: Procedure %s not found.\n", proc_name);
+ return -1;
+}
+
+#endif /* ROTATE_OPTIMIZE */
+
+static gint
+p_gimp_rotate (gint32 image_id,
+ gint32 drawable_id,
+ gint32 interpolation,
+ gdouble angle_deg)
+{
+ gdouble angle_rad;
+ gint rc;
+
+#ifdef ROTATE_OPTIMIZE
+ static gchar *rotate_proc = "plug-in-rotate";
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint32 angle_step;
+ gint nparams;
+
+ if (angle_deg == 90.0) { angle_step = 1; }
+ else if(angle_deg == 180.0) { angle_step = 2; }
+ else if(angle_deg == 270.0) { angle_step = 3; }
+ else { angle_step = 0; }
+
+ if (angle_step != 0)
+ {
+ nparams = p_pdb_procedure_available (rotate_proc);
+ if (nparams == 5)
+ {
+ /* use faster rotate plugin on multiples of 90 degrees */
+ return_vals = gimp_run_procedure (rotate_proc,
+ &nreturn_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_id,
+ GIMP_PDB_DRAWABLE, drawable_id,
+ GIMP_PDB_INT32, angle_step,
+ GIMP_PDB_INT32, FALSE, /* don't rotate the whole image */
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ return 0;
+ }
+ }
+ }
+#endif /* ROTATE_OPTIMIZE */
+
+ angle_rad = (angle_deg * G_PI) / 180.0;
+
+ gimp_context_push ();
+ if (! interpolation)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (GIMP_TRANSFORM_RESIZE_ADJUST);
+ rc = gimp_item_transform_rotate (drawable_id,
+ angle_rad,
+ TRUE /*auto_center*/,
+ -1.0 /*center_x*/,
+ -1.0 /*center_y*/);
+ gimp_context_pop ();
+
+ if (rc == -1)
+ g_printerr ("Error: gimp_drawable_transform_rotate_default call failed\n");
+
+ return rc;
+}
+
+/* ============================================================================
+ * p_if_selection_float_it
+ * ============================================================================
+ */
+
+static gint32
+p_if_selection_float_it (gint32 image_id,
+ gint32 layer_id)
+{
+ if (! gimp_layer_is_floating_sel (layer_id))
+ {
+ gint32 sel_channel_id;
+ gint32 x1, x2, y1, y2;
+ gint32 non_empty;
+
+ /* check and see if we have a selection mask */
+ sel_channel_id = gimp_image_get_selection (image_id);
+
+ gimp_selection_bounds (image_id, &non_empty, &x1, &y1, &x2, &y2);
+
+ if (non_empty && sel_channel_id >= 0)
+ {
+ /* selection is TRUE, make a layer (floating selection) from
+ the selection */
+ if (gimp_edit_copy (layer_id))
+ {
+ layer_id = gimp_edit_paste (layer_id, FALSE);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+
+ return layer_id;
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX END_PDB_STUFF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+/*
+ * M M AAAAA IIIIII N N
+ * M M M M A A II NN N
+ * M M M AAAAAAA II N N N
+ * M M A A II N NN
+ * M M A A IIIIII N N
+ */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (must be a layer without layermask)"},
+ { GIMP_PDB_FLOAT, "rotation", "Direction {angle 0 to 360 degree } of the bend effect"},
+ { GIMP_PDB_INT32, "smoothing", "Smoothing { TRUE, FALSE }"},
+ { GIMP_PDB_INT32, "antialias", "Antialias { TRUE, FALSE }"},
+ { GIMP_PDB_INT32, "work-on-copy", "{ TRUE, FALSE } TRUE: copy the drawable and bend the copy"},
+ { GIMP_PDB_INT32, "curve-type", " { 0, 1 } 0 == smooth (use 17 points), 1 == freehand (use 256 val_y) "},
+ { GIMP_PDB_INT32, "argc-upper-point-x", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "upper-point-x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-upper-point-y", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "upper-point-y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-lower_point-x", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "lower-point-x", "array of 17 x point_koords { 0.0 <= x <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-lower-point-y", "{2 <= argc <= 17} "},
+ { GIMP_PDB_FLOATARRAY, "lower_point_y", "array of 17 y point_koords { 0.0 <= y <= 1.0 or -1 for unused point }"},
+ { GIMP_PDB_INT32, "argc-upper-val-y", "{ 256 } "},
+ { GIMP_PDB_INT8ARRAY, "upper-val-y", "array of 256 y freehand koord { 0 <= y <= 255 }"},
+ { GIMP_PDB_INT32, "argc-lower-val-y", "{ 256 } "},
+ { GIMP_PDB_INT8ARRAY, "lower-val-y", "array of 256 y freehand koord { 0 <= y <= 255 }"}
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_LAYER, "bent-layer", "the handled layer" }
+ };
+
+ static const GimpParamDef args_iter[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_INT32, "total-steps", "total number of steps (# of layers-1 to apply the related plug-in)" },
+ { GIMP_PDB_FLOAT, "current-step", "current (for linear iterations this is the layerstack position, otherwise some value in between)" },
+ { GIMP_PDB_INT32, "len-struct", "length of stored data structure with id is equal to the plug_in proc_name" },
+ };
+
+ /* the actual installation of the bend plugin */
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Bend the image using two control curves"),
+ "This plug-in does bend the active layer "
+ "If there is a current selection it is copied to "
+ "floating selection and the curve_bend distortion "
+ "is done on the floating selection. If "
+ "work_on_copy parameter is TRUE, the curve_bend "
+ "distortion is done on a copy of the active layer "
+ "(or floating selection). The upper and lower edges "
+ "are bent in shape of 2 spline curves. both (upper "
+ "and lower) curves are determined by upto 17 points "
+ "or by 256 Y-Values if curve_type == 1 (freehand "
+ "mode) If rotation is not 0, the layer is rotated "
+ "before and rotated back after the bend operation. "
+ "This enables bending in other directions than "
+ "vertical. bending usually changes the size of "
+ "the handled layer. this plug-in sets the offsets "
+ "of the handled layer to keep its center at the "
+ "same position",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ N_("_Curve Bend..."),
+ PLUG_IN_IMAGE_TYPES,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
+
+ /* the installation of the Iterator procedure for the bend plugin */
+ gimp_install_procedure (PLUG_IN_ITER_NAME,
+ "This procedure calculates the modified values "
+ "for one iterationstep for the call of "
+ "plug_in_curve_bend",
+ "",
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ NULL, /* do not appear in menus */
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args_iter), 0,
+ args_iter, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ const gchar *env;
+ BenderDialog *cd;
+ gint32 active_drawable_id = -1;
+ gint32 image_id = -1;
+ gint32 layer_id = -1;
+ GError *error = NULL;
+
+ /* Get the runmode from the in-parameters */
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ /* status variable, use it to check for errors in invocation usually only
+ during non-interactive calling */
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ /*always return at least the status to the caller. */
+ static GimpParam values[2];
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ cd = NULL;
+
+ env = g_getenv ("BEND_DEBUG");
+ if (env != NULL)
+ {
+ if ((*env != 'n') && (*env != 'N')) gb_debug = 1;
+ }
+
+ if (gb_debug) g_printerr ("\n\nDEBUG: run %s\n", name);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ values[1].type = GIMP_PDB_LAYER;
+ values[1].data.d_int32 = -1;
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ if (strcmp (name, PLUG_IN_ITER_NAME) == 0)
+ {
+ gint32 len_struct;
+ gint32 total_steps;
+ gdouble current_step;
+ BenderValues bval; /* current values while iterating */
+ BenderValues bval_from, bval_to; /* start and end values */
+
+ /* Iterator procedure for animated calls is usually called from
+ * "plug_in_gap_layers_run_animfilter"
+ * (always run noninteractive)
+ */
+ if ((run_mode == GIMP_RUN_NONINTERACTIVE) && (nparams == 4))
+ {
+ total_steps = param[1].data.d_int32;
+ current_step = param[2].data.d_float;
+ len_struct = param[3].data.d_int32;
+
+ if (len_struct == sizeof (bval))
+ {
+ /* get _FROM and _TO data,
+ * This data was stored by plug_in_gap_layers_run_animfilter
+ */
+ gimp_get_data (PLUG_IN_DATA_ITER_FROM, &bval_from);
+ gimp_get_data (PLUG_IN_DATA_ITER_TO, &bval_to);
+ bval = bval_from;
+
+ p_delta_gdouble (&bval.rotation, bval_from.rotation,
+ bval_to.rotation, total_steps, current_step);
+ /* note: iteration of curve and points arrays would not
+ * give useful results. (there might be different
+ * number of points in the from/to bender values )
+ * the iteration is done later, (see
+ * p_bender_calculate_iter_curve) when the curve
+ * is calculated.
+ */
+
+ bval.total_steps = total_steps;
+ bval.current_step = current_step;
+
+ gimp_set_data (PLUG_IN_PROC, &bval, sizeof (bval));
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+ return;
+ }
+
+ /* get image and drawable */
+ image_id = param[1].data.d_int32;
+ layer_id = param[2].data.d_drawable;
+
+ if (! gimp_item_is_layer (layer_id))
+ {
+ g_set_error (&error, 0, 0, "%s",
+ _("Can operate on layers only "
+ "(but was called on channel or mask)."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* check for layermask */
+ if (gimp_layer_get_mask (layer_id) > 0)
+ {
+ g_set_error (&error, 0, 0, "%s",
+ _("Cannot operate on layers with masks."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* if there is a selection, make it the floating selection layer */
+ active_drawable_id = p_if_selection_float_it (image_id, layer_id);
+ if (active_drawable_id < 0)
+ {
+ /* could not float the selection because selection rectangle
+ * is completely empty return GIMP_PDB_EXECUTION_ERROR
+ */
+ g_set_error (&error, 0, 0, "%s",
+ _("Cannot operate on empty selections."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* how are we running today? */
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data from a previous run */
+ /* gimp_get_data (PLUG_IN_PROC, &g_bndvals); */
+
+ /* Get information from the dialog */
+ cd = do_dialog (active_drawable_id);
+ cd->show_progress = TRUE;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* check to see if invoked with the correct number of parameters */
+ if (nparams >= 20)
+ {
+ cd = g_new (BenderDialog, 1);
+ cd->run = TRUE;
+ cd->show_progress = TRUE;
+ cd->drawable_id = active_drawable_id;
+
+ cd->rotation = param[3].data.d_float;
+ cd->smoothing = param[4].data.d_int32 != 0;
+ cd->antialias = param[5].data.d_int32 != 0;
+ cd->work_on_copy = param[6].data.d_int32 != 0;
+ cd->curve_type = param[7].data.d_int32 != 0;
+
+ p_copy_points (cd, OUTLINE_UPPER, 0,
+ param[8].data.d_int32,
+ param[9].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_UPPER, 1,
+ param[10].data.d_int32,
+ param[11].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_LOWER, 0,
+ param[12].data.d_int32,
+ param[13].data.d_floatarray);
+ p_copy_points (cd, OUTLINE_LOWER, 1,
+ param[14].data.d_int32,
+ param[15].data.d_floatarray);
+
+ p_copy_yval (cd, OUTLINE_UPPER,
+ param[16].data.d_int32,
+ param[17].data.d_int8array);
+ p_copy_yval (cd, OUTLINE_LOWER,
+ param[18].data.d_int32,
+ param[19].data.d_int8array);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ cd = g_new (BenderDialog, 1);
+ cd->run = TRUE;
+ cd->show_progress = TRUE;
+ cd->drawable_id = active_drawable_id;
+ p_retrieve_values (cd); /* Possibly retrieve data from a previous run */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (! cd)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Run the main function */
+
+ if (cd->run)
+ {
+ gint32 bent_layer_id;
+
+ gimp_image_undo_group_start (image_id);
+
+ bent_layer_id = p_main_bend (cd, cd->drawable_id,
+ cd->work_on_copy);
+
+ gimp_image_undo_group_end (image_id);
+
+ /* Store variable states for next run */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ p_store_values (cd);
+ }
+
+ /* return the id of handled layer */
+ values[1].data.d_int32 = bent_layer_id;
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static int
+p_save_pointfile (BenderDialog *cd,
+ const gchar *filename)
+{
+ FILE *fp;
+ gint j;
+
+ fp = g_fopen(filename, "w+b");
+ if (! fp)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ fprintf (fp, "%s\n", KEY_POINTFILE);
+ fprintf (fp, "VERSION 1.0\n\n");
+
+ fprintf (fp, "# points for upper and lower smooth curve (0.0 <= pt <= 1.0)\n");
+ fprintf (fp, "# there are upto 17 points where unused points are set to -1\n");
+ fprintf (fp, "# UPPERX UPPERY LOWERX LOWERY\n");
+ fprintf (fp, "\n");
+
+ for(j = 0; j < 17; j++)
+ {
+ fprintf (fp, "%s %+.6f %+.6f %+.6f %+.6f\n", KEY_POINTS,
+ (float)cd->points[OUTLINE_UPPER][j][0],
+ (float)cd->points[OUTLINE_UPPER][j][1],
+ (float)cd->points[OUTLINE_LOWER][j][0],
+ (float)cd->points[OUTLINE_LOWER][j][1] );
+ }
+
+ fprintf (fp, "\n");
+ fprintf (fp, "# y values for upper/lower freehand curve (0 <= y <= 255) \n");
+ fprintf (fp, "# there must be exactly 256 y values \n");
+ fprintf (fp, "# UPPER_Y LOWER_Y\n");
+ fprintf (fp, "\n");
+
+ for (j = 0; j < 256; j++)
+ {
+ fprintf (fp, "%s %3d %3d\n", KEY_VAL_Y,
+ (int)cd->curve[OUTLINE_UPPER][j],
+ (int)cd->curve[OUTLINE_LOWER][j]);
+ }
+
+ fclose (fp);
+
+ return 0;
+}
+
+static int
+p_load_pointfile (BenderDialog *cd,
+ const gchar *filename)
+{
+ gint pi, ci, n, len;
+ FILE *fp;
+ char buff[2000];
+ float fux, fuy, flx, fly;
+ gint iuy, ily ;
+
+ fp = g_fopen(filename, "rb");
+ if (! fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ pi = 0;
+ ci = 0;
+
+ if (! fgets (buff, 2000 - 1, fp))
+ {
+ g_message (_("Error while reading '%s': %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ fclose (fp);
+ return -1;
+ }
+
+ if (strncmp (buff, KEY_POINTFILE, strlen(KEY_POINTFILE)) == 0)
+ {
+ while (NULL != fgets (buff, 2000-1, fp))
+ {
+ len = strlen(KEY_POINTS);
+
+ if (strncmp (buff, KEY_POINTS, len) == 0)
+ {
+ n = sscanf (&buff[len],
+ "%f %f %f %f", &fux, &fuy, &flx, &fly);
+
+ if ((n == 4) && (pi < 17))
+ {
+ cd->points[OUTLINE_UPPER][pi][0] = fux;
+ cd->points[OUTLINE_UPPER][pi][1] = fuy;
+ cd->points[OUTLINE_LOWER][pi][0] = flx;
+ cd->points[OUTLINE_LOWER][pi][1] = fly;
+ pi++;
+ }
+ else
+ {
+ g_printf("warning: BAD points[%d] in file %s are ignored\n", pi, filename);
+ }
+ }
+
+ len = strlen (KEY_VAL_Y);
+
+ if (strncmp (buff, KEY_VAL_Y, len) == 0)
+ {
+ n = sscanf (&buff[len], "%d %d", &iuy, &ily);
+
+ if ((n == 2) && (ci < 256))
+ {
+ cd->curve[OUTLINE_UPPER][ci] = iuy;
+ cd->curve[OUTLINE_LOWER][ci] = ily;
+ ci++;
+ }
+ else
+ {
+ g_printf("warning: BAD y_vals[%d] in file %s are ignored\n", ci, filename);
+ }
+ }
+ }
+ }
+
+ fclose (fp);
+
+ return 0;
+}
+
+static void
+p_cd_to_bval (BenderDialog *cd,
+ BenderValues *bval)
+{
+ gint i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ bval->curve[i][j] = cd->curve[i][j];
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ bval->points[i][j][0] = cd->points[i][j][0]; /* x */
+ bval->points[i][j][1] = cd->points[i][j][1]; /* y */
+ }
+ }
+
+ bval->curve_type = cd->curve_type;
+ bval->smoothing = cd->smoothing;
+ bval->antialias = cd->antialias;
+ bval->work_on_copy = cd->work_on_copy;
+ bval->rotation = cd->rotation;
+
+ bval->total_steps = 0;
+ bval->current_step = 0.0;
+}
+
+static void
+p_cd_from_bval (BenderDialog *cd,
+ BenderValues *bval)
+{
+ gint i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ cd->curve[i][j] = bval->curve[i][j];
+ }
+
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[i][j][0] = bval->points[i][j][0]; /* x */
+ cd->points[i][j][1] = bval->points[i][j][1]; /* y */
+ }
+ }
+
+ cd->curve_type = bval->curve_type;
+ cd->smoothing = bval->smoothing;
+ cd->antialias = bval->antialias;
+ cd->work_on_copy = bval->work_on_copy;
+ cd->rotation = bval->rotation;
+}
+
+static void
+p_store_values (BenderDialog *cd)
+{
+ BenderValues bval;
+
+ p_cd_to_bval (cd, &bval);
+ gimp_set_data (PLUG_IN_PROC, &bval, sizeof (bval));
+}
+
+static void
+p_retrieve_values (BenderDialog *cd)
+{
+ BenderValues bval;
+
+ bval.total_steps = 0;
+ bval.current_step = -444.4; /* init with an invalid dummy value */
+
+ gimp_get_data (PLUG_IN_PROC, &bval);
+
+ if (bval.total_steps == 0)
+ {
+ cd->bval_from = NULL;
+ cd->bval_to = NULL;
+ if(bval.current_step != -444.4)
+ {
+ /* last_value data was retrieved (and dummy value was overwritten) */
+ p_cd_from_bval(cd, &bval);
+ }
+ }
+ else
+ {
+ cd->bval_from = g_new (BenderValues, 1);
+ cd->bval_to = g_new (BenderValues, 1);
+ cd->bval_curr = g_new (BenderValues, 1);
+ *cd->bval_curr = bval;
+
+ /* it seems that we are called from GAP with "Varying Values" */
+ gimp_get_data(PLUG_IN_DATA_ITER_FROM, cd->bval_from);
+ gimp_get_data(PLUG_IN_DATA_ITER_TO, cd->bval_to);
+ *cd->bval_curr = bval;
+ p_cd_from_bval(cd, cd->bval_curr);
+ cd->work_on_copy = FALSE;
+ }
+}
+
+static void
+p_delta_gdouble (double *val,
+ double val_from,
+ double val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ double delta;
+
+ if (total_steps < 1)
+ return;
+
+ delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+static void
+p_delta_gint32 (gint32 *val,
+ gint32 val_from,
+ gint32 val_to,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ double delta;
+
+ if (total_steps < 1)
+ return;
+
+ delta = ((double)(val_to - val_from) / (double)total_steps) * ((double)total_steps - current_step);
+ *val = val_from + delta;
+}
+
+static void
+p_copy_points (BenderDialog *cd,
+ int outline,
+ int xy,
+ int argc,
+ gdouble *floatarray)
+{
+ int j;
+
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[outline][j][xy] = -1;
+ }
+
+ for (j = 0; j < argc; j++)
+ {
+ cd->points[outline][j][xy] = floatarray[j];
+ }
+}
+
+static void
+p_copy_yval (BenderDialog *cd,
+ int outline,
+ int argc,
+ guint8 *int8array)
+{
+ int j;
+ guchar fill;
+
+ fill = MIDDLE;
+
+ for (j = 0; j < 256; j++)
+ {
+ if (j < argc)
+ {
+ fill = cd->curve[outline][j] = int8array[j];
+ }
+ else
+ {
+ cd->curve[outline][j] = fill;
+ }
+ }
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+/* curves machinery */
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+
+static BenderDialog *
+do_dialog (gint32 drawable_id)
+{
+ BenderDialog *cd;
+
+ /* Init GTK */
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /* The curve_bend dialog */
+ cd = bender_new_dialog (drawable_id);
+
+ /* create temporary image (with a small copy of drawable) for the preview */
+ cd->preview_image_id = p_create_pv_image (drawable_id,
+ &cd->preview_layer_id1);
+ cd->preview_layer_id2 = -1;
+
+ if (! gtk_widget_get_visible (cd->shell))
+ gtk_widget_show (cd->shell);
+
+ bender_update (cd, UP_GRAPH | UP_DRAW | UP_PREVIEW_EXPOSE);
+
+ gtk_main ();
+ gdk_flush ();
+
+ gimp_image_delete(cd->preview_image_id);
+ cd->preview_image_id = -1;
+ cd->preview_layer_id1 = -1;
+ cd->preview_layer_id2 = -1;
+
+ return cd;
+}
+
+/**************************/
+/* Select Curves dialog */
+/**************************/
+
+static BenderDialog *
+bender_new_dialog (gint32 drawable_id)
+{
+ BenderDialog *cd;
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *vbox2;
+ GtkWidget *abox;
+ GtkWidget *frame;
+ GtkWidget *upper, *lower;
+ GtkWidget *smooth, *freew;
+ GtkWidget *toggle;
+ GtkWidget *button;
+ GtkWidget *spinbutton;
+ GtkWidget *label;
+ GdkDisplay *display;
+ gint i, j;
+
+ cd = g_new (BenderDialog, 1);
+
+ cd->preview = FALSE;
+ cd->curve_type = SMOOTH;
+ cd->pixmap = NULL;
+ cd->filechooser = NULL;
+ cd->outline = OUTLINE_UPPER;
+ cd->show_progress = FALSE;
+ cd->smoothing = TRUE;
+ cd->antialias = TRUE;
+ cd->work_on_copy = FALSE;
+ cd->rotation = 0.0; /* vertical bend */
+
+ cd->drawable_id = drawable_id;
+
+ cd->color = gimp_drawable_is_rgb (drawable_id);
+
+ cd->run = FALSE;
+ cd->bval_from = NULL;
+ cd->bval_to = NULL;
+ cd->bval_curr = NULL;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 256; j++)
+ cd->curve[i][j] = MIDDLE;
+
+ cd->grab_point = -1;
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 17; j++)
+ {
+ cd->points[i][j][0] = -1;
+ cd->points[i][j][1] = -1;
+ }
+ cd->points[i][0][0] = 0.0; /* x */
+ cd->points[i][0][1] = 0.5; /* y */
+ cd->points[i][16][0] = 1.0; /* x */
+ cd->points[i][16][1] = 0.5; /* y */
+ }
+
+ p_retrieve_values(cd); /* Possibly retrieve data from a previous run */
+
+ /* The shell and main vbox */
+ cd->shell = gimp_dialog_new (_("Curve Bend"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (cd->shell),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (cd->shell));
+
+ g_signal_connect (cd->shell, "response",
+ G_CALLBACK (bender_response),
+ cd);
+
+ /* busy cursor */
+ display = gtk_widget_get_display (cd->shell);
+ cd->cursor_busy = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+ /* The main hbox */
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (cd->shell))),
+ main_hbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_hbox);
+
+ /* Left side column */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* Preview area, top of column */
+ frame = gimp_frame_new (_("Preview"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_widget_show (vbox2);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox2), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ /* The range drawing area */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ cd->pv_widget = gimp_preview_area_new ();
+ gtk_widget_set_size_request (cd->pv_widget,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), cd->pv_widget);
+ gtk_widget_show (cd->pv_widget);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_end (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The preview button */
+ button = gtk_button_new_with_mnemonic (_("_Preview Once"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_preview_update_once),
+ cd);
+
+ /* The preview toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Automatic pre_view"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->preview);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_preview_update),
+ cd);
+
+ /* Options area, bottom of column */
+ frame = gimp_frame_new (_("Options"));
+ gtk_box_pack_end (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Render Options */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Rotate spinbutton */
+ label = gtk_label_new_with_mnemonic (_("Rotat_e:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ cd->rotate_data = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0.0, 360.0, 1, 45, 0));
+ gtk_adjustment_set_value (cd->rotate_data, cd->rotation);
+
+ spinbutton = gimp_spin_button_new (cd->rotate_data, 0.5, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+
+ g_signal_connect (cd->rotate_data, "value-changed",
+ G_CALLBACK (bender_rotate_adj_callback),
+ cd);
+
+ /* The smoothing toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Smoo_thing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->smoothing);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_smoothing_callback),
+ cd);
+
+ /* The antialiasing toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Antialiasing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->antialias);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_antialias_callback),
+ cd);
+
+ /* The work_on_copy toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("Work on cop_y"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), cd->work_on_copy);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (bender_work_on_copy_callback),
+ cd);
+
+ /* The curves graph */
+ frame = gimp_frame_new (_("Modify Curves"));
+ gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ cd->graph = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (cd->graph,
+ GRAPH_WIDTH + RADIUS * 2,
+ GRAPH_HEIGHT + RADIUS * 2);
+ gtk_widget_set_events (cd->graph, GRAPH_MASK);
+ gtk_container_add (GTK_CONTAINER (abox), cd->graph);
+ gtk_widget_show (cd->graph);
+
+ g_signal_connect (cd->graph, "event",
+ G_CALLBACK (bender_graph_events),
+ cd);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Curve for Border"),
+ G_CALLBACK (bender_border_callback),
+ &cd->outline, cd->outline,
+
+ C_("curve-border", "_Upper"), OUTLINE_UPPER, &upper,
+ C_("curve-border", "_Lower"), OUTLINE_LOWER, &lower,
+
+ NULL);
+
+ g_object_set_data (G_OBJECT (upper), "cd", cd);
+ g_object_set_data (G_OBJECT (lower), "cd", cd);
+
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Curve Type"),
+ G_CALLBACK (bender_type_callback),
+ &cd->curve_type, cd->curve_type,
+
+ _("Smoot_h"), SMOOTH, &smooth,
+ _("_Free"), GFREE, &freew,
+
+ NULL);
+ g_object_set_data (G_OBJECT (smooth), "cd", cd);
+ g_object_set_data (G_OBJECT (freew), "cd", cd);
+
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ /* hbox for curve options */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The Copy button */
+ button = gtk_button_new_with_mnemonic (_("_Copy"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Copy the active curve to the other border"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_copy_callback),
+ cd);
+
+ /* The CopyInv button */
+ button = gtk_button_new_with_mnemonic (_("_Mirror"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Mirror the active curve to the other border"),
+ NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_copy_inv_callback),
+ cd);
+
+ /* The Swap button */
+ button = gtk_button_new_with_mnemonic (_("S_wap"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Swap the two curves"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_swap_callback),
+ cd);
+
+ /* The Reset button */
+ button = gtk_button_new_with_mnemonic (_("_Reset"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Reset the active curve"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_reset_callback),
+ cd);
+
+ /* hbox for curve load and save */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* The Load button */
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Load the curves from a file"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_load_callback),
+ cd);
+
+ /* The Save button */
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Save the curves to a file"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (bender_save_callback),
+ cd);
+
+ gtk_widget_show (main_hbox);
+
+ return cd;
+}
+
+static void
+bender_update (BenderDialog *cd,
+ int update)
+{
+ GtkStyle *graph_style = gtk_widget_get_style (cd->graph);
+ gint i;
+ gint other;
+
+ if (update & UP_PREVIEW)
+ {
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (cd->shell)),
+ cd->cursor_busy);
+ gdk_flush ();
+
+ if (cd->preview_layer_id2 >= 0)
+ gimp_image_remove_layer(cd->preview_image_id, cd->preview_layer_id2);
+
+ cd->preview_layer_id2 = p_main_bend(cd, cd->preview_layer_id1, TRUE /* work_on_copy*/ );
+ p_render_preview(cd, cd->preview_layer_id2);
+
+ if (update & UP_DRAW)
+ gtk_widget_queue_draw (cd->pv_widget);
+
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (cd->shell)),
+ NULL);
+ }
+ if (update & UP_PREVIEW_EXPOSE)
+ {
+ /* on expose just redraw cd->preview_layer_id2
+ * that holds the bent version of the preview (if there is one)
+ */
+ if (cd->preview_layer_id2 < 0)
+ cd->preview_layer_id2 = p_main_bend(cd, cd->preview_layer_id1, TRUE /* work_on_copy*/ );
+ p_render_preview(cd, cd->preview_layer_id2);
+
+ if (update & UP_DRAW)
+ gtk_widget_queue_draw (cd->pv_widget);
+ }
+ if ((update & UP_GRAPH) && (update & UP_DRAW) && cd->pixmap != NULL)
+ {
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window (cd->graph));
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_translate (cr, 0.5, 0.5);
+
+ /* Clear the background */
+ gdk_cairo_set_source_color (cr, &graph_style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ /* Draw the grid lines */
+ for (i = 0; i < 5; i++)
+ {
+ cairo_move_to (cr, RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
+ cairo_line_to (cr, GRAPH_WIDTH + RADIUS, i * (GRAPH_HEIGHT / 4) + RADIUS);
+
+ cairo_move_to (cr, i * (GRAPH_WIDTH / 4) + RADIUS, RADIUS);
+ cairo_line_to (cr, i * (GRAPH_WIDTH / 4) + RADIUS, GRAPH_HEIGHT + RADIUS);
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->dark[GTK_STATE_NORMAL]);
+ cairo_stroke (cr);
+
+ /* Draw the other curve */
+ other = (cd->outline == 0) ? 1 : 0;
+
+ cairo_move_to (cr, RADIUS, 255 - cd->curve[other][0] + RADIUS);
+
+ for (i = 1; i < 256; i++)
+ {
+ cairo_line_to (cr, i + RADIUS, 255 - cd->curve[other][i] + RADIUS);
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->dark[GTK_STATE_NORMAL]);
+ cairo_stroke (cr);
+
+ /* Draw the active curve */
+ cairo_move_to (cr, RADIUS, 255 - cd->curve[cd->outline][0] + RADIUS);
+
+ for (i = 1; i < 256; i++)
+ {
+ cairo_line_to (cr, i + RADIUS, 255 - cd->curve[cd->outline][i] + RADIUS);
+ }
+
+ /* Draw the points */
+ if (cd->curve_type == SMOOTH)
+ {
+ for (i = 0; i < 17; i++)
+ {
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ (cd->points[cd->outline][i][0] * 255.0) + RADIUS,
+ 255 - (cd->points[cd->outline][i][1] * 255.0) + RADIUS,
+ RADIUS,
+ 0, 2 * G_PI);
+ }
+ }
+ }
+
+ gdk_cairo_set_source_color (cr, &graph_style->black);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+ }
+}
+
+static void
+bender_plot_curve (BenderDialog *cd,
+ int p1,
+ int p2,
+ int p3,
+ int p4,
+ gint32 xmax,
+ gint32 ymax,
+ gint fix255)
+{
+ CRMatrix geometry;
+ CRMatrix tmp1, tmp2;
+ CRMatrix deltas;
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d, d2, d3;
+ int lastx, lasty;
+ gint32 newx, newy;
+ gint32 ntimes;
+ gint32 i;
+
+ /* construct the geometry matrix from the segment */
+ for (i = 0; i < 4; i++)
+ {
+ geometry[i][2] = 0;
+ geometry[i][3] = 0;
+ }
+
+ geometry[0][0] = (cd->points[cd->outline][p1][0] * xmax);
+ geometry[1][0] = (cd->points[cd->outline][p2][0] * xmax);
+ geometry[2][0] = (cd->points[cd->outline][p3][0] * xmax);
+ geometry[3][0] = (cd->points[cd->outline][p4][0] * xmax);
+
+ geometry[0][1] = (cd->points[cd->outline][p1][1] * ymax);
+ geometry[1][1] = (cd->points[cd->outline][p2][1] * ymax);
+ geometry[2][1] = (cd->points[cd->outline][p3][1] * ymax);
+ geometry[3][1] = (cd->points[cd->outline][p4][1] * ymax);
+
+ /* subdivide the curve ntimes (1000) times */
+ ntimes = 4 * xmax;
+ /* ntimes can be adjusted to give a finer or coarser curve */
+ d = 1.0 / ntimes;
+ d2 = d * d;
+ d3 = d * d * d;
+
+ /* construct a temporary matrix for determining the forward differencing deltas */
+ tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1;
+ tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d; tmp2[1][3] = 0;
+ tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0;
+ tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0;
+
+ /* compose the basis and geometry matrices */
+ bender_CR_compose (CR_basis, geometry, tmp1);
+
+ /* compose the above results to get the deltas matrix */
+ bender_CR_compose (tmp2, tmp1, deltas);
+
+ /* extract the x deltas */
+ x = deltas[0][0];
+ dx = deltas[1][0];
+ dx2 = deltas[2][0];
+ dx3 = deltas[3][0];
+
+ /* extract the y deltas */
+ y = deltas[0][1];
+ dy = deltas[1][1];
+ dy2 = deltas[2][1];
+ dy3 = deltas[3][1];
+
+ lastx = CLAMP (x, 0, xmax);
+ lasty = CLAMP (y, 0, ymax);
+
+
+ if (fix255)
+ {
+ cd->curve[cd->outline][lastx] = lasty;
+ }
+ else
+ {
+ cd->curve_ptr[cd->outline][lastx] = lasty;
+ if(gb_debug) g_printf("bender_plot_curve xmax:%d ymax:%d\n",
+ (int)xmax, (int)ymax);
+ }
+
+ /* loop over the curve */
+ for (i = 0; i < ntimes; i++)
+ {
+ /* increment the x values */
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ /* increment the y values */
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP ((ROUND (x)), 0, xmax);
+ newy = CLAMP ((ROUND (y)), 0, ymax);
+
+ /* if this point is different than the last one...then draw it */
+ if ((lastx != newx) || (lasty != newy))
+ {
+ if (fix255)
+ {
+ /* use fixed array size (for the curve graph) */
+ cd->curve[cd->outline][newx] = newy;
+ }
+ else
+ {
+ /* use dynamic allocated curve_ptr (for the real curve) */
+ cd->curve_ptr[cd->outline][newx] = newy;
+
+ if(gb_debug) g_printf("outline: %d cX: %d cY: %d\n",
+ (int)cd->outline, (int)newx, (int)newy);
+ }
+ }
+
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+static void
+bender_calculate_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax,
+ gint fix255)
+{
+ int i;
+ int points[17];
+ int num_pts;
+ int p1, p2, p3, p4;
+ int xmid;
+ int yfirst, ylast;
+
+ switch (cd->curve_type)
+ {
+ case GFREE:
+ break;
+
+ case SMOOTH:
+ /* cycle through the curves */
+ num_pts = 0;
+ for (i = 0; i < 17; i++)
+ if (cd->points[cd->outline][i][0] != -1)
+ points[num_pts++] = i;
+
+ xmid = xmax / 2;
+ /* Initialize boundary curve points */
+ if (num_pts != 0)
+ {
+ if (fix255)
+ {
+ for (i = 0; i < (cd->points[cd->outline][points[0]][0] * 255); i++)
+ cd->curve[cd->outline][i] =
+ (cd->points[cd->outline][points[0]][1] * 255);
+
+ for (i = (cd->points[cd->outline][points[num_pts - 1]][0] * 255); i < 256; i++)
+ cd->curve[cd->outline][i] =
+ (cd->points[cd->outline][points[num_pts - 1]][1] * 255);
+ }
+ else
+ {
+ yfirst = cd->points[cd->outline][points[0]][1] * ymax;
+ ylast = cd->points[cd->outline][points[num_pts - 1]][1] * ymax;
+
+ for (i = 0; i < xmid; i++)
+ {
+ cd->curve_ptr[cd->outline][i] = yfirst;
+ }
+
+ for (i = xmid; i <= xmax; i++)
+ {
+ cd->curve_ptr[cd->outline][i] = ylast;
+ }
+ }
+ }
+
+ for (i = 0; i < num_pts - 1; i++)
+ {
+ p1 = (i == 0) ? points[i] : points[(i - 1)];
+ p2 = points[i];
+ p3 = points[(i + 1)];
+ p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)];
+
+ bender_plot_curve (cd, p1, p2, p3, p4, xmax, ymax, fix255);
+ }
+ break;
+ }
+}
+
+static void
+bender_rotate_adj_callback (GtkAdjustment *adjustment,
+ gpointer client_data)
+{
+ BenderDialog *cd = client_data;
+
+ if (gtk_adjustment_get_value (adjustment) != cd->rotation)
+ {
+ cd->rotation = gtk_adjustment_get_value (adjustment);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ }
+}
+
+static void
+bender_border_callback (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd;
+
+ gimp_radio_button_update (widget, data);
+ cd = g_object_get_data (G_OBJECT (widget), "cd");
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+}
+
+static void
+bender_type_callback (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd;
+
+ gimp_radio_button_update (widget, data);
+
+ cd = g_object_get_data (G_OBJECT (widget), "cd");
+ if (! cd)
+ return;
+
+ if (cd->curve_type == SMOOTH)
+ {
+ gint i;
+
+ /* pick representative points from the curve and make them control points */
+ for (i = 0; i <= 8; i++)
+ {
+ gint index = CLAMP ((i * 32), 0, 255);
+ cd->points[cd->outline][i * 2][0] = (gdouble)index / 255.0;
+ cd->points[cd->outline][i * 2][1] = (gdouble)cd->curve[cd->outline][index] / 255.0;
+ }
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ }
+ else
+ {
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+}
+
+static void
+bender_reset_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog *) client_data;
+ gint i;
+
+ /* Initialize the values */
+ for (i = 0; i < 256; i++)
+ cd->curve[cd->outline][i] = MIDDLE;
+
+ cd->grab_point = -1;
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[cd->outline][i][0] = -1;
+ cd->points[cd->outline][i][1] = -1;
+ }
+ cd->points[cd->outline][0][0] = 0.0; /* x */
+ cd->points[cd->outline][0][1] = 0.5; /* y */
+ cd->points[cd->outline][16][0] = 1.0; /* x */
+ cd->points[cd->outline][16][1] = 0.5; /* y */
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_copy_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog *) client_data;
+ int i;
+ int other;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[other][i][0] = cd->points[cd->outline][i][0];
+ cd->points[other][i][1] = cd->points[cd->outline][i][1];
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ cd->curve[other][i] = cd->curve[cd->outline][i];
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_copy_inv_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+ BenderDialog *cd = (BenderDialog*) client_data;
+ int i;
+ int other;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ cd->points[other][i][0] = cd->points[cd->outline][i][0]; /* x */
+ cd->points[other][i][1] = 1.0 - cd->points[cd->outline][i][1]; /* y */
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ cd->curve[other][i] = 255 - cd->curve[cd->outline][i];
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+
+static void
+bender_swap_callback (GtkWidget *widget,
+ gpointer client_data)
+{
+#define SWAP_VALUE(a, b, h) { h=a; a=b; b=h; }
+ BenderDialog *cd = (BenderDialog*) client_data;
+ int i;
+ int other;
+ gdouble hd;
+ guchar hu;
+
+ other = (cd->outline) ? 0 : 1;
+
+ for (i = 0; i < 17; i++)
+ {
+ SWAP_VALUE(cd->points[other][i][0], cd->points[cd->outline][i][0], hd); /* x */
+ SWAP_VALUE(cd->points[other][i][1], cd->points[cd->outline][i][1], hd); /* y */
+ }
+
+ for (i= 0; i < 256; i++)
+ {
+ SWAP_VALUE(cd->curve[other][i], cd->curve[cd->outline][i], hu);
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_response (GtkWidget *widget,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ cd->run = TRUE;
+
+ gtk_widget_destroy (GTK_WIDGET (cd->shell));
+ gtk_main_quit ();
+}
+
+static void
+bender_preview_update (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->preview = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if(cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_preview_update_once (GtkWidget *widget,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+p_points_save_to_file_response (GtkWidget *dialog,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ p_save_pointfile (cd, filename);
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+p_points_load_from_file_response (GtkWidget *dialog,
+ gint response_id,
+ BenderDialog *cd)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ p_load_pointfile (cd, filename);
+ bender_update (cd, UP_ALL);
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+bender_load_callback (GtkWidget *w,
+ BenderDialog *cd)
+{
+ if (! cd->filechooser)
+ {
+ cd->filechooser =
+ gtk_file_chooser_dialog_new (_("Load Curve Points from File"),
+ GTK_WINDOW (gtk_widget_get_toplevel (w)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (cd->filechooser),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (cd->filechooser),
+ GTK_RESPONSE_OK);
+
+ g_signal_connect (cd->filechooser, "response",
+ G_CALLBACK (p_points_load_from_file_response),
+ cd);
+ g_signal_connect (cd->filechooser, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &cd->filechooser);
+ }
+
+ gtk_window_present (GTK_WINDOW (cd->filechooser));
+}
+
+static void
+bender_save_callback (GtkWidget *w,
+ BenderDialog *cd)
+{
+ if (! cd->filechooser)
+ {
+ cd->filechooser =
+ gtk_file_chooser_dialog_new (_("Save Curve Points to File"),
+ GTK_WINDOW (gtk_widget_get_toplevel (w)),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ g_signal_connect (cd->filechooser, "response",
+ G_CALLBACK (p_points_save_to_file_response),
+ cd);
+ g_signal_connect (cd->filechooser, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &cd->filechooser);
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (cd->filechooser),
+ "curve_bend.points");
+ }
+
+ gtk_window_present (GTK_WINDOW (cd->filechooser));
+}
+
+static void
+bender_smoothing_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->smoothing = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+
+ if(cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_antialias_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->antialias = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+}
+
+static void
+bender_work_on_copy_callback (GtkWidget *w,
+ gpointer data)
+{
+ BenderDialog *cd = (BenderDialog*) data;
+
+ cd->work_on_copy = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
+}
+
+static gboolean
+bender_graph_events (GtkWidget *widget,
+ GdkEvent *event,
+ BenderDialog *cd)
+{
+ static GdkCursorType cursor_type = GDK_TOP_LEFT_ARROW;
+ GdkCursorType new_type;
+ GdkEventMotion *mevent;
+ int i;
+ int tx, ty;
+ int x, y;
+ int closest_point;
+ int distance;
+ int x1, x2, y1, y2;
+
+ new_type = GDK_X_CURSOR;
+ closest_point = 0;
+
+ /* get the pointer position */
+ gdk_window_get_pointer (gtk_widget_get_window (cd->graph), &tx, &ty, NULL);
+ x = CLAMP ((tx - RADIUS), 0, 255);
+ y = CLAMP ((ty - RADIUS), 0, 255);
+
+ distance = G_MAXINT;
+ for (i = 0; i < 17; i++)
+ {
+ if (cd->points[cd->outline][i][0] != -1)
+ if (abs ((int) (x - (cd->points[cd->outline][i][0] * 255.0))) < distance)
+ {
+ distance = abs ((int) (x - (cd->points[cd->outline][i][0] * 255.0)));
+ closest_point = i;
+ }
+ }
+ if (distance > MIN_DISTANCE)
+ closest_point = (x + 8) / 16;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (cd->pixmap == NULL)
+ cd->pixmap = gdk_pixmap_new (gtk_widget_get_window (cd->graph),
+ GRAPH_WIDTH + RADIUS * 2,
+ GRAPH_HEIGHT + RADIUS * 2, -1);
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ new_type = GDK_TCROSS;
+
+ switch (cd->curve_type)
+ {
+ case SMOOTH:
+ /* determine the leftmost and rightmost points */
+ cd->leftmost = -1;
+ for (i = closest_point - 1; i >= 0; i--)
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cd->leftmost = (cd->points[cd->outline][i][0] * 255.0);
+ break;
+ }
+ cd->rightmost = 256;
+ for (i = closest_point + 1; i < 17; i++)
+ if (cd->points[cd->outline][i][0] != -1)
+ {
+ cd->rightmost = (cd->points[cd->outline][i][0] * 255.0);
+ break;
+ }
+
+ cd->grab_point = closest_point;
+ cd->points[cd->outline][cd->grab_point][0] = (gdouble)x / 255.0;
+ cd->points[cd->outline][cd->grab_point][1] = (gdouble)(255 - y) / 255.0;
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ break;
+
+ case GFREE:
+ cd->curve[cd->outline][x] = 255 - y;
+ cd->grab_point = x;
+ cd->last = y;
+ break;
+ }
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ new_type = GDK_FLEUR;
+ cd->grab_point = -1;
+
+ if (cd->preview)
+ bender_update (cd, UP_PREVIEW | UP_DRAW);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = (GdkEventMotion *) event;
+
+ if (mevent->is_hint)
+ {
+ mevent->x = tx;
+ mevent->y = ty;
+ }
+
+ switch (cd->curve_type)
+ {
+ case SMOOTH:
+ /* If no point is grabbed... */
+ if (cd->grab_point == -1)
+ {
+ if (cd->points[cd->outline][closest_point][0] != -1)
+ new_type = GDK_FLEUR;
+ else
+ new_type = GDK_TCROSS;
+ }
+ /* Else, drag the grabbed point */
+ else
+ {
+ new_type = GDK_TCROSS;
+
+ cd->points[cd->outline][cd->grab_point][0] = -1;
+
+ if (x > cd->leftmost && x < cd->rightmost)
+ {
+ closest_point = (x + 8) / 16;
+ if (cd->points[cd->outline][closest_point][0] == -1)
+ cd->grab_point = closest_point;
+ cd->points[cd->outline][cd->grab_point][0] = (gdouble)x / 255.0;
+ cd->points[cd->outline][cd->grab_point][1] = (gdouble)(255 - y) / 255.0;
+ }
+
+ bender_calculate_curve (cd, 255, 255, TRUE);
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+ break;
+
+ case GFREE:
+ if (cd->grab_point != -1)
+ {
+ if (cd->grab_point > x)
+ {
+ x1 = x;
+ x2 = cd->grab_point;
+ y1 = y;
+ y2 = cd->last;
+ }
+ else
+ {
+ x1 = cd->grab_point;
+ x2 = x;
+ y1 = cd->last;
+ y2 = y;
+ }
+
+ if (x2 != x1)
+ for (i = x1; i <= x2; i++)
+ cd->curve[cd->outline][i] = 255 - (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
+ else
+ cd->curve[cd->outline][x] = 255 - y;
+
+ cd->grab_point = x;
+ cd->last = y;
+
+ bender_update (cd, UP_GRAPH | UP_DRAW);
+ }
+
+ if (mevent->state & GDK_BUTTON1_MASK)
+ new_type = GDK_TCROSS;
+ else
+ new_type = GDK_PENCIL;
+ break;
+ }
+
+ if (new_type != cursor_type)
+ {
+ cursor_type = new_type;
+ /* change_win_cursor (gtk_widget_get_window (cd->graph), cursor_type); */
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+bender_CR_compose (CRMatrix a,
+ CRMatrix b,
+ CRMatrix ab)
+{
+ gint i, j;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ ab[i][j] = (a[i][0] * b[0][j] +
+ a[i][1] * b[1][j] +
+ a[i][2] * b[2][j] +
+ a[i][3] * b[3][j]);
+ }
+ }
+}
+
+static void
+p_render_preview (BenderDialog *cd,
+ gint32 layer_id)
+{
+ guchar pixel[4];
+ guchar *buf, *ptr;
+ gint x, y;
+ gint ofx, ofy;
+ gint width, height;
+ t_GDRW l_gdrw;
+ t_GDRW *gdrw;
+
+ width = gimp_drawable_width (layer_id);
+ height = gimp_drawable_height (layer_id);
+
+ ptr = buf = g_new (guchar, PREVIEW_BPP * PREVIEW_SIZE_X * PREVIEW_SIZE_Y);
+ gdrw = &l_gdrw;
+ p_init_gdrw(gdrw, layer_id, FALSE);
+
+ /* offsets to set bend layer to preview center */
+ ofx = (width / 2) - (PREVIEW_SIZE_X / 2);
+ ofy = (height / 2) - (PREVIEW_SIZE_Y / 2);
+
+ /* render preview */
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ p_get_pixel (gdrw, x + ofx, y + ofy, &pixel[0]);
+
+ if (cd->color)
+ {
+ ptr[0] = pixel[0];
+ ptr[1] = pixel[1];
+ ptr[2] = pixel[2];
+ }
+ else
+ {
+ ptr[0] = pixel[0];
+ ptr[1] = pixel[0];
+ ptr[2] = pixel[0];
+ }
+
+ ptr[3] = pixel[gdrw->index_alpha];
+
+ ptr += PREVIEW_BPP;
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (cd->pv_widget),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGBA_IMAGE,
+ buf,
+ PREVIEW_BPP * PREVIEW_SIZE_X);
+ g_free (buf);
+
+ p_end_gdrw(gdrw);
+}
+
+/* ===================================================== */
+/* curve_bend worker procedures */
+/* ===================================================== */
+
+static void
+p_stretch_curves (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax)
+{
+ gint32 x1, x2;
+ gdouble ya, yb;
+ gdouble rest;
+ int outline;
+
+ for (outline = 0; outline < 2; outline++)
+ {
+ for(x1 = 0; x1 <= xmax; x1++)
+ {
+ x2 = (x1 * 255) / xmax;
+
+ if ((xmax <= 255) && (x2 < 255))
+ {
+ cd->curve_ptr[outline][x1] =
+ ROUND ((cd->curve[outline][x2] * ymax) / 255);
+ }
+ else
+ {
+ /* interpolate */
+ rest = (((gdouble)x1 * 255.0) / (gdouble)xmax) - x2;
+ ya = cd->curve[outline][x2]; /* y of this point */
+ yb = cd->curve[outline][x2 +1]; /* y of next point */
+
+ cd->curve_ptr[outline][x1] =
+ ROUND (((ya + ((yb -ya) * rest)) * ymax) / 255);
+ }
+ }
+ }
+}
+
+static void
+bender_init_min_max (BenderDialog *cd,
+ gint32 xmax)
+{
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ {
+ cd->min2[i] = 65000;
+ cd->max2[i] = 0;
+
+ for (j = 0; j <= xmax; j++)
+ {
+ if(cd->curve_ptr[i][j] > cd->max2[i])
+ {
+ cd->max2[i] = cd->curve_ptr[i][j];
+ }
+
+ if(cd->curve_ptr[i][j] < cd->min2[i])
+ {
+ cd->min2[i] = cd->curve_ptr[i][j];
+ }
+ }
+ }
+
+ /* for UPPER outline : y-zero line is assumed at the min leftmost or
+ * rightmost point
+ */
+ cd->zero2[OUTLINE_UPPER] = MIN (cd->curve_ptr[OUTLINE_UPPER][0],
+ cd->curve_ptr[OUTLINE_UPPER][xmax]);
+
+ /* for LOWER outline : y-zero line is assumed at the min leftmost or
+ * rightmost point
+ */
+ cd->zero2[OUTLINE_LOWER] = MAX (cd->curve_ptr[OUTLINE_LOWER][0],
+ cd->curve_ptr[OUTLINE_LOWER][xmax]);
+}
+
+static gint32
+p_curve_get_dy (BenderDialog *cd,
+ gint32 x,
+ gint32 drawable_width,
+ gint32 total_steps,
+ gdouble current_step)
+{
+ /* get y values of both upper and lower curve,
+ * and return the iterated value in between
+ */
+ gdouble y1, y2;
+ gdouble delta;
+
+ y1 = cd->zero2[OUTLINE_UPPER] - cd->curve_ptr[OUTLINE_UPPER][x];
+ y2 = cd->zero2[OUTLINE_LOWER] - cd->curve_ptr[OUTLINE_LOWER][x];
+
+ delta = ((double)(y2 - y1) / (double)(total_steps - 1)) * current_step;
+
+ return SIGNED_ROUND (y1 + delta);
+}
+
+static gint32
+p_upper_curve_extend (BenderDialog *cd,
+ gint32 drawable_width,
+ gint32 drawable_height)
+{
+ gint32 y1, y2;
+
+ y1 = cd->max2[OUTLINE_UPPER] - cd->zero2[OUTLINE_UPPER];
+ y2 = (cd->max2[OUTLINE_LOWER] - cd->zero2[OUTLINE_LOWER]) - drawable_height;
+
+ return MAX (y1, y2);
+}
+
+static gint32
+p_lower_curve_extend (BenderDialog *cd,
+ gint32 drawable_width,
+ gint32 drawable_height)
+{
+ gint32 y1, y2;
+
+ y1 = cd->zero2[OUTLINE_LOWER] - cd->min2[OUTLINE_LOWER];
+ y2 = (cd->zero2[OUTLINE_UPPER] - cd->min2[OUTLINE_UPPER]) - drawable_height;
+
+ return MAX (y1, y2);
+}
+
+static void
+p_end_gdrw (t_GDRW *gdrw)
+{
+ g_object_unref (gdrw->buffer);
+}
+
+static void
+p_init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ int shadow)
+{
+ gint w, h;
+
+ gdrw->drawable_id = drawable_id;
+
+ if (shadow)
+ gdrw->buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ else
+ gdrw->buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gdrw->width = gimp_drawable_width (gdrw->drawable_id);
+ gdrw->height = gimp_drawable_height (gdrw->drawable_id);
+
+ gdrw->tile_width = gimp_tile_width ();
+ gdrw->tile_height = gimp_tile_height ();
+
+ if (! gimp_drawable_mask_intersect (gdrw->drawable_id,
+ &gdrw->x1, &gdrw->y1, &w, &h))
+ {
+ w = 0;
+ h = 0;
+ }
+
+ gdrw->x2 = gdrw->x1 + w;
+ gdrw->y2 = gdrw->y1 + h;
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ gdrw->format = babl_format ("R'G'B'A u8");
+ else
+ gdrw->format = babl_format ("R'G'B' u8");
+
+ gdrw->bpp = babl_format_get_bytes_per_pixel (gdrw->format);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ {
+ /* index of the alpha channelbyte {1|3} */
+ gdrw->index_alpha = gdrw->bpp - 1;
+ }
+ else
+ {
+ gdrw->index_alpha = 0; /* there is no alpha channel */
+ }
+}
+
+/* get pixel value
+ * return light transparent black gray pixel if out of bounds
+ * (should occur in the previews only)
+ */
+static void
+p_get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ pixel[1] = 255;
+ pixel[3] = 255; /* simulate full visible alpha channel */
+
+ gegl_buffer_sample (gdrw->buffer, x, y, NULL, pixel, gdrw->format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+}
+
+static void
+p_put_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ gegl_buffer_set (gdrw->buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ gdrw->format, pixel, GEGL_AUTO_ROWSTRIDE);
+}
+
+static void
+p_put_mix_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *color,
+ gint32 nb_curvy,
+ gint32 nb2_curvy,
+ gint32 curvy)
+{
+ guchar pixel[4];
+ guchar mixmask;
+ gint idx;
+ gint diff;
+
+ mixmask = 255 - 96;
+ diff = abs(nb_curvy - curvy);
+
+ if (diff == 0)
+ {
+ mixmask = 255 - 48;
+ diff = abs(nb2_curvy - curvy);
+
+ if (diff == 0)
+ {
+ /* last 2 neighbours were not shifted against current pixel, do not mix */
+ p_put_pixel(gdrw, x, y, color);
+ return;
+ }
+ }
+
+ /* get left neighbour pixel */
+ p_get_pixel(gdrw, x-1, y, &pixel[0]);
+
+ if (pixel[gdrw->index_alpha] < 10)
+ {
+ /* neighbour is (nearly or full) transparent, do not mix */
+ p_put_pixel(gdrw, x, y, color);
+ return;
+ }
+
+ for (idx = 0; idx < gdrw->index_alpha ; idx++)
+ {
+ /* mix in left neighbour color */
+ pixel[idx] = MIX_CHANNEL(color[idx], pixel[idx], mixmask);
+ }
+
+ pixel[gdrw->index_alpha] = color[gdrw->index_alpha];
+ p_put_pixel(gdrw, x, y, &pixel[0]);
+}
+
+/* ============================================================================
+ * p_create_pv_image
+ * ============================================================================
+ */
+static gint32
+p_create_pv_image (gint32 src_drawable_id,
+ gint32 *layer_id)
+{
+ gint32 new_image_id;
+ guint new_width;
+ guint new_height;
+ GimpImageType type;
+ guint x, y;
+ double scale;
+ guchar pixel[4];
+ t_GDRW src_gdrw;
+ t_GDRW dst_gdrw;
+ gint src_width;
+ gint src_height;
+
+ src_width = gimp_drawable_width (src_drawable_id);
+ src_height = gimp_drawable_height (src_drawable_id);
+
+ new_image_id = gimp_image_new (PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ gimp_image_base_type (gimp_item_get_image (src_drawable_id)));
+ gimp_image_undo_disable (new_image_id);
+
+ type = gimp_drawable_type (src_drawable_id);
+ if (src_height > src_width)
+ {
+ new_height = PV_IMG_HEIGHT;
+ new_width = (src_width * new_height) / src_height;
+ scale = (float) src_height / PV_IMG_HEIGHT;
+ }
+ else
+ {
+ new_width = PV_IMG_WIDTH;
+ new_height = (src_height * new_width) / src_width;
+ scale = (float) src_width / PV_IMG_WIDTH;
+ }
+
+ *layer_id = gimp_layer_new(new_image_id, "preview_original",
+ new_width, new_height,
+ type,
+ 100.0, /* opacity */
+ 0); /* mode NORMAL */
+ if (! gimp_drawable_has_alpha(*layer_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha(*layer_id);
+ }
+
+ gimp_image_insert_layer(new_image_id, *layer_id, -1, 0);
+
+ p_init_gdrw (&src_gdrw, src_drawable_id, FALSE);
+ p_init_gdrw (&dst_gdrw, *layer_id, FALSE);
+
+ for (y = 0; y < new_height; y++)
+ {
+ for (x = 0; x < new_width; x++)
+ {
+ p_get_pixel(&src_gdrw, x * scale, y * scale, &pixel[0]);
+ p_put_pixel(&dst_gdrw, x, y, &pixel[0]);
+ }
+ }
+
+ p_end_gdrw (&src_gdrw);
+ p_end_gdrw (&dst_gdrw);
+
+ return new_image_id;
+}
+
+/* ============================================================================
+ * p_add_layer
+ * ============================================================================
+ */
+static gint32
+p_add_layer (gint width,
+ gint height,
+ gint32 src_drawable_id)
+{
+ GimpImageType type;
+ gint32 new_layer_id;
+ char *name;
+ char *name2;
+ gdouble opacity;
+ GimpLayerMode mode;
+ gint visible;
+ gint32 image_id;
+ gint stack_position;
+
+ image_id = gimp_item_get_image (src_drawable_id);
+ stack_position = 0; /* TODO: should be same as src_layer */
+
+ /* copy type, name, opacity and mode from src_drawable */
+ type = gimp_drawable_type (src_drawable_id);
+ visible = gimp_item_get_visible (src_drawable_id);
+
+ name2 = gimp_item_get_name (src_drawable_id);
+ name = g_strdup_printf ("%s_b", name2);
+ g_free (name2);
+
+ mode = gimp_layer_get_mode (src_drawable_id);
+ opacity = gimp_layer_get_opacity (src_drawable_id); /* full opacity */
+
+ new_layer_id = gimp_layer_new (image_id, name,
+ width, height,
+ type,
+ opacity,
+ mode);
+
+ g_free (name);
+ if (!gimp_drawable_has_alpha (new_layer_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha (new_layer_id);
+ }
+
+ /* add the copied layer to the temp. working image */
+ gimp_image_insert_layer (image_id, new_layer_id, -1, stack_position);
+
+ /* copy visibility state */
+ gimp_item_set_visible (new_layer_id, visible);
+
+ return new_layer_id;
+}
+
+/* ============================================================================
+ * p_bender_calculate_iter_curve
+ * ============================================================================
+ */
+
+static void
+p_bender_calculate_iter_curve (BenderDialog *cd,
+ gint32 xmax,
+ gint32 ymax)
+{
+ gint x;
+ gint outline;
+ BenderDialog *cd_from;
+ BenderDialog *cd_to;
+
+ outline = cd->outline;
+
+ if ((cd->bval_from == NULL) ||
+ (cd->bval_to == NULL) ||
+ (cd->bval_curr == NULL))
+ {
+ if(gb_debug) g_printf("p_bender_calculate_iter_curve NORMAL1\n");
+ if (cd->curve_type == SMOOTH)
+ {
+ cd->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd, xmax, ymax, FALSE);
+ cd->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves(cd, xmax, ymax);
+ }
+ }
+ else
+ {
+ /* compose curves by iterating between FROM/TO values */
+ if(gb_debug) g_printf ("p_bender_calculate_iter_curve ITERmode 1\n");
+
+ /* init FROM curves */
+ cd_from = g_new (BenderDialog, 1);
+ p_cd_from_bval (cd_from, cd->bval_from);
+ cd_from->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd_from->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ /* init TO curves */
+ cd_to = g_new (BenderDialog, 1);
+ p_cd_from_bval (cd_to, cd->bval_to);
+ cd_to->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd_to->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ if (cd_from->curve_type == SMOOTH)
+ {
+ /* calculate FROM curves */
+ cd_from->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd_from, xmax, ymax, FALSE);
+ cd_from->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd_from, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves (cd_from, xmax, ymax);
+ }
+
+ if (cd_to->curve_type == SMOOTH)
+ {
+ /* calculate TO curves */
+ cd_to->outline = OUTLINE_UPPER;
+ bender_calculate_curve (cd_to, xmax, ymax, FALSE);
+ cd_to->outline = OUTLINE_LOWER;
+ bender_calculate_curve (cd_to, xmax, ymax, FALSE);
+ }
+ else
+ {
+ p_stretch_curves (cd_to, xmax, ymax);
+ }
+
+ /* MIX Y-koords of the curves according to current iteration step */
+ for (x = 0; x <= xmax; x++)
+ {
+ p_delta_gint32 (&cd->curve_ptr[OUTLINE_UPPER][x],
+ cd_from->curve_ptr[OUTLINE_UPPER][x],
+ cd_to->curve_ptr[OUTLINE_UPPER][x],
+ cd->bval_curr->total_steps,
+ cd->bval_curr->current_step);
+
+ p_delta_gint32 (&cd->curve_ptr[OUTLINE_LOWER][x],
+ cd_from->curve_ptr[OUTLINE_LOWER][x],
+ cd_to->curve_ptr[OUTLINE_LOWER][x],
+ cd->bval_curr->total_steps,
+ cd->bval_curr->current_step);
+ }
+
+ g_free (cd_from->curve_ptr[OUTLINE_UPPER]);
+ g_free (cd_from->curve_ptr[OUTLINE_LOWER]);
+
+ g_free (cd_from);
+ g_free (cd_to);
+ }
+
+ cd->outline = outline;
+}
+
+/* ============================================================================
+ * p_vertical_bend
+ * ============================================================================
+ */
+
+static void
+p_vertical_bend (BenderDialog *cd,
+ t_GDRW *src_gdrw,
+ t_GDRW *dst_gdrw)
+{
+ gint32 row, col;
+ gint32 first_row, first_col, last_row, last_col;
+ gint32 x, y;
+ gint32 x2, y2;
+ gint32 curvy, nb_curvy, nb2_curvy;
+ gint32 desty, othery;
+ gint32 miny, maxy;
+ gint32 sign, dy, diff;
+ gint32 topshift;
+ float progress_step;
+ float progress_max;
+ float progress;
+
+ t_Last *last_arr;
+ t_Last *first_arr;
+ guchar color[4];
+ guchar mixcolor[4];
+ gint alias_dir;
+ guchar mixmask;
+
+ topshift = p_upper_curve_extend (cd,
+ src_gdrw->width,
+ src_gdrw->height);
+ diff = curvy = nb_curvy = nb2_curvy= miny = maxy = 0;
+
+ /* allocate array of last values (one element foreach x coordinate) */
+ last_arr = g_new (t_Last, src_gdrw->x2);
+ first_arr = g_new (t_Last, src_gdrw->x2);
+
+ /* ------------------------------------------------
+ * foreach pixel in the SAMPLE_drawable:
+ * ------------------------------------------------
+ * the inner loops (x/y) are designed to process
+ * all pixels of one tile in the sample drawable, the outer loops (row/col) do step
+ * to the next tiles. (this was done to reduce tile swapping)
+ */
+
+ first_row = src_gdrw->y1 / src_gdrw->tile_height;
+ last_row = (src_gdrw->y2 / src_gdrw->tile_height);
+ first_col = src_gdrw->x1 / src_gdrw->tile_width;
+ last_col = (src_gdrw->x2 / src_gdrw->tile_width);
+
+ /* init progress */
+ progress_max = (1 + last_row - first_row) * (1 + last_col - first_col);
+ progress_step = 1.0 / progress_max;
+ progress = 0.0;
+ if (cd->show_progress)
+ gimp_progress_init ( _("Curve Bend"));
+
+ for (row = first_row; row <= last_row; row++)
+ {
+ for (col = first_col; col <= last_col; col++)
+ {
+ if (col == first_col)
+ x = src_gdrw->x1;
+ else
+ x = col * src_gdrw->tile_width;
+ if (col == last_col)
+ x2 = src_gdrw->x2;
+ else
+ x2 = (col +1) * src_gdrw->tile_width;
+
+ if (cd->show_progress)
+ gimp_progress_update (progress += progress_step);
+
+ for( ; x < x2; x++)
+ {
+ if (row == first_row)
+ y = src_gdrw->y1;
+ else
+ y = row * src_gdrw->tile_height;
+
+ if (row == last_row)
+ y2 = src_gdrw->y2;
+ else
+ y2 = (row +1) * src_gdrw->tile_height ;
+
+ for( ; y < y2; y++)
+ {
+ /* ---------- copy SRC_PIXEL to curve position ------ */
+
+ p_get_pixel (src_gdrw, x, y, color);
+
+ curvy = p_curve_get_dy (cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ desty = y + topshift + curvy;
+
+ /* ----------- SMOOTHING ------------------ */
+ if (cd->smoothing && (x > 0))
+ {
+ nb_curvy = p_curve_get_dy (cd, x -1,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ if ((nb_curvy == curvy) && (x > 1))
+ {
+ nb2_curvy = p_curve_get_dy (cd, x -2,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)y);
+ }
+ else
+ {
+ nb2_curvy = nb_curvy;
+ }
+
+ p_put_mix_pixel (dst_gdrw, x, desty, color, nb_curvy, nb2_curvy, curvy);
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x, desty, color);
+ }
+
+ /* ----------- render ANTIALIAS ------------------ */
+
+ if (cd->antialias)
+ {
+ othery = desty;
+
+ if (y == src_gdrw->y1) /* Upper outline */
+ {
+ first_arr[x].y = curvy;
+ memcpy (first_arr[x].color, color,
+ dst_gdrw->bpp);
+
+ if (x > 0)
+ {
+ memcpy (mixcolor, first_arr[x-1].color,
+ dst_gdrw->bpp);
+
+ diff = abs(first_arr[x - 1].y - curvy) +1;
+ miny = MIN(first_arr[x - 1].y, curvy) -1;
+ maxy = MAX(first_arr[x - 1].y, curvy) +1;
+
+ othery = (src_gdrw->y2 -1)
+ + topshift
+ + p_curve_get_dy(cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)(src_gdrw->y2 -1));
+ }
+ }
+
+ if (y == src_gdrw->y2 - 1) /* Lower outline */
+ {
+ if (x > 0)
+ {
+ memcpy (mixcolor, last_arr[x-1].color,
+ dst_gdrw->bpp);
+
+ diff = abs (last_arr[x - 1].y - curvy) +1;
+ maxy = MAX (last_arr[x - 1].y, curvy) +1;
+ miny = MIN (last_arr[x - 1].y, curvy) -1;
+ }
+
+ othery = (src_gdrw->y1)
+ + topshift
+ + p_curve_get_dy(cd, x,
+ (gint32)src_gdrw->width,
+ (gint32)src_gdrw->height,
+ (gdouble)(src_gdrw->y1));
+ }
+
+ if(desty < othery) { alias_dir = 1; } /* fade to transp. with descending dy */
+ else if(desty > othery) { alias_dir = -1; } /* fade to transp. with ascending dy */
+ else { alias_dir = 0; } /* no antialias at curve crossing point(s) */
+
+ if (alias_dir != 0)
+ {
+ guchar alpha_lo = 20;
+
+ if (gimp_drawable_has_alpha (src_gdrw->drawable_id))
+ {
+ alpha_lo = MIN (20, mixcolor[src_gdrw->index_alpha]);
+ }
+
+ for (dy = 0; dy < diff; dy++)
+ {
+ /* iterate for fading alpha channel */
+ mixmask = 255 * ((gdouble)(dy + 1) / (gdouble) (diff+1));
+ mixcolor[dst_gdrw->index_alpha] = MIX_CHANNEL(color[dst_gdrw->index_alpha], alpha_lo, mixmask);
+
+ if(alias_dir > 0)
+ {
+ p_put_pixel (dst_gdrw, x -1, y + topshift + miny + dy, mixcolor);
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x -1, y + topshift + (maxy - dy), mixcolor);
+ }
+
+ }
+ }
+ }
+
+ /* ------------------ FILL HOLES ------------------ */
+
+ if (y == src_gdrw->y1)
+ {
+ diff = 0;
+ sign = 1;
+ }
+ else
+ {
+ diff = last_arr[x].y - curvy;
+ if (diff < 0)
+ {
+ diff = 0 - diff;
+ sign = -1;
+ }
+ else
+ {
+ sign = 1;
+ }
+
+ memcpy (mixcolor, color, dst_gdrw->bpp);
+ }
+
+ for (dy = 1; dy <= diff; dy++)
+ {
+ /* y differs more than 1 pixel from last y in the
+ * destination drawable. So we have to fill the empty
+ * space between using a mixed color
+ */
+
+ if (cd->smoothing)
+ {
+ /* smoothing is on, so we are using a mixed color */
+ gulong alpha1 = last_arr[x].color[3];
+ gulong alpha2 = color[3];
+ gulong alpha;
+
+ mixmask = 255 * ((gdouble)(dy) / (gdouble)(diff+1));
+ alpha = alpha1 * mixmask + alpha2 * (255 - mixmask);
+ mixcolor[3] = alpha/255;
+ if (mixcolor[3])
+ {
+ mixcolor[0] = (alpha1 * mixmask * last_arr[x].color[0]
+ + alpha2 * (255 - mixmask) * color[0])/alpha;
+ mixcolor[1] = (alpha1 * mixmask * last_arr[x].color[1]
+ + alpha2 * (255 - mixmask) * color[1])/alpha;
+ mixcolor[2] = (alpha1 * mixmask * last_arr[x].color[2]
+ + alpha2 * (255 - mixmask) * color[2])/alpha;
+ /*mixcolor[2] = MIX_CHANNEL(last_arr[x].color[2], color[2], mixmask);*/
+ }
+ }
+ else
+ {
+ /* smoothing is off, so we are using this color or
+ the last color */
+ if (dy < diff / 2)
+ {
+ memcpy (mixcolor, color,
+ dst_gdrw->bpp);
+ }
+ else
+ {
+ memcpy (mixcolor, last_arr[x].color,
+ dst_gdrw->bpp);
+ }
+ }
+
+ if (cd->smoothing)
+ {
+ p_put_mix_pixel (dst_gdrw, x,
+ desty + (dy * sign),
+ mixcolor,
+ nb_curvy, nb2_curvy, curvy );
+ }
+ else
+ {
+ p_put_pixel (dst_gdrw, x,
+ desty + (dy * sign), mixcolor);
+ }
+ }
+
+ /* store y and color */
+ last_arr[x].y = curvy;
+ memcpy (last_arr[x].color, color, dst_gdrw->bpp);
+ }
+ }
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ g_free (last_arr);
+ g_free (first_arr);
+}
+
+/* ============================================================================
+ * p_main_bend
+ * ============================================================================
+ */
+
+static gint32
+p_main_bend (BenderDialog *cd,
+ guint32 original_drawable_id,
+ gint work_on_copy)
+{
+ t_GDRW src_gdrw;
+ t_GDRW dst_gdrw;
+ gint32 src_drawable_id;
+ gint32 dst_drawable_id;
+ gint src_width;
+ gint src_height;
+ gint32 dst_height;
+ gint32 image_id;
+ gint32 tmp_layer_id;
+ gint32 interpolation;
+ gint offset_x, offset_y;
+ gint center_x, center_y;
+ gint32 xmax, ymax;
+
+ interpolation = cd->smoothing;
+ image_id = gimp_item_get_image (original_drawable_id);
+ gimp_drawable_offsets(original_drawable_id, &offset_x, &offset_y);
+
+ center_x = offset_x + (gimp_drawable_width (original_drawable_id) / 2 );
+ center_y = offset_y + (gimp_drawable_height (original_drawable_id) / 2 );
+
+ /* always copy original_drawable to a tmp src_layer */
+ tmp_layer_id = gimp_layer_copy(original_drawable_id);
+ /* set layer invisible and dummyname and
+ * add at top of the image while working
+ * (for the case of undo GIMP must know,
+ * that the layer was part of the image)
+ */
+ gimp_image_insert_layer (image_id, tmp_layer_id, -1, 0);
+ gimp_item_set_visible (tmp_layer_id, FALSE);
+ gimp_item_set_name (tmp_layer_id, "curve_bend_dummylayer");
+
+ if(gb_debug) g_printf("p_main_bend tmp_layer_id %d\n", (int)tmp_layer_id);
+
+ if (cd->rotation != 0.0)
+ {
+ if(gb_debug) g_printf("p_main_bend rotate: %f\n", (float)cd->rotation);
+ p_gimp_rotate(image_id, tmp_layer_id, interpolation, cd->rotation);
+ }
+
+ src_drawable_id = tmp_layer_id;
+
+ src_width = gimp_drawable_width (tmp_layer_id);
+ src_height = gimp_drawable_height (tmp_layer_id);
+
+ xmax = ymax = src_width -1;
+ cd->curve_ptr[OUTLINE_UPPER] = g_new (gint32, 1+xmax);
+ cd->curve_ptr[OUTLINE_LOWER] = g_new (gint32, 1+xmax);
+
+ p_bender_calculate_iter_curve(cd, xmax, ymax);
+ bender_init_min_max(cd, xmax);
+
+ dst_height = src_height
+ + p_upper_curve_extend(cd, src_width, src_height)
+ + p_lower_curve_extend(cd, src_width, src_height);
+
+ if(gb_debug) g_printf("p_main_bend: dst_height:%d\n", dst_height);
+
+ if (work_on_copy)
+ {
+ dst_drawable_id = p_add_layer (src_width, dst_height,
+ src_drawable_id);
+ if (gb_debug) g_printf("p_main_bend: DONE add layer\n");
+ }
+ else
+ {
+ /* work on the original */
+ gimp_layer_resize (original_drawable_id,
+ src_width,
+ dst_height,
+ offset_x, offset_y);
+ if (gb_debug) g_printf("p_main_bend: DONE layer resize\n");
+ if (! gimp_drawable_has_alpha (original_drawable_id))
+ {
+ /* always add alpha channel */
+ gimp_layer_add_alpha (original_drawable_id);
+ }
+
+ dst_drawable_id = original_drawable_id;
+ }
+
+ gimp_drawable_fill (dst_drawable_id, GIMP_FILL_TRANSPARENT);
+
+ p_init_gdrw (&src_gdrw, src_drawable_id, FALSE);
+ p_init_gdrw (&dst_gdrw, dst_drawable_id, FALSE);
+
+ p_vertical_bend (cd, &src_gdrw, &dst_gdrw);
+
+ if(gb_debug) g_printf("p_main_bend: DONE vertical bend\n");
+
+ p_end_gdrw (&src_gdrw);
+ p_end_gdrw (&dst_gdrw);
+
+ if (cd->rotation != 0.0)
+ {
+ p_gimp_rotate (image_id, dst_drawable_id,
+ interpolation, (gdouble)(360.0 - cd->rotation));
+
+ /* TODO: here we should crop dst_drawable to cut off full transparent borderpixels */
+ }
+
+ /* set offsets of the resulting new layer
+ *(center == center of original_drawable)
+ */
+ offset_x = center_x - (gimp_drawable_width (dst_drawable_id) / 2 );
+ offset_y = center_y - (gimp_drawable_height (dst_drawable_id) / 2 );
+ gimp_layer_set_offsets (dst_drawable_id, offset_x, offset_y);
+
+ /* delete the temp layer */
+ gimp_image_remove_layer (image_id, tmp_layer_id);
+
+ g_free (cd->curve_ptr[OUTLINE_UPPER]);
+ g_free (cd->curve_ptr[OUTLINE_LOWER]);
+
+ if (gb_debug) g_printf("p_main_bend: DONE bend main\n");
+
+ return dst_drawable_id;
+}
diff --git a/plug-ins/common/decompose.c b/plug-ins/common/decompose.c
new file mode 100644
index 0000000..b96c317
--- /dev/null
+++ b/plug-ins/common/decompose.c
@@ -0,0 +1,973 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Decompose plug-in (C) 1997 Peter Kirchgessner
+ * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
+ *
+ * Copyright 2013 Martijn van Beers <mail_dev@martijn.at>
+ * Copyright 2013 Téo Mazars <teo.mazars@ensimag.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Lab colorspace support originally written by Alexey Dyachenko,
+ * merged into the officical plug-in by Sven Neumann.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PLUG_IN_PROC "plug-in-decompose"
+#define PLUG_IN_PROC_REG "plug-in-decompose-registered"
+#define PLUG_IN_BINARY "decompose"
+#define PLUG_IN_ROLE "gimp-decompose"
+
+
+/* Description of a component */
+typedef struct
+{
+ const gchar *babl_name; /* channel's babl_component name */
+ const gchar *channel_name; /* name of channel to extract */
+
+ const gdouble range_min; /* min and max */
+ const gdouble range_max;
+ const gboolean perceptual_channel; /* "correct" the channel in Y' space */
+
+} Component;
+
+
+/* Maximum number of images/layers generated by an extraction */
+#define MAX_EXTRACT_IMAGES 4
+
+/* Description of an extraction */
+typedef struct
+{
+ const gchar *type; /* What to extract */
+ const gchar *model; /* the babl_model string to use */
+ const gboolean dialog; /* Set to TRUE if you want
+ * this extract function in the dialog */
+ const gint num_images; /* Number of images to create */
+
+ const gboolean clamp; /* clamping values in [0.0, 1.0] */
+
+ /* the babl_component names of the channels */
+ const Component component[MAX_EXTRACT_IMAGES];
+
+} Extract;
+
+typedef struct
+{
+ gchar extract_type[32];
+ gboolean as_layers;
+ gboolean use_registration;
+} DecomposeVals;
+
+
+/* Declare local functions
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 decompose (gint32 image_id,
+ gint32 drawable_ID,
+ const gchar *extract_type,
+ gint32 *image_ID_dst,
+ gint32 *num_layers,
+ gint32 *layer_ID_dst);
+static gint32 create_new_image (const gchar *filename,
+ const gchar *layername,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpPrecision precision,
+ gdouble xres,
+ gdouble yres,
+ gint32 *layer_ID);
+static gint32 create_new_layer (gint32 image_ID,
+ gint position,
+ const gchar *layername,
+ guint width,
+ guint height,
+ GimpImageBaseType type);
+static void transfer_registration_color (GeglBuffer *src,
+ GeglBuffer **dst,
+ gint count);
+static void cpn_affine_transform_clamp (GeglBuffer *buffer,
+ gdouble min,
+ gdouble max,
+ gboolean clamp);
+static void copy_n_components (GeglBuffer *src,
+ GeglBuffer **dst,
+ Extract ext);
+static void copy_one_component (GeglBuffer *src,
+ GeglBuffer *dst,
+ const char *model,
+ const Component component,
+ gboolean clamp);
+static gboolean decompose_dialog (void);
+static gchar * generate_filename (guint32 image_ID,
+ guint colorspace,
+ guint channel);
+
+
+#define CPN_RGBA_R { "R", N_("red"), 0.0, 1.0, FALSE }
+#define CPN_RGBA_G { "G", N_("green"), 0.0, 1.0, FALSE }
+#define CPN_RGBA_B { "B", N_("blue"), 0.0, 1.0, FALSE }
+#define CPN_RGBA_A { "A", N_("alpha"), 0.0, 1.0, TRUE }
+
+#define CPN_HSV_H { "hue", N_("hue"), 0.0, 1.0, TRUE }
+#define CPN_HSV_S { "saturation", N_("saturation"), 0.0, 1.0, TRUE }
+#define CPN_HSV_V { "value", N_("value"), 0.0, 1.0, TRUE }
+
+#define CPN_HSL_H { "hue", N_("hue"), 0.0, 1.0, TRUE }
+#define CPN_HSL_S { "saturation", N_("saturation"), 0.0, 1.0, TRUE }
+#define CPN_HSL_L { "lightness", N_("lightness"), 0.0, 1.0, TRUE }
+
+#define CPN_CMYK_C { "Cyan", N_("cyan"), 0.0, 1.0, TRUE }
+#define CPN_CMYK_M { "Magenta", N_("magenta"), 0.0, 1.0, TRUE }
+#define CPN_CMYK_Y { "Yellow", N_("yellow"), 0.0, 1.0, TRUE }
+#define CPN_CMYK_K { "Key", N_("black"), 0.0, 1.0, TRUE }
+
+#define CPN_LAB_L { "CIE L", N_("L"), 0.0, 100.0, TRUE }
+#define CPN_LAB_A { "CIE a", N_("A"), -127.5, 127.5, TRUE }
+#define CPN_LAB_B { "CIE b", N_("B"), -127.5, 127.5, TRUE }
+
+#define CPN_LCH_L { "CIE L", N_("L"), 0.0, 100.0, TRUE }
+#define CPN_LCH_C { "CIE C(ab)", N_("C"), 0.0, 200.0, TRUE }
+#define CPN_LCH_H { "CIE H(ab)", N_("H"), 0.0, 360.0, TRUE }
+
+#define CPN_YCBCR_Y { "Y'", N_("luma-y470"), 0.0, 1.0, TRUE }
+#define CPN_YCBCR_CB { "Cb", N_("blueness-cb470"), -0.5, 0.5, TRUE }
+#define CPN_YCBCR_CR { "Cr", N_("redness-cr470"), -0.5, 0.5, TRUE }
+
+#define CPN_YCBCR709_Y { "Y'", N_("luma-y709"), 0.0, 1.0, TRUE }
+#define CPN_YCBCR709_CB { "Cb", N_("blueness-cb709"), -0.5, 0.5, TRUE }
+#define CPN_YCBCR709_CR { "Cr", N_("redness-cr709"), -0.5, 0.5, TRUE }
+
+
+static const Extract extract[] =
+{
+ { N_("RGB"), "RGB", TRUE, 3, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B } },
+ { N_("RGBA"), "RGBA", TRUE, 4, FALSE, { CPN_RGBA_R, CPN_RGBA_G, CPN_RGBA_B, CPN_RGBA_A } },
+
+ { N_("Red"), "RGB", FALSE, 1, FALSE, { CPN_RGBA_R } },
+ { N_("Green"), "RGB", FALSE, 1, FALSE, { CPN_RGBA_G } },
+ { N_("Blue"), "RGB", FALSE, 1, FALSE, { CPN_RGBA_B } },
+ { N_("Alpha"), "RGBA", TRUE , 1, FALSE, { CPN_RGBA_A } },
+
+ { N_("HSV"), "HSV", TRUE, 3, FALSE, { CPN_HSV_H, CPN_HSV_S, CPN_HSV_V } },
+ { N_("Hue"), "HSV", FALSE, 1, FALSE, { CPN_HSV_H } },
+ { N_("Saturation"), "HSV", FALSE, 1, FALSE, { CPN_HSV_S } },
+ { N_("Value"), "HSV", FALSE, 1, FALSE, { CPN_HSV_V } },
+
+ { N_("HSL"), "HSL", TRUE, 3, FALSE, { CPN_HSL_H, CPN_HSL_S, CPN_HSL_L } },
+ { N_("Hue (HSL)"), "HSL", FALSE, 1, FALSE, { CPN_HSL_H } },
+ { N_("Saturation (HSL)"), "HSL", FALSE, 1, FALSE, { CPN_HSL_S } },
+ { N_("Lightness"), "HSL", FALSE, 1, FALSE, { CPN_HSL_L } },
+
+ { N_("CMYK"), "CMYK", TRUE, 4, FALSE, { CPN_CMYK_C, CPN_CMYK_M, CPN_CMYK_Y, CPN_CMYK_K } },
+ { N_("Cyan"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_C } },
+ { N_("Magenta"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_M } },
+ { N_("Yellow"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_Y } },
+ { N_("Black"), "CMYK", FALSE, 1, FALSE, { CPN_CMYK_K } },
+
+ { N_("LAB"), "CIE Lab", TRUE, 3, FALSE, { CPN_LAB_L, CPN_LAB_A, CPN_LAB_B } },
+
+ { N_("LCH"), "CIE LCH(ab)", TRUE, 3, FALSE, { CPN_LCH_L, CPN_LCH_C, CPN_LCH_H } },
+
+ { N_("YCbCr_ITU_R470"), "Y'CbCr", TRUE, 3, FALSE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },
+ { N_("YCbCr_ITU_R470_256"), "Y'CbCr", TRUE, 3, TRUE, { CPN_YCBCR_Y, CPN_YCBCR_CB, CPN_YCBCR_CR} },
+
+ { N_("YCbCr_ITU_R709"), "Y'CbCr709", TRUE, 3, FALSE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} },
+ { N_("YCbCr_ITU_R709_256"), "Y'CbCr709", TRUE, 3, TRUE, { CPN_YCBCR709_Y, CPN_YCBCR709_CB, CPN_YCBCR709_CR} }
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static DecomposeVals decovals =
+{
+ "rgb", /* Decompose type */
+ TRUE, /* Decompose to Layers */
+ FALSE /* use registration color */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "decompose-type", NULL },
+ { GIMP_PDB_INT32, "layers-mode", "Create channels as layers in a single image" }
+ };
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new-image", "Output gray image" },
+ { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
+ { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" },
+ { GIMP_PDB_IMAGE, "new-image", "Output gray image (N/A for single channel extract)" }
+ };
+
+ GString *type_desc;
+ gint i;
+
+ type_desc = g_string_new ("What to decompose: ");
+ g_string_append_c (type_desc, '"');
+ g_string_append (type_desc, extract[0].type);
+ g_string_append_c (type_desc, '"');
+
+ for (i = 1; i < G_N_ELEMENTS (extract); i++)
+ {
+ g_string_append (type_desc, ", ");
+ g_string_append_c (type_desc, '"');
+ g_string_append (type_desc, extract[i].type);
+ g_string_append_c (type_desc, '"');
+ }
+
+ args[3].description = type_desc->str;
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Decompose an image into separate colorspace components"),
+ "This function creates new gray images with "
+ "different channel information in each of them",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1997",
+ N_("_Decompose..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_install_procedure (PLUG_IN_PROC_REG,
+ N_("Decompose an image into separate colorspace components"),
+ "This function creates new gray images with "
+ "different channel information in each of them. "
+ "Pixels in the foreground color will appear black "
+ "in all output images. This can be used for "
+ "things like crop marks that have to show up on "
+ "all channels.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner, Clarence Risher",
+ "1997",
+ N_("_Decompose..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC_REG, "<Image>/Colors/Components");
+
+ g_string_free (type_desc, TRUE);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[MAX_EXTRACT_IMAGES + 1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 num_images;
+ gint32 image_ID_extract[MAX_EXTRACT_IMAGES];
+ gint32 layer_ID_extract[MAX_EXTRACT_IMAGES];
+ gint j;
+ gint32 layer;
+ gint32 num_layers;
+ gint32 image_ID;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_image;
+ layer = param[2].data.d_drawable;
+
+ *nreturn_vals = MAX_EXTRACT_IMAGES + 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ for (j = 0; j < MAX_EXTRACT_IMAGES; j++)
+ {
+ values[j+1].type = GIMP_PDB_IMAGE;
+ values[j+1].data.d_int32 = -1;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &decovals);
+
+ /* First acquire information with a dialog */
+ if (! decompose_dialog ())
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 4 && nparams != 5 && nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ strncpy (decovals.extract_type, param[3].data.d_string,
+ sizeof (decovals.extract_type));
+ decovals.extract_type[sizeof (decovals.extract_type) - 1] = '\0';
+
+ decovals.as_layers = nparams > 4 ? param[4].data.d_int32 : FALSE;
+ decovals.use_registration = (strcmp (name, PLUG_IN_PROC_REG) == 0);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &decovals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Decomposing"));
+
+ num_images = decompose (image_ID, layer,
+ decovals.extract_type,
+ image_ID_extract,
+ &num_layers,
+ layer_ID_extract);
+
+ if (num_images <= 0)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ /* create decompose-data parasite */
+ GString *data = g_string_new ("");
+
+ g_string_printf (data, "source=%d type=%s ",
+ layer, decovals.extract_type);
+
+ for (j = 0; j < num_layers; j++)
+ g_string_append_printf (data, "%d ", layer_ID_extract[j]);
+
+ for (j = 0; j < num_images; j++)
+ {
+ GimpParasite *parasite;
+
+ values[j+1].data.d_int32 = image_ID_extract[j];
+
+ gimp_image_undo_enable (image_ID_extract[j]);
+ gimp_image_clean_all (image_ID_extract[j]);
+
+ parasite = gimp_parasite_new ("decompose-data",
+ 0, data->len + 1, data->str);
+ gimp_image_attach_parasite (image_ID_extract[j], parasite);
+ gimp_parasite_free (parasite);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_display_new (image_ID_extract[j]);
+ }
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &decovals, sizeof (DecomposeVals));
+ }
+
+ gimp_progress_end ();
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* Decompose an image. It returns the number of new (gray) images.
+ * The image IDs for the new images are returned in image_ID_dst.
+ * On failure, -1 is returned.
+ */
+static gint32
+decompose (gint32 image_ID,
+ gint32 drawable_ID,
+ const gchar *extract_type,
+ gint32 *image_ID_dst,
+ gint32 *nlayers,
+ gint32 *layer_ID_dst)
+{
+ const gchar *layername;
+ gint j, extract_idx;
+ gint height, width, num_layers;
+ GeglBuffer *src_buffer;
+ GeglBuffer *dst_buffer[MAX_EXTRACT_IMAGES];
+ GimpPrecision precision;
+ gboolean requirements = FALSE;
+ gboolean decomp_has_alpha = FALSE;
+
+ extract_idx = -1; /* Search extract type */
+ for (j = 0; j < G_N_ELEMENTS (extract); j++)
+ {
+ if (g_ascii_strcasecmp (extract_type, extract[j].type) == 0)
+ {
+ extract_idx = j;
+ break;
+ }
+ }
+ if (extract_idx < 0)
+ return -1;
+
+ num_layers = extract[extract_idx].num_images;
+
+ /* Sanity checks */
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ precision = gimp_image_get_precision (image_ID);
+
+ for (j = 0; j < num_layers; j++)
+ {
+ /* FIXME: Not 100% reliable */
+ decomp_has_alpha |= ! g_strcmp0 ("alpha", extract[extract_idx].component[j].babl_name);
+ decomp_has_alpha |= ! g_strcmp0 ("A", extract[extract_idx].component[j].babl_name);
+ }
+
+ requirements |= (gimp_drawable_is_rgb (drawable_ID));
+ requirements |= (gimp_drawable_is_indexed (drawable_ID));
+ requirements |= (gimp_drawable_is_gray (drawable_ID)
+ && gimp_drawable_has_alpha (drawable_ID)
+ && (num_layers <= 2)
+ && decomp_has_alpha);
+ requirements &= (!decomp_has_alpha || gimp_drawable_has_alpha (drawable_ID));
+
+ if (!requirements)
+ {
+ g_message (_("Image not suitable for this decomposition"));
+ return -1;
+ }
+
+ width = gegl_buffer_get_width (src_buffer);
+ height = gegl_buffer_get_height (src_buffer);
+
+ /* Create all new gray images */
+ for (j = 0; j < num_layers; j++)
+ {
+ gchar *filename;
+ gdouble xres, yres;
+
+ filename = generate_filename (image_ID, extract_idx, j);
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+
+ if (decovals.as_layers)
+ {
+ layername = gettext (extract[extract_idx].component[j].channel_name);
+
+ if (j == 0)
+ image_ID_dst[j] = create_new_image (filename, layername,
+ width, height, GIMP_GRAY, precision,
+ xres, yres,
+ layer_ID_dst + j);
+ else
+ layer_ID_dst[j] = create_new_layer (image_ID_dst[0], j, layername,
+ width, height, GIMP_GRAY);
+ }
+ else
+ {
+ image_ID_dst[j] = create_new_image (filename, NULL,
+ width, height, GIMP_GRAY, precision,
+ xres, yres,
+ layer_ID_dst + j);
+ }
+
+ g_free (filename);
+
+ dst_buffer[j] = gimp_drawable_get_buffer (layer_ID_dst[j]);
+ }
+
+ copy_n_components (src_buffer, dst_buffer,
+ extract[extract_idx]);
+
+ if (decovals.use_registration)
+ transfer_registration_color (src_buffer, dst_buffer, num_layers);
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (src_buffer);
+
+ for (j = 0; j < num_layers; j++)
+ {
+ g_object_unref (dst_buffer[j]);
+ }
+
+ *nlayers = num_layers;
+
+ return (decovals.as_layers ? 1 : num_layers);
+}
+
+
+/* Create an image. Returns layer_ID and image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ const gchar *layername,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpPrecision precision,
+ gdouble xres,
+ gdouble yres,
+ gint32 *layer_ID)
+{
+ gint32 image_ID;
+
+ image_ID = gimp_image_new_with_precision (width, height, type, precision);
+
+ gimp_image_undo_disable (image_ID);
+ gimp_image_set_filename (image_ID, filename);
+ gimp_image_set_resolution (image_ID, xres, yres);
+
+ *layer_ID = create_new_layer (image_ID, 0,
+ layername, width, height, type);
+
+ return image_ID;
+}
+
+
+static gint32
+create_new_layer (gint32 image_ID,
+ gint position,
+ const gchar *layername,
+ guint width,
+ guint height,
+ GimpImageBaseType type)
+{
+ gint32 layer_ID;
+ GimpImageType gdtype = GIMP_RGB_IMAGE;
+
+ switch (type)
+ {
+ case GIMP_RGB:
+ gdtype = GIMP_RGB_IMAGE;
+ break;
+ case GIMP_GRAY:
+ gdtype = GIMP_GRAY_IMAGE;
+ break;
+ case GIMP_INDEXED:
+ gdtype = GIMP_INDEXED_IMAGE;
+ break;
+ }
+
+ if (! layername)
+ layername = _("Background");
+
+ layer_ID = gimp_layer_new (image_ID, layername, width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, position);
+
+ return layer_ID;
+}
+
+/* Registration Color function */
+
+static void
+transfer_registration_color (GeglBuffer *src,
+ GeglBuffer **dst,
+ gint count)
+{
+ GimpRGB color, test;
+ GeglBufferIterator *gi;
+ const Babl *src_format;
+ const Babl *dst_format;
+ gint src_bpp;
+ gint dst_bpp;
+ gint i;
+ gdouble white;
+
+ gimp_context_get_foreground (&color);
+ white = 1.0;
+
+ src_format = gegl_buffer_get_format (src);
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+
+ dst_format = gegl_buffer_get_format (dst[0]);
+ dst_bpp = babl_format_get_bytes_per_pixel (dst_format);
+
+ gi = gegl_buffer_iterator_new (src, NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 10);
+
+ for (i = 0; i < count; i++)
+ {
+ gegl_buffer_iterator_add (gi, dst[i], NULL, 0, NULL,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+ }
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ gpointer src_data;
+ gpointer dst_data[MAX_EXTRACT_IMAGES];
+ gint j, k;
+
+ src_data = gi->items[0].data;
+ for (j = 0; j < count; j++)
+ dst_data[j] = gi->items[j + 1].data;
+
+ for (k = 0; k < gi->length; k++)
+ {
+ gulong pos = k * src_bpp;
+
+ gimp_rgba_set_pixel (&test, src_format, ((guchar *)src_data) + pos);
+
+ if (gimp_rgb_distance (&test, &color) < 1e-6)
+ {
+ for (j = 0; j < count; j++)
+ {
+ gpointer data = dst_data[j];
+
+ babl_process (babl_fish (babl_format ("Y double"), dst_format),
+ &white, (guchar *)data + (k * dst_bpp), 1);
+ }
+ }
+ }
+ }
+}
+
+static void
+cpn_affine_transform_clamp (GeglBuffer *buffer,
+ gdouble min,
+ gdouble max,
+ gboolean clamp)
+{
+ GeglBufferIterator *gi;
+ gdouble scale = 1.0 / (max - min);
+ gdouble offset = - min;
+
+ /* We want to scale values linearly, regardless of the format of the buffer */
+ gegl_buffer_set_format (buffer, babl_format ("Y double"));
+
+ gi = gegl_buffer_iterator_new (buffer, NULL, 0, NULL,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ guint k;
+ double *data;
+
+ data = (double*) gi->items[0].data;
+
+ if (clamp)
+ {
+ for (k = 0; k < gi->length; k++)
+ {
+ data[k] = CLAMP ((data[k] + offset) * scale, 0.0, 1.0);
+ }
+ }
+ else
+ {
+ for (k = 0; k < gi->length; k++)
+ {
+ data[k] = (data[k] + offset) * scale;
+ }
+ }
+ }
+}
+
+static void
+copy_n_components (GeglBuffer *src,
+ GeglBuffer **dst,
+ Extract ext)
+{
+ gint i;
+
+ for (i = 0; i < ext.num_images; i++)
+ {
+ gimp_progress_update ((gdouble) i / (gdouble) ext.num_images);
+
+ copy_one_component (src, dst[i], ext.model, ext.component[i], ext.clamp);
+ }
+}
+
+static void
+copy_one_component (GeglBuffer *src,
+ GeglBuffer *dst,
+ const gchar *model,
+ const Component component,
+ gboolean clamp)
+{
+ const Babl *component_format;
+ const Babl *dst_format;
+ GeglBuffer *temp;
+ const GeglRectangle *extent;
+
+ /* We are working in linear double precision */
+ component_format = babl_format_new (babl_model (model),
+ babl_type ("double"),
+ babl_component (component.babl_name),
+ NULL);
+
+ /* We need to enforce linearity here
+ * If the output is "Y'", the output of temp is already ok
+ * If the output is "Y" , it will enforce gamma-decoding.
+ * A bit tricky and suboptimal...
+ */
+ if (component.perceptual_channel)
+ dst_format = babl_format ("Y' double");
+ else
+ dst_format = babl_format ("Y double");
+
+ extent = gegl_buffer_get_extent (src);
+ temp = gegl_buffer_new (extent, dst_format);
+
+ /* we want to copy the component as is */
+ gegl_buffer_set_format (temp, component_format);
+ gegl_buffer_copy (src, NULL, GEGL_ABYSS_NONE, temp, NULL);
+
+ if (component.range_min != 0.0 ||
+ component.range_max != 1.0 ||
+ clamp)
+ {
+ cpn_affine_transform_clamp (temp,
+ component.range_min, component.range_max,
+ clamp);
+ }
+
+ /* This is our new "Y(') double" component buffer */
+ gegl_buffer_set_format (temp, NULL);
+
+ /* Now we let babl convert it back to the format that dst needs */
+ gegl_buffer_copy (temp, NULL, GEGL_ABYSS_NONE, dst, NULL);
+
+ g_object_unref (temp);
+}
+
+static gboolean
+decompose_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *toggle;
+ gint j;
+ gint extract_idx;
+ gboolean run;
+
+ extract_idx = 0;
+ for (j = 0; j < G_N_ELEMENTS (extract); j++)
+ {
+ if (extract[j].dialog &&
+ g_ascii_strcasecmp (decovals.extract_type, extract[j].type) == 0)
+ {
+ extract_idx = j;
+ break;
+ }
+ }
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Decompose"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ frame = gimp_frame_new (_("Extract Channels"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Color _model:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+ for (j = 0; j < G_N_ELEMENTS (extract); j++)
+ {
+ if (extract[j].dialog)
+ {
+ gchar *label = g_strdup (gettext (extract[j].type));
+ gchar *l;
+
+ for (l = label; *l; l++)
+ if (*l == '-' || *l == '_')
+ *l = ' ';
+
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_LABEL, label,
+ GIMP_INT_STORE_VALUE, j,
+ -1);
+ g_free (label);
+ }
+ }
+
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ extract_idx,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &extract_idx);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Decompose to layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ decovals.as_layers);
+ gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &decovals.as_layers);
+
+ toggle =
+ gtk_check_button_new_with_mnemonic (_("_Foreground as registration color"));
+ gimp_help_set_help_data (toggle, _("Pixels in the foreground color will "
+ "appear black in all output images. "
+ "This can be used for things like crop "
+ "marks that have to show up on all "
+ "channels."), PLUG_IN_PROC);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ decovals.use_registration);
+ gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &decovals.use_registration);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ if (run)
+ strncpy (decovals.extract_type, extract[extract_idx].type,
+ sizeof decovals.extract_type - 1);
+
+ return run;
+}
+
+/* Build a filename like <imagename>-<channel>.<extension> */
+gchar *
+generate_filename (guint32 image_ID,
+ guint colorspace,
+ guint channel)
+{
+ /* Build a filename like <imagename>-<channel>.<extension> */
+ gchar *fname;
+ gchar *filename;
+ gchar *extension;
+
+ fname = gimp_image_get_filename (image_ID);
+
+ if (fname)
+ {
+ extension = fname + strlen (fname) - 1;
+
+ while (extension >= fname)
+ {
+ if (*extension == '.')
+ break;
+ extension--;
+ }
+
+ if (extension >= fname)
+ {
+ *(extension++) = '\0';
+
+ if (decovals.as_layers)
+ filename = g_strdup_printf ("%s-%s.%s", fname,
+ gettext (extract[colorspace].type),
+ extension);
+ else
+ filename = g_strdup_printf ("%s-%s.%s", fname,
+ gettext (extract[colorspace].component[channel].channel_name),
+ extension);
+ }
+ else
+ {
+ if (decovals.as_layers)
+ filename = g_strdup_printf ("%s-%s", fname,
+ gettext (extract[colorspace].type));
+ else
+ filename = g_strdup_printf ("%s-%s", fname,
+ gettext (extract[colorspace].component[channel].channel_name));
+ }
+ }
+ else
+ {
+ filename = g_strdup (gettext (extract[colorspace].component[channel].channel_name));
+ }
+
+ g_free (fname);
+
+ return filename;
+}
diff --git a/plug-ins/common/depth-merge.c b/plug-ins/common/depth-merge.c
new file mode 100644
index 0000000..ff65693
--- /dev/null
+++ b/plug-ins/common/depth-merge.c
@@ -0,0 +1,1050 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Depth Merge -- Combine two image layers via corresponding depth maps
+ * Copyright (C) 1997, 1998 Sean Cier (scier@PostHorizon.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* Version 1.0.0: (14 August 1998)
+ * Math optimizations, miscellaneous speedups
+ *
+ * Version 0.1: (6 July 1997)
+ * Initial Release
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define DEBUG
+
+#ifndef LERP
+#define LERP(frac,a,b) ((frac)*(b) + (1-(frac))*(a))
+#endif
+
+#define MUL255(i) ((i)*256 - (i))
+#define DIV255(i) (((i) + (i)/256 + 1) / 256)
+
+#define PLUG_IN_PROC "plug-in-depth-merge"
+#define PLUG_IN_VERSION "August 1998"
+#define PLUG_IN_BINARY "depth-merge"
+#define PLUG_IN_ROLE "gimp-depth-merge"
+
+#define PREVIEW_SIZE 256
+
+/* ----- DepthMerge ----- */
+
+struct _DepthMerge;
+
+typedef struct _DepthMergeInterface
+{
+ gboolean active;
+
+ GtkWidget *dialog;
+
+ GtkWidget *preview;
+ gint previewWidth;
+ gint previewHeight;
+
+ guchar *previewSource1;
+ guchar *previewSource2;
+ guchar *previewDepthMap1;
+ guchar *previewDepthMap2;
+} DepthMergeInterface;
+
+typedef struct _DepthMergeParams
+{
+ gint32 result;
+ gint32 source1;
+ gint32 source2;
+ gint32 depthMap1;
+ gint32 depthMap2;
+ gfloat overlap;
+ gfloat offset;
+ gfloat scale1;
+ gfloat scale2;
+} DepthMergeParams;
+
+typedef struct _DepthMerge
+{
+ DepthMergeInterface *interface;
+ DepthMergeParams params;
+
+ gint32 resultDrawable_id;
+ gint32 source1Drawable_id;
+ gint32 source2Drawable_id;
+ gint32 depthMap1Drawable_id;
+ gint32 depthMap2Drawable_id;
+ gint selectionX;
+ gint selectionY;
+ gint selectionWidth;
+ gint selectionHeight;
+ gint resultHasAlpha;
+} DepthMerge;
+
+static void DepthMerge_initParams (DepthMerge *dm);
+static gboolean DepthMerge_construct (DepthMerge *dm);
+static void DepthMerge_destroy (DepthMerge *dm);
+static gint32 DepthMerge_execute (DepthMerge *dm);
+static void DepthMerge_executeRegion (DepthMerge *dm,
+ guchar *source1Row,
+ guchar *source2Row,
+ guchar *depthMap1Row,
+ guchar *depthMap2Row,
+ guchar *resultRow,
+ gint length);
+static gboolean DepthMerge_dialog (DepthMerge *dm);
+static void DepthMerge_buildPreviewSourceImage (DepthMerge *dm);
+static void DepthMerge_updatePreview (DepthMerge *dm);
+
+
+static gboolean dm_constraint (gint32 imageId,
+ gint32 drawableId,
+ gpointer data);
+
+static void dialogSource1ChangedCallback (GtkWidget *widget, DepthMerge *dm);
+static void dialogSource2ChangedCallback (GtkWidget *widget, DepthMerge *dm);
+static void dialogDepthMap1ChangedCallback (GtkWidget *widget, DepthMerge *dm);
+static void dialogDepthMap2ChangedCallback (GtkWidget *widget, DepthMerge *dm);
+
+static void dialogValueScaleUpdateCallback (GtkAdjustment *adjustment,
+ gpointer data);
+
+static void util_fillReducedBuffer (guchar *dest,
+ const Babl *dest_format,
+ gint destWidth,
+ gint destHeight,
+ gint32 sourceDrawable_id,
+ gint x0,
+ gint y0,
+ gint sourceWidth,
+ gint sourceHeight);
+
+
+/* ----- plug-in entry points ----- */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "result", "Result" },
+ { GIMP_PDB_DRAWABLE, "source1", "Source 1" },
+ { GIMP_PDB_DRAWABLE, "source2", "Source 2" },
+ { GIMP_PDB_DRAWABLE, "depthMap1", "Depth map 1" },
+ { GIMP_PDB_DRAWABLE, "depthMap2", "Depth map 2" },
+ { GIMP_PDB_FLOAT, "overlap", "Overlap" },
+ { GIMP_PDB_FLOAT, "offset", "Depth relative offset" },
+ { GIMP_PDB_FLOAT, "scale1", "Depth relative scale 1" },
+ { GIMP_PDB_FLOAT, "scale2", "Depth relative scale 2" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Combine two images using depth maps (z-buffers)"),
+ "Taking as input two full-color, full-alpha "
+ "images and two corresponding grayscale depth "
+ "maps, this plug-in combines the images based "
+ "on which is closer (has a lower depth map value) "
+ "at each point.",
+ "Sean Cier",
+ "Sean Cier",
+ PLUG_IN_VERSION,
+ N_("_Depth Merge..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Combine");
+}
+
+static void
+run (const gchar *name,
+ gint numParams,
+ const GimpParam *param,
+ gint *numReturnVals,
+ GimpParam **returnVals)
+{
+ static GimpParam values[1];
+ GimpRunMode runMode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ DepthMerge dm;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ runMode = param[0].data.d_int32;
+
+ *numReturnVals = 1;
+ *returnVals = values;
+
+ switch (runMode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ DepthMerge_initParams (&dm);
+ gimp_get_data (PLUG_IN_PROC, &(dm.params));
+ dm.params.result = param[2].data.d_drawable;
+ if (!DepthMerge_construct (&dm))
+ return;
+
+ if (!DepthMerge_dialog (&dm))
+ {
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+ return;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ DepthMerge_initParams (&dm);
+ if (numParams != 11)
+ status = GIMP_PDB_CALLING_ERROR;
+ else
+ {
+ dm.params.result = param[ 2].data.d_drawable;
+ dm.params.source1 = param[ 3].data.d_drawable;
+ dm.params.source2 = param[ 4].data.d_drawable;
+ dm.params.depthMap1 = param[ 5].data.d_drawable;
+ dm.params.depthMap2 = param[ 6].data.d_drawable;
+ dm.params.overlap = param[ 7].data.d_float;
+ dm.params.offset = param[ 8].data.d_float;
+ dm.params.scale1 = param[ 9].data.d_float;
+ dm.params.scale2 = param[10].data.d_float;
+ }
+ if (!DepthMerge_construct (&dm))
+ return;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ DepthMerge_initParams (&dm);
+ gimp_get_data (PLUG_IN_PROC, &(dm.params));
+ if (!DepthMerge_construct (&dm))
+ return;
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (!DepthMerge_execute (&dm))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ if (runMode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (runMode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC,
+ &(dm.params), sizeof (DepthMergeParams));
+ }
+ }
+
+ DepthMerge_destroy (&dm);
+
+ values[0].data.d_status = status;
+}
+
+/* ----- DepthMerge ----- */
+
+static void
+DepthMerge_initParams (DepthMerge *dm)
+{
+ dm->params.result = -1;
+ dm->params.source1 = -1;
+ dm->params.source2 = -1;
+ dm->params.depthMap1 = -1;
+ dm->params.depthMap2 = -1;
+ dm->params.overlap = 0;
+ dm->params.offset = 0;
+ dm->params.scale1 = 1;
+ dm->params.scale2 = 1;
+}
+
+static gboolean
+DepthMerge_construct (DepthMerge *dm)
+{
+ dm->interface = NULL;
+
+ dm->resultDrawable_id = dm->params.result;
+
+ if (! gimp_drawable_mask_intersect (dm->resultDrawable_id,
+ &(dm->selectionX), &(dm->selectionY),
+ &(dm->selectionWidth),
+ &(dm->selectionHeight)))
+ {
+ return FALSE;
+ }
+
+ dm->resultHasAlpha = gimp_drawable_has_alpha (dm->resultDrawable_id);
+
+ dm->source1Drawable_id = dm->params.source1;
+ dm->source2Drawable_id = dm->params.source2;
+ dm->depthMap1Drawable_id = dm->params.depthMap1;
+ dm->depthMap2Drawable_id = dm->params.depthMap2;
+
+ dm->params.overlap = CLAMP (dm->params.overlap, 0, 2);
+ dm->params.offset = CLAMP (dm->params.offset, -1, 1);
+ dm->params.scale1 = CLAMP (dm->params.scale1, -1, 1);
+ dm->params.scale2 = CLAMP (dm->params.scale2, -1, 1);
+
+ return TRUE;
+}
+
+static void
+DepthMerge_destroy (DepthMerge *dm)
+{
+ if (dm->interface != NULL)
+ {
+ g_free (dm->interface->previewSource1);
+ g_free (dm->interface->previewSource2);
+ g_free (dm->interface->previewDepthMap1);
+ g_free (dm->interface->previewDepthMap2);
+ g_free (dm->interface);
+ }
+}
+
+static gint32
+DepthMerge_execute (DepthMerge *dm)
+{
+ int x, y;
+ GeglBuffer *source1_buffer = NULL;
+ GeglBuffer *source2_buffer = NULL;
+ GeglBuffer *depthMap1_buffer = NULL;
+ GeglBuffer *depthMap2_buffer = NULL;
+ GeglBuffer *result_buffer;
+ guchar *source1Row, *source2Row;
+ guchar *depthMap1Row, *depthMap2Row;
+ guchar *resultRow;
+ guchar *tempRow;
+
+ gimp_progress_init (_("Depth-merging"));
+
+ resultRow = g_new (guchar, dm->selectionWidth * 4);
+ source1Row = g_new (guchar, dm->selectionWidth * 4);
+ source2Row = g_new (guchar, dm->selectionWidth * 4);
+ depthMap1Row = g_new (guchar, dm->selectionWidth );
+ depthMap2Row = g_new (guchar, dm->selectionWidth );
+ tempRow = g_new (guchar, dm->selectionWidth * 4);
+
+ if (dm->source1Drawable_id > 0)
+ {
+ source1_buffer = gimp_drawable_get_buffer (dm->source1Drawable_id);
+ }
+ else
+ {
+ for (x = 0; x < dm->selectionWidth; x++)
+ {
+ source1Row[4 * x ] = 0;
+ source1Row[4 * x + 1] = 0;
+ source1Row[4 * x + 2] = 0;
+ source1Row[4 * x + 3] = 255;
+ }
+ }
+
+ if (dm->source2Drawable_id > 0)
+ {
+ source2_buffer = gimp_drawable_get_buffer (dm->source2Drawable_id);
+ }
+ else
+ {
+ for (x = 0; x < dm->selectionWidth; x++)
+ {
+ source2Row[4 * x ] = 0;
+ source2Row[4 * x + 1] = 0;
+ source2Row[4 * x + 2] = 0;
+ source2Row[4 * x + 3] = 255;
+ }
+ }
+
+ if (dm->depthMap1Drawable_id > 0)
+ {
+ depthMap1_buffer = gimp_drawable_get_buffer (dm->depthMap1Drawable_id);
+ }
+ else
+ {
+ for (x = 0; x < dm->selectionWidth; x++)
+ {
+ depthMap1Row[x] = 0;
+ }
+ }
+
+ if (dm->depthMap2Drawable_id > 0)
+ {
+ depthMap2_buffer = gimp_drawable_get_buffer (dm->depthMap2Drawable_id);
+ }
+ else
+ {
+ for (x = 0; x < dm->selectionWidth; x++)
+ {
+ depthMap2Row[x] = 0;
+ }
+ }
+
+ result_buffer = gimp_drawable_get_shadow_buffer (dm->resultDrawable_id);
+
+ for (y = dm->selectionY; y < (dm->selectionY + dm->selectionHeight); y++)
+ {
+ if (dm->source1Drawable_id > 0)
+ {
+ gegl_buffer_get (source1_buffer,
+ GEGL_RECTANGLE (dm->selectionX, y,
+ dm->selectionWidth, 1), 1.0,
+ babl_format ("R'G'B'A u8"), source1Row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ if (dm->source2Drawable_id > 0)
+ {
+ gegl_buffer_get (source2_buffer,
+ GEGL_RECTANGLE (dm->selectionX, y,
+ dm->selectionWidth, 1), 1.0,
+ babl_format ("R'G'B'A u8"), source2Row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ if (dm->depthMap1Drawable_id > 0)
+ {
+ gegl_buffer_get (depthMap1_buffer,
+ GEGL_RECTANGLE (dm->selectionX, y,
+ dm->selectionWidth, 1), 1.0,
+ babl_format ("Y' u8"), depthMap1Row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ if (dm->depthMap2Drawable_id > 0)
+ {
+ gegl_buffer_get (depthMap2_buffer,
+ GEGL_RECTANGLE (dm->selectionX, y,
+ dm->selectionWidth, 1), 1.0,
+ babl_format ("Y' u8"), depthMap2Row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ DepthMerge_executeRegion (dm,
+ source1Row, source2Row, depthMap1Row, depthMap2Row,
+ resultRow,
+ dm->selectionWidth);
+
+ gegl_buffer_set (result_buffer,
+ GEGL_RECTANGLE (dm->selectionX, y,
+ dm->selectionWidth, 1), 0,
+ babl_format ("R'G'B'A u8"), resultRow,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double)(y-dm->selectionY) /
+ (double)(dm->selectionHeight - 1));
+ }
+
+ g_free (resultRow);
+ g_free (source1Row);
+ g_free (source2Row);
+ g_free (depthMap1Row);
+ g_free (depthMap2Row);
+ g_free (tempRow);
+
+ gimp_progress_update (1.0);
+
+ if (source1_buffer)
+ g_object_unref (source1_buffer);
+
+ if (source2_buffer)
+ g_object_unref (source2_buffer);
+
+ if (depthMap1_buffer)
+ g_object_unref (depthMap1_buffer);
+
+ if (depthMap2_buffer)
+ g_object_unref (depthMap2_buffer);
+
+ g_object_unref (result_buffer);
+
+ gimp_drawable_merge_shadow (dm->resultDrawable_id, TRUE);
+ gimp_drawable_update (dm->resultDrawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ return TRUE;
+}
+
+static void
+DepthMerge_executeRegion (DepthMerge *dm,
+ guchar *source1Row,
+ guchar *source2Row,
+ guchar *depthMap1Row,
+ guchar *depthMap2Row,
+ guchar *resultRow,
+ gint length)
+{
+ gfloat scale1, scale2, offset255, invOverlap255;
+ gfloat frac, depth1, depth2;
+ gushort c1[4], c2[4];
+ gushort cR1[4] = { 0, 0, 0, 0 }, cR2[4] = { 0, 0, 0, 0 };
+ gushort cR[4], temp;
+ gint i, tempInt;
+
+ invOverlap255 = 1.0 / (MAX (dm->params.overlap, 0.001) * 255);
+ offset255 = dm->params.offset * 255;
+ scale1 = dm->params.scale1;
+ scale2 = dm->params.scale2;
+
+ for (i = 0; i < length; i++)
+ {
+ depth1 = (gfloat) depthMap1Row[i];
+ depth2 = (gfloat) depthMap2Row[i];
+
+ frac = (depth2 * scale2 - (depth1 * scale1 + offset255)) * invOverlap255;
+ frac = 0.5 * (frac + 1.0);
+ frac = CLAMP(frac, 0.0, 1.0);
+
+ /* c1 -> color corresponding to source1 */
+ c1[0] = source1Row[4 * i ];
+ c1[1] = source1Row[4 * i + 1];
+ c1[2] = source1Row[4 * i + 2];
+ c1[3] = source1Row[4 * i + 3];
+
+ /* c2 -> color corresponding to source2 */
+ c2[0] = source2Row[4 * i ];
+ c2[1] = source2Row[4 * i + 1];
+ c2[2] = source2Row[4 * i + 2];
+ c2[3] = source2Row[4 * i + 3];
+
+ if (frac != 0)
+ {
+ /* cR1 -> result if c1 is completely on top */
+ cR1[0] = c1[3] * c1[0] + (255 - c1[3]) * c2[0];
+ cR1[1] = c1[3] * c1[1] + (255 - c1[3]) * c2[1];
+ cR1[2] = c1[3] * c1[2] + (255 - c1[3]) * c2[2];
+ cR1[3] = MUL255 (c1[3]) + (255 - c1[3]) * c2[3];
+ }
+
+ if (frac != 1)
+ {
+ /* cR2 -> result if c2 is completely on top */
+ cR2[0] = c2[3] * c2[0] + (255 - c2[3]) * c1[0];
+ cR2[1] = c2[3] * c2[1] + (255 - c2[3]) * c1[1];
+ cR2[2] = c2[3] * c2[2] + (255 - c2[3]) * c1[2];
+ cR2[3] = MUL255 (c2[3]) + (255 - c2[3]) * c1[3];
+ }
+
+ if (frac == 1)
+ {
+ cR[0] = cR1[0];
+ cR[1] = cR1[1];
+ cR[2] = cR1[2];
+ cR[3] = cR1[3];
+ }
+ else if (frac == 0)
+ {
+ cR[0] = cR2[0];
+ cR[1] = cR2[1];
+ cR[2] = cR2[2];
+ cR[3] = cR2[3];
+ }
+ else
+ {
+ tempInt = LERP (frac, cR2[0], cR1[0]);
+ cR[0] = CLAMP (tempInt,0,255 * 255);
+ tempInt = LERP (frac, cR2[1], cR1[1]);
+ cR[1] = CLAMP (tempInt,0,255 * 255);
+ tempInt = LERP (frac, cR2[2], cR1[2]);
+ cR[2] = CLAMP (tempInt,0,255 * 255);
+ tempInt = LERP (frac, cR2[3], cR1[3]);
+ cR[3] = CLAMP (tempInt,0,255 * 255);
+ }
+
+ temp = DIV255 (cR[0]); resultRow[4 * i ] = MIN (temp, 255);
+ temp = DIV255 (cR[1]); resultRow[4 * i + 1] = MIN (temp, 255);
+ temp = DIV255 (cR[2]); resultRow[4 * i + 2] = MIN (temp, 255);
+ temp = DIV255 (cR[3]); resultRow[4 * i + 3] = MIN (temp, 255);
+ }
+}
+
+static gboolean
+DepthMerge_dialog (DepthMerge *dm)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkObject *adj;
+ gboolean run;
+
+ dm->interface = g_new0 (DepthMergeInterface, 1);
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dm->interface->dialog =
+ dialog = gimp_dialog_new (_("Depth Merge"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* Preview */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ dm->interface->previewWidth = MIN (dm->selectionWidth, PREVIEW_SIZE);
+ dm->interface->previewHeight = MIN (dm->selectionHeight, PREVIEW_SIZE);
+ dm->interface->preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (dm->interface->preview,
+ dm->interface->previewWidth,
+ dm->interface->previewHeight);
+ gtk_container_add (GTK_CONTAINER (frame), dm->interface->preview);
+ gtk_widget_show (dm->interface->preview);
+
+ DepthMerge_buildPreviewSourceImage (dm);
+
+ /* Source and Depth Map selection */
+ table = gtk_table_new (8, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 1, 12);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 3, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label = gtk_label_new (_("Source 1:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (dm_constraint, dm);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.source1,
+ G_CALLBACK (dialogSource1ChangedCallback),
+ dm);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ label = gtk_label_new(_("Depth map:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (dm_constraint, dm);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.depthMap1,
+ G_CALLBACK (dialogDepthMap1ChangedCallback),
+ dm);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ label = gtk_label_new (_("Source 2:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (dm_constraint, dm);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.source2,
+ G_CALLBACK (dialogSource2ChangedCallback),
+ dm);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 2, 3,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ label = gtk_label_new (_("Depth map:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (dm_constraint, dm);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.depthMap2,
+ G_CALLBACK (dialogDepthMap2ChangedCallback),
+ dm);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 3, 4,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ /* Numeric parameters */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("O_verlap:"), 0, 6,
+ dm->params.overlap, 0, 2, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialogValueScaleUpdateCallback),
+ &(dm->params.overlap));
+ g_object_set_data (G_OBJECT (adj), "dm", dm);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
+ _("O_ffset:"), 0, 6,
+ dm->params.offset, -1, 1, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialogValueScaleUpdateCallback),
+ &(dm->params.offset));
+ g_object_set_data (G_OBJECT (adj), "dm", dm);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
+ _("Sc_ale 1:"), 0, 6,
+ dm->params.scale1, -1, 1, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialogValueScaleUpdateCallback),
+ &(dm->params.scale1));
+ g_object_set_data (G_OBJECT (adj), "dm", dm);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 7,
+ _("Sca_le 2:"), 0, 6,
+ dm->params.scale2, -1, 1, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialogValueScaleUpdateCallback),
+ &(dm->params.scale2));
+ g_object_set_data (G_OBJECT (adj), "dm", dm);
+
+ dm->interface->active = TRUE;
+
+ gtk_widget_show (dm->interface->dialog);
+ DepthMerge_updatePreview (dm);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dm->interface->dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dm->interface->dialog);
+ dm->interface->dialog = NULL;
+
+ return run;
+}
+
+static void
+DepthMerge_buildPreviewSourceImage (DepthMerge *dm)
+{
+ dm->interface->previewSource1 =
+ g_new (guchar, dm->interface->previewWidth *
+ dm->interface->previewHeight * 4);
+ util_fillReducedBuffer (dm->interface->previewSource1,
+ babl_format ("R'G'B'A u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->source1Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ dm->interface->previewSource2 =
+ g_new (guchar, dm->interface->previewWidth *
+ dm->interface->previewHeight * 4);
+ util_fillReducedBuffer (dm->interface->previewSource2,
+ babl_format ("R'G'B'A u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->source2Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ dm->interface->previewDepthMap1 =
+ g_new (guchar, dm->interface->previewWidth *
+ dm->interface->previewHeight * 1);
+ util_fillReducedBuffer (dm->interface->previewDepthMap1,
+ babl_format ("Y' u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->depthMap1Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ dm->interface->previewDepthMap2 =
+ g_new (guchar, dm->interface->previewWidth *
+ dm->interface->previewHeight * 1);
+ util_fillReducedBuffer (dm->interface->previewDepthMap2,
+ babl_format ("Y' u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->depthMap2Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+}
+
+static void
+DepthMerge_updatePreview (DepthMerge *dm)
+{
+ gint y;
+ guchar *source1Row, *source2Row;
+ guchar *depthMap1Row, *depthMap2Row;
+ guchar *resultRGBA;
+
+ if (!dm->interface->active)
+ return;
+
+ resultRGBA = g_new (guchar, 4 * dm->interface->previewWidth *
+ dm->interface->previewHeight);
+
+ for (y = 0; y < dm->interface->previewHeight; y++)
+ {
+ source1Row =
+ &(dm->interface->previewSource1[ y * dm->interface->previewWidth * 4]);
+ source2Row =
+ &(dm->interface->previewSource2[ y * dm->interface->previewWidth * 4]);
+ depthMap1Row =
+ &(dm->interface->previewDepthMap1[y * dm->interface->previewWidth ]);
+ depthMap2Row =
+ &(dm->interface->previewDepthMap2[y * dm->interface->previewWidth ]);
+
+ DepthMerge_executeRegion(dm,
+ source1Row, source2Row, depthMap1Row, depthMap2Row,
+ resultRGBA + 4 * y * dm->interface->previewWidth,
+ dm->interface->previewWidth);
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (dm->interface->preview),
+ 0, 0,
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ GIMP_RGBA_IMAGE,
+ resultRGBA,
+ dm->interface->previewWidth * 4);
+ g_free(resultRGBA);
+}
+
+/* ----- Callbacks ----- */
+
+static gboolean
+dm_constraint (gint32 imageId,
+ gint32 drawableId,
+ gpointer data)
+{
+ DepthMerge *dm = (DepthMerge *)data;
+
+ return ((drawableId == -1) ||
+ ((gimp_drawable_width (drawableId) ==
+ gimp_drawable_width (dm->params.result)) &&
+ (gimp_drawable_height (drawableId) ==
+ gimp_drawable_height (dm->params.result)) &&
+ ((gimp_drawable_is_rgb (drawableId) &&
+ (gimp_drawable_is_rgb (dm->params.result))) ||
+ gimp_drawable_is_gray (drawableId))));
+}
+
+static void
+dialogSource1ChangedCallback (GtkWidget *widget,
+ DepthMerge *dm)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &dm->params.source1);
+
+ dm->source1Drawable_id = dm->params.source1;
+
+ util_fillReducedBuffer (dm->interface->previewSource1,
+ babl_format ("R'G'B'A u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->source1Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ DepthMerge_updatePreview (dm);
+}
+
+static void
+dialogSource2ChangedCallback (GtkWidget *widget,
+ DepthMerge *dm)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &dm->params.source2);
+
+ dm->source2Drawable_id = dm->params.source2;
+
+ util_fillReducedBuffer (dm->interface->previewSource2,
+ babl_format ("R'G'B'A u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->source2Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ DepthMerge_updatePreview (dm);
+}
+
+static void
+dialogDepthMap1ChangedCallback (GtkWidget *widget,
+ DepthMerge *dm)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &dm->params.depthMap1);
+
+ dm->depthMap1Drawable_id = dm->params.depthMap1;
+
+ util_fillReducedBuffer (dm->interface->previewDepthMap1,
+ babl_format ("Y' u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->depthMap1Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ DepthMerge_updatePreview (dm);
+}
+
+static void
+dialogDepthMap2ChangedCallback (GtkWidget *widget,
+ DepthMerge *dm)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &dm->params.depthMap2);
+
+ dm->depthMap2Drawable_id = dm->params.depthMap2;;
+
+ util_fillReducedBuffer (dm->interface->previewDepthMap2,
+ babl_format ("Y' u8"),
+ dm->interface->previewWidth,
+ dm->interface->previewHeight,
+ dm->depthMap2Drawable_id,
+ dm->selectionX, dm->selectionY,
+ dm->selectionWidth, dm->selectionHeight);
+
+ DepthMerge_updatePreview (dm);
+}
+
+static void
+dialogValueScaleUpdateCallback (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ DepthMerge *dm = g_object_get_data (G_OBJECT (adjustment), "dm");
+
+ gimp_float_adjustment_update (adjustment, data);
+
+ DepthMerge_updatePreview (dm);
+}
+
+/* ----- Utility routines ----- */
+
+static void
+util_fillReducedBuffer (guchar *dest,
+ const Babl *dest_format,
+ gint destWidth,
+ gint destHeight,
+ gint32 sourceDrawable_id,
+ gint x0,
+ gint y0,
+ gint sourceWidth,
+ gint sourceHeight)
+{
+ GeglBuffer *buffer;
+ guchar *sourceBuffer, *reducedRowBuffer;
+ guchar *sourceBufferPos, *reducedRowBufferPos;
+ guchar *sourceBufferRow;
+ gint x, y, i, yPrime;
+ gint destBPP;
+ gint *sourceRowOffsetLookup;
+
+ destBPP = babl_format_get_bytes_per_pixel (dest_format);
+
+ if ((sourceDrawable_id < 1) || (sourceWidth == 0) || (sourceHeight == 0))
+ {
+ for (x = 0; x < destWidth * destHeight * destBPP; x++)
+ dest[x] = 0;
+
+ return;
+ }
+
+ sourceBuffer = g_new (guchar, sourceWidth * sourceHeight * destBPP);
+ reducedRowBuffer = g_new (guchar, destWidth * destBPP);
+ sourceRowOffsetLookup = g_new (int, destWidth);
+
+ buffer = gimp_drawable_get_buffer (sourceDrawable_id);
+
+ for (x = 0; x < destWidth; x++)
+ sourceRowOffsetLookup[x] = (x * (sourceWidth - 1) / (destWidth - 1)) * destBPP;
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (x0, y0, sourceWidth, sourceHeight), 1.0,
+ dest_format, sourceBuffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (y = 0; y < destHeight; y++)
+ {
+ yPrime = y * (sourceHeight - 1) / (destHeight - 1);
+ sourceBufferRow = &(sourceBuffer[yPrime * sourceWidth * destBPP]);
+ reducedRowBufferPos = reducedRowBuffer;
+
+ for (x = 0; x < destWidth; x++)
+ {
+ sourceBufferPos = sourceBufferRow + sourceRowOffsetLookup[x];
+ for (i = 0; i < destBPP; i++)
+ reducedRowBufferPos[i] = sourceBufferPos[i];
+ reducedRowBufferPos += destBPP;
+ }
+
+ memcpy (&(dest[y * destWidth * destBPP]), reducedRowBuffer,
+ destWidth * destBPP);
+ }
+
+ g_object_unref (buffer);
+
+ g_free (sourceBuffer);
+ g_free (reducedRowBuffer);
+ g_free (sourceRowOffsetLookup);
+}
diff --git a/plug-ins/common/despeckle.c b/plug-ins/common/despeckle.c
new file mode 100644
index 0000000..8113343
--- /dev/null
+++ b/plug-ins/common/despeckle.c
@@ -0,0 +1,901 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Despeckle (adaptive median) filter
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
+ * optimized in 2010 by Przemyslaw Zych (kermidt.zed@gmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*
+ * Constants...
+ */
+
+#define PLUG_IN_PROC "plug-in-despeckle"
+#define PLUG_IN_BINARY "despeckle"
+#define PLUG_IN_ROLE "gimp-despeckle"
+#define PLUG_IN_VERSION "May 2010"
+#define SCALE_WIDTH 100
+#define ENTRY_WIDTH 3
+#define MAX_RADIUS 30
+
+#define FILTER_ADAPTIVE 0x01
+#define FILTER_RECURSIVE 0x02
+
+#define despeckle_radius (despeckle_vals[0]) /* diameter of filter */
+#define filter_type (despeckle_vals[1]) /* filter type */
+#define black_level (despeckle_vals[2]) /* Black level */
+#define white_level (despeckle_vals[3]) /* White level */
+
+/* List that stores pixels falling in to the same luma bucket */
+#define MAX_LIST_ELEMS SQR(2 * MAX_RADIUS + 1)
+
+typedef struct
+{
+ const guchar *elems[MAX_LIST_ELEMS];
+ gint start;
+ gint count;
+} PixelsList;
+
+typedef struct
+{
+ gint elems[256]; /* Number of pixels that fall into each luma bucket */
+ PixelsList origs[256]; /* Original pixels */
+ gint xmin;
+ gint ymin;
+ gint xmax;
+ gint ymax; /* Source rect */
+} DespeckleHistogram;
+
+/* Number of pixels in actual histogram falling into each category */
+static gint hist0; /* Less than min threshold */
+static gint hist255; /* More than max threshold */
+static gint histrest; /* From min to max */
+
+static DespeckleHistogram histogram;
+
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void despeckle (void);
+static void despeckle_median (guchar *src,
+ guchar *dst,
+ gint width,
+ gint height,
+ gint bpp,
+ gint radius,
+ gboolean preview);
+
+static gboolean despeckle_dialog (void);
+
+static void dialog_adaptive_callback (GtkWidget *widget,
+ gpointer data);
+static void dialog_recursive_callback (GtkWidget *widget,
+ gpointer data);
+
+static void preview_update (GtkWidget *preview);
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run /* run */
+};
+
+static GtkWidget *preview; /* Preview widget */
+static gint32 drawable_ID = -1; /* Current drawable */
+
+
+static gint despeckle_vals[4] =
+{
+ 3, /* Default value for the diameter */
+ FILTER_ADAPTIVE, /* Default value for the filter type */
+ 7, /* Default value for the black level */
+ 248 /* Default value for the white level */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "radius", "Filter box radius (default = 3)" },
+ { GIMP_PDB_INT32, "type", "Filter type { MEDIAN (0), ADAPTIVE (1), RECURSIVE-MEDIAN (2), RECURSIVE-ADAPTIVE (3) }" },
+ { GIMP_PDB_INT32, "black", "Black level (-1 to 255)" },
+ { GIMP_PDB_INT32, "white", "White level (0 to 256)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Remove speckle noise from the image"),
+ "This plug-in selectively performs a median or "
+ "adaptive box filter on an image.",
+ "Michael Sweet <mike@easysw.com>",
+ "Copyright 1997-1998 by Michael Sweet",
+ PLUG_IN_VERSION,
+ N_("Des_peckle..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Enhance");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ static GimpParam values[1];
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE :
+ gimp_get_data (PLUG_IN_PROC, &despeckle_radius);
+
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ if (! despeckle_dialog ())
+ return;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams < 4 || nparams > 9)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else if (nparams == 4)
+ {
+ despeckle_radius = param[3].data.d_int32;
+ filter_type = FILTER_ADAPTIVE;
+ black_level = 7;
+ white_level = 248;
+ }
+ else if (nparams == 5)
+ {
+ despeckle_radius = param[3].data.d_int32;
+ filter_type = param[4].data.d_int32;
+ black_level = 7;
+ white_level = 248;
+ }
+ else if (nparams == 6)
+ {
+ despeckle_radius = param[3].data.d_int32;
+ filter_type = param[4].data.d_int32;
+ black_level = param[5].data.d_int32;
+ white_level = 248;
+ }
+ else
+ {
+ despeckle_radius = param[3].data.d_int32;
+ filter_type = param[4].data.d_int32;
+ black_level = param[5].data.d_int32;
+ white_level = param[6].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, despeckle_vals);
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ despeckle ();
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC,
+ despeckle_vals, sizeof (despeckle_vals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+static inline guchar
+pixel_luminance (const guchar *p,
+ gint bpp)
+{
+ switch (bpp)
+ {
+ case 1:
+ case 2:
+ return p[0];
+
+ case 3:
+ case 4:
+ return GIMP_RGB_LUMINANCE (p[0], p[1], p[2]);
+
+ default:
+ return 0; /* should not be reached */
+ }
+}
+
+static inline void
+pixel_copy (guchar *dest,
+ const guchar *src,
+ gint bpp)
+{
+ switch (bpp)
+ {
+ case 4:
+ *dest++ = *src++;
+ case 3:
+ *dest++ = *src++;
+ case 2:
+ *dest++ = *src++;
+ case 1:
+ *dest++ = *src++;
+ }
+}
+
+/*
+ * 'despeckle()' - Despeckle an image using a median filter.
+ *
+ * A median filter basically collects pixel values in a region around the
+ * target pixel, sorts them, and uses the median value. This code uses a
+ * circular row buffer to improve performance.
+ *
+ * The adaptive filter is based on the median filter but analyzes the histogram
+ * of the region around the target pixel and adjusts the despeckle diameter
+ * accordingly.
+ */
+
+static void
+despeckle (void)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ guchar *src;
+ guchar *dst;
+ gint img_bpp;
+ gint x, y;
+ gint width, height;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x, &y, &width, &height))
+ return;
+
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+
+ img_bpp = babl_format_get_bytes_per_pixel (format);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ src = g_new (guchar, width * height * img_bpp);
+ dst = g_new (guchar, width * height * img_bpp);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, y, width, height), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ despeckle_median (src, dst, width, height, img_bpp, despeckle_radius, FALSE);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, width, height), 0,
+ format, dst,
+ GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, x, y, width, height);
+
+ g_free (dst);
+ g_free (src);
+}
+
+static gboolean
+despeckle_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Despeckle"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect (preview, "invalidated",
+ G_CALLBACK (preview_update),
+ NULL);
+
+ frame = gimp_frame_new (_("Median"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ button = gtk_check_button_new_with_mnemonic (_("_Adaptive"));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ filter_type & FILTER_ADAPTIVE);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (dialog_adaptive_callback),
+ NULL);
+
+ button = gtk_check_button_new_with_mnemonic (_("R_ecursive"));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ filter_type & FILTER_RECURSIVE);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (dialog_recursive_callback),
+ NULL);
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Box size (diameter) control...
+ */
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Radius:"), SCALE_WIDTH, ENTRY_WIDTH,
+ despeckle_radius, 1, MAX_RADIUS, 1, 5, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &despeckle_radius);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /*
+ * Black level control...
+ */
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Black level:"), SCALE_WIDTH, ENTRY_WIDTH,
+ black_level, -1, 255, 1, 8, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &black_level);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /*
+ * White level control...
+ */
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_White level:"), SCALE_WIDTH, ENTRY_WIDTH,
+ white_level, 0, 256, 1, 8, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &white_level);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+preview_update (GtkWidget *widget)
+{
+ GimpPreview *preview = GIMP_PREVIEW (widget);
+ GeglBuffer *src_buffer;
+ const Babl *format;
+ guchar *dst;
+ guchar *src;
+ gint img_bpp;
+ gint x1,y1;
+ gint width, height;
+
+ preview = GIMP_PREVIEW (widget);
+
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+
+ img_bpp = babl_format_get_bytes_per_pixel (format);
+
+ width = preview->width;
+ height = preview->height;
+
+ gimp_preview_get_position (preview, &x1, &y1);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ dst = g_new (guchar, width * height * img_bpp);
+ src = g_new (guchar, width * height * img_bpp);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ despeckle_median (src, dst, width, height, img_bpp, despeckle_radius, TRUE);
+
+ gimp_preview_draw_buffer (preview, dst, width * img_bpp);
+
+ g_object_unref (src_buffer);
+
+ g_free (src);
+ g_free (dst);
+}
+
+static void
+dialog_adaptive_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ filter_type |= FILTER_ADAPTIVE;
+ else
+ filter_type &= ~FILTER_ADAPTIVE;
+
+ gimp_preview_invalidate (GIMP_PREVIEW (preview));
+}
+
+static void
+dialog_recursive_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ filter_type |= FILTER_RECURSIVE;
+ else
+ filter_type &= ~FILTER_RECURSIVE;
+
+ gimp_preview_invalidate (GIMP_PREVIEW (preview));
+}
+
+
+static inline void
+list_add_elem (PixelsList *list,
+ const guchar *elem)
+{
+ const gint pos = list->start + list->count++;
+
+ list->elems[pos >= MAX_LIST_ELEMS ? pos - MAX_LIST_ELEMS : pos] = elem;
+}
+
+static inline void
+list_del_elem (PixelsList* list)
+{
+ list->count--;
+ list->start++;
+
+ if (list->start >= MAX_LIST_ELEMS)
+ list->start = 0;
+}
+
+static inline const guchar *
+list_get_random_elem (PixelsList *list)
+{
+ const gint pos = list->start + rand () % list->count;
+
+ if (pos >= MAX_LIST_ELEMS)
+ return list->elems[pos - MAX_LIST_ELEMS];
+
+ return list->elems[pos];
+}
+
+static inline void
+histogram_add (DespeckleHistogram *hist,
+ guchar val,
+ const guchar *orig)
+{
+ hist->elems[val]++;
+ list_add_elem (&hist->origs[val], orig);
+}
+
+static inline void
+histogram_remove (DespeckleHistogram *hist,
+ guchar val)
+{
+ hist->elems[val]--;
+ list_del_elem (&hist->origs[val]);
+}
+
+static inline void
+histogram_clean (DespeckleHistogram *hist)
+{
+ gint i;
+
+ for (i = 0; i < 256; i++)
+ {
+ hist->elems[i] = 0;
+ hist->origs[i].count = 0;
+ }
+}
+
+static inline const guchar *
+histogram_get_median (DespeckleHistogram *hist,
+ const guchar *_default)
+{
+ gint count = histrest;
+ gint i;
+ gint sum = 0;
+
+ if (! count)
+ return _default;
+
+ count = (count + 1) / 2;
+
+ i = 0;
+ while ((sum += hist->elems[i]) < count)
+ i++;
+
+ return list_get_random_elem (&hist->origs[i]);
+}
+
+static inline void
+add_val (DespeckleHistogram *hist,
+ const guchar *src,
+ gint width,
+ gint bpp,
+ gint x,
+ gint y)
+{
+ const gint pos = (x + (y * width)) * bpp;
+ const gint value = pixel_luminance (src + pos, bpp);
+
+ if (value > black_level && value < white_level)
+ {
+ histogram_add (hist, value, src + pos);
+ histrest++;
+ }
+ else
+ {
+ if (value <= black_level)
+ hist0++;
+
+ if (value >= white_level)
+ hist255++;
+ }
+}
+
+static inline void
+del_val (DespeckleHistogram *hist,
+ const guchar *src,
+ gint width,
+ gint bpp,
+ gint x,
+ gint y)
+{
+ const gint pos = (x + (y * width)) * bpp;
+ const gint value = pixel_luminance (src + pos, bpp);
+
+ if (value > black_level && value < white_level)
+ {
+ histogram_remove (hist, value);
+ histrest--;
+ }
+ else
+ {
+ if (value <= black_level)
+ hist0--;
+
+ if (value >= white_level)
+ hist255--;
+ }
+}
+
+static inline void
+add_vals (DespeckleHistogram *hist,
+ const guchar *src,
+ gint width,
+ gint bpp,
+ gint xmin,
+ gint ymin,
+ gint xmax,
+ gint ymax)
+{
+ gint x;
+ gint y;
+
+ if (xmin > xmax)
+ return;
+
+ for (y = ymin; y <= ymax; y++)
+ {
+ for (x = xmin; x <= xmax; x++)
+ {
+ add_val (hist, src, width, bpp, x, y);
+ }
+ }
+}
+
+static inline void
+del_vals (DespeckleHistogram *hist,
+ const guchar *src,
+ gint width,
+ gint bpp,
+ gint xmin,
+ gint ymin,
+ gint xmax,
+ gint ymax)
+{
+ gint x;
+ gint y;
+
+ if (xmin > xmax)
+ return;
+
+ for (y = ymin; y <= ymax; y++)
+ {
+ for (x = xmin; x <= xmax; x++)
+ {
+ del_val (hist, src, width, bpp, x, y);
+ }
+ }
+}
+
+static inline void
+update_histogram (DespeckleHistogram *hist,
+ const guchar *src,
+ gint width,
+ gint bpp,
+ gint xmin,
+ gint ymin,
+ gint xmax,
+ gint ymax)
+{
+ /* assuming that radious of the box can change no more than one
+ pixel in each call */
+ /* assuming that box is moving either right or down */
+
+ del_vals (hist,
+ src, width, bpp, hist->xmin, hist->ymin, xmin - 1, hist->ymax);
+ del_vals (hist, src, width, bpp, xmin, hist->ymin, xmax, ymin - 1);
+ del_vals (hist, src, width, bpp, xmin, ymax + 1, xmax, hist->ymax);
+
+ add_vals (hist, src, width, bpp, hist->xmax + 1, ymin, xmax, ymax);
+ add_vals (hist, src, width, bpp, xmin, ymin, hist->xmax, hist->ymin - 1);
+ add_vals (hist,
+ src, width, bpp, hist->xmin, hist->ymax + 1, hist->xmax, ymax);
+
+ hist->xmin = xmin;
+ hist->ymin = ymin;
+ hist->xmax = xmax;
+ hist->ymax = ymax;
+}
+
+static void
+despeckle_median (guchar *src,
+ guchar *dst,
+ gint width,
+ gint height,
+ gint bpp,
+ gint radius,
+ gboolean preview)
+{
+ guint progress;
+ guint max_progress;
+ gint x, y;
+ gint adapt_radius;
+ gint pos;
+ gint ymin;
+ gint ymax;
+ gint xmin;
+ gint xmax;
+
+ memset (&histogram, 0, sizeof(histogram));
+ progress = 0;
+ max_progress = width * height;
+
+ if (! preview)
+ gimp_progress_init (_("Despeckle"));
+
+ adapt_radius = radius;
+ for (y = 0; y < height; y++)
+ {
+ x = 0;
+ ymin = MAX (0, y - adapt_radius);
+ ymax = MIN (height - 1, y + adapt_radius);
+ xmin = MAX (0, x - adapt_radius);
+ xmax = MIN (width - 1, x + adapt_radius);
+ hist0 = 0;
+ histrest = 0;
+ hist255 = 0;
+ histogram_clean (&histogram);
+ histogram.xmin = xmin;
+ histogram.ymin = ymin;
+ histogram.xmax = xmax;
+ histogram.ymax = ymax;
+ add_vals (&histogram,
+ src, width, bpp,
+ histogram.xmin, histogram.ymin,
+ histogram.xmax, histogram.ymax);
+
+ for (x = 0; x < width; x++)
+ {
+ const guchar *pixel;
+
+ ymin = MAX (0, y - adapt_radius); /* update ymin, ymax when adapt_radius changed (FILTER_ADAPTIVE) */
+ ymax = MIN (height - 1, y + adapt_radius);
+ xmin = MAX (0, x - adapt_radius);
+ xmax = MIN (width - 1, x + adapt_radius);
+
+ update_histogram (&histogram,
+ src, width, bpp, xmin, ymin, xmax, ymax);
+
+ pos = (x + (y * width)) * bpp;
+ pixel = histogram_get_median (&histogram, src + pos);
+
+ if (filter_type & FILTER_RECURSIVE)
+ {
+ del_val (&histogram, src, width, bpp, x, y);
+ pixel_copy (src + pos, pixel, bpp);
+ add_val (&histogram, src, width, bpp, x, y);
+ }
+
+ pixel_copy (dst + pos, pixel, bpp);
+
+ /*
+ * Check the histogram and adjust the diameter accordingly...
+ */
+ if (filter_type & FILTER_ADAPTIVE)
+ {
+ if (hist0 >= adapt_radius || hist255 >= adapt_radius)
+ {
+ if (adapt_radius < radius)
+ adapt_radius++;
+ }
+ else if (adapt_radius > 1)
+ {
+ adapt_radius--;
+ }
+ }
+ }
+
+ progress += width;
+
+ if (! preview && y % 32 == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+
+ if (! preview)
+ gimp_progress_update (1.0);
+}
diff --git a/plug-ins/common/destripe.c b/plug-ins/common/destripe.c
new file mode 100644
index 0000000..d9994a6
--- /dev/null
+++ b/plug-ins/common/destripe.c
@@ -0,0 +1,530 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Destripe filter
+ *
+ * Copyright 1997 Marc Lehmann, heavily modified from a filter by
+ * Michael Sweet.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*
+ * Constants...
+ */
+
+#define PLUG_IN_PROC "plug-in-destripe"
+#define PLUG_IN_BINARY "destripe"
+#define PLUG_IN_ROLE "gimp-destripe"
+#define PLUG_IN_VERSION "0.2"
+#define SCALE_WIDTH 140
+#define MAX_AVG 100
+
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void destripe (gint32 drawable_ID,
+ GimpPreview *preview);
+static void destripe_preview (gpointer drawable_ID,
+ GimpPreview *preview);
+
+static gboolean destripe_dialog (gint32 drawable_ID);
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+typedef struct
+{
+ gboolean histogram;
+ gint avg_width;
+ gboolean preview;
+} DestripeValues;
+
+static DestripeValues vals =
+{
+ FALSE, /* histogram */
+ 36, /* average width */
+ TRUE /* preview */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "avg-width", "Averaging filter width (default = 36)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Remove vertical stripe artifacts from the image"),
+ "This plug-in tries to remove vertical stripes from "
+ "an image.",
+ "Marc Lehmann <pcg@goof.com>",
+ "Marc Lehmann <pcg@goof.com>",
+ PLUG_IN_VERSION,
+ N_("Des_tripe..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Enhance");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1]; /* Return values */
+ GimpPDBStatusType status; /* Return status */
+ GimpRunMode run_mode; /* Current run mode */
+ gint32 drawable_ID;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ status = GIMP_PDB_SUCCESS;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (PLUG_IN_PROC, &vals);
+
+ /*
+ * Get information from the dialog...
+ */
+ if (! destripe_dialog (drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are present...
+ */
+ if (nparams != 4)
+ status = GIMP_PDB_CALLING_ERROR;
+ else
+ vals.avg_width = param[3].data.d_int32;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS :
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (PLUG_IN_PROC, &vals);
+ break;
+
+ default :
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ };
+
+ /*
+ * Destripe the image...
+ */
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if ((gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID)))
+ {
+ /*
+ * Run!
+ */
+ destripe (drawable_ID, NULL);
+
+ /*
+ * If run mode is interactive, flush displays...
+ */
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /*
+ * Store data...
+ */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &vals, sizeof (vals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ };
+
+ /*
+ * Reset the current run status...
+ */
+ values[0].data.d_status = status;
+}
+
+static void
+destripe (gint32 drawable_ID,
+ GimpPreview *preview)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ guchar *src_rows; /* image data */
+ gdouble progress, progress_inc;
+ gint x1, x2, y1;
+ gint width, height;
+ gint bpp;
+ glong *hist, *corr; /* "histogram" data */
+ gint tile_width = gimp_tile_width ();
+ gint i, x, y, ox, cols;
+
+ progress = 0.0;
+ progress_inc = 0.0;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+ }
+ else
+ {
+ gimp_progress_init (_("Destriping"));
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x1, &y1, &width, &height))
+ {
+ return;
+ }
+
+ progress = 0;
+ progress_inc = 0.5 * tile_width / width;
+ }
+
+ x2 = x1 + width;
+
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ /*
+ * Setup for filter...
+ */
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ hist = g_new (long, width * bpp);
+ corr = g_new (long, width * bpp);
+ src_rows = g_new (guchar, tile_width * height * bpp);
+
+ memset (hist, 0, width * bpp * sizeof (long));
+
+ /*
+ * collect "histogram" data.
+ */
+
+ for (ox = x1; ox < x2; ox += tile_width)
+ {
+ guchar *rows = src_rows;
+
+ cols = x2 - ox;
+ if (cols > tile_width)
+ cols = tile_width;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
+ format, rows,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (y = 0; y < height; y++)
+ {
+ long *h = hist + (ox - x1) * bpp;
+ guchar *row_end = rows + cols * bpp;
+
+ while (rows < row_end)
+ *h++ += *rows++;
+ }
+
+ if (! preview)
+ gimp_progress_update (progress += progress_inc);
+ }
+
+ /*
+ * average out histogram
+ */
+
+ {
+ gint extend = (vals.avg_width / 2) * bpp;
+
+ for (i = 0; i < MIN (3, bpp); i++)
+ {
+ long *h = hist - extend + i;
+ long *c = corr - extend + i;
+ long sum = 0;
+ gint cnt = 0;
+
+ for (x = -extend; x < width * bpp; x += bpp)
+ {
+ if (x + extend < width * bpp)
+ {
+ sum += h[ extend]; cnt++;
+ }
+
+ if (x - extend >= 0)
+ {
+ sum -= h[-extend]; cnt--;
+ }
+
+ if (x >= 0)
+ {
+ if (*h)
+ *c = ((sum / cnt - *h) << 10) / *h;
+ else
+ *c = G_MAXINT;
+ }
+
+ h += bpp;
+ c += bpp;
+ }
+ }
+ }
+
+ /*
+ * remove stripes.
+ */
+
+ for (ox = x1; ox < x2; ox += tile_width)
+ {
+ guchar *rows = src_rows;
+
+ cols = x2 - ox;
+ if (cols > tile_width)
+ cols = tile_width;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 1.0,
+ format, rows,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (! preview)
+ gimp_progress_update (progress += progress_inc);
+
+ for (y = 0; y < height; y++)
+ {
+ long *c = corr + (ox - x1) * bpp;
+ guchar *row_end = rows + cols * bpp;
+
+ if (vals.histogram)
+ {
+ while (rows < row_end)
+ {
+ *rows = MIN (255, MAX (0, 128 + (*rows * *c >> 10)));
+ c++; rows++;
+ }
+ }
+ else
+ {
+ while (rows < row_end)
+ {
+ *rows = MIN (255, MAX (0, *rows + (*rows * *c >> 10) ));
+ c++; rows++;
+ }
+ }
+ }
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (ox, y1, cols, height), 0,
+ format, src_rows,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (! preview)
+ gimp_progress_update (progress += progress_inc);
+ }
+
+ g_free (src_rows);
+
+ g_object_unref (src_buffer);
+
+ if (preview)
+ {
+ guchar *buffer = g_new (guchar, width * height * bpp);
+
+ gegl_buffer_get (dest_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
+ format, buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gimp_preview_draw_buffer (GIMP_PREVIEW (preview),
+ buffer, width * bpp);
+
+ g_free (buffer);
+ g_object_unref (dest_buffer);
+ }
+ else
+ {
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID,
+ x1, y1, width, height);
+ }
+
+ g_free (hist);
+ g_free (corr);
+}
+
+static void
+destripe_preview (gpointer drawable_ID,
+ GimpPreview *preview)
+{
+ destripe (GPOINTER_TO_INT (drawable_ID), preview);
+}
+
+
+static gboolean
+destripe_dialog (gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Destripe"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (destripe_preview),
+ GINT_TO_POINTER (drawable_ID));
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Width:"), SCALE_WIDTH, 0,
+ vals.avg_width, 2, MAX_AVG, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &vals.avg_width);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ button = gtk_check_button_new_with_mnemonic (_("Create _histogram"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), vals.histogram);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &vals.histogram);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/edge-dog.c b/plug-ins/common/edge-dog.c
new file mode 100644
index 0000000..cb6701f
--- /dev/null
+++ b/plug-ins/common/edge-dog.c
@@ -0,0 +1,1029 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Gimp plug-in dog.c: (C) 2004 William Skaggs
+ *
+ * Edge detection using the "Difference of Gaussians" method.
+ * Finds edges by doing two Gaussian blurs with different radius, and
+ * subtracting the results. Blurring is done using code taken from
+ * gauss_rle.c (as of Gimp 2.1, incorporated into gauss.c).
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-dog"
+#define PLUG_IN_BINARY "edge-dog"
+#define PLUG_IN_ROLE "gimp-edge-dog"
+
+
+typedef struct
+{
+ gdouble inner;
+ gdouble outer;
+ gboolean normalize;
+ gboolean invert;
+} DoGValues;
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint dog_dialog (gint32 image_ID,
+ GimpDrawable *drawable);
+
+static void gauss_rle (GimpDrawable *drawable,
+ gdouble radius,
+ gint pass,
+ gboolean show_progress);
+
+static void compute_difference (GimpDrawable *drawable,
+ GimpDrawable *drawable1,
+ GimpDrawable *drawable2,
+ guchar *maxval);
+
+static void normalize_invert (GimpDrawable *drawable,
+ gboolean normalize,
+ guint maxval,
+ gboolean invert);
+
+static void dog (gint32 image_ID,
+ GimpDrawable *drawable,
+ gdouble inner,
+ gdouble outer,
+ gboolean show_progress);
+
+static void preview_update_preview (GimpPreview *preview,
+ GimpDrawable *drawable);
+static void change_radius_callback (GtkWidget *widget,
+ gpointer data);
+
+
+
+/*
+ * Gaussian blur helper functions
+ */
+static gint * make_curve (gdouble sigma,
+ gint *length);
+static void run_length_encode (guchar *src,
+ gint *dest,
+ gint bytes,
+ gint width);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static DoGValues dogvals =
+{
+ 3.0, /* inner radius */
+ 1.0, /* outer radius */
+ TRUE, /* normalize */
+ TRUE /* invert */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "inner", "Radius of inner gaussian blur (in pixels, > 0.0)" },
+ { GIMP_PDB_FLOAT, "outer", "Radius of outer gaussian blur (in pixels, > 0.0)" },
+ { GIMP_PDB_INT32, "normalize", "Normalize { TRUE, FALSE }" },
+ { GIMP_PDB_INT32, "invert", "Invert { TRUE, FALSE }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Edge detection with control of edge thickness"),
+ "Applies two Gaussian blurs to the drawable, and "
+ "subtracts the results. This is robust and widely "
+ "used method for detecting edges.",
+ "Spencer Kimball, Peter Mattis, Sven Neumann, William Skaggs",
+ "Spencer Kimball, Peter Mattis, Sven Neumann, William Skaggs",
+ "1995-2004",
+ N_("_Difference of Gaussians (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Edge-Detect");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ gint32 image_ID;
+ GimpDrawable *drawable;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ if (! gimp_item_is_layer (param[2].data.d_drawable))
+ {
+ g_set_error (&error, 0, 0, "%s",
+ _("Can operate on layers only "
+ "(but was called on channel or mask)."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Get the specified image and drawable */
+ image_ID = param[1].data.d_image;
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ /* set the tile cache size so that the gaussian blur works well */
+ gimp_tile_cache_ntiles (2 *
+ (MAX (drawable->width, drawable->height) /
+ gimp_tile_width () + 1));
+
+ if (strcmp (name, PLUG_IN_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &dogvals);
+
+ /* First acquire information with a dialog */
+ if (! dog_dialog (image_ID, drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 7)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ dogvals.inner = param[3].data.d_float;
+ dogvals.outer = param[4].data.d_float;
+ dogvals.normalize = param[5].data.d_int32;
+ dogvals.invert = param[6].data.d_int32;
+
+ if (dogvals.inner <= 0.0 || dogvals.outer <= 0.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &dogvals);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init (_("DoG Edge Detect"));
+
+ /* run the Difference of Gaussians */
+ gimp_image_undo_group_start (image_ID);
+
+ dog (image_ID, drawable, dogvals.inner, dogvals.outer, TRUE);
+
+ gimp_image_undo_group_end (image_ID);
+
+ gimp_progress_update (1.0);
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &dogvals, sizeof (DoGValues));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("Cannot operate on indexed color images.");
+ }
+
+ gimp_drawable_detach (drawable);
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint
+dog_dialog (gint32 image_ID,
+ GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *coord;
+ GimpUnit unit;
+ gdouble xres;
+ gdouble yres;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("DoG Edge Detect"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, FALSE, FALSE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect (preview, "invalidated",
+ G_CALLBACK (preview_update_preview),
+ drawable);
+
+ frame = gimp_frame_new (_("Smoothing Parameters"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ /* Get the image resolution and unit */
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ unit = gimp_image_get_unit (image_ID);
+
+ coord = gimp_coordinates_new (unit, "%a", TRUE, FALSE, -1,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE,
+
+ FALSE,
+ TRUE,
+
+ _("_Radius 1:"), dogvals.inner, xres,
+ 0, 8 * MAX (drawable->width, drawable->height),
+ 0, 0,
+
+ _("R_adius 2:"), dogvals.outer, yres,
+ 0, 8 * MAX (drawable->width, drawable->height),
+ 0, 0);
+
+ gtk_container_add (GTK_CONTAINER (frame), coord);
+ gtk_widget_show (coord);
+
+ gimp_size_entry_set_pixel_digits (GIMP_SIZE_ENTRY (coord), 1);
+ g_signal_connect (coord, "value-changed",
+ G_CALLBACK (change_radius_callback),
+ preview);
+
+ button = gtk_check_button_new_with_mnemonic (_("_Normalize"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), dogvals.normalize);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &dogvals.normalize);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ gtk_widget_show (button);
+
+ button = gtk_check_button_new_with_mnemonic (_("_Invert"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), dogvals.invert);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &dogvals.invert);
+ g_signal_connect_swapped (button, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ gtk_widget_show (button);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ dogvals.inner = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (coord), 0);
+ dogvals.outer = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (coord), 1);
+ }
+
+ gtk_widget_destroy (dialog);
+
+
+ return run;
+}
+
+
+/* Convert from separated to premultiplied alpha, on a single scan line. */
+static void
+multiply_alpha (guchar *buf,
+ gint width,
+ gint bytes)
+{
+ gint i, j;
+ gdouble alpha;
+
+ for (i = 0; i < width * bytes; i += bytes)
+ {
+ alpha = buf[i + bytes - 1] * (1.0 / 255.0);
+
+ for (j = 0; j < bytes - 1; j++)
+ buf[i + j] *= alpha;
+ }
+}
+
+/* Convert from premultiplied to separated alpha, on a single scan
+ line. */
+static void
+separate_alpha (guchar *buf,
+ gint width,
+ gint bytes)
+{
+ gint i, j;
+ guchar alpha;
+ gdouble recip_alpha;
+ gint new_val;
+
+ for (i = 0; i < width * bytes; i += bytes)
+ {
+ alpha = buf[i + bytes - 1];
+
+ if (alpha != 0 && alpha != 255)
+ {
+ recip_alpha = 255.0 / alpha;
+
+ for (j = 0; j < bytes - 1; j++)
+ {
+ new_val = buf[i + j] * recip_alpha;
+ buf[i + j] = MIN (255, new_val);
+ }
+ }
+ }
+}
+
+static void
+dog (gint32 image_ID,
+ GimpDrawable *drawable,
+ gdouble inner,
+ gdouble outer,
+ gboolean show_progress)
+{
+ GimpDrawable *drawable1;
+ GimpDrawable *drawable2;
+ gint32 drawable_id = drawable->drawable_id;
+ gint32 layer1;
+ gint32 layer2;
+ gint width, height;
+ gint x1, y1;
+ guchar maxval = 255;
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &x1, &y1, &width, &height))
+ return;
+
+ gimp_drawable_flush (drawable);
+
+ layer1 = gimp_layer_copy (drawable_id);
+ gimp_item_set_visible (layer1, FALSE);
+ gimp_item_set_name (layer1, "dog_scratch_layer1");
+ gimp_image_insert_layer (image_ID, layer1,
+ gimp_item_get_parent (drawable_id), 0);
+
+ layer2 = gimp_layer_copy (drawable_id);
+ gimp_item_set_visible (layer2, FALSE);
+ gimp_item_set_name (layer2, "dog_scratch_layer2");
+ gimp_image_insert_layer (image_ID, layer2,
+ gimp_item_get_parent (drawable_id), 0);
+
+ drawable1 = gimp_drawable_get (layer1);
+ drawable2 = gimp_drawable_get (layer2);
+
+ gauss_rle (drawable1, inner, 0, show_progress);
+ gauss_rle (drawable2, outer, 1, show_progress);
+
+ compute_difference (drawable, drawable1, drawable2, &maxval);
+
+ gimp_drawable_detach (drawable1);
+ gimp_drawable_detach (drawable2);
+
+ gimp_image_remove_layer (image_ID, layer1);
+ gimp_image_remove_layer (image_ID, layer2);
+
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x1, y1, width, height);
+
+ if (dogvals.normalize || dogvals.invert)
+ /* gimp_invert doesn't work properly with previews due to shadow handling
+ * so reimplement it here - see Bug 557380
+ */
+ {
+ normalize_invert (drawable, dogvals.normalize, maxval, dogvals.invert);
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x1, y1, width, height);
+ }
+}
+
+
+static void
+compute_difference (GimpDrawable *drawable,
+ GimpDrawable *drawable1,
+ GimpDrawable *drawable2,
+ guchar *maxval)
+{
+ GimpPixelRgn src1_rgn, src2_rgn, dest_rgn;
+ gint width, height;
+ gint bpp;
+ gpointer pr;
+ gint x, y, k;
+ gint x1, y1;
+ gboolean has_alpha;
+
+ *maxval = 0;
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ bpp = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ gimp_pixel_rgn_init (&src1_rgn,
+ drawable1, 0, 0, drawable1->width, drawable1->height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&src2_rgn,
+ drawable2, 0, 0, drawable1->width, drawable1->height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&dest_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (3, &src1_rgn, &src2_rgn, &dest_rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ guchar *src1 = src1_rgn.data;
+ guchar *src2 = src2_rgn.data;
+ guchar *dest = dest_rgn.data;
+ gint row = src1_rgn.y - y1;
+
+ for (y = 0; y < src1_rgn.h; y++, row++)
+ {
+ guchar *s1 = src1;
+ guchar *s2 = src2;
+ guchar *d = dest;
+ gint col = src1_rgn.x - x1;
+
+ for (x = 0; x < src1_rgn.w; x++, col++)
+ {
+
+ if (has_alpha)
+ {
+ for (k = 0; k < bpp-1; k++)
+ {
+ d[k] = CLAMP0255 (s1[k] - s2[k]);
+ *maxval = MAX (d[k], *maxval);
+ }
+ }
+ else
+ {
+ for (k = 0; k < bpp; k++)
+ {
+ d[k] = CLAMP0255 (s1[k] - s2[k]);
+ *maxval = MAX (d[k], *maxval);
+ }
+ }
+
+ s1 += bpp;
+ s2 += bpp;
+ d += bpp;
+ }
+
+ src1 += src1_rgn.rowstride;
+ src2 += src2_rgn.rowstride;
+ dest += dest_rgn.rowstride;
+ }
+ }
+}
+
+
+static void
+normalize_invert (GimpDrawable *drawable,
+ gboolean normalize,
+ guint maxval,
+ gboolean invert)
+{
+ GimpPixelRgn src_rgn, dest_rgn;
+ gint bpp;
+ gpointer pr;
+ gint x, y, k;
+ gint x1, y1;
+ gint width, height;
+ gboolean has_alpha;
+ gdouble factor;
+
+ if (normalize && maxval != 0) {
+ factor = 255.0 / maxval;
+ }
+ else
+ factor = 1.0;
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ bpp = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha(drawable->drawable_id);
+
+ gimp_pixel_rgn_init (&src_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&dest_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ guchar *src = src_rgn.data;
+ guchar *dest = dest_rgn.data;
+ gint row = src_rgn.y - y1;
+
+ for (y = 0; y < src_rgn.h; y++, row++)
+ {
+ guchar *s = src;
+ guchar *d = dest;
+ gint col = src_rgn.x - x1;
+
+ for (x = 0; x < src_rgn.w; x++, col++)
+ {
+
+ if (has_alpha)
+ {
+ for (k = 0; k < bpp-1; k++)
+ {
+ d[k] = factor * s[k];
+ if (invert)
+ d[k] = 255 - d[k];
+ }
+ }
+ else
+ {
+ for (k = 0; k < bpp; k++)
+ {
+ d[k] = factor * s[k];
+ if (invert)
+ d[k] = 255 - d[k];
+ }
+ }
+
+ s += bpp;
+ d += bpp;
+ }
+
+ src += src_rgn.rowstride;
+ dest += dest_rgn.rowstride;
+ }
+ }
+}
+
+
+static void
+gauss_rle (GimpDrawable *drawable,
+ gdouble radius,
+ gint pass,
+ gboolean show_progress)
+{
+ GimpPixelRgn src_rgn, dest_rgn;
+ gint width, height;
+ gint bytes;
+ gint has_alpha;
+ guchar *dest, *dp;
+ guchar *src, *sp;
+ gint *buf, *bb;
+ gint pixels;
+ gint total = 1;
+ gint x1, y1;
+ gint i, row, col, b;
+ gint start, end;
+ gdouble progress, max_progress;
+ gint *curve;
+ gint *sum = NULL;
+ gint val;
+ gint length;
+ gint initial_p, initial_m;
+ gdouble std_dev;
+
+ if (radius <= 0.0)
+ return;
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ bytes = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha(drawable->drawable_id);
+
+ buf = g_new (gint, MAX (width, height) * 2);
+
+ /* allocate buffers for source and destination pixels */
+ src = g_new (guchar, MAX (width, height) * bytes);
+ dest = g_new (guchar, MAX (width, height) * bytes);
+
+ gimp_pixel_rgn_init (&src_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&dest_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ TRUE, TRUE);
+
+ progress = 0.0;
+ max_progress = 2 * width * height;
+
+ /* First the vertical pass */
+ radius = fabs (radius) + 1.0;
+ std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ curve = make_curve (std_dev, &length);
+ sum = g_new (gint, 2 * length + 1);
+
+ sum[0] = 0;
+
+ for (i = 1; i <= length*2; i++)
+ sum[i] = curve[i-length-1] + sum[i-1];
+ sum += length;
+
+ total = sum[length] - sum[-length];
+
+ for (col = 0; col < width; col++)
+ {
+ gimp_pixel_rgn_get_col (&src_rgn, src, col + x1, y1, height);
+
+ if (has_alpha)
+ multiply_alpha (src, height, bytes);
+
+ sp = src;
+ dp = dest;
+
+ for (b = 0; b < bytes; b++)
+ {
+ initial_p = sp[b];
+ initial_m = sp[(height-1) * bytes + b];
+
+ /* Determine a run-length encoded version of the row */
+ run_length_encode (sp + b, buf, bytes, height);
+
+ for (row = 0; row < height; row++)
+ {
+ start = (row < length) ? -row : -length;
+ end = (height <= (row + length) ?
+ (height - row - 1) : length);
+
+ val = 0;
+ i = start;
+ bb = buf + (row + i) * 2;
+
+ if (start != -length)
+ val += initial_p * (sum[start] - sum[-length]);
+
+ while (i < end)
+ {
+ pixels = bb[0];
+ i += pixels;
+
+ if (i > end)
+ i = end;
+
+ val += bb[1] * (sum[i] - sum[start]);
+ bb += (pixels * 2);
+ start = i;
+ }
+
+ if (end != length)
+ val += initial_m * (sum[length] - sum[end]);
+
+ dp[row * bytes + b] = val / total;
+ }
+ }
+
+ if (has_alpha)
+ separate_alpha (dest, height, bytes);
+
+ gimp_pixel_rgn_set_col (&dest_rgn, dest, col + x1, y1, height);
+
+ if (show_progress)
+ {
+ progress += height;
+
+ if ((col % 32) == 0)
+ gimp_progress_update (0.5 * (pass + (progress / max_progress)));
+ }
+ }
+
+ /* prepare for the horizontal pass */
+ gimp_pixel_rgn_init (&src_rgn,
+ drawable, 0, 0, drawable->width, drawable->height,
+ FALSE, TRUE);
+
+ /* Now the horizontal pass */
+ for (row = 0; row < height; row++)
+ {
+ gimp_pixel_rgn_get_row (&src_rgn, src, x1, row + y1, width);
+ if (has_alpha)
+ multiply_alpha (src, width, bytes);
+
+ sp = src;
+ dp = dest;
+
+ for (b = 0; b < bytes; b++)
+ {
+ initial_p = sp[b];
+ initial_m = sp[(width-1) * bytes + b];
+
+ /* Determine a run-length encoded version of the row */
+ run_length_encode (sp + b, buf, bytes, width);
+
+ for (col = 0; col < width; col++)
+ {
+ start = (col < length) ? -col : -length;
+ end = (width <= (col + length)) ? (width - col - 1) : length;
+
+ val = 0;
+ i = start;
+ bb = buf + (col + i) * 2;
+
+ if (start != -length)
+ val += initial_p * (sum[start] - sum[-length]);
+
+ while (i < end)
+ {
+ pixels = bb[0];
+ i += pixels;
+
+ if (i > end)
+ i = end;
+
+ val += bb[1] * (sum[i] - sum[start]);
+ bb += (pixels * 2);
+ start = i;
+ }
+
+ if (end != length)
+ val += initial_m * (sum[length] - sum[end]);
+
+ dp[col * bytes + b] = val / total;
+ }
+ }
+
+ if (has_alpha)
+ separate_alpha (dest, width, bytes);
+
+ gimp_pixel_rgn_set_row (&dest_rgn, dest, x1, row + y1, width);
+
+ if (show_progress)
+ {
+ progress += width;
+
+ if ((row % 32) == 0)
+ gimp_progress_update (0.5 * (pass + (progress / max_progress)));
+ }
+ }
+
+ /* merge the shadow, update the drawable */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, width, height);
+
+ /* free buffers */
+ g_free (buf);
+ g_free (src);
+ g_free (dest);
+}
+
+/*
+ * The equations: g(r) = exp (- r^2 / (2 * sigma^2))
+ * r = sqrt (x^2 + y ^2)
+ */
+
+static gint *
+make_curve (gdouble sigma,
+ gint *length)
+{
+ gint *curve;
+ gdouble sigma2;
+ gdouble l;
+ gint temp;
+ gint i, n;
+
+ sigma2 = 2 * sigma * sigma;
+ l = sqrt (-sigma2 * log (1.0 / 255.0));
+
+ n = ceil (l) * 2;
+ if ((n % 2) == 0)
+ n += 1;
+
+ curve = g_new (gint, n);
+
+ *length = n / 2;
+ curve += *length;
+ curve[0] = 255;
+
+ for (i = 1; i <= *length; i++)
+ {
+ temp = (gint) (exp (- (i * i) / sigma2) * 255);
+ curve[-i] = temp;
+ curve[i] = temp;
+ }
+
+ return curve;
+}
+
+static void
+run_length_encode (guchar *src,
+ gint *dest,
+ gint bytes,
+ gint width)
+{
+ gint start;
+ gint i;
+ gint j;
+ guchar last;
+
+ last = *src;
+ src += bytes;
+ start = 0;
+
+ for (i = 1; i < width; i++)
+ {
+ if (*src != last)
+ {
+ for (j = start; j < i; j++)
+ {
+ *dest++ = (i - j);
+ *dest++ = last;
+ }
+ start = i;
+ last = *src;
+ }
+ src += bytes;
+ }
+
+ for (j = start; j < i; j++)
+ {
+ *dest++ = (i - j);
+ *dest++ = last;
+ }
+}
+
+static void
+preview_update_preview (GimpPreview *preview,
+ GimpDrawable *drawable)
+{
+ gint x1, y1;
+ gint width, height;
+ gint bpp;
+ guchar *buffer;
+ GimpPixelRgn src_rgn;
+ GimpPixelRgn preview_rgn;
+ gint32 image_id, src_image_id;
+ gint32 preview_id;
+ GimpDrawable *preview_drawable;
+
+ bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+
+ buffer = g_new (guchar, width * height * bpp);
+
+ gimp_pixel_rgn_init (&src_rgn, drawable,
+ x1, y1, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_get_rect (&src_rgn, buffer,
+ x1, y1, width, height);
+
+ /* set up gimp drawable for rendering preview into */
+ src_image_id = gimp_item_get_image (drawable->drawable_id);
+ image_id = gimp_image_new (width, height,
+ gimp_image_base_type (src_image_id));
+ preview_id = gimp_layer_new (image_id, "preview", width, height,
+ gimp_drawable_type (drawable->drawable_id),
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+ preview_drawable = gimp_drawable_get (preview_id);
+ gimp_image_insert_layer (image_id, preview_id, -1, 0);
+ gimp_layer_set_offsets (preview_id, 0, 0);
+ gimp_pixel_rgn_init (&preview_rgn, preview_drawable,
+ 0, 0, width, height, TRUE, TRUE);
+ gimp_pixel_rgn_set_rect (&preview_rgn, buffer,
+ 0, 0, width, height);
+ gimp_drawable_flush (preview_drawable);
+ gimp_drawable_merge_shadow (preview_id, TRUE);
+ gimp_drawable_update (preview_id, 0, 0, width, height);
+
+ dog (image_id, preview_drawable, dogvals.inner, dogvals.outer, FALSE);
+
+ gimp_pixel_rgn_get_rect (&preview_rgn, buffer,
+ 0, 0, width, height);
+
+ gimp_preview_draw_buffer (preview, buffer, width * bpp);
+
+ gimp_image_delete (image_id);
+ g_free (buffer);
+}
+
+static void
+change_radius_callback (GtkWidget *coord,
+ gpointer data)
+{
+ GimpPreview *preview = GIMP_PREVIEW (data);
+
+ dogvals.inner = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (coord), 0);
+ dogvals.outer = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (coord), 1);
+
+ gimp_preview_invalidate (preview);
+}
diff --git a/plug-ins/common/emboss.c b/plug-ins/common/emboss.c
new file mode 100644
index 0000000..51ec60e
--- /dev/null
+++ b/plug-ins/common/emboss.c
@@ -0,0 +1,550 @@
+/**************************************************
+ * file: emboss/emboss.c
+ *
+ * Copyright (c) 1997 Eric L. Hernes (erich@rrnet.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-emboss"
+#define PLUG_IN_BINARY "emboss"
+#define PLUG_IN_ROLE "gimp-emboss"
+
+
+enum
+{
+ FUNCTION_BUMPMAP = 0,
+ FUNCTION_EMBOSS = 1
+};
+
+typedef struct
+{
+ gdouble azimuth;
+ gdouble elevation;
+ gint32 depth;
+ gint32 embossp;
+} piArgs;
+
+static piArgs evals =
+{
+ 30.0, /* azimuth */
+ 45.0, /* elevation */
+ 20, /* depth */
+ 1 /* emboss */
+};
+
+struct embossFilter
+{
+ gdouble Lx;
+ gdouble Ly;
+ gdouble Lz;
+ gdouble Nz;
+ gdouble Nz2;
+ gdouble NzLz;
+ gdouble bg;
+} static Filter;
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparam,
+ const GimpParam *param,
+ gint *nretvals,
+ GimpParam **retvals);
+
+static void emboss (GimpDrawable *drawable,
+ GimpPreview *preview);
+static gboolean emboss_dialog (GimpDrawable *drawable);
+
+static void emboss_init (gdouble azimuth,
+ gdouble elevation,
+ gushort width45);
+static void emboss_row (const guchar *src,
+ const guchar *texture,
+ guchar *dst,
+ guint width,
+ guint bypp,
+ gboolean alpha);
+
+
+#define DtoR(d) ((d)*(G_PI/(gdouble)180))
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run, /* run */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "The Image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "The Drawable" },
+ { GIMP_PDB_FLOAT, "azimuth", "The Light Angle (degrees)" },
+ { GIMP_PDB_FLOAT, "elevation", "The Elevation Angle (degrees)" },
+ { GIMP_PDB_INT32, "depth", "The Filter Width" },
+ { GIMP_PDB_INT32, "emboss", "Emboss or Bumpmap" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simulate an image created by embossing"),
+ "Emboss or Bumpmap the given drawable, specifying "
+ "the angle and elevation for the light source.",
+ "Eric L. Hernes, John Schlag",
+ "Eric L. Hernes",
+ "1997",
+ N_("_Emboss (legacy)..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
+}
+
+static void
+run (const gchar *name,
+ gint nparam,
+ const GimpParam *param,
+ gint *nretvals,
+ GimpParam **retvals)
+{
+ static GimpParam rvals[1];
+ GimpDrawable *drawable;
+
+ *nretvals = 1;
+ *retvals = rvals;
+
+ INIT_I18N ();
+
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+ gimp_tile_cache_ntiles (drawable->ntile_cols);
+
+
+ rvals[0].type = GIMP_PDB_STATUS;
+ rvals[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ switch (param[0].data.d_int32)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &evals);
+
+ if (! emboss_dialog (drawable))
+ {
+ rvals[0].data.d_status = GIMP_PDB_CANCEL;
+ }
+ else
+ {
+ gimp_set_data (PLUG_IN_PROC, &evals, sizeof (piArgs));
+ }
+
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparam != 7)
+ {
+ rvals[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ evals.azimuth = param[3].data.d_float;
+ evals.elevation = param[4].data.d_float;
+ evals.depth = param[5].data.d_int32;
+ evals.embossp = param[6].data.d_int32;
+
+ emboss (drawable, NULL);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &evals);
+ /* use this image and drawable, even with last args */
+ emboss (drawable, NULL);
+ break;
+ }
+}
+
+#define pixelScale 255.9
+
+static void
+emboss_init (gdouble azimuth,
+ gdouble elevation,
+ gushort width45)
+{
+ /*
+ * compute the light vector from the input parameters.
+ * normalize the length to pixelScale for fast shading calculation.
+ */
+ Filter.Lx = cos (azimuth) * cos (elevation) * pixelScale;
+ Filter.Ly = sin (azimuth) * cos (elevation) * pixelScale;
+ Filter.Lz = sin (elevation) * pixelScale;
+
+ /*
+ * constant z component of image surface normal - this depends on the
+ * image slope we wish to associate with an angle of 45 degrees, which
+ * depends on the width of the filter used to produce the source image.
+ */
+ Filter.Nz = (6 * 255) / width45;
+ Filter.Nz2 = Filter.Nz * Filter.Nz;
+ Filter.NzLz = Filter.Nz * Filter.Lz;
+
+ /* optimization for vertical normals: L.[0 0 1] */
+ Filter.bg = Filter.Lz;
+}
+
+
+/*
+ * ANSI C code from the article
+ * "Fast Embossing Effects on Raster Image Data"
+ * by John Schlag, jfs@kerner.com
+ * in "Graphics Gems IV", Academic Press, 1994
+ *
+ *
+ * Emboss - shade 24-bit pixels using a single distant light source.
+ * Normals are obtained by differentiating a monochrome 'bump' image.
+ * The unary case ('texture' == NULL) uses the shading result as output.
+ * The binary case multiples the optional 'texture' image by the shade.
+ * Images are in row major order with interleaved color components (rgbrgb...).
+ * E.g., component c of pixel x,y of 'dst' is dst[3*(y*width + x) + c].
+ *
+ */
+
+static void
+emboss_row (const guchar *src,
+ const guchar *texture,
+ guchar *dst,
+ guint width,
+ guint bypp,
+ gboolean alpha)
+{
+ const guchar *s[3];
+ gdouble M[3][3];
+ gint x, bytes;
+
+ /* mung pixels, avoiding edge pixels */
+ s[0] = src;
+ s[1] = s[0] + (width * bypp);
+ s[2] = s[1] + (width * bypp);
+ dst += bypp;
+
+ bytes = (alpha) ? bypp - 1 : bypp;
+
+ if (texture)
+ texture += (width + 1) * bypp;
+
+ for (x = 1; x < width - 1; x++)
+ {
+ gdouble a;
+ glong Nx, Ny, NdotL;
+ gint shade, b;
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ M[i][j] = 0.0;
+
+ for (b = 0; b < bytes; b++)
+ {
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ {
+ if (alpha)
+ a = s[i][j * bypp + bytes] / 255.0;
+ else
+ a = 1.0;
+
+ M[i][j] += a * s[i][j * bypp + b];
+ }
+ }
+
+ Nx = M[0][0] + M[1][0] + M[2][0] - M[0][2] - M[1][2] - M[2][2];
+ Ny = M[2][0] + M[2][1] + M[2][2] - M[0][0] - M[0][1] - M[0][2];
+
+ /* shade with distant light source */
+ if ( Nx == 0 && Ny == 0 )
+ shade = Filter.bg;
+ else if ( (NdotL = Nx * Filter.Lx + Ny * Filter.Ly + Filter.NzLz) < 0 )
+ shade = 0;
+ else
+ shade = NdotL / sqrt(Nx*Nx + Ny*Ny + Filter.Nz2);
+
+ /* do something with the shading result */
+ if (texture)
+ {
+ for (b = 0; b < bytes; b++)
+ *dst++ = (*texture++ * shade) >> 8;
+
+ if (alpha)
+ {
+ *dst++ = s[1][bypp + bytes]; /* preserve the alpha */
+ texture++;
+ }
+ }
+ else
+ {
+ for (b = 0; b < bytes; b++)
+ *dst++ = shade;
+
+ if (alpha)
+ *dst++ = s[1][bypp + bytes]; /* preserve the alpha */
+ }
+
+ for (i = 0; i < 3; i++)
+ s[i] += bypp;
+ }
+
+ if (texture)
+ texture += bypp;
+}
+
+static void
+emboss (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ GimpPixelRgn src, dst;
+ gint p_update;
+ gint y;
+ gint x1, y1, x2, y2;
+ gint width, height;
+ gint bypp, rowsize;
+ gboolean has_alpha;
+ guchar *srcbuf, *dstbuf;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+ x2 = x1 + width;
+ y2 = y1 + height;
+ }
+ else
+ {
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ /* expand the bounds a little */
+ x1 = MAX (0, x1 - evals.depth);
+ y1 = MAX (0, y1 - evals.depth);
+ x2 = MIN (drawable->width, x1 + width + evals.depth);
+ y2 = MIN (drawable->height, y1 + height + evals.depth);
+
+ width = x2 - x1;
+ height = y2 - y1;
+ }
+
+ bypp = drawable->bpp;
+ p_update = MAX (1, height / 20);
+ rowsize = width * bypp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ gimp_pixel_rgn_init (&src, drawable,
+ x1, y1, width, height,
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&dst, drawable,
+ x1, y1, width, height,
+ preview == NULL, TRUE);
+
+ srcbuf = g_new0 (guchar, rowsize * 3);
+ dstbuf = g_new0 (guchar, rowsize);
+
+ emboss_init (DtoR(evals.azimuth), DtoR(evals.elevation), evals.depth);
+ if (!preview)
+ gimp_progress_init (_("Emboss"));
+
+ /* first row */
+ gimp_pixel_rgn_get_rect (&src, srcbuf, x1, y1, width, 3);
+ memcpy (srcbuf, srcbuf + rowsize, rowsize);
+ emboss_row (srcbuf, evals.embossp ? NULL : srcbuf,
+ dstbuf, width, bypp, has_alpha);
+ gimp_pixel_rgn_set_row (&dst, dstbuf, 0, 0, width);
+
+ /* middle rows */
+ for (y = 0; y < height - 2; y++)
+ {
+ if (! preview && (y % p_update == 0))
+ gimp_progress_update ((gdouble) y / (gdouble) height);
+
+ gimp_pixel_rgn_get_rect (&src, srcbuf, x1, y1 + y, width, 3);
+ emboss_row (srcbuf, evals.embossp ? NULL : srcbuf,
+ dstbuf, width, bypp, has_alpha);
+ gimp_pixel_rgn_set_row (&dst, dstbuf, x1, y1 + y + 1, width);
+ }
+
+ /* last row */
+ gimp_pixel_rgn_get_rect (&src, srcbuf, x1, y2 - 3, width, 3);
+ memcpy (srcbuf + rowsize * 2, srcbuf + rowsize, rowsize);
+ emboss_row (srcbuf, evals.embossp ? NULL : srcbuf,
+ dstbuf, width, bypp, has_alpha);
+ gimp_pixel_rgn_set_row (&dst, dstbuf, x1, y2 - 1, width);
+
+ if (preview)
+ {
+ gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
+ &dst);
+ }
+ else
+ {
+ gimp_progress_update (1.0);
+
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, width, height);
+ gimp_displays_flush ();
+ }
+
+ g_free (srcbuf);
+ g_free (dstbuf);
+}
+
+static gboolean
+emboss_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *radio1;
+ GtkWidget *radio2;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Emboss"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (emboss),
+ drawable);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Function"),
+ G_CALLBACK (gimp_radio_button_update),
+ &evals.embossp, evals.embossp,
+
+ _("_Bumpmap"), FUNCTION_BUMPMAP, &radio1,
+ _("_Emboss"), FUNCTION_EMBOSS, &radio2,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ g_signal_connect_swapped (radio1, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (radio2, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Azimuth:"), 100, 6,
+ evals.azimuth, 0.0, 360.0, 1.0, 10.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &evals.azimuth);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("E_levation:"), 100, 6,
+ evals.elevation, 0.0, 180.0, 1.0, 10.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &evals.elevation);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Depth:"), 100, 6,
+ evals.depth, 1.0, 100.0, 1.0, 5.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &evals.depth);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (table);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ if (run)
+ emboss (drawable, NULL);
+
+ return run;
+}
diff --git a/plug-ins/common/file-aa.c b/plug-ins/common/file-aa.c
new file mode 100644
index 0000000..abc6f9c
--- /dev/null
+++ b/plug-ins/common/file-aa.c
@@ -0,0 +1,412 @@
+/**
+ * aa.c version 1.0
+ * A plugin that uses libaa (ftp://ftp.ta.jcu.cz/pub/aa) to save images as
+ * ASCII.
+ * NOTE: This plugin *requires* aalib 1.2 or later. Earlier versions will
+ * not work.
+ * Code copied from all over the GIMP source.
+ * Tim Newsome <nuisance@cmu.edu>
+ */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <aalib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-aa-save"
+#define PLUG_IN_BINARY "file-aa"
+#define PLUG_IN_ROLE "gimp-file-aa"
+
+
+/*
+ * Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean save_aa (gint32 drawable_ID,
+ gchar *filename,
+ gint output_type);
+static void gimp2aa (gint32 drawable_ID,
+ aa_context *context);
+
+static gint aa_dialog (gint selected);
+
+
+/*
+ * Some global variables.
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ {GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
+ {GIMP_PDB_IMAGE, "image", "Input image"},
+ {GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"},
+ {GIMP_PDB_STRING, "filename", "The name of the file to save the image in"},
+ {GIMP_PDB_STRING, "raw-filename", "The name entered"},
+ {GIMP_PDB_STRING, "file-type", "File type to use"}
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves grayscale image in various text formats",
+ "This plug-in uses aalib to save grayscale image "
+ "as ascii art into a variety of text formats",
+ "Tim Newsome <nuisance@cmu.edu>",
+ "Tim Newsome <nuisance@cmu.edu>",
+ "1997",
+ N_("ASCII art"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "text/plain");
+ gimp_register_save_handler (SAVE_PROC, "txt,ansi,text", "");
+}
+
+/**
+ * Searches aa_formats defined by aalib to find the index of the type
+ * specified by string.
+ * -1 means it wasn't found.
+ */
+static gint
+get_type_from_string (const gchar *string)
+{
+ gint type = 0;
+ aa_format **p = (aa_format **) aa_formats;
+
+ while (*p && strcmp ((*p)->formatname, string))
+ {
+ p++;
+ type++;
+ }
+
+ if (*p == NULL)
+ return -1;
+
+ return type;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint output_type = 0;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "AA",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (! (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID)))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (SAVE_PROC, &output_type);
+ output_type = aa_dialog (output_type);
+ if (output_type < 0)
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ output_type = get_type_from_string (param[5].data.d_string);
+ if (output_type < 0)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC, &output_type);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_aa (drawable_ID, param[3].data.d_string, output_type))
+ {
+ gimp_set_data (SAVE_PROC, &output_type, sizeof (output_type));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ values[0].data.d_status = status;
+}
+
+/**
+ * The actual save function. What it's all about.
+ * The image type has to be GRAY.
+ */
+static gboolean
+save_aa (gint32 drawable_ID,
+ gchar *filename,
+ gint output_type)
+{
+ aa_savedata savedata;
+ aa_context *context;
+ aa_format format = *aa_formats[output_type];
+
+ format.width = gimp_drawable_width (drawable_ID) / 2;
+ format.height = gimp_drawable_height (drawable_ID) / 2;
+
+ /* Get a libaa context which will save its output to filename. */
+ savedata.name = filename;
+ savedata.format = &format;
+
+ context = aa_init (&save_d, &aa_defparams, &savedata);
+ if (!context)
+ return FALSE;
+
+ gimp2aa (drawable_ID, context);
+ aa_flush (context);
+ aa_close (context);
+
+ return TRUE;
+}
+
+static void
+gimp2aa (gint32 drawable_ID,
+ aa_context *context)
+{
+ GeglBuffer *buffer;
+ const Babl *format;
+ aa_renderparams *renderparams;
+ gint width;
+ gint height;
+ gint x, y;
+ gint bpp;
+ guchar *buf;
+ guchar *p;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = aa_imgwidth (context);
+ height = aa_imgheight (context);
+
+ switch (gimp_drawable_type (drawable_ID))
+ {
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_RGB_IMAGE:
+ case GIMP_INDEXED_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ default:
+ g_return_if_reached ();
+ break;
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ buf = g_new (guchar, width * bpp);
+
+ for (y = 0; y < height; y++)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ switch (bpp)
+ {
+ case 1: /* GRAY */
+ for (x = 0, p = buf; x < width; x++, p++)
+ aa_putpixel (context, x, y, *p);
+ break;
+
+ case 2: /* GRAYA, blend over black */
+ for (x = 0, p = buf; x < width; x++, p += 2)
+ aa_putpixel (context, x, y, (p[0] * (p[1] + 1)) >> 8);
+ break;
+
+ case 3: /* RGB */
+ for (x = 0, p = buf; x < width; x++, p += 3)
+ aa_putpixel (context, x, y,
+ GIMP_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5);
+ break;
+
+ case 4: /* RGBA, blend over black */
+ for (x = 0, p = buf; x < width; x++, p += 4)
+ aa_putpixel (context, x, y,
+ ((guchar) (GIMP_RGB_LUMINANCE (p[0], p[1], p[2]) + 0.5)
+ * (p[3] + 1)) >> 8);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ g_free (buf);
+
+ g_object_unref (buffer);
+
+ renderparams = aa_getrenderparams ();
+ renderparams->dither = AA_FLOYD_S;
+
+ aa_render (context, renderparams, 0, 0,
+ aa_scrwidth (context), aa_scrheight (context));
+}
+
+static gint
+aa_dialog (gint selected)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+ gint i;
+
+ /* Create the actual window. */
+ dialog = gimp_export_dialog_new (_("Text"), PLUG_IN_BINARY, SAVE_PROC);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("_Format:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ for (i = 0; aa_formats[i]; i++)
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, i,
+ GIMP_INT_STORE_LABEL, aa_formats[i]->formatname,
+ -1);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), selected,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &selected);
+
+ gtk_widget_show (dialog);
+
+ if (gimp_dialog_run (GIMP_DIALOG (dialog)) != GTK_RESPONSE_OK)
+ selected = -1;
+
+ gtk_widget_destroy (dialog);
+
+ return selected;
+}
diff --git a/plug-ins/common/file-cel.c b/plug-ins/common/file-cel.c
new file mode 100644
index 0000000..95706c1
--- /dev/null
+++ b/plug-ins/common/file-cel.c
@@ -0,0 +1,975 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * cel.c -- KISS CEL file format plug-in
+ * (copyright) 1997,1998 Nick Lamb (njl195@zepler.org.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-cel-load"
+#define SAVE_PROC "file-cel-save"
+#define PLUG_IN_BINARY "file-cel"
+#define PLUG_IN_ROLE "gimp-file-cel"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint load_palette (const gchar *file,
+ FILE *fp,
+ guchar palette[],
+ GError **error);
+static gint32 load_image (const gchar *file,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image,
+ gint32 layer,
+ GError **error);
+static void palette_dialog (const gchar *title);
+static gboolean need_palette (const gchar *file,
+ GError **error);
+
+
+/* Globals... */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gchar *palette_file = NULL;
+static gsize data_length = 0;
+
+/* Let GIMP library handle initialisation (and inquisitive users) */
+
+MAIN ()
+
+/* GIMP queries plug-in for parameters etc. */
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "Filename to load image from" },
+ { GIMP_PDB_STRING, "raw-filename", "Name entered" },
+ { GIMP_PDB_STRING, "palette-filename", "Filename to load palette from" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "Filename to export image to" },
+ { GIMP_PDB_STRING, "raw-filename", "Name entered" },
+ { GIMP_PDB_STRING, "palette-filename", "Filename to save palette to" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in KISS CEL file format",
+ "This plug-in loads individual KISS cell files.",
+ "Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "May 1998",
+ N_("KISS CEL"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "cel",
+ "",
+ "0,string,KiSS\\040");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in KISS CEL file format",
+ "This plug-in exports individual KISS cell files.",
+ "Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "May 1998",
+ N_("KISS CEL"),
+ "RGB*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "cel", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2]; /* Return values */
+ GimpRunMode run_mode;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+ gint needs_palette = 0;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ /* Set up default return values */
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ data_length = gimp_get_data_size (SAVE_PROC);
+ if (data_length > 0)
+ {
+ palette_file = g_malloc (data_length);
+ gimp_get_data (SAVE_PROC, palette_file);
+ }
+ else
+ {
+ palette_file = g_strdup ("*.kcf");
+ data_length = strlen (palette_file) + 1;
+ }
+ }
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ palette_file = param[3].data.d_string;
+ if (palette_file)
+ data_length = strlen (palette_file) + 1;
+ else
+ data_length = 0;
+ }
+ else if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ /* Let user choose KCF palette (cancel ignores) */
+ needs_palette = need_palette (param[1].data.d_string, &error);
+
+ if (! error)
+ {
+ if (needs_palette)
+ palette_dialog (_("Load KISS Palette"));
+
+ gimp_set_data (SAVE_PROC, palette_file, data_length);
+ }
+ }
+
+ if (! error)
+ {
+ image = load_image (param[1].data.d_string,
+ &error);
+
+ if (image != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "CEL",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID, &error))
+ {
+ if (data_length)
+ {
+ gimp_set_data (SAVE_PROC, palette_file, data_length);
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* Peek into the file to determine whether we need a palette */
+static gboolean
+need_palette (const gchar *file,
+ GError **error)
+{
+ FILE *fp;
+ guchar header[32];
+ size_t n_read;
+
+ fp = g_fopen (file, "rb");
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (file), g_strerror (errno));
+ return FALSE;
+ }
+
+ n_read = fread (header, 32, 1, fp);
+
+ fclose (fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image header"));
+ return FALSE;
+ }
+
+ return (header[5] < 32);
+}
+
+/* Load CEL image into GIMP */
+
+static gint32
+load_image (const gchar *file,
+ GError **error)
+{
+ FILE *fp; /* Read file pointer */
+ guchar header[32], /* File header */
+ file_mark, /* KiSS file type */
+ bpp; /* Bits per pixel */
+ gint height, width, /* Dimensions of image */
+ offx, offy, /* Layer offsets */
+ colors; /* Number of colors */
+
+ gint32 image, /* Image */
+ layer; /* Layer */
+ guchar *buf; /* Temporary buffer */
+ guchar *line; /* Pixel data */
+ GeglBuffer *buffer; /* Buffer for layer */
+
+ gint i, j, k; /* Counters */
+ size_t n_read; /* Number of items read from file */
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (file));
+
+ /* Open the file for reading */
+ fp = g_fopen (file, "r");
+
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (file), g_strerror (errno));
+ return -1;
+ }
+
+ /* Get the image dimensions and create the image... */
+
+ n_read = fread (header, 4, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image header"));
+ fclose (fp);
+ return -1;
+ }
+
+ if (strncmp ((const gchar *) header, "KiSS", 4))
+ {
+ colors= 16;
+ bpp = 4;
+ width = header[0] + (256 * header[1]);
+ height = header[2] + (256 * header[3]);
+ offx= 0;
+ offy= 0;
+ }
+ else
+ { /* New-style image file, read full header */
+ n_read = fread (header, 28, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image header"));
+ fclose (fp);
+ return -1;
+ }
+
+ file_mark = header[0];
+ if (file_mark != 0x20 && file_mark != 0x21)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("is not a CEL image file"));
+ fclose (fp);
+ return -1;
+ }
+
+ bpp = header[1];
+ switch (bpp)
+ {
+ case 4:
+ case 8:
+ case 32:
+ colors = (1 << bpp);
+ break;
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("illegal bpp value in image: %hhu"), bpp);
+ fclose (fp);
+ return -1;
+ }
+
+ width = header[4] + (256 * header[5]);
+ height = header[6] + (256 * header[7]);
+ offx = header[8] + (256 * header[9]);
+ offy = header[10] + (256 * header[11]);
+ }
+
+ if ((width == 0) || (height == 0) || (width + offx > GIMP_MAX_IMAGE_SIZE) ||
+ (height + offy > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("illegal image dimensions: width: %d, horizontal offset: "
+ "%d, height: %d, vertical offset: %d"),
+ width, offx, height, offy);
+ fclose (fp);
+ return -1;
+ }
+
+ if (bpp == 32)
+ image = gimp_image_new (width + offx, height + offy, GIMP_RGB);
+ else
+ image = gimp_image_new (width + offx, height + offy, GIMP_INDEXED);
+
+ if (image == -1)
+ {
+ g_set_error (error, 0, 0, _("Can't create a new image"));
+ fclose (fp);
+ return -1;
+ }
+
+ gimp_image_set_filename (image, file);
+
+ /* Create an indexed-alpha layer to hold the image... */
+ if (bpp == 32)
+ layer = gimp_layer_new (image, _("Background"), width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ else
+ layer = gimp_layer_new (image, _("Background"), width, height,
+ GIMP_INDEXEDA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ gimp_image_insert_layer (image, layer, -1, 0);
+ gimp_layer_set_offsets (layer, offx, offy);
+
+ /* Get the drawable and set the pixel region for our load... */
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ /* Read the image in and give it to GIMP a line at a time */
+ buf = g_new (guchar, width * 4);
+ line = g_new (guchar, (width + 1) * 4);
+
+ for (i = 0; i < height && !feof(fp); ++i)
+ {
+ switch (bpp)
+ {
+ case 4:
+ n_read = fread (buf, (width + 1) / 2, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image data"));
+ fclose (fp);
+ return -1;
+ }
+
+ for (j = 0, k = 0; j < width * 2; j+= 4, ++k)
+ {
+ if (buf[k] / 16 == 0)
+ {
+ line[j] = 16;
+ line[j+ 1 ] = 0;
+ }
+ else
+ {
+ line[j] = (buf[k] / 16) - 1;
+ line[j + 1] = 255;
+ }
+
+ if (buf[k] % 16 == 0)
+ {
+ line[j + 2] = 16;
+ line[j + 3] = 0;
+ }
+ else
+ {
+ line[j + 2] = (buf[k] % 16) - 1;
+ line[j + 3] = 255;
+ }
+ }
+ break;
+
+ case 8:
+ n_read = fread (buf, width, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image data"));
+ fclose (fp);
+ return -1;
+ }
+
+ for (j = 0, k = 0; j < width * 2; j+= 2, ++k)
+ {
+ if (buf[k] == 0)
+ {
+ line[j] = 255;
+ line[j + 1] = 0;
+ }
+ else
+ {
+ line[j] = buf[k] - 1;
+ line[j + 1] = 255;
+ }
+ }
+ break;
+
+ case 32:
+ n_read = fread (line, width * 4, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("EOF or error while reading image data"));
+ fclose (fp);
+ return -1;
+ }
+
+ /* The CEL file order is BGR so we need to swap B and R
+ * to get the Gimp RGB order.
+ */
+ for (j= 0; j < width; j++)
+ {
+ guint8 tmp = line[j*4];
+ line[j*4] = line[j*4+2];
+ line[j*4+2] = tmp;
+ }
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported bit depth (%d)!"), bpp);
+ fclose (fp);
+ return -1;
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i, width, 1), 0,
+ NULL, line, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((float) i / (float) height);
+ }
+
+ /* Close image files, give back allocated memory */
+
+ fclose (fp);
+ g_free (buf);
+ g_free (line);
+
+ if (bpp != 32)
+ {
+ /* Use palette from file or otherwise default grey palette */
+ guchar palette[256 * 3];
+
+ /* Open the file for reading if user picked one */
+ if (palette_file == NULL)
+ {
+ fp = NULL;
+ }
+ else
+ {
+ fp = g_fopen (palette_file, "r");
+
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (palette_file),
+ g_strerror (errno));
+ return -1;
+ }
+ }
+
+ if (fp != NULL)
+ {
+ colors = load_palette (palette_file, fp, palette, error);
+ fclose (fp);
+ if (colors < 0 || *error)
+ return -1;
+ }
+ else
+ {
+ for (i= 0; i < colors; ++i)
+ {
+ palette[i * 3] = palette[i * 3 + 1] = palette[i * 3 + 2]= i * 256 / colors;
+ }
+ }
+
+ gimp_image_set_colormap (image, palette + 3, colors - 1);
+ }
+
+ /* Now get everything redrawn and hand back the finished image */
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
+
+static gint
+load_palette (const gchar *file,
+ FILE *fp,
+ guchar palette[],
+ GError **error)
+{
+ guchar header[32]; /* File header */
+ guchar buffer[2];
+ guchar file_mark, bpp;
+ gint i, colors = 0;
+ size_t n_read;
+
+ n_read = fread (header, 4, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': EOF or error while reading palette header"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+
+ if (!strncmp ((const gchar *) header, "KiSS", 4))
+ {
+ n_read = fread (header+4, 28, 1, fp);
+
+ if (n_read < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': EOF or error while reading palette header"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+
+ file_mark = header[4];
+ if (file_mark != 0x10)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': is not a KCF palette file"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+
+ bpp = header[5];
+ if (bpp != 12 && bpp != 24)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': illegal bpp value in palette: %hhu"),
+ gimp_filename_to_utf8 (file), bpp);
+ return -1;
+ }
+
+ colors = header[8] + header[9] * 256;
+ if (colors != 16 && colors != 256)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': illegal number of colors: %u"),
+ gimp_filename_to_utf8 (file), colors);
+ return -1;
+ }
+
+ switch (bpp)
+ {
+ case 12:
+ for (i = 0; i < colors; ++i)
+ {
+ n_read = fread (buffer, 1, 2, fp);
+
+ if (n_read < 2)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': EOF or error while reading "
+ "palette data"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+
+ palette[i*3]= buffer[0] & 0xf0;
+ palette[i*3+1]= (buffer[1] & 0x0f) * 16;
+ palette[i*3+2]= (buffer[0] & 0x0f) * 16;
+ }
+ break;
+ case 24:
+ n_read = fread (palette, colors, 3, fp);
+
+ if (n_read < 3)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': EOF or error while reading palette data"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ else
+ {
+ colors = 16;
+ fseek (fp, 0, SEEK_SET);
+ for (i= 0; i < colors; ++i)
+ {
+ n_read = fread (buffer, 1, 2, fp);
+
+ if (n_read < 2)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s': EOF or error while reading palette data"),
+ gimp_filename_to_utf8 (file));
+ return -1;
+ }
+
+ palette[i*3] = buffer[0] & 0xf0;
+ palette[i*3+1] = (buffer[1] & 0x0f) * 16;
+ palette[i*3+2] = (buffer[0] & 0x0f) * 16;
+ }
+ }
+
+ return colors;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image,
+ gint32 layer,
+ GError **error)
+{
+ GOutputStream *output;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GCancellable *cancellable;
+ gint width;
+ gint height;
+ guchar header[32]; /* File header */
+ gint bpp; /* Bit per pixel */
+ gint colors; /* Number of colors */
+ gint type; /* type of layer */
+ gint offx, offy; /* Layer offsets */
+ guchar *buf = NULL; /* Temporary buffer */
+ guchar *line = NULL; /* Pixel data */
+ gint i, j, k; /* Counters */
+
+ /* Check that this is an indexed image, fail otherwise */
+ type = gimp_drawable_type (layer);
+
+ if (type == GIMP_INDEXEDA_IMAGE)
+ {
+ bpp = 4;
+ format = NULL;
+ }
+ else
+ {
+ bpp = 32;
+ format = babl_format ("R'G'B'A u8");
+ }
+
+ /* Find out how offset this layer was */
+ gimp_drawable_offsets (layer, &offx, &offy);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ /* Headers */
+ memset (header, 0, 32);
+ strcpy ((gchar *) header, "KiSS");
+ header[4]= 0x20;
+
+ /* Work out whether to save as 8bit or 4bit */
+ if (bpp < 32)
+ {
+ g_free (gimp_image_get_colormap (image, &colors));
+
+ if (colors > 15)
+ {
+ header[5] = 8;
+ }
+ else
+ {
+ header[5] = 4;
+ }
+ }
+ else
+ {
+ header[5] = 32;
+ }
+
+ /* Fill in the blanks ... */
+ header[8] = width % 256;
+ header[9] = width / 256;
+ header[10] = height % 256;
+ header[11] = height / 256;
+ header[12] = offx % 256;
+ header[13] = offx / 256;
+ header[14] = offy % 256;
+ header[15] = offy / 256;
+
+ if (! g_output_stream_write_all (output, header, 32, NULL,
+ NULL, error))
+ goto fail;
+
+ /* Arrange for memory etc. */
+ buf = g_new (guchar, width * 4);
+ line = g_new (guchar, (width + 1) * 4);
+
+ /* Get the image from GIMP one line at a time and write it out */
+ for (i = 0; i < height; ++i)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, 1), 1.0,
+ format, line,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ memset (buf, 0, width);
+
+ if (bpp == 32)
+ {
+ for (j = 0; j < width; j++)
+ {
+ buf[4 * j] = line[4 * j + 2]; /* B */
+ buf[4 * j + 1] = line[4 * j + 1]; /* G */
+ buf[4 * j + 2] = line[4 * j + 0]; /* R */
+ buf[4 * j + 3] = line[4 * j + 3]; /* Alpha */
+ }
+
+ if (! g_output_stream_write_all (output, buf, width * 4, NULL,
+ NULL, error))
+ goto fail;
+ }
+ else if (colors > 16)
+ {
+ for (j = 0, k = 0; j < width * 2; j += 2, ++k)
+ {
+ if (line[j + 1] > 127)
+ {
+ buf[k]= line[j] + 1;
+ }
+ }
+
+ if (! g_output_stream_write_all (output, buf, width, NULL,
+ NULL, error))
+ goto fail;
+ }
+ else
+ {
+ for (j = 0, k = 0; j < width * 2; j+= 4, ++k)
+ {
+ buf[k] = 0;
+
+ if (line[j + 1] > 127)
+ {
+ buf[k] += (line[j] + 1)<< 4;
+ }
+
+ if (line[j + 3] > 127)
+ {
+ buf[k] += (line[j + 2] + 1);
+ }
+ }
+
+ if (! g_output_stream_write_all (output, buf, width + 1 / 2, NULL,
+ NULL, error))
+ goto fail;
+ }
+
+ gimp_progress_update ((float) i / (float) height);
+ }
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ gimp_progress_update (1.0);
+
+ g_free (buf);
+ g_free (line);
+ g_object_unref (buffer);
+ g_object_unref (output);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_free (buf);
+ g_free (line);
+ g_object_unref (buffer);
+ g_object_unref (output);
+
+ return FALSE;
+}
+
+static void
+palette_dialog (const gchar *title)
+{
+ GtkWidget *dialog;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gtk_file_chooser_dialog_new (title, NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), palette_file);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_widget_show (dialog);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ g_free (palette_file);
+ palette_file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ data_length = strlen (palette_file) + 1;
+ }
+
+ gtk_widget_destroy (dialog);
+}
diff --git a/plug-ins/common/file-compressor.c b/plug-ins/common/file-compressor.c
new file mode 100644
index 0000000..a51da9b
--- /dev/null
+++ b/plug-ins/common/file-compressor.c
@@ -0,0 +1,1017 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Copyright (C) 1997 Daniel Risacher, magnus@alum.mit.edu
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Minor changes to support file magic */
+/* 4 Oct 1997 -- Risacher */
+
+/* compressor plug-in for GIMP */
+/* based on gz.c which in turn is */
+/* loosley based on url.c by */
+/* Josh MacDonald, jmacd@cs.berkeley.edu */
+
+/* and, very loosely on hrz.c by */
+/* Albert Cahalan <acahalan at cs.uml.edu> */
+
+/* LZMA compression code is based on code by Lasse Collin which was
+ * placed in the public-domain. */
+
+/* This is reads and writes compressed image files for GIMP
+ *
+ * It should work with file names of the form
+ * filename.foo.[gz|bz2] where foo is some already-recognized extension
+ *
+ * and it also works for names of the form
+ * filename.xcf[gz|bz2] - which is equivalent to
+ * filename.xcf.[gz|bz2]
+ *
+ * I added the xcfgz bit because having a default extension of xcf.gz
+ * can confuse the file selection dialog box somewhat, forcing the
+ * user to type sometimes when he/she otherwise wouldn't need to.
+ *
+ * I later decided I didn't like it because I don't like to bloat
+ * the file-extension namespace. But I left in the recognition
+ * feature/bug so if people want to use files named foo.xcfgz by
+ * default, they can just hack their pluginrc file.
+ *
+ * to do this hack, change :
+ * "xcf.gz,gz,xcfgz"
+ * to
+ * "xcfgz,gz,xcf.gz"
+ *
+ *
+ * -Dan Risacher, 0430 CDT, 26 May 1997
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include <zlib.h>
+#include <bzlib.h>
+#include <lzma.h>
+
+
+/* Author 1: Josh MacDonald (url.c) */
+/* Author 2: Daniel Risacher (gz.c) */
+/* Author 3: Michael Natterer (compressor.c) */
+
+/* According to USAF Lt Steve Werhle, US DoD software development
+ * contracts average about $25 USD per source line of code (SLOC). By
+ * that metric, I figure this plug-in is worth about $10,000 USD */
+/* But you got it free. Magic of Gnu. */
+
+typedef gboolean (*LoadFn) (const char *infile,
+ const char *outfile);
+typedef gboolean (*SaveFn) (const char *infile,
+ const char *outfile);
+
+typedef struct _Compressor Compressor;
+
+struct _Compressor
+{
+ const gchar *file_type;
+ const gchar *mime_type;
+ const gchar *extensions;
+ const gchar *magic;
+ const gchar *xcf_extension;
+ const gchar *generic_extension;
+
+ const gchar *load_proc;
+ const gchar *load_blurb;
+ const gchar *load_help;
+ LoadFn load_fn;
+
+ const gchar *save_proc;
+ const gchar *save_blurb;
+ const gchar *save_help;
+ SaveFn save_fn;
+};
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType save_image (const Compressor *compressor,
+ const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 run_mode,
+ GError **error);
+static gint32 load_image (const Compressor *compressor,
+ const gchar *filename,
+ gint32 run_mode,
+ GimpPDBStatusType *status,
+ GError **error);
+
+static gboolean valid_file (const gchar *filename);
+static const gchar * find_extension (const Compressor *compressor,
+ const gchar *filename);
+
+static gboolean gzip_load (const char *infile,
+ const char *outfile);
+static gboolean gzip_save (const char *infile,
+ const char *outfile);
+
+static gboolean bzip2_load (const char *infile,
+ const char *outfile);
+static gboolean bzip2_save (const char *infile,
+ const char *outfile);
+
+static gboolean xz_load (const char *infile,
+ const char *outfile);
+static gboolean xz_save (const char *infile,
+ const char *outfile);
+static goffset get_file_info (const gchar *filename);
+
+
+static const Compressor compressors[] =
+{
+ {
+ N_("gzip archive"),
+ "application/x-gzip",
+ "xcf.gz,xcfgz", /* FIXME "xcf.gz,gz,xcfgz" */
+ "0,string,\037\213",
+ ".xcfgz",
+ ".gz",
+
+ "file-gz-load",
+ "loads files compressed with gzip",
+ "This procedure loads files in the gzip compressed format.",
+ gzip_load,
+
+ "file-gz-save",
+ "saves files compressed with gzip",
+ "This procedure saves files in the gzip compressed format.",
+ gzip_save
+ },
+
+ {
+ N_("bzip archive"),
+ "application/x-bzip",
+ "xcf.bz2,xcfbz2", /* FIXME "xcf.bz2,bz2,xcfbz2" */
+ "0,string,BZh",
+ ".xcfbz2",
+ ".bz2",
+
+ "file-bz2-load",
+ "loads files compressed with bzip2",
+ "This procedure loads files in the bzip2 compressed format.",
+ bzip2_load,
+
+ "file-bz2-save",
+ "saves files compressed with bzip2",
+ "This procedure saves files in the bzip2 compressed format.",
+ bzip2_save
+ },
+
+ {
+ N_("xz archive"),
+ "application/x-xz",
+ "xcf.xz,xcfxz", /* FIXME "xcf.xz,xz,xcfxz" */
+ "0,string,\3757zXZ\x00",
+ ".xcfxz",
+ ".xz",
+
+ "file-xz-load",
+ "loads files compressed with xz",
+ "This procedure loads files in the xz compressed format.",
+ xz_load,
+
+ "file-xz-save",
+ "saves files compressed with xz",
+ "This procedure saves files in the xz compressed format.",
+ xz_save
+ }
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to "
+ "save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (compressors); i++)
+ {
+ const Compressor *compressor = &compressors[i];
+
+ gimp_install_procedure (compressor->load_proc,
+ compressor->load_blurb,
+ compressor->load_help,
+ "Daniel Risacher",
+ "Daniel Risacher, Spencer Kimball and Peter Mattis",
+ "1995-1997",
+ compressor->file_type,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (compressor->load_proc,
+ compressor->mime_type);
+ gimp_register_magic_load_handler (compressor->load_proc,
+ compressor->extensions,
+ "",
+ compressor->magic);
+
+ gimp_install_procedure (compressor->save_proc,
+ compressor->save_blurb,
+ compressor->save_help,
+ "Daniel Risacher",
+ "Daniel Risacher, Spencer Kimball and Peter Mattis",
+ "1995-1997",
+ compressor->file_type,
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (compressor->save_proc,
+ compressor->mime_type);
+ gimp_register_save_handler (compressor->save_proc,
+ compressor->extensions, "");
+ }
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+ gint32 image_ID;
+ gint i;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* We handle PDB errors by forwarding them to the caller in
+ * our return values.
+ */
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ for (i = 0; i < G_N_ELEMENTS (compressors); i++)
+ {
+ const Compressor *compressor = &compressors[i];
+
+ if (! strcmp (name, compressor->load_proc))
+ {
+ image_ID = load_image (compressor,
+ param[1].data.d_string,
+ param[0].data.d_int32,
+ &status, &error);
+
+ if (image_ID != -1 && status == GIMP_PDB_SUCCESS)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+
+ break;
+ }
+ else if (! strcmp (name, compressor->save_proc))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ status = save_image (compressor,
+ param[3].data.d_string,
+ param[1].data.d_int32,
+ param[2].data.d_int32,
+ param[0].data.d_int32,
+ &error);
+
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (compressors))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static GimpPDBStatusType
+save_image (const Compressor *compressor,
+ const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 run_mode,
+ GError **error)
+{
+ const gchar *ext;
+ gchar *tmpname;
+
+ ext = find_extension (compressor, filename);
+
+ if (! ext)
+ {
+ g_message (_("No sensible file extension, saving as compressed XCF."));
+ ext = ".xcf";
+ }
+
+ /* get a temp name with the right extension and save into it. */
+
+ tmpname = gimp_temp_name (ext + 1);
+
+ if (! (gimp_file_save (run_mode,
+ image_ID,
+ drawable_ID,
+ tmpname,
+ tmpname) && valid_file (tmpname)))
+ {
+ g_unlink (tmpname);
+ g_free (tmpname);
+
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", gimp_get_pdb_error ());
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_progress_init_printf (_("Compressing '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if (!compressor->save_fn (tmpname, filename))
+ {
+ g_unlink (tmpname);
+ g_free (tmpname);
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_unlink (tmpname);
+ gimp_progress_update (1.0);
+ g_free (tmpname);
+
+ /* ask the core to save a thumbnail for compressed XCF files */
+ if (strcmp (ext, ".xcf") == 0)
+ gimp_file_save_thumbnail (image_ID, filename);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static gint32
+load_image (const Compressor *compressor,
+ const gchar *filename,
+ gint32 run_mode,
+ GimpPDBStatusType *status,
+ GError **error)
+{
+ gint32 image_ID;
+ const gchar *ext;
+ gchar *tmpname;
+
+ ext = find_extension (compressor, filename);
+
+ if (! ext)
+ {
+ g_message (_("No sensible file extension, "
+ "attempting to load with file magic."));
+ ext = ".foo";
+ }
+
+ /* find a temp name */
+ tmpname = gimp_temp_name (ext + 1);
+
+ if (!compressor->load_fn (filename, tmpname))
+ {
+ g_free (tmpname);
+ *status = GIMP_PDB_EXECUTION_ERROR;
+ return -1;
+ }
+
+ /* now that we uncompressed it, load the temp file */
+
+ image_ID = gimp_file_load (run_mode, tmpname, tmpname);
+
+ g_unlink (tmpname);
+ g_free (tmpname);
+
+ if (image_ID != -1)
+ {
+ *status = GIMP_PDB_SUCCESS;
+
+ gimp_image_set_filename (image_ID, filename);
+ }
+ else
+ {
+ /* Forward the return status of the underlining plug-in for the
+ * given format.
+ */
+ *status = gimp_get_pdb_status ();
+
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", gimp_get_pdb_error ());
+ }
+
+ return image_ID;
+}
+
+static gboolean
+valid_file (const gchar *filename)
+{
+ GStatBuf buf;
+
+ return g_stat (filename, &buf) == 0 && buf.st_size > 0;
+}
+
+static const gchar *
+find_extension (const Compressor *compressor,
+ const gchar *filename)
+{
+ gchar *filename_copy;
+ gchar *ext;
+
+ /* we never free this copy - aren't we evil! */
+ filename_copy = g_strdup (filename);
+
+ /* find the extension, boy! */
+ ext = strrchr (filename_copy, '.');
+
+ while (TRUE)
+ {
+ if (!ext || ext[1] == '\0' || strchr (ext, G_DIR_SEPARATOR))
+ {
+ return NULL;
+ }
+
+ if (0 == g_ascii_strcasecmp (ext, compressor->xcf_extension))
+ {
+ return ".xcf"; /* we've found it */
+ }
+ if (0 != g_ascii_strcasecmp (ext, compressor->generic_extension))
+ {
+ return ext;
+ }
+ else
+ {
+ /* we found ".gz" so strip it, loop back, and look again */
+ *ext = '\0';
+ ext = strrchr (filename_copy, '.');
+ }
+ }
+}
+
+static gboolean
+gzip_load (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ int fd;
+ gzFile in;
+ FILE *out;
+ char buf[16384];
+ int len;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ fd = g_open (infile, O_RDONLY | _O_BINARY, 0);
+ if (fd == -1)
+ goto out;
+
+ in = gzdopen (fd, "rb");
+ if (!in)
+ {
+ close (fd);
+ goto out;
+ }
+
+ out = g_fopen (outfile, "wb");
+ if (!out)
+ goto out;
+
+ while (TRUE)
+ {
+ len = gzread (in, buf, sizeof buf);
+
+ if (len < 0)
+ break;
+ else if (len == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+
+ if (fwrite(buf, 1, len, out) != len)
+ break;
+ }
+
+ out:
+ /* There is no need to close(fd) as it is closed by gzclose(). */
+ if (in)
+ if (gzclose (in) != Z_OK)
+ ret = FALSE;
+
+ if (out)
+ fclose (out);
+
+ return ret;
+}
+
+static gboolean
+gzip_save (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ FILE *in;
+ int fd;
+ gzFile out;
+ char buf[16384];
+ int len;
+ goffset tot = 0, file_size;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ in = g_fopen (infile, "rb");
+ if (!in)
+ goto out;
+
+ fd = g_open (outfile, O_CREAT | O_WRONLY | O_TRUNC | _O_BINARY, 0664);
+ if (fd == -1)
+ goto out;
+
+ out = gzdopen (fd, "wb");
+ if (!out)
+ {
+ close (fd);
+ goto out;
+ }
+
+ file_size = get_file_info (infile);
+ while (TRUE)
+ {
+ len = fread (buf, 1, sizeof buf, in);
+ if (ferror (in))
+ break;
+
+ if (len < 0)
+ break;
+ else if (len == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+
+ if (gzwrite (out, buf, len) != len)
+ break;
+
+ gimp_progress_update ((tot += len) * 1.0 / file_size);
+ }
+
+ out:
+ if (in)
+ fclose (in);
+
+ /* There is no need to close(fd) as it is closed by gzclose(). */
+ if (out)
+ if (gzclose (out) != Z_OK)
+ ret = FALSE;
+
+ return ret;
+}
+
+static gboolean
+bzip2_load (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ int fd;
+ BZFILE *in;
+ FILE *out;
+ char buf[16384];
+ int len;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ fd = g_open (infile, O_RDONLY | _O_BINARY, 0);
+ if (fd == -1)
+ goto out;
+
+ in = BZ2_bzdopen (fd, "rb");
+ if (!in)
+ {
+ close (fd);
+ goto out;
+ }
+
+ out = g_fopen (outfile, "wb");
+ if (!out)
+ goto out;
+
+ while (TRUE)
+ {
+ len = BZ2_bzread (in, buf, sizeof buf);
+
+ if (len < 0)
+ break;
+ else if (len == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+
+ if (fwrite(buf, 1, len, out) != len)
+ break;
+ }
+
+ out:
+ /* TODO: Check this in the case of BZ2_bzclose(): */
+ /* There is no need to close(fd) as it is closed by BZ2_bzclose(). */
+ if (in)
+ BZ2_bzclose (in);
+
+ if (out)
+ fclose (out);
+
+ return ret;
+}
+
+static gboolean
+bzip2_save (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ FILE *in;
+ int fd;
+ BZFILE *out;
+ char buf[16384];
+ int len;
+ goffset tot = 0, file_size;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ in = g_fopen (infile, "rb");
+ if (!in)
+ goto out;
+
+ fd = g_open (outfile, O_CREAT | O_WRONLY | O_TRUNC | _O_BINARY, 0664);
+ if (fd == -1)
+ goto out;
+
+ out = BZ2_bzdopen (fd, "wb");
+ if (!out)
+ {
+ close (fd);
+ goto out;
+ }
+
+ file_size = get_file_info (infile);
+ while (TRUE)
+ {
+ len = fread (buf, 1, sizeof buf, in);
+ if (ferror (in))
+ break;
+
+ if (len < 0)
+ break;
+ else if (len == 0)
+ {
+ ret = TRUE;
+ break;
+ }
+
+ if (BZ2_bzwrite (out, buf, len) != len)
+ break;
+
+ gimp_progress_update ((tot += len) * 1.0 / file_size);
+ }
+
+ out:
+ if (in)
+ fclose (in);
+
+ /* TODO: Check this in the case of BZ2_bzclose(): */
+ /* There is no need to close(fd) as it is closed by BZ2_bzclose(). */
+ if (out)
+ BZ2_bzclose (out);
+
+ return ret;
+}
+
+static gboolean
+xz_load (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ FILE *in;
+ FILE *out;
+ lzma_stream strm = LZMA_STREAM_INIT;
+ lzma_action action;
+ guint8 inbuf[BUFSIZ];
+ guint8 outbuf[BUFSIZ];
+ lzma_ret status;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ in = g_fopen (infile, "rb");
+ if (!in)
+ goto out;
+
+ out = g_fopen (outfile, "wb");
+ if (!out)
+ goto out;
+
+ if (lzma_stream_decoder (&strm, UINT64_MAX, 0) != LZMA_OK)
+ goto out;
+
+ strm.next_in = NULL;
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = sizeof outbuf;
+
+ action = LZMA_RUN;
+ status = LZMA_OK;
+
+ while (status == LZMA_OK)
+ {
+ /* Fill the input buffer if it is empty. */
+ if ((strm.avail_in == 0) && (!feof(in)))
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = fread (inbuf, 1, sizeof inbuf, in);
+
+ if (ferror (in))
+ goto out;
+
+ /* Once the end of the input file has been reached, we need to
+ tell lzma_code() that no more input will be coming and that
+ it should finish the encoding. */
+ if (feof (in))
+ action = LZMA_FINISH;
+ }
+
+ status = lzma_code (&strm, action);
+
+ if ((strm.avail_out == 0) || (status == LZMA_STREAM_END))
+ {
+ /* When lzma_code() has returned LZMA_STREAM_END, the output
+ buffer is likely to be only partially full. Calculate how
+ much new data there is to be written to the output file. */
+ size_t write_size = sizeof outbuf - strm.avail_out;
+
+ if (fwrite (outbuf, 1, write_size, out) != write_size)
+ goto out;
+
+ /* Reset next_out and avail_out. */
+ strm.next_out = outbuf;
+ strm.avail_out = sizeof outbuf;
+ }
+ }
+
+ if (status != LZMA_STREAM_END)
+ goto out;
+
+ lzma_end (&strm);
+ ret = TRUE;
+
+ out:
+ if (in)
+ fclose (in);
+
+ if (out)
+ fclose (out);
+
+ return ret;
+}
+
+static gboolean
+xz_save (const char *infile,
+ const char *outfile)
+{
+ gboolean ret;
+ FILE *in;
+ FILE *out;
+ lzma_stream strm = LZMA_STREAM_INIT;
+ lzma_action action;
+ guint8 inbuf[BUFSIZ];
+ guint8 outbuf[BUFSIZ];
+ lzma_ret status;
+ goffset tot = 0, file_size;
+
+ ret = FALSE;
+ in = NULL;
+ out = NULL;
+
+ in = g_fopen (infile, "rb");
+ if (!in)
+ goto out;
+
+ file_size = get_file_info (infile);
+ out = g_fopen (outfile, "wb");
+ if (!out)
+ goto out;
+
+ if (lzma_easy_encoder (&strm,
+ LZMA_PRESET_DEFAULT,
+ LZMA_CHECK_CRC64) != LZMA_OK)
+ goto out;
+
+ strm.next_in = NULL;
+ strm.avail_in = 0;
+ strm.next_out = outbuf;
+ strm.avail_out = sizeof outbuf;
+
+ action = LZMA_RUN;
+ status = LZMA_OK;
+
+ while (status == LZMA_OK)
+ {
+ /* Fill the input buffer if it is empty. */
+ if ((strm.avail_in == 0) && (!feof(in)))
+ {
+ strm.next_in = inbuf;
+ strm.avail_in = fread (inbuf, 1, sizeof inbuf, in);
+
+ if (ferror (in))
+ goto out;
+
+ /* Once the end of the input file has been reached, we need to
+ tell lzma_code() that no more input will be coming and that
+ it should finish the encoding. */
+ if (feof (in))
+ action = LZMA_FINISH;
+
+ gimp_progress_update ((tot += strm.avail_in) * 1.0 / file_size);
+ }
+
+ status = lzma_code (&strm, action);
+
+ if ((strm.avail_out == 0) || (status == LZMA_STREAM_END))
+ {
+ /* When lzma_code() has returned LZMA_STREAM_END, the output
+ buffer is likely to be only partially full. Calculate how
+ much new data there is to be written to the output file. */
+ size_t write_size = sizeof outbuf - strm.avail_out;
+
+ if (fwrite (outbuf, 1, write_size, out) != write_size)
+ goto out;
+
+ /* Reset next_out and avail_out. */
+ strm.next_out = outbuf;
+ strm.avail_out = sizeof outbuf;
+ }
+ }
+
+ if (status != LZMA_STREAM_END)
+ goto out;
+
+ lzma_end (&strm);
+ ret = TRUE;
+
+ out:
+ if (in)
+ fclose (in);
+
+ if (out)
+ fclose (out);
+
+ return ret;
+}
+
+/* get file size from a filename */
+static goffset
+get_file_info (const gchar *filename)
+{
+ GFile *file = g_file_new_for_path (filename);
+ GFileInfo *info;
+ goffset size = 1;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info)
+ {
+ size = g_file_info_get_size (info);
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (file);
+
+ return size;
+}
diff --git a/plug-ins/common/file-csource.c b/plug-ins/common/file-csource.c
new file mode 100644
index 0000000..9bcc623
--- /dev/null
+++ b/plug-ins/common/file-csource.c
@@ -0,0 +1,1045 @@
+/* CSource - GIMP Plugin to dump image data in RGB(A) format for C source
+ * Copyright (C) 1999 Tim Janik
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This plugin is heavily based on the header plugin by Spencer Kimball and
+ * Peter Mattis.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-csource-save"
+#define PLUG_IN_BINARY "file-csource"
+#define PLUG_IN_ROLE "gimp-file-csource"
+
+
+typedef struct
+{
+ gchar *prefixed_name;
+ gchar *comment;
+ gboolean use_comment;
+ gboolean glib_types;
+ gboolean alpha;
+ gboolean rgb565;
+ gboolean use_macros;
+ gboolean use_rle;
+ gdouble opacity;
+} Config;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_image (GFile *file,
+ Config *config,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean run_save_dialog (Config *config);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static Config config =
+{
+ "gimp_image", /* prefixed_name */
+ NULL, /* comment */
+ FALSE, /* use_comment */
+ TRUE, /* glib_types */
+ FALSE, /* alpha */
+ FALSE, /* rgb565 */
+ FALSE, /* use_macros */
+ FALSE, /* use_rle */
+ 100.0, /* opacity */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Dump image data in RGB(A) format for C source",
+ "CSource cannot be run non-interactively.",
+ "Tim Janik",
+ "Tim Janik",
+ "1999",
+ N_("C source code"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "text/x-csrc");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "c", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ strcmp (name, SAVE_PROC) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpParasite *parasite;
+ gchar *x;
+
+ gimp_get_data (SAVE_PROC, &config);
+
+ config.prefixed_name = "gimp_image";
+ config.comment = NULL;
+ config.alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
+ if (parasite)
+ {
+ config.comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ }
+ x = config.comment;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "C Source",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ if (run_save_dialog (&config))
+ {
+ if (x != config.comment &&
+ !(x && config.comment && strcmp (x, config.comment) == 0))
+ {
+ if (!config.comment || !config.comment[0])
+ {
+ gimp_image_detach_parasite (image_ID, "gimp-comment");
+ }
+ else
+ {
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (config.comment) + 1,
+ config.comment);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ if (! save_image (g_file_new_for_uri (param[3].data.d_string),
+ &config, image_ID, drawable_ID, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ else
+ {
+ gimp_set_data (SAVE_PROC, &config, sizeof (config));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+diff2_rgb565 (guint8 *ip)
+{
+ return ip[0] != ip[2] || ip[1] != ip[3];
+}
+
+static gboolean
+diff2_rgb (guint8 *ip)
+{
+ return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
+}
+
+static gboolean
+diff2_rgba (guint8 *ip)
+{
+ return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
+}
+
+static guint8 *
+rl_encode_rgbx (guint8 *bp,
+ guint8 *ip,
+ guint8 *limit,
+ guint bpp)
+{
+ gboolean (*diff2_pix) (guint8 *);
+ guint8 *ilimit = limit - bpp;
+
+ switch (bpp)
+ {
+ case 2: diff2_pix = diff2_rgb565; break;
+ case 3: diff2_pix = diff2_rgb; break;
+ case 4: diff2_pix = diff2_rgba; break;
+ default: g_assert_not_reached ();
+ }
+
+ while (ip < limit)
+ {
+ g_assert (ip < ilimit); /* paranoid */
+
+ if (diff2_pix (ip))
+ {
+ guint8 *s_ip = ip;
+ guint l = 1;
+
+ ip += bpp;
+ while (l < 127 && ip < ilimit && diff2_pix (ip))
+ { ip += bpp; l += 1; }
+ if (ip == ilimit && l < 127)
+ { ip += bpp; l += 1; }
+ *(bp++) = l;
+ memcpy (bp, s_ip, l * bpp);
+ bp += l * bpp;
+ }
+ else
+ {
+ guint l = 2;
+
+ ip += bpp;
+ while (l < 127 && ip < ilimit && !diff2_pix (ip))
+ { ip += bpp; l += 1; }
+ *(bp++) = l | 128;
+ memcpy (bp, ip, bpp);
+ ip += bpp;
+ bp += bpp;
+ }
+ if (ip == ilimit)
+ {
+ *(bp++) = 1;
+ memcpy (bp, ip, bpp);
+ ip += bpp;
+ bp += bpp;
+ }
+ }
+
+ return bp;
+}
+
+static gboolean print (GOutputStream *stream,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+static gboolean
+print (GOutputStream *stream,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (stream, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
+
+static inline gboolean
+save_rle_decoder (GOutputStream *output,
+ const gchar *macro_name,
+ const gchar *s_uint,
+ const gchar *s_uint_8,
+ guint bpp,
+ GError **error)
+{
+ return
+ print (output, error,
+ "#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
+ macro_name) &&
+ print (output, error,
+ "{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n",
+ s_uint, s_uint_8, s_uint_8) &&
+ print (output, error,
+ " __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n"
+ " __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n"
+ " while (__ip < __il) { %s __l = *(__rd++); \\\n",
+ s_uint) &&
+ print (output, error,
+ " if (__l & 128) { __l = __l - 128; \\\n"
+ " do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n"
+ " } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n"
+ " __ip += __l; __rd += __l; } } \\\n"
+ " } else if (__bpp == 3) { /* RGB */ \\\n"
+ " while (__ip < __il) { %s __l = *(__rd++); \\\n",
+ s_uint) &&
+ print (output, error,
+ " if (__l & 128) { __l = __l - 128; \\\n"
+ " do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n"
+ " } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n"
+ " __ip += __l; __rd += __l; } } \\\n"
+ " } else { /* RGB16 */ \\\n"
+ " while (__ip < __il) { %s __l = *(__rd++); \\\n",
+ s_uint) &&
+ print (output, error,
+ " if (__l & 128) { __l = __l - 128; \\\n"
+ " do { memcpy (__ip, __rd, 2); __ip += 2; } while (--__l); __rd += 2; \\\n"
+ " } else { __l *= 2; memcpy (__ip, __rd, __l); \\\n"
+ " __ip += __l; __rd += __l; } } \\\n"
+ " } } while (0)\n");
+}
+
+static inline gboolean
+save_uchar (GOutputStream *output,
+ guint *c,
+ guint8 d,
+ Config *config,
+ GError **error)
+{
+ static guint8 pad = 0;
+
+ if (*c > 74)
+ {
+ if (! config->use_macros)
+ {
+ if (! print (output, error, "\"\n \""))
+ return FALSE;
+
+ *c = 3;
+ }
+ else
+ {
+ if (! print (output, error, "\"\n \""))
+ return FALSE;
+
+ *c = 2;
+ }
+ }
+
+ if (d < 33 || (d >= 48 && d <= 57) || d > 126)
+ {
+ if (! print (output, error, "\\%03o", d))
+ return FALSE;
+
+ *c += 1 + 1 + (d > 7) + (d > 63);
+ pad = d < 64;
+
+ return TRUE;
+ }
+
+ if (d == '\\')
+ {
+ if (! print (output, error, "\\\\"))
+ return FALSE;
+
+ *c += 2;
+ }
+ else if (d == '"')
+ {
+ if (! print (output, error, "\\\""))
+ return FALSE;
+
+ *c += 2;
+ }
+ else if (pad && d >= '0' && d <= '9')
+ {
+ if (! print (output, error, "\"\"%c", d))
+ return FALSE;
+
+ *c += 3;
+ }
+ else
+ {
+ if (! print (output, error, "%c", d))
+ return FALSE;
+
+ *c += 1;
+ }
+
+ pad = 0;
+
+ return TRUE;
+}
+
+static gboolean
+save_image (GFile *file,
+ Config *config,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GeglBuffer *buffer;
+ GCancellable *cancellable;
+ GimpImageType drawable_type = gimp_drawable_type (drawable_ID);
+ gchar *s_uint_8, *s_uint, *s_char, *s_null;
+ guint c;
+ gchar *macro_name;
+ guint8 *img_buffer, *img_buffer_end;
+ gchar *basename;
+ guint8 *data, *p;
+ gint width;
+ gint height;
+ gint x, y, pad, n_bytes, bpp;
+ const Babl *drawable_format;
+ gint drawable_bpp;
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ drawable_format = babl_format ("R'G'B'A u8");
+ else
+ drawable_format = babl_format ("R'G'B' u8");
+
+ drawable_bpp = babl_format_get_bytes_per_pixel (drawable_format);
+
+ bpp = config->rgb565 ? 2 : (config->alpha ? 4 : 3);
+ n_bytes = width * height * bpp;
+ pad = width * drawable_bpp;
+ if (config->use_rle)
+ pad = MAX (pad, 130 + n_bytes / 127);
+
+ data = g_new (guint8, pad + n_bytes);
+ p = data + pad;
+
+ for (y = 0; y < height; y++)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
+ drawable_format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (bpp == 2)
+ {
+ for (x = 0; x < width; x++)
+ {
+ guint8 *d = data + x * drawable_bpp;
+ guint8 r, g, b;
+ gushort rgb16;
+ gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
+
+ alpha *= config->opacity / 25500.0;
+ r = (0.5 + alpha * (gdouble) d[0]);
+ g = (0.5 + alpha * (gdouble) d[1]);
+ b = (0.5 + alpha * (gdouble) d[2]);
+ r >>= 3;
+ g >>= 2;
+ b >>= 3;
+ rgb16 = (r << 11) + (g << 5) + b;
+ *(p++) = (guchar) rgb16;
+ *(p++) = (guchar) (rgb16 >> 8);
+ }
+ }
+ else if (config->alpha)
+ {
+ for (x = 0; x < width; x++)
+ {
+ guint8 *d = data + x * drawable_bpp;
+ gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
+
+ alpha *= config->opacity / 100.0;
+ *(p++) = d[0];
+ *(p++) = d[1];
+ *(p++) = d[2];
+ *(p++) = alpha + 0.5;
+ }
+ }
+ else
+ {
+ for (x = 0; x < width; x++)
+ {
+ guint8 *d = data + x * drawable_bpp;
+ gdouble alpha = drawable_type == GIMP_RGBA_IMAGE ? d[3] : 0xff;
+
+ alpha *= config->opacity / 25500.0;
+ *(p++) = 0.5 + alpha * (gdouble) d[0];
+ *(p++) = 0.5 + alpha * (gdouble) d[1];
+ *(p++) = 0.5 + alpha * (gdouble) d[2];
+ }
+ }
+ }
+
+ img_buffer = data + pad;
+ if (config->use_rle)
+ {
+ img_buffer_end = rl_encode_rgbx (data, img_buffer,
+ img_buffer + n_bytes, bpp);
+ img_buffer = data;
+ }
+ else
+ {
+ img_buffer_end = img_buffer + n_bytes;
+ }
+
+ if (!config->use_macros && config->glib_types)
+ {
+ s_uint_8 = "guint8 ";
+ s_uint = "guint ";
+ s_char = "gchar ";
+ s_null = "NULL";
+ }
+ else if (!config->use_macros)
+ {
+ s_uint_8 = "unsigned char";
+ s_uint = "unsigned int ";
+ s_char = "char ";
+ s_null = "(char*) 0";
+ }
+ else if (config->use_macros && config->glib_types)
+ {
+ s_uint_8 = "guint8";
+ s_uint = "guint";
+ s_char = "gchar";
+ s_null = "NULL";
+ }
+ else /* config->use_macros && !config->glib_types */
+ {
+ s_uint_8 = "unsigned char";
+ s_uint = "unsigned int";
+ s_char = "char";
+ s_null = "(char*) 0";
+ }
+
+ macro_name = g_ascii_strup (config->prefixed_name, -1);
+
+ basename = g_file_get_basename (file);
+
+ if (! print (output, error,
+ "/* GIMP %s C-Source image dump %s(%s) */\n\n",
+ config->alpha ? "RGBA" : "RGB",
+ config->use_rle ? "1-byte-run-length-encoded " : "",
+ basename))
+ goto fail;
+
+ g_free (basename);
+
+ if (config->use_rle && !config->use_macros)
+ {
+ if (! save_rle_decoder (output,
+ macro_name,
+ config->glib_types ? "guint" : "unsigned int",
+ config->glib_types ? "guint8" : "unsigned char",
+ bpp,
+ error))
+ goto fail;
+ }
+
+ if (!config->use_macros)
+ {
+ if (! print (output, error,
+ "static const struct {\n"
+ " %s\t width;\n"
+ " %s\t height;\n"
+ " %s\t bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ \n",
+ s_uint, s_uint, s_uint))
+ goto fail;
+
+ if (config->use_comment)
+ {
+ if (! print (output, error, " %s\t*comment;\n", s_char))
+ goto fail;
+ }
+
+ if (! print (output, error,
+ " %s\t %spixel_data[",
+ s_uint_8,
+ config->use_rle ? "rle_" : ""))
+ goto fail;
+
+ if (config->use_rle)
+ {
+ if (! print (output, error,
+ "%u + 1];\n",
+ (guint) (img_buffer_end - img_buffer)))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%u * %u * %u + 1];\n",
+ width,
+ height,
+ bpp))
+ goto fail;
+ }
+
+ if (! print (output, error, "} %s = {\n", config->prefixed_name))
+ goto fail;
+
+ if (! print (output, error,
+ " %u, %u, %u,\n",
+ width,
+ height,
+ bpp))
+ goto fail;
+ }
+ else /* use macros */
+ {
+ if (! print (output, error,
+ "#define %s_WIDTH (%u)\n"
+ "#define %s_HEIGHT (%u)\n"
+ "#define %s_BYTES_PER_PIXEL (%u) /* 2:RGB16, 3:RGB, 4:RGBA */\n",
+ macro_name, width,
+ macro_name, height,
+ macro_name, bpp))
+ {
+ goto fail;
+ }
+ }
+
+ if (config->use_comment && !config->comment)
+ {
+ if (! config->use_macros)
+ {
+ if (! print (output, error, " %s,\n", s_null))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "#define %s_COMMENT (%s)\n",
+ macro_name, s_null))
+ goto fail;
+ }
+ }
+ else if (config->use_comment)
+ {
+ gchar *p = config->comment - 1;
+
+ if (config->use_macros)
+ {
+ if (! print (output, error, "#define %s_COMMENT \\\n", macro_name))
+ goto fail;
+ }
+
+ if (! print (output, error, " \""))
+ goto fail;
+
+ while (*(++p))
+ {
+ gboolean success = FALSE;
+
+ if (*p == '\\')
+ success = print (output, error, "\\\\");
+ else if (*p == '"')
+ success = print (output, error, "\\\"");
+ else if (*p == '\n' && p[1])
+ success = print (output, error,
+ "\\n\"%s\n \"",
+ config->use_macros ? " \\" : "");
+ else if (*p == '\n')
+ success = print (output, error, "\\n");
+ else if (*p == '\r')
+ success = print (output, error, "\\r");
+ else if (*p == '\b')
+ success = print (output, error, "\\b");
+ else if (*p == '\f')
+ success = print (output, error, "\\f");
+ else if (( *p >= 32 && *p <= 47 ) || (*p >= 58 && *p <= 126))
+ success = print (output, error, "%c", *p);
+ else
+ success = print (output, error, "\\%03o", *p);
+
+ if (! success)
+ goto fail;
+ }
+
+ if (! config->use_macros)
+ {
+ if (! print (output, error, "\",\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error, "\"\n"))
+ goto fail;
+ }
+ }
+
+ if (config->use_macros)
+ {
+ if (! print (output, error,
+ "#define %s_%sPIXEL_DATA ((%s*) %s_%spixel_data)\n",
+ macro_name,
+ config->use_rle ? "RLE_" : "",
+ s_uint_8,
+ macro_name,
+ config->use_rle ? "rle_" : ""))
+ goto fail;
+
+ if (config->use_rle)
+ {
+ if (! save_rle_decoder (output,
+ macro_name,
+ s_uint,
+ s_uint_8,
+ bpp,
+ error))
+ goto fail;
+ }
+
+ if (! print (output, error,
+ "static const %s %s_%spixel_data[",
+ s_uint_8,
+ macro_name,
+ config->use_rle ? "rle_" : ""))
+ goto fail;
+
+ if (config->use_rle)
+ {
+ if (! print (output, error,
+ "%u + 1] =\n",
+ (guint) (img_buffer_end - img_buffer)))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%u * %u * %u + 1] =\n",
+ width,
+ height,
+ bpp))
+ goto fail;
+ }
+
+ if (! print (output, error, "(\""))
+ goto fail;
+
+ c = 2;
+ }
+ else
+ {
+ if (! print (output, error, " \""))
+ goto fail;
+
+ c = 3;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ case GIMP_RGBA_IMAGE:
+ do
+ {
+ if (! save_uchar (output, &c, *(img_buffer++), config, error))
+ goto fail;
+ }
+ while (img_buffer < img_buffer_end);
+ break;
+
+ default:
+ g_warning ("unhandled drawable type (%d)", drawable_type);
+ goto fail;
+ }
+
+ if (! config->use_macros)
+ {
+ if (! print (output, error, "\",\n};\n\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error, "\");\n\n"))
+ goto fail;
+ }
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_object_unref (output);
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_object_unref (output);
+ g_object_unref (buffer);
+
+ return FALSE;
+}
+
+static void
+rgb565_toggle_button_update (GtkWidget *toggle,
+ gpointer data)
+{
+ GtkWidget *widget;
+ gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle));
+
+ gimp_toggle_button_update (toggle, data);
+
+ widget = g_object_get_data (G_OBJECT (toggle), "set-insensitive-1");
+ if (widget)
+ gtk_widget_set_sensitive (widget, ! active);
+}
+
+static gboolean
+run_save_dialog (Config *config)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *prefixed_name;
+ GtkWidget *centry;
+ GtkWidget *toggle;
+ GtkWidget *alpha_toggle;
+ GtkObject *adj;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("C-Source"), PLUG_IN_BINARY, SAVE_PROC);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Prefixed Name
+ */
+ prefixed_name = gtk_entry_new ();
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Prefixed name:"), 0.0, 0.5,
+ prefixed_name, 1, FALSE);
+ gtk_entry_set_text (GTK_ENTRY (prefixed_name),
+ config->prefixed_name ? config->prefixed_name : "");
+
+ /* Comment Entry
+ */
+ centry = gtk_entry_new ();
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Co_mment:"), 0.0, 0.5,
+ centry, 1, FALSE);
+ gtk_entry_set_text (GTK_ENTRY (centry),
+ config->comment ? config->comment : "");
+
+ /* Use Comment
+ */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Save comment to file"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->use_comment);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &config->use_comment);
+
+ /* GLib types
+ */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Use GLib types (guint8*)"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->glib_types);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &config->glib_types);
+
+ /* Use Macros
+ */
+ toggle =
+ gtk_check_button_new_with_mnemonic (_("Us_e macros instead of struct"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->use_macros);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &config->use_macros);
+
+ /* Use RLE
+ */
+ toggle =
+ gtk_check_button_new_with_mnemonic (_("Use _1 byte Run-Length-Encoding"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->use_rle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &config->use_rle);
+
+ /* Alpha
+ */
+ alpha_toggle = toggle =
+ gtk_check_button_new_with_mnemonic (_("Sa_ve alpha channel (RGBA/RGB)"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->alpha);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &config->alpha);
+
+ /* RGB-565
+ */
+ toggle = gtk_check_button_new_with_mnemonic (_("Save as _RGB565 (16-bit)"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+
+ /* Alpha setting is not used with RGB-565 */
+ g_object_set_data (G_OBJECT (toggle), "set-insensitive-1", alpha_toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (rgb565_toggle_button_update),
+ &config->rgb565);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ config->rgb565);
+
+ /* Max Alpha Value
+ */
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Op_acity:"), 100, 0,
+ config->opacity, 0, 100, 1, 10, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config->opacity);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ config->prefixed_name =
+ g_strdup (gtk_entry_get_text (GTK_ENTRY (prefixed_name)));
+ config->comment = g_strdup (gtk_entry_get_text (GTK_ENTRY (centry)));
+ }
+
+ gtk_widget_destroy (dialog);
+
+ if (!config->prefixed_name || !config->prefixed_name[0])
+ config->prefixed_name = "tmp";
+
+ if (config->comment && !config->comment[0])
+ config->comment = NULL;
+
+ return run;
+}
diff --git a/plug-ins/common/file-desktop-link.c b/plug-ins/common/file-desktop-link.c
new file mode 100644
index 0000000..315adb5
--- /dev/null
+++ b/plug-ins/common/file-desktop-link.c
@@ -0,0 +1,185 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Desktop Entry Specification
+ * http://standards.freedesktop.org/desktop-entry-spec/latest/
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-desktop-link-load"
+#define PLUG_IN_BINARY "file-desktop-link"
+#define PLUG_IN_ROLE "gimp-file-desktop-link"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Follows a link to an image in a .desktop file",
+ "Opens a .desktop file and if it is a link, it "
+ "asks GIMP to open the file the link points to.",
+ "Sven Neumann",
+ "Sven Neumann",
+ "2006",
+ N_("Desktop Link"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_load_handler (LOAD_PROC, "desktop", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ GError *error = NULL;
+ gint32 image_ID;
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, run_mode, &error);
+
+ if (image_ID != -1)
+ {
+ status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **load_error)
+{
+ GKeyFile *file = g_key_file_new ();
+ gchar *group = NULL;
+ gchar *value = NULL;
+ gint32 image_ID = -1;
+ GError *error = NULL;
+
+ if (! g_key_file_load_from_file (file, filename, G_KEY_FILE_NONE, &error))
+ goto out;
+
+ group = g_key_file_get_start_group (file);
+ if (! group || strcmp (group, G_KEY_FILE_DESKTOP_GROUP) != 0)
+ goto out;
+
+ value = g_key_file_get_value (file,
+ group, G_KEY_FILE_DESKTOP_KEY_TYPE, &error);
+ if (! value || strcmp (value, G_KEY_FILE_DESKTOP_TYPE_LINK) != 0)
+ goto out;
+
+ g_free (value);
+
+ value = g_key_file_get_value (file,
+ group, G_KEY_FILE_DESKTOP_KEY_URL, &error);
+ if (value)
+ image_ID = gimp_file_load (run_mode, value, value);
+
+ out:
+ if (error)
+ {
+ g_set_error (load_error, error->domain, error->code,
+ _("Error loading desktop file '%s': %s"),
+ gimp_filename_to_utf8 (filename), error->message);
+ g_error_free (error);
+ }
+
+ g_free (value);
+ g_free (group);
+ g_key_file_free (file);
+
+ return image_ID;
+}
diff --git a/plug-ins/common/file-dicom.c b/plug-ins/common/file-dicom.c
new file mode 100644
index 0000000..7a3d400
--- /dev/null
+++ b/plug-ins/common/file-dicom.c
@@ -0,0 +1,1796 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * PNM reading and writing code Copyright (C) 1996 Erik Nygren
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * The dicom reading and writing code was written from scratch
+ * by Dov Grobgeld. (dov.grobgeld@gmail.com).
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-dicom-load"
+#define SAVE_PROC "file-dicom-save"
+#define PLUG_IN_BINARY "file-dicom"
+#define PLUG_IN_ROLE "gimp-file-dicom"
+
+
+/* A lot of Dicom images are wrongly encoded. By guessing the endian
+ * we can get around this problem.
+ */
+#define GUESS_ENDIAN 1
+
+/* Declare local data types */
+typedef struct _DicomInfo
+{
+ guint width, height; /* The size of the image */
+ gint maxval; /* For 16 and 24 bit image files, the max
+ value which we need to normalize to */
+ gint samples_per_pixel; /* Number of image planes (0 for pbm) */
+ gint bpp;
+ gint bits_stored;
+ gint high_bit;
+ gboolean is_signed;
+ gboolean planar;
+ gboolean bw_inverted;
+} DicomInfo;
+
+/* Local function prototypes */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static void dicom_loader (guint8 *pix_buf,
+ DicomInfo *info,
+ GeglBuffer *buffer);
+static void guess_and_set_endian2 (guint16 *buf16,
+ gint length);
+static void toggle_endian2 (guint16 *buf16,
+ gint length);
+static void add_tag_pointer (GByteArray *group_stream,
+ gint group,
+ gint element,
+ const gchar *value_rep,
+ const guint8 *data,
+ gint length);
+static GSList * dicom_add_tags (FILE *DICOM,
+ GByteArray *group_stream,
+ GSList *elements);
+static gboolean write_group_to_file (FILE *DICOM,
+ gint group,
+ GByteArray *group_stream);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads files of the dicom file format",
+ "Load a file in the DICOM standard format."
+ "The standard is defined at "
+ "http://medical.nema.org/. The plug-in currently "
+ "only supports reading images with uncompressed "
+ "pixel sections.",
+ "Dov Grobgeld",
+ "Dov Grobgeld <dov@imagic.weizmann.ac.il>",
+ "2003",
+ N_("DICOM image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-dcm");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "dcm,dicom",
+ "",
+ "128,string,DICM"
+ );
+
+ gimp_install_procedure (SAVE_PROC,
+ "Save file in the DICOM file format",
+ "Save an image in the medical standard DICOM image "
+ "formats. The standard is defined at "
+ "http://medical.nema.org/. The file format is "
+ "defined in section 10 of the standard. The files "
+ "are saved uncompressed and the compulsory DICOM "
+ "tags are filled with default dummy values.",
+ "Dov Grobgeld",
+ "Dov Grobgeld <dov@imagic.weizmann.ac.il>",
+ "2003",
+ N_("Digital Imaging and Communications in "
+ "Medicine image"),
+ "RGB, GRAY",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-dcm");
+ gimp_register_save_handler (SAVE_PROC, "dcm,dicom", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ export = gimp_export_image (&image_ID, &drawable_ID, "DICOM",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/**
+ * add_parasites_to_image:
+ * @data: pointer to a GimpParasite to be attached to the image
+ * specified by @user_data.
+ * @user_data: pointer to the image_ID to which parasite @data should
+ * be added.
+ *
+ * Attaches parasite to image and also frees that parasite
+**/
+static void
+add_parasites_to_image (gpointer data,
+ gpointer user_data)
+{
+ GimpParasite *parasite = (GimpParasite *) data;
+ gint32 *image_ID = (gint32 *) user_data;
+
+ gimp_image_attach_parasite (*image_ID, parasite);
+ gimp_parasite_free (parasite);
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 volatile image_ID = -1;
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ GSList *elements = NULL;
+ FILE *DICOM;
+ gchar buf[500]; /* buffer for random things like scanning */
+ DicomInfo *dicominfo;
+ guint width = 0;
+ guint height = 0;
+ gint samples_per_pixel = 0;
+ gint bpp = 0;
+ gint bits_stored = 0;
+ gint high_bit = 0;
+ guint8 *pix_buf = NULL;
+ gboolean is_signed = FALSE;
+ guint8 in_sequence = 0;
+ gboolean implicit_encoding = FALSE;
+ gboolean big_endian = FALSE;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ DICOM = g_fopen (filename, "rb");
+
+ if (! DICOM)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ /* allocate the necessary structures */
+ dicominfo = g_new0 (DicomInfo, 1);
+
+ /* Parse the file */
+ fread (buf, 1, 128, DICOM); /* skip past buffer */
+
+ /* Check for unsupported formats */
+ if (g_ascii_strncasecmp (buf, "PAPYRUS", 7) == 0)
+ {
+ g_message ("'%s' is a PAPYRUS DICOM file.\n"
+ "This plug-in does not support this type yet.",
+ gimp_filename_to_utf8 (filename));
+ g_free (dicominfo);
+ fclose (DICOM);
+ return -1;
+ }
+
+ fread (buf, 1, 4, DICOM); /* This should be dicom */
+ if (g_ascii_strncasecmp (buf,"DICM",4) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a DICOM file."),
+ gimp_filename_to_utf8 (filename));
+ g_free (dicominfo);
+ fclose (DICOM);
+ return -1;
+ }
+
+ while (!feof (DICOM))
+ {
+ guint16 group_word;
+ guint16 element_word;
+ gchar value_rep[3];
+ guint32 element_length;
+ guint16 ctx_us;
+ guint8 *value;
+ guint32 tag;
+
+ if (fread (&group_word, 1, 2, DICOM) == 0)
+ break;
+ group_word = g_ntohs (GUINT16_SWAP_LE_BE (group_word));
+
+ fread (&element_word, 1, 2, DICOM);
+ element_word = g_ntohs (GUINT16_SWAP_LE_BE (element_word));
+
+ if (group_word != 0x0002 && big_endian)
+ {
+ group_word = GUINT16_SWAP_LE_BE (group_word);
+ element_word = GUINT16_SWAP_LE_BE (element_word);
+ }
+
+ tag = (group_word << 16) | element_word;
+ fread(value_rep, 2, 1, DICOM);
+ value_rep[2] = 0;
+
+ /* Check if the value rep looks valid. There probably is a
+ better way of checking this...
+ */
+ if ((/* Always need lookup for implicit encoding */
+ tag > 0x0002ffff && implicit_encoding)
+ /* This heuristics isn't used if we are doing implicit
+ encoding according to the value representation... */
+ || ((value_rep[0] < 'A' || value_rep[0] > 'Z'
+ || value_rep[1] < 'A' || value_rep[1] > 'Z')
+
+ /* I found this in one of Ednas images. It seems like a
+ bug...
+ */
+ && !(value_rep[0] == ' ' && value_rep[1]))
+ )
+ {
+ /* Look up type from the dictionary. At the time we don't
+ support this option... */
+ gchar element_length_chars[4];
+
+ /* Store the bytes that were read */
+ element_length_chars[0] = value_rep[0];
+ element_length_chars[1] = value_rep[1];
+
+ /* Unknown value rep. It is not used right now anyhow */
+ strcpy (value_rep, "??");
+
+ /* For implicit value_values the length is always four bytes,
+ so we need to read another two. */
+ fread (&element_length_chars[2], 1, 2, DICOM);
+
+ /* Now cast to integer and insert into element_length */
+ if (big_endian && group_word != 0x0002)
+ element_length =
+ g_ntohl (*((gint *) element_length_chars));
+ else
+ element_length =
+ g_ntohl (GUINT32_SWAP_LE_BE (*((gint *) element_length_chars)));
+ }
+ /* Binary value reps are OB, OW, SQ or UN */
+ else if (strncmp (value_rep, "OB", 2) == 0
+ || strncmp (value_rep, "OW", 2) == 0
+ || strncmp (value_rep, "SQ", 2) == 0
+ || strncmp (value_rep, "UN", 2) == 0)
+ {
+ fread (&element_length, 1, 2, DICOM); /* skip two bytes */
+ fread (&element_length, 1, 4, DICOM);
+ if (big_endian && group_word != 0x0002)
+ element_length = g_ntohl (element_length);
+ else
+ element_length = g_ntohl (GUINT32_SWAP_LE_BE (element_length));
+ }
+ /* Short length */
+ else
+ {
+ guint16 el16;
+
+ fread (&el16, 1, 2, DICOM);
+ if (big_endian && group_word != 0x0002)
+ element_length = g_ntohs (el16);
+ else
+ element_length = g_ntohs (GUINT16_SWAP_LE_BE (el16));
+ }
+
+ /* Sequence of items - just ignore the delimiters... */
+ if (element_length == 0xffffffff)
+ {
+ in_sequence = 1;
+ continue;
+ }
+ /* End of Sequence tag */
+ if (tag == 0xFFFEE0DD)
+ {
+ in_sequence = 0;
+ continue;
+ }
+
+ /* Sequence of items item tag... Ignore as well */
+ if (tag == 0xFFFEE000)
+ continue;
+
+ /* Even for pixel data, we don't handle very large element
+ lengths */
+
+ if (element_length >= (G_MAXUINT - 6))
+ {
+ g_message ("'%s' seems to have an incorrect value field length.",
+ gimp_filename_to_utf8 (filename));
+ gimp_quit ();
+ }
+
+ /* Read contents. Allocate a bit more to make room for casts to int
+ below. */
+ value = g_new0 (guint8, element_length + 4);
+ fread (value, 1, element_length, DICOM);
+
+ /* ignore everything inside of a sequence */
+ if (in_sequence)
+ {
+ g_free (value);
+ continue;
+ }
+ /* Some special casts that are used below */
+ ctx_us = *(guint16 *) value;
+ if (big_endian && group_word != 0x0002)
+ ctx_us = GUINT16_SWAP_LE_BE (ctx_us);
+
+ g_debug ("group: %04x, element: %04x, length: %d",
+ group_word, element_word, element_length);
+ g_debug ("Value: %s", (char*)value);
+ /* Recognize some critical tags */
+ if (group_word == 0x0002)
+ {
+ switch (element_word)
+ {
+ case 0x0010: /* transfer syntax id */
+ if (strcmp("1.2.840.10008.1.2", (char*)value) == 0)
+ {
+ implicit_encoding = TRUE;
+ g_debug ("Transfer syntax: Implicit VR Endian: Default Transfer Syntax for DICOM.");
+ }
+ else if (strcmp("1.2.840.10008.1.2.1", (char*)value) == 0)
+ {
+ g_debug ("Transfer syntax: Explicit VR Little Endian.");
+ }
+ else if (strcmp("1.2.840.10008.1.2.1.99", (char*)value) == 0)
+ {
+ g_debug ("Transfer syntax: Deflated Explicit VR Little Endian.");
+ }
+ else if (strcmp("1.2.840.10008.1.2.2", (char*)value) == 0)
+ {
+ /* This Transfer Syntax was retired in 2006. For the most recent description of it, see PS3.5 2016b */
+ big_endian = TRUE;
+ g_debug ("Transfer syntax: Deprecated Explicit VR Big Endian.");
+ }
+ else
+ {
+ g_debug ("Transfer syntax %s is not supported by GIMP.", (gchar *) value);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Transfer syntax %s is not supported by GIMP."),
+ (gchar *) value);
+ g_free (dicominfo);
+ fclose (DICOM);
+ return -1;
+ }
+ break;
+ }
+ }
+ else if (group_word == 0x0028)
+ {
+ gboolean supported = TRUE;
+
+ switch (element_word)
+ {
+ case 0x0002: /* samples per pixel */
+ samples_per_pixel = ctx_us;
+ g_debug ("spp: %d", samples_per_pixel);
+ break;
+ case 0x0004: /* photometric interpretation */
+ g_debug ("photometric interpretation: %s", (gchar *) value);
+
+ if (samples_per_pixel == 1)
+ {
+ if (strncmp ((gchar *) value, "MONOCHROME1", 11) == 0)
+ {
+ /* The minimum sample value is intended to be displayed
+ * as white after any VOI gray scale transformations
+ * have been performed. */
+ dicominfo->bw_inverted = TRUE;
+ }
+ else if (strncmp ((gchar *) value, "MONOCHROME2", 11) == 0)
+ {
+ /* The minimum sample value is intended to be displayed
+ * as black after any VOI gray scale transformations
+ * have been performed. */
+ dicominfo->bw_inverted = FALSE;
+ }
+ else
+ supported = FALSE;
+ }
+ else if (samples_per_pixel == 3)
+ {
+ if (strncmp ((gchar *) value, "RGB", 2) != 0)
+ {
+ supported = FALSE;
+ }
+ }
+ else
+ {
+ supported = FALSE;
+ }
+ if (! supported)
+ {
+ g_set_error (error, GIMP_PLUG_IN_ERROR, 0,
+ _("%s is not supported by GIMP in combination "
+ "with samples per pixel: %d"),
+ (gchar *) value, samples_per_pixel);
+ g_free (dicominfo);
+ fclose (DICOM);
+ return -1;
+ }
+
+ break;
+ case 0x0006: /* planar configuration */
+ g_debug ("planar configuration: %u", ctx_us);
+ dicominfo->planar = (ctx_us == 1);
+ break;
+ case 0x0008: /* number of frames */
+ g_debug ("number of frames: %d", ctx_us);
+ break;
+ case 0x0010: /* rows */
+ height = ctx_us;
+ g_debug ("height: %d", height);
+ break;
+ case 0x0011: /* columns */
+ width = ctx_us;
+ g_debug ("width: %d", width);
+ break;
+ case 0x0100: /* bits allocated */
+ bpp = ctx_us;
+ g_debug ("bpp: %d", bpp);
+ break;
+ case 0x0101: /* bits stored */
+ bits_stored = ctx_us;
+ g_debug ("bits stored: %d", bits_stored);
+ break;
+ case 0x0102: /* high bit */
+ high_bit = ctx_us;
+ g_debug ("high bit: %d", high_bit);
+ break;
+ case 0x0103: /* is pixel representation signed? */
+ is_signed = (ctx_us == 0) ? FALSE : TRUE;
+ g_debug ("is signed: %d", ctx_us);
+ break;
+ }
+ }
+
+ /* Pixel data */
+ if (group_word == 0x7fe0 && element_word == 0x0010)
+ {
+ pix_buf = value;
+ }
+ else
+ {
+ /* save this element to a parasite for later writing */
+ GimpParasite *parasite;
+ gchar pname[255];
+
+ /* all elements are retrievable using gimp_get_parasite_list() */
+ g_snprintf (pname, sizeof (pname),
+ "dcm/%04x-%04x-%s", group_word, element_word, value_rep);
+ if ((parasite = gimp_parasite_new (pname,
+ GIMP_PARASITE_PERSISTENT,
+ element_length, value)))
+ {
+ /*
+ * at this point, the image has not yet been created, so
+ * image_ID is not valid. keep the parasite around
+ * until we're able to attach it.
+ */
+
+ /* add to our list of parasites to be added (prepending
+ * for speed. we'll reverse it later)
+ */
+ elements = g_slist_prepend (elements, parasite);
+ }
+
+ g_free (value);
+ }
+ }
+
+ if ((bpp != 8) && (bpp != 16))
+ {
+ g_message ("'%s' has a bpp of %d which GIMP cannot handle.",
+ gimp_filename_to_utf8 (filename), bpp);
+ gimp_quit ();
+ }
+
+ if ((width > GIMP_MAX_IMAGE_SIZE) || (height > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_message ("'%s' has a larger image size (%d x %d) than GIMP can handle.",
+ gimp_filename_to_utf8 (filename), width, height);
+ gimp_quit ();
+ }
+
+ if (samples_per_pixel > 3)
+ {
+ g_message ("'%s' has samples per pixel of %d which GIMP cannot handle.",
+ gimp_filename_to_utf8 (filename), samples_per_pixel);
+ gimp_quit ();
+ }
+
+ dicominfo->width = width;
+ dicominfo->height = height;
+ dicominfo->bpp = bpp;
+
+ dicominfo->bits_stored = bits_stored;
+ dicominfo->high_bit = high_bit;
+ dicominfo->is_signed = is_signed;
+ dicominfo->samples_per_pixel = samples_per_pixel;
+ dicominfo->maxval = -1; /* External normalization factor - not used yet */
+
+ /* Create a new image of the proper size and associate the filename with it.
+ */
+ image_ID = gimp_image_new (dicominfo->width, dicominfo->height,
+ (dicominfo->samples_per_pixel >= 3 ?
+ GIMP_RGB : GIMP_GRAY));
+ gimp_image_set_filename (image_ID, filename);
+
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ dicominfo->width, dicominfo->height,
+ (dicominfo->samples_per_pixel >= 3 ?
+ GIMP_RGB_IMAGE : GIMP_GRAY_IMAGE),
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+#if GUESS_ENDIAN
+ if (bpp == 16)
+ guess_and_set_endian2 ((guint16 *) pix_buf, width * height);
+#endif
+
+ dicom_loader (pix_buf, dicominfo, buffer);
+
+ if (elements)
+ {
+ /* flip the parasites back around into the order they were
+ * created (read from the file)
+ */
+ elements = g_slist_reverse (elements);
+ /* and add each one to the image */
+ g_slist_foreach (elements, add_parasites_to_image, (gpointer) &image_ID);
+ g_slist_free (elements);
+ }
+
+ g_free (pix_buf);
+ g_free (dicominfo);
+
+ fclose (DICOM);
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+static void
+dicom_loader (guint8 *pix_buffer,
+ DicomInfo *info,
+ GeglBuffer *buffer)
+{
+ guchar *data;
+ gint row_idx;
+ gint width = info->width;
+ gint height = info->height;
+ gint samples_per_pixel = info->samples_per_pixel;
+ guint16 *buf16 = (guint16 *) pix_buffer;
+
+ if (info->bpp == 16)
+ {
+ gulong pix_idx;
+ guint shift = info->high_bit + 1 - info->bits_stored;
+
+ /* Reorder the buffer; also shift the data so that the LSB
+ * of the pixel data is at the LSB of the 16-bit array entries
+ * (i.e., compensate for high_bit and bits_stored).
+ */
+ for (pix_idx = 0; pix_idx < width * height * samples_per_pixel; pix_idx++)
+ buf16[pix_idx] = g_ntohs (GUINT16_SWAP_LE_BE (buf16[pix_idx])) >> shift;
+ }
+
+ data = g_malloc (gimp_tile_height () * width * samples_per_pixel);
+
+ for (row_idx = 0; row_idx < height; )
+ {
+ guchar *d = data;
+ gint start;
+ gint end;
+ gint scanlines;
+ gint i;
+
+ start = row_idx;
+ end = row_idx + gimp_tile_height ();
+ end = MIN (end, height);
+
+ scanlines = end - start;
+
+ for (i = 0; i < scanlines; i++)
+ {
+ if (info->bpp == 16)
+ {
+ guint16 *row_start;
+ gint col_idx;
+
+ row_start = buf16 + (row_idx + i) * width * samples_per_pixel;
+
+ for (col_idx = 0; col_idx < width * samples_per_pixel; col_idx++)
+ {
+ /* Shift it by 8 bits, or less in case bits_stored
+ * is less than bpp.
+ */
+ d[col_idx] = (guint8) (row_start[col_idx] >>
+ (info->bits_stored - 8));
+ if (info->bw_inverted)
+ {
+ d[col_idx] = ~d[col_idx];
+ }
+
+ if (info->is_signed)
+ {
+ /* If the data is negative, make it 0. Otherwise,
+ * multiply the positive value by 2, so that the
+ * positive values span between 0 and 254.
+ */
+ if (d[col_idx] > 127)
+ d[col_idx] = 0;
+ else
+ d[col_idx] <<= 1;
+ }
+ }
+ }
+ else if (info->bpp == 8)
+ {
+ if (! info->planar)
+ {
+ guint8 *row_start;
+ gint col_idx;
+
+ row_start = (pix_buffer +
+ (row_idx + i) * width * samples_per_pixel);
+
+ for (col_idx = 0; col_idx < width * samples_per_pixel; col_idx++)
+ {
+ /* Shift it by 0 bits, or more in case bits_stored is
+ * less than bpp.
+ */
+ d[col_idx] = row_start[col_idx] << (8 - info->bits_stored);
+ if (info->bw_inverted)
+ {
+ d[col_idx] = ~d[col_idx];
+ }
+
+ if (info->is_signed)
+ {
+ /* If the data is negative, make it 0. Otherwise,
+ * multiply the positive value by 2, so that the
+ * positive values span between 0 and 254.
+ */
+ if (d[col_idx] > 127)
+ d[col_idx] = 0;
+ else
+ d[col_idx] <<= 1;
+ }
+ }
+ }
+ else
+ {
+ /* planar organization of color data */
+ guint8 *row_start;
+ gint col_idx;
+ gint plane_size = width * height;
+
+ row_start = (pix_buffer + (row_idx + i) * width);
+
+ for (col_idx = 0; col_idx < width; col_idx++)
+ {
+ /* Shift it by 0 bits, or more in case bits_stored is
+ * less than bpp.
+ */
+ gint pix_idx;
+ gint src_offset = col_idx;
+
+ for (pix_idx = 0; pix_idx < samples_per_pixel; pix_idx++)
+ {
+ gint dest_idx = col_idx * samples_per_pixel + pix_idx;
+
+ d[dest_idx] = row_start[src_offset] << (8 - info->bits_stored);
+ if (info->is_signed)
+ {
+ /* If the data is negative, make it 0. Otherwise,
+ * multiply the positive value by 2, so that the
+ * positive values span between 0 and 254.
+ */
+ if (d[dest_idx] > 127)
+ d[dest_idx] = 0;
+ else
+ d[dest_idx] <<= 1;
+ }
+ src_offset += plane_size;
+ }
+ }
+ }
+ }
+
+ d += width * samples_per_pixel;
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, row_idx, width, scanlines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ row_idx += scanlines;
+
+ gimp_progress_update ((gdouble) row_idx / (gdouble) height);
+ }
+
+ g_free (data);
+
+ gimp_progress_update (1.0);
+}
+
+
+/* Guess and set endian. Guesses the endian of a buffer by
+ * checking the maximum value of the first and the last byte
+ * in the words of the buffer. It assumes that the least
+ * significant byte has a larger maximum than the most
+ * significant byte.
+ */
+static void
+guess_and_set_endian2 (guint16 *buf16,
+ int length)
+{
+ guint16 *p = buf16;
+ gint max_first = -1;
+ gint max_second = -1;
+
+ while (p<buf16+length)
+ {
+ if (*(guint8*)p > max_first)
+ max_first = *(guint8*)p;
+ if (((guint8*)p)[1] > max_second)
+ max_second = ((guint8*)p)[1];
+ p++;
+ }
+
+ if ( ((max_second > max_first) && (G_BYTE_ORDER == G_LITTLE_ENDIAN))
+ || ((max_second < max_first) && (G_BYTE_ORDER == G_BIG_ENDIAN)))
+ toggle_endian2 (buf16, length);
+}
+
+/* toggle_endian2 toggles the endian for a 16 bit entity. */
+static void
+toggle_endian2 (guint16 *buf16,
+ gint length)
+{
+ guint16 *p = buf16;
+
+ while (p < buf16 + length)
+ {
+ *p = ((*p & 0xff) << 8) | (*p >> 8);
+ p++;
+ }
+}
+
+typedef struct
+{
+ guint16 group_word;
+ guint16 element_word;
+ gchar value_rep[3];
+ guint32 element_length;
+ guint8 *value;
+ gboolean free;
+} DICOMELEMENT;
+
+/**
+ * dicom_add_element:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ * @group_word: Dicom Element group number for the tag to be added to
+ * @elements.
+ * @element_word: Dicom Element element number for the tag to be added
+ * to @elements.
+ * @value_rep: a string representing the Dicom VR for the new element.
+ * @value: a pointer to an integer containing the value for the
+ * element to be created.
+ *
+ * Creates a DICOMELEMENT object and inserts it into @elements.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_add_element (GSList *elements,
+ guint16 group_word,
+ guint16 element_word,
+ const gchar *value_rep,
+ guint32 element_length,
+ guint8 *value)
+{
+ DICOMELEMENT *element = g_slice_new0 (DICOMELEMENT);
+
+ element->group_word = group_word;
+ element->element_word = element_word;
+ strncpy (element->value_rep, value_rep, sizeof (element->value_rep));
+ element->element_length = element_length;
+ element->value = value;
+
+ return g_slist_prepend (elements, element);
+}
+
+static GSList *
+dicom_add_element_copy (GSList *elements,
+ guint16 group_word,
+ guint16 element_word,
+ gchar *value_rep,
+ guint32 element_length,
+ const guint8 *value)
+{
+ elements = dicom_add_element (elements,
+ group_word, element_word, value_rep,
+ element_length,
+ g_memdup (value, element_length));
+
+ ((DICOMELEMENT *) elements->data)->free = TRUE;
+
+ return elements;
+}
+
+/**
+ * dicom_add_element_int:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+
+ * @group_word: Dicom Element group number for the tag to be added to
+ * @elements.
+ * @element_word: Dicom Element element number for the tag to be added to
+ * @elements.
+ * @value_rep: a string representing the Dicom VR for the new element.
+ * @value: a pointer to an integer containing the value for the
+ * element to be created.
+ *
+ * Creates a DICOMELEMENT object from the passed integer pointer and
+ * adds it to @elements. Note: value should be the address of a
+ * guint16 for @value_rep == %US or guint32 for other values of
+ * @value_rep
+ *
+ * Return value: the new head of @elements
+ */
+static GSList *
+dicom_add_element_int (GSList *elements,
+ guint16 group_word,
+ guint16 element_word,
+ gchar *value_rep,
+ guint8 *value)
+{
+ guint32 len;
+
+ if (strcmp (value_rep, "US") == 0)
+ len = 2;
+ else
+ len = 4;
+
+ return dicom_add_element (elements,
+ group_word, element_word, value_rep,
+ len, value);
+}
+
+/**
+ * dicom_element_done:
+ * @data: pointer to a DICOMELEMENT structure which is to be destroyed.
+ *
+ * Destroys the DICOMELEMENT passed as @data
+**/
+static void
+dicom_element_done (gpointer data)
+{
+ if (data)
+ {
+ DICOMELEMENT *e = data;
+
+ if (e->free)
+ g_free (e->value);
+
+ g_slice_free (DICOMELEMENT, data);
+ }
+}
+
+/**
+ * dicom_elements_destroy:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ *
+ * Destroys the list of DICOMELEMENTs
+**/
+static void
+dicom_elements_destroy (GSList *elements)
+{
+ if (elements)
+ g_slist_free_full (elements, dicom_element_done);
+}
+
+/**
+ * dicom_destroy_element:
+ * @elements: head of a GSList containing DICOMELEMENT structures.
+ * @ele: a DICOMELEMENT structure to be removed from @elements
+ *
+ * Removes the specified DICOMELEMENT from @elements and Destroys it
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_destroy_element (GSList *elements,
+ DICOMELEMENT *ele)
+{
+ if (ele)
+ {
+ elements = g_slist_remove_all (elements, ele);
+
+ if (ele->free)
+ g_free (ele->value);
+
+ g_slice_free (DICOMELEMENT, ele);
+ }
+
+ return elements;
+}
+
+/**
+ * dicom_elements_compare:
+ * @a: pointer to a DICOMELEMENT structure.
+ * @b: pointer to a DICOMELEMENT structure.
+ *
+ * Determines the equality of @a and @b as strcmp
+ *
+ * Return value: an integer indicating the equality of @a and @b.
+**/
+static gint
+dicom_elements_compare (gconstpointer a,
+ gconstpointer b)
+{
+ DICOMELEMENT *e1 = (DICOMELEMENT *)a;
+ DICOMELEMENT *e2 = (DICOMELEMENT *)b;
+
+ if (e1->group_word == e2->group_word)
+ {
+ if (e1->element_word == e2->element_word)
+ {
+ return 0;
+ }
+ else if (e1->element_word > e2->element_word)
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else if (e1->group_word < e2->group_word)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+/**
+ * dicom_element_find_by_num:
+ * @head: head of a GSList containing DICOMELEMENT structures.
+ * @group_word: Dicom Element group number for the tag to be found.
+ * @element_word: Dicom Element element number for the tag to be found.
+ *
+ * Retrieves the specified DICOMELEMENT from @head, if available.
+ *
+ * Return value: a DICOMELEMENT matching the specified group,element,
+ * or NULL if the specified element was not found.
+**/
+static DICOMELEMENT *
+dicom_element_find_by_num (GSList *head,
+ guint16 group_word,
+ guint16 element_word)
+{
+ DICOMELEMENT data = { group_word,element_word, "", 0, NULL};
+ GSList *ele = g_slist_find_custom (head,&data,dicom_elements_compare);
+ return (ele ? ele->data : NULL);
+}
+
+/**
+ * dicom_get_elements_list:
+ * @image_ID: the image_ID from which to read parasites in order to
+ * retrieve the dicom elements
+ *
+ * Reads all DICOMELEMENTs from the specified image's parasites.
+ *
+ * Return value: a GSList of all known dicom elements
+**/
+static GSList *
+dicom_get_elements_list (gint32 image_ID)
+{
+ GSList *elements = NULL;
+ GimpParasite *parasite;
+ gchar **parasites = NULL;
+ gint count = 0;
+
+ parasites = gimp_image_get_parasite_list (image_ID, &count);
+
+ if (parasites && count > 0)
+ {
+ gint i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (strncmp (parasites[i], "dcm", 3) == 0)
+ {
+ parasite = gimp_image_get_parasite (image_ID, parasites[i]);
+
+ if (parasite)
+ {
+ gchar buf[1024];
+ gchar *ptr1;
+ gchar *ptr2;
+ gchar value_rep[3] = "";
+ guint16 group_word = 0;
+ guint16 element_word = 0;
+
+ /* sacrificial buffer */
+ strncpy (buf, parasites[i], sizeof (buf));
+
+ /* buf should now hold a string of the form
+ * dcm/XXXX-XXXX-AA where XXXX are Hex values for
+ * group and element respectively AA is the Value
+ * Representation of the element
+ *
+ * start off by jumping over the dcm/ to the first Hex blob
+ */
+ ptr1 = strchr (buf, '/');
+
+ if (ptr1)
+ {
+ gchar t[15];
+
+ ptr1++;
+ ptr2 = strchr (ptr1,'-');
+
+ if (ptr2)
+ *ptr2 = '\0';
+
+ g_snprintf (t, sizeof (t), "0x%s", ptr1);
+ group_word = (guint16) g_ascii_strtoull (t, NULL, 16);
+ ptr1 = ptr2 + 1;
+ }
+
+ /* now get the second Hex blob */
+ if (ptr1)
+ {
+ gchar t[15];
+
+ ptr2 = strchr (ptr1, '-');
+
+ if (ptr2)
+ *ptr2 = '\0';
+
+ g_snprintf (t, sizeof (t), "0x%s", ptr1);
+ element_word = (guint16) g_ascii_strtoull (t, NULL, 16);
+ ptr1 = ptr2 + 1;
+ }
+
+ /* and lastly, the VR */
+ if (ptr1)
+ strncpy (value_rep, ptr1, sizeof (value_rep));
+
+ /*
+ * If all went according to plan, we should be able
+ * to add this element
+ */
+ if (group_word > 0 && element_word > 0)
+ {
+ const guint8 *val = gimp_parasite_data (parasite);
+ const guint len = gimp_parasite_data_size (parasite);
+
+ /* and add the dicom element, asking to have
+ it's value copied for later garbage collection */
+ elements = dicom_add_element_copy (elements,
+ group_word,
+ element_word,
+ value_rep, len, val);
+ }
+
+ gimp_parasite_free (parasite);
+ }
+ }
+ }
+ }
+
+ /* cleanup the array of names */
+ g_strfreev (parasites);
+
+ return elements;
+}
+
+/**
+ * dicom_remove_gimp_specified_elements:
+ * @elements: GSList to remove elements from
+ * @samples_per_pixel: samples per pixel of the image to be written.
+ * if set to %3 the planar configuration for color images
+ * will also be removed from @elements
+ *
+ * Removes certain DICOMELEMENTs from the elements list which are specific to the output of this plugin.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_remove_gimp_specified_elements (GSList *elements,
+ gint samples_per_pixel)
+{
+ DICOMELEMENT remove[] = {
+ /* Image presentation group */
+ /* Samples per pixel */
+ {0x0028, 0x0002, "", 0, NULL},
+ /* Photometric interpretation */
+ {0x0028, 0x0004, "", 0, NULL},
+ /* rows */
+ {0x0028, 0x0010, "", 0, NULL},
+ /* columns */
+ {0x0028, 0x0011, "", 0, NULL},
+ /* Bits allocated */
+ {0x0028, 0x0100, "", 0, NULL},
+ /* Bits Stored */
+ {0x0028, 0x0101, "", 0, NULL},
+ /* High bit */
+ {0x0028, 0x0102, "", 0, NULL},
+ /* Pixel representation */
+ {0x0028, 0x0103, "", 0, NULL},
+
+ {0,0,"",0,NULL}
+ };
+ DICOMELEMENT *ele;
+ gint i;
+
+ /*
+ * Remove all Dicom elements which will be set as part of the writing of the new file
+ */
+ for (i=0; remove[i].group_word > 0;i++)
+ {
+ if ((ele = dicom_element_find_by_num (elements,remove[i].group_word,remove[i].element_word)))
+ {
+ elements = dicom_destroy_element (elements,ele);
+ }
+ }
+ /* special case - allow this to be overwritten if necessary */
+ if (samples_per_pixel == 3)
+ {
+ /* Planar configuration for color images */
+ if ((ele = dicom_element_find_by_num (elements,0x0028,0x0006)))
+ {
+ elements = dicom_destroy_element (elements,ele);
+ }
+ }
+ return elements;
+}
+
+/**
+ * dicom_ensure_required_elements_present:
+ * @elements: GSList to remove elements from
+ * @today_string: string containing today's date in DICOM format. This
+ * is used to default any required Dicom elements of date
+ * type to today's date.
+ *
+ * Defaults DICOMELEMENTs to the values set by previous version of
+ * this plugin, but only if they do not already exist.
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_ensure_required_elements_present (GSList *elements,
+ gchar *today_string)
+{
+ const DICOMELEMENT defaults[] = {
+ /* Meta element group */
+ /* 0002, 0001 - File Meta Information Version */
+ { 0x0002, 0x0001, "OB", 2, (guint8 *) "\0\1" },
+ /* 0002, 0010 - Transfer syntax uid */
+ { 0x0002, 0x0010, "UI",
+ strlen ("1.2.840.10008.1.2.1"), (guint8 *) "1.2.840.10008.1.2.1"},
+ /* 0002, 0013 - Implementation version name */
+ { 0x0002, 0x0013, "SH",
+ strlen ("GIMP Dicom Plugin 1.0"), (guint8 *) "GIMP Dicom Plugin 1.0" },
+ /* Identifying group */
+ /* ImageType */
+ { 0x0008, 0x0008, "CS",
+ strlen ("ORIGINAL\\PRIMARY"), (guint8 *) "ORIGINAL\\PRIMARY" },
+ { 0x0008, 0x0016, "UI",
+ strlen ("1.2.840.10008.5.1.4.1.1.7"), (guint8 *) "1.2.840.10008.5.1.4.1.1.7" },
+ /* Study date */
+ { 0x0008, 0x0020, "DA",
+ strlen (today_string), (guint8 *) today_string },
+ /* Series date */
+ { 0x0008, 0x0021, "DA",
+ strlen (today_string), (guint8 *) today_string },
+ /* Acquisition date */
+ { 0x0008, 0x0022, "DA",
+ strlen (today_string), (guint8 *) today_string },
+ /* Content Date */
+ { 0x0008, 0x0023, "DA",
+ strlen (today_string), (guint8 *) today_string},
+ /* Content Time */
+ { 0x0008, 0x0030, "TM",
+ strlen ("000000.000000"), (guint8 *) "000000.000000"},
+ /* AccessionNumber */
+ { 0x0008, 0x0050, "SH", strlen (""), (guint8 *) "" },
+ /* Modality */
+ { 0x0008, 0x0060, "CS", strlen ("MR"), (guint8 *) "MR" },
+ /* ConversionType */
+ { 0x0008, 0x0064, "CS", strlen ("WSD"), (guint8 *) "WSD" },
+ /* ReferringPhysiciansName */
+ { 0x0008, 0x0090, "PN", strlen (""), (guint8 *) "" },
+ /* Patient group */
+ /* Patient name */
+ { 0x0010, 0x0010, "PN",
+ strlen ("DOE^WILBER"), (guint8 *) "DOE^WILBER" },
+ /* Patient ID */
+ { 0x0010, 0x0020, "LO",
+ strlen ("314159265"), (guint8 *) "314159265" },
+ /* Patient Birth date */
+ { 0x0010, 0x0030, "DA",
+ strlen (today_string), (guint8 *) today_string },
+ /* Patient sex */
+ { 0x0010, 0x0040, "CS", strlen (""), (guint8 *) "" /* unknown */ },
+ /* Relationship group */
+ /* StudyId */
+ { 0x0020, 0x0010, "IS", strlen ("1"), (guint8 *) "1" },
+ /* SeriesNumber */
+ { 0x0020, 0x0011, "IS", strlen ("1"), (guint8 *) "1" },
+ /* AcquisitionNumber */
+ { 0x0020, 0x0012, "IS", strlen ("1"), (guint8 *) "1" },
+ /* Instance number */
+ { 0x0020, 0x0013, "IS", strlen ("1"), (guint8 *) "1" },
+
+ { 0, 0, "", 0, NULL }
+ };
+ gint i;
+
+ /*
+ * Make sure that all of the default elements have a value
+ */
+ for (i=0; defaults[i].group_word > 0; i++)
+ {
+ if (dicom_element_find_by_num (elements,
+ defaults[i].group_word,
+ defaults[i].element_word) == NULL)
+ {
+ elements = dicom_add_element (elements,
+ defaults[i].group_word,
+ defaults[i].element_word,
+ defaults[i].value_rep,
+ defaults[i].element_length,
+ defaults[i].value);
+ }
+ }
+
+ return elements;
+}
+
+/* save_image() saves an image in the dicom format. The DICOM format
+ * requires a lot of tags to be set. Some of them have real uses, others
+ * must just be filled with dummy values.
+ */
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ FILE *DICOM;
+ GimpImageType drawable_type;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+ GByteArray *group_stream;
+ GSList *elements = NULL;
+ gint group;
+ GDate *date;
+ gchar today_string[16];
+ gchar *photometric_interp;
+ gint samples_per_pixel;
+ gboolean retval = TRUE;
+ guint16 zero = 0;
+ guint16 seven = 7;
+ guint16 eight = 8;
+ guchar *src = NULL;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not saving an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_message (_("Cannot save images with alpha channel."));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ samples_per_pixel = 1;
+ photometric_interp = "MONOCHROME2";
+ break;
+
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ samples_per_pixel = 3;
+ photometric_interp = "RGB";
+ break;
+
+ default:
+ g_message (_("Cannot operate on unknown image types."));
+ return FALSE;
+ }
+
+ date = g_date_new ();
+ g_date_set_time_t (date, time (NULL));
+ g_snprintf (today_string, sizeof (today_string),
+ "%04d%02d%02d", date->year, date->month, date->day);
+ g_date_free (date);
+
+ /* Open the output file. */
+ DICOM = g_fopen (filename, "wb");
+
+ if (!DICOM)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ /* Print dicom header */
+ {
+ guint8 val = 0;
+ gint i;
+
+ for (i = 0; i < 0x80; i++)
+ fwrite (&val, 1, 1, DICOM);
+ }
+ fprintf (DICOM, "DICM");
+
+ group_stream = g_byte_array_new ();
+
+ elements = dicom_get_elements_list (image_ID);
+ if (0/*replaceElementsList*/)
+ {
+ /* to do */
+ }
+ else if (1/*insist_on_basic_elements*/)
+ {
+ elements = dicom_ensure_required_elements_present (elements,today_string);
+ }
+
+ /*
+ * Set value of custom elements
+ */
+ elements = dicom_remove_gimp_specified_elements (elements,samples_per_pixel);
+
+ /* Image presentation group */
+ group = 0x0028;
+ /* Samples per pixel */
+ elements = dicom_add_element_int (elements, group, 0x0002, "US",
+ (guint8 *) &samples_per_pixel);
+ /* Photometric interpretation */
+ elements = dicom_add_element (elements, group, 0x0004, "CS",
+ strlen (photometric_interp),
+ (guint8 *) photometric_interp);
+ /* Planar configuration for color images */
+ if (samples_per_pixel == 3)
+ elements = dicom_add_element_int (elements, group, 0x0006, "US",
+ (guint8 *) &zero);
+ /* rows */
+ elements = dicom_add_element_int (elements, group, 0x0010, "US",
+ (guint8 *) &height);
+ /* columns */
+ elements = dicom_add_element_int (elements, group, 0x0011, "US",
+ (guint8 *) &width);
+ /* Bits allocated */
+ elements = dicom_add_element_int (elements, group, 0x0100, "US",
+ (guint8 *) &eight);
+ /* Bits Stored */
+ elements = dicom_add_element_int (elements, group, 0x0101, "US",
+ (guint8 *) &eight);
+ /* High bit */
+ elements = dicom_add_element_int (elements, group, 0x0102, "US",
+ (guint8 *) &seven);
+ /* Pixel representation */
+ elements = dicom_add_element_int (elements, group, 0x0103, "US",
+ (guint8 *) &zero);
+
+ /* Pixel data */
+ group = 0x7fe0;
+ src = g_new (guchar, height * width * samples_per_pixel);
+ if (src)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ elements = dicom_add_element (elements, group, 0x0010, "OW",
+ width * height * samples_per_pixel,
+ (guint8 *) src);
+
+ elements = dicom_add_tags (DICOM, group_stream, elements);
+
+ g_free (src);
+ }
+ else
+ {
+ retval = FALSE;
+ }
+
+ fclose (DICOM);
+
+ dicom_elements_destroy (elements);
+ g_byte_array_free (group_stream, TRUE);
+ g_object_unref (buffer);
+
+ return retval;
+}
+
+/**
+ * dicom_print_tags:
+ * @data: pointer to a DICOMELEMENT structure which is to be written to file
+ * @user_data: structure containing state information and output parameters
+ *
+ * Writes the specified DICOMELEMENT to @user_data's group_stream member.
+ * Between groups, flushes the group_stream to @user_data's DICOM member.
+ */
+static void
+dicom_print_tags(gpointer data,
+ gpointer user_data)
+{
+ struct {
+ FILE *DICOM;
+ GByteArray *group_stream;
+ gint last_group;
+ } *d = user_data;
+ DICOMELEMENT *e = (DICOMELEMENT *) data;
+
+ if (d->last_group >= 0 && e->group_word != d->last_group)
+ {
+ write_group_to_file (d->DICOM, d->last_group, d->group_stream);
+ }
+
+ add_tag_pointer (d->group_stream,
+ e->group_word, e->element_word,
+ e->value_rep,e->value, e->element_length);
+ d->last_group = e->group_word;
+}
+
+/**
+ * dicom_add_tags:
+ * @DICOM: File pointer to which @elements should be written.
+ * @group_stream: byte array used for staging Dicom Element groups
+ * before flushing them to disk.
+ * @elements: GSList container the Dicom Element elements from
+ *
+ * Writes all Dicom tags in @elements to the file @DICOM
+ *
+ * Return value: the new head of @elements
+**/
+static GSList *
+dicom_add_tags (FILE *DICOM,
+ GByteArray *group_stream,
+ GSList *elements)
+{
+ struct {
+ FILE *DICOM;
+ GByteArray *group_stream;
+ gint last_group;
+ } data = { DICOM, group_stream, -1 };
+
+ elements = g_slist_sort (elements, dicom_elements_compare);
+ g_slist_foreach (elements, dicom_print_tags, &data);
+ /* make sure that the final group is written to the file */
+ write_group_to_file (data.DICOM, data.last_group, data.group_stream);
+
+ return elements;
+}
+
+/* add_tag_pointer () adds to the group_stream one single value with its
+ * corresponding value_rep. Note that we use "explicit VR".
+ */
+static void
+add_tag_pointer (GByteArray *group_stream,
+ gint group,
+ gint element,
+ const gchar *value_rep,
+ const guint8 *data,
+ gint length)
+{
+ gboolean is_long;
+ guint16 swapped16;
+ guint32 swapped32;
+ guint pad = 0;
+
+ is_long = (strstr ("OB|OW|SQ|UN", value_rep) != NULL) || length > 65535;
+
+ swapped16 = g_ntohs (GUINT16_SWAP_LE_BE (group));
+ g_byte_array_append (group_stream, (guint8 *) &swapped16, 2);
+
+ swapped16 = g_ntohs (GUINT16_SWAP_LE_BE (element));
+ g_byte_array_append (group_stream, (guint8 *) &swapped16, 2);
+
+ g_byte_array_append (group_stream, (const guchar *) value_rep, 2);
+
+ if (length % 2 != 0)
+ {
+ /* the dicom standard requires all elements to be of even byte
+ * length. this element would be odd, so we must pad it before
+ * adding it
+ */
+ pad = 1;
+ }
+
+ if (is_long)
+ {
+
+ g_byte_array_append (group_stream, (const guchar *) "\0\0", 2);
+
+ swapped32 = g_ntohl (GUINT32_SWAP_LE_BE (length + pad));
+ g_byte_array_append (group_stream, (guint8 *) &swapped32, 4);
+ }
+ else
+ {
+ swapped16 = g_ntohs (GUINT16_SWAP_LE_BE (length + pad));
+ g_byte_array_append (group_stream, (guint8 *) &swapped16, 2);
+ }
+
+ g_byte_array_append (group_stream, data, length);
+
+ if (pad)
+ {
+ /* add a padding byte to the stream
+ *
+ * From ftp://medical.nema.org/medical/dicom/2009/09_05pu3.pdf:
+ *
+ * Values with VRs constructed of character strings, except in
+ * the case of the VR UI, shall be padded with SPACE characters
+ * (20H, in the Default Character Repertoire) when necessary to
+ * achieve even length. Values with a VR of UI shall be padded
+ * with a single trailing NULL (00H) character when necessary
+ * to achieve even length. Values with a VR of OB shall be
+ * padded with a single trailing NULL byte value (00H) when
+ * necessary to achieve even length.
+ */
+ if (strstr ("UI|OB", value_rep) != NULL)
+ {
+ g_byte_array_append (group_stream, (guint8 *) "\0", 1);
+ }
+ else
+ {
+ g_byte_array_append (group_stream, (guint8 *) " ", 1);
+ }
+ }
+}
+
+/* Once a group has been built it has to be wrapped with a meta-group
+ * tag before it is written to the DICOM file. This is done by
+ * write_group_to_file.
+ */
+static gboolean
+write_group_to_file (FILE *DICOM,
+ gint group,
+ GByteArray *group_stream)
+{
+ gboolean retval = TRUE;
+ guint16 swapped16;
+ guint32 swapped32;
+
+ /* Add header to the group and output it */
+ swapped16 = g_ntohs (GUINT16_SWAP_LE_BE (group));
+
+ fwrite ((gchar *) &swapped16, 1, 2, DICOM);
+ fputc (0, DICOM);
+ fputc (0, DICOM);
+ fputc ('U', DICOM);
+ fputc ('L', DICOM);
+
+ swapped16 = g_ntohs (GUINT16_SWAP_LE_BE (4));
+ fwrite ((gchar *) &swapped16, 1, 2, DICOM);
+
+ swapped32 = g_ntohl (GUINT32_SWAP_LE_BE (group_stream->len));
+ fwrite ((gchar *) &swapped32, 1, 4, DICOM);
+
+ if (fwrite (group_stream->data,
+ 1, group_stream->len, DICOM) != group_stream->len)
+ retval = FALSE;
+
+ g_byte_array_set_size (group_stream, 0);
+
+ return retval;
+}
diff --git a/plug-ins/common/file-gbr.c b/plug-ins/common/file-gbr.c
new file mode 100644
index 0000000..90c7b65
--- /dev/null
+++ b/plug-ins/common/file-gbr.c
@@ -0,0 +1,367 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * gbr plug-in version 1.00
+ * Loads/exports version 2 GIMP .gbr files, by Tim Newsome <drz@frody.bloke.com>
+ * Some bits stolen from the .99.7 source tree.
+ *
+ * Added in GBR version 1 support after learning that there wasn't a
+ * tool to read them.
+ * July 6, 1998 by Seth Burgess <sjburges@gimp.org>
+ *
+ * Dec 17, 2000
+ * Load and save GIMP brushes in GRAY or RGBA. jtl + neo
+ *
+ *
+ * TODO: Give some better error reporting on not opening files/bad headers
+ * etc.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-gbr-save"
+#define PLUG_IN_BINARY "file-gbr"
+#define PLUG_IN_ROLE "gimp-file-gbr"
+
+
+typedef struct
+{
+ gchar description[256];
+ gint spacing;
+} BrushInfo;
+
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_dialog (void);
+static void entry_callback (GtkWidget *widget,
+ gpointer data);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* private variables */
+
+static BrushInfo info =
+{
+ "GIMP Brush",
+ 10
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "uri", "The URI of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-uri", "The URI of the file to export the image in" },
+ { GIMP_PDB_INT32, "spacing", "Spacing of the brush" },
+ { GIMP_PDB_STRING, "description", "Short description of the brush" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in the GIMP brush file format",
+ "Exports files in the GIMP brush file format",
+ "Tim Newsome, Jens Lautenbacher, Sven Neumann",
+ "Tim Newsome, Jens Lautenbacher, Sven Neumann",
+ "1997-2000",
+ N_("GIMP brush"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_plugin_icon_register (SAVE_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_BRUSH);
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-gimp-gbr");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "gbr", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GFile *file;
+ GimpParasite *parasite;
+ gint32 orig_image_ID;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+ file = g_file_new_for_uri (param[3].data.d_string);
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "GBR",
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &info);
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-brush-name");
+ if (parasite)
+ {
+ strncpy (info.description,
+ gimp_parasite_data (parasite),
+ MIN (sizeof (info.description),
+ gimp_parasite_data_size (parasite)));
+ info.description[sizeof (info.description) - 1] = '\0';
+
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ gchar *name = g_path_get_basename (gimp_file_get_utf8_name (file));
+
+ if (g_str_has_suffix (name, ".gbr"))
+ name[strlen (name) - 4] = '\0';
+
+ if (strlen (name))
+ {
+ strncpy (info.description, name, sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = '\0';
+ }
+
+ g_free (name);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ info.spacing = (param[5].data.d_int32);
+ strncpy (info.description, param[6].data.d_string,
+ sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = '\0';
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpParam *save_retvals;
+ gint n_save_retvals;
+
+ save_retvals =
+ gimp_run_procedure ("file-gbr-save-internal",
+ &n_save_retvals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, param[3].data.d_string,
+ GIMP_PDB_STRING, param[4].data.d_string,
+ GIMP_PDB_INT32, info.spacing,
+ GIMP_PDB_STRING, info.description,
+ GIMP_PDB_END);
+
+ if (save_retvals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_set_data (SAVE_PROC, &info, sizeof (info));
+ }
+ else
+ {
+ g_set_error (&error, 0, 0,
+ "Running procedure 'file-gbr-save-internal' "
+ "failed: %s",
+ gimp_get_pdb_error ());
+
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_destroy_params (save_retvals, n_save_retvals);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ if (strlen (info.description))
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-brush-name",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (info.description) + 1,
+ info.description);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ gimp_image_detach_parasite (orig_image_ID, "gimp-brush-name");
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *entry;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("Brush"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* The main table */
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (entry), 20);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_entry_set_text (GTK_ENTRY (entry), info.description);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Description:"), 1.0, 0.5,
+ entry, 1, FALSE);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_callback),
+ info.description);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (info.spacing, 1, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Spacing:"), 1.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &info.spacing);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ strncpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)),
+ sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = '\0';
+}
diff --git a/plug-ins/common/file-gegl.c b/plug-ins/common/file-gegl.c
new file mode 100644
index 0000000..978c7e7
--- /dev/null
+++ b/plug-ins/common/file-gegl.c
@@ -0,0 +1,492 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-gegl.c -- GEGL based file format plug-in
+ * Copyright (C) 2012 Simon Budig <simon@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_BINARY "file-gegl"
+
+
+typedef struct _FileFormat FileFormat;
+
+struct _FileFormat
+{
+ const gchar *file_type;
+ const gchar *mime_type;
+ const gchar *extensions;
+ const gchar *magic;
+
+ const gchar *load_proc;
+ const gchar *load_blurb;
+ const gchar *load_help;
+ const gchar *load_op;
+
+ const gchar *save_proc;
+ const gchar *save_blurb;
+ const gchar *save_help;
+ const gchar *save_op;
+};
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ const gchar *gegl_op,
+ GError **error);
+static gboolean save_image (const gchar *filename,
+ const gchar *gegl_op,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+
+static const FileFormat file_formats[] =
+{
+ {
+ N_("Radiance RGBE"),
+ "image/vnd.radiance",
+ "hdr",
+ "0,string,#?",
+
+ "file-load-rgbe",
+ "Load files in the RGBE file format",
+ "This procedure loads images in the RGBE format, using gegl:rgbe-load",
+ "gegl:rgbe-load",
+
+ "file-save-rgbe",
+ "Saves files in the RGBE file format",
+ "This procedure exports images in the RGBE format, using gegl:rgbe-save",
+ "gegl:rgbe-save",
+ },
+ {
+ N_("OpenEXR image"),
+ "image/x-exr",
+ "exr",
+ "0,lelong,20000630",
+
+ /* no EXR loading (implemented in native GIMP plug-in) */
+ NULL, NULL, NULL, NULL,
+
+ "file-exr-save",
+ "Saves files in the OpenEXR file format",
+ "This procedure saves images in the OpenEXR format, using gegl:exr-save",
+ "gegl:exr-save"
+ }
+};
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" }
+ };
+
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+
+ if (format->load_proc)
+ {
+ gimp_install_procedure (format->load_proc,
+ format->load_blurb,
+ format->load_help,
+ "Simon Budig",
+ "Simon Budig",
+ "2012",
+ format->file_type,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (format->load_proc,
+ format->mime_type);
+ gimp_register_magic_load_handler (format->load_proc,
+ format->extensions,
+ "",
+ format->magic);
+ }
+
+ if (format->save_proc)
+ {
+ gimp_install_procedure (format->save_proc,
+ format->save_blurb,
+ format->save_help,
+ "Simon Budig",
+ "Simon Budig",
+ "2012",
+ format->file_type,
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (format->save_proc,
+ format->mime_type);
+ gimp_register_save_handler (format->save_proc,
+ format->extensions, "");
+ }
+ }
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint image_ID;
+ gint drawable_ID;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+
+ if (format->load_proc && !strcmp (name, format->load_proc))
+ {
+ image_ID = load_image (param[1].data.d_string, format->load_op, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ break;
+ }
+ else if (format->save_proc && !strcmp (name, format->save_proc))
+ {
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "GEGL",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ *nreturn_vals = 1;
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (! save_image (param[3].data.d_string,
+ format->save_op,
+ image_ID, drawable_ID,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (file_formats))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+
+ gegl_exit ();
+}
+
+static gint32
+load_image (const gchar *filename,
+ const gchar *gegl_op,
+ GError **error)
+{
+ gint32 image_ID = -1;
+ gint32 layer_ID;
+ GimpImageType image_type;
+ GimpImageBaseType base_type;
+ GimpPrecision precision;
+ gint width;
+ gint height;
+ GeglNode *graph;
+ GeglNode *sink;
+ GeglNode *source;
+ GeglBuffer *src_buf = NULL;
+ GeglBuffer *dest_buf = NULL;
+ const Babl *format;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ graph = gegl_node_new ();
+
+ source = gegl_node_new_child (graph,
+ "operation", gegl_op,
+ "path", filename,
+ NULL);
+ sink = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-sink",
+ "buffer", &src_buf,
+ NULL);
+
+ gegl_node_connect_to (source, "output",
+ sink, "input");
+
+ gegl_node_process (sink);
+
+ g_object_unref (graph);
+
+ if (! src_buf)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s'"),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ gimp_progress_update (0.33);
+
+ width = gegl_buffer_get_width (src_buf);
+ height = gegl_buffer_get_height (src_buf);
+ format = gegl_buffer_get_format (src_buf);
+
+ if (babl_format_is_palette (format))
+ {
+ base_type = GIMP_INDEXED;
+
+ if (babl_format_has_alpha (format))
+ image_type = GIMP_INDEXEDA_IMAGE;
+ else
+ image_type = GIMP_INDEXED_IMAGE;
+
+ precision = GIMP_PRECISION_U8_GAMMA;
+ }
+ else
+ {
+ const Babl *model = babl_format_get_model (format);
+ const Babl *type = babl_format_get_type (format, 0);
+ gboolean linear = TRUE;
+
+ if (model == babl_model ("Y") ||
+ model == babl_model ("Y'") ||
+ model == babl_model ("YA") ||
+ model == babl_model ("Y'A"))
+ {
+ base_type = GIMP_GRAY;
+
+ if (babl_format_has_alpha (format))
+ image_type = GIMP_GRAYA_IMAGE;
+ else
+ image_type = GIMP_GRAY_IMAGE;
+
+ if (model == babl_model ("Y'") ||
+ model == babl_model ("Y'A"))
+ linear = FALSE;
+ }
+ else
+ {
+ base_type = GIMP_RGB;
+
+ if (babl_format_has_alpha (format))
+ image_type = GIMP_RGBA_IMAGE;
+ else
+ image_type = GIMP_RGB_IMAGE;
+
+ if (model == babl_model ("R'G'B'") ||
+ model == babl_model ("R'G'B'A"))
+ linear = FALSE;
+ }
+
+ if (linear)
+ {
+ if (type == babl_type ("u8"))
+ precision = GIMP_PRECISION_U8_LINEAR;
+ else if (type == babl_type ("u16"))
+ precision = GIMP_PRECISION_U16_LINEAR;
+ else if (type == babl_type ("u32"))
+ precision = GIMP_PRECISION_U32_LINEAR;
+ else if (type == babl_type ("half"))
+ precision = GIMP_PRECISION_HALF_LINEAR;
+ else
+ precision = GIMP_PRECISION_FLOAT_LINEAR;
+ }
+ else
+ {
+ if (type == babl_type ("u8"))
+ precision = GIMP_PRECISION_U8_GAMMA;
+ else if (type == babl_type ("u16"))
+ precision = GIMP_PRECISION_U16_GAMMA;
+ else if (type == babl_type ("u32"))
+ precision = GIMP_PRECISION_U32_GAMMA;
+ else if (type == babl_type ("half"))
+ precision = GIMP_PRECISION_HALF_GAMMA;
+ else
+ precision = GIMP_PRECISION_FLOAT_GAMMA;
+ }
+ }
+
+
+ image_ID = gimp_image_new_with_precision (width, height,
+ base_type, precision);
+ gimp_image_set_filename (image_ID, filename);
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("Background"),
+ width, height,
+ image_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+ dest_buf = gimp_drawable_get_buffer (layer_ID);
+
+ gimp_progress_update (0.66);
+
+ gegl_buffer_copy (src_buf, NULL, GEGL_ABYSS_NONE, dest_buf, NULL);
+
+ g_object_unref (src_buf);
+ g_object_unref (dest_buf);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+static gboolean
+save_image (const gchar *filename,
+ const gchar *gegl_op,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglNode *graph;
+ GeglNode *source;
+ GeglNode *sink;
+ GeglBuffer *src_buf;
+
+ src_buf = gimp_drawable_get_buffer (drawable_ID);
+
+ graph = gegl_node_new ();
+
+ source = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-source",
+ "buffer", src_buf,
+ NULL);
+ sink = gegl_node_new_child (graph,
+ "operation", gegl_op,
+ "path", filename,
+ NULL);
+
+ gegl_node_connect_to (source, "output",
+ sink, "input");
+
+ gegl_node_process (sink);
+
+ g_object_unref (graph);
+ g_object_unref (src_buf);
+
+ return TRUE;
+}
diff --git a/plug-ins/common/file-gif-load.c b/plug-ins/common/file-gif-load.c
new file mode 100644
index 0000000..e4702f4
--- /dev/null
+++ b/plug-ins/common/file-gif-load.c
@@ -0,0 +1,1252 @@
+/* GIF loading file filter for GIMP 2.x
+ * +-------------------------------------------------------------------+
+ * | Copyright Adam D. Moss, Peter Mattis, Spencer Kimball |
+ * +-------------------------------------------------------------------+
+ * Version 1.50.4 - 2003/06/03
+ * Adam D. Moss - <adam@gimp.org> <adam@foxbox.org>
+ */
+
+/* Copyright notice for old GIF code from which this plugin was long ago */
+/* derived (David Koblas has kindly granted permission to relicense): */
+/* +-------------------------------------------------------------------+ */
+/* | Copyright 1990, 1991, 1993, David Koblas. (koblas@extra.com) | */
+/* +-------------------------------------------------------------------+ */
+/* Also...
+ * 'This filter uses code taken from the "giftopnm" and "ppmtogif" programs
+ * which are part of the "netpbm" package.'
+ */
+/* Additionally...
+ * "The Graphics Interchange Format(c) is the Copyright property of
+ * CompuServe Incorporated. GIF(sm) is a Service Mark property of
+ * CompuServe Incorporated."
+ */
+
+/*
+ * REVISION HISTORY
+ *
+ * 2003/06/03
+ * 1.50.04 - When initializing the LZW state, watch out for a completely
+ * bogus input_code_size [based on fix by Raphael Quinet]
+ * Also, fix a stupid old bug when clearing the code table between
+ * subimages. (Enables us to deal better with errors when the stream is
+ * corrupted pretty early in a subimage.) [adam]
+ * Minor-version-bump to distinguish between gimp1.2/1.4 branches.
+ *
+ * 2000/03/31
+ * 1.00.03 - Just mildly more useful comments/messages concerning frame
+ * disposals.
+ *
+ * 1999/11/20
+ * 1.00.02 - Fixed a couple of possible infinite loops where an
+ * error condition was not being checked. Also changed some g_message()s
+ * back to g_warning()s as they should be (don't get carried away with
+ * the user feedback fellahs, no-one wants to be told of every single
+ * corrupt byte and block in its own little window. :-( ).
+ *
+ * 1999/11/11
+ * 1.00.01 - Fixed an uninitialized variable which has been around
+ * forever... thanks to jrb@redhat.com for noticing that there
+ * was a problem somewhere!
+ *
+ * 1999/03/20
+ * 1.00.00 - GIF load-only code split from main GIF plugin.
+ *
+ * For previous revision information, please consult the comments
+ * in the 'gif' plugin.
+ */
+
+/*
+ * TODO (more *'s means more important!)
+ *
+ * - PDB stuff for comments
+ *
+ * - Remove unused colormap entries for GRAYSCALE images.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-gif-load"
+#define LOAD_THUMB_PROC "file-gif-load-thumb"
+
+
+/* uncomment the line below for a little debugging info */
+/* #define GIFDEBUG yesplease */
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ gboolean thumbnail,
+ GError **error);
+
+
+static guchar used_cmap[3][256];
+static guchar highest_used_index;
+static gboolean promote_to_rgb = FALSE;
+static guchar gimp_cmap[768];
+static GimpParasite *comment_parasite = NULL;
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files of Compuserve GIF file format",
+ "FIXME: write help for gif_load",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "1995-2006",
+ N_("GIF image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/gif");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "gif",
+ "",
+ "0,string,GIF8");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads only the first frame of a GIF image, to be "
+ "used as a thumbnail",
+ "",
+ "Sven Neumann",
+ "Sven Neumann",
+ "2006",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+ gint32 image_ID;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, FALSE, &error);
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ image_ID = load_image (param[0].data.d_string, TRUE, &error);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (image_ID != -1)
+ {
+ /* The GIF format only tells you how many bits per pixel
+ * are in the image, not the actual number of used indices (D'OH!)
+ *
+ * So if we're not careful, repeated load/save of a transparent GIF
+ * without intermediate indexed->RGB->indexed pumps up the number of
+ * bits used, as we add an index each time for the transparent
+ * color. Ouch. We either do some heavier analysis at save-time,
+ * or trim down the number of GIMP colors at load-time. We do the
+ * latter for now.
+ */
+#ifdef GIFDEBUG
+ g_print ("GIF: Highest used index is %d\n", highest_used_index);
+#endif
+ if (! promote_to_rgb)
+ gimp_image_set_colormap (image_ID,
+ gimp_cmap, highest_used_index + 1);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+
+ if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ *nreturn_vals = 4;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = gimp_image_width (image_ID);
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = gimp_image_height (image_ID);
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+#define MAXCOLORMAPSIZE 256
+
+#define CM_RED 0
+#define CM_GREEN 1
+#define CM_BLUE 2
+
+#define MAX_LZW_BITS 12
+
+#define INTERLACE 0x40
+#define LOCALCOLORMAP 0x80
+#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
+
+#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
+#define LM_to_uint(a,b) (((b)<<8)|(a))
+
+#define GRAYSCALE 1
+#define COLOR 2
+
+typedef guchar CMap[3][MAXCOLORMAPSIZE];
+
+static struct
+{
+ guint Width;
+ guint Height;
+ CMap ColorMap;
+ guint BitPixel;
+ guint ColorResolution;
+ guint Background;
+ guint AspectRatio;
+ /*
+ **
+ */
+ gint GrayScale;
+} GifScreen;
+
+static struct
+{
+ gint transparent;
+ gint delayTime;
+ gint inputFlag;
+ gint disposal;
+} Gif89 = { -1, -1, -1, 0 };
+
+static gboolean ReadColorMap (FILE *fd,
+ gint number,
+ CMap buffer,
+ gint *format);
+static gint DoExtension (FILE *fd,
+ gint label);
+static gint GetDataBlock (FILE *fd,
+ guchar *buf);
+static gint GetCode (FILE *fd,
+ gint code_size,
+ gboolean flag);
+static gint LZWReadByte (FILE *fd,
+ gint just_reset_LZW,
+ gint input_code_size);
+static gboolean ReadImage (FILE *fd,
+ const gchar *filename,
+ gint len,
+ gint height,
+ CMap cmap,
+ gint ncols,
+ gint format,
+ gint interlace,
+ gint number,
+ guint leftpos,
+ guint toppos,
+ guint screenwidth,
+ guint screenheight,
+ gint32 *image_ID);
+
+
+static gint32
+load_image (const gchar *filename,
+ gboolean thumbnail,
+ GError **error)
+{
+ FILE *fd;
+ guchar buf[16];
+ guchar c;
+ CMap localColorMap;
+ gint grayScale;
+ gboolean useGlobalColormap;
+ gint bitPixel;
+ gint imageCount = 0;
+ gchar version[4];
+ gint32 image_ID = -1;
+ gboolean status;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fd = g_fopen (filename, "rb");
+
+ if (! fd)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ if (! ReadOK (fd, buf, 6))
+ {
+ g_message ("Error reading magic number");
+ fclose (fd);
+ return -1;
+ }
+
+ if (strncmp ((gchar *) buf, "GIF", 3) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("This is not a GIF file"));
+ fclose (fd);
+ return -1;
+ }
+
+ strncpy (version, (gchar *) buf + 3, 3);
+ version[3] = '\0';
+
+ if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0))
+ {
+ g_message ("Bad version number, not '87a' or '89a'");
+ fclose (fd);
+ return -1;
+ }
+
+ if (! ReadOK (fd, buf, 7))
+ {
+ g_message ("Failed to read screen descriptor");
+ fclose (fd);
+ return -1;
+ }
+
+ GifScreen.Width = LM_to_uint (buf[0], buf[1]);
+ GifScreen.Height = LM_to_uint (buf[2], buf[3]);
+ GifScreen.BitPixel = 2 << (buf[4] & 0x07);
+ GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
+ GifScreen.Background = buf[5];
+ GifScreen.AspectRatio = buf[6];
+
+ if (BitSet (buf[4], LOCALCOLORMAP))
+ {
+ /* Global Colormap */
+ if (! ReadColorMap (fd, GifScreen.BitPixel, GifScreen.ColorMap,
+ &GifScreen.GrayScale))
+ {
+ g_message ("Error reading global colormap");
+ fclose (fd);
+ return -1;
+ }
+ }
+
+ if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49)
+ {
+ g_message (_("Non-square pixels. Image might look squashed."));
+ }
+
+
+ highest_used_index = 0;
+
+ while (TRUE)
+ {
+ if (! ReadOK (fd, &c, 1))
+ {
+ g_message ("EOF / read error on image data");
+ fclose (fd);
+ return image_ID; /* will be -1 if failed on first image! */
+ }
+
+ if (c == ';')
+ {
+ /* GIF terminator */
+ fclose (fd);
+ return image_ID;
+ }
+
+ if (c == '!')
+ {
+ /* Extension */
+ if (! ReadOK (fd, &c, 1))
+ {
+ g_message ("EOF / read error on extension function code");
+ fclose (fd);
+ return image_ID; /* will be -1 if failed on first image! */
+ }
+
+ DoExtension (fd, c);
+ continue;
+ }
+
+ if (c != ',')
+ {
+ /* Not a valid start character */
+ g_printerr ("GIF: bogus character 0x%02x, ignoring.\n", (int) c);
+ continue;
+ }
+
+ ++imageCount;
+
+ if (! ReadOK (fd, buf, 9))
+ {
+ g_message ("Couldn't read left/top/width/height");
+ fclose (fd);
+ return image_ID; /* will be -1 if failed on first image! */
+ }
+
+ useGlobalColormap = !BitSet (buf[8], LOCALCOLORMAP);
+
+ bitPixel = 1 << ((buf[8] & 0x07) + 1);
+
+ if (! useGlobalColormap)
+ {
+ if (! ReadColorMap (fd, bitPixel, localColorMap, &grayScale))
+ {
+ g_message ("Error reading local colormap");
+ fclose (fd);
+ return image_ID; /* will be -1 if failed on first image! */
+ }
+
+ status = ReadImage (fd, filename, LM_to_uint (buf[4], buf[5]),
+ LM_to_uint (buf[6], buf[7]),
+ localColorMap, bitPixel,
+ grayScale,
+ BitSet (buf[8], INTERLACE), imageCount,
+ (guint) LM_to_uint (buf[0], buf[1]),
+ (guint) LM_to_uint (buf[2], buf[3]),
+ GifScreen.Width,
+ GifScreen.Height,
+ &image_ID);
+ }
+ else
+ {
+ status = ReadImage (fd, filename, LM_to_uint (buf[4], buf[5]),
+ LM_to_uint (buf[6], buf[7]),
+ GifScreen.ColorMap, GifScreen.BitPixel,
+ GifScreen.GrayScale,
+ BitSet (buf[8], INTERLACE), imageCount,
+ (guint) LM_to_uint (buf[0], buf[1]),
+ (guint) LM_to_uint (buf[2], buf[3]),
+ GifScreen.Width,
+ GifScreen.Height,
+ &image_ID);
+ }
+
+ if (!status)
+ {
+ break;
+ }
+
+ if (comment_parasite != NULL)
+ {
+ if (! thumbnail)
+ gimp_image_attach_parasite (image_ID, comment_parasite);
+
+ gimp_parasite_free (comment_parasite);
+ comment_parasite = NULL;
+ }
+
+ /* If we are loading a thumbnail, we stop after the first frame. */
+ if (thumbnail)
+ break;
+ }
+
+ fclose (fd);
+
+ return image_ID;
+}
+
+static gboolean
+ReadColorMap (FILE *fd,
+ gint number,
+ CMap buffer,
+ gint *format)
+{
+ guchar rgb[3];
+ gint flag;
+ gint i;
+
+ flag = TRUE;
+
+ for (i = 0; i < number; ++i)
+ {
+ if (! ReadOK (fd, rgb, sizeof (rgb)))
+ return FALSE;
+
+ buffer[CM_RED][i] = rgb[0];
+ buffer[CM_GREEN][i] = rgb[1];
+ buffer[CM_BLUE][i] = rgb[2];
+
+ flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
+ }
+
+ *format = (flag) ? GRAYSCALE : COLOR;
+
+ return TRUE;
+}
+
+static gint
+DoExtension (FILE *fd,
+ gint label)
+{
+ static guchar buf[256];
+#ifdef GIFDEBUG
+ gchar *str;
+#endif
+
+ switch (label)
+ {
+ case 0x01: /* Plain Text Extension */
+#ifdef GIFDEBUG
+ str = "Plain Text Extension";
+#endif
+
+#ifdef notdef
+ if (GetDataBlock (fd, (guchar *) buf) == 0)
+ ;
+
+ lpos = LM_to_uint (buf[0], buf[1]);
+ tpos = LM_to_uint (buf[2], buf[3]);
+ width = LM_to_uint (buf[4], buf[5]);
+ height = LM_to_uint (buf[6], buf[7]);
+ cellw = buf[8];
+ cellh = buf[9];
+ foreground = buf[10];
+ background = buf[11];
+
+ while (GetDataBlock (fd, (guchar *) buf) > 0)
+ {
+ PPM_ASSIGN (image[ypos][xpos],
+ cmap[CM_RED][v],
+ cmap[CM_GREEN][v],
+ cmap[CM_BLUE][v]);
+ ++index;
+ }
+
+ return FALSE;
+#else
+ break;
+#endif
+
+ case 0xff: /* Application Extension */
+#ifdef GIFDEBUG
+ str = "Application Extension";
+#endif
+ break;
+ case 0xfe: /* Comment Extension */
+#ifdef GIFDEBUG
+ str = "Comment Extension";
+#endif
+ while (GetDataBlock (fd, (guchar *) buf) > 0)
+ {
+ gchar *comment = (gchar *) buf;
+
+ if (! g_utf8_validate (comment, -1, NULL))
+ continue;
+
+ if (comment_parasite)
+ gimp_parasite_free (comment_parasite);
+
+ comment_parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1, comment);
+ }
+ return TRUE;
+ break;
+
+ case 0xf9: /* Graphic Control Extension */
+#ifdef GIFDEBUG
+ str = "Graphic Control Extension";
+#endif
+ (void) GetDataBlock (fd, (guchar *) buf);
+ Gif89.disposal = (buf[0] >> 2) & 0x7;
+ Gif89.inputFlag = (buf[0] >> 1) & 0x1;
+ Gif89.delayTime = LM_to_uint (buf[1], buf[2]);
+ if ((buf[0] & 0x1) != 0)
+ Gif89.transparent = buf[3];
+ else
+ Gif89.transparent = -1;
+
+ while (GetDataBlock (fd, (guchar *) buf) > 0);
+
+ return FALSE;
+ break;
+
+ default:
+#ifdef GIFDEBUG
+ str = (gchar *)buf;
+#endif
+ sprintf ((gchar *)buf, "UNKNOWN (0x%02x)", label);
+ break;
+ }
+
+#ifdef GIFDEBUG
+ g_print ("GIF: got a '%s'\n", str);
+#endif
+
+ while (GetDataBlock (fd, (guchar *) buf) > 0);
+
+ return FALSE;
+}
+
+static gint ZeroDataBlock = FALSE;
+
+static gint
+GetDataBlock (FILE *fd,
+ guchar *buf)
+{
+ guchar count;
+
+ if (! ReadOK (fd, &count, 1))
+ {
+ g_message ("Error in getting DataBlock size");
+ return -1;
+ }
+
+ ZeroDataBlock = (count == 0);
+
+ if ((count != 0) && (! ReadOK (fd, buf, count)))
+ {
+ g_message ("Error in reading DataBlock");
+ return -1;
+ }
+
+ return count;
+}
+
+static gint
+GetCode (FILE *fd,
+ gint code_size,
+ gboolean flag)
+{
+ static guchar buf[280];
+ static gint curbit, lastbit, done, last_byte;
+ gint i, j, ret, count;
+
+ if (flag)
+ {
+ curbit = 0;
+ lastbit = 0;
+ done = FALSE;
+ last_byte = 2;
+ return 0;
+ }
+
+ while ((curbit + code_size) > lastbit)
+ {
+ if (done)
+ {
+ if (curbit >= lastbit)
+ g_message ("Ran off the end of my bits");
+
+ return -1;
+ }
+
+ buf[0] = buf[last_byte - 2];
+ buf[1] = buf[last_byte - 1];
+
+ count = GetDataBlock (fd, &buf[2]);
+ if (count < 0)
+ {
+ return -1;
+ }
+ else if (count == 0)
+ {
+ done = TRUE;
+ }
+
+ last_byte = 2 + count;
+ curbit = (curbit - lastbit) + 16;
+ lastbit = (2 + count) * 8;
+ }
+
+ ret = 0;
+ for (i = curbit, j = 0; j < code_size; ++i, ++j)
+ ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
+
+ curbit += code_size;
+
+ return ret;
+}
+
+static gint
+LZWReadByte (FILE *fd,
+ gint just_reset_LZW,
+ gint input_code_size)
+{
+ static gint fresh = FALSE;
+ gint code, incode;
+ static gint code_size, set_code_size;
+ static gint max_code, max_code_size;
+ static gint firstcode, oldcode;
+ static gint clear_code, end_code;
+ static gint table[2][(1 << MAX_LZW_BITS)];
+#define STACK_SIZE ((1 << (MAX_LZW_BITS)) * 2)
+ static gint stack[STACK_SIZE], *sp;
+ gint i;
+
+ if (just_reset_LZW)
+ {
+ if (input_code_size > MAX_LZW_BITS || input_code_size <= 1)
+ {
+ g_message ("Value out of range for code size (corrupted file?)");
+ return -1;
+ }
+
+ set_code_size = input_code_size;
+ code_size = set_code_size + 1;
+ clear_code = 1 << set_code_size;
+ end_code = clear_code + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+
+ if (GetCode (fd, 0, TRUE) < 0)
+ {
+ return -1;
+ }
+
+ fresh = TRUE;
+
+ sp = stack;
+
+ for (i = 0; i < clear_code; ++i)
+ {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+ for (; i < (1 << MAX_LZW_BITS); ++i)
+ {
+ table[0][i] = 0;
+ table[1][i] = 0;
+ }
+
+ return 0;
+ }
+ else if (fresh)
+ {
+ fresh = FALSE;
+ do
+ {
+ firstcode = oldcode = GetCode (fd, code_size, FALSE);
+ }
+ while (firstcode == clear_code);
+
+ if (firstcode < 0)
+ {
+ return -1;
+ }
+
+ return firstcode & 255;
+ }
+
+ if (sp > stack)
+ return (*--sp) & 255;
+
+ while ((code = GetCode (fd, code_size, FALSE)) >= 0)
+ {
+ if (code == clear_code)
+ {
+ for (i = 0; i < clear_code; ++i)
+ {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+ for (; i < (1 << MAX_LZW_BITS); ++i)
+ {
+ table[0][i] = 0;
+ table[1][i] = 0;
+ }
+
+ code_size = set_code_size + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+ sp = stack;
+ firstcode = oldcode = GetCode (fd, code_size, FALSE);
+
+ if (firstcode < 0)
+ {
+ return -1;
+ }
+
+ return firstcode & 255;
+ }
+ else if (code == end_code || code > max_code)
+ {
+ gint count;
+ guchar buf[260];
+
+ if (ZeroDataBlock)
+ return -2;
+
+ while ((count = GetDataBlock (fd, buf)) > 0)
+ ;
+
+ if (count != 0)
+ g_print ("GIF: missing EOD in data stream (common occurrence)");
+
+ return -2;
+ }
+
+ incode = code;
+
+ if (code == max_code)
+ {
+ if (sp < &(stack[STACK_SIZE]))
+ *sp++ = firstcode;
+ code = oldcode;
+ }
+
+ while (code >= clear_code && sp < &(stack[STACK_SIZE]))
+ {
+ *sp++ = table[1][code];
+ if (code == table[0][code])
+ {
+ g_message ("Circular table entry. Corrupt file.");
+ gimp_quit ();
+ }
+ code = table[0][code];
+ }
+
+ if (sp < &(stack[STACK_SIZE]))
+ *sp++ = firstcode = table[1][code];
+
+ if ((code = max_code) < (1 << MAX_LZW_BITS))
+ {
+ table[0][code] = oldcode;
+ table[1][code] = firstcode;
+ ++max_code;
+ if ((max_code >= max_code_size) &&
+ (max_code_size < (1 << MAX_LZW_BITS)))
+ {
+ max_code_size *= 2;
+ ++code_size;
+ }
+ }
+
+ oldcode = incode;
+
+ if (sp > stack)
+ return (*--sp) & 255;
+ }
+
+ if (code < 0)
+ {
+ return -1;
+ }
+
+ return code & 255;
+}
+
+static gboolean
+ReadImage (FILE *fd,
+ const gchar *filename,
+ gint len,
+ gint height,
+ CMap cmap,
+ gint ncols,
+ gint format,
+ gint interlace,
+ gint number,
+ guint leftpos,
+ guint toppos,
+ guint screenwidth,
+ guint screenheight,
+ gint32 *image_ID)
+{
+ static gint frame_number = 1;
+
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ guchar *dest, *temp;
+ guchar c;
+ gint xpos = 0, ypos = 0, pass = 0;
+ gint cur_progress, max_progress;
+ gint v;
+ gint i, j;
+ gchar *framename;
+ gchar *framename_ptr;
+ gboolean alpha_frame = FALSE;
+ static gint previous_disposal;
+
+ /* Guard against bogus frame size */
+ if (len < 1 || height < 1)
+ {
+ g_message ("Bogus frame dimensions");
+ *image_ID = -1;
+ return FALSE;
+ }
+
+ /*
+ ** Initialize the Compression routines
+ */
+ if (! ReadOK (fd, &c, 1))
+ {
+ g_message ("EOF / read error on image data");
+ *image_ID = -1;
+ return FALSE;
+ }
+
+ if (LZWReadByte (fd, TRUE, c) < 0)
+ {
+ g_message ("Error while reading");
+ *image_ID = -1;
+ return FALSE;
+ }
+
+ if (frame_number == 1)
+ {
+ /* Guard against bogus logical screen size values */
+ if (screenwidth == 0)
+ screenwidth = len;
+
+ if (screenheight == 0)
+ screenheight = height;
+
+ *image_ID = gimp_image_new (screenwidth, screenheight, GIMP_INDEXED);
+ gimp_image_set_filename (*image_ID, filename);
+
+ for (i = 0, j = 0; i < ncols; i++)
+ {
+ used_cmap[0][i] = gimp_cmap[j++] = cmap[0][i];
+ used_cmap[1][i] = gimp_cmap[j++] = cmap[1][i];
+ used_cmap[2][i] = gimp_cmap[j++] = cmap[2][i];
+ }
+
+ gimp_image_set_colormap (*image_ID, gimp_cmap, ncols);
+
+ if (Gif89.delayTime < 0)
+ framename = g_strdup (_("Background"));
+ else
+ framename = g_strdup_printf (_("Background (%d%s)"),
+ 10 * Gif89.delayTime, "ms");
+
+ previous_disposal = Gif89.disposal;
+
+ if (Gif89.transparent == -1)
+ {
+ layer_ID = gimp_layer_new (*image_ID, framename,
+ len, height,
+ GIMP_INDEXED_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image_ID));
+ }
+ else
+ {
+ layer_ID = gimp_layer_new (*image_ID, framename,
+ len, height,
+ GIMP_INDEXEDA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image_ID));
+ alpha_frame=TRUE;
+ }
+
+ g_free (framename);
+ }
+ else /* NOT FIRST FRAME */
+ {
+ gimp_progress_set_text_printf (_("Opening '%s' (frame %d)"),
+ gimp_filename_to_utf8 (filename),
+ frame_number);
+ gimp_progress_pulse ();
+
+ /* If the colormap is now different, we have to promote to RGB! */
+ if (! promote_to_rgb)
+ {
+ for (i = 0; i < ncols; i++)
+ {
+ if ((used_cmap[0][i] != cmap[0][i]) ||
+ (used_cmap[1][i] != cmap[1][i]) ||
+ (used_cmap[2][i] != cmap[2][i]))
+ {
+ /* Everything is RGB(A) from now on... sigh. */
+ promote_to_rgb = TRUE;
+
+ /* Promote everything we have so far into RGB(A) */
+#ifdef GIFDEBUG
+ g_print ("GIF: Promoting image to RGB...\n");
+#endif
+ gimp_image_convert_rgb (*image_ID);
+
+ break;
+ }
+ }
+ }
+
+ if (Gif89.delayTime < 0)
+ framename = g_strdup_printf (_("Frame %d"), frame_number);
+ else
+ framename = g_strdup_printf (_("Frame %d (%d%s)"),
+ frame_number, 10 * Gif89.delayTime, "ms");
+
+ switch (previous_disposal)
+ {
+ case 0x00:
+ break; /* 'don't care' */
+ case 0x01:
+ framename_ptr = framename;
+ framename = g_strconcat (framename, " (combine)", NULL);
+ g_free (framename_ptr);
+ break;
+ case 0x02:
+ framename_ptr = framename;
+ framename = g_strconcat (framename, " (replace)", NULL);
+ g_free (framename_ptr);
+ break;
+ case 0x03: /* Rarely-used, and unhandled by many
+ loaders/players (including GIMP: we treat as
+ 'combine' mode). */
+ framename_ptr = framename;
+ framename = g_strconcat (framename, " (combine) (!)", NULL);
+ g_free (framename_ptr);
+ break;
+ case 0x04: /* I've seen a composite of this type. stvo_online_banner2.gif */
+ case 0x05:
+ case 0x06: /* I've seen a composite of this type. bn31.Gif */
+ case 0x07:
+ framename_ptr = framename;
+ framename = g_strconcat (framename, " (unknown disposal)", NULL);
+ g_free (framename_ptr);
+ g_message (_("GIF: Undocumented GIF composite type %d is "
+ "not handled. Animation might not play or "
+ "re-save perfectly."),
+ previous_disposal);
+ break;
+ default:
+ g_message ("Disposal word got corrupted. Bug.");
+ break;
+ }
+ previous_disposal = Gif89.disposal;
+
+ layer_ID = gimp_layer_new (*image_ID, framename,
+ len, height,
+ promote_to_rgb ?
+ GIMP_RGBA_IMAGE : GIMP_INDEXEDA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image_ID));
+ alpha_frame = TRUE;
+ g_free (framename);
+ }
+
+ frame_number++;
+
+ gimp_image_insert_layer (*image_ID, layer_ID, -1, 0);
+ gimp_item_transform_translate (layer_ID, (gint) leftpos, (gint) toppos);
+
+ cur_progress = 0;
+ max_progress = height;
+
+ if (len > (G_MAXSIZE / height / (alpha_frame ? (promote_to_rgb ? 4 : 2) : 1)))
+ {
+ g_message ("'%s' has a larger image size than GIMP can handle.",
+ gimp_filename_to_utf8 (filename));
+ *image_ID = -1;
+ return FALSE;
+ }
+
+ if (alpha_frame)
+ dest = (guchar *) g_malloc ((gsize)len * (gsize)height * (promote_to_rgb ? 4 : 2));
+ else
+ dest = (guchar *) g_malloc ((gsize)len * (gsize)height);
+
+#ifdef GIFDEBUG
+ g_print ("GIF: reading %d by %d%s GIF image, ncols=%d\n",
+ len, height, interlace ? " interlaced" : "", ncols);
+#endif
+
+ if (! alpha_frame && promote_to_rgb)
+ {
+ /* I don't see how one would easily construct a GIF in which
+ this could happen, but it's a mad mad world. */
+ g_message ("Ouch! Can't handle non-alpha RGB frames.\n"
+ "Please file a bug report at "
+ "https://gitlab.gnome.org/GNOME/gimp/issues");
+ gimp_quit ();
+ }
+
+ while ((v = LZWReadByte (fd, FALSE, c)) >= 0)
+ {
+ if (alpha_frame)
+ {
+ if (((guchar) v > highest_used_index) && !(v == Gif89.transparent))
+ highest_used_index = (guchar) v;
+
+ if (promote_to_rgb)
+ {
+ temp = dest + ( (ypos * len) + xpos ) * 4;
+ *(temp ) = (guchar) cmap[0][v];
+ *(temp+1) = (guchar) cmap[1][v];
+ *(temp+2) = (guchar) cmap[2][v];
+ *(temp+3) = (guchar) ((v == Gif89.transparent) ? 0 : 255);
+ }
+ else
+ {
+ temp = dest + ( (ypos * len) + xpos ) * 2;
+ *temp = (guchar) v;
+ *(temp+1) = (guchar) ((v == Gif89.transparent) ? 0 : 255);
+ }
+ }
+ else
+ {
+ if ((guchar) v > highest_used_index)
+ highest_used_index = (guchar) v;
+
+ temp = dest + (ypos * len) + xpos;
+ *temp = (guchar) v;
+ }
+
+ xpos++;
+ if (xpos == len)
+ {
+ xpos = 0;
+ if (interlace)
+ {
+ switch (pass)
+ {
+ case 0:
+ case 1:
+ ypos += 8;
+ break;
+ case 2:
+ ypos += 4;
+ break;
+ case 3:
+ ypos += 2;
+ break;
+ }
+
+ if (ypos >= height)
+ {
+ pass++;
+ switch (pass)
+ {
+ case 1:
+ ypos = 4;
+ break;
+ case 2:
+ ypos = 2;
+ break;
+ case 3:
+ ypos = 1;
+ break;
+ default:
+ goto fini;
+ }
+ }
+ }
+ else
+ {
+ ypos++;
+ }
+
+ if (frame_number == 1)
+ {
+ cur_progress++;
+ if ((cur_progress % 16) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+ }
+
+ if (ypos >= height)
+ break;
+ }
+
+ if (v < 0)
+ {
+ return FALSE;
+ }
+
+ fini:
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, len, height), 0,
+ NULL, dest, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (dest);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ if (LZWReadByte (fd, FALSE, c) >= 0)
+ {
+ g_print ("GIF: too much input data, ignoring extra...\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/common/file-gif-save.c b/plug-ins/common/file-gif-save.c
new file mode 100644
index 0000000..ddb555d
--- /dev/null
+++ b/plug-ins/common/file-gif-save.c
@@ -0,0 +1,2574 @@
+/* GIF exporting file filter for GIMP
+ *
+ * Copyright
+ * - Adam D. Moss
+ * - Peter Mattis
+ * - Spencer Kimball
+ *
+ * Based around original GIF code by David Koblas.
+ *
+ *
+ * Version 4.1.0 - 2003-06-16
+ * Adam D. Moss - <adam@gimp.org> <adam@foxbox.org>
+ */
+/*
+ * This filter uses code taken from the "giftopnm" and "ppmtogif" programs
+ * which are part of the "netpbm" package.
+ */
+/*
+ * "The Graphics Interchange Format(c) is the Copyright property of
+ * CompuServe Incorporated. GIF(sm) is a Service Mark property of
+ * CompuServe Incorporated."
+ */
+/* Copyright notice for GIF code from which this plugin was long ago */
+/* derived (David Koblas has granted permission to relicense): */
+/* +-------------------------------------------------------------------+ */
+/* | Copyright 1990, 1991, 1993, David Koblas. (koblas@extra.com) | */
+/* +-------------------------------------------------------------------+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-gif-save"
+#define SAVE2_PROC "file-gif-save2"
+#define PLUG_IN_BINARY "file-gif-save"
+#define PLUG_IN_ROLE "gimp-file-gif-save"
+
+
+/* uncomment the line below for a little debugging info */
+/* #define GIFDEBUG yesplease */
+
+
+enum
+{
+ DISPOSE_STORE_VALUE_COLUMN,
+ DISPOSE_STORE_LABEL_COLUMN
+};
+
+enum
+{
+ DISPOSE_UNSPECIFIED,
+ DISPOSE_COMBINE,
+ DISPOSE_REPLACE
+};
+
+typedef struct
+{
+ gint interlace;
+ gint save_comment;
+ gint loop;
+ gint default_delay;
+ gint default_dispose;
+ gboolean always_use_default_delay;
+ gboolean always_use_default_dispose;
+ gboolean as_animation;
+} GIFSaveVals;
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error);
+
+static GimpPDBStatusType sanity_check (GFile *file,
+ gint32 *image_ID,
+ GimpRunMode run_mode,
+ GError **error);
+static gboolean bad_bounds_dialog (void);
+
+static gboolean save_dialog (gint32 image_ID);
+static void comment_entry_callback (GtkTextBuffer *buffer);
+
+
+static gboolean comment_was_edited = FALSE;
+static gchar *globalcomment = NULL;
+static gint Interlace; /* For compression code */
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static GIFSaveVals gsvals =
+{
+ FALSE, /* interlace */
+ TRUE, /* save comment */
+ TRUE, /* loop infinitely */
+ 100, /* default_delay between frames (100ms) */
+ 0, /* default_dispose = "don't care" */
+ FALSE, /* don't always use default_delay */
+ FALSE, /* don't always use default_dispose */
+ FALSE /* as_animation */
+};
+
+
+MAIN ()
+
+#define COMMON_SAVE_ARGS \
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, \
+ { GIMP_PDB_IMAGE, "image", "Image to export" }, \
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" }, \
+ { GIMP_PDB_STRING, "uri", "The name of the URI to export the image in" }, \
+ { GIMP_PDB_STRING, "raw-uri", "The name of the URI to export the image in" }, \
+ { GIMP_PDB_INT32, "interlace", "Try to export as interlaced" }, \
+ { GIMP_PDB_INT32, "loop", "(animated gif) loop infinitely" }, \
+ { GIMP_PDB_INT32, "default-delay", "(animated gif) Default delay between frames in milliseconds" }, \
+ { GIMP_PDB_INT32, "default-dispose", "(animated gif) Default disposal type (0=`don't care`, 1=combine, 2=replace)" }
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ COMMON_SAVE_ARGS
+ };
+
+ static const GimpParamDef save2_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ { GIMP_PDB_INT32, "as-animation", "Export GIF as animation?" },
+ { GIMP_PDB_INT32, "force-delay", "(animated gif) Use specified delay for all frames?" },
+ { GIMP_PDB_INT32, "force-dispose", "(animated gif) Use specified disposal for all frames?" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "exports files in Compuserve GIF file format",
+ "Export a file in Compuserve GIF format, with "
+ "possible animation, transparency, and comment. "
+ "To export an animation, operate on a multi-layer "
+ "file. The plug-in will interpret <50% alpha as "
+ "transparent. When run non-interactively, the "
+ "value for the comment is taken from the "
+ "'gimp-comment' parasite. ",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "1995-1997",
+ N_("GIF image"),
+ "INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (SAVE2_PROC,
+ "exports files in Compuserve GIF file format",
+ "Export a file in Compuserve GIF format, with "
+ "possible animation, transparency, and comment. "
+ "To export an animation, operate on a multi-layer "
+ "file and give the 'as-animation' parameter "
+ "as TRUE. The plug-in will interpret <50% "
+ "alpha as transparent. When run "
+ "non-interactively, the value for the comment "
+ "is taken from the 'gimp-comment' parasite. ",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "Spencer Kimball, Peter Mattis, Adam Moss, David Koblas",
+ "1995-1997",
+ N_("GIF image"),
+ "INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save2_args), 0,
+ save2_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/gif");
+ gimp_register_save_handler (SAVE_PROC, "gif", "");
+ gimp_register_file_handler_uri (SAVE_PROC);
+
+ gimp_register_file_handler_uri (SAVE2_PROC);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, SAVE_PROC) == 0 ||
+ strcmp (name, SAVE2_PROC) == 0)
+ {
+ GFile *file;
+ gint32 image_ID;
+ gint32 orig_image_ID;
+ gint32 sanitized_image_ID = 0;
+ gint32 drawable_ID;
+
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+ file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE ||
+ run_mode == GIMP_RUN_WITH_LAST_VALS)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ status = sanity_check (file, &image_ID, run_mode, &error);
+
+ /* Get the export options */
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* If the sanity check succeeded, the image_ID will point to
+ * a duplicate image to delete later.
+ */
+ sanitized_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &gsvals);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog (image_ID))
+ {
+ gimp_image_delete (sanitized_image_ID);
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 9 && nparams != 12)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gsvals.interlace = (param[5].data.d_int32) ? TRUE : FALSE;
+ gsvals.save_comment = TRUE; /* no way to to specify that through the PDB */
+ gsvals.loop = (param[6].data.d_int32) ? TRUE : FALSE;
+ gsvals.default_delay = param[7].data.d_int32;
+ gsvals.default_dispose = param[8].data.d_int32;
+ if (nparams == 12)
+ {
+ gsvals.as_animation = (param[9].data.d_int32) ? TRUE : FALSE;
+ gsvals.always_use_default_delay = (param[10].data.d_int32) ? TRUE : FALSE;
+ gsvals.always_use_default_dispose = (param[11].data.d_int32) ? TRUE : FALSE;
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &gsvals);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Create an exportable image based on the export options */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ GimpExportCapabilities capabilities =
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA;
+
+ if (gsvals.as_animation)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "GIF",
+ capabilities);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ if (sanitized_image_ID)
+ gimp_image_delete (sanitized_image_ID);
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Write the image to file */
+ if (save_image (file,
+ image_ID, drawable_ID, orig_image_ID,
+ &error))
+ {
+ /* Store psvals data */
+ gimp_set_data (SAVE_PROC, &gsvals, sizeof (GIFSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_delete (sanitized_image_ID);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ g_object_unref (file);
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* ppmtogif.c - read a portable pixmap and produce a GIF file
+**
+** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
+** Lempel-Ziv compression based on "compress".
+**
+** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
+**
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation. This software is provided "as is" without express or
+** implied warranty.
+**
+** The Graphics Interchange Format(c) is the Copyright property of
+** CompuServe Incorporated. GIF(sm) is a Service Mark property of
+** CompuServe Incorporated.
+*/
+
+#define MAXCOLORS 256
+
+/*
+ * Pointer to function returning an int
+ */
+typedef gint (* ifunptr) (gint x,
+ gint y);
+
+
+static gint find_unused_ia_color (const guchar *pixels,
+ gint numpixels,
+ gint num_indices,
+ gint *colors);
+
+static void special_flatten_indexed_alpha (guchar *pixels,
+ gint transparent,
+ gint numpixels);
+
+static gint colors_to_bpp (gint colors);
+static gint bpp_to_colors (gint bpp);
+static gint get_pixel (gint x,
+ gint y);
+static gint gif_next_pixel (ifunptr getpixel);
+static void bump_pixel (void);
+
+static gboolean gif_encode_header (GOutputStream *output,
+ gboolean gif89,
+ gint width,
+ gint height,
+ gint background,
+ gint bpp,
+ gint *red,
+ gint *green,
+ gint *blue,
+ ifunptr get_pixel,
+ GError **error);
+static gboolean gif_encode_graphic_control_ext (GOutputStream *output,
+ gint disposal,
+ gint delay89,
+ gint n_frames,
+ gint width,
+ gint height,
+ gint transparent,
+ gint bpp,
+ ifunptr get_pixel,
+ GError **error);
+static gboolean gif_encode_image_data (GOutputStream *output,
+ gint width,
+ gint height,
+ gint interlace,
+ gint bpp,
+ ifunptr get_pixel,
+ gint offset_x,
+ gint offset_y,
+ GError **error);
+static gboolean gif_encode_close (GOutputStream *output,
+ GError **error);
+static gboolean gif_encode_loop_ext (GOutputStream *output,
+ guint n_loops,
+ GError **error);
+static gboolean gif_encode_comment_ext (GOutputStream *output,
+ const gchar *comment,
+ GError **error);
+
+static gint rowstride;
+static guchar *pixels;
+static gint cur_progress;
+static gint max_progress;
+
+static gboolean compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error);
+static gboolean no_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error);
+static gboolean rle_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error);
+static gboolean normal_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error);
+
+static gboolean put_byte (GOutputStream *output,
+ guchar b,
+ GError **error);
+static gboolean put_word (GOutputStream *output,
+ gint w,
+ GError **error);
+static gboolean put_string (GOutputStream *output,
+ const gchar *s,
+ GError **error);
+static gboolean output_code (GOutputStream *output,
+ gint code,
+ GError **error);
+static gboolean cl_block (GOutputStream *output,
+ GError **error);
+static void cl_hash (glong hsize);
+
+static void char_init (void);
+static gboolean char_out (GOutputStream *output,
+ gint c,
+ GError **error);
+static gboolean char_flush (GOutputStream *output,
+ GError **error);
+
+
+static gint
+find_unused_ia_color (const guchar *pixels,
+ gint numpixels,
+ gint num_indices,
+ gint *colors)
+{
+ gboolean ix_used[256];
+ gint i;
+
+#ifdef GIFDEBUG
+ g_printerr ("GIF: fuiac: Image claims to use %d/%d indices - finding free "
+ "index...\n", *colors, num_indices);
+#endif
+
+ for (i = 0; i < 256; i++)
+ ix_used[i] = FALSE;
+
+ for (i = 0; i < numpixels; i++)
+ {
+ if (pixels[i * 2 + 1])
+ ix_used[pixels[i * 2]] = TRUE;
+ }
+
+ for (i = num_indices - 1; i >= 0; i--)
+ {
+ if (! ix_used[i])
+ {
+#ifdef GIFDEBUG
+ g_printerr ("GIF: Found unused color index %d.\n", (int) i);
+#endif
+ return i;
+ }
+ }
+
+ /* Couldn't find an unused color index within the number of bits per
+ * pixel we wanted. Will have to increment the number of colors in
+ * the image and assign a transparent pixel there.
+ */
+ if (*colors < 256)
+ {
+ (*colors)++;
+
+ g_printerr ("GIF: 2nd pass "
+ "- Increasing bounds and using color index %d.\n",
+ *colors - 1);
+ return ((*colors) - 1);
+ }
+
+ g_message (_("Couldn't simply reduce colors further. Exporting as opaque."));
+
+ return -1;
+}
+
+
+static void
+special_flatten_indexed_alpha (guchar *pixels,
+ gint transparent,
+ gint numpixels)
+{
+ guint32 i;
+
+ /* Each transparent pixel in the image is mapped to a uniform value
+ * for encoding, if image already has <=255 colors
+ */
+
+ if (transparent == -1) /* tough, no indices left for the trans. index */
+ {
+ for (i = 0; i < numpixels; i++)
+ pixels[i] = pixels[i * 2];
+ }
+ else /* make transparent */
+ {
+ for (i = 0; i < numpixels; i++)
+ {
+ if (! (pixels[i * 2 + 1] & 128))
+ {
+ pixels[i] = (guchar) transparent;
+ }
+ else
+ {
+ pixels[i] = pixels[i * 2];
+ }
+ }
+ }
+}
+
+
+static gint
+parse_ms_tag (const gchar *str)
+{
+ gint sum = 0;
+ gint offset = 0;
+ gint length;
+
+ length = strlen (str);
+
+ find_another_bra:
+
+ while ((offset < length) && (str[offset] != '('))
+ offset++;
+
+ if (offset >= length)
+ return(-1);
+
+ if (! g_ascii_isdigit (str[++offset]))
+ goto find_another_bra;
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset < length) && (g_ascii_isdigit (str[offset])));
+
+ if (length - offset <= 2)
+ return(-3);
+
+ if ((g_ascii_toupper (str[offset]) != 'M') ||
+ (g_ascii_toupper (str[offset + 1]) != 'S'))
+ return -4;
+
+ return sum;
+}
+
+
+static gint
+parse_disposal_tag (const gchar *str)
+{
+ gint offset = 0;
+ gint length;
+
+ length = strlen (str);
+
+ while ((offset + 9) <= length)
+ {
+ if (strncmp (&str[offset], "(combine)", 9) == 0)
+ return 0x01;
+
+ if (strncmp (&str[offset], "(replace)", 9) == 0)
+ return 0x02 ;
+
+ offset++;
+ }
+
+ return gsvals.default_dispose;
+}
+
+
+static GimpPDBStatusType
+sanity_check (GFile *file,
+ gint32 *image_ID,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ gint32 *layers;
+ gint nlayers;
+ gint image_width;
+ gint image_height;
+ gint i;
+
+ image_width = gimp_image_width (*image_ID);
+ image_height = gimp_image_height (*image_ID);
+
+ if (image_width > G_MAXUSHORT || image_height > G_MAXUSHORT)
+ {
+ g_set_error (error, 0, 0,
+ _("Unable to export '%s'. "
+ "The GIF file format does not support images that are "
+ "more than %d pixels wide or tall."),
+ gimp_file_get_utf8_name (file), G_MAXUSHORT);
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /*** Iterate through the layers to make sure they're all ***/
+ /*** within the bounds of the image ***/
+
+ *image_ID = gimp_image_duplicate (*image_ID);
+ layers = gimp_image_get_layers (*image_ID, &nlayers);
+
+ for (i = 0; i < nlayers; i++)
+ {
+ gint offset_x;
+ gint offset_y;
+
+ gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
+
+ if (offset_x < 0 ||
+ offset_y < 0 ||
+ offset_x + gimp_drawable_width (layers[i]) > image_width ||
+ offset_y + gimp_drawable_height (layers[i]) > image_height)
+ {
+ g_free (layers);
+
+ /* Image has illegal bounds - ask the user what it wants to do */
+
+ /* Do the crop if we can't talk to the user, or if we asked
+ * the user and they said yes.
+ */
+ if ((run_mode == GIMP_RUN_NONINTERACTIVE) || bad_bounds_dialog ())
+ {
+ gimp_image_crop (*image_ID, image_width, image_height, 0, 0);
+ return GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ gimp_image_delete (*image_ID);
+ return GIMP_PDB_CANCEL;
+ }
+ }
+ }
+
+ g_free (layers);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ GimpImageType drawable_type;
+ const Babl *format = NULL;
+ GOutputStream *output;
+ gint Red[MAXCOLORS];
+ gint Green[MAXCOLORS];
+ gint Blue[MAXCOLORS];
+ guchar *cmap;
+ guint rows, cols;
+ gint BitsPerPixel;
+ gint liberalBPP = 0;
+ gint useBPP = 0;
+ gint colors;
+ gint i;
+ gint transparent;
+ gint offset_x, offset_y;
+
+ gint32 *layers;
+ gint nlayers;
+
+ gboolean is_gif89 = FALSE;
+
+ gint Delay89;
+ gint Disposal;
+ gchar *layer_name;
+
+ GimpRGB background;
+ guchar bgred, bggreen, bgblue;
+ guchar bgindex = 0;
+ guint best_error = 0xFFFFFFFF;
+
+ /* Save the comment back to the ImageID, if appropriate */
+ if (globalcomment != NULL && comment_was_edited)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (globalcomment) + 1,
+ (gpointer) globalcomment);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ /* The GIF spec says 7bit ASCII for the comment block. */
+ if (gsvals.save_comment && globalcomment)
+ {
+ const gchar *c = globalcomment;
+ gint len;
+
+ for (len = strlen (c); len; c++, len--)
+ {
+ if ((guchar) *c > 127)
+ {
+ g_message (_("The GIF format only supports comments in "
+ "7bit ASCII encoding. No comment is saved."));
+
+ g_free (globalcomment);
+ globalcomment = NULL;
+
+ break;
+ }
+ }
+ }
+
+ /* get a list of layers for this image_ID */
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+
+ drawable_type = gimp_drawable_type (layers[0]);
+
+ /* If the image has multiple layers (i.e. will be animated), a
+ * comment, or transparency, then it must be encoded as a GIF89a
+ * file, not a vanilla GIF87a.
+ */
+ if (nlayers > 1)
+ {
+ is_gif89 = TRUE;
+
+ /* Layers can be with or without alpha channel. Make sure we set
+ * alpha if there is at least one layer with alpha channel. */
+ if (drawable_type == GIMP_GRAY_IMAGE ||
+ drawable_type == GIMP_INDEXED_IMAGE)
+ {
+ for (i = nlayers - 1; i >= 0; i--)
+ {
+ GimpImageType dr_type = gimp_drawable_type (layers[i]);
+
+ if (dr_type == GIMP_GRAYA_IMAGE ||
+ dr_type == GIMP_INDEXEDA_IMAGE)
+ {
+ drawable_type = dr_type;
+ break;
+ }
+ }
+ }
+ }
+
+ if (gsvals.save_comment)
+ is_gif89 = TRUE;
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXEDA_IMAGE:
+ is_gif89 = TRUE;
+ case GIMP_INDEXED_IMAGE:
+ cmap = gimp_image_get_colormap (image_ID, &colors);
+
+ gimp_context_get_background (&background);
+ gimp_rgb_get_uchar (&background, &bgred, &bggreen, &bgblue);
+
+ for (i = 0; i < colors; i++)
+ {
+ Red[i] = *cmap++;
+ Green[i] = *cmap++;
+ Blue[i] = *cmap++;
+ }
+ for ( ; i < 256; i++)
+ {
+ Red[i] = bgred;
+ Green[i] = bggreen;
+ Blue[i] = bgblue;
+ }
+ break;
+ case GIMP_GRAYA_IMAGE:
+ is_gif89 = TRUE;
+ case GIMP_GRAY_IMAGE:
+ colors = 256; /* FIXME: Not ideal. */
+ for ( i = 0; i < 256; i++)
+ {
+ Red[i] = Green[i] = Blue[i] = i;
+ }
+ break;
+
+ default:
+ g_message (_("Cannot export RGB color images. Convert to "
+ "indexed color or grayscale first."));
+ return FALSE;
+ }
+
+
+ /* find earliest index in palette which is closest to the background
+ * color, and ATTEMPT to use that as the GIF's default background
+ * color.
+ */
+ for (i = 255; i >= 0; --i)
+ {
+ guint local_error = 0;
+
+ local_error += (Red[i] - bgred) * (Red[i] - bgred);
+ local_error += (Green[i] - bggreen) * (Green[i] - bggreen);
+ local_error += (Blue[i] - bgblue) * (Blue[i] - bgblue);
+
+ if (local_error <= best_error)
+ {
+ bgindex = i;
+ best_error = local_error;
+ }
+ }
+
+
+ /* init the progress meter */
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+
+ /* open the destination file for writing */
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GDataOutputStream *data_output;
+
+ data_output = g_data_output_stream_new (output);
+ g_object_unref (output);
+
+ g_data_output_stream_set_byte_order (data_output,
+ G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
+
+ output = G_OUTPUT_STREAM (data_output);
+ }
+ else
+ {
+ return FALSE;
+ }
+
+
+ /* write the GIFheader */
+
+ if (colors < 256)
+ {
+ /* we keep track of how many bits we promised to have in
+ * liberalBPP, so that we don't accidentally come under this
+ * when doing clever transparency stuff where we can re-use
+ * wasted indices.
+ */
+ liberalBPP = BitsPerPixel =
+ colors_to_bpp (colors + ((drawable_type==GIMP_INDEXEDA_IMAGE) ? 1 : 0));
+ }
+ else
+ {
+ liberalBPP = BitsPerPixel =
+ colors_to_bpp (256);
+
+ if (drawable_type == GIMP_INDEXEDA_IMAGE)
+ {
+ g_printerr ("GIF: Too many colors?\n");
+ }
+ }
+
+ cols = gimp_image_width (image_ID);
+ rows = gimp_image_height (image_ID);
+ Interlace = gsvals.interlace;
+ if (! gif_encode_header (output, is_gif89, cols, rows, bgindex,
+ BitsPerPixel, Red, Green, Blue, get_pixel,
+ error))
+ return FALSE;
+
+
+ /* If the image has multiple layers it'll be made into an animated
+ * GIF, so write out the infinite-looping extension
+ */
+ if ((nlayers > 1) && (gsvals.loop))
+ if (! gif_encode_loop_ext (output, 0, error))
+ return FALSE;
+
+ /* Write comment extension - mustn't be written before the looping ext. */
+ if (gsvals.save_comment && globalcomment)
+ {
+ if (! gif_encode_comment_ext (output, globalcomment, error))
+ return FALSE;
+ }
+
+
+ /*** Now for each layer in the image, save an image in a compound GIF ***/
+ /************************************************************************/
+
+ cur_progress = 0;
+ max_progress = nlayers * rows;
+
+ for (i = nlayers - 1; i >= 0; i--, cur_progress = (nlayers - i) * rows)
+ {
+ drawable_type = gimp_drawable_type (layers[i]);
+ if (drawable_type == GIMP_GRAYA_IMAGE)
+ {
+ format = babl_format ("Y'A u8");
+ }
+ else if (drawable_type == GIMP_GRAY_IMAGE)
+ {
+ format = babl_format ("Y' u8");
+ }
+ buffer = gimp_drawable_get_buffer (layers[i]);
+ gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
+ cols = gimp_drawable_width (layers[i]);
+ rows = gimp_drawable_height (layers[i]);
+ rowstride = cols;
+
+ pixels = g_new (guchar, (cols * rows *
+ (((drawable_type == GIMP_INDEXEDA_IMAGE) ||
+ (drawable_type == GIMP_GRAYA_IMAGE)) ? 2 : 1)));
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, cols, rows), 1.0,
+ format, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* sort out whether we need to do transparency jiggery-pokery */
+ if ((drawable_type == GIMP_INDEXEDA_IMAGE) ||
+ (drawable_type == GIMP_GRAYA_IMAGE))
+ {
+ /* Try to find an entry which isn't actually used in the
+ * image, for a transparency index.
+ */
+
+ transparent =
+ find_unused_ia_color (pixels,
+ cols * rows,
+ bpp_to_colors (colors_to_bpp (colors)),
+ &colors);
+
+ special_flatten_indexed_alpha (pixels,
+ transparent,
+ cols * rows);
+ }
+ else
+ {
+ transparent = -1;
+ }
+
+ BitsPerPixel = colors_to_bpp (colors);
+
+ if (BitsPerPixel != liberalBPP)
+ {
+ /* We were able to re-use an index within the existing
+ * bitspace, whereas the estimate in the header was
+ * pessimistic but still needs to be upheld...
+ */
+#ifdef GIFDEBUG
+ static gboolean onceonly = FALSE;
+
+ if (! onceonly)
+ {
+ g_warning ("Promised %d bpp, pondered writing chunk with %d bpp!",
+ liberalBPP, BitsPerPixel);
+ onceonly = TRUE;
+ }
+#endif
+ }
+
+ useBPP = (BitsPerPixel > liberalBPP) ? BitsPerPixel : liberalBPP;
+
+ if (is_gif89)
+ {
+ if (i > 0 && ! gsvals.always_use_default_dispose)
+ {
+ layer_name = gimp_item_get_name (layers[i - 1]);
+ Disposal = parse_disposal_tag (layer_name);
+ g_free (layer_name);
+ }
+ else
+ {
+ Disposal = gsvals.default_dispose;
+ }
+
+ layer_name = gimp_item_get_name (layers[i]);
+ Delay89 = parse_ms_tag (layer_name);
+ g_free (layer_name);
+
+ if (Delay89 < 0 || gsvals.always_use_default_delay)
+ Delay89 = (gsvals.default_delay + 5) / 10;
+ else
+ Delay89 = (Delay89 + 5) / 10;
+
+ /* don't allow a CPU-sucking completely 0-delay looping anim */
+ if ((nlayers > 1) && gsvals.loop && (Delay89 == 0))
+ {
+ static gboolean onceonly = FALSE;
+
+ if (! onceonly)
+ {
+ g_message (_("Delay inserted to prevent evil "
+ "CPU-sucking animation."));
+ onceonly = TRUE;
+ }
+
+ Delay89 = 1;
+ }
+
+ if (! gif_encode_graphic_control_ext (output,
+ Disposal, Delay89, nlayers,
+ cols, rows,
+ transparent,
+ useBPP,
+ get_pixel,
+ error))
+ return FALSE;
+ }
+
+ if (! gif_encode_image_data (output, cols, rows,
+ (rows > 4) ? gsvals.interlace : 0,
+ useBPP,
+ get_pixel,
+ offset_x, offset_y,
+ error))
+ return FALSE;
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (buffer);
+
+ g_free (pixels);
+ }
+
+ g_free (layers);
+
+ if (! gif_encode_close (output, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+bad_bounds_dialog (void)
+{
+ GtkWidget *dialog;
+ gboolean crop;
+
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
+ _("The image you are trying to export as a "
+ "GIF contains layers which extend beyond "
+ "the actual borders of the image."));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("Cr_op"), GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("The GIF file format does not "
+ "allow this. You may choose "
+ "whether to crop all of the "
+ "layers to the image borders, "
+ "or cancel this export."));
+
+ gtk_widget_show (dialog);
+
+ crop = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return crop;
+}
+
+static GtkWidget *
+file_gif_toggle_button_init (GtkBuilder *builder,
+ const gchar *name,
+ gboolean initial_value,
+ gboolean *value_pointer)
+{
+ GtkWidget *toggle = NULL;
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), initial_value);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ value_pointer);
+
+ return toggle;
+}
+
+static GtkWidget *
+file_gif_spin_button_int_init (GtkBuilder *builder,
+ const gchar *name,
+ int initial_value,
+ int *value_pointer)
+{
+ GtkWidget *spin_button = NULL;
+ GtkAdjustment *adjustment = NULL;
+
+ spin_button = GTK_WIDGET (gtk_builder_get_object (builder, name));
+
+ adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spin_button));
+ gtk_adjustment_set_value (adjustment, initial_value);
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ value_pointer);
+
+ return spin_button;
+}
+
+static void
+file_gif_combo_box_int_update_value (GtkComboBox *combo,
+ gint *value)
+{
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
+ {
+ gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)),
+ &iter,
+ DISPOSE_STORE_VALUE_COLUMN, value,
+ -1);
+ }
+}
+
+static GtkWidget *
+file_gif_combo_box_int_init (GtkBuilder *builder,
+ const gchar *name,
+ int initial_value,
+ int *value_pointer,
+ const gchar *first_label,
+ gint first_value,
+ ...)
+{
+ GtkWidget *combo = NULL;
+ GtkListStore *store = NULL;
+ const gchar *label = NULL;
+ gint value = 0;
+ GtkTreeIter iter = { 0, };
+ va_list values;
+
+ combo = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ store = GTK_LIST_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (combo)));
+
+ /* Populate */
+ va_start (values, first_value);
+ for (label = first_label, value = first_value;
+ label;
+ label = va_arg (values, const gchar *), value = va_arg (values, gint))
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ DISPOSE_STORE_VALUE_COLUMN, value,
+ DISPOSE_STORE_LABEL_COLUMN, label,
+ -1);
+ }
+ va_end (values);
+
+ /* Set initial value */
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store),
+ &iter,
+ NULL,
+ initial_value);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+
+ /* Arrange update of value */
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (file_gif_combo_box_int_update_value),
+ value_pointer);
+
+ return combo;
+}
+
+static gboolean
+save_dialog (gint32 image_ID)
+{
+ GtkBuilder *builder = NULL;
+ gchar *ui_file = NULL;
+ GError *error = NULL;
+ GtkWidget *dialog;
+ GtkWidget *text_view;
+ GtkTextBuffer *text_buffer;
+ GtkWidget *toggle;
+ GtkWidget *frame;
+ GimpParasite *GIF2_CMNT;
+ gint32 nlayers;
+ gboolean animation_supported = FALSE;
+ gboolean run;
+
+ g_free (gimp_image_get_layers (image_ID, &nlayers));
+ animation_supported = nlayers > 1;
+
+ dialog = gimp_export_dialog_new (_("GIF"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* GtkBuilder init */
+ builder = gtk_builder_new ();
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui/plug-ins/plug-in-file-gif.ui",
+ NULL);
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ g_printerr (_("Error loading UI file '%s':\n%s"),
+ ui_file, error ? error->message : "???");
+ g_free (ui_file);
+
+ /* Main vbox */
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ GTK_WIDGET (gtk_builder_get_object (builder, "main-vbox")),
+ TRUE, TRUE, 0);
+
+ /* regular gif parameter settings */
+ file_gif_toggle_button_init (builder, "interlace",
+ gsvals.interlace, &gsvals.interlace);
+ file_gif_toggle_button_init (builder, "save-comment",
+ gsvals.save_comment, &gsvals.save_comment);
+ file_gif_toggle_button_init (builder, "as-animation",
+ gsvals.as_animation, &gsvals.as_animation);
+
+ text_view = GTK_WIDGET (gtk_builder_get_object (builder, "comment"));
+ text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ if (globalcomment)
+ g_free (globalcomment);
+
+ GIF2_CMNT = gimp_image_get_parasite (image_ID, "gimp-comment");
+ if (GIF2_CMNT)
+ {
+ globalcomment = g_strndup (gimp_parasite_data (GIF2_CMNT),
+ gimp_parasite_data_size (GIF2_CMNT));
+ gimp_parasite_free (GIF2_CMNT);
+ }
+ else
+ {
+ globalcomment = gimp_get_default_comment ();
+ }
+
+ if (globalcomment)
+ gtk_text_buffer_set_text (text_buffer, globalcomment, -1);
+
+ g_signal_connect (text_buffer, "changed",
+ G_CALLBACK (comment_entry_callback),
+ NULL);
+
+ /* additional animated gif parameter settings */
+ file_gif_toggle_button_init (builder, "loop-forever",
+ gsvals.loop, &gsvals.loop);
+
+ /* default_delay entry field */
+ file_gif_spin_button_int_init (builder, "delay-spin",
+ gsvals.default_delay, &gsvals.default_delay);
+
+ /* Disposal selector */
+ file_gif_combo_box_int_init (builder, "dispose-combo",
+ gsvals.default_dispose, &gsvals.default_dispose,
+ _("I don't care"),
+ DISPOSE_UNSPECIFIED,
+ _("Cumulative layers (combine)"),
+ DISPOSE_COMBINE,
+ _("One frame per layer (replace)"),
+ DISPOSE_REPLACE,
+ NULL);
+
+ /* The "Always use default values" toggles */
+ file_gif_toggle_button_init (builder, "use-default-delay",
+ gsvals.always_use_default_delay,
+ &gsvals.always_use_default_delay);
+ file_gif_toggle_button_init (builder, "use-default-dispose",
+ gsvals.always_use_default_dispose,
+ &gsvals.always_use_default_dispose);
+
+ frame = GTK_WIDGET (gtk_builder_get_object (builder, "animation-frame"));
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "as-animation"));
+ if (! animation_supported)
+ {
+ gimp_help_set_help_data (toggle,
+ _("You can only export as animation when the "
+ "image has more than one layer. The image "
+ "you are trying to export only has one "
+ "layer."),
+ NULL);
+ /* Make sure the checkbox is not checked from session data. */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+ gtk_widget_set_sensitive (toggle, animation_supported);
+
+ g_object_bind_property (toggle, "active",
+ frame, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static int
+colors_to_bpp (gint colors)
+{
+ gint bpp;
+
+ if (colors <= 2)
+ bpp = 1;
+ else if (colors <= 4)
+ bpp = 2;
+ else if (colors <= 8)
+ bpp = 3;
+ else if (colors <= 16)
+ bpp = 4;
+ else if (colors <= 32)
+ bpp = 5;
+ else if (colors <= 64)
+ bpp = 6;
+ else if (colors <= 128)
+ bpp = 7;
+ else if (colors <= 256)
+ bpp = 8;
+ else
+ {
+ g_warning ("GIF: colors_to_bpp - Eep! too many colors: %d\n", colors);
+ return 8;
+ }
+
+ return bpp;
+}
+
+static int
+bpp_to_colors (gint bpp)
+{
+ gint colors;
+
+ if (bpp > 8)
+ {
+ g_warning ("GIF: bpp_to_colors - Eep! bpp==%d !\n", bpp);
+ return 256;
+ }
+
+ colors = 1 << bpp;
+
+ return colors;
+}
+
+
+
+static gint
+get_pixel (gint x,
+ gint y)
+{
+ return *(pixels + (rowstride * (long) y) + (long) x);
+}
+
+
+/*****************************************************************************
+ *
+ * GIFENCODE.C - GIF Image compression interface
+ *
+ * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
+ * BitsPerPixel, Red, Green, Blue, get_pixel )
+ *
+ *****************************************************************************/
+
+static gint Width, Height;
+static gint curx, cury;
+static glong CountDown;
+static gint Pass = 0;
+
+/*
+ * Bump the 'curx' and 'cury' to point to the next pixel
+ */
+static void
+bump_pixel (void)
+{
+ /*
+ * Bump the current X position
+ */
+ curx++;
+
+ /*
+ * If we are at the end of a scan line, set curx back to the beginning
+ * If we are interlaced, bump the cury to the appropriate spot,
+ * otherwise, just increment it.
+ */
+ if (curx == Width)
+ {
+ cur_progress++;
+
+ if ((cur_progress % 20) == 0)
+ gimp_progress_update ((gdouble) cur_progress / (gdouble) max_progress);
+
+ curx = 0;
+
+ if (! Interlace)
+ ++cury;
+ else
+ {
+ switch (Pass)
+ {
+
+ case 0:
+ cury += 8;
+ if (cury >= Height)
+ {
+ Pass++;
+ cury = 4;
+ }
+ break;
+
+ case 1:
+ cury += 8;
+ if (cury >= Height)
+ {
+ Pass++;
+ cury = 2;
+ }
+ break;
+
+ case 2:
+ cury += 4;
+ if (cury >= Height)
+ {
+ Pass++;
+ cury = 1;
+ }
+ break;
+
+ case 3:
+ cury += 2;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Return the next pixel from the image
+ */
+static gint
+gif_next_pixel (ifunptr getpixel)
+{
+ gint r;
+
+ if (CountDown == 0)
+ return EOF;
+
+ --CountDown;
+
+ r = (*getpixel) (curx, cury);
+
+ bump_pixel ();
+
+ return r;
+}
+
+/* public */
+
+static gboolean
+gif_encode_header (GOutputStream *output,
+ gboolean gif89,
+ gint GWidth,
+ gint GHeight,
+ gint Background,
+ gint BitsPerPixel,
+ gint Red[],
+ gint Green[],
+ gint Blue[],
+ ifunptr get_pixel,
+ GError **error)
+{
+ gint B;
+ gint RWidth, RHeight;
+ gint Resolution;
+ gint ColorMapSize;
+ gint i;
+
+ ColorMapSize = 1 << BitsPerPixel;
+
+ RWidth = Width = GWidth;
+ RHeight = Height = GHeight;
+
+ Resolution = BitsPerPixel;
+
+ /*
+ * Calculate number of bits we are expecting
+ */
+ CountDown = (long) Width *(long) Height;
+
+ /*
+ * Indicate which pass we are on (if interlace)
+ */
+ Pass = 0;
+
+ /*
+ * Set up the current x and y position
+ */
+ curx = cury = 0;
+
+ /*
+ * Write the Magic header
+ */
+ if (! put_string (output, gif89 ? "GIF89a" : "GIF87a", error))
+ return FALSE;
+
+ /*
+ * Write out the screen width and height
+ */
+ if (! put_word (output, RWidth, error) ||
+ ! put_word (output, RHeight, error))
+ return FALSE;
+
+ /*
+ * Indicate that there is a global color map
+ */
+ B = 0x80; /* Yes, there is a color map */
+
+ /*
+ * OR in the resolution
+ */
+ B |= (Resolution - 1) << 5;
+
+ /*
+ * OR in the Bits per Pixel
+ */
+ B |= (BitsPerPixel - 1);
+
+ /*
+ * Write it out
+ */
+ if (! put_byte (output, B, error))
+ return FALSE;
+
+ /*
+ * Write out the Background color
+ */
+ if (! put_byte (output, Background, error))
+ return FALSE;
+
+ /*
+ * Byte of 0's (future expansion)
+ */
+ if (! put_byte (output, 0, error))
+ return FALSE;
+
+ /*
+ * Write out the Global Color Map
+ */
+ for (i = 0; i < ColorMapSize; i++)
+ {
+ if (! put_byte (output, Red[i], error) ||
+ ! put_byte (output, Green[i], error) ||
+ ! put_byte (output, Blue[i], error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+gif_encode_graphic_control_ext (GOutputStream *output,
+ int Disposal,
+ int Delay89,
+ int NumFramesInImage,
+ int GWidth,
+ int GHeight,
+ int Transparent,
+ int BitsPerPixel,
+ ifunptr get_pixel,
+ GError **error)
+{
+ Width = GWidth;
+ Height = GHeight;
+
+ /*
+ * Calculate number of bits we are expecting
+ */
+ CountDown = (long) Width *(long) Height;
+
+ /*
+ * Indicate which pass we are on (if interlace)
+ */
+ Pass = 0;
+
+ /*
+ * Set up the current x and y position
+ */
+ curx = cury = 0;
+
+ /*
+ * Write out extension for transparent color index, if necessary.
+ */
+ if ( (Transparent >= 0) || (NumFramesInImage > 1) )
+ {
+ /* Extension Introducer - fixed. */
+ if (! put_byte (output, '!', error))
+ return FALSE;
+
+ /* Graphic Control Label - fixed. */
+ if (! put_byte (output, 0xf9, error))
+ return FALSE;
+
+ /* Block Size - fixed. */
+ if (! put_byte (output, 4, error))
+ return FALSE;
+
+ /* Packed Fields - XXXdddut (d=disposal, u=userInput, t=transFlag) */
+ /* s8421 */
+ if (! put_byte (output,
+ ((Transparent >= 0) ? 0x01 : 0x00) /* TRANSPARENCY */
+
+ /* DISPOSAL */
+ | ((NumFramesInImage > 1) ? (Disposal << 2) : 0x00 ),
+ /* 0x03 or 0x01 build frames cumulatively */
+ /* 0x02 clears frame before drawing */
+ /* 0x00 'don't care' */
+
+ error))
+ return FALSE;
+
+ if (! put_word (output, Delay89, error))
+ return FALSE;
+
+ if (! put_byte (output, Transparent, error) ||
+ ! put_byte (output, 0, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+gif_encode_image_data (GOutputStream *output,
+ int GWidth,
+ int GHeight,
+ int GInterlace,
+ int BitsPerPixel,
+ ifunptr get_pixel,
+ gint offset_x,
+ gint offset_y,
+ GError **error)
+{
+ gint LeftOfs, TopOfs;
+ gint InitCodeSize;
+
+ Interlace = GInterlace;
+
+ Width = GWidth;
+ Height = GHeight;
+ LeftOfs = (gint) offset_x;
+ TopOfs = (gint) offset_y;
+
+ /*
+ * Calculate number of bits we are expecting
+ */
+ CountDown = (long) Width * (long) Height;
+
+ /*
+ * Indicate which pass we are on (if interlace)
+ */
+ Pass = 0;
+
+ /*
+ * The initial code size
+ */
+ if (BitsPerPixel <= 1)
+ InitCodeSize = 2;
+ else
+ InitCodeSize = BitsPerPixel;
+
+ /*
+ * Set up the current x and y position
+ */
+ curx = cury = 0;
+
+ /*
+ * Write an Image separator
+ */
+ if (! put_byte (output, ',', error))
+ return FALSE;
+
+ /*
+ * Write the Image header
+ */
+
+ if (! put_word (output, LeftOfs, error) ||
+ ! put_word (output, TopOfs, error) ||
+ ! put_word (output, Width, error) ||
+ ! put_word (output, Height, error))
+ return FALSE;
+
+ /*
+ * Write out whether or not the image is interlaced
+ */
+ if (Interlace)
+ {
+ if (! put_byte (output, 0x40, error))
+ return FALSE;
+ }
+ else
+ {
+ if (! put_byte (output, 0x00, error))
+ return FALSE;
+ }
+
+ /*
+ * Write out the initial code size
+ */
+ if (! put_byte (output, InitCodeSize, error))
+ return FALSE;
+
+ /*
+ * Go and actually compress the data
+ */
+ if (! compress (output, InitCodeSize + 1, get_pixel, error))
+ return FALSE;
+
+ /*
+ * Write out a Zero-length packet (to end the series)
+ */
+ if (! put_byte (output, 0, error))
+ return FALSE;
+
+#if 0
+ /***************************/
+ Interlace = GInterlace;
+ Width = GWidth;
+ Height = GHeight;
+ LeftOfs = TopOfs = 0;
+
+ CountDown = (long) Width *(long) Height;
+ Pass = 0;
+ /*
+ * The initial code size
+ */
+ if (BitsPerPixel <= 1)
+ InitCodeSize = 2;
+ else
+ InitCodeSize = BitsPerPixel;
+ /*
+ * Set up the current x and y position
+ */
+ curx = cury = 0;
+#endif
+
+ return TRUE;
+}
+
+
+static gboolean
+gif_encode_close (GOutputStream *output,
+ GError **error)
+{
+ /*
+ * Write the GIF file terminator
+ */
+ if (! put_byte (output, ';', error))
+ return FALSE;
+
+ /*
+ * And close the file
+ */
+ return g_output_stream_close (output, NULL, error);
+}
+
+
+static gboolean
+gif_encode_loop_ext (GOutputStream *output,
+ guint num_loops,
+ GError **error)
+{
+ return (put_byte (output, 0x21, error) &&
+ put_byte (output, 0xff, error) &&
+ put_byte (output, 0x0b, error) &&
+ put_string (output, "NETSCAPE2.0", error) &&
+ put_byte (output, 0x03, error) &&
+ put_byte (output, 0x01, error) &&
+ put_word (output, num_loops, error) &&
+ put_byte (output, 0x00, error));
+
+ /* NOTE: num_loops == 0 means 'loop infinitely' */
+}
+
+
+static gboolean
+gif_encode_comment_ext (GOutputStream *output,
+ const gchar *comment,
+ GError **error)
+{
+ if (!comment || !*comment)
+ return TRUE;
+
+ if (strlen (comment) > 240)
+ {
+ g_printerr ("GIF: warning:"
+ "comment too large - comment block not written.\n");
+ return TRUE;
+ }
+
+ return (put_byte (output, 0x21, error) &&
+ put_byte (output, 0xfe, error) &&
+ put_byte (output, strlen (comment), error) &&
+ put_string (output, comment, error) &&
+ put_byte (output, 0x00, error));
+}
+
+
+/*
+ * Write stuff to the GIF file
+ */
+static gboolean
+put_byte (GOutputStream *output,
+ guchar b,
+ GError **error)
+{
+ return g_data_output_stream_put_byte (G_DATA_OUTPUT_STREAM (output),
+ b, NULL, error);
+}
+
+static gboolean
+put_word (GOutputStream *output,
+ gint w,
+ GError **error)
+{
+ return g_data_output_stream_put_int16 (G_DATA_OUTPUT_STREAM (output),
+ w, NULL, error);
+}
+
+static gboolean
+put_string (GOutputStream *output,
+ const gchar *s,
+ GError **error)
+{
+ return g_data_output_stream_put_string (G_DATA_OUTPUT_STREAM (output),
+ s, NULL, error);
+}
+
+
+/***************************************************************************
+ *
+ * GIFCOMPR.C - GIF Image compression routines
+ *
+ * Lempel-Ziv compression based on 'compress'. GIF modifications by
+ * David Rowley (mgardi@watdcsu.waterloo.edu)
+ *
+ ***************************************************************************/
+
+/*
+ * General DEFINEs
+ */
+
+#define GIF_BITS 12
+
+#define HSIZE 5003 /* 80% occupancy */
+
+/*
+ * GIF Image compression - modified 'compress'
+ *
+ * Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ *
+ */
+
+static gint n_bits; /* number of bits/code */
+static gint maxbits = GIF_BITS; /* user settable max # bits/code */
+static gint maxcode; /* maximum code, given n_bits */
+static gint maxmaxcode = (gint) 1 << GIF_BITS; /* should NEVER generate this code */
+#ifdef COMPATIBLE /* But wrong! */
+#define MAXCODE(Mn_bits) ((gint) 1 << (Mn_bits) - 1)
+#else /*COMPATIBLE */
+#define MAXCODE(Mn_bits) (((gint) 1 << (Mn_bits)) - 1)
+#endif /*COMPATIBLE */
+
+static glong htab[HSIZE];
+static gushort codetab[HSIZE];
+#define HashTabOf(i) htab[i]
+#define CodeTabOf(i) codetab[i]
+
+static const gint hsize = HSIZE; /* the original reason for this being
+ variable was "for dynamic table sizing",
+ but since it was never actually changed
+ I made it const --Adam. */
+
+static gint free_ent = 0; /* first unused entry */
+
+/*
+ * block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+static gint clear_flg = 0;
+
+static gint offset;
+static glong in_count = 1; /* length of input */
+static glong out_count = 0; /* # of codes output (for debugging) */
+
+/*
+ * compress stdin to stdout
+ *
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation. Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please direct
+ * questions about this implementation to ames!jaw.
+ */
+
+static gint g_init_bits;
+
+static gint ClearCode;
+static gint EOFCode;
+
+static gulong cur_accum;
+static gint cur_bits;
+
+static gulong masks[] =
+{
+ 0x0000, 0x0001, 0x0003, 0x0007,
+ 0x000F, 0x001F, 0x003F, 0x007F,
+ 0x00FF, 0x01FF, 0x03FF, 0x07FF,
+ 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
+ 0xFFFF
+};
+
+
+static gboolean
+compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error)
+{
+ if (FALSE)
+ return no_compress (output, init_bits, ReadValue, error);
+ else if (FALSE)
+ return rle_compress (output, init_bits, ReadValue, error);
+ else
+ return normal_compress (output, init_bits, ReadValue, error);
+}
+
+static gboolean
+no_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error)
+{
+ glong fcode;
+ gint i /* = 0 */ ;
+ gint c;
+ gint ent;
+ gint hsize_reg;
+ gint hshift;
+
+ /*
+ * Set up the globals: g_init_bits - initial number of bits
+ */
+ g_init_bits = init_bits;
+
+ cur_bits = 0;
+ cur_accum = 0;
+
+ /*
+ * Set up the necessary values
+ */
+ offset = 0;
+ out_count = 0;
+ clear_flg = 0;
+ in_count = 1;
+
+ ClearCode = (1 << (init_bits - 1));
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+
+ /* Had some problems here... should be okay now. --Adam */
+ n_bits = g_init_bits;
+ maxcode = MAXCODE (n_bits);
+
+
+ char_init();
+
+ ent = gif_next_pixel (ReadValue);
+
+ hshift = 0;
+ for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
+ ++hshift;
+ hshift = 8 - hshift; /* set hash code range bound */
+
+ hsize_reg = hsize;
+ cl_hash ((glong) hsize_reg); /* clear hash table */
+
+ if (! output_code (output, (gint) ClearCode, error))
+ return FALSE;
+
+ while ((c = gif_next_pixel (ReadValue)) != EOF)
+ {
+ ++in_count;
+
+ fcode = (long) (((long) c << maxbits) + ent);
+ i = (((gint) c << hshift) ^ ent); /* xor hashing */
+
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+ ent = c;
+
+ if (free_ent < maxmaxcode)
+ {
+ CodeTabOf (i) = free_ent++; /* code -> hashtable */
+ HashTabOf (i) = fcode;
+ }
+ else
+ {
+ if (! cl_block (output, error))
+ return FALSE;
+ }
+ }
+
+ /*
+ * Put out the final code.
+ */
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+
+ if (! output_code (output, (gint) EOFCode, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+rle_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error)
+{
+ glong fcode;
+ gint i /* = 0 */ ;
+ gint c, last;
+ gint ent;
+ gint disp;
+ gint hsize_reg;
+ gint hshift;
+
+ /*
+ * Set up the globals: g_init_bits - initial number of bits
+ */
+ g_init_bits = init_bits;
+
+ cur_bits = 0;
+ cur_accum = 0;
+
+ /*
+ * Set up the necessary values
+ */
+ offset = 0;
+ out_count = 0;
+ clear_flg = 0;
+ in_count = 1;
+
+ ClearCode = (1 << (init_bits - 1));
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+
+ /* Had some problems here... should be okay now. --Adam */
+ n_bits = g_init_bits;
+ maxcode = MAXCODE (n_bits);
+
+
+ char_init ();
+
+ last = ent = gif_next_pixel (ReadValue);
+
+ hshift = 0;
+ for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
+ ++hshift;
+ hshift = 8 - hshift; /* set hash code range bound */
+
+ hsize_reg = hsize;
+ cl_hash ((glong) hsize_reg); /* clear hash table */
+
+ if (! output_code (output, (gint) ClearCode, error))
+ return FALSE;
+
+
+ while ((c = gif_next_pixel (ReadValue)) != EOF)
+ {
+ ++in_count;
+
+ fcode = (long) (((long) c << maxbits) + ent);
+ i = (((gint) c << hshift) ^ ent); /* xor hashing */
+
+
+ if (last == c) {
+ if (HashTabOf (i) == fcode)
+ {
+ ent = CodeTabOf (i);
+ continue;
+ }
+ else if ((long) HashTabOf (i) < 0) /* empty slot */
+ goto nomatch;
+ disp = hsize_reg - i; /* secondary hash (after G. Knott) */
+ if (i == 0)
+ disp = 1;
+ probe:
+ if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (HashTabOf (i) == fcode)
+ {
+ ent = CodeTabOf (i);
+ continue;
+ }
+ if ((long) HashTabOf (i) > 0)
+ goto probe;
+ }
+ nomatch:
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+ last = ent = c;
+ if (free_ent < maxmaxcode)
+ {
+ CodeTabOf (i) = free_ent++; /* code -> hashtable */
+ HashTabOf (i) = fcode;
+ }
+ else
+ {
+ if (! cl_block (output, error))
+ return FALSE;
+ }
+ }
+
+ /*
+ * Put out the final code.
+ */
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+
+ if (! output_code (output, (gint) EOFCode, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+normal_compress (GOutputStream *output,
+ gint init_bits,
+ ifunptr ReadValue,
+ GError **error)
+{
+ glong fcode;
+ gint i /* = 0 */ ;
+ gint c;
+ gint ent;
+ gint disp;
+ gint hsize_reg;
+ gint hshift;
+
+ /*
+ * Set up the globals: g_init_bits - initial number of bits
+ */
+ g_init_bits = init_bits;
+
+ cur_bits = 0;
+ cur_accum = 0;
+
+ /*
+ * Set up the necessary values
+ */
+ offset = 0;
+ out_count = 0;
+ clear_flg = 0;
+ in_count = 1;
+
+ ClearCode = (1 << (init_bits - 1));
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+
+ /* Had some problems here... should be okay now. --Adam */
+ n_bits = g_init_bits;
+ maxcode = MAXCODE (n_bits);
+
+
+ char_init();
+
+ ent = gif_next_pixel (ReadValue);
+
+ hshift = 0;
+ for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
+ ++hshift;
+ hshift = 8 - hshift; /* set hash code range bound */
+
+ hsize_reg = hsize;
+ cl_hash ((glong) hsize_reg); /* clear hash table */
+
+ if (! output_code (output, (gint) ClearCode, error))
+ return FALSE;
+
+
+ while ((c = gif_next_pixel (ReadValue)) != EOF)
+ {
+ ++in_count;
+
+ fcode = (long) (((long) c << maxbits) + ent);
+ i = (((gint) c << hshift) ^ ent); /* xor hashing */
+
+ if (HashTabOf (i) == fcode)
+ {
+ ent = CodeTabOf (i);
+ continue;
+ }
+ else if ((long) HashTabOf (i) < 0) /* empty slot */
+ goto nomatch;
+ disp = hsize_reg - i; /* secondary hash (after G. Knott) */
+ if (i == 0)
+ disp = 1;
+ probe:
+ if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (HashTabOf (i) == fcode)
+ {
+ ent = CodeTabOf (i);
+ continue;
+ }
+ if ((long) HashTabOf (i) > 0)
+ goto probe;
+ nomatch:
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+ ent = c;
+ if (free_ent < maxmaxcode)
+ {
+ CodeTabOf (i) = free_ent++; /* code -> hashtable */
+ HashTabOf (i) = fcode;
+ }
+ else
+ {
+ if (! cl_block (output, error))
+ return FALSE;
+ }
+ }
+
+ /*
+ * Put out the final code.
+ */
+ if (! output_code (output, (gint) ent, error))
+ return FALSE;
+
+ ++out_count;
+
+ if (! output_code (output, (gint) EOFCode, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*****************************************************************
+ * TAG( output )
+ *
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a GIF_BITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+static gboolean
+output_code (GOutputStream *output,
+ gint code,
+ GError **error)
+{
+ cur_accum &= masks[cur_bits];
+
+ if (cur_bits > 0)
+ cur_accum |= ((long) code << cur_bits);
+ else
+ cur_accum = code;
+
+ cur_bits += n_bits;
+
+ while (cur_bits >= 8)
+ {
+ if (! char_out (output, (guchar) (cur_accum & 0xff), error))
+ return FALSE;
+
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ /*
+ * If the next entry is going to be too big for the code size,
+ * then increase it, if possible.
+ */
+ if (free_ent > maxcode || clear_flg)
+ {
+ if (clear_flg)
+ {
+ maxcode = MAXCODE (n_bits = g_init_bits);
+ clear_flg = 0;
+ }
+ else
+ {
+ ++n_bits;
+ if (n_bits == maxbits)
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE (n_bits);
+ }
+ }
+
+ if (code == EOFCode)
+ {
+ /*
+ * At EOF, write the rest of the buffer.
+ */
+ while (cur_bits > 0)
+ {
+ if (! char_out (output, (guchar) (cur_accum & 0xff), error))
+ return FALSE;
+
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ if (! char_flush (output, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Clear out the hash table
+ */
+static gboolean
+cl_block (GOutputStream *output,
+ GError **error) /* table clear for block compress */
+{
+ cl_hash ((glong) hsize);
+ free_ent = ClearCode + 2;
+ clear_flg = 1;
+
+ return output_code (output, (gint) ClearCode, error);
+}
+
+static void
+cl_hash (glong hsize) /* reset code table */
+{
+ glong *htab_p = htab + hsize;
+
+ long i;
+ long m1 = -1;
+
+ i = hsize - 16;
+ do
+ { /* might use Sys V memset(3) here */
+ *(htab_p - 16) = m1;
+ *(htab_p - 15) = m1;
+ *(htab_p - 14) = m1;
+ *(htab_p - 13) = m1;
+ *(htab_p - 12) = m1;
+ *(htab_p - 11) = m1;
+ *(htab_p - 10) = m1;
+ *(htab_p - 9) = m1;
+ *(htab_p - 8) = m1;
+ *(htab_p - 7) = m1;
+ *(htab_p - 6) = m1;
+ *(htab_p - 5) = m1;
+ *(htab_p - 4) = m1;
+ *(htab_p - 3) = m1;
+ *(htab_p - 2) = m1;
+ *(htab_p - 1) = m1;
+ htab_p -= 16;
+ }
+ while ((i -= 16) >= 0);
+
+ for (i += 16; i > 0; --i)
+ *--htab_p = m1;
+}
+
+
+/******************************************************************************
+ * GIF Specific routines
+ ******************************************************************************/
+
+/*
+ * Number of characters so far in this 'packet'
+ */
+static int a_count;
+
+/*
+ * Set up the 'byte output' routine
+ */
+static void
+char_init (void)
+{
+ a_count = 0;
+}
+
+/*
+ * Define the storage for the packet accumulator
+ */
+static char accum[256];
+
+/*
+ * Add a character to the end of the current packet, and if it is 254
+ * characters, flush the packet to disk.
+ */
+static gboolean
+char_out (GOutputStream *output,
+ gint c,
+ GError **error)
+{
+ accum[a_count++] = c;
+
+ if (a_count >= 254)
+ return char_flush (output, error);
+
+ return TRUE;
+}
+
+/*
+ * Flush the packet to disk, and reset the accumulator
+ */
+static gboolean
+char_flush (GOutputStream *output,
+ GError **error)
+{
+ if (a_count > 0)
+ {
+ if (! put_byte (output, a_count, error))
+ return FALSE;
+
+ if (! g_output_stream_write_all (output, accum, a_count,
+ NULL, NULL, error))
+ return FALSE;
+
+ a_count = 0;
+ }
+
+ return TRUE;
+}
+
+
+/* Save interface functions */
+
+static void
+comment_entry_callback (GtkTextBuffer *buffer)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+ gchar *text;
+
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
+
+#define MAX_COMMENT 240
+
+ if (strlen (text) > MAX_COMMENT)
+ {
+ /* translators: the %d is *always* 240 here */
+ g_message (_("The default comment is limited to %d characters."),
+ MAX_COMMENT);
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, MAX_COMMENT - 1);
+ gtk_text_buffer_get_end_iter (buffer, &end_iter);
+
+ /* this calls us recursivaly, but in the else branch
+ */
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+ else
+ {
+ g_free (globalcomment);
+ globalcomment = g_strdup (text);
+ comment_was_edited = TRUE;
+ }
+
+ g_free (text);
+}
diff --git a/plug-ins/common/file-gih.c b/plug-ins/common/file-gih.c
new file mode 100644
index 0000000..6e44f9a
--- /dev/null
+++ b/plug-ins/common/file-gih.c
@@ -0,0 +1,819 @@
+/* Plug-in to load and export .gih (GIMP Brush Pipe) files.
+ *
+ * Copyright (C) 1999 Tor Lillqvist
+ * Copyright (C) 2000 Jens Lautenbacher, Sven Neumann
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+ /* Example of how to call file_gih_save from script-fu:
+
+ (let ((ranks (cons-array 1 'byte)))
+ (aset ranks 0 12)
+ (file-gih-save 1
+ img
+ drawable
+ "foo.gih"
+ "foo.gih"
+ 100
+ "test brush"
+ 125
+ 125
+ 3
+ 4
+ 1
+ ranks
+ 1
+ '("random")))
+ */
+
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include <libgimpbase/gimpparasiteio.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-gih-save"
+#define PLUG_IN_BINARY "file-gih"
+#define PLUG_IN_ROLE "gimp-file-gih"
+
+
+/* Parameters applicable each time we export a gih, exported in the
+ * main gimp application between invocations of this plug-in.
+ */
+typedef struct
+{
+ gchar description[256];
+ gint spacing;
+} BrushInfo;
+
+typedef struct
+{
+ GimpOrientationType orientation;
+ gint32 image;
+ gint32 toplayer;
+ gint nguides;
+ gint32 *guides;
+ gint *value;
+ GtkWidget *count_label; /* Corresponding count adjustment, */
+ gint *count; /* cols or rows */
+ gint *other_count; /* and the other one */
+ GtkAdjustment *ncells;
+ GtkAdjustment *rank0;
+ GtkWidget *warning_label;
+ GtkWidget *rank_entry[GIMP_PIXPIPE_MAXDIM];
+ GtkWidget *mode_entry[GIMP_PIXPIPE_MAXDIM];
+} SizeAdjustmentData;
+
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean gih_save_dialog (gint32 image_ID);
+
+
+/* private variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static BrushInfo info =
+{
+ "GIMP Brush Pipe",
+ 20
+};
+
+static gint num_layers = 0;
+static GimpPixPipeParams gihparams = { 0, };
+
+static const gchar * const selection_modes[] = { "incremental",
+ "angular",
+ "random",
+ "velocity",
+ "pressure",
+ "xtilt",
+ "ytilt" };
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef gih_save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "uri", "The URI of the file to export the brush pipe in" },
+ { GIMP_PDB_STRING, "raw-uri", "The URI of the file to export the brush pipe in" },
+ { GIMP_PDB_INT32, "spacing", "Spacing of the brush" },
+ { GIMP_PDB_STRING, "description", "Short description of the brush pipe" },
+ { GIMP_PDB_INT32, "cell-width", "Width of the brush cells" },
+ { GIMP_PDB_INT32, "cell-height", "Width of the brush cells" },
+ { GIMP_PDB_INT8, "display-cols", "Display column number" },
+ { GIMP_PDB_INT8, "display-rows", "Display row number" },
+ { GIMP_PDB_INT32, "dimension", "Dimension of the brush pipe" },
+ /* The number of rank and sel args depend on the dimension */
+ { GIMP_PDB_INT8ARRAY, "rank", "Ranks of the dimensions" },
+ { GIMP_PDB_INT32, "dimension", "Dimension (again)" },
+ { GIMP_PDB_STRINGARRAY, "sel", "Selection modes" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "exports images in GIMP brush pipe format",
+ "This plug-in exports an image in the GIMP brush pipe "
+ "format. For a colored brush pipe, RGBA layers are "
+ "used, otherwise the layers should be grayscale "
+ "masks. The image can be multi-layered, and "
+ "additionally the layers can be divided into a "
+ "rectangular array of brushes.",
+ "Tor Lillqvist",
+ "Tor Lillqvist",
+ "1999",
+ N_("GIMP brush (animated)"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (gih_save_args), 0,
+ gih_save_args, NULL);
+
+ gimp_plugin_icon_register (SAVE_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_BRUSH);
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-gimp-gih");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "gih", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N();
+
+ run_mode = param[0].data.d_int32;
+
+ *return_vals = values;
+ *nreturn_vals = 1;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GFile *file;
+ GimpParasite *parasite;
+ gint32 orig_image_ID;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+ file = g_file_new_for_uri (param[3].data.d_string);
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "GIH",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &info);
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-brush-pipe-name");
+ if (parasite)
+ {
+ strncpy (info.description,
+ gimp_parasite_data (parasite),
+ MIN (sizeof (info.description),
+ gimp_parasite_data_size (parasite)));
+ info.description[sizeof (info.description) - 1] = '\0';
+
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ gchar *name = g_path_get_basename (gimp_file_get_utf8_name (file));
+
+ if (g_str_has_suffix (name, ".gih"))
+ name[strlen (name) - 4] = '\0';
+
+ if (strlen (name))
+ {
+ strncpy (info.description, name, sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = '\0';
+ }
+
+ g_free (name);
+ }
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-brush-pipe-spacing");
+ if (parasite)
+ {
+ info.spacing = atoi (gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ g_free (gimp_image_get_layers (image_ID, &num_layers));
+
+ gimp_pixpipe_params_init (&gihparams);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gihparams.ncells = (num_layers * gihparams.rows * gihparams.cols);
+
+ gihparams.cellwidth = gimp_image_width (image_ID) / gihparams.cols;
+ gihparams.cellheight = gimp_image_height (image_ID) / gihparams.rows;
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-brush-pipe-parameters");
+ if (parasite)
+ {
+ gimp_pixpipe_params_parse (gimp_parasite_data (parasite),
+ &gihparams);
+ gimp_parasite_free (parasite);
+ }
+
+ /* Force default rank to same as number of cells if there is
+ * just one dim
+ */
+ if (gihparams.dim == 1)
+ gihparams.rank[0] = gihparams.ncells;
+
+ if (! gih_save_dialog (image_ID))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 15)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ info.spacing = param[5].data.d_int32;
+ strncpy (info.description, param[6].data.d_string,
+ sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = '\0';
+
+ gihparams.cellwidth = param[7].data.d_int32;
+ gihparams.cellheight = param[8].data.d_int32;
+ gihparams.cols = param[9].data.d_int8;
+ gihparams.rows = param[10].data.d_int8;
+ gihparams.dim = param[11].data.d_int32;
+ gihparams.ncells = 1;
+
+ if (param[13].data.d_int32 != gihparams.dim)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ for (i = 0; i < gihparams.dim; i++)
+ {
+ gihparams.rank[i] = param[12].data.d_int8array[i];
+ gihparams.selection[i] = g_strdup (param[14].data.d_stringarray[i]);
+ gihparams.ncells *= gihparams.rank[i];
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-brush-pipe-parameters");
+ if (parasite)
+ {
+ gimp_pixpipe_params_parse (gimp_parasite_data (parasite),
+ &gihparams);
+ gimp_parasite_free (parasite);
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpParam *save_retvals;
+ gint n_save_retvals;
+ gchar spacing[8];
+ gchar *paramstring;
+
+ paramstring = gimp_pixpipe_params_build (&gihparams);
+
+ save_retvals =
+ gimp_run_procedure ("file-gih-save-internal",
+ &n_save_retvals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, param[3].data.d_string,
+ GIMP_PDB_STRING, param[4].data.d_string,
+ GIMP_PDB_INT32, info.spacing,
+ GIMP_PDB_STRING, info.description,
+ GIMP_PDB_STRING, paramstring,
+ GIMP_PDB_END);
+
+ if (save_retvals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_set_data (SAVE_PROC, &info, sizeof (info));
+
+ parasite = gimp_parasite_new ("gimp-brush-pipe-name",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (info.description) + 1,
+ info.description);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ g_snprintf (spacing, sizeof (spacing), "%d",
+ info.spacing);
+
+ parasite = gimp_parasite_new ("gimp-brush-pipe-spacing",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (spacing) + 1, spacing);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ parasite = gimp_parasite_new ("gimp-brush-pipe-parameters",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (paramstring) + 1,
+ paramstring);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ g_set_error (&error, 0, 0,
+ "Running procedure 'file-gih-save-internal' "
+ "failed: %s",
+ gimp_get_pdb_error ());
+
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_free (paramstring);
+ }
+
+ gimp_pixpipe_params_free (&gihparams);
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* save routines */
+
+static void
+size_adjustment_callback (GtkWidget *widget,
+ SizeAdjustmentData *adj)
+{
+ gint i;
+ gint size;
+ gint newn;
+ gchar buf[10];
+
+ for (i = 0; i < adj->nguides; i++)
+ gimp_image_delete_guide (adj->image, adj->guides[i]);
+
+ g_free (adj->guides);
+ adj->guides = NULL;
+ gimp_displays_flush ();
+
+ *(adj->value) = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
+
+ if (adj->orientation == GIMP_ORIENTATION_VERTICAL)
+ {
+ size = gimp_image_width (adj->image);
+ newn = size / *(adj->value);
+ adj->nguides = newn - 1;
+ adj->guides = g_new (gint32, adj->nguides);
+ for (i = 0; i < adj->nguides; i++)
+ adj->guides[i] = gimp_image_add_vguide (adj->image,
+ *(adj->value) * (i+1));
+ }
+ else
+ {
+ size = gimp_image_height (adj->image);
+ newn = size / *(adj->value);
+ adj->nguides = newn - 1;
+ adj->guides = g_new (gint32, adj->nguides);
+ for (i = 0; i < adj->nguides; i++)
+ adj->guides[i] = gimp_image_add_hguide (adj->image,
+ *(adj->value) * (i+1));
+ }
+ gimp_displays_flush ();
+ g_snprintf (buf, sizeof (buf), "%2d", newn);
+ gtk_label_set_text (GTK_LABEL (adj->count_label), buf);
+
+ *(adj->count) = newn;
+
+ gtk_widget_set_visible (GTK_WIDGET (adj->warning_label),
+ newn * *(adj->value) != size);
+
+ if (adj->ncells != NULL)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->ncells),
+ *(adj->other_count) * *(adj->count));
+ if (adj->rank0 != NULL)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (adj->rank0),
+ *(adj->other_count) * *(adj->count));
+}
+
+static void
+entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (data == info.description)
+ {
+ strncpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)),
+ sizeof (info.description));
+ info.description[sizeof (info.description) - 1] = 0;
+ }
+}
+
+static void
+cb_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint index;
+
+ index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+
+ *((const gchar **) data) = selection_modes [index];
+}
+
+static void
+dim_callback (GtkAdjustment *adjustment,
+ SizeAdjustmentData *data)
+{
+ gint i;
+
+ gihparams.dim = RINT (gtk_adjustment_get_value (adjustment));
+
+ for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
+ {
+ gtk_widget_set_sensitive (data->rank_entry[i], i < gihparams.dim);
+ gtk_widget_set_sensitive (data->mode_entry[i], i < gihparams.dim);
+ }
+}
+
+static gboolean
+gih_save_dialog (gint32 image_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *dimtable;
+ GtkWidget *label;
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+ GtkWidget *entry;
+ GtkWidget *box;
+ GtkWidget *cb;
+ gint i;
+ gchar buffer[100];
+ SizeAdjustmentData cellw_adjust;
+ SizeAdjustmentData cellh_adjust;
+ gint32 *layer_ID;
+ gint32 nlayers;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("Brush Pipe"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* The main table */
+ table = gtk_table_new (8, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Description: ___________
+ */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), info.description);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Description:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_callback),
+ info.description);
+
+ /*
+ * Spacing: __
+ */
+ adjustment = (GtkAdjustment *) gtk_adjustment_new (info.spacing,
+ 1, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Spacing (percent):"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &info.spacing);
+
+ /*
+ * Cell size: __ x __ pixels
+ */
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gihparams.cellwidth,
+ 2, gimp_image_width (image_ID), 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ layer_ID = gimp_image_get_layers (image_ID, &nlayers);
+ cellw_adjust.orientation = GIMP_ORIENTATION_VERTICAL;
+ cellw_adjust.image = image_ID;
+ cellw_adjust.toplayer = layer_ID[nlayers-1];
+ cellw_adjust.nguides = 0;
+ cellw_adjust.guides = NULL;
+ cellw_adjust.value = &gihparams.cellwidth;
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (size_adjustment_callback),
+ &cellw_adjust);
+
+ label = gtk_label_new ("x");
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gihparams.cellheight,
+ 2, gimp_image_height (image_ID), 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ cellh_adjust.orientation = GIMP_ORIENTATION_HORIZONTAL;
+ cellh_adjust.image = image_ID;
+ cellh_adjust.toplayer = layer_ID[nlayers-1];
+ cellh_adjust.nguides = 0;
+ cellh_adjust.guides = NULL;
+ cellh_adjust.value = &gihparams.cellheight;
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (size_adjustment_callback),
+ &cellh_adjust);
+
+ label = gtk_label_new ( _("Pixels"));
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Ce_ll size:"), 0.0, 0.5,
+ box, 1, FALSE);
+
+ g_free (layer_ID);
+
+ /*
+ * Number of cells: ___
+ */
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gihparams.ncells, 1, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Number of cells:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gihparams.ncells);
+
+ if (gihparams.dim == 1)
+ cellw_adjust.ncells = cellh_adjust.ncells = adjustment;
+ else
+ cellw_adjust.ncells = cellh_adjust.ncells = NULL;
+
+ /*
+ * Display as: __ rows x __ cols
+ */
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+
+ g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.rows);
+ label = gtk_label_new (buffer);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ cellh_adjust.count_label = label;
+ cellh_adjust.count = &gihparams.rows;
+ cellh_adjust.other_count = &gihparams.cols;
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_(" Rows of "));
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ g_snprintf (buffer, sizeof (buffer), "%2d", gihparams.cols);
+ label = gtk_label_new (buffer);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ cellw_adjust.count_label = label;
+ cellw_adjust.count = &gihparams.cols;
+ cellw_adjust.other_count = &gihparams.rows;
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_(" Columns on each layer"));
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_(" (Width Mismatch!) "));
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ cellw_adjust.warning_label = label;
+
+ label = gtk_label_new (_(" (Height Mismatch!) "));
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ cellh_adjust.warning_label = label;
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("Display as:"), 0.0, 0.5,
+ box, 1, FALSE);
+
+ /*
+ * Dimension: ___
+ */
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gihparams.dim, 1, GIMP_PIXPIPE_MAXDIM, 1, 1, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 5,
+ _("Di_mension:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (dim_callback),
+ &cellw_adjust);
+
+ /*
+ * Ranks / Selection: ______ ______ ______ ______ ______
+ */
+
+ dimtable = gtk_table_new (2, GIMP_PIXPIPE_MAXDIM, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (dimtable), 4);
+ for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
+ {
+ gint j;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gihparams.rank[i], 1, 100, 1, 1, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_table_attach (GTK_TABLE (dimtable), spinbutton, 0, 1, i, i + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ gtk_widget_show (spinbutton);
+
+ if (i >= gihparams.dim)
+ gtk_widget_set_sensitive (spinbutton, FALSE);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gihparams.rank[i]);
+
+ cellw_adjust.rank_entry[i] = cellh_adjust.rank_entry[i] = spinbutton;
+
+ if (i == 0)
+ {
+ if (gihparams.dim == 1)
+ cellw_adjust.rank0 = cellh_adjust.rank0 = adjustment;
+ else
+ cellw_adjust.rank0 = cellh_adjust.rank0 = NULL;
+ }
+
+ cb = gtk_combo_box_text_new ();
+
+ for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cb),
+ selection_modes[j]);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (cb), 2); /* random */
+
+ if (gihparams.selection[i])
+ for (j = 0; j < G_N_ELEMENTS (selection_modes); j++)
+ if (!strcmp (gihparams.selection[i], selection_modes[j]))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (cb), j);
+ break;
+ }
+
+ gtk_table_attach (GTK_TABLE (dimtable), cb, 1, 2, i, i + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ gtk_widget_show (cb);
+
+ if (i >= gihparams.dim)
+ gtk_widget_set_sensitive (cb, FALSE);
+
+ g_signal_connect (GTK_COMBO_BOX (cb), "changed",
+ G_CALLBACK (cb_callback),
+ &gihparams.selection[i]);
+
+ cellw_adjust.mode_entry[i] = cellh_adjust.mode_entry[i] = cb;
+
+
+ }
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
+ _("Ranks:"), 0.0, 0.0,
+ dimtable, 1, FALSE);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ gint i;
+
+ for (i = 0; i < GIMP_PIXPIPE_MAXDIM; i++)
+ gihparams.selection[i] = g_strdup (gihparams.selection[i]);
+
+ /* Fix up bogus values */
+ gihparams.ncells = MIN (gihparams.ncells,
+ num_layers * gihparams.rows * gihparams.cols);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ for (i = 0; i < cellw_adjust.nguides; i++)
+ gimp_image_delete_guide (image_ID, cellw_adjust.guides[i]);
+ for (i = 0; i < cellh_adjust.nguides; i++)
+ gimp_image_delete_guide (image_ID, cellh_adjust.guides[i]);
+
+ return run;
+}
diff --git a/plug-ins/common/file-glob.c b/plug-ins/common/file-glob.c
new file mode 100644
index 0000000..6ce0e00
--- /dev/null
+++ b/plug-ins/common/file-glob.c
@@ -0,0 +1,450 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* The idea is taken from a plug-in written by George Hartz; the code isn't.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "libgimp/gimp.h"
+
+
+#define PLUG_IN_PROC "file-glob"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean glob_match (const gchar *pattern,
+ gboolean filename_encoding,
+ gint *num_matches,
+ gchar ***matches);
+static gboolean glob_fnmatch (const gchar *pattern,
+ const gchar *string);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run,
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef glob_args[] =
+ {
+ { GIMP_PDB_STRING, "pattern" , "The glob pattern (in UTF-8 encoding)" },
+ { GIMP_PDB_INT32, "encoding", "Encoding of the returned names: "
+ "{ UTF-8 (0), filename encoding (1) }" }
+ };
+
+ static const GimpParamDef glob_return_vals[] =
+ {
+ { GIMP_PDB_INT32, "num-files", "The number of returned names" },
+ { GIMP_PDB_STRINGARRAY, "files", "The list of matching names" }
+ };
+
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ "Returns a list of matching filenames",
+ "This can be useful in scripts and other plug-ins "
+ "(e.g., batch-conversion). See the glob(7) manpage "
+ "for more info. Note however that this isn't a "
+ "full-featured glob implementation. It only handles "
+ "simple patterns like \"/home/foo/bar/*.jpg\".",
+ "Sven Neumann",
+ "Sven Neumann",
+ "2004",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (glob_args),
+ G_N_ELEMENTS (glob_return_vals),
+ glob_args,
+ glob_return_vals);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ if (strcmp (name, PLUG_IN_PROC) == 0 && nparams >= 1)
+ {
+ gchar **matches;
+ gint num_matches;
+ gboolean filename_encoding = FALSE;
+
+ if (nparams > 1)
+ filename_encoding = param[0].data.d_int32 ? TRUE : FALSE;
+
+ if (! glob_match (param[0].data.d_string, filename_encoding,
+ &num_matches, &matches))
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ return;
+ }
+
+ *nreturn_vals = 3;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = num_matches;
+
+ values[2].type = GIMP_PDB_STRINGARRAY;
+ values[2].data.d_stringarray = matches;
+ }
+}
+
+static gboolean
+glob_match (const gchar *pattern,
+ gboolean filename_encoding,
+ gint *num_matches,
+ gchar ***matches)
+{
+ GDir *dir;
+ GPtrArray *array;
+ const gchar *filename;
+ gchar *dirname;
+ gchar *tmp;
+
+ g_return_val_if_fail (pattern != NULL, FALSE);
+ g_return_val_if_fail (num_matches != NULL, FALSE);
+ g_return_val_if_fail (matches != NULL, FALSE);
+
+ *num_matches = 0;
+ *matches = NULL;
+
+ /* This is not a complete glob() implementation but rather a very
+ * simplistic approach. However it works for the most common use
+ * case and is better than nothing.
+ */
+
+ tmp = g_filename_from_utf8 (pattern, -1, NULL, NULL, NULL);
+ if (! tmp)
+ return FALSE;
+
+ dirname = g_path_get_dirname (tmp);
+
+ dir = g_dir_open (dirname, 0, NULL);
+ g_free (tmp);
+
+ if (! dir)
+ {
+ g_free (dirname);
+ return TRUE;
+ }
+
+ /* check if the pattern has a directory part at all */
+ tmp = g_path_get_basename (pattern);
+ if (strcmp (pattern, tmp) == 0)
+ {
+ g_free (dirname);
+ dirname = NULL;
+ }
+ g_free (tmp);
+
+ array = g_ptr_array_new ();
+
+ for (filename = g_dir_read_name (dir);
+ filename;
+ filename = g_dir_read_name (dir))
+ {
+ gchar *path;
+ gchar *name;
+
+ if (dirname)
+ path = g_build_filename (dirname, filename, NULL);
+ else
+ path = g_strdup (filename);
+
+ name = g_filename_to_utf8 (path, -1, NULL, NULL, NULL);
+
+ if (name && glob_fnmatch (pattern, name))
+ {
+ if (filename_encoding)
+ {
+ g_ptr_array_add (array, path);
+ path = NULL;
+ }
+ else
+ {
+ g_ptr_array_add (array, name);
+ name = NULL;
+ }
+ }
+
+ g_free (path);
+ g_free (name);
+ }
+
+ g_dir_close (dir);
+ g_free (dirname);
+
+ *num_matches = array->len;
+ *matches = (gchar **) g_ptr_array_free (array, FALSE);
+
+ return TRUE;
+}
+
+
+/*
+ * The following code is borrowed from GTK+.
+ *
+ * GTK+ used to use a old version of GNU fnmatch() that was buggy
+ * in various ways and didn't handle UTF-8. The following is
+ * converted to UTF-8. To simplify the process of making it
+ * correct, this is special-cased to the combinations of flags
+ * that gtkfilesel.c uses.
+ *
+ * FNM_FILE_NAME - always set
+ * FNM_LEADING_DIR - never set
+ * FNM_NOESCAPE - set only on windows
+ * FNM_CASEFOLD - set only on windows
+ */
+
+/* We need to make sure that all constants are defined
+ * to properly compile this file
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+static gunichar
+get_char (const char **str)
+{
+ gunichar c = g_utf8_get_char (*str);
+ *str = g_utf8_next_char (*str);
+
+#ifdef G_PLATFORM_WIN32
+ c = g_unichar_tolower (c);
+#endif
+
+ return c;
+}
+
+#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#define DO_ESCAPE 0
+#else
+#define DO_ESCAPE 1
+#endif
+
+static gunichar
+get_unescaped_char (const char **str,
+ gboolean *was_escaped)
+{
+ gunichar c = get_char (str);
+
+ *was_escaped = DO_ESCAPE && c == '\\';
+ if (*was_escaped)
+ c = get_char (str);
+
+ return c;
+}
+
+/* Match STRING against the filename pattern PATTERN,
+ * returning TRUE if it matches, FALSE otherwise.
+ */
+static gboolean
+fnmatch_intern (const gchar *pattern,
+ const gchar *string,
+ gboolean component_start,
+ gboolean no_leading_period)
+{
+ const char *p = pattern, *n = string;
+
+ while (*p)
+ {
+ const char *last_n = n;
+
+ gunichar c = get_char (&p);
+ gunichar nc = get_char (&n);
+
+ switch (c)
+ {
+ case '?':
+ if (nc == '\0')
+ return FALSE;
+ else if (nc == G_DIR_SEPARATOR)
+ return FALSE;
+ else if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
+ break;
+ case '\\':
+ if (DO_ESCAPE)
+ c = get_char (&p);
+ if (nc != c)
+ return FALSE;
+ break;
+ case '*':
+ if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
+
+ {
+ const char *last_p = p;
+
+ for (last_p = p, c = get_char (&p);
+ c == '?' || c == '*';
+ last_p = p, c = get_char (&p))
+ {
+ if (c == '?')
+ {
+ if (nc == '\0')
+ return FALSE;
+ else if (nc == G_DIR_SEPARATOR)
+ return FALSE;
+ else
+ {
+ last_n = n; nc = get_char (&n);
+ }
+ }
+ }
+
+ /* If the pattern ends with wildcards, we have a
+ * guaranteed match unless there is a dir separator
+ * in the remainder of the string.
+ */
+ if (c == '\0')
+ {
+ if (strchr (last_n, G_DIR_SEPARATOR) != NULL)
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+ if (DO_ESCAPE && c == '\\')
+ c = get_char (&p);
+
+ for (p = last_p; nc != '\0';)
+ {
+ if ((c == '[' || nc == c) &&
+ fnmatch_intern (p, last_n,
+ component_start, no_leading_period))
+ return TRUE;
+
+ component_start = (nc == G_DIR_SEPARATOR);
+ last_n = n;
+ nc = get_char (&n);
+ }
+
+ return FALSE;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ gboolean not;
+ gboolean was_escaped;
+
+ if (nc == '\0' || nc == G_DIR_SEPARATOR)
+ return FALSE;
+
+ if (nc == '.' && component_start && no_leading_period)
+ return FALSE;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = get_unescaped_char (&p, &was_escaped);
+ for (;;)
+ {
+ register gunichar cstart = c, cend = c;
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FALSE;
+
+ c = get_unescaped_char (&p, &was_escaped);
+
+ if (!was_escaped && c == '-' && *p != ']')
+ {
+ cend = get_unescaped_char (&p, &was_escaped);
+ if (cend == '\0')
+ return FALSE;
+
+ c = get_char (&p);
+ }
+
+ if (nc >= cstart && nc <= cend)
+ goto matched;
+
+ if (!was_escaped && c == ']')
+ break;
+ }
+ if (!not)
+ return FALSE;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ /* XXX 1003.2d11 is unclear if was_escaped is right. */
+ while (was_escaped || c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FALSE;
+
+ c = get_unescaped_char (&p, &was_escaped);
+ }
+ if (not)
+ return FALSE;
+ }
+ break;
+
+ default:
+ if (c != nc)
+ return FALSE;
+ }
+
+ component_start = (nc == G_DIR_SEPARATOR);
+ }
+
+ if (*n == '\0')
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+glob_fnmatch (const gchar *pattern,
+ const gchar *string)
+{
+ return fnmatch_intern (pattern, string, TRUE, TRUE);
+}
diff --git a/plug-ins/common/file-header.c b/plug-ins/common/file-header.c
new file mode 100644
index 0000000..50e5e37
--- /dev/null
+++ b/plug-ins/common/file-header.c
@@ -0,0 +1,439 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-header-save"
+#define PLUG_IN_BINARY "file-header"
+#define PLUG_IN_ROLE "gimp-file-header"
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "saves files as C unsigned character array",
+ "FIXME: write help",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("C source code header"),
+ "INDEXED, RGB",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "text/x-chdr");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "h", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "Header",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (! save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ const Babl *format;
+ GimpImageType drawable_type;
+ GOutputStream *output;
+ gint x, y, b, c;
+ const gchar *backslash = "\\\\";
+ const gchar *quote = "\\\"";
+ const gchar *newline = "\"\n\t\"";
+ gchar buf[4];
+ guchar *d = NULL;
+ guchar *data = NULL;
+ guchar *cmap;
+ GCancellable *cancellable;
+ gint colors;
+ gint width;
+ gint height;
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ if (! print (output, error,
+ "/* GIMP header image file format (%s): %s */\n\n",
+ GIMP_RGB_IMAGE == drawable_type ? "RGB" : "INDEXED",
+ gimp_file_get_utf8_name (file)) ||
+ ! print (output, error,
+ "static unsigned int width = %d;\n", width) ||
+ ! print (output, error,
+ "static unsigned int height = %d;\n\n", height) ||
+ ! print (output, error,
+ "/* Call this macro repeatedly. After each use, the pixel data can be extracted */\n\n"))
+ {
+ goto fail;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ if (! print (output, error,
+ "#define HEADER_PIXEL(data,pixel) {\\\n"
+ "pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \\\n"
+ "pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \\\n"
+ "pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \\\n"
+ "data += 4; \\\n}\n") ||
+ ! print (output, error,
+ "static char *header_data =\n\t\""))
+ {
+ goto fail;
+ }
+
+ format = babl_format ("R'G'B' u8");
+
+ data = g_new (guchar, width * babl_format_get_bytes_per_pixel (format));
+
+ c = 0;
+ for (y = 0; y < height; y++)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (x = 0; x < width; x++)
+ {
+ d = data + x * babl_format_get_bytes_per_pixel (format);
+
+ buf[0] = ((d[0] >> 2) & 0x3F) + 33;
+ buf[1] = ((((d[0] & 0x3) << 4) | (d[1] >> 4)) & 0x3F) + 33;
+ buf[2] = ((((d[1] & 0xF) << 2) | (d[2] >> 6)) & 0x3F) + 33;
+ buf[3] = (d[2] & 0x3F) + 33;
+
+ for (b = 0; b < 4; b++)
+ {
+ if (buf[b] == '"')
+ {
+ if (! print (output, error, "%s", quote))
+ goto fail;
+ }
+ else if (buf[b] == '\\')
+ {
+ if (! print (output, error, "%s", backslash))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error, "%c", buf[b]))
+ goto fail;
+ }
+ }
+
+ c++;
+ if (c >= 16)
+ {
+ if (! print (output, error, "%s", newline))
+ goto fail;
+
+ c = 0;
+ }
+ }
+ }
+
+ if (! print (output, error, "\";\n"))
+ goto fail;
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ if (! print (output, error,
+ "#define HEADER_PIXEL(data,pixel) {\\\n"
+ "pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \\\n"
+ "pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \\\n"
+ "pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \\\n"
+ "data ++; }\n\n"))
+ {
+ goto fail;
+ }
+
+ /* save colormap */
+ cmap = gimp_image_get_colormap (image_ID, &colors);
+
+ if (! print (output, error,
+ "static unsigned char header_data_cmap[256][3] = {") ||
+ ! print (output, error,
+ "\n\t{%3d,%3d,%3d}",
+ (gint) cmap[0], (gint) cmap[1], (gint) cmap[2]))
+ {
+ goto fail;
+ }
+
+ for (c = 1; c < colors; c++)
+ {
+ if (! print (output, error,
+ ",\n\t{%3d,%3d,%3d}",
+ (gint) cmap[3 * c],
+ (gint) cmap[3 * c + 1],
+ (gint) cmap[3 * c + 2]))
+ {
+ goto fail;
+ }
+ }
+
+ /* fill the rest */
+ for ( ; c < 256; c++)
+ {
+ if (! print (output, error, ",\n\t{255,255,255}"))
+ goto fail;
+ }
+
+ /* close bracket */
+ if (! print (output, error, "\n\t};\n"))
+ goto fail;
+
+ g_free (cmap);
+
+ /* save image */
+ if (! print (output, error, "static unsigned char header_data[] = {\n\t"))
+ goto fail;
+
+ data = g_new (guchar, width * 1);
+
+ c = 0;
+ for (y = 0; y < height; y++)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, 1), 1.0,
+ NULL, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (x = 0; x < width -1; x++)
+ {
+ d = data + x * 1;
+
+ if (! print (output, error, "%d,", (gint) d[0]))
+ goto fail;
+
+ c++;
+ if (c >= 16)
+ {
+ if (! print (output, error, "\n\t"))
+ goto fail;
+
+ c = 0;
+ }
+ }
+
+ if (y != height - 1)
+ {
+ if (! print (output, error, "%d,\n\t", (gint) d[1]))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error, "%d\n\t", (gint) d[1]))
+ goto fail;
+ }
+
+ c = 0; /* reset line counter */
+ }
+
+ if (! print (output, error, "};\n"))
+ goto fail;
+ break;
+
+ default:
+ g_warning ("unhandled drawable type (%d)", drawable_type);
+ goto fail;
+ }
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_free (data);
+ g_object_unref (output);
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+
+ g_free (data);
+ g_object_unref (output);
+ g_object_unref (buffer);
+ g_object_unref (cancellable);
+
+ return FALSE;
+}
+
+static gboolean
+print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (output, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
diff --git a/plug-ins/common/file-heif.c b/plug-ins/common/file-heif.c
new file mode 100644
index 0000000..9564355
--- /dev/null
+++ b/plug-ins/common/file-heif.c
@@ -0,0 +1,2173 @@
+/*
+ * GIMP HEIF loader / write plugin.
+ * Copyright (c) 2018 struktur AG, Dirk Farin <farin@struktur.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libheif/heif.h>
+#include <lcms2.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-heif-load"
+#define SAVE_PROC "file-heif-save"
+#define SAVE_PROC_AV1 "file-heif-av1-save"
+#define PLUG_IN_BINARY "file-heif"
+
+
+typedef struct _SaveParams SaveParams;
+
+struct _SaveParams
+{
+ gint quality;
+ gboolean lossless;
+ gboolean save_profile;
+ gint save_bit_depth;
+};
+
+
+/* local function prototypes */
+
+static void init (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (GFile *file,
+ gboolean interactive,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const SaveParams *params,
+ GError **error,
+ enum heif_compression_format compression);
+
+static gboolean load_dialog (struct heif_context *heif,
+ uint32_t *selected_image);
+static gboolean save_dialog (SaveParams *params,
+ gint32 image_ID,
+ const gchar *procname);
+
+
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ init, /* init_proc */
+ NULL, /* quit_proc */
+ NULL, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+init (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "uri", "The URI of the file to load" },
+ { GIMP_PDB_STRING, "raw-uri", "The URI of the file to load" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "uri", "The URI of the file to export the image to" },
+ { GIMP_PDB_STRING, "raw-uri", "The UTI of the file to export the image to" },
+ { GIMP_PDB_INT32, "quality", "Quality factor (range: 0-100. 0 = worst, 100 = best)" },
+ { GIMP_PDB_INT32, "lossless", "Use lossless compression (0 = lossy, 1 = lossless)" }
+ };
+
+ if (heif_have_decoder_for_format (heif_compression_HEVC)
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ || heif_have_decoder_for_format (heif_compression_AV1)
+#endif
+ )
+ {
+ GString *extensions = g_string_new (NULL);
+ GString *mimetypes = g_string_new (NULL);
+ GString *magic = g_string_new ("4,string,ftypmif1");
+
+ if (heif_have_decoder_for_format (heif_compression_HEVC))
+ {
+ g_string_append (extensions, "heic,heif");
+ g_string_append (mimetypes, "image/heif");
+ g_string_append (magic, ",4,string,ftypheic,4,string,ftypheix,"
+ "4,string,ftyphevc,4,string,ftypheim,"
+ "4,string,ftypheis,4,string,ftyphevm,"
+ "4,string,ftyphevs,4,string,ftypmsf1");
+ }
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ if (heif_have_decoder_for_format (heif_compression_AV1))
+ {
+ g_string_append_printf (extensions, "%savif", extensions->len ? "," : "");
+ g_string_append_printf (mimetypes, "%simage/avif", mimetypes->len ? "," : "");
+ g_string_append (magic, ",4,string,ftypavif");
+ }
+#endif
+
+ gimp_install_procedure (LOAD_PROC,
+ _("Loads HEIF images"),
+ _("Load image stored in HEIF format (High "
+ "Efficiency Image File Format). Typical "
+ "suffices for HEIF files are .heif, .heic."),
+ "Dirk Farin <farin@struktur.de>",
+ "Dirk Farin <farin@struktur.de>",
+ "2018",
+ _("HEIF/HEIC"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_load_handler (LOAD_PROC, extensions->str, "");
+ gimp_register_file_handler_mime (LOAD_PROC, mimetypes->str);
+ gimp_register_file_handler_uri (LOAD_PROC);
+
+ gimp_register_magic_load_handler (LOAD_PROC,
+ extensions->str, "",
+ magic->str);
+
+ g_string_free (magic, TRUE);
+ g_string_free (mimetypes, TRUE);
+ g_string_free (extensions, TRUE);
+ }
+
+ if (heif_have_encoder_for_format (heif_compression_HEVC))
+ {
+ gimp_install_procedure (SAVE_PROC,
+ _("Exports HEIF images"),
+ _("Save image in HEIF format (High Efficiency "
+ "Image File Format)."),
+ "Dirk Farin <farin@struktur.de>",
+ "Dirk Farin <farin@struktur.de>",
+ "2018",
+ _("HEIF/HEIC"),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_save_handler (SAVE_PROC, "heic,heif", "");
+ gimp_register_file_handler_mime (SAVE_PROC, "image/heif");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ }
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ if (heif_have_encoder_for_format (heif_compression_AV1))
+ {
+ gimp_install_procedure (SAVE_PROC_AV1,
+ _("Exports AVIF images"),
+ _("Save image in AV1 Image File Format (AVIF)"),
+ "Daniel Novomesky <dnovomesky@gmail.com>",
+ "Daniel Novomesky <dnovomesky@gmail.com>",
+ "2020",
+ "HEIF/AVIF",
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_save_handler (SAVE_PROC_AV1, "avif", "");
+ gimp_register_file_handler_mime (SAVE_PROC_AV1, "image/avif");
+ gimp_register_file_handler_uri (SAVE_PROC_AV1);
+ }
+#endif
+}
+
+#define LOAD_HEIF_CANCEL -2
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ GFile *file;
+ gboolean interactive;
+
+ if (n_params != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ file = g_file_new_for_uri (param[1].data.d_string);
+ interactive = (run_mode == GIMP_RUN_INTERACTIVE);
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint32 image_ID;
+
+ image_ID = load_image (file, interactive, &error);
+
+ if (image_ID >= 0)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else if (image_ID == LOAD_HEIF_CANCEL)
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+
+ g_object_unref (file);
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ SaveParams params;
+ GimpMetadata *metadata = NULL;
+ GimpMetadataSaveFlags metadata_flags;
+
+ metadata = gimp_image_metadata_save_prepare (image_ID,
+ "image/heif",
+ &metadata_flags);
+
+ params.lossless = FALSE;
+ params.quality = 50;
+ params.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ params.save_bit_depth = 8;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ export = gimp_export_image (&image_ID, &drawable_ID, "HEIF",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ g_clear_object (&metadata);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (SAVE_PROC, &params);
+
+ if (! save_dialog (&params, image_ID, name))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC, &params);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (n_params != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ params.quality = (param[5].data.d_int32);
+ params.lossless = (param[6].data.d_int32);
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (save_image (file, image_ID, drawable_ID,
+ &params,
+ &error,
+ heif_compression_HEVC))
+ {
+ /* Exiv2 doesn't support HEIC format yet, following code doesn't work
+ if (metadata)
+ gimp_image_metadata_save_finish (image_ID,
+ "image/heif",
+ metadata, metadata_flags,
+ file, NULL);
+ */
+ gimp_set_data (SAVE_PROC, &params, sizeof (params));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_object_unref (file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ g_clear_object (&metadata);
+ }
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ else if (strcmp (name, SAVE_PROC_AV1) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ SaveParams params;
+ GimpMetadata *metadata = NULL;
+ GimpMetadataSaveFlags metadata_flags;
+
+ metadata = gimp_image_metadata_save_prepare (image_ID,
+ "image/avif",
+ &metadata_flags);
+
+ params.lossless = FALSE;
+ params.quality = 50;
+ params.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ params.save_bit_depth = 8;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ export = gimp_export_image (&image_ID, &drawable_ID, "AVIF",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ g_clear_object (&metadata);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (SAVE_PROC_AV1, &params);
+
+ if (! save_dialog (&params, image_ID, name))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC_AV1, &params);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (n_params != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ params.quality = (param[5].data.d_int32);
+ params.lossless = (param[6].data.d_int32);
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (save_image (file, image_ID, drawable_ID,
+ &params,
+ &error,
+ heif_compression_AV1))
+ {
+ gimp_set_data (SAVE_PROC_AV1, &params, sizeof (params));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_object_unref (file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ g_clear_object (&metadata);
+ }
+#endif
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static goffset
+get_file_size (GFile *file,
+ GError **error)
+{
+ GFileInfo *info;
+ goffset size = 1;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, error);
+ if (info)
+ {
+ size = g_file_info_get_size (info);
+
+ g_object_unref (info);
+ }
+
+ return size;
+}
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+static void
+heifplugin_color_profile_set_tag (cmsHPROFILE profile,
+ cmsTagSignature sig,
+ const gchar *tag)
+{
+ cmsMLU *mlu;
+
+ mlu = cmsMLUalloc (NULL, 1);
+ cmsMLUsetASCII (mlu, "en", "US", tag);
+ cmsWriteTag (profile, sig, mlu);
+ cmsMLUfree (mlu);
+}
+
+static GimpColorProfile *
+nclx_to_gimp_profile (const struct heif_color_profile_nclx *nclx)
+{
+ const gchar *primaries_name = "";
+ const gchar *trc_name = "";
+ cmsHPROFILE profile = NULL;
+ cmsCIExyY whitepoint;
+ cmsCIExyYTRIPLE primaries;
+ cmsToneCurve *curve[3];
+
+ cmsFloat64Number srgb_parameters[5] =
+ { 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
+
+ cmsFloat64Number rec709_parameters[5] =
+ { 2.2, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.081 };
+
+ if (nclx == NULL)
+ {
+ return NULL;
+ }
+
+ if (nclx->color_primaries == heif_color_primaries_unspecified)
+ {
+ return NULL;
+ }
+
+ if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
+ {
+ if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1)
+ {
+ return gimp_color_profile_new_rgb_srgb();
+ }
+
+ if (nclx->transfer_characteristics == heif_transfer_characteristic_linear)
+ {
+ return gimp_color_profile_new_rgb_srgb_linear();
+ }
+ }
+
+ whitepoint.x = nclx->color_primary_white_x;
+ whitepoint.y = nclx->color_primary_white_y;
+ whitepoint.Y = 1.0f;
+
+ primaries.Red.x = nclx->color_primary_red_x;
+ primaries.Red.y = nclx->color_primary_red_y;
+ primaries.Red.Y = 1.0f;
+
+ primaries.Green.x = nclx->color_primary_green_x;
+ primaries.Green.y = nclx->color_primary_green_y;
+ primaries.Green.Y = 1.0f;
+
+ primaries.Blue.x = nclx->color_primary_blue_x;
+ primaries.Blue.y = nclx->color_primary_blue_y;
+ primaries.Blue.Y = 1.0f;
+
+ switch (nclx->color_primaries)
+ {
+ case heif_color_primaries_ITU_R_BT_709_5:
+ primaries_name = "BT.709";
+ break;
+ case heif_color_primaries_ITU_R_BT_470_6_System_M:
+ primaries_name = "BT.470-6 System M";
+ break;
+ case heif_color_primaries_ITU_R_BT_470_6_System_B_G:
+ primaries_name = "BT.470-6 System BG";
+ break;
+ case heif_color_primaries_ITU_R_BT_601_6:
+ primaries_name = "BT.601";
+ break;
+ case heif_color_primaries_SMPTE_240M:
+ primaries_name = "SMPTE 240M";
+ break;
+ case 8:
+ primaries_name = "Generic film";
+ break;
+ case 9:
+ primaries_name = "BT.2020";
+ break;
+ case 10:
+ primaries_name = "XYZ";
+ break;
+ case 11:
+ primaries_name = "SMPTE RP 431-2";
+ break;
+ case 12:
+ primaries_name = "SMPTE EG 432-1 (DCI P3)";
+ break;
+ case 22:
+ primaries_name = "EBU Tech. 3213-E";
+ break;
+ default:
+ g_warning ("%s: Unsupported color_primaries value %d.",
+ G_STRFUNC, nclx->color_primaries);
+ return NULL;
+ break;
+ }
+
+ switch (nclx->transfer_characteristics)
+ {
+ case heif_transfer_characteristic_ITU_R_BT_709_5:
+ curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
+ rec709_parameters);
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+ cmsFreeToneCurve (curve[0]);
+ trc_name = "Rec709 RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_470_6_System_M:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.2f);
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+ cmsFreeToneCurve (curve[0]);
+ trc_name = "Gamma2.2 RGB";
+ break;
+ case heif_transfer_characteristic_ITU_R_BT_470_6_System_B_G:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.8f);
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+ cmsFreeToneCurve (curve[0]);
+ trc_name = "Gamma2.8 RGB";
+ break;
+ case heif_transfer_characteristic_linear:
+ curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0f);
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+ cmsFreeToneCurve (curve[0]);
+ trc_name = "linear RGB";
+ break;
+ case heif_transfer_characteristic_IEC_61966_2_1:
+ /* same as default */
+ default:
+ curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
+ srgb_parameters);
+ profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
+ cmsFreeToneCurve (curve[0]);
+ trc_name = "sRGB-TRC RGB";
+ break;
+ }
+
+ if (profile)
+ {
+ GimpColorProfile *new_profile;
+ gchar *description = g_strdup_printf ("%s %s", primaries_name, trc_name);
+
+ heifplugin_color_profile_set_tag (profile, cmsSigProfileDescriptionTag,
+ description);
+ heifplugin_color_profile_set_tag (profile, cmsSigDeviceMfgDescTag,
+ "GIMP");
+ heifplugin_color_profile_set_tag (profile, cmsSigDeviceModelDescTag,
+ description);
+ heifplugin_color_profile_set_tag (profile, cmsSigCopyrightTag,
+ "Public Domain");
+
+ new_profile = gimp_color_profile_new_from_lcms_profile (profile, NULL);
+
+ cmsCloseProfile (profile);
+ g_free (description);
+ return new_profile;
+ }
+
+ return NULL;
+}
+#endif
+
+gint32
+load_image (GFile *file,
+ gboolean interactive,
+ GError **error)
+{
+ GInputStream *input;
+ goffset file_size;
+ guchar *file_buffer;
+ gsize bytes_read;
+ struct heif_context *ctx;
+ struct heif_error err;
+ struct heif_image_handle *handle = NULL;
+ struct heif_image *img = NULL;
+ GimpColorProfile *profile = NULL;
+ gint n_images;
+ heif_item_id primary;
+ heif_item_id selected_image;
+ gboolean has_alpha;
+ gint width;
+ gint height;
+ gint32 image_ID;
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ const Babl *format;
+ const guint8 *data;
+ gint stride;
+ gint bit_depth = 8;
+ enum heif_chroma chroma = heif_chroma_interleaved_RGB;
+ GimpPrecision precision;
+ gboolean load_linear;
+ const char *encoding;
+ char *filename = g_file_get_parse_name (file);
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ filename);
+ g_free (filename);
+
+ file_size = get_file_size (file, error);
+ if (file_size <= 0)
+ return -1;
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! input)
+ return -1;
+
+ file_buffer = g_malloc (file_size);
+
+ if (! g_input_stream_read_all (input, file_buffer, file_size,
+ &bytes_read, NULL, error) &&
+ bytes_read == 0)
+ {
+ g_free (file_buffer);
+ g_object_unref (input);
+ return -1;
+ }
+
+ gimp_progress_update (0.25);
+
+ ctx = heif_context_alloc ();
+ if (!ctx)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "cannot allocate heif_context");
+ g_free (file_buffer);
+ g_object_unref (input);
+ return -1;
+ }
+
+ err = heif_context_read_from_memory (ctx, file_buffer, file_size, NULL);
+ if (err.code)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Loading HEIF image failed: %s"),
+ err.message);
+ heif_context_free (ctx);
+ g_free (file_buffer);
+ g_object_unref (input);
+
+ return -1;
+ }
+
+ g_free (file_buffer);
+ g_object_unref (input);
+
+ gimp_progress_update (0.5);
+
+ /* analyze image content
+ * Is there more than one image? Which image is the primary image?
+ */
+
+ n_images = heif_context_get_number_of_top_level_images (ctx);
+ if (n_images == 0)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Loading HEIF image failed: "
+ "Input file contains no readable images"));
+ heif_context_free (ctx);
+
+ return -1;
+ }
+
+ err = heif_context_get_primary_image_ID (ctx, &primary);
+ if (err.code)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Loading HEIF image failed: %s"),
+ err.message);
+ heif_context_free (ctx);
+
+ return -1;
+ }
+
+ /* if primary image is no top level image or not present (invalid
+ * file), just take the first image
+ */
+
+ if (! heif_context_is_top_level_image_ID (ctx, primary))
+ {
+ gint n = heif_context_get_list_of_top_level_image_IDs (ctx, &primary, 1);
+ g_assert (n == 1);
+ }
+
+ selected_image = primary;
+
+ /* if there are several images in the file and we are running
+ * interactive, let the user choose a picture
+ */
+
+ if (interactive && n_images > 1)
+ {
+ if (! load_dialog (ctx, &selected_image))
+ {
+ heif_context_free (ctx);
+
+ return LOAD_HEIF_CANCEL;
+ }
+ }
+
+ /* load the picture */
+
+ err = heif_context_get_image_handle (ctx, selected_image, &handle);
+ if (err.code)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Loading HEIF image failed: %s"),
+ err.message);
+ heif_context_free (ctx);
+
+ return -1;
+ }
+
+ has_alpha = heif_image_handle_has_alpha_channel (handle);
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ bit_depth = heif_image_handle_get_luma_bits_per_pixel (handle);
+ if (bit_depth < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Input image has undefined bit-depth");
+ heif_image_handle_release (handle);
+ heif_context_free (ctx);
+
+ return -1;
+ }
+#endif
+
+ if (bit_depth == 8)
+ {
+ if (has_alpha)
+ {
+ chroma = heif_chroma_interleaved_RGBA;
+ }
+ else
+ {
+ chroma = heif_chroma_interleaved_RGB;
+ }
+ }
+ else /* high bit depth */
+ {
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+#if ( G_BYTE_ORDER == G_LITTLE_ENDIAN )
+ if (has_alpha)
+ {
+ chroma = heif_chroma_interleaved_RRGGBBAA_LE;
+ }
+ else
+ {
+ chroma = heif_chroma_interleaved_RRGGBB_LE;
+ }
+#else
+ if (has_alpha)
+ {
+ chroma = heif_chroma_interleaved_RRGGBBAA_BE;
+ }
+ else
+ {
+ chroma = heif_chroma_interleaved_RRGGBB_BE;
+ }
+#endif
+#endif
+ }
+
+ err = heif_decode_image (handle,
+ &img,
+ heif_colorspace_RGB,
+ chroma,
+ NULL);
+ if (err.code)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Loading HEIF image failed: %s"),
+ err.message);
+ heif_image_handle_release (handle);
+ heif_context_free (ctx);
+
+ return -1;
+ }
+
+#if LIBHEIF_HAVE_VERSION(1,4,0)
+ switch (heif_image_handle_get_color_profile_type (handle))
+ {
+ case heif_color_profile_type_not_present:
+ break;
+ case heif_color_profile_type_rICC:
+ case heif_color_profile_type_prof:
+ /* I am unsure, but it looks like both these types represent an
+ * ICC color profile. XXX
+ */
+ {
+ void *profile_data;
+ size_t profile_size;
+
+ profile_size = heif_image_handle_get_raw_color_profile_size (handle);
+ profile_data = g_malloc0 (profile_size);
+ err = heif_image_handle_get_raw_color_profile (handle, profile_data);
+
+ if (err.code)
+ g_warning ("%s: color profile loading failed and discarded.",
+ G_STRFUNC);
+ else
+ profile = gimp_color_profile_new_from_icc_profile ((guint8 *) profile_data,
+ profile_size, NULL);
+
+ g_free (profile_data);
+ }
+ break;
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ case heif_color_profile_type_nclx:
+ {
+ struct heif_color_profile_nclx *nclx = NULL;
+
+ err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
+ if (err.code)
+ {
+ g_warning ("%s: NCLX profile loading failed and discarded.",
+ G_STRFUNC);
+ }
+ else
+ {
+ profile = nclx_to_gimp_profile (nclx);
+ heif_nclx_color_profile_free (nclx);
+ }
+ }
+ break;
+#endif
+ default:
+ g_warning ("%s: unknown color profile type has been discarded.",
+ G_STRFUNC);
+ break;
+ }
+#endif /* LIBHEIF_HAVE_VERSION(1,4,0) */
+
+ gimp_progress_update (0.75);
+
+ width = heif_image_get_width (img, heif_channel_interleaved);
+ height = heif_image_get_height (img, heif_channel_interleaved);
+
+ /* create GIMP image and copy HEIF image into the GIMP image
+ * (converting it to RGB)
+ */
+
+ if (profile)
+ {
+ load_linear = gimp_color_profile_is_linear (profile);
+ }
+ else
+ {
+ load_linear = FALSE;
+ }
+
+ if (load_linear)
+ {
+ if (bit_depth == 8)
+ {
+ precision = GIMP_PRECISION_U8_LINEAR;
+ encoding = has_alpha ? "RGBA u8" : "RGB u8";
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U16_LINEAR;
+ encoding = has_alpha ? "RGBA u16" : "RGB u16";
+ }
+ }
+ else /* non-linear profiles */
+ {
+ if (bit_depth == 8)
+ {
+ precision = GIMP_PRECISION_U8_GAMMA;
+ encoding = has_alpha ? "R'G'B'A u8" : "R'G'B' u8";
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U16_GAMMA;
+ encoding = has_alpha ? "R'G'B'A u16" : "R'G'B' u16";
+ }
+ }
+
+ image_ID = gimp_image_new_with_precision (width, height, GIMP_RGB, precision);
+ gimp_image_set_filename (image_ID, g_file_get_uri (file));
+
+ if (profile)
+ {
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ gimp_image_set_color_profile (image_ID, profile);
+ }
+ else if (gimp_color_profile_is_gray (profile))
+ {
+ g_warning ("Gray ICC profile was not applied to the imported image.");
+ }
+ else
+ {
+ g_warning ("ICC profile was not applied to the imported image.");
+ }
+ }
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("image content"),
+ width, height,
+ has_alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ data = heif_image_get_plane_readonly (img, heif_channel_interleaved,
+ &stride);
+
+ format = babl_format_with_space (encoding,
+ gegl_buffer_get_format (buffer));
+
+ if (bit_depth == 8)
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 0, format, data, stride);
+ }
+ else /* high bit depth */
+ {
+ uint16_t *data16;
+ const uint16_t *src16;
+ uint16_t *dest16;
+ gint x, y, rowentries;
+ int tmp_pixelval;
+
+ if (has_alpha)
+ {
+ rowentries = width * 4;
+ }
+ else /* no alpha */
+ {
+ rowentries = width * 3;
+ }
+
+ data16 = g_malloc_n (height, rowentries * 2);
+ dest16 = data16;
+
+ switch (bit_depth)
+ {
+ case 10:
+ for (y = 0; y < height; y++)
+ {
+ src16 = (const uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (0x03ff & (*src16)) / 1023.0f) * 65535.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 65535);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ case 12:
+ for (y = 0; y < height; y++)
+ {
+ src16 = (const uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (0x0fff & (*src16)) / 4095.0f) * 65535.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 65535);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ default:
+ for (y = 0; y < height; y++)
+ {
+ src16 = (const uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ *dest16 = *src16;
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 0, format, data16, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (data16);
+ }
+
+ g_object_unref (buffer);
+
+ {
+ size_t exif_data_size = 0;
+ uint8_t *exif_data = NULL;
+ size_t xmp_data_size = 0;
+ uint8_t *xmp_data = NULL;
+ gint n_metadata;
+ heif_item_id metadata_id;
+
+ n_metadata =
+ heif_image_handle_get_list_of_metadata_block_IDs (handle,
+ "Exif",
+ &metadata_id, 1);
+ if (n_metadata > 0)
+ {
+ exif_data_size = heif_image_handle_get_metadata_size (handle,
+ metadata_id);
+ exif_data = g_alloca (exif_data_size);
+
+ err = heif_image_handle_get_metadata (handle, metadata_id, exif_data);
+ if (err.code != 0)
+ {
+ exif_data = NULL;
+ exif_data_size = 0;
+ }
+ }
+
+ n_metadata =
+ heif_image_handle_get_list_of_metadata_block_IDs (handle,
+ "mime",
+ &metadata_id, 1);
+ if (n_metadata > 0)
+ {
+ if (g_strcmp0 (
+ heif_image_handle_get_metadata_content_type (handle, metadata_id),
+ "application/rdf+xml") == 0)
+ {
+ xmp_data_size = heif_image_handle_get_metadata_size (handle,
+ metadata_id);
+ xmp_data = g_alloca (xmp_data_size);
+
+ err = heif_image_handle_get_metadata (handle, metadata_id, xmp_data);
+ if (err.code != 0)
+ {
+ xmp_data = NULL;
+ xmp_data_size = 0;
+ }
+ }
+ }
+
+ if (exif_data || xmp_data)
+ {
+ GimpMetadata *metadata = gimp_metadata_new ();
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_COMMENT | GIMP_METADATA_LOAD_RESOLUTION;
+
+ if (exif_data)
+ {
+ const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
+ const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
+ GExiv2Metadata *exif_metadata = GEXIV2_METADATA (metadata);
+ const guint8 *tiffheader = exif_data;
+ glong new_exif_size = exif_data_size;
+
+ while (new_exif_size >= 4) /*Searching for TIFF Header*/
+ {
+ if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
+ tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
+ {
+ break;
+ }
+ if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
+ tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
+ {
+ break;
+ }
+ new_exif_size--;
+ tiffheader++;
+ }
+
+ if (new_exif_size > 4) /* TIFF header + some data found*/
+ {
+ if (! gexiv2_metadata_open_buf (exif_metadata, tiffheader, new_exif_size, error))
+ {
+ g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+ else
+ {
+ g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
+ }
+ }
+
+
+ if (xmp_data)
+ {
+ if (!gimp_metadata_set_from_xmp (metadata, xmp_data, xmp_data_size, error))
+ {
+ g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+
+ gexiv2_metadata_set_orientation (GEXIV2_METADATA (metadata),
+ GEXIV2_ORIENTATION_NORMAL);
+ gexiv2_metadata_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
+ width);
+ gexiv2_metadata_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
+ height);
+ gimp_image_metadata_load_finish (image_ID, "image/heif",
+ metadata, flags,
+ interactive);
+ }
+ }
+
+ if (profile)
+ g_object_unref (profile);
+
+ heif_image_handle_release (handle);
+ heif_context_free (ctx);
+ heif_image_release (img);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+static struct heif_error
+write_callback (struct heif_context *ctx,
+ const void *data,
+ size_t size,
+ void *userdata)
+{
+ GOutputStream *output = userdata;
+ GError *error = NULL;
+ struct heif_error heif_error;
+
+ heif_error.code = heif_error_Ok;
+ heif_error.subcode = heif_suberror_Unspecified;
+ heif_error.message = "";
+
+ if (! g_output_stream_write_all (output, data, size, NULL, NULL, &error))
+ {
+ heif_error.code = 99; /* hmm */
+ heif_error.message = error->message;
+ }
+
+ return heif_error;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ const SaveParams *params,
+ GError **error,
+ enum heif_compression_format compression)
+{
+ struct heif_image *image = NULL;
+ struct heif_context *context = heif_context_alloc ();
+ struct heif_encoder *encoder = NULL;
+ const struct heif_encoder_descriptor *encoder_descriptor;
+ const char *encoder_name;
+ struct heif_image_handle *handle = NULL;
+ struct heif_writer writer;
+ struct heif_error err;
+ GOutputStream *output;
+ GeglBuffer *buffer;
+ const gchar *encoding;
+ const Babl *format;
+ const Babl *space = NULL;
+ guint8 *data;
+ gint stride;
+ gint width;
+ gint height;
+ gboolean has_alpha;
+ gboolean out_linear = FALSE;
+ char *filename;
+
+ if (!context)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "cannot allocate heif_context");
+ return FALSE;
+ }
+
+ if (compression == heif_compression_HEVC)
+ {
+ if (heif_context_get_encoder_descriptors (context,
+ heif_compression_HEVC,
+ NULL,
+ &encoder_descriptor, 1) == 1)
+ {
+ encoder_name = heif_encoder_descriptor_get_id_name (encoder_descriptor);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unable to find suitable HEIF encoder");
+ heif_context_free (context);
+ return FALSE;
+ }
+ }
+ else /* AV1 compression */
+ {
+ if (heif_context_get_encoder_descriptors (context,
+ compression,
+ "aom", /* we prefer aom rather than rav1e */
+ &encoder_descriptor, 1) == 1)
+ {
+ encoder_name = heif_encoder_descriptor_get_id_name (encoder_descriptor);
+ }
+ else if (heif_context_get_encoder_descriptors (context,
+ compression,
+ NULL,
+ &encoder_descriptor, 1) == 1)
+ {
+ encoder_name = heif_encoder_descriptor_get_id_name (encoder_descriptor);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unable to find suitable AVIF encoder");
+ heif_context_free (context);
+ return FALSE;
+ }
+ }
+
+ filename = g_file_get_parse_name (file);
+ gimp_progress_init_printf (_("Exporting '%s' using %s encoder"),
+ filename, encoder_name);
+ g_free (filename);
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ switch (params->save_bit_depth)
+ {
+ case 8:
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RGBA :
+ heif_chroma_interleaved_RGB,
+ &image);
+ break;
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ case 10:
+ case 12:
+#if ( G_BYTE_ORDER == G_LITTLE_ENDIAN )
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RRGGBBAA_LE :
+ heif_chroma_interleaved_RRGGBB_LE,
+ &image);
+#else
+ err = heif_image_create (width, height,
+ heif_colorspace_RGB,
+ has_alpha ?
+ heif_chroma_interleaved_RRGGBBAA_BE :
+ heif_chroma_interleaved_RRGGBB_BE,
+ &image);
+#endif
+ break;
+#endif
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unsupported bit depth: %d",
+ params->save_bit_depth);
+ heif_context_free (context);
+ return FALSE;
+ break;
+ }
+
+ if (err.code != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Encoding HEIF image failed: %s"),
+ err.message);
+ heif_context_free (context);
+ return FALSE;
+ }
+
+#if LIBHEIF_HAVE_VERSION(1,4,0)
+ if (params->save_profile)
+ {
+ GimpColorProfile *profile = NULL;
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ profile = gimp_image_get_color_profile (image_ID);
+ if (profile && gimp_color_profile_is_linear (profile))
+ out_linear = TRUE;
+
+ if (! profile)
+ {
+ profile = gimp_image_get_effective_color_profile (image_ID);
+
+ if (gimp_color_profile_is_linear (profile))
+ {
+ if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR)
+ {
+ /* If stored data was linear, let's convert the profile. */
+ GimpColorProfile *saved_profile;
+
+ saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile);
+ g_clear_object (&profile);
+ profile = saved_profile;
+ }
+ else
+ {
+ /* Keep linear profile as-is for 8-bit linear image. */
+ out_linear = TRUE;
+ }
+ }
+ }
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ heif_image_set_raw_color_profile (image, "prof", icc_data, icc_length);
+ space = gimp_color_profile_get_space (profile,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ error);
+ if (error && *error)
+ {
+ /* Don't make this a hard failure yet output the error. */
+ g_printerr ("%s: error getting the profile space: %s",
+ G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+
+ g_object_unref (profile);
+ }
+ else
+ {
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ /* We save as sRGB */
+ struct heif_color_profile_nclx nclx_profile;
+
+ nclx_profile.version = 1;
+ nclx_profile.color_primaries = heif_color_primaries_ITU_R_BT_709_5;
+ nclx_profile.transfer_characteristics = heif_transfer_characteristic_IEC_61966_2_1;
+ nclx_profile.matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
+ nclx_profile.full_range_flag = 1;
+
+ heif_image_set_nclx_color_profile (image, &nclx_profile);
+
+ space = babl_space ("sRGB");
+ out_linear = FALSE;
+#endif
+ }
+#endif /* LIBHEIF_HAVE_VERSION(1,4,0) */
+
+ if (! space)
+ space = gimp_drawable_get_format (drawable_ID);
+
+ if (params->save_bit_depth > 8)
+ {
+ uint16_t *data16;
+ const uint16_t *src16;
+ uint16_t *dest16;
+ gint x, y, rowentries;
+ int tmp_pixelval;
+
+ if (has_alpha)
+ {
+ rowentries = width * 4;
+
+ if (out_linear)
+ encoding = "RGBA u16";
+ else
+ encoding = "R'G'B'A u16";
+ }
+ else /* no alpha */
+ {
+ rowentries = width * 3;
+
+ if (out_linear)
+ encoding = "RGB u16";
+ else
+ encoding = "R'G'B' u16";
+ }
+
+ data16 = g_malloc_n (height, rowentries * 2);
+ src16 = data16;
+
+ format = babl_format_with_space (encoding, space);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 1.0, format, data16, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, params->save_bit_depth);
+
+ data = heif_image_get_plane (image, heif_channel_interleaved, &stride);
+
+ switch (params->save_bit_depth)
+ {
+ case 10:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (*src16) / 65535.0f) * 1023.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 1023);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ case 12:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ tmp_pixelval = (int) ( ( (float) (*src16) / 65535.0f) * 4095.0f + 0.5f);
+ *dest16 = CLAMP (tmp_pixelval, 0, 4095);
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ default:
+ for (y = 0; y < height; y++)
+ {
+ dest16 = (uint16_t *) (y * stride + data);
+ for (x = 0; x < rowentries; x++)
+ {
+ *dest16 = *src16;
+ dest16++;
+ src16++;
+ }
+ }
+ break;
+ }
+ g_free (data16);
+ }
+ else /* save_bit_depth == 8 */
+ {
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, 8);
+#else
+ /* old style settings */
+ heif_image_add_plane (image, heif_channel_interleaved,
+ width, height, has_alpha ? 32 : 24);
+#endif
+
+ data = heif_image_get_plane (image, heif_channel_interleaved, &stride);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ if (has_alpha)
+ {
+ if (out_linear)
+ encoding = "RGBA u8";
+ else
+ encoding = "R'G'B'A u8";
+ }
+ else
+ {
+ if (out_linear)
+ encoding = "RGB u8";
+ else
+ encoding = "R'G'B' u8";
+ }
+ format = babl_format_with_space (encoding, space);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, 0, width, height),
+ 1.0, format, data, stride, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+ }
+
+ gimp_progress_update (0.33);
+
+ /* encode to HEIF file */
+
+ err = heif_context_get_encoder (context,
+ encoder_descriptor,
+ &encoder);
+
+ if (err.code != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unable to get an encoder instance");
+ heif_image_release (image);
+ heif_context_free (context);
+ return FALSE;
+ }
+
+ /* workaround for a bug in libheif when heif_encoder_set_lossless is not working
+ (known problem with encoding via rav1e) */
+ if (params->lossless)
+ {
+ heif_encoder_set_lossy_quality (encoder, 100);
+ }
+ else
+ {
+ heif_encoder_set_lossy_quality (encoder, params->quality);
+ }
+
+ heif_encoder_set_lossless (encoder, params->lossless);
+ /* heif_encoder_set_logging_level (encoder, logging_level); */
+
+#if LIBHEIF_HAVE_VERSION(1,10,0)
+ if (params->lossless)
+ {
+ err = heif_encoder_set_parameter_string (encoder, "chroma", "444");
+ if (err.code != 0)
+ {
+ g_printerr ("Failed to set chroma=444 for %s encoder: %s", encoder_name, err.message);
+ }
+ }
+#endif
+
+ err = heif_context_encode_image (context,
+ image,
+ encoder,
+ NULL,
+ &handle);
+ if (err.code != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Encoding HEIF image failed: %s"),
+ err.message);
+ heif_encoder_release (encoder);
+ heif_image_release (image);
+ heif_context_free (context);
+ return FALSE;
+ }
+
+ heif_image_handle_release (handle);
+
+ gimp_progress_update (0.66);
+
+ writer.writer_api_version = 1;
+ writer.write = write_callback;
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! output)
+ {
+ heif_encoder_release (encoder);
+ heif_image_release (image);
+ heif_context_free (context);
+ return FALSE;
+ }
+
+ err = heif_context_write (context, &writer, output);
+
+ if (err.code != 0)
+ {
+ GCancellable *cancellable = g_cancellable_new ();
+
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Writing HEIF image failed: %s"),
+ err.message);
+
+ heif_encoder_release (encoder);
+ heif_image_release (image);
+ heif_context_free (context);
+ return FALSE;
+ }
+
+ g_object_unref (output);
+
+ heif_encoder_release (encoder);
+ heif_image_release (image);
+ heif_context_free (context);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+
+/* the load dialog */
+
+#define MAX_THUMBNAIL_SIZE 320
+
+typedef struct _HeifImage HeifImage;
+
+struct _HeifImage
+{
+ uint32_t ID;
+ gchar caption[100];
+ struct heif_image *thumbnail;
+ gint width;
+ gint height;
+};
+
+static gboolean
+load_thumbnails (struct heif_context *heif,
+ HeifImage *images)
+{
+ guint32 *IDs;
+ gint n_images;
+ gint i;
+
+ n_images = heif_context_get_number_of_top_level_images (heif);
+
+ /* get list of all (top level) image IDs */
+
+ IDs = g_alloca (n_images * sizeof (guint32));
+
+ heif_context_get_list_of_top_level_image_IDs (heif, IDs, n_images);
+
+
+ /* Load a thumbnail for each image. */
+
+ for (i = 0; i < n_images; i++)
+ {
+ struct heif_image_handle *handle = NULL;
+ struct heif_error err;
+ gint width;
+ gint height;
+ struct heif_image_handle *thumbnail_handle = NULL;
+ heif_item_id thumbnail_ID;
+ gint n_thumbnails;
+ struct heif_image *thumbnail_img = NULL;
+ gint thumbnail_width;
+ gint thumbnail_height;
+
+ images[i].ID = IDs[i];
+ images[i].caption[0] = 0;
+ images[i].thumbnail = NULL;
+
+ /* get image handle */
+
+ err = heif_context_get_image_handle (heif, IDs[i], &handle);
+ if (err.code)
+ {
+ gimp_message (err.message);
+ continue;
+ }
+
+ /* generate image caption */
+
+ width = heif_image_handle_get_width (handle);
+ height = heif_image_handle_get_height (handle);
+
+ if (heif_image_handle_is_primary_image (handle))
+ {
+ g_snprintf (images[i].caption, sizeof (images[i].caption),
+ "%dx%d (%s)", width, height, _("primary"));
+ }
+ else
+ {
+ g_snprintf (images[i].caption, sizeof (images[i].caption),
+ "%dx%d", width, height);
+ }
+
+ /* get handle to thumbnail image
+ *
+ * if there is no thumbnail image, just the the image itself
+ * (will be scaled down later)
+ */
+
+ n_thumbnails = heif_image_handle_get_list_of_thumbnail_IDs (handle,
+ &thumbnail_ID,
+ 1);
+
+ if (n_thumbnails > 0)
+ {
+ err = heif_image_handle_get_thumbnail (handle, thumbnail_ID,
+ &thumbnail_handle);
+ if (err.code)
+ {
+ gimp_message (err.message);
+ continue;
+ }
+ }
+ else
+ {
+ err = heif_context_get_image_handle (heif, IDs[i], &thumbnail_handle);
+ if (err.code)
+ {
+ gimp_message (err.message);
+ continue;
+ }
+ }
+
+ /* decode the thumbnail image */
+
+ err = heif_decode_image (thumbnail_handle,
+ &thumbnail_img,
+ heif_colorspace_RGB,
+ heif_chroma_interleaved_RGB,
+ NULL);
+ if (err.code)
+ {
+ gimp_message (err.message);
+ continue;
+ }
+
+ /* if thumbnail image size exceeds the maximum, scale it down */
+
+ thumbnail_width = heif_image_handle_get_width (thumbnail_handle);
+ thumbnail_height = heif_image_handle_get_height (thumbnail_handle);
+
+ if (thumbnail_width > MAX_THUMBNAIL_SIZE ||
+ thumbnail_height > MAX_THUMBNAIL_SIZE)
+ {
+ /* compute scaling factor to fit into a max sized box */
+
+ gfloat factor_h = thumbnail_width / (gfloat) MAX_THUMBNAIL_SIZE;
+ gfloat factor_v = thumbnail_height / (gfloat) MAX_THUMBNAIL_SIZE;
+ gint new_width, new_height;
+ struct heif_image *scaled_img = NULL;
+
+ if (factor_v > factor_h)
+ {
+ new_height = MAX_THUMBNAIL_SIZE;
+ new_width = thumbnail_width / factor_v;
+ }
+ else
+ {
+ new_height = thumbnail_height / factor_h;
+ new_width = MAX_THUMBNAIL_SIZE;
+ }
+
+ /* scale the image */
+
+ err = heif_image_scale_image (thumbnail_img,
+ &scaled_img,
+ new_width, new_height,
+ NULL);
+ if (err.code)
+ {
+ gimp_message (err.message);
+ continue;
+ }
+
+ /* release the old image and only keep the scaled down version */
+
+ heif_image_release (thumbnail_img);
+ thumbnail_img = scaled_img;
+
+ thumbnail_width = new_width;
+ thumbnail_height = new_height;
+ }
+
+ heif_image_handle_release (thumbnail_handle);
+ heif_image_handle_release (handle);
+
+ /* remember the HEIF thumbnail image (we need it for the GdkPixbuf) */
+
+ images[i].thumbnail = thumbnail_img;
+
+ images[i].width = thumbnail_width;
+ images[i].height = thumbnail_height;
+ }
+
+ return TRUE;
+}
+
+static void
+load_dialog_item_activated (GtkIconView *icon_view,
+ GtkTreePath *path,
+ GtkDialog *dialog)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_OK);
+}
+
+static gboolean
+load_dialog (struct heif_context *heif,
+ uint32_t *selected_image)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ HeifImage *heif_images;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ GtkWidget *scrolled_window;
+ GtkWidget *icon_view;
+ GtkCellRenderer *renderer;
+ gint n_images;
+ gint i;
+ gint selected_idx = -1;
+ gboolean run = FALSE;
+
+ n_images = heif_context_get_number_of_top_level_images (heif);
+
+ heif_images = g_alloca (n_images * sizeof (HeifImage));
+
+ if (! load_thumbnails (heif, heif_images))
+ return FALSE;
+
+ dialog = gimp_dialog_new (_("Load HEIF Image"), PLUG_IN_BINARY,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+
+ frame = gimp_frame_new (_("Select Image"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ /* prepare list store with all thumbnails and caption */
+
+ list_store = gtk_list_store_new (2, G_TYPE_STRING, GDK_TYPE_PIXBUF);
+
+ for (i = 0; i < n_images; i++)
+ {
+ GdkPixbuf *pixbuf;
+ const guint8 *data;
+ gint stride;
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter, 0, heif_images[i].caption, -1);
+
+ data = heif_image_get_plane_readonly (heif_images[i].thumbnail,
+ heif_channel_interleaved,
+ &stride);
+
+ pixbuf = gdk_pixbuf_new_from_data (data,
+ GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ heif_images[i].width,
+ heif_images[i].height,
+ stride,
+ NULL,
+ NULL);
+
+ gtk_list_store_set (list_store, &iter, 1, pixbuf, -1);
+ }
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request (scrolled_window,
+ 2 * MAX_THUMBNAIL_SIZE,
+ 1.5 * MAX_THUMBNAIL_SIZE);
+ gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
+ gtk_widget_show (scrolled_window);
+
+ icon_view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (list_store));
+ gtk_container_add (GTK_CONTAINER (scrolled_window), icon_view);
+ gtk_widget_show (icon_view);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), renderer, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer,
+ "pixbuf", 1,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (icon_view), renderer, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (icon_view), renderer,
+ "text", 0,
+ NULL);
+ g_object_set (renderer,
+ "alignment", PANGO_ALIGN_CENTER,
+ "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "xalign", 0.5,
+ "yalign", 0.0,
+ NULL);
+
+ g_signal_connect (icon_view, "item-activated",
+ G_CALLBACK (load_dialog_item_activated),
+ dialog);
+
+ /* pre-select the primary image */
+
+ for (i = 0; i < n_images; i++)
+ {
+ if (heif_images[i].ID == *selected_image)
+ {
+ selected_idx = i;
+ break;
+ }
+ }
+
+ if (selected_idx != -1)
+ {
+ GtkTreePath *path = gtk_tree_path_new_from_indices (selected_idx, -1);
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (icon_view), path);
+ gtk_tree_path_free (path);
+ }
+
+ gtk_widget_show (main_vbox);
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ GList *selected_items =
+ gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
+
+ if (selected_items)
+ {
+ GtkTreePath *path = selected_items->data;
+ gint *indices = gtk_tree_path_get_indices (path);
+
+ *selected_image = heif_images[indices[0]].ID;
+
+ g_list_free_full (selected_items,
+ (GDestroyNotify) gtk_tree_path_free);
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+
+ /* release thumbnail images */
+
+ for (i = 0 ; i < n_images; i++)
+ heif_image_release (heif_images[i].thumbnail);
+
+ return run;
+}
+
+
+/* the save dialog */
+
+static void
+save_dialog_lossless_button_toggled (GtkToggleButton *source,
+ GtkWidget *hbox)
+{
+ gboolean lossless = gtk_toggle_button_get_active (source);
+
+ gtk_widget_set_sensitive (hbox, ! lossless);
+}
+
+gboolean
+save_dialog (SaveParams *params,
+ gint32 image_ID,
+ const gchar *procname)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *lossless_button;
+ GtkWidget *frame;
+#if LIBHEIF_HAVE_VERSION(1,4,0)
+ GtkWidget *profile_button;
+#endif
+ GtkWidget *quality_slider;
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ GtkWidget *table;
+ GtkWidget *label2;
+ GtkWidget *combo;
+#endif
+ gboolean run = FALSE;
+
+ dialog = gimp_export_dialog_new (g_strcmp0 (procname, SAVE_PROC_AV1) == 0?
+ "AVIF" : "HEIF",
+ PLUG_IN_BINARY, procname);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ main_vbox, TRUE, TRUE, 0);
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ lossless_button = gtk_check_button_new_with_mnemonic (_("Nearly _lossless"));
+ gtk_frame_set_label_widget (GTK_FRAME (frame), lossless_button);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ label = gtk_label_new_with_mnemonic (_("_Quality:"));
+ quality_slider = gtk_hscale_new_with_range (0, 100, 5);
+ gtk_scale_set_value_pos (GTK_SCALE (quality_slider), GTK_POS_RIGHT);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), quality_slider, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ gtk_range_set_value (GTK_RANGE (quality_slider), params->quality);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lossless_button),
+ params->lossless);
+ gtk_widget_set_sensitive (hbox, !params->lossless);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), quality_slider);
+
+ g_signal_connect (lossless_button, "toggled",
+ G_CALLBACK (save_dialog_lossless_button_toggled),
+ hbox);
+
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ switch (gimp_image_get_precision (image_ID))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ /* image is 8bit depth */
+ params->save_bit_depth = 8;
+ break;
+ default:
+ /* high bit depth */
+ if (params->save_bit_depth < 12)
+ {
+ params->save_bit_depth = 10;
+ }
+ else if (params->save_bit_depth > 12)
+ {
+ params->save_bit_depth = 12;
+ }
+ break;
+ }
+
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label2 = gtk_label_new (_("Bit depth:"));
+ gtk_label_set_xalign (GTK_LABEL (label2), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label2, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label2);
+
+ combo = gimp_int_combo_box_new (_("8 bit/channel"), 8,
+ _("10 bit/channel"), 10,
+ _("12 bit/channel"), 12,
+ NULL);
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), params->save_bit_depth);
+#endif
+
+#if LIBHEIF_HAVE_VERSION(1,4,0)
+ profile_button = gtk_check_button_new_with_mnemonic (_("Save color _profile"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (profile_button),
+ params->save_profile);
+ g_signal_connect (profile_button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->save_profile);
+ gtk_box_pack_start (GTK_BOX (main_vbox), profile_button, FALSE, FALSE, 0);
+#endif
+
+ gtk_widget_show_all (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ params->quality = gtk_range_get_value (GTK_RANGE (quality_slider));
+ params->lossless = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lossless_button));
+#if LIBHEIF_HAVE_VERSION(1,8,0)
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &params->save_bit_depth);
+#endif
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/file-html-table.c b/plug-ins/common/file-html-table.c
new file mode 100644
index 0000000..0af9eb3
--- /dev/null
+++ b/plug-ins/common/file-html-table.c
@@ -0,0 +1,750 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GTM plug-in --- GIMP Table Magic
+ * Allows images to be exported as HTML tables with different colored cells.
+ * It doesn't have very much practical use other than being able to
+ * easily design a table by "painting" it in GIMP, or to make small HTML
+ * table images/icons.
+ *
+ * Copyright (C) 1997 Daniel Dunbar
+ * Email: ddunbar@diads.com
+ * WWW: http://millennium.diads.com/gimp/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Version 1.0:
+ * Once I first found out that it was possible to have pixel level control
+ * of HTML tables I instantly realized that it would be possible, however
+ * pointless, to save an image as a, albeit huge, HTML table.
+ *
+ * One night when I was feeling in an adventourously stupid programming mood
+ * I decided to write a program to do it.
+ *
+ * At first I just wrote a really ugly hack to do it, which I then planned
+ * on using once just to see how it worked, and then posting a URL and
+ * laughing about it on #gimp. As it turns out, tigert thought it actually
+ * had potential to be a useful plugin, so I started adding features and
+ * and making a nice UI.
+ *
+ * It's still not very useful, but I did manage to significantly improve my
+ * C programming skills in the process, so it was worth it.
+ *
+ * If you happen to find it useful I would appreciate any email about it.
+ * - Daniel Dunbar
+ * ddunbar@diads.com
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-gtm-save"
+#define PLUG_IN_BINARY "file-html-table"
+#define PLUG_IN_ROLE "gimp-file-html-table"
+
+
+/* Typedefs */
+
+typedef struct
+{
+ gchar captiontxt[256];
+ gchar cellcontent[256];
+ gchar clwidth[256];
+ gchar clheight[256];
+ gboolean fulldoc;
+ gboolean caption;
+ gint border;
+ gboolean spantags;
+ gboolean tdcomp;
+ gint cellpadding;
+ gint cellspacing;
+} GTMValues;
+
+
+/* Declare some local functions */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_image (GFile *file,
+ GeglBuffer *buffer,
+ GError **error);
+static gboolean save_dialog (gint32 image_ID);
+
+static gboolean print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 0);
+static gboolean color_comp (guchar *buffer,
+ guchar *buf2);
+static void entry_changed_callback (GtkEntry *entry,
+ gchar *string);
+
+
+/* Variables */
+
+static GTMValues gtmvals =
+{
+ "Made with GIMP Table Magic", /* caption text */
+ "&nbsp;", /* cellcontent text */
+ "", /* cell width text */
+ "", /* cell height text */
+ TRUE, /* fulldoc */
+ FALSE, /* caption */
+ 2, /* border */
+ FALSE, /* spantags */
+ FALSE, /* tdcomp */
+ 4, /* cellpadding */
+ 0 /* cellspacing */
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "GIMP Table Magic",
+ "Allows you to draw an HTML table in GIMP. See help for more info.",
+ "Daniel Dunbar",
+ "Daniel Dunbar",
+ "1998",
+ _("HTML table"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "text/html");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "html,htm", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ gimp_get_data (SAVE_PROC, &gtmvals);
+
+ if (save_dialog (param[1].data.d_int32))
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (param[2].data.d_int32);
+
+ if (save_image (g_file_new_for_uri (param[3].data.d_string),
+ buffer, &error))
+ {
+ gimp_set_data (SAVE_PROC, &gtmvals, sizeof (GTMValues));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_object_unref (buffer);
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+save_image (GFile *file,
+ GeglBuffer *buffer,
+ GError **error)
+{
+ const Babl *format = babl_format ("R'G'B'A u8");
+ GeglSampler *sampler;
+ GCancellable *cancellable;
+ GOutputStream *output;
+ gint row, col;
+ gint cols, rows;
+ gint x, y;
+ gint colcount, colspan, rowspan;
+ gint *palloc;
+ guchar *buf, *buf2;
+ gchar *width = NULL;
+ gchar *height = NULL;
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
+
+ palloc = g_new (int, rows * cols);
+
+ if (gtmvals.fulldoc)
+ {
+ if (! print (output, error,
+ "<HTML>\n<HEAD><TITLE>%s</TITLE></HEAD>\n<BODY>\n",
+ gimp_file_get_utf8_name (file)) ||
+ ! print (output, error, "<H1>%s</H1>\n",
+ gimp_file_get_utf8_name (file)))
+ {
+ goto fail;
+ }
+ }
+
+ if (! print (output, error,
+ "<TABLE BORDER=%d CELLPADDING=%d CELLSPACING=%d>\n",
+ gtmvals.border, gtmvals.cellpadding, gtmvals.cellspacing))
+ goto fail;
+
+ if (gtmvals.caption)
+ {
+ if (! print (output, error, "<CAPTION>%s</CAPTION>\n",
+ gtmvals.captiontxt))
+ goto fail;
+ }
+
+ buf = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
+ buf2 = g_newa (guchar, babl_format_get_bytes_per_pixel (format));
+
+ if (strcmp (gtmvals.clwidth, "") != 0)
+ {
+ width = g_strdup_printf (" WIDTH=\"%s\"", gtmvals.clwidth);
+ }
+
+ if (strcmp (gtmvals.clheight, "") != 0)
+ {
+ height = g_strdup_printf (" HEIGHT=\"%s\" ", gtmvals.clheight);
+ }
+
+ if (! width)
+ width = g_strdup (" ");
+
+ if (! height)
+ height = g_strdup (" ");
+
+ /* Initialize array to hold ROWSPAN and COLSPAN cell allocation table */
+
+ for (row = 0; row < rows; row++)
+ for (col = 0; col < cols; col++)
+ palloc[cols * row + col] = 1;
+
+ colspan = 0;
+ rowspan = 0;
+
+ for (y = 0; y < rows; y++)
+ {
+ if (! print (output, error, " <TR>\n"))
+ goto fail;
+
+ for (x = 0; x < cols; x++)
+ {
+ gegl_sampler_get (sampler, x, y, NULL, buf, GEGL_ABYSS_NONE);
+
+ /* Determine ROWSPAN and COLSPAN */
+
+ if (gtmvals.spantags)
+ {
+ col = x;
+ row = y;
+ colcount = 0;
+ colspan = 0;
+ rowspan = 0;
+
+ gegl_sampler_get (sampler, col, row, NULL, buf2, GEGL_ABYSS_NONE);
+
+ while (color_comp (buf, buf2) &&
+ palloc[cols * row + col] == 1 &&
+ row < rows)
+ {
+ while (color_comp (buf, buf2) &&
+ palloc[cols * row + col] == 1 &&
+ col < cols)
+ {
+ colcount++;
+ col++;
+
+ gegl_sampler_get (sampler,
+ col, row, NULL, buf2, GEGL_ABYSS_NONE);
+ }
+
+ if (colcount != 0)
+ {
+ row++;
+ rowspan++;
+ }
+
+ if (colcount < colspan || colspan == 0)
+ colspan = colcount;
+
+ col = x;
+ colcount = 0;
+
+ gegl_sampler_get (sampler,
+ col, row, NULL, buf2, GEGL_ABYSS_NONE);
+ }
+
+ if (colspan > 1 || rowspan > 1)
+ {
+ for (row = 0; row < rowspan; row++)
+ for (col = 0; col < colspan; col++)
+ palloc[cols * (row + y) + (col + x)] = 0;
+
+ palloc[cols * y + x] = 2;
+ }
+ }
+
+ if (palloc[cols * y + x] == 1)
+ {
+ if (! print (output, error,
+ " <TD%s%sBGCOLOR=#%02x%02x%02x>",
+ width, height, buf[0], buf[1], buf[2]))
+ goto fail;
+ }
+
+ if (palloc[cols * y + x] == 2)
+ {
+ if (! print (output, error,
+ " <TD ROWSPAN=\"%d\" COLSPAN=\"%d\"%s%sBGCOLOR=#%02x%02x%02x>",
+ rowspan, colspan, width, height,
+ buf[0], buf[1], buf[2]))
+ goto fail;
+ }
+
+ if (palloc[cols * y + x] != 0)
+ {
+ if (gtmvals.tdcomp)
+ {
+ if (! print (output, error,
+ "%s</TD>\n", gtmvals.cellcontent))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "\n %s\n </TD>\n", gtmvals.cellcontent))
+ goto fail;
+ }
+ }
+ }
+
+ if (! print (output, error, " </TR>\n"))
+ goto fail;
+
+ gimp_progress_update ((double) y / (double) rows);
+ }
+
+ if (gtmvals.fulldoc)
+ {
+ if (! print (output, error, "</TABLE></BODY></HTML>\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error, "</TABLE>\n"))
+ goto fail;
+ }
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_object_unref (output);
+ g_object_unref (sampler);
+ g_free (width);
+ g_free (height);
+ g_free (palloc);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_object_unref (output);
+ g_object_unref (sampler);
+ g_free (width);
+ g_free (height);
+ g_free (palloc);
+
+ return FALSE;
+}
+
+static gint
+save_dialog (gint32 image_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *entry;
+ GtkWidget *toggle;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_export_dialog_new (_("HTML table"), PLUG_IN_BINARY, SAVE_PROC);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ main_vbox, TRUE, TRUE, 0);
+
+ if (gimp_image_width (image_ID) * gimp_image_height (image_ID) > 4096)
+ {
+ GtkWidget *eek;
+ GtkWidget *label;
+ GtkWidget *hbox;
+
+ frame = gimp_frame_new (_("Warning"));
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ eek = gtk_image_new_from_icon_name (GIMP_ICON_WILBER_EEK,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox), eek, FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("You are about to create a huge\n"
+ "HTML file which will most likely\n"
+ "crash your browser."));
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+ gtk_widget_show_all (frame);
+ }
+
+ /* HTML Page Options */
+ frame = gimp_frame_new (_("HTML Page Options"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Generate full HTML document"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtmvals.fulldoc);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("If checked GTM will output a full HTML document "
+ "with <HTML>, <BODY>, etc. tags instead of just "
+ "the table html."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &gtmvals.fulldoc);
+
+ gtk_widget_show (main_vbox);
+ gtk_widget_show (frame);
+
+ /* HTML Table Creation Options */
+ frame = gimp_frame_new (_("Table Creation Options"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Use cellspan"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtmvals.spantags);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("If checked GTM will replace any rectangular "
+ "sections of identically colored blocks with one "
+ "large cell with ROWSPAN and COLSPAN values."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &gtmvals.spantags);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Co_mpress TD tags"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtmvals.tdcomp);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Checking this tag will cause GTM to leave no "
+ "whitespace between the TD tags and the "
+ "cellcontent. This is only necessary for pixel "
+ "level positioning control."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &gtmvals.tdcomp);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("C_aption"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), gtmvals.caption);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Check if you would like to have the table "
+ "captioned."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &gtmvals.caption);
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), gtmvals.captiontxt);
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 2, 3,
+ GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ gtk_widget_show (entry);
+
+ gimp_help_set_help_data (entry, _("The text for the table caption."), NULL);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_changed_callback),
+ gtmvals.captiontxt);
+
+ g_object_bind_property (toggle, "active",
+ entry, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), gtmvals.cellcontent);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("C_ell content:"), 0.0, 0.5,
+ entry, 1, FALSE);
+ gtk_widget_show (entry);
+
+ gimp_help_set_help_data (entry, _("The text to go into each cell."), NULL);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_changed_callback),
+ gtmvals.cellcontent);
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ /* HTML Table Options */
+ frame = gimp_frame_new (_("Table Options"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ table = gtk_table_new (5, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (gtmvals.border,
+ 0, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Border:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ gimp_help_set_help_data (spinbutton,
+ _("The number of pixels in the table border."),
+ NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gtmvals.border);
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 60, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), gtmvals.clwidth);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Width:"), 0.0, 0.5,
+ entry, 1, TRUE);
+
+ gimp_help_set_help_data (entry,
+ _("The width for each table cell. "
+ "Can be a number or a percent."),
+ NULL);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_changed_callback),
+ gtmvals.clwidth);
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 60, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), gtmvals.clheight);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Height:"), 0.0, 0.5,
+ entry, 1, TRUE);
+
+ gimp_help_set_help_data (entry,
+ _("The height for each table cell. "
+ "Can be a number or a percent."),
+ NULL);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (entry_changed_callback),
+ gtmvals.clheight);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (gtmvals.cellpadding,
+ 0, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("Cell-_padding:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ gimp_help_set_help_data (spinbutton,
+ _("The amount of cell padding."), NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gtmvals.cellpadding);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (gtmvals.cellspacing,
+ 0, 1000, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("Cell-_spacing:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ gimp_help_set_help_data (spinbutton,
+ _("The amount of cell spacing."), NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gtmvals.cellspacing);
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static gboolean
+print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (output, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
+
+static gboolean
+color_comp (guchar *buf,
+ guchar *buf2)
+{
+ return (buf[0] == buf2[0] &&
+ buf[1] == buf2[1] &&
+ buf[2] == buf2[2]);
+}
+
+/* Export interface functions */
+
+static void
+entry_changed_callback (GtkEntry *entry,
+ gchar *string)
+{
+ strncpy (string, gtk_entry_get_text (entry), 255);
+}
diff --git a/plug-ins/common/file-jp2-load.c b/plug-ins/common/file-jp2-load.c
new file mode 100644
index 0000000..9ab43b5
--- /dev/null
+++ b/plug-ins/common/file-jp2-load.c
@@ -0,0 +1,1344 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-jp2.c -- JPEG 2000 file format plug-in
+ * Copyright (C) 2009 Aurimas Juška <aurimas.juska@gmail.com>
+ * Copyright (C) 2004 Florian Traverse <florian.traverse@cpe.fr>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Portions of this plug-in code (color conversion, etc.) were imported
+ * from the OpenJPEG project covered under the following GNU GPL
+ * compatible license:
+ *
+ * The copyright in this software is being made available under the 2-clauses
+ * BSD License, included below. This software may be subject to other third
+ * party and contributor rights, including patent rights, and no such rights
+ * are granted under this license.
+ *
+ * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
+ * Copyright (c) 2002-2014, Professor Benoit Macq
+ * Copyright (c) 2001-2003, David Janssens
+ * Copyright (c) 2002-2003, Yannick Verschueren
+ * Copyright (c) 2003-2007, Francois-Olivier Devaux
+ * Copyright (c) 2003-2014, Antonin Descampe
+ * Copyright (c) 2005, Herve Drolon, FreeImage Team
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include <openjpeg.h>
+
+
+#define LOAD_JP2_PROC "file-jp2-load"
+#define LOAD_J2K_PROC "file-j2k-load"
+#define PLUG_IN_BINARY "file-jp2-load"
+#define PLUG_IN_ROLE "gimp-file-jp2-load"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ OPJ_CODEC_FORMAT format,
+ OPJ_COLOR_SPACE color_space,
+ gboolean interactive,
+ gboolean *profile_loaded,
+ GError **error);
+
+static OPJ_COLOR_SPACE open_dialog (const gchar *filename,
+ OPJ_CODEC_FORMAT format,
+ gint num_components,
+ GError **error);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef jp2_load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef j2k_load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "colorspace", "Color space { UNKNOWN (0), GRAYSCALE (1), RGB (2), CMYK (3), YCbCr (4), xvYCC (5) }" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gimp_install_procedure (LOAD_JP2_PROC,
+ "Loads JPEG 2000 images.",
+ "The JPEG 2000 image loader.",
+ "Mukund Sivaraman",
+ "Mukund Sivaraman",
+ "2009",
+ N_("JPEG 2000 image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (jp2_load_args),
+ G_N_ELEMENTS (load_return_vals),
+ jp2_load_args, load_return_vals);
+ /*
+ * XXX: more complete magic number would be:
+ * "0,string,\x00\x00\x00\x0C\x6A\x50\x20\x20\x0D\x0A\x87\x0A"
+ * But the '\0' character makes problem in a 0-terminated string
+ * obviously, as well as some other space characters, it would seem.
+ * The below smaller version seems ok and not interfering with other
+ * formats.
+ */
+ gimp_register_magic_load_handler (LOAD_JP2_PROC,
+ "jp2",
+ "",
+ "3,string,\x0CjP");
+ gimp_register_file_handler_mime (LOAD_JP2_PROC, "image/jp2");
+
+ gimp_install_procedure (LOAD_J2K_PROC,
+ "Loads JPEG 2000 codestream.",
+ "Loads JPEG 2000 codestream. "
+ "If the color space is set to UNKNOWN (0), "
+ "we will try to guess, which is only possible "
+ "for few spaces (such as grayscale). Most "
+ "such calls will fail. You are rather "
+ "expected to know the color space of your data.",
+ "Jehan",
+ "Jehan",
+ "2009",
+ N_("JPEG 2000 codestream"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (j2k_load_args),
+ G_N_ELEMENTS (load_return_vals),
+ j2k_load_args, load_return_vals);
+ gimp_register_magic_load_handler (LOAD_J2K_PROC,
+ "j2k,j2c,jpc",
+ "",
+ "0,string,\xff\x4f\xff\x51\x00");
+ gimp_register_file_handler_mime (LOAD_J2K_PROC, "image/x-jp2-codestream");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint image_ID;
+ gboolean profile_loaded = FALSE;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_JP2_PROC) == 0 ||
+ strcmp (name, LOAD_J2K_PROC) == 0)
+ {
+ OPJ_COLOR_SPACE color_space = OPJ_CLRSPC_UNKNOWN;
+ gboolean interactive;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ interactive = TRUE;
+ break;
+
+ default:
+ if (strcmp (name, LOAD_J2K_PROC) == 0)
+ {
+ /* Order is not the same as OpenJPEG enum on purpose,
+ * since it's better to not rely on a given order or
+ * on enum values.
+ */
+ switch (param[3].data.d_int32)
+ {
+ case 1:
+ color_space = OPJ_CLRSPC_GRAY;
+ break;
+ case 2:
+ color_space = OPJ_CLRSPC_SRGB;
+ break;
+ case 3:
+ color_space = OPJ_CLRSPC_CMYK;
+ break;
+ case 4:
+ color_space = OPJ_CLRSPC_SYCC;
+ break;
+ case 5:
+ color_space = OPJ_CLRSPC_EYCC;
+ break;
+ default:
+ /* Stays unknown. */
+ break;
+ }
+ }
+ interactive = FALSE;
+ break;
+ }
+
+ if (strcmp (name, LOAD_JP2_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, OPJ_CODEC_JP2,
+ color_space, interactive, &profile_loaded,
+ &error);
+ }
+ else /* strcmp (name, LOAD_J2K_PROC) == 0 */
+ {
+ image_ID = load_image (param[1].data.d_string, OPJ_CODEC_J2K,
+ color_space, interactive, &profile_loaded,
+ &error);
+ }
+
+ if (image_ID != -1)
+ {
+ GFile *file = g_file_new_for_path (param[1].data.d_string);
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/jp2",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (profile_loaded)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image_ID, "image/jp2",
+ metadata, flags,
+ interactive);
+
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else if (error)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static void
+sycc_to_rgb (int offset,
+ int upb,
+ int y,
+ int cb,
+ int cr,
+ int *out_r,
+ int *out_g,
+ int *out_b)
+{
+ int r, g, b;
+
+ cb -= offset;
+ cr -= offset;
+ r = y + (int) (1.402 * (float) cr);
+
+ if (r < 0)
+ r = 0;
+ else if (r > upb)
+ r = upb;
+ *out_r = r;
+
+ g = y - (int) (0.344 * (float) cb + 0.714 * (float) cr);
+ if (g < 0)
+ g = 0;
+ else if (g > upb)
+ g = upb;
+ *out_g = g;
+
+ b = y + (int) (1.772 * (float) cb);
+ if (b < 0)
+ b = 0;
+ else if (b > upb)
+ b = upb;
+ *out_b = b;
+}
+
+static gboolean
+sycc420_to_rgb (opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b, *nr, *ng, *nb;
+ const int *y, *cb, *cr, *ny;
+ size_t maxw, maxh, max, offx, loopmaxw, offy, loopmaxh;
+ int offset, upb;
+ size_t i;
+
+ upb = (int) img->comps[0].prec;
+ offset = 1 << (upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t) img->comps[0].w;
+ maxh = (size_t) img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int *) malloc (sizeof (int) * max);
+ d1 = g = (int *) malloc (sizeof (int) * max);
+ d2 = b = (int *) malloc (sizeof (int) * max);
+
+ if (r == NULL || g == NULL || b == NULL)
+ {
+ g_warning ("malloc() failed in sycc420_to_rgb()");
+ goto out;
+ }
+
+ /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
+ offx = img->x0 & 1U;
+ loopmaxw = maxw - offx;
+ /* if img->y0 is odd, then first line shall use Cb/Cr = 0 */
+ offy = img->y0 & 1U;
+ loopmaxh = maxh - offy;
+
+ if (offy > 0U)
+ {
+ size_t j;
+
+ for (j = 0; j < maxw; ++j)
+ {
+ sycc_to_rgb (offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ }
+ }
+
+ for (i = 0U; i < (loopmaxh & ~(size_t) 1U); i += 2U)
+ {
+ size_t j;
+
+ ny = y + maxw;
+ nr = r + maxw;
+ ng = g + maxw;
+ nb = b + maxw;
+
+ if (offx > 0U)
+ {
+ sycc_to_rgb (offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+ }
+
+ for (j = 0; j < (loopmaxw & ~(size_t) 1U); j += 2U)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+
+ sycc_to_rgb (offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+
+ ++cb;
+ ++cr;
+ }
+
+ if (j < loopmaxw)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *ny, *cb, *cr, nr, ng, nb);
+ ++ny;
+ ++nr;
+ ++ng;
+ ++nb;
+
+ ++cb;
+ ++cr;
+ }
+
+ y += maxw;
+ r += maxw;
+ g += maxw;
+ b += maxw;
+ }
+
+ if (i < loopmaxh)
+ {
+ size_t j;
+
+ for (j = 0U; j < (maxw & ~(size_t) 1U); j += 2U)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ ++cb;
+ ++cr;
+ }
+
+ if (j < maxw)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ }
+ }
+
+ free (img->comps[0].data);
+ img->comps[0].data = d0;
+ free (img->comps[1].data);
+ img->comps[1].data = d1;
+ free (img->comps[2].data);
+ img->comps[2].data = d2;
+
+ img->comps[1].w = img->comps[2].w = img->comps[0].w;
+ img->comps[1].h = img->comps[2].h = img->comps[0].h;
+ img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
+ img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
+ img->color_space = OPJ_CLRSPC_SRGB;
+
+ return TRUE;
+
+ out:
+ free (r);
+ free (g);
+ free (b);
+ return FALSE;
+}
+
+static gboolean
+sycc422_to_rgb (opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b;
+ const int *y, *cb, *cr;
+ size_t maxw, maxh, max, offx, loopmaxw;
+ int offset, upb;
+ size_t i;
+
+ upb = (int) img->comps[0].prec;
+ offset = 1 <<(upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t) img->comps[0].w;
+ maxh = (size_t) img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int *) malloc (sizeof (int) * max);
+ d1 = g = (int *) malloc (sizeof (int) * max);
+ d2 = b = (int *) malloc (sizeof (int) * max);
+
+ if (r == NULL || g == NULL || b == NULL)
+ {
+ g_warning ("malloc() failed in sycc422_to_rgb()");
+ goto out;
+ }
+
+ /* if img->x0 is odd, then first column shall use Cb/Cr = 0 */
+ offx = img->x0 & 1U;
+ loopmaxw = maxw - offx;
+
+ for (i = 0U; i < maxh; ++i)
+ {
+ size_t j;
+
+ if (offx > 0U)
+ {
+ sycc_to_rgb (offset, upb, *y, 0, 0, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ }
+
+ for (j = 0U; j < (loopmaxw & ~(size_t) 1U); j += 2U)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ ++cb;
+ ++cr;
+ }
+
+ if (j < loopmaxw)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++r;
+ ++g;
+ ++b;
+ ++cb;
+ ++cr;
+ }
+ }
+
+ free (img->comps[0].data);
+ img->comps[0].data = d0;
+ free (img->comps[1].data);
+ img->comps[1].data = d1;
+ free (img->comps[2].data);
+ img->comps[2].data = d2;
+
+ img->comps[1].w = img->comps[2].w = img->comps[0].w;
+ img->comps[1].h = img->comps[2].h = img->comps[0].h;
+ img->comps[1].dx = img->comps[2].dx = img->comps[0].dx;
+ img->comps[1].dy = img->comps[2].dy = img->comps[0].dy;
+ img->color_space = OPJ_CLRSPC_SRGB;
+
+ return (TRUE);
+
+ out:
+ free (r);
+ free (g);
+ free (b);
+ return (FALSE);
+}
+
+static gboolean
+sycc444_to_rgb (opj_image_t *img)
+{
+ int *d0, *d1, *d2, *r, *g, *b;
+ const int *y, *cb, *cr;
+ size_t maxw, maxh, max, i;
+ int offset, upb;
+
+ upb = (int) img->comps[0].prec;
+ offset = 1 << (upb - 1);
+ upb = (1 << upb) - 1;
+
+ maxw = (size_t) img->comps[0].w;
+ maxh = (size_t) img->comps[0].h;
+ max = maxw * maxh;
+
+ y = img->comps[0].data;
+ cb = img->comps[1].data;
+ cr = img->comps[2].data;
+
+ d0 = r = (int *) malloc(sizeof (int) * max);
+ d1 = g = (int *) malloc(sizeof (int) * max);
+ d2 = b = (int *) malloc(sizeof (int) * max);
+
+ if (r == NULL || g == NULL || b == NULL)
+ {
+ g_warning ("malloc() failed in sycc444_to_rgb()");
+ goto out;
+ }
+
+ for (i = 0U; i < max; ++i)
+ {
+ sycc_to_rgb (offset, upb, *y, *cb, *cr, r, g, b);
+ ++y;
+ ++cb;
+ ++cr;
+ ++r;
+ ++g;
+ ++b;
+ }
+
+ free (img->comps[0].data);
+ img->comps[0].data = d0;
+ free (img->comps[1].data);
+ img->comps[1].data = d1;
+ free (img->comps[2].data);
+ img->comps[2].data = d2;
+
+ img->color_space = OPJ_CLRSPC_SRGB;
+ return TRUE;
+
+ out:
+ free (r);
+ free (g);
+ free (b);
+ return FALSE;
+}
+
+static gboolean
+color_sycc_to_rgb (opj_image_t *img)
+{
+ if (img->numcomps < 3)
+ {
+ img->color_space = OPJ_CLRSPC_GRAY;
+ return TRUE;
+ }
+ else if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 2) &&
+ (img->comps[2].dx == 2) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 2) &&
+ (img->comps[2].dy == 2))
+ {
+ /* horizontal and vertical sub-sample */
+ return sycc420_to_rgb (img);
+ }
+ else if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 2) &&
+ (img->comps[2].dx == 2) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 1) &&
+ (img->comps[2].dy == 1))
+ {
+ /* horizontal sub-sample only */
+ return sycc422_to_rgb (img);
+ }
+ else if ((img->comps[0].dx == 1) &&
+ (img->comps[1].dx == 1) &&
+ (img->comps[2].dx == 1) &&
+ (img->comps[0].dy == 1) &&
+ (img->comps[1].dy == 1) &&
+ (img->comps[2].dy == 1))
+ {
+ /* no sub-sample */
+ return sycc444_to_rgb (img);
+ }
+ else
+ {
+ g_warning ("Cannot convert in color_sycc_to_rgb()");
+ return FALSE;
+ }
+}
+
+static gboolean
+color_cmyk_to_rgb (opj_image_t *image)
+{
+ float C, M, Y, K;
+ float sC, sM, sY, sK;
+ unsigned int w, h, max, i;
+
+ w = image->comps[0].w;
+ h = image->comps[0].h;
+
+ if ((image->numcomps < 4) ||
+ (image->comps[0].dx != image->comps[1].dx) ||
+ (image->comps[0].dx != image->comps[2].dx) ||
+ (image->comps[0].dx != image->comps[3].dx) ||
+ (image->comps[0].dy != image->comps[1].dy) ||
+ (image->comps[0].dy != image->comps[2].dy) ||
+ (image->comps[0].dy != image->comps[3].dy))
+ {
+ g_warning ("Cannot convert in color_cmyk_to_rgb()");
+ return FALSE;
+ }
+
+ max = w * h;
+
+ sC = 1.0f / (float) ((1 << image->comps[0].prec) - 1);
+ sM = 1.0f / (float) ((1 << image->comps[1].prec) - 1);
+ sY = 1.0f / (float) ((1 << image->comps[2].prec) - 1);
+ sK = 1.0f / (float) ((1 << image->comps[3].prec) - 1);
+
+ for (i = 0; i < max; ++i)
+ {
+ /* CMYK values from 0 to 1 */
+ C = (float) (image->comps[0].data[i]) * sC;
+ M = (float) (image->comps[1].data[i]) * sM;
+ Y = (float) (image->comps[2].data[i]) * sY;
+ K = (float) (image->comps[3].data[i]) * sK;
+
+ /* Invert all CMYK values */
+ C = 1.0f - C;
+ M = 1.0f - M;
+ Y = 1.0f - Y;
+ K = 1.0f - K;
+
+ /* CMYK -> RGB : RGB results from 0 to 255 */
+ image->comps[0].data[i] = (int) (255.0f * C * K); /* R */
+ image->comps[1].data[i] = (int) (255.0f * M * K); /* G */
+ image->comps[2].data[i] = (int) (255.0f * Y * K); /* B */
+ }
+
+ free (image->comps[3].data);
+ image->comps[3].data = NULL;
+ image->comps[0].prec = 8;
+ image->comps[1].prec = 8;
+ image->comps[2].prec = 8;
+ image->numcomps -= 1;
+ image->color_space = OPJ_CLRSPC_SRGB;
+
+ for (i = 3; i < image->numcomps; ++i)
+ {
+ memcpy(&(image->comps[i]), &(image->comps[i + 1]),
+ sizeof (image->comps[i]));
+ }
+
+ return TRUE;
+}
+
+/*
+ * This code has been adopted from sjpx_openjpeg.c of ghostscript
+ */
+static gboolean
+color_esycc_to_rgb (opj_image_t *image)
+{
+ int y, cb, cr, sign1, sign2, val;
+ unsigned int w, h, max, i;
+ int flip_value;
+ int max_value;
+
+ flip_value = (1 << (image->comps[0].prec - 1));
+ max_value = (1 << image->comps[0].prec) - 1;
+
+ if ((image->numcomps < 3) ||
+ (image->comps[0].dx != image->comps[1].dx) ||
+ (image->comps[0].dx != image->comps[2].dx) ||
+ (image->comps[0].dy != image->comps[1].dy) ||
+ (image->comps[0].dy != image->comps[2].dy))
+ {
+ g_warning ("Cannot convert in color_esycc_to_rgb()");
+ return FALSE;
+ }
+
+ w = image->comps[0].w;
+ h = image->comps[0].h;
+
+ sign1 = (int)image->comps[1].sgnd;
+ sign2 = (int)image->comps[2].sgnd;
+
+ max = w * h;
+
+ for (i = 0; i < max; ++i)
+ {
+
+ y = image->comps[0].data[i];
+ cb = image->comps[1].data[i];
+ cr = image->comps[2].data[i];
+
+ if (! sign1)
+ cb -= flip_value;
+
+ if (! sign2)
+ cr -= flip_value;
+
+ val = (int) ((float) y - (float) 0.0000368 *
+ (float) cb + (float) 1.40199 * (float) cr + (float) 0.5);
+
+ if (val > max_value)
+ val = max_value;
+ else if (val < 0)
+ val = 0;
+ image->comps[0].data[i] = val;
+
+ val = (int) ((float) 1.0003 * (float) y - (float) 0.344125 *
+ (float) cb - (float) 0.7141128 * (float) cr + (float) 0.5);
+
+ if (val > max_value)
+ val = max_value;
+ else if(val < 0)
+ val = 0;
+ image->comps[1].data[i] = val;
+
+ val = (int) ((float) 0.999823 * (float) y + (float) 1.77204 *
+ (float) cb - (float) 0.000008 * (float) cr + (float) 0.5);
+
+ if (val > max_value)
+ val = max_value;
+ else if (val < 0)
+ val = 0;
+ image->comps[2].data[i] = val;
+ }
+
+ image->color_space = OPJ_CLRSPC_SRGB;
+ return TRUE;
+}
+
+/*
+ * get_valid_precision() converts given precision to standard precision
+ * of gimp i.e. 8, 16, 32
+ * e.g 12-bit to 16-bit , 24-bit to 32-bit
+*/
+static gint
+get_valid_precision (gint precision_actual)
+{
+ if (precision_actual <= 8)
+ return 8;
+ else if (precision_actual <= 16)
+ return 16;
+ else
+ return 32;
+}
+
+static GimpPrecision
+get_image_precision (gint precision,
+ gboolean linear)
+{
+ switch (precision)
+ {
+ case 32:
+ if (linear)
+ return GIMP_PRECISION_U32_LINEAR;
+ return GIMP_PRECISION_U32_GAMMA;
+ case 16:
+ if (linear)
+ return GIMP_PRECISION_U16_LINEAR;
+ return GIMP_PRECISION_U16_GAMMA;
+ default:
+ if (linear)
+ return GIMP_PRECISION_U8_LINEAR;
+ return GIMP_PRECISION_U8_GAMMA;
+ }
+}
+
+static OPJ_COLOR_SPACE
+open_dialog (const gchar *filename,
+ OPJ_CODEC_FORMAT format,
+ gint num_components,
+ GError **error)
+{
+ const gchar *title;
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *combo = NULL;
+ OPJ_COLOR_SPACE color_space = OPJ_CLRSPC_SRGB;
+
+ if (format == OPJ_CODEC_J2K)
+ /* Not having color information is expected. */
+ title = "Opening JPEG 2000 codestream";
+ else
+ /* Unexpected, but let's be a bit flexible and ask. */
+ title = "JPEG 2000 image with no color space";
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (title, PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func,
+ (format == OPJ_CODEC_J2K) ? LOAD_J2K_PROC : LOAD_JP2_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_container_add (GTK_CONTAINER (main_vbox), table);
+ gtk_widget_show (table);
+
+ if (num_components == 3)
+ {
+ /* Can be RGB, YUV and YCC. */
+ combo = gimp_int_combo_box_new (_("sRGB"), OPJ_CLRSPC_SRGB,
+ _("YCbCr"), OPJ_CLRSPC_SYCC,
+ _("xvYCC"), OPJ_CLRSPC_EYCC,
+ NULL);
+ }
+ else if (num_components == 4)
+ {
+ /* Can be RGB, YUV and YCC with alpha or CMYK. */
+ combo = gimp_int_combo_box_new (_("sRGB"), OPJ_CLRSPC_SRGB,
+ _("YCbCr"), OPJ_CLRSPC_SYCC,
+ _("xvYCC"), OPJ_CLRSPC_EYCC,
+ _("CMYK"), OPJ_CLRSPC_CMYK,
+ NULL);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported JPEG 2000%s '%s' with %d components."),
+ (format == OPJ_CODEC_J2K) ? " codestream" : "",
+ gimp_filename_to_utf8 (filename), num_components);
+ color_space = OPJ_CLRSPC_UNKNOWN;
+ }
+
+ if (combo)
+ {
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Color space:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ gtk_widget_show (combo);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &color_space);
+
+ /* By default, RGB is active. */
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), OPJ_CLRSPC_SRGB);
+
+ gtk_widget_show (dialog);
+
+ if (gimp_dialog_run (GIMP_DIALOG (dialog)) != GTK_RESPONSE_OK)
+ {
+ /* Do not set an error here. The import was simply canceled.
+ * No error occurred. */
+ color_space = OPJ_CLRSPC_UNKNOWN;
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return color_space;
+}
+
+static gint32
+load_image (const gchar *filename,
+ OPJ_CODEC_FORMAT format,
+ OPJ_COLOR_SPACE color_space,
+ gboolean interactive,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ opj_stream_t *stream;
+ opj_codec_t *codec;
+ opj_dparameters_t parameters;
+ opj_image_t *image;
+ GimpColorProfile *profile;
+ gint32 image_ID;
+ gint32 layer_ID;
+ GimpImageType image_type;
+ GimpImageBaseType base_type;
+ gint width;
+ gint height;
+ gint num_components;
+ GeglBuffer *buffer;
+ gint i, j, k, it;
+ guchar *pixels;
+ const Babl *file_format;
+ gint bpp;
+ GimpPrecision image_precision;
+ gint precision_actual, precision_scaled;
+ gint temp;
+ gboolean linear;
+ unsigned char *c;
+
+ stream = NULL;
+ codec = NULL;
+ image = NULL;
+ profile = NULL;
+ image_ID = -1;
+ linear = FALSE;
+ c = NULL;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ stream = opj_stream_create_default_file_stream (filename, OPJ_TRUE);
+ if (! stream)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' for reading"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ codec = opj_create_decompress (format);
+
+ opj_set_default_decoder_parameters (&parameters);
+ if (opj_setup_decoder (codec, &parameters) != OPJ_TRUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't set parameters on decoder for '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (opj_read_header (stream, codec, &image) != OPJ_TRUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't read JP2 header from '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (opj_decode (codec, stream, image) != OPJ_TRUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't decode JP2 image in '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (opj_end_decompress (codec, stream) != OPJ_TRUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't decompress JP2 image in '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (image->icc_profile_buf)
+ {
+ if (image->icc_profile_len)
+ {
+ profile = gimp_color_profile_new_from_icc_profile (image->icc_profile_buf,
+ image->icc_profile_len,
+ error);
+ if (! profile)
+ goto out;
+
+ *profile_loaded = TRUE;
+
+ if (image->color_space == OPJ_CLRSPC_UNSPECIFIED ||
+ image->color_space == OPJ_CLRSPC_UNKNOWN)
+ {
+ if (gimp_color_profile_is_rgb (profile))
+ image->color_space = OPJ_CLRSPC_SRGB;
+ else if (gimp_color_profile_is_gray (profile))
+ image->color_space = OPJ_CLRSPC_GRAY;
+ else if (gimp_color_profile_is_cmyk (profile))
+ image->color_space = OPJ_CLRSPC_CMYK;
+ }
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't decode CIELAB JP2 image in '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ free (image->icc_profile_buf);
+ image->icc_profile_buf = NULL;
+ image->icc_profile_len = 0;
+ }
+
+ num_components = image->numcomps;
+
+ if ((image->color_space == OPJ_CLRSPC_UNSPECIFIED ||
+ image->color_space == OPJ_CLRSPC_UNKNOWN) && ! interactive)
+ image->color_space = color_space;
+
+ if (image->color_space == OPJ_CLRSPC_UNSPECIFIED ||
+ image->color_space == OPJ_CLRSPC_UNKNOWN)
+ {
+ /* Sometimes the color space is not set at this point, which
+ * sucks. This happens always with codestream images (.j2c or
+ * .j2k) which are meant to be embedded by other files.
+ *
+ * It might also happen with JP2 in case the header does not have
+ * color space and the ICC profile is absent (though this may mean
+ * that the JP2 is broken, but let's be flexible and allow manual
+ * fallback).
+ * Assuming RGB/RGBA space is bogus since this format can handle
+ * so much more. Therefore we instead pop-up a dialog asking one
+ * to specify the color space in interactive mode.
+ */
+ if (num_components == 1 || num_components == 2)
+ {
+ /* Only possibility is gray. */
+ image->color_space = OPJ_CLRSPC_GRAY;
+ }
+ else if (num_components == 5)
+ {
+ /* Can only be CMYK with Alpha. */
+ image->color_space = OPJ_CLRSPC_CMYK;
+ }
+ else if (interactive)
+ {
+ image->color_space = open_dialog (filename, format,
+ num_components, error);
+
+ if (image->color_space == OPJ_CLRSPC_UNKNOWN)
+ goto out;
+ }
+ else /* ! interactive */
+ {
+ /* API call where color space was set to UNKNOWN. We don't
+ * want to guess or assume anything. It is much better to just
+ * fail. It is the responsibility of the developer to know its
+ * data when loading it in a script.
+ */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unknown color space in JP2 codestream '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+
+ if (image->color_space == OPJ_CLRSPC_SYCC)
+ {
+ if (! color_sycc_to_rgb (image))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't convert YCbCr JP2 image '%s' to RGB."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+ else if ((image->color_space == OPJ_CLRSPC_CMYK))
+ {
+ if (! color_cmyk_to_rgb (image))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't convert CMYK JP2 image in '%s' to RGB."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+ else if (image->color_space == OPJ_CLRSPC_EYCC)
+ {
+ if (! color_esycc_to_rgb (image))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Couldn't convert xvYCC JP2 image in '%s' to RGB."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+
+ /* At this point, the image should be converted to Gray or RGB. */
+ if (image->color_space == OPJ_CLRSPC_GRAY)
+ {
+ base_type = GIMP_GRAY;
+ image_type = GIMP_GRAY_IMAGE;
+
+ if (num_components == 2)
+ image_type = GIMP_GRAYA_IMAGE;
+ }
+ else if (image->color_space == OPJ_CLRSPC_SRGB)
+ {
+ base_type = GIMP_RGB;
+ image_type = GIMP_RGB_IMAGE;
+
+ if (num_components == 4)
+ image_type = GIMP_RGBA_IMAGE;
+ }
+ else
+ {
+ /* If not gray or RGB, this is an image we cannot handle. */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported color space in JP2 image '%s'."),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ width = image->comps[0].w;
+ height = image->comps[0].h;
+
+ if (profile)
+ linear = gimp_color_profile_is_linear (profile);
+
+ precision_actual = image->comps[0].prec;
+
+ precision_scaled = get_valid_precision (precision_actual);
+ image_precision = get_image_precision (precision_scaled, linear);
+
+ image_ID = gimp_image_new_with_precision (width, height,
+ base_type, image_precision);
+
+ gimp_image_set_filename (image_ID, filename);
+
+ if (profile)
+ gimp_image_set_color_profile (image_ID, profile);
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("Background"),
+ width, height,
+ image_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ file_format = gimp_drawable_get_format (layer_ID);
+ bpp = babl_format_get_bytes_per_pixel (file_format);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+ pixels = g_new0 (guchar, width * bpp);
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < num_components; j++)
+ {
+ int shift = precision_scaled - precision_actual;
+
+ for (k = 0; k < width; k++)
+ {
+ if (shift >= 0)
+ temp = image->comps[j].data[i * width + k] << shift;
+ else /* precision_actual > 32 */
+ temp = image->comps[j].data[i * width + k] >> (- shift);
+
+ c = (unsigned char *) &temp;
+ for (it = 0; it < (precision_scaled / 8); it++)
+ {
+ pixels[k * bpp + j * (precision_scaled / 8) + it] = c[it];
+ }
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i, width, 1), 0,
+ file_format, pixels, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (pixels);
+
+ g_object_unref (buffer);
+ gimp_progress_update (1.0);
+
+ out:
+ if (profile)
+ g_object_unref (profile);
+ if (image)
+ opj_image_destroy (image);
+ if (codec)
+ opj_destroy_codec (codec);
+ if (stream)
+ opj_stream_destroy (stream);
+
+ return image_ID;
+}
diff --git a/plug-ins/common/file-jpegxl.c b/plug-ins/common/file-jpegxl.c
new file mode 100644
index 0000000..e85f647
--- /dev/null
+++ b/plug-ins/common/file-jpegxl.c
@@ -0,0 +1,1174 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-jpegxl - JPEG XL file format plug-in for the GIMP
+ * Copyright (C) 2022 Daniel Novomesky
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gexiv2/gexiv2.h>
+#include <glib/gstdio.h>
+
+#include <jxl/decode.h>
+#include <jxl/encode.h>
+#include <jxl/thread_parallel_runner.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define LOAD_PROC "file-jpegxl-load"
+#define SAVE_PROC "file-jpegxl-save"
+#define PLUG_IN_BINARY "file-jpegxl"
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+GimpPlugInInfo PLUG_IN_INFO = {
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] = {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"},
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] = {
+ {GIMP_PDB_IMAGE, "image", "Output image"}
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the JPEG XL file format",
+ "Loads files in the JPEG XL file format",
+ "Daniel Novomesky",
+ "(C) 2022 Daniel Novomesky",
+ "2022",
+ N_("JPEG XL image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/jxl");
+ gimp_register_magic_load_handler (LOAD_PROC, "jxl", "", "0,string,\xFF\x0A,0,string,\\000\\000\\000\x0CJXL\\040\\015\\012\x87\\012");
+ gimp_register_file_handler_priority (LOAD_PROC, 100);
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves files in the JPEG XL file format",
+ "Saves files in the JPEG XL file format",
+ "Daniel Novomesky",
+ "(C) 2022 Daniel Novomesky",
+ "2022",
+ N_("JPEG XL image"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/jxl");
+ gimp_register_save_handler (SAVE_PROC, "jxl", "");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_file_handler_priority (SAVE_PROC, 100);
+}
+
+static gint32
+load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ FILE *inputFile = g_fopen (filename, "rb");
+
+ gsize inputFileSize;
+ gpointer memory;
+
+ JxlSignature signature;
+ JxlDecoder *decoder;
+ void *runner;
+ JxlBasicInfo basicinfo;
+ JxlDecoderStatus status;
+ JxlPixelFormat pixel_format;
+ JxlColorEncoding color_encoding;
+ size_t icc_size = 0;
+ GimpColorProfile *profile = NULL;
+ gboolean loadlinear = FALSE;
+ size_t channel_depth;
+ size_t result_size;
+ gpointer picture_buffer;
+ gint32 image = -1;
+ gint32 layer;
+ GeglBuffer *buffer;
+ GimpPrecision precision_linear;
+ GimpPrecision precision_non_linear;
+ size_t num_worker_threads = 1;
+
+ if (! inputFile)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "Cannot open file for read: %s\n", filename);
+ return -1;
+ }
+
+ fseek (inputFile, 0, SEEK_END);
+ inputFileSize = ftell (inputFile);
+ fseek (inputFile, 0, SEEK_SET);
+
+ if (inputFileSize < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "File too small: %s\n", filename);
+ fclose (inputFile);
+ return -1;
+ }
+
+ memory = g_malloc (inputFileSize);
+ if (fread (memory, 1, inputFileSize, inputFile) != inputFileSize)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "Failed to read %zu bytes: %s\n",
+ inputFileSize, filename);
+ fclose (inputFile);
+ g_free (memory);
+ return -1;
+ }
+
+ fclose (inputFile);
+
+ signature = JxlSignatureCheck (memory, inputFileSize);
+ if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "File %s is probably not in JXL format!\n", filename);
+ g_free (memory);
+ return -1;
+ }
+
+ decoder = JxlDecoderCreate (NULL);
+ if (! decoder)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JxlDecoderCreate failed");
+ g_free (memory);
+ return -1;
+ }
+
+ num_worker_threads = g_get_num_processors ();
+ if (num_worker_threads > 16)
+ {
+ num_worker_threads = 16;
+ }
+ runner = JxlThreadParallelRunnerCreate (NULL, num_worker_threads);
+ if (JxlDecoderSetParallelRunner (decoder, JxlThreadParallelRunner, runner) != JXL_DEC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JxlDecoderSetParallelRunner failed");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JxlDecoderSetInput failed");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ JxlDecoderCloseInput (decoder);
+
+ if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE)
+ != JXL_DEC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JxlDecoderSubscribeEvents failed");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ status = JxlDecoderProcessInput (decoder);
+ if (status == JXL_DEC_ERROR)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JXL decoding failed");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (status == JXL_DEC_NEED_MORE_INPUT)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JXL data incomplete");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ status = JxlDecoderGetBasicInfo (decoder, &basicinfo);
+ if (status != JXL_DEC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JXL basic info not available");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (basicinfo.xsize == 0 || basicinfo.ysize == 0)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JXL image has zero dimensions");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ status = JxlDecoderProcessInput (decoder);
+ if (status != JXL_DEC_COLOR_ENCODING)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "Unexpected event %d instead of JXL_DEC_COLOR_ENCODING", status);
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (basicinfo.uses_original_profile == JXL_FALSE)
+ {
+ if (basicinfo.num_color_channels == 3)
+ {
+ JxlColorEncodingSetToSRGB (&color_encoding, JXL_FALSE);
+ JxlDecoderSetPreferredColorProfile (decoder, &color_encoding);
+ }
+ else if (basicinfo.num_color_channels == 1)
+ {
+ JxlColorEncodingSetToSRGB (&color_encoding, JXL_TRUE);
+ JxlDecoderSetPreferredColorProfile (decoder, &color_encoding);
+ }
+ }
+
+ pixel_format.endianness = JXL_NATIVE_ENDIAN;
+ pixel_format.align = 0;
+
+ if (basicinfo.uses_original_profile == JXL_FALSE || basicinfo.bits_per_sample > 16)
+ {
+ pixel_format.data_type = JXL_TYPE_FLOAT;
+ channel_depth = 4;
+ precision_linear = GIMP_PRECISION_FLOAT_LINEAR;
+ precision_non_linear = GIMP_PRECISION_FLOAT_GAMMA;
+ }
+ else if (basicinfo.bits_per_sample <= 8)
+ {
+ pixel_format.data_type = JXL_TYPE_UINT8;
+ channel_depth = 1;
+ precision_linear = GIMP_PRECISION_U8_LINEAR;
+ precision_non_linear = GIMP_PRECISION_U8_GAMMA;
+ }
+ else
+ {
+ pixel_format.data_type = JXL_TYPE_UINT16;
+ channel_depth = 2;
+ precision_linear = GIMP_PRECISION_U16_LINEAR;
+ precision_non_linear = GIMP_PRECISION_U16_GAMMA;
+ }
+
+ if (basicinfo.num_color_channels == 1) /* grayscale */
+ {
+ if (basicinfo.alpha_bits > 0)
+ {
+ pixel_format.num_channels = 2;
+ }
+ else
+ {
+ pixel_format.num_channels = 1;
+ }
+ }
+ else /* RGB */
+ {
+
+ if (basicinfo.alpha_bits > 0) /* RGB with alpha */
+ {
+ pixel_format.num_channels = 4;
+ }
+ else /* RGB no alpha */
+ {
+ pixel_format.num_channels = 3;
+ }
+ }
+
+ result_size = channel_depth * pixel_format.num_channels
+ * (size_t) basicinfo.xsize * (size_t) basicinfo.ysize;
+
+ if (JxlDecoderGetColorAsEncodedProfile (decoder, &pixel_format,
+ JXL_COLOR_PROFILE_TARGET_DATA,
+ &color_encoding) == JXL_DEC_SUCCESS)
+ {
+ if (color_encoding.white_point == JXL_WHITE_POINT_D65)
+ {
+ switch (color_encoding.transfer_function)
+ {
+ case JXL_TRANSFER_FUNCTION_LINEAR:
+ loadlinear = TRUE;
+
+ switch (color_encoding.color_space)
+ {
+ case JXL_COLOR_SPACE_RGB:
+ profile = gimp_color_profile_new_rgb_srgb_linear ();
+ break;
+ case JXL_COLOR_SPACE_GRAY:
+ profile = gimp_color_profile_new_d65_gray_linear ();
+ break;
+ default:
+ break;
+ }
+ break;
+ case JXL_TRANSFER_FUNCTION_SRGB:
+ switch (color_encoding.color_space)
+ {
+ case JXL_COLOR_SPACE_RGB:
+ profile = gimp_color_profile_new_rgb_srgb ();
+ break;
+ case JXL_COLOR_SPACE_GRAY:
+ profile = gimp_color_profile_new_d65_gray_srgb_trc ();
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (! profile)
+ {
+ if (JxlDecoderGetICCProfileSize (decoder, &pixel_format,
+ JXL_COLOR_PROFILE_TARGET_DATA,
+ &icc_size) == JXL_DEC_SUCCESS)
+ {
+ if (icc_size > 0)
+ {
+ gpointer raw_icc_profile = g_malloc (icc_size);
+
+ if (JxlDecoderGetColorAsICCProfile (decoder, &pixel_format, JXL_COLOR_PROFILE_TARGET_DATA,
+ raw_icc_profile, icc_size)
+ == JXL_DEC_SUCCESS)
+ {
+ profile = gimp_color_profile_new_from_icc_profile (raw_icc_profile,
+ icc_size, error);
+ if (profile)
+ {
+ loadlinear = gimp_color_profile_is_linear (profile);
+ }
+ else
+ {
+ g_printerr ("%s: Failed to read ICC profile: %s\n",
+ G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+ else
+ {
+ g_printerr ("Failed to obtain data from JPEG XL decoder");
+ }
+
+ g_free (raw_icc_profile);
+ }
+ else
+ {
+ g_printerr ("Empty ICC data");
+ }
+ }
+ else
+ {
+ g_message ("no ICC, other color profile");
+ }
+ }
+
+ status = JxlDecoderProcessInput (decoder);
+ if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER)
+ {
+ g_set_error (error, G_FILE_ERROR,
+ 0, "Unexpected event %d instead of JXL_DEC_NEED_IMAGE_OUT_BUFFER", status);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ picture_buffer = g_try_malloc (result_size);
+ if (! picture_buffer)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "Memory could not be allocated.");
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (JxlDecoderSetImageOutBuffer (decoder, &pixel_format, picture_buffer, result_size) != JXL_DEC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0, "ERROR: JxlDecoderSetImageOutBuffer failed");
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ status = JxlDecoderProcessInput (decoder);
+ if (status != JXL_DEC_FULL_IMAGE)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
+ g_free (picture_buffer);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return -1;
+ }
+
+ if (basicinfo.num_color_channels == 1) /* grayscale */
+ {
+ image = gimp_image_new_with_precision (basicinfo.xsize, basicinfo.ysize, GIMP_GRAY,
+ loadlinear ? precision_linear : precision_non_linear);
+
+ if (profile)
+ {
+ if (gimp_color_profile_is_gray (profile))
+ {
+ gimp_image_set_color_profile (image, profile);
+ }
+ }
+
+ layer = gimp_layer_new (image, "Background", basicinfo.xsize, basicinfo.ysize,
+ (basicinfo.alpha_bits > 0) ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE,
+ 100, gimp_image_get_default_new_layer_mode (image));
+ }
+ else /* RGB */
+ {
+ image = gimp_image_new_with_precision (basicinfo.xsize, basicinfo.ysize, GIMP_RGB,
+ loadlinear ? precision_linear : precision_non_linear);
+
+ if (profile)
+ {
+ if (gimp_color_profile_is_rgb (profile))
+ {
+ gimp_image_set_color_profile (image, profile);
+ }
+ }
+
+ layer = gimp_layer_new (image, "Background", basicinfo.xsize, basicinfo.ysize,
+ (basicinfo.alpha_bits > 0) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE,
+ 100, gimp_image_get_default_new_layer_mode (image));
+ }
+
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, basicinfo.xsize, basicinfo.ysize),
+ 0, NULL, picture_buffer, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ g_free (picture_buffer);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+
+ if (basicinfo.have_container)
+ {
+ JxlDecoderReleaseInput (decoder);
+ JxlDecoderRewind (decoder);
+
+ if (JxlDecoderSetInput (decoder, memory, inputFileSize) != JXL_DEC_SUCCESS)
+ {
+ g_printerr ("%s: JxlDecoderSetInput failed after JxlDecoderRewind\n", G_STRFUNC);
+ }
+ else
+ {
+ JxlDecoderCloseInput (decoder);
+ if (JxlDecoderSubscribeEvents (decoder, JXL_DEC_BOX) != JXL_DEC_SUCCESS)
+ {
+ g_printerr ("%s: JxlDecoderSubscribeEvents for JXL_DEC_BOX failed\n", G_STRFUNC);
+ }
+ else
+ {
+ gboolean search_exif = TRUE;
+ gboolean search_xmp = TRUE;
+ gboolean success_exif = FALSE;
+ gboolean success_xmp = FALSE;
+ JxlBoxType box_type = { 0, 0, 0, 0 };
+ GByteArray *exif_box = NULL;
+ GByteArray *xml_box = NULL;
+ size_t exif_remains = 0;
+ size_t xml_remains = 0;
+
+ while (search_exif || search_xmp)
+ {
+ status = JxlDecoderProcessInput (decoder);
+ switch (status)
+ {
+ case JXL_DEC_SUCCESS:
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
+ success_exif = TRUE;
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
+ success_xmp = TRUE;
+ }
+
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ break;
+ case JXL_DEC_ERROR:
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: Metadata decoding error\n", G_STRFUNC);
+ break;
+ case JXL_DEC_NEED_MORE_INPUT:
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: JXL metadata are probably incomplete\n", G_STRFUNC);
+ break;
+ case JXL_DEC_BOX:
+ JxlDecoderSetDecompressBoxes (decoder, JXL_TRUE);
+
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif && exif_box)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len - exif_remains);
+
+ search_exif = FALSE;
+ success_exif = TRUE;
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp && xml_box)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len - xml_remains);
+
+ search_xmp = FALSE;
+ success_xmp = TRUE;
+ }
+
+ if (JxlDecoderGetBoxType (decoder, box_type, JXL_TRUE) == JXL_DEC_SUCCESS)
+ {
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
+ {
+ exif_box = g_byte_array_sized_new (4096);
+ g_byte_array_set_size (exif_box, 4096);
+
+ JxlDecoderSetBoxBuffer (decoder, exif_box->data, exif_box->len);
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
+ {
+ xml_box = g_byte_array_sized_new (4096);
+ g_byte_array_set_size (xml_box, 4096);
+
+ JxlDecoderSetBoxBuffer (decoder, xml_box->data, xml_box->len);
+ }
+ }
+ else
+ {
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ g_printerr ("%s: Error in JxlDecoderGetBoxType\n", G_STRFUNC);
+ }
+ break;
+ case JXL_DEC_BOX_NEED_MORE_OUTPUT:
+ if (box_type[0] == 'E' && box_type[1] == 'x' && box_type[2] == 'i' && box_type[3] == 'f' && search_exif)
+ {
+ exif_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (exif_box, exif_box->len + 4096);
+ JxlDecoderSetBoxBuffer (decoder, exif_box->data + exif_box->len - (4096 + exif_remains), 4096 + exif_remains);
+ }
+ else if (box_type[0] == 'x' && box_type[1] == 'm' && box_type[2] == 'l' && box_type[3] == ' ' && search_xmp)
+ {
+ xml_remains = JxlDecoderReleaseBoxBuffer (decoder);
+ g_byte_array_set_size (xml_box, xml_box->len + 4096);
+ JxlDecoderSetBoxBuffer (decoder, xml_box->data + xml_box->len - (4096 + xml_remains), 4096 + xml_remains);
+ }
+ else
+ {
+ search_exif = FALSE;
+ search_xmp = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (success_exif || success_xmp)
+ {
+ GimpMetadata *metadata = gimp_metadata_new ();
+
+ if (success_exif && exif_box)
+ {
+ const guint8 tiffHeaderBE[4] = { 'M', 'M', 0, 42 };
+ const guint8 tiffHeaderLE[4] = { 'I', 'I', 42, 0 };
+ const guint8 *tiffheader = exif_box->data;
+ glong new_exif_size = exif_box->len;
+
+ while (new_exif_size >= 4) /*Searching for TIFF Header*/
+ {
+ if (tiffheader[0] == tiffHeaderBE[0] && tiffheader[1] == tiffHeaderBE[1] &&
+ tiffheader[2] == tiffHeaderBE[2] && tiffheader[3] == tiffHeaderBE[3])
+ {
+ break;
+ }
+ if (tiffheader[0] == tiffHeaderLE[0] && tiffheader[1] == tiffHeaderLE[1] &&
+ tiffheader[2] == tiffHeaderLE[2] && tiffheader[3] == tiffHeaderLE[3])
+ {
+ break;
+ }
+ new_exif_size--;
+ tiffheader++;
+ }
+
+ if (new_exif_size > 4) /* TIFF header + some data found*/
+ {
+ if (! gexiv2_metadata_open_buf (GEXIV2_METADATA (metadata), tiffheader, new_exif_size, error))
+ {
+ g_printerr ("%s: Failed to set EXIF metadata: %s\n", G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+ else
+ {
+ g_printerr ("%s: EXIF metadata not set\n", G_STRFUNC);
+ }
+ }
+
+ if (success_xmp && xml_box)
+ {
+ if (! gimp_metadata_set_from_xmp (metadata, xml_box->data, xml_box->len, error))
+ {
+ g_printerr ("%s: Failed to set XMP metadata: %s\n", G_STRFUNC, (*error)->message);
+ g_clear_error (error);
+ }
+ }
+
+ gexiv2_metadata_set_orientation (GEXIV2_METADATA (metadata),
+ GEXIV2_ORIENTATION_NORMAL);
+ gexiv2_metadata_set_metadata_pixel_width (GEXIV2_METADATA (metadata),
+ basicinfo.xsize);
+ gexiv2_metadata_set_metadata_pixel_height (GEXIV2_METADATA (metadata),
+ basicinfo.ysize);
+ gimp_image_metadata_load_finish (image, "image/jxl", metadata,
+ GIMP_METADATA_LOAD_COMMENT | GIMP_METADATA_LOAD_RESOLUTION,
+ (run_mode == GIMP_RUN_INTERACTIVE));
+ }
+
+ if (exif_box)
+ {
+ g_byte_array_free (exif_box, TRUE);
+ }
+
+ if (xml_box)
+ {
+ g_byte_array_free (xml_box, TRUE);
+ }
+ }
+ }
+ }
+
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlDecoderDestroy (decoder);
+ g_free (memory);
+ return image;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image,
+ gint32 drawable,
+ GError **error)
+{
+ JxlEncoder *encoder;
+ void *runner;
+ JxlEncoderFrameSettings *encoder_options;
+ JxlPixelFormat pixel_format;
+ JxlBasicInfo output_info;
+ JxlColorEncoding color_profile;
+ JxlEncoderStatus status;
+ size_t buffer_size;
+
+ GByteArray *compressed;
+
+ FILE *outfile;
+ GeglBuffer *buffer;
+ GimpImageType drawable_type;
+
+ gint drawable_width;
+ gint drawable_height;
+ gpointer picture_buffer;
+
+ GimpColorProfile *profile = NULL;
+ const Babl *file_format = NULL;
+ const Babl *space = NULL;
+ gboolean out_linear = FALSE;
+
+ size_t offset = 0;
+ uint8_t *next_out;
+ size_t avail_out;
+
+ gboolean save_icc;
+ size_t num_worker_threads = 1;
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ drawable_type = gimp_drawable_type (drawable);
+ buffer = gimp_drawable_get_buffer (drawable);
+ drawable_width = gegl_buffer_get_width (buffer);
+ drawable_height = gegl_buffer_get_height (buffer);
+
+ JxlEncoderInitBasicInfo(&output_info);
+
+ output_info.uses_original_profile = JXL_TRUE;
+
+ profile = gimp_image_get_effective_color_profile (image);
+ out_linear = gimp_color_profile_is_linear (profile);
+
+ space = gimp_color_profile_get_space (profile,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ error);
+
+ if (error && *error)
+ {
+ g_printerr ("%s: error getting the profile space: %s\n",
+ G_STRFUNC, (*error)->message);
+ g_object_unref (buffer);
+ return FALSE;
+ }
+
+ pixel_format.data_type = JXL_TYPE_UINT8;
+ output_info.bits_per_sample = 8;
+
+ pixel_format.endianness = JXL_NATIVE_ENDIAN;
+ pixel_format.align = 0;
+
+ output_info.xsize = drawable_width;
+ output_info.ysize = drawable_height;
+ output_info.exponent_bits_per_sample = 0;
+ output_info.orientation = JXL_ORIENT_IDENTITY;
+ output_info.animation.tps_numerator = 10;
+ output_info.animation.tps_denominator = 1;
+
+ switch (drawable_type)
+ {
+ case GIMP_GRAYA_IMAGE:
+ if (out_linear)
+ {
+ file_format = babl_format ( "YA u8");
+ JxlColorEncodingSetToLinearSRGB (&color_profile, JXL_TRUE);
+ }
+ else
+ {
+ file_format = babl_format ("Y'A u8");
+ JxlColorEncodingSetToSRGB (&color_profile, JXL_TRUE);
+ }
+ pixel_format.num_channels = 2;
+ output_info.num_color_channels = 1;
+ output_info.alpha_bits = 8;
+ output_info.alpha_exponent_bits = 0;
+ output_info.num_extra_channels = 1;
+
+ save_icc = FALSE;
+ break;
+ case GIMP_GRAY_IMAGE:
+ if (out_linear)
+ {
+ file_format = babl_format ("Y u8");
+ JxlColorEncodingSetToLinearSRGB (&color_profile, JXL_TRUE);
+ }
+ else
+ {
+ file_format = babl_format ("Y' u8");
+ JxlColorEncodingSetToSRGB (&color_profile, JXL_TRUE);
+ }
+ pixel_format.num_channels = 1;
+ output_info.num_color_channels = 1;
+ output_info.alpha_bits = 0;
+
+ save_icc = FALSE;
+ break;
+ case GIMP_RGBA_IMAGE:
+ file_format = babl_format_with_space (out_linear ? "RGBA u8" : "R'G'B'A u8", space);
+ output_info.alpha_bits = 8;
+ pixel_format.num_channels = 4;
+ output_info.num_color_channels = 3;
+ output_info.alpha_exponent_bits = 0;
+ output_info.num_extra_channels = 1;
+
+ save_icc = TRUE;
+ break;
+ case GIMP_RGB_IMAGE:
+ file_format = babl_format_with_space (out_linear ? "RGB u8" : "R'G'B' u8", space);
+ pixel_format.num_channels = 3;
+ output_info.num_color_channels = 3;
+ output_info.alpha_bits = 0;
+
+ save_icc = TRUE;
+ break;
+ default:
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ g_object_unref (buffer);
+ return FALSE;
+ break;
+ }
+
+ buffer_size = pixel_format.num_channels * (size_t) output_info.xsize * (size_t) output_info.ysize;
+ picture_buffer = g_malloc (buffer_size);
+
+ gimp_progress_update (0.3);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0,
+ drawable_width, drawable_height), 1.0,
+ file_format, picture_buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (0.4);
+
+ encoder = JxlEncoderCreate (NULL);
+ if (!encoder)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "Failed to create Jxl encoder");
+ g_free (picture_buffer);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ return FALSE;
+ }
+
+ num_worker_threads = g_get_num_processors ();
+ if (num_worker_threads > 16)
+ {
+ num_worker_threads = 16;
+ }
+ runner = JxlThreadParallelRunnerCreate (NULL, num_worker_threads);
+ if (JxlEncoderSetParallelRunner (encoder, JxlThreadParallelRunner, runner) != JXL_ENC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderSetParallelRunner failed");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ return FALSE;
+ }
+
+ status = JxlEncoderSetBasicInfo (encoder, &output_info);
+ if (status != JXL_ENC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderSetBasicInfo failed!");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ if (profile)
+ {
+ g_object_unref (profile);
+ }
+ return FALSE;
+ }
+
+ if (save_icc)
+ {
+ const uint8_t *icc_data = NULL;
+ size_t icc_length = 0;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ status = JxlEncoderSetICCProfile (encoder, icc_data, icc_length);
+ g_object_unref (profile);
+ profile = NULL;
+
+ if (status != JXL_ENC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderSetICCProfile failed!");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (profile)
+ {
+ g_object_unref (profile);
+ profile = NULL;
+ }
+
+ status = JxlEncoderSetColorEncoding (encoder, &color_profile);
+ if (status != JXL_ENC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderSetColorEncoding failed!");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ return FALSE;
+ }
+ }
+
+ encoder_options = JxlEncoderFrameSettingsCreate (encoder, NULL);
+ JxlEncoderSetFrameDistance (encoder_options, 0);
+ JxlEncoderSetFrameLossless (encoder_options, JXL_TRUE);
+
+ gimp_progress_update (0.5);
+
+ status = JxlEncoderAddImageFrame (encoder_options, &pixel_format, picture_buffer, buffer_size);
+ if (status != JXL_ENC_SUCCESS)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderAddImageFrame failed!");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ return FALSE;
+ }
+
+ gimp_progress_update (0.65);
+
+ JxlEncoderCloseInput (encoder);
+
+ gimp_progress_update (0.7);
+
+ compressed = g_byte_array_sized_new (4096);
+ g_byte_array_set_size (compressed, 4096);
+ do
+ {
+ next_out = compressed->data + offset;
+ avail_out = compressed->len - offset;
+ status = JxlEncoderProcessOutput (encoder, &next_out, &avail_out);
+
+ if (status == JXL_ENC_NEED_MORE_OUTPUT)
+ {
+ offset = next_out - compressed->data;
+ g_byte_array_set_size (compressed, compressed->len * 2);
+ }
+ else if (status == JXL_ENC_ERROR)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "JxlEncoderProcessOutput failed!");
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+ g_free (picture_buffer);
+ return FALSE;
+ }
+ }
+ while (status != JXL_ENC_SUCCESS);
+
+ JxlThreadParallelRunnerDestroy (runner);
+ JxlEncoderDestroy (encoder);
+
+ g_free (picture_buffer);
+
+ g_byte_array_set_size (compressed, next_out - compressed->data);
+
+ gimp_progress_update (0.8);
+
+ if (compressed->len > 0)
+ {
+ outfile = g_fopen (g_file_peek_path (file), "wb");
+ if (!outfile)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ "Could not open '%s' for writing!\n",
+ g_file_peek_path (file));
+ g_byte_array_free (compressed, TRUE);
+ return FALSE;
+ }
+
+ fwrite (compressed->data, 1, compressed->len, outfile);
+ fclose (outfile);
+
+ gimp_progress_update (1.0);
+
+ g_byte_array_free (compressed, TRUE);
+ return TRUE;
+ }
+
+ g_set_error (error, G_FILE_ERROR, 0,
+ "No data to write");
+ g_byte_array_free (compressed, TRUE);
+ return FALSE;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ break;
+ default:
+ break;
+ }
+
+ image_ID = load_image (param[1].data.d_string, run_mode, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "JPEG XL",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (!save_image (file, image_ID, drawable_ID, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_object_unref (file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/common/file-mng.c b/plug-ins/common/file-mng.c
new file mode 100644
index 0000000..14c10fe
--- /dev/null
+++ b/plug-ins/common/file-mng.c
@@ -0,0 +1,1791 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Multiple-image Network Graphics (MNG) plug-in
+ *
+ * Copyright (C) 2002 Mukund Sivaraman <muks@mukund.org>
+ * Portions are copyright of the authors of the file-gif-save, file-png-save
+ * and file-jpeg-save plug-ins' code. The exact ownership of these code
+ * fragments has not been determined.
+ *
+ * This work was sponsored by Xinit Systems Limited, UK.
+ * http://www.xinitsystems.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * --
+ *
+ * For now, this MNG plug-in can only save images. It cannot load images.
+ * Save your working copy as .xcf and use this for "exporting" your images
+ * to MNG. Supports animation the same way as animated GIFs. Supports alpha
+ * transparency. Uses the libmng library (http://www.libmng.com/).
+ * The MIME content-type for MNG images is video/x-mng for now. Make sure
+ * your web-server is configured appropriately if you want to serve MNG
+ * images.
+ *
+ * Since libmng cannot write PNG, JNG and delta PNG chunks at this time
+ * (when this text was written), this plug-in uses libpng and jpeglib to
+ * create the data for the chunks.
+ *
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+/* libpng and jpeglib are currently used in this plug-in. */
+
+#include <png.h>
+#include <jpeglib.h>
+
+
+/* Grrr. The grrr is because the following have to be defined
+ * by the application as well for some reason, although they
+ * were enabled when libmng was compiled. The authors of libmng
+ * must look into this. */
+
+#if !defined(MNG_SUPPORT_FULL)
+#define MNG_SUPPORT_FULL 1
+#endif
+
+#if !defined(MNG_SUPPORT_READ)
+#define MNG_SUPPORT_READ 1
+#endif
+
+#if !defined(MNG_SUPPORT_WRITE)
+#define MNG_SUPPORT_WRITE 1
+#endif
+
+#if !defined(MNG_SUPPORT_DISPLAY)
+#define MNG_SUPPORT_DISPLAY 1
+#endif
+
+#if !defined(MNG_ACCESS_CHUNKS)
+#define MNG_ACCESS_CHUNKS 1
+#endif
+
+#include <libmng.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-mng-save"
+#define PLUG_IN_BINARY "file-mng"
+#define PLUG_IN_ROLE "gimp-file-mng"
+#define SCALE_WIDTH 125
+
+enum
+{
+ CHUNKS_PNG_D,
+ CHUNKS_JNG_D,
+ CHUNKS_PNG,
+ CHUNKS_JNG
+};
+
+enum
+{
+ DISPOSE_COMBINE,
+ DISPOSE_REPLACE
+};
+
+
+/* The contents of this struct remain static among multiple
+ * invocations of the plug-in. */
+
+/* TODO: describe the members of the struct */
+
+struct mng_data_t
+{
+ gint32 interlaced;
+ gint32 bkgd;
+ gint32 gama;
+ gint32 phys;
+ gint32 time;
+ gint32 default_chunks;
+
+ gfloat quality;
+ gfloat smoothing;
+
+ gint32 compression_level;
+
+ gint32 loop;
+ gint32 default_delay;
+ gint32 default_dispose;
+};
+
+/* Values of the instance of the above struct when the plug-in is
+ * first invoked. */
+
+static struct mng_data_t mng_data =
+{
+ FALSE, /* interlaced */
+ FALSE, /* bkgd */
+ FALSE, /* gama */
+ TRUE, /* phys */
+ TRUE, /* time */
+ CHUNKS_PNG_D, /* default_chunks */
+
+ 0.75, /* quality */
+ 0.0, /* smoothing */
+
+ 9, /* compression_level */
+
+ TRUE, /* loop */
+ 100, /* default_delay */
+ DISPOSE_COMBINE /* default_dispose */
+};
+
+
+/* These are not saved or restored. */
+
+struct mng_globals_t
+{
+ gboolean has_trns;
+ png_bytep trans;
+ int num_trans;
+ gboolean has_plte;
+ png_colorp palette;
+ int num_palette;
+};
+
+static struct mng_globals_t mngg;
+
+
+/* The output FILE pointer which is used by libmng;
+ * passed around as user data. */
+struct mnglib_userdata_t
+{
+ FILE *fp;
+};
+
+
+/*
+ * Function prototypes
+ */
+
+static mng_ptr MNG_DECL myalloc (mng_size_t size);
+static void MNG_DECL myfree (mng_ptr ptr,
+ mng_size_t size);
+static mng_bool MNG_DECL myopenstream (mng_handle handle);
+static mng_bool MNG_DECL myclosestream (mng_handle handle);
+static mng_bool MNG_DECL mywritedata (mng_handle handle,
+ mng_ptr buf,
+ mng_uint32 size,
+ mng_uint32 *written_size);
+
+
+static gint32 parse_chunks_type_from_layer_name (const gchar *str);
+static gint32 parse_disposal_type_from_layer_name (const gchar *str);
+static gint32 parse_ms_tag_from_layer_name (const gchar *str);
+static gint find_unused_ia_color (guchar *pixels,
+ gint numpixels,
+ gint *colors);
+static gboolean ia_has_transparent_pixels (guchar *pixels,
+ gint numpixels);
+
+static gboolean respin_cmap (png_structp png_ptr,
+ png_infop png_info_ptr,
+ guchar *remap,
+ gint32 image_id,
+ GeglBuffer *buffer,
+ int *bit_depth);
+
+static gboolean mng_save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ gint32 original_image_id,
+ GError **error);
+static gboolean mng_save_dialog (gint32 image_id);
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+/*
+ * Callbacks for libmng
+ */
+
+static mng_ptr MNG_DECL
+myalloc (mng_size_t size)
+{
+ gpointer ptr;
+
+ ptr = g_try_malloc (size);
+
+ if (ptr != NULL)
+ memset (ptr, 0, size);
+
+ return ((mng_ptr) ptr);
+}
+
+static void MNG_DECL
+myfree (mng_ptr ptr,
+ mng_size_t size)
+{
+ g_free (ptr);
+}
+
+static mng_bool MNG_DECL
+myopenstream (mng_handle handle)
+{
+ return MNG_TRUE;
+}
+
+static mng_bool MNG_DECL
+myclosestream (mng_handle handle)
+{
+ return MNG_TRUE;
+}
+
+static mng_bool MNG_DECL
+mywritedata (mng_handle handle,
+ mng_ptr buf,
+ mng_uint32 size,
+ mng_uint32 *written_size)
+{
+ struct mnglib_userdata_t *userdata =
+ (struct mnglib_userdata_t *) mng_get_userdata (handle);
+
+
+ *written_size = (mng_uint32) fwrite ((void *) buf, 1,
+ (size_t) size, userdata->fp);
+
+ return MNG_TRUE;
+}
+
+
+/* Parses which output chunk type to use for this layer
+ * from the layer name. */
+
+static gint32
+parse_chunks_type_from_layer_name (const gchar *str)
+{
+ guint i;
+
+ for (i = 0; (i + 5) <= strlen (str); i++)
+ {
+ if (g_ascii_strncasecmp (str + i, "(png)", 5) == 0)
+ return CHUNKS_PNG;
+ else if (g_ascii_strncasecmp (str + i, "(jng)", 5) == 0)
+ return CHUNKS_JNG;
+ }
+
+ return mng_data.default_chunks;
+}
+
+
+/* Parses which disposal type to use for this layer
+ * from the layer name. */
+
+static gint32
+parse_disposal_type_from_layer_name (const gchar *str)
+{
+ guint i;
+
+ for (i = 0; (i + 9) <= strlen (str); i++)
+ {
+ if (g_ascii_strncasecmp (str + i, "(combine)", 9) == 0)
+ return DISPOSE_COMBINE;
+ else if (g_ascii_strncasecmp (str + i, "(replace)", 9) == 0)
+ return DISPOSE_REPLACE;
+ }
+
+ return mng_data.default_dispose;
+}
+
+
+/* Parses the millisecond delay to use for this layer
+ * from the layer name. */
+
+static gint32
+parse_ms_tag_from_layer_name (const gchar *str)
+{
+ guint offset = 0;
+ gint32 sum = 0;
+ guint length = strlen (str);
+
+ while (TRUE)
+ {
+ while ((offset < length) && (str[offset] != '('))
+ offset++;
+
+ if (offset >= length)
+ return mng_data.default_delay;
+
+ offset++;
+
+ if (g_ascii_isdigit (str[offset]))
+ break;
+ }
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset < length) && (g_ascii_isdigit (str[offset])));
+
+ if ((length - offset) <= 2)
+ return mng_data.default_delay;
+
+ if ((g_ascii_toupper (str[offset]) != 'M') ||
+ (g_ascii_toupper (str[offset + 1]) != 'S'))
+ return mng_data.default_delay;
+
+ return sum;
+}
+
+
+/* Try to find a color in the palette which isn't actually
+ * used in the image, so that we can use it as the transparency
+ * index. Taken from png.c */
+static gint
+find_unused_ia_color (guchar *pixels,
+ gint numpixels,
+ gint *colors)
+{
+ gint i;
+ gboolean ix_used[256];
+ gboolean trans_used = FALSE;
+
+ for (i = 0; i < *colors; i++)
+ {
+ ix_used[i] = FALSE;
+ }
+
+ for (i = 0; i < numpixels; i++)
+ {
+ /* If alpha is over a threshold, the color index in the
+ * palette is taken. Otherwise, this pixel is transparent. */
+ if (pixels[i * 2 + 1] > 127)
+ ix_used[pixels[i * 2]] = TRUE;
+ else
+ trans_used = TRUE;
+ }
+
+ /* If there is no transparency, ignore alpha. */
+ if (trans_used == FALSE)
+ return -1;
+
+ for (i = 0; i < *colors; i++)
+ {
+ if (ix_used[i] == FALSE)
+ {
+ return i;
+ }
+ }
+
+ /* Couldn't find an unused color index within the number of
+ bits per pixel we wanted. Will have to increment the number
+ of colors in the image and assign a transparent pixel there. */
+ if ((*colors) < 256)
+ {
+ (*colors)++;
+ return ((*colors) - 1);
+ }
+
+ return -1;
+}
+
+
+static gboolean
+ia_has_transparent_pixels (guchar *pixels,
+ gint numpixels)
+{
+ while (numpixels --)
+ {
+ if (pixels [1] <= 127)
+ return TRUE;
+ pixels += 2;
+ }
+ return FALSE;
+}
+
+static int
+get_bit_depth_for_palette (int num_palette)
+{
+ if (num_palette <= 2)
+ return 1;
+ else if (num_palette <= 4)
+ return 2;
+ else if (num_palette <= 16)
+ return 4;
+ else
+ return 8;
+}
+
+/* Spins the color map (palette) putting the transparent color at
+ * index 0 if there is space. If there isn't any space, warn the user
+ * and forget about transparency. Returns TRUE if the colormap has
+ * been changed and FALSE otherwise.
+ */
+
+static gboolean
+respin_cmap (png_structp pp,
+ png_infop info,
+ guchar *remap,
+ gint32 image_id,
+ GeglBuffer *buffer,
+ int *bit_depth)
+{
+ static guchar trans[] = { 0 };
+ guchar *before;
+ guchar *pixels;
+ gint numpixels;
+ gint colors;
+ gint transparent;
+ gint cols, rows;
+
+ before = gimp_image_get_colormap (image_id, &colors);
+
+ /* Make sure there is something in the colormap */
+ if (colors == 0)
+ {
+ before = g_newa (guchar, 3);
+ memset (before, 0, sizeof (guchar) * 3);
+
+ colors = 1;
+ }
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+ numpixels = cols * rows;
+
+ pixels = (guchar *) g_malloc (numpixels * 2);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, cols, rows), 1.0,
+ NULL, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (ia_has_transparent_pixels (pixels, numpixels))
+ {
+ transparent = find_unused_ia_color (pixels, numpixels, &colors);
+
+ if (transparent != -1)
+ {
+ static png_color palette[256] = { {0, 0, 0} };
+ gint i;
+
+ /* Set tRNS chunk values for writing later. */
+ mngg.has_trns = TRUE;
+ mngg.trans = trans;
+ mngg.num_trans = 1;
+
+ /* Transform all pixels with a value = transparent to
+ * 0 and vice versa to compensate for re-ordering in palette
+ * due to png_set_tRNS().
+ */
+
+ remap[0] = transparent;
+ remap[transparent] = 0;
+
+ /* Copy from index 0 to index transparent - 1 to index 1 to
+ * transparent of after, then from transparent+1 to colors-1
+ * unchanged, and finally from index transparent to index 0.
+ */
+
+ for (i = 1; i < colors; i++)
+ {
+ palette[i].red = before[3 * remap[i]];
+ palette[i].green = before[3 * remap[i] + 1];
+ palette[i].blue = before[3 * remap[i] + 2];
+ }
+
+ /* Set PLTE chunk values for writing later. */
+ mngg.has_plte = TRUE;
+ mngg.palette = palette;
+ mngg.num_palette = colors;
+
+ *bit_depth = get_bit_depth_for_palette (colors);
+
+ return TRUE;
+ }
+ else
+ {
+ g_message (_("Couldn't losslessly save transparency, "
+ "saving opacity instead."));
+ }
+ }
+
+ mngg.has_plte = TRUE;
+ mngg.palette = (png_colorp) before;
+ mngg.num_palette = colors;
+ *bit_depth = get_bit_depth_for_palette (colors);
+
+ return FALSE;
+}
+
+static mng_retcode
+mng_putchunk_plte_wrapper (mng_handle handle,
+ gint numcolors,
+ const guchar *colormap)
+{
+ mng_palette8 palette;
+
+ memset (palette, 0, sizeof palette);
+ if (0 < numcolors)
+ memcpy (palette, colormap, numcolors * sizeof palette[0]);
+
+ return mng_putchunk_plte (handle, numcolors, palette);
+}
+
+static mng_retcode
+mng_putchunk_trns_wrapper (mng_handle handle,
+ gint n_alphas,
+ const guchar *buffer)
+{
+ const mng_bool mng_global = TRUE;
+ const mng_bool mng_empty = TRUE;
+ mng_uint8arr alphas;
+
+ memset (alphas, 0, sizeof alphas);
+ if (buffer && 0 < n_alphas)
+ memcpy (alphas, buffer, n_alphas * sizeof alphas[0]);
+
+ return mng_putchunk_trns (handle,
+ ! mng_empty,
+ ! mng_global,
+ MNG_COLORTYPE_INDEXED,
+ n_alphas,
+ alphas,
+ 0, 0, 0, 0, 0, alphas);
+}
+
+static gboolean
+mng_save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ gint32 original_image_id,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gint rows, cols;
+ volatile gint i;
+ time_t t;
+ struct tm *gmt;
+
+ gint num_layers;
+ gint32 *layers;
+
+ struct mnglib_userdata_t *userdata;
+ mng_handle handle;
+ guint32 mng_ticks_per_second;
+ guint32 mng_simplicity_profile;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers < 1)
+ return FALSE;
+
+ if (num_layers > 1)
+ mng_ticks_per_second = 1000;
+ else
+ mng_ticks_per_second = 0;
+
+ rows = gimp_image_height (image_id);
+ cols = gimp_image_width (image_id);
+
+ mng_simplicity_profile = (MNG_SIMPLICITY_VALID |
+ MNG_SIMPLICITY_SIMPLEFEATURES |
+ MNG_SIMPLICITY_COMPLEXFEATURES);
+
+ /* JNG and delta-PNG chunks exist */
+ mng_simplicity_profile |= (MNG_SIMPLICITY_JNG |
+ MNG_SIMPLICITY_DELTAPNG);
+
+ for (i = 0; i < num_layers; i++)
+ if (gimp_drawable_has_alpha (layers[i]))
+ {
+ /* internal transparency exists */
+ mng_simplicity_profile |= MNG_SIMPLICITY_TRANSPARENCY;
+
+ /* validity of following flags */
+ mng_simplicity_profile |= 0x00000040;
+
+ /* semi-transparency exists */
+ mng_simplicity_profile |= 0x00000100;
+
+ /* background transparency should happen */
+ mng_simplicity_profile |= 0x00000080;
+
+ break;
+ }
+
+ userdata = g_new0 (struct mnglib_userdata_t, 1);
+ userdata->fp = g_fopen (filename, "wb");
+
+ if (NULL == userdata->fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ goto err;
+ }
+
+ handle = mng_initialize ((mng_ptr) userdata, myalloc, myfree, NULL);
+ if (NULL == handle)
+ {
+ g_warning ("Unable to mng_initialize() in mng_save_image()");
+ goto err2;
+ }
+
+ if ((mng_setcb_openstream (handle, myopenstream) != MNG_NOERROR) ||
+ (mng_setcb_closestream (handle, myclosestream) != MNG_NOERROR) ||
+ (mng_setcb_writedata (handle, mywritedata) != MNG_NOERROR))
+ {
+ g_warning ("Unable to setup callbacks in mng_save_image()");
+ goto err3;
+ }
+
+ if (mng_create (handle) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_create() image in mng_save_image()");
+ goto err3;
+ }
+
+ if (mng_putchunk_mhdr (handle, cols, rows, mng_ticks_per_second, 1,
+ num_layers, mng_data.default_delay,
+ mng_simplicity_profile) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_mhdr() in mng_save_image()");
+ goto err3;
+ }
+
+ if ((num_layers > 1) && (mng_data.loop))
+ {
+ gint32 ms =
+ parse_ms_tag_from_layer_name (gimp_item_get_name (layers[0]));
+
+ if (mng_putchunk_term (handle, MNG_TERMACTION_REPEAT,
+ MNG_ITERACTION_LASTFRAME,
+ ms, 0x7fffffff) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
+ goto err3;
+ }
+ }
+ else
+ {
+ gint32 ms =
+ parse_ms_tag_from_layer_name (gimp_item_get_name (layers[0]));
+
+ if (mng_putchunk_term (handle, MNG_TERMACTION_LASTFRAME,
+ MNG_ITERACTION_LASTFRAME,
+ ms, 0x7fffffff) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+
+ /* For now, we hardwire a comment */
+
+ if (mng_putchunk_text (handle,
+ strlen (MNG_TEXT_TITLE), MNG_TEXT_TITLE,
+ 18, "Created using GIMP") != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_text() in mng_save_image()");
+ goto err3;
+ }
+
+#if 0
+
+ /* how do we get this to work? */
+ if (mng_data.bkgd)
+ {
+ GimpRGB bgcolor;
+ guchar red, green, blue;
+
+ gimp_context_get_background (&bgcolor);
+ gimp_rgb_get_uchar (&bgcolor, &red, &green, &blue);
+
+ if (mng_putchunk_back (handle, red, green, blue,
+ MNG_BACKGROUNDCOLOR_MANDATORY,
+ 0, MNG_BACKGROUNDIMAGE_NOTILE) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_back() in mng_save_image()");
+ goto err3;
+ }
+
+ if (mng_putchunk_bkgd (handle, MNG_FALSE, 2, 0,
+ gimp_rgb_luminance_uchar (&bgcolor),
+ red, green, blue) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_bkgd() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+#endif
+
+ if (mng_data.gama)
+ {
+ if (mng_putchunk_gama (handle, MNG_FALSE,
+ (1.0 / 2.2 * 100000)) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_gama() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+#if 0
+
+ /* how do we get this to work? */
+ if (mng_data.phys)
+ {
+ gimp_image_get_resolution(original_image_id, &xres, &yres);
+
+ if (mng_putchunk_phyg (handle, MNG_FALSE,
+ (mng_uint32) (xres * 39.37),
+ (mng_uint32) (yres * 39.37), 1) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_phyg() in mng_save_image()");
+ goto err3;
+ }
+
+ if (mng_putchunk_phys (handle, MNG_FALSE,
+ (mng_uint32) (xres * 39.37),
+ (mng_uint32) (yres * 39.37), 1) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_phys() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+#endif
+
+ if (mng_data.time)
+ {
+ t = time (NULL);
+ gmt = gmtime (&t);
+
+ if (mng_putchunk_time (handle, gmt->tm_year + 1900, gmt->tm_mon + 1,
+ gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
+ gmt->tm_sec) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_time() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+ if (gimp_image_base_type (image_id) == GIMP_INDEXED)
+ {
+ guchar *palette;
+ gint numcolors;
+
+ palette = gimp_image_get_colormap (image_id, &numcolors);
+
+ if ((numcolors != 0) &&
+ (mng_putchunk_plte_wrapper (handle, numcolors,
+ palette) != MNG_NOERROR))
+ {
+ g_warning ("Unable to mng_putchunk_plte() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+ for (i = (num_layers - 1); i >= 0; i--)
+ {
+ GimpImageType layer_drawable_type;
+ GeglBuffer *layer_buffer;
+ gint layer_offset_x, layer_offset_y;
+ gint layer_rows, layer_cols;
+ gchar *layer_name;
+ gint layer_chunks_type;
+ const Babl *layer_format;
+ volatile gint layer_bpp;
+
+ guint8 __attribute__((unused))layer_mng_colortype;
+ guint8 __attribute__((unused))layer_mng_compression_type;
+ guint8 __attribute__((unused))layer_mng_interlace_type;
+ gboolean layer_has_unique_palette;
+
+ gchar frame_mode;
+ int frame_delay;
+ gchar *temp_file_name;
+ png_structp pp;
+ png_infop info;
+ FILE *infile, *outfile;
+ int num_passes;
+ int tile_height;
+ guchar **layer_pixels, *layer_pixel;
+ int pass, j, k, begin, end, num;
+ guchar *fixed;
+ guchar layer_remap[256];
+ int color_type;
+ int bit_depth;
+
+ layer_name = gimp_item_get_name (layers[i]);
+ layer_chunks_type = parse_chunks_type_from_layer_name (layer_name);
+ layer_drawable_type = gimp_drawable_type (layers[i]);
+
+ layer_buffer = gimp_drawable_get_buffer (layers[i]);
+ layer_cols = gegl_buffer_get_width (layer_buffer);
+ layer_rows = gegl_buffer_get_height (layer_buffer);
+
+ gimp_drawable_offsets (layers[i], &layer_offset_x, &layer_offset_y);
+ layer_has_unique_palette = TRUE;
+
+ for (j = 0; j < 256; j++)
+ layer_remap[j] = j;
+
+ switch (layer_drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ layer_format = babl_format ("R'G'B' u8");
+ layer_mng_colortype = MNG_COLORTYPE_RGB;
+ break;
+ case GIMP_RGBA_IMAGE:
+ layer_format = babl_format ("R'G'B'A u8");
+ layer_mng_colortype = MNG_COLORTYPE_RGBA;
+ break;
+ case GIMP_GRAY_IMAGE:
+ layer_format = babl_format ("Y' u8");
+ layer_mng_colortype = MNG_COLORTYPE_GRAY;
+ break;
+ case GIMP_GRAYA_IMAGE:
+ layer_format = babl_format ("Y'A u8");
+ layer_mng_colortype = MNG_COLORTYPE_GRAYA;
+ break;
+ case GIMP_INDEXED_IMAGE:
+ layer_format = gegl_buffer_get_format (layer_buffer);
+ layer_mng_colortype = MNG_COLORTYPE_INDEXED;
+ break;
+ case GIMP_INDEXEDA_IMAGE:
+ layer_format = gegl_buffer_get_format (layer_buffer);
+ layer_mng_colortype = MNG_COLORTYPE_INDEXED | MNG_COLORTYPE_GRAYA;
+ break;
+ default:
+ g_warning ("Unsupported GimpImageType in mng_save_image()");
+ goto err3;
+ }
+
+ layer_bpp = babl_format_get_bytes_per_pixel (layer_format);
+
+ /* Delta PNG chunks are not yet supported */
+
+ /* if (i == (num_layers - 1)) */
+ {
+ if (layer_chunks_type == CHUNKS_JNG_D)
+ layer_chunks_type = CHUNKS_JNG;
+ else if (layer_chunks_type == CHUNKS_PNG_D)
+ layer_chunks_type = CHUNKS_PNG;
+ }
+
+ switch (layer_chunks_type)
+ {
+ case CHUNKS_PNG_D:
+ layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
+ if (mng_data.interlaced != 0)
+ layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
+ else
+ layer_mng_interlace_type = MNG_INTERLACE_NONE;
+ break;
+ case CHUNKS_JNG_D:
+ layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
+ if (mng_data.interlaced != 0)
+ layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
+ else
+ layer_mng_interlace_type = MNG_INTERLACE_NONE;
+ break;
+ case CHUNKS_PNG:
+ layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
+ if (mng_data.interlaced != 0)
+ layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
+ else
+ layer_mng_interlace_type = MNG_INTERLACE_NONE;
+ break;
+ case CHUNKS_JNG:
+ layer_mng_compression_type = MNG_COMPRESSION_BASELINEJPEG;
+ if (mng_data.interlaced != 0)
+ layer_mng_interlace_type = MNG_INTERLACE_PROGRESSIVE;
+ else
+ layer_mng_interlace_type = MNG_INTERLACE_SEQUENTIAL;
+ break;
+ default:
+ g_warning ("Huh? Programmer stupidity error "
+ "with 'layer_chunks_type'");
+ goto err3;
+ }
+
+ if ((i == (num_layers - 1)) ||
+ (parse_disposal_type_from_layer_name (layer_name) != DISPOSE_COMBINE))
+ frame_mode = MNG_FRAMINGMODE_3;
+ else
+ frame_mode = MNG_FRAMINGMODE_1;
+
+ frame_delay = parse_ms_tag_from_layer_name (layer_name);
+
+ if (mng_putchunk_fram (handle, MNG_FALSE, frame_mode, 0, NULL,
+ MNG_CHANGEDELAY_DEFAULT,
+ MNG_CHANGETIMOUT_NO,
+ MNG_CHANGECLIPPING_DEFAULT,
+ MNG_CHANGESYNCID_NO,
+ frame_delay, 0, 0,
+ layer_offset_x,
+ layer_offset_x + layer_cols,
+ layer_offset_y,
+ layer_offset_y + layer_rows,
+ 0, NULL) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_fram() in mng_save_image()");
+ goto err3;
+ }
+
+ if ((layer_offset_x != 0) || (layer_offset_y != 0))
+ {
+ if (mng_putchunk_defi (handle, 0, 0, 1, 1, layer_offset_x,
+ layer_offset_y, 1, layer_offset_x,
+ layer_offset_x + layer_cols, layer_offset_y,
+ layer_offset_y + layer_rows) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_defi() in mng_save_image()");
+ goto err3;
+ }
+ }
+
+ if ((temp_file_name = gimp_temp_name ("mng")) == NULL)
+ {
+ g_warning ("gimp_temp_name() failed in mng_save_image()");
+ goto err3;
+ }
+
+ if ((outfile = g_fopen (temp_file_name, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (temp_file_name),
+ g_strerror (errno));
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ pp = png_create_write_struct (PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (NULL == pp)
+ {
+ g_warning ("Unable to png_create_write_struct() in mng_save_image()");
+ fclose (outfile);
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ info = png_create_info_struct (pp);
+ if (NULL == info)
+ {
+ g_warning
+ ("Unable to png_create_info_struct() in mng_save_image()");
+ png_destroy_write_struct (&pp, NULL);
+ fclose (outfile);
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ if (setjmp (png_jmpbuf (pp)) != 0)
+ {
+ g_warning ("HRM saving PNG in mng_save_image()");
+ png_destroy_write_struct (&pp, &info);
+ fclose (outfile);
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ png_init_io (pp, outfile);
+
+ bit_depth = 8;
+
+ switch (layer_drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case GIMP_RGBA_IMAGE:
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case GIMP_GRAY_IMAGE:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case GIMP_GRAYA_IMAGE:
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case GIMP_INDEXED_IMAGE:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ mngg.has_plte = TRUE;
+ mngg.palette = (png_colorp)
+ gimp_image_get_colormap (image_id, &mngg.num_palette);
+ bit_depth = get_bit_depth_for_palette (mngg.num_palette);
+ break;
+ case GIMP_INDEXEDA_IMAGE:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ layer_has_unique_palette =
+ respin_cmap (pp, info, layer_remap,
+ image_id, layer_buffer,
+ &bit_depth);
+ break;
+ default:
+ g_warning ("This can't be!\n");
+ png_destroy_write_struct (&pp, &info);
+ fclose (outfile);
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ /* Note: png_set_IHDR() must be called before any other
+ png_set_*() functions. */
+ png_set_IHDR (pp, info, layer_cols, layer_rows,
+ bit_depth,
+ color_type,
+ mng_data.interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+ if (mngg.has_trns)
+ {
+ png_set_tRNS (pp, info, mngg.trans, mngg.num_trans, NULL);
+ }
+
+ if (mngg.has_plte)
+ {
+ png_set_PLTE (pp, info, mngg.palette, mngg.num_palette);
+ }
+
+ png_set_compression_level (pp, mng_data.compression_level);
+
+ png_write_info (pp, info);
+
+ if (mng_data.interlaced != 0)
+ num_passes = png_set_interlace_handling (pp);
+ else
+ num_passes = 1;
+
+ if ((color_type == PNG_COLOR_TYPE_PALETTE) &&
+ (bit_depth < 8))
+ png_set_packing (pp);
+
+ tile_height = gimp_tile_height ();
+ layer_pixel = g_new (guchar, tile_height * layer_cols * layer_bpp);
+ layer_pixels = g_new (guchar *, tile_height);
+
+ for (j = 0; j < tile_height; j++)
+ layer_pixels[j] = layer_pixel + (layer_cols * layer_bpp * j);
+
+ for (pass = 0; pass < num_passes; pass++)
+ {
+ for (begin = 0, end = tile_height;
+ begin < layer_rows;
+ begin += tile_height, end += tile_height)
+ {
+ if (end > layer_rows)
+ end = layer_rows;
+
+ num = end - begin;
+
+ gegl_buffer_get (layer_buffer,
+ GEGL_RECTANGLE (0, begin, layer_cols, num), 1.0,
+ layer_format, layer_pixel,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (png_get_valid (pp, info, PNG_INFO_tRNS))
+ {
+ for (j = 0; j < num; j++)
+ {
+ fixed = layer_pixels[j];
+
+ for (k = 0; k < layer_cols; k++)
+ fixed[k] = (fixed[k * 2 + 1] > 127) ?
+ layer_remap[fixed[k * 2]] : 0;
+ }
+ }
+ else if (png_get_valid (pp, info, PNG_INFO_PLTE)
+ && (layer_bpp == 2))
+ {
+ for (j = 0; j < num; j++)
+ {
+ fixed = layer_pixels[j];
+
+ for (k = 0; k < layer_cols; k++)
+ fixed[k] = fixed[k * 2];
+ }
+ }
+
+ png_write_rows (pp, layer_pixels, num);
+ }
+ }
+
+ g_object_unref (layer_buffer);
+
+ png_write_end (pp, info);
+ png_destroy_write_struct (&pp, &info);
+
+ g_free (layer_pixels);
+ g_free (layer_pixel);
+
+ fclose (outfile);
+
+ infile = g_fopen (temp_file_name, "rb");
+ if (NULL == infile)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (temp_file_name),
+ g_strerror (errno));
+ g_unlink (temp_file_name);
+ goto err3;
+ }
+
+ fseek (infile, 8L, SEEK_SET);
+
+ while (!feof (infile))
+ {
+ guchar chunksize_chars[4];
+ gulong chunksize;
+ gchar chunkname[5];
+ guchar *chunkbuffer;
+ glong chunkwidth;
+ glong chunkheight;
+ gchar chunkbitdepth;
+ gchar chunkcolortype;
+ gchar chunkcompression;
+ gchar chunkfilter;
+ gchar chunkinterlaced;
+
+
+ if (fread (chunksize_chars, 1, 4, infile) != 4)
+ break;
+
+ if (fread (chunkname, 1, 4, infile) != 4)
+ break;
+
+ chunkname[4] = 0;
+
+ chunksize = ((chunksize_chars[0] << 24) |
+ (chunksize_chars[1] << 16) |
+ (chunksize_chars[2] << 8) |
+ (chunksize_chars[3]));
+
+ chunkbuffer = NULL;
+
+ if (chunksize > 0)
+ {
+ chunkbuffer = g_new (guchar, chunksize);
+
+ if (fread (chunkbuffer, 1, chunksize, infile) != chunksize)
+ break;
+ }
+
+ if (strncmp (chunkname, "IHDR", 4) == 0)
+ {
+ chunkwidth = ((chunkbuffer[0] << 24) |
+ (chunkbuffer[1] << 16) |
+ (chunkbuffer[2] << 8) |
+ (chunkbuffer[3]));
+
+ chunkheight = ((chunkbuffer[4] << 24) |
+ (chunkbuffer[5] << 16) |
+ (chunkbuffer[6] << 8) |
+ (chunkbuffer[7]));
+
+ chunkbitdepth = chunkbuffer[8];
+ chunkcolortype = chunkbuffer[9];
+ chunkcompression = chunkbuffer[10];
+ chunkfilter = chunkbuffer[11];
+ chunkinterlaced = chunkbuffer[12];
+
+ if (mng_putchunk_ihdr (handle, chunkwidth, chunkheight,
+ chunkbitdepth, chunkcolortype,
+ chunkcompression, chunkfilter,
+ chunkinterlaced) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_ihdr() "
+ "in mng_save_image()");
+ fclose (infile);
+ goto err3;
+ }
+ }
+ else if (strncmp (chunkname, "IDAT", 4) == 0)
+ {
+ if (mng_putchunk_idat (handle, chunksize,
+ chunkbuffer) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_idat() "
+ "in mng_save_image()");
+ fclose (infile);
+ goto err3;
+ }
+ }
+ else if (strncmp (chunkname, "IEND", 4) == 0)
+ {
+ if (mng_putchunk_iend (handle) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_iend() "
+ "in mng_save_image()");
+ fclose (infile);
+ goto err3;
+ }
+ }
+ else if (strncmp (chunkname, "PLTE", 4) == 0)
+ {
+ /* If this frame's palette is the same as the global palette,
+ * write a 0-color palette chunk.
+ */
+ if (mng_putchunk_plte_wrapper (handle,
+ (layer_has_unique_palette ?
+ (chunksize / 3) : 0),
+ chunkbuffer) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_plte() "
+ "in mng_save_image()");
+ fclose (infile);
+ goto err3;
+ }
+ }
+ else if (strncmp (chunkname, "tRNS", 4) == 0)
+ {
+ if (mng_putchunk_trns_wrapper (handle,
+ chunksize,
+ chunkbuffer) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_trns() "
+ "in mng_save_image()");
+ fclose (infile);
+ goto err3;
+ }
+ }
+
+ if (chunksize > 0)
+ g_free (chunkbuffer);
+
+ /* read 4 bytes after the chunk */
+
+ fread (chunkname, 1, 4, infile);
+ }
+
+ fclose (infile);
+ g_unlink (temp_file_name);
+ }
+
+ if (mng_putchunk_mend (handle) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_putchunk_mend() in mng_save_image()");
+ goto err3;
+ }
+
+ if (mng_write (handle) != MNG_NOERROR)
+ {
+ g_warning ("Unable to mng_write() the image in mng_save_image()");
+ goto err3;
+ }
+
+ ret = TRUE;
+
+ err3:
+ mng_cleanup (&handle);
+ err2:
+ fclose (userdata->fp);
+ err:
+ g_free (userdata);
+
+ return ret;
+}
+
+
+/* The interactive dialog. */
+
+static gboolean
+mng_save_dialog (gint32 image_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *toggle;
+ GtkWidget *hbox;
+ GtkWidget *combo;
+ GtkWidget *label;
+ GtkWidget *scale;
+ GtkAdjustment *scale_adj;
+ GtkWidget *spinbutton;
+ GtkAdjustment *spinbutton_adj;
+ gint num_layers;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("MNG"), PLUG_IN_BINARY, SAVE_PROC);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ main_vbox, TRUE, TRUE, 0);
+
+ frame = gimp_frame_new (_("MNG Options"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Interlace"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.interlaced);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mng_data.interlaced);
+
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Save _background color"));
+ gtk_widget_set_sensitive (toggle, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.bkgd);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.bkgd);
+
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Save _gamma"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.gama);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.gama);
+
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_label (_("Save resolution"));
+ gtk_widget_set_sensitive (toggle, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.phys);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.phys);
+
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Save creation _time"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.time);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.time);
+
+ gtk_widget_show (toggle);
+
+ table = gtk_table_new (2, 4, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers == 1)
+ combo = gimp_int_combo_box_new (_("PNG"), CHUNKS_PNG_D,
+ _("JNG"), CHUNKS_JNG_D,
+ NULL);
+ else
+ combo = gimp_int_combo_box_new (_("PNG + delta PNG"), CHUNKS_PNG_D,
+ _("JNG + delta PNG"), CHUNKS_JNG_D,
+ _("All PNG"), CHUNKS_PNG,
+ _("All JNG"), CHUNKS_JNG,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ mng_data.default_chunks);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &mng_data.default_chunks);
+
+ gtk_widget_set_sensitive (combo, FALSE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Default chunks type:"), 0.0, 0.5,
+ combo, 1, FALSE);
+
+ combo = gimp_int_combo_box_new (_("Combine"), DISPOSE_COMBINE,
+ _("Replace"), DISPOSE_REPLACE,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ mng_data.default_dispose);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &mng_data.default_dispose);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Default _frame disposal:"), 0.0, 0.5,
+ combo, 1, FALSE);
+
+ scale_adj = (GtkAdjustment *)
+ gtk_adjustment_new (mng_data.compression_level,
+ 0.0, 9.0, 1.0, 1.0, 0.0);
+
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_adj);
+ gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_PNG compression level:"), 0.0, 0.9,
+ scale, 1, FALSE);
+
+ g_signal_connect (scale_adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mng_data.compression_level);
+
+ gimp_help_set_help_data (scale,
+ _("Choose a high compression level "
+ "for small file size"),
+ NULL);
+
+ scale_adj = (GtkAdjustment *)
+ gtk_adjustment_new (mng_data.quality,
+ 0.0, 1.0, 0.01, 0.01, 0.0);
+
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_adj);
+ gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_scale_set_digits (GTK_SCALE (scale), 2);
+ gtk_widget_set_sensitive (scale, FALSE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("JPEG compression quality:"), 0.0, 0.9,
+ scale, 1, FALSE);
+
+ g_signal_connect (scale_adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mng_data.quality);
+
+ scale_adj = (GtkAdjustment *)
+ gtk_adjustment_new (mng_data.smoothing,
+ 0.0, 1.0, 0.01, 0.01, 0.0);
+
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_adj);
+ gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_scale_set_digits (GTK_SCALE (scale), 2);
+ gtk_widget_set_sensitive (scale, FALSE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("JPEG smoothing factor:"), 0.0, 0.9,
+ scale, 1, FALSE);
+
+ g_signal_connect (scale_adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mng_data.smoothing);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ frame = gimp_frame_new (_("Animated MNG Options"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Loop"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mng_data.loop);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mng_data.loop);
+
+ gtk_widget_show (toggle);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("Default frame delay:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ spinbutton_adj = (GtkAdjustment *)
+ gtk_adjustment_new (mng_data.default_delay,
+ 0, 65000, 10, 100, 0);
+ spinbutton = gimp_spin_button_new (spinbutton_adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ g_signal_connect (spinbutton_adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &mng_data.default_delay);
+
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+
+ gtk_widget_show (spinbutton);
+
+ label = gtk_label_new (_("milliseconds"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (hbox);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ if (num_layers <= 1)
+ {
+ gtk_widget_set_sensitive (frame, FALSE);
+ gimp_help_set_help_data (frame,
+ _("These options are only available when "
+ "the exported image has more than one "
+ "layer. The image you are exporting only has "
+ "one layer."),
+ NULL);
+ }
+
+ gtk_widget_show (main_vbox);
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+
+/* GIMP calls these methods. */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+
+ { GIMP_PDB_INT32, "interlace", "Use interlacing" },
+ { GIMP_PDB_INT32, "compression", "PNG deflate compression level (0 - 9)" },
+ { GIMP_PDB_FLOAT, "quality", "JPEG quality factor (0.00 - 1.00)" },
+ { GIMP_PDB_FLOAT, "smoothing", "JPEG smoothing factor (0.00 - 1.00)" },
+ { GIMP_PDB_INT32, "loop", "(ANIMATED MNG) Loop infinitely" },
+ { GIMP_PDB_INT32, "default-delay", "(ANIMATED MNG) Default delay between frames in milliseconds" },
+ { GIMP_PDB_INT32, "default-chunks", "(ANIMATED MNG) Default chunks type (0 = PNG + Delta PNG; 1 = JNG + Delta PNG; 2 = All PNG; 3 = All JNG)" },
+ { GIMP_PDB_INT32, "default-dispose", "(ANIMATED MNG) Default dispose type (0 = combine; 1 = replace)" },
+ { GIMP_PDB_INT32, "bkgd", "Write bKGD (background color) chunk" },
+ { GIMP_PDB_INT32, "gama", "Write gAMA (gamma) chunk"},
+ { GIMP_PDB_INT32, "phys", "Write pHYs (image resolution) chunk" },
+ { GIMP_PDB_INT32, "time", "Write tIME (creation time) chunk" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves images in the MNG file format",
+ "This plug-in saves images in the Multiple-image "
+ "Network Graphics (MNG) format which can be used as "
+ "a replacement for animated GIFs, and more.",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "Mukund Sivaraman <muks@mukund.org>",
+ "November 19, 2002",
+ N_("MNG animation"),
+ "RGB*,GRAY*,INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-mng");
+ gimp_register_save_handler (SAVE_PROC, "mng", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GimpRunMode run_mode = param[0].data.d_int32;
+ gint32 image_id = param[1].data.d_int32;
+ gint32 original_image_id = image_id;
+ gint32 drawable_id = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_IGNORE;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE ||
+ run_mode == GIMP_RUN_WITH_LAST_VALS)
+ {
+ gimp_procedural_db_get_data (SAVE_PROC, &mng_data);
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_id, &drawable_id, "MNG",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+ }
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ }
+ else if (export == GIMP_EXPORT_IGNORE || export == GIMP_EXPORT_EXPORT)
+ {
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (mng_save_dialog (image_id) == 0)
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ }
+ else if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ if (nparams != 17)
+ {
+ g_message ("Incorrect number of parameters "
+ "passed to file-mng-save()");
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ mng_data.interlaced = param[5].data.d_int32;
+ mng_data.compression_level = param[6].data.d_int32;
+ mng_data.quality = param[7].data.d_float;
+ mng_data.smoothing = param[8].data.d_float;
+ mng_data.loop = param[9].data.d_int32;
+ mng_data.default_delay = param[10].data.d_int32;
+ mng_data.default_chunks = param[11].data.d_int32;
+ mng_data.default_dispose = param[12].data.d_int32;
+ mng_data.bkgd = param[13].data.d_int32;
+ mng_data.gama = param[14].data.d_int32;
+ mng_data.phys = param[15].data.d_int32;
+ mng_data.time = param[16].data.d_int32;
+
+ if ((mng_data.compression_level < 0)
+ || (mng_data.compression_level > 9))
+ {
+ g_warning ("Parameter 'compression_level' passed to "
+ "file-mng-save() must be in the range 0 - 9; "
+ "Clamping it to the default value of 6.");
+ mng_data.compression_level = 6;
+ }
+
+ if ((mng_data.quality < ((float) 0))
+ || (mng_data.quality > ((float) 1)))
+ {
+ g_warning ("Parameter 'quality' passed to "
+ "file-mng-save() must be in the range "
+ "0.00 - 1.00; Clamping it to the "
+ "default value of 0.75.");
+ mng_data.quality = 0.75;
+ }
+
+ if ((mng_data.smoothing < ((float) 0))
+ || (mng_data.smoothing > ((float) 1)))
+ {
+ g_warning ("Parameter 'smoothing' passed to "
+ "file-mng-save() must be in the "
+ "range 0.00 - 1.00; Clamping it to "
+ "the default value of 0.00.");
+ mng_data.smoothing = 0.0;
+ }
+
+ if ((mng_data.default_chunks < 0)
+ || (mng_data.default_chunks > 3))
+ {
+ g_warning ("Parameter 'default_chunks' passed to "
+ "file-mng-save() must be in the range 0 - 2.");
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((mng_data.default_dispose < 0)
+ || (mng_data.default_dispose > 1))
+ {
+ g_warning ("Parameter 'default_dispose' passed to "
+ "file-mng-save() must be in the range 0 - 1.");
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+
+ if (values[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ GError *error = NULL;
+
+ if (mng_save_image (param[3].data.d_string,
+ image_id, drawable_id,
+ original_image_id, &error))
+ {
+ gimp_set_data (SAVE_PROC, &mng_data, sizeof (mng_data));
+ }
+ else
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_id);
+ }
+
+ }
+ else
+ {
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ }
+}
diff --git a/plug-ins/common/file-pat.c b/plug-ins/common/file-pat.c
new file mode 100644
index 0000000..3d1e43c
--- /dev/null
+++ b/plug-ins/common/file-pat.c
@@ -0,0 +1,320 @@
+/*
+ * pat plug-in version 1.01
+ * Loads/exports version 1 GIMP .pat files, by Tim Newsome <drz@frody.bloke.com>
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-pat-save"
+#define PLUG_IN_BINARY "file-pat"
+#define PLUG_IN_ROLE "gimp-file-pat"
+
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean save_dialog (void);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* private variables */
+
+static gchar description[256] = "GIMP Pattern";
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "uri", "The URI of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-uri", "The URI of the file to export the image in" },
+ { GIMP_PDB_STRING, "description", "Short description of the pattern" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports Gimp pattern file (.PAT)",
+ "New Gimp patterns can be created by exporting them "
+ "in the appropriate place with this plug-in.",
+ "Tim Newsome",
+ "Tim Newsome",
+ "1997",
+ N_("GIMP pattern"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_plugin_icon_register (SAVE_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_PATTERN);
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-gimp-pat");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "pat", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GFile *file;
+ GimpParasite *parasite;
+ gint32 orig_image_ID;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+ file = g_file_new_for_uri (param[3].data.d_string);
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "PAT",
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, description);
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "gimp-pattern-name");
+ if (parasite)
+ {
+ strncpy (description,
+ gimp_parasite_data (parasite),
+ MIN (sizeof (description),
+ gimp_parasite_data_size (parasite)));
+ description[sizeof (description) - 1] = '\0';
+
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ gchar *name = g_path_get_basename (gimp_file_get_utf8_name (file));
+
+ if (g_str_has_suffix (name, ".pat"))
+ name[strlen (name) - 4] = '\0';
+
+ if (strlen (name))
+ {
+ strncpy (description, name, sizeof (description));
+ description[sizeof (description) - 1] = '\0';
+ }
+
+ g_free (name);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ strncpy (description, param[5].data.d_string,
+ sizeof (description));
+ description[sizeof (description) - 1] = '\0';
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpParam *save_retvals;
+ gint n_save_retvals;
+
+ save_retvals =
+ gimp_run_procedure ("file-pat-save-internal",
+ &n_save_retvals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_DRAWABLE, drawable_ID,
+ GIMP_PDB_STRING, param[3].data.d_string,
+ GIMP_PDB_STRING, param[4].data.d_string,
+ GIMP_PDB_STRING, description,
+ GIMP_PDB_END);
+
+
+ if (save_retvals[0].data.d_status == GIMP_PDB_SUCCESS)
+ {
+ gimp_set_data (SAVE_PROC, description, sizeof (description));
+ }
+ else
+ {
+ g_set_error (&error, 0, 0,
+ "Running procedure 'file-pat-save-internal' "
+ "failed: %s",
+ gimp_get_pdb_error ());
+
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_destroy_params (save_retvals, n_save_retvals);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ if (strlen (description))
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-pattern-name",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (description) + 1,
+ description);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ gimp_image_detach_parasite (orig_image_ID, "gimp-pattern-name");
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *entry;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("Pattern"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* The main table */
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (entry), 20);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_entry_set_text (GTK_ENTRY (entry), description);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Description:"), 1.0, 0.5,
+ entry, 1, FALSE);
+ gtk_widget_show (entry);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ strncpy (description, gtk_entry_get_text (GTK_ENTRY (entry)),
+ sizeof (description));
+ description[sizeof (description) - 1] = '\0';
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/file-pcx.c b/plug-ins/common/file-pcx.c
new file mode 100644
index 0000000..9d5d9d9
--- /dev/null
+++ b/plug-ins/common/file-pcx.c
@@ -0,0 +1,1070 @@
+/*
+ * pcx.c GIMP plug-in for loading & exporting PCX files
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is based in parts on code by Francisco Bustamante, but the
+ largest portion of the code has been rewritten and is now maintained
+ occasionally by Nick Lamb njl195@zepler.org.uk */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-pcx-load"
+#define SAVE_PROC "file-pcx-save"
+#define PLUG_IN_BINARY "file-pcx"
+#define PLUG_IN_ROLE "gimp-file-pcx"
+
+
+/* Declare local functions. */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+
+static void load_1 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes);
+static void load_4 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes);
+static void load_sub_8 (FILE *fp,
+ gint width,
+ gint height,
+ gint bpp,
+ gint plane,
+ guchar *buf,
+ guint16 bytes);
+static void load_8 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes);
+static void load_24 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes);
+static void readline (FILE *fp,
+ guchar *buf,
+ gint bytes);
+
+static gint save_image (const gchar *filename,
+ gint32 image,
+ gint32 layer,
+ GError **error);
+static void save_less_than_8 (FILE *fp,
+ gint width,
+ gint height,
+ const gint bpp,
+ const guchar *buf,
+ gboolean padding);
+static void save_8 (FILE *fp,
+ gint width,
+ gint height,
+ const guchar *buf,
+ gboolean padding);
+static void save_24 (FILE *fp,
+ gint width,
+ gint height,
+ const guchar *buf,
+ gboolean padding);
+static void writeline (FILE *fp,
+ const guchar *buf,
+ gint bytes);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in Zsoft PCX file format",
+ "FIXME: write help for pcx_load",
+ "Francisco Bustamante & Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "January 1997",
+ N_("ZSoft PCX image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-pcx");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "pcx,pcc",
+ "",
+ "0&,byte,10,2&,byte,1,3&,byte,>0,3,byte,<9");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in ZSoft PCX file format",
+ "FIXME: write help for pcx_save",
+ "Francisco Bustamante & Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "January 1997",
+ N_("ZSoft PCX image"),
+ "INDEXED, RGB, GRAY",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-pcx");
+ gimp_register_save_handler (SAVE_PROC, "pcx,pcc", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "PCX",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static struct
+{
+ guint8 manufacturer;
+ guint8 version;
+ guint8 compression;
+ guint8 bpp;
+ guint16 x1, y1;
+ guint16 x2, y2;
+ guint16 hdpi;
+ guint16 vdpi;
+ guint8 colormap[48];
+ guint8 reserved;
+ guint8 planes;
+ guint16 bytesperline;
+ guint16 color;
+ guint8 filler[58];
+} pcx_header;
+
+static struct {
+ size_t size;
+ gpointer address;
+} const pcx_header_buf_xlate[] = {
+ { 1, &pcx_header.manufacturer },
+ { 1, &pcx_header.version },
+ { 1, &pcx_header.compression },
+ { 1, &pcx_header.bpp },
+ { 2, &pcx_header.x1 },
+ { 2, &pcx_header.y1 },
+ { 2, &pcx_header.x2 },
+ { 2, &pcx_header.y2 },
+ { 2, &pcx_header.hdpi },
+ { 2, &pcx_header.vdpi },
+ { 48, &pcx_header.colormap },
+ { 1, &pcx_header.reserved },
+ { 1, &pcx_header.planes },
+ { 2, &pcx_header.bytesperline },
+ { 2, &pcx_header.color },
+ { 58, &pcx_header.filler },
+ { 0, NULL }
+};
+
+static void
+pcx_header_from_buffer (guint8 *buf)
+{
+ gint i;
+ gint buf_offset = 0;
+
+ for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
+ {
+ memmove (pcx_header_buf_xlate[i].address, buf + buf_offset,
+ pcx_header_buf_xlate[i].size);
+ buf_offset += pcx_header_buf_xlate[i].size;
+ }
+}
+
+static void
+pcx_header_to_buffer (guint8 *buf)
+{
+ gint i;
+ gint buf_offset = 0;
+
+ for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
+ {
+ memmove (buf + buf_offset, pcx_header_buf_xlate[i].address,
+ pcx_header_buf_xlate[i].size);
+ buf_offset += pcx_header_buf_xlate[i].size;
+ }
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fd;
+ GeglBuffer *buffer;
+ guint16 offset_x, offset_y, bytesperline;
+ gint32 width, height;
+ guint16 resolution_x, resolution_y;
+ gint32 image, layer;
+ guchar *dest, cmap[768];
+ guint8 header_buf[128];
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fd = g_fopen (filename, "rb");
+
+ if (! fd)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ if (fread (header_buf, 128, 1, fd) == 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not read header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fd);
+ return -1;
+ }
+
+ pcx_header_from_buffer (header_buf);
+
+ if (pcx_header.manufacturer != 10)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a PCX file"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fd);
+ return -1;
+ }
+
+ offset_x = GUINT16_FROM_LE (pcx_header.x1);
+ offset_y = GUINT16_FROM_LE (pcx_header.y1);
+ width = GUINT16_FROM_LE (pcx_header.x2) - offset_x + 1;
+ height = GUINT16_FROM_LE (pcx_header.y2) - offset_y + 1;
+ bytesperline = GUINT16_FROM_LE (pcx_header.bytesperline);
+ resolution_x = GUINT16_FROM_LE (pcx_header.hdpi);
+ resolution_y = GUINT16_FROM_LE (pcx_header.vdpi);
+
+ if ((width <= 0) || (width > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_message (_("Unsupported or invalid image width: %d"), width);
+ fclose (fd);
+ return -1;
+ }
+ if ((height <= 0) || (height > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_message (_("Unsupported or invalid image height: %d"), height);
+ fclose (fd);
+ return -1;
+ }
+ if (bytesperline < ((width * pcx_header.bpp + 7) / 8))
+ {
+ g_message (_("Invalid number of bytes per line in PCX header"));
+ fclose (fd);
+ return -1;
+ }
+ if ((resolution_x < 1) || (resolution_x > GIMP_MAX_RESOLUTION) ||
+ (resolution_y < 1) || (resolution_y > GIMP_MAX_RESOLUTION))
+ {
+ g_message (_("Resolution out of bounds in XCX header, using 72x72"));
+ resolution_x = 72;
+ resolution_y = 72;
+ }
+
+ /* Shield against potential buffer overflows in load_*() functions. */
+ if (G_MAXSIZE / width / height < 3)
+ {
+ g_message (_("Image dimensions too large: width %d x height %d"), width, height);
+ fclose (fd);
+ return -1;
+ }
+
+ if (pcx_header.planes == 3 && pcx_header.bpp == 8)
+ {
+ image= gimp_image_new (width, height, GIMP_RGB);
+ layer= gimp_layer_new (image, _("Background"), width, height,
+ GIMP_RGB_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ }
+ else
+ {
+ image= gimp_image_new (width, height, GIMP_INDEXED);
+ layer= gimp_layer_new (image, _("Background"), width, height,
+ GIMP_INDEXED_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ }
+
+ gimp_image_set_filename (image, filename);
+ gimp_image_set_resolution (image, resolution_x, resolution_y);
+
+ gimp_image_insert_layer (image, layer, -1, 0);
+ gimp_layer_set_offsets (layer, offset_x, offset_y);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ if (pcx_header.planes == 1 && pcx_header.bpp == 1)
+ {
+ const guint8 *colormap = pcx_header.colormap;
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_1 (fd, width, height, dest, bytesperline);
+ /* Monochrome does not mean necessarily B&W. Therefore we still
+ * want to check the header palette, even for just 2 colors.
+ * Hopefully the header palette will always be filled with
+ * meaningful colors and the creator software did not just assume
+ * B&W by being monochrome.
+ * Until now test samples showed that even when B&W the header
+ * palette was correctly filled with these 2 colors and we didn't
+ * find counter-examples.
+ * See bug 159947, comment 21 and 23.
+ */
+ /* ... Actually, there *are* files out there with a zeroed 1-bit palette,
+ * which are supposed to be displayed as B&W (see issue #2997.) These
+ * files *might* be in the wrong (who knows...) but the fact is that
+ * other software, including older versions of GIMP, do display them
+ * "correctly", so let's follow suit: if the two palette colors are
+ * equal, use a B&W palette instead.
+ */
+ if (! memcmp (colormap, colormap + 3, 3))
+ {
+ static const guint8 bw_colormap[6] = { 0, 0, 0,
+ 255, 255, 255};
+ colormap = bw_colormap;
+ }
+ gimp_image_set_colormap (image, colormap, 2);
+ }
+ else if (pcx_header.bpp == 1 && pcx_header.planes == 2)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_sub_8 (fd, width, height, 1, 2, dest, bytesperline);
+ gimp_image_set_colormap (image, pcx_header.colormap, 4);
+ }
+ else if (pcx_header.bpp == 2 && pcx_header.planes == 1)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_sub_8 (fd, width, height, 2, 1, dest, bytesperline);
+ gimp_image_set_colormap (image, pcx_header.colormap, 4);
+ }
+ else if (pcx_header.bpp == 1 && pcx_header.planes == 3)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_sub_8 (fd, width, height, 1, 3, dest, bytesperline);
+ gimp_image_set_colormap (image, pcx_header.colormap, 8);
+ }
+ else if (pcx_header.bpp == 1 && pcx_header.planes == 4)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_4 (fd, width, height, dest, bytesperline);
+ gimp_image_set_colormap (image, pcx_header.colormap, 16);
+ }
+ else if (pcx_header.bpp == 4 && pcx_header.planes == 1)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_sub_8 (fd, width, height, 4, 1, dest, bytesperline);
+ gimp_image_set_colormap (image, pcx_header.colormap, 16);
+ }
+ else if (pcx_header.bpp == 8 && pcx_header.planes == 1)
+ {
+ dest = g_new (guchar, ((gsize) width) * height);
+ load_8 (fd, width, height, dest, bytesperline);
+ fseek (fd, -768L, SEEK_END);
+ fread (cmap, 768, 1, fd);
+ gimp_image_set_colormap (image, cmap, 256);
+ }
+ else if (pcx_header.bpp == 8 && pcx_header.planes == 3)
+ {
+ dest = g_new (guchar, ((gsize) width) * height * 3);
+ load_24 (fd, width, height, dest, bytesperline);
+ }
+ else
+ {
+ g_message (_("Unusual PCX flavour, giving up"));
+ fclose (fd);
+ return -1;
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, dest, GEGL_AUTO_ROWSTRIDE);
+
+ fclose (fd);
+ g_free (dest);
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
+
+static void
+load_8 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes)
+{
+ gint row;
+ guchar *line = g_new (guchar, bytes);
+
+ for (row = 0; row < height; buf += width, ++row)
+ {
+ readline (fp, line, bytes);
+ memcpy (buf, line, width);
+ gimp_progress_update ((double) row / (double) height);
+ }
+
+ g_free (line);
+}
+
+static void
+load_24 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes)
+{
+ gint x, y, c;
+ guchar *line = g_new (guchar, bytes);
+
+ for (y = 0; y < height; buf += width * 3, ++y)
+ {
+ for (c = 0; c < 3; ++c)
+ {
+ readline (fp, line, bytes);
+ for (x = 0; x < width; ++x)
+ {
+ buf[x * 3 + c] = line[x];
+ }
+ }
+ gimp_progress_update ((double) y / (double) height);
+ }
+
+ g_free (line);
+}
+
+static void
+load_1 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes)
+{
+ gint x, y;
+ guchar *line = g_new (guchar, bytes);
+
+ for (y = 0; y < height; buf += width, ++y)
+ {
+ readline (fp, line, bytes);
+ for (x = 0; x < width; ++x)
+ {
+ if (line[x / 8] & (128 >> (x % 8)))
+ buf[x] = 1;
+ else
+ buf[x] = 0;
+ }
+ gimp_progress_update ((double) y / (double) height);
+ }
+
+ g_free (line);
+}
+
+static void
+load_4 (FILE *fp,
+ gint width,
+ gint height,
+ guchar *buf,
+ guint16 bytes)
+{
+ gint x, y, c;
+ guchar *line = g_new (guchar, bytes);
+
+ for (y = 0; y < height; buf += width, ++y)
+ {
+ for (x = 0; x < width; ++x)
+ buf[x] = 0;
+ for (c = 0; c < 4; ++c)
+ {
+ readline(fp, line, bytes);
+ for (x = 0; x < width; ++x)
+ {
+ if (line[x / 8] & (128 >> (x % 8)))
+ buf[x] += (1 << c);
+ }
+ }
+ gimp_progress_update ((double) y / (double) height);
+ }
+
+ g_free (line);
+}
+
+static void
+load_sub_8 (FILE *fp,
+ gint width,
+ gint height,
+ gint bpp,
+ gint plane,
+ guchar *buf,
+ guint16 bytes)
+{
+ gint x, y, c, b;
+ guchar *line = g_new (guchar, bytes);
+ gint real_bpp = bpp - 1;
+ gint current_bit = 0;
+
+ for (y = 0; y < height; buf += width, ++y)
+ {
+ for (x = 0; x < width; ++x)
+ buf[x] = 0;
+ for (c = 0; c < plane; ++c)
+ {
+ readline (fp, line, bytes);
+ for (x = 0; x < width; ++x)
+ {
+ for (b = 0; b < bpp; b++)
+ {
+ current_bit = bpp * x + b;
+ if (line[current_bit / 8] & (128 >> (current_bit % 8)))
+ buf[x] += (1 << (real_bpp - b + c));
+ }
+ }
+ }
+ gimp_progress_update ((double) y / (double) height);
+ }
+
+ g_free (line);
+}
+
+static void
+readline (FILE *fp,
+ guchar *buf,
+ gint bytes)
+{
+ static guchar count = 0, value = 0;
+
+ if (pcx_header.compression)
+ {
+ while (bytes--)
+ {
+ if (count == 0)
+ {
+ value = fgetc (fp);
+ if (value < 0xc0)
+ {
+ count = 1;
+ }
+ else
+ {
+ count = value - 0xc0;
+ value = fgetc (fp);
+ }
+ }
+ count--;
+ *(buf++) = value;
+ }
+ }
+ else
+ {
+ fread (buf, bytes, 1, fp);
+ }
+}
+
+static gint
+save_image (const gchar *filename,
+ gint32 image,
+ gint32 layer,
+ GError **error)
+{
+ FILE *fp;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GimpImageType drawable_type;
+ guchar *cmap= NULL;
+ guchar *pixels;
+ gint offset_x, offset_y;
+ guint width, height;
+ gdouble resolution_x, resolution_y;
+ gint colors, i;
+ guint8 header_buf[128];
+ gboolean padding = FALSE;
+
+ drawable_type = gimp_drawable_type (layer);
+ gimp_drawable_offsets (layer, &offset_x, &offset_y);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ pcx_header.manufacturer = 0x0a;
+ pcx_header.version = 5;
+ pcx_header.compression = 1;
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ cmap = gimp_image_get_colormap (image, &colors);
+ if (colors > 16)
+ {
+ pcx_header.bpp = 8;
+ pcx_header.planes = 1;
+ pcx_header.bytesperline = width;
+ }
+ else if (colors > 2)
+ {
+ pcx_header.bpp = 4;
+ pcx_header.planes = 1;
+ pcx_header.bytesperline = (width + 1) / 2;
+ }
+ else
+ {
+ pcx_header.bpp = 1;
+ pcx_header.planes = 1;
+ pcx_header.bytesperline = (width + 7) / 8;
+ }
+ pcx_header.color = GUINT16_TO_LE (1);
+ format = NULL;
+
+ /* Some references explain that 2bpp/1plane and 4bpp/1plane files
+ * would use the palette at EOF (not the one from the header) if
+ * we are in version 5 of PCX. Other sources affirm that even in
+ * version 5, EOF palette must be used only when there are more
+ * than 16 colors. We go with this second assumption.
+ * See bug 159947, comment 21 and 23.
+ */
+ if (colors <= 16)
+ {
+ for (i = 0; i < (colors * 3); i++)
+ {
+ pcx_header.colormap[i] = cmap[i];
+ }
+ }
+
+ break;
+
+ case GIMP_RGB_IMAGE:
+ pcx_header.bpp = 8;
+ pcx_header.planes = 3;
+ pcx_header.color = GUINT16_TO_LE (1);
+ pcx_header.bytesperline = width;
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ pcx_header.bpp = 8;
+ pcx_header.planes = 1;
+ pcx_header.color = GUINT16_TO_LE (2);
+ pcx_header.bytesperline = width;
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ g_message (_("Cannot export images with alpha channel."));
+ return FALSE;
+ }
+
+ /* Bytes per Line must be an even number, according to spec */
+ if (pcx_header.bytesperline % 2 != 0)
+ {
+ pcx_header.bytesperline++;
+ padding = TRUE;
+ }
+ pcx_header.bytesperline = GUINT16_TO_LE (pcx_header.bytesperline);
+
+ pixels = (guchar *) g_malloc (width * height * pcx_header.planes);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
+ format, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if ((offset_x < 0) || (offset_x > (1<<16)))
+ {
+ g_message (_("Invalid X offset: %d"), offset_x);
+ return FALSE;
+ }
+
+ if ((offset_y < 0) || (offset_y > (1<<16)))
+ {
+ g_message (_("Invalid Y offset: %d"), offset_y);
+ return FALSE;
+ }
+
+ if (offset_x + width - 1 > (1<<16))
+ {
+ g_message (_("Right border out of bounds (must be < %d): %d"), (1<<16),
+ offset_x + width - 1);
+ return FALSE;
+ }
+
+ if (offset_y + height - 1 > (1<<16))
+ {
+ g_message (_("Bottom border out of bounds (must be < %d): %d"), (1<<16),
+ offset_y + height - 1);
+ return FALSE;
+ }
+
+ if ((fp = g_fopen (filename, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ pcx_header.x1 = GUINT16_TO_LE ((guint16)offset_x);
+ pcx_header.y1 = GUINT16_TO_LE ((guint16)offset_y);
+ pcx_header.x2 = GUINT16_TO_LE ((guint16)(offset_x + width - 1));
+ pcx_header.y2 = GUINT16_TO_LE ((guint16)(offset_y + height - 1));
+
+ gimp_image_get_resolution (image, &resolution_x, &resolution_y);
+
+ pcx_header.hdpi = GUINT16_TO_LE (RINT (MAX (resolution_x, 1.0)));
+ pcx_header.vdpi = GUINT16_TO_LE (RINT (MAX (resolution_y, 1.0)));
+ pcx_header.reserved = 0;
+
+ pcx_header_to_buffer (header_buf);
+
+ fwrite (header_buf, 128, 1, fp);
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ if (colors > 16)
+ {
+ save_8 (fp, width, height, pixels, padding);
+ fputc (0x0c, fp);
+ fwrite (cmap, colors, 3, fp);
+ for (i = colors; i < 256; i++)
+ {
+ fputc (0, fp);
+ fputc (0, fp);
+ fputc (0, fp);
+ }
+ }
+ else /* Covers 1 and 4 bpp */
+ {
+ save_less_than_8 (fp, width, height, pcx_header.bpp, pixels, padding);
+ }
+ break;
+
+ case GIMP_RGB_IMAGE:
+ save_24 (fp, width, height, pixels, padding);
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ save_8 (fp, width, height, pixels, padding);
+ fputc (0x0c, fp);
+ for (i = 0; i < 256; i++)
+ {
+ fputc ((guchar) i, fp);
+ fputc ((guchar) i, fp);
+ fputc ((guchar) i, fp);
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ g_object_unref (buffer);
+ g_free (pixels);
+
+ if (fclose (fp) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Writing to file '%s' failed: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+save_less_than_8 (FILE *fp,
+ gint width,
+ gint height,
+ const gint bpp,
+ const guchar *buf,
+ gboolean padding)
+{
+ const gint bit_limit = (8 - bpp);
+ const gint buf_size = width * height;
+ const gint line_end = width - 1;
+ gint j = bit_limit;
+ gint count = 0;
+ guchar byte_to_write = 0x00;
+ guchar *line;
+ gint x;
+
+ line = (guchar *) g_malloc (((width + 7) / 8) * bpp);
+
+ for (x = 0; x < buf_size; x++)
+ {
+ byte_to_write |= (buf[x] << j);
+ j -= bpp;
+
+ if (j < 0 || (x % width == line_end))
+ {
+ line[count] = byte_to_write;
+ count++;
+ byte_to_write = 0x00;
+ j = bit_limit;
+
+ if ((x % width == line_end))
+ {
+ writeline (fp, line, count);
+ count = 0;
+ if (padding)
+ fputc ('\0', fp);
+ gimp_progress_update ((double) x / (double) buf_size);
+ }
+ }
+ }
+ g_free (line);
+}
+
+static void
+save_8 (FILE *fp,
+ gint width,
+ gint height,
+ const guchar *buf,
+ gboolean padding)
+{
+ int row;
+
+ for (row = 0; row < height; ++row)
+ {
+ writeline (fp, buf, width);
+ buf += width;
+ if (padding)
+ fputc ('\0', fp);
+ gimp_progress_update ((double) row / (double) height);
+ }
+}
+
+static void
+save_24 (FILE *fp,
+ gint width,
+ gint height,
+ const guchar *buf,
+ gboolean padding)
+{
+ int x, y, c;
+ guchar *line;
+
+ line = (guchar *) g_malloc (width);
+
+ for (y = 0; y < height; ++y)
+ {
+ for (c = 0; c < 3; ++c)
+ {
+ for (x = 0; x < width; ++x)
+ {
+ line[x] = buf[(3*x) + c];
+ }
+ writeline (fp, line, width);
+ if (padding)
+ fputc ('\0', fp);
+ }
+ buf += width * 3;
+ gimp_progress_update ((double) y / (double) height);
+ }
+ g_free (line);
+}
+
+static void
+writeline (FILE *fp,
+ const guchar *buf,
+ gint bytes)
+{
+ const guchar *finish = buf + bytes;
+ guchar value;
+ guchar count;
+
+ while (buf < finish)
+ {
+ value = *(buf++);
+ count = 1;
+
+ while (buf < finish && count < 63 && *buf == value)
+ {
+ count++; buf++;
+ }
+
+ if (value < 0xc0 && count == 1)
+ {
+ fputc (value, fp);
+ }
+ else
+ {
+ fputc (0xc0 + count, fp);
+ fputc (value, fp);
+ }
+ }
+}
diff --git a/plug-ins/common/file-pdf-load.c b/plug-ins/common/file-pdf-load.c
new file mode 100644
index 0000000..a1ff026
--- /dev/null
+++ b/plug-ins/common/file-pdf-load.c
@@ -0,0 +1,2058 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * file-pdf-load.c - PDF file loader
+ *
+ * Copyright (C) 2005 Nathan Summers
+ *
+ * Some code in render_page_to_surface() borrowed from
+ * poppler.git/glib/poppler-page.cc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#undef GTK_DISABLE_SINGLE_INCLUDES
+#include <poppler.h>
+#define GTK_DISABLE_SINGLE_INCLUDES
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-pdf-load"
+#define LOAD2_PROC "file-pdf-load2"
+#define LOAD_THUMB_PROC "file-pdf-load-thumb"
+#define PLUG_IN_BINARY "file-pdf-load"
+#define PLUG_IN_ROLE "gimp-file-pdf-load"
+
+#define THUMBNAIL_SIZE 128
+
+#define GIMP_PLUGIN_PDF_LOAD_ERROR gimp_plugin_pdf_load_error_quark ()
+static GQuark
+gimp_plugin_pdf_load_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-plugin-pdf-load-error-quark");
+}
+
+/* Structs for the load dialog */
+typedef struct
+{
+ GimpPageSelectorTarget target;
+ gdouble resolution;
+ gboolean antialias;
+ gboolean reverse_order;
+ gchar *PDF_password;
+ gboolean white_background;
+} PdfLoadVals;
+
+static PdfLoadVals loadvals =
+{
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS,
+ 100.00, /* resolution in dpi */
+ TRUE, /* antialias */
+ FALSE, /* reverse_order */
+ NULL, /* pdf_password */
+ TRUE, /* white_background */
+};
+
+typedef struct
+{
+ gint n_pages;
+ gint *pages;
+} PdfSelectedPages;
+
+/* Declare local functions */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (PopplerDocument *doc,
+ const gchar *filename,
+ GimpRunMode run_mode,
+ GimpPageSelectorTarget target,
+ gdouble resolution,
+ gboolean antialias,
+ gboolean white_background,
+ gboolean reverse_order,
+ PdfSelectedPages *pages);
+
+static GimpPDBStatusType load_dialog (PopplerDocument *doc,
+ PdfSelectedPages *pages);
+
+static PopplerDocument * open_document (const gchar *filename,
+ const gchar *PDF_password,
+ GimpRunMode run_mode,
+ GError **error);
+
+static cairo_surface_t * get_thumb_surface (PopplerDocument *doc,
+ gint page,
+ gint preferred_size,
+ gboolean white_background);
+
+static GdkPixbuf * get_thumb_pixbuf (PopplerDocument *doc,
+ gint page,
+ gint preferred_size,
+ gboolean white_background);
+
+static gint32 layer_from_surface (gint32 image,
+ const gchar *layer_name,
+ gint position,
+ cairo_surface_t *surface,
+ gdouble progress_start,
+ gdouble progress_scale);
+
+/**
+ ** the following was formerly part of
+ ** gimpresolutionentry.h and gimpresolutionentry.c,
+ ** moved here because this is the only thing that uses
+ ** it, and it is undesirable to maintain all that api.
+ ** Most unused functions have been removed.
+ **/
+#define GIMP_TYPE_RESOLUTION_ENTRY (gimp_resolution_entry_get_type ())
+#define GIMP_RESOLUTION_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_RESOLUTION_ENTRY, GimpResolutionEntry))
+#define GIMP_RESOLUTION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_RESOLUTION_ENTRY, GimpResolutionEntryClass))
+#define GIMP_IS_RESOLUTION_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_RESOLUTION_ENTRY))
+#define GIMP_IS_RESOLUTION_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_RESOLUTION_ENTRY))
+#define GIMP_RESOLUTION_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_RESOLUTION_ENTRY, GimpResolutionEntryClass))
+
+
+typedef struct _GimpResolutionEntry GimpResolutionEntry;
+typedef struct _GimpResolutionEntryClass GimpResolutionEntryClass;
+
+typedef struct _GimpResolutionEntryField GimpResolutionEntryField;
+
+struct _GimpResolutionEntryField
+{
+ GimpResolutionEntry *gre;
+ GimpResolutionEntryField *corresponding;
+
+ gboolean size;
+
+ GtkWidget *label;
+
+ guint changed_signal;
+
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+
+ gdouble phy_size;
+
+ gdouble value;
+ gdouble min_value;
+ gdouble max_value;
+
+ gint stop_recursion;
+};
+
+
+struct _GimpResolutionEntry
+{
+ GtkTable parent_instance;
+
+ GimpUnit size_unit;
+ GimpUnit unit;
+
+ GtkWidget *unitmenu;
+ GtkWidget *chainbutton;
+
+ GimpResolutionEntryField width;
+ GimpResolutionEntryField height;
+ GimpResolutionEntryField x;
+ GimpResolutionEntryField y;
+
+};
+
+struct _GimpResolutionEntryClass
+{
+ GtkTableClass parent_class;
+
+ void (* value_changed) (GimpResolutionEntry *gse);
+ void (* refval_changed) (GimpResolutionEntry *gse);
+ void (* unit_changed) (GimpResolutionEntry *gse);
+};
+
+
+GType gimp_resolution_entry_get_type (void) G_GNUC_CONST;
+
+GtkWidget * gimp_resolution_entry_new (const gchar *width_label,
+ gdouble width,
+ const gchar *height_label,
+ gdouble height,
+ GimpUnit size_unit,
+ const gchar *res_label,
+ gdouble initial_res,
+ GimpUnit initial_unit);
+
+GtkWidget * gimp_resolution_entry_attach_label (GimpResolutionEntry *gre,
+ const gchar *text,
+ gint row,
+ gint column,
+ gfloat alignment);
+
+gdouble gimp_resolution_entry_get_x_in_dpi (GimpResolutionEntry *gre);
+
+gdouble gimp_resolution_entry_get_y_in_dpi (GimpResolutionEntry *gre);
+
+
+/* signal callback convenience functions */
+void gimp_resolution_entry_update_x_in_dpi (GimpResolutionEntry *gre,
+ gpointer data);
+
+void gimp_resolution_entry_update_y_in_dpi (GimpResolutionEntry *gre,
+ gpointer data);
+
+
+enum
+{
+ WIDTH_CHANGED,
+ HEIGHT_CHANGED,
+ X_CHANGED,
+ Y_CHANGED,
+ UNIT_CHANGED,
+ LAST_SIGNAL
+};
+
+static void gimp_resolution_entry_class_init (GimpResolutionEntryClass *class);
+static void gimp_resolution_entry_init (GimpResolutionEntry *gre);
+
+static void gimp_resolution_entry_update_value (GimpResolutionEntryField *gref,
+ gdouble value);
+static void gimp_resolution_entry_value_callback (GtkWidget *widget,
+ gpointer data);
+static void gimp_resolution_entry_update_unit (GimpResolutionEntry *gre,
+ GimpUnit unit);
+static void gimp_resolution_entry_unit_callback (GtkWidget *widget,
+ GimpResolutionEntry *gre);
+
+static void gimp_resolution_entry_field_init (GimpResolutionEntry *gre,
+ GimpResolutionEntryField *gref,
+ GimpResolutionEntryField *corresponding,
+ guint changed_signal,
+ gdouble initial_val,
+ GimpUnit initial_unit,
+ gboolean size,
+ gint spinbutton_width);
+
+static void gimp_resolution_entry_format_label (GimpResolutionEntry *gre,
+ GtkWidget *label,
+ gdouble size);
+
+/**
+ ** end of gimpresolutionentry stuff
+ ** the actual code can be found at the end of this file
+ **/
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load2_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_STRING, "pdf-password", "The password to decrypt the encrypted PDF file" },
+ { GIMP_PDB_INT32, "n-pages", "Number of pages to load (0 for all)" },
+ { GIMP_PDB_INT32ARRAY,"pages", "The pages to load in the expected order" },
+ /* XXX: Nice to have API at some point, but needs work
+ { GIMP_PDB_INT32, "resolution", "Resolution to rasterize to (dpi)" },
+ { GIMP_PDB_INT32, "antialiasing", "Use anti-aliasing" }, */
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" },
+ { GIMP_PDB_INT32, "image-type", "Image type" },
+ { GIMP_PDB_INT32, "num-layers", "Number of pages" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Load file in PDF format",
+ "Loads files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.\n"
+ "If the PDF document has multiple pages, only the first "
+ "page will be loaded. Call file_pdf_load2() to load "
+ "several pages as layers.",
+ "Nathan Summers",
+ "Nathan Summers",
+ "2005",
+ N_("Portable Document Format"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_install_procedure (LOAD2_PROC,
+ "Load file in PDF format",
+ "Loads files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.\n"
+ "This procedure adds extra parameters to "
+ "file-pdf-load to open encrypted PDF and to allow "
+ "multiple page loading.",
+ "Nathan Summers, Lionel N.",
+ "Nathan Summers, Lionel N.",
+ "2005, 2017",
+ N_("Portable Document Format"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load2_args),
+ G_N_ELEMENTS (load_return_vals),
+ load2_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD2_PROC, "application/pdf");
+ gimp_register_magic_load_handler (LOAD2_PROC,
+ "pdf",
+ "",
+ "0, string,%PDF-");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads a preview from a PDF file.",
+ "Loads a small preview of the first page of the PDF "
+ "format file. Uses the embedded thumbnail if "
+ "present.",
+ "Nathan Summers",
+ "Nathan Summers",
+ "2005",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD2_PROC, LOAD_THUMB_PROC);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[7];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID = -1;
+ PopplerDocument *doc = NULL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0 || strcmp (name, LOAD2_PROC) == 0)
+ {
+ PdfSelectedPages pages = { 0, NULL };
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve last settings */
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gimp_get_data (LOAD_PROC, &loadvals);
+ }
+ else if (strcmp (name, LOAD2_PROC) == 0)
+ {
+ gimp_get_data (LOAD2_PROC, &loadvals);
+ }
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ doc = open_document (param[1].data.d_string,
+ loadvals.PDF_password,
+ run_mode, &error);
+
+ if (!doc)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+ }
+
+ status = load_dialog (doc, &pages);
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gimp_set_data (LOAD_PROC, &loadvals, sizeof(loadvals));
+ }
+ else if (strcmp (name, LOAD2_PROC) == 0)
+ {
+ gimp_set_data (LOAD2_PROC, &loadvals, sizeof(loadvals));
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* FIXME: implement last vals mode */
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ doc = open_document (param[1].data.d_string,
+ NULL, run_mode, &error);
+ }
+ else if (strcmp (name, LOAD2_PROC) == 0)
+ {
+ doc = open_document (param[1].data.d_string,
+ param[3].data.d_string,
+ run_mode, &error);
+ }
+
+ if (doc)
+ {
+ PopplerPage *test_page = poppler_document_get_page (doc, 0);
+
+ if (test_page)
+ {
+ if (strcmp (name, LOAD2_PROC) != 0)
+ {
+ /* For retrocompatibility, file-pdf-load always
+ * just loads the first page. */
+ pages.n_pages = 1;
+ pages.pages = g_new (gint, 1);
+ pages.pages[0] = 0;
+
+ g_object_unref (test_page);
+ }
+ else
+ {
+ gint i;
+ gint doc_n_pages;
+
+ doc_n_pages = poppler_document_get_n_pages (doc);
+ /* The number of imported pages may be bigger than
+ * the number of pages from the original document.
+ * Indeed it is possible to duplicate some pages
+ * by setting the same number several times in the
+ * "pages" argument.
+ * Not ceiling this value is *not* an error.
+ */
+ pages.n_pages = param[4].data.d_int32;
+ if (pages.n_pages <= 0)
+ {
+ pages.n_pages = doc_n_pages;
+ pages.pages = g_new (gint, pages.n_pages);
+ for (i = 0; i < pages.n_pages; i++)
+ pages.pages[i] = i;
+ }
+ else
+ {
+ pages.pages = g_new (gint, pages.n_pages);
+ for (i = 0; i < pages.n_pages; i++)
+ {
+ if (param[5].data.d_int32array[i] >= doc_n_pages)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ g_set_error (&error, GIMP_PLUGIN_PDF_LOAD_ERROR, 0,
+ /* TRANSLATORS: first argument is file name,
+ * second is out-of-range page number, third is
+ * number of pages. Specify order as in English if needed.
+ */
+ ngettext ("PDF document '%1$s' has %3$d page. Page %2$d is out of range.",
+ "PDF document '%1$s' has %3$d pages. Page %2$d is out of range.",
+ doc_n_pages),
+ param[1].data.d_string,
+ param[5].data.d_int32array[i],
+ doc_n_pages);
+ break;
+ }
+ else
+ {
+ pages.pages[i] = param[5].data.d_int32array[i];
+ }
+ }
+ }
+ g_object_unref (test_page);
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ g_object_unref (doc);
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ image_ID = load_image (doc, param[1].data.d_string,
+ run_mode,
+ loadvals.target,
+ loadvals.resolution,
+ loadvals.antialias,
+ loadvals.white_background,
+ loadvals.reverse_order,
+ &pages);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (doc)
+ g_object_unref (doc);
+
+ g_free (pages.pages);
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gdouble width = 0;
+ gdouble height = 0;
+ gdouble scale;
+ gint32 image = -1;
+ gint num_pages = 0;
+ cairo_surface_t *surface = NULL;
+
+ /* Possibly retrieve last settings */
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gimp_get_data (LOAD_PROC, &loadvals);
+ }
+ else if (strcmp (name, LOAD2_PROC) == 0)
+ {
+ gimp_get_data (LOAD2_PROC, &loadvals);
+ }
+
+ doc = open_document (param[0].data.d_string,
+ loadvals.PDF_password,
+ GIMP_RUN_NONINTERACTIVE,
+ &error);
+
+ if (doc)
+ {
+ PopplerPage *page = poppler_document_get_page (doc, 0);
+
+ if (page)
+ {
+ poppler_page_get_size (page, &width, &height);
+
+ g_object_unref (page);
+ }
+
+ num_pages = poppler_document_get_n_pages (doc);
+
+ surface = get_thumb_surface (doc, 0, param[1].data.d_int32, TRUE);
+
+ g_object_unref (doc);
+ }
+
+ if (surface)
+ {
+ image = gimp_image_new (cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface),
+ GIMP_RGB);
+
+ gimp_image_undo_disable (image);
+
+
+ layer_from_surface (image, "thumbnail", 0, surface, 0.0, 1.0);
+ cairo_surface_destroy (surface);
+
+ gimp_image_undo_enable (image);
+ gimp_image_clean_all (image);
+ }
+
+ scale = loadvals.resolution / gimp_unit_get_factor (GIMP_UNIT_POINT);
+
+ width *= scale;
+ height *= scale;
+
+ if (image != -1)
+ {
+ *nreturn_vals = 6;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = GIMP_RGB_IMAGE;
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = num_pages;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+
+ gegl_exit ();
+}
+
+static PopplerDocument*
+open_document (const gchar *filename,
+ const gchar *PDF_password,
+ GimpRunMode run_mode,
+ GError **load_error)
+{
+ PopplerDocument *doc;
+ GFile *file;
+ GError *error = NULL;
+
+ file = g_file_new_for_path (filename);
+ doc = poppler_document_new_from_gfile (file, PDF_password, NULL, &error);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ GtkWidget *label;
+
+ label = gtk_label_new (_("PDF is password protected, please input the password:"));
+ while (error &&
+ error->domain == POPPLER_ERROR &&
+ error->code == POPPLER_ERROR_ENCRYPTED)
+ {
+ GtkWidget *vbox;
+ GtkWidget *dialog;
+ GtkWidget *entry;
+ gint run;
+
+ dialog = gimp_dialog_new (_("Encrypted PDF"), PLUG_IN_ROLE,
+ NULL, 0,
+ NULL, NULL,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+ NULL);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ entry = gtk_entry_new ();
+ gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_container_add (GTK_CONTAINER (vbox), label);
+ gtk_container_add (GTK_CONTAINER (vbox), entry);
+
+ gtk_widget_show_all (dialog);
+
+ run = gimp_dialog_run (GIMP_DIALOG (dialog));
+ if (run == GTK_RESPONSE_OK)
+ {
+ g_clear_error (&error);
+ doc = poppler_document_new_from_gfile (file,
+ gtk_entry_get_text (GTK_ENTRY (entry)),
+ NULL, &error);
+ }
+ label = gtk_label_new (_("Wrong password! Please input the right one:"));
+ gtk_widget_destroy (dialog);
+ if (run == GTK_RESPONSE_CANCEL || run == GTK_RESPONSE_DELETE_EVENT)
+ {
+ break;
+ }
+ }
+ gtk_widget_destroy (label);
+ }
+ g_object_unref (file);
+
+ /* We can't g_mapped_file_unref(mapped_file) as apparently doc has
+ * references to data in there. No big deal, this is just a
+ * short-lived plug-in.
+ */
+ if (! doc)
+ {
+ g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not load '%s': %s"),
+ gimp_filename_to_utf8 (filename),
+ error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ return doc;
+}
+
+/* FIXME: Remove this someday when we depend fully on GTK+ >= 3 */
+
+#if (!GTK_CHECK_VERSION (3, 0, 0))
+
+static cairo_format_t
+gdk_cairo_format_for_content (cairo_content_t content)
+{
+ switch (content)
+ {
+ case CAIRO_CONTENT_COLOR:
+ return CAIRO_FORMAT_RGB24;
+ case CAIRO_CONTENT_ALPHA:
+ return CAIRO_FORMAT_A8;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ default:
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+
+static cairo_surface_t *
+gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
+ cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ cairo_surface_t *copy;
+ cairo_t *cr;
+
+ copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
+ width,
+ height);
+
+ cr = cairo_create (copy);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface (cr, surface, -src_x, -src_y);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ return copy;
+}
+
+static void
+convert_alpha (guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ int x, y;
+
+ src_data += src_stride * src_y + src_x * 4;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ guint alpha = src[x] >> 24;
+
+ if (alpha == 0)
+ {
+ dest_data[x * 4 + 0] = 0;
+ dest_data[x * 4 + 1] = 0;
+ dest_data[x * 4 + 2] = 0;
+ }
+ else
+ {
+ dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ }
+ dest_data[x * 4 + 3] = alpha;
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+static void
+convert_no_alpha (guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ int x, y;
+
+ src_data += src_stride * src_y + src_x * 4;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ dest_data[x * 3 + 0] = src[x] >> 16;
+ dest_data[x * 3 + 1] = src[x] >> 8;
+ dest_data[x * 3 + 2] = src[x];
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+/**
+ * gdk_pixbuf_get_from_surface:
+ * @surface: surface to copy from
+ * @src_x: Source X coordinate within @surface
+ * @src_y: Source Y coordinate within @surface
+ * @width: Width in pixels of region to get
+ * @height: Height in pixels of region to get
+ *
+ * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
+ * representation inside a #GdkPixbuf. This allows you to efficiently read
+ * individual pixels from cairo surfaces. For #GdkWindows, use
+ * gdk_pixbuf_get_from_window() instead.
+ *
+ * This function will create an RGB pixbuf with 8 bits per channel.
+ * The pixbuf will contain an alpha channel if the @surface contains one.
+ *
+ * Return value: (transfer full): A newly-created pixbuf with a reference
+ * count of 1, or %NULL on error
+ */
+static GdkPixbuf *
+gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
+ gint src_x,
+ gint src_y,
+ gint width,
+ gint height)
+{
+ cairo_content_t content;
+ GdkPixbuf *dest;
+
+ /* General sanity checks */
+ g_return_val_if_fail (surface != NULL, NULL);
+ g_return_val_if_fail (width > 0 && height > 0, NULL);
+
+ content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+ dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ !!(content & CAIRO_CONTENT_ALPHA),
+ 8,
+ width, height);
+
+ surface = gdk_cairo_surface_coerce_to_image (surface, content,
+ src_x, src_y,
+ width, height);
+ cairo_surface_flush (surface);
+ if (cairo_surface_status (surface) || dest == NULL)
+ {
+ cairo_surface_destroy (surface);
+ return NULL;
+ }
+
+ if (gdk_pixbuf_get_has_alpha (dest))
+ convert_alpha (gdk_pixbuf_get_pixels (dest),
+ gdk_pixbuf_get_rowstride (dest),
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_stride (surface),
+ 0, 0,
+ width, height);
+ else
+ convert_no_alpha (gdk_pixbuf_get_pixels (dest),
+ gdk_pixbuf_get_rowstride (dest),
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_stride (surface),
+ 0, 0,
+ width, height);
+
+ cairo_surface_destroy (surface);
+ return dest;
+}
+
+#endif
+
+static gint32
+layer_from_surface (gint32 image,
+ const gchar *layer_name,
+ gint position,
+ cairo_surface_t *surface,
+ gdouble progress_start,
+ gdouble progress_scale)
+{
+ gint32 layer;
+
+ layer = gimp_layer_new_from_surface (image, layer_name, surface,
+ progress_start,
+ progress_start + progress_scale);
+ gimp_image_insert_layer (image, layer, -1, position);
+
+ return layer;
+}
+
+static cairo_surface_t *
+render_page_to_surface (PopplerPage *page,
+ int width,
+ int height,
+ double scale,
+ gboolean antialias,
+ gboolean white_background)
+{
+ cairo_surface_t *surface;
+ cairo_t *cr;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+
+ cairo_save (cr);
+ cairo_translate (cr, 0.0, 0.0);
+
+ if (scale != 1.0)
+ cairo_scale (cr, scale, scale);
+
+ if (! antialias)
+ {
+ cairo_font_options_t *options = cairo_font_options_create ();
+
+ cairo_get_font_options (cr, options);
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
+ cairo_set_font_options (cr, options);
+ cairo_font_options_destroy (options);
+
+ cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+ }
+
+ poppler_page_render (page, cr);
+ cairo_restore (cr);
+
+ if (white_background)
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_paint (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return surface;
+}
+
+#if 0
+
+/* This is currently unused, but we'll have it here in case the military
+ wants it. */
+
+static GdkPixbuf *
+render_page_to_pixbuf (PopplerPage *page,
+ int width,
+ int height,
+ double scale)
+{
+ GdkPixbuf *pixbuf;
+ cairo_surface_t *surface;
+
+ surface = render_page_to_surface (page, width, height, scale);
+ pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0,
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface));
+ cairo_surface_destroy (surface);
+
+ return pixbuf;
+}
+
+#endif
+
+static gint32
+load_image (PopplerDocument *doc,
+ const gchar *filename,
+ GimpRunMode run_mode,
+ GimpPageSelectorTarget target,
+ gdouble resolution,
+ gboolean antialias,
+ gboolean white_background,
+ gboolean reverse_order,
+ PdfSelectedPages *pages)
+{
+ gint32 image_ID = 0;
+ gint32 *images = NULL;
+ gint i;
+ gdouble scale;
+ gdouble doc_progress = 0;
+ gint base_index = 0;
+ gint sign = 1;
+
+ if (reverse_order && pages->n_pages > 0)
+ {
+ base_index = pages->n_pages - 1;
+ sign = -1;
+ }
+
+ if (target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ images = g_new0 (gint32, pages->n_pages);
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ scale = resolution / gimp_unit_get_factor (GIMP_UNIT_POINT);
+
+ /* read the file */
+
+ for (i = 0; i < pages->n_pages; i++)
+ {
+ PopplerPage *page;
+ gchar *page_label;
+ gdouble page_width;
+ gdouble page_height;
+
+ cairo_surface_t *surface;
+ gint width;
+ gint height;
+ gint page_index;
+
+ page_index = base_index + sign * i;
+
+ page = poppler_document_get_page (doc, pages->pages[page_index]);
+
+ poppler_page_get_size (page, &page_width, &page_height);
+ width = page_width * scale;
+ height = page_height * scale;
+
+ g_object_get (G_OBJECT (page), "label", &page_label, NULL);
+
+ if (! image_ID)
+ {
+ gchar *name;
+
+ image_ID = gimp_image_new (width, height, GIMP_RGB);
+ gimp_image_undo_disable (image_ID);
+
+ if (target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ name = g_strdup_printf (_("%s-%s"), filename, page_label);
+ else
+ name = g_strdup_printf (_("%s-pages"), filename);
+
+ gimp_image_set_filename (image_ID, name);
+ g_free (name);
+
+ gimp_image_set_resolution (image_ID, resolution, resolution);
+ }
+
+ surface = render_page_to_surface (page, width, height, scale,
+ antialias, white_background);
+
+ layer_from_surface (image_ID, page_label, 0, surface,
+ doc_progress, 1.0 / pages->n_pages);
+
+ g_free (page_label);
+ cairo_surface_destroy (surface);
+
+ doc_progress = (double) (i + 1) / pages->n_pages;
+ gimp_progress_update (doc_progress);
+
+ if (target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ images[i] = image_ID;
+
+ gimp_image_undo_enable (image_ID);
+ gimp_image_clean_all (image_ID);
+
+ image_ID = 0;
+ }
+ }
+ gimp_progress_update (1.0);
+
+ if (image_ID)
+ {
+ gimp_image_undo_enable (image_ID);
+ gimp_image_clean_all (image_ID);
+ }
+
+ if (target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ /* Display images in reverse order. The last will be
+ * displayed by GIMP itself
+ */
+ for (i = pages->n_pages - 1; i > 0; i--)
+ gimp_display_new (images[i]);
+ }
+
+ image_ID = images[0];
+
+ g_free (images);
+ }
+
+ return image_ID;
+}
+
+static cairo_surface_t *
+get_thumb_surface (PopplerDocument *doc,
+ gint page_num,
+ gint preferred_size,
+ gboolean white_background)
+{
+ PopplerPage *page;
+ cairo_surface_t *surface;
+
+ page = poppler_document_get_page (doc, page_num);
+
+ if (! page)
+ return NULL;
+
+ surface = poppler_page_get_thumbnail (page);
+
+ if (! surface)
+ {
+ gdouble width;
+ gdouble height;
+ gdouble scale;
+
+ poppler_page_get_size (page, &width, &height);
+
+ scale = (gdouble) preferred_size / MAX (width, height);
+
+ width *= scale;
+ height *= scale;
+
+ surface = render_page_to_surface (page, width, height, scale, TRUE, white_background);
+ }
+
+ g_object_unref (page);
+
+ return surface;
+}
+
+static GdkPixbuf *
+get_thumb_pixbuf (PopplerDocument *doc,
+ gint page_num,
+ gint preferred_size,
+ gboolean white_background)
+{
+ cairo_surface_t *surface;
+ GdkPixbuf *pixbuf;
+
+ surface = get_thumb_surface (doc, page_num, preferred_size, white_background);
+ pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0,
+ cairo_image_surface_get_width (surface),
+ cairo_image_surface_get_height (surface));
+ cairo_surface_destroy (surface);
+
+ return pixbuf;
+}
+
+typedef struct
+{
+ PopplerDocument *document;
+ GimpPageSelector *selector;
+ gboolean stop_thumbnailing;
+ gboolean white_background;
+
+ GMutex mutex;
+ GCond render_thumb;
+} ThreadData;
+
+typedef struct
+{
+ GimpPageSelector *selector;
+ gint page_no;
+ GdkPixbuf *pixbuf;
+} IdleData;
+
+static gboolean
+idle_set_thumbnail (gpointer data)
+{
+ IdleData *idle_data = data;
+
+ gimp_page_selector_set_page_thumbnail (idle_data->selector,
+ idle_data->page_no,
+ idle_data->pixbuf);
+ g_object_unref (idle_data->pixbuf);
+ g_free (idle_data);
+
+ return FALSE;
+}
+
+static gpointer
+thumbnail_thread (gpointer data)
+{
+ ThreadData *thread_data = data;
+ gboolean first_loop = TRUE;
+ gint n_pages;
+ gint i;
+
+ n_pages = poppler_document_get_n_pages (thread_data->document);
+
+ while (TRUE)
+ {
+ gboolean white_background;
+ gboolean stop_thumbnailing;
+
+ g_mutex_lock (&thread_data->mutex);
+ if (! first_loop)
+ g_cond_wait (&thread_data->render_thumb, &thread_data->mutex);
+ white_background = thread_data->white_background;
+ stop_thumbnailing = thread_data->stop_thumbnailing;
+ g_mutex_unlock (&thread_data->mutex);
+
+ if (stop_thumbnailing)
+ break;
+
+ for (i = 0; i < n_pages; i++)
+ {
+ IdleData *idle_data = g_new0 (IdleData, 1);
+ gboolean white_background2;
+
+ idle_data->selector = thread_data->selector;
+ idle_data->page_no = i;
+
+ /* FIXME get preferred size from somewhere? */
+ idle_data->pixbuf = get_thumb_pixbuf (thread_data->document, i,
+ THUMBNAIL_SIZE,
+ white_background);
+
+ g_idle_add (idle_set_thumbnail, idle_data);
+
+ g_mutex_lock (&thread_data->mutex);
+ white_background2 = thread_data->white_background;
+ stop_thumbnailing = thread_data->stop_thumbnailing;
+ g_mutex_unlock (&thread_data->mutex);
+
+ if (stop_thumbnailing || white_background2 != white_background)
+ break;
+ }
+
+ if (stop_thumbnailing)
+ break;
+ }
+
+ return NULL;
+}
+
+static void
+white_background_toggled (GtkToggleButton *widget,
+ ThreadData *thread_data)
+{
+ g_mutex_lock (&thread_data->mutex);
+ thread_data->white_background = gtk_toggle_button_get_active (widget);
+ g_cond_signal (&thread_data->render_thumb);
+ g_mutex_unlock (&thread_data->mutex);
+}
+
+static GimpPDBStatusType
+load_dialog (PopplerDocument *doc,
+ PdfSelectedPages *pages)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *title;
+ GtkWidget *selector;
+ GtkWidget *resolution;
+ GtkWidget *antialias;
+ GtkWidget *white_bg;
+ GtkWidget *hbox;
+ GtkWidget *reverse_order;
+ GtkWidget *separator;
+
+ ThreadData thread_data;
+ GThread *thread;
+
+ gint i;
+ gint n_pages;
+
+ gdouble width;
+ gdouble height;
+
+ gboolean run;
+
+ dialog = gimp_dialog_new (_("Import from PDF"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* Title */
+ title = gimp_prop_label_new (G_OBJECT (doc), "title");
+ gtk_label_set_ellipsize (GTK_LABEL (title), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (vbox), title, FALSE, FALSE, 0);
+ gtk_widget_show (title);
+
+ /* Page Selector */
+ selector = gimp_page_selector_new ();
+ gtk_widget_set_size_request (selector, 380, 360);
+ gtk_box_pack_start (GTK_BOX (vbox), selector, TRUE, TRUE, 0);
+ gtk_widget_show (selector);
+
+ n_pages = poppler_document_get_n_pages (doc);
+
+ if (n_pages <= 0)
+ {
+ g_message (_("Error getting number of pages from the given PDF file."));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector), n_pages);
+ gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector),
+ loadvals.target);
+
+ for (i = 0; i < n_pages; i++)
+ {
+ PopplerPage *page;
+ gchar *label;
+
+ page = poppler_document_get_page (doc, i);
+ g_object_get (G_OBJECT (page), "label", &label, NULL);
+
+ gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (selector), i,
+ label);
+
+ if (i == 0)
+ poppler_page_get_size (page, &width, &height);
+
+ g_object_unref (page);
+ g_free (label);
+ }
+ /* Since selecting none will be equivalent to selecting all, this is
+ * only useful as a feedback for the default behavior of selecting all
+ * pages. */
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
+
+ g_signal_connect_swapped (selector, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+
+ thread_data.document = doc;
+ thread_data.selector = GIMP_PAGE_SELECTOR (selector);
+ thread_data.stop_thumbnailing = FALSE;
+ thread_data.white_background = loadvals.white_background;
+ g_mutex_init (&thread_data.mutex);
+ g_cond_init (&thread_data.render_thumb);
+
+ thread = g_thread_new ("thumbnailer", thumbnail_thread, &thread_data);
+
+ /* "Load in reverse order" toggle button */
+ reverse_order = gtk_check_button_new_with_mnemonic (_("Load in reverse order"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (reverse_order), loadvals.reverse_order);
+ gtk_box_pack_start (GTK_BOX (vbox), reverse_order, FALSE, FALSE, 0);
+
+ g_signal_connect (reverse_order, "toggled",
+ G_CALLBACK (gimp_toggle_button_update), &loadvals.reverse_order);
+ gtk_widget_show (reverse_order);
+
+ /* Separator */
+ separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+ gtk_widget_show (separator);
+
+ /* Resolution */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ resolution = gimp_resolution_entry_new (_("_Width (pixels):"), width,
+ _("_Height (pixels):"), height,
+ GIMP_UNIT_POINT,
+ _("_Resolution:"),
+ loadvals.resolution, GIMP_UNIT_INCH);
+
+ gtk_box_pack_start (GTK_BOX (hbox), resolution, FALSE, FALSE, 0);
+ gtk_widget_show (resolution);
+
+ g_signal_connect (resolution, "x-changed",
+ G_CALLBACK (gimp_resolution_entry_update_x_in_dpi),
+ &loadvals.resolution);
+
+ /* Antialiasing*/
+ antialias = gtk_check_button_new_with_mnemonic (_("Use _Anti-aliasing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (antialias), loadvals.antialias);
+ gtk_box_pack_start (GTK_BOX (vbox), antialias, FALSE, FALSE, 0);
+
+ g_signal_connect (antialias, "toggled",
+ G_CALLBACK (gimp_toggle_button_update), &loadvals.antialias);
+ gtk_widget_show (antialias);
+
+ /* White Background */
+ white_bg = gtk_check_button_new_with_mnemonic (_("_Fill transparent areas with white"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (white_bg), loadvals.white_background);
+ gtk_box_pack_start (GTK_BOX (vbox), white_bg, FALSE, FALSE, 0);
+
+ g_signal_connect (white_bg, "toggled",
+ G_CALLBACK (gimp_toggle_button_update), &loadvals.white_background);
+ g_signal_connect (white_bg, "toggled",
+ G_CALLBACK (white_background_toggled), &thread_data);
+ gtk_widget_show (white_bg);
+
+ /* Setup done; display the dialog */
+ gtk_widget_show (dialog);
+
+ /* run the dialog */
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ loadvals.target =
+ gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (selector),
+ &pages->n_pages);
+
+ /* select all if none selected */
+ if (pages->n_pages == 0)
+ {
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (selector),
+ &pages->n_pages);
+ }
+
+ /* cleanup */
+ g_mutex_lock (&thread_data.mutex);
+ thread_data.stop_thumbnailing = TRUE;
+ g_cond_signal (&thread_data.render_thumb);
+ g_mutex_unlock (&thread_data.mutex);
+ g_thread_join (thread);
+
+ g_mutex_clear (&thread_data.mutex);
+ g_cond_clear (&thread_data.render_thumb);
+
+ return run ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL;
+}
+
+
+/**
+ ** code for GimpResolutionEntry widget, formerly in libgimpwidgets
+ **/
+
+static guint gimp_resolution_entry_signals[LAST_SIGNAL] = { 0 };
+
+static GtkTableClass *parent_class = NULL;
+
+
+GType
+gimp_resolution_entry_get_type (void)
+{
+ static GType gre_type = 0;
+
+ if (! gre_type)
+ {
+ const GTypeInfo gre_info =
+ {
+ sizeof (GimpResolutionEntryClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_resolution_entry_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GimpResolutionEntry),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gimp_resolution_entry_init,
+ };
+
+ gre_type = g_type_register_static (GTK_TYPE_TABLE,
+ "GimpResolutionEntry",
+ &gre_info, 0);
+ }
+
+ return gre_type;
+}
+
+static void
+gimp_resolution_entry_class_init (GimpResolutionEntryClass *klass)
+{
+ parent_class = g_type_class_peek_parent (klass);
+
+ gimp_resolution_entry_signals[HEIGHT_CHANGED] =
+ g_signal_new ("height-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpResolutionEntryClass, value_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_resolution_entry_signals[WIDTH_CHANGED] =
+ g_signal_new ("width-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpResolutionEntryClass, value_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_resolution_entry_signals[X_CHANGED] =
+ g_signal_new ("x-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpResolutionEntryClass, value_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_resolution_entry_signals[Y_CHANGED] =
+ g_signal_new ("y-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpResolutionEntryClass, refval_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gimp_resolution_entry_signals[UNIT_CHANGED] =
+ g_signal_new ("unit-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpResolutionEntryClass, unit_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->value_changed = NULL;
+ klass->refval_changed = NULL;
+ klass->unit_changed = NULL;
+}
+
+static void
+gimp_resolution_entry_init (GimpResolutionEntry *gre)
+{
+ gre->unitmenu = NULL;
+ gre->unit = GIMP_UNIT_INCH;
+
+ gtk_table_set_col_spacings (GTK_TABLE (gre), 4);
+ gtk_table_set_row_spacings (GTK_TABLE (gre), 2);
+}
+
+static void
+gimp_resolution_entry_field_init (GimpResolutionEntry *gre,
+ GimpResolutionEntryField *gref,
+ GimpResolutionEntryField *corresponding,
+ guint changed_signal,
+ gdouble initial_val,
+ GimpUnit initial_unit,
+ gboolean size,
+ gint spinbutton_width)
+{
+ gint digits;
+
+ g_return_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre));
+
+ gref->gre = gre;
+ gref->corresponding = corresponding;
+ gref->changed_signal = gimp_resolution_entry_signals[changed_signal];
+
+ if (size)
+ {
+ gref->value = initial_val /
+ gimp_unit_get_factor (initial_unit) *
+ corresponding->value *
+ gimp_unit_get_factor (gre->unit);
+
+ gref->phy_size = initial_val /
+ gimp_unit_get_factor (initial_unit);
+ }
+ else
+ {
+ gref->value = initial_val;
+ }
+
+ gref->min_value = GIMP_MIN_RESOLUTION;
+ gref->max_value = GIMP_MAX_RESOLUTION;
+ gref->adjustment = NULL;
+
+ gref->stop_recursion = 0;
+
+ gref->size = size;
+
+ if (size)
+ {
+ gref->label = g_object_new (GTK_TYPE_LABEL,
+ "xalign", 0.0,
+ "yalign", 0.5,
+ NULL);
+ gimp_label_set_attributes (GTK_LABEL (gref->label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+
+ gimp_resolution_entry_format_label (gre, gref->label, gref->phy_size);
+ }
+
+ digits = size ? 0 : MIN (gimp_unit_get_digits (initial_unit), 5) + 1;
+
+ gref->adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (gref->value,
+ gref->min_value,
+ gref->max_value,
+ 1.0, 10.0, 0.0);
+ gref->spinbutton = gimp_spin_button_new (gref->adjustment,
+ 1.0, digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (gref->spinbutton), TRUE);
+
+ if (spinbutton_width > 0)
+ {
+ if (spinbutton_width < 17)
+ gtk_entry_set_width_chars (GTK_ENTRY (gref->spinbutton),
+ spinbutton_width);
+ else
+ gtk_widget_set_size_request (gref->spinbutton,
+ spinbutton_width, -1);
+ }
+}
+
+/**
+ * gimp_resolution_entry_new:
+ * @width_label: Optional label for the width control.
+ * @width: Width of the item, specified in terms of @size_unit.
+ * @height_label: Optional label for the height control.
+ * @height: Height of the item, specified in terms of @size_unit.
+ * @size_unit: Unit used to specify the width and height.
+ * @res_label: Optional label for the resolution entry.
+ * @initial_res: The initial resolution.
+ * @initial_unit: The initial unit.
+ *
+ * Creates a new #GimpResolutionEntry widget.
+ *
+ * The #GimpResolutionEntry is derived from #GtkTable and will have
+ * an empty border of one cell width on each side plus an empty column left
+ * of the #GimpUnitMenu to allow the caller to add labels or other widgets.
+ *
+ * A #GimpChainButton is displayed if independent is set to %TRUE.
+ *
+ * Returns: A pointer to the new #GimpResolutionEntry widget.
+ **/
+GtkWidget *
+gimp_resolution_entry_new (const gchar *width_label,
+ gdouble width,
+ const gchar *height_label,
+ gdouble height,
+ GimpUnit size_unit,
+ const gchar *res_label,
+ gdouble initial_res,
+ GimpUnit initial_unit)
+{
+ GimpResolutionEntry *gre;
+ GtkTreeModel *model;
+
+ gre = g_object_new (GIMP_TYPE_RESOLUTION_ENTRY, NULL);
+
+ gre->unit = initial_unit;
+
+ gtk_table_resize (GTK_TABLE (gre), 4, 4);
+
+ gimp_resolution_entry_field_init (gre, &gre->x,
+ &gre->width,
+ X_CHANGED,
+ initial_res, initial_unit,
+ FALSE, 0);
+
+ gtk_table_attach_defaults (GTK_TABLE (gre), gre->x.spinbutton,
+ 1, 2,
+ 3, 4);
+
+ g_signal_connect (gre->x.adjustment, "value-changed",
+ G_CALLBACK (gimp_resolution_entry_value_callback),
+ &gre->x);
+
+ gtk_widget_show (gre->x.spinbutton);
+
+ gre->unitmenu = gimp_unit_combo_box_new ();
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (gre->unitmenu));
+ gimp_unit_store_set_has_pixels (GIMP_UNIT_STORE (model), FALSE);
+ gimp_unit_store_set_has_percent (GIMP_UNIT_STORE (model), FALSE);
+ g_object_set (model,
+ "short-format", _("pixels/%a"),
+ "long-format", _("pixels/%a"),
+ NULL);
+ gimp_unit_combo_box_set_active (GIMP_UNIT_COMBO_BOX (gre->unitmenu),
+ initial_unit);
+
+ gtk_table_attach (GTK_TABLE (gre), gre->unitmenu,
+ 3, 4, 3, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ g_signal_connect (gre->unitmenu, "changed",
+ G_CALLBACK (gimp_resolution_entry_unit_callback),
+ gre);
+ gtk_widget_show (gre->unitmenu);
+
+ gimp_resolution_entry_field_init (gre, &gre->width,
+ &gre->x,
+ WIDTH_CHANGED,
+ width, size_unit,
+ TRUE, 0);
+
+ gtk_table_attach_defaults (GTK_TABLE (gre), gre->width.spinbutton,
+ 1, 2,
+ 1, 2);
+
+ gtk_table_attach_defaults (GTK_TABLE (gre), gre->width.label,
+ 3, 4,
+ 1, 2);
+
+ g_signal_connect (gre->width.adjustment, "value-changed",
+ G_CALLBACK (gimp_resolution_entry_value_callback),
+ &gre->width);
+
+ gtk_widget_show (gre->width.spinbutton);
+ gtk_widget_show (gre->width.label);
+
+ gimp_resolution_entry_field_init (gre, &gre->height, &gre->x,
+ HEIGHT_CHANGED,
+ height, size_unit,
+ TRUE, 0);
+
+ gtk_table_attach_defaults (GTK_TABLE (gre), gre->height.spinbutton,
+ 1, 2, 2, 3);
+
+ gtk_table_attach_defaults (GTK_TABLE (gre), gre->height.label,
+ 3, 4, 2, 3);
+
+ g_signal_connect (gre->height.adjustment, "value-changed",
+ G_CALLBACK (gimp_resolution_entry_value_callback),
+ &gre->height);
+
+ gtk_widget_show (gre->height.spinbutton);
+ gtk_widget_show (gre->height.label);
+
+ if (width_label)
+ gimp_resolution_entry_attach_label (gre, width_label, 1, 0, 0.0);
+
+ if (height_label)
+ gimp_resolution_entry_attach_label (gre, height_label, 2, 0, 0.0);
+
+ if (res_label)
+ gimp_resolution_entry_attach_label (gre, res_label, 3, 0, 0.0);
+
+ return GTK_WIDGET (gre);
+}
+
+/**
+ * gimp_resolution_entry_attach_label:
+ * @gre: The #GimpResolutionEntry you want to add a label to.
+ * @text: The text of the label.
+ * @row: The row where the label will be attached.
+ * @column: The column where the label will be attached.
+ * @alignment: The horizontal alignment of the label.
+ *
+ * Attaches a #GtkLabel to the #GimpResolutionEntry (which is a #GtkTable).
+ *
+ * Returns: A pointer to the new #GtkLabel widget.
+ **/
+GtkWidget *
+gimp_resolution_entry_attach_label (GimpResolutionEntry *gre,
+ const gchar *text,
+ gint row,
+ gint column,
+ gfloat alignment)
+{
+ GtkWidget *label;
+
+ g_return_val_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre), NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+
+ label = gtk_label_new_with_mnemonic (text);
+
+ if (column == 0)
+ {
+ GList *children;
+ GList *list;
+
+ children = gtk_container_get_children (GTK_CONTAINER (gre));
+
+ for (list = children; list; list = g_list_next (list))
+ {
+ GtkWidget *child = list->data;
+ gint left_attach;
+ gint top_attach;
+
+ gtk_container_child_get (GTK_CONTAINER (gre), child,
+ "left-attach", &left_attach,
+ "top-attach", &top_attach,
+ NULL);
+
+ if (left_attach == 1 && top_attach == row)
+ {
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), child);
+ break;
+ }
+ }
+
+ g_list_free (children);
+ }
+
+ gtk_label_set_xalign (GTK_LABEL (label), alignment);
+
+ gtk_table_attach (GTK_TABLE (gre), label, column, column+1, row, row+1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ return label;
+}
+
+/**
+ * gimp_resolution_entry_get_x_in_dpi;
+ * @gre: The #GimpResolutionEntry you want to know the resolution of.
+ *
+ * Returns the X resolution of the #GimpResolutionEntry in pixels per inch.
+ **/
+gdouble
+gimp_resolution_entry_get_x_in_dpi (GimpResolutionEntry *gre)
+{
+ g_return_val_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre), 0);
+
+ /* dots_in_one_unit * units_in_one_inch -> dpi */
+ return gre->x.value * gimp_unit_get_factor (gre->unit);
+}
+
+/**
+ * gimp_resolution_entry_get_y_in_dpi;
+ * @gre: The #GimpResolutionEntry you want to know the resolution of.
+ *
+ * Returns the Y resolution of the #GimpResolutionEntry in pixels per inch.
+ **/
+gdouble
+gimp_resolution_entry_get_y_in_dpi (GimpResolutionEntry *gre)
+{
+ g_return_val_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre), 0);
+
+ return gre->y.value * gimp_unit_get_factor (gre->unit);
+}
+
+
+static void
+gimp_resolution_entry_update_value (GimpResolutionEntryField *gref,
+ gdouble value)
+{
+ if (gref->stop_recursion > 0)
+ return;
+
+ gref->value = value;
+
+ gref->stop_recursion++;
+
+ if (gref->size)
+ gimp_resolution_entry_update_value (gref->corresponding,
+ gref->value /
+ gref->phy_size /
+ gimp_unit_get_factor (gref->gre->unit));
+ else
+ {
+ gdouble factor = gimp_unit_get_factor (gref->gre->unit);
+
+ gimp_resolution_entry_update_value (&gref->gre->width,
+ gref->value *
+ gref->gre->width.phy_size *
+ factor);
+
+ gimp_resolution_entry_update_value (&gref->gre->height,
+ gref->value *
+ gref->gre->height.phy_size *
+ factor);
+ }
+
+ gtk_adjustment_set_value (gref->adjustment, value);
+
+ gref->stop_recursion--;
+
+ g_signal_emit (gref->gre, gref->changed_signal, 0);
+}
+
+static void
+gimp_resolution_entry_value_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GimpResolutionEntryField *gref = (GimpResolutionEntryField *) data;
+ gdouble new_value;
+
+ new_value = gtk_adjustment_get_value (GTK_ADJUSTMENT (widget));
+
+ if (gref->value != new_value)
+ gimp_resolution_entry_update_value (gref, new_value);
+}
+
+static void
+gimp_resolution_entry_update_unit (GimpResolutionEntry *gre,
+ GimpUnit unit)
+{
+ GimpUnit old_unit;
+ gint digits;
+ gdouble factor;
+
+ old_unit = gre->unit;
+ gre->unit = unit;
+
+ digits = (gimp_unit_get_digits (GIMP_UNIT_INCH) -
+ gimp_unit_get_digits (unit));
+
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (gre->x.spinbutton),
+ MAX (3 + digits, 3));
+
+ factor = gimp_unit_get_factor (old_unit) / gimp_unit_get_factor (unit);
+
+ gre->x.min_value *= factor;
+ gre->x.max_value *= factor;
+ gre->x.value *= factor;
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (gre->x.adjustment),
+ gre->x.value);
+
+ gimp_resolution_entry_format_label (gre,
+ gre->width.label, gre->width.phy_size);
+ gimp_resolution_entry_format_label (gre,
+ gre->height.label, gre->height.phy_size);
+
+ g_signal_emit (gre, gimp_resolution_entry_signals[UNIT_CHANGED], 0);
+}
+
+static void
+gimp_resolution_entry_unit_callback (GtkWidget *widget,
+ GimpResolutionEntry *gre)
+{
+ GimpUnit new_unit;
+
+ new_unit = gimp_unit_combo_box_get_active (GIMP_UNIT_COMBO_BOX (widget));
+
+ if (gre->unit != new_unit)
+ gimp_resolution_entry_update_unit (gre, new_unit);
+}
+
+/**
+ * gimp_resolution_entry_update_x_in_dpi:
+ * @gre: the #GimpResolutionEntry
+ * @data: a pointer to a gdouble
+ *
+ * Convenience function to set a double to the X resolution, suitable
+ * for use as a signal callback.
+ */
+void
+gimp_resolution_entry_update_x_in_dpi (GimpResolutionEntry *gre,
+ gpointer data)
+{
+ gdouble *val;
+
+ g_return_if_fail (gre != NULL);
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre));
+
+ val = (gdouble *) data;
+
+ *val = gimp_resolution_entry_get_x_in_dpi (gre);
+}
+
+/**
+ * gimp_resolution_entry_update_y_in_dpi:
+ * @gre: the #GimpResolutionEntry
+ * @data: a pointer to a gdouble
+ *
+ * Convenience function to set a double to the Y resolution, suitable
+ * for use as a signal callback.
+ */
+void
+gimp_resolution_entry_update_y_in_dpi (GimpResolutionEntry *gre,
+ gpointer data)
+{
+ gdouble *val;
+
+ g_return_if_fail (gre != NULL);
+ g_return_if_fail (data != NULL);
+ g_return_if_fail (GIMP_IS_RESOLUTION_ENTRY (gre));
+
+ val = (gdouble *) data;
+
+ *val = gimp_resolution_entry_get_y_in_dpi (gre);
+}
+
+static void
+gimp_resolution_entry_format_label (GimpResolutionEntry *gre,
+ GtkWidget *label,
+ gdouble size)
+{
+ gchar *format = g_strdup_printf ("%%.%df %%s",
+ gimp_unit_get_digits (gre->unit));
+ gchar *text = g_strdup_printf (format,
+ size * gimp_unit_get_factor (gre->unit),
+ gimp_unit_get_plural (gre->unit));
+ g_free (format);
+
+ gtk_label_set_text (GTK_LABEL (label), text);
+ g_free (text);
+}
diff --git a/plug-ins/common/file-pdf-save.c b/plug-ins/common/file-pdf-save.c
new file mode 100644
index 0000000..ce7fa8d
--- /dev/null
+++ b/plug-ins/common/file-pdf-save.c
@@ -0,0 +1,1882 @@
+/* GIMP - The GNU Image Manipulation Program
+ *
+ * file-pdf-save.c - PDF file exporter, based on the cairo PDF surface
+ *
+ * Copyright (C) 2010 Barak Itkin <lightningismyname@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* The PDF export plugin has 3 main procedures:
+ * 1. file-pdf-save
+ * This is the main procedure. It has 3 options for optimizations of
+ * the pdf file, and it can show a gui. This procedure works on a single
+ * image.
+ * 2. file-pdf-save-defaults
+ * This procedures is the one that will be invoked by gimp's file-save,
+ * when the pdf extension is chosen. If it's in RUN_INTERACTIVE, it will
+ * pop a user interface with more options, like file-pdf-save. If it's in
+ * RUN_NONINTERACTIVE, it will simply use the default values. Note that on
+ * RUN_WITH_LAST_VALS there will be no gui, however the values will be the
+ * ones that were used in the last interactive run (or the defaults if none
+ * are available.
+ * 3. file-pdf-save-multi
+ * This procedures is more advanced, and it allows the creation of multiple
+ * paged pdf files. It will be located in File/Create/Multiple page PDF...
+ *
+ * It was suggested that file-pdf-save-multi will be removed from the UI as it
+ * does not match the product vision (GIMP isn't a program for editing multiple
+ * paged documents).
+ */
+
+/* Known Issues (except for the coding style issues):
+ * 1. Grayscale layers are inverted (although layer masks which are not grayscale,
+ * are not inverted)
+ * 2. Exporting some fonts doesn't work since gimp_text_layer_get_font Returns a
+ * font which is sometimes incompatiable with pango_font_description_from_string
+ * (gimp_text_layer_get_font sometimes returns suffixes such as "semi-expanded" to
+ * the font's name although the GIMP's font selection dialog shows the don'ts name
+ * normally - This should be checked again in GIMP 2.7)
+ * 3. Indexed layers can't be optimized yet (Since gimp_histogram won't work on
+ * indexed layers)
+ * 4. Rendering the pango layout requires multiplying the size in PANGO_SCALE. This
+ * means I'll need to do some hacking on the markup returned from GIMP.
+ * 5. When accessing the contents of layer groups is supported, we should do use it
+ * (since this plugin should preserve layers).
+ *
+ * Also, there are 2 things which we should warn the user about:
+ * 1. Cairo does not support bitmap masks for text.
+ * 2. Currently layer modes are ignored. We do support layers, including
+ * transparency and opacity, but layer modes are not supported.
+ */
+
+/* Changelog
+ *
+ * April 29, 2009 | Barak Itkin <lightningismyname@gmail.com>
+ * First version of the plugin. This is only a proof of concept and not a full
+ * working plugin.
+ *
+ * May 6, 2009 Barak | Itkin <lightningismyname@gmail.com>
+ * Added new features and several bugfixes:
+ * - Added handling for image resolutions
+ * - fixed the behaviour of getting font sizes
+ * - Added various optimizations (solid rectangles instead of bitmaps, ignoring
+ * invisible layers, etc.) as a macro flag.
+ * - Added handling for layer masks, use CAIRO_FORMAT_A8 for grayscale drawables.
+ * - Indexed layers are now supported
+ *
+ * August 17, 2009 | Barak Itkin <lightningismyname@gmail.com>
+ * Most of the plugin was rewritten from scratch and it now has several new
+ * features:
+ * - Got rid of the optimization macros in the code. The gui now supports
+ * selecting which optimizations to apply.
+ * - Added a procedure to allow the creation of multiple paged PDF's
+ * - Registered the plugin on "<Image>/File/Create/PDF"
+ *
+ * August 21, 2009 | Barak Itkin <lightningismyname@gmail.com>
+ * Fixed a typo that prevented the plugin from compiling...
+ * A migration to the new GIMP 2.8 api, which includes:
+ * - Now using gimp_export_dialog_new
+ * - Using gimp_text_layer_get_hint_style (2.8) instead of the depreceated
+ * gimp_text_layer_get_hinting (2.6).
+ *
+ * August 24, 2010 | Barak Itkin <lightningismyname@gmail.com>
+ * More migrations to the new GIMP 2.8 api:
+ * - Now using the GimpItem api
+ * - Using gimp_text_layer_get_markup where possible
+ * - Fixed some compiler warnings
+ * Also merged the header and c file into one file, Updated some of the comments
+ * and documentation, and moved this into the main source repository.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <cairo-pdf.h>
+#include <pango/pangocairo.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-pdf-save"
+#define SAVE2_PROC "file-pdf-save2"
+#define SAVE_MULTI_PROC "file-pdf-save-multi"
+#define SAVE_TRANSPARENT_PROC "file-pdf-save-transparent"
+#define SAVE_MULTI_TRANSPARENT_PROC "file-pdf-save-multi-transparent"
+#define PLUG_IN_BINARY "file-pdf-save"
+#define PLUG_IN_ROLE "gimp-file-pdf-save"
+
+#define DATA_OPTIMIZE "file-pdf-data-optimize"
+#define DATA_IMAGE_LIST "file-pdf-data-multi-page"
+
+/* Gimp will crash before you reach this limitation :D */
+#define MAX_PAGE_COUNT 350
+#define MAX_FILE_NAME_LENGTH 350
+
+#define THUMB_WIDTH 90
+#define THUMB_HEIGHT 120
+
+#define GIMP_PLUGIN_PDF_SAVE_ERROR gimp_plugin_pdf_save_error_quark ()
+
+typedef enum
+{
+ GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED
+} GimpPluginPDFError;
+
+GQuark gimp_plugin_pdf_save_error_quark (void);
+
+typedef enum
+{
+ SA_RUN_MODE,
+ SA_IMAGE,
+ SA_DRAWABLE,
+ SA_FILENAME,
+ SA_RAW_FILENAME,
+ SA_VECTORIZE,
+ SA_IGNORE_HIDDEN,
+ SA_APPLY_MASKS,
+ SA_LAYERS_AS_PAGES,
+ SA_REVERSE_ORDER,
+ SA_TRANSPARENT_BACKGROUND,
+ SA_ARG_COUNT
+} SaveArgs;
+
+typedef enum
+{
+ SMA_RUN_MODE,
+ SMA_COUNT,
+ SMA_IMAGES,
+ SMA_VECTORIZE,
+ SMA_IGNORE_HIDDEN,
+ SMA_APPLY_MASKS,
+ SMA_FILENAME,
+ SMA_RAWFILENAME,
+ SMA_TRANSPARENT_BACKGROUND,
+ SMA_ARG_COUNT
+} SaveMultiArgs;
+
+typedef struct
+{
+ gboolean vectorize;
+ gboolean ignore_hidden;
+ gboolean apply_masks;
+ gboolean layers_as_pages;
+ gboolean reverse_order;
+ gboolean transparent_background;
+} PdfOptimize;
+
+typedef struct
+{
+ gint32 images[MAX_PAGE_COUNT];
+ guint32 image_count;
+ gchar file_name[MAX_FILE_NAME_LENGTH];
+} PdfMultiPage;
+
+typedef struct
+{
+ PdfOptimize optimize;
+ GArray *images;
+} PdfMultiVals;
+
+enum
+{
+ THUMB,
+ PAGE_NUMBER,
+ IMAGE_NAME,
+ IMAGE_ID
+};
+
+typedef struct
+{
+ GdkPixbuf *thumb;
+ gint32 page_number;
+ gchar *image_name;
+} Page;
+
+
+static void query (void);
+
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean init_vals (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gboolean *single,
+ gboolean *defaults,
+ GimpRunMode *run_mode);
+
+static void init_image_list_defaults (gint32 image);
+
+static void validate_image_list (void);
+
+static gboolean gui_single (void);
+
+static gboolean gui_multi (void);
+
+static void reverse_order_toggled (GtkToggleButton *reverse_order,
+ GtkButton *layers_as_pages);
+
+static void choose_file_call (GtkWidget *browse_button,
+ gpointer file_entry);
+
+static gboolean get_image_list (void);
+
+static GtkTreeModel * create_model (void);
+
+static void add_image_call (GtkWidget *widget,
+ gpointer img_combo);
+static void del_image_call (GtkWidget *widget,
+ gpointer icon_view);
+static void remove_call (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data);
+static void recount_pages (void);
+
+static cairo_surface_t * get_cairo_surface (gint32 drawable_ID,
+ gboolean as_mask,
+ GError **error);
+
+static GimpRGB get_layer_color (gint32 layer_ID,
+ gboolean *single);
+
+static void drawText (gint32 text_id,
+ gdouble opacity,
+ cairo_t *cr,
+ gdouble x_res,
+ gdouble y_res);
+
+static gboolean draw_layer (gint32 *layers,
+ gint n_layers,
+ gint j,
+ cairo_t *cr,
+ gdouble x_res,
+ gdouble y_res,
+ const gchar *name,
+ GError **error);
+
+static gboolean dnd_remove = TRUE;
+static PdfMultiPage multi_page;
+
+static PdfOptimize optimize =
+{
+ TRUE, /* vectorize */
+ TRUE, /* ignore_hidden */
+ TRUE, /* apply_masks */
+ FALSE, /* layers_as_pages */
+ FALSE, /* reverse_order */
+ TRUE /* transparent_background */
+};
+
+static GtkTreeModel *model;
+static GtkWidget *file_choose;
+static gchar *file_name;
+
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+G_DEFINE_QUARK (gimp-plugin-pdf-save-error-quark, gimp_plugin_pdf_save_error)
+
+MAIN()
+
+static void
+query (void)
+{
+ static GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" }
+ };
+
+ static GimpParamDef save2_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
+ { GIMP_PDB_INT32, "layers-as-pages", "Layers as pages (bottom layers first). TRUE or FALSE" },
+ { GIMP_PDB_INT32, "reverse-order", "Reverse the pages order (top layers first). TRUE or FALSE" }
+ };
+
+ static GimpParamDef save_transparent_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
+ { GIMP_PDB_INT32, "layers-as-pages", "Layers as pages (bottom layers first). TRUE or FALSE" },
+ { GIMP_PDB_INT32, "fill-background-color", "Fill transparent areas with background color if layer has an alpha channel. TRUE or FALSE" }
+ };
+
+ static GimpParamDef save_multi_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode" },
+ { GIMP_PDB_INT32, "count", "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT" },
+ { GIMP_PDB_INT32ARRAY, "images", "Input image for each page (An image can appear more than once)" },
+ { GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" }
+ };
+
+ static GimpParamDef save_multi_transparent_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode" },
+ { GIMP_PDB_INT32, "count", "The amount of images entered (This will be the amount of pages). 1 <= count <= MAX_PAGE_COUNT" },
+ { GIMP_PDB_INT32ARRAY, "images", "Input image for each page (An image can appear more than once)" },
+ { GIMP_PDB_INT32, "vectorize", "Convert bitmaps to vector graphics where possible. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "ignore-hidden", "Omit hidden layers and layers with zero opacity. TRUE or FALSE" },
+ { GIMP_PDB_INT32, "apply-masks", "Apply layer masks before saving. TRUE or FALSE (Keeping them will not change the output)" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_INT32, "fill-background-color", "Fill transparent areas with background color if layer has an alpha channel. TRUE or FALSE" }
+ };
+
+ gimp_install_procedure (SAVE_PROC,
+ "Save files in PDF format",
+ "Saves files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.",
+ "Barak Itkin",
+ "Copyright Barak Itkin",
+ "August 2009",
+ N_("Portable Document Format"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (SAVE2_PROC,
+ "Save files in PDF format",
+ "Saves files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.\n"
+ "This procedure adds an extra parameter to "
+ "file-pdf-save to save layers as pages.",
+ "Barak Itkin, Lionel N., Jehan",
+ "Copyright Barak Itkin, Lionel N., Jehan",
+ "August 2009, 2017",
+ N_("Portable Document Format"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save2_args), 0,
+ save2_args, NULL);
+
+ gimp_install_procedure (SAVE_TRANSPARENT_PROC,
+ "Save files in PDF format",
+ "Saves files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.\n"
+ "This procedure adds an extra parameter to "
+ "file-pdf-save2 to optionally fill transparent "
+ "areas with the background color",
+ "Barak Itkin, Lionel N., Jehan",
+ "Copyright Barak Itkin, Lionel N., Jehan",
+ "August 2009, 2017",
+ N_("Portable Document Format"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_transparent_args), 0,
+ save_transparent_args, NULL);
+
+ gimp_install_procedure (SAVE_MULTI_PROC,
+ "Save files in PDF format",
+ "Saves files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.",
+ "Barak Itkin",
+ "Copyright Barak Itkin",
+ "August 2009",
+ N_("_Create multipage PDF..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_multi_args), 0,
+ save_multi_args, NULL);
+
+ gimp_install_procedure (SAVE_MULTI_TRANSPARENT_PROC,
+ "Save files in PDF format",
+ "Saves files in Adobe's Portable Document Format. "
+ "PDF is designed to be easily processed by a variety "
+ "of different platforms, and is a distant cousin of "
+ "PostScript.\n"
+ "This procedure adds an extra parameter to "
+ "file-pdf-multi to optionally fill transparent "
+ "areas with the background color",
+ "Barak Itkin",
+ "Copyright Barak Itkin",
+ "August 2009",
+ N_("_Create multipage PDF..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_multi_transparent_args), 0,
+ save_multi_transparent_args, NULL);
+
+
+#if 0
+ gimp_plugin_menu_register (SAVE_MULTI_PROC,
+ "<Image>/File/Create/PDF");
+#endif
+
+ gimp_register_file_handler_mime (SAVE2_PROC, "application/pdf");
+ gimp_register_save_handler (SAVE2_PROC, "pdf", "");
+}
+
+static cairo_status_t
+write_func (void *fp,
+ const unsigned char *data,
+ unsigned int size)
+{
+ return fwrite (data, 1, size, fp) == size ? CAIRO_STATUS_SUCCESS
+ : CAIRO_STATUS_WRITE_ERROR;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gboolean single_image;
+ gboolean defaults_proc;
+ cairo_surface_t *pdf_file;
+ cairo_t *cr;
+ GimpExportCapabilities capabilities;
+ FILE *fp;
+ gint i;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ /* Setting mandatory output values */
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ /* Initializing all the settings */
+ multi_page.image_count = 0;
+
+ if (! init_vals (name, nparams, param, &single_image,
+ &defaults_proc, &run_mode))
+ {
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ return;
+ }
+
+ /* Starting the executions */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (single_image)
+ {
+ if (! gui_single ())
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ }
+ else if (! gui_multi ())
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ if (file_name == NULL)
+ {
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ gimp_message (_("You must select a file to save!"));
+ return;
+ }
+ }
+
+ fp = g_fopen (file_name, "wb");
+ if (fp == NULL)
+ {
+ *nreturn_vals = 2;
+
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ values[1].type = GIMP_PDB_STRING;
+ if (error == NULL)
+ {
+ g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (file_name), g_strerror (errno));
+ }
+ values[1].data.d_string = error->message;
+ return;
+ }
+
+ pdf_file = cairo_pdf_surface_create_for_stream (write_func, fp, 1, 1);
+
+ if (cairo_surface_status (pdf_file) != CAIRO_STATUS_SUCCESS)
+ {
+ g_message (_("An error occurred while creating the PDF file:\n"
+ "%s\n"
+ "Make sure you entered a valid filename and that the "
+ "selected location isn't read only!"),
+ cairo_status_to_string (cairo_surface_status (pdf_file)));
+
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ return;
+ }
+
+ cr = cairo_create (pdf_file);
+
+ capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+ /* This seems counter-intuitive, but not setting the mask capability
+ * will apply any layer mask upon gimp_export_image().
+ */
+ if (! optimize.apply_masks)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS;
+
+ for (i = 0; i < multi_page.image_count; i++)
+ {
+ gint32 image_ID = multi_page.images[i];
+ gint32 *layers;
+ gint32 n_layers;
+ gdouble x_res, y_res;
+ gdouble x_scale, y_scale;
+ gint32 temp;
+ gint j;
+
+ temp = gimp_image_get_active_drawable (image_ID);
+ if (temp < 1)
+ continue;
+
+ /* Save the state of the surface before any changes, so that
+ * settings from one page won't affect all the others
+ */
+ cairo_save (cr);
+
+ if (! (gimp_export_image (&image_ID, &temp, NULL,
+ capabilities) == GIMP_EXPORT_EXPORT))
+ {
+ /* gimp_drawable_histogram() only works within the bounds of
+ * the selection, which is a problem (see issue #2431).
+ * Instead of saving the selection, unselecting to later
+ * reselect, let's just always work on a duplicate of the
+ * image.
+ */
+ image_ID = gimp_image_duplicate (image_ID);
+ }
+ gimp_selection_none (image_ID);
+
+ gimp_image_get_resolution (image_ID, &x_res, &y_res);
+ x_scale = 72.0 / x_res;
+ y_scale = 72.0 / y_res;
+
+ cairo_pdf_surface_set_size (pdf_file,
+ gimp_image_width (image_ID) * x_scale,
+ gimp_image_height (image_ID) * y_scale);
+
+ /* This way we set how many pixels are there in every inch.
+ * It's very important for PangoCairo
+ */
+ cairo_surface_set_fallback_resolution (pdf_file, x_res, y_res);
+
+ /* Cairo has a concept of user-space vs device-space units.
+ * From what I understand, by default the user-space unit is the
+ * typographical "point". Since we work mostly with pixels, not
+ * points, the following call simply scales the transformation
+ * matrix from points to pixels, relatively to the image
+ * resolution, knowing that 1 typographical point == 1/72 inch.
+ */
+ cairo_scale (cr, x_scale, y_scale);
+
+ layers = gimp_image_get_layers (image_ID, &n_layers);
+
+ /* Fill image with background color if transparent and
+ * user chose that option.
+ */
+ if (gimp_drawable_has_alpha (layers[n_layers - 1]) &&
+ optimize.transparent_background)
+ {
+ GimpRGB color;
+
+ cairo_rectangle (cr, 0.0, 0.0,
+ gimp_image_width (image_ID),
+ gimp_image_height (image_ID));
+ gimp_context_get_background (&color);
+ cairo_set_source_rgb (cr,
+ color.r,
+ color.g,
+ color.b);
+ cairo_fill (cr);
+ }
+
+ /* Now, we should loop over the layers of each image */
+ for (j = 0; j < n_layers; j++)
+ {
+ if (! draw_layer (layers, n_layers, j, cr, x_res, y_res, name, &error))
+ {
+ *nreturn_vals = 2;
+
+ /* free the resources */
+ g_free (layers);
+ cairo_surface_destroy (pdf_file);
+ cairo_destroy (cr);
+ fclose (fp);
+
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ return;
+ }
+ }
+ g_free (layers);
+
+ /* We are done with this image - Show it!
+ * Unless that's a multi-page to avoid blank page at the end
+ */
+ if (g_strcmp0 (name, SAVE2_PROC) != 0 ||
+ ! optimize.layers_as_pages)
+ cairo_show_page (cr);
+ cairo_restore (cr);
+
+ gimp_image_delete (image_ID);
+ }
+
+ /* We are done with all the images - time to free the resources */
+ cairo_surface_destroy (pdf_file);
+ cairo_destroy (cr);
+
+ fclose (fp);
+
+ /* Finally done, let's save the parameters */
+ gimp_set_data (DATA_OPTIMIZE, &optimize, sizeof (optimize));
+
+ if (! single_image)
+ {
+ g_strlcpy (multi_page.file_name, file_name, MAX_FILE_NAME_LENGTH);
+ gimp_set_data (DATA_IMAGE_LIST, &multi_page, sizeof (multi_page));
+ }
+}
+
+/******************************************************/
+/* Beginning of parameter handling functions */
+/******************************************************/
+
+/* A function that takes care of loading the basic parameters
+ */
+static gboolean
+init_vals (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gboolean *single_image,
+ gboolean *defaults_proc,
+ GimpRunMode *run_mode)
+{
+ gboolean had_saved_list = FALSE;
+ gboolean single;
+ gboolean defaults = FALSE;
+ gint32 i;
+ gint32 image;
+
+ if ((g_str_equal (name, SAVE_PROC) && nparams == SA_ARG_COUNT - 3) ||
+ (g_str_equal (name, SAVE2_PROC) && nparams == SA_ARG_COUNT - 1) ||
+ (g_str_equal (name, SAVE_TRANSPARENT_PROC) && nparams == SA_ARG_COUNT))
+ {
+ single = TRUE;
+ *run_mode = param[SA_RUN_MODE].data.d_int32;
+ image = param[SA_IMAGE].data.d_int32;
+ file_name = param[SA_FILENAME].data.d_string;
+
+ if (*run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ optimize.apply_masks = param[SA_APPLY_MASKS].data.d_int32;
+ optimize.vectorize = param[SA_VECTORIZE].data.d_int32;
+ optimize.ignore_hidden = param[SA_IGNORE_HIDDEN].data.d_int32;
+ if (nparams >= SA_ARG_COUNT - 1)
+ {
+ optimize.layers_as_pages = param[SA_LAYERS_AS_PAGES].data.d_int32;
+ optimize.reverse_order = param[SA_REVERSE_ORDER].data.d_int32;
+ }
+ if (nparams == SA_ARG_COUNT)
+ optimize.transparent_background = param[SA_TRANSPARENT_BACKGROUND].data.d_int32;
+ }
+ else
+ defaults = TRUE;
+ }
+ else if ((g_str_equal (name, SAVE_MULTI_PROC) && nparams == SMA_ARG_COUNT - 1) ||
+ (g_str_equal (name, SAVE_MULTI_TRANSPARENT_PROC) && nparams == SMA_ARG_COUNT))
+ {
+ single = FALSE;
+
+ *run_mode = param[SMA_RUN_MODE].data.d_int32;
+ image = -1;
+ file_name = param[SMA_FILENAME].data.d_string;
+
+ optimize.apply_masks = param[SMA_APPLY_MASKS].data.d_int32;
+ optimize.vectorize = param[SMA_VECTORIZE].data.d_int32;
+ optimize.ignore_hidden = param[SMA_IGNORE_HIDDEN].data.d_int32;
+
+ if (nparams == SA_ARG_COUNT)
+ optimize.transparent_background = param[SA_TRANSPARENT_BACKGROUND].data.d_int32;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ switch (*run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (single)
+ {
+ init_image_list_defaults (image);
+ }
+ else
+ {
+ multi_page.image_count = param[SMA_COUNT].data.d_int32;
+ if (param[SMA_IMAGES].data.d_int32array != NULL)
+ for (i = 0; i < param[SMA_COUNT].data.d_int32; i++)
+ multi_page.images[i] = param[SMA_IMAGES].data.d_int32array[i];
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (DATA_OPTIMIZE, &optimize);
+ had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
+
+ if (had_saved_list && (file_name == NULL || strlen (file_name) == 0))
+ {
+ file_name = multi_page.file_name;
+ }
+
+ if (single || ! had_saved_list )
+ init_image_list_defaults (image);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ if (! single)
+ {
+ had_saved_list = gimp_get_data (DATA_IMAGE_LIST, &multi_page);
+ if (had_saved_list)
+ {
+ file_name = multi_page.file_name;
+ }
+ }
+ else
+ {
+ init_image_list_defaults (image);
+ }
+ gimp_get_data (DATA_OPTIMIZE, &optimize);
+ break;
+ }
+
+ *defaults_proc = defaults;
+ *single_image = single;
+
+ validate_image_list ();
+
+ return TRUE;
+}
+
+/* A function that initializes the image list to default values */
+static void
+init_image_list_defaults (gint32 image)
+{
+ if (image != -1)
+ {
+ multi_page.images[0] = image;
+ multi_page.image_count = 1;
+ }
+ else
+ {
+ multi_page.image_count = 0;
+ }
+}
+
+/* A function that removes images that are no longer valid from the
+ * image list
+ */
+static void
+validate_image_list (void)
+{
+ gint32 valid = 0;
+ guint32 i = 0;
+
+ for (i = 0 ; i < MAX_PAGE_COUNT && i < multi_page.image_count ; i++)
+ {
+ if (gimp_image_is_valid (multi_page.images[i]))
+ {
+ multi_page.images[valid] = multi_page.images[i];
+ valid++;
+ }
+ }
+
+ multi_page.image_count = valid;
+}
+
+
+/******************************************************/
+/* Beginning of GUI functions */
+/******************************************************/
+
+/* The main GUI function for saving single-paged PDFs */
+
+static gboolean
+gui_single (void)
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *vectorize_c;
+ GtkWidget *ignore_hidden_c;
+ GtkWidget *apply_c;
+ GtkWidget *layers_as_pages_c;
+ GtkWidget *reverse_order_c;
+ GtkWidget *fill_background_c;
+ GtkWidget *frame;
+ gchar *text;
+ gboolean run;
+ gint32 n_layers;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_PROC);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
+ vbox, TRUE, TRUE, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (window), 12);
+
+ fill_background_c = gtk_check_button_new_with_mnemonic (_("_Fill transparent areas with background color"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fill_background_c),
+ optimize.transparent_background);
+ gtk_box_pack_end (GTK_BOX (vbox), fill_background_c, TRUE, TRUE, 0);
+
+ ignore_hidden_c = gtk_check_button_new_with_mnemonic (_("_Omit hidden layers and layers with zero opacity"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
+ optimize.ignore_hidden);
+ gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, TRUE, TRUE, 0);
+
+ vectorize_c = gtk_check_button_new_with_mnemonic (_("Convert _bitmaps to vector graphics where possible"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
+ optimize.vectorize);
+ gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, TRUE, TRUE, 0);
+
+ apply_c = gtk_check_button_new_with_mnemonic (_("_Apply layer masks before saving"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
+ optimize.apply_masks);
+ gtk_box_pack_end (GTK_BOX (vbox), apply_c, TRUE, TRUE, 0);
+ gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
+
+ /* Frame for multi-page from layers. */
+ frame = gtk_frame_new (NULL);
+ gtk_box_pack_end (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+
+ text = g_strdup_printf (_("_Layers as pages (%s)"),
+ optimize.reverse_order ?
+ _("top layers first") : _("bottom layers first"));
+ layers_as_pages_c = gtk_check_button_new_with_mnemonic (text);
+ g_free (text);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
+ optimize.layers_as_pages);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), layers_as_pages_c);
+ g_free (gimp_image_get_layers (multi_page.images[0], &n_layers));
+
+ reverse_order_c = gtk_check_button_new_with_mnemonic (_("_Reverse the pages order"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (reverse_order_c),
+ optimize.reverse_order);
+ gtk_container_add (GTK_CONTAINER (frame), reverse_order_c);
+
+ if (n_layers <= 1)
+ {
+ gtk_widget_set_sensitive (layers_as_pages_c, FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (layers_as_pages_c),
+ FALSE);
+ }
+
+ g_object_bind_property (layers_as_pages_c, "active",
+ reverse_order_c, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_signal_connect (G_OBJECT (reverse_order_c), "toggled",
+ G_CALLBACK (reverse_order_toggled),
+ layers_as_pages_c);
+
+ gtk_widget_show_all (window);
+
+ run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
+
+ optimize.transparent_background =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fill_background_c));
+ optimize.ignore_hidden =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
+ optimize.vectorize =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
+ optimize.apply_masks =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
+ optimize.layers_as_pages =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (layers_as_pages_c));
+ optimize.reverse_order =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (reverse_order_c));
+
+ gtk_widget_destroy (window);
+
+ return run;
+}
+
+/* The main GUI function for saving multi-paged PDFs */
+
+static gboolean
+gui_multi (void)
+{
+ GtkWidget *window;
+ GtkWidget *vbox;
+ GtkWidget *file_label;
+ GtkWidget *file_entry;
+ GtkWidget *file_browse;
+ GtkWidget *file_hbox;
+ GtkWidget *vectorize_c;
+ GtkWidget *ignore_hidden_c;
+ GtkWidget *fill_background_c;
+ GtkWidget *apply_c;
+ GtkWidget *scroll;
+ GtkWidget *page_view;
+ GtkWidget *h_but_box;
+ GtkWidget *del;
+ GtkWidget *h_box;
+ GtkWidget *img_combo;
+ GtkWidget *add_image;
+ gboolean run;
+ const gchar *temp;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ window = gimp_export_dialog_new ("PDF", PLUG_IN_ROLE, SAVE_MULTI_PROC);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (window)),
+ vbox, TRUE, TRUE, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (window), 12);
+
+ file_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+ file_label = gtk_label_new (_("Save to:"));
+ file_entry = gtk_entry_new ();
+ if (file_name != NULL)
+ gtk_entry_set_text (GTK_ENTRY (file_entry), file_name);
+ file_browse = gtk_button_new_with_label (_("Browse..."));
+ file_choose = gtk_file_chooser_dialog_new (_("Multipage PDF export"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("_Save"), GTK_RESPONSE_OK,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (file_hbox), file_label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (file_hbox), file_entry, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (file_hbox), file_browse, FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), file_hbox, TRUE, TRUE, 0);
+
+ page_view = gtk_icon_view_new ();
+ model = create_model ();
+ gtk_icon_view_set_model (GTK_ICON_VIEW (page_view), model);
+ gtk_icon_view_set_reorderable (GTK_ICON_VIEW (page_view), TRUE);
+ gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (page_view),
+ GTK_SELECTION_MULTIPLE);
+
+ gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (page_view), THUMB);
+ gtk_icon_view_set_text_column (GTK_ICON_VIEW (page_view), PAGE_NUMBER);
+ gtk_icon_view_set_tooltip_column (GTK_ICON_VIEW (page_view), IMAGE_NAME);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (scroll, -1, 300);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_container_add (GTK_CONTAINER (scroll), page_view);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
+
+ h_but_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (h_but_box), GTK_BUTTONBOX_START);
+
+ del = gtk_button_new_with_label (_("Remove the selected pages"));
+ gtk_box_pack_start (GTK_BOX (h_but_box), del, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), h_but_box, FALSE, FALSE, 0);
+
+ h_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
+
+ img_combo = gimp_image_combo_box_new (NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (h_box), img_combo, FALSE, FALSE, 0);
+
+ add_image = gtk_button_new_with_label (_("Add this image"));
+ gtk_box_pack_start (GTK_BOX (h_box), add_image, FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), h_box, FALSE, FALSE, 0);
+
+ fill_background_c = gtk_check_button_new_with_mnemonic (_("_Fill transparent areas with background color"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fill_background_c),
+ optimize.transparent_background);
+ gtk_box_pack_end (GTK_BOX (vbox), fill_background_c, TRUE, TRUE, 0);
+
+ ignore_hidden_c = gtk_check_button_new_with_mnemonic (_("_Omit hidden layers and layers with zero opacity"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ignore_hidden_c),
+ optimize.ignore_hidden);
+ gtk_box_pack_end (GTK_BOX (vbox), ignore_hidden_c, FALSE, FALSE, 0);
+
+ vectorize_c = gtk_check_button_new_with_mnemonic (_("Convert _bitmaps to vector graphics where possible"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vectorize_c),
+ optimize.vectorize);
+ gtk_box_pack_end (GTK_BOX (vbox), vectorize_c, FALSE, FALSE, 0);
+
+ apply_c = gtk_check_button_new_with_mnemonic (_("_Apply layer masks before saving"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_c),
+ optimize.apply_masks);
+ gtk_box_pack_end (GTK_BOX (vbox), apply_c, FALSE, FALSE, 0);
+ gimp_help_set_help_data (apply_c, _("Keeping the masks will not change the output"), NULL);
+
+ gtk_widget_show_all (window);
+
+ g_signal_connect (G_OBJECT (file_browse), "clicked",
+ G_CALLBACK (choose_file_call),
+ file_entry);
+
+ g_signal_connect (G_OBJECT (add_image), "clicked",
+ G_CALLBACK (add_image_call),
+ img_combo);
+
+ g_signal_connect (G_OBJECT (del), "clicked",
+ G_CALLBACK (del_image_call),
+ page_view);
+
+ g_signal_connect (G_OBJECT (model), "row-deleted",
+ G_CALLBACK (remove_call),
+ NULL);
+
+ run = gtk_dialog_run (GTK_DIALOG (window)) == GTK_RESPONSE_OK;
+
+ run &= get_image_list ();
+
+ temp = gtk_entry_get_text (GTK_ENTRY (file_entry));
+ g_stpcpy (file_name, temp);
+
+ optimize.transparent_background =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fill_background_c));
+ optimize.ignore_hidden =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ignore_hidden_c));
+ optimize.vectorize =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vectorize_c));
+ optimize.apply_masks =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_c));
+
+ gtk_widget_destroy (window);
+
+ return run;
+}
+
+static void
+reverse_order_toggled (GtkToggleButton *reverse_order,
+ GtkButton *layers_as_pages)
+{
+ gchar *text;
+
+ text = g_strdup_printf (_("Layers as pages (%s)"),
+ gtk_toggle_button_get_active (reverse_order) ?
+ _("top layers first") : _("bottom layers first"));
+ gtk_button_set_label (layers_as_pages, text);
+ g_free (text);
+}
+
+/* A function that is called when the button for browsing for file
+ * locations was clicked
+ */
+static void
+choose_file_call (GtkWidget *browse_button,
+ gpointer file_entry)
+{
+ GFile *file = g_file_new_for_path (gtk_entry_get_text (GTK_ENTRY (file_entry)));
+
+ gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (file_choose),
+ g_file_get_uri (file));
+
+ if (gtk_dialog_run (GTK_DIALOG (file_choose)) == GTK_RESPONSE_OK)
+ {
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (file_choose));
+ gtk_entry_set_text (GTK_ENTRY (file_entry), g_file_get_path (file));
+ }
+
+ file_name = g_file_get_path (file);
+ gtk_widget_hide (file_choose);
+}
+
+/* A function to create the basic GtkTreeModel for the icon view */
+static GtkTreeModel*
+create_model (void)
+{
+ GtkListStore *model;
+ guint32 i;
+
+ /* validate_image_list was called earlier, so all the images
+ * up to multi_page.image_count are valid
+ */
+ model = gtk_list_store_new (4,
+ GDK_TYPE_PIXBUF, /* THUMB */
+ G_TYPE_STRING, /* PAGE_NUMBER */
+ G_TYPE_STRING, /* IMAGE_NAME */
+ G_TYPE_INT); /* IMAGE_ID */
+
+ for (i = 0 ; i < multi_page.image_count && i < MAX_PAGE_COUNT ; i++)
+ {
+ GtkTreeIter iter;
+ gint32 image = multi_page.images[i];
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ THUMB, pixbuf,
+ PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
+ IMAGE_NAME, gimp_image_get_name (image),
+ IMAGE_ID, image,
+ -1);
+
+ g_object_unref (pixbuf);
+ }
+
+ return GTK_TREE_MODEL (model);
+}
+
+/* A function that puts the images from the model inside the images
+ * (pages) array
+ */
+static gboolean
+get_image_list (void)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ multi_page.image_count = 0;
+
+ for (valid = gtk_tree_model_get_iter_first (model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gint32 image;
+
+ gtk_tree_model_get (model, &iter,
+ IMAGE_ID, &image,
+ -1);
+ multi_page.images[multi_page.image_count] = image;
+ multi_page.image_count++;
+ }
+
+ if (multi_page.image_count == 0)
+ {
+ g_message (_("Error! In order to save the file, at least one image "
+ "should be added!"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* A function that is called when the button for adding an image was
+ * clicked
+ */
+static void
+add_image_call (GtkWidget *widget,
+ gpointer img_combo)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gint32 image;
+ GdkPixbuf *pixbuf;
+
+ dnd_remove = FALSE;
+
+ gimp_int_combo_box_get_active (img_combo, &image);
+
+ store = GTK_LIST_STORE (model);
+
+ pixbuf = gimp_image_get_thumbnail (image, THUMB_WIDTH, THUMB_HEIGHT,
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ PAGE_NUMBER, g_strdup_printf (_("Page %d"),
+ multi_page.image_count+1),
+ THUMB, pixbuf,
+ IMAGE_NAME, gimp_image_get_name (image),
+ IMAGE_ID, image,
+ -1);
+
+ g_object_unref (pixbuf);
+
+ multi_page.image_count++;
+
+ dnd_remove = TRUE;
+}
+
+/* A function that is called when the button for deleting the selected
+ * images was clicked
+ */
+static void
+del_image_call (GtkWidget *widget,
+ gpointer icon_view)
+{
+ GList *list;
+ GtkTreeRowReference **items;
+ GtkTreePath *item_path;
+ GtkTreeIter item;
+ gpointer temp;
+ guint32 len;
+
+ dnd_remove = FALSE;
+
+ list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (icon_view));
+
+ len = g_list_length (list);
+ if (len > 0)
+ {
+ gint i;
+
+ items = g_newa (GtkTreeRowReference*, len);
+
+ for (i = 0; i < len; i++)
+ {
+ temp = g_list_nth_data (list, i);
+ items[i] = gtk_tree_row_reference_new (model, temp);
+ gtk_tree_path_free (temp);
+ }
+ g_list_free (list);
+
+ for (i = 0; i < len; i++)
+ {
+ item_path = gtk_tree_row_reference_get_path (items[i]);
+
+ gtk_tree_model_get_iter (model, &item, item_path);
+ gtk_list_store_remove (GTK_LIST_STORE (model), &item);
+
+ gtk_tree_path_free (item_path);
+
+ gtk_tree_row_reference_free (items[i]);
+ multi_page.image_count--;
+ }
+ }
+
+ dnd_remove = TRUE;
+
+ recount_pages ();
+}
+
+/* A function that is called on rows-deleted signal. It will call the
+ * function to relabel the pages
+ */
+static void
+remove_call (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+
+ if (dnd_remove)
+ /* The gtk documentation says that we should not free the indices array */
+ recount_pages ();
+}
+
+/* A function to relabel the pages in the icon view, when their order
+ * was changed
+ */
+static void
+recount_pages (void)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gboolean valid;
+ gint32 i = 0;
+
+ store = GTK_LIST_STORE (model);
+
+ for (valid = gtk_tree_model_get_iter_first (model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (model, &iter))
+ {
+ gtk_list_store_set (store, &iter,
+ PAGE_NUMBER, g_strdup_printf (_("Page %d"), i + 1),
+ -1);
+ i++;
+ }
+}
+
+
+/******************************************************/
+/* Beginning of the actual PDF functions */
+/******************************************************/
+
+static cairo_surface_t *
+get_cairo_surface (gint32 drawable_ID,
+ gboolean as_mask,
+ GError **error)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ cairo_format_t format;
+ gint width;
+ gint height;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (src_buffer);
+ height = gegl_buffer_get_height (src_buffer);
+
+ if (as_mask)
+ format = CAIRO_FORMAT_A8;
+ else if (gimp_drawable_has_alpha (drawable_ID))
+ format = CAIRO_FORMAT_ARGB32;
+ else
+ format = CAIRO_FORMAT_RGB24;
+
+ surface = cairo_image_surface_create (format, width, height);
+
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ switch (status)
+ {
+ case CAIRO_STATUS_INVALID_SIZE:
+ g_set_error_literal (error,
+ GIMP_PLUGIN_PDF_SAVE_ERROR,
+ GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
+ _("Cannot handle the size (either width or height) of the image."));
+ break;
+ default:
+ g_set_error (error,
+ GIMP_PLUGIN_PDF_SAVE_ERROR,
+ GIMP_PLUGIN_PDF_SAVE_ERROR_FAILED,
+ "Cairo error: %s",
+ cairo_status_to_string (status));
+ break;
+ }
+
+ return NULL;
+ }
+
+ dest_buffer = gimp_cairo_surface_create_buffer (surface);
+ if (as_mask)
+ {
+ /* src_buffer represents a mask in "Y u8", "Y u16", etc. formats.
+ * Yet cairo_mask*() functions only care about the alpha channel of
+ * the surface. Hence I change the format of dest_buffer so that the
+ * Y channel of src_buffer actually refers to the A channel of
+ * dest_buffer/surface in Cairo.
+ */
+ gegl_buffer_set_format (dest_buffer, babl_format ("Y u8"));
+ }
+
+ gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, dest_buffer, NULL);
+
+ cairo_surface_mark_dirty (surface);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ return surface;
+}
+
+/* A function to check if a drawable is single colored This allows to
+ * convert bitmaps to vector where possible
+ */
+static GimpRGB
+get_layer_color (gint32 layer_ID,
+ gboolean *single)
+{
+ GimpRGB col;
+ gdouble red, green, blue, alpha;
+ gdouble dev, devSum;
+ gdouble median, pixels, count, precentile;
+
+ devSum = 0;
+ red = 0;
+ green = 0;
+ blue = 0;
+ alpha = 0;
+ dev = 0;
+
+ if (gimp_drawable_is_indexed (layer_ID))
+ {
+ /* FIXME: We can't do a proper histogram on indexed layers! */
+ *single = FALSE;
+ col. r = col.g = col.b = col.a = 0;
+ return col;
+ }
+
+ if (gimp_drawable_bpp (layer_ID) >= 3)
+ {
+ /* Are we in RGB mode? */
+
+ gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_RED, 0.0, 1.0,
+ &red, &dev, &median, &pixels, &count, &precentile);
+ devSum += dev;
+ gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_GREEN, 0.0, 1.0,
+ &green, &dev, &median, &pixels, &count, &precentile);
+ devSum += dev;
+ gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_BLUE, 0.0, 1.0,
+ &blue, &dev, &median, &pixels, &count, &precentile);
+ devSum += dev;
+ }
+ else
+ {
+ /* We are in Grayscale mode (or Indexed) */
+
+ gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_VALUE, 0.0, 1.0,
+ &red, &dev, &median, &pixels, &count, &precentile);
+ devSum += dev;
+ green = red;
+ blue = red;
+ }
+
+ if (gimp_drawable_has_alpha (layer_ID))
+ gimp_drawable_histogram (layer_ID, GIMP_HISTOGRAM_ALPHA, 0.0, 1.0,
+ &alpha, &dev, &median, &pixels, &count, &precentile);
+ else
+ alpha = 255;
+
+ devSum += dev;
+ *single = devSum == 0;
+
+ col.r = red/255;
+ col.g = green/255;
+ col.b = blue/255;
+ col.a = alpha/255;
+
+ return col;
+}
+
+/* A function that uses Pango to render the text to our cairo surface,
+ * in the same way it was the user saw it inside gimp.
+ * Needs some work on choosing the font name better, and on hinting
+ * (freetype and pango differences)
+ */
+static void
+drawText (gint32 text_id,
+ gdouble opacity,
+ cairo_t *cr,
+ gdouble x_res,
+ gdouble y_res)
+{
+ GimpImageType type = gimp_drawable_type (text_id);
+ gchar *text = gimp_text_layer_get_text (text_id);
+ gchar *markup = gimp_text_layer_get_markup (text_id);
+ gchar *font_family;
+ gchar *language;
+ cairo_font_options_t *options;
+ gint x;
+ gint y;
+ GimpRGB color;
+ GimpUnit unit;
+ gdouble size;
+ GimpTextHintStyle hinting;
+ GimpTextJustification j;
+ gboolean justify;
+ PangoAlignment align;
+ GimpTextDirection dir;
+ PangoLayout *layout;
+ PangoContext *context;
+ PangoFontDescription *font_description;
+ gdouble indent;
+ gdouble line_spacing;
+ gdouble letter_spacing;
+ PangoAttribute *letter_spacing_at;
+ PangoAttrList *attr_list = pango_attr_list_new ();
+ PangoFontMap *fontmap;
+
+ cairo_save (cr);
+
+ options = cairo_font_options_create ();
+ attr_list = pango_attr_list_new ();
+ cairo_get_font_options (cr, options);
+
+ /* Position */
+ gimp_drawable_offsets (text_id, &x, &y);
+ cairo_translate (cr, x, y);
+
+ /* Color */
+ /* When dealing with a gray/indexed image, the viewed color of the text layer
+ * can be different than the one kept in the memory */
+ if (type == GIMP_RGBA_IMAGE)
+ gimp_text_layer_get_color (text_id, &color);
+ else
+ gimp_image_pick_color (gimp_item_get_image (text_id),
+ text_id, x, y, FALSE, FALSE, 0, &color);
+
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, opacity);
+
+ /* Hinting */
+ hinting = gimp_text_layer_get_hint_style (text_id);
+ switch (hinting)
+ {
+ case GIMP_TEXT_HINT_STYLE_NONE:
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
+ break;
+
+ case GIMP_TEXT_HINT_STYLE_SLIGHT:
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_SLIGHT);
+ break;
+
+ case GIMP_TEXT_HINT_STYLE_MEDIUM:
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
+ break;
+
+ case GIMP_TEXT_HINT_STYLE_FULL:
+ cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
+ break;
+ }
+
+ /* Antialiasing */
+ if (gimp_text_layer_get_antialias (text_id))
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_DEFAULT);
+ else
+ cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_NONE);
+
+ /* We are done with cairo's settings. It's time to create the
+ * context
+ */
+ fontmap = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
+
+ pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (fontmap), y_res);
+
+ context = pango_font_map_create_context (fontmap);
+ g_object_unref (fontmap);
+
+ pango_cairo_context_set_font_options (context, options);
+
+ /* Language */
+ language = gimp_text_layer_get_language (text_id);
+ if (language)
+ pango_context_set_language (context,
+ pango_language_from_string(language));
+
+ /* Text Direction */
+ dir = gimp_text_layer_get_base_direction (text_id);
+
+ switch (dir)
+ {
+ case GIMP_TEXT_DIRECTION_LTR:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
+ break;
+
+ case GIMP_TEXT_DIRECTION_RTL:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_RTL);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_NATURAL);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_SOUTH);
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_RTL:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_LTR:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
+ break;
+
+ case GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT:
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+ pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
+ pango_context_set_base_gravity (context, PANGO_GRAVITY_WEST);
+ break;
+ }
+
+ /* We are done with the context's settings. It's time to create the
+ * layout
+ */
+ layout = pango_layout_new (context);
+ pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
+
+ /* Font */
+ font_family = gimp_text_layer_get_font (text_id);
+
+ /* We need to find a way to convert GIMP's returned font name to a
+ * normal Pango name... Hopefully GIMP 2.8 with Pango will fix it.
+ */
+ font_description = pango_font_description_from_string (font_family);
+
+ /* Font Size */
+ size = gimp_text_layer_get_font_size (text_id, &unit);
+ size = gimp_units_to_pixels (size, unit, y_res);
+ pango_font_description_set_absolute_size (font_description, size * PANGO_SCALE);
+
+ pango_layout_set_font_description (layout, font_description);
+
+ /* Width */
+ if (! PANGO_GRAVITY_IS_VERTICAL (pango_context_get_base_gravity (context)))
+ pango_layout_set_width (layout, gimp_drawable_width (text_id) * PANGO_SCALE);
+ else
+ pango_layout_set_width (layout, gimp_drawable_height (text_id) * PANGO_SCALE);
+
+ /* Justification, and Alignment */
+ justify = FALSE;
+ j = gimp_text_layer_get_justification (text_id);
+ align = PANGO_ALIGN_LEFT;
+ switch (j)
+ {
+ case GIMP_TEXT_JUSTIFY_LEFT:
+ align = PANGO_ALIGN_LEFT;
+ break;
+ case GIMP_TEXT_JUSTIFY_RIGHT:
+ align = PANGO_ALIGN_RIGHT;
+ break;
+ case GIMP_TEXT_JUSTIFY_CENTER:
+ align = PANGO_ALIGN_CENTER;
+ break;
+ case GIMP_TEXT_JUSTIFY_FILL:
+ align = PANGO_ALIGN_LEFT;
+ justify = TRUE;
+ break;
+ }
+ pango_layout_set_alignment (layout, align);
+ pango_layout_set_justify (layout, justify);
+
+ /* Indentation */
+ indent = gimp_text_layer_get_indent (text_id);
+ pango_layout_set_indent (layout, (int)(PANGO_SCALE * indent));
+
+ /* Line Spacing */
+ line_spacing = gimp_text_layer_get_line_spacing (text_id);
+ pango_layout_set_spacing (layout, (int)(PANGO_SCALE * line_spacing));
+
+ /* Letter Spacing */
+ letter_spacing = gimp_text_layer_get_letter_spacing (text_id);
+ letter_spacing_at = pango_attr_letter_spacing_new ((int)(PANGO_SCALE * letter_spacing));
+ pango_attr_list_insert (attr_list, letter_spacing_at);
+
+
+ pango_layout_set_attributes (layout, attr_list);
+
+ /* Use the pango markup of the text layer */
+
+ if (markup != NULL && markup[0] != '\0')
+ pango_layout_set_markup (layout, markup, -1);
+ else /* If we can't find a markup, then it has just text */
+ pango_layout_set_text (layout, text, -1);
+
+ if (dir == GIMP_TEXT_DIRECTION_TTB_RTL ||
+ dir == GIMP_TEXT_DIRECTION_TTB_RTL_UPRIGHT)
+ {
+ cairo_translate (cr, gimp_drawable_width (text_id), 0);
+ cairo_rotate (cr, G_PI_2);
+ }
+
+ if (dir == GIMP_TEXT_DIRECTION_TTB_LTR ||
+ dir == GIMP_TEXT_DIRECTION_TTB_LTR_UPRIGHT)
+ {
+ cairo_translate (cr, 0, gimp_drawable_height (text_id));
+ cairo_rotate (cr, -G_PI_2);
+ }
+
+ pango_cairo_show_layout (cr, layout);
+
+ g_free (text);
+ g_free (font_family);
+ g_free (language);
+
+ g_object_unref (layout);
+ pango_font_description_free (font_description);
+ g_object_unref (context);
+ pango_attr_list_unref (attr_list);
+
+ cairo_font_options_destroy (options);
+
+ cairo_restore (cr);
+}
+
+static gboolean
+draw_layer (gint32 *layers,
+ gint n_layers,
+ gint j,
+ cairo_t *cr,
+ gdouble x_res,
+ gdouble y_res,
+ const gchar *name,
+ GError **error)
+{
+ gint32 layer_ID;
+ gdouble opacity;
+
+ if (optimize.reverse_order && optimize.layers_as_pages)
+ layer_ID = layers [j];
+ else
+ layer_ID = layers [n_layers - j - 1];
+
+ opacity = gimp_layer_get_opacity (layer_ID) / 100.0;
+ if ((! gimp_item_get_visible (layer_ID) || opacity == 0.0) &&
+ optimize.ignore_hidden)
+ return TRUE;
+
+ if (gimp_item_is_group (layer_ID))
+ {
+ gint *children;
+ gint children_num;
+ gint i;
+
+ children = gimp_item_get_children (layer_ID, &children_num);
+ for (i = 0; i < children_num; i++)
+ {
+ if (! draw_layer (children, children_num, i,
+ cr, x_res, y_res, name, error))
+ {
+ g_free (children);
+ return FALSE;
+ }
+ }
+ g_free (children);
+ }
+ else
+ {
+ cairo_surface_t *mask_image = NULL;
+ gint32 mask_ID = -1;
+ gint x, y;
+
+ mask_ID = gimp_layer_get_mask (layer_ID);
+ if (mask_ID != -1)
+ {
+ mask_image = get_cairo_surface (mask_ID, TRUE, error);
+
+ if (*error)
+ return FALSE;
+ }
+
+ gimp_drawable_offsets (layer_ID, &x, &y);
+
+ if (! gimp_item_is_text_layer (layer_ID))
+ {
+ /* For raster layers */
+ GimpRGB layer_color;
+ gboolean single_color = FALSE;
+
+ layer_color = get_layer_color (layer_ID, &single_color);
+
+ cairo_rectangle (cr, x, y,
+ gimp_drawable_width (layer_ID),
+ gimp_drawable_height (layer_ID));
+
+ if (optimize.vectorize && single_color)
+ {
+ cairo_set_source_rgba (cr,
+ layer_color.r,
+ layer_color.g,
+ layer_color.b,
+ layer_color.a * opacity);
+ if (mask_ID != -1)
+ cairo_mask_surface (cr, mask_image, x, y);
+ else
+ cairo_fill (cr);
+ }
+ else
+ {
+ cairo_surface_t *layer_image;
+
+ layer_image = get_cairo_surface (layer_ID, FALSE, error);
+
+ if (*error)
+ return FALSE;
+
+ cairo_clip (cr);
+
+ cairo_set_source_surface (cr, layer_image, x, y);
+ cairo_push_group (cr);
+ cairo_paint_with_alpha (cr, opacity);
+ cairo_pop_group_to_source (cr);
+
+ if (mask_ID != -1)
+ cairo_mask_surface (cr, mask_image, x, y);
+ else
+ cairo_paint (cr);
+
+ cairo_reset_clip (cr);
+
+ cairo_surface_destroy (layer_image);
+ }
+ }
+ else
+ {
+ /* For text layers */
+ drawText (layer_ID, opacity, cr, x_res, y_res);
+ }
+
+ /* draw new page if "layers as pages" option is checked */
+ if (optimize.layers_as_pages &&
+ g_strcmp0 (name, SAVE2_PROC) == 0)
+ cairo_show_page (cr);
+
+ /* We are done with the layer - time to free some resources */
+ if (mask_ID != -1)
+ cairo_surface_destroy (mask_image);
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/common/file-pix.c b/plug-ins/common/file-pix.c
new file mode 100644
index 0000000..751a5a7
--- /dev/null
+++ b/plug-ins/common/file-pix.c
@@ -0,0 +1,736 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Alias|Wavefront pix/matte image reading and writing code
+ * Copyright (C) 1997 Mike Taylor
+ * (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* This plug-in was written using the online documentation from
+ * Alias|Wavefront Inc's PowerAnimator product.
+ *
+ * Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
+ */
+
+/* Event history:
+ * V 1.0, MT, 02-Jul-97: initial version of plug-in
+ * V 1.1, MT, 04-Dec-97: added .als file extension
+ */
+
+/* Features
+ * - loads and exports
+ * - 24-bit (.pix)
+ * - 8-bit (.matte, .alpha, or .mask) images
+ *
+ * NOTE: pix and matte files do not support alpha channels or indexed
+ * color, so neither does this plug-in
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-pix-load"
+#define SAVE_PROC "file-pix-save"
+#define PLUG_IN_BINARY "file-pix"
+#define PLUG_IN_ROLE "gimp-file-pix"
+
+
+/* #define PIX_DEBUG */
+
+#ifdef PIX_DEBUG
+# define PIX_DEBUG_PRINT(a,b) g_printerr (a,b)
+#else
+# define PIX_DEBUG_PRINT(a,b)
+#endif
+
+
+/**************
+ * Prototypes *
+ **************/
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (GFile *file,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean get_short (GInputStream *input,
+ guint16 *value,
+ GError **error);
+static gboolean put_short (GOutputStream *output,
+ guint16 value,
+ GError **error);
+
+
+/******************
+ * Implementation *
+ ******************/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ /*
+ * Description:
+ * Register the services provided by this plug-in
+ */
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads files of the Alias|Wavefront Pix file format",
+ "loads files of the Alias|Wavefront Pix file format",
+ "Michael Taylor",
+ "Michael Taylor",
+ "1997",
+ N_("Alias Pix image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_uri (LOAD_PROC);
+ gimp_register_load_handler (LOAD_PROC, "pix,matte,mask,alpha,als", "");
+
+ gimp_install_procedure (SAVE_PROC,
+ "export file in the Alias|Wavefront pix/matte file format",
+ "export file in the Alias|Wavefront pix/matte file format",
+ "Michael Taylor",
+ "Michael Taylor",
+ "1997",
+ N_("Alias Pix image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "pix,matte,mask,alpha,als", "");
+}
+
+/*
+ * Description:
+ * perform registered plug-in function
+ *
+ * Arguments:
+ * name - name of the function to perform
+ * nparams - number of parameters passed to the function
+ * param - parameters passed to the function
+ * nreturn_vals - number of parameters returned by the function
+ * return_vals - parameters returned by the function
+ */
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ /* Perform the image load */
+ image_ID = load_image (g_file_new_for_uri (param[1].data.d_string),
+ &error);
+
+ if (image_ID != -1)
+ {
+ /* The image load was successful */
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ /* The image load failed */
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "PIX",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/*
+ * Description:
+ * Reads a 16-bit integer from a file in such a way that the machine's
+ * byte order should not matter.
+ */
+
+static gboolean
+get_short (GInputStream *input,
+ guint16 *value,
+ GError **error)
+{
+ guchar buf[2];
+ gsize bytes_read;
+
+ if (! g_input_stream_read_all (input, buf, 2,
+ &bytes_read, NULL, error) ||
+ bytes_read != 2)
+ {
+ return FALSE;
+ }
+
+ if (value)
+ *value = (buf[0] << 8) + buf[1];
+
+ return TRUE;
+}
+
+/*
+ * Description:
+ * Writes a 16-bit integer to a file in such a way that the machine's
+ * byte order should not matter.
+ */
+
+static gboolean
+put_short (GOutputStream *output,
+ guint16 value,
+ GError **error)
+{
+ guchar buf[2];
+
+ buf[0] = (value >> 8) & 0xFF;
+ buf[1] = value & 0xFF;
+
+ return g_output_stream_write_all (output, buf, 2, NULL, NULL, error);
+}
+
+/*
+ * Description:
+ * load the given image into gimp
+ *
+ * Arguments:
+ * filename - name on the file to read
+ *
+ * Return Value:
+ * Image id for the loaded image
+ *
+ */
+
+static gint32
+load_image (GFile *file,
+ GError **error)
+{
+ GInputStream *input;
+ GeglBuffer *buffer;
+ GimpImageBaseType imgtype;
+ GimpImageType gdtype;
+ guchar *dest;
+ guchar *dest_base;
+ gint32 image_ID;
+ gint32 layer_ID;
+ gushort width, height, depth;
+ gint i, j, tile_height, row;
+
+ PIX_DEBUG_PRINT ("Opening file: %s\n", filename);
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ g_file_get_parse_name (file));
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! input)
+ return -1;
+
+ /* Read header information */
+ if (! get_short (input, &width, error) ||
+ ! get_short (input, &height, error) ||
+ ! get_short (input, NULL, error) || /* Discard obsolete field */
+ ! get_short (input, NULL, error) || /* Discard obsolete field */
+ ! get_short (input, &depth, error))
+ {
+ g_object_unref (input);
+ return -1;
+ }
+
+ PIX_DEBUG_PRINT ("Width %hu\n", width);
+ PIX_DEBUG_PRINT ("Height %hu\n", height);
+
+ if (depth == 8)
+ {
+ /* Loading a matte file */
+ imgtype = GIMP_GRAY;
+ gdtype = GIMP_GRAY_IMAGE;
+ }
+ else if (depth == 24)
+ {
+ /* Loading an RGB file */
+ imgtype = GIMP_RGB;
+ gdtype = GIMP_RGB_IMAGE;
+ }
+ else
+ {
+ /* Header is invalid */
+ g_object_unref (input);
+ return -1;
+ }
+
+ image_ID = gimp_image_new (width, height, imgtype);
+ gimp_image_set_filename (image_ID, g_file_get_uri (file));
+
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ tile_height = gimp_tile_height ();
+
+ if (depth == 24)
+ {
+ /* Read a 24-bit Pix image */
+
+ dest_base = dest = g_new (guchar, 3 * width * tile_height);
+
+ for (i = 0; i < height;)
+ {
+ for (dest = dest_base, row = 0;
+ row < tile_height && i < height;
+ i++, row++)
+ {
+ guchar record[4];
+ gsize bytes_read;
+ guchar count;
+
+ /* Read a row of the image */
+ j = 0;
+ while (j < width)
+ {
+ if (! g_input_stream_read_all (input, record, 4,
+ &bytes_read, NULL, error) ||
+ bytes_read != 4)
+ break;
+
+ for (count = 0; count < record[0]; ++count)
+ {
+ dest[0] = record[3];
+ dest[1] = record[2];
+ dest[2] = record[1];
+ dest += 3;
+ j++;
+ if (j >= width)
+ break;
+ }
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
+ NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) i / (double) height);
+ }
+
+ g_free (dest_base);
+ }
+ else
+ {
+ /* Read an 8-bit Matte image */
+
+ dest_base = dest = g_new (guchar, width * tile_height);
+
+ for (i = 0; i < height;)
+ {
+ for (dest = dest_base, row = 0;
+ row < tile_height && i < height;
+ i++, row++)
+ {
+ guchar record[2];
+ gsize bytes_read;
+ guchar count;
+
+ /* Read a row of the image */
+ j = 0;
+ while (j < width)
+ {
+ if (! g_input_stream_read_all (input, record, 2,
+ &bytes_read, NULL, error) ||
+ bytes_read != 2)
+ break;
+
+ for (count = 0; count < record[0]; ++count)
+ {
+ dest[j] = record[1];
+ j++;
+ if (j >= width)
+ break;
+ }
+ }
+
+ dest += width;
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
+ NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) i / (double) height);
+ }
+
+ g_free (dest_base);
+ }
+
+ g_object_unref (buffer);
+ g_object_unref (input);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+/*
+ * Description:
+ * save the given file out as an alias pix or matte file
+ *
+ * Arguments:
+ * filename - name of file to save to
+ * image_ID - ID of image to save
+ * drawable_ID - current drawable
+ */
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GCancellable *cancellable;
+ gint width;
+ gint height;
+ gint depth, i, j, row, tile_height, rectHeight;
+ gboolean savingColor = TRUE;
+ guchar *src;
+ guchar *src_base;
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ g_file_get_parse_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! output)
+ return FALSE;
+
+ /* Get info about image */
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ savingColor = ! gimp_drawable_is_gray (drawable_ID);
+
+ if (savingColor)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("Y' u8");
+
+ depth = babl_format_get_bytes_per_pixel (format);
+
+ /* Write the image header */
+ PIX_DEBUG_PRINT ("Width %hu\n", width);
+ PIX_DEBUG_PRINT ("Height %hu\n", height);
+
+ if (! put_short (output, width, error) ||
+ ! put_short (output, height, error) ||
+ ! put_short (output, 0, error) ||
+ ! put_short (output, 0, error))
+ {
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_object_unref (output);
+ g_object_unref (buffer);
+ return FALSE;
+ }
+
+ tile_height = gimp_tile_height ();
+ src_base = g_new (guchar, tile_height * width * depth);
+
+ if (savingColor)
+ {
+ /* Writing a 24-bit Pix image */
+
+ if (! put_short (output, 24, error))
+ goto fail;
+
+ for (i = 0; i < height;)
+ {
+ rectHeight = (tile_height < (height - i)) ?
+ tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
+ format, src_base,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (src = src_base, row = 0;
+ row < tile_height && i < height;
+ i += 1, row += 1)
+ {
+ /* Write a row of the image */
+
+ guchar record[4];
+
+ record[0] = 1;
+ record[3] = src[0];
+ record[2] = src[1];
+ record[1] = src[2];
+ src += depth;
+ for (j = 1; j < width; ++j)
+ {
+ if ((record[3] != src[0]) ||
+ (record[2] != src[1]) ||
+ (record[1] != src[2]) ||
+ (record[0] == 255))
+ {
+ /* Write current RLE record and start a new one */
+
+ if (! g_output_stream_write_all (output, record, 4,
+ NULL, NULL, error))
+ {
+ goto fail;
+ }
+
+ record[0] = 1;
+ record[3] = src[0];
+ record[2] = src[1];
+ record[1] = src[2];
+ }
+ else
+ {
+ /* increment run length in current record */
+ record[0]++;
+ }
+ src += depth;
+ }
+
+ /* Write last record in row */
+
+ if (! g_output_stream_write_all (output, record, 4,
+ NULL, NULL, error))
+ {
+ goto fail;
+ }
+ }
+
+ gimp_progress_update ((double) i / (double) height);
+ }
+ }
+ else
+ {
+ /* Writing a 8-bit Matte (Mask) image */
+
+ if (! put_short (output, 8, error))
+ goto fail;
+
+ for (i = 0; i < height;)
+ {
+ rectHeight = (tile_height < (height - i)) ?
+ tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
+ format, src_base,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (src = src_base, row = 0;
+ row < tile_height && i < height;
+ i += 1, row += 1)
+ {
+ /* Write a row of the image */
+
+ guchar record[2];
+
+ record[0] = 1;
+ record[1] = src[0];
+ src += depth;
+ for (j = 1; j < width; ++j)
+ {
+ if ((record[1] != src[0]) || (record[0] == 255))
+ {
+ /* Write current RLE record and start a new one */
+
+ if (! g_output_stream_write_all (output, record, 2,
+ NULL, NULL, error))
+ {
+ goto fail;
+ }
+
+ record[0] = 1;
+ record[1] = src[0];
+ }
+ else
+ {
+ /* increment run length in current record */
+ record[0] ++;
+ }
+ src += depth;
+ }
+
+ /* Write last record in row */
+
+ if (! g_output_stream_write_all (output, record, 2,
+ NULL, NULL, error))
+ {
+ goto fail;
+ }
+ }
+
+ gimp_progress_update ((double) i / (double) height);
+ }
+ }
+
+ g_free (src_base);
+ g_object_unref (output);
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_free (src_base);
+ g_object_unref (output);
+ g_object_unref (buffer);
+
+ return FALSE;
+}
diff --git a/plug-ins/common/file-png.c b/plug-ins/common/file-png.c
new file mode 100644
index 0000000..063f201
--- /dev/null
+++ b/plug-ins/common/file-png.c
@@ -0,0 +1,2654 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Portable Network Graphics (PNG) plug-in
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com) and
+ * Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz).
+ * and 1999-2000 Nick Lamb (njl195@zepler.org.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Contents:
+ *
+ * main() - Main entry - just call gimp_main()...
+ * query() - Respond to a plug-in query...
+ * run() - Run the plug-in...
+ * load_image() - Load a PNG image into a new image window.
+ * offsets_dialog() - Asks the user about offsets when loading.
+ * respin_cmap() - Re-order a Gimp colormap for PNG tRNS
+ * save_image() - Export the specified image to a PNG file.
+ * save_compression_callback() - Update the image compression level.
+ * save_interlace_update() - Update the interlacing option.
+ * save_dialog() - Pop up the export dialog.
+ *
+ * Revision History:
+ *
+ * see ChangeLog
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <png.h> /* PNG library definitions */
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*
+ * Constants...
+ */
+
+#define LOAD_PROC "file-png-load"
+#define SAVE_PROC "file-png-save"
+#define SAVE2_PROC "file-png-save2"
+#define SAVE_DEFAULTS_PROC "file-png-save-defaults"
+#define GET_DEFAULTS_PROC "file-png-get-defaults"
+#define SET_DEFAULTS_PROC "file-png-set-defaults"
+#define PLUG_IN_BINARY "file-png"
+#define PLUG_IN_ROLE "gimp-file-png"
+
+#define PLUG_IN_VERSION "1.3.4 - 03 September 2002"
+#define SCALE_WIDTH 125
+
+#define DEFAULT_GAMMA 2.20
+
+#define PNG_DEFAULTS_PARASITE "png-save-defaults"
+
+/*
+ * Structures...
+ */
+
+typedef enum _PngExportformat {
+ PNG_FORMAT_AUTO = 0,
+ PNG_FORMAT_RGB8,
+ PNG_FORMAT_GRAY8,
+ PNG_FORMAT_RGBA8,
+ PNG_FORMAT_GRAYA8,
+ PNG_FORMAT_RGB16,
+ PNG_FORMAT_GRAY16,
+ PNG_FORMAT_RGBA16,
+ PNG_FORMAT_GRAYA16
+} PngExportFormat;
+
+typedef struct
+{
+ gboolean interlaced;
+ gboolean bkgd;
+ gboolean gama;
+ gboolean offs;
+ gboolean phys;
+ gboolean time;
+ gboolean comment;
+ gboolean save_transp_pixels;
+ gint compression_level;
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_thumbnail;
+ gboolean save_profile;
+ PngExportFormat export_format;
+}
+PngSaveVals;
+
+typedef struct
+{
+ gboolean run;
+
+ GtkWidget *interlaced;
+ GtkWidget *bkgd;
+ GtkWidget *gama;
+ GtkWidget *offs;
+ GtkWidget *phys;
+ GtkWidget *time;
+ GtkWidget *comment;
+ GtkWidget *pixelformat;
+ GtkWidget *save_transp_pixels;
+ GtkAdjustment *compression_level;
+ GtkWidget *save_exif;
+ GtkWidget *save_xmp;
+ GtkWidget *save_iptc;
+ GtkWidget *save_thumbnail;
+ GtkWidget *save_profile;
+}
+PngSaveGui;
+
+/* These are not saved or restored. */
+typedef struct
+{
+ gboolean has_trns;
+ png_bytep trans;
+ int num_trans;
+ gboolean has_plte;
+ png_colorp palette;
+ int num_palette;
+}
+PngGlobals;
+
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ gboolean interactive,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+static gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error);
+
+static int respin_cmap (png_structp pp,
+ png_infop info,
+ guchar *remap,
+ gint32 image_ID,
+ gint32 drawable_ID);
+
+static gboolean save_dialog (gint32 image_ID,
+ gboolean alpha);
+
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+
+static gboolean offsets_dialog (gint offset_x,
+ gint offset_y);
+
+static gboolean ia_has_transparent_pixels (GeglBuffer *buffer);
+
+static gint find_unused_ia_color (GeglBuffer *buffer,
+ gint *colors);
+
+static void load_parasite (void);
+static void save_parasite (void);
+static void load_gui_defaults (PngSaveGui *pg);
+
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+static const PngSaveVals defaults =
+{
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ 9,
+ FALSE, /* save exif */
+ FALSE, /* save xmp */
+ FALSE, /* save iptc */
+ TRUE, /* save thumbnail */
+ PNG_FORMAT_AUTO
+};
+
+static PngSaveVals pngvals;
+static PngGlobals pngg;
+
+
+/*
+ * 'main()' - Main entry - just call gimp_main()...
+ */
+
+MAIN ()
+
+
+/*
+ * 'query()' - Respond to a plug-in query...
+ */
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+#define COMMON_SAVE_ARGS \
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, \
+ { GIMP_PDB_IMAGE, "image", "Input image" }, \
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" }, \
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in"}, \
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in"}
+
+#define OLD_CONFIG_ARGS \
+ { GIMP_PDB_INT32, "interlace", "Use Adam7 interlacing?" }, \
+ { GIMP_PDB_INT32, "compression", "Deflate Compression factor (0--9)" }, \
+ { GIMP_PDB_INT32, "bkgd", "Write bKGD chunk?" }, \
+ { GIMP_PDB_INT32, "gama", "Write gAMA chunk?" }, \
+ { GIMP_PDB_INT32, "offs", "Write oFFs chunk?" }, \
+ { GIMP_PDB_INT32, "phys", "Write pHYs chunk?" }, \
+ { GIMP_PDB_INT32, "time", "Write tIME chunk?" }
+
+#define FULL_CONFIG_ARGS \
+ OLD_CONFIG_ARGS, \
+ { GIMP_PDB_INT32, "comment", "Write comment?" }, \
+ { GIMP_PDB_INT32, "svtrans", "Preserve color of transparent pixels?" }
+
+ static const GimpParamDef save_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ OLD_CONFIG_ARGS
+ };
+
+ static const GimpParamDef save_args2[] =
+ {
+ COMMON_SAVE_ARGS,
+ FULL_CONFIG_ARGS
+ };
+
+ static const GimpParamDef save_args_defaults[] =
+ {
+ COMMON_SAVE_ARGS
+ };
+
+ static const GimpParamDef save_get_defaults_return_vals[] =
+ {
+ FULL_CONFIG_ARGS
+ };
+
+ static const GimpParamDef save_args_set_defaults[] =
+ {
+ FULL_CONFIG_ARGS
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in PNG file format",
+ "This plug-in loads Portable Network Graphics "
+ "(PNG) files.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ N_("PNG image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/png");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "png", "", "0,string,\211PNG\r\n\032\n");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in PNG file format",
+ "This plug-in exports Portable Network Graphics "
+ "(PNG) files.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ N_("PNG image"),
+ "RGB*,GRAY*,INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (SAVE2_PROC,
+ "Exports files in PNG file format",
+ "This plug-in exports Portable Network Graphics "
+ "(PNG) files. "
+ "This procedure adds 2 extra parameters to "
+ "file-png-save that control whether "
+ "image comments are saved and whether transparent "
+ "pixels are saved or nullified.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ N_("PNG image"),
+ "RGB*,GRAY*,INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args2), 0,
+ save_args2, NULL);
+
+ gimp_install_procedure (SAVE_DEFAULTS_PROC,
+ "Exports files in PNG file format",
+ "This plug-in exports Portable Network Graphics (PNG) "
+ "files, using the default settings stored as "
+ "a parasite.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ N_("PNG image"),
+ "RGB*,GRAY*,INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args_defaults), 0,
+ save_args_defaults, NULL);
+
+ gimp_register_file_handler_mime (SAVE_DEFAULTS_PROC, "image/png");
+ gimp_register_save_handler (SAVE_DEFAULTS_PROC, "png", "");
+
+ gimp_install_procedure (GET_DEFAULTS_PROC,
+ "Get the current set of defaults used by the "
+ "PNG file export plug-in",
+ "This procedure returns the current set of "
+ "defaults stored as a parasite for the PNG "
+ "export plug-in. "
+ "These defaults are used to seed the UI, by the "
+ "file_png_save_defaults procedure, and by "
+ "gimp_file_save when it detects to use PNG.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ 0, G_N_ELEMENTS (save_get_defaults_return_vals),
+ NULL, save_get_defaults_return_vals);
+
+ gimp_install_procedure (SET_DEFAULTS_PROC,
+ "Set the current set of defaults used by the "
+ "PNG file export plug-in",
+ "This procedure set the current set of defaults "
+ "stored as a parasite for the PNG export plug-in. "
+ "These defaults are used to seed the UI, by the "
+ "file_png_save_defaults procedure, and by "
+ "gimp_file_save when it detects to use PNG.",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
+ "Michael Sweet <mike@easysw.com>, "
+ "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
+ "Nick Lamb <njl195@zepler.org.uk>",
+ PLUG_IN_VERSION,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args_set_defaults), 0,
+ save_args_set_defaults, NULL);
+}
+
+
+/*
+ * 'run()' - Run the plug-in...
+ */
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[10];
+ GimpRunMode run_mode = GIMP_RUN_NONINTERACTIVE;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GError *error = NULL;
+
+ if (nparams)
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gboolean interactive;
+ gboolean resolution_loaded = FALSE;
+ gboolean profile_loaded = FALSE;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ interactive = TRUE;
+ break;
+ default:
+ interactive = FALSE;
+ break;
+ }
+
+ image_ID = load_image (param[1].data.d_string,
+ interactive,
+ &resolution_loaded,
+ &profile_loaded,
+ &error);
+
+ if (image_ID != -1)
+ {
+ GFile *file = g_file_new_for_path (param[1].data.d_string);
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/png",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ if (profile_loaded)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image_ID, "image/png",
+ metadata, flags,
+ interactive);
+
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0 ||
+ strcmp (name, SAVE2_PROC) == 0 ||
+ strcmp (name, SAVE_DEFAULTS_PROC) == 0)
+ {
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ gboolean alpha;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "PNG",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ *nreturn_vals = 1;
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Initialize with hardcoded defaults */
+ pngvals = defaults;
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (orig_image_ID,
+ "image/png",
+ &metadata_flags);
+ pngvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ pngvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ pngvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ pngvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+ pngvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+
+ /* Override preferences from PNG export defaults (if saved). */
+ load_parasite ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Finally possibly retrieve data from previous run. */
+ gimp_get_data (SAVE_PROC, &pngvals);
+
+ alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ /* If the image has no transparency, then there is usually
+ * no need to save a bKGD chunk. For more information, see:
+ * http://bugzilla.gnome.org/show_bug.cgi?id=92395
+ */
+ if (! alpha)
+ pngvals.bkgd = FALSE;
+
+ /* Then acquire information with a dialog...
+ */
+ if (! save_dialog (orig_image_ID, alpha))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are there!
+ */
+ if (nparams != 5)
+ {
+ if (nparams != 12 && nparams != 14)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ pngvals.interlaced = param[5].data.d_int32;
+ pngvals.compression_level = param[6].data.d_int32;
+ pngvals.bkgd = param[7].data.d_int32;
+ pngvals.gama = param[8].data.d_int32;
+ pngvals.offs = param[9].data.d_int32;
+ pngvals.phys = param[10].data.d_int32;
+ pngvals.time = param[11].data.d_int32;
+
+ if (nparams == 14)
+ {
+ pngvals.comment = param[12].data.d_int32;
+ pngvals.save_transp_pixels = param[13].data.d_int32;
+ }
+ else
+ {
+ pngvals.comment = TRUE;
+ pngvals.save_transp_pixels = TRUE;
+ }
+
+ if (pngvals.compression_level < 0 ||
+ pngvals.compression_level > 9)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &pngvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string,
+ image_ID, drawable_ID, orig_image_ID, &error))
+ {
+ if (metadata)
+ {
+ GFile *file;
+
+ gimp_metadata_set_bits_per_sample (metadata, 8);
+
+ if (pngvals.save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (pngvals.save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (pngvals.save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ if (pngvals.save_thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (pngvals.save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ file = g_file_new_for_path (param[3].data.d_string);
+ gimp_image_metadata_save_finish (orig_image_ID,
+ "image/png",
+ metadata, metadata_flags,
+ file, NULL);
+ g_object_unref (file);
+ }
+
+ gimp_set_data (SAVE_PROC, &pngvals, sizeof (pngvals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+ else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
+ {
+ pngvals = defaults;
+ load_parasite ();
+
+ *nreturn_vals = 10;
+
+#define SET_VALUE(index, field) G_STMT_START { \
+ values[(index)].type = GIMP_PDB_INT32; \
+ values[(index)].data.d_int32 = pngvals.field; \
+} G_STMT_END
+
+ SET_VALUE (1, interlaced);
+ SET_VALUE (2, compression_level);
+ SET_VALUE (3, bkgd);
+ SET_VALUE (4, gama);
+ SET_VALUE (5, offs);
+ SET_VALUE (6, phys);
+ SET_VALUE (7, time);
+ SET_VALUE (8, comment);
+ SET_VALUE (9, save_transp_pixels);
+
+#undef SET_VALUE
+ }
+ else if (strcmp (name, SET_DEFAULTS_PROC) == 0)
+ {
+ if (nparams == 9)
+ {
+ pngvals = defaults;
+ load_parasite ();
+
+ pngvals.interlaced = param[0].data.d_int32;
+ pngvals.compression_level = param[1].data.d_int32;
+ pngvals.bkgd = param[2].data.d_int32;
+ pngvals.gama = param[3].data.d_int32;
+ pngvals.offs = param[4].data.d_int32;
+ pngvals.phys = param[5].data.d_int32;
+ pngvals.time = param[6].data.d_int32;
+ pngvals.comment = param[7].data.d_int32;
+ pngvals.save_transp_pixels = param[8].data.d_int32;
+
+ save_parasite ();
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+struct read_error_data
+{
+ guchar *pixel; /* Pixel data */
+ GeglBuffer *buffer; /* GEGL buffer for layer */
+ const Babl *file_format;
+ guint32 width; /* png_infop->width */
+ guint32 height; /* png_infop->height */
+ gint bpp; /* Bytes per pixel */
+ gint tile_height; /* Height of tile in GIMP */
+ gint begin; /* Beginning tile row */
+ gint end; /* Ending tile row */
+ gint num; /* Number of rows to load */
+};
+
+static void
+on_read_error (png_structp png_ptr,
+ png_const_charp error_msg)
+{
+ struct read_error_data *error_data = png_get_error_ptr (png_ptr);
+ gint begin;
+ gint end;
+ gint num;
+
+ g_printerr (_("Error loading PNG file: %s\n"), error_msg);
+
+ /* Flush the current half-read row of tiles */
+
+ gegl_buffer_set (error_data->buffer,
+ GEGL_RECTANGLE (0, error_data->begin,
+ error_data->width,
+ error_data->num),
+ 0,
+ error_data->file_format,
+ error_data->pixel,
+ GEGL_AUTO_ROWSTRIDE);
+
+ begin = error_data->begin + error_data->tile_height;
+
+ if (begin < error_data->height)
+ {
+ end = MIN (error_data->end + error_data->tile_height, error_data->height);
+ num = end - begin;
+
+ gegl_buffer_clear (error_data->buffer,
+ GEGL_RECTANGLE (0, begin, error_data->width, num));
+ }
+
+ g_object_unref (error_data->buffer);
+ longjmp (png_jmpbuf (png_ptr), 1);
+}
+
+static int
+get_bit_depth_for_palette (int num_palette)
+{
+ if (num_palette <= 2)
+ return 1;
+ else if (num_palette <= 4)
+ return 2;
+ else if (num_palette <= 16)
+ return 4;
+ else
+ return 8;
+}
+
+static GimpColorProfile *
+load_color_profile (png_structp pp,
+ png_infop info,
+ gchar **profile_name)
+{
+ GimpColorProfile *profile = NULL;
+
+#if defined(PNG_iCCP_SUPPORTED)
+ png_uint_32 proflen;
+ png_charp profname;
+ png_bytep prof;
+ int profcomp;
+
+ if (png_get_iCCP (pp, info, &profname, &profcomp, &prof, &proflen))
+ {
+ profile = gimp_color_profile_new_from_icc_profile ((guint8 *) prof,
+ proflen, NULL);
+ if (profile && profname)
+ {
+ *profile_name = g_convert (profname, strlen (profname),
+ "ISO-8859-1", "UTF-8", NULL, NULL, NULL);
+ }
+ }
+#endif
+
+ return profile;
+}
+
+/*
+ * 'load_image()' - Load a PNG image into a new image window.
+ */
+static gint32
+load_image (const gchar *filename,
+ gboolean interactive,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ gint i; /* Looping var */
+ gint trns; /* Transparency present */
+ gint bpp; /* Bytes per pixel */
+ gint width; /* image width */
+ gint height; /* image height */
+ gint num_passes; /* Number of interlace passes in file */
+ gint pass; /* Current pass in file */
+ gint tile_height; /* Height of tile in GIMP */
+ gint begin; /* Beginning tile row */
+ gint end; /* Ending tile row */
+ gint num; /* Number of rows to load */
+ GimpImageBaseType image_type; /* Type of image */
+ GimpPrecision image_precision; /* Precision of image */
+ GimpImageType layer_type; /* Type of drawable/layer */
+ GimpColorProfile *profile = NULL; /* Color profile */
+ gchar *profile_name = NULL; /* Profile's name */
+ gboolean linear = FALSE; /* Linear RGB */
+ FILE *fp; /* File pointer */
+ volatile gint32 image = -1; /* Image -- protected for setjmp() */
+ gint32 layer; /* Layer */
+ GeglBuffer *buffer; /* GEGL buffer for layer */
+ const Babl *file_format; /* BABL format for layer */
+ png_structp pp; /* PNG read pointer */
+ png_infop info; /* PNG info pointers */
+ guchar **pixels; /* Pixel rows */
+ guchar *pixel; /* Pixel data */
+ guchar alpha[256]; /* Index -> Alpha */
+ png_textp text;
+ gint num_texts;
+ struct read_error_data error_data;
+
+ pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (! pp)
+ {
+ /* this could happen if the compile time and run-time libpng
+ versions do not match. */
+
+ g_set_error (error, 0, 0,
+ _("Error creating PNG read struct while loading '%s'."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ info = png_create_info_struct (pp);
+ if (! info)
+ {
+ g_set_error (error, 0, 0,
+ _("Error while reading '%s'. Could not create PNG header info structure."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ if (setjmp (png_jmpbuf (pp)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error while reading '%s'. File corrupted?"),
+ gimp_filename_to_utf8 (filename));
+ return image;
+ }
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+ /* Change some libpng errors to warnings (e.g. bug 721135) */
+ png_set_benign_errors (pp, TRUE);
+
+ /* bug 765850 */
+ png_set_option (pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
+#endif
+
+ /*
+ * Open the file and initialize the PNG read "engine"...
+ */
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "rb");
+
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ png_init_io (pp, fp);
+ png_set_compression_buffer_size (pp, 512);
+
+ /*
+ * Get the image info
+ */
+
+ png_read_info (pp, info);
+
+ if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ png_set_swap (pp);
+
+ /*
+ * Get the iCCP (color profile) chunk, if any, and figure if it's
+ * a linear RGB profile
+ */
+ profile = load_color_profile (pp, info, &profile_name);
+
+ if (profile)
+ {
+ *profile_loaded = TRUE;
+
+ linear = gimp_color_profile_is_linear (profile);
+ }
+
+ /*
+ * Get image precision and color model
+ */
+
+ if (png_get_bit_depth (pp, info) == 16)
+ {
+ if (linear)
+ image_precision = GIMP_PRECISION_U16_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U16_GAMMA;
+ }
+ else
+ {
+ if (linear)
+ image_precision = GIMP_PRECISION_U8_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U8_GAMMA;
+ }
+
+ if (png_get_bit_depth (pp, info) < 8)
+ {
+ if (png_get_color_type (pp, info) == PNG_COLOR_TYPE_GRAY)
+ png_set_expand (pp);
+
+ if (png_get_color_type (pp, info) == PNG_COLOR_TYPE_PALETTE)
+ png_set_packing (pp);
+ }
+
+ /*
+ * Expand G+tRNS to GA, RGB+tRNS to RGBA
+ */
+
+ if (png_get_color_type (pp, info) != PNG_COLOR_TYPE_PALETTE &&
+ png_get_valid (pp, info, PNG_INFO_tRNS))
+ png_set_expand (pp);
+
+ /*
+ * Turn on interlace handling... libpng returns just 1 (ie single pass)
+ * if the image is not interlaced
+ */
+
+ num_passes = png_set_interlace_handling (pp);
+
+ /*
+ * Special handling for INDEXED + tRNS (transparency palette)
+ */
+
+ if (png_get_valid (pp, info, PNG_INFO_tRNS) &&
+ png_get_color_type (pp, info) == PNG_COLOR_TYPE_PALETTE)
+ {
+ guchar *alpha_ptr;
+
+ png_get_tRNS (pp, info, &alpha_ptr, &num, NULL);
+
+ /* Copy the existing alpha values from the tRNS chunk */
+ for (i = 0; i < num; ++i)
+ alpha[i] = alpha_ptr[i];
+
+ /* And set any others to fully opaque (255) */
+ for (i = num; i < 256; ++i)
+ alpha[i] = 255;
+
+ trns = 1;
+ }
+ else
+ {
+ trns = 0;
+ }
+
+ /*
+ * Update the info structures after the transformations take effect
+ */
+
+ png_read_update_info (pp, info);
+
+ switch (png_get_color_type (pp, info))
+ {
+ case PNG_COLOR_TYPE_RGB:
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGBA_IMAGE;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY:
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAY_IMAGE;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAYA_IMAGE;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ image_type = GIMP_INDEXED;
+ layer_type = GIMP_INDEXED_IMAGE;
+ break;
+
+ default:
+ g_set_error (error, 0, 0,
+ _("Unknown color model in PNG file '%s'."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ width = png_get_image_width (pp, info);
+ height = png_get_image_height (pp, info);
+
+ image = gimp_image_new_with_precision (width, height,
+ image_type, image_precision);
+ if (image == -1)
+ {
+ g_set_error (error, 0, 0,
+ _("Could not create new image for '%s': %s"),
+ gimp_filename_to_utf8 (filename), gimp_get_pdb_error ());
+ return -1;
+ }
+
+ /*
+ * Create the "background" layer to hold the image...
+ */
+
+ layer = gimp_layer_new (image, _("Background"), width, height,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ file_format = gimp_drawable_get_format (layer);
+
+ /*
+ * Find out everything we can about the image resolution
+ * This is only practical with the new 1.0 APIs, I'm afraid
+ * due to a bug in libpng-1.0.6, see png-implement for details
+ */
+
+ if (png_get_valid (pp, info, PNG_INFO_gAMA))
+ {
+ GimpParasite *parasite;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+ gdouble gamma;
+
+ png_get_gAMA (pp, info, &gamma);
+
+ g_ascii_dtostr (buf, sizeof (buf), gamma);
+
+ parasite = gimp_parasite_new ("gamma",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (buf) + 1, buf);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (png_get_valid (pp, info, PNG_INFO_oFFs))
+ {
+ gint offset_x = png_get_x_offset_pixels (pp, info);
+ gint offset_y = png_get_y_offset_pixels (pp, info);
+
+ if (offset_x != 0 ||
+ offset_y != 0)
+ {
+ if (! interactive)
+ {
+ gimp_layer_set_offsets (layer, offset_x, offset_y);
+ }
+ else if (offsets_dialog (offset_x, offset_y))
+ {
+ gimp_layer_set_offsets (layer, offset_x, offset_y);
+
+ if (abs (offset_x) > width ||
+ abs (offset_y) > height)
+ {
+ g_message (_("The PNG file specifies an offset that caused "
+ "the layer to be positioned outside the image."));
+ }
+ }
+ }
+ }
+
+ if (png_get_valid (pp, info, PNG_INFO_pHYs))
+ {
+ png_uint_32 xres;
+ png_uint_32 yres;
+ gint unit_type;
+
+ if (png_get_pHYs (pp, info,
+ &xres, &yres, &unit_type) && xres > 0 && yres > 0)
+ {
+ switch (unit_type)
+ {
+ case PNG_RESOLUTION_UNKNOWN:
+ {
+ gdouble image_xres, image_yres;
+
+ gimp_image_get_resolution (image, &image_xres, &image_yres);
+
+ if (xres > yres)
+ image_xres = image_yres * (gdouble) xres / (gdouble) yres;
+ else
+ image_yres = image_xres * (gdouble) yres / (gdouble) xres;
+
+ gimp_image_set_resolution (image, image_xres, image_yres);
+
+ *resolution_loaded = TRUE;
+ }
+ break;
+
+ case PNG_RESOLUTION_METER:
+ gimp_image_set_resolution (image,
+ (gdouble) xres * 0.0254,
+ (gdouble) yres * 0.0254);
+ gimp_image_set_unit (image, GIMP_UNIT_MM);
+
+ *resolution_loaded = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ }
+
+ gimp_image_set_filename (image, filename);
+
+ /*
+ * Load the colormap as necessary...
+ */
+
+ if (png_get_color_type (pp, info) & PNG_COLOR_MASK_PALETTE)
+ {
+ png_colorp palette;
+ int num_palette;
+
+ png_get_PLTE (pp, info, &palette, &num_palette);
+ gimp_image_set_colormap (image, (guchar *) palette,
+ num_palette);
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (file_format);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ /*
+ * Temporary buffer...
+ */
+
+ tile_height = gimp_tile_height ();
+ pixel = g_new0 (guchar, tile_height * width * bpp);
+ pixels = g_new (guchar *, tile_height);
+
+ for (i = 0; i < tile_height; i++)
+ pixels[i] = pixel + width * bpp * i;
+
+ /* Install our own error handler to handle incomplete PNG files better */
+ error_data.buffer = buffer;
+ error_data.pixel = pixel;
+ error_data.file_format = file_format;
+ error_data.tile_height = tile_height;
+ error_data.width = width;
+ error_data.height = height;
+ error_data.bpp = bpp;
+
+ png_set_error_fn (pp, &error_data, on_read_error, NULL);
+
+ for (pass = 0; pass < num_passes; pass++)
+ {
+ /*
+ * This works if you are only reading one row at a time...
+ */
+
+ for (begin = 0; begin < height; begin += tile_height)
+ {
+ end = MIN (begin + tile_height, height);
+ num = end - begin;
+
+ if (pass != 0) /* to handle interlaced PiNGs */
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, begin, width, num),
+ 1.0,
+ file_format,
+ pixel,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+
+ error_data.begin = begin;
+ error_data.end = end;
+ error_data.num = num;
+
+ png_read_rows (pp, pixels, NULL, num);
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, begin, width, num),
+ 0,
+ file_format,
+ pixel,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update
+ (((gdouble) pass +
+ (gdouble) end / (gdouble) height) /
+ (gdouble) num_passes);
+ }
+ }
+
+ png_read_end (pp, info);
+
+ /* Switch back to default error handler */
+ png_set_error_fn (pp, NULL, NULL, NULL);
+
+ if (png_get_text (pp, info, &text, &num_texts))
+ {
+ gchar *comment = NULL;
+
+ for (i = 0; i < num_texts && !comment; i++, text++)
+ {
+ if (text->key == NULL || strcmp (text->key, "Comment"))
+ continue;
+
+ if (text->text_length > 0) /* tEXt */
+ {
+ comment = g_convert (text->text, -1,
+ "UTF-8", "ISO-8859-1",
+ NULL, NULL, NULL);
+ }
+ else if (g_utf8_validate (text->text, -1, NULL))
+ { /* iTXt */
+ comment = g_strdup (text->text);
+ }
+ }
+
+ if (comment && *comment)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1, comment);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ g_free (comment);
+ }
+
+ /*
+ * Attach the color profile, if any
+ */
+
+ if (profile)
+ {
+ gimp_image_set_color_profile (image, profile);
+ g_object_unref (profile);
+
+ if (profile_name)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("icc-profile-name",
+ GIMP_PARASITE_PERSISTENT |
+ GIMP_PARASITE_UNDOABLE,
+ strlen (profile_name),
+ profile_name);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+
+ g_free (profile_name);
+ }
+ }
+
+ /*
+ * Done with the file...
+ */
+
+ png_destroy_read_struct (&pp, &info, NULL);
+
+ g_free (pixel);
+ g_free (pixels);
+ g_object_unref (buffer);
+ free (pp);
+ free (info);
+
+ fclose (fp);
+
+ if (trns)
+ {
+ GeglBufferIterator *iter;
+ gint n_components;
+
+ gimp_layer_add_alpha (layer);
+ buffer = gimp_drawable_get_buffer (layer);
+ file_format = gegl_buffer_get_format (buffer);
+
+ iter = gegl_buffer_iterator_new (buffer, NULL, 0, file_format,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+ n_components = babl_format_get_n_components (file_format);
+ g_warn_if_fail (n_components == 2);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *data = iter->items[0].data;
+ gint length = iter->length;
+
+ while (length--)
+ {
+ data[1] = alpha[data[0]];
+
+ data += n_components;
+ }
+ }
+
+ g_object_unref (buffer);
+ }
+
+ return image;
+}
+
+/*
+ * 'offsets_dialog ()' - Asks the user about offsets when loading.
+ */
+static gboolean
+offsets_dialog (gint offset_x,
+ gint offset_y)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *label;
+ gchar *message;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Apply PNG Offset"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("Ignore PNG offset"), GTK_RESPONSE_NO,
+ _("Apply PNG offset to layer"), GTK_RESPONSE_YES,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_YES,
+ GTK_RESPONSE_NO,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_QUESTION,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ message = g_strdup_printf (_("The PNG image you are importing specifies an "
+ "offset of %d, %d. Do you want to apply "
+ "this offset to the layer?"),
+ offset_x, offset_y);
+ label = gtk_label_new (message);
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_YES);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+/*
+ * 'save_image ()' - Export the specified image to a PNG file.
+ */
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error)
+{
+ gint i, k; /* Looping vars */
+ gint bpp = 0; /* Bytes per pixel */
+ gint type; /* Type of drawable/layer */
+ gint num_passes; /* Number of interlace passes in file */
+ gint pass; /* Current pass in file */
+ gint tile_height; /* Height of tile in GIMP */
+ gint width; /* image width */
+ gint height; /* image height */
+ gint begin; /* Beginning tile row */
+ gint end; /* Ending tile row */
+ gint num; /* Number of rows to load */
+ FILE *fp; /* File pointer */
+ GimpColorProfile *profile = NULL; /* Color profile */
+ gboolean out_linear; /* Save linear RGB */
+ GeglBuffer *buffer; /* GEGL buffer for layer */
+ const Babl *file_format; /* BABL format of file */
+ png_structp pp; /* PNG read pointer */
+ png_infop info; /* PNG info pointer */
+ gint offx, offy; /* Drawable offsets from origin */
+ guchar **pixels; /* Pixel rows */
+ guchar *fixed; /* Fixed-up pixel data */
+ guchar *pixel; /* Pixel data */
+ gdouble xres, yres; /* GIMP resolution (dpi) */
+ png_color_16 background; /* Background color */
+ png_time mod_time; /* Modification time (ie NOW) */
+ time_t cutime; /* Time since epoch */
+ struct tm *gmt; /* GMT broken down */
+ gint color_type; /* PNG color type */
+ gint bit_depth; /* Default to bit depth 16 */
+
+ guchar remap[256]; /* Re-mapping for the palette */
+
+ png_textp text = NULL;
+
+ out_linear = FALSE;
+#if defined(PNG_iCCP_SUPPORTED)
+ /* If no profile is written: export as sRGB.
+ * If manually assigned profile written: follow its TRC.
+ * If default profile written:
+ * - when export as auto or 16-bit: follow the storage TRC.
+ * - when export from 8-bit storage: follow the storage TRC.
+ * - when converting high bit depth to 8-bit: export as sRGB.
+ */
+ if (pngvals.save_profile)
+ {
+ profile = gimp_image_get_color_profile (orig_image_ID);
+
+ if (profile ||
+ pngvals.export_format == PNG_FORMAT_AUTO ||
+ pngvals.export_format == PNG_FORMAT_RGB16 ||
+ pngvals.export_format == PNG_FORMAT_RGBA16 ||
+ pngvals.export_format == PNG_FORMAT_GRAY16 ||
+ pngvals.export_format == PNG_FORMAT_GRAYA16 ||
+ gimp_image_get_precision (image_ID) == GIMP_PRECISION_U8_LINEAR ||
+ gimp_image_get_precision (image_ID) == GIMP_PRECISION_U8_GAMMA)
+ {
+ if (! profile)
+ profile = gimp_image_get_effective_color_profile (orig_image_ID);
+ out_linear = (gimp_color_profile_is_linear (profile));
+ }
+ else
+ {
+ /* When converting higher bit depth work image into 8-bit,
+ * with no manually assigned profile, make sure the result is
+ * sRGB.
+ */
+ profile = gimp_image_get_effective_color_profile (orig_image_ID);
+
+ if (gimp_color_profile_is_linear (profile))
+ {
+ GimpColorProfile *saved_profile;
+
+ saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile);
+ g_object_unref (profile);
+ profile = saved_profile;
+ }
+ }
+ }
+#endif
+
+ /* We save as 8-bit PNG only if:
+ * (1) Work image is 8-bit linear with linear profile to be saved.
+ * (2) Work image is 8-bit non-linear or perceptual with or without
+ * profile.
+ */
+ bit_depth = 16;
+ switch (gimp_image_get_precision (image_ID))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ if (out_linear)
+ bit_depth = 8;
+ break;
+
+ case GIMP_PRECISION_U8_GAMMA:
+ if (! out_linear)
+ bit_depth = 8;
+ break;
+
+ default:
+ break;
+ }
+
+ pp = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!pp)
+ {
+ /* this could happen if the compile time and run-time libpng
+ * versions do not match.
+ */
+ g_set_error (error, 0, 0,
+ _("Error creating PNG write struct while exporting '%s'."),
+ gimp_filename_to_utf8 (filename));
+ return FALSE;
+ }
+
+ info = png_create_info_struct (pp);
+ if (! info)
+ {
+ g_set_error (error, 0, 0,
+ _("Error while exporting '%s'. Could not create PNG header info structure."),
+ gimp_filename_to_utf8 (filename));
+ return FALSE;
+ }
+
+ if (setjmp (png_jmpbuf (pp)))
+ {
+ g_set_error (error, 0, 0,
+ _("Error while exporting '%s'. Could not export image."),
+ gimp_filename_to_utf8 (filename));
+ return FALSE;
+ }
+
+#ifdef PNG_BENIGN_ERRORS_SUPPORTED
+ /* Change some libpng errors to warnings (e.g. bug 721135) */
+ png_set_benign_errors (pp, TRUE);
+
+ /* bug 765850 */
+ png_set_option (pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
+#endif
+
+ /*
+ * Open the file and initialize the PNG write "engine"...
+ */
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "wb");
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ png_init_io (pp, fp);
+
+ /*
+ * Get the buffer for the current image...
+ */
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ type = gimp_drawable_type (drawable_ID);
+
+ /*
+ * Initialise remap[]
+ */
+ for (i = 0; i < 256; i++)
+ remap[i] = i;
+
+ if (pngvals.export_format == PNG_FORMAT_AUTO)
+ {
+ /*
+ * Set color type and remember bytes per pixel count
+ */
+
+ switch (type)
+ {
+ case GIMP_RGB_IMAGE:
+ color_type = PNG_COLOR_TYPE_RGB;
+ if (bit_depth == 8)
+ {
+ if (out_linear)
+ file_format = babl_format ("RGB u8");
+ else
+ file_format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (out_linear)
+ file_format = babl_format ("RGB u16");
+ else
+ file_format = babl_format ("R'G'B' u16");
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ if (bit_depth == 8)
+ {
+ if (out_linear)
+ file_format = babl_format ("RGBA u8");
+ else
+ file_format = babl_format ("R'G'B'A u8");
+ }
+ else
+ {
+ if (out_linear)
+ file_format = babl_format ("RGBA u16");
+ else
+ file_format = babl_format ("R'G'B'A u16");
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ if (bit_depth == 8)
+ {
+ if (out_linear)
+ file_format = babl_format ("Y u8");
+ else
+ file_format = babl_format ("Y' u8");
+ }
+ else
+ {
+ if (out_linear)
+ file_format = babl_format ("Y u16");
+ else
+ file_format = babl_format ("Y' u16");
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ if (bit_depth == 8)
+ {
+ if (out_linear)
+ file_format = babl_format ("YA u8");
+ else
+ file_format = babl_format ("Y'A u8");
+ }
+ else
+ {
+ if (out_linear)
+ file_format = babl_format ("YA u16");
+ else
+ file_format = babl_format ("Y'A u16");
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ file_format = gimp_drawable_get_format (drawable_ID);
+ pngg.has_plte = TRUE;
+ pngg.palette = (png_colorp) gimp_image_get_colormap (image_ID,
+ &pngg.num_palette);
+ bit_depth = get_bit_depth_for_palette (pngg.num_palette);
+ break;
+
+ case GIMP_INDEXEDA_IMAGE:
+ color_type = PNG_COLOR_TYPE_PALETTE;
+ file_format = gimp_drawable_get_format (drawable_ID);
+ /* fix up transparency */
+ bit_depth = respin_cmap (pp, info, remap, image_ID, drawable_ID);
+ break;
+
+ default:
+ g_set_error (error, 0, 0, "Image type can't be exported as PNG");
+ return FALSE;
+ }
+ }
+ else
+ {
+ switch (pngvals.export_format)
+ {
+ case PNG_FORMAT_RGB8:
+ color_type = PNG_COLOR_TYPE_RGB;
+ if (out_linear)
+ file_format = babl_format ("RGB u8");
+ else
+ file_format = babl_format ("R'G'B' u8");
+ bit_depth = 8;
+ break;
+ case PNG_FORMAT_GRAY8:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ if (out_linear)
+ file_format = babl_format ("Y u8");
+ else
+ file_format = babl_format ("Y' u8");
+ bit_depth = 8;
+ break;
+ case PNG_FORMAT_RGBA8:
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ if (out_linear)
+ file_format = babl_format ("RGBA u8");
+ else
+ file_format = babl_format ("R'G'B'A u8");
+ bit_depth = 8;
+ break;
+ case PNG_FORMAT_GRAYA8:
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ if (out_linear)
+ file_format = babl_format ("YA u8");
+ else
+ file_format = babl_format ("Y'A u8");
+ bit_depth = 8;
+ break;
+ case PNG_FORMAT_RGB16:
+ color_type = PNG_COLOR_TYPE_RGB;
+ if (out_linear)
+ file_format = babl_format ("RGB u16");
+ else
+ file_format = babl_format ("R'G'B' u16");
+ bit_depth = 16;
+ break;
+ case PNG_FORMAT_GRAY16:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ if (out_linear)
+ file_format = babl_format ("Y u16");
+ else
+ file_format = babl_format ("Y' u16");
+ bit_depth = 16;
+ break;
+ case PNG_FORMAT_RGBA16:
+ color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ if (out_linear)
+ file_format = babl_format ("RGBA u16");
+ else
+ file_format = babl_format ("R'G'B'A u16");
+ bit_depth = 16;
+ break;
+ case PNG_FORMAT_GRAYA16:
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ if (out_linear)
+ file_format = babl_format ("YA u16");
+ else
+ file_format = babl_format ("Y'A u16");
+ bit_depth = 16;
+ break;
+ case PNG_FORMAT_AUTO:
+ g_return_val_if_reached (FALSE);
+ }
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (file_format);
+
+ /* Note: png_set_IHDR() must be called before any other png_set_*()
+ functions. */
+ png_set_IHDR (pp, info, width, height, bit_depth, color_type,
+ pngvals.interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE,
+ PNG_FILTER_TYPE_BASE);
+
+ if (pngg.has_trns)
+ png_set_tRNS (pp, info, pngg.trans, pngg.num_trans, NULL);
+
+ if (pngg.has_plte)
+ png_set_PLTE (pp, info, pngg.palette, pngg.num_palette);
+
+ /* Set the compression level */
+
+ png_set_compression_level (pp, pngvals.compression_level);
+
+ /* All this stuff is optional extras, if the user is aiming for smallest
+ possible file size she can turn them all off */
+
+ if (pngvals.bkgd)
+ {
+ GimpRGB color;
+ guchar red, green, blue;
+
+ gimp_context_get_background (&color);
+ gimp_rgb_get_uchar (&color, &red, &green, &blue);
+
+ background.index = 0;
+ background.red = red;
+ background.green = green;
+ background.blue = blue;
+ background.gray = gimp_rgb_luminance_uchar (&color);
+ png_set_bKGD (pp, info, &background);
+ }
+
+ if (pngvals.gama)
+ {
+ GimpParasite *parasite;
+ gdouble gamma = 1.0 / DEFAULT_GAMMA;
+
+ parasite = gimp_image_get_parasite (orig_image_ID, "gamma");
+ if (parasite)
+ {
+ gamma = g_ascii_strtod (gimp_parasite_data (parasite), NULL);
+ gimp_parasite_free (parasite);
+ }
+
+ png_set_gAMA (pp, info, gamma);
+ }
+
+ if (pngvals.offs)
+ {
+ gimp_drawable_offsets (drawable_ID, &offx, &offy);
+ if (offx != 0 || offy != 0)
+ png_set_oFFs (pp, info, offx, offy, PNG_OFFSET_PIXEL);
+ }
+
+ if (pngvals.phys)
+ {
+ gimp_image_get_resolution (orig_image_ID, &xres, &yres);
+ png_set_pHYs (pp, info, RINT (xres / 0.0254), RINT (yres / 0.0254),
+ PNG_RESOLUTION_METER);
+ }
+
+ if (pngvals.time)
+ {
+ cutime = time (NULL); /* time right NOW */
+ gmt = gmtime (&cutime);
+
+ mod_time.year = gmt->tm_year + 1900;
+ mod_time.month = gmt->tm_mon + 1;
+ mod_time.day = gmt->tm_mday;
+ mod_time.hour = gmt->tm_hour;
+ mod_time.minute = gmt->tm_min;
+ mod_time.second = gmt->tm_sec;
+ png_set_tIME (pp, info, &mod_time);
+ }
+
+#if defined(PNG_iCCP_SUPPORTED)
+ if (pngvals.save_profile)
+ {
+ GimpParasite *parasite;
+ gchar *profile_name = NULL;
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "icc-profile-name");
+ if (parasite)
+ profile_name = g_convert (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite),
+ "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
+
+ png_set_iCCP (pp,
+ info,
+ profile_name ? profile_name : "ICC profile",
+ 0,
+ icc_data,
+ icc_length);
+
+ g_free (profile_name);
+ g_object_unref (profile);
+ }
+#endif
+
+#ifdef PNG_zTXt_SUPPORTED
+/* Small texts are not worth compressing and will be even bigger if compressed.
+ Empirical length limit of a text being worth compressing. */
+#define COMPRESSION_WORTHY_LENGTH 200
+#endif
+
+ if (pngvals.comment)
+ {
+ GimpParasite *parasite;
+ gsize text_length = 0;
+
+ parasite = gimp_image_get_parasite (orig_image_ID, "gimp-comment");
+ if (parasite)
+ {
+ gchar *comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+
+ gimp_parasite_free (parasite);
+
+ if (comment && strlen (comment) > 0)
+ {
+ text = g_new0 (png_text, 1);
+
+ text[0].key = "Comment";
+
+#ifdef PNG_iTXt_SUPPORTED
+
+ text[0].text = g_convert (comment, -1,
+ "ISO-8859-1",
+ "UTF-8",
+ NULL,
+ &text_length,
+ NULL);
+
+ if (text[0].text == NULL || strlen (text[0].text) == 0)
+ {
+ /* We can't convert to ISO-8859-1 without loss.
+ Save the comment as iTXt (UTF-8). */
+ g_free (text[0].text);
+
+ text[0].text = g_strdup (comment);
+ text[0].itxt_length = strlen (text[0].text);
+
+#ifdef PNG_zTXt_SUPPORTED
+ text[0].compression = strlen (text[0].text) > COMPRESSION_WORTHY_LENGTH ?
+ PNG_ITXT_COMPRESSION_zTXt : PNG_ITXT_COMPRESSION_NONE;
+#else
+ text[0].compression = PNG_ITXT_COMPRESSION_NONE;
+#endif /* PNG_zTXt_SUPPORTED */
+ }
+ else
+ /* The comment is ISO-8859-1 compatible, so we use tEXt
+ even if there is iTXt support for compatibility to more
+ png reading programs. */
+#endif /* PNG_iTXt_SUPPORTED */
+ {
+#ifndef PNG_iTXt_SUPPORTED
+ /* No iTXt support, so we are forced to use tEXt (ISO-8859-1).
+ A broken comment is better than no comment at all, so the
+ conversion does not fail on unknown character.
+ They are simply ignored. */
+ text[0].text = g_convert_with_fallback (comment, -1,
+ "ISO-8859-1",
+ "UTF-8",
+ "",
+ NULL,
+ &text_length,
+ NULL);
+#endif
+
+#ifdef PNG_zTXt_SUPPORTED
+ text[0].compression = strlen (text[0].text) > COMPRESSION_WORTHY_LENGTH ?
+ PNG_TEXT_COMPRESSION_zTXt : PNG_TEXT_COMPRESSION_NONE;
+#else
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+#endif /* PNG_zTXt_SUPPORTED */
+
+ text[0].text_length = text_length;
+ }
+
+ if (! text[0].text || strlen (text[0].text) == 0)
+ {
+ g_free (text[0].text);
+ g_free (text);
+ text = NULL;
+ }
+
+ g_free (comment);
+ }
+ }
+ }
+
+#ifdef PNG_zTXt_SUPPORTED
+#undef COMPRESSION_WORTHY_LENGTH
+#endif
+
+ if (text)
+ png_set_text (pp, info, text, 1);
+
+ png_write_info (pp, info);
+ if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
+ png_set_swap (pp);
+
+ /*
+ * Turn on interlace handling...
+ */
+
+ if (pngvals.interlaced)
+ num_passes = png_set_interlace_handling (pp);
+ else
+ num_passes = 1;
+
+ /*
+ * Convert unpacked pixels to packed if necessary
+ */
+
+ if (color_type == PNG_COLOR_TYPE_PALETTE &&
+ bit_depth < 8)
+ png_set_packing (pp);
+
+ /*
+ * Allocate memory for "tile_height" rows and export the image...
+ */
+
+ tile_height = gimp_tile_height ();
+ pixel = g_new (guchar, tile_height * width * bpp);
+ pixels = g_new (guchar *, tile_height);
+
+ for (i = 0; i < tile_height; i++)
+ pixels[i] = pixel + width * bpp * i;
+
+ for (pass = 0; pass < num_passes; pass++)
+ {
+ /* This works if you are only writing one row at a time... */
+ for (begin = 0, end = tile_height;
+ begin < height; begin += tile_height, end += tile_height)
+ {
+ if (end > height)
+ end = height;
+
+ num = end - begin;
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, begin, width, num),
+ 1.0,
+ file_format,
+ pixel,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+
+ /* If we are with a RGBA image and have to pre-multiply the
+ alpha channel */
+ if (bpp == 4 && ! pngvals.save_transp_pixels)
+ {
+ for (i = 0; i < num; ++i)
+ {
+ fixed = pixels[i];
+ for (k = 0; k < width; ++k)
+ {
+ if (!fixed[3])
+ fixed[0] = fixed[1] = fixed[2] = 0;
+ fixed += bpp;
+ }
+ }
+ }
+
+ if (bpp == 8 && ! pngvals.save_transp_pixels)
+ {
+ for (i = 0; i < num; ++i)
+ {
+ fixed = pixels[i];
+ for (k = 0; k < width; ++k)
+ {
+ if (!fixed[6] && !fixed[7])
+ fixed[0] = fixed[1] = fixed[2] =
+ fixed[3] = fixed[4] = fixed[5] = 0;
+ fixed += bpp;
+ }
+ }
+ }
+
+ /* If we're dealing with a paletted image with
+ * transparency set, write out the remapped palette */
+ if (png_get_valid (pp, info, PNG_INFO_tRNS))
+ {
+ guchar inverse_remap[256];
+
+ for (i = 0; i < 256; i++)
+ inverse_remap[ remap[i] ] = i;
+
+ for (i = 0; i < num; ++i)
+ {
+ fixed = pixels[i];
+ for (k = 0; k < width; ++k)
+ {
+ fixed[k] = (fixed[k*2+1] > 127) ?
+ inverse_remap[ fixed[k*2] ] :
+ 0;
+ }
+ }
+ }
+
+ /* Otherwise if we have a paletted image and transparency
+ * couldn't be set, we ignore the alpha channel */
+ else if (png_get_valid (pp, info, PNG_INFO_PLTE) &&
+ bpp == 2)
+ {
+ for (i = 0; i < num; ++i)
+ {
+ fixed = pixels[i];
+ for (k = 0; k < width; ++k)
+ {
+ fixed[k] = fixed[k * 2];
+ }
+ }
+ }
+
+ png_write_rows (pp, pixels, num);
+
+ gimp_progress_update (((double) pass + (double) end /
+ (double) height) /
+ (double) num_passes);
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ png_write_end (pp, info);
+ png_destroy_write_struct (&pp, &info);
+
+ g_free (pixel);
+ g_free (pixels);
+
+ /*
+ * Done with the file...
+ */
+
+ if (text)
+ {
+ g_free (text[0].text);
+ g_free (text);
+ }
+
+ free (pp);
+ free (info);
+
+ fclose (fp);
+
+ return TRUE;
+}
+
+static gboolean
+ia_has_transparent_pixels (GeglBuffer *buffer)
+{
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint n_components;
+
+ format = gegl_buffer_get_format (buffer);
+ iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
+ n_components = babl_format_get_n_components (format);
+ g_return_val_if_fail (n_components == 2, FALSE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *data = iter->items[0].data;
+ gint length = iter->length;
+
+ while (length--)
+ {
+ if (data[1] <= 127)
+ {
+ gegl_buffer_iterator_stop (iter);
+ return TRUE;
+ }
+
+ data += n_components;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Try to find a color in the palette which isn't actually
+ * used in the image, so that we can use it as the transparency
+ * index. Taken from gif.c */
+static gint
+find_unused_ia_color (GeglBuffer *buffer,
+ gint *colors)
+{
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint n_components;
+ gboolean ix_used[256];
+ gboolean trans_used = FALSE;
+ gint i;
+
+ for (i = 0; i < *colors; i++)
+ ix_used[i] = FALSE;
+
+ format = gegl_buffer_get_format (buffer);
+ iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
+ n_components = babl_format_get_n_components (format);
+ g_return_val_if_fail (n_components == 2, FALSE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *data = iter->items[0].data;
+ gint length = iter->length;
+
+ while (length--)
+ {
+ if (data[1] > 127)
+ ix_used[data[0]] = TRUE;
+ else
+ trans_used = TRUE;
+
+ data += n_components;
+ }
+ }
+
+ /* If there is no transparency, ignore alpha. */
+ if (trans_used == FALSE)
+ return -1;
+
+ /* If there is still some room at the end of the palette, increment
+ * the number of colors in the image and assign a transparent pixel
+ * there. */
+ if ((*colors) < 256)
+ {
+ (*colors)++;
+
+ return (*colors) - 1;
+ }
+
+ for (i = 0; i < *colors; i++)
+ {
+ if (ix_used[i] == FALSE)
+ return i;
+ }
+
+ return -1;
+}
+
+
+static int
+respin_cmap (png_structp pp,
+ png_infop info,
+ guchar *remap,
+ gint32 image_ID,
+ gint32 drawable_ID)
+{
+ static guchar trans[] = { 0 };
+ GeglBuffer *buffer;
+
+ gint colors;
+ guchar *before;
+
+ before = gimp_image_get_colormap (image_ID, &colors);
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ /*
+ * Make sure there is something in the colormap.
+ */
+ if (colors == 0)
+ {
+ before = g_newa (guchar, 3);
+ memset (before, 0, sizeof (guchar) * 3);
+
+ colors = 1;
+ }
+
+ /* Try to find an entry which isn't actually used in the
+ image, for a transparency index. */
+
+ if (ia_has_transparent_pixels (buffer))
+ {
+ gint transparent = find_unused_ia_color (buffer, &colors);
+
+ if (transparent != -1) /* we have a winner for a transparent
+ * index - do like gif2png and swap
+ * index 0 and index transparent */
+ {
+ static png_color palette[256];
+ gint i;
+
+ /* Set tRNS chunk values for writing later. */
+ pngg.has_trns = TRUE;
+ pngg.trans = trans;
+ pngg.num_trans = 1;
+
+ /* Transform all pixels with a value = transparent to
+ * 0 and vice versa to compensate for re-ordering in palette
+ * due to png_set_tRNS() */
+
+ remap[0] = transparent;
+ for (i = 1; i <= transparent; i++)
+ remap[i] = i - 1;
+
+ /* Copy from index 0 to index transparent - 1 to index 1 to
+ * transparent of after, then from transparent+1 to colors-1
+ * unchanged, and finally from index transparent to index 0. */
+
+ for (i = 0; i < colors; i++)
+ {
+ palette[i].red = before[3 * remap[i]];
+ palette[i].green = before[3 * remap[i] + 1];
+ palette[i].blue = before[3 * remap[i] + 2];
+ }
+
+ /* Set PLTE chunk values for writing later. */
+ pngg.has_plte = TRUE;
+ pngg.palette = palette;
+ pngg.num_palette = colors;
+ }
+ else
+ {
+ /* Inform the user that we couldn't losslessly save the
+ * transparency & just use the full palette */
+ g_message (_("Couldn't losslessly save transparency, "
+ "saving opacity instead."));
+
+ /* Set PLTE chunk values for writing later. */
+ pngg.has_plte = TRUE;
+ pngg.palette = (png_colorp) before;
+ pngg.num_palette = colors;
+ }
+ }
+ else
+ {
+ /* Set PLTE chunk values for writing later. */
+ pngg.has_plte = TRUE;
+ pngg.palette = (png_colorp) before;
+ pngg.num_palette = colors;
+ }
+
+ g_object_unref (buffer);
+
+ return get_bit_depth_for_palette (colors);
+}
+
+static GtkWidget *
+toggle_button_init (GtkBuilder *builder,
+ const gchar *name,
+ gboolean initial_value,
+ gboolean *value_pointer)
+{
+ GtkWidget *toggle = NULL;
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), initial_value);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ value_pointer);
+
+ return toggle;
+}
+
+static void pixformat_changed (GtkWidget *widget,
+ void *foo)
+{
+ PngExportFormat *ep = foo;
+ *ep = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+}
+
+static gboolean
+save_dialog (gint32 image_ID,
+ gboolean alpha)
+{
+ PngSaveGui pg;
+ GtkWidget *dialog;
+ GtkBuilder *builder;
+ gchar *ui_file;
+ GimpParasite *parasite;
+ GError *error = NULL;
+
+ /* Dialog init */
+ dialog = gimp_export_dialog_new (_("PNG"), PLUG_IN_BINARY, SAVE_PROC);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_dialog_response),
+ &pg);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ /* GtkBuilder init */
+ builder = gtk_builder_new ();
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui/plug-ins/plug-in-file-png.ui",
+ NULL);
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ gchar *display_name = g_filename_display_name (ui_file);
+
+ g_printerr (_("Error loading UI file '%s': %s"),
+ display_name, error ? error->message : _("Unknown error"));
+
+ g_free (display_name);
+ }
+
+ g_free (ui_file);
+
+ /* Table */
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ GTK_WIDGET (gtk_builder_get_object (builder, "table")),
+ FALSE, FALSE, 0);
+
+ /* Toggles */
+ pg.interlaced = toggle_button_init (builder, "interlace",
+ pngvals.interlaced,
+ &pngvals.interlaced);
+ pg.bkgd = toggle_button_init (builder, "save-background-color",
+ pngvals.bkgd,
+ &pngvals.bkgd);
+ pg.gama = toggle_button_init (builder, "save-gamma",
+ pngvals.gama,
+ &pngvals.gama);
+ pg.offs = toggle_button_init (builder, "save-layer-offset",
+ pngvals.offs,
+ &pngvals.offs);
+ pg.phys = toggle_button_init (builder, "save-resolution",
+ pngvals.phys,
+ &pngvals.phys);
+ pg.time = toggle_button_init (builder, "save-creation-time",
+ pngvals.time,
+ &pngvals.time);
+ pg.save_exif = toggle_button_init (builder, "save-exif",
+ pngvals.save_exif,
+ &pngvals.save_exif);
+ pg.save_xmp = toggle_button_init (builder, "save-xmp",
+ pngvals.save_xmp,
+ &pngvals.save_xmp);
+ pg.save_iptc = toggle_button_init (builder, "save-iptc",
+ pngvals.save_iptc,
+ &pngvals.save_iptc);
+ pg.save_thumbnail = toggle_button_init (builder, "save-thumbnail",
+ pngvals.save_thumbnail,
+ &pngvals.save_thumbnail);
+ pg.save_profile = toggle_button_init (builder, "save-color-profile",
+ pngvals.save_profile,
+ &pngvals.save_profile);
+
+#if !defined(PNG_iCCP_SUPPORTED)
+ gtk_widget_hide (pg.save_profile);
+#endif
+
+ /* Comment toggle */
+ parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
+ pg.comment =
+ toggle_button_init (builder, "save-comment",
+ pngvals.comment && parasite != NULL,
+ &pngvals.comment);
+ gtk_widget_set_sensitive (pg.comment, parasite != NULL);
+ gimp_parasite_free (parasite);
+
+ /* Transparent pixels toggle */
+ pg.save_transp_pixels =
+ toggle_button_init (builder,
+ "save-transparent-pixels",
+ alpha && pngvals.save_transp_pixels,
+ &pngvals.save_transp_pixels);
+ gtk_widget_set_sensitive (pg.save_transp_pixels, alpha);
+
+ /* Compression level scale */
+ pg.compression_level =
+ GTK_ADJUSTMENT (gtk_builder_get_object (builder, "compression-level"));
+ gtk_adjustment_set_value (pg.compression_level, pngvals.compression_level);
+ g_signal_connect (pg.compression_level, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &pngvals.compression_level);
+
+ /* Compression level scale */
+ pg.pixelformat =
+ GTK_WIDGET (gtk_builder_get_object (builder, "pixelformat-combo"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (pg.pixelformat), pngvals.export_format);
+ g_signal_connect (pg.pixelformat, "changed",
+ G_CALLBACK (pixformat_changed),
+ &pngvals.export_format);
+
+#if 0
+ gtk_adjustment_set_value (pg.compression_level, pngvals.compression_level);
+ g_signal_connect (pg.compression_level, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &pngvals.compression_level);
+#endif
+
+ /* Load/save defaults buttons */
+ g_signal_connect_swapped (gtk_builder_get_object (builder, "load-defaults"),
+ "clicked",
+ G_CALLBACK (load_gui_defaults),
+ &pg);
+
+ g_signal_connect_swapped (gtk_builder_get_object (builder, "save-defaults"),
+ "clicked",
+ G_CALLBACK (save_parasite),
+ &pg);
+
+ /* Show dialog and run */
+ gtk_widget_show (dialog);
+
+ pg.run = FALSE;
+
+ gtk_main ();
+
+ return pg.run;
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ PngSaveGui *pg = data;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ pg->run = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+load_parasite (void)
+{
+ GimpParasite *parasite;
+
+ parasite = gimp_get_parasite (PNG_DEFAULTS_PARASITE);
+
+ if (parasite)
+ {
+ gchar *def_str;
+ PngSaveVals tmpvals = defaults;
+ gint num_fields;
+
+ def_str = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+
+ gimp_parasite_free (parasite);
+
+ num_fields = sscanf (def_str, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ &tmpvals.interlaced,
+ &tmpvals.bkgd,
+ &tmpvals.gama,
+ &tmpvals.offs,
+ &tmpvals.phys,
+ &tmpvals.time,
+ &tmpvals.comment,
+ &tmpvals.save_transp_pixels,
+ &tmpvals.compression_level,
+ &tmpvals.save_exif,
+ &tmpvals.save_xmp,
+ &tmpvals.save_iptc,
+ &tmpvals.save_thumbnail,
+ &tmpvals.save_profile);
+
+ g_free (def_str);
+
+ if (num_fields == 9 || num_fields == 13 || num_fields == 14)
+ pngvals = tmpvals;
+ }
+}
+
+static void
+save_parasite (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+
+ def_str = g_strdup_printf ("%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ pngvals.interlaced,
+ pngvals.bkgd,
+ pngvals.gama,
+ pngvals.offs,
+ pngvals.phys,
+ pngvals.time,
+ pngvals.comment,
+ pngvals.save_transp_pixels,
+ pngvals.compression_level,
+ pngvals.save_exif,
+ pngvals.save_xmp,
+ pngvals.save_iptc,
+ pngvals.save_thumbnail,
+ pngvals.save_profile);
+
+ parasite = gimp_parasite_new (PNG_DEFAULTS_PARASITE,
+ GIMP_PARASITE_PERSISTENT,
+ strlen (def_str), def_str);
+
+ gimp_attach_parasite (parasite);
+
+ gimp_parasite_free (parasite);
+ g_free (def_str);
+}
+
+static void
+load_gui_defaults (PngSaveGui *pg)
+{
+ /* initialize with hardcoded defaults */
+ pngvals = defaults;
+ /* Override with parasite. */
+ load_parasite ();
+
+#define SET_ACTIVE(field) \
+ if (gtk_widget_is_sensitive (pg->field)) \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->field), pngvals.field)
+
+ SET_ACTIVE (interlaced);
+ SET_ACTIVE (bkgd);
+ SET_ACTIVE (gama);
+ SET_ACTIVE (offs);
+ SET_ACTIVE (phys);
+ SET_ACTIVE (time);
+ SET_ACTIVE (comment);
+ SET_ACTIVE (save_transp_pixels);
+ SET_ACTIVE (save_exif);
+ SET_ACTIVE (save_xmp);
+ SET_ACTIVE (save_iptc);
+ SET_ACTIVE (save_thumbnail);
+ SET_ACTIVE (save_profile);
+
+#undef SET_ACTIVE
+
+ gtk_adjustment_set_value (pg->compression_level,
+ pngvals.compression_level);
+}
diff --git a/plug-ins/common/file-pnm.c b/plug-ins/common/file-pnm.c
new file mode 100644
index 0000000..2132eec
--- /dev/null
+++ b/plug-ins/common/file-pnm.c
@@ -0,0 +1,1820 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * PNM reading and writing code Copyright (C) 1996 Erik Nygren
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * The pnm reading and writing code was written from scratch by Erik Nygren
+ * (nygren@mit.edu) based on the specifications in the man pages and
+ * does not contain any code from the netpbm or pbmplus distributions.
+ *
+ * 2006: pbm saving written by Martin K Collins (martin@mkcollins.org)
+ * 2015: pfm reading written by Tobias Ellinghaus (me@houz.org)
+ */
+
+#include "config.h"
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <math.h>
+#include <errno.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-pnm-load"
+#define PNM_SAVE_PROC "file-pnm-save"
+#define PBM_SAVE_PROC "file-pbm-save"
+#define PGM_SAVE_PROC "file-pgm-save"
+#define PPM_SAVE_PROC "file-ppm-save"
+#define PFM_SAVE_PROC "file-pfm-save"
+#define PLUG_IN_BINARY "file-pnm"
+#define PLUG_IN_ROLE "gimp-file-pnm"
+
+
+/* Declare local data types
+ */
+
+typedef struct _PNMScanner PNMScanner;
+typedef struct _PNMInfo PNMInfo;
+typedef struct _PNMRowInfo PNMRowInfo;
+
+typedef void (* PNMLoaderFunc) (PNMScanner *scanner,
+ PNMInfo *info,
+ GeglBuffer *buffer);
+typedef gboolean (* PNMSaverowFunc) (PNMRowInfo *info,
+ guchar *data,
+ GError **error);
+
+struct _PNMScanner
+{
+ GInputStream *input; /* The input stream of the file being read */
+ gchar cur; /* The current character in the input stream */
+ gint eof; /* Have we reached end of file? */
+ gchar *inbuf; /* Input buffer - initially 0 */
+ gint inbufsize; /* Size of input buffer */
+ gint inbufvalidsize; /* Size of input buffer with valid data */
+ gint inbufpos; /* Position in input buffer */
+};
+
+struct _PNMInfo
+{
+ gint xres, yres; /* The size of the image */
+ gboolean float_format; /* Whether it is a floating point format */
+ gint maxval; /* For integer format image files, the max value
+ * which we need to normalize to */
+ gfloat scale_factor; /* PFM files have a scale factor */
+ gint np; /* Number of image planes (0 for pbm) */
+ gboolean asciibody; /* 1 if ascii body, 0 if raw body */
+ jmp_buf jmpbuf; /* Where to jump to on an error loading */
+
+ PNMLoaderFunc loader; /* Routine to use to load the pnm body */
+};
+
+/* Contains the information needed to write out PNM rows */
+struct _PNMRowInfo
+{
+ GOutputStream *output; /* Output stream */
+ gchar *rowbuf; /* Buffer for writing out rows */
+ gint xres; /* X resolution */
+ gint np; /* Number of planes */
+ gint bpc; /* Bytes per color */
+ guchar *red; /* Colormap red */
+ guchar *grn; /* Colormap green */
+ guchar *blu; /* Colormap blue */
+ gboolean zero_is_black; /* index zero is black (PBM only) */
+};
+
+/* Export info */
+typedef struct
+{
+ gint raw; /* raw or ascii */
+} PNMSaveVals;
+
+#define BUFLEN 512 /* The input buffer size for data returned
+ * from the scanner. Note that lines
+ * aren't allowed to be over 256 characters
+ * by the spec anyways so this shouldn't
+ * be an issue. */
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (GFile *file,
+ GError **error);
+static gint save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean pbm,
+ gboolean float_format,
+ GError **error);
+
+static gboolean save_dialog (void);
+
+static void pnm_load_ascii (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer);
+static void pnm_load_raw (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer);
+static void pnm_load_rawpbm (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer);
+static void pnm_load_rawpfm (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer);
+
+static gboolean pnmsaverow_ascii (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+static gboolean pnmsaverow_raw (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+static gboolean pnmsaverow_raw_pbm (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+static gboolean pnmsaverow_ascii_pbm (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+static gboolean pnmsaverow_ascii_indexed (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+static gboolean pnmsaverow_raw_indexed (PNMRowInfo *ri,
+ guchar *data,
+ GError **error);
+
+static void pnmscanner_destroy (PNMScanner *s);
+static void pnmscanner_createbuffer (PNMScanner *s,
+ gint bufsize);
+static void pnmscanner_getchar (PNMScanner *s);
+static void pnmscanner_eatwhitespace (PNMScanner *s);
+static void pnmscanner_gettoken (PNMScanner *s,
+ gchar *buf,
+ gint bufsize);
+static void pnmscanner_getsmalltoken (PNMScanner *s,
+ gchar *buf);
+
+static PNMScanner * pnmscanner_create (GInputStream *input);
+
+
+#define pnmscanner_eof(s) ((s)->eof)
+#define pnmscanner_input(s) ((s)->input)
+
+/* Checks for a fatal error */
+#define CHECK_FOR_ERROR(predicate, jmpbuf, ...) \
+ if ((predicate)) \
+ { g_message (__VA_ARGS__); longjmp ((jmpbuf), 1); }
+
+static const struct
+{
+ gchar name;
+ gint np;
+ gint asciibody;
+ gint maxval;
+ PNMLoaderFunc loader;
+} pnm_types[] =
+{
+ { '1', 0, 1, 1, pnm_load_ascii }, /* ASCII PBM */
+ { '2', 1, 1, 255, pnm_load_ascii }, /* ASCII PGM */
+ { '3', 3, 1, 255, pnm_load_ascii }, /* ASCII PPM */
+ { '4', 0, 0, 1, pnm_load_rawpbm }, /* RAW PBM */
+ { '5', 1, 0, 255, pnm_load_raw }, /* RAW PGM */
+ { '6', 3, 0, 255, pnm_load_raw }, /* RAW PPM */
+ { 'F', 3, 0, 0, pnm_load_rawpfm }, /* RAW PFM (color) */
+ { 'f', 1, 0, 0, pnm_load_rawpfm }, /* RAW PFM (grayscale) */
+ { 0 , 0, 0, 0, NULL}
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static PNMSaveVals psvals =
+{
+ TRUE /* raw? or ascii */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "raw", "TRUE for raw output, FALSE for ascii output" }
+ };
+
+ static const GimpParamDef pfm_save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the PNM file format",
+ "This plug-in loads files in the various Netpbm portable file formats.",
+ "Erik Nygren",
+ "Erik Nygren",
+ "1996",
+ N_("PNM Image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-portable-anymap");
+ gimp_register_file_handler_uri (LOAD_PROC);
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "pnm,ppm,pgm,pbm,pfm",
+ "",
+ "0,string,P1,0,string,P2,0,string,P3,"
+ "0,string,P4,0,string,P5,0,string,P6,"
+ "0,string,PF,0,string,Pf");
+
+ gimp_install_procedure (PNM_SAVE_PROC,
+ "Exports files in the PNM file format",
+ "PNM exporting handles all image types without transparency.",
+ "Erik Nygren",
+ "Erik Nygren",
+ "1996",
+ N_("PNM image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (PBM_SAVE_PROC,
+ "Exports files in the PBM file format",
+ "PBM exporting produces mono images without transparency.",
+ "Martin K Collins",
+ "Erik Nygren",
+ "2006",
+ N_("PBM image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (PGM_SAVE_PROC,
+ "Exports files in the PGM file format",
+ "PGM exporting produces grayscale images without transparency.",
+ "Erik Nygren",
+ "Erik Nygren",
+ "1996",
+ N_("PGM image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (PPM_SAVE_PROC,
+ "Exports files in the PPM file format",
+ "PPM exporting handles RGB images without transparency.",
+ "Erik Nygren",
+ "Erik Nygren",
+ "1996",
+ N_("PPM image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (PFM_SAVE_PROC,
+ "Exports files in the PFM file format",
+ "PFM exporting handles all images without transparency.",
+ "Mukund Sivaraman",
+ "Mukund Sivaraman",
+ "2015",
+ N_("PFM image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (pfm_save_args), 0,
+ pfm_save_args, NULL);
+
+ gimp_register_file_handler_mime (PNM_SAVE_PROC, "image/x-portable-anymap");
+ gimp_register_file_handler_mime (PBM_SAVE_PROC, "image/x-portable-bitmap");
+ gimp_register_file_handler_mime (PGM_SAVE_PROC, "image/x-portable-graymap");
+ gimp_register_file_handler_mime (PPM_SAVE_PROC, "image/x-portable-pixmap");
+ gimp_register_file_handler_mime (PPM_SAVE_PROC, "image/x-portable-floatmap");
+
+ gimp_register_file_handler_uri (PNM_SAVE_PROC);
+ gimp_register_file_handler_uri (PBM_SAVE_PROC);
+ gimp_register_file_handler_uri (PGM_SAVE_PROC);
+ gimp_register_file_handler_uri (PPM_SAVE_PROC);
+ gimp_register_file_handler_uri (PFM_SAVE_PROC);
+
+ gimp_register_save_handler (PNM_SAVE_PROC, "pnm", "");
+ gimp_register_save_handler (PBM_SAVE_PROC, "pbm", "");
+ gimp_register_save_handler (PGM_SAVE_PROC, "pgm", "");
+ gimp_register_save_handler (PPM_SAVE_PROC, "ppm", "");
+ gimp_register_save_handler (PFM_SAVE_PROC, "pfm", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+ gboolean pbm = FALSE; /* flag for PBM output */
+ gboolean float_format = FALSE; /* flag for PFM output */
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (g_file_new_for_uri (param[1].data.d_string),
+ &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, PNM_SAVE_PROC) == 0 ||
+ strcmp (name, PBM_SAVE_PROC) == 0 ||
+ strcmp (name, PGM_SAVE_PROC) == 0 ||
+ strcmp (name, PPM_SAVE_PROC) == 0 ||
+ strcmp (name, PFM_SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ if (strcmp (name, PNM_SAVE_PROC) == 0)
+ {
+ export = gimp_export_image (&image_ID, &drawable_ID, "PNM",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+ }
+ else if (strcmp (name, PBM_SAVE_PROC) == 0)
+ {
+ export = gimp_export_image (&image_ID, &drawable_ID, "PBM",
+ GIMP_EXPORT_CAN_HANDLE_BITMAP);
+ pbm = TRUE; /* gimp has no mono image type so hack it */
+ }
+ else if (strcmp (name, PGM_SAVE_PROC) == 0)
+ {
+ export = gimp_export_image (&image_ID, &drawable_ID, "PGM",
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+ }
+ else if (strcmp (name, PPM_SAVE_PROC) == 0)
+ {
+ export = gimp_export_image (&image_ID, &drawable_ID, "PPM",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+ }
+ else
+ {
+ export = gimp_export_image (&image_ID, &drawable_ID, "PFM",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+ float_format = TRUE;
+ }
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (strcmp (name, PFM_SAVE_PROC) != 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ psvals.raw = (param[5].data.d_int32) ? TRUE : FALSE;
+ pbm = (strcmp (name, PBM_SAVE_PROC) == 0);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID, pbm, float_format,
+ &error))
+ {
+ if (strcmp (name, PFM_SAVE_PROC) != 0)
+ {
+ /* Store psvals data */
+ gimp_set_data (name, &psvals, sizeof (PNMSaveVals));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+
+ gegl_exit ();
+}
+
+static gint32
+load_image (GFile *file,
+ GError **error)
+{
+ GInputStream *input;
+ GeglBuffer *buffer;
+ gint32 volatile image_ID = -1;
+ gint32 layer_ID;
+ char buf[BUFLEN + 4]; /* buffer for random things like scanning */
+ PNMInfo *pnminfo;
+ PNMScanner *volatile scan;
+ int ctr;
+ GimpPrecision precision;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ g_file_get_parse_name (file));
+
+ input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! input)
+ return -1;
+
+ /* allocate the necessary structures */
+ pnminfo = g_new (PNMInfo, 1);
+
+ scan = NULL;
+ /* set error handling */
+ if (setjmp (pnminfo->jmpbuf))
+ {
+ /* If we get here, we had a problem reading the file */
+ if (scan)
+ pnmscanner_destroy (scan);
+
+ g_object_unref (input);
+ g_free (pnminfo);
+
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+
+ return -1;
+ }
+
+ if (! (scan = pnmscanner_create (input)))
+ longjmp (pnminfo->jmpbuf, 1);
+
+ /* Get magic number */
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
+ _("Premature end of file."));
+ CHECK_FOR_ERROR ((buf[0] != 'P' || buf[2]), pnminfo->jmpbuf,
+ _("Invalid file."));
+
+ /* Look up magic number to see what type of PNM this is */
+ for (ctr = 0; pnm_types[ctr].name; ctr++)
+ if (buf[1] == pnm_types[ctr].name)
+ {
+ pnminfo->np = pnm_types[ctr].np;
+ pnminfo->asciibody = pnm_types[ctr].asciibody;
+ pnminfo->float_format = g_ascii_tolower (pnm_types[ctr].name) == 'f';
+ pnminfo->maxval = pnm_types[ctr].maxval;
+ pnminfo->loader = pnm_types[ctr].loader;
+ }
+
+ if (!pnminfo->loader)
+ {
+ g_message (_("File not in a supported format."));
+ longjmp (pnminfo->jmpbuf, 1);
+ }
+
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
+ _("Premature end of file."));
+ pnminfo->xres = g_ascii_isdigit(*buf) ? atoi (buf) : 0;
+ CHECK_FOR_ERROR (pnminfo->xres <= 0, pnminfo->jmpbuf,
+ _("Invalid X resolution."));
+ CHECK_FOR_ERROR (pnminfo->xres > GIMP_MAX_IMAGE_SIZE, pnminfo->jmpbuf,
+ _("Image width is larger than GIMP can handle."));
+
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
+ _("Premature end of file."));
+ pnminfo->yres = g_ascii_isdigit (*buf) ? atoi (buf) : 0;
+ CHECK_FOR_ERROR (pnminfo->yres <= 0, pnminfo->jmpbuf,
+ _("Invalid Y resolution."));
+ CHECK_FOR_ERROR (pnminfo->yres > GIMP_MAX_IMAGE_SIZE, pnminfo->jmpbuf,
+ _("Image height is larger than GIMP can handle."));
+
+ if (pnminfo->float_format)
+ {
+ gchar *endptr = NULL;
+
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
+ _("Premature end of file."));
+
+ pnminfo->scale_factor = g_ascii_strtod (buf, &endptr);
+ CHECK_FOR_ERROR (endptr == NULL || *endptr != 0 || errno == ERANGE,
+ pnminfo->jmpbuf, _("Bogus scale factor."));
+ CHECK_FOR_ERROR (!isnormal (pnminfo->scale_factor),
+ pnminfo->jmpbuf, _("Unsupported scale factor."));
+ precision = GIMP_PRECISION_FLOAT_LINEAR;
+ }
+ else if (pnminfo->np != 0) /* pbm's don't have a maxval field */
+ {
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ CHECK_FOR_ERROR (pnmscanner_eof (scan), pnminfo->jmpbuf,
+ _("Premature end of file."));
+
+ pnminfo->maxval = g_ascii_isdigit (*buf) ? atoi (buf) : 0;
+ CHECK_FOR_ERROR (((pnminfo->maxval<=0) || (pnminfo->maxval>65535)),
+ pnminfo->jmpbuf, _("Unsupported maximum value."));
+ if (pnminfo->maxval < 256)
+ {
+ precision = GIMP_PRECISION_U8_GAMMA;
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U16_GAMMA;
+ }
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U8_GAMMA;
+ }
+
+ /* Create a new image of the proper size and associate the filename
+ with it. */
+ image_ID = gimp_image_new_with_precision
+ (pnminfo->xres, pnminfo->yres,
+ (pnminfo->np >= 3) ? GIMP_RGB : GIMP_GRAY,
+ precision);
+
+ gimp_image_set_filename (image_ID, g_file_get_uri (file));
+
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ pnminfo->xres, pnminfo->yres,
+ (pnminfo->np >= 3 ?
+ GIMP_RGB_IMAGE : GIMP_GRAY_IMAGE),
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ pnminfo->loader (scan, pnminfo, buffer);
+
+ /* Destroy the scanner */
+ pnmscanner_destroy (scan);
+
+ g_object_unref (buffer);
+ g_free (pnminfo);
+ g_object_unref (input);
+
+ return image_ID;
+}
+
+static void
+pnm_load_ascii (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer)
+{
+ gint bpc;
+ guchar *data, *d;
+ gushort *s;
+ gint x, y, i, b;
+ gint start, end, scanlines;
+ gint np;
+ gchar buf[BUFLEN];
+ gboolean aborted = FALSE;
+
+ np = (info->np) ? (info->np) : 1;
+
+ if (info->maxval > 255)
+ bpc = 2;
+ else
+ bpc = 1;
+
+ /* No overflow as long as gimp_tile_height() < 1365 = 2^(31 - 18) / 6 */
+ data = g_new (guchar, gimp_tile_height () * info->xres * np * bpc);
+
+ /* Buffer reads to increase performance */
+ pnmscanner_createbuffer (scan, 4096);
+
+ for (y = 0; y < info->yres; y += scanlines)
+ {
+ start = y;
+ end = y + gimp_tile_height ();
+ end = MIN (end, info->yres);
+
+ scanlines = end - start;
+
+ d = data;
+ s = (gushort *)d;
+
+ for (i = 0; i < scanlines; i++)
+ for (x = 0; x < info->xres; x++)
+ {
+ for (b = 0; b < np; b++)
+ {
+ if (aborted)
+ {
+ d[b] = 0;
+ continue;
+ }
+
+ /* Truncated files will just have all 0's
+ at the end of the images */
+ if (pnmscanner_eof (scan))
+ {
+ g_message (_("Premature end of file."));
+ aborted = TRUE;
+
+ d[b] = 0;
+ continue;
+ }
+
+ if (info->np)
+ pnmscanner_gettoken (scan, buf, BUFLEN);
+ else
+ pnmscanner_getsmalltoken (scan, buf);
+
+ if (info->maxval == 1)
+ {
+ if (info->np)
+ d[b] = (*buf == '0') ? 0x00 : 0xff;
+ else
+ d[b] = (*buf == '0') ? 0xff : 0x00; /* invert for PBM */
+ }
+ else if (bpc > 1)
+ {
+ s[b] = (65535.0 * (((gdouble) (g_ascii_isdigit (*buf) ?
+ atoi (buf) : 0))
+ / (gdouble) (info->maxval)));
+ }
+ else
+ {
+ d[b] = (255.0 * (((gdouble) (g_ascii_isdigit (*buf) ?
+ atoi (buf) : 0))
+ / (gdouble) (info->maxval)));
+ }
+ }
+
+ if (bpc > 1)
+ {
+ s += np;
+ }
+ else
+ {
+ d += np;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y, info->xres, scanlines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) y / (double) info->yres);
+ }
+
+ g_free (data);
+
+ gimp_progress_update (1.0);
+}
+
+static void
+pnm_load_raw (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer)
+{
+ GInputStream *input;
+ gint bpc;
+ guchar *data, *d;
+ gushort *s;
+ gint x, y, i;
+ gint start, end, scanlines;
+
+ if (info->maxval > 255)
+ bpc = 2;
+ else
+ bpc = 1;
+
+ /* No overflow as long as gimp_tile_height() < 1365 = 2^(31 - 18) / 6 */
+ data = g_new (guchar, gimp_tile_height () * info->xres * info->np * bpc);
+
+ input = pnmscanner_input (scan);
+
+ for (y = 0; y < info->yres; y += scanlines)
+ {
+ start = y;
+ end = y + gimp_tile_height ();
+ end = MIN (end, info->yres);
+ scanlines = end - start;
+ d = data;
+ s = (gushort *)data;
+
+ for (i = 0; i < scanlines; i++)
+ {
+ gsize bytes_read;
+ GError *error = NULL;
+
+ if (g_input_stream_read_all (input, d, info->xres * info->np * bpc,
+ &bytes_read, NULL, &error))
+ {
+ CHECK_FOR_ERROR (info->xres * info->np * bpc != bytes_read,
+ info->jmpbuf,
+ _("Premature end of file."));
+ }
+ else
+ {
+ CHECK_FOR_ERROR (FALSE, info->jmpbuf, "%s", error->message);
+ }
+
+ if (bpc > 1)
+ {
+ for (x = 0; x < info->xres * info->np; x++)
+ {
+ int v;
+
+ v = *d++ << 8;
+ v += *d++;
+
+ s[x] = MIN (v, info->maxval); /* guard against overflow */
+ s[x] = 65535.0 * (gdouble) v / (gdouble) info->maxval;
+ }
+ s += info->xres * info->np;
+ }
+ else
+ {
+ if (info->maxval != 255) /* Normalize if needed */
+ {
+ for (x = 0; x < info->xres * info->np; x++)
+ {
+ d[x] = MIN (d[x], info->maxval); /* guard against overflow */
+ d[x] = 255.0 * (gdouble) d[x] / (gdouble) info->maxval;
+ }
+ }
+
+ d += info->xres * info->np;
+ }
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, y, info->xres, scanlines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) y / (double) info->yres);
+ }
+
+ g_free (data);
+
+ gimp_progress_update (1.0);
+}
+
+static void
+pnm_load_rawpbm (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer)
+{
+ GInputStream *input;
+ guchar *buf;
+ guchar curbyte;
+ guchar *data, *d;
+ gint x, y, i;
+ gint start, end, scanlines;
+ gint rowlen, bufpos;
+
+ input = pnmscanner_input (scan);
+
+ rowlen = (int)ceil ((double)(info->xres)/8.0);
+ data = g_new (guchar, gimp_tile_height () * info->xres);
+ buf = g_new (guchar, rowlen);
+
+ for (y = 0; y < info->yres; y += scanlines)
+ {
+ start = y;
+ end = y + gimp_tile_height ();
+ end = MIN (end, info->yres);
+ scanlines = end - start;
+ d = data;
+
+ for (i = 0; i < scanlines; i++)
+ {
+ gsize bytes_read;
+ GError *error = NULL;
+
+ if (g_input_stream_read_all (input, buf, rowlen,
+ &bytes_read, NULL, &error))
+ {
+ CHECK_FOR_ERROR (rowlen != bytes_read,
+ info->jmpbuf,
+ _("Premature end of file."));
+ }
+ else
+ {
+ CHECK_FOR_ERROR (FALSE, info->jmpbuf, "%s", error->message);
+ }
+
+ bufpos = 0;
+ curbyte = buf[0];
+
+ for (x = 0; x < info->xres; x++)
+ {
+ if ((x % 8) == 0)
+ curbyte = buf[bufpos++];
+ d[x] = (curbyte & 0x80) ? 0x00 : 0xff;
+ curbyte <<= 1;
+ }
+
+ d += info->xres;
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y, info->xres, scanlines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) y / (double) info->yres);
+ }
+
+ g_free (buf);
+ g_free (data);
+
+ gimp_progress_update (1.0);
+}
+
+static void
+pnm_load_rawpfm (PNMScanner *scan,
+ PNMInfo *info,
+ GeglBuffer *buffer)
+{
+ GInputStream *input;
+ gfloat *data;
+ gint x, y;
+ gboolean swap_byte_order;
+
+ swap_byte_order =
+ (info->scale_factor >= 0.0) ^ (G_BYTE_ORDER == G_BIG_ENDIAN);
+
+ data = g_new (gfloat, info->xres * info->np);
+
+ input = pnmscanner_input (scan);
+
+ for (y = info->yres - 1; y >= 0; y--)
+ {
+ gsize bytes_read;
+ GError *error = NULL;
+
+ if (g_input_stream_read_all (input, data,
+ info->xres * info->np * sizeof (float),
+ &bytes_read, NULL, &error))
+ {
+ CHECK_FOR_ERROR
+ (info->xres * info->np * sizeof (float) != bytes_read,
+ info->jmpbuf, _("Premature end of file."));
+ }
+ else
+ {
+ CHECK_FOR_ERROR (FALSE, info->jmpbuf, "%s", error->message);
+ }
+
+ for (x = 0; x < info->xres * info->np; x++)
+ {
+ if (swap_byte_order)
+ {
+ union { gfloat f; guint32 i; } v;
+
+ v.f = data[x];
+ v.i = GUINT32_SWAP_LE_BE (v.i);
+ data[x] = v.f;
+ }
+
+ /* let's see if this is what people want, the PFM specs are a
+ * little vague about what the scale factor should be used
+ * for */
+ data[x] *= fabsf (info->scale_factor);
+ /* Keep values smaller than zero. That is in line with what the
+ * TIFF loader does. If the user doesn't want the negative numbers
+ * he has to get rid of them afterwards */
+ /* data[x] = fmaxf (0.0f, fminf (FLT_MAX, data[x])); */
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, y, info->xres, 1), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ if (y % 32 == 0)
+ gimp_progress_update ((double) (info->yres - y) / (double) info->yres);
+ }
+
+ g_free (data);
+
+ gimp_progress_update (1.0);
+}
+
+static gboolean
+output_write (GOutputStream *output,
+ gconstpointer buffer,
+ gsize count,
+ GError **error)
+{
+ return g_output_stream_write_all (output, buffer, count, NULL, NULL, error);
+}
+
+/* Writes out mono raw rows */
+static gboolean
+pnmsaverow_raw_pbm (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ gint b, p = 0;
+ gchar *rbcur = ri->rowbuf;
+ gint32 len = (gint) ceil ((gdouble) (ri->xres) / 8.0);
+
+ for (b = 0; b < len; b++) /* each output byte */
+ {
+ gint i;
+
+ rbcur[b] = 0;
+
+ for (i = 0; i < 8; i++) /* each bit in this byte */
+ {
+ if (p >= ri->xres)
+ break;
+
+ if (data[p] != ri->zero_is_black)
+ rbcur[b] |= (char) (1 << (7 - i));
+
+ p++;
+ }
+ }
+
+ return output_write (ri->output, ri->rowbuf, len, error);
+}
+
+/* Writes out mono ascii rows */
+static gboolean
+pnmsaverow_ascii_pbm (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ static gint line_len = 0; /* ascii pbm lines must be <= 70 chars long */
+ gint32 len = 0;
+ gint i;
+ gchar *rbcur = ri->rowbuf;
+
+ for (i = 0; i < ri->xres; i++)
+ {
+ if (line_len > 69)
+ {
+ rbcur[i] = '\n';
+ line_len = 0;
+ len++;
+ rbcur++;
+ }
+
+ if (data[i] == ri->zero_is_black)
+ rbcur[i] = '0';
+ else
+ rbcur[i] = '1';
+
+ line_len++;
+ len++;
+ }
+
+ *(rbcur+i) = '\n';
+
+ return output_write (ri->output, ri->rowbuf, len, error);
+}
+
+/* Writes out RGB and grayscale raw rows */
+static gboolean
+pnmsaverow_raw (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ gint i;
+ if (ri->bpc == 2)
+ {
+ gushort *d = (gushort *)data;
+ for (i = 0; i < ri->xres * ri->np; i++)
+ {
+ *d = g_htons(*d);
+ d++;
+ }
+ }
+ return output_write (ri->output, data, ri->xres * ri->np * ri->bpc, error);
+}
+
+/* Writes out RGB and grayscale float rows */
+static gboolean
+pnmsaverow_float (PNMRowInfo *ri,
+ const float *data,
+ GError **error)
+{
+ return output_write (ri->output, data,
+ ri->xres * ri->np * sizeof (float),
+ error);
+}
+
+/* Writes out indexed raw rows */
+static gboolean
+pnmsaverow_raw_indexed (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ gint i;
+ gchar *rbcur = ri->rowbuf;
+
+ for (i = 0; i < ri->xres; i++)
+ {
+ *(rbcur++) = ri->red[*data];
+ *(rbcur++) = ri->grn[*data];
+ *(rbcur++) = ri->blu[*(data++)];
+ }
+
+ return output_write (ri->output, ri->rowbuf, ri->xres * 3, error);
+}
+
+/* Writes out RGB and grayscale ascii rows */
+static gboolean
+pnmsaverow_ascii (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ gint i;
+ gchar *rbcur = ri->rowbuf;
+ gushort *sdata = (gushort *)data;
+
+ for (i = 0; i < ri->xres * ri->np; i++)
+ {
+ if (ri->bpc == 2)
+ {
+ sprintf ((gchar *) rbcur,"%d\n", 0xffff & *(sdata++));
+ }
+ else
+ {
+ sprintf ((gchar *) rbcur,"%d\n", 0xff & *(data++));
+ }
+ rbcur += strlen (rbcur);
+ }
+
+ return output_write (ri->output, ri->rowbuf, rbcur - ri->rowbuf,
+ error);
+}
+
+/* Writes out RGB and grayscale ascii rows */
+static gboolean
+pnmsaverow_ascii_indexed (PNMRowInfo *ri,
+ guchar *data,
+ GError **error)
+{
+ gint i;
+ gchar *rbcur = ri->rowbuf;
+
+ for (i = 0; i < ri->xres; i++)
+ {
+ sprintf (rbcur, "%d\n", 0xff & ri->red[*(data)]);
+ rbcur += strlen (rbcur);
+ sprintf (rbcur, "%d\n", 0xff & ri->grn[*(data)]);
+ rbcur += strlen (rbcur);
+ sprintf (rbcur, "%d\n", 0xff & ri->blu[*(data++)]);
+ rbcur += strlen (rbcur);
+ }
+
+ return output_write (ri->output, ri->rowbuf, strlen ((char *) ri->rowbuf),
+ error);
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean pbm,
+ gboolean float_format,
+ GError **error)
+{
+ gboolean status = FALSE;
+ GOutputStream *output = NULL;
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ const gchar *header_string = NULL;
+ GimpImageType drawable_type;
+ PNMRowInfo rowinfo;
+ PNMSaverowFunc saverow = NULL;
+ guchar red[256];
+ guchar grn[256];
+ guchar blu[256];
+ gchar buf[BUFLEN];
+ gint np = 0;
+ gint xres, yres;
+ gint ypos, yend;
+ gint rowbufsize = 0;
+ gchar *comment = NULL;
+
+ /* Make sure we're not saving an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_message (_("Cannot export images with alpha channel."));
+ goto out;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ g_file_get_parse_name (file));
+
+ /* open the file */
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! output)
+ goto out;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ xres = gegl_buffer_get_width (buffer);
+ yres = gegl_buffer_get_height (buffer);
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ switch (gimp_image_get_precision (image_ID))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ rowinfo.bpc = 1;
+ break;
+ default:
+ rowinfo.bpc = 2;
+ break;
+ }
+
+ /* write out magic number */
+ if (!float_format && !psvals.raw)
+ {
+ if (pbm)
+ {
+ header_string = "P1\n";
+ format = gegl_buffer_get_format (buffer);
+ np = 0;
+ rowbufsize = xres + (int) (xres / 70) + 1;
+ saverow = pnmsaverow_ascii_pbm;
+ }
+ else
+ {
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ header_string = "P2\n";
+ if (rowinfo.bpc == 1)
+ {
+ format = babl_format ("Y' u8");
+ rowbufsize = xres * 4;
+ }
+ else
+ {
+ format = babl_format ("Y' u16");
+ rowbufsize = xres * 6;
+ }
+ np = 1;
+ saverow = pnmsaverow_ascii;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ header_string = "P3\n";
+ if (rowinfo.bpc == 1)
+ {
+ format = babl_format ("R'G'B' u8");
+ rowbufsize = xres * 12;
+ }
+ else
+ {
+ format = babl_format ("R'G'B' u16");
+ rowbufsize = xres * 18;
+ }
+ np = 3;
+ saverow = pnmsaverow_ascii;
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ header_string = "P3\n";
+ format = gegl_buffer_get_format (buffer);
+ np = 1;
+ rowbufsize = xres * 12;
+ saverow = pnmsaverow_ascii_indexed;
+ break;
+
+ default:
+ g_warning ("PNM: Unknown drawable_type\n");
+ goto out;
+ }
+ }
+ }
+ else if (!float_format)
+ {
+ if (pbm)
+ {
+ header_string = "P4\n";
+ format = gegl_buffer_get_format (buffer);
+ np = 0;
+ rowbufsize = (gint) ceil ((gdouble) xres / 8.0);
+ saverow = pnmsaverow_raw_pbm;
+ }
+ else
+ {
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ header_string = "P5\n";
+ if (rowinfo.bpc == 1)
+ {
+ format = babl_format ("Y' u8");
+ rowbufsize = xres;
+ }
+ else
+ {
+ format = babl_format ("Y' u16");
+ rowbufsize = xres * 2;
+ }
+ np = 1;
+ saverow = pnmsaverow_raw;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ header_string = "P6\n";
+ if (rowinfo.bpc == 1)
+ {
+ format = babl_format ("R'G'B' u8");
+ rowbufsize = xres * 3;
+ }
+ else
+ {
+ format = babl_format ("R'G'B' u16");
+ rowbufsize = xres * 6;
+ }
+ np = 3;
+ saverow = pnmsaverow_raw;
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ header_string = "P6\n";
+ format = gegl_buffer_get_format (buffer);
+ np = 1;
+ rowbufsize = xres * 3;
+ saverow = pnmsaverow_raw_indexed;
+ break;
+
+ default:
+ g_warning ("PNM: Unknown drawable_type\n");
+ goto out;
+ }
+ }
+ }
+ else
+ {
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ header_string = "Pf\n";
+ format = babl_format ("Y float");
+ np = 1;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ header_string = "PF\n";
+ format = babl_format ("RGB float");
+ np = 3;
+ break;
+
+ default:
+ g_warning ("PFM: Unknown drawable_type\n");
+ goto out;
+ }
+ }
+
+ if (! output_write (output, header_string, strlen (header_string), error))
+ goto out;
+
+ rowinfo.zero_is_black = FALSE;
+
+ if (drawable_type == GIMP_INDEXED_IMAGE)
+ {
+ guchar *cmap;
+ gint num_colors;
+
+ cmap = gimp_image_get_colormap (image_ID, &num_colors);
+
+ if (pbm)
+ {
+ /* Test which of the two colors is white and which is black */
+ switch (num_colors)
+ {
+ case 1:
+ rowinfo.zero_is_black = (GIMP_RGB_LUMINANCE (cmap[0],
+ cmap[1],
+ cmap[2]) < 128);
+ break;
+
+ case 2:
+ rowinfo.zero_is_black = (GIMP_RGB_LUMINANCE (cmap[0],
+ cmap[1],
+ cmap[2]) <
+ GIMP_RGB_LUMINANCE (cmap[3],
+ cmap[4],
+ cmap[5]));
+ break;
+
+ default:
+ g_warning ("Images exported as PBM should be black/white");
+ break;
+ }
+ }
+ else
+ {
+ const guchar *c = cmap;
+ gint i;
+
+ for (i = 0; i < num_colors; i++)
+ {
+ red[i] = *c++;
+ grn[i] = *c++;
+ blu[i] = *c++;
+ }
+
+ rowinfo.red = red;
+ rowinfo.grn = grn;
+ rowinfo.blu = blu;
+ }
+
+ g_free (cmap);
+ }
+
+ if (!float_format)
+ {
+ /* write out comment string */
+ comment = g_strdup_printf("# Created by GIMP version %s PNM plug-in\n",
+ GIMP_VERSION);
+
+ if (! output_write (output, comment, strlen (comment), error))
+ goto out;
+ }
+
+ /* write out resolution and maxval */
+ if (pbm)
+ g_snprintf (buf, sizeof (buf), "%d %d\n", xres, yres);
+ else if (!float_format)
+ g_snprintf (buf, sizeof (buf), "%d %d\n%d\n", xres, yres,
+ rowinfo.bpc == 1 ? 255 : 65535);
+ else
+ g_snprintf (buf, sizeof (buf), "%d %d\n%s\n", xres, yres,
+ G_BYTE_ORDER == G_BIG_ENDIAN ? "1.0" : "-1.0");
+
+ if (! output_write (output, buf, strlen (buf), error))
+ goto out;
+
+ if (!float_format)
+ {
+ guchar *data;
+ guchar *d;
+ gchar *rowbuf = NULL;
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ data = g_new (guchar,
+ gimp_tile_height () * xres *
+ babl_format_get_bytes_per_pixel (format));
+
+ rowbuf = g_new (gchar, rowbufsize + 1);
+
+ rowinfo.output = output;
+ rowinfo.rowbuf = rowbuf;
+ rowinfo.xres = xres;
+ rowinfo.np = np;
+
+ d = NULL; /* only to please the compiler */
+
+ /* Write the body out */
+ for (ypos = 0; ypos < yres; ypos++)
+ {
+ if ((ypos % gimp_tile_height ()) == 0)
+ {
+ yend = ypos + gimp_tile_height ();
+ yend = MIN (yend, yres);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, ypos, xres, yend - ypos), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ d = data;
+ }
+
+ if (! saverow (&rowinfo, d, error))
+ {
+ g_free (rowbuf);
+ g_free (data);
+ goto out;
+ }
+
+ d += xres * (np ? np : 1) * rowinfo.bpc;
+
+ if (ypos % 32 == 0)
+ gimp_progress_update ((double) ypos / (double) yres);
+ }
+
+ g_free (rowbuf);
+ g_free (data);
+ }
+ else
+ {
+ /* allocate a buffer for retrieving information from the pixel
+ region */
+ gfloat *data = g_new (gfloat, xres * np);
+
+ rowinfo.output = output;
+ rowinfo.rowbuf = NULL;
+ rowinfo.xres = xres;
+ rowinfo.np = np;
+
+ /* Write the body out in reverse row order */
+ for (ypos = yres - 1; ypos >= 0; ypos--)
+ {
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, ypos, xres, 1), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (! pnmsaverow_float (&rowinfo, data, error))
+ {
+ g_free (data);
+ goto out;
+ }
+
+ if (ypos % 32 == 0)
+ gimp_progress_update ((double) (yres - ypos) / (double) yres);
+ }
+
+ g_free (data);
+ }
+
+ gimp_progress_update (1.0);
+ status = TRUE;
+
+ out:
+ if (! status)
+ {
+ GCancellable *cancellable = g_cancellable_new ();
+
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+ }
+
+ if (comment)
+ g_free (comment);
+ if (buffer)
+ g_object_unref (buffer);
+ if (output)
+ g_object_unref (output);
+
+ return status;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("PNM"), PLUG_IN_BINARY, PNM_SAVE_PROC);
+
+ /* file save type */
+ frame = gimp_int_radio_group_new (TRUE, _("Data formatting"),
+ G_CALLBACK (gimp_radio_button_update),
+ &psvals.raw, psvals.raw,
+
+ _("_Raw"), TRUE, NULL,
+ _("_ASCII"), FALSE, NULL,
+
+ NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+
+/**************** FILE SCANNER UTILITIES **************/
+
+/* pnmscanner_create ---
+ * Creates a new scanner based on a file descriptor. The
+ * look ahead buffer is one character initially.
+ */
+static PNMScanner *
+pnmscanner_create (GInputStream *input)
+{
+ PNMScanner *s;
+ gsize bytes_read;
+
+ s = g_new (PNMScanner, 1);
+
+ s->input = input;
+ s->inbuf = NULL;
+ s->eof = FALSE;
+
+ if (! g_input_stream_read_all (input, &s->cur, 1,
+ &bytes_read, NULL, NULL) ||
+ bytes_read != 1)
+ {
+ s->eof = TRUE;
+ }
+
+ return s;
+}
+
+/* pnmscanner_destroy ---
+ * Destroys a scanner and its resources. Doesn't close the fd.
+ */
+static void
+pnmscanner_destroy (PNMScanner *s)
+{
+ if (s->inbuf)
+ g_free (s->inbuf);
+
+ g_free (s);
+}
+
+/* pnmscanner_createbuffer ---
+ * Creates a buffer so we can do buffered reads.
+ */
+static void
+pnmscanner_createbuffer (PNMScanner *s,
+ gint bufsize)
+{
+ gsize bytes_read;
+
+ s->inbuf = g_new (gchar, bufsize);
+ s->inbufsize = bufsize;
+ s->inbufpos = 0;
+ s->inbufvalidsize = 0;
+
+ g_input_stream_read_all (s->input, s->inbuf, bufsize,
+ &bytes_read, NULL, NULL);
+
+ s->inbufvalidsize = bytes_read;
+}
+
+/* pnmscanner_gettoken ---
+ * Gets the next token, eating any leading whitespace.
+ */
+static void
+pnmscanner_gettoken (PNMScanner *s,
+ gchar *buf,
+ gint bufsize)
+{
+ gint ctr = 0;
+
+ pnmscanner_eatwhitespace (s);
+
+ while (! s->eof &&
+ ! g_ascii_isspace (s->cur) &&
+ (s->cur != '#') &&
+ (ctr < bufsize))
+ {
+ buf[ctr++] = s->cur;
+ pnmscanner_getchar (s);
+ }
+
+ buf[ctr] = '\0';
+}
+
+/* pnmscanner_getsmalltoken ---
+ * Gets the next char, eating any leading whitespace.
+ */
+static void
+pnmscanner_getsmalltoken (PNMScanner *s,
+ gchar *buf)
+{
+ pnmscanner_eatwhitespace (s);
+
+ if (! s->eof && ! g_ascii_isspace (s->cur) && (s->cur != '#'))
+ {
+ *buf = s->cur;
+ pnmscanner_getchar (s);
+ }
+}
+
+/* pnmscanner_getchar ---
+ * Reads a character from the input stream
+ */
+static void
+pnmscanner_getchar (PNMScanner *s)
+{
+ if (s->inbuf)
+ {
+ s->cur = s->inbuf[s->inbufpos++];
+
+ if (s->inbufpos >= s->inbufvalidsize)
+ {
+ if (s->inbufpos > s->inbufvalidsize)
+ {
+ s->eof = 1;
+ }
+ else
+ {
+ gsize bytes_read;
+
+ g_input_stream_read_all (s->input, s->inbuf, s->inbufsize,
+ &bytes_read, NULL, NULL);
+
+ s->inbufvalidsize = bytes_read;
+ }
+
+ s->inbufpos = 0;
+ }
+ }
+ else
+ {
+ gsize bytes_read;
+
+ s->eof = FALSE;
+
+ if (! g_input_stream_read_all (s->input, &s->cur, 1,
+ &bytes_read, NULL, NULL) ||
+ bytes_read != 1)
+ {
+ s->eof = TRUE;
+ }
+ }
+}
+
+/* pnmscanner_eatwhitespace ---
+ * Eats up whitespace from the input and returns when done or eof.
+ * Also deals with comments.
+ */
+static void
+pnmscanner_eatwhitespace (PNMScanner *s)
+{
+ gint state = 0;
+
+ while (!(s->eof) && (state != -1))
+ {
+ switch (state)
+ {
+ case 0: /* in whitespace */
+ if (s->cur == '#')
+ {
+ state = 1; /* goto comment */
+ pnmscanner_getchar (s);
+ }
+ else if (! g_ascii_isspace (s->cur))
+ {
+ state = -1;
+ }
+ else
+ {
+ pnmscanner_getchar (s);
+ }
+ break;
+
+ case 1: /* in comment */
+ if (s->cur == '\n')
+ state = 0; /* goto whitespace */
+ pnmscanner_getchar (s);
+ break;
+ }
+ }
+}
diff --git a/plug-ins/common/file-ps.c b/plug-ins/common/file-ps.c
new file mode 100644
index 0000000..ee432a8
--- /dev/null
+++ b/plug-ins/common/file-ps.c
@@ -0,0 +1,3940 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * PostScript file plugin
+ * PostScript writing and GhostScript interfacing code
+ * Copyright (C) 1997-98 Peter Kirchgessner
+ * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
+ *
+ * Added controls for TextAlphaBits and GraphicsAlphaBits
+ * George White <aa056@chebucto.ns.ca>
+ *
+ * Added Ascii85 encoding
+ * Austin Donnelly <austin@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Event history:
+ * V 0.90, PK, 28-Mar-97: Creation.
+ * V 0.91, PK, 03-Apr-97: Clip everything outside BoundingBox.
+ * 24-Apr-97: Multi page read support.
+ * V 1.00, PK, 30-Apr-97: PDF support.
+ * V 1.01, PK, 05-Oct-97: Parse rc-file.
+ * V 1.02, GW, 09-Oct-97: Antialiasing support.
+ * PK, 11-Oct-97: No progress bars when running non-interactive.
+ * New procedure file_ps_load_setargs to set
+ * load-arguments non-interactively.
+ * If GS_OPTIONS are not set, use at least "-dSAFER"
+ * V 1.03, nn, 20-Dec-97: Initialize some variables
+ * V 1.04, PK, 20-Dec-97: Add Encapsulated PostScript output and preview
+ * V 1.05, PK, 21-Sep-98: Write b/w-images (indexed) using image-operator
+ * V 1.06, PK, 22-Dec-98: Fix problem with writing color PS files.
+ * Ghostview may hang when displaying the files.
+ * V 1.07, PK, 14-Sep-99: Add resolution to image
+ * V 1.08, PK, 16-Jan-2000: Add PostScript-Level 2 by Austin Donnelly
+ * V 1.09, PK, 15-Feb-2000: Force showpage on EPS-files
+ * Add "RunLength" compression
+ * Fix problem with "Level 2" toggle
+ * V 1.10, PK, 15-Mar-2000: For load EPSF, allow negative Bounding Box Values
+ * Save PS: don't start lines of image data with %%
+ * to prevent problems with stupid PostScript
+ * analyzer programs (Stanislav Brabec)
+ * Add BeginData/EndData comments
+ * Save PS: Set default rotation to 0
+ * V 1.11, PK, 20-Aug-2000: Fix problem with BoundingBox recognition
+ * for Mac files.
+ * Fix problem with loop when reading not all
+ * images of a multi page file.
+ * PK, 31-Aug-2000: Load PS: Add checks for space in filename.
+ * V 1.12 PK, 19-Jun-2001: Fix problem with command line switch --
+ * (reported by Ferenc Wagner)
+ * V 1.13 PK, 07-Apr-2002: Fix problem with DOS binary EPS files
+ * V 1.14 PK, 14-May-2002: Workaround EPS files of Adb. Ill. 8.0
+ * V 1.15 PK, 04-Oct-2002: Be more accurate with using BoundingBox
+ * V 1.16 PK, 22-Jan-2004: Don't use popen(), use g_spawn_async_with_pipes()
+ * or g_spawn_sync().
+ * V 1.17 PK, 19-Sep-2004: Fix problem with interpretation of bounding box
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include <ghostscript/ierrors.h>
+#include <ghostscript/iapi.h>
+#include <ghostscript/gdevdsp.h>
+
+#define VERSIO 1.17
+static const gchar dversio[] = "v1.17 19-Sep-2004";
+
+#define LOAD_PS_PROC "file-ps-load"
+#define LOAD_EPS_PROC "file-eps-load"
+#define LOAD_PS_SETARGS_PROC "file-ps-load-setargs"
+#define LOAD_PS_THUMB_PROC "file-ps-load-thumb"
+#define SAVE_PS_PROC "file-ps-save"
+#define SAVE_EPS_PROC "file-eps-save"
+#define PLUG_IN_BINARY "file-ps"
+#define PLUG_IN_ROLE "gimp-file-ps"
+
+
+#define STR_LENGTH 64
+#define MIN_RESOLUTION 5
+#define MAX_RESOLUTION 8192
+
+/* Load info */
+typedef struct
+{
+ guint resolution; /* resolution (dpi) at which to run ghostscript */
+ guint width, height; /* desired size (ghostscript may ignore this) */
+ gboolean use_bbox; /* 0: use width/height, 1: try to use BoundingBox */
+ gchar pages[STR_LENGTH]; /* Pages to load (eg.: 1,3,5-7) */
+ gint pnm_type; /* 4: pbm, 5: pgm, 6: ppm, 7: automatic */
+ gint textalpha; /* antialiasing: 1,2, or 4 TextAlphaBits */
+ gint graphicsalpha; /* antialiasing: 1,2, or 4 GraphicsAlphaBits */
+} PSLoadVals;
+
+static PSLoadVals plvals =
+{
+ 100, /* 100 dpi */
+ 826, 1170, /* default width/height (A4) */
+ TRUE, /* try to use BoundingBox */
+ "1", /* pages to load */
+ 6, /* use ppm (color) */
+ 1, /* don't use text antialiasing */
+ 1 /* don't use graphics antialiasing */
+};
+
+/* Widgets for width and height of PostScript image to
+* be loaded, so that they can be updated when desired resolution is
+* changed
+*/
+static GtkWidget *ps_width_spinbutton;
+static GtkWidget *ps_height_spinbutton;
+
+/* Save info */
+typedef struct
+{
+ gdouble width, height; /* Size of image */
+ gdouble x_offset, y_offset; /* Offset to image on page */
+ gboolean unit_mm; /* Unit of measure (0: inch, 1: mm) */
+ gboolean keep_ratio; /* Keep aspect ratio */
+ gint rotate; /* Rotation (0, 90, 180, 270) */
+ gint level; /* PostScript Level */
+ gboolean eps; /* Encapsulated PostScript flag */
+ gboolean preview; /* Preview Flag */
+ gint preview_size; /* Preview size */
+} PSSaveVals;
+
+static PSSaveVals psvals =
+{
+ 287.0, 200.0, /* Image size (A4) */
+ 5.0, 5.0, /* Offset */
+ TRUE, /* Unit is mm */
+ TRUE, /* Keep edge ratio */
+ 0, /* Rotate */
+ 2, /* PostScript Level */
+ FALSE, /* Encapsulated PostScript flag */
+ FALSE, /* Preview flag */
+ 256 /* Preview size */
+};
+
+static const char hex[] = "0123456789abcdef";
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_ps_header (GOutputStream *output,
+ GFile *file,
+ GError **error);
+static gboolean save_ps_setup (GOutputStream *output,
+ gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint bpp,
+ GError **error);
+static gboolean save_ps_trailer (GOutputStream *output,
+ GError **error);
+
+static gboolean save_ps_preview (GOutputStream *output,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_gray (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_bw (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+static gint32 create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID);
+
+static void check_load_vals (void);
+static void check_save_vals (void);
+
+static gint page_in_list (gchar *list,
+ guint pagenum);
+
+static gint get_bbox (const gchar *filename,
+ gint *x0,
+ gint *y0,
+ gint *x1,
+ gint *y1);
+
+static FILE * ps_open (const gchar *filename,
+ const PSLoadVals *loadopt,
+ gint *llx,
+ gint *lly,
+ gint *urx,
+ gint *ury,
+ gboolean *is_epsf,
+ gchar **tmp_filename);
+
+static void ps_close (FILE *ifp,
+ gchar *tmp_filename);
+
+static gboolean skip_ps (FILE *ifp);
+
+static gint32 load_ps (const gchar *filename,
+ guint pagenum,
+ FILE *ifp,
+ gint llx,
+ gint lly,
+ gint urx,
+ gint ury);
+
+static void dither_grey (const guchar *grey,
+ guchar *bw,
+ gint npix,
+ gint linecount);
+
+
+/* Dialog-handling */
+
+static gint32 count_ps_pages (const gchar *filename);
+static gboolean load_dialog (const gchar *filename);
+static void load_pages_entry_callback (GtkWidget *widget,
+ gpointer data);
+
+static gboolean resolution_change_callback (GtkAdjustment *adjustment,
+ gpointer data);
+
+typedef struct
+{
+ GtkAdjustment *adjustment[4];
+ gint level;
+} SaveDialogVals;
+
+static gboolean save_dialog (void);
+static void save_unit_toggle_update (GtkWidget *widget,
+ gpointer data);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* The run mode */
+static GimpRunMode l_run_mode;
+
+static void compress_packbits (int nin,
+ unsigned char *src,
+ int *nout,
+ unsigned char *dst);
+
+
+static guint32 ascii85_buf = 0;
+static gint ascii85_len = 0;
+static gint ascii85_linewidth = 0;
+
+static GimpPageSelectorTarget ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+
+static void
+ascii85_init (void)
+{
+ ascii85_len = 0;
+ ascii85_linewidth = 0;
+}
+
+static gboolean
+ascii85_flush (GOutputStream *output,
+ GError **error)
+{
+ gchar c[5];
+ gint i;
+ gboolean zero_case = (ascii85_buf == 0);
+ GString *string = g_string_new (NULL);
+
+ static gint max_linewidth = 75;
+
+ for (i = 4; i >= 0; i--)
+ {
+ c[i] = (ascii85_buf % 85) + '!';
+ ascii85_buf /= 85;
+ }
+
+ /* check for special case: "!!!!!" becomes "z", but only if not
+ * at end of data. */
+ if (zero_case && (ascii85_len == 4))
+ {
+ if (ascii85_linewidth >= max_linewidth)
+ {
+ g_string_append_c (string, '\n');
+
+ ascii85_linewidth = 0;
+ }
+
+ g_string_append_c (string, 'z');
+
+ ascii85_linewidth++;
+ }
+ else
+ {
+ for (i = 0; i < ascii85_len + 1; i++)
+ {
+ if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%'))
+ {
+ g_string_append_c (string, '\n');
+
+ ascii85_linewidth = 0;
+ }
+
+ g_string_append_c (string, c[i]);
+
+ ascii85_linewidth++;
+ }
+ }
+
+ ascii85_len = 0;
+ ascii85_buf = 0;
+
+ if (string->len > 0 &&
+ ! g_output_stream_write_all (output,
+ string->str, string->len, NULL,
+ NULL, error))
+ {
+ g_string_free (string, TRUE);
+
+ return FALSE;
+ }
+
+ g_string_free (string, TRUE);
+
+ return TRUE;
+}
+
+static inline gboolean
+ascii85_out (GOutputStream *output,
+ guchar byte,
+ GError **error)
+{
+ if (ascii85_len == 4)
+ if (! ascii85_flush (output, error))
+ return FALSE;
+
+ ascii85_buf <<= 8;
+ ascii85_buf |= byte;
+ ascii85_len++;
+
+ return TRUE;
+}
+
+static gboolean
+ascii85_nout (GOutputStream *output,
+ gint n,
+ guchar *uptr,
+ GError **error)
+{
+ while (n-- > 0)
+ {
+ if (! ascii85_out (output, *uptr, error))
+ return FALSE;
+
+ uptr++;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ascii85_done (GOutputStream *output,
+ GError **error)
+{
+ if (ascii85_len)
+ {
+ /* zero any unfilled buffer portion, then flush */
+ ascii85_buf <<= (8 * (4 - ascii85_len));
+
+ if (! ascii85_flush (output, error))
+ return FALSE;
+ }
+
+ if (! print (output, error, "~>\n"))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+compress_packbits (int nin,
+ unsigned char *src,
+ int *nout,
+ unsigned char *dst)
+
+{
+ unsigned char c;
+ int nrepeat, nliteral;
+ unsigned char *run_start;
+ unsigned char *start_dst = dst;
+ unsigned char *last_literal = NULL;
+
+ for (;;)
+ {
+ if (nin <= 0) break;
+
+ run_start = src;
+ c = *run_start;
+
+ /* Search repeat bytes */
+ if ((nin > 1) && (c == src[1]))
+ {
+ nrepeat = 1;
+ nin -= 2;
+ src += 2;
+ while ((nin > 0) && (c == *src))
+ {
+ nrepeat++;
+ src++;
+ nin--;
+ if (nrepeat == 127) break; /* Maximum repeat */
+ }
+
+ /* Add two-byte repeat to last literal run ? */
+ if ( (nrepeat == 1)
+ && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128))
+ {
+ *last_literal += 2;
+ *(dst++) = c;
+ *(dst++) = c;
+ continue;
+ }
+
+ /* Add repeat run */
+ *(dst++) = (unsigned char)((-nrepeat) & 0xff);
+ *(dst++) = c;
+ last_literal = NULL;
+ continue;
+ }
+ /* Search literal bytes */
+ nliteral = 1;
+ nin--;
+ src++;
+
+ for (;;)
+ {
+ if (nin <= 0) break;
+
+ if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */
+ break;
+
+ nliteral++;
+ nin--;
+ src++;
+ if (nliteral == 128) break; /* Maximum literal run */
+ }
+
+ /* Could be added to last literal run ? */
+ if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128))
+ {
+ *last_literal += nliteral;
+ }
+ else
+ {
+ last_literal = dst;
+ *(dst++) = (unsigned char)(nliteral-1);
+ }
+ while (nliteral-- > 0) *(dst++) = *(run_start++);
+ }
+ *nout = dst - start_dst;
+}
+
+
+typedef struct
+{
+ goffset eol;
+ goffset begin_data;
+} PS_DATA_POS;
+
+static PS_DATA_POS ps_data_pos = { 0, 0 };
+
+static gboolean
+ps_begin_data (GOutputStream *output,
+ GError **error)
+{
+ /* %%BeginData: 123456789012 ASCII Bytes */
+ if (! print (output, error, "%s", "%%BeginData: "))
+ return FALSE;
+
+ ps_data_pos.eol = g_seekable_tell (G_SEEKABLE (output));
+
+ if (! print (output, error, "\n"))
+ return FALSE;
+
+ ps_data_pos.begin_data = g_seekable_tell (G_SEEKABLE (output));
+
+ return TRUE;
+}
+
+static gboolean
+ps_end_data (GOutputStream *output,
+ GError **error)
+{
+ goffset end_data;
+ gchar s[64];
+
+ if ((ps_data_pos.begin_data > 0) && (ps_data_pos.eol > 0))
+ {
+ end_data = g_seekable_tell (G_SEEKABLE (output));
+
+ if (end_data > 0)
+ {
+ g_snprintf (s, sizeof (s),
+ "%"G_GOFFSET_FORMAT" ASCII Bytes", end_data - ps_data_pos.begin_data);
+
+ if (! g_seekable_seek (G_SEEKABLE (output),
+ ps_data_pos.eol - strlen (s), G_SEEK_SET,
+ NULL, error))
+ return FALSE;
+
+ if (! print (output, error, "%s", s))
+ return FALSE;
+
+ if (! g_seekable_seek (G_SEEKABLE (output),
+ end_data, G_SEEK_SET,
+ NULL, error))
+ return FALSE;
+ }
+ }
+
+ if (! print (output, error, "%s\n", "%%EndData"))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef set_load_args[] =
+ {
+ { GIMP_PDB_INT32, "resolution", "Resolution to interpret image (dpi)" },
+ { GIMP_PDB_INT32, "width", "Desired width" },
+ { GIMP_PDB_INT32, "height", "Desired height" },
+ { GIMP_PDB_INT32, "check-bbox", "0: Use width/height, 1: Use BoundingBox" },
+ { GIMP_PDB_STRING, "pages", "Pages to load (e.g.: 1,3,5-7)" },
+ { GIMP_PDB_INT32, "coloring", "4: b/w, 5: grey, 6: color image, 7: automatic" },
+ { GIMP_PDB_INT32, "text-alpha-bits", "1, 2, or 4" },
+ { GIMP_PDB_INT32, "graphic-alpha-bits", "1, 2, or 4" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_FLOAT, "width", "Width of the image in PostScript file (0: use input image size)" },
+ { GIMP_PDB_FLOAT, "height", "Height of image in PostScript file (0: use input image size)" },
+ { GIMP_PDB_FLOAT, "x-offset", "X-offset to image from lower left corner" },
+ { GIMP_PDB_FLOAT, "y-offset", "Y-offset to image from lower left corner" },
+ { GIMP_PDB_INT32, "unit", "Unit for width/height/offset. 0: inches, 1: millimeters" },
+ { GIMP_PDB_INT32, "keep-ratio", "0: use width/height, 1: keep aspect ratio" },
+ { GIMP_PDB_INT32, "rotation", "0, 90, 180, 270" },
+ { GIMP_PDB_INT32, "eps-flag", "0: PostScript, 1: Encapsulated PostScript" },
+ { GIMP_PDB_INT32, "preview", "0: no preview, >0: max. size of preview" },
+ { GIMP_PDB_INT32, "level", "1: PostScript Level 1, 2: PostScript Level 2" }
+ };
+
+ gimp_install_procedure (LOAD_PS_PROC,
+ "load PostScript documents",
+ "load PostScript documents",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("PostScript document"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PS_PROC, "application/postscript");
+ gimp_register_magic_load_handler (LOAD_PS_PROC,
+ "ps",
+ "",
+ "0,string,%!,0,long,0xc5d0d3c6");
+
+ gimp_install_procedure (LOAD_EPS_PROC,
+ "load Encapsulated PostScript images",
+ "load Encapsulated PostScript images",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("Encapsulated PostScript image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_EPS_PROC, "image/x-eps");
+ gimp_register_magic_load_handler (LOAD_EPS_PROC,
+ "eps",
+ "",
+ "0,string,%!,0,long,0xc5d0d3c6");
+
+ gimp_install_procedure (LOAD_PS_SETARGS_PROC,
+ "set additional parameters for procedure file-ps-load",
+ "set additional parameters for procedure file-ps-load",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (set_load_args), 0,
+ set_load_args, NULL);
+
+ gimp_install_procedure (LOAD_PS_THUMB_PROC,
+ "Loads a small preview from a PostScript or PDF document",
+ "",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PS_PROC, LOAD_PS_THUMB_PROC);
+ gimp_register_thumbnail_loader (LOAD_EPS_PROC, LOAD_PS_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PS_PROC,
+ "export image as PostScript document",
+ "PostScript exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("PostScript document"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PS_PROC, "application/postscript");
+ gimp_register_file_handler_uri (SAVE_PS_PROC);
+ gimp_register_save_handler (SAVE_PS_PROC, "ps", "");
+
+ gimp_install_procedure (SAVE_EPS_PROC,
+ "export image as Encapsulated PostScript image",
+ "PostScript exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner <peter@kirchgessner.net>",
+ "Peter Kirchgessner",
+ dversio,
+ N_("Encapsulated PostScript image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_EPS_PROC, "application/x-eps");
+ gimp_register_file_handler_uri (SAVE_EPS_PROC);
+ gimp_register_save_handler (SAVE_EPS_PROC, "eps", "");
+}
+
+static void
+ps_set_save_size (PSSaveVals *vals,
+ gint32 image_ID)
+{
+ gdouble xres, yres, factor, iw, ih;
+ guint width, height;
+ GimpUnit unit;
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+
+ if ((xres < 1e-5) || (yres < 1e-5))
+ xres = yres = 72.0;
+
+ /* Calculate size of image in inches */
+ width = gimp_image_width (image_ID);
+ height = gimp_image_height (image_ID);
+ iw = width / xres;
+ ih = height / yres;
+
+ unit = gimp_image_get_unit (image_ID);
+ factor = gimp_unit_get_factor (unit);
+
+ if (factor == 0.0254 ||
+ factor == 0.254 ||
+ factor == 2.54 ||
+ factor == 25.4)
+ {
+ vals->unit_mm = TRUE;
+ }
+
+ if (vals->unit_mm)
+ {
+ iw *= 25.4;
+ ih *= 25.4;
+ }
+
+ vals->width = iw;
+ vals->height = ih;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID = -1;
+ gint32 drawable_ID = -1;
+ gint32 orig_image_ID = -1;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ l_run_mode = run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PS_PROC) == 0 ||
+ strcmp (name, LOAD_EPS_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+
+ if (! load_dialog (param[1].data.d_string))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ else /* Get additional interpretation arguments */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PS_PROC, &plvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ check_load_vals ();
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ /* Store plvals data */
+ if (status == GIMP_PDB_SUCCESS)
+ gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
+ }
+ else if (strcmp (name, LOAD_PS_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gint size = param[1].data.d_int32;
+
+ /* We should look for an embedded preview but for now we
+ * just load the document at a small resolution and the
+ * first page only.
+ */
+
+ plvals.resolution = size / 4;
+ plvals.width = size;
+ plvals.height = size;
+ strncpy (plvals.pages, "1", sizeof (plvals.pages) - 1);
+
+ check_load_vals ();
+ image_ID = load_image (param[0].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PS_PROC) == 0 ||
+ strcmp (name, SAVE_EPS_PROC) == 0)
+ {
+ psvals.eps = strcmp (name, SAVE_PS_PROC);
+
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID,
+ psvals.eps ? "EPS" : "PostScript",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+
+ ps_set_save_size (&psvals, orig_image_ID);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 15)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ psvals.width = param[5].data.d_float;
+ psvals.height = param[6].data.d_float;
+ psvals.x_offset = param[7].data.d_float;
+ psvals.y_offset = param[8].data.d_float;
+ psvals.unit_mm = (param[9].data.d_int32 != 0);
+ psvals.keep_ratio = (param[10].data.d_int32 != 0);
+ psvals.rotate = param[11].data.d_int32;
+ psvals.eps = (param[12].data.d_int32 != 0);
+ psvals.preview = (param[13].data.d_int32 != 0);
+ psvals.preview_size = param[13].data.d_int32;
+ psvals.level = param[14].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &psvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if ((psvals.width == 0.0) || (psvals.height == 0.0))
+ ps_set_save_size (&psvals, orig_image_ID);
+
+ check_save_vals ();
+
+ if (save_image (g_file_new_for_uri (param[3].data.d_string),
+ image_ID, drawable_ID,
+ &error))
+ {
+ /* Store psvals data */
+ gimp_set_data (name, &psvals, sizeof (PSSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else if (strcmp (name, LOAD_PS_SETARGS_PROC) == 0)
+ {
+ /* Make sure all the arguments are there! */
+ if (nparams != 8)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ plvals.resolution = param[0].data.d_int32;
+ plvals.width = param[1].data.d_int32;
+ plvals.height = param[2].data.d_int32;
+ plvals.use_bbox = param[3].data.d_int32;
+ if (param[4].data.d_string != NULL)
+ strncpy (plvals.pages, param[4].data.d_string,
+ sizeof (plvals.pages));
+ else
+ plvals.pages[0] = '\0';
+ plvals.pages[sizeof (plvals.pages) - 1] = '\0';
+ plvals.pnm_type = param[5].data.d_int32;
+ plvals.textalpha = param[6].data.d_int32;
+ plvals.graphicsalpha = param[7].data.d_int32;
+ check_load_vals ();
+
+ gimp_set_data (LOAD_PS_PROC, &plvals, sizeof (PSLoadVals));
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 image_ID = 0;
+ gint32 *image_list, *nl;
+ guint page_count;
+ FILE *ifp;
+ gchar *temp;
+ gint llx, lly, urx, ury;
+ gint k, n_images, max_images, max_pagenum;
+ gboolean is_epsf;
+ GdkPixbuf *pixbuf = NULL;
+ gchar *tmp_filename = NULL;
+
+#ifdef PS_DEBUG
+ g_print ("load_image:\n resolution = %d\n", plvals.resolution);
+ g_print (" %dx%d pixels\n", plvals.width, plvals.height);
+ g_print (" BoundingBox: %d\n", plvals.use_bbox);
+ g_print (" Coloring: %d\n", plvals.pnm_type);
+ g_print (" TextAlphaBits: %d\n", plvals.textalpha);
+ g_print (" GraphicsAlphaBits: %d\n", plvals.graphicsalpha);
+#endif
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Try to see if PostScript file is available */
+ ifp = g_fopen (filename, "r");
+ if (ifp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+ fclose (ifp);
+
+ ifp = ps_open (filename, &plvals, &llx, &lly, &urx, &ury, &is_epsf, &tmp_filename);
+ if (!ifp)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR,
+ _("Could not interpret PostScript file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ image_list = g_new (gint32, 10);
+ n_images = 0;
+ max_images = 10;
+
+ max_pagenum = 9999; /* Try to get the maximum pagenumber to read */
+ if (is_epsf)
+ {
+ max_pagenum = 1;
+ /* Use pixbuf to load transparent EPS as PNGs */
+ pixbuf = gdk_pixbuf_new_from_file (tmp_filename, error);
+ if (! pixbuf)
+ return -1;
+ }
+
+ if (!page_in_list (plvals.pages, max_pagenum)) /* Is there a limit in list ? */
+ {
+ max_pagenum = -1;
+ for (temp = plvals.pages; *temp != '\0'; temp++)
+ {
+ if ((*temp < '0') || (*temp > '9'))
+ continue; /* Search next digit */
+ sscanf (temp, "%d", &k);
+ if (k > max_pagenum)
+ max_pagenum = k;
+ while ((*temp >= '0') && (*temp <= '9'))
+ temp++;
+ temp--;
+ }
+
+ if (max_pagenum < 1)
+ max_pagenum = 9999;
+ }
+
+ /* Load all images */
+ for (page_count = 1; page_count <= max_pagenum; page_count++)
+ {
+ if (page_in_list (plvals.pages, page_count))
+ {
+ image_ID = load_ps (filename, page_count, ifp, llx, lly, urx, ury);
+ if (image_ID == -1)
+ break;
+
+ gimp_image_set_resolution (image_ID,
+ (gdouble) plvals.resolution,
+ (gdouble) plvals.resolution);
+
+ if (n_images == max_images)
+ {
+ nl = (gint32 *) g_realloc (image_list,
+ (max_images+10)*sizeof (gint32));
+ if (nl == NULL) break;
+ image_list = nl;
+ max_images += 10;
+ }
+ image_list[n_images++] = image_ID;
+ }
+ else /* Skip an image */
+ {
+ image_ID = -1;
+ if (! skip_ps (ifp))
+ break;
+ }
+ }
+
+ ps_close (ifp, tmp_filename);
+
+ /* EPS are now imported using pngalpha, so they can be converted
+ * to a layer with gimp_layer_new_from_pixbuf () and exported at
+ * this part of the loading process
+ */
+ if (is_epsf)
+ {
+ gint32 layer;
+
+ image_ID = gimp_image_new (urx, ury, GIMP_RGB);
+
+ gimp_image_undo_disable (image_ID);
+
+ gimp_image_set_filename (image_ID, filename);
+ gimp_image_set_resolution (image_ID,
+ plvals.resolution,
+ plvals.resolution);
+
+ layer = gimp_layer_new_from_pixbuf (image_ID, _("Rendered EPS"), pixbuf,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID),
+ 0.0, 1.0);
+ gimp_image_insert_layer (image_ID, layer, -1, 0);
+
+ gimp_image_undo_enable (image_ID);
+
+ g_free (image_list);
+ g_object_unref (pixbuf);
+
+ return image_ID;
+ }
+
+ if (ps_pagemode == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
+ {
+ for (k = 0; k < n_images; k++)
+ {
+ gchar *name;
+
+ if (k == 0)
+ {
+ image_ID = image_list[0];
+
+ name = g_strdup_printf (_("%s-pages"), filename);
+ gimp_image_set_filename (image_ID, name);
+ g_free (name);
+ }
+ else
+ {
+ gint32 current_layer;
+ gint32 tmp_ID;
+
+ tmp_ID = gimp_image_get_active_drawable (image_list[k]);
+
+ name = gimp_item_get_name (tmp_ID);
+
+ current_layer = gimp_layer_new_from_drawable (tmp_ID, image_ID);
+ gimp_item_set_name (current_layer, name);
+ gimp_image_insert_layer (image_ID, current_layer, -1, -1);
+ gimp_image_delete (image_list[k]);
+
+ g_free (name);
+ }
+ }
+
+ gimp_image_undo_enable (image_ID);
+ }
+ else
+ {
+ /* Display images in reverse order.
+ * The last will be displayed by GIMP itself
+ */
+ for (k = n_images - 1; k >= 0; k--)
+ {
+ gimp_image_undo_enable (image_list[k]);
+ gimp_image_clean_all (image_list[k]);
+
+ if (l_run_mode != GIMP_RUN_NONINTERACTIVE && k > 0)
+ gimp_display_new (image_list[k]);
+ }
+
+ image_ID = (n_images > 0) ? image_list[0] : -1;
+ }
+
+ g_free (image_list);
+
+ return image_ID;
+}
+
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GCancellable *cancellable;
+ GimpImageType drawable_type;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("PostScript export cannot handle images with alpha channels"));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_GRAY_IMAGE:
+ case GIMP_RGB_IMAGE:
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Cannot operate on unknown image types."));
+ return FALSE;
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (! save_ps_header (output, file, error))
+ goto fail;
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ if (! save_index (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ if (! save_gray (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ if (! save_rgb (output, image_ID, drawable_ID, error))
+ goto fail;
+ break;
+
+ default:
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (! save_ps_trailer (output, error))
+ goto fail;
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_object_unref (output);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+
+ g_object_unref (output);
+ g_object_unref (cancellable);
+
+ return FALSE;
+}
+
+
+/* Check (and correct) the load values plvals */
+static void
+check_load_vals (void)
+{
+ if (plvals.resolution < MIN_RESOLUTION)
+ plvals.resolution = MIN_RESOLUTION;
+ else if (plvals.resolution > MAX_RESOLUTION)
+ plvals.resolution = MAX_RESOLUTION;
+
+ if (plvals.width < 2)
+ plvals.width = 2;
+ if (plvals.height < 2)
+ plvals.height = 2;
+ plvals.use_bbox = (plvals.use_bbox != 0);
+ if (plvals.pages[0] == '\0')
+ strncpy (plvals.pages, "1-99", sizeof (plvals.pages) - 1);
+ if ((plvals.pnm_type < 4) || (plvals.pnm_type > 7))
+ plvals.pnm_type = 6;
+ if ( (plvals.textalpha != 1) && (plvals.textalpha != 2)
+ && (plvals.textalpha != 4))
+ plvals.textalpha = 1;
+ if ( (plvals.graphicsalpha != 1) && (plvals.graphicsalpha != 2)
+ && (plvals.graphicsalpha != 4))
+ plvals.graphicsalpha = 1;
+}
+
+
+/* Check (and correct) the save values psvals */
+static void
+check_save_vals (void)
+{
+ int i;
+
+ i = psvals.rotate;
+ if ((i != 0) && (i != 90) && (i != 180) && (i != 270))
+ psvals.rotate = 90;
+ if (psvals.preview_size <= 0)
+ psvals.preview = FALSE;
+}
+
+
+/* Check if a page is in a given list */
+static gint
+page_in_list (gchar *list,
+ guint page_num)
+{
+ char tmplist[STR_LENGTH], *c0, *c1;
+ int state, start_num, end_num;
+#define READ_STARTNUM 0
+#define READ_ENDNUM 1
+#define CHK_LIST(a,b,c) {int low=(a),high=(b),swp; \
+ if ((low>0) && (high>0)) { \
+ if (low>high) {swp=low; low=high; high=swp;} \
+ if ((low<=(c))&&(high>=(c))) return (1); } }
+
+ if ((list == NULL) || (*list == '\0'))
+ return 1;
+
+ strncpy (tmplist, list, STR_LENGTH);
+ tmplist[STR_LENGTH-1] = '\0';
+
+ c0 = c1 = tmplist;
+ while (*c1) /* Remove all whitespace and break on unsupported characters */
+ {
+ if ((*c1 >= '0') && (*c1 <= '9'))
+ {
+ *(c0++) = *c1;
+ }
+ else if ((*c1 == '-') || (*c1 == ','))
+ { /* Try to remove double occurrences of these characters */
+ if (c0 == tmplist)
+ {
+ *(c0++) = *c1;
+ }
+ else
+ {
+ if (*(c0-1) != *c1)
+ *(c0++) = *c1;
+ }
+ }
+ else
+ break;
+
+ c1++;
+ }
+
+ if (c0 == tmplist)
+ return 1;
+
+ *c0 = '\0';
+
+ /* Now we have a comma separated list like 1-4-1,-3,1- */
+
+ start_num = end_num = -1;
+ state = READ_STARTNUM;
+ for (c0 = tmplist; *c0 != '\0'; c0++)
+ {
+ switch (state)
+ {
+ case READ_STARTNUM:
+ if (*c0 == ',')
+ {
+ if ((start_num > 0) && (start_num == (int)page_num))
+ return -1;
+ start_num = -1;
+ }
+ else if (*c0 == '-')
+ {
+ if (start_num < 0) start_num = 1;
+ state = READ_ENDNUM;
+ }
+ else /* '0' - '9' */
+ {
+ if (start_num < 0) start_num = 0;
+ start_num *= 10;
+ start_num += *c0 - '0';
+ }
+ break;
+
+ case READ_ENDNUM:
+ if (*c0 == ',')
+ {
+ if (end_num < 0) end_num = 9999;
+ CHK_LIST (start_num, end_num, (int)page_num);
+ start_num = end_num = -1;
+ state = READ_STARTNUM;
+ }
+ else if (*c0 == '-')
+ {
+ CHK_LIST (start_num, end_num, (int)page_num);
+ start_num = end_num;
+ end_num = -1;
+ }
+ else /* '0' - '9' */
+ {
+ if (end_num < 0) end_num = 0;
+ end_num *= 10;
+ end_num += *c0 - '0';
+ }
+ break;
+ }
+ }
+ if (state == READ_STARTNUM)
+ {
+ if (start_num > 0)
+ return (start_num == (int) page_num);
+ }
+ else
+ {
+ if (end_num < 0) end_num = 9999;
+ CHK_LIST (start_num, end_num, (int)page_num);
+ }
+
+ return 0;
+#undef CHK_LIST
+}
+
+
+/* A function like fgets, but treats single CR-character as line break. */
+/* As a line break the newline-character is returned. */
+static char *psfgets (char *s, int size, FILE *stream)
+
+{
+ int c;
+ char *sptr = s;
+
+ if (size <= 0)
+ return NULL;
+
+ if (size == 1)
+ {
+ *s = '\0';
+ return NULL;
+ }
+
+ c = getc (stream);
+ if (c == EOF)
+ return NULL;
+
+ for (;;)
+ {
+ /* At this point we have space in sptr for at least two characters */
+ if (c == '\n') /* Got end of line (UNIX line end) ? */
+ {
+ *(sptr++) = '\n';
+ break;
+ }
+ else if (c == '\r') /* Got a carriage return. Check next character */
+ {
+ c = getc (stream);
+ if ((c == EOF) || (c == '\n')) /* EOF or DOS line end ? */
+ {
+ *(sptr++) = '\n'; /* Return UNIX line end */
+ break;
+ }
+ else /* Single carriage return. Return UNIX line end. */
+ {
+ ungetc (c, stream); /* Save the extra character */
+ *(sptr++) = '\n';
+ break;
+ }
+ }
+ else /* no line end character */
+ {
+ *(sptr++) = (char)c;
+ size--;
+ }
+ if (size == 1)
+ break; /* Only space for the nul-character ? */
+
+ c = getc (stream);
+ if (c == EOF)
+ break;
+ }
+
+ *sptr = '\0';
+
+ return s;
+}
+
+
+/* Get the BoundingBox of a PostScript file. On success, 0 is returned. */
+/* On failure, -1 is returned. */
+static gint
+get_bbox (const gchar *filename,
+ gint *x0,
+ gint *y0,
+ gint *x1,
+ gint *y1)
+{
+ char line[1024], *src;
+ FILE *ifp;
+ int retval = -1;
+
+ ifp = g_fopen (filename, "rb");
+ if (ifp == NULL)
+ return -1;
+
+ for (;;)
+ {
+ if (psfgets (line, sizeof (line)-1, ifp) == NULL) break;
+ if ((line[0] != '%') || (line[1] != '%')) continue;
+ src = &(line[2]);
+ while ((*src == ' ') || (*src == '\t')) src++;
+ if (strncmp (src, "BoundingBox", 11) != 0) continue;
+ src += 11;
+ while ((*src == ' ') || (*src == '\t') || (*src == ':')) src++;
+ if (strncmp (src, "(atend)", 7) == 0) continue;
+ if (sscanf (src, "%d%d%d%d", x0, y0, x1, y1) == 4)
+ retval = 0;
+ break;
+ }
+ fclose (ifp);
+
+ return retval;
+}
+
+/* Open the PostScript file. On failure, NULL is returned. */
+/* The filepointer returned will give a PNM-file generated */
+/* by the PostScript-interpreter. */
+static FILE *
+ps_open (const gchar *filename,
+ const PSLoadVals *loadopt,
+ gint *llx,
+ gint *lly,
+ gint *urx,
+ gint *ury,
+ gboolean *is_epsf,
+ gchar **tmp_filename)
+{
+ const gchar *driver;
+ GPtrArray *cmdA;
+ gchar **pcmdA;
+ FILE *fd_popen = NULL;
+ FILE *eps_file;
+ gint width, height;
+ gint resolution;
+ gint x0, y0, x1, y1;
+ gint offx = 0;
+ gint offy = 0;
+ gboolean is_pdf;
+ gboolean maybe_epsf = FALSE;
+ int code;
+ void *instance = NULL;
+
+ resolution = loadopt->resolution;
+ *llx = *lly = 0;
+ width = loadopt->width;
+ height = loadopt->height;
+ *urx = width - 1;
+ *ury = height - 1;
+
+ /* Check if the file is a PDF. For PDF, we can't set geometry */
+ is_pdf = FALSE;
+
+ /* Check if it is a EPS-file */
+ *is_epsf = FALSE;
+
+ eps_file = g_fopen (filename, "rb");
+
+ if (eps_file != NULL)
+ {
+ gchar hdr[512];
+
+ fread (hdr, 1, sizeof(hdr), eps_file);
+ is_pdf = (strncmp (hdr, "%PDF", 4) == 0);
+
+ if (!is_pdf) /* Check for EPSF */
+ {
+ char *adobe, *epsf;
+ int ds = 0;
+ static unsigned char doseps[5] = { 0xc5, 0xd0, 0xd3, 0xc6, 0 };
+
+ hdr[sizeof(hdr)-1] = '\0';
+ adobe = strstr (hdr, "PS-Adobe-");
+ epsf = strstr (hdr, "EPSF-");
+
+ if ((adobe != NULL) && (epsf != NULL))
+ ds = epsf - adobe;
+
+ *is_epsf = ((ds >= 11) && (ds <= 15));
+
+ /* Illustrator uses negative values in BoundingBox without marking */
+ /* files as EPSF. Try to handle that. */
+ maybe_epsf =
+ (strstr (hdr, "%%Creator: Adobe Illustrator(R) 8.0") != 0);
+
+ /* Check DOS EPS binary file */
+ if ((!*is_epsf) && (strncmp (hdr, (char *)doseps, 4) == 0))
+ *is_epsf = 1;
+ }
+
+ fclose (eps_file);
+ }
+
+ if ((!is_pdf) && (loadopt->use_bbox)) /* Try the BoundingBox ? */
+ {
+ if (get_bbox (filename, &x0, &y0, &x1, &y1) == 0)
+ {
+ if (maybe_epsf && ((x0 < 0) || (y0 < 0)))
+ *is_epsf = 1;
+
+ if (*is_epsf) /* Handle negative BoundingBox for EPSF */
+ {
+ offx = -x0; x1 += offx; x0 += offx;
+ offy = -y0; y1 += offy; y0 += offy;
+ }
+ if ((x0 >= 0) && (y0 >= 0) && (x1 > x0) && (y1 > y0))
+ {
+ *llx = (int)((x0/72.0) * resolution + 0.0001);
+ *lly = (int)((y0/72.0) * resolution + 0.0001);
+ /* Use upper bbox values as image size */
+ width = (int)((x1/72.0) * resolution + 0.5);
+ height = (int)((y1/72.0) * resolution + 0.5);
+ /* Pixel coordinates must be one less */
+ *urx = width - 1;
+ *ury = height - 1;
+ if (*urx < *llx) *urx = *llx;
+ if (*ury < *lly) *ury = *lly;
+ }
+ }
+ }
+
+ switch (loadopt->pnm_type)
+ {
+ case 4:
+ driver = "pbmraw";
+ break;
+ case 5:
+ driver = "pgmraw";
+ break;
+ case 7:
+ driver = "pnmraw";
+ break;
+ default:
+ driver = "ppmraw";
+ break;
+ }
+
+ /* For instance, the Win32 port of ghostscript doesn't work correctly when
+ * using standard output as output file.
+ * Thus, use a real output file.
+ */
+ if (*is_epsf)
+ {
+ driver = "pngalpha";
+ *tmp_filename = gimp_temp_name ("png");
+ }
+ else
+ {
+ *tmp_filename = gimp_temp_name ("pnm");
+ }
+
+ /* Build command array */
+ cmdA = g_ptr_array_new ();
+
+ g_ptr_array_add (cmdA, g_strdup (g_get_prgname ()));
+ g_ptr_array_add (cmdA, g_strdup_printf ("-sDEVICE=%s", driver));
+ g_ptr_array_add (cmdA, g_strdup_printf ("-r%d", resolution));
+
+ if (is_pdf)
+ {
+ /* Acrobat Reader honors CropBox over MediaBox, so let's match that
+ * behavior.
+ */
+ g_ptr_array_add (cmdA, g_strdup ("-dUseCropBox"));
+ }
+ else
+ {
+ /* For PDF, we can't set geometry */
+ g_ptr_array_add (cmdA, g_strdup_printf ("-g%dx%d", width, height));
+ }
+
+ /* Antialiasing not available for PBM-device */
+ if ((loadopt->pnm_type != 4) && (loadopt->textalpha != 1))
+ g_ptr_array_add (cmdA, g_strdup_printf ("-dTextAlphaBits=%d",
+ loadopt->textalpha));
+ if ((loadopt->pnm_type != 4) && (loadopt->graphicsalpha != 1))
+ g_ptr_array_add (cmdA, g_strdup_printf ("-dGraphicsAlphaBits=%d",
+ loadopt->graphicsalpha));
+ g_ptr_array_add (cmdA, g_strdup ("-q"));
+ g_ptr_array_add (cmdA, g_strdup ("-dBATCH"));
+ g_ptr_array_add (cmdA, g_strdup ("-dNOPAUSE"));
+
+ /* If no additional options specified, use at least -dSAFER */
+ if (g_getenv ("GS_OPTIONS") == NULL)
+ g_ptr_array_add (cmdA, g_strdup ("-dSAFER"));
+
+ /* Output file name */
+ g_ptr_array_add (cmdA, g_strdup_printf ("-sOutputFile=%s", *tmp_filename));
+
+ /* Offset command for gs to get image part with negative x/y-coord. */
+ if ((offx != 0) || (offy != 0))
+ {
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup_printf ("%d", offx));
+ g_ptr_array_add (cmdA, g_strdup_printf ("%d", offy));
+ g_ptr_array_add (cmdA, g_strdup ("translate"));
+ }
+
+ /* input file name */
+ g_ptr_array_add (cmdA, g_strdup ("-f"));
+ g_ptr_array_add (cmdA, g_strdup (filename));
+
+ if (*is_epsf)
+ {
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup ("showpage"));
+ }
+
+ g_ptr_array_add (cmdA, g_strdup ("-c"));
+ g_ptr_array_add (cmdA, g_strdup ("quit"));
+ g_ptr_array_add (cmdA, NULL);
+
+ pcmdA = (gchar **) cmdA->pdata;
+
+#ifdef PS_DEBUG
+ {
+ gchar **p = pcmdA;
+ g_print ("Passing args (argc=%d):\n", cmdA->len - 1);
+
+ while (*p)
+ {
+ g_print ("%s\n", *p);
+ p++;
+ }
+ }
+#endif
+
+ code = gsapi_new_instance (&instance, NULL);
+ if (code == 0) {
+ code = gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
+ code = gsapi_init_with_args (instance, cmdA->len - 1, pcmdA);
+ code = gsapi_exit (instance);
+ gsapi_delete_instance (instance);
+ }
+
+ /* Don't care about exit status of ghostscript. */
+ /* Just try to read what it wrote. */
+
+ fd_popen = g_fopen (*tmp_filename, "rb");
+
+ g_ptr_array_free (cmdA, FALSE);
+ g_strfreev (pcmdA);
+
+ return fd_popen;
+}
+
+
+/* Close the PNM-File of the PostScript interpreter */
+static void
+ps_close (FILE *ifp, gchar *tmp_filename)
+{
+ /* If a real outputfile was used, close the file and remove it. */
+ fclose (ifp);
+ g_unlink (tmp_filename);
+}
+
+
+/* Read the header of a raw PNM-file and return type (4-6) or -1 on failure */
+static gint
+read_pnmraw_type (FILE *ifp,
+ gint *width,
+ gint *height,
+ gint *maxval)
+{
+ int frst, scnd, thrd;
+ gint pnmtype;
+ gchar line[1024];
+
+ /* GhostScript may write some informational messages infront of the header. */
+ /* We are just looking at a Px\n in the input stream. */
+ frst = getc (ifp);
+ scnd = getc (ifp);
+ thrd = getc (ifp);
+ for (;;)
+ {
+ if (thrd == EOF) return -1;
+#ifdef G_OS_WIN32
+ if (thrd == '\r') thrd = getc (ifp);
+#endif
+ if ((thrd == '\n') && (frst == 'P') && (scnd >= '1') && (scnd <= '6'))
+ break;
+ frst = scnd;
+ scnd = thrd;
+ thrd = getc (ifp);
+ }
+ pnmtype = scnd - '0';
+ /* We don't use the ASCII-versions */
+ if ((pnmtype >= 1) && (pnmtype <= 3))
+ return -1;
+
+ /* Read width/height */
+ for (;;)
+ {
+ if (fgets (line, sizeof (line)-1, ifp) == NULL)
+ return -1;
+ if (line[0] != '#')
+ break;
+ }
+ if (sscanf (line, "%d%d", width, height) != 2)
+ return -1;
+
+ *maxval = 255;
+
+ if (pnmtype != 4) /* Read maxval */
+ {
+ for (;;)
+ {
+ if (fgets (line, sizeof (line)-1, ifp) == NULL)
+ return -1;
+ if (line[0] != '#')
+ break;
+ }
+ if (sscanf (line, "%d", maxval) != 1)
+ return -1;
+ }
+
+ return pnmtype;
+}
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID)
+{
+ gint32 image_ID;
+ GimpImageType gdtype;
+ gchar *tmp;
+
+ switch (type)
+ {
+ case GIMP_GRAY:
+ gdtype = GIMP_GRAY_IMAGE;
+ break;
+ case GIMP_INDEXED:
+ gdtype = GIMP_INDEXED_IMAGE;
+ break;
+ case GIMP_RGB:
+ default:
+ gdtype = GIMP_RGB_IMAGE;
+ }
+
+ image_ID = gimp_image_new_with_precision (width, height, type,
+ GIMP_PRECISION_U8_GAMMA);
+ gimp_image_undo_disable (image_ID);
+
+ tmp = g_strdup_printf ("%s-%d", filename, pagenum);
+ gimp_image_set_filename (image_ID, tmp);
+ g_free (tmp);
+
+ tmp = g_strdup_printf (_("Page %d"), pagenum);
+ *layer_ID = gimp_layer_new (image_ID, tmp, width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ g_free (tmp);
+
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+
+/* Skip PNM image generated from PostScript file. */
+/* Return TRUE on success, FALSE otherwise. */
+static gboolean
+skip_ps (FILE *ifp)
+{
+ guchar buf[8192];
+ gsize len;
+ gint pnmtype, width, height, maxval, bpl;
+
+ pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
+
+ if (pnmtype == 4) /* Portable bitmap */
+ bpl = (width + 7) / 8;
+ else if (pnmtype == 5)
+ bpl = width;
+ else if (pnmtype == 6)
+ bpl = width * 3;
+ else
+ return FALSE;
+
+ len = bpl * height;
+ while (len)
+ {
+ gsize bytes = fread (buf, 1, MIN (len, sizeof (buf)), ifp);
+
+ if (bytes < MIN (len, sizeof (buf)))
+ return FALSE;
+
+ len -= bytes;
+ }
+
+ return TRUE;
+}
+
+
+/* Load PNM image generated from PostScript file */
+static gint32
+load_ps (const gchar *filename,
+ guint pagenum,
+ FILE *ifp,
+ gint llx,
+ gint lly,
+ gint urx,
+ gint ury)
+{
+ guchar *dest;
+ guchar *data, *bitline = NULL, *byteline = NULL, *byteptr, *temp;
+ guchar bit2byte[256*8];
+ int width, height, tile_height, scan_lines, total_scan_lines;
+ int image_width, image_height;
+ int skip_left, skip_bottom;
+ int i, j, pnmtype, maxval, bpp, nread;
+ GimpImageBaseType imagetype;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer = NULL;
+ int err = 0, e;
+
+ pnmtype = read_pnmraw_type (ifp, &width, &height, &maxval);
+
+ if ((width == urx+1) && (height == ury+1)) /* gs respected BoundingBox ? */
+ {
+ skip_left = llx; skip_bottom = lly;
+ image_width = width - skip_left;
+ image_height = height - skip_bottom;
+ }
+ else
+ {
+ skip_left = skip_bottom = 0;
+ image_width = width;
+ image_height = height;
+ }
+ if (pnmtype == 4) /* Portable Bitmap */
+ {
+ imagetype = GIMP_INDEXED;
+ nread = (width+7)/8;
+ bpp = 1;
+ bitline = g_new (guchar, nread);
+ byteline = g_new (guchar, nread * 8);
+
+ /* Get an array for mapping 8 bits in a byte to 8 bytes */
+ temp = bit2byte;
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+ else if (pnmtype == 5) /* Portable Greymap */
+ {
+ imagetype = GIMP_GRAY;
+ nread = width;
+ bpp = 1;
+ byteline = g_new (guchar, nread);
+ }
+ else if (pnmtype == 6) /* Portable Pixmap */
+ {
+ imagetype = GIMP_RGB;
+ nread = width * 3;
+ bpp = 3;
+ byteline = g_new (guchar, nread);
+ }
+ else
+ return -1;
+
+ image_ID = create_new_image (filename, pagenum,
+ image_width, image_height, imagetype,
+ &layer_ID);
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * image_width * bpp);
+
+ dest = data;
+ total_scan_lines = scan_lines = 0;
+
+ if (pnmtype == 4) /* Read bitimage ? Must be mapped to indexed */
+ {
+ const guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
+
+ gimp_image_set_colormap (image_ID, BWColorMap, 2);
+
+ for (i = 0; i < height; i++)
+ {
+ e = (fread (bitline, 1, nread, ifp) != nread);
+ if (total_scan_lines >= image_height)
+ continue;
+ err |= e;
+ if (err)
+ break;
+
+ j = width; /* Map 1 byte of bitimage to 8 bytes of indexed image */
+ temp = bitline;
+ byteptr = byteline;
+ while (j >= 8)
+ {
+ memcpy (byteptr, bit2byte + *(temp++)*8, 8);
+ byteptr += 8;
+ j -= 8;
+ }
+ if (j > 0)
+ memcpy (byteptr, bit2byte + *temp*8, j);
+
+ memcpy (dest, byteline+skip_left, image_width);
+ dest += image_width;
+ scan_lines++;
+ total_scan_lines++;
+
+ if ((scan_lines == tile_height) || ((i + 1) == image_height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i-scan_lines+1,
+ image_width, scan_lines),
+ 0,
+ NULL,
+ data,
+ GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
+
+ if (err)
+ break;
+ }
+ }
+ else /* Read gray/rgb-image */
+ {
+ for (i = 0; i < height; i++)
+ {
+ e = (fread (byteline, bpp, width, ifp) != width);
+ if (total_scan_lines >= image_height)
+ continue;
+ err |= e;
+ if (err)
+ break;
+
+ memcpy (dest, byteline+skip_left*bpp, image_width*bpp);
+ dest += image_width*bpp;
+ scan_lines++;
+ total_scan_lines++;
+
+ if ((scan_lines == tile_height) || ((i + 1) == image_height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i-scan_lines+1,
+ image_width, scan_lines),
+ 0,
+ NULL,
+ data,
+ GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) image_height);
+
+ if (err)
+ break;
+ }
+ }
+ gimp_progress_update (1.0);
+
+ g_free (data);
+ g_free (byteline);
+ g_free (bitline);
+
+ if (err)
+ g_message ("EOF encountered on reading");
+
+ g_object_unref (buffer);
+
+ return (err ? -1 : image_ID);
+}
+
+
+/* Write out the PostScript file header */
+static gboolean
+save_ps_header (GOutputStream *output,
+ GFile *file,
+ GError **error)
+{
+ gchar *basename = g_path_get_basename (gimp_file_get_utf8_name (file));
+ time_t cutime = time (NULL);
+
+ if (! print (output, error,
+ "%%!PS-Adobe-3.0%s\n", psvals.eps ? " EPSF-3.0" : ""))
+ goto fail;
+
+ if (! print (output, error,
+ "%%%%Creator: GIMP PostScript file plug-in V %4.2f "
+ "by Peter Kirchgessner\n", VERSIO))
+ goto fail;
+
+ if (! print (output, error,
+ "%%%%Title: %s\n"
+ "%%%%CreationDate: %s"
+ "%%%%DocumentData: Clean7Bit\n",
+ basename, ctime (&cutime)))
+ goto fail;
+
+ if (psvals.eps || (psvals.level > 1))
+ if (! print (output, error,"%%%%LanguageLevel: 2\n"))
+ goto fail;
+
+ if (! print (output, error, "%%%%Pages: 1\n"))
+ goto fail;
+
+ g_free (basename);
+
+ return TRUE;
+
+ fail:
+
+ g_free (basename);
+
+ return FALSE;
+}
+
+
+/* Write out transformation for image */
+static gboolean
+save_ps_setup (GOutputStream *output,
+ gint32 drawable_ID,
+ gint width,
+ gint height,
+ gint bpp,
+ GError **error)
+{
+ gdouble x_offset, y_offset, x_size, y_size;
+ gdouble urx, ury;
+ gdouble width_inch, height_inch;
+ gdouble f1, f2, dx, dy;
+ gint xtrans, ytrans;
+ gint i_urx, i_ury;
+ gchar tmpbuf1[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar tmpbuf2[G_ASCII_DTOSTR_BUF_SIZE];
+
+ /* initialize */
+
+ dx = 0.0;
+ dy = 0.0;
+
+ x_offset = psvals.x_offset;
+ y_offset = psvals.y_offset;
+ width_inch = fabs (psvals.width);
+ height_inch = fabs (psvals.height);
+
+ if (psvals.unit_mm)
+ {
+ x_offset /= 25.4; y_offset /= 25.4;
+ width_inch /= 25.4; height_inch /= 25.4;
+ }
+
+ if (psvals.keep_ratio) /* Proportions to keep ? */
+ { /* Fit the image into the allowed size */
+ f1 = width_inch / width;
+ f2 = height_inch / height;
+ if (f1 < f2)
+ height_inch = width_inch * (gdouble) height / (gdouble) width;
+ else
+ width_inch = fabs (height_inch) * (gdouble) width / (gdouble) height;
+ }
+
+ if ((psvals.rotate == 0) || (psvals.rotate == 180))
+ {
+ x_size = width_inch;
+ y_size = height_inch;
+ }
+ else
+ {
+ y_size = width_inch;
+ x_size = height_inch;
+ }
+
+ /* Round up upper right corner only for non-integer values */
+ urx = (x_offset + x_size) * 72.0;
+ ury = (y_offset + y_size) * 72.0;
+ i_urx = (gint) urx;
+ i_ury = (gint) ury;
+ if (urx != (gdouble) i_urx) i_urx++; /* Check for non-integer value */
+ if (ury != (gdouble) i_ury) i_ury++;
+
+ if (! print (output, error,
+ "%%%%BoundingBox: %d %d %d %d\n"
+ "%%%%EndComments\n",
+ (gint) (x_offset * 72.0), (gint) (y_offset * 72.0), i_urx, i_ury))
+ return FALSE;
+
+ if (psvals.preview && (psvals.preview_size > 0))
+ {
+ if (! save_ps_preview (output, drawable_ID, error))
+ return FALSE;
+ }
+
+ if (! print (output, error,
+ "%%%%BeginProlog\n"
+ "%% Use own dictionary to avoid conflicts\n"
+ "10 dict begin\n"
+ "%%%%EndProlog\n"
+ "%%%%Page: 1 1\n"
+ "%% Translate for offset\n"
+ "%s %s translate\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), x_offset * 72.0),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), y_offset * 72.0)))
+ return FALSE;
+
+ /* Calculate translation to startpoint of first scanline */
+ switch (psvals.rotate)
+ {
+ case 0: dx = 0.0; dy = y_size * 72.0;
+ break;
+ case 90: dx = dy = 0.0;
+ break;
+ case 180: dx = x_size * 72.0; dy = 0.0;
+ break;
+ case 270: dx = x_size * 72.0; dy = y_size * 72.0;
+ break;
+ }
+
+ if ((dx != 0.0) || (dy != 0.0))
+ {
+ if (! print (output, error,
+ "%% Translate to begin of first scanline\n"
+ "%s %s translate\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), dx),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), dy)))
+ return FALSE;
+ }
+
+ if (psvals.rotate)
+ if (! print (output, error, "%d rotate\n", (gint) psvals.rotate))
+ return FALSE;
+
+ if (! print (output, error,
+ "%s %s scale\n",
+ g_ascii_dtostr (tmpbuf1, sizeof (tmpbuf1), 72.0 * width_inch),
+ g_ascii_dtostr (tmpbuf2, sizeof (tmpbuf2), -72.0 * height_inch)))
+ return FALSE;
+
+ /* Write the PostScript procedures to read the image */
+ if (psvals.level <= 1)
+ {
+ if (! print (output, error,
+ "%% Variable to keep one line of raster data\n"))
+ return FALSE;
+
+ if (bpp == 1)
+ {
+ if (! print (output, error,
+ "/scanline %d string def\n", (width + 7) / 8))
+ return FALSE;
+ }
+ else
+ {
+ if (! print (output, error,
+ "/scanline %d %d mul string def\n", width, bpp / 8))
+ return FALSE;
+ }
+ }
+
+ if (! print (output, error,
+ "%% Image geometry\n%d %d %d\n"
+ "%% Transformation matrix\n",
+ width, height, (bpp == 1) ? 1 : 8))
+ return FALSE;
+
+ xtrans = ytrans = 0;
+ if (psvals.width < 0.0) { width = -width; xtrans = -width; }
+ if (psvals.height < 0.0) { height = -height; ytrans = -height; }
+
+ if (! print (output, error,
+ "[ %d 0 0 %d %d %d ]\n", width, height, xtrans, ytrans))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+save_ps_trailer (GOutputStream *output,
+ GError **error)
+{
+ return print (output, error,
+ "%%%%Trailer\n"
+ "end\n%%%%EOF\n");
+}
+
+/* Do a Floyd-Steinberg dithering on a grayscale scanline. */
+/* linecount must keep the counter for the actual scanline (0, 1, 2, ...). */
+/* If linecount is less than zero, all used memory is freed. */
+
+static void
+dither_grey (const guchar *grey,
+ guchar *bw,
+ gint npix,
+ gint linecount)
+{
+ static gboolean do_init_arrays = TRUE;
+ static gint *fs_error = NULL;
+ static gint limit[1278];
+ static gint east_error[256];
+ static gint seast_error[256];
+ static gint south_error[256];
+ static gint swest_error[256];
+
+ const guchar *greyptr;
+ guchar *bwptr, mask;
+ gint *fse;
+ gint x, greyval, fse_inline;
+
+ if (linecount <= 0)
+ {
+ g_free (fs_error);
+
+ if (linecount < 0)
+ return;
+
+ fs_error = g_new0 (gint, npix + 2);
+
+ /* Initialize some arrays that speed up dithering */
+ if (do_init_arrays)
+ {
+ gint i;
+
+ do_init_arrays = FALSE;
+
+ for (i = 0, x = -511; x <= 766; i++, x++)
+ limit[i] = (x < 0) ? 0 : ((x > 255) ? 255 : x);
+
+ for (greyval = 0; greyval < 256; greyval++)
+ {
+ east_error[greyval] = (greyval < 128) ?
+ ((greyval * 79) >> 8) : (((greyval - 255) * 79) >> 8);
+ seast_error[greyval] = (greyval < 128) ?
+ ((greyval * 34) >> 8) : (((greyval - 255) * 34) >> 8);
+ south_error[greyval] = (greyval < 128) ?
+ ((greyval * 56) >> 8) : (((greyval - 255) * 56) >> 8);
+ swest_error[greyval] = (greyval < 128) ?
+ ((greyval * 12) >> 8) : (((greyval - 255) * 12) >> 8);
+ }
+ }
+ }
+
+ g_return_if_fail (fs_error != NULL);
+
+ memset (bw, 0, (npix + 7) / 8); /* Initialize with white */
+
+ greyptr = grey;
+ bwptr = bw;
+ mask = 0x80;
+
+ fse_inline = fs_error[1];
+
+ for (x = 0, fse = fs_error + 1; x < npix; x++, fse++)
+ {
+ greyval =
+ limit[*(greyptr++) + fse_inline + 512]; /* 0 <= greyval <= 255 */
+
+ if (greyval < 128)
+ *bwptr |= mask; /* Set a black pixel */
+
+ /* Error distribution */
+ fse_inline = east_error[greyval] + fse[1];
+ fse[1] = seast_error[greyval];
+ fse[0] += south_error[greyval];
+ fse[-1] += swest_error[greyval];
+
+ mask >>= 1; /* Get mask for next b/w-pixel */
+
+ if (!mask)
+ {
+ mask = 0x80;
+ bwptr++;
+ }
+ }
+}
+
+/* Write a device independent screen preview */
+static gboolean
+save_ps_preview (GOutputStream *output,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GimpImageType drawable_type;
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ guchar *bwptr, *greyptr;
+ gint width, height, x, y, nbsl, out_count;
+ gint nchar_pl = 72, src_y;
+ gdouble f1, f2;
+ guchar *grey, *bw, *src_row, *src_ptr;
+ guchar *cmap;
+ gint ncols, cind;
+
+ if (psvals.preview_size <= 0)
+ return TRUE;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ cmap = NULL;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+ switch (drawable_type)
+ {
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ cmap = gimp_image_get_colormap (gimp_item_get_image (drawable_ID),
+ &ncols);
+ format = gimp_drawable_get_format (drawable_ID);
+ break;
+
+ case GIMP_RGB_IMAGE:
+ default:
+ format = babl_format ("R'G'B' u8");
+ break;
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ /* Calculate size of preview */
+ if ((width > psvals.preview_size) ||
+ (height > psvals.preview_size))
+ {
+ f1 = (gdouble) psvals.preview_size / (gdouble) width;
+ f2 = (gdouble) psvals.preview_size / (gdouble) height;
+
+ if (f1 < f2)
+ {
+ width = psvals.preview_size;
+ height *= f1;
+ if (height <= 0)
+ height = 1;
+ }
+ else
+ {
+ height = psvals.preview_size;
+ width *= f1;
+ if (width <= 0)
+ width = 1;
+ }
+ }
+
+ nbsl = (width + 7) / 8; /* Number of bytes per scanline in bitmap */
+
+ grey = g_new (guchar, width);
+ bw = g_new (guchar, nbsl);
+ src_row = g_new (guchar, gegl_buffer_get_width (buffer) * bpp);
+
+ if (! print (output, error,
+ "%%%%BeginPreview: %d %d 1 %d\n",
+ width, height,
+ ((nbsl * 2 + nchar_pl - 1) / nchar_pl) * height))
+ goto fail;
+
+ for (y = 0; y < height; y++)
+ {
+ /* Get a scanline from the input image and scale it to the desired
+ width */
+ src_y = (y * gegl_buffer_get_height (buffer)) / height;
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, src_y,
+ gegl_buffer_get_width (buffer), 1),
+ 1.0, format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ greyptr = grey;
+ if (bpp == 3) /* RGB-image */
+ {
+ for (x = 0; x < width; x++)
+ { /* Convert to grey */
+ src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width) * 3;
+ *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
+ }
+ }
+ else if (cmap) /* Indexed image */
+ {
+ for (x = 0; x < width; x++)
+ {
+ src_ptr = src_row + ((x * gegl_buffer_get_width (buffer)) / width);
+ cind = *src_ptr; /* Get color index and convert to grey */
+ src_ptr = (cind >= ncols) ? cmap : (cmap + 3*cind);
+ *(greyptr++) = (3*src_ptr[0] + 6*src_ptr[1] + src_ptr[2]) / 10;
+ }
+ }
+ else /* Grey image */
+ {
+ for (x = 0; x < width; x++)
+ *(greyptr++) = *(src_row + ((x * gegl_buffer_get_width (buffer)) / width));
+ }
+
+ /* Now we have a grayscale line for the desired width. */
+ /* Dither it to b/w */
+ dither_grey (grey, bw, width, y);
+
+ /* Write out the b/w line */
+ out_count = 0;
+ bwptr = bw;
+ for (x = 0; x < nbsl; x++)
+ {
+ if (out_count == 0)
+ if (! print (output, error, "%% "))
+ goto fail;
+
+ if (! print (output, error, "%02x", *(bwptr++)))
+ goto fail;
+
+ out_count += 2;
+ if (out_count >= nchar_pl)
+ {
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ out_count = 0;
+ }
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ if ((y % 20) == 0)
+ gimp_progress_update ((gdouble) y / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! print (output, error, "%%%%EndPreview\n"))
+ goto fail;
+
+ dither_grey (grey, bw, width, -1);
+ g_free (src_row);
+ g_free (bw);
+ g_free (grey);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (src_row);
+ g_free (bw);
+ g_free (grey);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+}
+
+static gboolean
+save_gray (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint tile_height;
+ guchar *data;
+ guchar *src;
+ guchar *packb = NULL;
+ gboolean level2 = (psvals.level > 1);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = babl_format ("Y' u8");
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = (guchar *) g_malloc (tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 1 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop }\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
+ goto fail;
+
+ ascii85_init ();
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "image\n"))
+ goto fail;
+
+#define GET_GRAY_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_GRAY_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! print (output, error, "%c", hex[(*src) >> 4]))
+ goto fail;
+
+ if (! print (output, error, "%c", hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (((j + 1) % 39) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint nout;
+
+ compress_packbits (width, src, &nout, packb);
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ src += width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (level2)
+ {
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ if (! ps_end_data (output, error))
+ return FALSE;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_GRAY_TILE
+}
+
+static gboolean
+save_bw (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint ncols, nbsl, nwrite;
+ gint tile_height;
+ guchar *cmap, *ct;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *scanline, *dst, mask;
+ guchar *hex_scanline;
+ gboolean level2 = (psvals.level > 1);
+
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = gimp_drawable_get_format (drawable_ID);
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar, tile_height * width * bpp);
+
+ nbsl = (width + 7) / 8;
+
+ scanline = g_new (guchar, nbsl + 1);
+ hex_scanline = g_new (guchar, (nbsl + 1) * 2);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 1, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop }\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "currentfile /ASCII85Decode filter /RunLengthDecode filter\n"))
+ goto fail;
+
+ ascii85_init ();
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = g_new (guchar, ((nbsl+1) * 105) / 100 + 2);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "image\n"))
+ goto fail;
+
+#define GET_BW_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_BW_TILE (data); /* Get more data */
+
+ dst = scanline;
+ memset (dst, 0, nbsl);
+ mask = 0x80;
+
+ /* Build a bitmap for a scanline */
+ for (j = 0; j < width; j++)
+ {
+ ct = cmap + *(src++)*3;
+ if (ct[0] || ct[1] || ct[2])
+ *dst |= mask;
+
+ if (mask == 0x01)
+ {
+ mask = 0x80; dst++;
+ }
+ else
+ {
+ mask >>= 1;
+ }
+ }
+
+ if (! level2)
+ {
+ /* Convert to hexstring */
+ for (j = 0; j < nbsl; j++)
+ {
+ hex_scanline[j * 2] = (guchar) hex[scanline[j] >> 4];
+ hex_scanline[j * 2 + 1] = (guchar) hex[scanline[j] & 0x0f];
+ }
+
+ /* Write out hexstring */
+ j = nbsl * 2;
+ dst = hex_scanline;
+
+ while (j > 0)
+ {
+ nwrite = (j > 78) ? 78 : j;
+
+ if (! g_output_stream_write_all (output,
+ dst, nwrite, NULL,
+ NULL, error))
+ goto fail;
+
+ if (! print (output, error, "\n"))
+ goto fail;
+
+ j -= nwrite;
+ dst += nwrite;
+ }
+ }
+ else
+ {
+ gint nout;
+
+ compress_packbits (nbsl, scanline, &nout, packb);
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (level2)
+ {
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (hex_scanline);
+ g_free (scanline);
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (hex_scanline);
+ g_free (scanline);
+ g_free (data);
+ g_free (packb);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_BW_TILE
+}
+
+static gboolean
+save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, i, j;
+ gint ncols, bw;
+ gint tile_height;
+ guchar *cmap, *cmap_start;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *plane = NULL;
+ gchar coltab[256 * 6], *ct;
+ gboolean level2 = (psvals.level > 1);
+
+ cmap = cmap_start = gimp_image_get_colormap (image_ID, &ncols);
+
+ ct = coltab;
+ bw = 1;
+ for (j = 0; j < 256; j++)
+ {
+ if (j >= ncols)
+ {
+ memset (ct, 0, 6);
+ ct += 6;
+ }
+ else
+ {
+ bw &= ((cmap[0] == 0) && (cmap[1] == 0) && (cmap[2] == 0)) ||
+ ((cmap[0] == 255) && (cmap[1] == 255) && (cmap[2] == 255));
+
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ *(ct++) = (guchar) hex[(*cmap) >> 4];
+ *(ct++) = (guchar) hex[(*(cmap++)) & 0x0f];
+ }
+ }
+
+ if (bw)
+ return save_bw (output, image_ID, drawable_ID, error);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = gimp_drawable_get_format (drawable_ID);
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = (guchar *) g_malloc (tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop } false 3\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%% Strings to hold RGB-samples per scanline\n"
+ "/rstr %d string def\n"
+ "/gstr %d string def\n"
+ "/bstr %d string def\n",
+ width, width, width))
+ goto fail;
+
+ if (! print (output, error,
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ rstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ gstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ bstr readstring pop}\n"
+ "true 3\n"))
+ goto fail;
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = (guchar *) g_malloc ((width * 105) / 100 + 2);
+ plane = (guchar *) g_malloc (width);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "colorimage\n"))
+ goto fail;
+
+#define GET_INDEX_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_INDEX_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! g_output_stream_write_all (output,
+ coltab + (*(src++)) * 6, 6, NULL,
+ NULL, error))
+ goto fail;
+
+ if (((j + 1) % 13) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint rgb;
+
+ for (rgb = 0; rgb < 3; rgb++)
+ {
+ guchar *src_ptr = src;
+ guchar *plane_ptr = plane;
+ gint nout;
+
+ for (j = 0; j < width; j++)
+ *(plane_ptr++) = cmap_start[3 * *(src_ptr++) + rgb];
+
+ compress_packbits (width, plane, &nout, packb);
+
+ ascii85_init ();
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ src += width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_INDEX_TILE
+}
+
+static gboolean
+save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint bpp;
+ gint height, width, tile_height;
+ gint i, j;
+ guchar *data, *src;
+ guchar *packb = NULL;
+ guchar *plane = NULL;
+ gboolean level2 = (psvals.level > 1);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ format = babl_format ("R'G'B' u8");
+ bpp = babl_format_get_bytes_per_pixel (format);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar, tile_height * width * bpp);
+
+ /* Set up transformation in PostScript */
+ if (! save_ps_setup (output, drawable_ID, width, height, 3 * 8, error))
+ goto fail;
+
+ /* Write read image procedure */
+ if (! level2)
+ {
+ if (! print (output, error,
+ "{ currentfile scanline readhexstring pop } false 3\n"))
+ goto fail;
+ }
+ else
+ {
+ if (! print (output, error,
+ "%% Strings to hold RGB-samples per scanline\n"
+ "/rstr %d string def\n"
+ "/gstr %d string def\n"
+ "/bstr %d string def\n",
+ width, width, width))
+ goto fail;
+
+ if (! print (output, error,
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ rstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ gstr readstring pop}\n"
+ "{currentfile /ASCII85Decode filter /RunLengthDecode filter\
+ bstr readstring pop}\n"
+ "true 3\n"))
+ goto fail;
+
+ /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
+ packb = g_new (guchar, (width * 105) / 100 + 2);
+ plane = g_new (guchar, width);
+ }
+
+ if (! ps_begin_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "colorimage\n"))
+ goto fail;
+
+#define GET_RGB_TILE(begin) \
+ { gint scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), \
+ 1.0, format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_RGB_TILE (data); /* Get more data */
+
+ if (! level2)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Red */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Green */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*src) >> 4])) /* Blue */
+ goto fail;
+
+ if (! print (output, error, "%c",
+ hex[(*(src++)) & 0x0f]))
+ goto fail;
+
+ if (((j+1) % 13) == 0)
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+
+ if (! print (output, error, "\n"))
+ goto fail;
+ }
+ else
+ {
+ gint rgb;
+
+ for (rgb = 0; rgb < 3; rgb++)
+ {
+ guchar *src_ptr = src + rgb;
+ guchar *plane_ptr = plane;
+ gint nout;
+
+ for (j = 0; j < width; j++)
+ {
+ *(plane_ptr++) = *src_ptr;
+ src_ptr += 3;
+ }
+
+ compress_packbits (width, plane, &nout, packb);
+
+ ascii85_init ();
+
+ if (! ascii85_nout (output, nout, packb, error))
+ goto fail;
+
+ /* Write EOD of RunLengthDecode filter */
+ if (! ascii85_out (output, 128, error))
+ goto fail;
+
+ if (! ascii85_done (output, error))
+ goto fail;
+ }
+
+ src += 3 * width;
+ }
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+
+ if (! ps_end_data (output, error))
+ goto fail;
+
+ if (! print (output, error, "showpage\n"))
+ goto fail;
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+
+ fail:
+
+ g_free (data);
+ g_free (packb);
+ g_free (plane);
+
+ g_object_unref (buffer);
+
+ return FALSE;
+
+#undef GET_RGB_TILE
+}
+
+static gboolean
+print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (output, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
+
+/* Load interface functions */
+
+static gint32
+count_ps_pages (const gchar *filename)
+{
+ FILE *psfile;
+ gchar *extension;
+ gchar buf[1024];
+ gint32 num_pages = 0;
+ gint32 showpage_count = 0;
+
+ extension = strrchr (filename, '.');
+ if (extension)
+ {
+ extension = g_ascii_strdown (extension + 1, -1);
+
+ if (strcmp (extension, "eps") == 0)
+ {
+ g_free (extension);
+ return 1;
+ }
+
+ g_free (extension);
+ }
+
+ psfile = g_fopen (filename, "r");
+
+ if (psfile == NULL)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return 0;
+ }
+
+ while (num_pages == 0 && !feof (psfile))
+ {
+ fgets (buf, sizeof (buf), psfile);
+
+ if (strncmp (buf + 2, "Pages:", 6) == 0)
+ sscanf (buf + strlen ("%%Pages:"), "%d", &num_pages);
+ else if (strncmp (buf, "showpage", 8) == 0)
+ showpage_count++;
+ }
+
+ if (feof (psfile) && num_pages < 1 && showpage_count > 0)
+ num_pages = showpage_count;
+
+ fclose (psfile);
+
+ return num_pages;
+}
+
+static gboolean
+load_dialog (const gchar *filename)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *entry = NULL;
+ GtkWidget *target = NULL;
+ GtkWidget *toggle;
+ GtkWidget *selector = NULL;
+ gint32 page_count;
+ gchar *range = NULL;
+ gboolean run;
+
+ page_count = count_ps_pages (filename);
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Import from PostScript"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PS_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ if (page_count > 1)
+ {
+ selector = gimp_page_selector_new ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), selector, TRUE, TRUE, 0);
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector),
+ page_count);
+ gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector),
+ ps_pagemode);
+
+ gtk_widget_show (selector);
+
+ g_signal_connect_swapped (selector, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+ }
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Rendering */
+ frame = gimp_frame_new (_("Rendering"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* Resolution/Width/Height/Pages labels */
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.resolution,
+ MIN_RESOLUTION, MAX_RESOLUTION,
+ 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Resolution:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (resolution_change_callback),
+ &plvals.resolution);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.resolution);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.width,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 1, 10, 0);
+ ps_width_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Width:"), 0.0, 0.5,
+ ps_width_spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.width);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (plvals.height,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 1, 10, 0);
+ ps_height_spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Height:"), 0.0, 0.5,
+ ps_height_spinbutton, 1, FALSE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &plvals.height);
+
+ if (page_count == 0)
+ {
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 80, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), plvals.pages);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("Pages:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (load_pages_entry_callback),
+ NULL);
+ gimp_help_set_help_data (GTK_WIDGET (entry),
+ _("Pages to load (e.g.: 1-4 or 1,3,5-7)"), NULL);
+
+ target = gtk_combo_box_text_new ();
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
+ GIMP_PAGE_SELECTOR_TARGET_LAYERS,
+ _("Layers"));
+ gtk_combo_box_text_insert_text (GTK_COMBO_BOX_TEXT (target),
+ GIMP_PAGE_SELECTOR_TARGET_IMAGES,
+ _("Images"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (target), (int) ps_pagemode);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("Open as"), 0.0, 0.5,
+ target, 1, FALSE);
+ }
+
+ toggle = gtk_check_button_new_with_label (_("Try Bounding Box"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), plvals.use_bbox);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &plvals.use_bbox);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ /* Coloring */
+ frame = gimp_int_radio_group_new (TRUE, _("Coloring"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.pnm_type, plvals.pnm_type,
+
+ _("B/W"), 4, NULL,
+ _("Gray"), 5, NULL,
+ _("Color"), 6, NULL,
+ _("Automatic"), 7, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Text antialiasing"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.textalpha, plvals.textalpha,
+
+ C_("antialiasing", "None"), 1, NULL,
+ _("Weak"), 2, NULL,
+ _("Strong"), 4, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Graphic antialiasing"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.graphicsalpha, plvals.graphicsalpha,
+
+ C_("antialiasing", "None"), 1, NULL,
+ _("Weak"), 2, NULL,
+ _("Strong"), 4, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (selector)
+ {
+ range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
+
+ if (strlen (range) < 1)
+ {
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
+ range = gimp_page_selector_get_selected_range (GIMP_PAGE_SELECTOR (selector));
+ }
+
+ strncpy (plvals.pages, range, sizeof (plvals.pages));
+ plvals.pages[strlen (range)] = '\0';
+
+ ps_pagemode = gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (selector));
+ }
+ else if (page_count == 0)
+ {
+ ps_pagemode = gtk_combo_box_get_active (GTK_COMBO_BOX (target));
+ }
+ else
+ {
+ strncpy (plvals.pages, "1", 1);
+ plvals.pages[1] = '\0';
+ ps_pagemode = GIMP_PAGE_SELECTOR_TARGET_IMAGES;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+load_pages_entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gsize nelem = sizeof (plvals.pages);
+
+ strncpy (plvals.pages, gtk_entry_get_text (GTK_ENTRY (widget)), nelem);
+ plvals.pages[nelem-1] = '\0';
+}
+
+
+/* Save interface functions */
+
+static gboolean
+save_dialog (void)
+{
+ SaveDialogVals *vals;
+ GtkWidget *dialog;
+ GtkWidget *toggle;
+ GtkWidget *frame, *uframe;
+ GtkWidget *hbox, *vbox;
+ GtkWidget *main_vbox[2];
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint j;
+ gboolean run;
+
+ vals = g_new (SaveDialogVals, 1);
+ vals->level = (psvals.level > 1);
+
+ dialog = gimp_export_dialog_new (_("PostScript"), PLUG_IN_BINARY, SAVE_PS_PROC);
+
+ /* Main hbox */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ hbox, FALSE, FALSE, 0);
+ main_vbox[0] = main_vbox[1] = NULL;
+
+ for (j = 0; j < G_N_ELEMENTS (main_vbox); j++)
+ {
+ main_vbox[j] = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox[j], FALSE, TRUE, 0);
+ gtk_widget_show (main_vbox[j]);
+ }
+
+ /* Image Size */
+ frame = gimp_frame_new (_("Image Size"));
+ gtk_box_pack_start (GTK_BOX (main_vbox[0]), frame, FALSE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* Width/Height/X-/Y-offset labels */
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ vals->adjustment[0] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.width,
+ 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[0], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Width:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[0], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.width);
+
+ vals->adjustment[1] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.height,
+ 1e-5, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[1], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Height:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[1], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.height);
+
+ vals->adjustment[2] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.x_offset,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[2], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_X offset:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[2], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.x_offset);
+
+ vals->adjustment[3] = (GtkAdjustment *)
+ gtk_adjustment_new (psvals.y_offset,
+ 0.0, GIMP_MAX_IMAGE_SIZE, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (vals->adjustment[3], 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Y offset:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (vals->adjustment[3], "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &psvals.y_offset);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Keep aspect ratio"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.keep_ratio);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("When toggled, the resulting image will be "
+ "scaled to fit into the given size without "
+ "changing the aspect ratio."),
+ "#keep_aspect_ratio"),
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.keep_ratio);
+
+ /* Unit */
+ uframe = gimp_int_radio_group_new (TRUE, _("Unit"),
+ G_CALLBACK (save_unit_toggle_update),
+ vals, psvals.unit_mm,
+
+ _("_Inch"), FALSE, NULL,
+ _("_Millimeter"), TRUE, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox[0]), uframe, TRUE, TRUE, 0);
+ gtk_widget_show (uframe);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ /* Rotation */
+ frame = gimp_int_radio_group_new (TRUE, _("Rotation"),
+ G_CALLBACK (gimp_radio_button_update),
+ &psvals.rotate, psvals.rotate,
+
+ "_0", 0, NULL,
+ "_90", 90, NULL,
+ "_180", 180, NULL,
+ "_270", 270, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ /* Format */
+ frame = gimp_frame_new (_("Output"));
+ gtk_box_pack_start (GTK_BOX (main_vbox[1]), frame, TRUE, TRUE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_PostScript level 2"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), vals->level);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &vals->level);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Encapsulated PostScript"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.eps);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.eps);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("P_review"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), psvals.preview);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &psvals.preview);
+
+ /* Preview size label/entry */
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (psvals.preview_size,
+ 0, 1024, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Preview _size:"), 1.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &psvals.preview_size);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ psvals.level = (vals->level) ? 2 : 1;
+
+ g_free (vals);
+
+ return run;
+}
+
+static void
+save_unit_toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ SaveDialogVals *vals = (SaveDialogVals *) data;
+ gdouble factor;
+ gdouble value;
+ gint unit_mm;
+ gint i;
+
+ unit_mm = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
+ "gimp-item-data"));
+
+ psvals.unit_mm = unit_mm;
+
+ if (unit_mm)
+ factor = 25.4;
+ else
+ factor = 1.0 / 25.4;
+
+ for (i = 0; i < 4; i++)
+ {
+ value = gtk_adjustment_get_value (vals->adjustment[i]) * factor;
+
+ gtk_adjustment_set_value (vals->adjustment[i], value);
+ }
+ }
+}
+
+static gboolean
+resolution_change_callback (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ guint *old_resolution = (guint *) data;
+ gdouble ratio;
+
+ if (*old_resolution)
+ ratio = (gdouble) gtk_adjustment_get_value (adjustment) / *old_resolution;
+ else
+ ratio = 1.0;
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_width_spinbutton),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_width_spinbutton)) * ratio);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (ps_height_spinbutton),
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (ps_height_spinbutton)) * ratio);
+
+ return TRUE;
+
+}
diff --git a/plug-ins/common/file-psp.c b/plug-ins/common/file-psp.c
new file mode 100644
index 0000000..c0f3480
--- /dev/null
+++ b/plug-ins/common/file-psp.c
@@ -0,0 +1,2571 @@
+/* GIMP plug-in to load and export Paint Shop Pro files (.PSP and .TUB)
+ *
+ * Copyright (C) 1999 Tor Lillqvist
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ *
+ * Work in progress! Doesn't handle exporting yet.
+ *
+ * For a copy of the PSP file format documentation, surf to
+ * http://www.jasc.com.
+ *
+ */
+
+#define LOAD_PROC "file-psp-load"
+#define SAVE_PROC "file-psp-save"
+#define PLUG_IN_BINARY "file-psp"
+#define PLUG_IN_ROLE "gimp-file-psp"
+
+/* set to the level of debugging output you want, 0 for none */
+#define PSP_DEBUG 0
+
+#define IFDBG(level) if (PSP_DEBUG >= level)
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <zlib.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include <libgimpbase/gimpparasiteio.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Note that the upcoming PSP version 6 writes PSP file format version
+ * 4.0, but the documentation for that apparently isn't publicly
+ * available (yet). The format is luckily designed to be somwehat
+ * downward compatible, however. The semantics of many of the
+ * additional fields and block types can be relatively easily reverse
+ * engineered.
+ */
+
+/* The following was cut and pasted from the PSP file format
+ * documentation version 3.0.(Minor stylistic changes done.)
+ *
+ *
+ * To be on the safe side, here is the whole copyright notice from the
+ * specification:
+ *
+ * The Paint Shop Pro File Format Specification (the Specification) is
+ * copyright 1998 by Jasc Software, Inc. Jasc grants you a
+ * nonexclusive license to use the Specification for the sole purposes
+ * of developing software products(s) incorporating the
+ * Specification. You are also granted the right to identify your
+ * software product(s) as incorporating the Paint Shop Pro Format
+ * (PSP) provided that your software in incorporating the
+ * Specification complies with the terms, definitions, constraints and
+ * specifications contained in the Specification and subject to the
+ * following: DISCLAIMER OF WARRANTIES. THE SPECIFICATION IS PROVIDED
+ * AS IS. JASC DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.
+ *
+ * You are solely responsible for the selection, use, efficiency and
+ * suitability of the Specification for your software products. OTHER
+ * WARRANTIES EXCLUDED. JASC SHALL NOT BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, CONSEQUENTIAL, EXEMPLARY, PUNITIVE OR INCIDENTAL DAMAGES
+ * ARISING FROM ANY CAUSE EVEN IF JASC HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES. CERTAIN JURISDICTIONS DO NOT PERMIT
+ * THE LIMITATION OR EXCLUSION OF INCIDENTAL DAMAGES, SO THIS
+ * LIMITATION MAY NOT APPLY TO YOU. IN NO EVENT WILL JASC BE LIABLE
+ * FOR ANY AMOUNT GREATER THAN WHAT YOU ACTUALLY PAID FOR THE
+ * SPECIFICATION. Should any warranties be found to exist, such
+ * warranties shall be limited in duration to ninety (90) days
+ * following the date you receive the Specification.
+ *
+ * Indemnification. By your inclusion of the Paint Shop Pro File
+ * Format in your software product(s) you agree to indemnify and hold
+ * Jasc Software, Inc. harmless from any and all claims of any kind or
+ * nature made by any of your customers with respect to your software
+ * product(s).
+ *
+ * Export Laws. You agree that you and your customers will not export
+ * your software or Specification except in compliance with the laws
+ * and regulations of the United States.
+ *
+ * US Government Restricted Rights. The Specification and any
+ * accompanying materials are provided with Restricted Rights. Use,
+ * duplication or disclosure by the Government is subject to
+ * restrictions as set forth in subparagraph (c)(1)(ii) of The Rights
+ * in Technical Data and Computer Software clause at DFARS
+ * 252.227-7013, or subparagraphs (c)(1) and (2) of the Commercial
+ * Computer Software - Restricted Rights at 48 CFR 52.227-19, as
+ * applicable. Contractor/manufacturer is Jasc Software, Inc., PO Box
+ * 44997, Eden Prairie MN 55344.
+ *
+ * Jasc reserves the right to amend, modify, change, revoke or
+ * withdraw the Specification at any time and from time to time. Jasc
+ * shall have no obligation to support or maintain the Specification.
+ */
+
+/* Block identifiers.
+ */
+typedef enum {
+ PSP_IMAGE_BLOCK = 0, /* General Image Attributes Block (main) */
+ PSP_CREATOR_BLOCK, /* Creator Data Block (main) */
+ PSP_COLOR_BLOCK, /* Color Palette Block (main and sub) */
+ PSP_LAYER_START_BLOCK, /* Layer Bank Block (main) */
+ PSP_LAYER_BLOCK, /* Layer Block (sub) */
+ PSP_CHANNEL_BLOCK, /* Channel Block (sub) */
+ PSP_SELECTION_BLOCK, /* Selection Block (main) */
+ PSP_ALPHA_BANK_BLOCK, /* Alpha Bank Block (main) */
+ PSP_ALPHA_CHANNEL_BLOCK, /* Alpha Channel Block (sub) */
+ PSP_THUMBNAIL_BLOCK, /* Thumbnail Block (main) */
+ PSP_EXTENDED_DATA_BLOCK, /* Extended Data Block (main) */
+ PSP_TUBE_BLOCK, /* Picture Tube Data Block (main) */
+ PSP_ADJUSTMENT_EXTENSION_BLOCK, /* Adjustment Layer Extension Block (sub) (since PSP6)*/
+ PSP_VECTOR_EXTENSION_BLOCK, /* Vector Layer Extension Block (sub) (since PSP6) */
+ PSP_SHAPE_BLOCK, /* Vector Shape Block (sub) (since PSP6) */
+ PSP_PAINTSTYLE_BLOCK, /* Paint Style Block (sub) (since PSP6) */
+ PSP_COMPOSITE_IMAGE_BANK_BLOCK, /* Composite Image Bank (main) (since PSP6) */
+ PSP_COMPOSITE_ATTRIBUTES_BLOCK, /* Composite Image Attributes (sub) (since PSP6) */
+ PSP_JPEG_BLOCK, /* JPEG Image Block (sub) (since PSP6) */
+ PSP_LINESTYLE_BLOCK, /* Line Style Block (sub) (since PSP7) */
+ PSP_TABLE_BANK_BLOCK, /* Table Bank Block (main) (since PSP7) */
+ PSP_TABLE_BLOCK, /* Table Block (sub) (since PSP7) */
+ PSP_PAPER_BLOCK, /* Vector Table Paper Block (sub) (since PSP7) */
+ PSP_PATTERN_BLOCK, /* Vector Table Pattern Block (sub) (since PSP7) */
+ PSP_GRADIENT_BLOCK, /* Vector Table Gradient Block (not used) (since PSP8) */
+ PSP_GROUP_EXTENSION_BLOCK, /* Group Layer Block (sub) (since PSP8) */
+ PSP_MASK_EXTENSION_BLOCK, /* Mask Layer Block (sub) (since PSP8) */
+ PSP_BRUSH_BLOCK, /* Brush Data Block (main) (since PSP8) */
+ PSP_ART_MEDIA_BLOCK, /* Art Media Layer Block (main) (since PSP9) */
+ PSP_ART_MEDIA_MAP_BLOCK, /* Art Media Layer map data Block (main) (since PSP9) */
+ PSP_ART_MEDIA_TILE_BLOCK, /* Art Media Layer map tile Block(main) (since PSP9) */
+ PSP_ART_MEDIA_TEXTURE_BLOCK, /* AM Layer map texture Block (main) (since PSP9) */
+ PSP_COLORPROFILE_BLOCK, /* ICC Color profile block (since PSP10) */
+ PSP_RASTER_EXTENSION_BLOCK, /* Assumed name based on usage, probably since PSP11 */
+} PSPBlockID;
+
+/* Bitmap type.
+ */
+typedef enum {
+ PSP_DIB_IMAGE = 0, /* Layer color bitmap */
+ PSP_DIB_TRANS_MASK, /* Layer transparency mask bitmap */
+ PSP_DIB_USER_MASK, /* Layer user mask bitmap */
+ PSP_DIB_SELECTION, /* Selection mask bitmap */
+ PSP_DIB_ALPHA_MASK, /* Alpha channel mask bitmap */
+ PSP_DIB_THUMBNAIL, /* Thumbnail bitmap */
+ PSP_DIB_THUMBNAIL_TRANS_MASK, /* Thumbnail transparency mask (since PSP6) */
+ PSP_DIB_ADJUSTMENT_LAYER, /* Adjustment layer bitmap (since PSP6) */
+ PSP_DIB_COMPOSITE, /* Composite image bitmap (since PSP6) */
+ PSP_DIB_COMPOSITE_TRANS_MASK, /* Composite image transparency (since PSP6) */
+ PSP_DIB_PAPER, /* Paper bitmap (since PSP7) */
+ PSP_DIB_PATTERN, /* Pattern bitmap (since PSP7) */
+ PSP_DIB_PATTERN_TRANS_MASK, /* Pattern transparency mask (since PSP7) */
+} PSPDIBType;
+
+/* Type of image in the composite image bank block. (since PSP6)
+ */
+typedef enum {
+ PSP_IMAGE_COMPOSITE = 0, /* Composite Image */
+ PSP_IMAGE_THUMBNAIL, /* Thumbnail Image */
+} PSPCompositeImageType;
+
+/* Graphic contents flags. (since PSP6)
+ */
+typedef enum {
+ /* Layer types */
+ keGCRasterLayers = 0x00000001, /* At least one raster layer */
+ keGCVectorLayers = 0x00000002, /* At least one vector layer */
+ keGCAdjustmentLayers = 0x00000004, /* At least one adjustment layer */
+ keGCGroupLayers = 0x00000008, /* at least one group layer */
+ keGCMaskLayers = 0x00000010, /* at least one mask layer */
+ keGCArtMediaLayers = 0x00000020, /* at least one art media layer */
+
+ /* Additional attributes */
+ keGCMergedCache = 0x00800000, /* merged cache (composite image) */
+ keGCThumbnail = 0x01000000, /* Has a thumbnail */
+ keGCThumbnailTransparency = 0x02000000, /* Thumbnail transp. */
+ keGCComposite = 0x04000000, /* Has a composite image */
+ keGCCompositeTransparency = 0x08000000, /* Composite transp. */
+ keGCFlatImage = 0x10000000, /* Just a background */
+ keGCSelection = 0x20000000, /* Has a selection */
+ keGCFloatingSelectionLayer = 0x40000000, /* Has float. selection */
+ keGCAlphaChannels = 0x80000000, /* Has alpha channel(s) */
+} PSPGraphicContents;
+
+/* Character style flags. (since PSP6)
+ */
+typedef enum {
+ keStyleItalic = 0x00000001, /* Italic property bit */
+ keStyleStruck = 0x00000002, /* Strike­out property bit */
+ keStyleUnderlined = 0x00000004, /* Underlined property bit */
+ keStyleWarped = 0x00000008, /* Warped property bit (since PSP8) */
+ keStyleAntiAliased = 0x00000010, /* Anti­aliased property bit (since PSP8) */
+} PSPCharacterProperties;
+
+/* Table type. (since PSP7)
+ */
+typedef enum {
+ keTTUndefined = 0, /* Undefined table type */
+ keTTGradientTable, /* Gradient table type */
+ keTTPaperTable, /* Paper table type */
+ keTTPatternTable /* Pattern table type */
+} PSPTableType;
+
+/* Layer flags. (since PSP6)
+ */
+typedef enum {
+ keVisibleFlag = 0x00000001, /* Layer is visible */
+ keMaskPresenceFlag = 0x00000002, /* Layer has a mask */
+} PSPLayerProperties;
+
+/* Shape property flags. (since PSP6)
+ */
+typedef enum {
+ keShapeAntiAliased = 0x00000001, /* Shape is anti­aliased */
+ keShapeSelected = 0x00000002, /* Shape is selected */
+ keShapeVisible = 0x00000004, /* Shape is visible */
+} PSPShapeProperties;
+
+/* Polyline node type flags. (since PSP7)
+ */
+typedef enum {
+ keNodeUnconstrained = 0x0000, /* Default node type */
+ keNodeSmooth = 0x0001, /* Node is smooth */
+ keNodeSymmetric = 0x0002, /* Node is symmetric */
+ keNodeAligned = 0x0004, /* Node is aligned */
+ keNodeActive = 0x0008, /* Node is active */
+ keNodeLocked = 0x0010, /* Node is locked */
+ keNodeSelected = 0x0020, /* Node is selected */
+ keNodeVisible = 0x0040, /* Node is visible */
+ keNodeClosed = 0x0080, /* Node is closed */
+
+ /* TODO: This might be a thinko in the spec document only or in the image
+ * format itself. Need to investigate that later
+ */
+ keNodeLockedPSP6 = 0x0016, /* Node is locked */
+ keNodeSelectedPSP6 = 0x0032, /* Node is selected */
+ keNodeVisiblePSP6 = 0x0064, /* Node is visible */
+ keNodeClosedPSP6 = 0x0128, /* Node is closed */
+
+} PSPPolylineNodeTypes;
+
+/* Blend modes. (since PSP6)
+ */
+typedef enum {
+ PSP_BLEND_NORMAL,
+ PSP_BLEND_DARKEN,
+ PSP_BLEND_LIGHTEN,
+ PSP_BLEND_HUE,
+ PSP_BLEND_SATURATION,
+ PSP_BLEND_COLOR,
+ PSP_BLEND_LUMINOSITY,
+ PSP_BLEND_MULTIPLY,
+ PSP_BLEND_SCREEN,
+ PSP_BLEND_DISSOLVE,
+ PSP_BLEND_OVERLAY,
+ PSP_BLEND_HARD_LIGHT,
+ PSP_BLEND_SOFT_LIGHT,
+ PSP_BLEND_DIFFERENCE,
+ PSP_BLEND_DODGE,
+ PSP_BLEND_BURN,
+ PSP_BLEND_EXCLUSION,
+ PSP_BLEND_TRUE_HUE, /* since PSP8 */
+ PSP_BLEND_TRUE_SATURATION, /* since PSP8 */
+ PSP_BLEND_TRUE_COLOR, /* since PSP8 */
+ PSP_BLEND_TRUE_LIGHTNESS, /* since PSP8 */
+ PSP_BLEND_ADJUST = 255,
+} PSPBlendModes;
+
+/* Adjustment layer types. (since PSP6)
+ */
+typedef enum {
+ keAdjNone = 0, /* Undefined adjustment layer type */
+ keAdjLevel, /* Level adjustment */
+ keAdjCurve, /* Curve adjustment */
+ keAdjBrightContrast, /* Brightness­contrast adjustment */
+ keAdjColorBal, /* Color balance adjustment */
+ keAdjHSL, /* HSL adjustment */
+ keAdjChannelMixer, /* Channel mixer adjustment */
+ keAdjInvert, /* Invert adjustment */
+ keAdjThreshold, /* Threshold adjustment */
+ keAdjPoster /* Posterize adjustment */
+} PSPAdjustmentLayerType;
+
+/* Vector shape types. (since PSP6)
+ */
+typedef enum {
+ keVSTUnknown = 0, /* Undefined vector type */
+ keVSTText, /* Shape represents lines of text */
+ keVSTPolyline, /* Shape represents a multiple segment line */
+ keVSTEllipse, /* Shape represents an ellipse (or circle) */
+ keVSTPolygon, /* Shape represents a closed polygon */
+ keVSTGroup, /* Shape represents a group shape (since PSP7) */
+} PSPVectorShapeType;
+
+/* Text element types. (since PSP6)
+ */
+typedef enum {
+ keTextElemUnknown = 0, /* Undefined text element type */
+ keTextElemChar, /* A single character code */
+ keTextElemCharStyle, /* A character style change */
+ keTextElemLineStyle /* A line style change */
+} PSPTextElementType;
+
+/* Text alignment types. (since PSP6)
+ */
+typedef enum {
+ keTextAlignmentLeft = 0, /* Left text alignment */
+ keTextAlignmentCenter, /* Center text alignment */
+ keTextAlignmentRight /* Right text alignment */
+} PSPTextAlignment;
+
+/* Text antialias modes. */
+typedef enum {
+ keNoAntialias = 0, /* Antialias off */
+ keSharpAntialias, /* Sharp */
+ keSmoothAntialias /* Smooth */
+} PSPAntialiasMode;
+
+/* Text flow types */
+typedef enum {
+ keTFHorizontalDown = 0, /* Horizontal then down */
+ keTFVerticalLeft, /* Vertical then left */
+ keTFVerticalRight, /* Vertical then right */
+ keTFHorizontalUp /* Horizontal then up */
+} PSPTextFlow;
+
+/* Paint style types. (since PSP6)
+ */
+typedef enum {
+ keStyleNone = 0x0000, /* No paint style info applies */
+ keStyleColor = 0x0001, /* Color paint style info */
+ keStyleGradient = 0x0002, /* Gradient paint style info */
+ keStylePattern = 0x0004, /* Pattern paint style info (since PSP7) */
+ keStylePaper = 0x0008, /* Paper paint style info (since PSP7) */
+ keStylePen = 0x0010, /* Organic pen paint style info (since PSP7) */
+} PSPPaintStyleType;
+
+/* Gradient type. (since PSP7)
+ */
+typedef enum {
+ keSGTLinear = 0, /* Linera gradient type */
+ keSGTRadial, /* Radial gradient type */
+ keSGTRectangular, /* Rectangulat gradient type */
+ keSGTSunburst /* Sunburst gradient type */
+} PSPStyleGradientType;
+
+/* Paint Style Cap Type (Start & End). (since PSP7)
+ */
+typedef enum {
+ keSCTCapFlat = 0, /* Flat cap type (was round in psp6) */
+ keSCTCapRound, /* Round cap type (was square in psp6) */
+ keSCTCapSquare, /* Square cap type (was flat in psp6) */
+ keSCTCapArrow, /* Arrow cap type */
+ keSCTCapCadArrow, /* Cad arrow cap type */
+ keSCTCapCurvedTipArrow, /* Curved tip arrow cap type */
+ keSCTCapRingBaseArrow, /* Ring base arrow cap type */
+ keSCTCapFluerDelis, /* Fluer deLis cap type */
+ keSCTCapFootball, /* Football cap type */
+ keSCTCapXr71Arrow, /* Xr71 arrow cap type */
+ keSCTCapLilly, /* Lilly cap type */
+ keSCTCapPinapple, /* Pinapple cap type */
+ keSCTCapBall, /* Ball cap type */
+ keSCTCapTulip /* Tulip cap type */
+} PSPStyleCapType;
+
+/* Paint Style Join Type. (since PSP7)
+ */
+typedef enum {
+ keSJTJoinMiter = 0,
+ keSJTJoinRound,
+ keSJTJoinBevel
+} PSPStyleJoinType;
+
+/* Organic pen type. (since PSP7)
+ */
+typedef enum {
+ keSPTOrganicPenNone = 0, /* Undefined pen type */
+ keSPTOrganicPenMesh, /* Mesh pen type */
+ keSPTOrganicPenSand, /* Sand pen type */
+ keSPTOrganicPenCurlicues, /* Curlicues pen type */
+ keSPTOrganicPenRays, /* Rays pen type */
+ keSPTOrganicPenRipple, /* Ripple pen type */
+ keSPTOrganicPenWave, /* Wave pen type */
+ keSPTOrganicPen /* Generic pen type */
+} PSPStylePenType;
+
+
+/* Channel types.
+ */
+typedef enum {
+ PSP_CHANNEL_COMPOSITE = 0, /* Channel of single channel bitmap */
+ PSP_CHANNEL_RED, /* Red channel of 24 bit bitmap */
+ PSP_CHANNEL_GREEN, /* Green channel of 24 bit bitmap */
+ PSP_CHANNEL_BLUE /* Blue channel of 24 bit bitmap */
+} PSPChannelType;
+
+/* Possible metrics used to measure resolution.
+ */
+typedef enum {
+ PSP_METRIC_UNDEFINED = 0, /* Metric unknown */
+ PSP_METRIC_INCH, /* Resolution is in inches */
+ PSP_METRIC_CM /* Resolution is in centimeters */
+} PSP_METRIC;
+
+/* Possible types of compression.
+ */
+typedef enum {
+ PSP_COMP_NONE = 0, /* No compression */
+ PSP_COMP_RLE, /* RLE compression */
+ PSP_COMP_LZ77, /* LZ77 compression */
+ PSP_COMP_JPEG /* JPEG compression (only used by thumbnail and composite image) (since PSP6) */
+} PSPCompression;
+
+/* Picture tube placement mode.
+ */
+typedef enum {
+ tpmRandom, /* Place tube images in random intervals */
+ tpmConstant /* Place tube images in constant intervals */
+} TubePlacementMode;
+
+/* Picture tube selection mode.
+ */
+typedef enum {
+ tsmRandom, /* Randomly select the next image in */
+ /* tube to display */
+ tsmIncremental, /* Select each tube image in turn */
+ tsmAngular, /* Select image based on cursor direction */
+ tsmPressure, /* Select image based on pressure */
+ /* (from pressure-sensitive pad) */
+ tsmVelocity /* Select image based on cursor speed */
+} TubeSelectionMode;
+
+/* Extended data field types.
+ */
+typedef enum {
+ PSP_XDATA_TRNS_INDEX = 0, /* Transparency index field */
+ PSP_XDATA_GRID, /* Image grid information (since PSP7) */
+ PSP_XDATA_GUIDE, /* Image guide information (since PSP7) */
+ PSP_XDATA_EXIF, /* Image Exif information (since PSP8) */
+ PSP_XDATA_IPTC, /* Image IPTC information (since PSP10) */
+} PSPExtendedDataID;
+
+/* Creator field types.
+ */
+typedef enum {
+ PSP_CRTR_FLD_TITLE = 0, /* Image document title field */
+ PSP_CRTR_FLD_CRT_DATE, /* Creation date field */
+ PSP_CRTR_FLD_MOD_DATE, /* Modification date field */
+ PSP_CRTR_FLD_ARTIST, /* Artist name field */
+ PSP_CRTR_FLD_CPYRGHT, /* Copyright holder name field */
+ PSP_CRTR_FLD_DESC, /* Image document description field */
+ PSP_CRTR_FLD_APP_ID, /* Creating app id field */
+ PSP_CRTR_FLD_APP_VER /* Creating app version field */
+} PSPCreatorFieldID;
+
+/* Grid units type. (since PSP7)
+ */
+typedef enum {
+ keGridUnitsPixels = 0, /* Grid units is pixels */
+ keGridUnitsInches, /* Grid units is inches */
+ keGridUnitsCentimeters /* Grid units is centimeters */
+} PSPGridUnitsType;
+
+/* Guide orientation type. (since PSP7)
+ */
+typedef enum {
+ keHorizontalGuide = 0,
+ keVerticalGuide
+} PSPGuideOrientationType;
+
+/* Creator application identifiers.
+ */
+typedef enum {
+ PSP_CREATOR_APP_UNKNOWN = 0, /* Creator application unknown */
+ PSP_CREATOR_APP_PAINT_SHOP_PRO /* Creator is Paint Shop Pro */
+} PSPCreatorAppID;
+
+/* Layer types.
+ */
+typedef enum {
+ PSP_LAYER_NORMAL = 0, /* Normal layer */
+ PSP_LAYER_FLOATING_SELECTION /* Floating selection layer */
+} PSPLayerTypePSP5;
+
+/* Layer types. (since PSP6)
+ */
+typedef enum {
+ keGLTUndefined = 0, /* Undefined layer type */
+ keGLTRaster, /* Standard raster layer */
+ keGLTFloatingRasterSelection, /* Floating selection (raster layer) */
+ keGLTVector, /* Vector layer */
+ keGLTAdjustment, /* Adjustment layer */
+ keGLTGroup, /* Group layer (since PSP8) */
+ keGLTMask, /* Mask layer (since PSP8) */
+ keGLTArtMedia /* Art media layer (since PSP9) */
+} PSPLayerTypePSP6;
+
+/* Art media layer map types (since PSP7) */
+typedef enum {
+keArtMediaColorMap = 0,
+keArtMediaBumpMap,
+keArtMediaShininessMap,
+keArtMediaReflectivityMap,
+keArtMediaDrynessMap
+} PSPArtMediaMapType;
+
+
+/* Truth values.
+ */
+#if 0 /* FALSE and TRUE taken by GLib */
+typedef enum {
+ FALSE = 0,
+ TRUE
+} PSP_BOOLEAN;
+#else
+typedef gboolean PSP_BOOLEAN;
+#endif
+
+/* End of cut&paste from psp spec */
+
+/* We store the various PSP data in own structures.
+ * We cannot use structs intended to be direct copies of the file block
+ * headers because of struct alignment issues.
+ */
+typedef struct
+{
+ guint32 width, height;
+ gdouble resolution;
+ guchar metric;
+ guint16 compression;
+ guint16 depth;
+ guchar grayscale;
+ guint32 active_layer;
+ guint16 layer_count;
+ guint16 bytes_per_sample;
+ GimpImageBaseType base_type;
+ GimpPrecision precision;
+} PSPimage;
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gint save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+/* Various local variables...
+ */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* Save info */
+typedef struct
+{
+ PSPCompression compression;
+} PSPSaveVals;
+
+static PSPSaveVals psvals =
+{
+ PSP_COMP_LZ77
+};
+
+static guint16 psp_ver_major, psp_ver_minor;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+#if 0
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "compression", "Specify 0 for no compression, 1 for RLE, and 2 for LZ77" }
+ };
+#endif
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads images from the Paint Shop Pro PSP file format",
+ "This plug-in loads and exports images in "
+ "Paint Shop Pro's native PSP format. "
+ "Vector layers aren't handled. Exporting isn't "
+ "yet implemented.",
+ "Tor Lillqvist",
+ "Tor Lillqvist",
+ "1999",
+ N_("Paint Shop Pro image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-psp");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "psp,tub,pspimage",
+ "",
+ "0,string,Paint\\040Shop\\040Pro\\040Image\\040File\n\032");
+
+ /* commented out until exporting is implemented */
+#if 0
+ gimp_install_procedure (SAVE_PROC,
+ "exports images in the Paint Shop Pro PSP file format",
+ "This plug-in loads and exports images in "
+ "Paint Shop Pro's native PSP format. "
+ "Vector layers aren't handled. Exporting isn't "
+ "yet implemented.",
+ "Tor Lillqvist",
+ "Tor Lillqvist",
+ "1999",
+ N_("Paint Shop Pro image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_save_handler (SAVE_PROC, "psp,tub", "");
+#endif
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ gint run;
+
+ dialog = gimp_export_dialog_new (_("PSP"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* file save type */
+ frame = gimp_int_radio_group_new (TRUE, _("Data Compression"),
+ G_CALLBACK (gimp_radio_button_update),
+ &psvals.compression, psvals.compression,
+
+ C_("compression", "None"), PSP_COMP_NONE, NULL,
+ _("RLE"), PSP_COMP_RLE, NULL,
+ _("LZ77"), PSP_COMP_LZ77, NULL,
+
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, FALSE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+/* This helper method is used to get the name of the block for the known block
+ * types. The enum PSPBlockID must cover the input values.
+ */
+static const gchar *
+block_name (gint id)
+{
+ static const gchar *block_names[] =
+ {
+ "IMAGE",
+ "CREATOR",
+ "COLOR",
+ "LAYER_START",
+ "LAYER",
+ "CHANNEL",
+ "SELECTION",
+ "ALPHA_BANK",
+ "ALPHA_CHANNEL",
+ "THUMBNAIL",
+ "EXTENDED_DATA",
+ "TUBE",
+ "ADJUSTMENT_EXTENSION",
+ "VECTOR_EXTENSION_BLOCK",
+ "SHAPE_BLOCK",
+ "PAINTSTYLE_BLOCK",
+ "COMPOSITE_IMAGE_BANK_BLOCK",
+ "COMPOSITE_ATTRIBUTES_BLOCK",
+ "JPEG_BLOCK",
+ "LINESTYLE_BLOCK",
+ "TABLE_BANK_BLOCK",
+ "TABLE_BLOCK",
+ "PAPER_BLOCK",
+ "PATTERN_BLOCK",
+ "GRADIENT_BLOCK",
+ "GROUP_EXTENSION_BLOCK",
+ "MASK_EXTENSION_BLOCK",
+ "BRUSH_BLOCK",
+ "ART_MEDIA_BLOCK",
+ "ART_MEDIA_MAP_BLOCK",
+ "ART_MEDIA_TILE_BLOCK",
+ "ART_MEDIA_TEXTURE_BLOCK",
+ "COLORPROFILE_BLOCK",
+ "RASTER_EXTENSION_BLOCK",
+};
+ static gchar *err_name = NULL;
+
+ if (id >= 0 && id <= PSP_RASTER_EXTENSION_BLOCK)
+ return block_names[id];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("id=%d", id);
+
+ return err_name;
+}
+
+/* This helper method is used during loading. It verifies the block we are
+ * reading has a valid header. Fills the variables init_len and total_len
+ */
+static gint
+read_block_header (FILE *f,
+ guint32 *init_len,
+ guint32 *total_len,
+ GError **error)
+{
+ guchar buf[4];
+ guint16 id;
+ long header_start;
+ guint32 len;
+
+ IFDBG(3) header_start = ftell (f);
+
+ if (fread (buf, 4, 1, f) < 1
+ || fread (&id, 2, 1, f) < 1
+ || fread (&len, 4, 1, f) < 1
+ || (psp_ver_major < 4 && fread (total_len, 4, 1, f) < 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading block header"));
+ return -1;
+ }
+ if (memcmp (buf, "~BK\0", 4) != 0)
+ {
+ IFDBG(3)
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid block header at %ld"), header_start);
+ else
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid block header"));
+ return -1;
+ }
+
+ IFDBG(3) g_message ("%s at %ld", block_name (id), header_start);
+
+ if (psp_ver_major < 4)
+ {
+ *init_len = GUINT32_FROM_LE (len);
+ *total_len = GUINT32_FROM_LE (*total_len);
+ }
+ else
+ {
+ /* Version 4.0 seems to have dropped the initial data chunk length
+ * field.
+ */
+ *init_len = 0xDEADBEEF; /* Intentionally bogus, should not be used */
+ *total_len = GUINT32_FROM_LE (len);
+ }
+
+ return GUINT16_FROM_LE (id);
+}
+
+static gint
+try_fseek (FILE *f,
+ glong pos,
+ gint whence,
+ GError **error)
+{
+ if (fseek (f, pos, whence) < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Seek error: %s"), g_strerror (errno));
+ fclose (f);
+ return -1;
+ }
+ return 0;
+}
+
+/* Read the PSP_IMAGE_BLOCK */
+static gint
+read_general_image_attribute_block (FILE *f,
+ guint init_len,
+ guint total_len,
+ PSPimage *ia,
+ GError **error)
+{
+ gchar buf[6];
+ guint64 res;
+ gchar graphics_content[4];
+ long chunk_start;
+
+ if (init_len < 38 || total_len < 38)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid general image attribute chunk size."));
+ return -1;
+ }
+
+ chunk_start = ftell (f);
+ if ((psp_ver_major >= 4
+ && (fread (&init_len, 4, 1, f) < 1 || ((init_len = GUINT32_FROM_LE (init_len)) < 46)))
+ || fread (&ia->width, 4, 1, f) < 1
+ || fread (&ia->height, 4, 1, f) < 1
+ || fread (&res, 8, 1, f) < 1
+ || fread (&ia->metric, 1, 1, f) < 1
+ || fread (&ia->compression, 2, 1, f) < 1
+ || fread (&ia->depth, 2, 1, f) < 1
+ || fread (buf, 2+4, 1, f) < 1 /* Skip plane and color count */
+ || fread (&ia->grayscale, 1, 1, f) < 1
+ || fread (buf, 4, 1, f) < 1 /* Skip total image size */
+ || fread (&ia->active_layer, 4, 1, f) < 1
+ || fread (&ia->layer_count, 2, 1, f) < 1
+ || (psp_ver_major >= 4 && fread (graphics_content, 4, 1, f) < 1)
+ || try_fseek (f, chunk_start + init_len, SEEK_SET, error) < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading general image attribute block."));
+ return -1;
+ }
+ ia->width = GUINT32_FROM_LE (ia->width);
+ ia->height = GUINT32_FROM_LE (ia->height);
+
+ res = GUINT64_FROM_LE (res);
+ memcpy (&ia->resolution, &res, 8);
+ if (ia->metric == PSP_METRIC_CM)
+ ia->resolution /= 2.54;
+
+ ia->compression = GUINT16_FROM_LE (ia->compression);
+ if (ia->compression > PSP_COMP_LZ77)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unknown compression type %d"), ia->compression);
+ return -1;
+ }
+
+ ia->depth = GUINT16_FROM_LE (ia->depth);
+ switch (ia->depth)
+ {
+ case 24:
+ case 48:
+ ia->base_type = GIMP_RGB;
+ if (ia->depth == 24)
+ ia->precision = GIMP_PRECISION_U8_GAMMA;
+ else
+ ia->precision = GIMP_PRECISION_U16_GAMMA;
+ break;
+
+ case 1:
+ case 4:
+ case 8:
+ case 16:
+ if (ia->grayscale && ia->depth >= 8)
+ {
+ ia->base_type = GIMP_GRAY;
+ if (ia->depth == 8)
+ ia->precision = GIMP_PRECISION_U8_GAMMA;
+ else
+ ia->precision = GIMP_PRECISION_U16_GAMMA;
+ }
+ else if (ia->depth <= 8 && ! ia->grayscale)
+ {
+ ia->base_type = GIMP_INDEXED;
+ ia->precision = GIMP_PRECISION_U8_GAMMA;
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported bit depth %d"), ia->depth);
+ return -1;
+ }
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported bit depth %d"), ia->depth);
+ return -1;
+ break;
+ }
+
+ if (ia->precision == GIMP_PRECISION_U16_GAMMA)
+ ia->bytes_per_sample = 2;
+ else
+ ia->bytes_per_sample = 1;
+
+ ia->active_layer = GUINT32_FROM_LE (ia->active_layer);
+ ia->layer_count = GUINT16_FROM_LE (ia->layer_count);
+
+ return 0;
+}
+
+static gint
+read_creator_block (FILE *f,
+ gint image_ID,
+ guint total_len,
+ PSPimage *ia,
+ GError **error)
+{
+ long data_start;
+ guchar buf[4];
+ guint16 keyword;
+ guint32 length;
+ gchar *string;
+ gchar *title = NULL, *artist = NULL, *copyright = NULL, *description = NULL;
+ guint32 dword;
+ guint32 __attribute__((unused))cdate = 0;
+ guint32 __attribute__((unused))mdate = 0;
+ guint32 __attribute__((unused))appid;
+ guint32 __attribute__((unused))appver;
+ GString *comment;
+ GimpParasite *comment_parasite;
+
+ data_start = ftell (f);
+ comment = g_string_new (NULL);
+
+ while (ftell (f) < data_start + total_len)
+ {
+ if (fread (buf, 4, 1, f) < 1
+ || fread (&keyword, 2, 1, f) < 1
+ || fread (&length, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading creator keyword chunk"));
+ return -1;
+ }
+ if (memcmp (buf, "~FL\0", 4) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid keyword chunk header"));
+ return -1;
+ }
+ keyword = GUINT16_FROM_LE (keyword);
+ length = GUINT32_FROM_LE (length);
+ switch (keyword)
+ {
+ case PSP_CRTR_FLD_TITLE:
+ case PSP_CRTR_FLD_ARTIST:
+ case PSP_CRTR_FLD_CPYRGHT:
+ case PSP_CRTR_FLD_DESC:
+ string = g_malloc (length + 1);
+ if (fread (string, length, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading creator keyword data"));
+ g_free (string);
+ return -1;
+ }
+ /* PSP does not zero terminate strings */
+ string[length] = '\0';
+ switch (keyword)
+ {
+ case PSP_CRTR_FLD_TITLE:
+ g_free (title); title = string; break;
+ case PSP_CRTR_FLD_ARTIST:
+ g_free (artist); artist = string; break;
+ case PSP_CRTR_FLD_CPYRGHT:
+ g_free (copyright); copyright = string; break;
+ case PSP_CRTR_FLD_DESC:
+ g_free (description); description = string; break;
+ default:
+ g_free (string);
+ }
+ break;
+ case PSP_CRTR_FLD_CRT_DATE:
+ case PSP_CRTR_FLD_MOD_DATE:
+ case PSP_CRTR_FLD_APP_ID:
+ case PSP_CRTR_FLD_APP_VER:
+ if (fread (&dword, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading creator keyword data"));
+ return -1;
+ }
+ switch (keyword)
+ {
+ case PSP_CRTR_FLD_CRT_DATE:
+ cdate = dword; break;
+ case PSP_CRTR_FLD_MOD_DATE:
+ mdate = dword; break;
+ case PSP_CRTR_FLD_APP_ID:
+ appid = dword; break;
+ case PSP_CRTR_FLD_APP_VER:
+ appver = dword; break;
+ }
+ break;
+ default:
+ if (try_fseek (f, length, SEEK_CUR, error) < 0)
+ {
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if (title)
+ {
+ g_string_append (comment, title);
+ g_free (title);
+ g_string_append (comment, "\n");
+ }
+ if (artist)
+ {
+ g_string_append (comment, artist);
+ g_free (artist);
+ g_string_append (comment, "\n");
+ }
+ if (copyright)
+ {
+ g_string_append (comment, "Copyright ");
+ g_string_append (comment, copyright);
+ g_free (copyright);
+ g_string_append (comment, "\n");
+ }
+ if (description)
+ {
+ g_string_append (comment, description);
+ g_free (description);
+ g_string_append (comment, "\n");
+ }
+ if (comment->len > 0)
+ {
+ comment_parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment->str) + 1,
+ comment->str);
+ gimp_image_attach_parasite (image_ID, comment_parasite);
+ gimp_parasite_free (comment_parasite);
+ }
+
+ g_string_free (comment, FALSE);
+
+ return 0;
+}
+
+static gint
+read_color_block (FILE *f,
+ gint image_ID,
+ guint total_len,
+ PSPimage *ia,
+ GError **error)
+{
+ long block_start;
+ guint32 chunk_len, entry_count, pal_size;
+ guint32 color_palette_entries;
+ guchar *color_palette;
+
+ block_start = ftell (f);
+
+ if (psp_ver_major >= 4)
+ {
+ if (fread (&chunk_len, 4, 1, f) < 1
+ || fread (&entry_count, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading color block"));
+ return -1;
+ }
+
+ chunk_len = GUINT32_FROM_LE (chunk_len);
+
+ if (try_fseek (f, block_start + chunk_len, SEEK_SET, error) < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading color block"));
+ return -1;
+ }
+ }
+ else
+ {
+ if (fread (&entry_count, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading color block"));
+ return -1;
+ }
+ }
+
+ color_palette_entries = GUINT32_FROM_LE (entry_count);
+ /* psp color palette entries are stored as RGBA so 4 bytes per entry
+ where the fourth bytes is always zero */
+ pal_size = color_palette_entries * 4;
+ color_palette = g_malloc (pal_size);
+ if (fread (color_palette, pal_size, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading color palette"));
+ return -1;
+ }
+ else
+ {
+ guchar *tmpmap;
+ gint i;
+
+ /* Convert to BGR palette */
+ tmpmap = g_malloc (3 * color_palette_entries);
+ for (i = 0; i < color_palette_entries; ++i)
+ {
+ tmpmap[i*3 ] = color_palette[i*4+2];
+ tmpmap[i*3+1] = color_palette[i*4+1];
+ tmpmap[i*3+2] = color_palette[i*4];
+ }
+
+ memcpy (color_palette, tmpmap, color_palette_entries * 3);
+ g_free (tmpmap);
+
+ gimp_image_set_colormap (image_ID, color_palette, color_palette_entries);
+ g_free (color_palette);
+ }
+
+ return 0;
+}
+
+static void inline
+swab_rect (guint32 *rect)
+{
+ rect[0] = GUINT32_FROM_LE (rect[0]);
+ rect[1] = GUINT32_FROM_LE (rect[1]);
+ rect[2] = GUINT32_FROM_LE (rect[2]);
+ rect[3] = GUINT32_FROM_LE (rect[3]);
+}
+
+static GimpLayerMode
+gimp_layer_mode_from_psp_blend_mode (PSPBlendModes mode)
+{
+ switch (mode)
+ {
+ case PSP_BLEND_NORMAL:
+ return GIMP_LAYER_MODE_NORMAL_LEGACY;
+
+ case PSP_BLEND_DARKEN:
+ return GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY;
+
+ case PSP_BLEND_LIGHTEN:
+ return GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY;
+
+ case PSP_BLEND_HUE:
+ return GIMP_LAYER_MODE_HSV_HUE_LEGACY;
+
+ case PSP_BLEND_SATURATION:
+ return GIMP_LAYER_MODE_HSV_SATURATION_LEGACY;
+
+ case PSP_BLEND_COLOR:
+ return GIMP_LAYER_MODE_HSL_COLOR_LEGACY;
+
+ case PSP_BLEND_LUMINOSITY:
+ return GIMP_LAYER_MODE_HSV_VALUE_LEGACY; /* ??? */
+
+ case PSP_BLEND_MULTIPLY:
+ return GIMP_LAYER_MODE_MULTIPLY_LEGACY;
+
+ case PSP_BLEND_SCREEN:
+ return GIMP_LAYER_MODE_SCREEN_LEGACY;
+
+ case PSP_BLEND_DISSOLVE:
+ return GIMP_LAYER_MODE_DISSOLVE;
+
+ case PSP_BLEND_OVERLAY:
+ return GIMP_LAYER_MODE_OVERLAY;
+
+ case PSP_BLEND_HARD_LIGHT:
+ return GIMP_LAYER_MODE_HARDLIGHT_LEGACY;
+
+ case PSP_BLEND_SOFT_LIGHT:
+ return GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+ case PSP_BLEND_DIFFERENCE:
+ return GIMP_LAYER_MODE_DIFFERENCE_LEGACY;
+
+ case PSP_BLEND_DODGE:
+ return GIMP_LAYER_MODE_DODGE_LEGACY;
+
+ case PSP_BLEND_BURN:
+ return GIMP_LAYER_MODE_BURN_LEGACY;
+
+ case PSP_BLEND_EXCLUSION:
+ return GIMP_LAYER_MODE_EXCLUSION;
+
+ case PSP_BLEND_TRUE_HUE:
+ return GIMP_LAYER_MODE_HSV_HUE;
+
+ case PSP_BLEND_TRUE_SATURATION:
+ return GIMP_LAYER_MODE_HSV_SATURATION;
+
+ case PSP_BLEND_TRUE_COLOR:
+ return GIMP_LAYER_MODE_HSL_COLOR;
+
+ case PSP_BLEND_TRUE_LIGHTNESS:
+ return GIMP_LAYER_MODE_HSV_VALUE;
+
+ case PSP_BLEND_ADJUST:
+ return -1; /* ??? */
+ }
+ return -1;
+}
+
+static const gchar *
+blend_mode_name (PSPBlendModes mode)
+{
+ static const gchar *blend_mode_names[] =
+ {
+ "NORMAL",
+ "DARKEN",
+ "LIGHTEN",
+ "HUE",
+ "SATURATION",
+ "COLOR",
+ "LUMINOSITY",
+ "MULTIPLY",
+ "SCREEN",
+ "DISSOLVE",
+ "OVERLAY",
+ "HARD_LIGHT",
+ "SOFT_LIGHT",
+ "DIFFERENCE",
+ "DODGE",
+ "BURN",
+ "EXCLUSION",
+ "TRUE HUE",
+ "TRUE SATURATION",
+ "TRUE COLOR",
+ "TRUE LIGHTNESS",
+ /* ADJUST should always be the last one. */
+ "ADJUST"
+ };
+ static gchar *err_name = NULL;
+
+ if (mode >= 0 && mode <= PSP_BLEND_TRUE_LIGHTNESS)
+ return blend_mode_names[mode];
+ else if (mode == PSP_BLEND_ADJUST)
+ return blend_mode_names[PSP_BLEND_TRUE_LIGHTNESS+1];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("unknown layer blend mode %d", mode);
+
+ return err_name;
+}
+
+static const gchar *
+layer_type_name (PSPLayerTypePSP6 type)
+{
+ static const gchar *layer_type_names[] =
+ {
+ "Undefined",
+ "Raster",
+ "Floating Raster Selection",
+ "Vector",
+ "Adjustment",
+ "Group",
+ "Mask",
+ "Art Media",
+ };
+ static gchar *err_name = NULL;
+
+ if (type >= 0 && type <= keGLTArtMedia)
+ return layer_type_names[type];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("unknown layer type %d", type);
+
+ return err_name;
+}
+
+static const gchar *
+bitmap_type_name (gint type)
+{
+ static const gchar *bitmap_type_names[] =
+ {
+ "IMAGE",
+ "TRANS_MASK",
+ "USER_MASK",
+ "SELECTION",
+ "ALPHA_MASK",
+ "THUMBNAIL"
+ };
+ static gchar *err_name = NULL;
+
+ if (type >= 0 && type <= PSP_DIB_THUMBNAIL)
+ return bitmap_type_names[type];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("unknown bitmap type %d", type);
+
+ return err_name;
+}
+
+static const gchar *
+channel_type_name (gint type)
+{
+ static const gchar *channel_type_names[] =
+ {
+ "COMPOSITE",
+ "RED",
+ "GREEN",
+ "BLUE"
+ };
+ static gchar *err_name = NULL;
+
+ if (type >= 0 && type <= PSP_CHANNEL_BLUE)
+ return channel_type_names[type];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("unknown channel type %d", type);
+
+ return err_name;
+}
+
+static void *
+psp_zalloc (void *opaque,
+ guint items,
+ guint size)
+{
+ return g_malloc (items*size);
+}
+
+static void
+psp_zfree (void *opaque,
+ void *ptr)
+{
+ g_free (ptr);
+}
+
+static void
+upscale_indexed_sub_8 (FILE *f,
+ gint width,
+ gint height,
+ gint bpp,
+ guchar *buf)
+{
+ gint x, y, b, line_width;
+ gint bpp_zero_based = bpp - 1;
+ gint current_bit = 0;
+ guchar *tmpbuf, *buf_start, *src;
+
+ /* Scanlines for 1 and 4 bit only end on a 4-byte boundary. */
+ line_width = (((width * bpp + 7) / 8) + bpp_zero_based) / 4 * 4;
+ buf_start = g_malloc0 (width * height);
+ tmpbuf = buf_start;
+
+ for (y = 0; y < height; tmpbuf += width, ++y)
+ {
+ src = buf + y * line_width;
+ for (x = 0; x < width; ++x)
+ {
+ for (b = 0; b < bpp; b++)
+ {
+ current_bit = bpp * x + b;
+ if (src[current_bit / 8] & (128 >> (current_bit % 8)))
+ tmpbuf[x] += (1 << (bpp_zero_based - b));
+ }
+ }
+ }
+
+ memcpy (buf, buf_start, width * height);
+ g_free (buf_start);
+}
+
+static int
+read_channel_data (FILE *f,
+ PSPimage *ia,
+ guchar **pixels,
+ guint bytespp,
+ guint offset,
+ GeglBuffer *buffer,
+ guint32 compressed_len,
+ GError **error)
+{
+ gint i, y, line_width;
+ gint width = gegl_buffer_get_width (buffer);
+ gint height = gegl_buffer_get_height (buffer);
+ gint npixels = width * height;
+ guchar *buf;
+ guchar *buf2 = NULL; /* please the compiler */
+ guchar runcount, byte;
+ z_stream zstream;
+
+ g_assert (ia->bytes_per_sample <= 2);
+
+ if (ia->depth < 8)
+ {
+ /* Scanlines for 1 and 4 bit only end on a 4-byte boundary. */
+ line_width = (((width * ia->depth + 7) / 8) + ia->depth - 1) / 4 * 4;
+ }
+ else
+ {
+ line_width = width * ia->bytes_per_sample;
+ }
+
+ switch (ia->compression)
+ {
+ case PSP_COMP_NONE:
+ if (bytespp == 1)
+ {
+ fread (pixels[0], height * line_width, 1, f);
+ }
+ else
+ {
+ buf = g_malloc (line_width);
+ if (ia->bytes_per_sample == 1)
+ {
+ for (y = 0; y < height; y++)
+ {
+ guchar *p, *q;
+
+ fread (buf, width, 1, f);
+ /* Contrary to what the PSP specification seems to suggest
+ scanlines are not stored on a 4-byte boundary. */
+ p = buf;
+ q = pixels[y] + offset;
+ for (i = 0; i < width; i++)
+ {
+ *q = *p++;
+ q += bytespp;
+ }
+ }
+ }
+ else if (ia->bytes_per_sample == 2)
+ {
+ for (y = 0; y < height; y++)
+ {
+ guint16 *p, *q;
+
+ fread (buf, width * ia->bytes_per_sample, 1, f);
+ /* Contrary to what the PSP specification seems to suggest
+ scanlines are not stored on a 4-byte boundary. */
+ p = (guint16 *) buf;
+ q = (guint16 *) (pixels[y] + offset);
+ for (i = 0; i < width; i++)
+ {
+ *q = GUINT16_FROM_LE (*p++);
+ q += bytespp / 2;
+ }
+ }
+ }
+
+ g_free (buf);
+ }
+ break;
+
+ case PSP_COMP_RLE:
+ {
+ guchar *q, *endq;
+
+ q = pixels[0] + offset;
+ if (ia->depth >= 8)
+ endq = q + npixels * bytespp;
+ else
+ endq = q + line_width * height;
+
+ buf = g_malloc (127);
+ while (q < endq)
+ {
+ fread (&runcount, 1, 1, f);
+ if (runcount > 128)
+ {
+ runcount -= 128;
+ fread (&byte, 1, 1, f);
+ memset (buf, byte, runcount);
+ }
+ else
+ fread (buf, runcount, 1, f);
+
+ /* prevent buffer overflow for bogus data */
+ if (runcount > (endq - q) / bytespp + ia->bytes_per_sample - 1)
+ {
+ g_printerr ("Buffer overflow decompressing RLE data.\n");
+ break;
+ }
+
+ if (bytespp == 1)
+ {
+ memmove (q, buf, runcount);
+ q += runcount;
+ }
+ else if (ia->bytes_per_sample == 1)
+ {
+ guchar *p = buf;
+
+ for (i = 0; i < runcount; i++)
+ {
+ *q = *p++;
+ q += bytespp;
+ }
+ }
+ else if (ia->bytes_per_sample == 2)
+ {
+ guint16 *p = (guint16 *) buf;
+ guint16 *r = (guint16 *) q;
+
+ for (i = 0; i < runcount / 2; i++)
+ {
+ *r = GUINT16_FROM_LE (*p++);
+ r += bytespp / 2;
+ }
+ q = (guchar *) r;
+ }
+ }
+ g_free (buf);
+ }
+ break;
+
+ case PSP_COMP_LZ77:
+ buf = g_malloc (compressed_len);
+ fread (buf, compressed_len, 1, f);
+ zstream.next_in = buf;
+ zstream.avail_in = compressed_len;
+ zstream.zalloc = psp_zalloc;
+ zstream.zfree = psp_zfree;
+ zstream.opaque = f;
+ if (inflateInit (&zstream) != Z_OK)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("zlib error"));
+ return -1;
+ }
+ if (bytespp == 1)
+ zstream.next_out = pixels[0];
+ else
+ {
+ buf2 = g_malloc (npixels * ia->bytes_per_sample);
+ zstream.next_out = buf2;
+ }
+ zstream.avail_out = npixels * ia->bytes_per_sample;
+ if (inflate (&zstream, Z_FINISH) != Z_STREAM_END)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("zlib error"));
+ inflateEnd (&zstream);
+ return -1;
+ }
+ inflateEnd (&zstream);
+ g_free (buf);
+
+ if (bytespp > 1)
+ {
+ if (ia->bytes_per_sample == 1)
+ {
+ guchar *p, *q;
+
+ p = buf2;
+ q = pixels[0] + offset;
+ for (i = 0; i < npixels; i++)
+ {
+ *q = *p++;
+ q += bytespp;
+ }
+ g_free (buf2);
+ }
+ else if (ia->bytes_per_sample == 2)
+ {
+ guint16 *p, *q;
+
+ p = (guint16 *) buf2;
+ q = (guint16 *) (pixels[0] + offset);
+ for (i = 0; i < npixels; i++)
+ {
+ *q = GUINT16_FROM_LE (*p++);
+ q += bytespp / 2;
+ }
+ g_free (buf2);
+ }
+ }
+ break;
+ }
+
+ if (ia->base_type == GIMP_INDEXED && ia->depth < 8)
+ {
+ /* We need to convert 1 and 4 bit to 8 bit indexed */
+ upscale_indexed_sub_8 (f, width, height, ia->depth, pixels[0]);
+ }
+
+ return 0;
+}
+
+static gboolean
+read_raster_layer_info (FILE *f,
+ long layer_extension_start,
+ guint16 *bitmap_count,
+ guint16 *channel_count,
+ GError **error)
+{
+ long block_start;
+ guint32 layer_extension_len, block_len;
+ gint block_id;
+
+ if (fseek (f, layer_extension_start, SEEK_SET) < 0
+ || fread (&layer_extension_len, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer extension information"));
+ return FALSE;
+ }
+
+ /* Newer versions of PSP have an extra block here for raster layers
+ with block id = 0x21. Most likely this was to fix an oversight in
+ the specification since vector and adjustment layers already were
+ using a similar extension block since file version 4.
+ The old chunk with bitmap_count and channel_count starts after this block.
+ We do not know starting from which version this change was implemented
+ but most likely version 9 (could also be version 10) so we can't test
+ based on version number only.
+ Although this is kind of a hack we can safely test for the block starting
+ code since the layer_extension_len here is always a small number.
+ */
+ if (psp_ver_major > 8 && memcmp (&layer_extension_len, "~BK\0", 4) == 0)
+ {
+ if (fread (&block_id, 2, 1, f) < 1
+ || fread (&block_len, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading block information"));
+ return FALSE;
+ }
+ block_id = GUINT16_FROM_LE (block_id);
+ block_len = GUINT32_FROM_LE (block_len);
+
+ block_start = ftell (f);
+ layer_extension_start = block_start + block_len;
+
+ if (fseek (f, layer_extension_start, SEEK_SET) < 0
+ || fread (&layer_extension_len, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer extension information"));
+ return FALSE;
+ }
+ }
+ layer_extension_len = GUINT32_FROM_LE (layer_extension_len);
+
+ if (fread (bitmap_count, 2, 1, f) < 1
+ || fread (channel_count, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer extension information"));
+ return FALSE;
+ }
+ if (try_fseek (f, layer_extension_start + layer_extension_len, SEEK_SET, error) < 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gint
+read_layer_block (FILE *f,
+ gint image_ID,
+ guint total_len,
+ PSPimage *ia,
+ GError **error)
+{
+ gint i;
+ long block_start, sub_block_start, channel_start;
+ long layer_extension_start;
+ gint sub_id;
+ guint32 sub_init_len, sub_total_len;
+ guint32 chunk_len;
+ gchar *name = NULL;
+ gchar *layer_name = NULL;
+ guint16 namelen;
+ guchar type, opacity, blend_mode, visibility, transparency_protected;
+ guchar link_group_id, mask_linked, mask_disabled;
+ guint32 image_rect[4], saved_image_rect[4], mask_rect[4], saved_mask_rect[4];
+ gboolean null_layer, can_handle_layer;
+ guint16 bitmap_count, channel_count;
+ GimpImageType drawable_type;
+ guint32 layer_ID = 0;
+ GimpLayerMode layer_mode;
+ guint32 channel_init_len, channel_total_len;
+ guint32 compressed_len, uncompressed_len;
+ guint16 bitmap_type, channel_type;
+ gint width, height, bytespp, offset;
+ guchar **pixels, *pixel;
+ GeglBuffer *buffer;
+
+ block_start = ftell (f);
+
+ while (ftell (f) < block_start + total_len)
+ {
+ null_layer = FALSE;
+ can_handle_layer = FALSE;
+
+ /* Read the layer sub-block header */
+ sub_id = read_block_header (f, &sub_init_len, &sub_total_len, error);
+ if (sub_id == -1)
+ return -1;
+
+ if (sub_id != PSP_LAYER_BLOCK)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid layer sub-block %s, should be LAYER"),
+ block_name (sub_id));
+ return -1;
+ }
+
+ sub_block_start = ftell (f);
+
+ /* Read layer information chunk */
+ if (psp_ver_major >= 4)
+ {
+ if (fread (&chunk_len, 4, 1, f) < 1
+ || fread (&namelen, 2, 1, f) < 1
+ /* A zero length layer name is apparently valid. To not get a warning for
+ namelen < 0 always being false we use this more complicated comparison. */
+ || ((namelen = GUINT16_FROM_LE (namelen)) && (FALSE || namelen == 0))
+ || (name = g_malloc (namelen + 1)) == NULL
+ || (namelen > 0 && fread (name, namelen, 1, f) < 1)
+ || fread (&type, 1, 1, f) < 1
+ || fread (&image_rect, 16, 1, f) < 1
+ || fread (&saved_image_rect, 16, 1, f) < 1
+ || fread (&opacity, 1, 1, f) < 1
+ || fread (&blend_mode, 1, 1, f) < 1
+ || fread (&visibility, 1, 1, f) < 1
+ || fread (&transparency_protected, 1, 1, f) < 1
+ || fread (&link_group_id, 1, 1, f) < 1
+ || fread (&mask_rect, 16, 1, f) < 1
+ || fread (&saved_mask_rect, 16, 1, f) < 1
+ || fread (&mask_linked, 1, 1, f) < 1
+ || fread (&mask_disabled, 1, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer information chunk"));
+ g_free (name);
+ return -1;
+ }
+
+ name[namelen] = 0;
+ layer_name = g_convert (name, -1, "utf-8", "iso8859-1", NULL, NULL, NULL);
+ g_free (name);
+
+ chunk_len = GUINT32_FROM_LE (chunk_len);
+ layer_extension_start = sub_block_start + chunk_len;
+
+ switch (type)
+ {
+ case keGLTFloatingRasterSelection:
+ g_message ("Floating selection restored as normal layer (%s)", layer_name);
+ case keGLTRaster:
+ if (! read_raster_layer_info (f, layer_extension_start,
+ &bitmap_count,
+ &channel_count,
+ error))
+ {
+ g_free (layer_name);
+ return -1;
+ }
+ can_handle_layer = TRUE;
+ break;
+ default:
+ bitmap_count = 0;
+ channel_count = 0;
+ g_message ("Unsupported layer type %s (%s)", layer_type_name(type), layer_name);
+ break;
+ }
+ }
+ else
+ {
+ name = g_malloc (257);
+ name[256] = 0;
+
+ if (fread (name, 256, 1, f) < 1
+ || fread (&type, 1, 1, f) < 1
+ || fread (&image_rect, 16, 1, f) < 1
+ || fread (&saved_image_rect, 16, 1, f) < 1
+ || fread (&opacity, 1, 1, f) < 1
+ || fread (&blend_mode, 1, 1, f) < 1
+ || fread (&visibility, 1, 1, f) < 1
+ || fread (&transparency_protected, 1, 1, f) < 1
+ || fread (&link_group_id, 1, 1, f) < 1
+ || fread (&mask_rect, 16, 1, f) < 1
+ || fread (&saved_mask_rect, 16, 1, f) < 1
+ || fread (&mask_linked, 1, 1, f) < 1
+ || fread (&mask_disabled, 1, 1, f) < 1
+ || fseek (f, 43, SEEK_CUR) < 0
+ || fread (&bitmap_count, 2, 1, f) < 1
+ || fread (&channel_count, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading layer information chunk"));
+ g_free (name);
+ return -1;
+ }
+ layer_name = g_convert (name, -1, "utf-8", "iso8859-1", NULL, NULL, NULL);
+ g_free (name);
+ if (type == PSP_LAYER_FLOATING_SELECTION)
+ g_message ("Floating selection restored as normal layer");
+ type = keGLTRaster;
+ can_handle_layer = TRUE;
+ if (try_fseek (f, sub_block_start + sub_init_len, SEEK_SET, error) < 0)
+ {
+ g_free (layer_name);
+ return -1;
+ }
+ }
+
+ swab_rect (image_rect);
+ swab_rect (saved_image_rect);
+ swab_rect (mask_rect);
+ swab_rect (saved_mask_rect);
+ bitmap_count = GUINT16_FROM_LE (bitmap_count);
+ channel_count = GUINT16_FROM_LE (channel_count);
+
+ layer_mode = gimp_layer_mode_from_psp_blend_mode (blend_mode);
+ if ((int) layer_mode == -1)
+ {
+ g_message ("Unsupported PSP layer blend mode %s "
+ "for layer %s, setting layer invisible",
+ blend_mode_name (blend_mode), layer_name);
+ layer_mode = GIMP_LAYER_MODE_NORMAL_LEGACY;
+ visibility = FALSE;
+ }
+
+ width = saved_image_rect[2] - saved_image_rect[0];
+ height = saved_image_rect[3] - saved_image_rect[1];
+
+ if ((width < 0) || (width > GIMP_MAX_IMAGE_SIZE) /* w <= 2^18 */
+ || (height < 0) || (height > GIMP_MAX_IMAGE_SIZE) /* h <= 2^18 */
+ || ((width / 256) * (height / 256) >= 8192)) /* w * h < 2^29 */
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid layer dimensions: %dx%d"),
+ width, height);
+ g_free (layer_name);
+ return -1;
+ }
+
+ IFDBG(2) g_message
+ ("layer: %s %dx%d (%dx%d) @%d,%d opacity %d blend_mode %s "
+ "%d bitmaps %d channels",
+ layer_name,
+ image_rect[2] - image_rect[0], image_rect[3] - image_rect[1],
+ width, height,
+ image_rect[0]+saved_image_rect[0], image_rect[1]+saved_image_rect[1],
+ opacity, blend_mode_name (blend_mode),
+ bitmap_count, channel_count);
+
+ IFDBG(2) g_message
+ ("mask %dx%d (%dx%d) @%d,%d",
+ mask_rect[2] - mask_rect[0],
+ mask_rect[3] - mask_rect[1],
+ saved_mask_rect[2] - saved_mask_rect[0],
+ saved_mask_rect[3] - saved_mask_rect[1],
+ saved_mask_rect[0], saved_mask_rect[1]);
+
+ if (width == 0)
+ {
+ width++;
+ null_layer = TRUE;
+ }
+ if (height == 0)
+ {
+ height++;
+ null_layer = TRUE;
+ }
+
+ if (ia->base_type == GIMP_RGB)
+ if (bitmap_count == 1)
+ drawable_type = GIMP_RGB_IMAGE, bytespp = 3;
+ else
+ drawable_type = GIMP_RGBA_IMAGE, bytespp = 4;
+ else if (ia->base_type == GIMP_GRAY)
+ if (bitmap_count == 1)
+ drawable_type = GIMP_GRAY_IMAGE, bytespp = 1;
+ else
+ drawable_type = GIMP_GRAYA_IMAGE, bytespp = 2;
+ else
+ if (bitmap_count == 1)
+ drawable_type = GIMP_INDEXED_IMAGE, bytespp = 1;
+ else
+ drawable_type = GIMP_INDEXEDA_IMAGE, bytespp = 2;
+ bytespp *= ia->bytes_per_sample;
+
+ layer_ID = gimp_layer_new (image_ID, layer_name,
+ width, height,
+ drawable_type,
+ 100.0 * opacity / 255.0,
+ layer_mode);
+ g_free (layer_name);
+ if (layer_ID == -1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error creating layer"));
+ return -1;
+ }
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, -1);
+
+ if (image_rect[0] != 0 || image_rect[1] != 0 || saved_image_rect[0] != 0 || saved_image_rect[1] != 0)
+ gimp_layer_set_offsets (layer_ID,
+ image_rect[0] + saved_image_rect[0], image_rect[1] + saved_image_rect[1]);
+
+ if (!visibility)
+ gimp_item_set_visible (layer_ID, FALSE);
+
+ gimp_layer_set_lock_alpha (layer_ID, transparency_protected);
+
+ if (can_handle_layer)
+ {
+ pixel = g_malloc0 (height * width * bytespp);
+ if (null_layer)
+ {
+ pixels = NULL;
+ }
+ else
+ {
+ pixels = g_new (guchar *, height);
+ for (i = 0; i < height; i++)
+ pixels[i] = pixel + width * bytespp * i;
+ }
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ /* Read the layer channel sub-blocks */
+ while (ftell (f) < sub_block_start + sub_total_len)
+ {
+ sub_id = read_block_header (f, &channel_init_len,
+ &channel_total_len, error);
+ if (sub_id == -1)
+ {
+ gimp_image_delete (image_ID);
+ return -1;
+ }
+
+ if (sub_id != PSP_CHANNEL_BLOCK)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid layer sub-block %s, should be CHANNEL"),
+ block_name (sub_id));
+ return -1;
+ }
+
+ channel_start = ftell (f);
+ chunk_len = channel_init_len; /* init chunk_len for psp_ver_major == 3 */
+ if ((psp_ver_major >= 4
+ && (fread (&chunk_len, 4, 1, f) < 1
+ || ((chunk_len = GUINT32_FROM_LE (chunk_len)) < 16)))
+ || fread (&compressed_len, 4, 1, f) < 1
+ || fread (&uncompressed_len, 4, 1, f) < 1
+ || fread (&bitmap_type, 2, 1, f) < 1
+ || fread (&channel_type, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading channel information chunk"));
+ return -1;
+ }
+
+ compressed_len = GUINT32_FROM_LE (compressed_len);
+ uncompressed_len = GUINT32_FROM_LE (uncompressed_len);
+ bitmap_type = GUINT16_FROM_LE (bitmap_type);
+ channel_type = GUINT16_FROM_LE (channel_type);
+
+ if (bitmap_type > PSP_DIB_USER_MASK)
+ {
+ g_message ("Conversion of bitmap type %d is not supported.", bitmap_type);
+ }
+ else if (bitmap_type == PSP_DIB_USER_MASK)
+ {
+ /* FIXME: Add as layer mask */
+ g_message ("Conversion of layer mask is not supported");
+ }
+ else
+ {
+ if (channel_type > PSP_CHANNEL_BLUE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid channel type %d in channel information chunk"),
+ channel_type);
+ return -1;
+ }
+
+ IFDBG(2) g_message ("channel: %s %s %d (%d) bytes %d bytespp",
+ bitmap_type_name (bitmap_type),
+ channel_type_name (channel_type),
+ uncompressed_len, compressed_len,
+ bytespp);
+
+ if (bitmap_type == PSP_DIB_TRANS_MASK || channel_type == PSP_CHANNEL_COMPOSITE)
+ offset = bytespp - ia->bytes_per_sample;
+ else
+ offset = (channel_type - PSP_CHANNEL_RED) * ia->bytes_per_sample;
+
+ if (!null_layer)
+ {
+ if (try_fseek (f, channel_start + chunk_len, SEEK_SET, error) < 0)
+ {
+ return -1;
+ }
+
+ if (read_channel_data (f, ia, pixels, bytespp, offset,
+ buffer, compressed_len, error) == -1)
+ {
+ return -1;
+ }
+ }
+ }
+ if (try_fseek (f, channel_start + channel_total_len, SEEK_SET, error) < 0)
+ {
+ return -1;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, pixel, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ g_free (pixels);
+ g_free (pixel);
+ if (psp_ver_major >= 4)
+ {
+ if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ /* Can't handle this type of layer, skip the data so we can read the next layer. */
+ if (psp_ver_major >= 4)
+ {
+ if (try_fseek (f, sub_block_start + sub_total_len, SEEK_SET, error) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ if (try_fseek (f, block_start + total_len, SEEK_SET, error) < 0)
+ {
+ return -1;
+ }
+
+ return layer_ID;
+}
+
+static gint
+read_tube_block (FILE *f,
+ gint image_ID,
+ guint total_len,
+ PSPimage *ia,
+ GError **error)
+{
+ guint16 version;
+ guchar name[514];
+ guint32 step_size, column_count, row_count, cell_count;
+ guint32 placement_mode, selection_mode;
+ guint32 chunk_len;
+ gint i;
+ GimpPixPipeParams params;
+ GimpParasite *pipe_parasite;
+ gchar *parasite_text;
+
+ gimp_pixpipe_params_init (&params);
+
+ if (psp_ver_major >= 4)
+ {
+ name[0] = 0;
+ if (fread (&chunk_len, 4, 1, f) < 1
+ || fread (&version, 2, 1, f) < 1
+ || fread (&step_size, 4, 1, f) < 1
+ || fread (&column_count, 4, 1, f) < 1
+ || fread (&row_count, 4, 1, f) < 1
+ || fread (&cell_count, 4, 1, f) < 1
+ || fread (&placement_mode, 4, 1, f) < 1
+ || fread (&selection_mode, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading tube data chunk"));
+ return -1;
+ }
+ }
+ else
+ {
+ chunk_len = 0;
+ if (fread (&version, 2, 1, f) < 1
+ || fread (name, 513, 1, f) < 1
+ || fread (&step_size, 4, 1, f) < 1
+ || fread (&column_count, 4, 1, f) < 1
+ || fread (&row_count, 4, 1, f) < 1
+ || fread (&cell_count, 4, 1, f) < 1
+ || fread (&placement_mode, 4, 1, f) < 1
+ || fread (&selection_mode, 4, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading tube data chunk"));
+ return -1;
+ }
+ name[513] = 0;
+ }
+
+ version = GUINT16_FROM_LE (version);
+ params.step = GUINT32_FROM_LE (step_size);
+ params.cols = GUINT32_FROM_LE (column_count);
+ params.rows = GUINT32_FROM_LE (row_count);
+ params.ncells = GUINT32_FROM_LE (cell_count);
+ placement_mode = GUINT32_FROM_LE (placement_mode);
+ selection_mode = GUINT32_FROM_LE (selection_mode);
+
+ for (i = 1; i < params.cols; i++)
+ gimp_image_add_vguide (image_ID, (ia->width * i)/params.cols);
+ for (i = 1; i < params.rows; i++)
+ gimp_image_add_hguide (image_ID, (ia->height * i)/params.rows);
+
+ /* We use a parasite to pass in the tube (pipe) parameters in
+ * case we will have any use of those, for instance in the gpb
+ * plug-in that exports a GIMP image pipe.
+ */
+ params.dim = 1;
+ params.cellwidth = ia->width / params.cols;
+ params.cellheight = ia->height / params.rows;
+ params.placement = (placement_mode == tpmRandom ? "random" :
+ (placement_mode == tpmConstant ? "constant" :
+ "default"));
+ params.rank[0] = params.ncells;
+ params.selection[0] = (selection_mode == tsmRandom ? "random" :
+ (selection_mode == tsmIncremental ? "incremental" :
+ (selection_mode == tsmAngular ? "angular" :
+ (selection_mode == tsmPressure ? "pressure" :
+ (selection_mode == tsmVelocity ? "velocity" :
+ "default")))));
+ parasite_text = gimp_pixpipe_params_build (&params);
+
+ IFDBG(2) g_message ("parasite: %s", parasite_text);
+
+ pipe_parasite = gimp_parasite_new ("gimp-brush-pipe-parameters",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (parasite_text) + 1, parasite_text);
+ gimp_image_attach_parasite (image_ID, pipe_parasite);
+ gimp_parasite_free (pipe_parasite);
+ g_free (parasite_text);
+
+ return 0;
+}
+
+static const gchar *
+compression_name (gint compression)
+{
+ switch (compression)
+ {
+ case PSP_COMP_NONE:
+ return "no compression";
+ case PSP_COMP_RLE:
+ return "RLE";
+ case PSP_COMP_LZ77:
+ return "LZ77";
+ }
+ g_assert_not_reached ();
+
+ return NULL;
+}
+
+/* The main function for loading PSP-images
+ */
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *f;
+ GStatBuf st;
+ char buf[32];
+ PSPimage ia;
+ guint32 block_init_len, block_total_len;
+ long block_start;
+ PSPBlockID id = -1;
+ gint block_number;
+ gint32 image_ID = -1;
+
+ if (g_stat (filename, &st) == -1)
+ return -1;
+
+ f = g_fopen (filename, "rb");
+ if (f == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ /* Read the PSP File Header and determine file version */
+ if (fread (buf, 32, 1, f) < 1
+ || fread (&psp_ver_major, 2, 1, f) < 1
+ || fread (&psp_ver_minor, 2, 1, f) < 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading file header."));
+ goto error;
+ }
+
+ if (memcmp (buf, "Paint Shop Pro Image File\n\032\0\0\0\0\0", 32) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Incorrect file signature."));
+ goto error;
+ }
+
+ psp_ver_major = GUINT16_FROM_LE (psp_ver_major);
+ psp_ver_minor = GUINT16_FROM_LE (psp_ver_minor);
+
+ /* We don't have the documentation for file format versions before 3.0,
+ * but newer versions should be mostly backwards compatible so that
+ * we can still read the image and skip unknown parts safely.
+ */
+ if (psp_ver_major < 3)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported PSP file format version %d.%d."),
+ psp_ver_major, psp_ver_minor);
+ goto error;
+ }
+
+ /* Read all the blocks */
+ block_number = 0;
+
+ IFDBG(3) g_message ("size = %d", (int)st.st_size);
+ while (ftell (f) != st.st_size
+ && (id = read_block_header (f, &block_init_len,
+ &block_total_len, error)) != -1)
+ {
+ block_start = ftell (f);
+
+ if (block_start + block_total_len > st.st_size)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename),
+ _("invalid block size"));
+ goto error;
+ }
+
+ if (id == PSP_IMAGE_BLOCK)
+ {
+ if (block_number != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Duplicate General Image Attributes block."));
+ goto error;
+ }
+ if (read_general_image_attribute_block (f, block_init_len,
+ block_total_len, &ia, error) == -1)
+ {
+ goto error;
+ }
+
+ IFDBG(2) g_message ("%d dpi %dx%d %s",
+ (int) ia.resolution,
+ ia.width, ia.height,
+ compression_name (ia.compression));
+
+ image_ID = gimp_image_new_with_precision (ia.width, ia.height,
+ ia.base_type, ia.precision);
+ if (image_ID == -1)
+ {
+ goto error;
+ }
+
+ gimp_image_set_filename (image_ID, filename);
+
+ gimp_image_set_resolution (image_ID, ia.resolution, ia.resolution);
+ }
+ else
+ {
+ if (block_number == 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Missing General Image Attributes block."));
+ goto error;
+ }
+
+ switch (id)
+ {
+ case PSP_CREATOR_BLOCK:
+ if (read_creator_block (f, image_ID, block_total_len, &ia, error) == -1)
+ goto error;
+ break;
+
+ case PSP_COLOR_BLOCK:
+ if (read_color_block (f, image_ID, block_total_len, &ia, error) == -1)
+ goto error;
+ break;
+
+ case PSP_LAYER_START_BLOCK:
+ if (read_layer_block (f, image_ID, block_total_len, &ia, error) == -1)
+ goto error;
+ break;
+
+ case PSP_SELECTION_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_ALPHA_BANK_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_THUMBNAIL_BLOCK:
+ break; /* No use for it */
+
+ case PSP_EXTENDED_DATA_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_TUBE_BLOCK:
+ if (read_tube_block (f, image_ID, block_total_len, &ia, error) == -1)
+ goto error;
+ break;
+
+ case PSP_COMPOSITE_IMAGE_BANK_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_TABLE_BANK_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_BRUSH_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_ART_MEDIA_BLOCK:
+ case PSP_ART_MEDIA_MAP_BLOCK:
+ case PSP_ART_MEDIA_TILE_BLOCK:
+ case PSP_ART_MEDIA_TEXTURE_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_COLORPROFILE_BLOCK:
+ break; /* Not yet implemented */
+
+ case PSP_LAYER_BLOCK:
+ case PSP_CHANNEL_BLOCK:
+ case PSP_ALPHA_CHANNEL_BLOCK:
+ case PSP_ADJUSTMENT_EXTENSION_BLOCK:
+ case PSP_VECTOR_EXTENSION_BLOCK:
+ case PSP_SHAPE_BLOCK:
+ case PSP_PAINTSTYLE_BLOCK:
+ case PSP_COMPOSITE_ATTRIBUTES_BLOCK:
+ case PSP_JPEG_BLOCK:
+ case PSP_LINESTYLE_BLOCK:
+ case PSP_TABLE_BLOCK:
+ case PSP_PAPER_BLOCK:
+ case PSP_PATTERN_BLOCK:
+ case PSP_GRADIENT_BLOCK:
+ case PSP_GROUP_EXTENSION_BLOCK:
+ case PSP_MASK_EXTENSION_BLOCK:
+ case PSP_RASTER_EXTENSION_BLOCK:
+ g_message ("Sub-block %s should not occur "
+ "at main level of file",
+ block_name (id));
+ break;
+
+ default:
+ g_message ("Unrecognized block id %d", id);
+ break;
+ }
+ }
+
+ if (block_start + block_total_len >= st.st_size)
+ break;
+
+ if (try_fseek (f, block_start + block_total_len, SEEK_SET, error) < 0)
+ goto error;
+
+ block_number++;
+ }
+
+ if (id == -1)
+ {
+ error:
+ fclose (f);
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+ return -1;
+ }
+
+ fclose (f);
+
+ return image_ID;
+}
+
+static gint
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Exporting not implemented yet."));
+
+ return FALSE;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "PSP",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &psvals);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ psvals.compression = (param[5].data.d_int32) ? TRUE : FALSE;
+
+ if (param[5].data.d_int32 < 0 ||
+ param[5].data.d_int32 > PSP_COMP_LZ77)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC, &psvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ gimp_set_data (SAVE_PROC, &psvals, sizeof (PSPSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/common/file-raw-data.c b/plug-ins/common/file-raw-data.c
new file mode 100644
index 0000000..484ee4c
--- /dev/null
+++ b/plug-ins/common/file-raw-data.c
@@ -0,0 +1,2261 @@
+/* Raw data image loader (and saver) plugin 3.4
+ *
+ * by tim copperfield [timecop@japan.co.jp]
+ * http://www.ne.jp/asahi/linux/timecop
+ *
+ * Updated for Gimp 2.1 by pg@futureware.at and mitch@gimp.org
+ *
+ * This plugin is not based on any other plugin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-raw-load"
+#define LOAD_HGT_PROC "file-hgt-load"
+#define SAVE_PROC "file-raw-save"
+#define SAVE_PROC2 "file-raw-save2"
+#define GET_DEFAULTS_PROC "file-raw-get-defaults"
+#define SET_DEFAULTS_PROC "file-raw-set-defaults"
+#define PLUG_IN_BINARY "file-raw-data"
+#define PLUG_IN_ROLE "gimp-file-raw-data"
+#define PREVIEW_SIZE 350
+
+#define RAW_DEFAULTS_PARASITE "raw-save-defaults"
+
+#define GIMP_PLUGIN_HGT_LOAD_ERROR gimp_plugin_hgt_load_error_quark ()
+
+typedef enum
+{
+ GIMP_PLUGIN_HGT_LOAD_ARGUMENT_ERROR
+} GimpPluginHGTError;
+
+static GQuark
+gimp_plugin_hgt_load_error_quark (void)
+{
+ return g_quark_from_static_string ("gimp-plugin-hgt-load-error-quark");
+}
+
+typedef enum
+{
+ RAW_RGB, /* RGB Image */
+ RAW_RGBA, /* RGB Image with an Alpha channel */
+ RAW_RGB565_BE, /* RGB Image 16bit, 5,6,5 bits per channel, big-endian */
+ RAW_RGB565_LE, /* RGB Image 16bit, 5,6,5 bits per channel, little-endian */
+ RAW_BGR565_BE, /* RGB Image 16bit, 5,6,5 bits per channel, big-endian, red and blue swapped */
+ RAW_BGR565_LE, /* RGB Image 16bit, 5,6,5 bits per channel, little-endian, red and blue swapped */
+ RAW_PLANAR, /* Planar RGB */
+ RAW_GRAY_1BPP,
+ RAW_GRAY_2BPP,
+ RAW_GRAY_4BPP,
+ RAW_GRAY_8BPP,
+ RAW_INDEXED, /* Indexed image */
+ RAW_INDEXEDA, /* Indexed image with an Alpha channel */
+ RAW_GRAY_16BPP_BE,
+ RAW_GRAY_16BPP_LE,
+ RAW_GRAY_16BPP_SBE,
+ RAW_GRAY_16BPP_SLE,
+} RawType;
+
+typedef enum
+{
+ RAW_PALETTE_RGB, /* standard RGB */
+ RAW_PALETTE_BGR /* Windows BGRX */
+} RawPaletteType;
+
+typedef struct
+{
+ RawType image_type; /* type of image (RGB, PLANAR) */
+ RawPaletteType palette_type; /* type of palette (RGB/BGR) */
+} RawSaveVals;
+
+typedef struct
+{
+ gboolean run;
+
+ GtkWidget *image_type_standard;
+ GtkWidget *image_type_planar;
+ GtkWidget *palette_type_normal;
+ GtkWidget *palette_type_bmp;
+} RawSaveGui;
+
+typedef struct
+{
+ gint32 file_offset; /* offset to beginning of image in raw data */
+ gint32 image_width; /* width of the raw image */
+ gint32 image_height; /* height of the raw image */
+ RawType image_type; /* type of image (RGB, INDEXED, etc) */
+ gint32 palette_offset; /* offset inside the palette file, if any */
+ RawPaletteType palette_type; /* type of palette (RGB/BGR) */
+} RawConfig;
+
+typedef struct
+{
+ FILE *fp; /* pointer to the already open file */
+ GeglBuffer *buffer; /* gimp drawable buffer */
+ gint32 image_id; /* gimp image id */
+ guchar cmap[768]; /* color map for indexed images */
+} RawGimpData;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* prototypes for the new load functions */
+static gboolean raw_load_standard (RawGimpData *data,
+ gint bpp);
+static gboolean raw_load_gray (RawGimpData *data,
+ gint bpp,
+ gint bitspp);
+static gboolean raw_load_rgb565 (RawGimpData *data,
+ RawType type);
+static gboolean raw_load_planar (RawGimpData *data);
+static gboolean raw_load_palette (RawGimpData *data,
+ const gchar *palette_filename);
+
+/* support functions */
+static goffset get_file_info (const gchar *filename);
+static void raw_read_row (FILE *fp,
+ guchar *buf,
+ gint32 offset,
+ gint32 size);
+static int mmap_read (gint fd,
+ gpointer buf,
+ gint32 len,
+ gint32 pos,
+ gint rowstride);
+static void rgb_565_to_888 (guint16 *in,
+ guchar *out,
+ gint32 num_pixels,
+ RawType type);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ GError **error);
+
+/* gui functions */
+static void preview_update_size (GimpPreviewArea *preview);
+static void preview_update (GimpPreviewArea *preview);
+static void palette_update (GimpPreviewArea *preview);
+static gboolean load_dialog (const gchar *filename,
+ gboolean is_hgt);
+static gboolean save_dialog (gint32 image_id);
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void palette_callback (GtkFileChooser *button,
+ GimpPreviewArea *preview);
+
+static void load_defaults (void);
+static void save_defaults (void);
+static void load_gui_defaults (RawSaveGui *rg);
+
+static RawConfig *runtime = NULL;
+static gchar *palfile = NULL;
+static gint preview_fd = -1;
+static guchar preview_cmap[1024];
+static gboolean preview_cmap_update = TRUE;
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static const RawSaveVals defaults =
+{
+ RAW_RGB,
+ RAW_PALETTE_RGB
+};
+
+static RawSaveVals rawvals;
+
+MAIN()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_hgt_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "samplespacing", "The sample spacing of the data. "
+ "Only supported values are 0, 1 and 3 "
+ "(respectively auto-detect, SRTM-1 "
+ "and SRTM-3 data)" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+#define COMMON_SAVE_ARGS \
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, \
+ { GIMP_PDB_IMAGE, "image", "Input image" }, \
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" }, \
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" }, \
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+
+#define CONFIG_ARGS \
+ { GIMP_PDB_INT32, "image-type", "The image type { RAW_RGB (0), RAW_PLANAR (3) }" }, \
+ { GIMP_PDB_INT32, "palette-type", "The palette type { RAW_PALETTE_RGB (0), RAW_PALETTE_BGR (1) }" }
+
+ static const GimpParamDef save_args[] =
+ {
+ COMMON_SAVE_ARGS
+ };
+
+ static const GimpParamDef save_args2[] =
+ {
+ COMMON_SAVE_ARGS,
+ CONFIG_ARGS
+ };
+
+ static const GimpParamDef save_get_defaults_return_vals[] =
+ {
+ CONFIG_ARGS
+ };
+
+ static const GimpParamDef save_args_set_defaults[] =
+ {
+ CONFIG_ARGS
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Load raw images, specifying image information",
+ "Load raw images, specifying image information",
+ "timecop, pg@futureware.at",
+ "timecop, pg@futureware.at",
+ "Aug 2004",
+ N_("Raw image data"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+ gimp_register_load_handler (LOAD_PROC, "data", "");
+
+ gimp_install_procedure (LOAD_HGT_PROC,
+ "Load HGT data as images",
+ "Load Digital Elevation Model data in HGT format "
+ "from the Shuttle Radar Topography Mission as "
+ "images. Though the output image will be RGB, all "
+ "colors are grayscale by default and the contrast "
+ "will be quite low on most earth relief. Therefore "
+ "You will likely want to remap elevation to colors "
+ "as a second step, for instance with the \"Gradient "
+ "Map\" plug-in.",
+ "",
+ "",
+ "2017-12-09",
+ N_("Digital Elevation Model data"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_hgt_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_hgt_args, load_return_vals);
+ gimp_register_load_handler (LOAD_HGT_PROC, "hgt", "");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Dump images to disk in raw format",
+ "This plug-in dumps images to disk in raw format, "
+ "using the default settings stored as a parasite.",
+ "timecop, pg@futureware.at",
+ "timecop, pg@futureware.at",
+ "Aug 2004",
+ N_("Raw image data"),
+ "INDEXED, GRAY, RGB, RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_install_procedure (SAVE_PROC2,
+ "Dump images to disk in raw format",
+ "Dump images to disk in raw format",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "April 2014",
+ N_("Raw image data"),
+ "INDEXED, GRAY, RGB, RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args2), 0,
+ save_args2, NULL);
+
+ gimp_register_save_handler (SAVE_PROC2, "data,raw", "");
+
+ gimp_install_procedure (GET_DEFAULTS_PROC,
+ "Get the current set of defaults used by the "
+ "raw image data dump plug-in",
+ "This procedure returns the current set of "
+ "defaults stored as a parasite for the raw "
+ "image data dump plug-in. "
+ "These defaults are used to seed the UI, by the "
+ "file_raw_save_defaults procedure, and by "
+ "gimp_file_save when it detects to use RAW.",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "April 2014",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ 0, G_N_ELEMENTS (save_get_defaults_return_vals),
+ NULL, save_get_defaults_return_vals);
+
+ gimp_install_procedure (SET_DEFAULTS_PROC,
+ "Set the current set of defaults used by the "
+ "raw image dump plug-in",
+ "This procedure sets the current set of defaults "
+ "stored as a parasite for the raw image data dump plug-in. "
+ "These defaults are used to seed the UI, by the "
+ "file_raw_save_defaults procedure, and by "
+ "gimp_file_save when it detects to use RAW.",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "Björn Kautler, Bjoern@Kautler.net",
+ "April 2014",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args_set_defaults), 0,
+ save_args_set_defaults, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+ gint32 image_id;
+ gint32 drawable_id;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0 ||
+ strcmp (name, LOAD_HGT_PROC) == 0)
+ {
+ gboolean is_hgt = (strcmp (name, LOAD_HGT_PROC) == 0);
+
+ run_mode = param[0].data.d_int32;
+
+ /* allocate config structure and fill with defaults */
+ runtime = g_new0 (RawConfig, 1);
+
+ runtime->file_offset = 0;
+ runtime->palette_offset = 0;
+ runtime->palette_type = RAW_PALETTE_RGB;
+ if (is_hgt)
+ {
+ FILE *fp;
+ glong pos;
+ gint hgt_size;
+
+ runtime->image_type = RAW_GRAY_16BPP_SBE;
+
+ fp = g_fopen (param[1].data.d_string, "rb");
+ if (! fp)
+ {
+ g_set_error (&error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for size verification: %s"),
+ gimp_filename_to_utf8 (param[1].data.d_string),
+ g_strerror (errno));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ fseek (fp, 0, SEEK_END);
+ pos = ftell (fp);
+
+ /* HGT files have always the same size, either 1201*1201
+ * or 3601*3601 of 16-bit values.
+ */
+ if (pos == 1201*1201*2)
+ {
+ hgt_size = 1201;
+ }
+ else if (pos == 3601*3601*2)
+ {
+ hgt_size = 3601;
+ }
+ else
+ {
+ /* As a special exception, if the file looks like an HGT
+ * format from extension, yet it doesn't have the right
+ * size, we will degrade a bit the experience by
+ * adding sample spacing choice.
+ */
+ hgt_size = 0;
+ }
+ runtime->image_width = hgt_size;
+ runtime->image_height = hgt_size;
+
+ fclose (fp);
+ }
+ }
+ else
+ {
+ runtime->image_width = PREVIEW_SIZE;
+ runtime->image_height = PREVIEW_SIZE;
+ runtime->image_type = RAW_RGB;
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! is_hgt)
+ gimp_get_data (LOAD_PROC, runtime);
+
+ preview_fd = g_open (param[1].data.d_string, O_RDONLY, 0);
+ if (preview_fd < 0)
+ {
+ g_set_error (&error,
+ G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (param[1].data.d_string),
+ g_strerror (errno));
+
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ if (! load_dialog (param[1].data.d_string, is_hgt))
+ status = GIMP_PDB_CANCEL;
+
+ close (preview_fd);
+ }
+ }
+ else if (is_hgt) /* HGT file in non-interactive mode. */
+ {
+ gint32 sample_spacing = param[3].data.d_int32;
+
+ if (sample_spacing != 0 &&
+ sample_spacing != 1 &&
+ sample_spacing != 3)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ g_set_error (&error,
+ GIMP_PLUGIN_HGT_LOAD_ERROR, GIMP_PLUGIN_HGT_LOAD_ARGUMENT_ERROR,
+ _("%d is not a valid sample spacing. "
+ "Valid values are: 0 (auto-detect), 1 and 3."),
+ sample_spacing);
+ }
+ else
+ {
+ switch (sample_spacing)
+ {
+ case 0:
+ /* Auto-detection already occurred. Let's just check if
+ *it was successful.
+ */
+ if (runtime->image_width != 1201 &&
+ runtime->image_width != 3601)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ g_set_error (&error,
+ G_FILE_ERROR, G_FILE_ERROR_INVAL,
+ _("Auto-detection of sample spacing failed. "
+ "\"%s\" does not appear to be a valid HGT file "
+ "or its variant is not supported yet. "
+ "Supported HGT files are: SRTM-1 and SRTM-3. "
+ "If you know the variant, run with argument 1 or 3."),
+ gimp_filename_to_utf8 (param[1].data.d_string));
+ }
+ break;
+ case 1:
+ runtime->image_width = 3601;
+ runtime->image_height = 3601;
+ break;
+ default: /* 3 */
+ runtime->image_width = 1201;
+ runtime->image_height = 1201;
+ break;
+ }
+ status = GIMP_PDB_SUCCESS;
+ }
+ }
+ else
+ {
+ /* we only run interactively due to the nature of this plugin.
+ * things like generate preview etc like to call us non-
+ * interactively. here we stop that.
+ */
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ /* we are okay, and the user clicked OK in the load dialog */
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ image_id = load_image (param[1].data.d_string, &error);
+
+ if (image_id != -1)
+ {
+ if (! is_hgt)
+ gimp_set_data (LOAD_PROC, runtime, sizeof (RawConfig));
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_id;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ g_warning ("Loading \"%s\" failed with error: %s",
+ param[1].data.d_string,
+ error->message);
+ }
+
+ g_free (runtime);
+ }
+ else if (strcmp (name, SAVE_PROC) == 0 ||
+ strcmp (name, SAVE_PROC2) == 0)
+ {
+ run_mode = param[0].data.d_int32;
+ image_id = param[1].data.d_int32;
+ drawable_id = param[2].data.d_int32;
+
+ load_defaults ();
+
+ /* export the image */
+ export = gimp_export_image (&image_id, &drawable_id, "RAW",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ *nreturn_vals = 1;
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (SAVE_PROC, &rawvals);
+
+ /*
+ * Then acquire information with a dialog...
+ */
+ if (! save_dialog (image_id))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are there!
+ */
+ if (nparams != 5)
+ {
+ if (nparams != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ rawvals.image_type = param[5].data.d_int32;
+ rawvals.palette_type = param[6].data.d_int32;
+
+ if (((rawvals.image_type != RAW_RGB) && (rawvals.image_type != RAW_PLANAR)) ||
+ ((rawvals.palette_type != RAW_PALETTE_RGB) && (rawvals.palette_type != RAW_PALETTE_BGR)))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (SAVE_PROC, &rawvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string,
+ image_id, drawable_id, &error))
+ {
+ gimp_set_data (SAVE_PROC, &rawvals, sizeof (rawvals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_id);
+ }
+ else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
+ {
+ load_defaults ();
+
+ *nreturn_vals = 3;
+
+#define SET_VALUE(index, field) G_STMT_START { \
+ values[(index)].type = GIMP_PDB_INT32; \
+ values[(index)].data.d_int32 = rawvals.field; \
+} G_STMT_END
+
+ SET_VALUE (1, image_type);
+ SET_VALUE (2, palette_type);
+
+#undef SET_VALUE
+ }
+ else if (strcmp (name, SET_DEFAULTS_PROC) == 0)
+ {
+ if (nparams == 2)
+ {
+ load_defaults ();
+
+ rawvals.image_type = param[0].data.d_int32;
+ rawvals.palette_type = param[1].data.d_int32;
+
+ save_defaults ();
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* get file size from a filename */
+static goffset
+get_file_info (const gchar *filename)
+{
+ GFile *file = g_file_new_for_path (filename);
+ GFileInfo *info;
+ goffset size = 0;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info)
+ {
+ size = g_file_info_get_size (info);
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (file);
+
+ return size;
+}
+
+/* new image handle functions */
+static void
+raw_read_row (FILE *fp,
+ guchar *buf,
+ gint32 offset,
+ gint32 size)
+{
+ size_t bread;
+
+ fseek (fp, offset, SEEK_SET);
+
+ memset (buf, 0xFF, size);
+ bread = fread (buf, 1, size, fp);
+ if (bread < size)
+ {
+ g_printerr ("fread failed: read %u instead of %u bytes\n", (guint) bread, (guint) size);
+ }
+}
+
+/* similar to the above function but memset is done differently. has nothing
+ * to do with mmap, by the way
+ */
+static gint
+mmap_read (gint fd,
+ void *buf,
+ gint32 len,
+ gint32 pos,
+ gint rowstride)
+{
+ lseek (fd, pos, SEEK_SET);
+ if (! read (fd, buf, len))
+ memset (buf, 0xFF, rowstride);
+ return 0;
+}
+
+/* this handles 1, 2, 3, 4 bpp "standard" images */
+static gboolean
+raw_load_standard (RawGimpData *data,
+ gint bpp)
+{
+ guchar *row = NULL;
+
+ row = g_try_malloc (runtime->image_width * runtime->image_height * bpp);
+ if (! row)
+ return FALSE;
+
+ raw_read_row (data->fp, row, runtime->file_offset,
+ runtime->image_width * runtime->image_height * bpp);
+
+ gegl_buffer_set (data->buffer, GEGL_RECTANGLE (0, 0,
+ runtime->image_width,
+ runtime->image_height), 0,
+ NULL, row, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (row);
+
+ return TRUE;
+}
+
+static gboolean
+raw_load_gray16 (RawGimpData *data,
+ RawType type)
+{
+ guint16 *in_raw = NULL;
+ guint16 *out_raw = NULL;
+ gsize in_size;
+ gsize out_size;
+ gsize i;
+
+ in_size = runtime->image_width * runtime->image_height;
+ out_size = runtime->image_width * runtime->image_height * 3 * sizeof (*out_raw);
+
+ in_raw = g_try_malloc (in_size * sizeof (*in_raw));
+ if (! in_raw)
+ return FALSE;
+
+ out_raw = g_try_malloc0 (out_size);
+ if (! out_raw)
+ {
+ g_free (in_raw);
+ return FALSE;
+ }
+
+ raw_read_row (data->fp, (guchar*) in_raw, runtime->file_offset,
+ in_size * sizeof (*in_raw));
+
+ for (i = 0; i < in_size; i++)
+ {
+ gint pixel_val;
+
+ if (type == RAW_GRAY_16BPP_BE)
+ pixel_val = GUINT16_FROM_BE (in_raw[i]);
+ else if (type == RAW_GRAY_16BPP_LE)
+ pixel_val = GUINT16_FROM_LE (in_raw[i]);
+ else if (type == RAW_GRAY_16BPP_SBE)
+ pixel_val = GINT16_FROM_BE (in_raw[i]) - G_MININT16;
+ else /* if (type == RAW_GRAY_16BPP_SLE)*/
+ pixel_val = GINT16_FROM_LE (in_raw[i]) - G_MININT16;
+
+ out_raw[3 * i + 0] = pixel_val;
+ out_raw[3 * i + 1] = pixel_val;
+ out_raw[3 * i + 2] = pixel_val;
+ }
+
+ gegl_buffer_set (data->buffer, GEGL_RECTANGLE (0, 0,
+ runtime->image_width,
+ runtime->image_height), 0,
+ NULL, out_raw, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (in_raw);
+ g_free (out_raw);
+
+ return TRUE;
+}
+
+/* this handles black and white, gray with 1, 2, 4, and 8 _bits_ per
+ * pixel images - hopefully lots of binaries too
+ */
+static gboolean
+raw_load_gray (RawGimpData *data,
+ gint bpp,
+ gint bitspp)
+{
+ guchar *in_raw = NULL;
+ guchar *out_raw = NULL;
+ gint in_size;
+ gint out_size;
+ guchar pixel_mask_hi;
+ guchar pixel_mask_lo;
+ guint x;
+ gint i;
+
+ in_size = runtime->image_width * runtime->image_height / (8 / bitspp);
+ out_size = runtime->image_width * runtime->image_height * 3;
+
+ in_raw = g_try_malloc (in_size);
+ if (! in_raw)
+ return FALSE;
+
+ out_raw = g_try_malloc (out_size);
+ if (! out_raw)
+ return FALSE;
+ memset (out_raw, 0, out_size);
+
+ /* calculate a pixel_mask_hi
+ 0x80 for 1 bitspp
+ 0xc0 for 2 bitspp
+ 0xf0 for 4 bitspp
+ 0xff for 8 bitspp
+ and a pixel_mask_lo
+ 0x01 for 1 bitspp
+ 0x03 for 2 bitspp
+ 0x0f for 4 bitspp
+ 0xff for 8 bitspp
+ */
+ pixel_mask_hi = 0x80;
+ pixel_mask_lo = 0x01;
+ for (i = 1; i < bitspp; i++)
+ {
+ pixel_mask_hi |= pixel_mask_hi >> 1;
+ pixel_mask_lo |= pixel_mask_lo << 1;
+ }
+
+ raw_read_row (data->fp, in_raw, runtime->file_offset,
+ in_size);
+
+ x = 0; /* walks though all output pixels */
+ for (i = 0; i < in_size; i++)
+ {
+ guchar bit;
+
+ for (bit = 0; bit < 8 / bitspp; bit++)
+ {
+ guchar pixel_val;
+
+ pixel_val = in_raw[i] & (pixel_mask_hi >> (bit * bitspp));
+ pixel_val >>= 8 - bitspp - bit * bitspp;
+ pixel_val *= 0xff / pixel_mask_lo;
+
+ out_raw[3 * x + 0] = pixel_val;
+ out_raw[3 * x + 1] = pixel_val;
+ out_raw[3 * x + 2] = pixel_val;
+
+ x++;
+ }
+ }
+
+ gegl_buffer_set (data->buffer, GEGL_RECTANGLE (0, 0,
+ runtime->image_width,
+ runtime->image_height), 0,
+ NULL, out_raw, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (in_raw);
+ g_free (out_raw);
+
+ return TRUE;
+}
+
+/* this handles RGB565 images */
+static gboolean
+raw_load_rgb565 (RawGimpData *data,
+ RawType type)
+{
+ gint32 num_pixels = runtime->image_width * runtime->image_height;
+ guint16 *in = g_malloc (num_pixels * 2);
+ guchar *row = g_malloc (num_pixels * 3);
+
+ raw_read_row (data->fp, (guchar *)in, runtime->file_offset, num_pixels * 2);
+ rgb_565_to_888 (in, row, num_pixels, type);
+
+ gegl_buffer_set (data->buffer, GEGL_RECTANGLE (0, 0,
+ runtime->image_width,
+ runtime->image_height), 0,
+ NULL, row, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (in);
+ g_free (row);
+
+ return TRUE;
+}
+
+/* this converts a 2bpp buffer to a 3bpp buffer in is a buffer of
+ * 16bit pixels, out is a buffer of 24bit pixels
+ */
+static void
+rgb_565_to_888 (guint16 *in,
+ guchar *out,
+ gint32 num_pixels,
+ RawType type)
+{
+ guint32 i, j;
+ guint16 input;
+ gboolean swap_endian;
+
+ if (G_BYTE_ORDER == G_LITTLE_ENDIAN || G_BYTE_ORDER == G_PDP_ENDIAN)
+ {
+ swap_endian = (type == RAW_RGB565_BE || type == RAW_BGR565_BE);
+ }
+ else if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ {
+ swap_endian = (type == RAW_RGB565_LE || type == RAW_BGR565_LE);
+ }
+
+ switch (type)
+ {
+ case RAW_RGB565_LE:
+ case RAW_RGB565_BE:
+ for (i = 0, j = 0; i < num_pixels; i++)
+ {
+ input = in[i];
+
+ if (swap_endian)
+ input = GUINT16_SWAP_LE_BE (input);
+
+ out[j++] = ((((input >> 11) & 0x1f) * 0x21) >> 2);
+ out[j++] = ((((input >> 5) & 0x3f) * 0x41) >> 4);
+ out[j++] = ((((input >> 0) & 0x1f) * 0x21) >> 2);
+ }
+ break;
+
+ case RAW_BGR565_BE:
+ case RAW_BGR565_LE:
+ for (i = 0, j = 0; i < num_pixels; i++)
+ {
+ input = in[i];
+
+ if (swap_endian)
+ input = GUINT16_SWAP_LE_BE (input);
+
+ out[j++] = ((((input >> 0) & 0x1f) * 0x21) >> 2);
+ out[j++] = ((((input >> 5) & 0x3f) * 0x41) >> 4);
+ out[j++] = ((((input >> 11) & 0x1f) * 0x21) >> 2);
+ }
+ break;
+
+ default:
+ /*This conversion function does not handle the passed in image-type*/
+ g_assert_not_reached ();
+ }
+}
+
+/* this handles 3 bpp "planar" images */
+static gboolean
+raw_load_planar (RawGimpData *data)
+{
+ gint32 r_offset, g_offset, b_offset, i, j, k;
+ guchar *r_row, *b_row, *g_row, *row;
+ gint bpp = 3; /* adding support for alpha channel should be easy */
+
+ /* red, green, blue rows temporary data */
+ r_row = g_malloc (runtime->image_width);
+ g_row = g_malloc (runtime->image_width);
+ b_row = g_malloc (runtime->image_width);
+
+ /* row for the pixel region, after combining RGB together */
+ row = g_malloc (runtime->image_width * bpp);
+
+ r_offset = runtime->file_offset;
+ g_offset = r_offset + runtime->image_width * runtime->image_height;
+ b_offset = g_offset + runtime->image_width * runtime->image_height;
+
+ for (i = 0; i < runtime->image_height; i++)
+ {
+ /* Read R, G, B rows */
+ raw_read_row (data->fp, r_row, r_offset + (runtime->image_width * i),
+ runtime->image_width);
+ raw_read_row (data->fp, g_row, g_offset + (runtime->image_width * i),
+ runtime->image_width);
+ raw_read_row (data->fp, b_row, b_offset + (runtime->image_width * i),
+ runtime->image_width);
+
+ /* Combine separate R, G and B rows into RGB triples */
+ for (j = 0, k = 0; j < runtime->image_width; j++)
+ {
+ row[k++] = r_row[j];
+ row[k++] = g_row[j];
+ row[k++] = b_row[j];
+ }
+
+ gegl_buffer_set (data->buffer,
+ GEGL_RECTANGLE (0, i, runtime->image_width, 1), 0,
+ NULL, row, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((gfloat) i / (gfloat) runtime->image_height);
+ }
+
+ gimp_progress_update (1.0);
+
+ g_free (row);
+ g_free (r_row);
+ g_free (g_row);
+ g_free (b_row);
+
+ return TRUE;
+}
+
+static gboolean
+raw_load_palette (RawGimpData *data,
+ const gchar *palette_file)
+{
+ guchar temp[1024];
+ gint fd, i, j;
+
+ if (palette_file)
+ {
+ fd = g_open (palette_file, O_RDONLY, 0);
+
+ if (! fd)
+ return FALSE;
+
+ lseek (fd, runtime->palette_offset, SEEK_SET);
+
+ switch (runtime->palette_type)
+ {
+ case RAW_PALETTE_RGB:
+ read (fd, data->cmap, 768);
+ break;
+
+ case RAW_PALETTE_BGR:
+ read (fd, temp, 1024);
+ for (i = 0, j = 0; i < 256; i++)
+ {
+ data->cmap[j++] = temp[i * 4 + 2];
+ data->cmap[j++] = temp[i * 4 + 1];
+ data->cmap[j++] = temp[i * 4 + 0];
+ }
+ break;
+ }
+
+ close (fd);
+ }
+ else
+ {
+ /* make a fake grayscale color map */
+ for (i = 0, j = 0; i < 256; i++)
+ {
+ data->cmap[j++] = i;
+ data->cmap[j++] = i;
+ data->cmap[j++] = i;
+ }
+ }
+
+ gimp_image_set_colormap (data->image_id, data->cmap, 256);
+
+ return TRUE;
+}
+
+/* end new image handle functions */
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ const Babl *format = NULL;
+ guchar *cmap = NULL; /* colormap for indexed images */
+ guchar *buf;
+ guchar *components[4] = { 0, };
+ gint n_components;
+ gint32 width, height, bpp;
+ FILE *fp;
+ gint i, j, c;
+ gint palsize = 0;
+ gboolean ret = FALSE;
+
+ /* get info about the current image */
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ format = gimp_drawable_get_format (drawable_id);
+
+ n_components = babl_format_get_n_components (format);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ if (gimp_drawable_is_indexed (drawable_id))
+ cmap = gimp_image_get_colormap (image_id, &palsize);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ buf = g_new (guchar, width * height * bpp);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ fp = g_fopen (filename, "wb");
+
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ ret = TRUE;
+
+ switch (rawvals.image_type)
+ {
+ case RAW_RGB:
+ if (! fwrite (buf, width * height * bpp, 1, fp))
+ {
+ fclose (fp);
+ return FALSE;
+ }
+
+ fclose (fp);
+
+ if (cmap)
+ {
+ /* we have colormap, too.write it into filename+pal */
+ gchar *newfile = g_strconcat (filename, ".pal", NULL);
+ gchar *temp;
+
+ fp = g_fopen (newfile, "wb");
+
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (newfile), g_strerror (errno));
+ return FALSE;
+ }
+
+ switch (rawvals.palette_type)
+ {
+ case RAW_PALETTE_RGB:
+ if (!fwrite (cmap, palsize * 3, 1, fp))
+ ret = FALSE;
+ fclose (fp);
+ break;
+
+ case RAW_PALETTE_BGR:
+ temp = g_malloc0 (palsize * 4);
+ for (i = 0, j = 0; i < palsize * 3; i += 3)
+ {
+ temp[j++] = cmap[i + 2];
+ temp[j++] = cmap[i + 1];
+ temp[j++] = cmap[i + 0];
+ temp[j++] = 0;
+ }
+ if (!fwrite (temp, palsize * 4, 1, fp))
+ ret = FALSE;
+ fclose (fp);
+ g_free (temp);
+ break;
+ }
+ }
+ break;
+
+ case RAW_PLANAR:
+ for (c = 0; c < n_components; c++)
+ components[c] = g_new (guchar, width * height);
+
+ for (i = 0, j = 0; i < width * height * bpp; i += bpp, j++)
+ {
+ for (c = 0; c < n_components; c++)
+ components[c][j] = buf[i + c];
+ }
+
+ ret = TRUE;
+ for (c = 0; c < n_components; c++)
+ {
+ if (! fwrite (components[c], width * height, 1, fp))
+ ret = FALSE;
+
+ g_free (components[c]);
+ }
+
+ fclose (fp);
+ break;
+
+ default:
+ fclose (fp);
+ break;
+ }
+
+ return ret;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ RawGimpData *data;
+ gint32 layer_id = -1;
+ GimpImageType ltype = GIMP_RGB_IMAGE;
+ GimpImageBaseType itype = GIMP_RGB;
+ goffset size;
+ gint bpp = 0;
+ gint bitspp = 8;
+
+ data = g_new0 (RawGimpData, 1);
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ data->fp = g_fopen (filename, "rb");
+ if (! data->fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ size = get_file_info (filename);
+
+ switch (runtime->image_type)
+ {
+ case RAW_RGB: /* standard RGB */
+ case RAW_PLANAR: /* planar RGB */
+ bpp = 3;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+
+ case RAW_RGB565_BE: /* RGB565 big endian */
+ case RAW_RGB565_LE: /* RGB565 little endian */
+ case RAW_BGR565_BE: /* RGB565 big endian */
+ case RAW_BGR565_LE: /* RGB565 little endian */
+ bpp = 2;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+
+ case RAW_RGBA: /* RGB + alpha */
+ bpp = 4;
+ ltype = GIMP_RGBA_IMAGE;
+ itype = GIMP_RGB;
+ break;
+
+ case RAW_GRAY_1BPP:
+ bpp = 1;
+ bitspp = 1;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+ case RAW_GRAY_2BPP:
+ bpp = 1;
+ bitspp = 2;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+ case RAW_GRAY_4BPP:
+ bpp = 1;
+ bitspp = 4;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+ case RAW_GRAY_8BPP:
+ bpp = 1;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+
+ case RAW_INDEXED: /* Indexed */
+ bpp = 1;
+ ltype = GIMP_INDEXED_IMAGE;
+ itype = GIMP_INDEXED;
+ break;
+
+ case RAW_INDEXEDA: /* Indexed + alpha */
+ bpp = 2;
+ ltype = GIMP_INDEXEDA_IMAGE;
+ itype = GIMP_INDEXED;
+ break;
+
+ case RAW_GRAY_16BPP_BE:
+ case RAW_GRAY_16BPP_LE:
+ case RAW_GRAY_16BPP_SBE:
+ case RAW_GRAY_16BPP_SLE:
+ bpp = 2;
+ ltype = GIMP_RGB_IMAGE;
+ itype = GIMP_RGB;
+ break;
+ }
+
+ /* make sure we don't load image bigger than file size */
+ if (runtime->image_height > (size / runtime->image_width / bpp * 8 / bitspp))
+ runtime->image_height = size / runtime->image_width / bpp * 8 / bitspp;
+
+ if (runtime->image_type >= RAW_GRAY_16BPP_BE)
+ data->image_id = gimp_image_new_with_precision (runtime->image_width,
+ runtime->image_height,
+ itype,
+ GIMP_PRECISION_U16_GAMMA);
+ else
+ data->image_id = gimp_image_new (runtime->image_width,
+ runtime->image_height,
+ itype);
+ gimp_image_set_filename (data->image_id, filename);
+ layer_id = gimp_layer_new (data->image_id, _("Background"),
+ runtime->image_width, runtime->image_height,
+ ltype,
+ 100,
+ gimp_image_get_default_new_layer_mode (data->image_id));
+ gimp_image_insert_layer (data->image_id, layer_id, -1, 0);
+
+ data->buffer = gimp_drawable_get_buffer (layer_id);
+
+ switch (runtime->image_type)
+ {
+ case RAW_RGB:
+ case RAW_RGBA:
+ raw_load_standard (data, bpp);
+ break;
+
+ case RAW_RGB565_BE:
+ case RAW_RGB565_LE:
+ case RAW_BGR565_BE:
+ case RAW_BGR565_LE:
+ raw_load_rgb565 (data, runtime->image_type);
+ break;
+
+ case RAW_PLANAR:
+ raw_load_planar (data);
+ break;
+
+ case RAW_GRAY_1BPP:
+ raw_load_gray (data, bpp, bitspp);
+ break;
+ case RAW_GRAY_2BPP:
+ raw_load_gray (data, bpp, bitspp);
+ break;
+ case RAW_GRAY_4BPP:
+ raw_load_gray (data, bpp, bitspp);
+ break;
+ case RAW_GRAY_8BPP:
+ raw_load_gray (data, bpp, bitspp);
+ break;
+
+ case RAW_INDEXED:
+ case RAW_INDEXEDA:
+ raw_load_palette (data, palfile);
+ raw_load_standard (data, bpp);
+ break;
+
+ case RAW_GRAY_16BPP_BE:
+ case RAW_GRAY_16BPP_LE:
+ case RAW_GRAY_16BPP_SBE:
+ case RAW_GRAY_16BPP_SLE:
+ raw_load_gray16 (data, runtime->image_type);
+ break;
+ }
+
+ fclose (data->fp);
+
+ g_object_unref (data->buffer);
+
+ return data->image_id;
+}
+
+
+/* misc GUI stuff */
+
+static void
+preview_update_size (GimpPreviewArea *preview)
+{
+ gtk_widget_set_size_request (GTK_WIDGET (preview),
+ runtime->image_width, runtime->image_height);
+}
+
+static void
+preview_update (GimpPreviewArea *preview)
+{
+ gint width;
+ gint height;
+ gint32 pos;
+ gint x, y;
+ gint bitspp = 0;
+
+ width = MIN (runtime->image_width, preview->width);
+ height = MIN (runtime->image_height, preview->height);
+
+ gimp_preview_area_fill (preview,
+ 0, 0, preview->width, preview->height,
+ 255, 255, 255);
+
+ switch (runtime->image_type)
+ {
+ case RAW_RGB:
+ /* standard RGB image */
+ {
+ guchar *row = g_malloc0 (width * 3);
+
+ for (y = 0; y < height; y++)
+ {
+ pos = runtime->file_offset + runtime->image_width * y * 3;
+ mmap_read (preview_fd, row, width * 3, pos, width * 3);
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGB_IMAGE, row, width * 3);
+ }
+
+ g_free (row);
+ }
+ break;
+
+ case RAW_RGBA:
+ /* RGB + alpha image */
+ {
+ guchar *row = g_malloc0 (width * 4);
+
+ for (y = 0; y < height; y++)
+ {
+ pos = runtime->file_offset + runtime->image_width * y * 4;
+ mmap_read (preview_fd, row, width * 4, pos, width * 4);
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGBA_IMAGE, row, width * 4);
+ }
+
+ g_free (row);
+ }
+ break;
+
+ case RAW_RGB565_BE:
+ case RAW_RGB565_LE:
+ case RAW_BGR565_BE:
+ case RAW_BGR565_LE:
+ /* RGB565 image, big/little endian */
+ {
+ guint16 *in = g_malloc0 (width * 2);
+ guchar *row = g_malloc0 (width * 3);
+
+ for (y = 0; y < height; y++)
+ {
+ pos = runtime->file_offset + runtime->image_width * y * 2;
+ mmap_read (preview_fd, in, width * 2, pos, width * 2);
+ rgb_565_to_888 (in, row, width, runtime->image_type);
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGB_IMAGE, row, width * 3);
+ }
+
+ g_free (row);
+ g_free (in);
+ }
+ break;
+
+ case RAW_PLANAR:
+ {
+ guchar *r_row = g_malloc0 (width);
+ guchar *g_row = g_malloc0 (width);
+ guchar *b_row = g_malloc0 (width);
+ guchar *row = g_malloc0 (width * 3);
+
+ for (y = 0; y < height; y++)
+ {
+ gint j, k;
+
+ pos = (runtime->file_offset +
+ (y * runtime->image_width));
+ mmap_read (preview_fd, r_row, width, pos, width);
+
+ pos = (runtime->file_offset +
+ (runtime->image_width * (runtime->image_height + y)));
+ mmap_read (preview_fd, g_row, width, pos, width);
+
+ pos = (runtime->file_offset +
+ (runtime->image_width * (runtime->image_height * 2 + y)));
+ mmap_read (preview_fd, b_row, width, pos, width);
+
+ for (j = 0, k = 0; j < width; j++)
+ {
+ row[k++] = r_row[j];
+ row[k++] = g_row[j];
+ row[k++] = b_row[j];
+ }
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGB_IMAGE, row, width * 3);
+ }
+
+ g_free (b_row);
+ g_free (g_row);
+ g_free (r_row);
+ g_free (row);
+ }
+ break;
+
+ case RAW_GRAY_1BPP:
+ if (! bitspp) bitspp = 1;
+ case RAW_GRAY_2BPP:
+ if (! bitspp) bitspp = 2;
+ case RAW_GRAY_4BPP:
+ if (! bitspp) bitspp = 4;
+ case RAW_GRAY_8BPP:
+ if (! bitspp) bitspp = 8;
+
+ {
+ guint in_size = height * width / (8 / bitspp);
+ guint out_size = height * width * 3;
+ guchar *in_raw = g_malloc0 (in_size);
+ guchar *out_raw = g_malloc0 (out_size);
+ guchar pixel_mask_hi;
+ guchar pixel_mask_lo;
+ gint i;
+
+ /* calculate a pixel_mask_hi
+ 0x80 for 1 bitspp
+ 0xc0 for 2 bitspp
+ 0xf0 for 4 bitspp
+ 0xff for 8 bitspp
+ and a pixel_mask_lo
+ 0x01 for 1 bitspp
+ 0x03 for 2 bitspp
+ 0x0f for 4 bitspp
+ 0xff for 8 bitspp
+ */
+ pixel_mask_hi = 0x80;
+ pixel_mask_lo = 0x01;
+
+ for (i = 1; i < bitspp; i++)
+ {
+ pixel_mask_hi |= pixel_mask_hi >> 1;
+ pixel_mask_lo |= pixel_mask_lo << 1;
+ }
+
+ mmap_read (preview_fd, in_raw, in_size,
+ runtime->file_offset,
+ in_size);
+
+ x = 0; /* walks though all output pixels */
+ for (i = 0; i < in_size; i++)
+ {
+ guchar bit;
+
+ for (bit = 0; bit < 8 / bitspp; bit++)
+ {
+ guchar pixel_val;
+
+ pixel_val = in_raw[i] & (pixel_mask_hi >> (bit * bitspp));
+ pixel_val >>= 8 - bitspp - bit * bitspp;
+ pixel_val *= 0xff / pixel_mask_lo;
+
+ out_raw[3 * x + 0] = pixel_val;
+ out_raw[3 * x + 1] = pixel_val;
+ out_raw[3 * x + 2] = pixel_val;
+
+ x++;
+ }
+ }
+
+ gimp_preview_area_draw (preview, 0, 0, width, height,
+ GIMP_RGB_IMAGE, out_raw, width * 3);
+ g_free (in_raw);
+ g_free (out_raw);
+ }
+ break;
+
+ case RAW_INDEXED:
+ case RAW_INDEXEDA:
+ /* indexed image */
+ {
+ gboolean alpha = (runtime->image_type == RAW_INDEXEDA);
+ guchar *index = g_malloc0 (width * (alpha ? 2 : 1));
+ guchar *row = g_malloc0 (width * (alpha ? 4 : 3));
+
+ if (preview_cmap_update)
+ {
+ if (palfile)
+ {
+ gint fd;
+
+ fd = g_open (palfile, O_RDONLY, 0);
+ lseek (fd, runtime->palette_offset, SEEK_SET);
+ read (fd, preview_cmap,
+ (runtime->palette_type == RAW_PALETTE_RGB) ? 768 : 1024);
+ close (fd);
+ }
+ else
+ {
+ /* make fake palette, maybe overwrite it later */
+ for (y = 0, x = 0; y < 256; y++)
+ {
+ preview_cmap[x++] = y;
+ preview_cmap[x++] = y;
+
+ if (runtime->palette_type == RAW_PALETTE_RGB)
+ {
+ preview_cmap[x++] = y;
+ }
+ else
+ {
+ preview_cmap[x++] = y;
+ preview_cmap[x++] = 0;
+ }
+ }
+ }
+
+ preview_cmap_update = FALSE;
+ }
+
+ for (y = 0; y < height; y++)
+ {
+ guchar *p = row;
+
+ if (alpha)
+ {
+ pos = runtime->file_offset + runtime->image_width * 2 * y;
+ mmap_read (preview_fd, index, width * 2, pos, width);
+
+ for (x = 0; x < width; x++)
+ {
+ switch (runtime->palette_type)
+ {
+ case RAW_PALETTE_RGB:
+ *p++ = preview_cmap[index[2 * x] * 3 + 0];
+ *p++ = preview_cmap[index[2 * x] * 3 + 1];
+ *p++ = preview_cmap[index[2 * x] * 3 + 2];
+ *p++ = index[2 * x + 1];
+ break;
+ case RAW_PALETTE_BGR:
+ *p++ = preview_cmap[index[2 * x] * 4 + 2];
+ *p++ = preview_cmap[index[2 * x] * 4 + 1];
+ *p++ = preview_cmap[index[2 * x] * 4 + 0];
+ *p++ = index[2 * x + 1];
+ break;
+ }
+ }
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGBA_IMAGE, row, width * 4);
+ }
+ else
+ {
+ pos = runtime->file_offset + runtime->image_width * y;
+ mmap_read (preview_fd, index, width, pos, width);
+
+ for (x = 0; x < width; x++)
+ {
+ switch (runtime->palette_type)
+ {
+ case RAW_PALETTE_RGB:
+ *p++ = preview_cmap[index[x] * 3 + 0];
+ *p++ = preview_cmap[index[x] * 3 + 1];
+ *p++ = preview_cmap[index[x] * 3 + 2];
+ break;
+ case RAW_PALETTE_BGR:
+ *p++ = preview_cmap[index[x] * 4 + 2];
+ *p++ = preview_cmap[index[x] * 4 + 1];
+ *p++ = preview_cmap[index[x] * 4 + 0];
+ break;
+ }
+ }
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_RGB_IMAGE, row, width * 3);
+ }
+ }
+
+ g_free (row);
+ g_free (index);
+ }
+ break;
+
+ case RAW_GRAY_16BPP_BE:
+ case RAW_GRAY_16BPP_LE:
+ case RAW_GRAY_16BPP_SBE:
+ case RAW_GRAY_16BPP_SLE:
+ {
+ guint16 *r_row = g_new (guint16, width);
+ guchar *row = g_malloc (width);
+
+ for (y = 0; y < height; y++)
+ {
+ gint j;
+
+ pos = (runtime->file_offset + (y * runtime->image_width * 2));
+ mmap_read (preview_fd, (guchar*) r_row, 2 * width, pos, width);
+
+ for (j = 0; j < width; j++)
+ {
+ gint pixel_val;
+
+ if (runtime->image_type == RAW_GRAY_16BPP_BE)
+ pixel_val = GUINT16_FROM_BE (r_row[j]);
+ else if (runtime->image_type == RAW_GRAY_16BPP_LE)
+ pixel_val = GUINT16_FROM_LE (r_row[j]);
+ else if (runtime->image_type == RAW_GRAY_16BPP_SBE)
+ pixel_val = GINT16_FROM_BE (r_row[j]) - G_MININT16;
+ else /* if (runtime->image_type == RAW_GRAY_16BPP_SLE)*/
+ pixel_val = GINT16_FROM_LE (r_row[j]) - G_MININT16;
+
+ row[j] = pixel_val / 257;
+ }
+
+ gimp_preview_area_draw (preview, 0, y, width, 1,
+ GIMP_GRAY_IMAGE, row, width * 3);
+ }
+
+ g_free (r_row);
+ g_free (row);
+ }
+ break;
+ }
+}
+
+static void
+palette_update (GimpPreviewArea *preview)
+{
+ preview_cmap_update = TRUE;
+
+ preview_update (preview);
+}
+
+static gboolean
+load_dialog (const gchar *filename,
+ gboolean is_hgt)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *sw;
+ GtkWidget *viewport;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *button;
+ GtkObject *adj;
+ goffset file_size;
+ gboolean run;
+
+ file_size = get_file_info (filename);
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Load Image from Raw Data"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func,
+ is_hgt ? LOAD_HGT_PROC : LOAD_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (main_vbox), sw, TRUE, TRUE, 0);
+ gtk_widget_set_size_request (sw, PREVIEW_SIZE, PREVIEW_SIZE);
+ gtk_widget_show (sw);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (sw), viewport);
+ gtk_widget_show (viewport);
+
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview,
+ runtime->image_width, runtime->image_height);
+ gtk_container_add (GTK_CONTAINER (viewport), preview);
+ gtk_widget_show (preview);
+
+ g_signal_connect_after (preview, "size-allocate",
+ G_CALLBACK (preview_update),
+ NULL);
+
+ if (is_hgt)
+ {
+ if (runtime->image_width == 1201)
+ /* Translators: Digital Elevation Model (DEM) is a technical term
+ * used for 3D surface modeling or relief maps; so it must be
+ * translated by the proper technical term in your language.
+ */
+ frame = gimp_frame_new (_("Digital Elevation Model data (1 arc-second)"));
+ else if (runtime->image_width == 3601)
+ frame = gimp_frame_new (_("Digital Elevation Model data (3 arc-seconds)"));
+ else
+ frame = gimp_frame_new (_("Digital Elevation Model data"));
+ }
+ else
+ {
+ frame = gimp_frame_new (_("Image"));
+ }
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ combo = NULL;
+ if (is_hgt &&
+ runtime->image_width != 1201 &&
+ runtime->image_width != 3601)
+ {
+ /* When auto-detection of the HGT variant failed, let's just
+ * default to SRTM-3 and show a dropdown list.
+ */
+ runtime->image_width = 1201;
+ runtime->image_height = 1201;
+
+ /* 2 types of HGT files are possible: SRTM-1 and SRTM-3.
+ * From the documentation: https://dds.cr.usgs.gov/srtm/version1/Documentation/SRTM_Topo.txt
+ * "SRTM-1 data are sampled at one arc-second of latitude and longitude and
+ * each file contains 3601 lines and 3601 samples.
+ * [...]
+ * SRTM-3 data are sampled at three arc-seconds and contain 1201 lines and
+ * 1201 samples with similar overlapping rows and columns."
+ */
+ combo = gimp_int_combo_box_new (_("SRTM-1 (1 arc-second)"), 3601,
+ _("SRTM-3 (3 arc-seconds)"), 1201,
+ NULL);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Sample Spacing:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &runtime->image_width);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &runtime->image_height);
+ /* By default, SRTM-3 is active. */
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 1201);
+ }
+ else if (! is_hgt)
+ {
+ /* Generic case for any data. Let's leave choice to select the
+ * right type of raw data.
+ */
+ combo = gimp_int_combo_box_new (_("RGB"), RAW_RGB,
+ _("RGB Alpha"), RAW_RGBA,
+ _("RGB565 Big Endian"), RAW_RGB565_BE,
+ _("RGB565 Little Endian"), RAW_RGB565_LE,
+ _("BGR565 Big Endian"), RAW_BGR565_BE,
+ _("BGR565 Little Endian"), RAW_BGR565_LE,
+ _("Planar RGB"), RAW_PLANAR,
+ _("B&W 1 bit"), RAW_GRAY_1BPP,
+ _("Gray 2 bit"), RAW_GRAY_2BPP,
+ _("Gray 4 bit"), RAW_GRAY_4BPP,
+ _("Gray 8 bit"), RAW_GRAY_8BPP,
+ _("Indexed"), RAW_INDEXED,
+ _("Indexed Alpha"), RAW_INDEXEDA,
+ _("Gray unsigned 16 bit Big Endian"), RAW_GRAY_16BPP_BE,
+ _("Gray unsigned 16 bit Little Endian"), RAW_GRAY_16BPP_LE,
+ _("Gray 16 bit Big Endian"), RAW_GRAY_16BPP_SBE,
+ _("Gray 16 bit Little Endian"), RAW_GRAY_16BPP_SLE,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ runtime->image_type);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Image _Type:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &runtime->image_type);
+ }
+ if (combo)
+ g_signal_connect_swapped (combo, "changed",
+ G_CALLBACK (preview_update),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("O_ffset:"), -1, 9,
+ runtime->file_offset, 0, file_size, 1, 1000, 0,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &runtime->file_offset);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (preview_update),
+ preview);
+
+ if (! is_hgt)
+ {
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Width:"), -1, 9,
+ runtime->image_width, 1, file_size, 1, 10, 0,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &runtime->image_width);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (preview_update_size),
+ preview);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (preview_update),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("_Height:"), -1, 9,
+ runtime->image_height, 1, file_size, 1, 10, 0,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &runtime->image_height);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (preview_update_size),
+ preview);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (preview_update),
+ preview);
+ }
+
+
+ frame = gimp_frame_new (_("Palette"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new (_("R, G, B (normal)"), RAW_PALETTE_RGB,
+ _("B, G, R, X (BMP style)"), RAW_PALETTE_BGR,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ runtime->palette_type);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Palette Type:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &runtime->palette_type);
+ g_signal_connect_swapped (combo, "changed",
+ G_CALLBACK (palette_update),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Off_set:"), -1, 0,
+ runtime->palette_offset, 0, 1 << 24, 1, 768, 0,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &runtime->palette_offset);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (palette_update),
+ preview);
+
+ button = gtk_file_chooser_button_new (_("Select Palette File"),
+ GTK_FILE_CHOOSER_ACTION_OPEN);
+ if (palfile)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (button), palfile);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Pal_ette File:"), 0.0, 0.5,
+ button, 2, FALSE);
+
+ g_signal_connect (button, "selection-changed",
+ G_CALLBACK (palette_callback),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static GtkWidget *
+radio_button_init (GtkBuilder *builder,
+ const gchar *name,
+ gint item_data,
+ gint initial_value,
+ gpointer value_pointer)
+{
+ GtkWidget *radio = NULL;
+
+ radio = GTK_WIDGET (gtk_builder_get_object (builder, name));
+ if (item_data)
+ g_object_set_data (G_OBJECT (radio), "gimp-item-data", GINT_TO_POINTER (item_data));
+ if (initial_value == item_data)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
+ g_signal_connect (radio, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ value_pointer);
+
+ return radio;
+}
+
+static gboolean
+save_dialog (gint32 image_id)
+{
+ RawSaveGui rg;
+ GtkWidget *dialog;
+ GtkBuilder *builder;
+ gchar *ui_file;
+ GError *error = NULL;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /* Dialog init */
+ dialog = gimp_export_dialog_new (_("Raw Image"), PLUG_IN_BINARY, SAVE_PROC);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_dialog_response),
+ &rg);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ /* GtkBuilder init */
+ builder = gtk_builder_new ();
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui/plug-ins/plug-in-file-raw.ui",
+ NULL);
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ gchar *display_name = g_filename_display_name (ui_file);
+ g_printerr (_("Error loading UI file '%s': %s"),
+ display_name, error ? error->message : _("Unknown error"));
+ g_free (display_name);
+ }
+
+ g_free (ui_file);
+
+ /* VBox */
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ GTK_WIDGET (gtk_builder_get_object (builder, "vbox")),
+ FALSE, FALSE, 0);
+
+ /* Radios */
+ rg.image_type_standard = radio_button_init (builder, "image-type-standard",
+ RAW_RGB,
+ rawvals.image_type,
+ &rawvals.image_type);
+ rg.image_type_planar = radio_button_init (builder, "image-type-planar",
+ RAW_PLANAR,
+ rawvals.image_type,
+ &rawvals.image_type);
+ rg.palette_type_normal = radio_button_init (builder, "palette-type-normal",
+ RAW_PALETTE_RGB,
+ rawvals.palette_type,
+ &rawvals.palette_type);
+ rg.palette_type_bmp = radio_button_init (builder, "palette-type-bmp",
+ RAW_PALETTE_BGR,
+ rawvals.palette_type,
+ &rawvals.palette_type);
+
+ /* Load/save defaults buttons */
+ g_signal_connect_swapped (gtk_builder_get_object (builder, "load-defaults"),
+ "clicked",
+ G_CALLBACK (load_gui_defaults),
+ &rg);
+
+ g_signal_connect_swapped (gtk_builder_get_object (builder, "save-defaults"),
+ "clicked",
+ G_CALLBACK (save_defaults),
+ &rg);
+
+ /* Show dialog and run */
+ gtk_widget_show (dialog);
+
+ rg.run = FALSE;
+
+ gtk_main ();
+
+ return rg.run;
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ RawSaveGui *rg = data;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ rg->run = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+
+static void
+palette_callback (GtkFileChooser *button,
+ GimpPreviewArea *preview)
+{
+ if (palfile)
+ g_free (palfile);
+
+ palfile = gtk_file_chooser_get_filename (button);
+
+ palette_update (preview);
+}
+
+static void
+load_defaults (void)
+{
+ GimpParasite *parasite;
+
+ /* initialize with hardcoded defaults */
+ rawvals = defaults;
+
+ parasite = gimp_get_parasite (RAW_DEFAULTS_PARASITE);
+
+ if (parasite)
+ {
+ gchar *def_str;
+ RawSaveVals tmpvals = defaults;
+ gint num_fields;
+
+ def_str = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+
+ gimp_parasite_free (parasite);
+
+ num_fields = sscanf (def_str, "%d %d",
+ (int *) &tmpvals.image_type,
+ (int *) &tmpvals.palette_type);
+
+ g_free (def_str);
+
+ if (num_fields == 2)
+ rawvals = tmpvals;
+ }
+}
+
+static void
+save_defaults (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+
+ def_str = g_strdup_printf ("%d %d",
+ rawvals.image_type,
+ rawvals.palette_type);
+
+ parasite = gimp_parasite_new (RAW_DEFAULTS_PARASITE,
+ GIMP_PARASITE_PERSISTENT,
+ strlen (def_str), def_str);
+
+ gimp_attach_parasite (parasite);
+
+ gimp_parasite_free (parasite);
+ g_free (def_str);
+}
+
+static void
+load_gui_defaults (RawSaveGui *rg)
+{
+ load_defaults ();
+
+#define SET_ACTIVE(field, datafield) \
+ if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (rg->field), "gimp-item-data")) == rawvals.datafield) \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rg->field), TRUE)
+
+ SET_ACTIVE (image_type_standard, image_type);
+ SET_ACTIVE (image_type_planar, image_type);
+ SET_ACTIVE (palette_type_normal, palette_type);
+ SET_ACTIVE (palette_type_bmp, palette_type);
+
+#undef SET_ACTIVE
+}
diff --git a/plug-ins/common/file-sunras.c b/plug-ins/common/file-sunras.c
new file mode 100644
index 0000000..62928cf
--- /dev/null
+++ b/plug-ins/common/file-sunras.c
@@ -0,0 +1,1784 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * SUN raster reading and writing code Copyright (C) 1996 Peter Kirchgessner
+ * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* This program was written using pages 625-629 of the book
+ * "Encyclopedia of Graphics File Formats", Murray/van Ryper,
+ * O'Reilly & Associates Inc.
+ * Bug reports or suggestions should be e-mailed to peter@kirchgessner.net
+ */
+
+/* Event history:
+ * V 1.00, PK, 25-Jul-96: First try
+ * V 1.90, PK, 15-Mar-97: Upgrade to work with GIMP V0.99
+ * V 1.91, PK, 05-Apr-97: Return all arguments, even in case of an error
+ * V 1.92, PK, 18-May-97: Ignore EOF-error on reading image data
+ * V 1.93, PK, 05-Oct-97: Parse rc file
+ * V 1.94, PK, 12-Oct-97: No progress bars for non-interactive mode
+ * V 1.95, nn, 20-Dec-97: Initialize some variable
+ * V 1.96, PK, 21-Nov-99: Internationalization
+ * V 1.97, PK, 20-Dec-00: Recognize extensions .rs and .ras too
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-sunras-load"
+#define SAVE_PROC "file-sunras-save"
+#define PLUG_IN_BINARY "file-sunras"
+#define PLUG_IN_ROLE "gimp-file-sunras"
+
+
+typedef int WRITE_FUN(void*,size_t,size_t,FILE*);
+
+typedef gulong L_CARD32;
+typedef gushort L_CARD16;
+typedef guchar L_CARD8;
+
+/* Fileheader of SunRaster files */
+typedef struct
+{
+ L_CARD32 l_ras_magic; /* Magic Number */
+ L_CARD32 l_ras_width; /* Width */
+ L_CARD32 l_ras_height; /* Height */
+ L_CARD32 l_ras_depth; /* Number of bits per pixel (1,8,24,32) */
+ L_CARD32 l_ras_length; /* Length of image data (but may also be 0) */
+ L_CARD32 l_ras_type; /* Encoding */
+ L_CARD32 l_ras_maptype; /* Type of colormap */
+ L_CARD32 l_ras_maplength;/* Number of bytes for colormap */
+} L_SUNFILEHEADER;
+
+/* Sun-raster magic */
+#define RAS_MAGIC 0x59a66a95
+
+#define RAS_TYPE_STD 1 /* Standard uncompressed format */
+#define RAS_TYPE_RLE 2 /* Runlength compression format */
+
+typedef struct
+{
+ gint val; /* The value that is to be repeated */
+ gint n; /* How many times it is repeated */
+} RLEBUF;
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static void set_color_table (gint32 image_ID,
+ L_SUNFILEHEADER *sunhdr,
+ const guchar *suncolmap);
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID,
+ GeglBuffer **buffer);
+
+static gint32 load_sun_d1 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap);
+static gint32 load_sun_d8 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap);
+static gint32 load_sun_d24 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap);
+static gint32 load_sun_d32 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap);
+
+static L_CARD32 read_card32 (FILE *ifp,
+ int *err);
+
+static void write_card32 (FILE *ofp,
+ L_CARD32 c);
+
+static void byte2bit (guchar *byteline,
+ int width,
+ guchar *bitline,
+ gboolean invert);
+
+static void rle_startread (FILE *ifp);
+static int rle_fread (char *ptr,
+ int sz,
+ int nelem,
+ FILE *ifp);
+static int rle_fgetc (FILE *ifp);
+#define rle_getc(fp) ((rlebuf.n > 0) ? (rlebuf.n)--,rlebuf.val : rle_fgetc (fp))
+
+static void rle_startwrite (FILE *ofp);
+static int rle_fwrite (char *ptr,
+ int sz,
+ int nelem,
+ FILE *ofp);
+static int rle_fputc (int val,
+ FILE *ofp);
+static int rle_putrun (int n,
+ int val,
+ FILE *ofp);
+static void rle_endwrite (FILE *ofp);
+#define rle_putc rle_fputc
+
+static void read_sun_header (FILE *ifp,
+ L_SUNFILEHEADER *sunhdr);
+static void write_sun_header (FILE *ofp,
+ L_SUNFILEHEADER *sunhdr);
+static void read_sun_cols (FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *colormap);
+static void write_sun_cols (FILE *ofp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *colormap);
+
+static gint save_index (FILE *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean grey,
+ gboolean rle);
+static gint save_rgb (FILE *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean rle);
+
+static gboolean save_dialog (void);
+
+/* Portability kludge */
+static int my_fwrite (void *ptr,
+ int size,
+ int nmemb,
+ FILE *stream);
+
+
+static int read_msb_first = 1;
+static RLEBUF rlebuf;
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+/* Export info */
+typedef struct
+{
+ gboolean rle; /* rle or standard */
+} SUNRASSaveVals;
+
+
+static SUNRASSaveVals psvals =
+{
+ TRUE /* rle */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "rle", "Specify non-zero for rle output, zero for standard output" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "load file of the SunRaster file format",
+ "load file of the SunRaster file format",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("SUN Rasterfile image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-sun-raster");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "im1,im8,im24,im32,rs,ras,sun",
+ "",
+ "0,long,0x59a66a95");
+
+ gimp_install_procedure (SAVE_PROC,
+ "export file in the SunRaster file format",
+ "SUNRAS exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("SUN Rasterfile image"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-sun-raster");
+ gimp_register_save_handler (SAVE_PROC,
+ "im1,im8,im24,im32,rs,ras,sun", "");
+}
+
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "SUNRAS",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &psvals);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ psvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &psvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ /* Store psvals data */
+ gimp_set_data (SAVE_PROC, &psvals, sizeof (SUNRASSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 image_ID;
+ FILE *ifp;
+ L_SUNFILEHEADER sunhdr;
+ guchar *suncolmap = NULL;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ ifp = g_fopen (filename, "rb");
+ if (!ifp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ read_msb_first = 1; /* SUN raster is always most significant byte first */
+
+ read_sun_header (ifp, &sunhdr);
+ if (sunhdr.l_ras_magic != RAS_MAGIC)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' as SUN-raster-file"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ return -1;
+ }
+
+ if (sunhdr.l_ras_type > 5)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s",
+ _("The type of this SUN-rasterfile is not supported"));
+ fclose (ifp);
+ return -1;
+ }
+
+ if (sunhdr.l_ras_maplength > (256 * 3))
+ {
+ g_message ("Map lengths greater than 256 entries are unsupported by GIMP.");
+ gimp_quit ();
+ }
+
+ /* Is there a RGB colormap ? */
+ if ((sunhdr.l_ras_maptype == 1) && (sunhdr.l_ras_maplength > 0))
+ {
+ suncolmap = g_new (guchar, sunhdr.l_ras_maplength);
+
+ read_sun_cols (ifp, &sunhdr, suncolmap);
+#ifdef DEBUG
+ {
+ int j, ncols;
+ printf ("File %s\n",filename);
+ ncols = sunhdr.l_ras_maplength/3;
+ for (j=0; j < ncols; j++)
+ printf ("Entry 0x%08x: 0x%04x, 0x%04x, 0x%04x\n",
+ j,suncolmap[j],suncolmap[j+ncols],suncolmap[j+2*ncols]);
+ }
+#endif
+ if (sunhdr.l_ras_magic != RAS_MAGIC)
+ {
+ g_message (_("Could not read color entries from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ g_free (suncolmap);
+ return -1;
+ }
+ }
+ else if (sunhdr.l_ras_maplength > 0)
+ {
+ g_message (_("Type of colormap not supported"));
+ fseek (ifp, (sizeof (L_SUNFILEHEADER)/sizeof (L_CARD32))
+ *4 + sunhdr.l_ras_maplength, SEEK_SET);
+ }
+
+ if (sunhdr.l_ras_width <= 0)
+ {
+ g_message (_("'%s':\nNo image width specified"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ return -1;
+ }
+
+ if (sunhdr.l_ras_width > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage width is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ return -1;
+ }
+
+ if (sunhdr.l_ras_height <= 0)
+ {
+ g_message (_("'%s':\nNo image height specified"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ return -1;
+ }
+
+ if (sunhdr.l_ras_height > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage height is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ fclose (ifp);
+ return -1;
+ }
+
+ switch (sunhdr.l_ras_depth)
+ {
+ case 1: /* bitmap */
+ image_ID = load_sun_d1 (filename, ifp, &sunhdr, suncolmap);
+ break;
+
+ case 8: /* 256 colors */
+ image_ID = load_sun_d8 (filename, ifp, &sunhdr, suncolmap);
+ break;
+
+ case 24: /* True color */
+ image_ID = load_sun_d24 (filename, ifp, &sunhdr, suncolmap);
+ break;
+
+ case 32: /* True color with extra byte */
+ image_ID = load_sun_d32 (filename, ifp, &sunhdr, suncolmap);
+ break;
+
+ default:
+ image_ID = -1;
+ break;
+ }
+ gimp_progress_update (1.0);
+
+ fclose (ifp);
+
+ g_free (suncolmap);
+
+ if (image_ID == -1)
+ {
+ g_message (_("This image depth is not supported"));
+ return -1;
+ }
+
+ return image_ID;
+}
+
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ FILE *ofp;
+ GimpImageType drawable_type;
+ gboolean retval;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("SUNRAS export cannot handle images with alpha channels"));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_GRAY_IMAGE:
+ case GIMP_RGB_IMAGE:
+ break;
+ default:
+ g_message (_("Can't operate on unknown image types"));
+ return FALSE;
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Open the output file. */
+ ofp = g_fopen (filename, "wb");
+ if (! ofp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ if (drawable_type == GIMP_INDEXED_IMAGE)
+ retval = save_index (ofp,image_ID, drawable_ID, FALSE, psvals.rle);
+ else if (drawable_type == GIMP_GRAY_IMAGE)
+ retval = save_index (ofp,image_ID, drawable_ID, TRUE, psvals.rle);
+ else if (drawable_type == GIMP_RGB_IMAGE)
+ retval = save_rgb (ofp,image_ID, drawable_ID, psvals.rle);
+ else
+ retval = FALSE;
+
+ fclose (ofp);
+
+ return retval;
+}
+
+
+static L_CARD32
+read_card32 (FILE *ifp,
+ gint *err)
+{
+ L_CARD32 c;
+
+ if (read_msb_first)
+ {
+ c = (((L_CARD32)(getc (ifp))) << 24);
+ c |= (((L_CARD32)(getc (ifp))) << 16);
+ c |= (((L_CARD32)(getc (ifp))) << 8);
+ c |= ((L_CARD32)(*err = getc (ifp)));
+ }
+ else
+ {
+ c = ((L_CARD32)(getc (ifp)));
+ c |= (((L_CARD32)(getc (ifp))) << 8);
+ c |= (((L_CARD32)(getc (ifp))) << 16);
+ c |= (((L_CARD32)(*err = getc (ifp))) << 24);
+ }
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+
+static void
+write_card32 (FILE *ofp,
+ L_CARD32 c)
+{
+ putc ((int)((c >> 24) & 0xff), ofp);
+ putc ((int)((c >> 16) & 0xff), ofp);
+ putc ((int)((c >> 8) & 0xff), ofp);
+ putc ((int)((c) & 0xff), ofp);
+}
+
+
+/* Convert n bytes of 0/1 to a line of bits */
+static void
+byte2bit (guchar *byteline,
+ gint width,
+ guchar *bitline,
+ gboolean invert)
+{
+ guchar bitval;
+ guchar rest[8];
+
+ while (width >= 8)
+ {
+ bitval = 0;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ if (*(byteline++)) bitval |= 0x01;
+ *(bitline++) = invert ? ~bitval : bitval;
+ width -= 8;
+ }
+ if (width > 0)
+ {
+ memset (rest, 0, 8);
+ memcpy (rest, byteline, width);
+ bitval = 0;
+ byteline = rest;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ *bitline = invert ? ~bitval : bitval;
+ }
+}
+
+
+/* Start reading Runlength Encoded Data */
+static void
+rle_startread (FILE *ifp)
+{
+ /* Clear RLE-buffer */
+ rlebuf.val = rlebuf.n = 0;
+}
+
+
+/* Read uncompressed elements from RLE-stream */
+static gint
+rle_fread (gchar *ptr,
+ gint sz,
+ gint nelem,
+ FILE *ifp)
+{
+ int elem_read, cnt, val, err = 0;
+
+ for (elem_read = 0; elem_read < nelem; elem_read++)
+ {
+ for (cnt = 0; cnt < sz; cnt++)
+ {
+ val = rle_getc (ifp);
+
+ if (val < 0)
+ {
+ err = 1;
+ break;
+ }
+
+ *(ptr++) = (char)val;
+ }
+
+ if (err)
+ break;
+ }
+
+ return elem_read;
+}
+
+
+/* Get one byte of uncompressed data from RLE-stream */
+static gint
+rle_fgetc (FILE *ifp)
+{
+ int flag, runcnt, runval;
+
+ if (rlebuf.n > 0) /* Something in the buffer ? */
+ {
+ (rlebuf.n)--;
+ return rlebuf.val;
+ }
+
+ /* Nothing in the buffer. We have to read something */
+ if ((flag = getc (ifp)) < 0) return -1;
+ if (flag != 0x0080) return flag; /* Single byte run ? */
+
+ if ((runcnt = getc (ifp)) < 0) return -1;
+ if (runcnt == 0) return 0x0080; /* Single 0x80 ? */
+
+ /* The run */
+ if ((runval = getc (ifp)) < 0) return -1;
+ rlebuf.n = runcnt;
+ rlebuf.val = runval;
+
+ return runval;
+}
+
+
+/* Start writing Runlength Encoded Data */
+static void
+rle_startwrite (FILE *ofp)
+{
+ /* Clear RLE-buffer */
+ rlebuf.val = rlebuf.n = 0;
+}
+
+
+/* Write uncompressed elements to RLE-stream */
+static gint
+rle_fwrite (gchar *ptr,
+ gint sz,
+ gint nelem,
+ FILE *ofp)
+{
+ int elem_write, cnt, val, err = 0;
+ guchar *pixels = (unsigned char *)ptr;
+
+ for (elem_write = 0; elem_write < nelem; elem_write++)
+ {
+ for (cnt = 0; cnt < sz; cnt++)
+ {
+ val = rle_fputc (*(pixels++), ofp);
+ if (val < 0)
+ {
+ err = 1;
+ break;
+ }
+ }
+
+ if (err)
+ break;
+ }
+
+ return elem_write;
+}
+
+
+/* Write uncompressed character to RLE-stream */
+static gint
+rle_fputc (gint val,
+ FILE *ofp)
+{
+ int retval;
+
+ if (rlebuf.n == 0) /* Nothing in the buffer ? Save the value */
+ {
+ rlebuf.n = 1;
+ rlebuf.val = val;
+
+ return val;
+ }
+
+ /* Something in the buffer */
+
+ if (rlebuf.val == val) /* Same value in the buffer ? */
+ {
+ (rlebuf.n)++;
+ if (rlebuf.n == 257) /* Can not be encoded in a single run ? */
+ {
+ retval = rle_putrun (256, rlebuf.val, ofp);
+ if (retval < 0)
+ return retval;
+
+ rlebuf.n -= 256;
+ }
+
+ return val;
+ }
+
+ /* Something different in the buffer ? Write out the run */
+
+ retval = rle_putrun (rlebuf.n, rlebuf.val, ofp);
+ if (retval < 0)
+ return retval;
+
+ /* Save the new value */
+ rlebuf.n = 1;
+ rlebuf.val = val;
+
+ return val;
+}
+
+
+/* Write out a run with 0 < n < 257 */
+static gint
+rle_putrun (gint n,
+ gint val,
+ FILE *ofp)
+{
+ int retval, flag = 0x80;
+
+ /* Useful to write a 3 byte run ? */
+ if ((n > 2) || ((n == 2) && (val == flag)))
+ {
+ putc (flag, ofp);
+ putc (n-1, ofp);
+ retval = putc (val, ofp);
+ }
+ else if (n == 2) /* Write two single runs (could not be value 0x80) */
+ {
+ putc (val, ofp);
+ retval = putc (val, ofp);
+ }
+ else /* Write a single run */
+ {
+ if (val == flag)
+ retval = putc (flag, ofp), putc (0x00, ofp);
+ else
+ retval = putc (val, ofp);
+ }
+
+ return (retval < 0) ? retval : val;
+}
+
+
+/* End writing Runlength Encoded Data */
+static void
+rle_endwrite (FILE *ofp)
+{
+ if (rlebuf.n > 0)
+ {
+ rle_putrun (rlebuf.n, rlebuf.val, ofp);
+ rlebuf.val = rlebuf.n = 0; /* Clear RLE-buffer */
+ }
+}
+
+
+static void
+read_sun_header (FILE *ifp,
+ L_SUNFILEHEADER *sunhdr)
+{
+ int j, err;
+ L_CARD32 *cp;
+
+ cp = (L_CARD32 *)sunhdr;
+
+ /* Read in all 32-bit values of the header and check for byte order */
+ for (j = 0; j < sizeof (L_SUNFILEHEADER) / sizeof(sunhdr->l_ras_magic); j++)
+ {
+ *(cp++) = read_card32 (ifp, &err);
+ if (err)
+ break;
+ }
+
+ if (err)
+ sunhdr->l_ras_magic = 0; /* Not a valid SUN-raster file */
+}
+
+
+/* Write out a SUN-fileheader */
+
+static void
+write_sun_header (FILE *ofp,
+ L_SUNFILEHEADER *sunhdr)
+{
+ int j, hdr_entries;
+ L_CARD32 *cp;
+
+ hdr_entries = sizeof (L_SUNFILEHEADER) / sizeof(sunhdr->l_ras_magic);
+
+ cp = (L_CARD32 *)sunhdr;
+
+ /* Write out all 32-bit values of the header and check for byte order */
+ for (j = 0; j < hdr_entries; j++)
+ {
+ write_card32 (ofp, *(cp++));
+ }
+}
+
+
+/* Read the sun colormap */
+
+static void
+read_sun_cols (FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *colormap)
+{
+ int ncols, err = 0;
+
+ /* Read in SUN-raster Colormap */
+ ncols = sunhdr->l_ras_maplength / 3;
+ if (ncols <= 0)
+ err = 1;
+ else
+ err = (fread (colormap, 3, ncols, ifp) != ncols);
+
+ if (err)
+ sunhdr->l_ras_magic = 0; /* Not a valid SUN-raster file */
+}
+
+
+/* Write a sun colormap */
+
+static void
+write_sun_cols (FILE *ofp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *colormap)
+{
+ int ncols;
+
+ ncols = sunhdr->l_ras_maplength / 3;
+ fwrite (colormap, 3, ncols, ofp);
+}
+
+
+/* Set a GIMP colortable using the sun colormap */
+
+static void
+set_color_table (gint32 image_ID,
+ L_SUNFILEHEADER *sunhdr,
+ const guchar *suncolmap)
+{
+ guchar ColorMap[256 * 3];
+ gint ncols, j;
+
+ ncols = sunhdr->l_ras_maplength / 3;
+ if (ncols <= 0)
+ return;
+
+ for (j = 0; j < MIN (ncols, 256); j++)
+ {
+ ColorMap[j * 3 + 0] = suncolmap[j];
+ ColorMap[j * 3 + 1] = suncolmap[j + ncols];
+ ColorMap[j * 3 + 2] = suncolmap[j + 2 * ncols];
+ }
+
+#ifdef DEBUG
+ printf ("Set GIMP colortable:\n");
+ for (j = 0; j < ncols; j++)
+ printf ("%3d: 0x%02x 0x%02x 0x%02x\n", j,
+ ColorMap[j*3], ColorMap[j*3+1], ColorMap[j*3+2]);
+#endif
+
+ gimp_image_set_colormap (image_ID, ColorMap, ncols);
+}
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ gint32 *layer_ID,
+ GeglBuffer **buffer)
+{
+ gint32 image_ID;
+ GimpImageType gdtype;
+
+ switch (type)
+ {
+ case GIMP_RGB:
+ gdtype = GIMP_RGB_IMAGE;
+ break;
+ case GIMP_GRAY:
+ gdtype = GIMP_GRAY_IMAGE;
+ break;
+ case GIMP_INDEXED:
+ gdtype = GIMP_INDEXED_IMAGE;
+ break;
+ default:
+ g_warning ("Unsupported image type");
+ return -1;
+ }
+
+ image_ID = gimp_image_new (width, height, type);
+ gimp_image_set_filename (image_ID, filename);
+
+ *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ *buffer = gimp_drawable_get_buffer (*layer_ID);
+
+ return image_ID;
+}
+
+
+/* Load SUN-raster-file with depth 1 */
+static gint32
+load_sun_d1 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap)
+{
+ int pix8;
+ int width, height, linepad, scan_lines, tile_height;
+ int i, j;
+ guchar *dest, *data;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+ guchar bit2byte[256 * 8];
+ L_SUNFILEHEADER sun_bwhdr;
+ guchar sun_bwcolmap[6] = { 255,0,255,0,255,0 };
+ int err = 0;
+ gboolean rle = (sunhdr->l_ras_type == RAS_TYPE_RLE);
+
+ width = sunhdr->l_ras_width;
+ height = sunhdr->l_ras_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_INDEXED,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ if (suncolmap != NULL) /* Set up the specified color map */
+ {
+ set_color_table (image_ID, sunhdr, suncolmap);
+ }
+ else /* No colormap available. Set up a dummy b/w-colormap */
+ { /* Copy the original header and simulate b/w-colormap */
+ memcpy ((char *)&sun_bwhdr,(char *)sunhdr,sizeof (L_SUNFILEHEADER));
+ sun_bwhdr.l_ras_maptype = 2;
+ sun_bwhdr.l_ras_maplength = 6;
+ set_color_table (image_ID, &sun_bwhdr, sun_bwcolmap);
+ }
+
+ /* Get an array for mapping 8 bits in a byte to 8 bytes */
+ dest = bit2byte;
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ *(dest++) = ((j & (1 << i)) != 0);
+
+ linepad = (((sunhdr->l_ras_width+7)/8) % 2); /* Check for 16bit align */
+
+ if (rle)
+ rle_startread (ifp);
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ j = width;
+ while (j >= 8)
+ {
+ pix8 = rle ? rle_getc (ifp) : getc (ifp);
+ if (pix8 < 0) { err = 1; pix8 = 0; }
+
+ memcpy (dest, bit2byte + pix8*8, 8);
+ dest += 8;
+ j -= 8;
+ }
+
+ if (j > 0)
+ {
+ pix8 = rle ? rle_getc (ifp) : getc (ifp);
+ if (pix8 < 0) { err = 1; pix8 = 0; }
+
+ memcpy (dest, bit2byte + pix8*8, j);
+ dest += j;
+ }
+
+ if (linepad)
+ err |= ((rle ? rle_getc (ifp) : getc (ifp)) < 0);
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+
+/* Load SUN-raster-file with depth 8 */
+
+static gint32
+load_sun_d8 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap)
+{
+ int width, height, linepad, i, j;
+ gboolean grayscale;
+ gint ncols;
+ int scan_lines, tile_height;
+ guchar *dest, *data;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+ int err = 0;
+ gboolean rle = (sunhdr->l_ras_type == RAS_TYPE_RLE);
+
+ width = sunhdr->l_ras_width;
+ height = sunhdr->l_ras_height;
+
+ /* This could also be a grayscale image. Check it */
+ ncols = sunhdr->l_ras_maplength / 3;
+
+ grayscale = TRUE; /* Also grayscale if no colormap present */
+
+ if ((ncols > 0) && (suncolmap != NULL))
+ {
+ for (j = 0; j < ncols; j++)
+ {
+ if ((suncolmap[j] != j) ||
+ (suncolmap[j + ncols] != j) ||
+ (suncolmap[j + 2 * ncols] != j))
+ {
+ grayscale = FALSE;
+ break;
+ }
+ }
+ }
+
+ image_ID = create_new_image (filename, width, height,
+ grayscale ? GIMP_GRAY : GIMP_INDEXED,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ if (!grayscale)
+ set_color_table (image_ID, sunhdr, suncolmap);
+
+ linepad = (sunhdr->l_ras_width % 2);
+
+ if (rle)
+ rle_startread (ifp); /* Initialize RLE-buffer */
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ memset ((char *)dest, 0, width);
+ err |= ((rle ? rle_fread ((char *)dest, 1, width, ifp)
+ : fread ((char *)dest, 1, width, ifp)) != width);
+
+ if (linepad)
+ err |= ((rle ? rle_getc (ifp) : getc (ifp)) < 0);
+
+ dest += width;
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+
+/* Load SUN-raster-file with depth 24 */
+static gint32
+load_sun_d24 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap)
+{
+ guchar *dest, blue;
+ guchar *data;
+ int width, height, linepad, tile_height, scan_lines;
+ int i, j;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+ int err = 0;
+ gboolean rle = (sunhdr->l_ras_type == RAS_TYPE_RLE);
+
+ width = sunhdr->l_ras_width;
+ height = sunhdr->l_ras_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ linepad = ((sunhdr->l_ras_width*3) % 2);
+
+ if (rle)
+ rle_startread (ifp); /* Initialize RLE-buffer */
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ memset ((char *)dest, 0, 3*width);
+ err |= ((rle ? rle_fread ((char *)dest, 3, width, ifp)
+ : fread ((char *)dest, 3, width, ifp)) != width);
+
+ if (linepad)
+ err |= ((rle ? rle_getc (ifp) : getc (ifp)) < 0);
+
+ if (sunhdr->l_ras_type == 3) /* RGB-format ? That is what GIMP wants */
+ {
+ dest += width * 3;
+ }
+ else /* We have BGR format. Correct it */
+ {
+ for (j = 0; j < width; j++)
+ {
+ blue = *dest;
+ *dest = *(dest+2);
+ *(dest+2) = blue;
+ dest += 3;
+ }
+ }
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i + 1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i + 1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+
+/* Load SUN-raster-file with depth 32 */
+
+static gint32
+load_sun_d32 (const gchar *filename,
+ FILE *ifp,
+ L_SUNFILEHEADER *sunhdr,
+ guchar *suncolmap)
+{
+ guchar *dest, blue;
+ guchar *data;
+ int width, height, tile_height, scan_lines;
+ int i, j;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+ int err = 0;
+ int cerr;
+ gboolean rle = (sunhdr->l_ras_type == RAS_TYPE_RLE);
+
+ width = sunhdr->l_ras_width;
+ height = sunhdr->l_ras_height;
+
+ /* initialize */
+
+ cerr = 0;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ if (rle)
+ rle_startread (ifp); /* Initialize RLE-buffer */
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if (rle)
+ {
+ for (j = 0; j < width; j++)
+ {
+ rle_getc (ifp); /* Skip unused byte */
+ *(dest++) = rle_getc (ifp);
+ *(dest++) = rle_getc (ifp);
+ *(dest++) = (cerr = (rle_getc (ifp)));
+ }
+ }
+ else
+ {
+ for (j = 0; j < width; j++)
+ {
+ getc (ifp); /* Skip unused byte */
+ *(dest++) = getc (ifp);
+ *(dest++) = getc (ifp);
+ *(dest++) = (cerr = (getc (ifp)));
+ }
+ }
+ err |= (cerr < 0);
+
+ if (sunhdr->l_ras_type != 3) /* BGR format ? Correct it */
+ {
+ for (j = 0; j < width; j++)
+ {
+ dest -= 3;
+ blue = *dest;
+ *dest = *(dest+2);
+ *(dest+2) = blue;
+ }
+ dest += width*3;
+ }
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i + 1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i + 1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+
+static gint
+save_index (FILE *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean grey,
+ gboolean rle)
+{
+ int height, width, linepad, i, j;
+ int ncols, bw, is_bw, is_wb, bpl;
+ int tile_height;
+ long tmp = 0;
+ guchar *cmap, *bwline = NULL;
+ guchar *data, *src;
+ L_SUNFILEHEADER sunhdr;
+ guchar sun_colormap[256*3];
+ static guchar sun_bwmap[6] = { 0, 255, 0, 255, 0, 255 };
+ static guchar sun_wbmap[6] = { 255, 0, 255, 0, 255, 0 };
+ unsigned char *suncolmap = sun_colormap;
+ GeglBuffer *buffer;
+ const Babl *format;
+ WRITE_FUN *write_fun;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ if (grey)
+ format = babl_format ("Y' u8");
+ else
+ format = gegl_buffer_get_format (buffer);
+
+ /* allocate a buffer for retrieving information from the buffer */
+ src = data = g_malloc (tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ /* Fill SUN-color map */
+ if (grey)
+ {
+ ncols = 256;
+
+ for (j = 0; j < ncols; j++)
+ {
+ suncolmap[j] = j;
+ suncolmap[j + ncols] = j;
+ suncolmap[j + ncols * 2] = j;
+ }
+ }
+ else
+ {
+ cmap = gimp_image_get_colormap (image_ID, &ncols);
+
+ for (j = 0; j < ncols; j++)
+ {
+ suncolmap[j] = *(cmap++);
+ suncolmap[j + ncols] = *(cmap++);
+ suncolmap[j + ncols * 2] = *(cmap++);
+ }
+ }
+
+ bw = (ncols == 2); /* Maybe this is a two-color image */
+ if (bw)
+ {
+ bwline = g_malloc ((width + 7) / 8);
+ if (bwline == NULL)
+ bw = 0;
+ }
+
+ is_bw = is_wb = 0;
+ if (bw) /* The Sun-OS imagetool generates index 0 for white and */
+ { /* index 1 for black. Do the same without colortable. */
+ is_bw = (memcmp (suncolmap, sun_bwmap, 6) == 0);
+ is_wb = (memcmp (suncolmap, sun_wbmap, 6) == 0);
+ }
+
+ /* Number of data bytes per line */
+ bpl = bw ? (width+7)/8 : width;
+ linepad = bpl % 2;
+
+ /* Fill in the SUN header */
+ sunhdr.l_ras_magic = RAS_MAGIC;
+ sunhdr.l_ras_width = width;
+ sunhdr.l_ras_height = height;
+ sunhdr.l_ras_depth = bw ? 1 : 8;
+ sunhdr.l_ras_length = (bpl + linepad) * height;
+ sunhdr.l_ras_type = rle ? RAS_TYPE_RLE : RAS_TYPE_STD;
+
+ if (is_bw || is_wb) /* No colortable for real b/w images */
+ {
+ sunhdr.l_ras_maptype = 0; /* No colormap */
+ sunhdr.l_ras_maplength = 0; /* Length of colormap */
+ }
+ else
+ {
+ sunhdr.l_ras_maptype = 1; /* RGB colormap */
+ sunhdr.l_ras_maplength = ncols*3; /* Length of colormap */
+ }
+
+ write_sun_header (ofp, &sunhdr);
+
+ if (sunhdr.l_ras_maplength > 0)
+ write_sun_cols (ofp, &sunhdr, suncolmap);
+
+#define GET_INDEX_TILE(begin) \
+ {int scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0, \
+ format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ if (rle)
+ {
+ write_fun = (WRITE_FUN *) &rle_fwrite;
+ rle_startwrite (ofp);
+ }
+ else
+ {
+ write_fun = (WRITE_FUN *) &my_fwrite;
+ }
+
+ if (bw) /* Two color image */
+ {
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_INDEX_TILE (data); /* Get more data */
+
+ byte2bit (src, width, bwline, is_bw);
+ (*write_fun) (bwline, bpl, 1, ofp);
+ if (linepad)
+ (*write_fun) ((char *)&tmp, linepad, 1, ofp);
+ src += width;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double) i / (double) height);
+ }
+ }
+ else /* Color or grey-image */
+ {
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_INDEX_TILE (data); /* Get more data */
+
+ (*write_fun) ((char *)src, width, 1, ofp);
+ if (linepad)
+ (*write_fun) ((char *)&tmp, linepad, 1, ofp);
+ src += width;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double) i / (double) height);
+ }
+ }
+
+#undef GET_INDEX_TILE
+
+ if (rle)
+ rle_endwrite (ofp);
+
+ g_free (data);
+
+ if (bwline)
+ g_free (bwline);
+
+ g_object_unref (buffer);
+
+ if (ferror (ofp))
+ {
+ g_message (_("Write error occurred"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static gint
+save_rgb (FILE *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean rle)
+{
+ int height, width, tile_height, linepad;
+ int i, j, bpp;
+ guchar *data, *src;
+ L_SUNFILEHEADER sunhdr;
+ GeglBuffer *buffer;
+ const Babl *format;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ tile_height = gimp_tile_height ();
+
+ format = babl_format ("R'G'B' u8");
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_malloc (tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+/* #define SUNRAS_32 */
+#ifdef SUNRAS_32
+ bpp = 4;
+#else
+ bpp = 3;
+#endif
+ linepad = (width * bpp) % 2;
+
+ /* Fill in the SUN header */
+ sunhdr.l_ras_magic = RAS_MAGIC;
+ sunhdr.l_ras_width = width;
+ sunhdr.l_ras_height = height;
+ sunhdr.l_ras_depth = 8 * bpp;
+ sunhdr.l_ras_length = (width * bpp + linepad) * height;
+ sunhdr.l_ras_type = rle ? RAS_TYPE_RLE : RAS_TYPE_STD;
+ sunhdr.l_ras_maptype = 0; /* No colormap */
+ sunhdr.l_ras_maplength = 0; /* Length of colormap */
+
+ write_sun_header (ofp, &sunhdr);
+
+#define GET_RGB_TILE(begin) \
+ {int scan_lines; \
+ scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0, \
+ format, begin, \
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); \
+ src = begin; }
+
+ if (! rle)
+ {
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_RGB_TILE (data); /* Get more data */
+
+ for (j = 0; j < width; j++)
+ {
+ if (bpp == 4) putc (0, ofp); /* Dummy */
+ putc (*(src + 2), ofp); /* Blue */
+ putc (*(src + 1), ofp); /* Green */
+ putc (*src, ofp); /* Red */
+ src += 3;
+ }
+
+ for (j = 0; j < linepad; j++)
+ putc (0, ofp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double) i / (double) height);
+ }
+ }
+ else /* Write runlength encoded */
+ {
+ rle_startwrite (ofp);
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ GET_RGB_TILE (data); /* Get more data */
+
+ for (j = 0; j < width; j++)
+ {
+ if (bpp == 4) rle_putc (0, ofp); /* Dummy */
+ rle_putc (*(src + 2), ofp); /* Blue */
+ rle_putc (*(src + 1), ofp); /* Green */
+ rle_putc (*src, ofp); /* Red */
+ src += 3;
+ }
+
+ for (j = 0; j < linepad; j++)
+ rle_putc (0, ofp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double) i / (double) height);
+ }
+
+ rle_endwrite (ofp);
+ }
+
+#undef GET_RGB_TILE
+
+ g_free (data);
+
+ g_object_unref (buffer);
+
+ if (ferror (ofp))
+ {
+ g_message (_("Write error occurred"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Save interface functions */
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("SUNRAS"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* file save type */
+ frame = gimp_int_radio_group_new (TRUE, _("Data Formatting"),
+ G_CALLBACK (gimp_radio_button_update),
+ &psvals.rle, psvals.rle,
+
+ _("_RunLength Encoded"), TRUE, NULL,
+ _("_Standard"), FALSE, NULL,
+
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static int
+my_fwrite (void *ptr,
+ int size,
+ int nmemb,
+ FILE *stream)
+{
+ return fwrite (ptr, size, nmemb, stream);
+}
diff --git a/plug-ins/common/file-svg.c b/plug-ins/common/file-svg.c
new file mode 100644
index 0000000..6786bee
--- /dev/null
+++ b/plug-ins/common/file-svg.c
@@ -0,0 +1,914 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* SVG loader plug-in
+ * (C) Copyright 2003 Dom Lachowicz <cinamod@hotmail.com>
+ *
+ * Largely rewritten in September 2003 by Sven Neumann <sven@gimp.org>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <librsvg/rsvg.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-svg-load"
+#define LOAD_THUMB_PROC "file-svg-load-thumb"
+#define PLUG_IN_BINARY "file-svg"
+#define PLUG_IN_ROLE "gimp-file-svg"
+#define SVG_VERSION "2.5.0"
+#define SVG_DEFAULT_RESOLUTION 90.0
+#define SVG_DEFAULT_SIZE 500
+#define SVG_PREVIEW_SIZE 128
+
+
+typedef struct
+{
+ gdouble resolution;
+ gint width;
+ gint height;
+ gboolean import;
+ gboolean merge;
+} SvgLoadVals;
+
+static SvgLoadVals load_vals =
+{
+ SVG_DEFAULT_RESOLUTION,
+ 0,
+ 0,
+ FALSE,
+ FALSE
+};
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static GdkPixbuf * load_rsvg_pixbuf (const gchar *filename,
+ SvgLoadVals *vals,
+ GError **error);
+static gboolean load_rsvg_size (const gchar *filename,
+ SvgLoadVals *vals,
+ GError **error);
+static GimpPDBStatusType load_dialog (const gchar *filename,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run,
+};
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
+ { GIMP_PDB_FLOAT, "resolution",
+ "Resolution to use for rendering the SVG (defaults to 90 dpi)" },
+ { GIMP_PDB_INT32, "width",
+ "Width (in pixels) to load the SVG in. "
+ "(0 for original width, a negative width to specify a maximum width)" },
+ { GIMP_PDB_INT32, "height",
+ "Height (in pixels) to load the SVG in. "
+ "(0 for original height, a negative width to specify a maximum height)"},
+ { GIMP_PDB_INT32, "paths",
+ "Whether to not import paths (0), import paths individually (1) "
+ "or merge all imported paths (2)" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the SVG file format",
+ "Renders SVG files to raster graphics using librsvg.",
+ "Dom Lachowicz, Sven Neumann",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ SVG_VERSION,
+ N_("SVG image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/svg+xml");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "svg", "",
+ "0,string,<?xml,0,string,<svg");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Generates a thumbnail of an SVG image",
+ "Renders a thumbnail of an SVG file using librsvg.",
+ "Dom Lachowicz, Sven Neumann",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ SVG_VERSION,
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gimp_get_data (LOAD_PROC, &load_vals);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams > 3) load_vals.resolution = param[3].data.d_float;
+ if (nparams > 4) load_vals.width = param[4].data.d_int32;
+ if (nparams > 5) load_vals.height = param[5].data.d_int32;
+ if (nparams > 6)
+ {
+ load_vals.import = param[6].data.d_int32 != FALSE;
+ load_vals.merge = param[6].data.d_int32 > TRUE;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ status = load_dialog (param[1].data.d_string, &error);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+ }
+
+ /* Don't clamp this; insane values are probably not meant to be
+ * used as resolution anyway.
+ */
+ if (load_vals.resolution < GIMP_MIN_RESOLUTION ||
+ load_vals.resolution > GIMP_MAX_RESOLUTION)
+ {
+ load_vals.resolution = SVG_DEFAULT_RESOLUTION;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ const gchar *filename = param[1].data.d_string;
+ gint32 image_ID = load_image (filename, &error);
+
+ if (image_ID != -1)
+ {
+ if (load_vals.import)
+ {
+ gint32 *vectors;
+ gint num_vectors;
+
+ gimp_vectors_import_from_file (image_ID, filename,
+ load_vals.merge, TRUE,
+ &num_vectors, &vectors);
+ if (num_vectors > 0)
+ g_free (vectors);
+ }
+
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_set_data (LOAD_PROC, &load_vals, sizeof (load_vals));
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ const gchar *filename = param[0].data.d_string;
+ gint width = 0;
+ gint height = 0;
+ gint32 image_ID;
+
+ if (load_rsvg_size (filename, &load_vals, NULL))
+ {
+ width = load_vals.width;
+ height = load_vals.height;
+ }
+
+ load_vals.resolution = SVG_DEFAULT_RESOLUTION;
+ load_vals.width = - param[1].data.d_int32;
+ load_vals.height = - param[1].data.d_int32;
+
+ image_ID = load_image (filename, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 4;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **load_error)
+{
+ gint32 image;
+ gint32 layer;
+ GdkPixbuf *pixbuf;
+ gint width;
+ gint height;
+ GError *error = NULL;
+
+ pixbuf = load_rsvg_pixbuf (filename, &load_vals, &error);
+
+ if (! pixbuf)
+ {
+ /* Do not rely on librsvg setting GError on failure! */
+ g_set_error (load_error,
+ error ? error->domain : 0, error ? error->code : 0,
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename),
+ error ? error->message : _("Unknown reason"));
+ g_clear_error (&error);
+
+ return -1;
+ }
+
+ gimp_progress_init (_("Rendering SVG"));
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ image = gimp_image_new (width, height, GIMP_RGB);
+ gimp_image_undo_disable (image);
+
+ gimp_image_set_filename (image, filename);
+ gimp_image_set_resolution (image,
+ load_vals.resolution, load_vals.resolution);
+
+ layer = gimp_layer_new_from_pixbuf (image, _("Rendered SVG"), pixbuf,
+ 100,
+ gimp_image_get_default_new_layer_mode (image),
+ 0.0, 1.0);
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ gimp_image_undo_enable (image);
+
+ return image;
+}
+
+/* This is the callback used from load_rsvg_pixbuf(). */
+static void
+load_set_size_callback (gint *width,
+ gint *height,
+ gpointer data)
+{
+ SvgLoadVals *vals = data;
+
+ if (*width < 1 || *height < 1)
+ {
+ *width = SVG_DEFAULT_SIZE;
+ *height = SVG_DEFAULT_SIZE;
+ }
+
+ if (!vals->width || !vals->height)
+ return;
+
+ /* either both arguments negative or none */
+ if ((vals->width * vals->height) < 0)
+ return;
+
+ if (vals->width > 0)
+ {
+ *width = vals->width;
+ *height = vals->height;
+ }
+ else
+ {
+ gdouble w = *width;
+ gdouble h = *height;
+ gdouble aspect = (gdouble) vals->width / (gdouble) vals->height;
+
+ if (aspect > (w / h))
+ {
+ *height = abs (vals->height);
+ *width = (gdouble) abs (vals->width) * (w / h) + 0.5;
+ }
+ else
+ {
+ *width = abs (vals->width);
+ *height = (gdouble) abs (vals->height) / (w / h) + 0.5;
+ }
+
+ vals->width = *width;
+ vals->height = *height;
+ }
+}
+
+/* This function renders a pixbuf from an SVG file according to vals. */
+static GdkPixbuf *
+load_rsvg_pixbuf (const gchar *filename,
+ SvgLoadVals *vals,
+ GError **error)
+{
+ GdkPixbuf *pixbuf = NULL;
+ RsvgHandle *handle;
+ GFile *file;
+
+ file = g_file_new_for_path (filename);
+
+ handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL, error);
+
+ g_object_unref (file);
+
+ if (!handle)
+ {
+ return NULL;
+ }
+
+ rsvg_handle_set_dpi (handle, vals->resolution);
+ rsvg_handle_set_size_callback (handle, load_set_size_callback, vals, NULL);
+
+ pixbuf = rsvg_handle_get_pixbuf (handle);
+
+ g_object_unref (handle);
+
+ return pixbuf;
+}
+
+static GtkWidget *size_label = NULL;
+
+/* This function retrieves the pixel size from an SVG file. */
+static gboolean
+load_rsvg_size (const gchar *filename,
+ SvgLoadVals *vals,
+ GError **error)
+{
+ RsvgHandle *handle;
+ GFile *file;
+ RsvgDimensionData dim;
+ gboolean has_size;
+
+ file = g_file_new_for_path (filename);
+
+ handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL, error);
+
+ g_object_unref (file);
+
+ if (!handle)
+ {
+ return FALSE;
+ }
+
+ rsvg_handle_set_dpi (handle, vals->resolution);
+
+ rsvg_handle_get_dimensions (handle, &dim);
+
+ if (dim.width > 0 && dim.height > 0)
+ {
+ vals->width = dim.width;
+ vals->height = dim.height;
+ has_size = TRUE;
+ }
+ else
+ {
+ vals->width = SVG_DEFAULT_SIZE;
+ vals->height = SVG_DEFAULT_SIZE;
+ has_size = FALSE;
+ }
+
+ if (size_label)
+ {
+ if (has_size)
+ {
+ gchar *text = g_strdup_printf (_("%d × %d"),
+ vals->width, vals->height);
+ gtk_label_set_text (GTK_LABEL (size_label), text);
+ g_free (text);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (size_label),
+ _("SVG file does not\nspecify a size!"));
+ }
+ }
+
+ g_object_unref (handle);
+
+ if (vals->width < 1) vals->width = 1;
+ if (vals->height < 1) vals->height = 1;
+
+ return TRUE;
+}
+
+
+/* User interface */
+
+static GimpSizeEntry *size = NULL;
+static GtkAdjustment *xadj = NULL;
+static GtkAdjustment *yadj = NULL;
+static GtkWidget *constrain = NULL;
+static gdouble ratio_x = 1.0;
+static gdouble ratio_y = 1.0;
+static gint svg_width = 0;
+static gint svg_height = 0;
+
+static void load_dialog_set_ratio (gdouble x,
+ gdouble y);
+
+
+static void
+load_dialog_size_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
+ {
+ gdouble x = gimp_size_entry_get_refval (size, 0) / (gdouble) svg_width;
+ gdouble y = gimp_size_entry_get_refval (size, 1) / (gdouble) svg_height;
+
+ if (x != ratio_x)
+ {
+ load_dialog_set_ratio (x, x);
+ }
+ else if (y != ratio_y)
+ {
+ load_dialog_set_ratio (y, y);
+ }
+ }
+}
+
+static void
+load_dialog_ratio_callback (GtkAdjustment *adj,
+ gpointer data)
+{
+ gdouble x = gtk_adjustment_get_value (xadj);
+ gdouble y = gtk_adjustment_get_value (yadj);
+
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
+ {
+ if (x != ratio_x)
+ y = x;
+ else
+ x = y;
+ }
+
+ load_dialog_set_ratio (x, y);
+}
+
+static void
+load_dialog_resolution_callback (GimpSizeEntry *res,
+ const gchar *filename)
+{
+ SvgLoadVals vals = { 0.0, 0, 0 };
+
+ load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);
+
+ if (!load_rsvg_size (filename, &vals, NULL))
+ return;
+
+ g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
+
+ gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
+ gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
+
+ g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
+
+ if (gimp_size_entry_get_unit (size) != GIMP_UNIT_PIXEL)
+ {
+ ratio_x = gimp_size_entry_get_refval (size, 0) / vals.width;
+ ratio_y = gimp_size_entry_get_refval (size, 1) / vals.height;
+ }
+
+ svg_width = vals.width;
+ svg_height = vals.height;
+
+ load_dialog_set_ratio (ratio_x, ratio_y);
+}
+
+static void
+load_dialog_set_ratio (gdouble x,
+ gdouble y)
+{
+ ratio_x = x;
+ ratio_y = y;
+
+ g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
+
+ gimp_size_entry_set_refval (size, 0, svg_width * x);
+ gimp_size_entry_set_refval (size, 1, svg_height * y);
+
+ g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
+
+ g_signal_handlers_block_by_func (xadj, load_dialog_ratio_callback, NULL);
+ g_signal_handlers_block_by_func (yadj, load_dialog_ratio_callback, NULL);
+
+ gtk_adjustment_set_value (xadj, x);
+ gtk_adjustment_set_value (yadj, y);
+
+ g_signal_handlers_unblock_by_func (xadj, load_dialog_ratio_callback, NULL);
+ g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
+}
+
+static GimpPDBStatusType
+load_dialog (const gchar *filename,
+ GError **load_error)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *image;
+ GdkPixbuf *preview;
+ GtkWidget *table;
+ GtkWidget *table2;
+ GtkWidget *abox;
+ GtkWidget *res;
+ GtkWidget *label;
+ GtkWidget *spinbutton;
+ GtkWidget *toggle;
+ GtkWidget *toggle2;
+ GtkAdjustment *adj;
+ gboolean run;
+ GError *error = NULL;
+ SvgLoadVals vals =
+ {
+ SVG_DEFAULT_RESOLUTION,
+ - SVG_PREVIEW_SIZE,
+ - SVG_PREVIEW_SIZE
+ };
+
+ preview = load_rsvg_pixbuf (filename, &vals, &error);
+
+ if (! preview)
+ {
+ /* Do not rely on librsvg setting GError on failure! */
+ g_set_error (load_error,
+ error ? error->domain : 0, error ? error->code : 0,
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename),
+ error ? error->message : _("Unknown reason"));
+ g_clear_error (&error);
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ /* Scalable Vector Graphics is SVG, should perhaps not be translated */
+ dialog = gimp_dialog_new (_("Render Scalable Vector Graphics"),
+ PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ /* The SVG preview */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ image = gtk_image_new_from_pixbuf (preview);
+ gtk_container_add (GTK_CONTAINER (frame), image);
+ gtk_widget_show (image);
+
+ size_label = gtk_label_new (NULL);
+ gtk_label_set_justify (GTK_LABEL (size_label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (vbox), size_label, TRUE, TRUE, 4);
+ gtk_widget_show (size_label);
+
+ /* query the initial size after the size label is created */
+ vals.resolution = load_vals.resolution;
+
+ load_rsvg_size (filename, &vals, NULL);
+
+ svg_width = vals.width;
+ svg_height = vals.height;
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ /* Width and Height */
+ label = gtk_label_new (_("Width:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("Height:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ size = GIMP_SIZE_ENTRY (gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
+ TRUE, FALSE, FALSE, 10,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE));
+ gtk_table_set_col_spacing (GTK_TABLE (size), 1, 6);
+
+ gimp_size_entry_add_field (size, GTK_SPIN_BUTTON (spinbutton), NULL);
+
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (size), FALSE, FALSE, 0);
+ gtk_widget_show (GTK_WIDGET (size));
+
+ gimp_size_entry_set_refval_boundaries (size, 0,
+ GIMP_MIN_IMAGE_SIZE,
+ GIMP_MAX_IMAGE_SIZE);
+ gimp_size_entry_set_refval_boundaries (size, 1,
+ GIMP_MIN_IMAGE_SIZE,
+ GIMP_MAX_IMAGE_SIZE);
+
+ gimp_size_entry_set_refval (size, 0, svg_width);
+ gimp_size_entry_set_refval (size, 1, svg_height);
+
+ gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
+ gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
+
+ g_signal_connect (size, "value-changed",
+ G_CALLBACK (load_dialog_size_callback),
+ NULL);
+
+ /* Scale ratio */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ table2 = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table2), 0, 2);
+ gtk_table_set_row_spacing (GTK_TABLE (table2), 0, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), table2, FALSE, FALSE, 0);
+
+ xadj = (GtkAdjustment *)
+ gtk_adjustment_new (ratio_x,
+ (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_width,
+ (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_width,
+ 0.01, 0.1, 0);
+ spinbutton = gimp_spin_button_new (xadj, 0.01, 4);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 0, 1);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (xadj, "value-changed",
+ G_CALLBACK (load_dialog_ratio_callback),
+ NULL);
+
+ label = gtk_label_new_with_mnemonic (_("_X ratio:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ yadj = (GtkAdjustment *)
+ gtk_adjustment_new (ratio_y,
+ (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) svg_height,
+ (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) svg_height,
+ 0.01, 0.1, 0);
+ spinbutton = gimp_spin_button_new (yadj, 0.01, 4);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 1, 2);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (yadj, "value-changed",
+ G_CALLBACK (load_dialog_ratio_callback),
+ NULL);
+
+ label = gtk_label_new_with_mnemonic (_("_Y ratio:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ /* the constrain ratio chainbutton */
+ constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (constrain), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table2), constrain, 1, 2, 0, 2);
+ gtk_widget_show (constrain);
+
+ gimp_help_set_help_data (GIMP_CHAIN_BUTTON (constrain)->button,
+ _("Constrain aspect ratio"), NULL);
+
+ gtk_widget_show (table2);
+
+ /* Resolution */
+ label = gtk_label_new (_("Resolution:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ res = gimp_size_entry_new (1, GIMP_UNIT_INCH, _("pixels/%a"),
+ FALSE, FALSE, FALSE, 10,
+ GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
+ gtk_table_set_col_spacing (GTK_TABLE (res), 1, 6);
+
+ gtk_table_attach (GTK_TABLE (table), res, 1, 2, 4, 5,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (res);
+
+ /* don't let the resolution become too small, librsvg tends to
+ crash with very small resolutions */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (res), 0,
+ 5.0, GIMP_MAX_RESOLUTION);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);
+
+ g_signal_connect (res, "value-changed",
+ G_CALLBACK (load_dialog_resolution_callback),
+ (gpointer) filename);
+
+ /* Path Import */
+ toggle = gtk_check_button_new_with_mnemonic (_("Import _paths"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), load_vals.import);
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Import path elements of the SVG so they "
+ "can be used with the GIMP path tool"),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &load_vals.import);
+
+ toggle2 = gtk_check_button_new_with_mnemonic (_("Merge imported paths"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle2), load_vals.merge);
+ gtk_table_attach (GTK_TABLE (table), toggle2, 0, 2, 6, 7,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (toggle2);
+
+ g_signal_connect (toggle2, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &load_vals.merge);
+
+ g_object_bind_property (toggle, "active",
+ toggle2, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ load_vals.width = ROUND (gimp_size_entry_get_refval (size, 0));
+ load_vals.height = ROUND (gimp_size_entry_get_refval (size, 1));
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return run ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL;
+}
diff --git a/plug-ins/common/file-tga.c b/plug-ins/common/file-tga.c
new file mode 100644
index 0000000..e833c60
--- /dev/null
+++ b/plug-ins/common/file-tga.c
@@ -0,0 +1,1486 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * TrueVision Targa loading and exporting file filter for GIMP.
+ * Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit
+ *
+ * The Targa reading and writing code was written from scratch by
+ * Raphael FRANCOIS <fraph@ibm.net> and Gordon Matzigkeit
+ * <gord@gnu.ai.mit.edu> based on the TrueVision TGA File Format
+ * Specification, Version 2.0:
+ *
+ * <URL:ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/>
+ *
+ * It does not contain any code written for other TGA file loaders.
+ * Not even the RLE handling. ;)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified 2007-07-20, Raphaël Quinet <raphael@gimp.org>:
+ * - Workaround for loading indexed images with full alpha channel.
+ * - Bug fix: save_image() was saving uninitialized tile data for
+ * indexed images with alpha.
+ *
+ * Modified August-November 2000, Nick Lamb <njl195@zepler.org.uk>
+ * - Clean-up more code, avoid structure implementation dependency,
+ * - Load more types of images reliably, reject others firmly
+ * - This is not perfect, but I think it's much better. Test please!
+ *
+ * Release 1.2, 1997-09-24, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
+ * - Bug fixes and source cleanups.
+ *
+ * Release 1.1, 1997-09-19, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
+ * - Preserve alpha channels. For indexed images, this can only be
+ * done if there is at least one free colormap entry.
+ *
+ * Release 1.0, 1997-09-06, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
+ * - Handle loading all image types from the 2.0 specification.
+ * - Fix many alignment and endianness problems.
+ * - Use tiles for lower memory consumption and better speed.
+ * - Rewrite RLE code for clarity and speed.
+ * - Handle saving with RLE.
+ *
+ * Release 0.9, 1997-06-18, Raphael FRANCOIS <fraph@ibm.net>:
+ * - Can load 24 and 32-bit Truecolor images, with and without RLE.
+ * - Saving currently only works without RLE.
+ *
+ *
+ * TODO:
+ * - Handle TGA images with version 2 extensions (image comment,
+ * resolution, date, ...).
+ * - GIMP stores the indexed alpha channel as a separate byte,
+ * one for each pixel. The TGA file format spec requires that the
+ * alpha channel be stored as part of the colormap, not with each
+ * individual pixel. This means that we have no good way of
+ * saving and loading INDEXEDA images that use alpha channel values
+ * other than 0 and 255. Workaround implemented for loading by
+ * promoting the image to RGBA, but saving indexed TGA images with
+ * full alpha information in the coloramp is not supported yet (only
+ * one fully transparent color is allowed in INDEXEDA mode).
+ */
+
+/* Set these for debugging. */
+/* #define PROFILE 1 */
+
+#include "config.h"
+
+#ifdef PROFILE
+# include <sys/times.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-tga-load"
+#define SAVE_PROC "file-tga-save"
+#define PLUG_IN_BINARY "file-tga"
+#define PLUG_IN_ROLE "gimp-file-tga"
+
+typedef enum
+{
+ ORIGIN_TOP_LEFT = 0,
+ ORIGIN_BOTTOM_LEFT = 1
+} TgaOrigin;
+
+typedef struct _TgaSaveVals
+{
+ gboolean rle;
+ TgaOrigin origin;
+} TgaSaveVals;
+
+static TgaSaveVals tsvals =
+{
+ TRUE, /* rle */
+ ORIGIN_BOTTOM_LEFT /* origin */
+};
+
+
+ /* TRUEVISION-XFILE magic signature string */
+static guchar magic[18] =
+{
+ 0x54, 0x52, 0x55, 0x45, 0x56, 0x49, 0x53, 0x49, 0x4f,
+ 0x4e, 0x2d, 0x58, 0x46, 0x49, 0x4c, 0x45, 0x2e, 0x0
+};
+
+typedef struct tga_info_struct
+{
+ guint8 idLength;
+ guint8 colorMapType;
+
+ guint8 imageType;
+ /* Known image types. */
+#define TGA_TYPE_MAPPED 1
+#define TGA_TYPE_COLOR 2
+#define TGA_TYPE_GRAY 3
+
+ guint8 imageCompression;
+ /* Only known compression is RLE */
+#define TGA_COMP_NONE 0
+#define TGA_COMP_RLE 1
+
+ /* Color Map Specification. */
+ /* We need to separately specify high and low bytes to avoid endianness
+ and alignment problems. */
+
+ guint16 colorMapIndex;
+ guint16 colorMapLength;
+ guint8 colorMapSize;
+
+ /* Image Specification. */
+ guint16 xOrigin;
+ guint16 yOrigin;
+
+ guint16 width;
+ guint16 height;
+
+ guint8 bpp;
+ guint8 bytes;
+
+ guint8 alphaBits;
+ guint8 flipHoriz;
+ guint8 flipVert;
+
+ /* Extensions (version 2) */
+
+/* Not all the structures described in the standard are transcribed here
+ only those which seem applicable to Gimp */
+
+ gchar authorName[41];
+ gchar comment[324];
+ guint month, day, year, hour, minute, second;
+ gchar jobName[41];
+ gchar softwareID[41];
+ guint pixelWidth, pixelHeight; /* write dpi? */
+ gdouble gamma;
+} tga_info;
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gint save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_dialog (void);
+
+static gint32 ReadImage (FILE *fp,
+ tga_info *info,
+ const gchar *filename);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "rle", "Use RLE compression" },
+ { GIMP_PDB_INT32, "origin", "Image origin (0 = top-left, 1 = bottom-left)"}
+ } ;
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files of Targa file format",
+ "FIXME: write help for tga_load",
+ "Raphael FRANCOIS, Gordon Matzigkeit",
+ "Raphael FRANCOIS, Gordon Matzigkeit",
+ "1997,2000,2007",
+ N_("TarGA image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-tga");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "tga,vda,icb,vst",
+ "",
+ "-18&,string,TRUEVISION-XFILE.,-1,byte,0");
+
+ gimp_install_procedure (SAVE_PROC,
+ "exports files in the Targa file format",
+ "FIXME: write help for tga_save",
+ "Raphael FRANCOIS, Gordon Matzigkeit",
+ "Raphael FRANCOIS, Gordon Matzigkeit",
+ "1997,2000",
+ N_("TarGA image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-tga");
+ gimp_register_save_handler (SAVE_PROC, "tga", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+#ifdef PROFILE
+ struct tms tbuf1, tbuf2;
+#endif
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+#ifdef PROFILE
+ times (&tbuf1);
+#endif
+
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ export = gimp_export_image (&image_ID, &drawable_ID, "TGA",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &tsvals);
+
+ /* First acquire information with a dialog */
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &tsvals);
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef PROFILE
+ times (&tbuf1);
+#endif
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ /* Store psvals data */
+ gimp_set_data (SAVE_PROC, &tsvals, sizeof (tsvals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+
+#ifdef PROFILE
+ times (&tbuf2);
+ printf ("TGA: %s profile: %ld user %ld system\n", name,
+ (long) tbuf2.tms_utime - tbuf1.tms_utime,
+ (long) tbuf2.tms_stime - tbuf2.tms_stime);
+#endif
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fp;
+ tga_info info;
+ guchar header[18];
+ guchar footer[26];
+ guchar extension[495];
+ long offset;
+ gint32 image_ID = -1;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "rb");
+
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ /* Is file big enough for a footer? */
+ if (!fseek (fp, -26L, SEEK_END))
+ {
+ if (fread (footer, sizeof (footer), 1, fp) != 1)
+ {
+ g_message (_("Cannot read footer from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+ else if (memcmp (footer + 8, magic, sizeof (magic)) == 0)
+ {
+ /* Check the signature. */
+
+ offset = (footer[0] +
+ footer[1] * 256L +
+ footer[2] * 65536L +
+ footer[3] * 16777216L);
+
+ if (offset != 0)
+ {
+ if (fseek (fp, offset, SEEK_SET) ||
+ fread (extension, sizeof (extension), 1, fp) != 1)
+ {
+ g_message (_("Cannot read extension from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+ /* Eventually actually handle version 2 TGA here */
+ }
+ }
+ }
+
+ if (fseek (fp, 0, SEEK_SET) ||
+ fread (header, sizeof (header), 1, fp) != 1)
+ {
+ g_message (_("Cannot read header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ switch (header[2])
+ {
+ case 1:
+ info.imageType = TGA_TYPE_MAPPED;
+ info.imageCompression = TGA_COMP_NONE;
+ break;
+ case 2:
+ info.imageType = TGA_TYPE_COLOR;
+ info.imageCompression = TGA_COMP_NONE;
+ break;
+ case 3:
+ info.imageType = TGA_TYPE_GRAY;
+ info.imageCompression = TGA_COMP_NONE;
+ break;
+
+ case 9:
+ info.imageType = TGA_TYPE_MAPPED;
+ info.imageCompression = TGA_COMP_RLE;
+ break;
+ case 10:
+ info.imageType = TGA_TYPE_COLOR;
+ info.imageCompression = TGA_COMP_RLE;
+ break;
+ case 11:
+ info.imageType = TGA_TYPE_GRAY;
+ info.imageCompression = TGA_COMP_RLE;
+ break;
+
+ default:
+ info.imageType = 0;
+ }
+
+ info.idLength = header[0];
+ info.colorMapType = header[1];
+
+ info.colorMapIndex = header[3] + header[4] * 256;
+ info.colorMapLength = header[5] + header[6] * 256;
+ info.colorMapSize = header[7];
+
+ info.xOrigin = header[8] + header[9] * 256;
+ info.yOrigin = header[10] + header[11] * 256;
+ info.width = header[12] + header[13] * 256;
+ info.height = header[14] + header[15] * 256;
+
+ info.bpp = header[16];
+ info.bytes = (info.bpp + 7) / 8;
+ info.alphaBits = header[17] & 0x0f; /* Just the low 4 bits */
+ info.flipHoriz = (header[17] & 0x10) ? 1 : 0;
+ info.flipVert = (header[17] & 0x20) ? 0 : 1;
+
+ /* hack to handle some existing files with incorrect headers, see bug #306675 */
+ if (info.alphaBits == info.bpp)
+ info.alphaBits = 0;
+
+ /* hack to handle yet another flavor of incorrect headers, see bug #540969 */
+ if (info.alphaBits == 0)
+ {
+ if (info.imageType == TGA_TYPE_MAPPED && info.colorMapSize == 32)
+ info.alphaBits = 8;
+
+ if (info.imageType == TGA_TYPE_COLOR && info.bpp == 32)
+ info.alphaBits = 8;
+
+ if (info.imageType == TGA_TYPE_GRAY && info.bpp == 16)
+ info.alphaBits = 8;
+ }
+ else if (info.alphaBits == 4 && info.imageType == TGA_TYPE_COLOR && info.bpp == 32)
+ {
+ /* Incorrect TGA saved by Krita, see issue #9067*/
+ info.alphaBits = 8;
+ }
+
+ switch (info.imageType)
+ {
+ case TGA_TYPE_MAPPED:
+ if (info.bpp != 8)
+ {
+ g_message ("Unhandled sub-format in '%s' (type = %u, bpp = %u)",
+ gimp_filename_to_utf8 (filename),
+ info.imageType, info.bpp);
+ fclose (fp);
+ return -1;
+ }
+ break;
+ case TGA_TYPE_COLOR:
+ if ((info.bpp != 15 && info.bpp != 16 &&
+ info.bpp != 24 && info.bpp != 32) ||
+ ((info.bpp == 15 || info.bpp == 24) &&
+ info.alphaBits != 0) ||
+ (info.bpp == 16 && info.alphaBits != 1 &&
+ info.alphaBits != 0) ||
+ (info.bpp == 32 && info.alphaBits != 8))
+ {
+ g_message ("Unhandled sub-format in '%s' (type = %u, bpp = %u, alpha = %u)",
+ gimp_filename_to_utf8 (filename),
+ info.imageType, info.bpp, info.alphaBits);
+ fclose (fp);
+ return -1;
+ }
+ break;
+ case TGA_TYPE_GRAY:
+ if (info.bpp != 8 &&
+ (info.alphaBits != 8 || (info.bpp != 16 && info.bpp != 15)))
+ {
+ g_message ("Unhandled sub-format in '%s' (type = %u, bpp = %u)",
+ gimp_filename_to_utf8 (filename),
+ info.imageType, info.bpp);
+ fclose (fp);
+ return -1;
+ }
+ break;
+
+ default:
+ g_message ("Unknown image type %u for '%s'",
+ info.imageType, gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ /* Plausible but unhandled formats */
+ if (info.bytes * 8 != info.bpp && info.bpp != 15)
+ {
+ g_message ("Unhandled sub-format in '%s' (type = %u, bpp = %u)",
+ gimp_filename_to_utf8 (filename),
+ info.imageType, info.bpp);
+ fclose (fp);
+ return -1;
+ }
+
+ /* Check that we have a color map only when we need it. */
+ if (info.imageType == TGA_TYPE_MAPPED && info.colorMapType != 1)
+ {
+ g_message ("Indexed image has invalid color map type %u",
+ info.colorMapType);
+ fclose (fp);
+ return -1;
+ }
+ else if (info.imageType != TGA_TYPE_MAPPED && info.colorMapType != 0)
+ {
+ g_message ("Non-indexed image has invalid color map type %u",
+ info.colorMapType);
+ fclose (fp);
+ return -1;
+ }
+
+ /* Skip the image ID field. */
+ if (info.idLength && fseek (fp, info.idLength, SEEK_CUR))
+ {
+ g_message ("File '%s' is truncated or corrupted",
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ image_ID = ReadImage (fp, &info, filename);
+
+ fclose (fp);
+
+ return image_ID;
+}
+
+static void
+rle_write (FILE *fp,
+ guchar *buf,
+ guint width,
+ guint bytes)
+{
+ gint repeat = 0;
+ gint direct = 0;
+ guchar *from = buf;
+ guint x;
+
+ for (x = 1; x < width; ++x)
+ {
+ if (memcmp (buf, buf + bytes, bytes))
+ {
+ /* next pixel is different */
+ if (repeat)
+ {
+ putc (128 + repeat, fp);
+ fwrite (from, bytes, 1, fp);
+ from = buf + bytes; /* point to first different pixel */
+ repeat = 0;
+ direct = 0;
+ }
+ else
+ {
+ direct += 1;
+ }
+ }
+ else
+ {
+ /* next pixel is the same */
+ if (direct)
+ {
+ putc (direct - 1, fp);
+ fwrite (from, bytes, direct, fp);
+ from = buf; /* point to first identical pixel */
+ direct = 0;
+ repeat = 1;
+ }
+ else
+ {
+ repeat += 1;
+ }
+ }
+
+ if (repeat == 128)
+ {
+ putc (255, fp);
+ fwrite (from, bytes, 1, fp);
+ from = buf + bytes;
+ direct = 0;
+ repeat = 0;
+ }
+ else if (direct == 128)
+ {
+ putc (127, fp);
+ fwrite (from, bytes, direct, fp);
+ from = buf+ bytes;
+ direct = 0;
+ repeat = 0;
+ }
+
+ buf += bytes;
+ }
+
+ if (repeat > 0)
+ {
+ putc (128 + repeat, fp);
+ fwrite (from, bytes, 1, fp);
+ }
+ else
+ {
+ putc (direct, fp);
+ fwrite (from, bytes, direct + 1, fp);
+ }
+}
+
+static gint
+rle_read (FILE *fp,
+ guchar *buf,
+ tga_info *info)
+{
+ static gint repeat = 0;
+ static gint direct = 0;
+ static guchar sample[4];
+ gint head;
+ gint x, k;
+
+ for (x = 0; x < info->width; x++)
+ {
+ if (repeat == 0 && direct == 0)
+ {
+ head = getc (fp);
+
+ if (head == EOF)
+ {
+ return EOF;
+ }
+ else if (head >= 128)
+ {
+ repeat = head - 127;
+
+ if (fread (sample, info->bytes, 1, fp) < 1)
+ return EOF;
+ }
+ else
+ {
+ direct = head + 1;
+ }
+ }
+
+ if (repeat > 0)
+ {
+ for (k = 0; k < info->bytes; ++k)
+ {
+ buf[k] = sample[k];
+ }
+
+ repeat--;
+ }
+ else /* direct > 0 */
+ {
+ if (fread (buf, info->bytes, 1, fp) < 1)
+ return EOF;
+
+ direct--;
+ }
+
+ buf += info->bytes;
+ }
+
+ return 0;
+}
+
+static void
+flip_line (guchar *buf,
+ tga_info *info)
+{
+ guchar temp;
+ guchar *alt;
+ gint x, s;
+
+ alt = buf + (info->bytes * (info->width - 1));
+
+ for (x = 0; x * 2 < info->width; x++)
+ {
+ for (s = 0; s < info->bytes; ++s)
+ {
+ temp = buf[s];
+ buf[s] = alt[s];
+ alt[s] = temp;
+ }
+
+ buf += info->bytes;
+ alt -= info->bytes;
+ }
+}
+
+/* Some people write 16-bit RGB TGA files. The spec would probably
+ allow 27-bit RGB too, for what it's worth, but I won't fix that
+ unless someone actually provides an existence proof */
+
+static void
+upsample (guchar *dest,
+ const guchar *src,
+ guint width,
+ guint bytes,
+ guint alpha)
+{
+ guint x;
+
+ for (x = 0; x < width; x++)
+ {
+ dest[0] = ((src[1] << 1) & 0xf8);
+ dest[0] += (dest[0] >> 5);
+
+ dest[1] = ((src[0] & 0xe0) >> 2) + ((src[1] & 0x03) << 6);
+ dest[1] += (dest[1] >> 5);
+
+ dest[2] = ((src[0] << 3) & 0xf8);
+ dest[2] += (dest[2] >> 5);
+
+ if (alpha)
+ {
+ dest[3] = (src[1] & 0x80) ? 255 : 0;
+ dest += 4;
+ }
+ else
+ {
+ dest += 3;
+ }
+
+ src += bytes;
+ }
+}
+
+static void
+bgr2rgb (guchar *dest,
+ const guchar *src,
+ guint width,
+ guint bytes,
+ guint alpha)
+{
+ guint x;
+
+ if (alpha)
+ {
+ for (x = 0; x < width; x++)
+ {
+ *(dest++) = src[2];
+ *(dest++) = src[1];
+ *(dest++) = src[0];
+ *(dest++) = src[3];
+
+ src += bytes;
+ }
+ }
+ else
+ {
+ for (x = 0; x < width; x++)
+ {
+ *(dest++) = src[2];
+ *(dest++) = src[1];
+ *(dest++) = src[0];
+
+ src += bytes;
+ }
+ }
+}
+
+static void
+apply_colormap (guchar *dest,
+ const guchar *src,
+ guint width,
+ const guchar *cmap,
+ gboolean alpha,
+ guint16 index)
+{
+ guint x;
+
+ if (alpha)
+ {
+ for (x = 0; x < width; x++)
+ {
+ *(dest++) = cmap[(*src - index) * 4];
+ *(dest++) = cmap[(*src - index) * 4 + 1];
+ *(dest++) = cmap[(*src - index) * 4 + 2];
+ *(dest++) = cmap[(*src - index) * 4 + 3];
+
+ src++;
+ }
+ }
+ else
+ {
+ for (x = 0; x < width; x++)
+ {
+ *(dest++) = cmap[(*src - index) * 3];
+ *(dest++) = cmap[(*src - index) * 3 + 1];
+ *(dest++) = cmap[(*src - index) * 3 + 2];
+
+ src++;
+ }
+ }
+}
+
+static void
+apply_index (guchar *dest,
+ const guchar *src,
+ guint width,
+ guint16 index)
+{
+ guint x;
+
+ for (x = 0; x < width; x++)
+ {
+ *(dest++) = *(src++) - index;
+ }
+}
+
+static void
+read_line (FILE *fp,
+ guchar *row,
+ guchar *buf,
+ tga_info *info,
+ gint bpp,
+ const guchar *convert_cmap)
+{
+ if (info->imageCompression == TGA_COMP_RLE)
+ {
+ rle_read (fp, buf, info);
+ }
+ else
+ {
+ fread (buf, info->bytes, info->width, fp);
+ }
+
+ if (info->flipHoriz)
+ {
+ flip_line (buf, info);
+ }
+
+ if (info->imageType == TGA_TYPE_COLOR)
+ {
+ if (info->bpp == 16 || info->bpp == 15)
+ {
+ upsample (row, buf, info->width, info->bytes, info->alphaBits);
+ }
+ else
+ {
+ bgr2rgb (row, buf, info->width, info->bytes, info->alphaBits);
+ }
+ }
+ else if (convert_cmap)
+ {
+ gboolean has_alpha = (info->alphaBits > 0);
+
+ apply_colormap (row, buf, info->width, convert_cmap, has_alpha,
+ info->colorMapIndex);
+ }
+ else if (info->imageType == TGA_TYPE_MAPPED)
+ {
+ g_assert (bpp == 1);
+
+ apply_index (row, buf, info->width, info->colorMapIndex);
+ }
+ else
+ {
+ memcpy (row, buf, info->width * bpp);
+ }
+}
+
+static gint32
+ReadImage (FILE *fp,
+ tga_info *info,
+ const gchar *filename)
+{
+ static gint32 image_ID;
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ guchar *data, *buf, *row;
+ GimpImageType dtype = 0;
+ GimpImageBaseType itype = 0;
+ gint bpp;
+ gint i, y;
+ gint max_tileheight, tileheight;
+ guint cmap_bytes = 0;
+ guchar *tga_cmap = NULL;
+ guchar *gimp_cmap = NULL;
+ guchar *convert_cmap = NULL;
+
+ switch (info->imageType)
+ {
+ case TGA_TYPE_MAPPED:
+ cmap_bytes = (info->colorMapSize + 7 ) / 8;
+ tga_cmap = g_new (guchar, info->colorMapLength * cmap_bytes);
+
+ if (info->colorMapSize > 24)
+ {
+ /* indexed + full alpha => promoted to RGBA */
+ itype = GIMP_RGB;
+ dtype = GIMP_RGBA_IMAGE;
+ convert_cmap = g_new (guchar, info->colorMapLength * 4);
+ }
+ else if (info->colorMapIndex + info->colorMapLength > 256)
+ {
+ /* more than 256 colormap entries => promoted to RGB */
+ itype = GIMP_RGB;
+ dtype = GIMP_RGB_IMAGE;
+ convert_cmap = g_new (guchar, info->colorMapLength * 3);
+ }
+ else if (info->alphaBits > 0)
+ {
+ /* if alpha exists here, promote to RGB */
+ itype = GIMP_RGB;
+ dtype = GIMP_RGBA_IMAGE;
+ convert_cmap = g_new (guchar, info->colorMapLength * 4);
+ }
+ else
+ {
+ itype = GIMP_INDEXED;
+ dtype = GIMP_INDEXED_IMAGE;
+ gimp_cmap = g_new (guchar, info->colorMapLength * 3);
+ }
+ break;
+
+ case TGA_TYPE_GRAY:
+ itype = GIMP_GRAY;
+
+ if (info->alphaBits)
+ dtype = GIMP_GRAYA_IMAGE;
+ else
+ dtype = GIMP_GRAY_IMAGE;
+ break;
+
+ case TGA_TYPE_COLOR:
+ itype = GIMP_RGB;
+
+ if (info->alphaBits)
+ dtype = GIMP_RGBA_IMAGE;
+ else
+ dtype = GIMP_RGB_IMAGE;
+ break;
+ }
+
+ /* Handle colormap */
+
+ if (info->imageType == TGA_TYPE_MAPPED)
+ {
+ if (cmap_bytes <= 4 &&
+ fread (tga_cmap, info->colorMapLength * cmap_bytes, 1, fp) == 1)
+ {
+ if (convert_cmap)
+ {
+ if (info->colorMapSize == 32)
+ bgr2rgb (convert_cmap, tga_cmap,
+ info->colorMapLength, cmap_bytes, 1);
+ else if (info->colorMapSize == 24)
+ bgr2rgb (convert_cmap, tga_cmap,
+ info->colorMapLength, cmap_bytes, 0);
+ else if (info->colorMapSize == 16 || info->colorMapSize == 15)
+ upsample (convert_cmap, tga_cmap,
+ info->colorMapLength, cmap_bytes, info->alphaBits);
+ else
+ {
+ g_message ("Unsupported colormap depth: %u",
+ info->colorMapSize);
+ return -1;
+ }
+ }
+ else
+ {
+ if (info->colorMapSize == 24)
+ bgr2rgb (gimp_cmap, tga_cmap,
+ info->colorMapLength, cmap_bytes, 0);
+ else if (info->colorMapSize == 16 || info->colorMapSize == 15)
+ upsample (gimp_cmap, tga_cmap,
+ info->colorMapLength, cmap_bytes, info->alphaBits);
+ else
+ {
+ g_message ("Unsupported colormap depth: %u",
+ info->colorMapSize);
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ g_message ("File '%s' is truncated or corrupted",
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+ }
+
+ image_ID = gimp_image_new (info->width, info->height, itype);
+ gimp_image_set_filename (image_ID, filename);
+
+ if (gimp_cmap)
+ gimp_image_set_colormap (image_ID, gimp_cmap, info->colorMapLength);
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("Background"),
+ info->width, info->height,
+ dtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ bpp = gimp_drawable_bpp (layer_ID);
+
+ /* Allocate the data. */
+ max_tileheight = gimp_tile_height ();
+ data = g_new (guchar, info->width * max_tileheight * bpp);
+ buf = g_new (guchar, info->width * info->bytes);
+
+ if (info->flipVert)
+ {
+ for (i = 0; i < info->height; i += tileheight)
+ {
+ tileheight = i ? max_tileheight : (info->height % max_tileheight);
+ if (tileheight == 0)
+ tileheight = max_tileheight;
+
+ for (y = 1; y <= tileheight; ++y)
+ {
+ row = data + (info->width * bpp * (tileheight - y));
+ read_line (fp, row, buf, info, bpp, convert_cmap);
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, info->height - i - tileheight,
+ info->width, tileheight), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((gdouble) (i + tileheight) /
+ (gdouble) info->height);
+ }
+ }
+ else
+ {
+ for (i = 0; i < info->height; i += max_tileheight)
+ {
+ tileheight = MIN (max_tileheight, info->height - i);
+
+ for (y = 0; y < tileheight; ++y)
+ {
+ row= data + (info->width * bpp * y);
+ read_line (fp, row, buf, info, bpp, convert_cmap);
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i, info->width, tileheight), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((gdouble) (i + tileheight) /
+ (gdouble) info->height);
+ }
+ }
+
+ g_free (data);
+ g_free (buf);
+
+ g_free (convert_cmap);
+ g_free (gimp_cmap);
+ g_free (tga_cmap);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ const Babl *format = NULL;
+ GimpImageType dtype;
+ gint width;
+ gint height;
+ FILE *fp;
+ gint out_bpp = 0;
+ gboolean status = TRUE;
+ gint i, row;
+ guchar header[18];
+ guchar footer[26];
+ guchar *pixels;
+ guchar *data;
+ gint num_colors;
+ guchar *gimp_cmap = NULL;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ dtype = gimp_drawable_type (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if ((fp = g_fopen (filename, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ header[0] = 0; /* No image identifier / description */
+
+ if (dtype == GIMP_INDEXED_IMAGE)
+ {
+ gimp_cmap = gimp_image_get_colormap (image_ID, &num_colors);
+
+ header[1] = 1; /* cmap type */
+ header[2] = (tsvals.rle) ? 9 : 1;
+ header[3] = header[4] = 0; /* no offset */
+ header[5] = num_colors % 256;
+ header[6] = num_colors / 256;
+ header[7] = 24; /* cmap size / bits */
+ }
+ else if (dtype == GIMP_INDEXEDA_IMAGE)
+ {
+ gimp_cmap = gimp_image_get_colormap (image_ID, &num_colors);
+
+ header[1] = 1; /* cmap type */
+ header[2] = (tsvals.rle) ? 9 : 1;
+ header[3] = header[4] = 0; /* no offset */
+ header[5] = (num_colors + 1) % 256;
+ header[6] = (num_colors + 1) / 256;
+ header[7] = 32; /* cmap size / bits */
+ }
+ else
+ {
+ header[1]= 0;
+
+ if (dtype == GIMP_RGB_IMAGE || dtype == GIMP_RGBA_IMAGE)
+ {
+ header[2]= (tsvals.rle) ? 10 : 2;
+ }
+ else
+ {
+ header[2]= (tsvals.rle) ? 11 : 3;
+ }
+
+ header[3] = header[4] = header[5] = header[6] = header[7] = 0;
+ }
+
+ header[8] = header[9] = 0; /* xorigin */
+ header[10] = tsvals.origin ? 0 : (height % 256); /* yorigin */
+ header[11] = tsvals.origin ? 0 : (height / 256); /* yorigin */
+
+
+ header[12] = width % 256;
+ header[13] = width / 256;
+
+ header[14] = height % 256;
+ header[15] = height / 256;
+
+ switch (dtype)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = NULL;
+ out_bpp = 1;
+ header[16] = 8; /* bpp */
+ header[17] = tsvals.origin ? 0 : 0x20; /* alpha + orientation */
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ out_bpp = 1;
+ header[16] = 8; /* bpp */
+ header[17] = tsvals.origin ? 0 : 0x20; /* alpha + orientation */
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ out_bpp = 2;
+ header[16] = 16; /* bpp */
+ header[17] = tsvals.origin ? 8 : 0x28; /* alpha + orientation */
+ break;
+
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ out_bpp = 3;
+ header[16] = 24; /* bpp */
+ header[17] = tsvals.origin ? 0 : 0x20; /* alpha + orientation */
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ out_bpp = 4;
+ header[16] = 32; /* bpp */
+ header[17] = tsvals.origin ? 8 : 0x28; /* alpha + orientation */
+ break;
+ }
+
+ /* write header to front of file */
+ fwrite (header, sizeof (header), 1, fp);
+
+ if (dtype == GIMP_INDEXED_IMAGE)
+ {
+ /* write out palette */
+ for (i = 0; i < num_colors; ++i)
+ {
+ fputc (gimp_cmap[(i * 3) + 2], fp);
+ fputc (gimp_cmap[(i * 3) + 1], fp);
+ fputc (gimp_cmap[(i * 3) + 0], fp);
+ }
+ }
+ else if (dtype == GIMP_INDEXEDA_IMAGE)
+ {
+ /* write out palette */
+ for (i = 0; i < num_colors; ++i)
+ {
+ fputc (gimp_cmap[(i * 3) + 2], fp);
+ fputc (gimp_cmap[(i * 3) + 1], fp);
+ fputc (gimp_cmap[(i * 3) + 0], fp);
+ fputc (255, fp);
+ }
+
+ fputc (0, fp);
+ fputc (0, fp);
+ fputc (0, fp);
+ fputc (0, fp);
+ }
+
+ if (dtype == GIMP_INDEXEDA_IMAGE)
+ pixels = g_new (guchar, width * 2);
+ else
+ pixels = g_new (guchar, width * out_bpp);
+ data = g_new (guchar, width * out_bpp);
+
+ for (row = 0; row < height; ++row)
+ {
+ if (tsvals.origin)
+ {
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, height - (row + 1), width, 1), 1.0,
+ format, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+ else
+ {
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, row, width, 1), 1.0,
+ format, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ if (dtype == GIMP_RGB_IMAGE)
+ {
+ bgr2rgb (data, pixels, width, out_bpp, 0);
+ }
+ else if (dtype == GIMP_RGBA_IMAGE)
+ {
+ bgr2rgb (data, pixels, width, out_bpp, 1);
+ }
+ else if (dtype == GIMP_INDEXEDA_IMAGE)
+ {
+ for (i = 0; i < width; ++i)
+ {
+ if (pixels[i * 2 + 1] > 127)
+ data[i] = pixels[i * 2];
+ else
+ data[i] = num_colors;
+ }
+ }
+ else
+ {
+ memcpy (data, pixels, width * out_bpp);
+ }
+
+ if (tsvals.rle)
+ {
+ rle_write (fp, data, width, out_bpp);
+ }
+ else
+ {
+ fwrite (data, width * out_bpp, 1, fp);
+ }
+
+ if (row % 16 == 0)
+ gimp_progress_update ((gdouble) row / (gdouble) height);
+ }
+
+ g_object_unref (buffer);
+
+ g_free (data);
+ g_free (pixels);
+
+ /* footer must be the last thing written to file */
+ memset (footer, 0, 8); /* No extensions, no developer directory */
+ memcpy (footer + 8, magic, sizeof (magic)); /* magic signature */
+ fwrite (footer, sizeof (footer), 1, fp);
+
+ fclose (fp);
+
+ gimp_progress_update (1.0);
+
+ return status;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *toggle;
+ GtkWidget *combo;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("TGA"), PLUG_IN_BINARY, SAVE_PROC);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* rle */
+ toggle = gtk_check_button_new_with_mnemonic (_("_RLE compression"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tsvals.rle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals.rle);
+
+ /* origin */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Or_igin:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_int_combo_box_new (_("Bottom left"), ORIGIN_BOTTOM_LEFT,
+ _("Top left"), ORIGIN_TOP_LEFT,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ tsvals.origin,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &tsvals.origin);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/file-wmf.c b/plug-ins/common/file-wmf.c
new file mode 100644
index 0000000..8c86510
--- /dev/null
+++ b/plug-ins/common/file-wmf.c
@@ -0,0 +1,1049 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* WMF loading file filter for GIMP
+ * -Dom Lachowicz <cinamod@hotmail.com> 2003
+ * -Francis James Franklin <fjf@alinameridon.com>
+ */
+
+#include "config.h"
+
+#include <libwmf/api.h>
+#include <libwmf/gd.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+#include <libgimpmath/gimpmath.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-wmf-load"
+#define LOAD_THUMB_PROC "file-wmf-load-thumb"
+#define PLUG_IN_BINARY "file-wmf"
+#define PLUG_IN_ROLE "gimp-file-wmf"
+
+#define WMF_DEFAULT_RESOLUTION 90.0
+#define WMF_DEFAULT_SIZE 500
+#define WMF_PREVIEW_SIZE 128
+
+typedef struct
+{
+ gdouble resolution;
+ gint width;
+ gint height;
+} WmfLoadVals;
+
+static WmfLoadVals load_vals =
+{
+ WMF_DEFAULT_RESOLUTION,
+ 0,
+ 0,
+};
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean load_wmf_size (const gchar *filename,
+ WmfLoadVals *vals);
+static gboolean load_dialog (const gchar *filename);
+static guchar *wmf_get_pixbuf (const gchar *filename,
+ gint *width,
+ gint *height);
+static guchar *wmf_load_file (const gchar *filename,
+ guint *width,
+ guint *height,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+
+/*
+ * 'query()' - Respond to a plug-in query...
+ */
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
+ { GIMP_PDB_FLOAT, "resolution", "Resolution to use for rendering the WMF (defaults to 72 dpi" },
+ { GIMP_PDB_INT32, "width", "Width (in pixels) to load the WMF in, 0 for original width" },
+ { GIMP_PDB_INT32, "height", "Height (in pixels) to load the WMF in, 0 for original height" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the WMF file format",
+ "Loads files in the WMF file format",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ "(c) 2003 - Version 0.3.0",
+ N_("Microsoft WMF file"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-wmf");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "wmf,apm", "",
+ "0,string,\\327\\315\\306\\232,0,string,\\1\\0\\11\\0");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads a small preview from a WMF image",
+ "",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ "Dom Lachowicz <cinamod@hotmail.com>",
+ "(c) 2003 - Version 0.3.0",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+}
+
+/*
+ * 'run()' - Run the plug-in...
+ */
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ const gchar *filename = NULL;
+ GError *error = NULL;
+ gint32 image_ID = -1;
+ gint width = 0;
+ gint height = 0;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ filename = param[1].data.d_string;
+
+ gimp_get_data (LOAD_PROC, &load_vals);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams > 3) load_vals.resolution = param[3].data.d_float;
+ if (nparams > 4) load_vals.width = param[4].data.d_int32;
+ if (nparams > 5) load_vals.height = param[5].data.d_int32;
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ if (!load_dialog (param[1].data.d_string))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ gint size = param[1].data.d_int32;
+
+ filename = param[0].data.d_string;
+
+ if (size > 0 &&
+ load_wmf_size (filename, &load_vals) &&
+ load_vals.width > 0 &&
+ load_vals.height > 0)
+ {
+ width = load_vals.width;
+ height = load_vals.height;
+
+ if ((gdouble) load_vals.width > (gdouble) load_vals.height)
+ {
+ load_vals.width = size;
+ load_vals.height *= size / (gdouble) load_vals.width;
+ }
+ else
+ {
+ load_vals.width *= size / (gdouble) load_vals.height;
+ load_vals.height = size;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (load_vals.resolution < GIMP_MIN_RESOLUTION ||
+ load_vals.resolution > GIMP_MAX_RESOLUTION)
+ {
+ load_vals.resolution = WMF_DEFAULT_RESOLUTION;
+ }
+
+ image_ID = load_image (filename, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ *nreturn_vals = 4;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ }
+ else
+ {
+ gimp_set_data (LOAD_PROC, &load_vals, sizeof (load_vals));
+ }
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static GtkWidget *size_label = NULL;
+
+
+/* This function retrieves the pixel size from a WMF file. */
+static gboolean
+load_wmf_size (const gchar *filename,
+ WmfLoadVals *vals)
+{
+ GMappedFile *file;
+ /* the bits we need to decode the WMF via libwmf2's GD layer */
+ wmf_error_t err;
+ gulong flags;
+ wmf_gd_t *ddata = NULL;
+ wmfAPI *API = NULL;
+ wmfAPI_Options api_options;
+ wmfD_Rect bbox;
+ guint width = -1;
+ guint height = -1;
+ gboolean success = TRUE;
+ char* wmffontdirs[2] = { NULL, NULL };
+
+ file = g_mapped_file_new (filename, FALSE, NULL);
+ if (! file)
+ return FALSE;
+
+ flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
+ api_options.function = wmf_gd_function;
+
+#ifdef ENABLE_RELOCATABLE_RESOURCES
+ wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
+ "share/libwmf/fonts", NULL);
+ flags |= WMF_OPT_FONTDIRS;
+ api_options.fontdirs = wmffontdirs;
+#endif
+
+ err = wmf_api_create (&API, flags, &api_options);
+ if (wmffontdirs[0])
+ g_free (wmffontdirs[0]);
+ if (err != wmf_E_None)
+ success = FALSE;
+
+ ddata = WMF_GD_GetData (API);
+ ddata->type = wmf_gd_image;
+
+ err = wmf_mem_open (API,
+ (guchar *) g_mapped_file_get_contents (file),
+ g_mapped_file_get_length (file));
+ if (err != wmf_E_None)
+ success = FALSE;
+
+ err = wmf_scan (API, 0, &bbox);
+ if (err != wmf_E_None)
+ success = FALSE;
+
+ err = wmf_display_size (API, &width, &height,
+ vals->resolution, vals->resolution);
+ if (err != wmf_E_None || width <= 0 || height <= 0)
+ success = FALSE;
+
+ wmf_mem_close (API);
+ g_mapped_file_unref (file);
+
+ if (width < 1 || height < 1)
+ {
+ width = WMF_DEFAULT_SIZE;
+ height = WMF_DEFAULT_SIZE;
+
+ if (size_label)
+ gtk_label_set_text (GTK_LABEL (size_label),
+ _("WMF file does not\nspecify a size!"));
+ }
+ else
+ {
+ if (size_label)
+ {
+ gchar *text = g_strdup_printf (_("%d × %d"), width, height);
+
+ gtk_label_set_text (GTK_LABEL (size_label), text);
+ g_free (text);
+ }
+ }
+
+ vals->width = width;
+ vals->height = height;
+
+ return success;
+}
+
+
+/* User interface */
+
+static GimpSizeEntry *size = NULL;
+static GtkAdjustment *xadj = NULL;
+static GtkAdjustment *yadj = NULL;
+static GtkWidget *constrain = NULL;
+static gdouble ratio_x = 1.0;
+static gdouble ratio_y = 1.0;
+static gint wmf_width = 0;
+static gint wmf_height = 0;
+
+static void load_dialog_set_ratio (gdouble x,
+ gdouble y);
+
+
+static void
+load_dialog_size_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
+ {
+ gdouble x = gimp_size_entry_get_refval (size, 0) / (gdouble) wmf_width;
+ gdouble y = gimp_size_entry_get_refval (size, 1) / (gdouble) wmf_height;
+
+ if (x != ratio_x)
+ {
+ load_dialog_set_ratio (x, x);
+ }
+ else if (y != ratio_y)
+ {
+ load_dialog_set_ratio (y, y);
+ }
+ }
+}
+
+static void
+load_dialog_ratio_callback (GtkAdjustment *adj,
+ gpointer data)
+{
+ gdouble x = gtk_adjustment_get_value (xadj);
+ gdouble y = gtk_adjustment_get_value (yadj);
+
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (constrain)))
+ {
+ if (x != ratio_x)
+ y = x;
+ else
+ x = y;
+ }
+
+ load_dialog_set_ratio (x, y);
+}
+
+static void
+load_dialog_resolution_callback (GimpSizeEntry *res,
+ const gchar *filename)
+{
+ WmfLoadVals vals = { 0.0, 0, 0 };
+
+ load_vals.resolution = vals.resolution = gimp_size_entry_get_refval (res, 0);
+
+ if (!load_wmf_size (filename, &vals))
+ return;
+
+ wmf_width = vals.width;
+ wmf_height = vals.height;
+
+ load_dialog_set_ratio (ratio_x, ratio_y);
+}
+
+static void
+wmf_preview_callback (GtkWidget *widget,
+ GtkAllocation *allocation,
+ guchar *pixels)
+{
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (widget),
+ 0, 0, allocation->width, allocation->height,
+ GIMP_RGBA_IMAGE,
+ pixels, allocation->width * 4);
+}
+
+static void
+load_dialog_set_ratio (gdouble x,
+ gdouble y)
+{
+ ratio_x = x;
+ ratio_y = y;
+
+ g_signal_handlers_block_by_func (size, load_dialog_size_callback, NULL);
+
+ gimp_size_entry_set_refval (size, 0, wmf_width * x);
+ gimp_size_entry_set_refval (size, 1, wmf_height * y);
+
+ g_signal_handlers_unblock_by_func (size, load_dialog_size_callback, NULL);
+
+ g_signal_handlers_block_by_func (xadj, load_dialog_ratio_callback, NULL);
+ g_signal_handlers_block_by_func (yadj, load_dialog_ratio_callback, NULL);
+
+ gtk_adjustment_set_value (xadj, x);
+ gtk_adjustment_set_value (yadj, y);
+
+ g_signal_handlers_unblock_by_func (xadj, load_dialog_ratio_callback, NULL);
+ g_signal_handlers_unblock_by_func (yadj, load_dialog_ratio_callback, NULL);
+}
+
+static gboolean
+load_dialog (const gchar *filename)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *image;
+ GtkWidget *table;
+ GtkWidget *table2;
+ GtkWidget *abox;
+ GtkWidget *res;
+ GtkWidget *label;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ guchar *pixels;
+ gboolean run = FALSE;
+
+ WmfLoadVals vals = { WMF_DEFAULT_RESOLUTION,
+ - WMF_PREVIEW_SIZE, - WMF_PREVIEW_SIZE };
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Render Windows Metafile"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ /* The WMF preview */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ pixels = wmf_get_pixbuf (filename, &vals.width, &vals.height);
+ image = gimp_preview_area_new ();
+ gtk_widget_set_size_request (image, vals.width, vals.height);
+ gtk_container_add (GTK_CONTAINER (frame), image);
+ gtk_widget_show (image);
+
+ g_signal_connect (image, "size-allocate",
+ G_CALLBACK (wmf_preview_callback),
+ pixels);
+
+ size_label = gtk_label_new (NULL);
+ gtk_label_set_justify (GTK_LABEL (size_label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (vbox), size_label, TRUE, TRUE, 4);
+ gtk_widget_show (size_label);
+
+ /* query the initial size after the size label is created */
+ vals.resolution = load_vals.resolution;
+
+ load_wmf_size (filename, &vals);
+
+ wmf_width = vals.width;
+ wmf_height = vals.height;
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ /* Width and Height */
+ label = gtk_label_new (_("Width:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("Height:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ size = GIMP_SIZE_ENTRY (gimp_size_entry_new (1, GIMP_UNIT_PIXEL, "%a",
+ TRUE, FALSE, FALSE, 10,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE));
+ gtk_table_set_col_spacing (GTK_TABLE (size), 1, 6);
+
+ gimp_size_entry_add_field (size, GTK_SPIN_BUTTON (spinbutton), NULL);
+
+ gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (size), FALSE, FALSE, 0);
+ gtk_widget_show (GTK_WIDGET (size));
+
+ gimp_size_entry_set_refval_boundaries (size, 0,
+ GIMP_MIN_IMAGE_SIZE,
+ GIMP_MAX_IMAGE_SIZE);
+ gimp_size_entry_set_refval_boundaries (size, 1,
+ GIMP_MIN_IMAGE_SIZE,
+ GIMP_MAX_IMAGE_SIZE);
+
+ gimp_size_entry_set_refval (size, 0, wmf_width);
+ gimp_size_entry_set_refval (size, 1, wmf_height);
+
+ gimp_size_entry_set_resolution (size, 0, load_vals.resolution, FALSE);
+ gimp_size_entry_set_resolution (size, 1, load_vals.resolution, FALSE);
+
+ g_signal_connect (size, "value-changed",
+ G_CALLBACK (load_dialog_size_callback),
+ NULL);
+
+ /* Scale ratio */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ table2 = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table2), 0, 2);
+ gtk_table_set_row_spacing (GTK_TABLE (table2), 0, 4);
+ gtk_box_pack_start (GTK_BOX (hbox), table2, FALSE, FALSE, 0);
+
+ xadj = (GtkAdjustment *)
+ gtk_adjustment_new (ratio_x,
+ (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) wmf_width,
+ (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) wmf_width,
+ 0.01, 0.1, 0);
+ spinbutton = gimp_spin_button_new (xadj, 0.01, 4);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 0, 1);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (xadj, "value-changed",
+ G_CALLBACK (load_dialog_ratio_callback),
+ NULL);
+
+ label = gtk_label_new_with_mnemonic (_("_X ratio:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ yadj = (GtkAdjustment *)
+ gtk_adjustment_new (ratio_y,
+ (gdouble) GIMP_MIN_IMAGE_SIZE / (gdouble) wmf_height,
+ (gdouble) GIMP_MAX_IMAGE_SIZE / (gdouble) wmf_height,
+ 0.01, 0.1, 0);
+ spinbutton = gimp_spin_button_new (yadj, 0.01, 4);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_entry_set_width_chars (GTK_ENTRY (spinbutton), 10);
+ gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 1, 2);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (yadj, "value-changed",
+ G_CALLBACK (load_dialog_ratio_callback),
+ NULL);
+
+ label = gtk_label_new_with_mnemonic (_("_Y ratio:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ /* the constrain ratio chainbutton */
+ constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (constrain), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table2), constrain, 1, 2, 0, 2);
+ gtk_widget_show (constrain);
+
+ gimp_help_set_help_data (GIMP_CHAIN_BUTTON (constrain)->button,
+ _("Constrain aspect ratio"), NULL);
+
+ gtk_widget_show (table2);
+
+ /* Resolution */
+ label = gtk_label_new (_("Resolution:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ res = gimp_size_entry_new (1, GIMP_UNIT_INCH, _("pixels/%a"),
+ FALSE, FALSE, FALSE, 10,
+ GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
+ gtk_table_set_col_spacing (GTK_TABLE (res), 1, 6);
+
+ gtk_table_attach (GTK_TABLE (table), res, 1, 2, 4, 5,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (res);
+
+ /* don't let the resolution become too small ? does libwmf tend to
+ crash with very small resolutions */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (res), 0,
+ 5.0, GIMP_MAX_RESOLUTION);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (res), 0, load_vals.resolution);
+
+ g_signal_connect (res, "value-changed",
+ G_CALLBACK (load_dialog_resolution_callback),
+ (gpointer) filename);
+
+ gtk_widget_show (dialog);
+
+ if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ load_vals.width = ROUND (gimp_size_entry_get_refval (size, 0));
+ load_vals.height = ROUND (gimp_size_entry_get_refval (size, 1));
+ run = TRUE;
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return run;
+}
+
+static guchar *
+pixbuf_gd_convert (const gint *gd_pixels,
+ gint width,
+ gint height)
+{
+ const gint *gd_ptr = gd_pixels;
+ guchar *pixels;
+ guchar *px_ptr;
+ gint i, j;
+
+ pixels = (guchar *) g_try_malloc (width * height * sizeof (guchar) * 4);
+ if (! pixels)
+ return NULL;
+
+ px_ptr = pixels;
+
+ for (i = 0; i < height; i++)
+ for (j = 0; j < width; j++)
+ {
+ guchar r, g, b, a;
+ guint pixel = (guint) (*gd_ptr++);
+
+ b = (guchar) (pixel & 0xff);
+ pixel >>= 8;
+ g = (guchar) (pixel & 0xff);
+ pixel >>= 8;
+ r = (guchar) (pixel & 0xff);
+ pixel >>= 7;
+ a = (guchar) (pixel & 0xfe);
+ a ^= 0xff;
+
+ *px_ptr++ = r;
+ *px_ptr++ = g;
+ *px_ptr++ = b;
+ *px_ptr++ = a;
+ }
+
+ return pixels;
+}
+
+static guchar *
+wmf_get_pixbuf (const gchar *filename,
+ gint *width,
+ gint *height)
+{
+ GMappedFile *file;
+ guchar *pixels = NULL;
+
+ /* the bits we need to decode the WMF via libwmf2's GD layer */
+ wmf_error_t err;
+ gulong flags;
+ wmf_gd_t *ddata = NULL;
+ wmfAPI *API = NULL;
+ wmfAPI_Options api_options;
+ guint file_width;
+ guint file_height;
+ wmfD_Rect bbox;
+ gint *gd_pixels = NULL;
+ char* wmffontdirs[2] = { NULL, NULL };
+
+ file = g_mapped_file_new (filename, FALSE, NULL);
+ if (! file)
+ return NULL;
+
+ flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
+ api_options.function = wmf_gd_function;
+
+#ifdef ENABLE_RELOCATABLE_RESOURCES
+ wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
+ "share/libwmf/fonts", NULL);
+ flags |= WMF_OPT_FONTDIRS;
+ api_options.fontdirs = wmffontdirs;
+#endif
+
+ err = wmf_api_create (&API, flags, &api_options);
+ if (wmffontdirs[0])
+ g_free (wmffontdirs[0]);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ ddata = WMF_GD_GetData (API);
+ ddata->type = wmf_gd_image;
+
+ err = wmf_mem_open (API,
+ (guchar *) g_mapped_file_get_contents (file),
+ g_mapped_file_get_length (file));
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ err = wmf_scan (API, 0, &bbox);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ err = wmf_display_size (API, &file_width, &file_height,
+ WMF_DEFAULT_RESOLUTION, WMF_DEFAULT_RESOLUTION);
+ if (err != wmf_E_None || file_width <= 0 || file_height <= 0)
+ goto _wmf_error;
+
+ if (!*width || !*height)
+ goto _wmf_error;
+
+ /* either both arguments negative or none */
+ if ((*width * *height) < 0)
+ goto _wmf_error;
+
+ ddata->bbox = bbox;
+
+ if (*width > 0)
+ {
+ ddata->width = *width;
+ ddata->height = *height;
+ }
+ else
+ {
+ gdouble w = file_width;
+ gdouble h = file_height;
+ gdouble aspect = ((gdouble) *width) / (gdouble) *height;
+
+ if (aspect > (w / h))
+ {
+ ddata->height = abs (*height);
+ ddata->width = (gdouble) abs (*width) * (w / h) + 0.5;
+ }
+ else
+ {
+ ddata->width = abs (*width);
+ ddata->height = (gdouble) abs (*height) / (w / h) + 0.5;
+ }
+ }
+
+ err = wmf_play (API, 0, &bbox);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ if (ddata->gd_image != NULL)
+ gd_pixels = wmf_gd_image_pixels (ddata->gd_image);
+ if (gd_pixels == NULL)
+ goto _wmf_error;
+
+ pixels = pixbuf_gd_convert (gd_pixels, ddata->width, ddata->height);
+ if (pixels == NULL)
+ goto _wmf_error;
+
+ *width = ddata->width;
+ *height = ddata->height;
+
+ _wmf_error:
+ if (API)
+ {
+ wmf_mem_close (API);
+ wmf_api_destroy (API);
+ }
+
+ g_mapped_file_unref (file);
+
+ return pixels;
+}
+
+static guchar *
+wmf_load_file (const gchar *filename,
+ guint *width,
+ guint *height,
+ GError **error)
+{
+ GMappedFile *file;
+ guchar *pixels = NULL;
+
+ /* the bits we need to decode the WMF via libwmf2's GD layer */
+ wmf_error_t err;
+ gulong flags;
+ wmf_gd_t *ddata = NULL;
+ wmfAPI *API = NULL;
+ wmfAPI_Options api_options;
+ wmfD_Rect bbox;
+ gint *gd_pixels = NULL;
+ char* wmffontdirs[2] = { NULL, NULL };
+
+ *width = *height = -1;
+
+ file = g_mapped_file_new (filename, FALSE, error);
+ if (! file)
+ return NULL;
+
+ flags = WMF_OPT_IGNORE_NONFATAL | WMF_OPT_FUNCTION;
+ api_options.function = wmf_gd_function;
+
+#ifdef ENABLE_RELOCATABLE_RESOURCES
+ wmffontdirs[0] = g_build_filename (gimp_installation_directory (),
+ "share/libwmf/fonts/", NULL);
+ flags |= WMF_OPT_FONTDIRS;
+ api_options.fontdirs = wmffontdirs;
+#endif
+
+ err = wmf_api_create (&API, flags, &api_options);
+ if (wmffontdirs[0])
+ g_free (wmffontdirs[0]);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ ddata = WMF_GD_GetData (API);
+ ddata->type = wmf_gd_image;
+
+ err = wmf_mem_open (API,
+ (guchar *) g_mapped_file_get_contents (file),
+ g_mapped_file_get_length (file));
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ err = wmf_scan (API, 0, &bbox);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ err = wmf_display_size (API,
+ width, height,
+ load_vals.resolution, load_vals.resolution);
+ if (err != wmf_E_None || *width <= 0 || *height <= 0)
+ goto _wmf_error;
+
+ if (load_vals.width > 0 && load_vals.height > 0)
+ {
+ *width = load_vals.width;
+ *height = load_vals.height;
+ }
+
+ ddata->bbox = bbox;
+ ddata->width = *width;
+ ddata->height = *height;
+
+ err = wmf_play (API, 0, &bbox);
+ if (err != wmf_E_None)
+ goto _wmf_error;
+
+ if (ddata->gd_image != NULL)
+ gd_pixels = wmf_gd_image_pixels (ddata->gd_image);
+ if (gd_pixels == NULL)
+ goto _wmf_error;
+
+ pixels = pixbuf_gd_convert (gd_pixels, *width, *height);
+ if (pixels == NULL)
+ goto _wmf_error;
+
+ _wmf_error:
+ if (API)
+ {
+ wmf_mem_close (API);
+ wmf_api_destroy (API);
+ }
+
+ g_mapped_file_unref (file);
+
+ /* FIXME: improve error message */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' for reading"),
+ gimp_filename_to_utf8 (filename));
+
+ return pixels;
+}
+
+/*
+ * 'load_image()' - Load a WMF image into a new image window.
+ */
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 image;
+ gint32 layer;
+ GeglBuffer *buffer;
+ guchar *pixels;
+ guint width, height;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ pixels = wmf_load_file (filename, &width, &height, error);
+
+ if (! pixels)
+ return -1;
+
+ image = gimp_image_new (width, height, GIMP_RGB);
+ gimp_image_set_filename (image, filename);
+ gimp_image_set_resolution (image,
+ load_vals.resolution, load_vals.resolution);
+
+ layer = gimp_layer_new (image,
+ _("Rendered WMF"),
+ width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ babl_format ("R'G'B'A u8"),
+ pixels, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ g_free (pixels);
+
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
diff --git a/plug-ins/common/file-xbm.c b/plug-ins/common/file-xbm.c
new file mode 100644
index 0000000..5ab3b00
--- /dev/null
+++ b/plug-ins/common/file-xbm.c
@@ -0,0 +1,1445 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * X10 and X11 bitmap (XBM) loading and exporting file filter for GIMP.
+ * XBM code Copyright (C) 1998 Gordon Matzigkeit
+ *
+ * The XBM reading and writing code was written from scratch by Gordon
+ * Matzigkeit <gord@gnu.org> based on the XReadBitmapFile(3X11) manual
+ * page distributed with X11R6 and by staring at valid XBM files. It
+ * does not contain any code written for other XBM file loaders.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Release 1.0, 1998-02-04, Gordon Matzigkeit <gord@gnu.org>:
+ * - Load and save X10 and X11 bitmaps.
+ * - Allow the user to specify the C identifier prefix.
+ *
+ * TODO:
+ * - Parsing is very tolerant, and the algorithms are quite hairy, so
+ * load_image should be carefully tested to make sure there are no XBM's
+ * that fail.
+ */
+
+/* Set this for debugging. */
+/* #define VERBOSE 2 */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-xbm-load"
+#define SAVE_PROC "file-xbm-save"
+#define PLUG_IN_BINARY "file-xbm"
+#define PLUG_IN_ROLE "gimp-file-xbm"
+
+
+/* Wear your GIMP with pride! */
+#define DEFAULT_USE_COMMENT TRUE
+#define MAX_COMMENT 72
+#define MAX_MASK_EXT 32
+
+/* C identifier prefix. */
+#define DEFAULT_PREFIX "bitmap"
+#define MAX_PREFIX 64
+
+/* Whether or not to export as X10 bitmap. */
+#define DEFAULT_X10_FORMAT FALSE
+
+typedef struct _XBMSaveVals
+{
+ gchar comment[MAX_COMMENT + 1];
+ gint x10_format;
+ gint use_hot;
+ gint x_hot;
+ gint y_hot;
+ gchar prefix[MAX_PREFIX + 1];
+ gboolean write_mask;
+ gchar mask_ext[MAX_MASK_EXT + 1];
+} XBMSaveVals;
+
+static XBMSaveVals xsvals =
+{
+ "###", /* comment */
+ DEFAULT_X10_FORMAT, /* x10_format */
+ FALSE,
+ 0, /* x_hot */
+ 0, /* y_hot */
+ DEFAULT_PREFIX, /* prefix */
+ FALSE, /* write_mask */
+ "-mask"
+};
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gint save_image (GFile *file,
+ const gchar *prefix,
+ const gchar *comment,
+ gboolean save_mask,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_dialog (gint32 drawable_ID);
+
+static gboolean print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+#if 0
+/* DISABLED - see http://bugzilla.gnome.org/show_bug.cgi?id=82763 */
+static void comment_entry_callback (GtkWidget *widget,
+ gpointer data);
+#endif
+static void prefix_entry_callback (GtkWidget *widget,
+ gpointer data);
+static void mask_ext_entry_callback (GtkWidget *widget,
+ gpointer data);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+#ifdef VERBOSE
+static int verbose = VERBOSE;
+#endif
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_STRING, "comment", "Image description (maximum 72 bytes)" },
+ { GIMP_PDB_INT32, "x10", "Export in X10 format" },
+ { GIMP_PDB_INT32, "x-hot", "X coordinate of hotspot" },
+ { GIMP_PDB_INT32, "y-hot", "Y coordinate of hotspot" },
+ { GIMP_PDB_STRING, "prefix", "Identifier prefix [determined from filename]"},
+ { GIMP_PDB_INT32, "write-mask", "(0 = ignore, 1 = save as extra file)" },
+ { GIMP_PDB_STRING, "mask-extension", "Extension of the mask file" }
+ } ;
+
+ gimp_install_procedure (LOAD_PROC,
+ "Load a file in X10 or X11 bitmap (XBM) file format",
+ "Load a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.",
+ "Gordon Matzigkeit",
+ "Gordon Matzigkeit",
+ "1998",
+ N_("X BitMap image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-xbitmap");
+ gimp_register_load_handler (LOAD_PROC,
+ "xbm,icon,bitmap",
+ "");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Export a file in X10 or X11 bitmap (XBM) file format",
+ "Export a file in X10 or X11 bitmap (XBM) file format. XBM is a lossless format for flat black-and-white (two color indexed) images.",
+ "Gordon Matzigkeit",
+ "Gordon Matzigkeit",
+ "1998",
+ N_("X BitMap image"),
+ "INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-xbitmap");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "xbm,icon,bitmap", "");
+}
+
+static gchar *
+init_prefix (const gchar *filename)
+{
+ gchar *p, *prefix;
+ gint len;
+
+ prefix = g_path_get_basename (filename);
+
+ memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
+
+ if (prefix)
+ {
+ /* Strip any extension. */
+ p = strrchr (prefix, '.');
+ if (p && p != prefix)
+ len = MIN (MAX_PREFIX, p - prefix);
+ else
+ len = MAX_PREFIX;
+
+ strncpy (xsvals.prefix, prefix, len);
+ g_free (prefix);
+ }
+
+ return xsvals.prefix;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpParasite *parasite = NULL;
+ gchar *mask_basename = NULL;
+ GError *error = NULL;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ strncpy (xsvals.comment, "Created with GIMP", MAX_COMMENT);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+#ifdef VERBOSE
+ if (verbose)
+ printf ("XBM: RUN %s\n", name);
+#endif
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "XBM",
+ GIMP_EXPORT_CAN_HANDLE_BITMAP |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &xsvals);
+
+ /* Always override the prefix with the filename. */
+ mask_basename = g_strdup (init_prefix (param[3].data.d_string));
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the required arguments are there! */
+ if (nparams < 5)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gint i = 5;
+
+ if (nparams > i)
+ {
+ memset (xsvals.comment, 0, sizeof (xsvals.comment));
+ strncpy (xsvals.comment, param[i].data.d_string,
+ MAX_COMMENT);
+ }
+
+ i ++;
+ if (nparams > i)
+ xsvals.x10_format = (param[i].data.d_int32) ? TRUE : FALSE;
+
+ i += 2;
+ if (nparams > i)
+ {
+ /* They've asked for a hotspot. */
+ xsvals.use_hot = TRUE;
+ xsvals.x_hot = param[i - 1].data.d_int32;
+ xsvals.y_hot = param[i].data.d_int32;
+ }
+
+ mask_basename = g_strdup (init_prefix (param[3].data.d_string));
+
+ i ++;
+ if (nparams > i)
+ {
+ memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
+ strncpy (xsvals.prefix, param[i].data.d_string,
+ MAX_PREFIX);
+ }
+
+ i += 2;
+ if (nparams > i)
+ {
+ xsvals.write_mask = param[i - 1].data.d_int32;
+ memset (xsvals.mask_ext, 0, sizeof (xsvals.mask_ext));
+ strncpy (xsvals.mask_ext, param[i].data.d_string,
+ MAX_MASK_EXT);
+ }
+
+ i ++;
+ /* Too many arguments. */
+ if (nparams > i)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ /* Get the parasites */
+ parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
+
+ if (parasite)
+ {
+ gint size = gimp_parasite_data_size (parasite);
+
+ strncpy (xsvals.comment,
+ gimp_parasite_data (parasite), MIN (size, MAX_COMMENT));
+ xsvals.comment[MIN (size, MAX_COMMENT) + 1] = 0;
+
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image_ID, "hot-spot");
+
+ if (parasite)
+ {
+ gint x, y;
+
+ if (sscanf (gimp_parasite_data (parasite), "%i %i", &x, &y) == 2)
+ {
+ xsvals.use_hot = TRUE;
+ xsvals.x_hot = x;
+ xsvals.y_hot = y;
+ }
+ gimp_parasite_free (parasite);
+ }
+
+ /* Acquire information with a dialog */
+ if (! save_dialog (drawable_ID))
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+ GFile *mask_file;
+ GFile *dir;
+ gchar *mask_prefix;
+ gchar *temp;
+
+ dir = g_file_get_parent (file);
+ temp = g_strdup_printf ("%s%s.xbm", mask_basename, xsvals.mask_ext);
+
+ mask_file = g_file_get_child (dir, temp);
+
+ g_free (temp);
+ g_object_unref (dir);
+
+ /* Change any non-alphanumeric prefix characters to underscores. */
+ for (temp = xsvals.prefix; *temp; temp++)
+ if (! g_ascii_isalnum (*temp))
+ *temp = '_';
+
+ mask_prefix = g_strdup_printf ("%s%s",
+ xsvals.prefix, xsvals.mask_ext);
+
+ for (temp = mask_prefix; *temp; temp++)
+ if (! g_ascii_isalnum (*temp))
+ *temp = '_';
+
+ if (save_image (file,
+ xsvals.prefix,
+ xsvals.comment,
+ FALSE,
+ image_ID, drawable_ID,
+ &error)
+
+ && (! xsvals.write_mask ||
+ save_image (mask_file,
+ mask_prefix,
+ xsvals.comment,
+ TRUE,
+ image_ID, drawable_ID,
+ &error)))
+ {
+ /* Store xsvals data */
+ gimp_set_data (SAVE_PROC, &xsvals, sizeof (xsvals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_free (mask_prefix);
+ g_free (mask_basename);
+
+ g_object_unref (file);
+ g_object_unref (mask_file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* Return the value of a digit. */
+static gint
+getval (gint c,
+ gint base)
+{
+ const gchar *digits = "0123456789abcdefABCDEF";
+ gint val;
+
+ /* Include uppercase hex digits. */
+ if (base == 16)
+ base = 22;
+
+ /* Find a match. */
+ for (val = 0; val < base; val ++)
+ if (c == digits[val])
+ return (val < 16) ? val : (val - 6);
+ return -1;
+}
+
+
+/* Get a comment */
+static gchar *
+fgetcomment (FILE *fp)
+{
+ GString *str = NULL;
+ gint comment, c;
+
+ comment = 0;
+ do
+ {
+ c = fgetc (fp);
+ if (comment)
+ {
+ if (c == '*')
+ {
+ /* In a comment, with potential to leave. */
+ comment = 1;
+ }
+ else if (comment == 1 && c == '/')
+ {
+ gchar *retval;
+
+ /* Leaving a comment. */
+ comment = 0;
+
+ retval = g_strstrip (g_strdup (str->str));
+ g_string_free (str, TRUE);
+ return retval;
+ }
+ else
+ {
+ /* In a comment, with no potential to leave. */
+ comment = 2;
+ g_string_append_c (str, c);
+ }
+ }
+ else
+ {
+ /* Not in a comment. */
+ if (c == '/')
+ {
+ /* Potential to enter a comment. */
+ c = fgetc (fp);
+ if (c == '*')
+ {
+ /* Entered a comment, with no potential to leave. */
+ comment = 2;
+ str = g_string_new (NULL);
+ }
+ else
+ {
+ /* put everything back and return */
+ ungetc (c, fp);
+ c = '/';
+ ungetc (c, fp);
+ return NULL;
+ }
+ }
+ else if (c != EOF && g_ascii_isspace (c))
+ {
+ /* Skip leading whitespace */
+ continue;
+ }
+ }
+ }
+ while (comment && c != EOF);
+
+ if (str)
+ g_string_free (str, TRUE);
+
+ return NULL;
+}
+
+
+/* Same as fgetc, but skip C-style comments and insert whitespace. */
+static gint
+cpp_fgetc (FILE *fp)
+{
+ gint comment, c;
+
+ /* FIXME: insert whitespace as advertised. */
+ comment = 0;
+ do
+ {
+ c = fgetc (fp);
+ if (comment)
+ {
+ if (c == '*')
+ /* In a comment, with potential to leave. */
+ comment = 1;
+ else if (comment == 1 && c == '/')
+ /* Leaving a comment. */
+ comment = 0;
+ else
+ /* In a comment, with no potential to leave. */
+ comment = 2;
+ }
+ else
+ {
+ /* Not in a comment. */
+ if (c == '/')
+ {
+ /* Potential to enter a comment. */
+ c = fgetc (fp);
+ if (c == '*')
+ /* Entered a comment, with no potential to leave. */
+ comment = 2;
+ else
+ {
+ /* Just a slash in the open. */
+ ungetc (c, fp);
+ c = '/';
+ }
+ }
+ }
+ }
+ while (comment && c != EOF);
+ return c;
+}
+
+
+/* Match a string with a file. */
+static gint
+match (FILE *fp,
+ const gchar *s)
+{
+ gint c;
+
+ do
+ {
+ c = fgetc (fp);
+ if (c == *s)
+ s ++;
+ else
+ break;
+ }
+ while (c != EOF && *s);
+
+ if (!*s)
+ return TRUE;
+
+ if (c != EOF)
+ ungetc (c, fp);
+ return FALSE;
+}
+
+
+/* Read the next integer from the file, skipping all non-integers. */
+static gint
+get_int (FILE *fp)
+{
+ int digval, base, val, c;
+
+ do
+ c = cpp_fgetc (fp);
+ while (c != EOF && ! g_ascii_isdigit (c));
+
+ if (c == EOF)
+ return 0;
+
+ /* Check for the base. */
+ if (c == '0')
+ {
+ c = fgetc (fp);
+ if (c == 'x' || c == 'X')
+ {
+ c = fgetc (fp);
+ base = 16;
+ }
+ else if (g_ascii_isdigit (c))
+ base = 8;
+ else
+ {
+ ungetc (c, fp);
+ return 0;
+ }
+ }
+ else
+ base = 10;
+
+ val = 0;
+ for (;;)
+ {
+ digval = getval (c, base);
+ if (digval == -1)
+ {
+ ungetc (c, fp);
+ break;
+ }
+ val *= base;
+ val += digval;
+ c = fgetc (fp);
+ }
+
+ return val;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fp;
+ GeglBuffer *buffer;
+ gint32 image_ID;
+ gint32 layer_ID;
+ guchar *data;
+ gint intbits;
+ gint width = 0;
+ gint height = 0;
+ gint x_hot = 0;
+ gint y_hot = 0;
+ gint c, i, j, k;
+ gint tileheight, rowoffset;
+ gchar *comment;
+
+ const guchar cmap[] =
+ {
+ 0x00, 0x00, 0x00, /* black */
+ 0xff, 0xff, 0xff /* white */
+ };
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "rb");
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ comment = fgetcomment (fp);
+
+ /* Loosely parse the header */
+ intbits = height = width = 0;
+ c = ' ';
+ do
+ {
+ if (g_ascii_isspace (c))
+ {
+ if (match (fp, "char"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ intbits = 8;
+ continue;
+ }
+ }
+ else if (match (fp, "short"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ intbits = 16;
+ continue;
+ }
+ }
+ }
+
+ if (c == '_')
+ {
+ if (match (fp, "width"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ width = get_int (fp);
+ continue;
+ }
+ }
+ else if (match (fp, "height"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ height = get_int (fp);
+ continue;
+ }
+ }
+ else if (match (fp, "x_hot"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ x_hot = get_int (fp);
+ continue;
+ }
+ }
+ else if (match (fp, "y_hot"))
+ {
+ c = fgetc (fp);
+ if (g_ascii_isspace (c))
+ {
+ y_hot = get_int (fp);
+ continue;
+ }
+ }
+ }
+
+ c = cpp_fgetc (fp);
+ }
+ while (c != '{' && c != EOF);
+
+ if (c == EOF)
+ {
+ g_message (_("'%s':\nCould not read header (ftell == %ld)"),
+ gimp_filename_to_utf8 (filename), ftell (fp));
+ fclose (fp);
+ return -1;
+ }
+
+ if (width <= 0)
+ {
+ g_message (_("'%s':\nNo image width specified"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ if (width > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage width is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ if (height <= 0)
+ {
+ g_message (_("'%s':\nNo image height specified"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ if (height > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage height is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ if (intbits == 0)
+ {
+ g_message (_("'%s':\nNo image data type specified"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ image_ID = gimp_image_new (width, height, GIMP_INDEXED);
+ gimp_image_set_filename (image_ID, filename);
+
+ if (comment)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1, (gpointer) comment);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ g_free (comment);
+ }
+
+ x_hot = CLAMP (x_hot, 0, width);
+ y_hot = CLAMP (y_hot, 0, height);
+
+ if (x_hot > 0 || y_hot > 0)
+ {
+ GimpParasite *parasite;
+ gchar *str;
+
+ str = g_strdup_printf ("%d %d", x_hot, y_hot);
+ parasite = gimp_parasite_new ("hot-spot",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (str) + 1, (gpointer) str);
+ g_free (str);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ /* Set a black-and-white colormap. */
+ gimp_image_set_colormap (image_ID, cmap, 2);
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("Background"),
+ width, height,
+ GIMP_INDEXED_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ /* Allocate the data. */
+ tileheight = gimp_tile_height ();
+ data = (guchar *) g_malloc (width * tileheight);
+
+ for (i = 0; i < height; i += tileheight)
+ {
+ tileheight = MIN (tileheight, height - i);
+
+#ifdef VERBOSE
+ if (verbose > 1)
+ printf ("XBM: reading %dx(%d+%d) pixel region\n", width, i,
+ tileheight);
+#endif
+
+ /* Parse the data from the file */
+ for (j = 0; j < tileheight; j ++)
+ {
+ /* Read each row. */
+ rowoffset = j * width;
+ for (k = 0; k < width; k ++)
+ {
+ /* Expand each integer into INTBITS pixels. */
+ if (k % intbits == 0)
+ {
+ c = get_int (fp);
+
+ /* Flip all the bits so that 1's become black and
+ 0's become white. */
+ c ^= 0xffff;
+ }
+
+ data[rowoffset + k] = c & 1;
+ c >>= 1;
+ }
+ }
+
+ /* Put the data into the image. */
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i, width, tileheight), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((double) (i + tileheight) / (double) height);
+ }
+
+ g_free (data);
+ g_object_unref (buffer);
+ fclose (fp);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+static gboolean
+save_image (GFile *file,
+ const gchar *prefix,
+ const gchar *comment,
+ gboolean save_mask,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GeglBuffer *buffer;
+ GCancellable *cancellable;
+ gint width, height, colors, dark;
+ gint intbits, lineints, need_comma, nints, rowoffset, tileheight;
+ gint c, i, j, k, thisbit;
+ gboolean has_alpha;
+ gint bpp;
+ guchar *data = NULL;
+ guchar *cmap;
+ const gchar *intfmt;
+
+#if 0
+ if (save_mask)
+ g_printerr ("%s: save_mask '%s'\n", G_STRFUNC, prefix);
+ else
+ g_printerr ("%s: save_image '%s'\n", G_STRFUNC, prefix);
+#endif
+
+ cmap = gimp_image_get_colormap (image_ID, &colors);
+
+ if (! gimp_drawable_is_indexed (drawable_ID) || colors > 2)
+ {
+ /* The image is not black-and-white. */
+ g_message (_("The image which you are trying to export as "
+ "an XBM contains more than two colors.\n\n"
+ "Please convert it to a black and white "
+ "(1-bit) indexed image and try again."));
+ g_free (cmap);
+ return FALSE;
+ }
+
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (! has_alpha && save_mask)
+ {
+ g_message (_("You cannot save a cursor mask for an image\n"
+ "which has no alpha channel."));
+ return FALSE;
+ }
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ bpp = gimp_drawable_bpp (drawable_ID);
+
+ /* Figure out which color is black, and which is white. */
+ dark = 0;
+ if (colors > 1)
+ {
+ gint first, second;
+
+ /* Maybe the second color is darker than the first. */
+ first = (cmap[0] * cmap[0]) + (cmap[1] * cmap[1]) + (cmap[2] * cmap[2]);
+ second = (cmap[3] * cmap[3]) + (cmap[4] * cmap[4]) + (cmap[5] * cmap[5]);
+
+ if (second < first)
+ dark = 1;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE, G_FILE_CREATE_NONE,
+ NULL, error));
+ if (output)
+ {
+ GOutputStream *buffered;
+
+ buffered = g_buffered_output_stream_new (output);
+ g_object_unref (output);
+
+ output = buffered;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ /* Maybe write the image comment. */
+#if 0
+ /* DISABLED - see http://bugzilla.gnome.org/show_bug.cgi?id=82763 */
+ /* a future version should write the comment at the end of the file */
+ if (*comment)
+ {
+ if (! print (output, error, "/* %s */\n", comment))
+ goto fail;
+ }
+#endif
+
+ /* Write out the image height and width. */
+ if (! print (output, error, "#define %s_width %d\n", prefix, width) ||
+ ! print (output, error, "#define %s_height %d\n", prefix, height))
+ goto fail;
+
+ /* Write out the hotspot, if any. */
+ if (xsvals.use_hot)
+ {
+ if (! print (output, error,
+ "#define %s_x_hot %d\n", prefix, xsvals.x_hot) ||
+ ! print (output, error,
+ "#define %s_y_hot %d\n", prefix, xsvals.y_hot))
+ goto fail;
+ }
+
+ /* Now write the actual data. */
+ if (xsvals.x10_format)
+ {
+ /* We can fit 9 hex shorts on a single line. */
+ lineints = 9;
+ intbits = 16;
+ intfmt = " 0x%04x";
+ }
+ else
+ {
+ /* We can fit 12 hex chars on a single line. */
+ lineints = 12;
+ intbits = 8;
+ intfmt = " 0x%02x";
+ }
+
+ if (! print (output, error,
+ "static %s %s_bits[] = {\n ",
+ xsvals.x10_format ? "unsigned short" : "unsigned char", prefix))
+ goto fail;
+
+ /* Allocate a new set of pixels. */
+ tileheight = gimp_tile_height ();
+ data = (guchar *) g_malloc (width * tileheight * bpp);
+
+ /* Write out the integers. */
+ need_comma = 0;
+ nints = 0;
+ for (i = 0; i < height; i += tileheight)
+ {
+ /* Get a horizontal slice of the image. */
+ tileheight = MIN (tileheight, height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, tileheight), 1.0,
+ NULL, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+#ifdef VERBOSE
+ if (verbose > 1)
+ printf ("XBM: writing %dx(%d+%d) pixel region\n",
+ width, i, tileheight);
+#endif
+
+ for (j = 0; j < tileheight; j ++)
+ {
+ /* Write out a row at a time. */
+ rowoffset = j * width * bpp;
+ c = 0;
+ thisbit = 0;
+
+ for (k = 0; k < width * bpp; k += bpp)
+ {
+ if (k != 0 && thisbit == intbits)
+ {
+ /* Output a completed integer. */
+ if (need_comma)
+ {
+ if (! print (output, error, ","))
+ goto fail;
+ }
+
+ need_comma = 1;
+
+ /* Maybe start a new line. */
+ if (nints ++ >= lineints)
+ {
+ nints = 1;
+
+ if (! print (output, error, "\n "))
+ goto fail;
+ }
+
+ if (! print (output, error, intfmt, c))
+ goto fail;
+
+ /* Start a new integer. */
+ c = 0;
+ thisbit = 0;
+ }
+
+ /* Pack INTBITS pixels into an integer. */
+ if (save_mask)
+ {
+ c |= ((data[rowoffset + k + 1] < 128) ? 0 : 1) << (thisbit ++);
+ }
+ else
+ {
+ if (has_alpha && (data[rowoffset + k + 1] < 128))
+ c |= 0 << (thisbit ++);
+ else
+ c |= ((data[rowoffset + k] == dark) ? 1 : 0) << (thisbit ++);
+ }
+ }
+
+ if (thisbit != 0)
+ {
+ /* Write out the last oddball int. */
+ if (need_comma)
+ {
+ if (! print (output, error, ","))
+ goto fail;
+ }
+
+ need_comma = 1;
+
+ /* Maybe start a new line. */
+ if (nints ++ == lineints)
+ {
+ nints = 1;
+
+ if (! print (output, error, "\n "))
+ goto fail;
+ }
+
+ if (! print (output, error, intfmt, c))
+ goto fail;
+ }
+ }
+
+ gimp_progress_update ((double) (i + tileheight) / (double) height);
+ }
+
+ /* Write the trailer. */
+ if (! print (output, error, " };\n"))
+ goto fail;
+
+ if (! g_output_stream_close (output, NULL, error))
+ goto fail;
+
+ g_free (data);
+ g_object_unref (buffer);
+ g_object_unref (output);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+
+ fail:
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+
+ g_free (data);
+ g_object_unref (buffer);
+ g_object_unref (output);
+
+ return FALSE;
+}
+
+static gboolean
+save_dialog (gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *toggle;
+ GtkWidget *table;
+ GtkWidget *entry;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("XBM"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* parameter settings */
+ frame = gimp_frame_new (_("XBM Options"));
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ /* X10 format */
+ toggle = gtk_check_button_new_with_mnemonic (_("_X10 format bitmap"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), xsvals.x10_format);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &xsvals.x10_format);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* prefix */
+ entry = gtk_entry_new ();
+ gtk_entry_set_max_length (GTK_ENTRY (entry), MAX_PREFIX);
+ gtk_entry_set_text (GTK_ENTRY (entry), xsvals.prefix);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Identifier prefix:"), 0.0, 0.5,
+ entry, 1, TRUE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (prefix_entry_callback),
+ NULL);
+
+ /* comment string. */
+#if 0
+ /* DISABLED - see http://bugzilla.gnome.org/show_bug.cgi?id=82763 */
+ entry = gtk_entry_new ();
+ gtk_entry_set_max_length (GTK_ENTRY (entry), MAX_COMMENT);
+ gtk_widget_set_size_request (entry, 240, -1);
+ gtk_entry_set_text (GTK_ENTRY (entry), xsvals.comment);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Comment:"), 0.0, 0.5,
+ entry, 1, TRUE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (comment_entry_callback),
+ NULL);
+#endif
+
+ /* hotspot toggle */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Write hot spot values"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), xsvals.use_hot);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &xsvals.use_hot);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (xsvals.x_hot, 0,
+ gimp_drawable_width (drawable_ID) - 1,
+ 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Hot spot _X:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xsvals.x_hot);
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (xsvals.y_hot, 0,
+ gimp_drawable_height (drawable_ID) - 1,
+ 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Hot spot _Y:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xsvals.y_hot);
+
+ /* mask file */
+ frame = gimp_frame_new (_("Mask File"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("W_rite extra mask file"));
+ gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 2, 0, 1);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), xsvals.write_mask);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &xsvals.write_mask);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_max_length (GTK_ENTRY (entry), MAX_MASK_EXT);
+ gtk_entry_set_text (GTK_ENTRY (entry), xsvals.mask_ext);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Mask file extension:"), 0.0, 0.5,
+ entry, 1, TRUE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (mask_ext_entry_callback),
+ NULL);
+
+ g_object_bind_property (toggle, "active",
+ entry, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ gtk_widget_set_sensitive (frame, gimp_drawable_has_alpha (drawable_ID));
+
+ /* Done. */
+ gtk_widget_show (vbox);
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static gboolean
+print (GOutputStream *output,
+ GError **error,
+ const gchar *format,
+ ...)
+{
+ va_list args;
+ gboolean success;
+
+ va_start (args, format);
+ success = g_output_stream_vprintf (output, NULL, NULL,
+ error, format, args);
+ va_end (args);
+
+ return success;
+}
+
+/* Update the comment string. */
+#if 0
+/* DISABLED - see http://bugzilla.gnome.org/show_bug.cgi?id=82763 */
+static void
+comment_entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ memset (xsvals.comment, 0, sizeof (xsvals.comment));
+ strncpy (xsvals.comment,
+ gtk_entry_get_text (GTK_ENTRY (widget)), MAX_COMMENT);
+}
+#endif
+
+static void
+prefix_entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
+ strncpy (xsvals.prefix,
+ gtk_entry_get_text (GTK_ENTRY (widget)), MAX_PREFIX);
+}
+
+static void
+mask_ext_entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ memset (xsvals.mask_ext, 0, sizeof (xsvals.mask_ext));
+ strncpy (xsvals.mask_ext,
+ gtk_entry_get_text (GTK_ENTRY (widget)), MAX_MASK_EXT);
+}
diff --git a/plug-ins/common/file-xmc.c b/plug-ins/common/file-xmc.c
new file mode 100644
index 0000000..a5c41f7
--- /dev/null
+++ b/plug-ins/common/file-xmc.c
@@ -0,0 +1,2492 @@
+/*
+ * X11 Mouse Cursor (XMC) plug-in for GIMP
+ *
+ * Copyright 2008-2009 Takeshi Matsuyama <tksmashiw@gmail.com>
+ *
+ * Special thanks: Alexia Death, Sven Neumann, Martin Nordholts
+ * and all community members.
+ */
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Todo: if drawable->bpp != 4 in save_image for GIMP-2.8?
+ * Todo: support for "gimp-metadata" parasite.
+ * "xmc-copyright" and "xmc-license" may be deprecated in future?
+ */
+
+/*
+ * This plug-in use these four parasites.
+ * "hot-spot" common with file-xbm plug-in
+ * "xmc-copyright" original, store contents of type1 comment chunk of Xcursor
+ * "xmc-license" original, store contents of type2 comment chunk of Xcursor
+ * "gimp-comment" common, store contents of type3 comment chunk of Xcursor
+ */
+
+/* *** Caution: Size vs Dimension ***
+ *
+ * In this file, "size" and "dimension" are used in definitely
+ * different contexts. "Size" means nominal size of Xcursor which is
+ * used to determine which frame depends on which animation sequence
+ * and which sequence is really used. (for more detail, please read
+ * Xcursor(3).) On the other hand, "Dimension" simply means width
+ * and/or height.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xcursor/Xcursor.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/* For debug */
+/* #define XMC_DEBUG */
+#ifdef XMC_DEBUG
+# define DM_XMC(...) g_fprintf(stderr, __VA_ARGS__)
+#else
+# define DM_XMC(...)
+#endif
+
+/*
+ * Constants...
+ */
+
+#define LOAD_PROC "file-xmc-load"
+#define LOAD_THUMB_PROC "file-xmc-load-thumb"
+#define SAVE_PROC "file-xmc-save"
+
+#define PLUG_IN_BINARY "file-xmc"
+#define PLUG_IN_ROLE "gimp-file-xmc"
+
+/* We use "xmc" as the file extension of X cursor for convenience */
+#define XCURSOR_EXTENSION "xmc"
+#define XCURSOR_MIME_TYPE "image/x-xcursor"
+
+/* The maximum dimension of Xcursor which is fully supported in any
+ * environments. This is defined on line 59 of xcursorint.h in
+ * libXcursor source code. Make sure this is about dimensions (width
+ * and height) not about nominal size despite of it's name.
+ *
+ * As of 2018, this macro still exists in libXCursor codebase, but I am
+ * unsure how far this restriction is enforced since this is a very low
+ * max dimension for today's displays. Therefore our code will not
+ * enforce this value anymore, but only warn about possible
+ * incompatibilities when using higher values.
+ */
+#define MAX_BITMAP_CURSOR_SIZE 64
+
+/* The maximum dimension of each frame of X cursor we want to save
+ * should be MAX_BITMAP_CURSOR_SIZE but about loading, xhot(& yhot) of
+ * each frame varies from 0 to MAX_BITMAP_CURSOR_SIZE-1, so we need to
+ * set the maximum dimension of image no less than
+ * MAX_BITMAP_CURSOR_SIZE * 2( -1 to be precise) to remain hotspots on
+ * the same coordinates.
+ *
+ * We use four times value (256 for saving, 512 for loading) as a
+ * limitation because some cursors generated by CursorXP/FX to X11
+ * Mouse Theme Converter is very large.
+ *
+ * The biggest cursor I found is "watch" of OuterLimits which size is
+ * 213x208. If you found bigger one, please tell me ;-)
+ */
+#define MAX_LOAD_DIMENSION 512
+#define MAX_SAVE_DIMENSION 256
+
+/* The maximum number of different nominal sizes in one cursor this
+ * plug-in can treat. This is based on the number of cursor size which
+ * gnome-appearance-properties supports.(12,16,24,32,36,40,48,64)
+ * ref. capplets/common/gnome-theme-info.c in source of
+ * gnome-control-center
+ */
+#define MAX_SIZE_NUM 8
+
+/* cursor delay is guint32 defined in Xcursor.h */
+#define CURSOR_MAX_DELAY 100000000
+#define CURSOR_DEFAULT_DELAY 50
+#define CURSOR_MINIMUM_DELAY 5
+
+#define div_255(x) (((x) + 0x80 + (((x) + 0x80) >> 8)) >> 8)
+#define READ32(f, e) read32 ((f), (e)); if (*(e)) return -1;
+#define DISPLAY_DIGIT(x) ((x) > 100) ? 3 : ((x) > 10) ? 2 : 1
+
+/*
+ * Structures...
+ */
+
+typedef struct
+{
+ gboolean crop;
+ gint size;
+ gboolean size_replace;
+ gint32 delay;
+ gboolean delay_replace;
+} XmcSaveVals;
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+
+static gint32 load_thumbnail (const gchar *filename,
+ gint32 thumb_size,
+ gint32 *width,
+ gint32 *height,
+ gint32 *num_layers,
+ GError **error);
+
+static guint32 read32 (FILE *f,
+ GError **error);
+
+static gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error);
+
+static gboolean save_dialog (const gint32 image_ID,
+ GeglRectangle *hotspotRange);
+
+static void comment_entry_callback (GtkWidget *widget,
+ gchar **commentp);
+
+static void text_view_callback (GtkTextBuffer *buffer,
+ gchar **commentp);
+
+static gboolean load_default_hotspot (const gint32 image_ID,
+ GeglRectangle *hotspotRange);
+
+static inline guint32 separate_alpha (guint32 pixel);
+
+static inline guint32 premultiply_alpha (guint32 pixel);
+
+static XcursorComments *set_cursor_comments (void);
+
+static void load_comments (const gint32 image_ID);
+
+static gboolean set_comment_to_pname (const gint32 image_ID,
+ const gchar *content,
+ const gchar *pname);
+
+static gchar *get_comment_from_pname (const gint32 image_ID,
+ const gchar *pname);
+
+static gboolean set_hotspot_to_parasite (gint32 image_ID);
+
+static gboolean get_hotspot_from_parasite (gint32 image_ID);
+
+static void set_size_and_delay (const gchar *framename,
+ guint32 *sizep,
+ guint32 *delayp,
+ GRegex *re,
+ gboolean *size_warnp);
+
+static gchar *make_framename (guint32 size,
+ guint32 delay,
+ guint indent,
+ GError **errorp);
+
+static void get_cropped_region (GeglRectangle *retrun_rgn,
+ GeglBuffer *buffer);
+
+static inline gboolean pix_is_opaque (guint32 pix);
+
+static GeglRectangle * get_intersection_of_frames (gint32 image_ID);
+
+static gboolean pix_in_region (gint32 x,
+ gint32 y,
+ GeglRectangle *xmcrp);
+
+static void find_hotspots_and_dimensions (XcursorImages *xcIs,
+ gint32 *xhot,
+ gint32 *yhot,
+ gint32 *width,
+ gint32 *height);
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+static XmcSaveVals xmcvals =
+{
+ /* saved in pdb after this plug-in's process has gone. */
+ FALSE, /* crop */
+ 32, /* size */
+ FALSE, /* size_replace */
+ CURSOR_DEFAULT_DELAY, /* delay */
+ FALSE /* delay_replace */
+};
+
+static struct
+{
+ /* saved as parasites of original image after this plug-in's process has gone.*/
+ gint32 x; /* hotspot x */
+ gint32 y; /* hotspot y */
+ gchar *comments[3]; /* copyright, license, other */
+} xmcparas = {0,};
+
+/* parasites correspond to XcursorComment type */
+static const gchar *parasiteName[3] = { "xmc-copyright",
+ "xmc-license",
+ "gimp-comment" };
+
+/*
+ * 'main()' - Main entry - just call gimp_main()...
+ */
+
+MAIN ()
+
+
+/*
+ * 'query()' - Respond to a plug-in query...
+ */
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "The width of image" },
+ { GIMP_PDB_INT32, "image-height", "The height of image" },
+ { GIMP_PDB_INT32, "image-type", "The color type of image"},
+ { GIMP_PDB_INT32, "image-num-layers", "The number of layeres" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ /* following elements are XMC specific options */
+ { GIMP_PDB_INT32, "x_hot", "X-coordinate of hot spot" },
+ { GIMP_PDB_INT32, "y_hot", "Y-coordinate of hot spot\n"
+ "Use (-1, -1) to keep original hot spot."},
+ { GIMP_PDB_INT32, "crop", "Auto-crop or not" },
+ { GIMP_PDB_INT32, "size", "Default nominal size" },
+ { GIMP_PDB_INT32, "size_replace", "Replace existent size or not." },
+ { GIMP_PDB_INT32, "delay", "Default delay" },
+ { GIMP_PDB_INT32, "delay_replace","Replace existent delay or not."},
+ { GIMP_PDB_STRING, "copyright", "Copyright information." },
+ { GIMP_PDB_STRING, "license", "License information." },
+ { GIMP_PDB_STRING, "other", "Other comment.(taken from "
+ "\"gimp-comment\" parasite)" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files of X11 Mouse Cursor file format",
+ "This plug-in loads X11 Mouse Cursor (XMC) files.",
+ "Takeshi Matsuyama <tksmashiw@gmail.com>",
+ "Takeshi Matsuyama",
+ "26 May 2009",
+ N_("X11 Mouse Cursor"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args,
+ load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, XCURSOR_MIME_TYPE);
+ gimp_register_magic_load_handler (LOAD_PROC,
+ XCURSOR_EXTENSION,
+ "",
+ "0,string,Xcur");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads only first frame of X11 Mouse Cursor's "
+ "animation sequence which nominal size is the closest "
+ "of thumb-size to be used as a thumbnail",
+ "",
+ "Takeshi Matsuyama <tksmashiw@gmail.com>",
+ "Takeshi Matsuyama",
+ "26 May 2009",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args,
+ thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files of X11 cursor file",
+ "This plug-in exports X11 Mouse Cursor (XMC) files",
+ "Takeshi Matsuyama <tksmashiw@gmail.com>",
+ "Takeshi Matsuyama",
+ "26 May 2009",
+ N_("X11 Mouse Cursor"),
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, XCURSOR_MIME_TYPE);
+ gimp_register_save_handler (SAVE_PROC, XCURSOR_EXTENSION, "");
+}
+
+/*
+ * 'run()' - Run the plug-in...
+ */
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GeglRectangle *hotspotRange = NULL;
+ gint32 width, height;
+ gint32 num_layers;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ DM_XMC ("run: start.\n");
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ DM_XMC ("Starting to load file.\tparam.data=%s\n",
+ param[1].data.d_string);
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ DM_XMC ("LOAD_PROC successfully load image. image_ID=%i\n", image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ DM_XMC ("Starting to load thumbnail.\tfilename=%s\tthumb-size=%d\n",
+ param[0].data.d_string, param[1].data.d_int32);
+ image_ID = load_thumbnail (param[0].data.d_string,
+ param[1].data.d_int32,
+ &width,
+ &height,
+ &num_layers,
+ &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 6;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width; /* width */
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height; /* height */
+ /* This will not work on GIMP 2.6, but not harmful. */
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = GIMP_RGBA_IMAGE; /* type */
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = num_layers; /* num_layers */
+
+ DM_XMC ("LOAD_THUMB_PROC successfully load image. image_ID=%i\n", image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ DM_XMC ("run: export %s\n", name);
+ run_mode = param[0].data.d_int32;
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+ hotspotRange = get_intersection_of_frames (image_ID);
+
+ if (! hotspotRange)
+ {
+ g_set_error (&error, 0, 0,
+ _("Cannot set the hot spot!\n"
+ "You must arrange layers so that all of them have an intersection."));
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ return;
+ }
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "XMC",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS |
+ GIMP_EXPORT_NEEDS_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ *nreturn_vals = 1;
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (SAVE_PROC, &xmcvals);
+ load_comments (image_ID);
+
+ load_default_hotspot (image_ID, hotspotRange);
+
+ if (! save_dialog (image_ID, hotspotRange))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are there!
+ */
+ if (nparams != 15)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ if (pix_in_region (param[5].data.d_int32, param[6].data.d_int32,
+ hotspotRange))
+ { /* if passed hotspot is acceptable, use that ones. */
+ xmcparas.x = param[5].data.d_int32;
+ xmcparas.y = param[6].data.d_int32;
+ }
+ else
+ {
+ load_default_hotspot (image_ID, hotspotRange);
+ /* you can purposely choose non acceptable values for hotspot
+ to use cursor's original values. */
+ }
+ xmcvals.crop = param[7].data.d_int32;
+ xmcvals.size = param[8].data.d_int32;
+ xmcvals.size_replace = param[9].data.d_int32;
+ /* load delay */
+ if (param[10].data.d_int32 < CURSOR_MINIMUM_DELAY)
+ {
+ xmcvals.delay = CURSOR_DEFAULT_DELAY;
+ }
+ else
+ {
+ xmcvals.delay = param[10].data.d_int32;
+ }
+ xmcvals.delay_replace = param[11].data.d_int32;
+ load_comments (image_ID);
+ for (i = 0; i < 3; ++i)
+ {
+ if (param[i + 12].data.d_string &&
+ g_utf8_validate (param[i + 12].data.d_string, -1, NULL))
+ {
+ xmcparas.comments[i] = g_strdup (param[i + 12].data.d_string);
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data... */
+ gimp_get_data (SAVE_PROC, &xmcvals);
+ load_comments (image_ID);
+ load_default_hotspot (image_ID, hotspotRange);
+ break;
+
+ default:
+ break;
+ }
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string, image_ID,
+ drawable_ID, orig_image_ID, &error))
+ {
+ gimp_set_data (SAVE_PROC, &xmcvals, sizeof (XmcSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ g_free (hotspotRange);
+
+ for (i = 0; i < 3 ; ++i)
+ {
+ g_free (xmcparas.comments[i]);
+ xmcparas.comments[i] = NULL;
+ }
+ }
+ else
+ {
+ DM_XMC ("name=%s\n", name);
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+ DM_XMC ("run: finish\n");
+}
+
+
+/*
+ * 'load_image()' - Load a X cursor image into a new image window.
+ */
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fp;
+ gint32 image_ID;
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ XcursorComments *commentsp; /* pointer to comments */
+ XcursorImages *imagesp; /* pointer to images*/
+ guint32 delay; /* use guint32 instead CARD32(in X11/Xmd.h) */
+ gchar *framename; /* name of layer */
+ guint32 *tmppixel; /* pixel data (guchar * bpp = guint32) */
+ gint img_width;
+ gint img_height;
+ gint i, j;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Open the file and check it is a valid X cursor */
+
+ fp = g_fopen (filename, "rb");
+
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ if (! XcursorFileLoad (fp, &commentsp, &imagesp))
+ {
+ g_set_error (error, 0, 0, _("'%s' is not a valid X cursor."),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ /* check dimension is valid. */
+
+ for (i = 0; i < imagesp->nimage; i++)
+ {
+ if (imagesp->images[i]->width > MAX_LOAD_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("Frame %d of '%s' is too wide for an X cursor."),
+ i + 1, gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+ if (imagesp->images[i]->height > MAX_LOAD_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("Frame %d of '%s' is too high for an X cursor."),
+ i + 1, gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+ }
+
+ find_hotspots_and_dimensions (imagesp,
+ &xmcparas.x, &xmcparas.y,
+ &img_width, &img_height);
+
+ DM_XMC ("xhot=%i,\tyhot=%i,\timg_width=%i,\timg_height=%i\n",
+ xmcparas.x, xmcparas.y, img_width, img_height);
+
+ image_ID = gimp_image_new (img_width, img_height, GIMP_RGB);
+
+ gimp_image_set_filename (image_ID, filename);
+
+ if (! set_hotspot_to_parasite (image_ID))
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ /* Temporary buffer */
+ tmppixel = g_new (guint32, img_width * img_height);
+
+ /* load each frame to each layer one by one */
+ for (i = 0; i < imagesp->nimage; i++)
+ {
+ gint width = imagesp->images[i]->width;
+ gint height = imagesp->images[i]->height;
+
+ delay = imagesp->images[i]->delay;
+
+ if (delay < CURSOR_MINIMUM_DELAY)
+ {
+ delay = CURSOR_DEFAULT_DELAY;
+ }
+
+ DM_XMC ("images[%i]->delay=%i\twidth=%d\theight=%d\n",
+ i ,delay, imagesp->images[i]->width, imagesp->images[i]->height);
+
+ framename = make_framename (imagesp->images[i]->size, delay,
+ DISPLAY_DIGIT (imagesp->nimage), error);
+ if (! framename)
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ layer_ID = gimp_layer_new (image_ID, framename, width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ /* Adjust layer position to let hotspot sit on the same point. */
+ gimp_item_transform_translate (layer_ID,
+ xmcparas.x - imagesp->images[i]->xhot,
+ xmcparas.y - imagesp->images[i]->yhot);
+
+ g_free (framename);
+
+ /* Get the buffer for our load... */
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ /* set color to each pixel */
+ for (j = 0; j < width * height; j++)
+ {
+ tmppixel[j] = separate_alpha (imagesp->images[i]->pixels[j]);
+ }
+
+ /* set pixel */
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, tmppixel, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((i + 1) / imagesp->nimage);
+
+ g_object_unref (buffer);
+ }
+
+ g_free (tmppixel);
+
+ gimp_progress_update (1.0);
+
+ /* Comment parsing */
+
+ if (commentsp)
+ {
+ for (i = 0; i < commentsp->ncomment; ++i)
+ {
+ DM_XMC ("comment type=%d\tcomment=%s\n",
+ commentsp->comments[i]->comment_type,
+ commentsp->comments[i]->comment);
+ if (! set_comment_to_pname (image_ID,
+ commentsp->comments[i]->comment,
+ parasiteName[commentsp->comments[i]->comment_type -1]))
+ {
+ DM_XMC ("Failed to write %ith comment.\n", i);
+ fclose (fp);
+ return -1;
+ }
+ }
+ }
+
+ DM_XMC ("Comment parsing done.\n");
+ XcursorImagesDestroy (imagesp);
+ XcursorCommentsDestroy (commentsp);
+ fclose (fp);
+
+ gimp_progress_end ();
+
+ return image_ID;
+}
+
+/*
+ * load_thumbnail
+ */
+
+static gint32
+load_thumbnail (const gchar *filename,
+ gint32 thumb_size,
+ gint32 *thumb_width,
+ gint32 *thumb_height,
+ gint32 *thumb_num_layers,
+ GError **error)
+{
+ /* Return only one frame for thumbnail.
+ * We select first frame of an animation sequence which nominal size is the
+ * closest of thumb_size.
+ */
+
+ XcursorImages *xcIs = NULL; /* use to find the dimensions of thumbnail */
+ XcursorImage *xcI; /* temporary pointer to XcursorImage */
+ guint32 *positions; /* array of the offsets of image chunks */
+ guint32 size; /* nominal size */
+ guint32 diff; /* difference between thumb_size and current size */
+ guint32 min_diff = XCURSOR_IMAGE_MAX_SIZE; /* minimum value of diff */
+ guint32 type; /* chunk type */
+ FILE *fp = NULL;
+ gint32 image_ID = -1;
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ guint32 *tmppixel; /* pixel data (guchar * bpp = guint32) */
+ guint32 ntoc = 0; /* the number of table of contents */
+ gint sel_num = -1; /* the index of selected image chunk */
+ gint width;
+ gint height;
+ gint i;
+
+ g_return_val_if_fail (thumb_width, -1);
+ g_return_val_if_fail (thumb_height, -1);
+ g_return_val_if_fail (thumb_num_layers, -1);
+
+ *thumb_width = 0;
+ *thumb_height = 0;
+ *thumb_num_layers = 0;
+
+ fp = g_fopen (filename, "rb");
+
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ /* From this line, we make a XcursorImages struct so that we can find out the
+ * width and height of entire image.
+ * We can use XcursorFileLoadImages (fp, thumb_size) from libXcursor instead
+ * of this ugly code but XcursorFileLoadImages loads all pixel data of the
+ * image chunks on memory thus we should not use it.
+ */
+
+ /* find which image chunk is preferred to load. */
+
+ /* skip magic, headersize, version */
+ fseek (fp, 12, SEEK_SET);
+ /* read the number of chunks */
+ ntoc = READ32 (fp, error)
+ if (ntoc > (G_MAXUINT32 / sizeof (guint32)))
+ {
+ g_set_error (error, 0, 0,
+ "'%s' seems to have an incorrect toc size.",
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+ positions = g_malloc (ntoc * sizeof (guint32));
+
+ /* enter list of toc(table of contents) */
+ for (; ntoc > 0; --ntoc)
+ {
+ /* read entry type */
+ type = READ32 (fp, error)
+ if (type != XCURSOR_IMAGE_TYPE)
+ {
+ /* not a image */
+
+ /* skip rest of this content */
+ fseek (fp, 8, SEEK_CUR);
+ }
+ else
+ {
+ /* this content is image */
+
+ size = READ32 (fp, error)
+ positions[*thumb_num_layers] = READ32 (fp, error)
+ /* is this image is more preferred than selected before? */
+ diff = MAX (thumb_size, size) - MIN (thumb_size, size);
+ if (diff < min_diff)
+ {/* the image size is closer than current selected image */
+ min_diff = diff;
+ sel_num = *thumb_num_layers;
+ }
+ ++*thumb_num_layers;
+ }
+ }
+
+ if (sel_num < 0)
+ {
+ g_set_error (error, 0, 0,
+ _("there is no image chunk in \"%s\"."),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ /* get width and height of entire image */
+
+ /* Let's make XcursorImages */
+ xcIs = XcursorImagesCreate (*thumb_num_layers);
+ xcIs->nimage = *thumb_num_layers;
+ for (i = 0; i < xcIs->nimage; ++i)
+ {
+ /* make XcursorImage with no pixel buffer */
+ xcI = XcursorImageCreate (0, 0);
+ /* go to the image chunk header */
+ fseek (fp, positions[i], SEEK_SET);
+ /* skip chunk header */
+ fseek (fp, 16, SEEK_CUR);
+ /* read properties of this image to determine entire image dimensions */
+ xcI->width = READ32 (fp, error)
+ xcI->height = READ32 (fp, error)
+ xcI->xhot = READ32 (fp, error)
+ xcI->yhot = READ32 (fp, error)
+
+ xcIs->images[i] = xcI;
+ }
+
+ DM_XMC ("selected size is %i or %i\n",
+ thumb_size - min_diff, thumb_size + min_diff);
+
+ /* get entire image dimensions */
+ find_hotspots_and_dimensions (xcIs, NULL, NULL, thumb_width, thumb_height);
+
+ DM_XMC ("width=%i\theight=%i\tnum-layers=%i\n",
+ *thumb_width, *thumb_height, xcIs->nimage);
+
+ /* dimension check */
+ if (*thumb_width > MAX_LOAD_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("'%s' is too wide for an X cursor."),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ if (*thumb_height > MAX_LOAD_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("'%s' is too high for an X cursor."),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return -1;
+ }
+
+ /* create new image! */
+
+ width = xcIs->images[sel_num]->width;
+ height = xcIs->images[sel_num]->height;
+
+ image_ID = gimp_image_new (width, height, GIMP_RGB);
+
+ layer_ID = gimp_layer_new (image_ID, NULL, width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ /*
+ * Get the drawable and set the pixel region for our load...
+ */
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ /* Temporary buffer */
+ tmppixel = g_new (guint32, width * height);
+
+ /* copy the chunk data to tmppixel */
+ fseek (fp, positions[sel_num], SEEK_SET);
+ fseek (fp, 36, SEEK_CUR); /* skip chunk header(16bytes), xhot, yhot, width, height, delay */
+
+ for (i = 0; i < width * height; i++)
+ {
+ tmppixel[i] = READ32 (fp, error)
+ /* get back separate alpha */
+ tmppixel[i] = separate_alpha (tmppixel[i]);
+ }
+
+ /* set pixel */
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, tmppixel, GEGL_AUTO_ROWSTRIDE);
+
+ /* free tmppixel */
+ g_free(tmppixel);
+ g_free (positions);
+ fclose (fp);
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
+
+/* read guint32 value from f despite of host's byte order. */
+static guint32
+read32 (FILE *f,
+ GError **error)
+{
+ guchar p[4];
+ guint32 ret;
+
+ if (fread (p, 1, 4, f) != 4)
+ {
+ g_set_error (error, 0, 0, _("A read error occurred."));
+ return 0;
+ }
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ ret = p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24);
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+ ret = p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24);
+#elif G_BYTE_ORDER == G_PDP_ENDIAN
+ ret = p[2] + (p[3]<<8) + (p[0]<<16) + (p[1]<<24);
+#else
+ g_return_val_if_rearched ();
+#endif
+
+ return ret;
+}
+
+/* 'save_dialog ()'
+ */
+static gboolean
+save_dialog (const gint32 image_ID,
+ GeglRectangle *hotspotRange)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *box;
+ GtkAdjustment *adjustment;
+ GtkWidget *alignment;
+ GtkWidget *tmpwidget;
+ GtkWidget *label;
+ GtkTextBuffer *textbuffer;
+ GValue val = G_VALUE_INIT;
+ gint x1, x2, y1, y2;
+ gboolean run;
+
+ g_value_init (&val, G_TYPE_DOUBLE);
+ dialog = gimp_export_dialog_new (_("X11 Mouse Cursor"),
+ PLUG_IN_BINARY, SAVE_PROC);
+
+ /*
+ * parameter settings
+ */
+ frame = gimp_frame_new (_("XMC Options"));
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (9, 3, FALSE);
+ gtk_widget_show (table);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ /*
+ * Hotspot
+ */
+ /* label "Hot spot _X:" + spinbox */
+ x1 = hotspotRange->x;
+ x2 = hotspotRange->width + hotspotRange->x - 1;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (xmcparas.x, x1, x2, 1, 5, 0);
+ tmpwidget = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (tmpwidget), TRUE);
+ g_value_set_double (&val, 1.0);
+ g_object_set_property (G_OBJECT (tmpwidget), "xalign", &val);/* align right*/
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Hot spot _X:"), 0, 0.5, tmpwidget, 1, TRUE);
+ gtk_widget_show (tmpwidget);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xmcparas.x);
+
+ gimp_help_set_help_data (tmpwidget,
+ _("Enter the X coordinate of the hot spot. "
+ "The origin is top left corner."),
+ NULL);
+
+ /* label "Y:" + spinbox */
+ y1 = hotspotRange->y;
+ y2 = hotspotRange->height + hotspotRange->y - 1;
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (xmcparas.y, y1, y2, 1, 5, 0);
+ tmpwidget = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (tmpwidget), TRUE);
+ g_value_set_double (&val, 1.0);
+ g_object_set_property (G_OBJECT (tmpwidget), "xalign", &val);/* align right*/
+ gimp_table_attach_aligned (GTK_TABLE (table), 1, 0,
+ "_Y:", 1.0, 0.5, tmpwidget, 1, TRUE);
+ gtk_widget_show (tmpwidget);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xmcparas.y);
+
+ gimp_help_set_help_data (tmpwidget,
+ _("Enter the Y coordinate of the hot spot. "
+ "The origin is top left corner."),
+ NULL);
+
+ /*
+ * Auto-crop
+ */
+ /* check button */
+ tmpwidget =
+ gtk_check_button_new_with_mnemonic (_("_Auto-Crop all frames."));
+ gtk_table_attach (GTK_TABLE (table),
+ tmpwidget, 0, 3, 1, 2, GTK_FILL, 0, 0, 10);
+ gtk_widget_show (tmpwidget);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpwidget),
+ xmcvals.crop);
+ gtk_widget_show (tmpwidget);
+ g_signal_connect (tmpwidget, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &xmcvals.crop);
+ /* tooltip */
+ gimp_help_set_help_data (tmpwidget,
+ _("Remove the empty borders of all frames.\n"
+ "This reduces the file size and may fix "
+ "the problem that some large cursors disorder "
+ "the screen.\n"
+ "Uncheck if you plan to edit the exported "
+ "cursor using other programs."),
+ NULL);
+
+ /*
+ * size
+ */
+ tmpwidget =
+ gimp_int_combo_box_new ("12px", 12, "16px", 16,
+ "24px", 24, "32px", 32,
+ "36px", 36, "40px", 40,
+ "48px", 48, "64px", 64, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (tmpwidget),
+ 32,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &xmcvals.size);
+ gtk_widget_show (tmpwidget);
+ /* tooltip */
+ gimp_help_set_help_data (tmpwidget,
+ _("Choose the nominal size of frames.\n"
+ "If you don't have plans to make multi-sized "
+ "cursor, or you have no idea, leave it \"32px\".\n"
+ "Nominal size has no relation with the actual "
+ "size (width or height).\n"
+ "It is only used to determine which frame depends "
+ "on which animation sequence, and which sequence "
+ "is used based on the value of "
+ "\"gtk-cursor-theme-size\"."),
+ NULL);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Size:"), 0, 0.5, tmpwidget, 3, TRUE);
+ /* Replace size ? */
+ tmpwidget =
+ gimp_int_radio_group_new (FALSE, NULL, G_CALLBACK (gimp_radio_button_update),
+ &xmcvals.size_replace, xmcvals.size_replace,
+ _("_Use this value only for a frame which size "
+ "is not specified."),
+ FALSE, NULL,
+ _("_Replace the size of all frames even if it "
+ "is specified."),
+ TRUE, NULL,
+ NULL);
+ alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+ gtk_widget_show (alignment);
+ gtk_table_attach (GTK_TABLE (table), alignment, 0, 3, 3, 4, 0, 0, 0, 0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 6, 20, 0); /*padding left*/
+ gtk_container_add (GTK_CONTAINER (alignment), tmpwidget);
+ gtk_widget_show (tmpwidget);
+
+ /*
+ * delay
+ */
+ /* spin button */
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4, _("_Delay:"),
+ 0, 0.5, box, 3, TRUE);
+ gtk_widget_show (box);
+
+ gimp_help_set_help_data (box,
+ _("Enter time span in milliseconds in which "
+ "each frame is rendered."),
+ NULL);
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (xmcvals.delay, CURSOR_MINIMUM_DELAY,
+ CURSOR_MAX_DELAY, 1, 5, 0);
+ tmpwidget = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (tmpwidget), TRUE);
+ g_value_set_double (&val, 1.0);
+ g_object_set_property (G_OBJECT (tmpwidget), "xalign", &val);/* align right*/
+ gtk_box_pack_start (GTK_BOX (box), tmpwidget, TRUE, TRUE, 0);
+ gtk_widget_show (tmpwidget);
+
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xmcvals.delay);
+
+ /* appended "ms" */
+ tmpwidget = gtk_label_new ("ms");
+ gtk_label_set_xalign (GTK_LABEL (tmpwidget), 0.0); /*align left*/
+ gtk_box_pack_start (GTK_BOX (box), tmpwidget, TRUE, TRUE, 0);
+ gtk_widget_show (tmpwidget);
+
+ /* Replace delay? */
+ tmpwidget =
+ gimp_int_radio_group_new (FALSE, NULL, G_CALLBACK (gimp_radio_button_update),
+ &xmcvals.delay_replace, xmcvals.delay_replace,
+ _("_Use this value only for a frame which delay "
+ "is not specified."),
+ FALSE, NULL,
+ _("_Replace the delay of all frames even if it "
+ "is specified."),
+ TRUE, NULL,
+ NULL);
+ alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+ gtk_widget_show (alignment);
+ gtk_table_attach (GTK_TABLE (table), alignment, 0, 3, 5, 6, 0, 0, 0, 0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 6, 20, 0); /*padding left*/
+ gtk_container_add (GTK_CONTAINER (alignment), tmpwidget);
+ gtk_widget_show (tmpwidget);
+
+ /*
+ * Copyright
+ */
+ tmpwidget = gtk_entry_new ();
+ /* Maximum length will be clamped to 65536 */
+ gtk_entry_set_max_length (GTK_ENTRY (tmpwidget), XCURSOR_COMMENT_MAX_LEN);
+
+ if (xmcparas.comments[0])
+ {
+ gtk_entry_set_text (GTK_ENTRY (tmpwidget),
+ gimp_any_to_utf8 (xmcparas.comments[0], - 1, NULL));
+ /* show warning if comment is over 65535 characters
+ * because gtk_entry can hold only that. */
+ if (strlen (gtk_entry_get_text (GTK_ENTRY (tmpwidget))) >= 65535)
+ g_message (_("The part of copyright information "
+ "that exceeded 65535 characters was removed."));
+ }
+
+ g_signal_connect (tmpwidget, "changed",
+ G_CALLBACK (comment_entry_callback),
+ xmcparas.comments);
+ gtk_widget_show (tmpwidget);
+ /* tooltip */
+ gimp_help_set_help_data (tmpwidget,
+ _("Enter copyright information."),
+ NULL);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 6, _("_Copyright:"),
+ 0, 0.5, tmpwidget, 3, FALSE);
+ /*
+ * License
+ */
+ tmpwidget = gtk_entry_new ();
+ /* Maximum length will be clamped to 65536 */
+ gtk_entry_set_max_length (GTK_ENTRY (tmpwidget), XCURSOR_COMMENT_MAX_LEN);
+
+ if (xmcparas.comments[1])
+ {
+ gtk_entry_set_text (GTK_ENTRY (tmpwidget),
+ gimp_any_to_utf8 (xmcparas.comments[1], - 1, NULL));
+ /* show warning if comment is over 65535 characters
+ * because gtk_entry can hold only that. */
+ if (strlen (gtk_entry_get_text (GTK_ENTRY (tmpwidget))) >= 65535)
+ g_message (_("The part of license information "
+ "that exceeded 65535 characters was removed."));
+ }
+
+ g_signal_connect (tmpwidget, "changed",
+ G_CALLBACK (comment_entry_callback),
+ xmcparas.comments + 1);
+ gtk_widget_show (tmpwidget);
+ /* tooltip */
+ gimp_help_set_help_data (tmpwidget,
+ _("Enter license information."),
+ NULL);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 7, _("_License:"),
+ 0, 0.5, tmpwidget, 3, FALSE);
+ /*
+ * Other
+ */
+ /* We use gtk_text_view for "Other" while "Copyright" & "License" is entered
+ * in gtk_entry because We want allow '\n' for "Other". */
+ label = gtk_label_new_with_mnemonic (_("_Other:"));
+ gtk_widget_show (label);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0); /*align top-left*/
+ gtk_label_set_yalign (GTK_LABEL (label), 0.0); /*align top-left*/
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 8, 9, GTK_FILL, 0, 0, 0);
+ /* content of Other */
+ /* scrolled window */
+ box = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (box),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (box),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_table_attach (GTK_TABLE (table), box, 1, 3, 8, 9, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (box);
+ /* textbuffer */
+ textbuffer = gtk_text_buffer_new (NULL);
+ if (xmcparas.comments[2])
+ gtk_text_buffer_set_text (textbuffer,
+ gimp_any_to_utf8 (xmcparas.comments[2], -1, NULL),
+ -1);
+ g_signal_connect (textbuffer, "changed",
+ G_CALLBACK (text_view_callback),
+ xmcparas.comments + 2);
+ /* textview */
+ tmpwidget =
+ gtk_text_view_new_with_buffer (GTK_TEXT_BUFFER (textbuffer));
+ gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW (tmpwidget), FALSE);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tmpwidget), GTK_WRAP_WORD);
+ g_object_unref (textbuffer);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tmpwidget), GTK_WRAP_WORD);
+ gtk_container_add (GTK_CONTAINER (box), tmpwidget);
+ gtk_widget_show (tmpwidget);
+ /* tooltip */
+ gimp_help_set_help_data (tmpwidget,
+ _("Enter other comment if you want."),
+ NULL);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), tmpwidget);
+
+ /*
+ * all widget is prepared. Let's show dialog.
+ */
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+/*
+ * callback function of gtk_entry for "copyright" and "license".
+ * "other" is processed by text_view_callback
+ */
+static void
+comment_entry_callback (GtkWidget *widget,
+ gchar **commentp)
+{
+ const gchar *text;
+
+ g_return_if_fail (commentp);
+
+ text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ /* This will not happen because sizeof(gtk_entry) < XCURSOR_COMMENT_MAX_LEN */
+ g_return_if_fail (strlen (text) <= XCURSOR_COMMENT_MAX_LEN);
+
+ g_free (*commentp);
+ *commentp = g_strdup (text);
+}
+
+static void
+text_view_callback (GtkTextBuffer *buffer,
+ gchar **commentp)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+ gchar *text;
+
+ g_return_if_fail (commentp != NULL);
+
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+ text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
+
+ if (strlen (text) > XCURSOR_COMMENT_MAX_LEN)
+ {
+ g_message (_("Comment is limited to %d characters."),
+ XCURSOR_COMMENT_MAX_LEN);
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start_iter,
+ XCURSOR_COMMENT_MAX_LEN - 1);
+ gtk_text_buffer_get_end_iter (buffer, &end_iter);
+
+ gtk_text_buffer_delete (buffer, &start_iter, &end_iter);
+ }
+ else
+ {
+ g_free (*commentp);
+ *commentp = g_strdup (text);
+ }
+}
+
+/**
+ * Set default hotspot based on hotspotRange.
+**/
+static gboolean
+load_default_hotspot (const gint32 image_ID,
+ GeglRectangle *hotspotRange)
+{
+
+ g_return_val_if_fail (hotspotRange, FALSE);
+
+ if ( /* if we cannot load hotspot correctly */
+ ! get_hotspot_from_parasite (image_ID) ||
+ /* ,or hostspot is out of range */
+ ! pix_in_region (xmcparas.x, xmcparas.y, hotspotRange))
+ {
+ /* then use top left point of hotspotRange as fallback. */
+ xmcparas.x = hotspotRange->x;
+ xmcparas.y = hotspotRange->y;
+ }
+
+ return TRUE;
+}
+
+/*
+ * 'save_image ()' - Save the specified image to X cursor file.
+ */
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ GError **error)
+{
+ FILE *fp; /* File pointer */
+ gboolean dimension_warn = FALSE; /* become TRUE if even one
+ * of the dimensions of the
+ * frames of the cursor is
+ * over
+ * MAX_BITMAP_CURSOR_SIZE */
+ gboolean size_warn = FALSE; /* become TRUE if even one
+ * of the nominal size of
+ * the frames is not
+ * supported by
+ * gnome-appearance-properties */
+ GRegex *re; /* used to get size and delay from
+ * framename */
+ XcursorComments *commentsp; /* pointer to comments */
+ XcursorImages *imagesp; /* pointer to images */
+ gint32 *layers; /* Array of layer */
+ gint32 *orig_layers; /* Array of layer of orig_image */
+ gint nlayers; /* Number of layers */
+ gchar *framename; /* framename of a layer */
+ GeglRectangle save_rgn; /* region to save */
+ gint layer_xoffset, layer_yoffset;
+ /* temporary buffer which store pixel data (guchar * bpp = guint32) */
+ guint32 pixelbuf[SQR (MAX_SAVE_DIMENSION)];
+ gint i, j; /* Looping vars */
+
+ /* This will be used in set_size_and_delay function later. To
+ * define this in that function is easy to read but place here to
+ * reduce overheads.
+ */
+ re = g_regex_new ("[(][ 0]*(\\d+)[ ]*(px|ms)[ ]*[)]",
+ G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+
+ gimp_progress_init_printf (_("Saving '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /*
+ * Open the file pointer.
+ */
+ DM_XMC ("Open the file pointer.\n");
+ fp = g_fopen (filename, "wb");
+ if (fp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ /* get layers */
+ orig_layers = gimp_image_get_layers (orig_image_ID, &nlayers);
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+
+ /* create new XcursorImages. */
+ imagesp = XcursorImagesCreate (nlayers);
+ if (!imagesp)
+ {
+ DM_XMC ("Failed to XcursorImagesCreate!\n");
+ fclose (fp);
+ return FALSE;
+ }
+ imagesp->nimage = nlayers;
+
+ /* XcursorImages also have `name' member but it is not used as long as I know.
+ We leave it NULL here. */
+
+ /*
+ * Now we start to convert each layer to a XcurosrImage one by one.
+ */
+ for (i = 0; i < nlayers; i++)
+ {
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+
+ buffer = gimp_drawable_get_buffer (layers[nlayers - 1 - i]);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ format = babl_format ("R'G'B'A u8");
+
+ /* get framename of this layer */
+ framename = gimp_item_get_name (layers[nlayers - 1 - i]);
+ /* get offset of this layer. */
+ gimp_drawable_offsets (layers[nlayers - 1 - i], &layer_xoffset, &layer_yoffset);
+
+ /*
+ * layer dimension check.
+ */
+ DM_XMC ("layer size check.\n");
+ /* We allow to save a cursor which dimensions are no more than
+ * MAX_SAVE_DIMENSION but after auto-cropping, we warn (only
+ * warn, don't stop) if dimension is over
+ * MAX_BITMAP_CURSOR_SIZE.
+ */
+ if (width > MAX_SAVE_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("Frame '%s' is too wide. Please reduce to no more than %dpx."),
+ gimp_any_to_utf8 (framename, -1, NULL),
+ MAX_SAVE_DIMENSION);
+ fclose (fp);
+ return FALSE;
+ }
+
+ if (height > MAX_SAVE_DIMENSION)
+ {
+ g_set_error (error, 0, 0,
+ _("Frame '%s' is too high. Please reduce to no more than %dpx."),
+ gimp_any_to_utf8 (framename, -1, NULL),
+ MAX_SAVE_DIMENSION);
+ fclose (fp);
+ return FALSE;
+ }
+
+ if (height == 0 || width == 0)
+ {
+ g_set_error (error, 0, 0,
+ _("Width and/or height of frame '%s' is zero!"),
+ gimp_any_to_utf8 (framename, -1, NULL));
+ fclose (fp);
+ return FALSE;
+ }
+
+ if (xmcvals.crop) /* with auto-cropping */
+ {
+ /* get the region of auto-cropped area. */
+ DM_XMC ("get_cropped_region\n");
+ get_cropped_region (&save_rgn, buffer);
+
+ /* don't forget save_rgn's origin is not a entire image
+ * but a layer which we are doing on.
+ */
+
+ if (save_rgn.width == 0 || save_rgn.height == 0)
+ {/* perfectly transparent frames become 1x1px transparent pixel. */
+ DM_XMC ("get_cropped_region return 0.\n");
+ imagesp->images[i] = XcursorImageCreate (1, 1);
+ if (!imagesp->images[i])
+ {
+ DM_XMC ("Failed to XcursorImageCreate.\n");
+ fclose (fp);
+ return FALSE;
+ }
+ imagesp->images[i]->pixels[0] = 0x0;
+ imagesp->images[i]->xhot = 0;
+ imagesp->images[i]->yhot = 0;
+ set_size_and_delay (framename, &(imagesp->images[i]->size),
+ &(imagesp->images[i]->delay), re,
+ &size_warn);
+ continue;
+ }
+ /* OK save_rgn is not 0x0 */
+ /* is hotspot in save_rgn ? */
+ if (! pix_in_region (xmcparas.x - layer_xoffset,
+ xmcparas.y - layer_yoffset,
+ &save_rgn))
+ { /* if hotspot is not on save_rgn */
+ g_set_error (error, 0, 0,
+ _("Cannot export the cursor because the hot spot "
+ "is not on frame '%s'.\n"
+ "Try to change the hot spot position, "
+ "layer geometry or export without auto-crop."),
+ gimp_any_to_utf8 (framename, -1, NULL));
+ fclose (fp);
+ return FALSE;
+ }
+ }
+ else /* if without auto-cropping... */
+ {
+ /* set save_rgn for the case not to auto-crop */
+ save_rgn.width = width;
+ save_rgn.height = height;
+ save_rgn.x = 0;
+ save_rgn.y = 0;
+ }
+
+ /* We warn if the dimension of the layer is over MAX_BITMAP_CURSOR_SIZE. */
+ if (! dimension_warn)
+ {
+ if (save_rgn.width > MAX_BITMAP_CURSOR_SIZE ||
+ save_rgn.height > MAX_BITMAP_CURSOR_SIZE)
+ {
+ dimension_warn = TRUE;
+ /* actual warning is done after the cursor is successfully saved.*/
+ }
+ }
+ /*
+ * Create new XcursorImage.
+ */
+ DM_XMC ("create new xcursorimage.\twidth=%i\theight=%i\n",
+ save_rgn.width, save_rgn.height);
+ imagesp->images[i] = XcursorImageCreate (save_rgn.width, save_rgn.height);
+ /* Cursor width & height is automatically set by function */
+ /* XcursorImageCreate, so no need to set manually. */
+ if (!imagesp->images[i])
+ {
+ DM_XMC ("Failed to XcursorImageCreate.\n");
+ fclose (fp);
+ return FALSE;
+ }
+ /*
+ ** set images[i]'s xhot & yhot.
+ */
+ /* [Cropped layer's hotspot] =
+ [image's hotspot] - [layer's offset] - [save_rgn's offset]. */
+ DM_XMC ("xhot=%i\tsave_rgn->xoffset=%i\tlayer_xoffset=%i\n",
+ xmcparas.x, layer_xoffset, save_rgn.x);
+ DM_XMC ("yhot=%i\tsave_rgn->yoffset=%i\tlayer_yoffset=%i\n",
+ xmcparas.y, layer_yoffset, save_rgn.y);
+ imagesp->images[i]->xhot = xmcparas.x - layer_xoffset - save_rgn.x;
+ imagesp->images[i]->yhot = xmcparas.y - layer_yoffset - save_rgn.y;
+ DM_XMC ("images[%i]->xhot=%i\tyhot=%i\n", i,
+ imagesp->images[i]->xhot, imagesp->images[i]->yhot);
+
+ /*
+ * set images[i]->pixels
+ */
+ /* get image data to pixelbuf. */
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (save_rgn.x, save_rgn.y,
+ save_rgn.width, save_rgn.height), 1.0,
+ format, pixelbuf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /*convert pixel date to XcursorPixel. */
+ g_assert (save_rgn.width * save_rgn.height < SQR (MAX_SAVE_DIMENSION));
+ for (j = 0; j < save_rgn.width * save_rgn.height; j++)
+ {
+ imagesp->images[i]->pixels[j] = premultiply_alpha (pixelbuf[j]);
+ }
+
+ /*
+ * get back size & delay from framename.
+ */
+ set_size_and_delay (framename, &(imagesp->images[i]->size),
+ &(imagesp->images[i]->delay), re, &size_warn);
+
+ /*
+ * All property of this XcursorImage is loaded.
+ */
+
+ /* set the layer name of original image with the saved value */
+ g_free (framename);
+ framename = make_framename (imagesp->images[i]->size,
+ imagesp->images[i]->delay,
+ DISPLAY_DIGIT (imagesp->nimage),
+ error);
+ if (! framename)
+ {
+ fclose (fp);
+ return FALSE;
+ }
+
+ gimp_item_set_name (orig_layers[nlayers - 1 - i], framename);
+ g_free (framename);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update ((i + 1) / imagesp->nimage);
+ }
+
+ gimp_progress_update (1.0);
+
+ /*
+ * comment parsing
+ */
+ commentsp = set_cursor_comments ();
+
+#ifdef XMC_DEBUG
+ DM_XMC ("imagesp->nimage=%i\tname=%s\n", imagesp->nimage, imagesp->name);
+ for (i = 0; i < imagesp->nimage; ++i)
+ {
+ DM_XMC ("\timages[%i]->size=%i\n\
+ \twidth=%i\n\
+ \theight=%i\n\
+ \txhot=%i\n\
+ \tyhot=%i\n\
+ \tdelay=%i\n\
+ \t*pixels=%p\n",
+ i,
+ imagesp->images[i]->size,
+ imagesp->images[i]->width,
+ imagesp->images[i]->height,
+ imagesp->images[i]->xhot,
+ imagesp->images[i]->yhot,
+ imagesp->images[i]->delay,
+ imagesp->images[i]->pixels);
+ }
+
+ if (commentsp)
+ {
+ for (i = 0; i < commentsp->ncomment; ++i)
+ {
+ DM_XMC ("comment type=%d\tcomment=%s\n",
+ commentsp->comments[i]->comment_type,
+ commentsp->comments[i]->comment);
+ }
+ }
+#endif
+
+ /*
+ * save cursor to file *fp.
+ */
+
+ if (commentsp)
+ {
+ if (! XcursorFileSave (fp, commentsp, imagesp))
+ {
+ DM_XMC ("Failed to XcursorFileSave.\t%p\t%p\t%p\n",
+ fp, commentsp, imagesp);
+ fclose (fp);
+ return FALSE;
+ }
+
+ }
+ else /* if no comments exist */
+ {
+ if (! XcursorFileSaveImages (fp, imagesp))
+ {
+ DM_XMC ("Failed to XcursorFileSaveImages.\t%p\t%p\n", fp, imagesp);
+ fclose (fp);
+ return FALSE;
+ }
+ }
+
+ /* actual warning about dimensions */
+ if (dimension_warn)
+ {
+ g_message (_("Your cursor was successfully exported but it contains one or "
+ "more frames whose width or height is more than %ipx, "
+ "a historical max dimension value for X bitmap cursors.\n"
+ "It might be unsupported by some environments."),
+ MAX_BITMAP_CURSOR_SIZE);
+ }
+ if (size_warn)
+ {
+ g_message (_("Your cursor was successfully exported but it contains one "
+ "or more frames whose nominal size is not supported by "
+ "GNOME settings.\n"
+ "You can satisfy it by checking \"Replace the size of all "
+ "frames...\" in the export dialog, or your cursor may not "
+ "appear in GNOME settings."));
+ }
+ /*
+ * Done with the file...
+ */
+ g_regex_unref (re);
+ DM_XMC ("fp=%p\n", fp);
+ fclose (fp);
+ DM_XMC ("%i frames written.\n", imagesp->nimage);
+ XcursorImagesDestroy (imagesp);
+ DM_XMC ("Xcursor destroyed.\n");
+ XcursorCommentsDestroy (commentsp); /* this is safe even if commentsp is NULL. */
+ gimp_progress_end ();
+
+ /* Save the comment back to the original image */
+ for (i = 0; i < 3; i++)
+ {
+ gimp_image_detach_parasite (orig_image_ID, parasiteName[i]);
+
+ if (xmcparas.comments[i])
+ {
+ if (! set_comment_to_pname (orig_image_ID,
+ xmcparas.comments[i], parasiteName[i]))
+ {
+ DM_XMC ("Failed to write back %ith comment to orig_image.\n", i);
+ }
+ }
+ }
+ /* Save hotspot back to the original image */
+ set_hotspot_to_parasite (orig_image_ID);
+
+ return TRUE;
+}
+
+static inline guint32
+separate_alpha (guint32 pixel)
+{
+ guint alpha, red, green, blue;
+ guint32 retval;
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ pixel = GUINT32_TO_LE (pixel);
+#endif
+
+ blue = pixel & 0xff;
+ green = (pixel>>8) & 0xff;
+ red = (pixel>>16) & 0xff;
+ alpha = (pixel>>24) & 0xff;
+
+ if (alpha == 0)
+ return 0;
+
+ /* resume separate alpha data. */
+ red = MIN (red * 255 / alpha, 255);
+ blue = MIN (blue * 255 / alpha, 255);
+ green = MIN (green * 255 / alpha, 255);
+
+ retval = red + (green<<8) + (blue<<16) + (alpha<<24);
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ pixel = GUINT32_FROM_LE (pixel);
+#endif
+
+ return retval;
+}
+
+static inline guint32
+premultiply_alpha (guint32 pixel)
+{
+ guint alpha, red, green, blue;
+ guint32 retval;
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ pixel = GUINT32_TO_LE (pixel);
+#endif
+
+ red = pixel & 0xff;
+ green = (pixel >> 8) & 0xff;
+ blue = (pixel >> 16) & 0xff;
+ alpha = (pixel >> 24) & 0xff;
+
+ /* premultiply alpha
+ (see "premultiply_data" function at line 154 of xcursorgen.c) */
+ red = div_255 (red * alpha);
+ green = div_255 (green * alpha);
+ blue = div_255 (blue * alpha);
+
+ retval = blue + (green << 8) + (red << 16) + (alpha << 24);
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ pixel = GUINT32_FROM_LE (pixel);
+#endif
+
+ return retval;
+}
+
+/* set comments to cursor from xmcparas.comments.
+ * don't forget to XcursorCommentsDestroy returned pointer later.
+ */
+static XcursorComments *
+set_cursor_comments (void)
+{
+ gint i;
+ guint gcomlen, arraylen;
+ GArray *xcCommentsArray;
+ XcursorComment *(xcCommentp[3]) = {NULL,};
+ XcursorComments *xcCommentsp;
+
+ xcCommentsArray = g_array_new (FALSE, FALSE, sizeof (XcursorComment *));
+
+ for (i = 0; i < 3; ++i)
+ {
+ if (xmcparas.comments[i])
+ {
+ gcomlen = strlen (xmcparas.comments[i]);
+ if (gcomlen > 0)
+ {
+ xcCommentp[i] = XcursorCommentCreate (i + 1, gcomlen);
+ /* first argument of XcursorCommentCreate is comment_type
+ defined in Xcursor.h as enumerator.
+ i + 1 is appropriate when we dispose parasiteName before MAIN(). */
+ if (!xcCommentp[i])
+ {
+ g_warning ("Cannot create xcCommentp[%i]\n", i);
+ return NULL;
+ }
+ else
+ {
+ g_stpcpy (xcCommentp[i]->comment, xmcparas.comments[i]);
+ g_array_append_val (xcCommentsArray, xcCommentp[i]);
+ }
+ }
+ }
+ }
+
+ arraylen = xcCommentsArray->len;
+
+ if (arraylen == 0)
+ return NULL;
+
+ xcCommentsp = XcursorCommentsCreate (arraylen);
+ xcCommentsp->ncomment = arraylen;
+
+ for (i = 0; i < arraylen; ++i)
+ {
+ xcCommentsp->comments[i] =
+ g_array_index (xcCommentsArray, XcursorComment* ,i);
+ }
+
+ return xcCommentsp;
+}
+
+/* Load xmcparas.comments from three parasites named as "xmc-copyright",
+ * "xmc-license","gimp-comment".
+ * This alignment sequence is depends on the definition of comment_type
+ * in Xcursor.h .
+ * Don't forget to g_free each element of xmcparas.comments later.
+ */
+static void
+load_comments (const gint32 image_ID)
+{
+ gint i;
+
+ g_return_if_fail (image_ID != -1);
+
+ for (i = 0; i < 3; ++i)
+ xmcparas.comments[i] = get_comment_from_pname (image_ID, parasiteName[i]);
+}
+
+/* Set content to a parasite named as pname. if parasite already
+ * exists, append the new one to the old one with "\n"
+ */
+static gboolean
+set_comment_to_pname (const gint32 image_ID,
+ const gchar *content,
+ const gchar *pname)
+{
+ gboolean ret = FALSE;
+ gchar *tmpstring, *joind;
+ GimpParasite *parasite;
+
+ g_return_val_if_fail (image_ID != -1, FALSE);
+ g_return_val_if_fail (content, FALSE);
+
+ parasite = gimp_image_get_parasite (image_ID, pname);
+ if (! parasite)
+ {
+ parasite = gimp_parasite_new (pname, GIMP_PARASITE_PERSISTENT,
+ strlen (content) + 1, content);
+ }
+ else
+ {
+ tmpstring = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ joind = g_strjoin ("\n", tmpstring, content, NULL);
+ g_free (tmpstring);
+ parasite = gimp_parasite_new (pname, GIMP_PARASITE_PERSISTENT,
+ strlen (joind) + 1, joind);
+ g_free (joind);
+ }
+
+ if (parasite)
+ {
+ ret = gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ return ret;
+}
+
+/* get back comment from parasite name, don't forget to call
+ * g_free(returned pointer) later
+ */
+static gchar *
+get_comment_from_pname (const gint32 image_ID,
+ const gchar *pname)
+{
+ gchar *string = NULL;
+ GimpParasite *parasite;
+ glong length;
+
+ g_return_val_if_fail (image_ID != -1, NULL);
+
+ parasite = gimp_image_get_parasite (image_ID, pname);
+ length = gimp_parasite_data_size (parasite);
+
+ if (parasite)
+ {
+ if (length > XCURSOR_COMMENT_MAX_LEN)
+ {
+ length = XCURSOR_COMMENT_MAX_LEN;
+ g_message (_("The parasite \"%s\" is too long for an X cursor "
+ "comment. It was cut off to fit."),
+ gimp_any_to_utf8 (pname, -1,NULL));
+ }
+
+ string = g_strndup (gimp_parasite_data (parasite), length);
+ gimp_parasite_free (parasite);
+ }
+
+ return string;
+}
+
+/* Set hotspot to "hot-spot" parasite which format is common with that
+ * of file-xbm.
+ */
+static gboolean
+set_hotspot_to_parasite (gint32 image_ID)
+{
+ gboolean ret = FALSE;
+ gchar *tmpstr;
+ GimpParasite *parasite;
+
+ g_return_val_if_fail (image_ID != -1, FALSE);
+
+ tmpstr = g_strdup_printf ("%d %d", xmcparas.x, xmcparas.y);
+ parasite = gimp_parasite_new ("hot-spot",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (tmpstr) + 1,
+ tmpstr);
+ g_free (tmpstr);
+
+ if (parasite)
+ {
+ ret = gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ return ret;
+}
+
+/* Get back xhot & yhot from "hot-spot" parasite.
+ * If succeed, hotspot coordinate is set to xmcparas.x, xmcparas.y and
+ * return TRUE.
+ * If "hot-spot" is not found or broken, return FALSE.
+ */
+static gboolean
+get_hotspot_from_parasite (gint32 image_ID)
+{
+ GimpParasite *parasite;
+
+ g_return_val_if_fail (image_ID != -1, FALSE);
+
+ DM_XMC ("function: getHotsopt\n");
+
+ parasite = gimp_image_get_parasite (image_ID, "hot-spot");
+ if (!parasite) /* cannot find a parasite named "hot-spot". */
+ {
+ return FALSE;
+ }
+
+ if (sscanf (gimp_parasite_data (parasite),
+ "%i %i", &xmcparas.x, &xmcparas.y) < 2)
+ { /*cannot load hotspot.(parasite is broken?) */
+ return FALSE;
+ }
+
+ /*OK, hotspot is set to *xhotp & *yhotp. */
+ return TRUE;
+}
+
+/* Set size to sizep, delay to delayp from drawable's framename.
+ */
+static void
+set_size_and_delay (const gchar *framename,
+ guint32 *sizep,
+ guint32 *delayp,
+ GRegex *re,
+ gboolean *size_warnp)
+{
+ guint32 size = 0;
+ guint32 delay = 0;
+ gchar *digits = NULL;
+ gchar *suffix = NULL;
+ GMatchInfo *info = NULL;
+
+ g_return_if_fail (framename);
+ g_return_if_fail (sizep);
+ g_return_if_fail (delayp);
+ g_return_if_fail (re);
+
+ DM_XMC ("function: set_size_and_delay\tframename=%s\n", framename);
+
+ /* re is defined at the start of save_image() as
+ [(] : open parenthesis
+ [ ]* : ignore zero or more spaces
+ (\\d+) : the number we want to get out
+ [ ]* : ignore zero or more spaces
+ (px|ms) : whether "px"(size) or "ms"(delay)
+ [ ]* : ignore zero or more spaces
+ [)] : close parenthesis
+ This is intended to match for the animation-play plug-in. */
+
+ g_regex_match (re, framename, 0, &info);
+
+ while (g_match_info_matches (info))
+ {
+ digits = g_match_info_fetch (info, 1);
+ suffix = g_match_info_fetch (info, 2);
+
+ if (g_ascii_strcasecmp (suffix, "px") == 0)
+ {
+ if (!size) /* substitute it only for the first time */
+ {
+ if (strlen (digits) > 8) /* too large number should be clamped */
+ {
+ g_message (_("Your cursor was successfully exported but it contains one or "
+ "more frames whose size is over 8 digits.\n"
+ "We clamped it to %dpx. You should check the exported cursor."),
+ MAX_BITMAP_CURSOR_SIZE);
+ size = MAX_BITMAP_CURSOR_SIZE;
+ }
+ else
+ {
+ size = atoi (digits);
+ }
+ }
+ }
+ else /* suffix is "ms" */
+ {
+ if (!delay) /* substitute it only for the first time */
+ {
+ if (strlen (digits) > 8) /* too large number should be clamped */
+ delay = CURSOR_MAX_DELAY;
+ else
+ delay = MIN (CURSOR_MAX_DELAY, atoi (digits));
+ }
+ }
+
+ g_free (digits);
+ g_free (suffix);
+
+ g_match_info_next (info, NULL);
+ }
+
+ g_match_info_free (info);
+
+ /* if size is not set, or size_replace is TRUE, set default size
+ * (which was chosen in save dialog) */
+ if (size == 0 || xmcvals.size_replace == TRUE)
+ {
+ size = xmcvals.size;
+ }
+ else if (! *size_warnp &&
+ size != 12 && size != 16 && size != 24 && size != 32 &&
+ size != 36 && size != 40 && size != 48 && size != 64 &&
+ size != 96)
+ { /* if the size is different from these values, we warn about it after
+ successfully saving because gnome-appearance-properties only support
+ them. */
+ *size_warnp = TRUE;
+ }
+
+ *sizep = size;
+
+ /* if delay is not set, or delay_replace is TRUE, set default delay
+ * (which was chosen in save dialog) */
+ if (delay == 0 || xmcvals.delay_replace == TRUE)
+ {
+ delay = xmcvals.delay;
+ }
+
+ *delayp = delay;
+
+ DM_XMC ("set_size_and_delay return\tsize=%i\tdelay=%i\n", size, delay);
+}
+
+/* Return framename as format: "([x]px)_[i] ([t]ms) (replace)"
+ * where [x] is nominal size, [t] is delay passed as argument respectively,
+ * and [i] is an index separately counted by [x].
+ * This format is compatible with "animation-play" plug-in.
+ * Don't forget to g_free returned framename later.
+ */
+static gchar *
+make_framename (guint32 size,
+ guint32 delay,
+ guint indent,
+ GError **errorp)
+{
+ static struct
+ {
+ guint32 size;
+ guint count;
+ } Counter[MAX_SIZE_NUM + 1] = {{0,}};
+
+ int i; /* loop index */
+
+ /* don't pass 0 for size. */
+ g_return_val_if_fail (size > 0, NULL);
+
+ /* "count" member of Counter's element means how many time corresponding
+ "size" is passed to this function. The size member of the last element
+ of Counter must be 0, so Counter can have MAX_SIZE_NUM elements at most.
+ This is not a smart way but rather simple than using dynamic method. */
+
+ for (i = 0; Counter[i].size != size; ++i)
+ {
+ if (Counter[i].size == 0) /* the end of Counter elements */
+ {
+ if (i > MAX_SIZE_NUM)
+ {
+ g_set_error (errorp, 0, 0,
+ /* translators: the %i is *always* 8 here */
+ _("Sorry, this plug-in cannot handle a cursor "
+ "which contains over %i different nominal sizes."),
+ MAX_SIZE_NUM);
+ return NULL;
+ }
+ else /* append new element which "size" is given value. */
+ {
+ Counter[i].size = size;
+ break;
+ }
+ }
+ }
+
+ Counter[i].count += 1;
+
+ return g_strdup_printf ("(%dpx)_%0*d (%dms) (replace)", size, indent,
+ Counter[i].count, delay);
+}
+
+/* Get the region which is maintained when auto-crop.
+ */
+static void
+get_cropped_region (GeglRectangle *return_rgn,
+ GeglBuffer *buffer)
+{
+ gint width = gegl_buffer_get_width (buffer);
+ gint height = gegl_buffer_get_height (buffer);
+ guint32 *buf = g_malloc (MAX (width, height) * sizeof (guint32));
+ const Babl *format = babl_format ("R'G'B'A u8");
+ guint i, j;
+
+ g_return_if_fail (GEGL_IS_BUFFER (buffer));
+
+ DM_XMC ("function:get_cropped_region\n");
+
+ DM_XMC ("getTrim:\tMAX=%i\tpr->w=%i\tpr->h=%i\n", sizeof (buf)/4, pr->w, pr->h);
+
+ /* find left border. */
+ for (i = 0; i < width; ++i)
+ {
+ DM_XMC ("i=%i width=%i\n", i, width);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (i, 0, 1, height), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (j = 0; j < height; ++j)
+ {
+ if (pix_is_opaque (buf[j])) /* if a opaque pixel exist. */
+ {
+ return_rgn->x = i;
+ goto find_right;
+ }
+ }
+ }
+
+ /* pr has no opaque pixel. */
+ return_rgn->width = 0;
+ return;
+
+ /* find right border. */
+ find_right:
+ for (i = 0; i < width ; ++i)
+ {
+ DM_XMC ("width-1-i=%i height=%i\n", width - 1 - i, height);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (width - 1 - i, 0, 1, height), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (j = 0; j < height; ++j)
+ {
+ if (pix_is_opaque (buf[j])) /* if a opaque pixel exist. */
+ {
+ return_rgn->width = width - i - return_rgn->x;
+ goto find_top;
+ }
+ }
+ }
+ g_return_if_reached ();
+
+ /* find top border. */
+ find_top:
+ for (j = 0; j < height; ++j)
+ {
+ DM_XMC ("j=%i width=%i\n", j, width);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, j, width, 1), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (i = 0; i < width; ++i)
+ {
+ if (pix_is_opaque (buf[i])) /* if a opaque pixel exist. */
+ {
+ return_rgn->y = j;
+ goto find_bottom;
+ }
+ }
+ }
+
+ g_return_if_reached ();
+
+ /* find bottom border. */
+ find_bottom:
+ for (j = 0; j < height; ++j)
+ {
+ DM_XMC ("height-1-j=%i width=%i\n", height - 1 - j, width);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, height - 1 - j, width, 1), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (i = 0; i < width; ++i)
+ {
+ if (pix_is_opaque (buf[i])) /* if a opaque pixel exist. */
+ {
+ return_rgn->height = height - j - return_rgn->y;
+ goto end_trim;
+ }
+ }
+ }
+
+ g_return_if_reached ();
+
+ end_trim:
+ DM_XMC ("width=%i\theight=%i\txoffset=%i\tyoffset=%i\n",
+ return_rgn->width, return_rgn->height,
+ return_rgn->x, return_rgn->y);
+
+ g_free (buf);
+}
+
+/* Return true if alpha of pix is not 0.
+ */
+static inline gboolean
+pix_is_opaque (guint32 pix)
+{
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ pix = GUINT32_TO_LE (pix);
+#endif
+
+ return ((pix >> 24) != 0);
+}
+
+/* Get the intersection of the all layers of the image specified by image_ID.
+ * if the intersection is empty return NULL.
+ * don't forget to g_free returned pointer later.
+ */
+static GeglRectangle *
+get_intersection_of_frames (gint32 image_ID)
+{
+ GeglRectangle *iregion;
+ gint i;
+ gint32 x1 = G_MININT32, x2 = G_MAXINT32;
+ gint32 y1 = G_MININT32, y2 = G_MAXINT32;
+ gint32 x_off, y_off;
+ gint nlayers;
+ gint *layers;
+
+ g_return_val_if_fail (image_ID != -1, FALSE);
+
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+
+ for (i = 0; i < nlayers; ++i)
+ {
+ if (! gimp_drawable_offsets (layers[i], &x_off, &y_off))
+ return NULL;
+
+ x1 = MAX (x1, x_off);
+ y1 = MAX (y1, y_off);
+ x2 = MIN (x2, x_off + gimp_drawable_width (layers[i]) - 1);
+ y2 = MIN (y2, y_off + gimp_drawable_height (layers[i]) - 1);
+ }
+
+ if (x1 > x2 || y1 > y2)
+ return NULL;
+
+ /* OK intersection exists. */
+ iregion = g_new (GeglRectangle, 1);
+ iregion->x = x1;
+ iregion->y = y1;
+ iregion->width = x2 - x1 + 1;
+ iregion->height = y2 - y1 + 1;
+
+ return iregion;
+}
+
+/* If (x,y) is in xmcrp, return TRUE.
+ */
+static gboolean
+pix_in_region (gint32 x,
+ gint32 y,
+ GeglRectangle *xmcrp)
+{
+ g_return_val_if_fail (xmcrp, FALSE);
+
+ if (x < xmcrp->x || y < xmcrp->y ||
+ x >= xmcrp->x + xmcrp->width || y >= xmcrp->y + xmcrp->height)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/**
+ * Find out xhot, yhot, width and height of the Xcursor specified by xcIs.
+ * Use NULL for the value you don't want to return.
+**/
+static void
+find_hotspots_and_dimensions (XcursorImages *xcIs,
+ gint32 *xhotp,
+ gint32 *yhotp,
+ gint32 *widthp,
+ gint32 *heightp)
+{
+ gint32 dw, dh; /* the distance between hotspot and right(bottom) border */
+ gint32 max_xhot;
+ gint32 max_yhot; /* the maximum value of xhot(yhot) */
+ gint i;
+
+ g_return_if_fail (xcIs);
+
+ max_xhot = max_yhot = dw = dh = 0;
+
+ for (i = 0; i < xcIs->nimage; ++i)
+ {
+ /* xhot of entire image is the maximum value of xhot of all frames */
+ max_xhot = MAX (xcIs->images[i]->xhot, max_xhot);
+ /* same for yhot */
+ max_yhot = MAX (xcIs->images[i]->yhot, max_yhot);
+ /* the maximum distance between right border and xhot */
+ dw = MAX (dw, xcIs->images[i]->width - xcIs->images[i]->xhot);
+ /* the maximum distance between bottom border and yhot */
+ dh = MAX (dh, xcIs->images[i]->height - xcIs->images[i]->yhot);
+ }
+
+ if (xhotp)
+ *xhotp = max_xhot;
+ if (yhotp)
+ *yhotp = max_yhot;
+ if (widthp)
+ *widthp = dw + max_xhot;
+ if (heightp)
+ *heightp = dh + max_yhot;
+}
diff --git a/plug-ins/common/file-xpm.c b/plug-ins/common/file-xpm.c
new file mode 100644
index 0000000..34c946e
--- /dev/null
+++ b/plug-ins/common/file-xpm.c
@@ -0,0 +1,881 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* XPM plugin version 1.2.7 */
+
+/*
+1.2.7 fixes saving unused transparency (bug #4560)
+
+1.2.6 fixes crash when saving indexed images (bug #109567)
+
+1.2.5 only creates a "None" color entry if the image has alpha (bug #108034)
+
+1.2.4 displays an error message if saving fails (bug #87588)
+
+1.2.3 fixes bug when running in noninteractive mode
+changes alpha_threshold range from [0, 1] to [0,255] for consistency with
+the threshold_alpha plugin
+
+1.2.2 fixes bug that generated bad digits on images with more than 20000
+colors. (thanks, yanele)
+parses gtkrc (thanks, yosh)
+doesn't load parameter screen on images that don't have alpha
+
+1.2.1 fixes some minor bugs -- spaces in #XXXXXX strings, small typos in code.
+
+1.2 compute color indexes so that we don't have to use XpmSaveXImage*
+
+Previous...Inherited code from Ray Lehtiniemi, who inherited it from S & P.
+*/
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gdk/gdk.h> /* For GDK_WINDOWING_WIN32 */
+
+#ifndef GDK_WINDOWING_X11
+#ifndef XPM_NO_X
+#define XPM_NO_X
+#endif
+#else
+#include <X11/Xlib.h>
+#endif
+
+#include <X11/xpm.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static const gchar linenoise [] =
+" .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyz\
+ABCDEFGHIJKLMNOPQRSTUVWXYZ`";
+
+#define LOAD_PROC "file-xpm-load"
+#define SAVE_PROC "file-xpm-save"
+#define PLUG_IN_BINARY "file-xpm"
+#define PLUG_IN_ROLE "gimp-file-xpm"
+#define SCALE_WIDTH 125
+
+/* Structs for the save dialog */
+typedef struct
+{
+ gint threshold;
+} XpmSaveVals;
+
+typedef struct
+{
+ guchar r;
+ guchar g;
+ guchar b;
+} rgbkey;
+
+/* whether the image is color or not. global so I only have to pass
+ * one user value to the GHFunc
+ */
+static gboolean color;
+
+/* bytes per pixel. global so I only have to pass one user value
+ * to the GHFunc
+ */
+static gint cpp;
+
+/* Declare local functions */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static guchar * parse_colors (XpmImage *xpm_image);
+static void parse_image (gint32 image_ID,
+ XpmImage *xpm_image,
+ guchar *cmap);
+static gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gboolean save_dialog (void);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static XpmSaveVals xpmvals =
+{
+ 127 /* alpha threshold */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "threshold", "Alpha threshold (0-255)" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Load files in XPM (X11 Pixmap) format.",
+ "Load files in XPM (X11 Pixmap) format. "
+ "XPM is a portable image format designed to be "
+ "included in C source code. XLib provides utility "
+ "functions to read this format. Newer code should "
+ "however be using gdk-pixbuf-csource instead. "
+ "XPM supports colored images, unlike the XBM "
+ "format which XPM was designed to replace.",
+ "Spencer Kimball & Peter Mattis & Ray Lehtiniemi",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("X PixMap image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-xpixmap");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "xpm",
+ "",
+ "0, string,/*\\040XPM\\040*/");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Export files in XPM (X11 Pixmap) format.",
+ "Export files in XPM (X11 Pixmap) format. "
+ "XPM is a portable image format designed to be "
+ "included in C source code. XLib provides utility "
+ "functions to read this format. Newer code should "
+ "however be using gdk-pixbuf-csource instead. "
+ "XPM supports colored images, unlike the XBM "
+ "format which XPM was designed to replace.",
+ "Spencer Kimball & Peter Mattis & Ray Lehtiniemi & Nathan Summers",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("X PixMap image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-xpixmap");
+ gimp_register_save_handler (SAVE_PROC, "xpm", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ export = gimp_export_image (&image_ID, &drawable_ID, "XPM",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data ("file_xpm_save", &xpmvals);
+
+ /* First acquire information with a dialog */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ if (! save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ xpmvals.threshold = param[5].data.d_int32;
+
+ if (xpmvals.threshold < 0 ||
+ xpmvals.threshold > 255)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data ("file_xpm_save", &xpmvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string,
+ image_ID, drawable_ID, &error))
+ {
+ gimp_set_data ("file_xpm_save", &xpmvals, sizeof (XpmSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ XpmImage xpm_image;
+ guchar *cmap;
+ gint32 image_ID;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* read the raw file */
+ switch (XpmReadFileToXpmImage ((char *) filename, &xpm_image, NULL))
+ {
+ case XpmSuccess:
+ break;
+
+ case XpmOpenFailed:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error opening file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+
+ case XpmFileInvalid:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("XPM file invalid"));
+ return -1;
+
+ default:
+ return -1;
+ }
+
+ /* parse out the colors into a cmap */
+ cmap = parse_colors (&xpm_image);
+
+ /* create the new image */
+ image_ID = gimp_image_new (xpm_image.width,
+ xpm_image.height,
+ GIMP_RGB);
+
+ /* name it */
+ gimp_image_set_filename (image_ID, filename);
+
+ /* fill it */
+ parse_image (image_ID, &xpm_image, cmap);
+
+ /* clean up and exit */
+ g_free (cmap);
+
+ return image_ID;
+}
+
+static guchar *
+parse_colors (XpmImage *xpm_image)
+{
+#ifndef XPM_NO_X
+ Display *display;
+ Colormap colormap;
+#endif
+ gint i, j;
+ guchar *cmap;
+
+#ifndef XPM_NO_X
+ /* open the display and get the default color map */
+ display = XOpenDisplay (NULL);
+ if (display == NULL)
+ g_printerr ("Could not open display\n");
+
+ colormap = DefaultColormap (display, DefaultScreen (display));
+#endif
+
+ /* alloc a buffer to hold the parsed colors */
+ cmap = g_new0 (guchar, 4 * xpm_image->ncolors);
+
+ /* parse each color in the file */
+ for (i = 0, j = 0; i < xpm_image->ncolors; i++)
+ {
+ gchar *colorspec = "None";
+ XpmColor *xpm_color;
+#ifndef XPM_NO_X
+ XColor xcolor;
+#else
+ GdkColor xcolor;
+#endif
+
+ xpm_color = &(xpm_image->colorTable[i]);
+
+ /* pick the best spec available */
+ if (xpm_color->c_color)
+ colorspec = xpm_color->c_color;
+ else if (xpm_color->g_color)
+ colorspec = xpm_color->g_color;
+ else if (xpm_color->g4_color)
+ colorspec = xpm_color->g4_color;
+ else if (xpm_color->m_color)
+ colorspec = xpm_color->m_color;
+
+ /* parse if it's not transparent */
+ if (strcmp (colorspec, "None") != 0)
+ {
+#ifndef XPM_NO_X
+ XParseColor (display, colormap, colorspec, &xcolor);
+#else
+ gdk_color_parse (colorspec, &xcolor);
+#endif
+ cmap[j++] = xcolor.red >> 8;
+ cmap[j++] = xcolor.green >> 8;
+ cmap[j++] = xcolor.blue >> 8;
+ cmap[j++] = ~0;
+ }
+ else
+ {
+ j += 4;
+ }
+ }
+#ifndef XPM_NO_X
+ XCloseDisplay (display);
+#endif
+ return cmap;
+}
+
+static void
+parse_image (gint32 image_ID,
+ XpmImage *xpm_image,
+ guchar *cmap)
+{
+ GeglBuffer *buffer;
+ gint tile_height;
+ gint scanlines;
+ gint val;
+ guchar *buf;
+ guchar *dest;
+ guint *src;
+ gint32 layer_ID;
+ gint i;
+
+ layer_ID = gimp_layer_new (image_ID,
+ _("Color"),
+ xpm_image->width,
+ xpm_image->height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ tile_height = gimp_tile_height ();
+
+ buf = g_new (guchar, tile_height * xpm_image->width * 4);
+
+ src = xpm_image->data;
+ for (i = 0; i < xpm_image->height; i += tile_height)
+ {
+ gint j;
+
+ dest = buf;
+ scanlines = MIN (tile_height, xpm_image->height - i);
+ j = scanlines * xpm_image->width;
+ while (j--)
+ {
+ {
+ val = *(src++) * 4;
+ *(dest) = cmap[val];
+ *(dest+1) = cmap[val+1];
+ *(dest+2) = cmap[val+2];
+ *(dest+3) = cmap[val+3];
+ dest += 4;
+ }
+
+ if ((j % 100) == 0)
+ gimp_progress_update ((double) i / (double) xpm_image->height);
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, i, xpm_image->width, scanlines), 0,
+ NULL, buf, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (buf);
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+}
+
+static guint
+rgbhash (rgbkey *c)
+{
+ return ((guint)c->r) ^ ((guint)c->g) ^ ((guint)c->b);
+}
+
+static guint
+compare (rgbkey *c1,
+ rgbkey *c2)
+{
+ return (c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b);
+}
+
+static void
+set_XpmImage (XpmColor *array,
+ guint index,
+ gchar *colorstring)
+{
+ gchar *p;
+ gint i, charnum, indtemp;
+
+ indtemp=index;
+ array[index].string = p = g_new (gchar, cpp+1);
+
+ /*convert the index number to base sizeof(linenoise)-1 */
+ for (i=0; i<cpp; ++i)
+ {
+ charnum = indtemp % (sizeof (linenoise) - 1);
+ indtemp = indtemp / (sizeof (linenoise) - 1);
+ *p++ = linenoise[charnum];
+ }
+ /* *p++=linenoise[indtemp]; */
+
+ *p = '\0'; /* C and its stupid null-terminated strings... */
+
+ array[index].symbolic = NULL;
+ array[index].m_color = NULL;
+ array[index].g4_color = NULL;
+
+ if (color)
+ {
+ array[index].g_color = NULL;
+ array[index].c_color = colorstring;
+ }
+ else
+ {
+ array[index].c_color = NULL;
+ array[index].g_color = colorstring;
+ }
+}
+
+static void
+create_colormap_from_hash (gpointer gkey,
+ gpointer value,
+ gpointer user_data)
+{
+ rgbkey *key = gkey;
+ gchar *string = g_new (gchar, 8);
+
+ sprintf (string, "#%02X%02X%02X", (int)key->r, (int)key->g, (int)key->b);
+ set_XpmImage (user_data, *((int *) value), string);
+}
+
+static void
+decrement_hash_values (gpointer gkey,
+ gpointer value,
+ gpointer user_data)
+{
+ --(*((guint*) value));
+}
+
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+ gint ncolors = 1;
+ gint *indexno;
+ gboolean indexed;
+ gboolean alpha;
+ gboolean alpha_used = FALSE;
+ XpmColor *colormap;
+ XpmImage *image;
+ guint *ibuff = NULL;
+ guchar *buf;
+ guchar *data;
+ GHashTable *hash = NULL;
+ gint i, j, k;
+ gint threshold = xpmvals.threshold;
+ gboolean success = FALSE;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ alpha = gimp_drawable_has_alpha (drawable_ID);
+ color = !gimp_drawable_is_gray (drawable_ID);
+ indexed = gimp_drawable_is_indexed (drawable_ID);
+
+ switch (gimp_drawable_type (drawable_ID))
+ {
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = gegl_buffer_get_format (buffer);
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported drawable type"));
+ g_object_unref (buffer);
+ return FALSE;
+ }
+
+ /* allocate buffer making the assumption that ibuff is 32 bit aligned... */
+ ibuff = g_new (guint, width * height);
+
+ hash = g_hash_table_new ((GHashFunc) rgbhash, (GCompareFunc) compare);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ ncolors = alpha ? 1 : 0;
+
+ /* allocate a pixel region to work with */
+ buf = g_new (guchar,
+ gimp_tile_height () * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ /* process each row of tiles */
+ for (i = 0; i < height; i += gimp_tile_height ())
+ {
+ gint scanlines;
+
+ /* read the next row of tiles */
+ scanlines = MIN (gimp_tile_height(), height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scanlines), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ data = buf;
+
+ /* process each pixel row */
+ for (j = 0; j < scanlines; j++)
+ {
+ /* go to the start of this row in each image */
+ guint *idata = ibuff + (i+j) * width;
+
+ /* do each pixel in the row */
+ for (k = 0; k < width; k++)
+ {
+ rgbkey *key = g_new (rgbkey, 1);
+ guchar a;
+
+ /* get pixel data */
+ key->r = *(data++);
+ key->g = color && !indexed ? *(data++) : key->r;
+ key->b = color && !indexed ? *(data++) : key->r;
+ a = alpha ? *(data++) : 255;
+
+ if (a < threshold)
+ {
+ *(idata++) = 0;
+ alpha_used = TRUE;
+ }
+ else
+ {
+ if (indexed)
+ {
+ *(idata++) = (key->r) + (alpha ? 1 : 0);
+ }
+ else
+ {
+ indexno = g_hash_table_lookup (hash, key);
+ if (!indexno)
+ {
+ indexno = g_new (gint, 1);
+ *indexno = ncolors++;
+ g_hash_table_insert (hash, key, indexno);
+ key = g_new (rgbkey, 1);
+ }
+ *(idata++) = *indexno;
+ }
+ }
+ }
+
+ /* kick the progress bar */
+ gimp_progress_update ((gdouble) (i+j) / (gdouble) height);
+ }
+ }
+
+ g_free (buf);
+
+ /* remove alpha if not actually used */
+ if (alpha && !alpha_used)
+ {
+ gint i;
+ --ncolors;
+ for (i = 0; i < width * height; ++i)
+ --ibuff[i];
+
+ g_hash_table_foreach (hash, decrement_hash_values, NULL);
+ }
+
+ if (indexed)
+ {
+ guchar *cmap = gimp_image_get_colormap (image_ID, &ncolors);
+ guchar *c;
+
+ c = cmap;
+
+ if (alpha_used)
+ ncolors++;
+
+ colormap = g_new (XpmColor, ncolors);
+ cpp =
+ 1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
+
+ if (alpha_used)
+ set_XpmImage (colormap, 0, "None");
+
+ for (i = alpha_used ? 1 : 0; i < ncolors; i++)
+ {
+ gchar *string;
+ guchar r, g, b;
+
+ r = *c++;
+ g = *c++;
+ b = *c++;
+
+ string = g_new (gchar, 8);
+ sprintf (string, "#%02X%02X%02X", (int)r, (int)g, (int)b);
+ set_XpmImage (colormap, i, string);
+ }
+
+ g_free (cmap);
+ }
+ else
+ {
+ colormap = g_new (XpmColor, ncolors);
+ cpp =
+ 1 + (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
+
+ if (alpha_used)
+ set_XpmImage (colormap, 0, "None");
+
+ g_hash_table_foreach (hash, create_colormap_from_hash, colormap);
+ }
+
+ image = g_new (XpmImage, 1);
+
+ image->width = width;
+ image->height = height;
+ image->ncolors = ncolors;
+ image->cpp = cpp;
+ image->colorTable = colormap;
+ image->data = ibuff;
+
+ /* do the save */
+ switch (XpmWriteFileFromXpmImage ((char *) filename, image, NULL))
+ {
+ case XpmSuccess:
+ success = TRUE;
+ break;
+
+ case XpmOpenFailed:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error opening file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ break;
+
+ case XpmFileInvalid:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("XPM file invalid"));
+ break;
+
+ default:
+ break;
+ }
+
+ g_object_unref (buffer);
+ g_free (ibuff);
+
+ if (hash)
+ g_hash_table_destroy (hash);
+
+ gimp_progress_update (1.0);
+
+ return success;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkObject *scale_data;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("XPM"), PLUG_IN_BINARY, SAVE_PROC);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Alpha threshold:"), SCALE_WIDTH, 0,
+ xpmvals.threshold, 0, 255, 1, 8, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &xpmvals.threshold);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/file-xwd.c b/plug-ins/common/file-xwd.c
new file mode 100644
index 0000000..20943c9
--- /dev/null
+++ b/plug-ins/common/file-xwd.c
@@ -0,0 +1,2587 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * XWD reading and writing code Copyright (C) 1996 Peter Kirchgessner
+ * (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * XWD-input/output was written by Peter Kirchgessner (peter@kirchgessner.net)
+ * Examples from mainly used UNIX-systems have been used for testing.
+ * If a file does not work, please return a small (!!) compressed example.
+ * Currently the following formats are supported:
+ * pixmap_format | pixmap_depth | bits_per_pixel
+ * ---------------------------------------------
+ * 0 | 1 | 1
+ * 1 | 1,...,24 | 1
+ * 2 | 1 | 1
+ * 2 | 1,...,8 | 8
+ * 2 | 1,...,16 | 16
+ * 2 | 1,...,24 | 24
+ * 2 | 1,...,32 | 32
+ */
+/* Event history:
+ * PK = Peter Kirchgessner, ME = Mattias Engdegård
+ * V 1.00, PK, xx-Aug-96: First try
+ * V 1.01, PK, 03-Sep-96: Check for bitmap_bit_order
+ * V 1.90, PK, 17-Mar-97: Upgrade to work with GIMP V0.99
+ * Use visual class 3 to write indexed image
+ * Set gimp b/w-colormap if no xwdcolormap present
+ * V 1.91, PK, 05-Apr-97: Return all arguments, even in case of an error
+ * V 1.92, PK, 12-Oct-97: No progress bars for non-interactive mode
+ * V 1.93, PK, 11-Apr-98: Fix problem with overwriting memory
+ * V 1.94, ME, 27-Feb-00: Remove superfluous little-endian support (format is
+ specified as big-endian). Trim magic header
+ * V 1.95, PK, 02-Jul-01: Fix problem with 8 bit image
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-xwd-load"
+#define SAVE_PROC "file-xwd-save"
+#define PLUG_IN_BINARY "file-xwd"
+#define PLUG_IN_ROLE "gimp-file-xwd"
+
+
+typedef gulong L_CARD32;
+typedef gushort L_CARD16;
+typedef guchar L_CARD8;
+
+typedef struct
+{
+ L_CARD32 l_header_size; /* Header size */
+
+ L_CARD32 l_file_version; /* File version (7) */
+ L_CARD32 l_pixmap_format; /* Image type */
+ L_CARD32 l_pixmap_depth; /* Number of planes */
+ L_CARD32 l_pixmap_width; /* Image width */
+ L_CARD32 l_pixmap_height; /* Image height */
+ L_CARD32 l_xoffset; /* x-offset (0 ?) */
+ L_CARD32 l_byte_order; /* Byte ordering */
+
+ L_CARD32 l_bitmap_unit;
+ L_CARD32 l_bitmap_bit_order; /* Bit order */
+ L_CARD32 l_bitmap_pad;
+ L_CARD32 l_bits_per_pixel; /* Number of bits per pixel */
+
+ L_CARD32 l_bytes_per_line; /* Number of bytes per scanline */
+ L_CARD32 l_visual_class; /* Visual class */
+ L_CARD32 l_red_mask; /* Red mask */
+ L_CARD32 l_green_mask; /* Green mask */
+ L_CARD32 l_blue_mask; /* Blue mask */
+ L_CARD32 l_bits_per_rgb; /* Number of bits per RGB-part */
+ L_CARD32 l_colormap_entries; /* Number of colors in color table (?) */
+ L_CARD32 l_ncolors; /* Number of xwdcolor structures */
+ L_CARD32 l_window_width; /* Window width */
+ L_CARD32 l_window_height; /* Window height */
+ L_CARD32 l_window_x; /* Window position x */
+ L_CARD32 l_window_y; /* Window position y */
+ L_CARD32 l_window_bdrwidth;/* Window border width */
+} L_XWDFILEHEADER;
+
+typedef struct
+{
+ L_CARD32 l_pixel; /* Color index */
+ L_CARD16 l_red, l_green, l_blue; /* RGB-values */
+ L_CARD8 l_flags, l_pad;
+} L_XWDCOLOR;
+
+
+/* Some structures for mapping up to 32bit-pixel */
+/* values which are kept in the XWD-Colormap */
+
+#define MAPPERBITS 12
+#define MAPPERMASK ((1 << MAPPERBITS)-1)
+
+typedef struct
+{
+ L_CARD32 pixel_val;
+ guchar red;
+ guchar green;
+ guchar blue;
+} PMAP;
+
+typedef struct
+{
+ gint npixel; /* Number of pixel values in map */
+ guchar pixel_in_map[1 << MAPPERBITS];
+ PMAP pmap[256];
+} PIXEL_MAP;
+
+#define XWDHDR_PAD 0 /* Total number of padding bytes for XWD header */
+#define XWDCOL_PAD 0 /* Total number of padding bytes for each XWD color */
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gboolean save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpImageType gdtype,
+ gint32 *layer_ID,
+ GeglBuffer **buffer);
+
+static int set_pixelmap (gint ncols,
+ L_XWDCOLOR *xwdcol,
+ PIXEL_MAP *pixelmap);
+static gboolean get_pixelmap (L_CARD32 pixelval,
+ PIXEL_MAP *pixelmap,
+ guchar *red,
+ guchar *green,
+ guchar *glue);
+
+static void set_bw_color_table (gint32 image_ID);
+static void set_color_table (gint32 image_ID,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+
+static gint32 load_xwd_f2_d1_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d8_b8 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d16_b16 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f2_d24_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error);
+static gint32 load_xwd_f2_d32_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+static gint32 load_xwd_f1_d24_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error);
+
+static L_CARD32 read_card32 (FILE *ifp,
+ gint *err);
+static L_CARD16 read_card16 (FILE *ifp,
+ gint *err);
+static L_CARD8 read_card8 (FILE *ifp,
+ gint *err);
+
+static gboolean write_card32 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+static gboolean write_card16 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+static gboolean write_card8 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error);
+
+static void read_xwd_header (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr);
+
+static gboolean write_xwd_header (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ GError **error);
+
+static void read_xwd_cols (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap);
+
+static gboolean write_xwd_cols (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap,
+ GError **error);
+
+static gint save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean gray,
+ GError **error);
+static gint save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the XWD (X Window Dump) format",
+ "Loads files in the XWD (X Window Dump) format. "
+ "XWD image files are produced by the program xwd. "
+ "Xwd is an X Window System window dumping utility.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("X window dump"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-xwindowdump");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "xwd",
+ "",
+ "4,long,0x00000007");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in the XWD (X Window Dump) format",
+ "XWD exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner",
+ "1996",
+ N_("X window dump"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-xwindowdump");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "xwd", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "XWD",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* No additional data to retrieve */
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GFile *file = g_file_new_for_uri (param[3].data.d_string);
+
+ if (! save_image (file, image_ID, drawable_ID, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_object_unref (file);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *ifp = NULL;
+ gint depth, bpp;
+ gint32 image_ID = -1;
+ L_XWDFILEHEADER xwdhdr;
+ L_XWDCOLOR *xwdcolmap = NULL;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ ifp = g_fopen (filename, "rb");
+ if (!ifp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ goto out;
+ }
+
+ read_xwd_header (ifp, &xwdhdr);
+ if (xwdhdr.l_file_version != 7)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not read XWD header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+#ifdef XWD_COL_WAIT_DEBUG
+ {
+ int k = 1;
+
+ while (k)
+ k = k;
+ }
+#endif
+
+ /* Position to start of XWDColor structures */
+ fseek (ifp, (long)xwdhdr.l_header_size, SEEK_SET);
+
+ /* Guard against insanely huge color maps -- gimp_image_set_colormap() only
+ * accepts colormaps with 0..256 colors anyway. */
+ if (xwdhdr.l_colormap_entries > 256)
+ {
+ g_message (_("'%s':\nIllegal number of colormap entries: %ld"),
+ gimp_filename_to_utf8 (filename),
+ (long)xwdhdr.l_colormap_entries);
+ goto out;
+ }
+
+ if (xwdhdr.l_colormap_entries > 0)
+ {
+ if (xwdhdr.l_colormap_entries < xwdhdr.l_ncolors)
+ {
+ g_message (_("'%s':\nNumber of colormap entries < number of colors"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ xwdcolmap = g_new (L_XWDCOLOR, xwdhdr.l_colormap_entries);
+
+ read_xwd_cols (ifp, &xwdhdr, xwdcolmap);
+
+#ifdef XWD_COL_DEBUG
+ {
+ int j;
+ g_printf ("File %s\n",filename);
+ for (j = 0; j < xwdhdr.l_colormap_entries; j++)
+ g_printf ("Entry 0x%08lx: 0x%04lx, 0x%04lx, 0x%04lx, %d\n",
+ (long)xwdcolmap[j].l_pixel,(long)xwdcolmap[j].l_red,
+ (long)xwdcolmap[j].l_green,(long)xwdcolmap[j].l_blue,
+ (int)xwdcolmap[j].l_flags);
+ }
+#endif
+
+ if (xwdhdr.l_file_version != 7)
+ {
+ g_message (_("Can't read color entries"));
+ goto out;
+ }
+ }
+
+ if (xwdhdr.l_pixmap_width <= 0)
+ {
+ g_message (_("'%s':\nNo image width specified"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_width > GIMP_MAX_IMAGE_SIZE
+ || xwdhdr.l_bytes_per_line > GIMP_MAX_IMAGE_SIZE * 3)
+ {
+ g_message (_("'%s':\nImage width is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_height <= 0)
+ {
+ g_message (_("'%s':\nNo image height specified"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (xwdhdr.l_pixmap_height > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_message (_("'%s':\nImage height is larger than GIMP can handle"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ depth = xwdhdr.l_pixmap_depth;
+ bpp = xwdhdr.l_bits_per_pixel;
+
+ image_ID = -1;
+ switch (xwdhdr.l_pixmap_format)
+ {
+ case 0: /* Single plane bitmap */
+ if ((depth == 1) && (bpp == 1))
+ { /* Can be performed by format 2 loader */
+ image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ break;
+
+ case 1: /* Single plane pixmap */
+ if ((depth <= 24) && (bpp == 1))
+ {
+ image_ID = load_xwd_f1_d24_b1 (filename, ifp, &xwdhdr, xwdcolmap,
+ error);
+ }
+ break;
+
+ case 2: /* Multiplane pixmaps */
+ if ((depth == 1) && (bpp == 1))
+ {
+ image_ID = load_xwd_f2_d1_b1 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 8) && (bpp == 8))
+ {
+ image_ID = load_xwd_f2_d8_b8 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 16) && (bpp == 16))
+ {
+ image_ID = load_xwd_f2_d16_b16 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ else if ((depth <= 24) && ((bpp == 24) || (bpp == 32)))
+ {
+ image_ID = load_xwd_f2_d24_b32 (filename, ifp, &xwdhdr, xwdcolmap,
+ error);
+ }
+ else if ((depth <= 32) && (bpp == 32))
+ {
+ image_ID = load_xwd_f2_d32_b32 (filename, ifp, &xwdhdr, xwdcolmap);
+ }
+ break;
+ }
+ gimp_progress_update (1.0);
+
+ if (image_ID == -1 && ! (error && *error))
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s has format %d, depth %d and bits per pixel %d. "
+ "Currently this is not supported."),
+ gimp_filename_to_utf8 (filename),
+ (gint) xwdhdr.l_pixmap_format, depth, bpp);
+
+out:
+ if (ifp)
+ {
+ fclose (ifp);
+ }
+
+ if (xwdcolmap)
+ {
+ g_free (xwdcolmap);
+ }
+
+ return image_ID;
+}
+
+static gboolean
+save_image (GFile *file,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ GOutputStream *output;
+ GimpImageType drawable_type;
+ gboolean success;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Cannot export images with alpha channels."));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_GRAY_IMAGE:
+ case GIMP_RGB_IMAGE:
+ break;
+ default:
+ g_message (_("Cannot operate on unknown image types."));
+ return FALSE;
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, 0, NULL, error));
+ if (! output)
+ {
+ g_prefix_error (error,
+ _("Could not open '%s' for writing: "),
+ gimp_file_get_utf8_name (file));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ success = save_index (output, image_ID, drawable_ID, FALSE, error);
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ success = save_index (output, image_ID, drawable_ID, TRUE, error);
+ break;
+
+ case GIMP_RGB_IMAGE:
+ success = save_rgb (output, image_ID, drawable_ID, error);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success && ! g_output_stream_close (output, NULL, error))
+ {
+ g_prefix_error (error,
+ _("Error exporting '%s': "),
+ gimp_file_get_utf8_name (file));
+ success = FALSE;
+ }
+ else if (! success)
+ {
+ GCancellable *cancellable;
+
+ cancellable = g_cancellable_new ();
+ g_cancellable_cancel (cancellable);
+ g_output_stream_close (output, cancellable, NULL);
+ g_object_unref (cancellable);
+ }
+
+ g_object_unref (output);
+
+ gimp_progress_update (1.0);
+
+ return success;
+}
+
+
+static L_CARD32
+read_card32 (FILE *ifp,
+ int *err)
+
+{
+ L_CARD32 c;
+
+ c = (((L_CARD32) (getc (ifp))) << 24);
+ c |= (((L_CARD32) (getc (ifp))) << 16);
+ c |= (((L_CARD32) (getc (ifp))) << 8);
+ c |= ((L_CARD32) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+static L_CARD16
+read_card16 (FILE *ifp,
+ int *err)
+
+{
+ L_CARD16 c;
+
+ c = (((L_CARD16) (getc (ifp))) << 8);
+ c |= ((L_CARD16) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+static L_CARD8
+read_card8 (FILE *ifp,
+ int *err)
+{
+ L_CARD8 c;
+
+ c = ((L_CARD8) (*err = getc (ifp)));
+
+ *err = (*err < 0);
+
+ return c;
+}
+
+
+static gboolean
+write_card32 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[4];
+
+ buffer[0] = (c >> 24) & 0xff;
+ buffer[1] = (c >> 16) & 0xff;
+ buffer[2] = (c >> 8) & 0xff;
+ buffer[3] = (c) & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 4,
+ NULL, NULL, error);
+}
+
+static gboolean
+write_card16 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[2];
+
+ buffer[0] = (c >> 8) & 0xff;
+ buffer[1] = (c) & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 2,
+ NULL, NULL, error);
+}
+
+static gboolean
+write_card8 (GOutputStream *output,
+ L_CARD32 c,
+ GError **error)
+{
+ guchar buffer[1];
+
+ buffer[0] = c & 0xff;
+
+ return g_output_stream_write_all (output, buffer, 1,
+ NULL, NULL, error);
+}
+
+
+static void
+read_xwd_header (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr)
+{
+ gint j, err;
+ L_CARD32 *cp;
+
+ cp = (L_CARD32 *) xwdhdr;
+
+ /* Read in all 32-bit values of the header and check for byte order */
+ for (j = 0; j < sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version); j++)
+ {
+ *(cp++) = read_card32 (ifp, &err);
+
+ if (err)
+ break;
+ }
+
+ if (err)
+ xwdhdr->l_file_version = 0; /* Not a valid XWD-file */
+}
+
+
+/* Write out an XWD-fileheader. The header size is calculated here */
+static gboolean
+write_xwd_header (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ GError **error)
+
+{
+ gint j, hdrpad, hdr_entries;
+ L_CARD32 *cp;
+
+ hdrpad = XWDHDR_PAD;
+ hdr_entries = sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version);
+ xwdhdr->l_header_size = hdr_entries * 4 + hdrpad;
+
+ cp = (L_CARD32 *) xwdhdr;
+
+ /* Write out all 32-bit values of the header and check for byte order */
+ for (j = 0; j < sizeof (L_XWDFILEHEADER) / sizeof(xwdhdr->l_file_version); j++)
+ {
+ if (! write_card32 (output, *(cp++), error))
+ return FALSE;
+ }
+
+ /* Add padding bytes after XWD header */
+ for (j = 0; j < hdrpad; j++)
+ if (! write_card8 (output, (L_CARD32) 0, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+read_xwd_cols (FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap)
+{
+ gint j, err = 0;
+ gint flag_is_bad, index_is_bad;
+ gint indexed = (xwdhdr->l_pixmap_depth <= 8);
+ glong colmappos = ftell (ifp);
+
+ /* Read in XWD-Color structures (the usual case) */
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (with 4 bytes inserted infront of RGB) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card32 (ifp, &err); /* Empty bytes on Alpha OSF */
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (with 2 bytes inserted infront of RGB) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card16 (ifp, &err); /* Empty bytes (from where ?) */
+
+ colormap[j].l_red = read_card16 (ifp, &err);
+ colormap[j].l_green = read_card16 (ifp, &err);
+ colormap[j].l_blue = read_card16 (ifp, &err);
+
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+
+ /* if ((colormap[j].l_flags == 0) || (colormap[j].l_flags > 7))
+ flag_is_bad++; */
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (err) /* Not a valid XWD-file ? */
+ xwdhdr->l_file_version = 0;
+ if (err || ((flag_is_bad == 0) && (index_is_bad == 0)))
+ return;
+
+ /* Read in XWD-Color structures (every value is 8 bytes from a CRAY) */
+ fseek (ifp, colmappos, SEEK_SET);
+ flag_is_bad = index_is_bad = 0;
+ for (j = 0; j < xwdhdr->l_ncolors; j++)
+ {
+ read_card32 (ifp, &err);
+ colormap[j].l_pixel = read_card32 (ifp, &err);
+
+ read_card32 (ifp, &err);
+ colormap[j].l_red = read_card32 (ifp, &err);
+ read_card32 (ifp, &err);
+ colormap[j].l_green = read_card32 (ifp, &err);
+ read_card32 (ifp, &err);
+ colormap[j].l_blue = read_card32 (ifp, &err);
+
+ /* The flag byte is kept in the first byte */
+ colormap[j].l_flags = read_card8 (ifp, &err);
+ colormap[j].l_pad = read_card8 (ifp, &err);
+ read_card16 (ifp, &err);
+ read_card32 (ifp, &err);
+
+ if (indexed && (colormap[j].l_pixel > 255))
+ index_is_bad++;
+
+ if (err)
+ break;
+ }
+
+ if (flag_is_bad || index_is_bad)
+ {
+ g_printf ("xwd: Warning. Error in XWD-color-structure (");
+
+ if (flag_is_bad) g_printf ("flag");
+ if (index_is_bad) g_printf ("index");
+
+ g_printf (")\n");
+ }
+
+ if (err)
+ xwdhdr->l_file_version = 0; /* Not a valid XWD-file */
+}
+
+static gboolean
+write_xwd_cols (GOutputStream *output,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *colormap,
+ GError **error)
+{
+ gint j;
+
+ for (j = 0; j < xwdhdr->l_colormap_entries; j++)
+ {
+#ifdef CRAY
+ if (! (write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error) &&
+ write_card16 (output, (L_CARD32)0, error) &&
+ write_card32 (output, (L_CARD32)0, error)))
+ {
+ return FALSE;
+ }
+#else
+#ifdef __alpha
+ if (! (write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card32 (output, (L_CARD32)0, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error)))
+ {
+ return FALSE;
+ }
+#else
+ if (! (write_card32 (output, colormap[j].l_pixel, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_red, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_green, error) &&
+ write_card16 (output, (L_CARD32)colormap[j].l_blue, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_flags, error) &&
+ write_card8 (output, (L_CARD32)colormap[j].l_pad, error)))
+ {
+ return FALSE;
+ }
+#endif
+#endif
+ }
+
+ return TRUE;
+}
+
+
+/* Create a map for mapping up to 32 bit pixelvalues to RGB.
+ * Returns number of colors kept in the map (up to 256)
+ */
+static gint
+set_pixelmap (int ncols,
+ L_XWDCOLOR *xwdcol,
+ PIXEL_MAP *pixelmap)
+
+{
+ gint i, j, k, maxcols;
+ L_CARD32 pixel_val;
+
+ memset ((gchar *) pixelmap, 0, sizeof (PIXEL_MAP));
+
+ maxcols = 0;
+
+ for (j = 0; j < ncols; j++) /* For each entry of the XWD colormap */
+ {
+ pixel_val = xwdcol[j].l_pixel;
+ for (k = 0; k < maxcols; k++) /* Where to insert in list ? */
+ {
+ if (pixel_val <= pixelmap->pmap[k].pixel_val)
+ break;
+ }
+ if ((k < maxcols) && (pixel_val == pixelmap->pmap[k].pixel_val))
+ break; /* It was already in list */
+
+ if (k >= 256)
+ break;
+
+ if (k < maxcols) /* Must move entries to the back ? */
+ {
+ for (i = maxcols-1; i >= k; i--)
+ memcpy ((char *)&(pixelmap->pmap[i+1]),(char *)&(pixelmap->pmap[i]),
+ sizeof (PMAP));
+ }
+ pixelmap->pmap[k].pixel_val = pixel_val;
+ pixelmap->pmap[k].red = xwdcol[j].l_red >> 8;
+ pixelmap->pmap[k].green = xwdcol[j].l_green >> 8;
+ pixelmap->pmap[k].blue = xwdcol[j].l_blue >> 8;
+ pixelmap->pixel_in_map[pixel_val & MAPPERMASK] = 1;
+ maxcols++;
+ }
+ pixelmap->npixel = maxcols;
+#ifdef XWD_COL_DEBUG
+ g_printf ("Colors in pixelmap: %d\n",pixelmap->npixel);
+ for (j=0; j<pixelmap->npixel; j++)
+ g_printf ("Pixelvalue 0x%08lx, 0x%02x 0x%02x 0x%02x\n",
+ pixelmap->pmap[j].pixel_val,
+ pixelmap->pmap[j].red,pixelmap->pmap[j].green,
+ pixelmap->pmap[j].blue);
+ for (j=0; j<=MAPPERMASK; j++)
+ g_printf ("0x%08lx: %d\n",(long)j,pixelmap->pixel_in_map[j]);
+#endif
+
+ return pixelmap->npixel;
+}
+
+
+/* Search a pixel value in the pixel map. Returns FALSE if the
+ * pixelval was not found in map. Returns TRUE if found.
+ */
+static gboolean
+get_pixelmap (L_CARD32 pixelval,
+ PIXEL_MAP *pixelmap,
+ guchar *red,
+ guchar *green,
+ guchar *blue)
+
+{
+ register PMAP *low, *high, *middle;
+
+ if (pixelmap->npixel == 0)
+ return FALSE;
+
+ if (!(pixelmap->pixel_in_map[pixelval & MAPPERMASK]))
+ return FALSE;
+
+ low = &(pixelmap->pmap[0]);
+ high = &(pixelmap->pmap[pixelmap->npixel-1]);
+
+ /* Do a binary search on the array */
+ while (low < high)
+ {
+ middle = low + ((high - low)/2);
+ if (pixelval <= middle->pixel_val)
+ high = middle;
+ else
+ low = middle+1;
+ }
+
+ if (pixelval == low->pixel_val)
+ {
+ *red = low->red; *green = low->green; *blue = low->blue;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+set_bw_color_table (gint32 image_ID)
+{
+ static guchar BWColorMap[2*3] = { 255, 255, 255, 0, 0, 0 };
+
+#ifdef XWD_COL_DEBUG
+ g_printf ("Set GIMP b/w-colortable:\n");
+#endif
+
+ gimp_image_set_colormap (image_ID, BWColorMap, 2);
+}
+
+
+/* Initialize an 8-bit colortable from the mask-values of the XWD-header */
+static void
+init_color_table256 (L_XWDFILEHEADER *xwdhdr,
+ guchar *ColorMap)
+
+{
+ gint i, j, k, cuind;
+ gint redshift, greenshift, blueshift;
+ gint maxred, maxgreen, maxblue;
+
+ /* Assume: the bit masks for red/green/blue are grouped together
+ * Example: redmask = 0xe0, greenmask = 0x1c, bluemask = 0x03
+ * We need to know where to place the RGB-values (shifting)
+ * and the maximum value for each component.
+ */
+ redshift = greenshift = blueshift = 0;
+ if ((maxred = xwdhdr->l_red_mask) == 0)
+ return;
+
+ /* Shift the redmask to the rightmost bit position to get
+ * maximum value for red.
+ */
+ while ((maxred & 1) == 0)
+ {
+ redshift++;
+ maxred >>= 1;
+ }
+
+ if ((maxgreen = xwdhdr->l_green_mask) == 0)
+ return;
+
+ while ((maxgreen & 1) == 0)
+ {
+ greenshift++;
+ maxgreen >>= 1;
+ }
+
+ if ((maxblue = xwdhdr->l_blue_mask) == 0)
+ return;
+
+ while ((maxblue & 1) == 0)
+ {
+ blueshift++;
+ maxblue >>= 1;
+ }
+
+ memset ((gchar *) ColorMap, 0, 256 * 3);
+
+ for (i = 0; i <= maxred; i++)
+ for (j = 0; j <= maxgreen; j++)
+ for (k = 0; k <= maxblue; k++)
+ {
+ cuind = (i << redshift) | (j << greenshift) | (k << blueshift);
+
+ if (cuind < 256)
+ {
+ ColorMap[cuind*3] = (i * 255)/maxred;
+ ColorMap[cuind*3+1] = (j * 255)/maxgreen;
+ ColorMap[cuind*3+2] = (k * 255)/maxblue;
+ }
+ }
+}
+
+
+static void
+set_color_table (gint32 image_ID,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+
+{
+ gint ncols, i, j;
+ guchar ColorMap[256 * 3];
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+ if (ncols <= 0)
+ return;
+ if (ncols > 256)
+ ncols = 256;
+
+ /* Initialize color table for all 256 entries from mask-values */
+ init_color_table256 (xwdhdr, ColorMap);
+
+ for (j = 0; j < ncols; j++)
+ {
+ i = xwdcolmap[j].l_pixel;
+ if ((i >= 0) && (i < 256))
+ {
+ ColorMap[i*3] = (xwdcolmap[j].l_red) >> 8;
+ ColorMap[i*3+1] = (xwdcolmap[j].l_green) >> 8;
+ ColorMap[i*3+2] = (xwdcolmap[j].l_blue) >> 8;
+ }
+ }
+
+#ifdef XWD_COL_DEBUG
+ g_printf ("Set GIMP colortable:\n");
+ for (j = 0; j < 256; j++)
+ g_printf ("%3d: 0x%02x 0x%02x 0x%02x\n", j,
+ ColorMap[j*3], ColorMap[j*3+1], ColorMap[j*3+2]);
+#endif
+
+ gimp_image_set_colormap (image_ID, ColorMap, 256);
+}
+
+
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageBaseType type,
+ GimpImageType gdtype,
+ gint32 *layer_ID,
+ GeglBuffer **buffer)
+{
+ gint32 image_ID;
+
+ image_ID = gimp_image_new (width, height, type);
+ gimp_image_set_filename (image_ID, filename);
+
+ *layer_ID = gimp_layer_new (image_ID, "Background", width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ *buffer = gimp_drawable_get_buffer (*layer_ID);
+
+ return image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth 1, bits_per_pixel 1 */
+
+static gint32
+load_xwd_f2_d1_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register int pix8;
+ register guchar *dest, *src;
+ guchar c1, c2, c3, c4;
+ gint width, height, scan_lines, tile_height;
+ gint i, j, ncols;
+ gchar *temp;
+ guchar bit2byte[256 * 8];
+ guchar *data, *scanline;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d1_b1 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_INDEXED,
+ GIMP_INDEXED_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ scanline = g_new (guchar, xwdhdr->l_bytes_per_line + 8);
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+
+ temp = (gchar *) bit2byte;
+
+ /* Get an array for mapping 8 bits in a byte to 8 bytes */
+ if (!xwdhdr->l_bitmap_bit_order)
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 0; i < 8; i++)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+ else
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ *(temp++) = ((j & (1 << i)) != 0);
+ }
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if (fread (scanline, xwdhdr->l_bytes_per_line, 1, ifp) != 1)
+ {
+ err = 1;
+ break;
+ }
+
+ /* Need to check byte order ? */
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ src = scanline;
+ switch (xwdhdr->l_bitmap_unit)
+ {
+ case 16:
+ j = xwdhdr->l_bytes_per_line;
+ while (j > 0)
+ {
+ c1 = src[0]; c2 = src[1];
+ *(src++) = c2; *(src++) = c1;
+ j -= 2;
+ }
+ break;
+
+ case 32:
+ j = xwdhdr->l_bytes_per_line;
+ while (j > 0)
+ {
+ c1 = src[0]; c2 = src[1]; c3 = src[2]; c4 = src[3];
+ *(src++) = c4; *(src++) = c3; *(src++) = c2; *(src++) = c1;
+ j -= 4;
+ }
+ break;
+ }
+ }
+ src = scanline;
+ j = width;
+ while (j >= 8)
+ {
+ pix8 = *(src++);
+ memcpy (dest, bit2byte + pix8*8, 8);
+ dest += 8;
+ j -= 8;
+ }
+ if (j > 0)
+ {
+ pix8 = *(src++);
+ memcpy (dest, bit2byte + pix8*8, j);
+ dest += j;
+ }
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ if (err) break;
+ }
+
+ g_free (data);
+ g_free (scanline);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth 8, bits_per_pixel 8 */
+
+static gint32
+load_xwd_f2_d8_b8 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ gint width, height, linepad, tile_height, scan_lines;
+ gint i, j, ncols;
+ gint grayscale;
+ guchar *dest, *data;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d8_b8 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ /* This could also be a grayscale image. Check it */
+ grayscale = 0;
+ if ((xwdhdr->l_ncolors == 256) && (xwdhdr->l_colormap_entries == 256))
+ {
+ for (j = 0; j < 256; j++)
+ {
+ if ((xwdcolmap[j].l_pixel != j)
+ || ((xwdcolmap[j].l_red >> 8) != j)
+ || ((xwdcolmap[j].l_green >> 8) != j)
+ || ((xwdcolmap[j].l_blue >> 8) != j))
+ break;
+ }
+
+ grayscale = (j == 256);
+ }
+
+ image_ID = create_new_image (filename, width, height,
+ grayscale ? GIMP_GRAY : GIMP_INDEXED,
+ grayscale ? GIMP_GRAY_IMAGE : GIMP_INDEXED_IMAGE,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width);
+
+ if (!grayscale)
+ {
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols) ncols = xwdhdr->l_ncolors;
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+ }
+
+ linepad = xwdhdr->l_bytes_per_line - xwdhdr->l_pixmap_width;
+ if (linepad < 0)
+ linepad = 0;
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if (fread (dest, 1, width, ifp) != width)
+ {
+ err = 1;
+ break;
+ }
+ dest += width;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 16, bits_per_pixel 16 */
+
+static gint32
+load_xwd_f2_d16_b16 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, ncols;
+ gint red, green, blue, redval, greenval, blueval;
+ gint maxred, maxgreen, maxblue;
+ gint tile_height, scan_lines;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ gulong maxval;
+ guchar *ColorMap, *cm, *data;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d16_b16 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGB_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ /* Get memory for mapping 16 bit XWD-pixel to GIMP-RGB */
+ maxval = 0x10000 * 3;
+ ColorMap = g_new0 (guchar, maxval);
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ /* Built up the array to map XWD-pixel value to GIMP-RGB */
+ for (red = 0; red <= maxred; red++)
+ {
+ redval = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ {
+ greenval = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ {
+ blueval = (blue * 255) / maxblue;
+ cm = ColorMap + ((red << redshift) + (green << greenshift)
+ + (blue << blueshift)) * 3;
+ *(cm++) = redval;
+ *(cm++) = greenval;
+ *cm = blueval;
+ }
+ }
+ }
+
+ /* Now look what was written to the XWD-Colormap */
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ for (j = 0; j < ncols; j++)
+ {
+ cm = ColorMap + xwdcolmap[j].l_pixel * 3;
+ *(cm++) = (xwdcolmap[j].l_red >> 8);
+ *(cm++) = (xwdcolmap[j].l_green >> 8);
+ *cm = (xwdcolmap[j].l_blue >> 8);
+ }
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ if (c1 < 0)
+ {
+ err = 1;
+ break;
+ }
+
+ if (lsbyte_first)
+ c0 = c0 | (c1 << 8);
+ else
+ c0 = (c0 << 8) | c1;
+
+ cm = ColorMap + c0 * 3;
+ *(dest++) = *(cm++);
+ *(dest++) = *(cm++);
+ *(dest++) = *cm;
+ }
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((double)(i+1) / (double)height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ g_free (data);
+ g_free (ColorMap);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 24, bits_per_pixel 24/32 */
+
+static gint32
+load_xwd_f2_d24_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, c2, c3;
+ gint tile_height, scan_lines;
+ L_CARD32 pixelval;
+ gint red, green, blue, ncols;
+ gint maxred, maxgreen, maxblue;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ guchar redmap[256], greenmap[256], bluemap[256];
+ guchar *data;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d24_b32 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ if (maxred > sizeof (redmap) ||
+ maxgreen > sizeof (greenmap) ||
+ maxblue > sizeof (bluemap))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s is corrupt."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGB_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 3);
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ if (xwdhdr->l_bits_per_pixel == 32)
+ {
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ c3 = getc (ifp);
+ if (c3 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
+ else
+ pixelval = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ dest += 3;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ }
+ else /* 24 bits per pixel */
+ {
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ if (c2 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16);
+ else
+ pixelval = (c0 << 16) | (c1 << 8) | c2;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ dest += 3;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+/* Load XWD with pixmap_format 2, pixmap_depth up to 32, bits_per_pixel 32 */
+
+static gint32
+load_xwd_f2_d32_b32 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap)
+{
+ register guchar *dest, lsbyte_first;
+ gint width, height, linepad, i, j, c0, c1, c2, c3;
+ gint tile_height, scan_lines;
+ L_CARD32 pixelval;
+ gint red, green, blue, alpha, ncols;
+ gint maxred, maxgreen, maxblue, maxalpha;
+ gulong redmask, greenmask, bluemask, alphamask;
+ guint redshift, greenshift, blueshift, alphashift;
+ guchar redmap[256], greenmap[256], bluemap[256], alphamap[256];
+ guchar *data;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f2_d32_b32 (%s)\n", filename);
+#endif
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+
+ image_ID = create_new_image (filename, width, height, GIMP_RGB,
+ GIMP_RGBA_IMAGE, &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * 4);
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+ alphamask = 0xffffffff & ~(redmask | greenmask | bluemask);
+
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+ redshift = greenshift = blueshift = alphashift = 0;
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+ while (((1 << alphashift) & alphamask) == 0) alphashift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ maxalpha = 0; while (alphamask >> (alphashift + maxalpha)) maxalpha++;
+ maxalpha = (1 << maxalpha) - 1;
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+ for (alpha = 0; alpha <= maxalpha; alpha++)
+ alphamap[alpha] = (alpha * 255) / maxalpha;
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+
+ /* What do we have to consume after a line has finished ? */
+ linepad = xwdhdr->l_bytes_per_line
+ - (xwdhdr->l_pixmap_width*xwdhdr->l_bits_per_pixel)/8;
+ if (linepad < 0) linepad = 0;
+
+ lsbyte_first = (xwdhdr->l_byte_order == 0);
+
+ dest = data;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ c0 = getc (ifp);
+ c1 = getc (ifp);
+ c2 = getc (ifp);
+ c3 = getc (ifp);
+ if (c3 < 0)
+ {
+ err = 1;
+ break;
+ }
+ if (lsbyte_first)
+ pixelval = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);
+ else
+ pixelval = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2))
+ {
+ /* FIXME: is it always transparent or encoded in an unknown way? */
+ *(dest+3) = 0x00;
+ dest += 4;
+ }
+ else
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ *(dest++) = alphamap[(pixelval & alphamask) >> alphashift];
+ }
+ }
+ scan_lines++;
+
+ if (err)
+ break;
+
+ for (j = 0; j < linepad; j++)
+ getc (ifp);
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i+1) == height))
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - scan_lines + 1,
+ width, scan_lines), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data;
+ }
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+/* Load XWD with pixmap_format 1, pixmap_depth up to 24, bits_per_pixel 1 */
+
+static gint32
+load_xwd_f1_d24_b1 (const gchar *filename,
+ FILE *ifp,
+ L_XWDFILEHEADER *xwdhdr,
+ L_XWDCOLOR *xwdcolmap,
+ GError **error)
+{
+ register guchar *dest, outmask, inmask, do_reverse;
+ gint width, height, i, j, plane, fromright;
+ gint tile_height, tile_start, tile_end;
+ gint indexed, bytes_per_pixel;
+ gint maxred, maxgreen, maxblue;
+ gint red, green, blue, ncols, standard_rgb;
+ glong data_offset, plane_offset, tile_offset;
+ gulong redmask, greenmask, bluemask;
+ guint redshift, greenshift, blueshift;
+ gulong g;
+ guchar redmap[256], greenmap[256], bluemap[256];
+ guchar bit_reverse[256];
+ guchar *xwddata, *xwdin, *data;
+ L_CARD32 pixelval;
+ PIXEL_MAP pixel_map;
+ gint err = 0;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+
+#ifdef XWD_DEBUG
+ g_printf ("load_xwd_f1_d24_b1 (%s)\n", filename);
+#endif
+
+ xwddata = g_malloc (xwdhdr->l_bytes_per_line);
+ if (xwddata == NULL)
+ return -1;
+
+ width = xwdhdr->l_pixmap_width;
+ height = xwdhdr->l_pixmap_height;
+ indexed = (xwdhdr->l_pixmap_depth <= 8);
+ bytes_per_pixel = (indexed ? 1 : 3);
+
+ for (j = 0; j < 256; j++) /* Create an array for reversing bits */
+ {
+ inmask = 0;
+ for (i = 0; i < 8; i++)
+ {
+ inmask <<= 1;
+ if (j & (1 << i)) inmask |= 1;
+ }
+ bit_reverse[j] = inmask;
+ }
+
+ redmask = xwdhdr->l_red_mask;
+ greenmask = xwdhdr->l_green_mask;
+ bluemask = xwdhdr->l_blue_mask;
+
+ if (redmask == 0) redmask = 0xff0000;
+ if (greenmask == 0) greenmask = 0x00ff00;
+ if (bluemask == 0) bluemask = 0x0000ff;
+
+ standard_rgb = (redmask == 0xff0000) && (greenmask == 0x00ff00)
+ && (bluemask == 0x0000ff);
+ redshift = greenshift = blueshift = 0;
+
+ if (!standard_rgb) /* Do we need to re-map the pixel-values ? */
+ {
+ /* How to shift RGB to be right aligned ? */
+ /* (We rely on the the mask bits are grouped and not mixed) */
+
+ while (((1 << redshift) & redmask) == 0) redshift++;
+ while (((1 << greenshift) & greenmask) == 0) greenshift++;
+ while (((1 << blueshift) & bluemask) == 0) blueshift++;
+
+ /* The bits_per_rgb may not be correct. Use redmask instead */
+
+ maxred = 0; while (redmask >> (redshift + maxred)) maxred++;
+ maxred = (1 << maxred) - 1;
+
+ maxgreen = 0; while (greenmask >> (greenshift + maxgreen)) maxgreen++;
+ maxgreen = (1 << maxgreen) - 1;
+
+ maxblue = 0; while (bluemask >> (blueshift + maxblue)) maxblue++;
+ maxblue = (1 << maxblue) - 1;
+
+ if (maxred > sizeof (redmap) ||
+ maxgreen > sizeof (greenmap) ||
+ maxblue > sizeof (bluemap))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("XWD-file %s is corrupt."),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ /* Set map-arrays for red, green, blue */
+ for (red = 0; red <= maxred; red++)
+ redmap[red] = (red * 255) / maxred;
+ for (green = 0; green <= maxgreen; green++)
+ greenmap[green] = (green * 255) / maxgreen;
+ for (blue = 0; blue <= maxblue; blue++)
+ bluemap[blue] = (blue * 255) / maxblue;
+ }
+
+ image_ID = create_new_image (filename, width, height,
+ indexed ? GIMP_INDEXED : GIMP_RGB,
+ indexed ? GIMP_INDEXED_IMAGE : GIMP_RGB_IMAGE,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+ data = g_malloc (tile_height * width * bytes_per_pixel);
+
+ ncols = xwdhdr->l_colormap_entries;
+ if (xwdhdr->l_ncolors < ncols)
+ ncols = xwdhdr->l_ncolors;
+
+ if (indexed)
+ {
+ if (ncols < 2)
+ set_bw_color_table (image_ID);
+ else
+ set_color_table (image_ID, xwdhdr, xwdcolmap);
+ }
+ else
+ {
+ set_pixelmap (ncols, xwdcolmap, &pixel_map);
+ }
+
+ do_reverse = !xwdhdr->l_bitmap_bit_order;
+
+ /* This is where the image data starts within the file */
+ data_offset = ftell (ifp);
+
+ for (tile_start = 0; tile_start < height; tile_start += tile_height)
+ {
+ memset (data, 0, width*tile_height*bytes_per_pixel);
+
+ tile_end = tile_start + tile_height - 1;
+ if (tile_end >= height)
+ tile_end = height - 1;
+
+ for (plane = 0; plane < xwdhdr->l_pixmap_depth; plane++)
+ {
+ dest = data; /* Position to start of tile within the plane */
+ plane_offset = data_offset + plane*height*xwdhdr->l_bytes_per_line;
+ tile_offset = plane_offset + tile_start*xwdhdr->l_bytes_per_line;
+ fseek (ifp, tile_offset, SEEK_SET);
+
+ /* Place the last plane at the least significant bit */
+
+ if (indexed) /* Only 1 byte per pixel */
+ {
+ fromright = xwdhdr->l_pixmap_depth-1-plane;
+ outmask = (1 << fromright);
+ }
+ else /* 3 bytes per pixel */
+ {
+ fromright = xwdhdr->l_pixmap_depth-1-plane;
+ dest += 2 - fromright/8;
+ outmask = (1 << (fromright % 8));
+ }
+
+ for (i = tile_start; i <= tile_end; i++)
+ {
+ if (fread (xwddata,xwdhdr->l_bytes_per_line,1,ifp) != 1)
+ {
+ err = 1;
+ break;
+ }
+ xwdin = xwddata;
+
+ /* Handle bitmap unit */
+ if (xwdhdr->l_bitmap_unit == 16)
+ {
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ j = xwdhdr->l_bytes_per_line/2;
+ while (j--)
+ {
+ inmask = xwdin[0]; xwdin[0] = xwdin[1]; xwdin[1] = inmask;
+ xwdin += 2;
+ }
+ xwdin = xwddata;
+ }
+ }
+ else if (xwdhdr->l_bitmap_unit == 32)
+ {
+ if (xwdhdr->l_bitmap_bit_order != xwdhdr->l_byte_order)
+ {
+ j = xwdhdr->l_bytes_per_line/4;
+ while (j--)
+ {
+ inmask = xwdin[0]; xwdin[0] = xwdin[3]; xwdin[3] = inmask;
+ inmask = xwdin[1]; xwdin[1] = xwdin[2]; xwdin[2] = inmask;
+ xwdin += 4;
+ }
+ xwdin = xwddata;
+ }
+ }
+
+ g = inmask = 0;
+ for (j = 0; j < width; j++)
+ {
+ if (!inmask)
+ {
+ g = *(xwdin++);
+ if (do_reverse)
+ g = bit_reverse[g];
+ inmask = 0x80;
+ }
+
+ if (g & inmask)
+ *dest |= outmask;
+ dest += bytes_per_pixel;
+
+ inmask >>= 1;
+ }
+ }
+ }
+
+ /* For indexed images, the mapping to colors is done by the color table. */
+ /* Otherwise we must do the mapping by ourself. */
+ if (!indexed)
+ {
+ dest = data;
+ for (i = tile_start; i <= tile_end; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ pixelval = (*dest << 16) | (*(dest+1) << 8) | *(dest+2);
+
+ if (get_pixelmap (pixelval, &pixel_map, dest, dest+1, dest+2)
+ || standard_rgb)
+ {
+ dest += 3;
+ }
+ else /* We have to map RGB to 0,...,255 */
+ {
+ *(dest++) = redmap[(pixelval & redmask) >> redshift];
+ *(dest++) = greenmap[(pixelval & greenmask) >> greenshift];
+ *(dest++) = bluemap[(pixelval & bluemask) >> blueshift];
+ }
+ }
+ }
+ }
+
+ gimp_progress_update ((gdouble) tile_end / (gdouble) height);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, tile_start,
+ width, tile_end-tile_start+1), 0,
+ NULL, data, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (data);
+ g_free (xwddata);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ return err ? -1 : image_ID;
+}
+
+
+static gboolean
+save_index (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gboolean gray,
+ GError **error)
+{
+ gint height, width;
+ gint linepad;
+ gint tile_height;
+ gint i, j;
+ gint ncolors, vclass;
+ glong tmp = 0;
+ guchar *data, *src, *cmap;
+ L_XWDFILEHEADER xwdhdr;
+ L_XWDCOLOR xwdcolmap[256];
+ const Babl *format;
+ GeglBuffer *buffer;
+ gboolean success = TRUE;
+
+#ifdef XWD_DEBUG
+ g_printf ("save_index ()\n");
+#endif
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ tile_height = gimp_tile_height ();
+
+ if (gray)
+ format = babl_format ("Y' u8");
+ else
+ format = gegl_buffer_get_format (buffer);
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar,
+ tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ linepad = width % 4;
+ if (linepad)
+ linepad = 4 - linepad;
+
+ /* Fill XWD-color map */
+ if (gray)
+ {
+ vclass = 0;
+ ncolors = 256;
+
+ for (j = 0; j < ncolors; j++)
+ {
+ xwdcolmap[j].l_pixel = j;
+ xwdcolmap[j].l_red = (j << 8) | j;
+ xwdcolmap[j].l_green = (j << 8) | j;
+ xwdcolmap[j].l_blue = (j << 8) | j;
+ xwdcolmap[j].l_flags = 7;
+ xwdcolmap[j].l_pad = 0;
+ }
+ }
+ else
+ {
+ vclass = 3;
+ cmap = gimp_image_get_colormap (image_ID, &ncolors);
+
+ for (j = 0; j < ncolors; j++)
+ {
+ xwdcolmap[j].l_pixel = j;
+ xwdcolmap[j].l_red = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_green = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_blue = ((*cmap) << 8) | *cmap; cmap++;
+ xwdcolmap[j].l_flags = 7;
+ xwdcolmap[j].l_pad = 0;
+ }
+ }
+
+ /* Fill in the XWD header (header_size is evaluated by write_xwd_hdr ()) */
+ xwdhdr.l_header_size = 0;
+ xwdhdr.l_file_version = 7;
+ xwdhdr.l_pixmap_format = 2;
+ xwdhdr.l_pixmap_depth = 8;
+ xwdhdr.l_pixmap_width = width;
+ xwdhdr.l_pixmap_height = height;
+ xwdhdr.l_xoffset = 0;
+ xwdhdr.l_byte_order = 1;
+ xwdhdr.l_bitmap_unit = 32;
+ xwdhdr.l_bitmap_bit_order = 1;
+ xwdhdr.l_bitmap_pad = 32;
+ xwdhdr.l_bits_per_pixel = 8;
+ xwdhdr.l_bytes_per_line = width + linepad;
+ xwdhdr.l_visual_class = vclass;
+ xwdhdr.l_red_mask = 0x000000;
+ xwdhdr.l_green_mask = 0x000000;
+ xwdhdr.l_blue_mask = 0x000000;
+ xwdhdr.l_bits_per_rgb = 8;
+ xwdhdr.l_colormap_entries = ncolors;
+ xwdhdr.l_ncolors = ncolors;
+ xwdhdr.l_window_width = width;
+ xwdhdr.l_window_height = height;
+ xwdhdr.l_window_x = 64;
+ xwdhdr.l_window_y = 64;
+ xwdhdr.l_window_bdrwidth = 0;
+
+ success = (write_xwd_header (output, &xwdhdr, error) &&
+ write_xwd_cols (output, &xwdhdr, xwdcolmap, error));
+ if (! success)
+ goto out;
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0) /* Get more data */
+ {
+ gint scan_lines = (i + tile_height - 1 < height) ? tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = data;
+ }
+
+ success = g_output_stream_write_all (output, src, width,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+
+ if (linepad)
+ {
+ success = g_output_stream_write_all (output, &tmp, linepad,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+ }
+
+ src += width;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ out:
+ g_free (data);
+ g_object_unref (buffer);
+
+ return success;
+}
+
+static gboolean
+save_rgb (GOutputStream *output,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ gint height, width;
+ gint linepad;
+ gint tile_height;
+ gint i;
+ glong tmp = 0;
+ guchar *data, *src;
+ L_XWDFILEHEADER xwdhdr;
+ const Babl *format;
+ GeglBuffer *buffer;
+ gboolean success = TRUE;
+
+#ifdef XWD_DEBUG
+ g_printf ("save_rgb ()\n");
+#endif
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+ tile_height = gimp_tile_height ();
+ format = babl_format ("R'G'B' u8");
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = g_new (guchar,
+ tile_height * width *
+ babl_format_get_bytes_per_pixel (format));
+
+ linepad = (width * 3) % 4;
+ if (linepad)
+ linepad = 4 - linepad;
+
+ /* Fill in the XWD header (header_size is evaluated by write_xwd_hdr ()) */
+ xwdhdr.l_header_size = 0;
+ xwdhdr.l_file_version = 7;
+ xwdhdr.l_pixmap_format = 2;
+ xwdhdr.l_pixmap_depth = 24;
+ xwdhdr.l_pixmap_width = width;
+ xwdhdr.l_pixmap_height = height;
+ xwdhdr.l_xoffset = 0;
+ xwdhdr.l_byte_order = 1;
+
+ xwdhdr.l_bitmap_unit = 32;
+ xwdhdr.l_bitmap_bit_order = 1;
+ xwdhdr.l_bitmap_pad = 32;
+ xwdhdr.l_bits_per_pixel = 24;
+
+ xwdhdr.l_bytes_per_line = width * 3 + linepad;
+ xwdhdr.l_visual_class = 5;
+ xwdhdr.l_red_mask = 0xff0000;
+ xwdhdr.l_green_mask = 0x00ff00;
+ xwdhdr.l_blue_mask = 0x0000ff;
+ xwdhdr.l_bits_per_rgb = 8;
+ xwdhdr.l_colormap_entries = 0;
+ xwdhdr.l_ncolors = 0;
+ xwdhdr.l_window_width = width;
+ xwdhdr.l_window_height = height;
+ xwdhdr.l_window_x = 64;
+ xwdhdr.l_window_y = 64;
+ xwdhdr.l_window_bdrwidth = 0;
+
+ success = write_xwd_header (output, &xwdhdr, error);
+ if (! success)
+ goto out;
+
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0) /* Get more data */
+ {
+ gint scan_lines = (i + tile_height - 1 < height) ? tile_height : (height - i);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, scan_lines), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = data;
+ }
+
+ success = g_output_stream_write_all (output, src, width * 3,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+
+ if (linepad)
+ {
+ success = g_output_stream_write_all (output, &tmp, linepad,
+ NULL, NULL, error);
+ if (! success)
+ goto out;
+ }
+
+ src += width * 3;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) i / (gdouble) height);
+ }
+
+ out:
+ g_free (data);
+ g_object_unref (buffer);
+
+ return success;
+}
diff --git a/plug-ins/common/film.c b/plug-ins/common/film.c
new file mode 100644
index 0000000..59548b9
--- /dev/null
+++ b/plug-ins/common/film.c
@@ -0,0 +1,1287 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Film plug-in (C) 1997 Peter Kirchgessner
+ * e-mail: pkirchg@aol.com, WWW: http://members.aol.com/pkirchg
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This plug-in generates a film roll with several images
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-film"
+#define PLUG_IN_BINARY "film"
+#define PLUG_IN_ROLE "gimp-film"
+
+/* Maximum number of pictures per film */
+#define MAX_FILM_PICTURES 64
+#define COLOR_BUTTON_WIDTH 50
+#define COLOR_BUTTON_HEIGHT 20
+
+#define FONT_LEN 256
+
+/* Define how the plug-in works. Values marked (r) are with regard */
+/* to film_height (i.e. it should be a value from 0.0 to 1.0) */
+typedef struct
+{
+ gint film_height; /* height of the film */
+ GimpRGB film_color; /* color of film */
+ gdouble picture_height; /* height of picture (r) */
+ gdouble picture_space; /* space between pictures (r) */
+ gdouble hole_offset; /* distance from hole to edge of film (r) */
+ gdouble hole_width; /* width of hole (r) */
+ gdouble hole_height; /* height of holes (r) */
+ gdouble hole_space; /* distance of holes (r) */
+ gdouble number_height; /* height of picture numbering (r) */
+ gint number_start; /* number for first picture */
+ GimpRGB number_color; /* color of number */
+ gchar number_font[FONT_LEN]; /* font family to use for numbering */
+ gint number_pos[2]; /* flags where to draw numbers (top/bottom) */
+ gint keep_height; /* flag if to keep max. image height */
+ gint num_images; /* number of images */
+ gint32 image[MAX_FILM_PICTURES]; /* list of image IDs */
+} FilmVals;
+
+/* Data to use for the dialog */
+typedef struct
+{
+ GtkObject *advanced_adj[7];
+ GtkTreeModel *image_list_all;
+ GtkTreeModel *image_list_film;
+} FilmInterface;
+
+
+/* Declare local functions
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+static gint32 create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ gint32 *layer_ID);
+
+static gchar * compose_image_name (gint32 image_ID);
+
+static gint32 film (void);
+
+static gboolean check_filmvals (void);
+
+static void set_pixels (gint numpix,
+ guchar *dst,
+ GimpRGB *color);
+
+static guchar * create_hole_rgb (gint width,
+ gint height);
+
+static void draw_number (gint32 layer_ID,
+ gint num,
+ gint x,
+ gint y,
+ gint height);
+
+
+static void add_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel);
+static void del_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel);
+
+static GtkTreeModel * add_image_list (gboolean add_box_flag,
+ gint n,
+ gint32 *image_id,
+ GtkWidget *hbox);
+
+static gboolean film_dialog (gint32 image_ID);
+static void film_reset_callback (GtkWidget *widget,
+ gpointer data);
+static void film_font_select_callback (GimpFontSelectButton *button,
+ const gchar *name,
+ gboolean closing,
+ gpointer data);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gdouble advanced_defaults[] =
+{
+ 0.695, /* Picture height */
+ 0.040, /* Picture spacing */
+ 0.058, /* Hole offset to edge of film */
+ 0.052, /* Hole width */
+ 0.081, /* Hole height */
+ 0.081, /* Hole distance */
+ 0.052 /* Image number height */
+};
+
+static FilmVals filmvals =
+{
+ 256, /* Height of film */
+ { 0.0, 0.0, 0.0, 1.0 }, /* Color of film */
+ 0.695, /* Picture height */
+ 0.040, /* Picture spacing */
+ 0.058, /* Hole offset to edge of film */
+ 0.052, /* Hole width */
+ 0.081, /* Hole height */
+ 0.081, /* Hole distance */
+ 0.052, /* Image number height */
+ 1, /* Start index of numbering */
+ { 0.93, 0.61, 0.0, 1.0 }, /* Color of number */
+ "Monospace", /* Font family for numbering */
+ { TRUE, TRUE }, /* Numbering on top and bottom */
+ 0, /* Don't keep max. image height */
+ 0, /* Number of images */
+ { 0 } /* Input image list */
+};
+
+
+static FilmInterface filmint =
+{
+ { NULL }, /* advanced adjustments */
+ NULL, NULL /* image list widgets */
+};
+
+
+static GimpRunMode run_mode;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (only used as default image in interactive mode)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (not used)" },
+ { GIMP_PDB_INT32, "film-height", "Height of film (0: fit to images)" },
+ { GIMP_PDB_COLOR, "film-color", "Color of the film" },
+ { GIMP_PDB_INT32, "number-start", "Start index for numbering" },
+ { GIMP_PDB_STRING, "number-font", "Font for drawing numbers" },
+ { GIMP_PDB_COLOR, "number-color", "Color for numbers" },
+ { GIMP_PDB_INT32, "at-top", "Flag for drawing numbers at top of film" },
+ { GIMP_PDB_INT32, "at-bottom", "Flag for drawing numbers at bottom of film" },
+ { GIMP_PDB_INT32, "num-images", "Number of images to be used for film" },
+ { GIMP_PDB_INT32ARRAY, "image-ids", "num-images image IDs to be used for film" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new-image", "Output image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Combine several images on a film strip"),
+ "Compose several images to a roll film",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1997",
+ N_("_Filmstrip..."),
+ "INDEXED*, GRAY*, RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Combine");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint k;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_int32 = -1;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &filmvals);
+
+ /* First acquire information with a dialog */
+ if (! film_dialog (param[1].data.d_int32))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ /* Also we want to have some images to compose */
+ if ((nparams != 12) || (param[10].data.d_int32 < 1))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ filmvals.keep_height = (param[3].data.d_int32 <= 0);
+ filmvals.film_height = (filmvals.keep_height ?
+ 128 : param[3].data.d_int32);
+ filmvals.film_color = param[4].data.d_color;
+ filmvals.number_start = param[5].data.d_int32;
+ g_strlcpy (filmvals.number_font, param[6].data.d_string, FONT_LEN);
+ filmvals.number_color = param[7].data.d_color;
+ filmvals.number_pos[0] = param[8].data.d_int32;
+ filmvals.number_pos[1] = param[9].data.d_int32;
+ filmvals.num_images = param[10].data.d_int32;
+ if (filmvals.num_images > MAX_FILM_PICTURES)
+ filmvals.num_images = MAX_FILM_PICTURES;
+ for (k = 0; k < filmvals.num_images; k++)
+ filmvals.image[k] = param[11].data.d_int32array[k];
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &filmvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (! check_filmvals ())
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Composing images"));
+
+ image_ID = film ();
+
+ if (image_ID < 0)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ values[1].data.d_int32 = image_ID;
+ gimp_image_undo_enable (image_ID);
+ gimp_image_clean_all (image_ID);
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_display_new (image_ID);
+ }
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &filmvals, sizeof (FilmVals));
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* Compose a roll film image from several images */
+static gint32
+film (void)
+{
+ gint width, height;
+ guchar *hole;
+ gint film_height, film_width;
+ gint picture_width, picture_height;
+ gint picture_space, picture_x0, picture_y0;
+ gint hole_offset, hole_width, hole_height, hole_space, hole_x;
+ gint number_height, num_images, num_pictures;
+ gint j, k, picture_count;
+ gdouble f;
+ gint num_layers;
+ gint32 *image_ID_src, image_ID_dst, layer_ID_src, layer_ID_dst;
+ gint image_ID_tmp;
+ gint32 *layers;
+ gint new_layer;
+ gint floating_sel;
+
+ /* initialize */
+
+ layers = NULL;
+
+ num_images = filmvals.num_images;
+ image_ID_src = filmvals.image;
+
+ if (num_images <= 0)
+ return (-1);
+
+ gimp_context_push ();
+ gimp_context_set_foreground (&filmvals.number_color);
+ gimp_context_set_background (&filmvals.film_color);
+
+ if (filmvals.keep_height) /* Search maximum picture height */
+ {
+ picture_height = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ height = gimp_image_height (image_ID_src[j]);
+ if (height > picture_height) picture_height = height;
+ }
+ film_height = (int)(picture_height / filmvals.picture_height + 0.5);
+ filmvals.film_height = film_height;
+ }
+ else
+ {
+ film_height = filmvals.film_height;
+ picture_height = (int)(film_height * filmvals.picture_height + 0.5);
+ }
+
+ picture_space = (int)(film_height * filmvals.picture_space + 0.5);
+ picture_y0 = (film_height - picture_height)/2;
+
+ number_height = film_height * filmvals.number_height;
+
+ /* Calculate total film width */
+ film_width = 0;
+ num_pictures = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ layers = gimp_image_get_layers (image_ID_src[j], &num_layers);
+ /* Get scaled image size */
+ width = gimp_image_width (image_ID_src[j]);
+ height = gimp_image_height (image_ID_src[j]);
+ f = ((double)picture_height) / (double)height;
+ picture_width = width * f;
+
+ for (k = 0; k < num_layers; k++)
+ {
+ if (gimp_layer_is_floating_sel (layers[k]))
+ continue;
+
+ film_width += (picture_space/2); /* Leading space */
+ film_width += picture_width; /* Scaled image width */
+ film_width += (picture_space/2); /* Trailing space */
+ num_pictures++;
+ }
+
+ g_free (layers);
+ }
+
+#ifdef FILM_DEBUG
+ g_printerr ("film_height = %d, film_width = %d\n", film_height, film_width);
+ g_printerr ("picture_height = %d, picture_space = %d, picture_y0 = %d\n",
+ picture_height, picture_space, picture_y0);
+ g_printerr ("Number of pictures = %d\n", num_pictures);
+#endif
+
+ image_ID_dst = create_new_image (_("Untitled"),
+ (guint) film_width, (guint) film_height,
+ GIMP_RGB_IMAGE, &layer_ID_dst);
+
+ /* Fill film background */
+ gimp_drawable_fill (layer_ID_dst, GIMP_FILL_BACKGROUND);
+
+ /* Draw all the holes */
+ hole_offset = film_height * filmvals.hole_offset;
+ hole_width = film_height * filmvals.hole_width;
+ hole_height = film_height * filmvals.hole_height;
+ hole_space = film_height * filmvals.hole_space;
+ hole_x = hole_space / 2;
+
+#ifdef FILM_DEBUG
+ g_printerr ("hole_x %d hole_offset %d hole_width %d hole_height %d hole_space %d\n",
+ hole_x, hole_offset, hole_width, hole_height, hole_space );
+#endif
+
+ hole = create_hole_rgb (hole_width, hole_height);
+ if (hole)
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (layer_ID_dst);
+
+ while (hole_x < film_width)
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (hole_x,
+ hole_offset,
+ hole_width,
+ hole_height), 0,
+ babl_format ("R'G'B' u8"), hole,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (hole_x,
+ film_height - hole_offset - hole_height,
+ hole_width,
+ hole_height), 0,
+ babl_format ("R'G'B' u8"), hole,
+ GEGL_AUTO_ROWSTRIDE);
+
+ hole_x += hole_width + hole_space;
+ }
+
+ g_object_unref (buffer);
+ g_free (hole);
+ }
+
+ /* Compose all images and layers */
+ picture_x0 = 0;
+ picture_count = 0;
+ for (j = 0; j < num_images; j++)
+ {
+ image_ID_tmp = gimp_image_duplicate (image_ID_src[j]);
+ width = gimp_image_width (image_ID_tmp);
+ height = gimp_image_height (image_ID_tmp);
+ f = ((gdouble) picture_height) / (gdouble) height;
+ picture_width = width * f;
+ if (gimp_image_base_type (image_ID_tmp) != GIMP_RGB)
+ gimp_image_convert_rgb (image_ID_tmp);
+ gimp_image_scale (image_ID_tmp, picture_width, picture_height);
+
+ layers = gimp_image_get_layers (image_ID_tmp, &num_layers);
+ for (k = 0; k < num_layers; k++)
+ {
+ if (gimp_layer_is_floating_sel (layers[k]))
+ continue;
+
+ picture_x0 += picture_space / 2;
+
+ layer_ID_src = layers[k];
+ gimp_layer_resize_to_image_size (layer_ID_src);
+ new_layer = gimp_layer_new_from_drawable (layer_ID_src,
+ image_ID_dst);
+ gimp_image_insert_layer (image_ID_dst, new_layer, -1, -1);
+ gimp_layer_set_offsets (new_layer, picture_x0, picture_y0);
+
+ /* Draw picture numbers */
+ if ((number_height > 0) &&
+ (filmvals.number_pos[0] || filmvals.number_pos[1]))
+ {
+ if (filmvals.number_pos[0])
+ draw_number (layer_ID_dst,
+ filmvals.number_start + picture_count,
+ picture_x0 + picture_width/2,
+ (hole_offset-number_height)/2, number_height);
+ if (filmvals.number_pos[1])
+ draw_number (layer_ID_dst,
+ filmvals.number_start + picture_count,
+ picture_x0 + picture_width/2,
+ film_height - (hole_offset + number_height)/2,
+ number_height);
+ }
+
+ picture_x0 += picture_width + (picture_space/2);
+
+ gimp_progress_update (((gdouble) (picture_count + 1)) /
+ (gdouble) num_pictures);
+
+ picture_count++;
+ }
+
+ g_free (layers);
+ gimp_image_delete (image_ID_tmp);
+ }
+ gimp_progress_update (1.0);
+
+ gimp_image_flatten (image_ID_dst);
+
+ /* Drawing text/numbers leaves us with a floating selection. Stop it */
+ floating_sel = gimp_image_get_floating_sel (image_ID_dst);
+ if (floating_sel != -1)
+ gimp_floating_sel_anchor (floating_sel);
+
+ gimp_context_pop ();
+
+ return image_ID_dst;
+}
+
+/* Check filmvals. Unreasonable values are reset to a default. */
+/* If this is not possible, FALSE is returned. Otherwise TRUE is returned. */
+static gboolean
+check_filmvals (void)
+{
+ if (filmvals.film_height < 10)
+ filmvals.film_height = 10;
+
+ if (filmvals.number_start < 0)
+ filmvals.number_start = 0;
+
+ if (filmvals.number_font[0] == '\0')
+ strcpy (filmvals.number_font, "Monospace");
+
+ if (filmvals.num_images < 1)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Assigns numpix pixels starting at dst with color r,g,b */
+static void
+set_pixels (gint numpix,
+ guchar *dst,
+ GimpRGB *color)
+{
+ register gint k;
+ register guchar ur, ug, ub, *udest;
+
+ ur = color->r * 255.999;
+ ug = color->g * 255.999;
+ ub = color->b * 255.999;
+ k = numpix;
+ udest = dst;
+
+ while (k-- > 0)
+ {
+ *(udest++) = ur;
+ *(udest++) = ug;
+ *(udest++) = ub;
+ }
+}
+
+/* Create the RGB-pixels that make up the hole */
+static guchar *
+create_hole_rgb (gint width,
+ gint height)
+{
+ guchar *hole, *top, *bottom;
+ gint radius, length, k;
+
+ hole = g_new (guchar, width * height * 3);
+
+ /* Fill a rectangle with white */
+ memset (hole, 255, width * height * 3);
+ radius = height / 4;
+ if (radius > width / 2)
+ radius = width / 2;
+ top = hole;
+ bottom = hole + (height-1)*width*3;
+ for (k = radius-1; k > 0; k--) /* Rounding corners */
+ {
+ length = (int)(radius - sqrt ((gdouble) (radius * radius - k * k))
+ - 0.5);
+ if (length > 0)
+ {
+ set_pixels (length, top, &filmvals.film_color);
+ set_pixels (length, top + (width-length)*3, &filmvals.film_color);
+ set_pixels (length, bottom, &filmvals.film_color);
+ set_pixels (length, bottom + (width-length)*3, &filmvals.film_color);
+ }
+ top += width*3;
+ bottom -= width*3;
+ }
+
+ return hole;
+}
+
+/* Draw the number of the picture onto the film */
+static void
+draw_number (gint32 layer_ID,
+ gint num,
+ gint x,
+ gint y,
+ gint height)
+{
+ gchar buf[32];
+ gint k, delta, max_delta;
+ gint32 image_ID;
+ gint32 text_layer_ID;
+ gint text_width, text_height, text_ascent, descent;
+ gchar *fontname = filmvals.number_font;
+
+ g_snprintf (buf, sizeof (buf), "%d", num);
+
+ image_ID = gimp_item_get_image (layer_ID);
+
+ max_delta = height / 10;
+ if (max_delta < 1)
+ max_delta = 1;
+
+ /* Numbers don't need the descent. Inquire it and move the text down */
+ for (k = 0; k < max_delta * 2 + 1; k++)
+ {
+ /* Try different font sizes if inquire of extent failed */
+ gboolean success;
+
+ delta = (k+1) / 2;
+
+ if ((k & 1) == 0)
+ delta = -delta;
+
+ success = gimp_text_get_extents_fontname (buf,
+ height + delta, GIMP_PIXELS,
+ fontname,
+ &text_width, &text_height,
+ &text_ascent, &descent);
+
+ if (success)
+ {
+ height += delta;
+ break;
+ }
+ }
+
+ text_layer_ID = gimp_text_fontname (image_ID, layer_ID,
+ x, y + descent / 2,
+ buf, 1, FALSE,
+ height, GIMP_PIXELS,
+ fontname);
+
+ if (text_layer_ID == -1)
+ g_message ("draw_number: Error in drawing text\n");
+}
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint width,
+ guint height,
+ GimpImageType gdtype,
+ gint32 *layer_ID)
+{
+ gint32 image_ID;
+ GimpImageBaseType gitype;
+
+ if ((gdtype == GIMP_GRAY_IMAGE) || (gdtype == GIMP_GRAYA_IMAGE))
+ gitype = GIMP_GRAY;
+ else if ((gdtype == GIMP_INDEXED_IMAGE) || (gdtype == GIMP_INDEXEDA_IMAGE))
+ gitype = GIMP_INDEXED;
+ else
+ gitype = GIMP_RGB;
+
+ image_ID = gimp_image_new (width, height, gitype);
+ gimp_image_set_filename (image_ID, filename);
+
+ gimp_image_undo_disable (image_ID);
+ *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
+ gdtype,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+static gchar *
+compose_image_name (gint32 image_ID)
+{
+ gchar *image_name;
+ gchar *name;
+
+ /* Compose a name of the basename and the image-ID */
+
+ name = gimp_image_get_name (image_ID);
+
+ image_name = g_strdup_printf ("%s-%d", name, image_ID);
+
+ g_free (name);
+
+ return image_name;
+}
+
+static void
+add_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel)
+{
+ GtkTreeModel *model;
+ GList *paths;
+ GList *list;
+
+ paths = gtk_tree_selection_get_selected_rows (sel, &model);
+
+ for (list = paths; list; list = g_list_next (list))
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter (model, &iter, list->data))
+ {
+ gint32 image_ID;
+ gchar *name;
+
+ gtk_tree_model_get (model, &iter,
+ 0, &image_ID,
+ 1, &name,
+ -1);
+
+ gtk_list_store_append (GTK_LIST_STORE (filmint.image_list_film),
+ &iter);
+
+ gtk_list_store_set (GTK_LIST_STORE (filmint.image_list_film),
+ &iter,
+ 0, image_ID,
+ 1, name,
+ -1);
+
+ g_free (name);
+ }
+
+ gtk_tree_path_free (list->data);
+ }
+
+ g_list_free (paths);
+}
+
+static void
+del_list_item_callback (GtkWidget *widget,
+ GtkTreeSelection *sel)
+{
+ GtkTreeModel *model;
+ GList *paths;
+ GList *references = NULL;
+ GList *list;
+
+ paths = gtk_tree_selection_get_selected_rows (sel, &model);
+
+ for (list = paths; list; list = g_list_next (list))
+ {
+ GtkTreeRowReference *ref;
+
+ ref = gtk_tree_row_reference_new (model, list->data);
+ references = g_list_prepend (references, ref);
+ gtk_tree_path_free (list->data);
+ }
+
+ g_list_free (paths);
+
+ for (list = references; list; list = g_list_next (list))
+ {
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ path = gtk_tree_row_reference_get_path (list->data);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ gtk_tree_path_free (path);
+ gtk_tree_row_reference_free (list->data);
+ }
+
+ g_list_free (references);
+}
+
+static GtkTreeModel *
+add_image_list (gboolean add_box_flag,
+ gint n,
+ gint32 *image_id,
+ GtkWidget *hbox)
+{
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *scrolled_win;
+ GtkWidget *tv;
+ GtkWidget *button;
+ GtkListStore *store;
+ GtkTreeSelection *sel;
+ gint i;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (add_box_flag ?
+ _("Available images:") :
+ _("On film:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_win);
+
+ store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tv), FALSE);
+
+ if (! add_box_flag)
+ gtk_tree_view_set_reorderable (GTK_TREE_VIEW (tv), TRUE);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv), 0, NULL,
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ NULL);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
+ gtk_widget_show (tv);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tv));
+ gtk_tree_selection_set_mode (sel, GTK_SELECTION_MULTIPLE);
+
+ for (i = 0; i < n; i++)
+ {
+ GtkTreeIter iter;
+ gchar *name;
+
+ gtk_list_store_append (store, &iter);
+
+ name = compose_image_name (image_id[i]);
+
+ gtk_list_store_set (store, &iter,
+ 0, image_id[i],
+ 1, name,
+ -1);
+
+ g_free (name);
+ }
+
+ button = gtk_button_new_with_mnemonic (add_box_flag ?
+ _("_Add") : _("_Remove"));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ add_box_flag ?
+ G_CALLBACK (add_list_item_callback) :
+ G_CALLBACK (del_list_item_callback),
+ sel);
+
+ return GTK_TREE_MODEL (store);
+}
+
+static void
+create_selection_tab (GtkWidget *notebook,
+ gint32 image_ID)
+{
+ GimpColorConfig *config;
+ GtkSizeGroup *group;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *button;
+ GtkWidget *font_button;
+ gint32 *image_id_list;
+ gint nimages, j;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), hbox,
+ gtk_label_new_with_mnemonic (_("Selection")));
+ gtk_widget_show (hbox);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* Film height/color */
+ frame = gimp_frame_new (_("Filmstrip"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Keep maximum image height */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Fit height to images"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &filmvals.keep_height);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Film height */
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.film_height, 10,
+ GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Height:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &filmvals.film_height);
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+ g_object_bind_property (toggle, "active",
+ /* FIXME: eeeeeek */
+ g_list_nth_data (gtk_container_get_children (GTK_CONTAINER (table)), 1), "sensitive",
+ G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ filmvals.keep_height);
+
+ /* Film color */
+ button = gimp_color_button_new (_("Select Film Color"),
+ COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
+ &filmvals.film_color,
+ GIMP_COLOR_AREA_FLAT);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Co_lor:"), 0.0, 0.5,
+ button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &filmvals.film_color);
+
+ config = gimp_get_color_configuration ();
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
+
+ /* Film numbering: Startindex/Font/color */
+ frame = gimp_frame_new (_("Numbering"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Startindex */
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (filmvals.number_start, 0,
+ GIMP_MAX_IMAGE_SIZE, 1, 10, 0));
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Start _index:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &filmvals.number_start);
+
+ /* Fontfamily for numbering */
+ font_button = gimp_font_select_button_new (NULL, filmvals.number_font);
+ g_signal_connect (font_button, "font-set",
+ G_CALLBACK (film_font_select_callback), &filmvals);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Font:"), 0.0, 0.5,
+ font_button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ /* Numbering color */
+ button = gimp_color_button_new (_("Select Number Color"),
+ COLOR_BUTTON_WIDTH, COLOR_BUTTON_HEIGHT,
+ &filmvals.number_color,
+ GIMP_COLOR_AREA_FLAT);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Co_lor:"), 0.0, 0.5,
+ button, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &filmvals.number_color);
+
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button), config);
+ g_object_unref (config);
+
+ for (j = 0; j < 2; j++)
+ {
+ toggle = gtk_check_button_new_with_mnemonic (j ? _("At _bottom")
+ : _("At _top"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ filmvals.number_pos[j]);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &filmvals.number_pos[j]);
+ }
+
+
+ /*** The right frame keeps the image selection ***/
+ frame = gimp_frame_new (_("Image Selection"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ /* Get a list of all image names */
+ image_id_list = gimp_image_list (&nimages);
+ filmint.image_list_all = add_image_list (TRUE, nimages, image_id_list, hbox);
+
+ /* Get a list of the images used for the film */
+ filmint.image_list_film = add_image_list (FALSE, 1, &image_ID, hbox);
+
+ gtk_widget_show (hbox);
+}
+
+static void
+create_advanced_tab (GtkWidget *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkObject *adj;
+ GtkWidget *button;
+ gint row;
+
+ frame = gimp_frame_new (_("All Values are Fractions of the Strip Height"));
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
+ gtk_label_new_with_mnemonic (_("Ad_vanced")));
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 1, 12);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ filmint.advanced_adj[0] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Image _height:"), 0, 0,
+ filmvals.picture_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.picture_height);
+
+ filmint.advanced_adj[1] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Image spac_ing:"), 0, 0,
+ filmvals.picture_space,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.picture_space);
+
+ filmint.advanced_adj[2] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Hole offset:"), 0, 0,
+ filmvals.hole_offset,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_offset);
+
+ filmint.advanced_adj[3] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Ho_le width:"), 0, 0,
+ filmvals.hole_width,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_width);
+
+ filmint.advanced_adj[4] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hol_e height:"), 0, 0,
+ filmvals.hole_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_height);
+
+ filmint.advanced_adj[5] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hole sp_acing:"), 0, 0,
+ filmvals.hole_space,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.hole_space);
+
+ filmint.advanced_adj[6] = adj =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Number height:"), 0, 0,
+ filmvals.number_height,
+ 0.0, 1.0, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &filmvals.number_height);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("Re_set"));
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (film_reset_callback),
+ NULL);
+}
+
+static gboolean
+film_dialog (gint32 image_ID)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *notebook;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dlg = gimp_dialog_new (_("Filmstrip"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), notebook, TRUE, TRUE, 0);
+
+ create_selection_tab (notebook, image_ID);
+ create_advanced_tab (notebook);
+
+ gtk_widget_show (notebook);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ gint num_images = 0;
+ gboolean iter_valid;
+ GtkTreeIter iter;
+
+ for (iter_valid = gtk_tree_model_get_iter_first (filmint.image_list_film,
+ &iter);
+ iter_valid;
+ iter_valid = gtk_tree_model_iter_next (filmint.image_list_film,
+ &iter))
+ {
+ gint image_ID;
+
+ gtk_tree_model_get (filmint.image_list_film, &iter,
+ 0, &image_ID,
+ -1);
+
+ if ((image_ID >= 0) && (num_images < MAX_FILM_PICTURES))
+ filmvals.image[num_images++] = image_ID;
+ }
+
+ filmvals.num_images = num_images;
+ }
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+static void
+film_reset_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (advanced_defaults) ; i++)
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (filmint.advanced_adj[i]),
+ advanced_defaults[i]);
+}
+
+static void
+film_font_select_callback (GimpFontSelectButton *button,
+ const gchar *name,
+ gboolean closing,
+ gpointer data)
+{
+ FilmVals *vals = (FilmVals *) data;
+
+ g_strlcpy (vals->number_font, name, FONT_LEN);
+}
diff --git a/plug-ins/common/filter-pack.c b/plug-ins/common/filter-pack.c
new file mode 100644
index 0000000..9664596
--- /dev/null
+++ b/plug-ins/common/filter-pack.c
@@ -0,0 +1,2178 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Copyright (C) Pavel Grinfeld (pavel@ml.com)
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-filter-pack"
+#define PLUG_IN_BINARY "filter-pack"
+#define PLUG_IN_ROLE "gimp-filter-pack"
+
+#define MAX_PREVIEW_SIZE 125
+#define MAX_ROUGHNESS 128
+#define RANGE_HEIGHT 15
+#define PR_BX_BRDR 4
+#define MARGIN 4
+
+#define RANGE_ADJUST_MASK (GDK_EXPOSURE_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK)
+
+
+typedef struct
+{
+ gboolean run;
+} fpInterface;
+
+typedef struct
+{
+ gint width;
+ gint height;
+ guchar *rgb;
+ gdouble *hsv;
+ guchar *mask;
+} ReducedImage;
+
+typedef enum
+{
+ SHADOWS,
+ MIDTONES,
+ HIGHLIGHTS,
+ INTENSITIES
+} FPIntensity;
+
+enum
+{
+ NONEATALL = 0,
+ CURRENT = 1,
+ HUE = 2,
+ SATURATION = 4,
+ VALUE = 8
+};
+
+enum
+{
+ BY_HUE,
+ BY_SAT,
+ BY_VAL,
+ JUDGE_BY
+};
+
+enum
+{
+ RED,
+ GREEN,
+ BLUE,
+ CYAN,
+ YELLOW,
+ MAGENTA,
+ ALL_PRIMARY
+};
+
+enum
+{
+ DOWN = -1,
+ UP = 1
+};
+
+typedef struct
+{
+ GtkWidget *window;
+ GtkWidget *range_preview;
+ GtkWidget *aliasing_preview;
+ GtkWidget *aliasing_graph;
+} AdvancedWindow;
+
+
+typedef struct
+{
+ gdouble roughness;
+ gdouble aliasing;
+ gdouble preview_size;
+ FPIntensity intensity_range;
+ gint value_by;
+ gint selection_only;
+ guchar offset;
+ guchar visible_frames;
+ guchar cutoff[INTENSITIES];
+ gboolean touched[JUDGE_BY];
+ gint red_adjust[JUDGE_BY][256];
+ gint blue_adjust[JUDGE_BY][256];
+ gint green_adjust[JUDGE_BY][256];
+ gint sat_adjust[JUDGE_BY][256];
+} FPValues;
+
+typedef struct
+{
+ GtkWidget *roughness_scale;
+ GtkWidget *aliasing_scale;
+ GtkWidget *preview_size_scale;
+ GtkWidget *range_label[12];
+} FPWidgets;
+
+static void fp_show_hide_frame (GtkWidget *button,
+ GtkWidget *frame);
+
+static ReducedImage * fp_reduce_image (GimpDrawable *drawable,
+ GimpDrawable *mask,
+ gint longer_size,
+ gint selection);
+
+static void fp_render_preview (GtkWidget *preview,
+ gint change_what,
+ gint change_which);
+
+static void update_current_fp (gint change_what,
+ gint change_which);
+
+static void fp_create_nudge (gint *adj_array);
+
+static gboolean fp_dialog (void);
+static void fp_advanced_dialog (GtkWidget *parent);
+
+static void fp_selection_made (GtkWidget *widget,
+ gpointer data);
+static void fp_scale_update (GtkAdjustment *adjustment,
+ gdouble *scale_val);
+static void fp_reset_filter_packs (void);
+
+static void fp_create_smoothness_graph (GtkWidget *preview);
+
+static void fp_range_preview_spill (GtkWidget *preview,
+ gint type);
+static void fp_adjust_preview_sizes (gint width,
+ gint height);
+static void fp_redraw_all_windows (void);
+static void fp_refresh_previews (gint which);
+static void fp_init_filter_packs (void);
+
+static void fp_preview_scale_update (GtkAdjustment *adjustment,
+ gdouble *scale_val);
+
+static void fp (GimpDrawable *drawable);
+static GtkWidget * fp_create_bna (void);
+static GtkWidget * fp_create_rough (void);
+static GtkWidget * fp_create_range (void);
+static GtkWidget * fp_create_circle_palette (GtkWidget *parent);
+static GtkWidget * fp_create_lnd (GtkWidget *parent);
+static GtkWidget * fp_create_show (void);
+static GtkWidget * fp_create_msnls (GtkWidget *parent);
+static GtkWidget * fp_create_pixels_select_by (void);
+static void update_range_labels (void);
+static gboolean fp_range_change_events (GtkWidget *widget,
+ GdkEvent *event,
+ FPValues *current);
+
+static void fp_create_preview (GtkWidget **preview,
+ GtkWidget **frame,
+ gint preview_width,
+ gint preview_height);
+
+static void fp_create_table_entry (GtkWidget **box,
+ GtkWidget *smaller_frame,
+ const gchar *description);
+
+static void fp_frames_checkbutton_in_box (GtkWidget *vbox,
+ const gchar *label,
+ GCallback func,
+ GtkWidget *frame,
+ gboolean clicked);
+
+static void fp_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+
+#define RESPONSE_RESET 1
+
+/* These values are translated for the GUI but also used internally
+ to figure out which button the user pushed, etc.
+ Not my design, please don't blame me -- njl */
+
+static const gchar *hue_red = N_("Red:");
+static const gchar *hue_green = N_("Green:");
+static const gchar *hue_blue = N_("Blue:");
+static const gchar *hue_cyan = N_("Cyan:");
+static const gchar *hue_yellow = N_("Yellow:");
+static const gchar *hue_magenta = N_("Magenta:");
+
+static const gchar *val_darker = N_("Darker:");
+static const gchar *val_lighter = N_("Lighter:");
+
+static const gchar *sat_more = N_("More Sat:");
+static const gchar *sat_less = N_("Less Sat:");
+
+static const gchar *current_val = N_("Current:");
+
+static const gint colorSign[3][ALL_PRIMARY]=
+{{1,-1,-1,-1,1,1},{-1,1,-1,1,1,-1},{-1,-1,1,1,-1,1}};
+
+static AdvancedWindow AW = { NULL, NULL, NULL, NULL };
+
+static FPWidgets fp_widgets = { NULL, NULL, NULL };
+
+static gint nudgeArray[256];
+
+static GtkWidget *origPreview, *curPreview;
+static GtkWidget *rPreview, *gPreview, *bPreview;
+static GtkWidget *cPreview, *yPreview, *mPreview;
+static GtkWidget *centerPreview;
+static GtkWidget *darkerPreview, *lighterPreview, *middlePreview;
+static GtkWidget *dlg;
+static GtkWidget *plusSatPreview, *SatPreview, *minusSatPreview;
+
+static struct
+{
+ GtkWidget *bna;
+ GtkWidget *palette;
+ GtkWidget *rough;
+ GtkWidget *range;
+ GtkWidget *show;
+ GtkWidget *lnd;
+ GtkWidget *pixelsBy;
+ GtkWidget *frameSelect;
+ GtkWidget *satur;
+} fp_frames;
+
+static fpInterface FPint =
+{
+ FALSE /* run */
+};
+
+static ReducedImage *reduced;
+
+static FPValues fpvals =
+{
+ .25, /* Initial Roughness */
+ .6, /* Initial Degree of Aliasing */
+ 80, /* Initial preview size */
+ MIDTONES, /* Initial Range */
+ BY_VAL, /* Initial God knows what */
+ TRUE, /* Selection Only */
+ 0, /* Offset */
+ 0, /* Visible frames */
+ { 32, 224, 255 }, /* cutoffs */
+ { FALSE, FALSE, FALSE } /* touched */
+};
+
+static GimpDrawable *drawable = NULL;
+static GimpDrawable *mask = NULL;
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN()
+
+static void
+query (void)
+{
+ GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (used for indexed images)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Interactively modify the image colors"),
+ "Interactively modify the image colors.",
+ "Pavel Grinfeld (pavel@ml.com)",
+ "Pavel Grinfeld (pavel@ml.com)",
+ "27th March 1997",
+ N_("_Filter Pack..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+/********************************STANDARD RUN*************************/
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ fp_init_filter_packs();
+
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ if (gimp_selection_is_empty (param[1].data.d_image))
+ mask = NULL;
+ else
+ mask = gimp_drawable_get (gimp_image_get_selection (param[1].data.d_image));
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &fpvals);
+
+ if (gimp_drawable_is_indexed (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id) )
+ {
+ gimp_message (_("FP can only be used on RGB images."));
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else if (! fp_dialog())
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ gimp_message (_("FP can only be run interactively."));
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &fpvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id))
+ {
+ gimp_progress_init (_("Applying filter pack"));
+ gimp_tile_cache_ntiles (2 * (drawable->width /
+ gimp_tile_width () + 1));
+ fp (drawable);
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &fpvals, sizeof (FPValues));
+
+ gimp_displays_flush ();
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ gimp_drawable_detach (drawable);
+ if (mask)
+ gimp_drawable_detach (mask);
+
+ values[0].data.d_status = status;
+}
+
+static void
+fp_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ gint bytenum, k;
+ gint JudgeBy, Intensity = 0, P[3];
+ GimpRGB rgb;
+ GimpHSV hsv;
+ gint M, m, middle;
+
+ P[0] = src[0];
+ P[1] = src[1];
+ P[2] = src[2];
+
+ gimp_rgb_set_uchar (&rgb, (guchar) P[0], (guchar) P[1], (guchar) P[2]);
+ gimp_rgb_to_hsv (&rgb, &hsv);
+
+ for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++)
+ {
+ if (!fpvals.touched[JudgeBy])
+ continue;
+
+ switch (JudgeBy)
+ {
+ case BY_HUE:
+ Intensity = 255 * hsv.h;
+ break;
+
+ case BY_SAT:
+ Intensity = 255 * hsv.s;
+ break;
+
+ case BY_VAL:
+ Intensity = 255 * hsv.v;
+ break;
+ }
+
+
+ /* It's important to take care of Saturation first!!! */
+
+ m = MIN (MIN (P[0], P[1]), P[2]);
+ M = MAX (MAX (P[0], P[1]), P[2]);
+ middle = (M + m) / 2;
+
+ for (k = 0; k < 3; k++)
+ if (P[k] != m && P[k] != M)
+ middle = P[k];
+
+ for (k = 0; k < 3; k++)
+ if (M != m)
+ {
+ if (P[k] == M)
+ P[k] = MAX (P[k] + fpvals.sat_adjust[JudgeBy][Intensity], middle);
+ else if (P[k] == m)
+ P[k] = MIN (P[k] - fpvals.sat_adjust[JudgeBy][Intensity], middle);
+ }
+
+ P[0] += fpvals.red_adjust[JudgeBy][Intensity];
+ P[1] += fpvals.green_adjust[JudgeBy][Intensity];
+ P[2] += fpvals.blue_adjust[JudgeBy][Intensity];
+
+ P[0] = CLAMP0255(P[0]);
+ P[1] = CLAMP0255(P[1]);
+ P[2] = CLAMP0255(P[2]);
+ }
+
+ dest[0] = P[0];
+ dest[1] = P[1];
+ dest[2] = P[2];
+
+ for (bytenum = 3; bytenum < bpp; bytenum++)
+ dest[bytenum] = src[bytenum];
+}
+
+static void
+fp (GimpDrawable *drawable)
+{
+ GimpPixelRgn srcPR, destPR;
+ gint x1, y1, x2, y2;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ g_return_if_fail (drawable != NULL);
+
+ gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ return;
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR.data;
+ guchar *dest = destPR.data;
+ gint row;
+
+ for (row = 0; row < srcPR.h; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint pixels = srcPR.w;
+
+ while (pixels--)
+ {
+ fp_func (s, d, srcPR.bpp, NULL);
+
+ s += srcPR.bpp;
+ d += destPR.bpp;
+ }
+
+ src += srcPR.rowstride;
+ dest += destPR.rowstride;
+ }
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ /* update the processed region */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+}
+
+/***********************************************************/
+/************ Main Dialog Window ******************/
+/***********************************************************/
+
+static GtkWidget *
+fp_create_bna (void)
+{
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *bframe, *aframe;
+
+ fp_create_preview (&origPreview, &bframe, reduced->width, reduced->height);
+ fp_create_preview (&curPreview, &aframe, reduced->width, reduced->height);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+
+ label = gtk_label_new (_("Original:"));
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (label);
+
+ gtk_table_attach (GTK_TABLE (table), bframe, 0, 1, 1, 2,
+ GTK_EXPAND, 0, 0, 0);
+
+ label = gtk_label_new (_("Current:"));
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (label);
+
+ gtk_table_attach (GTK_TABLE (table), aframe, 1, 2, 1, 2,
+ GTK_EXPAND, 0, 0, 0);
+
+ gtk_widget_show (table);
+
+ return table;
+}
+
+/* close a sub dialog (from window manager) by simulating toggle click */
+static gboolean
+sub_dialog_destroy (GtkWidget *dialog,
+ GdkEvent *ev,
+ gpointer dummy)
+{
+ GtkWidget *button =
+ GTK_WIDGET (g_object_get_data (G_OBJECT (dialog), "ctrlButton"));
+
+ gtk_button_clicked (GTK_BUTTON (button));
+
+ return TRUE;
+}
+
+static GtkWidget *
+fp_create_circle_palette (GtkWidget *parent)
+{
+ GtkWidget *table;
+ GtkWidget *rVbox, *rFrame;
+ GtkWidget *gVbox, *gFrame;
+ GtkWidget *bVbox, *bFrame;
+ GtkWidget *cVbox, *cFrame;
+ GtkWidget *yVbox, *yFrame;
+ GtkWidget *mVbox, *mFrame;
+ GtkWidget *centerVbox, *centerFrame;
+ GtkWidget *win;
+
+ win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ gtk_window_set_title (GTK_WINDOW (win), _("Hue Variations"));
+ gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent));
+
+ g_signal_connect (win, "delete-event",
+ G_CALLBACK (sub_dialog_destroy),
+ NULL);
+
+ table = gtk_table_new (11, 11, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_container_add (GTK_CONTAINER (win), table);
+ gtk_widget_show (table);
+
+ fp_create_preview (&rPreview, &rFrame, reduced->width, reduced->height);
+ fp_create_preview (&gPreview, &gFrame, reduced->width, reduced->height);
+ fp_create_preview (&bPreview, &bFrame, reduced->width, reduced->height);
+ fp_create_preview (&cPreview, &cFrame, reduced->width, reduced->height);
+ fp_create_preview (&yPreview, &yFrame, reduced->width, reduced->height);
+ fp_create_preview (&mPreview, &mFrame, reduced->width, reduced->height);
+ fp_create_preview (&centerPreview, &centerFrame,
+ reduced->width, reduced->height);
+
+ fp_create_table_entry (&rVbox, rFrame, hue_red);
+ fp_create_table_entry (&gVbox, gFrame, hue_green);
+ fp_create_table_entry (&bVbox, bFrame, hue_blue);
+ fp_create_table_entry (&cVbox, cFrame, hue_cyan);
+ fp_create_table_entry (&yVbox, yFrame, hue_yellow);
+ fp_create_table_entry (&mVbox, mFrame, hue_magenta);
+ fp_create_table_entry (&centerVbox, centerFrame, current_val);
+
+ gtk_table_attach (GTK_TABLE (table), rVbox, 8, 11 ,4 , 7,
+ GTK_EXPAND , GTK_EXPAND, 0 ,0);
+ gtk_table_attach (GTK_TABLE (table), gVbox, 2, 5, 0, 3,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), bVbox, 2, 5, 8, 11,
+ GTK_EXPAND, GTK_EXPAND,0,0);
+ gtk_table_attach (GTK_TABLE (table), cVbox, 0, 3, 4, 7,
+ GTK_EXPAND, GTK_EXPAND, 0 ,0);
+ gtk_table_attach (GTK_TABLE (table), yVbox, 6, 9, 0, 3,
+ GTK_EXPAND, GTK_EXPAND, 0 ,0);
+ gtk_table_attach (GTK_TABLE (table), mVbox, 6, 9, 8, 11,
+ GTK_EXPAND, GTK_EXPAND, 0 ,0);
+ gtk_table_attach (GTK_TABLE (table), centerVbox, 4, 7, 4, 7,
+ GTK_EXPAND, GTK_EXPAND, 0 ,0);
+
+ return win;
+}
+
+static GtkWidget *
+fp_create_rough (void)
+{
+ GtkWidget *frame, *vbox, *scale;
+ GtkAdjustment *data;
+
+ frame = gimp_frame_new (_("Roughness"));
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ data = (GtkAdjustment *)
+ gtk_adjustment_new (fpvals.roughness, 0, 1.0, 0.05, 0.01, 0.0);
+ fp_widgets.roughness_scale = scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL,
+ data);
+
+ gtk_widget_set_size_request (scale, 60, -1);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_scale_set_digits (GTK_SCALE (scale), 2);
+ gtk_widget_show (scale);
+
+ g_signal_connect (data, "value-changed",
+ G_CALLBACK (fp_scale_update),
+ &fpvals.roughness);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+
+ return frame;
+}
+
+static void
+fp_change_current_range (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_radio_button_update (widget, data);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ fp_refresh_previews (fpvals.visible_frames);
+ if (AW.window && gtk_widget_get_visible (AW.window))
+ fp_create_smoothness_graph (AW.aliasing_preview);
+ }
+}
+
+static GtkWidget *
+fp_create_range (void)
+{
+ GtkWidget *frame;
+
+ frame = gimp_int_radio_group_new (TRUE, _("Affected Range"),
+ G_CALLBACK (fp_change_current_range),
+ &fpvals.intensity_range, fpvals.intensity_range,
+
+ _("Sha_dows"), SHADOWS, NULL,
+ _("_Midtones"), MIDTONES, NULL,
+ _("H_ighlights"), HIGHLIGHTS, NULL,
+
+ NULL);
+
+ gtk_widget_show (frame);
+
+ return frame;
+}
+
+static GtkWidget *
+fp_create_control (void)
+{
+ GtkWidget *frame, *box;
+
+ frame = gimp_frame_new (_("Windows"));
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_widget_show (box);
+
+ fp_frames_checkbutton_in_box (box, _("_Hue"),
+ G_CALLBACK (fp_show_hide_frame),
+ fp_frames.palette,
+ fpvals.visible_frames & HUE);
+ fp_frames_checkbutton_in_box (box, _("_Saturation"),
+ G_CALLBACK (fp_show_hide_frame),
+ fp_frames.satur,
+ fpvals.visible_frames & SATURATION);
+ fp_frames_checkbutton_in_box (box, _("_Value"),
+ G_CALLBACK (fp_show_hide_frame),
+ fp_frames.lnd,
+ fpvals.visible_frames & VALUE);
+ fp_frames_checkbutton_in_box (box, _("A_dvanced"),
+ G_CALLBACK (fp_show_hide_frame),
+ AW.window,
+ FALSE);
+ gtk_widget_show (frame);
+
+ return frame;
+}
+
+static GtkWidget *
+fp_create_lnd (GtkWidget *parent)
+{
+ GtkWidget *table, *lighterFrame, *middleFrame, *darkerFrame;
+ GtkWidget *lighterVbox, *middleVbox, *darkerVbox;
+ GtkWidget *win;
+
+ win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ gtk_window_set_title (GTK_WINDOW (win), _("Value Variations"));
+ gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent));
+
+ g_signal_connect (win, "delete-event",
+ G_CALLBACK (sub_dialog_destroy),
+ NULL);
+
+ fp_create_preview (&lighterPreview, &lighterFrame,
+ reduced->width, reduced->height);
+ fp_create_preview (&middlePreview, &middleFrame,
+ reduced->width, reduced->height);
+ fp_create_preview (&darkerPreview, &darkerFrame,
+ reduced->width, reduced->height);
+
+ fp_create_table_entry (&lighterVbox, lighterFrame, val_lighter);
+ fp_create_table_entry (&middleVbox, middleFrame, current_val);
+ fp_create_table_entry (&darkerVbox, darkerFrame, val_darker);
+
+ table = gtk_table_new (1, 11, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_container_add (GTK_CONTAINER (win), table);
+ gtk_widget_show (table);
+
+ gtk_table_attach (GTK_TABLE (table), lighterVbox, 0, 3, 0, 1,
+ GTK_EXPAND , GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), middleVbox, 4, 7, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), darkerVbox, 8, 11, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+
+ return win;
+}
+
+static GtkWidget *
+fp_create_msnls (GtkWidget *parent)
+{
+ GtkWidget *table, *lessFrame, *middleFrame, *moreFrame;
+ GtkWidget *lessVbox, *middleVbox, *moreVbox;
+ GtkWidget *win;
+
+ win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gimp_help_connect (win, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ gtk_window_set_title (GTK_WINDOW (win), _("Saturation Variations"));
+ gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (parent));
+
+ g_signal_connect (win, "delete-event",
+ G_CALLBACK (sub_dialog_destroy),
+ NULL);
+
+ fp_create_preview (&minusSatPreview, &lessFrame,
+ reduced->width, reduced->height);
+ fp_create_preview (&SatPreview, &middleFrame,
+ reduced->width, reduced->height);
+ fp_create_preview (&plusSatPreview, &moreFrame,
+ reduced->width, reduced->height);
+
+ fp_create_table_entry (&moreVbox, moreFrame, sat_more);
+ fp_create_table_entry (&middleVbox, middleFrame, current_val);
+ fp_create_table_entry (&lessVbox, lessFrame, sat_less);
+
+ table = gtk_table_new (1, 11, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_container_add (GTK_CONTAINER (win), table);
+ gtk_widget_show (table);
+
+ gtk_table_attach (GTK_TABLE (table), moreVbox, 0, 3, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), middleVbox, 4, 7, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), lessVbox, 8, 11, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+
+ return win;
+}
+
+static void
+fp_change_current_pixels_by (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_radio_button_update (widget, data);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ fp_refresh_previews (fpvals.visible_frames);
+ if (AW.window && gtk_widget_get_visible (AW.window) && AW.range_preview)
+ fp_range_preview_spill (AW.range_preview,fpvals.value_by);
+ }
+}
+
+static GtkWidget *
+fp_create_pixels_select_by (void)
+{
+ GtkWidget *frame;
+
+ frame = gimp_int_radio_group_new (TRUE, _("Select Pixels By"),
+ G_CALLBACK (fp_change_current_pixels_by),
+ &fpvals.value_by,
+ fpvals.value_by,
+
+ _("H_ue"), 0, NULL,
+ _("Satu_ration"), 1, NULL,
+ _("V_alue"), 2, NULL,
+
+ NULL);
+
+ gtk_widget_show (frame);
+
+ return frame;
+}
+
+static void
+fp_change_selection (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_radio_button_update (widget, data);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ fp_redraw_all_windows ();
+ }
+}
+
+static GtkWidget *
+fp_create_show (void)
+{
+ GtkWidget *frame;
+
+ frame = gimp_int_radio_group_new (TRUE, _("Show"),
+ G_CALLBACK (fp_change_selection),
+ &fpvals.selection_only,
+ fpvals.selection_only,
+
+ _("_Entire image"), 0, NULL,
+ _("Se_lection only"), 1, NULL,
+ _("Selec_tion in context"), 2, NULL,
+
+ NULL);
+
+ gtk_widget_show (frame);
+
+ return frame;
+}
+
+static void
+fp_create_preview (GtkWidget **preview,
+ GtkWidget **frame,
+ gint preview_width,
+ gint preview_height)
+{
+ *frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (*frame), GTK_SHADOW_IN);
+ gtk_widget_show (*frame);
+
+ *preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (*preview, preview_width, preview_height);
+ g_signal_connect (*preview, "size-allocate",
+ G_CALLBACK (fp_preview_size_allocate), NULL);
+ gtk_widget_show (*preview);
+ gtk_container_add (GTK_CONTAINER (*frame), *preview);
+}
+
+static void
+fp_frames_checkbutton_in_box (GtkWidget *vbox,
+ const gchar *label,
+ GCallback function,
+ GtkWidget *frame,
+ gboolean clicked)
+{
+ GtkWidget *button;
+
+ button = gtk_check_button_new_with_mnemonic (label);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT (frame), "ctrlButton", (gpointer) button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ function,
+ frame);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), clicked);
+}
+
+static void
+fp_create_table_entry (GtkWidget **box,
+ GtkWidget *smaller_frame,
+ const gchar *description)
+{
+ GtkWidget *label;
+ GtkWidget *button;
+ GtkWidget *table;
+
+ *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ gtk_container_set_border_width (GTK_CONTAINER (*box), PR_BX_BRDR);
+ gtk_widget_show (*box);
+
+ /* Delayed translation applied here */
+ label = gtk_label_new (gettext (description));
+
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+
+ table = gtk_table_new (2, 1, FALSE);
+ gtk_widget_show (table);
+
+ gtk_box_pack_start (GTK_BOX (*box), table, TRUE, TRUE, 0);
+
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ 0, 0, 0, 0);
+
+ if (description != current_val)
+ {
+ button = gtk_button_new ();
+ gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2,
+ 0, 0, 0, 4);
+ gtk_widget_show (button);
+
+ gtk_container_add (GTK_CONTAINER (button), smaller_frame);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (fp_selection_made),
+ (gchar *) description);
+ }
+ else
+ {
+ gtk_table_attach (GTK_TABLE (table), smaller_frame, 0, 1, 1, 2,
+ 0, 0, 0, 4);
+ }
+}
+
+static void
+fp_redraw_all_windows (void)
+{
+ if (reduced)
+ {
+ g_free (reduced->rgb);
+ g_free (reduced->hsv);
+ g_free (reduced->mask);
+
+ g_free (reduced);
+ }
+
+ reduced = fp_reduce_image (drawable, mask,
+ fpvals.preview_size,
+ fpvals.selection_only);
+
+ fp_adjust_preview_sizes (reduced->width, reduced->height);
+
+ gtk_widget_queue_draw (fp_frames.palette);
+ gtk_widget_queue_draw (fp_frames.satur);
+ gtk_widget_queue_draw (fp_frames.lnd);
+ gtk_widget_queue_draw (dlg);
+
+ fp_refresh_previews (fpvals.visible_frames);
+}
+
+static void
+fp_show_hide_frame (GtkWidget *button,
+ GtkWidget *frame)
+{
+ gint prev = fpvals.visible_frames;
+
+ if (frame == NULL)
+ return;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+ {
+ if (! gtk_widget_get_visible (frame))
+ {
+ gtk_widget_show (frame);
+
+ if (frame==fp_frames.palette)
+ fpvals.visible_frames |= HUE;
+ else if (frame==fp_frames.satur)
+ fpvals.visible_frames |= SATURATION;
+ else if (frame==fp_frames.lnd)
+ fpvals.visible_frames |= VALUE;
+
+ fp_refresh_previews (fpvals.visible_frames & ~prev);
+ fp_create_smoothness_graph (AW.aliasing_preview);
+ fp_range_preview_spill (AW.range_preview,fpvals.value_by);
+ }
+ }
+ else
+ {
+ if (gtk_widget_get_visible (frame))
+ {
+ gtk_widget_hide (frame);
+
+ if (frame==fp_frames.palette)
+ fpvals.visible_frames &= ~HUE;
+ else if (frame==fp_frames.satur)
+ fpvals.visible_frames &= ~SATURATION;
+ else if (frame==fp_frames.lnd)
+ fpvals.visible_frames &= ~VALUE;
+ }
+ }
+}
+
+static void
+fp_adjust_preview_sizes (gint width,
+ gint height)
+{
+ gtk_widget_set_size_request (origPreview, width, height);
+ gtk_widget_set_size_request (curPreview, width, height);
+ gtk_widget_set_size_request (rPreview, width, height);
+ gtk_widget_set_size_request (gPreview, width, height);
+ gtk_widget_set_size_request (bPreview, width, height);
+ gtk_widget_set_size_request (cPreview, width, height);
+ gtk_widget_set_size_request (yPreview, width, height);
+ gtk_widget_set_size_request (mPreview, width, height);
+ gtk_widget_set_size_request (centerPreview, width, height);
+ gtk_widget_set_size_request (lighterPreview, width, height);
+ gtk_widget_set_size_request (darkerPreview, width, height);
+ gtk_widget_set_size_request (middlePreview, width, height);
+ gtk_widget_set_size_request (minusSatPreview, width, height);
+ gtk_widget_set_size_request (SatPreview, width, height);
+ gtk_widget_set_size_request (plusSatPreview, width, height);
+
+}
+
+static void
+fp_selection_made (GtkWidget *widget,
+ gpointer data)
+{
+ fpvals.touched[fpvals.value_by] = TRUE;
+
+ if (data == (gpointer) hue_red)
+ {
+ update_current_fp (HUE, RED);
+ }
+ else if (data == (gpointer) hue_green)
+ {
+ update_current_fp (HUE, GREEN);
+ }
+ else if (data == (gpointer) hue_blue)
+ {
+ update_current_fp (HUE, BLUE);
+ }
+ else if (data == (gpointer) hue_cyan)
+ {
+ update_current_fp (HUE, CYAN);
+ }
+ else if (data == (gpointer) hue_yellow)
+ {
+ update_current_fp (HUE, YELLOW);
+ }
+ else if (data == (gpointer) hue_magenta)
+ {
+ update_current_fp (HUE, MAGENTA);
+ }
+ else if (data == (gpointer) val_darker)
+ {
+ update_current_fp (VALUE, DOWN);
+ }
+ else if (data == (gpointer) val_lighter)
+ {
+ update_current_fp (VALUE, UP);
+ }
+ else if (data == (gpointer) sat_more)
+ {
+ update_current_fp (SATURATION, UP);
+ }
+ else if (data == (gpointer) sat_less)
+ {
+ update_current_fp (SATURATION, DOWN);
+ }
+
+ fp_refresh_previews (fpvals.visible_frames);
+}
+
+static void
+fp_refresh_previews (gint which)
+{
+ fp_create_nudge (nudgeArray);
+ fp_render_preview (origPreview, NONEATALL, 0);
+ fp_render_preview (curPreview, CURRENT, 0);
+
+ if (which & HUE)
+ {
+ fp_render_preview (rPreview, HUE, RED);
+ fp_render_preview (gPreview, HUE, GREEN);
+ fp_render_preview (bPreview, HUE, BLUE);
+ fp_render_preview (cPreview, HUE, CYAN);
+ fp_render_preview (yPreview, HUE, YELLOW);
+ fp_render_preview (mPreview, HUE, MAGENTA);
+ fp_render_preview (centerPreview, CURRENT, 0);
+ }
+
+ if (which & VALUE)
+ {
+ fp_render_preview (lighterPreview, VALUE, UP);
+ fp_render_preview (middlePreview, CURRENT, 0);
+ fp_render_preview (darkerPreview, VALUE, DOWN);
+ }
+
+ if (which & SATURATION)
+ {
+ fp_render_preview (plusSatPreview, SATURATION, UP);
+ fp_render_preview (SatPreview, CURRENT, 0);
+ fp_render_preview (minusSatPreview, SATURATION, DOWN);
+ }
+}
+
+static void
+fp_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ fp_reset_filter_packs ();
+ break;
+
+ case GTK_RESPONSE_OK:
+ FPint.run = TRUE;
+ gtk_widget_destroy (widget);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+fp_scale_update (GtkAdjustment *adjustment,
+ gdouble *scale_val)
+{
+ static gdouble prevValue = 0.25;
+
+ *scale_val = gtk_adjustment_get_value (adjustment);
+
+ if (prevValue != gtk_adjustment_get_value (adjustment))
+ {
+ fp_create_nudge (nudgeArray);
+ fp_refresh_previews (fpvals.visible_frames);
+
+ if (AW.window != NULL && gtk_widget_get_visible (AW.window))
+ fp_create_smoothness_graph (AW.aliasing_preview);
+
+ prevValue = gtk_adjustment_get_value (adjustment);
+ }
+}
+
+static gboolean
+fp_dialog (void)
+{
+ GtkWidget *bna;
+ GtkWidget *palette;
+ GtkWidget *lnd;
+ GtkWidget *show;
+ GtkWidget *rough;
+ GtkWidget *range;
+ GtkWidget *pixelsBy;
+ GtkWidget *satur;
+ GtkWidget *control;
+ GtkWidget *table;
+
+ reduced = fp_reduce_image (drawable, mask,
+ fpvals.preview_size,
+ fpvals.selection_only);
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dlg = gimp_dialog_new (_("Filter Pack Simulation"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (fp_response),
+ dlg);
+
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ fp_advanced_dialog (dlg);
+
+ fp_frames.bna = bna = fp_create_bna ();
+ fp_frames.rough = rough = fp_create_rough ();
+ fp_frames.range = range = fp_create_range ();
+ fp_frames.palette = palette = fp_create_circle_palette (dlg);
+ fp_frames.lnd = lnd = fp_create_lnd (dlg);
+ fp_frames.show = show = fp_create_show ();
+ fp_frames.satur = satur = fp_create_msnls (dlg);
+ fp_frames.pixelsBy = pixelsBy = fp_create_pixels_select_by ();
+ control = fp_create_control ();
+ /********************************************************************/
+ /******************** PUT EVERYTHING TOGETHER ******************/
+
+ table = gtk_table_new (4, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ gtk_table_attach (GTK_TABLE (table), bna, 0, 2, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), control, 1, 2, 1, 3,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), rough, 1, 2, 3, 4,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), show, 0, 1, 1, 2,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), range, 0, 1, 2, 3,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+
+ gtk_table_attach (GTK_TABLE (table), pixelsBy, 0, 1, 3, 4,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+
+ gtk_widget_show (dlg);
+
+ fp_refresh_previews (fpvals.visible_frames);
+
+ gtk_main ();
+
+ return FPint.run;
+}
+
+/***********************************************************/
+/************ Advanced Options Window ******************/
+/***********************************************************/
+
+static void
+fp_preview_scale_update (GtkAdjustment *adjustment,
+ gdouble *scale_val)
+{
+ fpvals.preview_size = gtk_adjustment_get_value (adjustment);
+ fp_redraw_all_windows();
+}
+
+static void
+fp_advanced_dialog (GtkWidget *parent)
+{
+ const gchar *rangeNames[] = { N_("Shadows:"),
+ N_("Midtones:"),
+ N_("Highlights:") };
+ GtkWidget *frame, *hbox;
+ GtkAdjustment *smoothnessData;
+ GtkWidget *graphFrame, *scale;
+ GtkWidget *vbox, *label, *labelTable, *alignment;
+ GtkWidget *inner_vbox, *innermost_vbox;
+ gint i;
+
+ AW.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+ gimp_help_connect (AW.window, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ gtk_window_set_title (GTK_WINDOW (AW.window),
+ _("Advanced Filter Pack Options"));
+ gtk_window_set_transient_for (GTK_WINDOW (AW.window), GTK_WINDOW (parent));
+
+ g_signal_connect (AW.window, "delete-event",
+ G_CALLBACK (sub_dialog_destroy),
+ NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_container_add (GTK_CONTAINER (AW.window), hbox);
+ gtk_widget_show (hbox);
+
+ frame = gimp_frame_new (_("Affected Range"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ graphFrame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1, TRUE);
+ gtk_frame_set_shadow_type (GTK_FRAME (graphFrame), GTK_SHADOW_IN);
+ gtk_container_set_border_width (GTK_CONTAINER (graphFrame), 0);
+ gtk_box_pack_start (GTK_BOX (vbox), graphFrame, FALSE, FALSE, 0);
+ gtk_widget_show (graphFrame);
+
+ inner_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (graphFrame), inner_vbox);
+ gtk_widget_show (inner_vbox);
+
+ alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (inner_vbox), alignment, TRUE, TRUE, 0);
+ gtk_widget_show (alignment);
+
+ innermost_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (alignment), innermost_vbox);
+ gtk_widget_show (innermost_vbox);
+
+ AW.aliasing_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (AW.aliasing_preview, 256, MAX_ROUGHNESS);
+ gtk_box_pack_start (GTK_BOX (innermost_vbox),
+ AW.aliasing_preview, TRUE, TRUE, 0);
+ gtk_widget_show (AW.aliasing_preview);
+
+ fp_create_smoothness_graph (AW.aliasing_preview);
+
+ AW.range_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (AW.range_preview, 256, RANGE_HEIGHT);
+ gtk_box_pack_start(GTK_BOX (innermost_vbox),
+ AW.range_preview, TRUE, TRUE, 0);
+ gtk_widget_show (AW.range_preview);
+
+ fp_range_preview_spill (AW.range_preview, fpvals.value_by);
+
+ labelTable = gtk_table_new (3, 4, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (labelTable), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (labelTable), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), labelTable, FALSE, FALSE, 0);
+ gtk_widget_show (labelTable);
+
+ /************************************************************/
+
+ AW.aliasing_graph = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (AW.aliasing_graph,
+ 2 * MARGIN + 256,
+ RANGE_HEIGHT);
+ gtk_box_pack_start (GTK_BOX (inner_vbox), AW.aliasing_graph, TRUE, TRUE, 0);
+ gtk_widget_show (AW.aliasing_graph);
+ gtk_widget_set_events (AW.aliasing_graph, RANGE_ADJUST_MASK);
+
+ g_signal_connect (AW.aliasing_graph, "event",
+ G_CALLBACK (fp_range_change_events),
+ &fpvals);
+
+ /************************************************************/
+
+ for (i = 0; i < 12; i++)
+ {
+ label = fp_widgets.range_label[i] = gtk_label_new ("-");
+
+ if (!(i % 4))
+ {
+ gtk_label_set_text (GTK_LABEL(label), gettext (rangeNames[i/4]));
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
+ -1);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 1.0);
+ }
+
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (labelTable), label, i%4, i%4+1, i/4, i/4+1,
+ GTK_EXPAND | GTK_FILL, 0, 0, 0);
+ }
+
+ smoothnessData = (GtkAdjustment *)
+ gtk_adjustment_new (fpvals.aliasing,
+ 0, 1.0, 0.05, 0.01, 0.0);
+
+ fp_widgets.aliasing_scale = scale =
+ gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, smoothnessData);
+ gtk_widget_set_size_request (scale, 200, -1);
+ gtk_scale_set_digits (GTK_SCALE (scale), 2);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_show (scale);
+
+ g_signal_connect (smoothnessData, "value-changed",
+ G_CALLBACK (fp_scale_update),
+ &fpvals.aliasing);
+
+ /******************* MISC OPTIONS ***************************/
+
+ frame = gimp_frame_new (_("Preview Size"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ smoothnessData = (GtkAdjustment *)
+ gtk_adjustment_new (fpvals.preview_size,
+ 50, MAX_PREVIEW_SIZE,
+ 5, 5, 0.0);
+
+ fp_widgets.preview_size_scale = scale =
+ gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, smoothnessData);
+ gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+ gtk_widget_set_size_request (scale, 100, -1);
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ gtk_widget_show (scale);
+
+ g_signal_connect (smoothnessData, "value-changed",
+ G_CALLBACK (fp_preview_scale_update),
+ &fpvals.preview_size);
+
+ update_range_labels ();
+}
+
+static void
+slider_erase (GdkWindow *window,
+ int xpos)
+{
+ gdk_window_clear_area (window, MARGIN + xpos - (RANGE_HEIGHT - 1) / 2, 0,
+ RANGE_HEIGHT, RANGE_HEIGHT);
+}
+
+static void
+draw_slider (cairo_t *cr,
+ GdkColor *border_color,
+ GdkColor *fill_color,
+ gint xpos)
+{
+ cairo_move_to (cr, MARGIN + xpos, 0);
+ cairo_line_to (cr, MARGIN + xpos - (RANGE_HEIGHT - 1) / 2, RANGE_HEIGHT - 1);
+ cairo_line_to (cr, MARGIN + xpos + (RANGE_HEIGHT - 1) / 2, RANGE_HEIGHT - 1);
+ cairo_line_to (cr, MARGIN + xpos, 0);
+
+ gdk_cairo_set_source_color (cr, fill_color);
+ cairo_fill_preserve (cr);
+
+ gdk_cairo_set_source_color (cr, border_color);
+ cairo_stroke (cr);
+}
+
+static void
+draw_it (GtkWidget *widget)
+{
+ GtkStyle *style = gtk_widget_get_style (AW.aliasing_graph);
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (AW.aliasing_graph));
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 1.0);
+
+ draw_slider (cr,
+ &style->black,
+ &style->dark[GTK_STATE_NORMAL],
+ fpvals.cutoff[SHADOWS]);
+
+ draw_slider (cr,
+ &style->black,
+ &style->dark[GTK_STATE_NORMAL],
+ fpvals.cutoff[MIDTONES]);
+
+ draw_slider (cr,
+ &style->black,
+ &style->dark[GTK_STATE_SELECTED],
+ fpvals.offset);
+
+ cairo_destroy (cr);
+}
+
+static gboolean
+fp_range_change_events (GtkWidget *widget,
+ GdkEvent *event,
+ FPValues *current)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gint shad, mid, offset, min;
+ static guchar *new;
+ gint x;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ draw_it (NULL);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ bevent= (GdkEventButton *) event;
+
+ shad = abs (bevent->x - fpvals.cutoff[SHADOWS]);
+ mid = abs (bevent->x - fpvals.cutoff[MIDTONES]);
+ offset = abs (bevent->x - fpvals.offset);
+
+ min = MIN (MIN (shad, mid), offset);
+
+ if (bevent->x >0 && bevent->x<256)
+ {
+ if (min == shad)
+ new = &fpvals.cutoff[SHADOWS];
+ else if (min == mid)
+ new = &fpvals.cutoff[MIDTONES];
+ else
+ new = &fpvals.offset;
+
+ slider_erase (gtk_widget_get_window (AW.aliasing_graph), *new);
+ *new = bevent->x;
+ }
+
+ draw_it (NULL);
+
+ fp_range_preview_spill (AW.range_preview, fpvals.value_by);
+ update_range_labels ();
+ fp_create_smoothness_graph (AW.aliasing_preview);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ fp_refresh_previews (fpvals.visible_frames);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = (GdkEventMotion *) event;
+ x = mevent->x;
+
+ if (x >= 0 && x < 256)
+ {
+ slider_erase (gtk_widget_get_window (AW.aliasing_graph), *new);
+ *new = x;
+ draw_it (NULL);
+ fp_range_preview_spill (AW.range_preview, fpvals.value_by);
+ update_range_labels ();
+ fp_create_smoothness_graph (AW.aliasing_preview);
+ }
+
+ gdk_event_request_motions (mevent);
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+update_range_labels (void)
+{
+ gchar buffer[4];
+
+ gtk_label_set_text (GTK_LABEL(fp_widgets.range_label[1]), "0");
+
+ g_snprintf (buffer, sizeof (buffer), "%d", fpvals.cutoff[SHADOWS]);
+ gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[3]), buffer);
+ gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[5]), buffer);
+
+ g_snprintf (buffer, sizeof (buffer), "%d", fpvals.cutoff[MIDTONES]);
+ gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[7]), buffer);
+ gtk_label_set_text (GTK_LABEL (fp_widgets.range_label[9]), buffer);
+
+ gtk_label_set_text (GTK_LABEL(fp_widgets.range_label[11]), "255");
+}
+
+static void
+fp_init_filter_packs (void)
+{
+ gint i, j;
+
+ for (i = 0; i < 256; i++)
+ for (j = BY_HUE; j < JUDGE_BY; j++)
+ {
+ fpvals.red_adjust [j][i] = 0;
+ fpvals.green_adjust [j][i] = 0;
+ fpvals.blue_adjust [j][i] = 0;
+ fpvals.sat_adjust [j][i] = 0;
+ }
+}
+
+static void
+fp_reset_filter_packs (void)
+{
+ fp_init_filter_packs ();
+ fp_refresh_previews (fpvals.visible_frames);
+}
+
+static ReducedImage *
+fp_reduce_image (GimpDrawable *drawable,
+ GimpDrawable *mask,
+ gint longer_size,
+ gint selection)
+{
+ gint RH, RW, bytes = drawable->bpp;
+ gint x, y, width, height;
+ ReducedImage *temp = g_new0 (ReducedImage, 1);
+ guchar *tempRGB, *src_row, *tempmask, *src_mask_row, R, G, B;
+ gint i, j, whichcol, whichrow;
+ GimpPixelRgn srcPR, srcMask;
+ gdouble *tempHSV;
+ GimpRGB rgb;
+ GimpHSV hsv;
+
+ switch (selection)
+ {
+ case 0:
+ x = 0;
+ width = drawable->width;
+ y = 0;
+ height = drawable->height;
+ break;
+
+ case 1:
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x, &y, &width, &height))
+ return temp;
+ break;
+
+ case 2:
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x, &y, &width, &height) ||
+ ! gimp_rectangle_intersect (x - width / 2, y - height / 2,
+ 2 * width, 2 * height,
+ 0, 0, drawable->width, drawable->height,
+ &x, &y, &width, &height))
+ return temp;
+ break;
+
+ default:
+ return temp;
+ }
+
+ if (width > height)
+ {
+ RW = longer_size;
+ RH = (gdouble) height * (gdouble) longer_size / (gdouble) width;
+ }
+ else
+ {
+ RH = longer_size;
+ RW = (gdouble) width * (gdouble) longer_size / (gdouble) height;
+ }
+
+ tempRGB = g_new (guchar, RW * RH * bytes);
+ tempHSV = g_new (gdouble, RW * RH * bytes);
+ tempmask = g_new (guchar, RW * RH);
+
+ src_row = g_new (guchar, width * bytes);
+ src_mask_row = g_new (guchar, width);
+
+ gimp_pixel_rgn_init (&srcPR, drawable, x, y, width, height, FALSE, FALSE);
+
+ if (mask)
+ {
+ gimp_pixel_rgn_init (&srcMask, mask, x, y, width, height, FALSE, FALSE);
+ }
+ else
+ {
+ memset (src_mask_row, 255, width);
+ }
+
+ for (i = 0; i < RH; i++)
+ {
+ whichrow = (gdouble) i * (gdouble) height / (gdouble) RH;
+
+ gimp_pixel_rgn_get_row (&srcPR, src_row, x, y + whichrow, width);
+
+ if (mask)
+ gimp_pixel_rgn_get_row (&srcMask, src_mask_row, x, y + whichrow, width);
+
+ for (j = 0; j < RW; j++)
+ {
+ whichcol = (gdouble) j * (gdouble) width / (gdouble) RW;
+
+ tempmask[i * RW + j] = src_mask_row[whichcol];
+
+ R = src_row[whichcol * bytes + 0];
+ G = src_row[whichcol * bytes + 1];
+ B = src_row[whichcol * bytes + 2];
+
+ gimp_rgb_set_uchar (&rgb, R, G, B);
+ gimp_rgb_to_hsv (&rgb, &hsv);
+
+ tempRGB[i * RW * bytes + j * bytes + 0] = R;
+ tempRGB[i * RW * bytes + j * bytes + 1] = G;
+ tempRGB[i * RW * bytes + j * bytes + 2] = B;
+
+ tempHSV[i * RW * bytes + j * bytes + 0] = hsv.h;
+ tempHSV[i * RW * bytes + j * bytes + 1] = hsv.s;
+ tempHSV[i * RW * bytes + j * bytes + 2] = hsv.v;
+
+ if (bytes == 4)
+ {
+ tempRGB[i * RW * bytes + j * bytes + 3] =
+ src_row[whichcol * bytes + 3];
+ }
+ }
+ }
+
+ g_free (src_row);
+ g_free (src_mask_row);
+
+ temp->width = RW;
+ temp->height = RH;
+ temp->rgb = tempRGB;
+ temp->hsv = tempHSV;
+ temp->mask = tempmask;
+
+ return temp;
+}
+
+static void
+fp_render_preview (GtkWidget *preview,
+ gint change_what,
+ gint change_which)
+{
+ guchar *a;
+ gint Inten;
+ gint bytes = drawable->bpp;
+ gint i, j, k, nudge, M, m, middle, JudgeBy;
+ gdouble partial;
+ gint RW = reduced->width;
+ gint RH = reduced->height;
+ gint backupP[3];
+ gint P[3];
+ gint tempSat[JUDGE_BY][256];
+
+ a = g_new (guchar, 4 * RW * RH);
+
+ if (change_what == SATURATION)
+ for (k = 0; k < 256; k++)
+ {
+ for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++)
+ tempSat[JudgeBy][k] = 0;
+
+ tempSat[fpvals.value_by][k] +=
+ change_which * nudgeArray[(k + fpvals.offset) % 256];
+ }
+
+ for (i = 0; i < RH; i++)
+ {
+ for (j = 0; j < RW; j++)
+ {
+ backupP[0] = P[0] = reduced->rgb[i * RW * bytes + j * bytes + 0];
+ backupP[1] = P[1] = reduced->rgb[i * RW * bytes + j * bytes + 1];
+ backupP[2] = P[2] = reduced->rgb[i * RW * bytes + j * bytes + 2];
+
+ m = MIN (MIN (P[0], P[1]), P[2]);
+ M = MAX (MAX (P[0], P[1]), P[2]);
+
+ middle = (M + m) / 2;
+
+ for (k = 0; k < 3; k++)
+ if (P[k] != m && P[k] != M) middle = P[k];
+
+ partial = reduced->mask[i * RW + j] / 255.0;
+
+ for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++)
+ {
+ if (!fpvals.touched[JudgeBy])
+ continue;
+
+ Inten =
+ reduced->hsv[i * RW * bytes + j * bytes + JudgeBy] * 255.0;
+
+ /*DO SATURATION FIRST*/
+ if (change_what != NONEATALL)
+ {
+ gint adjust = partial * fpvals.sat_adjust[JudgeBy][Inten];
+
+ if (M != m)
+ {
+ for (k = 0; k < 3; k++)
+ if (backupP[k] == M)
+ {
+ P[k] = MAX (P[k] + adjust, middle);
+ }
+ else if (backupP[k] == m)
+ {
+ P[k] = MIN (P[k] - adjust, middle);
+ }
+ }
+
+ P[0] += partial * fpvals.red_adjust[JudgeBy][Inten];
+ P[1] += partial * fpvals.green_adjust[JudgeBy][Inten];
+ P[2] += partial * fpvals.blue_adjust[JudgeBy][Inten];
+ }
+ }
+
+ Inten =
+ reduced->hsv[i * RW * bytes + j * bytes + fpvals.value_by] * 255.0;
+ nudge = partial * nudgeArray[(Inten + fpvals.offset) % 256];
+
+ switch (change_what)
+ {
+ case HUE:
+ P[0] += colorSign[RED][change_which] * nudge;
+ P[1] += colorSign[GREEN][change_which] * nudge;
+ P[2] += colorSign[BLUE][change_which] * nudge;
+ break;
+
+ case SATURATION:
+ for (JudgeBy = BY_HUE; JudgeBy < JUDGE_BY; JudgeBy++)
+ {
+ gint adjust = partial * tempSat[JudgeBy][Inten];
+
+ for (k = 0; k < 3; k++)
+ if (M != m)
+ {
+ if (backupP[k] == M)
+ {
+ P[k] = MAX (P[k] + adjust, middle);
+ }
+ else if (backupP[k] == m)
+ {
+ P[k] = MIN (P[k] - adjust, middle);
+ }
+ }
+ }
+ break;
+
+ case VALUE:
+ P[0] += change_which * nudge;
+ P[1] += change_which * nudge;
+ P[2] += change_which * nudge;
+ break;
+
+ default:
+ break;
+ }
+
+ a[(i * RW + j) * 4 + 0] = CLAMP0255 (P[0]);
+ a[(i * RW + j) * 4 + 1] = CLAMP0255 (P[1]);
+ a[(i * RW + j) * 4 + 2] = CLAMP0255 (P[2]);
+
+ if (bytes == 4)
+ a[(i * RW + j) * 4 + 3] = reduced->rgb[i * RW * bytes + j * bytes + 3];
+ else
+ a[(i * RW + j) * 4 + 3] = 255;
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, RW, RH,
+ GIMP_RGBA_IMAGE,
+ a,
+ RW * 4);
+ g_free (a);
+}
+
+static void
+update_current_fp (gint change_what,
+ gint change_which)
+{
+ gint i;
+
+ for (i = 0; i < 256; i++)
+ {
+ gint nudge;
+
+ fp_create_nudge (nudgeArray);
+ nudge = nudgeArray[(i + fpvals.offset) % 256];
+
+ switch (change_what) {
+ case HUE:
+ fpvals.red_adjust[fpvals.value_by][i] +=
+ colorSign[RED][change_which] * nudge;
+
+ fpvals.green_adjust[fpvals.value_by][i] +=
+ colorSign[GREEN][change_which] * nudge;
+
+ fpvals.blue_adjust[fpvals.value_by][i] +=
+ colorSign[BLUE][change_which] * nudge;
+ break;
+
+ case SATURATION:
+ fpvals.sat_adjust[fpvals.value_by][i] += change_which * nudge;
+ break;
+
+ case VALUE:
+ fpvals.red_adjust[fpvals.value_by][i] += change_which * nudge;
+ fpvals.green_adjust[fpvals.value_by][i] += change_which * nudge;
+ fpvals.blue_adjust[fpvals.value_by][i] += change_which * nudge;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+fp_create_smoothness_graph (GtkWidget *preview)
+{
+ guchar data[256 * MAX_ROUGHNESS * 3];
+ gint nArray[256];
+ gint i, j;
+ gboolean toBeBlack;
+
+ fp_create_nudge(nArray);
+
+ for (i = 0; i < MAX_ROUGHNESS; i++)
+ {
+ gint coor = MAX_ROUGHNESS - i;
+
+ for (j = 0; j < 256; j++)
+ {
+ data[3 * (i * 256 + j) + 0] = 255;
+ data[3 * (i * 256 + j) + 1] = 255;
+ data[3 * (i * 256 + j) + 2] = 255;
+
+ if (!(i % (MAX_ROUGHNESS / 4)))
+ {
+ data[3 * (i * 256 + j) + 0] = 255;
+ data[3 * (i * 256 + j) + 1] = 128;
+ data[3 * (i * 256 + j) + 2] = 128;
+ }
+
+ if (!((j + 1) % 32))
+ {
+ data[3 * (i * 256 + j) + 0] = 255;
+ data[3 * (i * 256 + j) + 1] = 128;
+ data[3 * (i * 256 + j) + 2] = 128;
+ }
+
+ toBeBlack = FALSE;
+
+ if (nArray[j] == coor)
+ toBeBlack = TRUE;
+
+ if (j < 255)
+ {
+ gint jump = abs (nArray[j] - nArray[j+1]);
+
+ if (abs (coor - nArray[j]) < jump &&
+ abs (coor - nArray[j + 1]) < jump)
+ toBeBlack = TRUE;
+ }
+
+ if (toBeBlack)
+ {
+ data[3 * (i * 256 + j) + 0] = 0;
+ data[3 * (i * 256 + j) + 1] = 0;
+ data[3 * (i * 256 + j) + 2] = 0;
+ }
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, 256, MAX_ROUGHNESS,
+ GIMP_RGB_IMAGE,
+ data,
+ 256 * 3);
+}
+
+static void
+fp_range_preview_spill (GtkWidget *preview,
+ gint type)
+{
+ gint i, j;
+ guchar data[256 * RANGE_HEIGHT * 3];
+
+ for (i = 0; i < RANGE_HEIGHT; i++)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ GimpRGB rgb;
+ GimpHSV hsv;
+
+ if (! ((j + 1) % 32))
+ {
+ data[3 * (i * 256 + j) + 0] = 255;
+ data[3 * (i * 256 + j) + 1] = 128;
+ data[3 * (i * 256 + j) + 2] = 128;
+ }
+ else
+ {
+ switch (type)
+ {
+ case BY_VAL:
+ data[3 * (i * 256 + j) + 0] = j - fpvals.offset;
+ data[3 * (i * 256 + j) + 1] = j - fpvals.offset;
+ data[3 * (i * 256 + j) + 2] = j - fpvals.offset;
+ break;
+
+ case BY_HUE:
+ gimp_hsv_set (&hsv,
+ ((j - fpvals.offset + 256) % 256) / 255.0,
+ 1.0,
+ 0.5);
+ gimp_hsv_to_rgb (&hsv, &rgb);
+ gimp_rgb_get_uchar (&rgb,
+ &data[3 * (i * 256 + j) + 0],
+ &data[3 * (i * 256 + j) + 1],
+ &data[3 * (i * 256 + j) + 2]);
+ break;
+
+ case BY_SAT:
+ gimp_hsv_set (&hsv,
+ 0.5,
+ ((j - (gint) fpvals.offset + 256) % 256) / 255.0,
+ 0.5);
+ gimp_hsv_to_rgb (&hsv, &rgb);
+ gimp_rgb_get_uchar (&rgb,
+ &data[3 * (i * 256 + j) + 0],
+ &data[3 * (i * 256 + j) + 1],
+ &data[3 * (i * 256 + j) + 2]);
+ break;
+ }
+ }
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, 256, RANGE_HEIGHT,
+ GIMP_RGB_IMAGE,
+ data,
+ 256 * 3);
+}
+
+static void
+fp_create_nudge (gint *adj_array)
+{
+ gint left, right, middle,i;
+ /* The following function was determined by trial and error */
+ gdouble Steepness = pow (1 - fpvals.aliasing, 4) * .8;
+
+ left = (fpvals.intensity_range == SHADOWS) ? 0 : fpvals.cutoff[fpvals.intensity_range - 1];
+ right = fpvals.cutoff[fpvals.intensity_range];
+ middle = (left + right)/2;
+
+ if (fpvals.aliasing)
+ for (i = 0; i < 256; i++)
+ if (i <= middle)
+ adj_array[i] = MAX_ROUGHNESS *
+ fpvals.roughness * (1 + tanh (Steepness * (i - left))) / 2;
+ else
+ adj_array[i] = MAX_ROUGHNESS *
+ fpvals.roughness * (1 + tanh (Steepness * (right - i))) / 2;
+ else
+ for (i = 0; i < 256; i++)
+ adj_array[i] = (left <= i && i <= right)
+ ? MAX_ROUGHNESS * fpvals.roughness : 0;
+}
+
+static void
+fp_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ gint which = fpvals.visible_frames;
+
+ if (widget == origPreview)
+ fp_render_preview (origPreview, NONEATALL, 0);
+ else if (widget == curPreview)
+ fp_render_preview (curPreview, CURRENT, 0);
+
+ if (which & HUE)
+ {
+ if (widget == rPreview)
+ fp_render_preview (rPreview, HUE, RED);
+ else if (widget == gPreview)
+ fp_render_preview (gPreview, HUE, GREEN);
+ else if (widget == bPreview)
+ fp_render_preview (bPreview, HUE, BLUE);
+ else if (widget == cPreview)
+ fp_render_preview (cPreview, HUE, CYAN);
+ else if (widget == yPreview)
+ fp_render_preview (yPreview, HUE, YELLOW);
+ else if (widget == mPreview)
+ fp_render_preview (mPreview, HUE, MAGENTA);
+ else if (widget == centerPreview)
+ fp_render_preview (centerPreview, CURRENT, 0);
+ }
+
+ if (which & VALUE)
+ {
+ if (widget == lighterPreview)
+ fp_render_preview (lighterPreview, VALUE, UP);
+ else if (widget == middlePreview)
+ fp_render_preview (middlePreview, CURRENT, 0);
+ else if (widget == darkerPreview)
+ fp_render_preview (darkerPreview, VALUE, DOWN);
+ }
+
+ if (which & SATURATION)
+ {
+ if (widget == plusSatPreview)
+ fp_render_preview (plusSatPreview, SATURATION, UP);
+ else if (widget == SatPreview)
+ fp_render_preview (SatPreview, CURRENT, 0);
+ else if (widget == minusSatPreview)
+ fp_render_preview (minusSatPreview, SATURATION, DOWN);
+ }
+}
diff --git a/plug-ins/common/fractal-trace.c b/plug-ins/common/fractal-trace.c
new file mode 100644
index 0000000..9ac702e
--- /dev/null
+++ b/plug-ins/common/fractal-trace.c
@@ -0,0 +1,829 @@
+/******************************************************************************
+
+ fractaltrace.c -- This is a plug-in for GIMP 1.0
+
+ Copyright (C) 1997 Hirotsuna Mizuno
+ s1041150@u-aizu.ac.jp
+
+ This program is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see <https://www.gnu.org/licenses/>.
+
+******************************************************************************/
+
+#define PLUG_IN_PROC "plug-in-fractal-trace"
+#define PLUG_IN_BINARY "fractal-trace"
+#define PLUG_IN_ROLE "gimp-fractal-trace"
+#define PLUG_IN_VERSION "v0.4 test version (Dec. 25 1997)"
+
+/*****************************************************************************/
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/******************************************************************************/
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void filter (GimpDrawable *drawable);
+
+static void pixels_init (GimpDrawable *drawable);
+static void pixels_free (void);
+
+static int dialog_show (void);
+static void dialog_preview_draw (void);
+
+/******************************************************************************/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+/******************************************************************************/
+
+enum
+{
+ OUTSIDE_TYPE_WRAP,
+ OUTSIDE_TYPE_TRANSPARENT,
+ OUTSIDE_TYPE_BLACK,
+ OUTSIDE_TYPE_WHITE
+};
+
+typedef struct
+{
+ gdouble x1;
+ gdouble x2;
+ gdouble y1;
+ gdouble y2;
+ gint32 depth;
+ gint32 outside_type;
+} parameter_t;
+
+static parameter_t parameters =
+{
+ -1.0,
+ +0.5,
+ -1.0,
+ +1.0,
+ 3,
+ OUTSIDE_TYPE_WRAP
+};
+
+/******************************************************************************/
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "xmin", "xmin fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "xmax", "xmax fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "ymin", "ymin fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "ymax", "ymax fractal image delimiter" },
+ { GIMP_PDB_INT32, "depth", "Trace depth" },
+ { GIMP_PDB_INT32, "outside-type", "Outside type "
+ "{ WRAP (0), TRANS (1), BLACK (2), WHITE (3) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Transform image with the Mandelbrot Fractal"),
+ "transform image with the Mandelbrot Fractal",
+ "Hirotsuna Mizuno <s1041150@u-aizu.ac.jp>",
+ "Copyright (C) 1997 Hirotsuna Mizuno",
+ PLUG_IN_VERSION,
+ N_("_Fractal Trace (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Map");
+}
+
+/******************************************************************************/
+
+typedef struct
+{
+ gint x1;
+ gint x2;
+ gint y1;
+ gint y2;
+ gint width;
+ gint height;
+ gdouble center_x;
+ gdouble center_y;
+} selection_t;
+
+typedef struct
+{
+ gint width;
+ gint height;
+ gint bpp;
+ gint alpha;
+} image_t;
+
+static selection_t selection;
+static image_t image;
+
+/******************************************************************************/
+
+static void
+run (const gchar *name,
+ gint argc,
+ const GimpParam *args,
+ gint *retc,
+ GimpParam **rets)
+{
+ GimpDrawable *drawable;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status;
+ static GimpParam returns[1];
+
+ run_mode = args[0].data.d_int32;
+ status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+
+ drawable = gimp_drawable_get (args[2].data.d_drawable);
+ image.width = gimp_drawable_width( drawable->drawable_id);
+ image.height = gimp_drawable_height (drawable->drawable_id);
+ image.bpp = gimp_drawable_bpp (drawable->drawable_id);
+ image.alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &selection.x1, &selection.y1,
+ &selection.width, &selection.height))
+ {
+ returns[0].type = GIMP_PDB_STATUS;
+ returns[0].data.d_status = status;
+ *retc = 1;
+ *rets = returns;
+
+ return;
+ }
+
+ selection.x2 = selection.x1 + selection.width;
+ selection.y2 = selection.y1 + selection.height;
+ selection.center_x = selection.x1 + (gdouble) selection.width / 2.0;
+ selection.center_y = selection.y1 + (gdouble) selection.height / 2.0;
+
+ pixels_init (drawable);
+
+ if (!gimp_drawable_is_rgb(drawable->drawable_id) &&
+ !gimp_drawable_is_gray(drawable->drawable_id))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &parameters);
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &parameters);
+ if (!dialog_show ())
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+ }
+ gimp_set_data (PLUG_IN_PROC, &parameters, sizeof (parameter_t));
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (argc != 9)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ parameters.x1 = args[3].data.d_float;
+ parameters.x2 = args[4].data.d_float;
+ parameters.y1 = args[5].data.d_float;
+ parameters.y2 = args[6].data.d_float;
+ parameters.depth = args[7].data.d_int32;
+ parameters.outside_type = args[8].data.d_int32;
+ }
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_tile_cache_ntiles(2 * (drawable->width / gimp_tile_width() + 1));
+ filter (drawable);
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush();
+ }
+
+ gimp_drawable_detach (drawable);
+
+ pixels_free ();
+
+ returns[0].type = GIMP_PDB_STATUS;
+ returns[0].data.d_status = status;
+ *retc = 1;
+ *rets = returns;
+}
+
+/******************************************************************************/
+
+static guchar **spixels;
+static guchar **dpixels;
+static GimpPixelRgn sPR;
+static GimpPixelRgn dPR;
+
+typedef struct
+{
+ guchar r;
+ guchar g;
+ guchar b;
+ guchar a;
+} pixel_t;
+
+static void
+pixels_init (GimpDrawable *drawable)
+{
+ gint y;
+
+ gimp_pixel_rgn_init (&sPR, drawable,
+ 0, 0, image.width, image.height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&dPR, drawable,
+ 0, 0, image.width, image.height, TRUE, TRUE);
+
+ spixels = g_new (guchar *, image.height);
+ dpixels = g_new (guchar *, image.height);
+
+ for (y = 0; y < image.height; y++)
+ {
+ spixels[y] = g_new (guchar, image.width * image.bpp);
+ dpixels[y] = g_new (guchar, image.width * image.bpp);
+ gimp_pixel_rgn_get_row (&sPR, spixels[y], 0, y, image.width);
+ }
+}
+
+static void
+pixels_free (void)
+{
+ gint y;
+
+ for (y = 0; y < image.height; y++)
+ {
+ g_free (spixels[y]);
+ g_free (dpixels[y]);
+ }
+ g_free (spixels);
+ g_free (dpixels);
+}
+
+static void
+pixels_get (gint x,
+ gint y,
+ pixel_t *pixel)
+{
+ if(x < 0) x = 0; else if (image.width <= x) x = image.width - 1;
+ if(y < 0) y = 0; else if (image.height <= y) y = image.height - 1;
+
+ switch (image.bpp)
+ {
+ case 1: /* GRAY */
+ pixel->r = spixels[y][x*image.bpp];
+ pixel->g = spixels[y][x*image.bpp];
+ pixel->b = spixels[y][x*image.bpp];
+ pixel->a = 255;
+ break;
+ case 2: /* GRAY+A */
+ pixel->r = spixels[y][x*image.bpp];
+ pixel->g = spixels[y][x*image.bpp];
+ pixel->b = spixels[y][x*image.bpp];
+ pixel->a = spixels[y][x*image.bpp+1];
+ break;
+ case 3: /* RGB */
+ pixel->r = spixels[y][x*image.bpp];
+ pixel->g = spixels[y][x*image.bpp+1];
+ pixel->b = spixels[y][x*image.bpp+2];
+ pixel->a = 255;
+ break;
+ case 4: /* RGB+A */
+ pixel->r = spixels[y][x*image.bpp];
+ pixel->g = spixels[y][x*image.bpp+1];
+ pixel->b = spixels[y][x*image.bpp+2];
+ pixel->a = spixels[y][x*image.bpp+3];
+ break;
+ }
+}
+
+static void
+pixels_get_biliner (gdouble x,
+ gdouble y,
+ pixel_t *pixel)
+{
+ pixel_t A, B, C, D;
+ gdouble a, b, c, d;
+ gint x1, y1, x2, y2;
+ gdouble dx, dy;
+ gdouble alpha;
+
+ x1 = (gint) floor (x);
+ x2 = x1 + 1;
+ y1 = (gint) floor (y);
+ y2 = y1 + 1;
+
+ dx = x - (gdouble) x1;
+ dy = y - (gdouble) y1;
+ a = (1.0 - dx) * (1.0 - dy);
+ b = dx * (1.0 - dy);
+ c = (1.0 - dx) * dy;
+ d = dx * dy;
+
+ pixels_get (x1, y1, &A);
+ pixels_get (x2, y1, &B);
+ pixels_get (x1, y2, &C);
+ pixels_get (x2, y2, &D);
+
+ alpha = 1.0001*(a * (gdouble) A.a + b * (gdouble) B.a
+ + c * (gdouble) C.a + d * (gdouble) D.a);
+ pixel->a = (guchar) alpha;
+
+ if (pixel->a)
+ {
+ pixel->r = (guchar) ((a * (gdouble) A.r * A.a
+ + b * (gdouble) B.r * B.a
+ + c * (gdouble) C.r * C.a
+ + d * (gdouble) D.r * D.a) / alpha);
+ pixel->g = (guchar) ((a * (gdouble) A.g * A.a
+ + b * (gdouble) B.g * B.a
+ + c * (gdouble) C.g * C.a
+ + d * (gdouble) D.g * D.a) / alpha);
+ pixel->b = (guchar) ((a * (gdouble) A.b * A.a
+ + b * (gdouble) B.b * B.a
+ + c * (gdouble) C.b * C.a
+ + d * (gdouble) D.b * D.a) / alpha);
+ }
+}
+
+static void
+pixels_set (gint x,
+ gint y,
+ pixel_t *pixel)
+{
+ switch (image.bpp)
+ {
+ case 1: /* GRAY */
+ dpixels[y][x*image.bpp] = pixel->r;
+ break;
+ case 2: /* GRAY+A */
+ dpixels[y][x*image.bpp] = pixel->r;
+ dpixels[y][x*image.bpp+1] = pixel->a;
+ break;
+ case 3: /* RGB */
+ dpixels[y][x*image.bpp] = pixel->r;
+ dpixels[y][x*image.bpp+1] = pixel->g;
+ dpixels[y][x*image.bpp+2] = pixel->b;
+ break;
+ case 4: /* RGB+A */
+ dpixels[y][x*image.bpp] = pixel->r;
+ dpixels[y][x*image.bpp+1] = pixel->g;
+ dpixels[y][x*image.bpp+2] = pixel->b;
+ dpixels[y][x*image.bpp+3] = pixel->a;
+ break;
+ }
+}
+
+static void
+pixels_store (void)
+{
+ gint y;
+
+ for (y = selection.y1; y < selection.y2; y++)
+ {
+ gimp_pixel_rgn_set_row (&dPR, dpixels[y], 0, y, image.width);
+ }
+}
+
+/******************************************************************************/
+
+static void
+mandelbrot (gdouble x,
+ gdouble y,
+ gdouble *u,
+ gdouble *v)
+{
+ gint iter = 0;
+ gdouble xx = x;
+ gdouble yy = y;
+ gdouble x2 = xx * xx;
+ gdouble y2 = yy * yy;
+ gdouble tmp;
+
+ while (iter < parameters.depth)
+ {
+ tmp = x2 - y2 + x;
+ yy = 2 * xx * yy + y;
+ xx = tmp;
+ x2 = xx * xx;
+ y2 = yy * yy;
+ iter++;
+ }
+ *u = xx;
+ *v = yy;
+}
+
+/******************************************************************************/
+
+static void
+filter (GimpDrawable *drawable)
+{
+ gint x, y;
+ pixel_t pixel;
+ gdouble scale_x, scale_y;
+ gdouble cx, cy;
+ gdouble px, py;
+ gint h_percent;
+
+ gimp_progress_init (_("Fractal Trace"));
+
+ if (selection.width == 0 || selection.height == 0)
+ return;
+
+ h_percent = selection.height / 100;
+
+ scale_x = (parameters.x2 - parameters.x1) / selection.width;
+ scale_y = (parameters.y2 - parameters.y1) / selection.height;
+
+ for (y = selection.y1; y < selection.y2; y++)
+ {
+ cy = parameters.y1 + (y - selection.y1) * scale_y;
+ for (x = selection.x1; x < selection.x2; x++)
+ {
+ cx = parameters.x1 + (x - selection.x1) * scale_x;
+ mandelbrot (cx, cy, &px, &py);
+ px = (px - parameters.x1) / scale_x + selection.x1;
+ py = (py - parameters.y1) / scale_y + selection.y1;
+ if (0 <= px && px < image.width && 0 <= py && py < image.height)
+ {
+ pixels_get_biliner (px, py, &pixel);
+ }
+ else
+ {
+ switch (parameters.outside_type)
+ {
+ case OUTSIDE_TYPE_WRAP:
+ px = fmod (px, image.width);
+ py = fmod (py, image.height);
+ if( px < 0.0) px += image.width;
+ if (py < 0.0) py += image.height;
+ pixels_get_biliner (px, py, &pixel);
+ break;
+ case OUTSIDE_TYPE_TRANSPARENT:
+ pixel.r = pixel.g = pixel.b = 0;
+ pixel.a = 0;
+ break;
+ case OUTSIDE_TYPE_BLACK:
+ pixel.r = pixel.g = pixel.b = 0;
+ pixel.a = 255;
+ break;
+ case OUTSIDE_TYPE_WHITE:
+ pixel.r = pixel.g = pixel.b = 255;
+ pixel.a = 255;
+ break;
+ }
+ }
+ pixels_set (x, y, &pixel);
+ }
+
+ if (h_percent == 0 || ((y - selection.y1) % h_percent) == 0)
+ gimp_progress_update ((gdouble) (y-selection.y1) / selection.height);
+ }
+
+ gimp_progress_update (1.0);
+
+ pixels_store ();
+
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id,
+ selection.x1, selection.y1,
+ selection.width, selection.height);
+}
+
+/******************************************************************************/
+
+#define PREVIEW_SIZE 200
+
+typedef struct
+{
+ GtkWidget *preview;
+ guchar *pixels;
+ gdouble scale;
+ gint width;
+ gint height;
+ gint bpp;
+} preview_t;
+
+static preview_t preview;
+
+static void
+dialog_preview_setpixel (gint x,
+ gint y,
+ pixel_t *pixel)
+{
+ preview.pixels[(y * preview.width + x) * 4 + 0] = pixel->r;
+ preview.pixels[(y * preview.width + x) * 4 + 1] = pixel->g;
+ preview.pixels[(y * preview.width + x) * 4 + 2] = pixel->b;
+ preview.pixels[(y * preview.width + x) * 4 + 3] = pixel->a;
+}
+
+static void
+dialog_preview_init (void)
+{
+ pixel_t pixel;
+ gint x, y;
+ gdouble cx, cy;
+
+ if (image.width < image.height)
+ preview.scale = (gdouble)selection.height / (gdouble)PREVIEW_SIZE;
+ else
+ preview.scale = (gdouble)selection.width / (gdouble)PREVIEW_SIZE;
+ preview.width = (gdouble)selection.width / preview.scale;
+ preview.height = (gdouble)selection.height / preview.scale;
+
+ preview.preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview.preview,
+ preview.width, preview.height);
+
+ preview.pixels = g_new (guchar, preview.height * preview.width * 4);
+
+ for (y = 0; y < preview.height; y++)
+ {
+ cy = selection.y1 + (gdouble)y * preview.scale;
+ for (x = 0; x < preview.width; x++)
+ {
+ cx = selection.x1 + (gdouble)x * preview.scale;
+ pixels_get_biliner (cx, cy, &pixel);
+ dialog_preview_setpixel (x, y, &pixel);
+ }
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview.preview),
+ 0, 0, preview.width, preview.height,
+ GIMP_RGBA_IMAGE,
+ preview.pixels,
+ preview.width *4);
+}
+
+static void
+dialog_preview_draw (void)
+{
+ gint x, y;
+ pixel_t pixel;
+ gdouble scale_x, scale_y;
+ gdouble cx, cy;
+ gdouble px, py;
+
+ scale_x = (parameters.x2 - parameters.x1) / preview.width;
+ scale_y = (parameters.y2 - parameters.y1) / preview.height;
+
+ for (y = 0; y < preview.height; y++)
+ {
+ cy = parameters.y1 + y * scale_y;
+ for (x = 0; x < preview.width; x++)
+ {
+ cx = parameters.x1 + x * scale_x;
+ mandelbrot(cx, cy, &px, &py);
+ px = (px - parameters.x1) / scale_x * preview.scale + selection.x1;
+ py = (py - parameters.y1) / scale_y * preview.scale + selection.y1;
+ if (0 <= px && px < image.width && 0 <= py && py < image.height)
+ {
+ pixels_get_biliner (px, py, &pixel);
+ }
+ else
+ {
+ switch (parameters.outside_type)
+ {
+ case OUTSIDE_TYPE_WRAP:
+ px = fmod (px, image.width);
+ py = fmod (py, image.height);
+ if (px < 0.0) px += image.width;
+ if (py < 0.0) py += image.height;
+ pixels_get_biliner (px, py, &pixel);
+ break;
+ case OUTSIDE_TYPE_TRANSPARENT:
+ pixel.r = pixel.g = pixel.b = 0;
+ pixel.a = 0;
+ break;
+ case OUTSIDE_TYPE_BLACK:
+ pixel.r = pixel.g = pixel.b = 0;
+ pixel.a = 255;
+ break;
+ case OUTSIDE_TYPE_WHITE:
+ pixel.r = pixel.g = pixel.b = 255;
+ pixel.a = 255;
+ break;
+ }
+ }
+ dialog_preview_setpixel (x, y, &pixel);
+ }
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview.preview),
+ 0, 0, preview.width, preview.height,
+ GIMP_RGBA_IMAGE,
+ preview.pixels,
+ preview.width *4);
+}
+
+/******************************************************************************/
+
+static void
+dialog_int_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_int_adjustment_update (adjustment, data);
+
+ dialog_preview_draw ();
+}
+
+static void
+dialog_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_double_adjustment_update (adjustment, data);
+
+ dialog_preview_draw ();
+}
+
+static void
+dialog_outside_type_callback (GtkWidget *widget,
+ gpointer *data)
+{
+ gimp_radio_button_update (widget, data);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ dialog_preview_draw ();
+}
+
+/******************************************************************************/
+
+static gboolean
+dialog_show (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *mainbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *abox;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Fractal Trace"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (mainbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ mainbox, TRUE, TRUE, 0);
+ gtk_widget_show (mainbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Preview */
+ abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ dialog_preview_init ();
+ gtk_container_add (GTK_CONTAINER (frame), preview.preview);
+ gtk_widget_show (preview.preview);
+
+ /* Settings */
+ frame = gimp_int_radio_group_new (TRUE, _("Outside Type"),
+ G_CALLBACK (dialog_outside_type_callback),
+ &parameters.outside_type,
+ parameters.outside_type,
+
+ _("_Wrap"),
+ OUTSIDE_TYPE_WRAP, NULL,
+ _("_Transparent"),
+ OUTSIDE_TYPE_TRANSPARENT, NULL,
+ _("_Black"),
+ OUTSIDE_TYPE_BLACK, NULL,
+ _("_White"),
+ OUTSIDE_TYPE_WHITE, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_frame_new (_("Mandelbrot Parameters"));
+ gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (5, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("X_1:"), 0, 6,
+ parameters.x1, -50, 50, 0.1, 0.5, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_double_adjustment_update),
+ &parameters.x1);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("X_2:"), 0, 6,
+ parameters.x2, -50, 50, 0.1, 0.5, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_double_adjustment_update),
+ &parameters.x2);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Y_1:"), 0, 6,
+ parameters.y1, -50, 50, 0.1, 0.5, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_double_adjustment_update),
+ &parameters.y1);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Y_2:"), 0, 6,
+ parameters.y2, -50, 50, 0.1, 0.5, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_double_adjustment_update),
+ &parameters.y2);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("_Depth:"), 0, 6,
+ parameters.depth, 1, 50, 1, 5, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dialog_int_adjustment_update),
+ &parameters.depth);
+
+ gtk_widget_show (dialog);
+ dialog_preview_draw ();
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/gimprc.common b/plug-ins/common/gimprc.common
new file mode 100644
index 0000000..e8cbbd1
--- /dev/null
+++ b/plug-ins/common/gimprc.common
@@ -0,0 +1,90 @@
+align_layers_RC = align-layers.rc.o
+animation_optimize_RC = animation-optimize.rc.o
+animation_play_RC = animation-play.rc.o
+blinds_RC = blinds.rc.o
+blur_RC = blur.rc.o
+border_average_RC = border-average.rc.o
+busy_dialog_RC = busy-dialog.rc.o
+cartoon_RC = cartoon.rc.o
+checkerboard_RC = checkerboard.rc.o
+cml_explorer_RC = cml-explorer.rc.o
+color_cube_analyze_RC = color-cube-analyze.rc.o
+color_enhance_RC = color-enhance.rc.o
+colorify_RC = colorify.rc.o
+colormap_remap_RC = colormap-remap.rc.o
+compose_RC = compose.rc.o
+contrast_retinex_RC = contrast-retinex.rc.o
+crop_zealous_RC = crop-zealous.rc.o
+curve_bend_RC = curve-bend.rc.o
+decompose_RC = decompose.rc.o
+depth_merge_RC = depth-merge.rc.o
+despeckle_RC = despeckle.rc.o
+destripe_RC = destripe.rc.o
+edge_dog_RC = edge-dog.rc.o
+emboss_RC = emboss.rc.o
+file_aa_RC = file-aa.rc.o
+file_cel_RC = file-cel.rc.o
+file_compressor_RC = file-compressor.rc.o
+file_csource_RC = file-csource.rc.o
+file_desktop_link_RC = file-desktop-link.rc.o
+file_dicom_RC = file-dicom.rc.o
+file_gbr_RC = file-gbr.rc.o
+file_gegl_RC = file-gegl.rc.o
+file_gif_load_RC = file-gif-load.rc.o
+file_gif_save_RC = file-gif-save.rc.o
+file_gih_RC = file-gih.rc.o
+file_glob_RC = file-glob.rc.o
+file_header_RC = file-header.rc.o
+file_heif_RC = file-heif.rc.o
+file_html_table_RC = file-html-table.rc.o
+file_jp2_load_RC = file-jp2-load.rc.o
+file_jpegxl_RC = file-jpegxl.rc.o
+file_mng_RC = file-mng.rc.o
+file_pat_RC = file-pat.rc.o
+file_pcx_RC = file-pcx.rc.o
+file_pdf_load_RC = file-pdf-load.rc.o
+file_pdf_save_RC = file-pdf-save.rc.o
+file_pix_RC = file-pix.rc.o
+file_png_RC = file-png.rc.o
+file_pnm_RC = file-pnm.rc.o
+file_ps_RC = file-ps.rc.o
+file_psp_RC = file-psp.rc.o
+file_raw_data_RC = file-raw-data.rc.o
+file_sunras_RC = file-sunras.rc.o
+file_svg_RC = file-svg.rc.o
+file_tga_RC = file-tga.rc.o
+file_wmf_RC = file-wmf.rc.o
+file_xbm_RC = file-xbm.rc.o
+file_xmc_RC = file-xmc.rc.o
+file_xpm_RC = file-xpm.rc.o
+file_xwd_RC = file-xwd.rc.o
+film_RC = film.rc.o
+filter_pack_RC = filter-pack.rc.o
+fractal_trace_RC = fractal-trace.rc.o
+goat_exercise_RC = goat-exercise.rc.o
+gradient_map_RC = gradient-map.rc.o
+grid_RC = grid.rc.o
+guillotine_RC = guillotine.rc.o
+hot_RC = hot.rc.o
+jigsaw_RC = jigsaw.rc.o
+mail_RC = mail.rc.o
+max_rgb_RC = max-rgb.rc.o
+nl_filter_RC = nl-filter.rc.o
+photocopy_RC = photocopy.rc.o
+plugin_browser_RC = plugin-browser.rc.o
+procedure_browser_RC = procedure-browser.rc.o
+qbist_RC = qbist.rc.o
+sample_colorize_RC = sample-colorize.rc.o
+sharpen_RC = sharpen.rc.o
+smooth_palette_RC = smooth-palette.rc.o
+softglow_RC = softglow.rc.o
+sparkle_RC = sparkle.rc.o
+sphere_designer_RC = sphere-designer.rc.o
+tile_RC = tile.rc.o
+tile_small_RC = tile-small.rc.o
+unit_editor_RC = unit-editor.rc.o
+van_gogh_lic_RC = van-gogh-lic.rc.o
+warp_RC = warp.rc.o
+wavelet_decompose_RC = wavelet-decompose.rc.o
+web_browser_RC = web-browser.rc.o
+web_page_RC = web-page.rc.o
diff --git a/plug-ins/common/goat-exercise.c b/plug-ins/common/goat-exercise.c
new file mode 100644
index 0000000..b01d938
--- /dev/null
+++ b/plug-ins/common/goat-exercise.c
@@ -0,0 +1,119 @@
+/*
+ * Goat exercise plug-in by Øyvind Kolås, pippin@gimp.org
+ */
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-goat-exercise"
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Exercise a goat"),
+ "takes a goat for a walk",
+ "Øyvind Kolås <pippin@gimp.org>",
+ "Øyvind Kolås <pippin@gimp.org>",
+ "21march 2012",
+ N_("Goat-e_xercise"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 drawable_id;
+ gint x, y, width, height;
+
+ INIT_I18N();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ drawable_id = param[2].data.d_drawable;
+
+ if (gimp_drawable_mask_intersect (drawable_id, &x, &y, &width, &height))
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *shadow_buffer;
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+ shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ gegl_render_op (buffer, shadow_buffer, "gegl:invert", NULL);
+
+ g_object_unref (shadow_buffer); /* flushes the shadow tiles */
+ g_object_unref (buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x, y, width, height);
+ gimp_displays_flush ();
+ }
+
+ values[0].data.d_status = status;
+ gegl_exit ();
+}
diff --git a/plug-ins/common/gradient-map.c b/plug-ins/common/gradient-map.c
new file mode 100644
index 0000000..94c3f00
--- /dev/null
+++ b/plug-ins/common/gradient-map.c
@@ -0,0 +1,412 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Gradient Map plug-in
+ * Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+#define GRADMAP_PROC "plug-in-gradmap"
+#define PALETTEMAP_PROC "plug-in-palettemap"
+#define PLUG_IN_BINARY "gradient-map"
+#define PLUG_IN_ROLE "gimp-gradient-map"
+#define NSAMPLES 2048
+
+typedef enum
+ {
+ GRADIENT_MODE = 1,
+ PALETTE_MODE
+ } MapMode;
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static void map (GeglBuffer *buffer,
+ GeglBuffer *shadow_buffer,
+ gint32 drawable_id,
+ MapMode mode);
+static gdouble * get_samples_gradient (gint32 drawable_id);
+static gdouble * get_samples_palette (gint32 drawable_id);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[]=
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (GRADMAP_PROC,
+ N_("Recolor the image using colors from the active gradient"),
+ "This plug-in maps the contents of the specified "
+ "drawable with active gradient. It calculates "
+ "luminosity of each pixel and replaces the pixel "
+ "by the sample of active gradient at the position "
+ "proportional to that luminosity. Complete black "
+ "pixel becomes the leftmost color of the gradient, "
+ "and complete white becomes the rightmost. Works on "
+ "both Grayscale and RGB image with/without alpha "
+ "channel.",
+ "Eiichi Takamori",
+ "Eiichi Takamori",
+ "1997",
+ N_("_Gradient Map"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (GRADMAP_PROC, "<Image>/Colors/Map");
+
+ gimp_install_procedure (PALETTEMAP_PROC,
+ N_("Recolor the image using colors from the active palette"),
+ "This plug-in maps the contents of the specified "
+ "drawable with the active palette. It calculates "
+ "luminosity of each pixel and replaces the pixel "
+ "by the palette sample at the corresponding "
+ "index. A complete black "
+ "pixel becomes the lowest palette entry, "
+ "and complete white becomes the highest. Works on "
+ "both Grayscale and RGB image with/without alpha "
+ "channel.",
+ "Bill Skaggs",
+ "Bill Skaggs",
+ "2004",
+ N_("_Palette Map"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PALETTEMAP_PROC, "<Image>/Colors/Map");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GeglBuffer *shadow_buffer;
+ GeglBuffer *buffer;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ /* Get the specified drawable */
+ shadow_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id))
+ {
+ MapMode mode = 0;
+
+ if ( !strcmp (name, GRADMAP_PROC))
+ {
+ mode = GRADIENT_MODE;
+ gimp_progress_init (_("Gradient Map"));
+ }
+ else if ( !strcmp (name, PALETTEMAP_PROC))
+ {
+ mode = PALETTE_MODE;
+ gimp_progress_init (_("Palette Map"));
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (mode)
+ map (buffer, shadow_buffer, drawable_id, mode);
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ g_object_unref (buffer);
+ g_object_unref (shadow_buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+
+ gimp_drawable_update (drawable_id, 0, 0,
+ gimp_drawable_width (drawable_id),
+ gimp_drawable_height (drawable_id));
+
+ values[0].data.d_status = status;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+}
+
+static void
+map (GeglBuffer *buffer,
+ GeglBuffer *shadow_buffer,
+ gint32 drawable_id,
+ MapMode mode)
+{
+ GeglBufferIterator *gi;
+ gint nb_color_chan;
+ gint nb_chan;
+ gint nb_chan2;
+ gint nb_chan_samp;
+ gint index_iter;
+ gboolean interpolate;
+ gdouble *samples;
+ gboolean is_rgb;
+ gboolean has_alpha;
+ const Babl *format_shadow;
+ const Babl *format_buffer;
+
+ is_rgb = gimp_drawable_is_rgb (drawable_id);
+ has_alpha = gimp_drawable_has_alpha (drawable_id);
+
+ switch (mode)
+ {
+ case GRADIENT_MODE:
+ samples = get_samples_gradient (drawable_id);
+ interpolate = TRUE;
+ break;
+ case PALETTE_MODE:
+ samples = get_samples_palette (drawable_id);
+ interpolate = FALSE;
+ break;
+ default:
+ g_error ("plug_in_gradmap: invalid mode");
+ }
+
+ if (is_rgb)
+ {
+ nb_color_chan = 3;
+ nb_chan_samp = 4;
+ if (has_alpha)
+ format_shadow = babl_format ("R'G'B'A float");
+ else
+ format_shadow = babl_format ("R'G'B' float");
+ }
+ else
+ {
+ nb_color_chan = 1;
+ nb_chan_samp = 2;
+ if (has_alpha)
+ format_shadow = babl_format ("Y'A float");
+ else
+ format_shadow = babl_format ("Y' float");
+ }
+
+
+ if (has_alpha)
+ {
+ nb_chan = nb_color_chan + 1;
+ nb_chan2 = 2;
+ format_buffer = babl_format ("Y'A float");
+ }
+ else
+ {
+ nb_chan = nb_color_chan;
+ nb_chan2 = 1;
+ format_buffer = babl_format ("Y' float");
+ }
+
+ gi = gegl_buffer_iterator_new (shadow_buffer, NULL, 0, format_shadow,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
+
+ index_iter = gegl_buffer_iterator_add (gi, buffer, NULL,
+ 0, format_buffer,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (gi))
+ {
+ guint k;
+ gfloat *data;
+ gfloat *data2;
+
+ data = (gfloat*) gi->items[0].data;
+ data2 = (gfloat*) gi->items[index_iter].data;
+
+ if (interpolate)
+ {
+ for (k = 0; k < gi->length; k++)
+ {
+ gint b, ind1, ind2;
+ gdouble *samp1, *samp2;
+ gfloat c1, c2, val;
+
+ val = data2[0] * (NSAMPLES-1);
+
+ ind1 = CLAMP (floor (val), 0, NSAMPLES-1);
+ ind2 = CLAMP (ceil (val), 0, NSAMPLES-1);
+
+ c1 = 1.0 - (val - ind1);
+ c2 = 1.0 - c1;
+
+ samp1 = &(samples[ind1 * nb_chan_samp]);
+ samp2 = &(samples[ind2 * nb_chan_samp]);
+
+ for (b = 0; b < nb_color_chan; b++)
+ data[b] = (samp1[b] * c1 + samp2[b] * c2);
+
+ if (has_alpha)
+ {
+ float alpha = (samp1[b] * c1 + samp2[b] * c2);
+ data[b] = alpha * data2[1];
+ }
+
+ data += nb_chan;
+ data2 += nb_chan2;
+ }
+ }
+ else
+ {
+ for (k = 0; k < gi->length; k++)
+ {
+ gint b, ind;
+ gdouble *samp;
+ ind = CLAMP (data2[0] * (NSAMPLES-1), 0, NSAMPLES-1);
+
+ samp = &(samples[ind * nb_chan_samp]);
+
+ for (b = 0; b < nb_color_chan; b++)
+ data[b] = samp[b];
+
+ if (has_alpha)
+ {
+ data[b] = samp[b] * data2[1];
+ }
+
+ data += nb_chan;
+ data2 += nb_chan2;
+ }
+ }
+ }
+
+ g_free (samples);
+}
+
+/*
+ Returns 2048 samples of the gradient.
+ Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
+ */
+static gdouble *
+get_samples_gradient (gint32 drawable_id)
+{
+ gchar *gradient_name;
+ gint n_d_samples;
+ gdouble *d_samples = NULL;
+
+ gradient_name = gimp_context_get_gradient ();
+
+ /* FIXME: "reverse" hardcoded to FALSE. */
+ gimp_gradient_get_uniform_samples (gradient_name, NSAMPLES, FALSE,
+ &n_d_samples, &d_samples);
+ g_free (gradient_name);
+
+ if (!gimp_drawable_is_rgb (drawable_id))
+ {
+ const Babl *format_src = babl_format ("R'G'B'A double");
+ const Babl *format_dst = babl_format ("Y'A double");
+ const Babl *fish = babl_fish (format_src, format_dst);
+ babl_process (fish, d_samples, d_samples, NSAMPLES);
+ }
+
+ return d_samples;
+}
+
+/*
+ Returns 2048 samples of the palette.
+ Each sample is (R'G'B'A float) or (Y'A float), depending on the drawable
+ */
+static gdouble *
+get_samples_palette (gint32 drawable_id)
+{
+ gchar *palette_name;
+ GimpRGB color_sample;
+ gdouble *d_samples, *d_samp;
+ gboolean is_rgb;
+ gdouble factor;
+ gint pal_entry, num_colors;
+ gint nb_color_chan, nb_chan, i;
+ const Babl *format;
+
+ palette_name = gimp_context_get_palette ();
+ gimp_palette_get_info (palette_name, &num_colors);
+
+ is_rgb = gimp_drawable_is_rgb (drawable_id);
+
+ factor = ((double) num_colors) / NSAMPLES;
+ format = is_rgb ? babl_format ("R'G'B'A double") : babl_format ("Y'A double");
+ nb_color_chan = is_rgb ? 3 : 1;
+ nb_chan = nb_color_chan + 1;
+
+ d_samples = g_new (gdouble, NSAMPLES * nb_chan);
+
+ for (i = 0; i < NSAMPLES; i++)
+ {
+ d_samp = &d_samples[i * nb_chan];
+ pal_entry = CLAMP ((int)(i * factor), 0, num_colors - 1);
+
+ gimp_palette_entry_get_color (palette_name, pal_entry, &color_sample);
+ gimp_rgb_get_pixel (&color_sample,
+ format,
+ d_samp);
+ }
+
+ g_free (palette_name);
+ return d_samples;
+}
diff --git a/plug-ins/common/grid.c b/plug-ins/common/grid.c
new file mode 100644
index 0000000..7abdf2f
--- /dev/null
+++ b/plug-ins/common/grid.c
@@ -0,0 +1,1011 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Original plug-in coded by Tim Newsome.
+ *
+ * Changed to make use of real-life units by Sven Neumann <sven@gimp.org>.
+ *
+ * The interface code is heavily commented in the hope that it will
+ * help other plug-in developers to adapt their plug-ins to make use
+ * of the gimp_size_entry functionality.
+ *
+ * Note: There is a convenience constructor called gimp_coordinetes_new ()
+ * which simplifies the task of setting up a standard X,Y sizeentry.
+ *
+ * For more info and bugs see libgimp/gimpsizeentry.h and libgimp/gimpwidgets.h
+ *
+ * May 2000 tim copperfield [timecop@japan.co.jp]
+ * http://www.ne.jp/asahi/linux/timecop
+ * Added dynamic preview. Due to weird implementation of signals from all
+ * controls, preview will not auto-update. But this plugin isn't really
+ * crying for real-time updating either.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-grid"
+#define PLUG_IN_BINARY "grid"
+#define PLUG_IN_ROLE "gimp-grid"
+#define SPIN_BUTTON_WIDTH 8
+#define COLOR_BUTTON_WIDTH 55
+
+
+/* Declare local functions. */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static guchar best_cmap_match (const guchar *cmap,
+ gint ncolors,
+ const GimpRGB *color);
+static void grid (gint32 image_ID,
+ gint32 drawable_ID,
+ GimpPreview *preview);
+static gint dialog (gint32 image_ID,
+ gint32 drawable_ID);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gint sx1, sy1, sx2, sy2;
+
+static GtkWidget *main_dialog = NULL;
+static GtkWidget *hcolor_button = NULL;
+static GtkWidget *vcolor_button = NULL;
+
+typedef struct
+{
+ gint hwidth;
+ gint hspace;
+ gint hoffset;
+ GimpRGB hcolor;
+ gint vwidth;
+ gint vspace;
+ gint voffset;
+ GimpRGB vcolor;
+ gint iwidth;
+ gint ispace;
+ gint ioffset;
+ GimpRGB icolor;
+} Config;
+
+static Config grid_cfg =
+{
+ 1, 16, 8, { 0.0, 0.0, 0.0, 1.0 }, /* horizontal */
+ 1, 16, 8, { 0.0, 0.0, 0.0, 1.0 }, /* vertical */
+ 0, 2, 6, { 0.0, 0.0, 0.0, 1.0 }, /* intersection */
+};
+
+
+MAIN ()
+
+static
+void query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+
+ { GIMP_PDB_INT32, "hwidth", "Horizontal Width (>= 0)" },
+ { GIMP_PDB_INT32, "hspace", "Horizontal Spacing (>= 1)" },
+ { GIMP_PDB_INT32, "hoffset", "Horizontal Offset (>= 0)" },
+ { GIMP_PDB_COLOR, "hcolor", "Horizontal Colour" },
+ { GIMP_PDB_INT8, "hopacity", "Horizontal Opacity (0...255)" },
+
+ { GIMP_PDB_INT32, "vwidth", "Vertical Width (>= 0)" },
+ { GIMP_PDB_INT32, "vspace", "Vertical Spacing (>= 1)" },
+ { GIMP_PDB_INT32, "voffset", "Vertical Offset (>= 0)" },
+ { GIMP_PDB_COLOR, "vcolor", "Vertical Colour" },
+ { GIMP_PDB_INT8, "vopacity", "Vertical Opacity (0...255)" },
+
+ { GIMP_PDB_INT32, "iwidth", "Intersection Width (>= 0)" },
+ { GIMP_PDB_INT32, "ispace", "Intersection Spacing (>= 0)" },
+ { GIMP_PDB_INT32, "ioffset", "Intersection Offset (>= 0)" },
+ { GIMP_PDB_COLOR, "icolor", "Intersection Colour" },
+ { GIMP_PDB_INT8, "iopacity", "Intersection Opacity (0...255)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Draw a grid on the image"),
+ "Draws a grid using the specified colors. "
+ "The grid origin is the upper left corner.",
+ "Tim Newsome",
+ "Tim Newsome, Sven Neumann, Tom Rathborne, TC",
+ "1997 - 2000",
+ N_("_Grid (legacy)..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ if (n_params != 18)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ grid_cfg.hwidth = MAX (0, param[3].data.d_int32);
+ grid_cfg.hspace = MAX (1, param[4].data.d_int32);
+ grid_cfg.hoffset = MAX (0, param[5].data.d_int32);
+ grid_cfg.hcolor = param[6].data.d_color;
+
+ gimp_rgb_set_alpha (&(grid_cfg.hcolor),
+ ((double) param[7].data.d_int8) / 255.0);
+
+
+ grid_cfg.vwidth = MAX (0, param[8].data.d_int32);
+ grid_cfg.vspace = MAX (1, param[9].data.d_int32);
+ grid_cfg.voffset = MAX (0, param[10].data.d_int32);
+ grid_cfg.vcolor = param[11].data.d_color;
+
+ gimp_rgb_set_alpha (&(grid_cfg.vcolor),
+ ((double) param[12].data.d_int8) / 255.0);
+
+
+
+ grid_cfg.iwidth = MAX (0, param[13].data.d_int32);
+ grid_cfg.ispace = MAX (0, param[14].data.d_int32);
+ grid_cfg.ioffset = MAX (0, param[15].data.d_int32);
+ grid_cfg.icolor = param[16].data.d_color;
+
+ gimp_rgb_set_alpha (&(grid_cfg.icolor),
+ ((double) (guint) param[17].data.d_int8) / 255.0);
+
+
+ }
+ }
+ else
+ {
+ gimp_context_get_foreground (&grid_cfg.hcolor);
+ grid_cfg.vcolor = grid_cfg.icolor = grid_cfg.hcolor;
+
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &grid_cfg);
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! dialog (image_ID, drawable_ID))
+ {
+ /* The dialog was closed, or something similarly evil happened. */
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (grid_cfg.hspace <= 0 || grid_cfg.vspace <= 0)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Drawing grid"));
+
+ grid (image_ID, drawable_ID, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &grid_cfg, sizeof (grid_cfg));
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+
+#define MAXDIFF 195076
+
+static guchar
+best_cmap_match (const guchar *cmap,
+ gint ncolors,
+ const GimpRGB *color)
+{
+ guchar cmap_index = 0;
+ gint max = MAXDIFF;
+ gint i, diff, sum;
+ guchar r, g, b;
+
+ gimp_rgb_get_uchar (color, &r, &g, &b);
+
+ for (i = 0; i < ncolors; i++)
+ {
+ diff = r - *cmap++;
+ sum = SQR (diff);
+ diff = g - *cmap++;
+ sum += SQR (diff);
+ diff = b - *cmap++;
+ sum += SQR (diff);
+
+ if (sum < max)
+ {
+ cmap_index = i;
+ max = sum;
+ }
+ }
+
+ return cmap_index;
+}
+
+static inline void
+pix_composite (guchar *p1,
+ guchar p2[4],
+ gint bytes,
+ gboolean blend,
+ gboolean alpha)
+{
+ gint b;
+
+ if (blend)
+ {
+ if (alpha)
+ bytes--;
+
+ for (b = 0; b < bytes; b++)
+ {
+ *p1 = *p1 * (1.0 - p2[3]/255.0) + p2[b] * p2[3]/255.0;
+ p1++;
+ }
+ }
+ else
+ {
+ /* blend should only be TRUE for indexed (bytes == 1) */
+ *p1++ = *p2;
+ }
+
+ if (alpha && *p1 < 255)
+ {
+ b = *p1 + 255.0 * ((gdouble) p2[3] / (255.0 - *p1));
+
+ *p1 = b > 255 ? 255 : b;
+ }
+}
+
+static void
+grid (gint32 image_ID,
+ gint32 drawable_ID,
+ GimpPreview *preview)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ gint bytes;
+ gint x_offset;
+ gint y_offset;
+ guchar *dest;
+ guchar *buffer = NULL;
+ gint x, y;
+ gboolean alpha;
+ gboolean blend;
+ guchar hcolor[4];
+ guchar vcolor[4];
+ guchar icolor[4];
+ guchar *cmap;
+ gint ncolors;
+
+ gimp_rgba_get_uchar (&grid_cfg.hcolor,
+ hcolor, hcolor + 1, hcolor + 2, hcolor + 3);
+ gimp_rgba_get_uchar (&grid_cfg.vcolor,
+ vcolor, vcolor + 1, vcolor + 2, vcolor + 3);
+ gimp_rgba_get_uchar (&grid_cfg.icolor,
+ icolor, icolor + 1, icolor + 2, icolor + 3);
+
+ alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ switch (gimp_image_base_type (image_ID))
+ {
+ case GIMP_RGB:
+ blend = TRUE;
+
+ if (alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAY:
+ hcolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.hcolor);
+ vcolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.vcolor);
+ icolor[0] = gimp_rgb_luminance_uchar (&grid_cfg.icolor);
+ blend = TRUE;
+
+ if (alpha)
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_INDEXED:
+ cmap = gimp_image_get_colormap (image_ID, &ncolors);
+
+ hcolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.hcolor);
+ vcolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.vcolor);
+ icolor[0] = best_cmap_match (cmap, ncolors, &grid_cfg.icolor);
+
+ g_free (cmap);
+ blend = FALSE;
+
+ format = gimp_drawable_get_format (drawable_ID);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ blend = FALSE;
+ }
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &sx1, &sy1);
+ gimp_preview_get_size (preview, &sx2, &sy2);
+
+ buffer = g_new (guchar, bytes * sx2 * sy2);
+
+ sx2 += sx1;
+ sy2 += sy1;
+ }
+ else
+ {
+ gint w, h;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &sx1, &sy1, &w, &h))
+ return;
+
+ sx2 = sx1 + w;
+ sy2 = sy1 + h;
+
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+ }
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ dest = g_new (guchar, (sx2 - sx1) * bytes);
+
+ for (y = sy1; y < sy2; y++)
+ {
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (sx1, y, sx2 - sx1, 1), 1.0,
+ format, dest,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ y_offset = y - grid_cfg.hoffset;
+ while (y_offset < 0)
+ y_offset += grid_cfg.hspace;
+
+ if ((y_offset +
+ (grid_cfg.hwidth / 2)) % grid_cfg.hspace < grid_cfg.hwidth)
+ {
+ for (x = sx1; x < sx2; x++)
+ {
+ pix_composite (&dest[(x-sx1) * bytes],
+ hcolor, bytes, blend, alpha);
+ }
+ }
+
+ for (x = sx1; x < sx2; x++)
+ {
+ x_offset = grid_cfg.vspace + x - grid_cfg.voffset;
+ while (x_offset < 0)
+ x_offset += grid_cfg.vspace;
+
+ if ((x_offset +
+ (grid_cfg.vwidth / 2)) % grid_cfg.vspace < grid_cfg.vwidth)
+ {
+ pix_composite (&dest[(x-sx1) * bytes],
+ vcolor, bytes, blend, alpha);
+ }
+
+ if ((x_offset +
+ (grid_cfg.iwidth / 2)) % grid_cfg.vspace < grid_cfg.iwidth
+ &&
+ ((y_offset % grid_cfg.hspace >= grid_cfg.ispace
+ &&
+ y_offset % grid_cfg.hspace < grid_cfg.ioffset)
+ ||
+ (grid_cfg.hspace -
+ (y_offset % grid_cfg.hspace) >= grid_cfg.ispace
+ &&
+ grid_cfg.hspace -
+ (y_offset % grid_cfg.hspace) < grid_cfg.ioffset)))
+ {
+ pix_composite (&dest[(x-sx1) * bytes],
+ icolor, bytes, blend, alpha);
+ }
+ }
+
+ if ((y_offset +
+ (grid_cfg.iwidth / 2)) % grid_cfg.hspace < grid_cfg.iwidth)
+ {
+ for (x = sx1; x < sx2; x++)
+ {
+ x_offset = grid_cfg.vspace + x - grid_cfg.voffset;
+ while (x_offset < 0)
+ x_offset += grid_cfg.vspace;
+
+ if ((x_offset % grid_cfg.vspace >= grid_cfg.ispace
+ &&
+ x_offset % grid_cfg.vspace < grid_cfg.ioffset)
+ ||
+ (grid_cfg.vspace -
+ (x_offset % grid_cfg.vspace) >= grid_cfg.ispace
+ &&
+ grid_cfg.vspace -
+ (x_offset % grid_cfg.vspace) < grid_cfg.ioffset))
+ {
+ pix_composite (&dest[(x-sx1) * bytes],
+ icolor, bytes, blend, alpha);
+ }
+ }
+ }
+
+ if (preview)
+ {
+ memcpy (buffer + (y - sy1) * (sx2 - sx1) * bytes,
+ dest,
+ (sx2 - sx1) * bytes);
+ }
+ else
+ {
+ gegl_buffer_set (dest_buffer,
+ GEGL_RECTANGLE (sx1, y, sx2 - sx1, 1), 0,
+ format, dest,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (y % 16 == 0)
+ gimp_progress_update ((gdouble) y / (gdouble) (sy2 - sy1));
+ }
+ }
+
+ g_free (dest);
+
+ g_object_unref (src_buffer);
+
+ if (preview)
+ {
+ gimp_preview_draw_buffer (preview, buffer, bytes * (sx2 - sx1));
+ g_free (buffer);
+ }
+ else
+ {
+ gimp_progress_update (1.0);
+
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID,
+ sx1, sy1, sx2 - sx1, sy2 - sy1);
+ }
+}
+
+
+/***************************************************
+ * GUI stuff
+ */
+
+
+static void
+update_values (void)
+{
+ GtkWidget *entry;
+
+ entry = g_object_get_data (G_OBJECT (main_dialog), "width");
+
+ grid_cfg.hwidth =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
+ grid_cfg.vwidth =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
+ grid_cfg.iwidth =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
+
+ entry = g_object_get_data (G_OBJECT (main_dialog), "space");
+
+ grid_cfg.hspace =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
+ grid_cfg.vspace =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
+ grid_cfg.ispace =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
+
+ entry = g_object_get_data (G_OBJECT (main_dialog), "offset");
+
+ grid_cfg.hoffset =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 0));
+ grid_cfg.voffset =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 1));
+ grid_cfg.ioffset =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (entry), 2));
+}
+
+static void
+update_preview (GimpPreview *preview,
+ gpointer drawable_ID)
+{
+ update_values ();
+
+ grid (gimp_item_get_image (GPOINTER_TO_INT (drawable_ID)),
+ GPOINTER_TO_INT (drawable_ID),
+ preview);
+}
+
+static void
+entry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ static gdouble x = -1.0;
+ static gdouble y = -1.0;
+ gdouble new_x;
+ gdouble new_y;
+
+ new_x = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0);
+ new_y = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1);
+
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
+ {
+ if (new_x != x)
+ {
+ y = new_y = x = new_x;
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 1, y);
+ }
+ if (new_y != y)
+ {
+ x = new_x = y = new_y;
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (widget), 0, x);
+ }
+ }
+ else
+ {
+ x = new_x;
+ y = new_y;
+ }
+}
+
+static void
+color_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (data)))
+ {
+ GimpRGB color;
+
+ gimp_color_button_get_color (GIMP_COLOR_BUTTON (widget), &color);
+
+ if (widget == vcolor_button)
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (hcolor_button), &color);
+ else if (widget == hcolor_button)
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (vcolor_button), &color);
+ }
+}
+
+
+static gint
+dialog (gint32 image_ID,
+ gint32 drawable_ID)
+{
+ GimpColorConfig *config;
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkSizeGroup *group;
+ GtkWidget *label;
+ GtkWidget *preview;
+ GtkWidget *button;
+ GtkWidget *width;
+ GtkWidget *space;
+ GtkWidget *offset;
+ GtkWidget *chain_button;
+ GtkWidget *table;
+ GimpUnit unit;
+ gint d_width;
+ gint d_height;
+ gdouble xres;
+ gdouble yres;
+ gboolean run;
+
+ g_return_val_if_fail (main_dialog == NULL, FALSE);
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ d_width = gimp_drawable_width (drawable_ID);
+ d_height = gimp_drawable_height (drawable_ID);
+
+ main_dialog = dlg = gimp_dialog_new (_("Grid"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ /* Get the image resolution and unit */
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+ unit = gimp_image_get_unit (image_ID);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect (preview, "invalidated",
+ G_CALLBACK (update_preview),
+ GINT_TO_POINTER (drawable_ID));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* The width entries */
+ width = gimp_size_entry_new (3, /* number_of_fields */
+ unit, /* unit */
+ "%a", /* unit_format */
+ TRUE, /* menu_show_pixels */
+ TRUE, /* menu_show_percent */
+ FALSE, /* show_refval */
+ SPIN_BUTTON_WIDTH, /* spinbutton_usize */
+ GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
+
+
+ gtk_box_pack_start (GTK_BOX (vbox), width, FALSE, FALSE, 0);
+ gtk_widget_show (width);
+
+ /* set the unit back to pixels, since most times we will want pixels */
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (width), GIMP_UNIT_PIXEL);
+
+ /* set the resolution to the image resolution */
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 1, yres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (width), 2, xres, TRUE);
+
+ /* set the size (in pixels) that will be treated as 0% and 100% */
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 0, 0.0, d_height);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 1, 0.0, d_width);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (width), 2, 0.0, d_width);
+
+ /* set upper and lower limits (in pixels) */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 0, 0.0,
+ d_height);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 1, 0.0,
+ d_width);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (width), 2, 0.0,
+ MAX (d_width, d_height));
+ gtk_table_set_row_spacing (GTK_TABLE (width), 0, 6);
+ gtk_table_set_col_spacings (GTK_TABLE (width), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (width), 2, 12);
+
+ /* initialize the values */
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 0, grid_cfg.hwidth);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 1, grid_cfg.vwidth);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (width), 2, grid_cfg.iwidth);
+
+ /* attach labels */
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Horizontal\nLines"),
+ 0, 1, 0.0);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Vertical\nLines"),
+ 0, 2, 0.0);
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Intersection"),
+ 0, 3, 0.0);
+
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (width), _("Width:"),
+ 1, 0, 0.0);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (group, label);
+ g_object_unref (group);
+
+ /* put a chain_button under the size_entries */
+ chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
+ if (grid_cfg.hwidth == grid_cfg.vwidth)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (width), chain_button, 1, 3, 2, 3);
+ gtk_widget_show (chain_button);
+
+ /* connect to the 'value-changed' signal because we have to take care
+ * of keeping the entries in sync when the chainbutton is active
+ */
+ g_signal_connect (width, "value-changed",
+ G_CALLBACK (entry_callback),
+ chain_button);
+ g_signal_connect_swapped (width, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* The spacing entries */
+ space = gimp_size_entry_new (3, /* number_of_fields */
+ unit, /* unit */
+ "%a", /* unit_format */
+ TRUE, /* menu_show_pixels */
+ TRUE, /* menu_show_percent */
+ FALSE, /* show_refval */
+ SPIN_BUTTON_WIDTH, /* spinbutton_usize */
+ GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
+
+ gtk_box_pack_start (GTK_BOX (vbox), space, FALSE, FALSE, 0);
+ gtk_widget_show (space);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (space), GIMP_UNIT_PIXEL);
+
+ /* set the resolution to the image resolution */
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 1, yres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (space), 2, xres, TRUE);
+
+ /* set the size (in pixels) that will be treated as 0% and 100% */
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 0, 0.0, d_height);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 1, 0.0, d_width);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (space), 2, 0.0, d_width);
+
+ /* set upper and lower limits (in pixels) */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 0, 1.0,
+ d_height);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 1, 1.0,
+ d_width);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (space), 2, 0.0,
+ MAX (d_width, d_height));
+ gtk_table_set_col_spacings (GTK_TABLE (space), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (space), 2, 12);
+
+ /* initialize the values */
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 0, grid_cfg.hspace);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 1, grid_cfg.vspace);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (space), 2, grid_cfg.ispace);
+
+ /* attach labels */
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (space), _("Spacing:"),
+ 1, 0, 0.0);
+ gtk_size_group_add_widget (group, label);
+
+ /* put a chain_button under the spacing_entries */
+ chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
+ if (grid_cfg.hspace == grid_cfg.vspace)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (space), chain_button, 1, 3, 2, 3);
+ gtk_widget_show (chain_button);
+
+ /* connect to the 'value-changed' and "unit-changed" signals because
+ * we have to take care of keeping the entries in sync when the
+ * chainbutton is active
+ */
+ g_signal_connect (space, "value-changed",
+ G_CALLBACK (entry_callback),
+ chain_button);
+ g_signal_connect (space, "unit-changed",
+ G_CALLBACK (entry_callback),
+ chain_button);
+ g_signal_connect_swapped (space, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* The offset entries */
+ offset = gimp_size_entry_new (3, /* number_of_fields */
+ unit, /* unit */
+ "%a", /* unit_format */
+ TRUE, /* menu_show_pixels */
+ TRUE, /* menu_show_percent */
+ FALSE, /* show_refval */
+ SPIN_BUTTON_WIDTH, /* spinbutton_usize */
+ GIMP_SIZE_ENTRY_UPDATE_SIZE); /* update_policy */
+
+ gtk_box_pack_start (GTK_BOX (vbox), offset, FALSE, FALSE, 0);
+ gtk_widget_show (offset);
+
+ gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (offset), GIMP_UNIT_PIXEL);
+
+ /* set the resolution to the image resolution */
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 0, xres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 1, yres, TRUE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (offset), 2, xres, TRUE);
+
+ /* set the size (in pixels) that will be treated as 0% and 100% */
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 0, 0.0, d_height);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 1, 0.0, d_width);
+ gimp_size_entry_set_size (GIMP_SIZE_ENTRY (offset), 2, 0.0, d_width);
+
+ /* set upper and lower limits (in pixels) */
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 0, 0.0,
+ d_height);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 1, 0.0,
+ d_width);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (offset), 2, 0.0,
+ MAX (d_width, d_height));
+ gtk_table_set_col_spacings (GTK_TABLE (offset), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (offset), 2, 12);
+
+ /* initialize the values */
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 0, grid_cfg.hoffset);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 1, grid_cfg.voffset);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (offset), 2, grid_cfg.ioffset);
+
+ /* attach labels */
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (offset), _("Offset:"),
+ 1, 0, 0.0);
+ gtk_size_group_add_widget (group, label);
+
+ /* this is a weird hack: we put a table into the offset table */
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_attach_defaults (GTK_TABLE (offset), table, 1, 4, 2, 3);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 10);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+
+ /* put a chain_button under the offset_entries */
+ chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
+ if (grid_cfg.hoffset == grid_cfg.voffset)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 0, 1);
+ gtk_widget_show (chain_button);
+
+ /* connect to the 'value-changed' and "unit-changed" signals because
+ * we have to take care of keeping the entries in sync when the
+ * chainbutton is active
+ */
+ g_signal_connect (offset, "value-changed",
+ G_CALLBACK (entry_callback),
+ chain_button);
+ g_signal_connect (offset, "unit-changed",
+ G_CALLBACK (entry_callback),
+ chain_button);
+ g_signal_connect_swapped (offset, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* put a chain_button under the color_buttons */
+ chain_button = gimp_chain_button_new (GIMP_CHAIN_BOTTOM);
+ if (gimp_rgba_distance (&grid_cfg.hcolor, &grid_cfg.vcolor) < 0.0001)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), chain_button, 0, 2, 2, 3);
+ gtk_widget_show (chain_button);
+
+ /* attach color selectors */
+ hcolor_button = gimp_color_button_new (_("Horizontal Color"),
+ COLOR_BUTTON_WIDTH, 16,
+ &grid_cfg.hcolor,
+ GIMP_COLOR_AREA_SMALL_CHECKS);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (hcolor_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), hcolor_button, 0, 1, 1, 2);
+ gtk_widget_show (hcolor_button);
+
+ config = gimp_get_color_configuration ();
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (hcolor_button),
+ config);
+
+ g_signal_connect (hcolor_button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &grid_cfg.hcolor);
+ g_signal_connect (hcolor_button, "color-changed",
+ G_CALLBACK (color_callback),
+ chain_button);
+ g_signal_connect_swapped (hcolor_button, "color-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ vcolor_button = gimp_color_button_new (_("Vertical Color"),
+ COLOR_BUTTON_WIDTH, 16,
+ &grid_cfg.vcolor,
+ GIMP_COLOR_AREA_SMALL_CHECKS);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (vcolor_button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), vcolor_button, 1, 2, 1, 2);
+ gtk_widget_show (vcolor_button);
+
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (vcolor_button),
+ config);
+
+ g_signal_connect (vcolor_button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &grid_cfg.vcolor);
+ g_signal_connect (vcolor_button, "color-changed",
+ G_CALLBACK (color_callback),
+ chain_button);
+ g_signal_connect_swapped (vcolor_button, "color-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ button = gimp_color_button_new (_("Intersection Color"),
+ COLOR_BUTTON_WIDTH, 16,
+ &grid_cfg.icolor,
+ GIMP_COLOR_AREA_SMALL_CHECKS);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 3, 1, 2);
+ gtk_widget_show (button);
+
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (button),
+ config);
+ g_object_unref (config);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &grid_cfg.icolor);
+ g_signal_connect_swapped (button, "color-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (table);
+
+ gtk_widget_show (dlg);
+
+ g_object_set_data (G_OBJECT (dlg), "width", width);
+ g_object_set_data (G_OBJECT (dlg), "space", space);
+ g_object_set_data (G_OBJECT (dlg), "offset", offset);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ if (run)
+ update_values ();
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
diff --git a/plug-ins/common/guillotine.c b/plug-ins/common/guillotine.c
new file mode 100644
index 0000000..ad3ad3f
--- /dev/null
+++ b/plug-ins/common/guillotine.c
@@ -0,0 +1,305 @@
+/*
+ * Guillotine plug-in v0.9 by Adam D. Moss, adam@foxbox.org. 1998/09/01
+ */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-guillotine"
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GList * guillotine (gint32 image_ID,
+ gboolean interactive);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" }
+ };
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_INT32, "image-count", "Number of images created" },
+ { GIMP_PDB_INT32ARRAY, "image-ids", "Output images" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Slice the image into subimages using guides"),
+ "This function takes an image and slices it along "
+ "its guides, creating new images. The original "
+ "image is not modified.",
+ "Adam D. Moss (adam@foxbox.org)",
+ "Adam D. Moss (adam@foxbox.org)",
+ "1998",
+ N_("Slice Using G_uides"),
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Crop");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+ GimpRunMode run_mode = param[0].data.d_int32;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 3;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = 0;
+ values[2].type = GIMP_PDB_INT32ARRAY;
+ values[2].data.d_int32array = NULL;
+
+ INIT_I18N();
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GList *images;
+ GList *list;
+ gint i;
+
+ gimp_progress_init (_("Guillotine"));
+
+ images = guillotine (param[1].data.d_image,
+ run_mode == GIMP_RUN_INTERACTIVE);
+
+ values[1].data.d_int32 = g_list_length (images);
+ values[2].data.d_int32array = g_new (gint32, values[1].data.d_int32);
+
+ for (list = images, i = 0; list; list = g_list_next (list), i++)
+ {
+ values[2].data.d_int32array[i] = GPOINTER_TO_INT (list->data);
+ }
+
+ g_list_free (images);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_displays_flush ();
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint
+guide_sort_func (gconstpointer a,
+ gconstpointer b)
+{
+ return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
+}
+
+static GList *
+guillotine (gint32 image_ID,
+ gboolean interactive)
+{
+ GList *images = NULL;
+ gint guide;
+ gint image_width;
+ gint image_height;
+ gboolean guides_found = FALSE;
+ GList *hguides, *hg;
+ GList *vguides, *vg;
+
+ image_width = gimp_image_width (image_ID);
+ image_height = gimp_image_height (image_ID);
+
+ hguides = g_list_append (NULL, GINT_TO_POINTER (0));
+ hguides = g_list_append (hguides, GINT_TO_POINTER (image_height));
+
+ vguides = g_list_append (NULL, GINT_TO_POINTER (0));
+ vguides = g_list_append (vguides, GINT_TO_POINTER (image_width));
+
+ for (guide = gimp_image_find_next_guide (image_ID, 0);
+ guide > 0;
+ guide = gimp_image_find_next_guide (image_ID, guide))
+ {
+ gint position = gimp_image_get_guide_position (image_ID, guide);
+
+ switch (gimp_image_get_guide_orientation (image_ID, guide))
+ {
+ case GIMP_ORIENTATION_HORIZONTAL:
+ if (! g_list_find (hguides, GINT_TO_POINTER (position)))
+ {
+ hguides = g_list_insert_sorted (hguides,
+ GINT_TO_POINTER (position),
+ guide_sort_func);
+ guides_found = TRUE;
+ }
+ break;
+
+ case GIMP_ORIENTATION_VERTICAL:
+ if (! g_list_find (vguides, GINT_TO_POINTER (position)))
+ {
+ vguides = g_list_insert_sorted (vguides,
+ GINT_TO_POINTER (position),
+ guide_sort_func);
+ guides_found = TRUE;
+ }
+ break;
+
+ case GIMP_ORIENTATION_UNKNOWN:
+ g_assert_not_reached ();
+ break;
+ }
+ }
+
+ if (guides_found)
+ {
+ gchar *filename;
+ gint h, v, hpad, vpad;
+ gint x, y;
+ gchar *hformat;
+ gchar *format;
+
+ filename = gimp_image_get_filename (image_ID);
+
+ if (! filename)
+ filename = g_strdup (_("Untitled"));
+
+ /* get the number horizontal and vertical slices */
+ h = g_list_length (hguides);
+ v = g_list_length (vguides);
+
+ /* need the number of digits of h and v for the padding */
+ hpad = log10(h) + 1;
+ vpad = log10(v) + 1;
+
+ /* format for the x-y coordinates in the filename */
+ hformat = g_strdup_printf ("%%0%i", MAX (hpad, vpad));
+ format = g_strdup_printf ("-%si-%si", hformat, hformat);
+
+ /* Do the actual dup'ing and cropping... this isn't a too naive a
+ * way to do this since we got copy-on-write tiles, either.
+ */
+ for (y = 0, hg = hguides; hg && hg->next; y++, hg = hg->next)
+ {
+ for (x = 0, vg = vguides; vg && vg->next; x++, vg = vg->next)
+ {
+ gint32 new_image = gimp_image_duplicate (image_ID);
+ GString *new_filename;
+ gchar *fileextension;
+ gchar *fileindex;
+ gchar *fileindex_with_xcf_extension;
+ gint pos;
+
+ if (new_image == -1)
+ {
+ g_warning ("Couldn't create new image.");
+ g_free (hformat);
+ g_free (format);
+ return images;
+ }
+
+ gimp_image_undo_disable (new_image);
+
+ gimp_image_crop (new_image,
+ GPOINTER_TO_INT (vg->next->data) -
+ GPOINTER_TO_INT (vg->data),
+ GPOINTER_TO_INT (hg->next->data) -
+ GPOINTER_TO_INT (hg->data),
+ GPOINTER_TO_INT (vg->data),
+ GPOINTER_TO_INT (hg->data));
+
+
+ new_filename = g_string_new (filename);
+
+ /* show the rough coordinates of the image in the title */
+ fileindex = g_strdup_printf (format, x, y);
+
+ /* preparation to replace original image extension with GIMP default
+ see issue #8581 for details */
+ fileindex_with_xcf_extension = g_strdup_printf ("%s.xcf", fileindex);
+ g_free (fileindex);
+
+ /* get the position of the file extension - last . in filename */
+ fileextension = strrchr (new_filename->str, '.');
+ pos = fileextension - new_filename->str;
+
+ /* insert the coordinates before the extension */
+ g_string_truncate (new_filename, pos);
+ g_string_insert (new_filename, pos, fileindex_with_xcf_extension);
+ g_free (fileindex_with_xcf_extension);
+
+ gimp_image_set_filename (new_image, new_filename->str);
+ g_string_free (new_filename, TRUE);
+
+ while ((guide = gimp_image_find_next_guide (new_image, 0)))
+ gimp_image_delete_guide (new_image, guide);
+
+ gimp_image_undo_enable (new_image);
+
+ if (interactive)
+ gimp_display_new (new_image);
+
+ images = g_list_prepend (images, GINT_TO_POINTER (new_image));
+ }
+ }
+
+ g_free (filename);
+ g_free (hformat);
+ g_free (format);
+ }
+
+ g_list_free (hguides);
+ g_list_free (vguides);
+
+ return g_list_reverse (images);
+}
diff --git a/plug-ins/common/hot.c b/plug-ins/common/hot.c
new file mode 100644
index 0000000..8ed215e
--- /dev/null
+++ b/plug-ins/common/hot.c
@@ -0,0 +1,782 @@
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * hot.c - Scan an image for pixels with RGB values that will give
+ * "unsafe" values of chrominance signal or composite signal
+ * amplitude when encoded into an NTSC or PAL color signal.
+ * (This happens for certain high-intensity high-saturation colors
+ * that are rare in real scenes, but can easily be present
+ * in synthetic images.)
+ *
+ * Such pixels can be flagged so the user may then choose other
+ * colors. Or, the offending pixels can be made "safe"
+ * in a manner that preserves hue.
+ *
+ * There are two reasonable ways to make a pixel "safe":
+ * We can reduce its intensity (luminance) while leaving
+ * hue and saturation the same. Or, we can reduce saturation
+ * while leaving hue and luminance the same. A #define selects
+ * which strategy to use.
+ *
+ * Note to the user: You must add your own read_pixel() and write_pixel()
+ * routines. You may have to modify pix_decode() and pix_encode().
+ * MAXPIX, WID, and HGT are likely to need modification.
+ */
+
+/*
+ * Originally written as "ikNTSC.c" by Alan Wm Paeth,
+ * University of Waterloo, August, 1985
+ * Updated by Dave Martindale, Imax Systems Corp., December 1990
+ */
+
+/*
+ * Compile time options:
+ *
+ *
+ * CHROMA_LIM is the limit (in IRE units) of the overall
+ * chrominance amplitude; it should be 50 or perhaps
+ * very slightly higher.
+ *
+ * COMPOS_LIM is the maximum amplitude (in IRE units) allowed for
+ * the composite signal. A value of 100 is the maximum
+ * monochrome white, and is always safe. 120 is the absolute
+ * limit for NTSC broadcasting, since the transmitter's carrier
+ * goes to zero with 120 IRE input signal. Generally, 110
+ * is a good compromise - it allows somewhat brighter colors
+ * than 100, while staying safely away from the hard limit.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-hot"
+#define PLUG_IN_BINARY "hot"
+#define PLUG_IN_ROLE "gimp-hot"
+
+
+typedef struct
+{
+ gint32 image;
+ gint32 drawable;
+ gint32 mode;
+ gint32 action;
+ gint32 new_layerp;
+} piArgs;
+
+typedef enum
+{
+ ACT_LREDUX,
+ ACT_SREDUX,
+ ACT_FLAG
+} hotAction;
+
+typedef enum
+{
+ MODE_NTSC,
+ MODE_PAL
+} hotModes;
+
+#define CHROMA_LIM 50.0 /* chroma amplitude limit */
+#define COMPOS_LIM 110.0 /* max IRE amplitude */
+
+/*
+ * RGB to YIQ encoding matrix.
+ */
+
+struct
+{
+ gdouble pedestal;
+ gdouble gamma;
+ gdouble code[3][3];
+} static mode[2] = {
+ {
+ 7.5,
+ 2.2,
+ {
+ { 0.2989, 0.5866, 0.1144 },
+ { 0.5959, -0.2741, -0.3218 },
+ { 0.2113, -0.5227, 0.3113 }
+ }
+ },
+ {
+ 0.0,
+ 2.8,
+ {
+ { 0.2989, 0.5866, 0.1144 },
+ { -0.1473, -0.2891, 0.4364 },
+ { 0.6149, -0.5145, -0.1004 }
+ }
+ }
+};
+
+
+#define SCALE 8192 /* scale factor: do floats with int math */
+#define MAXPIX 255 /* white value */
+
+static gint tab[3][3][MAXPIX+1]; /* multiply lookup table */
+static gdouble chroma_lim; /* chroma limit */
+static gdouble compos_lim; /* composite amplitude limit */
+static glong ichroma_lim2; /* chroma limit squared (scaled integer) */
+static gint icompos_lim; /* composite amplitude limit (scaled integer) */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparam,
+ const GimpParam *param,
+ gint *nretvals,
+ GimpParam **retvals);
+
+static gboolean pluginCore (piArgs *argp);
+static gboolean plugin_dialog (piArgs *argp);
+static gboolean hotp (guint8 r,
+ guint8 g,
+ guint8 b);
+static void build_tab (gint m);
+
+/*
+ * gc: apply the gamma correction specified for this video standard.
+ * inv_gc: inverse function of gc.
+ *
+ * These are generally just a call to pow(), but be careful!
+ * Future standards may use more complex functions.
+ * (e.g. SMPTE 240M's "electro-optic transfer characteristic").
+ */
+#define gc(x,m) pow(x, 1.0 / mode[m].gamma)
+#define inv_gc(x,m) pow(x, mode[m].gamma)
+
+/*
+ * pix_decode: decode an integer pixel value into a floating-point
+ * intensity in the range [0, 1].
+ *
+ * pix_encode: encode a floating-point intensity into an integer
+ * pixel value.
+ *
+ * The code given here assumes simple linear encoding; you must change
+ * these routines if you use a different pixel encoding technique.
+ */
+#define pix_decode(v) ((double)v / (double)MAXPIX)
+#define pix_encode(v) ((int)(v * (double)MAXPIX + 0.5))
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "The Image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "The Drawable" },
+ { GIMP_PDB_INT32, "mode", "Mode { NTSC (0), PAL (1) }" },
+ { GIMP_PDB_INT32, "action", "The action to perform" },
+ { GIMP_PDB_INT32, "new-layer", "Create a new layer { TRUE, FALSE }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Find and fix pixels that may be unsafely bright"),
+ "hot scans an image for pixels that will give unsave "
+ "values of chrominance or composite signale "
+ "amplitude when encoded into an NTSC or PAL signal. "
+ "Three actions can be performed on these ``hot'' "
+ "pixels. (0) reduce luminance, (1) reduce "
+ "saturation, or (2) Blacken.",
+ "Eric L. Hernes, Alan Wm Paeth",
+ "Eric L. Hernes",
+ "1997",
+ N_("_Hot..."),
+ "RGB",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Modify");
+}
+
+static void
+run (const gchar *name,
+ gint nparam,
+ const GimpParam *param,
+ gint *nretvals,
+ GimpParam **retvals)
+{
+ static GimpParam rvals[1];
+ piArgs args;
+
+ *nretvals = 1;
+ *retvals = rvals;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ memset (&args, 0, sizeof (args));
+ args.mode = -1;
+
+ gimp_get_data (PLUG_IN_PROC, &args);
+
+ args.image = param[1].data.d_image;
+ args.drawable = param[2].data.d_drawable;
+
+ rvals[0].type = GIMP_PDB_STATUS;
+ rvals[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ switch (param[0].data.d_int32)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* XXX: add code here for interactive running */
+ if (args.mode == -1)
+ {
+ args.mode = MODE_NTSC;
+ args.action = ACT_LREDUX;
+ args.new_layerp = 1;
+ }
+
+ if (plugin_dialog (&args))
+ {
+ if (! pluginCore (&args))
+ {
+ rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ rvals[0].data.d_status = GIMP_PDB_CANCEL;
+ }
+
+ gimp_set_data (PLUG_IN_PROC, &args, sizeof (args));
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* XXX: add code here for non-interactive running */
+ if (nparam != 6)
+ {
+ rvals[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ args.mode = param[3].data.d_int32;
+ args.action = param[4].data.d_int32;
+ args.new_layerp = param[5].data.d_int32;
+
+ if (! pluginCore (&args))
+ {
+ rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* XXX: add code here for last-values running */
+ if (! pluginCore (&args))
+ {
+ rvals[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+ }
+}
+
+static gboolean
+pluginCore (piArgs *argp)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *src_format;
+ const Babl *dest_format;
+ gint src_bpp;
+ gint dest_bpp;
+ gboolean success = TRUE;
+ gint nl = 0;
+ gint y, i;
+ gint Y, I, Q;
+ gint width, height;
+ gint sel_x1, sel_x2, sel_y1, sel_y2;
+ gint prog_interval;
+ guchar *src, *s, *dst, *d;
+ guchar r, prev_r=0, new_r=0;
+ guchar g, prev_g=0, new_g=0;
+ guchar b, prev_b=0, new_b=0;
+ gdouble fy, fc, t, scale;
+ gdouble pr, pg, pb;
+ gdouble py;
+
+ width = gimp_drawable_width (argp->drawable);
+ height = gimp_drawable_height (argp->drawable);
+
+ if (gimp_drawable_has_alpha (argp->drawable))
+ src_format = babl_format ("R'G'B'A u8");
+ else
+ src_format = babl_format ("R'G'B' u8");
+
+ dest_format = src_format;
+
+ if (argp->new_layerp)
+ {
+ gchar name[40];
+ const gchar *mode_names[] =
+ {
+ "ntsc",
+ "pal",
+ };
+ const gchar *action_names[] =
+ {
+ "lum redux",
+ "sat redux",
+ "flag",
+ };
+
+ g_snprintf (name, sizeof (name), "hot mask (%s, %s)",
+ mode_names[argp->mode],
+ action_names[argp->action]);
+
+ nl = gimp_layer_new (argp->image, name, width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (argp->image));
+
+ gimp_drawable_fill (nl, GIMP_FILL_TRANSPARENT);
+ gimp_image_insert_layer (argp->image, nl, -1, 0);
+
+ dest_format = babl_format ("R'G'B'A u8");
+ }
+
+ if (! gimp_drawable_mask_intersect (argp->drawable,
+ &sel_x1, &sel_y1, &width, &height))
+ return success;
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ sel_x2 = sel_x1 + width;
+ sel_y2 = sel_y1 + height;
+
+ src = g_new (guchar, width * height * src_bpp);
+ dst = g_new (guchar, width * height * dest_bpp);
+
+ src_buffer = gimp_drawable_get_buffer (argp->drawable);
+
+ if (argp->new_layerp)
+ {
+ dest_buffer = gimp_drawable_get_buffer (nl);
+ }
+ else
+ {
+ dest_buffer = gimp_drawable_get_shadow_buffer (argp->drawable);
+ }
+
+ gegl_buffer_get (src_buffer,
+ GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 1.0,
+ src_format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ s = src;
+ d = dst;
+
+ build_tab (argp->mode);
+
+ gimp_progress_init (_("Hot"));
+ prog_interval = height / 10;
+
+ for (y = sel_y1; y < sel_y2; y++)
+ {
+ gint x;
+
+ if (y % prog_interval == 0)
+ gimp_progress_update ((double) y / (double) (sel_y2 - sel_y1));
+
+ for (x = sel_x1; x < sel_x2; x++)
+ {
+ if (hotp (r = *(s + 0), g = *(s + 1), b = *(s + 2)))
+ {
+ if (argp->action == ACT_FLAG)
+ {
+ for (i = 0; i < 3; i++)
+ *d++ = 0;
+ s += 3;
+ if (src_bpp == 4)
+ *d++ = *s++;
+ else if (argp->new_layerp)
+ *d++ = 255;
+ }
+ else
+ {
+ /*
+ * Optimization: cache the last-computed hot pixel.
+ */
+ if (r == prev_r && g == prev_g && b == prev_b)
+ {
+ *d++ = new_r;
+ *d++ = new_g;
+ *d++ = new_b;
+ s += 3;
+ if (src_bpp == 4)
+ *d++ = *s++;
+ else if (argp->new_layerp)
+ *d++ = 255;
+ }
+ else
+ {
+ Y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
+ I = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
+ Q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
+
+ prev_r = r;
+ prev_g = g;
+ prev_b = b;
+ /*
+ * Get Y and chroma amplitudes in floating point.
+ *
+ * If your C library doesn't have hypot(), just use
+ * hypot(a,b) = sqrt(a*a, b*b);
+ *
+ * Then extract linear (un-gamma-corrected)
+ * floating-point pixel RGB values.
+ */
+ fy = (double)Y / (double)SCALE;
+ fc = hypot ((double) I / (double) SCALE,
+ (double) Q / (double) SCALE);
+
+ pr = (double) pix_decode (r);
+ pg = (double) pix_decode (g);
+ pb = (double) pix_decode (b);
+
+ /*
+ * Reducing overall pixel intensity by scaling R,
+ * G, and B reduces Y, I, and Q by the same factor.
+ * This changes luminance but not saturation, since
+ * saturation is determined by the chroma/luminance
+ * ratio.
+ *
+ * On the other hand, by linearly interpolating
+ * between the original pixel value and a grey
+ * pixel with the same luminance (R=G=B=Y), we
+ * change saturation without affecting luminance.
+ */
+ if (argp->action == ACT_LREDUX)
+ {
+ /*
+ * Calculate a scale factor that will bring the pixel
+ * within both chroma and composite limits, if we scale
+ * luminance and chroma simultaneously.
+ *
+ * The calculated chrominance reduction applies
+ * to the gamma-corrected RGB values that are
+ * the input to the RGB-to-YIQ operation.
+ * Multiplying the original un-gamma-corrected
+ * pixel values by the scaling factor raised to
+ * the "gamma" power is equivalent, and avoids
+ * calling gc() and inv_gc() three times each. */
+ scale = chroma_lim / fc;
+ t = compos_lim / (fy + fc);
+ if (t < scale)
+ scale = t;
+ scale = pow (scale, mode[argp->mode].gamma);
+
+ r = (guint8) pix_encode (scale * pr);
+ g = (guint8) pix_encode (scale * pg);
+ b = (guint8) pix_encode (scale * pb);
+ }
+ else
+ { /* ACT_SREDUX hopefully */
+ /*
+ * Calculate a scale factor that will bring the
+ * pixel within both chroma and composite
+ * limits, if we scale chroma while leaving
+ * luminance unchanged.
+ *
+ * We have to interpolate gamma-corrected RGB
+ * values, so we must convert from linear to
+ * gamma-corrected before interpolation and then
+ * back to linear afterwards.
+ */
+ scale = chroma_lim / fc;
+ t = (compos_lim - fy) / fc;
+ if (t < scale)
+ scale = t;
+
+ pr = gc (pr, argp->mode);
+ pg = gc (pg, argp->mode);
+ pb = gc (pb, argp->mode);
+
+ py = pr * mode[argp->mode].code[0][0] +
+ pg * mode[argp->mode].code[0][1] +
+ pb * mode[argp->mode].code[0][2];
+
+ r = pix_encode (inv_gc (py + scale * (pr - py),
+ argp->mode));
+ g = pix_encode (inv_gc (py + scale * (pg - py),
+ argp->mode));
+ b = pix_encode (inv_gc (py + scale * (pb - py),
+ argp->mode));
+ }
+
+ *d++ = new_r = r;
+ *d++ = new_g = g;
+ *d++ = new_b = b;
+
+ s += 3;
+
+ if (src_bpp == 4)
+ *d++ = *s++;
+ else if (argp->new_layerp)
+ *d++ = 255;
+ }
+ }
+ }
+ else
+ {
+ if (! argp->new_layerp)
+ {
+ for (i = 0; i < src_bpp; i++)
+ *d++ = *s++;
+ }
+ else
+ {
+ s += src_bpp;
+ d += dest_bpp;
+ }
+ }
+ }
+ }
+
+ gegl_buffer_set (dest_buffer,
+ GEGL_RECTANGLE (sel_x1, sel_y1, width, height), 0,
+ dest_format, dst,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update (1.0);
+
+ g_free (src);
+ g_free (dst);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ if (argp->new_layerp)
+ {
+ gimp_drawable_update (nl, sel_x1, sel_y1, width, height);
+ }
+ else
+ {
+ gimp_drawable_merge_shadow (argp->drawable, TRUE);
+ gimp_drawable_update (argp->drawable, sel_x1, sel_y1, width, height);
+ }
+
+ gimp_displays_flush ();
+
+ return success;
+}
+
+static gboolean
+plugin_dialog (piArgs *argp)
+{
+ GtkWidget *dlg;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *toggle;
+ GtkWidget *frame;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dlg = gimp_dialog_new (_("Hot"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Mode"),
+ G_CALLBACK (gimp_radio_button_update),
+ &argp->mode, argp->mode,
+
+ "N_TSC", MODE_NTSC, NULL,
+ "_PAL", MODE_PAL, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Create _new layer"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), argp->new_layerp);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &argp->new_layerp);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Action"),
+ G_CALLBACK (gimp_radio_button_update),
+ &argp->action, argp->action,
+
+ _("Reduce _Luminance"), ACT_LREDUX, NULL,
+ _("Reduce _Saturation"), ACT_SREDUX, NULL,
+ _("_Blacken"), ACT_FLAG, NULL,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+/*
+ * build_tab: Build multiply lookup table.
+ *
+ * For each possible pixel value, decode value into floating-point
+ * intensity. Then do gamma correction required by the video
+ * standard. Scale the result by our fixed-point scale factor.
+ * Then calculate 9 lookup table entries for this pixel value.
+ *
+ * We also calculate floating-point and scaled integer versions
+ * of our limits here. This prevents evaluating expressions every pixel
+ * when the compiler is too stupid to evaluate constant-valued
+ * floating-point expressions at compile time.
+ *
+ * For convenience, the limits are #defined using IRE units.
+ * We must convert them here into the units in which YIQ
+ * are measured. The conversion from IRE to internal units
+ * depends on the pedestal level in use, since as Y goes from
+ * 0 to 1, the signal goes from the pedestal level to 100 IRE.
+ * Chroma is always scaled to remain consistent with Y.
+ */
+static void
+build_tab (int m)
+{
+ double f;
+ int pv;
+
+ for (pv = 0; pv <= MAXPIX; pv++)
+ {
+ f = (double)SCALE * (double)gc((double)pix_decode(pv),m);
+ tab[0][0][pv] = (int)(f * mode[m].code[0][0] + 0.5);
+ tab[0][1][pv] = (int)(f * mode[m].code[0][1] + 0.5);
+ tab[0][2][pv] = (int)(f * mode[m].code[0][2] + 0.5);
+ tab[1][0][pv] = (int)(f * mode[m].code[1][0] + 0.5);
+ tab[1][1][pv] = (int)(f * mode[m].code[1][1] + 0.5);
+ tab[1][2][pv] = (int)(f * mode[m].code[1][2] + 0.5);
+ tab[2][0][pv] = (int)(f * mode[m].code[2][0] + 0.5);
+ tab[2][1][pv] = (int)(f * mode[m].code[2][1] + 0.5);
+ tab[2][2][pv] = (int)(f * mode[m].code[2][2] + 0.5);
+ }
+
+ chroma_lim = (double)CHROMA_LIM / (100.0 - mode[m].pedestal);
+ compos_lim = ((double)COMPOS_LIM - mode[m].pedestal) /
+ (100.0 - mode[m].pedestal);
+
+ ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
+ ichroma_lim2 *= ichroma_lim2;
+ icompos_lim = (int)(compos_lim * SCALE + 0.5);
+}
+
+static gboolean
+hotp (guint8 r,
+ guint8 g,
+ guint8 b)
+{
+ int y, i, q;
+ long y2, c2;
+
+ /*
+ * Pixel decoding, gamma correction, and matrix multiplication
+ * all done by lookup table.
+ *
+ * "i" and "q" are the two chrominance components;
+ * they are I and Q for NTSC.
+ * For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
+ * Since we only care about the length of the chroma vector,
+ * not its angle, we don't care which is which.
+ */
+ y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
+ i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
+ q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
+
+ /*
+ * Check to see if the chrominance vector is too long or the
+ * composite waveform amplitude is too large.
+ *
+ * Chrominance is too large if
+ *
+ * sqrt(i^2, q^2) > chroma_lim.
+ *
+ * The composite signal amplitude is too large if
+ *
+ * y + sqrt(i^2, q^2) > compos_lim.
+ *
+ * We avoid doing the sqrt by checking
+ *
+ * i^2 + q^2 > chroma_lim^2
+ * and
+ * y + sqrt(i^2 + q^2) > compos_lim
+ * sqrt(i^2 + q^2) > compos_lim - y
+ * i^2 + q^2 > (compos_lim - y)^2
+ *
+ */
+
+ c2 = (long)i * i + (long)q * q;
+ y2 = (long)icompos_lim - y;
+ y2 *= y2;
+
+ if (c2 <= ichroma_lim2 && c2 <= y2)
+ { /* no problems */
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/common/jigsaw.c b/plug-ins/common/jigsaw.c
new file mode 100644
index 0000000..eade3d3
--- /dev/null
+++ b/plug-ins/common/jigsaw.c
@@ -0,0 +1,2563 @@
+/*
+ * jigsaw - a plug-in for GIMP
+ *
+ * Copyright (C) Nigel Wetten
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Contact info: nigel@cs.nwu.edu
+ *
+ * Version: 1.0.0
+ *
+ * Version: 1.0.1
+ *
+ * tim coppefield [timecop@japan.co.jp]
+ *
+ * Added dynamic preview mode.
+ *
+ * Damn, this plugin is the tightest piece of code I ever seen.
+ * I wish all filters in the plugins operated on guchar *buffer
+ * of the entire image :) sweet stuff.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-jigsaw"
+#define PLUG_IN_BINARY "jigsaw"
+#define PLUG_IN_ROLE "gimp-jigsaw"
+
+
+typedef enum
+{
+ BEZIER_1,
+ BEZIER_2
+} style_t;
+
+typedef enum
+{
+ LEFT,
+ RIGHT,
+ UP,
+ DOWN
+} bump_t;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void jigsaw (guint32 drawable_id,
+ GimpPreview *preview);
+static void jigsaw_preview (gpointer drawable_id,
+ GimpPreview *preview);
+
+static gboolean jigsaw_dialog (guint32 drawable_id);
+
+static void draw_jigsaw (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint height,
+ gint bytes,
+ gboolean preview_mode);
+
+static void draw_vertical_border (guchar *buffer, gint bufsize,
+ gint width, gint height,
+ gint bytes, gint x_offset, gint ytiles,
+ gint blend_lines, gdouble blend_amount);
+static void draw_horizontal_border (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint xtiles,
+ gint blend_lines, gdouble blend_amount);
+static void draw_vertical_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint px[2], gint py[2]);
+static void draw_horizontal_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint px[2], gint py[2]);
+static void darken_vertical_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint *px, gint *py, gdouble delta);
+static void lighten_vertical_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint *px, gint *py, gdouble delta);
+static void darken_horizontal_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint *px, gint *py, gdouble delta);
+static void lighten_horizontal_line(guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint *px, gint *py, gdouble delta);
+static void draw_right_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps);
+static void draw_left_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps);
+static void draw_up_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offset,
+ gint steps);
+static void draw_down_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offset,
+ gint steps);
+static void darken_right_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void lighten_right_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void darken_left_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void lighten_left_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void darken_up_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offest,
+ gint steps, gdouble delta, gint counter);
+static void lighten_up_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void darken_down_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void lighten_down_bump (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint y_offset, gint curve_start_offset,
+ gint steps, gdouble delta, gint counter);
+static void generate_grid (gint width, gint height, gint xtiles, gint ytiles,
+ gint *x, gint *y);
+static void generate_bezier (gint px[4], gint py[4], gint steps,
+ gint *cachex, gint *cachey);
+static void malloc_cache (void);
+static void free_cache (void);
+static void init_right_bump (gint width, gint height);
+static void init_left_bump (gint width, gint height);
+static void init_up_bump (gint width, gint height);
+static void init_down_bump (gint width, gint height);
+static void draw_bezier_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint steps, gint *cx, gint *cy);
+static void darken_bezier_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint y_offset, gint steps,
+ gint *cx, gint *cy, gdouble delta);
+static void lighten_bezier_line (guchar *buffer, gint bufsize,
+ gint width, gint bytes,
+ gint x_offset, gint y_offset, gint steps,
+ gint *cx, gint *cy, gdouble delta);
+static void draw_bezier_vertical_border (guchar *buffer, gint bufsize,
+ gint width, gint height,
+ gint bytes,
+ gint x_offset, gint xtiles,
+ gint ytiles, gint blend_lines,
+ gdouble blend_amount, gint steps);
+static void draw_bezier_horizontal_border (guchar *buffer, gint bufsize,
+ gint width, gint height,
+ gint bytes,
+ gint x_offset, gint xtiles,
+ gint ytiles, gint blend_lines,
+ gdouble blend_amount, gint steps);
+static void check_config (gint width, gint height);
+
+
+
+#define XFACTOR2 0.0833
+#define XFACTOR3 0.2083
+#define XFACTOR4 0.2500
+
+#define XFACTOR5 0.2500
+#define XFACTOR6 0.2083
+#define XFACTOR7 0.0833
+
+#define YFACTOR2 0.1000
+#define YFACTOR3 0.2200
+#define YFACTOR4 0.1000
+
+#define YFACTOR5 0.1000
+#define YFACTOR6 0.4666
+#define YFACTOR7 0.1000
+#define YFACTOR8 0.2000
+
+#define MAX_VALUE 255
+#define MIN_VALUE 0
+#define DELTA 0.15
+
+#define BLACK_R 30
+#define BLACK_G 30
+#define BLACK_B 30
+
+#define WALL_XFACTOR2 0.05
+#define WALL_XFACTOR3 0.05
+#define WALL_YFACTOR2 0.05
+#define WALL_YFACTOR3 0.05
+
+#define WALL_XCONS2 0.2
+#define WALL_XCONS3 0.3
+#define WALL_YCONS2 0.2
+#define WALL_YCONS3 0.3
+
+#define FUDGE 1.2
+
+#define MIN_XTILES 1
+#define MAX_XTILES 20
+#define MIN_YTILES 1
+#define MAX_YTILES 20
+#define MIN_BLEND_LINES 0
+#define MAX_BLEND_LINES 10
+#define MIN_BLEND_AMOUNT 0
+#define MAX_BLEND_AMOUNT 1.0
+
+#define SCALE_WIDTH 200
+
+#define DRAW_POINT(buffer, bufsize, index) \
+ do \
+ { \
+ if ((index) >= 0 && (index) + 2 < (bufsize)) \
+ { \
+ buffer[(index) + 0] = BLACK_R; \
+ buffer[(index) + 1] = BLACK_G; \
+ buffer[(index) + 2] = BLACK_B; \
+ } \
+ } \
+ while (0)
+
+#define DARKEN_POINT(buffer, bufsize, index, delta, temp) \
+ do \
+ { \
+ if ((index) >= 0 && (index) + 2 < (bufsize)) \
+ { \
+ temp = MAX (buffer[(index) + 0] * (1.0 - (delta)), MIN_VALUE); \
+ buffer[(index) + 0] = temp; \
+ temp = MAX (buffer[(index) + 1] * (1.0 - (delta)), MIN_VALUE); \
+ buffer[(index) + 1] = temp; \
+ temp = MAX (buffer[(index) + 2] * (1.0 - (delta)), MIN_VALUE); \
+ buffer[(index) + 2] = temp; \
+ } \
+ } \
+ while (0)
+
+#define LIGHTEN_POINT(buffer, bufsize, index, delta, temp) \
+ do \
+ { \
+ if ((index) >= 0 && (index) + 2 < (bufsize)) \
+ { \
+ temp = MIN (buffer[(index) + 0] * (1.0 + (delta)), MAX_VALUE); \
+ buffer[(index) + 0] = temp; \
+ temp = MIN (buffer[(index) + 1] * (1.0 + (delta)), MAX_VALUE); \
+ buffer[(index) + 1] = temp; \
+ temp = MIN (buffer[(index) + 2] * (1.0 + (delta)), MAX_VALUE); \
+ buffer[(index) + 2] = temp; \
+ } \
+ } \
+ while (0)
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+struct config_tag
+{
+ gint x;
+ gint y;
+ style_t style;
+ gint blend_lines;
+ gdouble blend_amount;
+};
+
+
+typedef struct config_tag config_t;
+
+static config_t config =
+{
+ 5,
+ 5,
+ BEZIER_1,
+ 3,
+ 0.5
+};
+
+struct globals_tag
+{
+ gint *cachex1[4];
+ gint *cachex2[4];
+ gint *cachey1[4];
+ gint *cachey2[4];
+ gint steps[4];
+ gint *gridx;
+ gint *gridy;
+ gint **blend_outer_cachex1[4];
+ gint **blend_outer_cachex2[4];
+ gint **blend_outer_cachey1[4];
+ gint **blend_outer_cachey2[4];
+ gint **blend_inner_cachex1[4];
+ gint **blend_inner_cachex2[4];
+ gint **blend_inner_cachey1[4];
+ gint **blend_inner_cachey2[4];
+};
+
+typedef struct globals_tag globals_t;
+
+static globals_t globals;
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "x", "Number of tiles across > 0" },
+ { GIMP_PDB_INT32, "y", "Number of tiles down > 0" },
+ { GIMP_PDB_INT32, "style", "The style/shape of the jigsaw puzzle { 0, 1 }" },
+ { GIMP_PDB_INT32, "blend-lines", "Number of lines for shading bevels >= 0" },
+ { GIMP_PDB_FLOAT, "blend-amount", "The power of the light highlights 0 =< 5" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Add a jigsaw-puzzle pattern to the image"),
+ "Jigsaw puzzle look",
+ "Nigel Wetten",
+ "Nigel Wetten",
+ "May 2000",
+ N_("_Jigsaw..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ guint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams == 8)
+ {
+ config.x = param[3].data.d_int32;
+ config.y = param[4].data.d_int32;
+ config.style = param[5].data.d_int32;
+ config.blend_lines = param[6].data.d_int32;
+ config.blend_amount = param[7].data.d_float;
+
+ jigsaw (drawable_id, NULL);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &config);
+ if (! jigsaw_dialog (drawable_id))
+ {
+ status = GIMP_PDB_CANCEL;
+ break;
+ }
+ gimp_progress_init (_("Assembling jigsaw"));
+
+ jigsaw (drawable_id, NULL);
+ gimp_set_data (PLUG_IN_PROC, &config, sizeof(config_t));
+ gimp_displays_flush ();
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &config);
+ jigsaw (drawable_id, NULL);
+ gimp_displays_flush ();
+ } /* switch */
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static void
+jigsaw (guint32 drawable_id,
+ GimpPreview *preview)
+{
+ GeglBuffer *gegl_buffer = NULL;
+ const Babl *format = NULL;
+ guchar *buffer;
+ gint width;
+ gint height;
+ gint bytes;
+ gint buffer_size;
+
+ if (preview)
+ {
+ gimp_preview_get_size (preview, &width, &height);
+ buffer = gimp_drawable_get_thumbnail_data (drawable_id,
+ &width, &height, &bytes);
+ buffer_size = bytes * width * height;
+ }
+ else
+ {
+ gegl_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ width = gimp_drawable_width (drawable_id);
+ height = gimp_drawable_height (drawable_id);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ /* setup image buffer */
+ buffer_size = bytes * width * height;
+ buffer = g_new (guchar, buffer_size);
+
+ gegl_buffer_get (gegl_buffer, GEGL_RECTANGLE (0, 0, width, height), 1.0,
+ format, buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ g_object_unref (gegl_buffer);
+ }
+
+ check_config (width, height);
+ globals.steps[LEFT] = globals.steps[RIGHT] = globals.steps[UP]
+ = globals.steps[DOWN] = (config.x < config.y) ?
+ (width / config.x * 2) : (height / config.y * 2);
+
+ malloc_cache ();
+ draw_jigsaw (buffer, buffer_size, width, height, bytes, preview != NULL);
+ free_cache ();
+
+ /* cleanup */
+ if (preview)
+ {
+ gimp_preview_draw_buffer (preview, buffer, width * bytes);
+ }
+ else
+ {
+ gegl_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ gegl_buffer_set (gegl_buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ format, buffer,
+ GEGL_AUTO_ROWSTRIDE);
+ g_object_unref (gegl_buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, 0, 0, width, height);
+ }
+
+ g_free (buffer);
+}
+
+static void
+jigsaw_preview (gpointer drawable_id,
+ GimpPreview *preview)
+{
+ jigsaw (GPOINTER_TO_INT (drawable_id), preview);
+}
+
+static void
+generate_bezier (gint px[4],
+ gint py[4],
+ gint steps,
+ gint *cachex,
+ gint *cachey)
+{
+ gdouble t = 0.0;
+ gdouble sigma = 1.0 / steps;
+ gint i;
+
+ for (i = 0; i < steps; i++)
+ {
+ gdouble t2, t3, x, y, t_1;
+
+ t += sigma;
+ t2 = t * t;
+ t3 = t2 * t;
+ t_1 = 1 - t;
+ x = t_1 * t_1 * t_1 * px[0]
+ + 3 * t * t_1 * t_1 * px[1]
+ + 3 * t2 * t_1 * px[2]
+ + t3 * px[3];
+ y = t_1 * t_1 * t_1 * py[0]
+ + 3 * t * t_1 * t_1 * py[1]
+ + 3 * t2 * t_1 * py[2]
+ + t3 * py[3];
+ cachex[i] = (gint) (x + 0.2);
+ cachey[i] = (gint) (y + 0.2);
+ } /* for */
+}
+
+static void
+draw_jigsaw (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint height,
+ gint bytes,
+ gboolean preview_mode)
+{
+ gint i;
+ gint *x, *y;
+ gint xtiles = config.x;
+ gint ytiles = config.y;
+ gint xlines = xtiles - 1;
+ gint ylines = ytiles - 1;
+ gint blend_lines = config.blend_lines;
+ gdouble blend_amount = config.blend_amount;
+ gint steps = globals.steps[RIGHT];
+ style_t style = config.style;
+ gint progress_total = xlines + ylines - 1;
+
+ g_return_if_fail (buffer != NULL);
+
+ globals.gridx = g_new (gint, xtiles);
+ globals.gridy = g_new (gint, ytiles);
+ x = globals.gridx;
+ y = globals.gridy;
+
+ generate_grid (width, height, xtiles, ytiles, globals.gridx, globals.gridy);
+
+ init_right_bump (width, height);
+ init_left_bump (width, height);
+ init_up_bump (width, height);
+ init_down_bump (width, height);
+
+ if (style == BEZIER_1)
+ {
+ for (i = 0; i < xlines; i++)
+ {
+ draw_vertical_border (buffer, bufsize, width, height, bytes,
+ x[i], ytiles,
+ blend_lines, blend_amount);
+ if (!preview_mode)
+ gimp_progress_update ((gdouble) i / (gdouble) progress_total);
+ }
+ for (i = 0; i < ylines; i++)
+ {
+ draw_horizontal_border (buffer, bufsize, width, bytes, y[i], xtiles,
+ blend_lines, blend_amount);
+ if (!preview_mode)
+ gimp_progress_update ((gdouble) (i + xlines) / (gdouble) progress_total);
+ }
+ }
+ else if (style == BEZIER_2)
+ {
+ for (i = 0; i < xlines; i++)
+ {
+ draw_bezier_vertical_border (buffer, bufsize, width, height, bytes,
+ x[i], xtiles, ytiles, blend_lines,
+ blend_amount, steps);
+ if (!preview_mode)
+ gimp_progress_update ((gdouble) i / (gdouble) progress_total);
+ }
+ for (i = 0; i < ylines; i++)
+ {
+ draw_bezier_horizontal_border (buffer, bufsize, width, height, bytes,
+ y[i], xtiles, ytiles, blend_lines,
+ blend_amount, steps);
+ if (!preview_mode)
+ gimp_progress_update ((gdouble) (i + xlines) / (gdouble) progress_total);
+ }
+ }
+ else
+ {
+ g_printerr ("draw_jigsaw: bad style\n");
+ gimp_quit ();
+ }
+ gimp_progress_update (1.0);
+
+ g_free (globals.gridx);
+ g_free (globals.gridy);
+}
+
+static void
+draw_vertical_border (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint height,
+ gint bytes,
+ gint x_offset,
+ gint ytiles,
+ gint blend_lines,
+ gdouble blend_amount)
+{
+ gint i, j;
+ gint tile_height = height / ytiles;
+ gint tile_height_eighth = tile_height / 8;
+ gint curve_start_offset = 3 * tile_height_eighth;
+ gint curve_end_offset = curve_start_offset + 2 * tile_height_eighth;
+ gint px[2], py[2];
+ gint ly[2], dy[2];
+ gint y_offset = 0;
+ gdouble delta;
+ gdouble sigma = blend_amount / blend_lines;
+ gint right;
+
+ for (i = 0; i < ytiles; i++)
+ {
+ right = g_random_int_range (0, 2);
+
+ /* first straight line from top downwards */
+ px[0] = px[1] = x_offset;
+ py[0] = y_offset; py[1] = y_offset + curve_start_offset - 1;
+ draw_vertical_line (buffer, bufsize, width, bytes, px, py);
+ delta = blend_amount;
+ dy[0] = ly[0] = py[0]; dy[1] = ly[1] = py[1];
+ if (!right)
+ {
+ ly[1] += blend_lines + 2;
+ }
+ for (j = 0; j < blend_lines; j++)
+ {
+ dy[0]++; dy[1]--; ly[0]++; ly[1]--;
+ px[0] = x_offset - j - 1;
+ darken_vertical_line (buffer, bufsize, width, bytes, px, dy, delta);
+ px[0] = x_offset + j + 1;
+ lighten_vertical_line (buffer, bufsize, width, bytes, px, ly, delta);
+ delta -= sigma;
+ }
+
+ /* top half of curve */
+ if (right)
+ {
+ draw_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT], delta, j);
+ /* use to be +j + 1 */
+ lighten_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT], delta, j);
+ delta -= sigma;
+ }
+ }
+ else
+ {
+ draw_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT], delta, j);
+ /* use to be -j - 1 */
+ lighten_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT], delta, j);
+ delta -= sigma;
+ }
+ }
+ /* bottom straight line of a tile wall */
+ px[0] = px[1] = x_offset;
+ py[0] = y_offset + curve_end_offset;
+ py[1] = globals.gridy[i];
+ draw_vertical_line (buffer, bufsize, width, bytes, px, py);
+ delta = blend_amount;
+ dy[0] = ly[0] = py[0]; dy[1] = ly[1] = py[1];
+ if (right)
+ {
+ dy[0] -= blend_lines + 2;
+ }
+ for (j = 0; j < blend_lines; j++)
+ {
+ dy[0]++; dy[1]--; ly[0]++; ly[1]--;
+ px[0] = x_offset - j - 1;
+ darken_vertical_line (buffer, bufsize, width, bytes, px, dy, delta);
+ px[0] = x_offset + j + 1;
+ lighten_vertical_line (buffer, bufsize, width, bytes, px, ly, delta);
+ delta -= sigma;
+ }
+
+ y_offset = globals.gridy[i];
+ } /* for */
+}
+
+/* assumes RGB* */
+static void
+draw_horizontal_border (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint xtiles,
+ gint blend_lines,
+ gdouble blend_amount)
+{
+ gint i, j;
+ gint tile_width = width / xtiles;
+ gint tile_width_eighth = tile_width / 8;
+ gint curve_start_offset = 3 * tile_width_eighth;
+ gint curve_end_offset = curve_start_offset + 2 * tile_width_eighth;
+ gint px[2], py[2];
+ gint dx[2], lx[2];
+ gint x_offset = 0;
+ gdouble delta;
+ gdouble sigma = blend_amount / blend_lines;
+ gint up;
+
+ for (i = 0; i < xtiles; i++)
+ {
+ up = g_random_int_range (0, 2);
+
+ /* first horizontal line across */
+ px[0] = x_offset; px[1] = x_offset + curve_start_offset - 1;
+ py[0] = py[1] = y_offset;
+ draw_horizontal_line (buffer, bufsize, width, bytes, px, py);
+ delta = blend_amount;
+ dx[0] = lx[0] = px[0]; dx[1] = lx[1] = px[1];
+ if (up)
+ {
+ lx[1] += blend_lines + 2;
+ }
+ for (j = 0; j < blend_lines; j++)
+ {
+ dx[0]++; dx[1]--; lx[0]++; lx[1]--;
+ py[0] = y_offset - j - 1;
+ darken_horizontal_line (buffer, bufsize, width, bytes, dx, py,
+ delta);
+ py[0] = y_offset + j + 1;
+ lighten_horizontal_line (buffer, bufsize, width, bytes, lx, py,
+ delta);
+ delta -= sigma;
+ }
+
+ if (up)
+ {
+ draw_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP], delta, j);
+ /* use to be +j + 1 */
+ lighten_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP], delta, j);
+ delta -= sigma;
+ }
+ }
+ else
+ {
+ draw_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be +j + 1 */
+ darken_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN], delta, j);
+ /* use to be -j -1 */
+ lighten_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN], delta, j);
+ delta -= sigma;
+ }
+ }
+ /* right horizontal line of tile */
+ px[0] = x_offset + curve_end_offset;
+ px[1] = globals.gridx[i];
+ py[0] = py[1] = y_offset;
+ draw_horizontal_line (buffer, bufsize, width, bytes, px, py);
+ delta = blend_amount;
+ dx[0] = lx[0] = px[0]; dx[1] = lx[1] = px[1];
+ if (!up)
+ {
+ dx[0] -= blend_lines + 2;
+ }
+ for (j = 0;j < blend_lines; j++)
+ {
+ dx[0]++; dx[1]--; lx[0]++; lx[1]--;
+ py[0] = y_offset - j - 1;
+ darken_horizontal_line (buffer, bufsize, width, bytes, dx, py,
+ delta);
+ py[0] = y_offset + j + 1;
+ lighten_horizontal_line (buffer, bufsize, width, bytes, lx, py,
+ delta);
+ delta -= sigma;
+ }
+ x_offset = globals.gridx[i];
+ } /* for */
+}
+
+/* assumes going top to bottom */
+static void
+draw_vertical_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2])
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + rowstride * py[0];
+ length = py[1] - py[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ DRAW_POINT (buffer, bufsize, index);
+ index += rowstride;
+ }
+}
+
+/* assumes going left to right */
+static void
+draw_horizontal_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2])
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + rowstride * py[0];
+ length = px[1] - px[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ DRAW_POINT (buffer, bufsize, index);
+ index += bytes;
+ }
+}
+
+static void
+draw_right_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint rowstride;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = globals.cachex1[RIGHT][i] + x_offset;
+ y = globals.cachey1[RIGHT][i] + curve_start_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+
+ x = globals.cachex2[RIGHT][i] + x_offset;
+ y = globals.cachey2[RIGHT][i] + curve_start_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+ }
+}
+
+static void
+draw_left_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint rowstride;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = globals.cachex1[LEFT][i] + x_offset;
+ y = globals.cachey1[LEFT][i] + curve_start_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+
+ x = globals.cachex2[LEFT][i] + x_offset;
+ y = globals.cachey2[LEFT][i] + curve_start_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+ }
+}
+
+static void
+draw_up_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint rowstride;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = globals.cachex1[UP][i] + curve_start_offset;
+ y = globals.cachey1[UP][i] + y_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+
+ x = globals.cachex2[UP][i] + curve_start_offset;
+ y = globals.cachey2[UP][i] + y_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+ }
+}
+
+static void
+draw_down_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint rowstride;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = globals.cachex1[DOWN][i] + curve_start_offset;
+ y = globals.cachey1[DOWN][i] + y_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+
+ x = globals.cachex2[DOWN][i] + curve_start_offset;
+ y = globals.cachey2[DOWN][i] + y_offset;
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+ }
+}
+
+static void
+malloc_cache (void)
+{
+ gint i, j;
+ gint blend_lines = config.blend_lines;
+
+ for (i = 0; i < 4; i++)
+ {
+ gint steps = globals.steps[i];
+
+ globals.cachex1[i] = g_new (gint, steps);
+ globals.cachex2[i] = g_new (gint, steps);
+ globals.cachey1[i] = g_new (gint, steps);
+ globals.cachey2[i] = g_new (gint, steps);
+ globals.blend_outer_cachex1[i] = g_new (gint *, blend_lines);
+ globals.blend_outer_cachex2[i] = g_new (gint *, blend_lines);
+ globals.blend_outer_cachey1[i] = g_new (gint *, blend_lines);
+ globals.blend_outer_cachey2[i] = g_new (gint *, blend_lines);
+ globals.blend_inner_cachex1[i] = g_new (gint *, blend_lines);
+ globals.blend_inner_cachex2[i] = g_new (gint *, blend_lines);
+ globals.blend_inner_cachey1[i] = g_new (gint *, blend_lines);
+ globals.blend_inner_cachey2[i] = g_new (gint *, blend_lines);
+ for (j = 0; j < blend_lines; j++)
+ {
+ globals.blend_outer_cachex1[i][j] = g_new (gint, steps);
+ globals.blend_outer_cachex2[i][j] = g_new (gint, steps);
+ globals.blend_outer_cachey1[i][j] = g_new (gint, steps);
+ globals.blend_outer_cachey2[i][j] = g_new (gint, steps);
+ globals.blend_inner_cachex1[i][j] = g_new (gint, steps);
+ globals.blend_inner_cachex2[i][j] = g_new (gint, steps);
+ globals.blend_inner_cachey1[i][j] = g_new (gint, steps);
+ globals.blend_inner_cachey2[i][j] = g_new (gint, steps);
+ }
+ }
+}
+
+static void
+free_cache (void)
+{
+ gint i, j;
+ gint blend_lines = config.blend_lines;
+
+ for (i = 0; i < 4; i ++)
+ {
+ g_free (globals.cachex1[i]);
+ g_free (globals.cachex2[i]);
+ g_free (globals.cachey1[i]);
+ g_free (globals.cachey2[i]);
+ for (j = 0; j < blend_lines; j++)
+ {
+ g_free (globals.blend_outer_cachex1[i][j]);
+ g_free (globals.blend_outer_cachex2[i][j]);
+ g_free (globals.blend_outer_cachey1[i][j]);
+ g_free (globals.blend_outer_cachey2[i][j]);
+ g_free (globals.blend_inner_cachex1[i][j]);
+ g_free (globals.blend_inner_cachex2[i][j]);
+ g_free (globals.blend_inner_cachey1[i][j]);
+ g_free (globals.blend_inner_cachey2[i][j]);
+ }
+ g_free (globals.blend_outer_cachex1[i]);
+ g_free (globals.blend_outer_cachex2[i]);
+ g_free (globals.blend_outer_cachey1[i]);
+ g_free (globals.blend_outer_cachey2[i]);
+ g_free (globals.blend_inner_cachex1[i]);
+ g_free (globals.blend_inner_cachex2[i]);
+ g_free (globals.blend_inner_cachey1[i]);
+ g_free (globals.blend_inner_cachey2[i]);
+ }
+}
+
+static void
+init_right_bump (gint width,
+ gint height)
+{
+ gint i;
+ gint xtiles = config.x;
+ gint ytiles = config.y;
+ gint steps = globals.steps[RIGHT];
+ gint px[4], py[4];
+ gint x_offset = 0;
+ gint tile_width = width / xtiles;
+ gint tile_height = height/ ytiles;
+ gint tile_height_eighth = tile_height / 8;
+ gint curve_start_offset = 0;
+ gint curve_end_offset = curve_start_offset + 2 * tile_height_eighth;
+ gint blend_lines = config.blend_lines;
+
+ px[0] = x_offset;
+ px[1] = x_offset + XFACTOR2 * tile_width;
+ px[2] = x_offset + XFACTOR3 * tile_width;
+ px[3] = x_offset + XFACTOR4 * tile_width;
+ py[0] = curve_start_offset;
+ py[1] = curve_start_offset + YFACTOR2 * tile_height;
+ py[2] = curve_start_offset - YFACTOR3 * tile_height;
+ py[3] = curve_start_offset + YFACTOR4 * tile_height;
+ generate_bezier(px, py, steps, globals.cachex1[RIGHT],
+ globals.cachey1[RIGHT]);
+ /* outside right bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[0]--; py[1]--; py[2]--; px[3]++;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex1[RIGHT][i],
+ globals.blend_outer_cachey1[RIGHT][i]);
+ }
+ /* inside right bump */
+ py[0] += blend_lines; py[1] += blend_lines; py[2] += blend_lines;
+ px[3] -= blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[0]++; py[1]++; py[2]++; px[3]--;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex1[RIGHT][i],
+ globals.blend_inner_cachey1[RIGHT][i]);
+ }
+
+ /* bottom half of bump */
+ px[0] = x_offset + XFACTOR5 * tile_width;
+ px[1] = x_offset + XFACTOR6 * tile_width;
+ px[2] = x_offset + XFACTOR7 * tile_width;
+ px[3] = x_offset;
+ py[0] = curve_start_offset + YFACTOR5 * tile_height;
+ py[1] = curve_start_offset + YFACTOR6 * tile_height;
+ py[2] = curve_start_offset + YFACTOR7 * tile_height;
+ py[3] = curve_end_offset;
+ generate_bezier(px, py, steps, globals.cachex2[RIGHT],
+ globals.cachey2[RIGHT]);
+ /* outer right bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[1]++; py[2]++; py[3]++; px[0]++;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex2[RIGHT][i],
+ globals.blend_outer_cachey2[RIGHT][i]);
+ }
+ /* inner right bump */
+ py[1] -= blend_lines; py[2] -= blend_lines; py[3] -= blend_lines;
+ px[0] -= blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[1]--; py[2]--; py[3]--; px[0]--;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex2[RIGHT][i],
+ globals.blend_inner_cachey2[RIGHT][i]);
+ }
+}
+
+static void
+init_left_bump (gint width,
+ gint height)
+{
+ gint i;
+ gint xtiles = config.x;
+ gint ytiles = config.y;
+ gint steps = globals.steps[LEFT];
+ gint px[4], py[4];
+ gint x_offset = 0;
+ gint tile_width = width / xtiles;
+ gint tile_height = height / ytiles;
+ gint tile_height_eighth = tile_height / 8;
+ gint curve_start_offset = 0;
+ gint curve_end_offset = curve_start_offset + 2 * tile_height_eighth;
+ gint blend_lines = config.blend_lines;
+
+ px[0] = x_offset;
+ px[1] = x_offset - XFACTOR2 * tile_width;
+ px[2] = x_offset - XFACTOR3 * tile_width;
+ px[3] = x_offset - XFACTOR4 * tile_width;
+ py[0] = curve_start_offset;
+ py[1] = curve_start_offset + YFACTOR2 * tile_height;
+ py[2] = curve_start_offset - YFACTOR3 * tile_height;
+ py[3] = curve_start_offset + YFACTOR4 * tile_height;
+ generate_bezier(px, py, steps, globals.cachex1[LEFT],
+ globals.cachey1[LEFT]);
+ /* outer left bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[0]--; py[1]--; py[2]--; px[3]--;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex1[LEFT][i],
+ globals.blend_outer_cachey1[LEFT][i]);
+ }
+ /* inner left bump */
+ py[0] += blend_lines; py[1] += blend_lines; py[2] += blend_lines;
+ px[3] += blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[0]++; py[1]++; py[2]++; px[3]++;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex1[LEFT][i],
+ globals.blend_inner_cachey1[LEFT][i]);
+ }
+
+ /* bottom half of bump */
+ px[0] = x_offset - XFACTOR5 * tile_width;
+ px[1] = x_offset - XFACTOR6 * tile_width;
+ px[2] = x_offset - XFACTOR7 * tile_width;
+ px[3] = x_offset;
+ py[0] = curve_start_offset + YFACTOR5 * tile_height;
+ py[1] = curve_start_offset + YFACTOR6 * tile_height;
+ py[2] = curve_start_offset + YFACTOR7 * tile_height;
+ py[3] = curve_end_offset;
+ generate_bezier(px, py, steps, globals.cachex2[LEFT],
+ globals.cachey2[LEFT]);
+ /* outer left bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[1]++; py[2]++; py[3]++; px[0]--;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex2[LEFT][i],
+ globals.blend_outer_cachey2[LEFT][i]);
+ }
+ /* inner left bump */
+ py[1] -= blend_lines; py[2] -= blend_lines; py[3] -= blend_lines;
+ px[0] += blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ py[1]--; py[2]--; py[3]--; px[0]++;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex2[LEFT][i],
+ globals.blend_inner_cachey2[LEFT][i]);
+ }
+}
+
+static void
+init_up_bump (gint width,
+ gint height)
+{
+ gint i;
+ gint xtiles = config.x;
+ gint ytiles = config.y;
+ gint steps = globals.steps[UP];
+ gint px[4], py[4];
+ gint y_offset = 0;
+ gint tile_width = width / xtiles;
+ gint tile_height = height/ ytiles;
+ gint tile_width_eighth = tile_width / 8;
+ gint curve_start_offset = 0;
+ gint curve_end_offset = curve_start_offset + 2 * tile_width_eighth;
+ gint blend_lines = config.blend_lines;
+
+ px[0] = curve_start_offset;
+ px[1] = curve_start_offset + YFACTOR2 * tile_width;
+ px[2] = curve_start_offset - YFACTOR3 * tile_width;
+ px[3] = curve_start_offset + YFACTOR4 * tile_width;
+ py[0] = y_offset;
+ py[1] = y_offset - XFACTOR2 * tile_height;
+ py[2] = y_offset - XFACTOR3 * tile_height;
+ py[3] = y_offset - XFACTOR4 * tile_height;
+ generate_bezier(px, py, steps, globals.cachex1[UP],
+ globals.cachey1[UP]);
+ /* outer up bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[0]--; px[1]--; px[2]--; py[3]--;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex1[UP][i],
+ globals.blend_outer_cachey1[UP][i]);
+ }
+ /* inner up bump */
+ px[0] += blend_lines; px[1] += blend_lines; px[2] += blend_lines;
+ py[3] += blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[0]++; px[1]++; px[2]++; py[3]++;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex1[UP][i],
+ globals.blend_inner_cachey1[UP][i]);
+ }
+
+ /* bottom half of bump */
+ px[0] = curve_start_offset + YFACTOR5 * tile_width;
+ px[1] = curve_start_offset + YFACTOR6 * tile_width;
+ px[2] = curve_start_offset + YFACTOR7 * tile_width;
+ px[3] = curve_end_offset;
+ py[0] = y_offset - XFACTOR5 * tile_height;
+ py[1] = y_offset - XFACTOR6 * tile_height;
+ py[2] = y_offset - XFACTOR7 * tile_height;
+ py[3] = y_offset;
+ generate_bezier(px, py, steps, globals.cachex2[UP],
+ globals.cachey2[UP]);
+ /* outer up bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[1]++; px[2]++; px[3]++; py[0]--;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex2[UP][i],
+ globals.blend_outer_cachey2[UP][i]);
+ }
+ /* inner up bump */
+ px[1] -= blend_lines; px[2] -= blend_lines; px[3] -= blend_lines;
+ py[0] += blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[1]--; px[2]--; px[3]--; py[0]++;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex2[UP][i],
+ globals.blend_inner_cachey2[UP][i]);
+ }
+}
+
+static void
+init_down_bump (gint width,
+ gint height)
+{
+ gint i;
+ gint xtiles = config.x;
+ gint ytiles = config.y;
+ gint steps = globals.steps[DOWN];
+ gint px[4], py[4];
+ gint y_offset = 0;
+ gint tile_width = width / xtiles;
+ gint tile_height = height/ ytiles;
+ gint tile_width_eighth = tile_width / 8;
+ gint curve_start_offset = 0;
+ gint curve_end_offset = curve_start_offset + 2 * tile_width_eighth;
+ gint blend_lines = config.blend_lines;
+
+ px[0] = curve_start_offset;
+ px[1] = curve_start_offset + YFACTOR2 * tile_width;
+ px[2] = curve_start_offset - YFACTOR3 * tile_width;
+ px[3] = curve_start_offset + YFACTOR4 * tile_width;
+ py[0] = y_offset;
+ py[1] = y_offset + XFACTOR2 * tile_height;
+ py[2] = y_offset + XFACTOR3 * tile_height;
+ py[3] = y_offset + XFACTOR4 * tile_height;
+ generate_bezier(px, py, steps, globals.cachex1[DOWN],
+ globals.cachey1[DOWN]);
+ /* outer down bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[0]--; px[1]--; px[2]--; py[3]++;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex1[DOWN][i],
+ globals.blend_outer_cachey1[DOWN][i]);
+ }
+ /* inner down bump */
+ px[0] += blend_lines; px[1] += blend_lines; px[2] += blend_lines;
+ py[3] -= blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[0]++; px[1]++; px[2]++; py[3]--;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex1[DOWN][i],
+ globals.blend_inner_cachey1[DOWN][i]);
+ }
+
+ /* bottom half of bump */
+ px[0] = curve_start_offset + YFACTOR5 * tile_width;
+ px[1] = curve_start_offset + YFACTOR6 * tile_width;
+ px[2] = curve_start_offset + YFACTOR7 * tile_width;
+ px[3] = curve_end_offset;
+ py[0] = y_offset + XFACTOR5 * tile_height;
+ py[1] = y_offset + XFACTOR6 * tile_height;
+ py[2] = y_offset + XFACTOR7 * tile_height;
+ py[3] = y_offset;
+ generate_bezier(px, py, steps, globals.cachex2[DOWN],
+ globals.cachey2[DOWN]);
+ /* outer down bump */
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[1]++; px[2]++; px[3]++; py[0]++;
+ generate_bezier(px, py, steps,
+ globals.blend_outer_cachex2[DOWN][i],
+ globals.blend_outer_cachey2[DOWN][i]);
+ }
+ /* inner down bump */
+ px[1] -= blend_lines; px[2] -= blend_lines; px[3] -= blend_lines;
+ py[0] -= blend_lines;
+ for (i = 0; i < blend_lines; i++)
+ {
+ px[1]--; px[2]--; px[3]--; py[0]--;
+ generate_bezier(px, py, steps,
+ globals.blend_inner_cachex2[DOWN][i],
+ globals.blend_inner_cachey2[DOWN][i]);
+ }
+}
+
+static void
+generate_grid (gint width,
+ gint height,
+ gint xtiles,
+ gint ytiles,
+ gint *x,
+ gint *y)
+{
+ gint i;
+ gint xlines = xtiles - 1;
+ gint ylines = ytiles - 1;
+ gint tile_width = width / xtiles;
+ gint tile_height = height / ytiles;
+ gint tile_width_leftover = width % xtiles;
+ gint tile_height_leftover = height % ytiles;
+ gint x_offset = tile_width;
+ gint y_offset = tile_height;
+ gint carry;
+
+ for (i = 0; i < xlines; i++)
+ {
+ x[i] = x_offset;
+ x_offset += tile_width;
+ }
+ carry = 0;
+ while (tile_width_leftover)
+ {
+ for (i = carry; i < xlines; i++)
+ {
+ x[i] += 1;
+ }
+ tile_width_leftover--;
+ carry++;
+ }
+ x[xlines] = width - 1; /* padding for draw_horizontal_border */
+
+ for (i = 0; i < ytiles; i++)
+ {
+ y[i] = y_offset;
+ y_offset += tile_height;
+ }
+ carry = 0;
+ while (tile_height_leftover)
+ {
+ for (i = carry; i < ylines; i++)
+ {
+ y[i] += 1;
+ }
+ tile_height_leftover--;
+ carry++;
+ }
+ y[ylines] = height - 1; /* padding for draw_vertical_border */
+}
+
+/* assumes RGB* */
+/* assumes py[1] > py[0] and px[0] = px[1] */
+static void
+darken_vertical_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2],
+ gdouble delta)
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+ gint temp;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + py[0] * rowstride;
+ length = py[1] - py[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ index += rowstride;
+ }
+}
+
+/* assumes RGB* */
+/* assumes py[1] > py[0] and px[0] = px[1] */
+static void
+lighten_vertical_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2],
+ gdouble delta)
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+ gint temp;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + py[0] * rowstride;
+ length = py[1] - py[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ index += rowstride;
+ }
+}
+
+/* assumes RGB* */
+/* assumes px[1] > px[0] and py[0] = py[1] */
+static void
+darken_horizontal_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2],
+ gdouble delta)
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+ gint temp;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + py[0] * rowstride;
+ length = px[1] - px[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ index += bytes;
+ }
+}
+
+/* assumes RGB* */
+/* assumes px[1] > px[0] and py[0] = py[1] */
+static void
+lighten_horizontal_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint px[2],
+ gint py[2],
+ gdouble delta)
+{
+ gint i;
+ gint rowstride;
+ gint index;
+ gint length;
+ gint temp;
+
+ rowstride = bytes * width;
+ index = px[0] * bytes + py[0] * rowstride;
+ length = px[1] - px[0] + 1;
+
+ for (i = 0; i < length; i++)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ index += bytes;
+ }
+}
+
+static void
+darken_right_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = x_offset
+ + globals.blend_inner_cachex1[RIGHT][j][i];
+ y = curve_start_offset
+ + globals.blend_inner_cachey1[RIGHT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ if (i < steps / 1.3)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index1 = index;
+ }
+
+ x = x_offset
+ + globals.blend_inner_cachex2[RIGHT][j][i];
+ y = curve_start_offset
+ + globals.blend_inner_cachey2[RIGHT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+lighten_right_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = x_offset
+ + globals.blend_outer_cachex1[RIGHT][j][i];
+ y = curve_start_offset
+ + globals.blend_outer_cachey1[RIGHT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ if (i < steps / 1.3)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index1 = index;
+ }
+
+ x = x_offset
+ + globals.blend_outer_cachex2[RIGHT][j][i];
+ y = curve_start_offset
+ + globals.blend_outer_cachey2[RIGHT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+darken_left_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = x_offset
+ + globals.blend_outer_cachex1[LEFT][j][i];
+ y = curve_start_offset
+ + globals.blend_outer_cachey1[LEFT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index1 = index;
+ }
+
+ x = x_offset
+ + globals.blend_outer_cachex2[LEFT][j][i];
+ y = curve_start_offset
+ + globals.blend_outer_cachey2[LEFT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ if (i < steps / 4)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+lighten_left_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = x_offset
+ + globals.blend_inner_cachex1[LEFT][j][i];
+ y = curve_start_offset
+ + globals.blend_inner_cachey1[LEFT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index1 = index;
+ }
+
+ x = x_offset
+ + globals.blend_inner_cachex2[LEFT][j][i];
+ y = curve_start_offset
+ + globals.blend_inner_cachey2[LEFT][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ if (i < steps / 4)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+darken_up_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = curve_start_offset
+ + globals.blend_outer_cachex1[UP][j][i];
+ y = y_offset
+ + globals.blend_outer_cachey1[UP][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index1 = index;
+ }
+
+ x = curve_start_offset
+ + globals.blend_outer_cachex2[UP][j][i];
+ y = y_offset
+ + globals.blend_outer_cachey2[UP][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ if (i < steps / 4)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+lighten_up_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = curve_start_offset
+ + globals.blend_inner_cachex1[UP][j][i];
+ y = y_offset
+ + globals.blend_inner_cachey1[UP][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index1 = index;
+ }
+
+ x = curve_start_offset
+ + globals.blend_inner_cachex2[UP][j][i];
+ y = y_offset
+ + globals.blend_inner_cachey2[UP][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ if (i < steps / 4)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+darken_down_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = curve_start_offset
+ + globals.blend_inner_cachex1[DOWN][j][i];
+ y = y_offset
+ + globals.blend_inner_cachey1[DOWN][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ if (i < steps / 1.2)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index1 = index;
+ }
+
+ x = curve_start_offset
+ + globals.blend_inner_cachex2[DOWN][j][i];
+ y = y_offset
+ + globals.blend_inner_cachey2[DOWN][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+lighten_down_bump (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint y_offset,
+ gint curve_start_offset,
+ gint steps,
+ gdouble delta,
+ gint counter)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index1 = -1;
+ gint last_index2 = -1;
+ gint rowstride;
+ gint temp;
+ gint j = counter;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = curve_start_offset
+ + globals.blend_outer_cachex1[DOWN][j][i];
+ y = y_offset
+ + globals.blend_outer_cachey1[DOWN][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index1)
+ {
+ if (i < steps / 1.2)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ else
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ }
+ last_index1 = index;
+ }
+
+ x = curve_start_offset
+ + globals.blend_outer_cachex2[DOWN][j][i];
+ y = y_offset
+ + globals.blend_outer_cachey2[DOWN][j][i];
+ index = y * rowstride + x * bytes;
+ if (index != last_index2)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index2 = index;
+ }
+ }
+}
+
+static void
+draw_bezier_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint steps,
+ gint *cx,
+ gint *cy)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint rowstride;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = cx[i];
+ y = cy[i];
+ index = y * rowstride + x * bytes;
+ DRAW_POINT (buffer, bufsize, index);
+ }
+}
+
+static void
+darken_bezier_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint y_offset,
+ gint steps,
+ gint *cx,
+ gint *cy,
+ gdouble delta)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index = -1;
+ gint rowstride;
+ gint temp;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = cx[i] + x_offset;
+ y = cy[i] + y_offset;
+ index = y * rowstride + x * bytes;
+ if (index != last_index)
+ {
+ DARKEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index = index;
+ }
+ }
+}
+
+static void
+lighten_bezier_line (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint bytes,
+ gint x_offset,
+ gint y_offset,
+ gint steps,
+ gint *cx,
+ gint *cy,
+ gdouble delta)
+{
+ gint i;
+ gint x, y;
+ gint index;
+ gint last_index = -1;
+ gint rowstride;
+ gint temp;
+
+ rowstride = bytes * width;
+
+ for (i = 0; i < steps; i++)
+ {
+ x = cx[i] + x_offset;
+ y = cy[i] + y_offset;
+ index = y * rowstride + x * bytes;
+ if (index != last_index)
+ {
+ LIGHTEN_POINT (buffer, bufsize, index, delta, temp);
+ last_index = index;
+ }
+ }
+}
+
+static void
+draw_bezier_vertical_border (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint height,
+ gint bytes,
+ gint x_offset,
+ gint xtiles,
+ gint ytiles,
+ gint blend_lines,
+ gdouble blend_amount,
+ gint steps)
+{
+ gint i, j;
+ gint tile_width = width / xtiles;
+ gint tile_height = height / ytiles;
+ gint tile_height_eighth = tile_height / 8;
+ gint curve_start_offset = 3 * tile_height_eighth;
+ gint curve_end_offset = curve_start_offset + 2 * tile_height_eighth;
+ gint px[4], py[4];
+ gint y_offset = 0;
+ gdouble delta;
+ gdouble sigma = blend_amount / blend_lines;
+ gint right;
+ gint *cachex, *cachey;
+
+ cachex = g_new (gint, steps);
+ cachey = g_new (gint, steps);
+
+ for (i = 0; i < ytiles; i++)
+ {
+ right = g_random_int_range (0, 2);
+
+ px[0] = px[3] = x_offset;
+ px[1] = x_offset + WALL_XFACTOR2 * tile_width * FUDGE;
+ px[2] = x_offset + WALL_XFACTOR3 * tile_width * FUDGE;
+ py[0] = y_offset;
+ py[1] = y_offset + WALL_YCONS2 * tile_height;
+ py[2] = y_offset + WALL_YCONS3 * tile_height;
+ py[3] = y_offset + curve_start_offset;
+
+ if (right)
+ {
+ px[1] = x_offset - WALL_XFACTOR2 * tile_width;
+ px[2] = x_offset - WALL_XFACTOR3 * tile_width;
+ }
+ generate_bezier (px, py, steps, cachex, cachey);
+ draw_bezier_line (buffer, bufsize, width, bytes, steps, cachex, cachey);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ px[0] = -j - 1;
+ darken_bezier_line (buffer, bufsize, width, bytes, px[0], 0,
+ steps, cachex, cachey, delta);
+ px[0] = j + 1;
+ lighten_bezier_line (buffer, bufsize, width, bytes, px[0], 0,
+ steps, cachex, cachey, delta);
+ delta -= sigma;
+ }
+ if (right)
+ {
+ draw_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT], delta, j);
+ /* use to be +j + 1 */
+ lighten_right_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[RIGHT], delta, j);
+ delta -= sigma;
+ }
+ }
+ else
+ {
+ draw_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT], delta, j);
+ /* use to be -j - 1 */
+ lighten_left_bump (buffer, bufsize, width, bytes, x_offset,
+ y_offset + curve_start_offset,
+ globals.steps[LEFT], delta, j);
+ delta -= sigma;
+ }
+ }
+ px[0] = px[3] = x_offset;
+ px[1] = x_offset + WALL_XFACTOR2 * tile_width * FUDGE;
+ px[2] = x_offset + WALL_XFACTOR3 * tile_width * FUDGE;
+ py[0] = y_offset + curve_end_offset;
+ py[1] = y_offset + curve_end_offset + WALL_YCONS2 * tile_height;
+ py[2] = y_offset + curve_end_offset + WALL_YCONS3 * tile_height;
+ py[3] = globals.gridy[i];
+ if (right)
+ {
+ px[1] = x_offset - WALL_XFACTOR2 * tile_width;
+ px[2] = x_offset - WALL_XFACTOR3 * tile_width;
+ }
+ generate_bezier (px, py, steps, cachex, cachey);
+ draw_bezier_line (buffer, bufsize, width, bytes, steps, cachex, cachey);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ px[0] = -j - 1;
+ darken_bezier_line (buffer, bufsize, width, bytes, px[0], 0,
+ steps, cachex, cachey, delta);
+ px[0] = j + 1;
+ lighten_bezier_line (buffer, bufsize, width, bytes, px[0], 0,
+ steps, cachex, cachey, delta);
+ delta -= sigma;
+ }
+ y_offset = globals.gridy[i];
+ } /* for */
+ g_free(cachex);
+ g_free(cachey);
+}
+
+static void
+draw_bezier_horizontal_border (guchar *buffer,
+ gint bufsize,
+ gint width,
+ gint height,
+ gint bytes,
+ gint y_offset,
+ gint xtiles,
+ gint ytiles,
+ gint blend_lines,
+ gdouble blend_amount,
+ gint steps)
+{
+ gint i, j;
+ gint tile_width = width / xtiles;
+ gint tile_height = height / ytiles;
+ gint tile_width_eighth = tile_width / 8;
+ gint curve_start_offset = 3 * tile_width_eighth;
+ gint curve_end_offset = curve_start_offset + 2 * tile_width_eighth;
+ gint px[4], py[4];
+ gint x_offset = 0;
+ gdouble delta;
+ gdouble sigma = blend_amount / blend_lines;
+ gint up;
+ gint *cachex, *cachey;
+
+ cachex = g_new (gint, steps);
+ cachey = g_new (gint, steps);
+
+ for (i = 0; i < xtiles; i++)
+ {
+ up = g_random_int_range (0, 2);
+
+ px[0] = x_offset;
+ px[1] = x_offset + WALL_XCONS2 * tile_width;
+ px[2] = x_offset + WALL_XCONS3 * tile_width;
+ px[3] = x_offset + curve_start_offset;
+ py[0] = py[3] = y_offset;
+ py[1] = y_offset + WALL_YFACTOR2 * tile_height * FUDGE;
+ py[2] = y_offset + WALL_YFACTOR3 * tile_height * FUDGE;
+ if (!up)
+ {
+ py[1] = y_offset - WALL_YFACTOR2 * tile_height;
+ py[2] = y_offset - WALL_YFACTOR3 * tile_height;
+ }
+ generate_bezier (px, py, steps, cachex, cachey);
+ draw_bezier_line (buffer, bufsize, width, bytes, steps, cachex, cachey);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ py[0] = -j - 1;
+ darken_bezier_line (buffer, bufsize, width, bytes, 0, py[0],
+ steps, cachex, cachey, delta);
+ py[0] = j + 1;
+ lighten_bezier_line (buffer, bufsize, width, bytes, 0, py[0],
+ steps, cachex, cachey, delta);
+ delta -= sigma;
+ }
+ /* bumps */
+ if (up)
+ {
+ draw_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be -j -1 */
+ darken_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP], delta, j);
+ /* use to be +j + 1 */
+ lighten_up_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[UP], delta, j);
+ delta -= sigma;
+ }
+ }
+ else
+ {
+ draw_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN]);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ /* use to be +j + 1 */
+ darken_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN], delta, j);
+ /* use to be -j -1 */
+ lighten_down_bump (buffer, bufsize, width, bytes, y_offset,
+ x_offset + curve_start_offset,
+ globals.steps[DOWN], delta, j);
+ delta -= sigma;
+ }
+ }
+ /* ending side wall line */
+ px[0] = x_offset + curve_end_offset;
+ px[1] = x_offset + curve_end_offset + WALL_XCONS2 * tile_width;
+ px[2] = x_offset + curve_end_offset + WALL_XCONS3 * tile_width;
+ px[3] = globals.gridx[i];
+ py[0] = py[3] = y_offset;
+ py[1] = y_offset + WALL_YFACTOR2 * tile_height * FUDGE;
+ py[2] = y_offset + WALL_YFACTOR3 * tile_height * FUDGE;
+ if (!up)
+ {
+ py[1] = y_offset - WALL_YFACTOR2 * tile_height;
+ py[2] = y_offset - WALL_YFACTOR3 * tile_height;
+ }
+ generate_bezier (px, py, steps, cachex, cachey);
+ draw_bezier_line (buffer, bufsize, width, bytes, steps, cachex, cachey);
+ delta = blend_amount;
+ for (j = 0; j < blend_lines; j++)
+ {
+ py[0] = -j - 1;
+ darken_bezier_line (buffer, bufsize, width, bytes, 0, py[0],
+ steps, cachex, cachey, delta);
+ py[0] = j + 1;
+ lighten_bezier_line (buffer, bufsize, width, bytes, 0, py[0],
+ steps, cachex, cachey, delta);
+ delta -= sigma;
+ }
+ x_offset = globals.gridx[i];
+ } /* for */
+ g_free(cachex);
+ g_free(cachey);
+}
+
+static void
+check_config (gint width,
+ gint height)
+{
+ gint tile_width, tile_height;
+ gint tile_width_limit, tile_height_limit;
+
+ if (config.x < 1)
+ {
+ config.x = 1;
+ }
+ if (config.y < 1)
+ {
+ config.y = 1;
+ }
+ if (config.blend_amount < 0)
+ {
+ config.blend_amount = 0;
+ }
+ if (config.blend_amount > 5)
+ {
+ config.blend_amount = 5;
+ }
+ tile_width = width / config.x;
+ tile_height = height / config.y;
+ tile_width_limit = 0.4 * tile_width;
+ tile_height_limit = 0.4 * tile_height;
+ if ((config.blend_lines > tile_width_limit)
+ || (config.blend_lines > tile_height_limit))
+ {
+ config.blend_lines = MIN(tile_width_limit, tile_height_limit);
+ }
+}
+
+/********************************************************
+ GUI
+********************************************************/
+
+static gboolean
+jigsaw_dialog (guint32 drawable_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkSizeGroup *group;
+ GtkWidget *frame;
+ GtkWidget *rbutton1;
+ GtkWidget *rbutton2;
+ GtkWidget *table;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Jigsaw"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_aspect_preview_new_from_drawable_id (drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (jigsaw_preview),
+ GINT_TO_POINTER (drawable_id));
+
+ frame = gimp_frame_new (_("Number of Tiles"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* xtiles */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Horizontal:"), SCALE_WIDTH, 0,
+ config.x, MIN_XTILES, MAX_XTILES, 1.0, 4.0, 0,
+ TRUE, 0, 0,
+ _("Number of pieces going across"), NULL);
+
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ g_object_unref (group);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &config.x);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* ytiles */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Vertical:"), SCALE_WIDTH, 0,
+ config.y, MIN_YTILES, MAX_YTILES, 1.0, 4.0, 0,
+ TRUE, 0, 0,
+ _("Number of pieces going down"), NULL);
+
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &config.y);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ frame = gimp_frame_new (_("Bevel Edges"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ /* number of blending lines */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Bevel width:"), SCALE_WIDTH, 4,
+ config.blend_lines,
+ MIN_BLEND_LINES, MAX_BLEND_LINES, 1.0, 2.0, 0,
+ TRUE, 0, 0,
+ _("Degree of slope of each piece's edge"), NULL);
+
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &config.blend_lines);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* blending amount */
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("H_ighlight:"), SCALE_WIDTH, 4,
+ config.blend_amount,
+ MIN_BLEND_AMOUNT, MAX_BLEND_AMOUNT, 0.05, 0.1, 2,
+ TRUE, 0, 0,
+ _("The amount of highlighting on the edges "
+ "of each piece"), NULL);
+
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.blend_amount);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ /* frame for primitive radio buttons */
+
+ frame = gimp_int_radio_group_new (TRUE, _("Jigsaw Style"),
+ G_CALLBACK (gimp_radio_button_update),
+ &config.style, config.style,
+
+ _("_Square"), BEZIER_1, &rbutton1,
+ _("C_urved"), BEZIER_2, &rbutton2,
+
+ NULL);
+
+ gimp_help_set_help_data (rbutton1, _("Each piece has straight sides"), NULL);
+ gimp_help_set_help_data (rbutton2, _("Each piece has curved sides"), NULL);
+ g_signal_connect_swapped (rbutton1, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (rbutton2, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/mail.c b/plug-ins/common/mail.c
new file mode 100644
index 0000000..668c932
--- /dev/null
+++ b/plug-ins/common/mail.c
@@ -0,0 +1,830 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Copyright (C) 1997 Daniel Risacher
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * GUMP - Gimp Useless Mail Plugin
+ * (or Gump Useless Mail Plugin if you prefer)
+ *
+ * by Adrian Likins <adrian@gimp.org>
+ * MIME encapsulation by Reagan Blundell <reagan@emails.net>
+ *
+ * As always: The utility of this plugin is left as an exercise for
+ * the reader
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#ifdef SENDMAIL
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define BUFFER_SIZE 256
+
+#define PLUG_IN_PROC "plug-in-mail-image"
+#define PLUG_IN_BINARY "mail"
+#define PLUG_IN_ROLE "gimp-mail"
+
+typedef struct
+{
+ gchar filename[BUFFER_SIZE];
+ gchar receipt[BUFFER_SIZE];
+ gchar from[BUFFER_SIZE];
+ gchar subject[BUFFER_SIZE];
+ gchar comment[BUFFER_SIZE];
+} m_info;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType send_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 run_mode);
+
+static gboolean send_dialog (void);
+static void mail_entry_callback (GtkWidget *widget,
+ gchar *data);
+static gboolean valid_file (const gchar *filename);
+static gchar * find_extension (const gchar *filename);
+
+#ifdef SENDMAIL
+static void mesg_body_callback (GtkTextBuffer *buffer,
+ gpointer data);
+
+static gchar * sendmail_content_type (const gchar *filename);
+static void sendmail_create_headers (FILE *mailpipe);
+static gboolean sendmail_to64 (const gchar *filename,
+ FILE *outfile,
+ GError **error);
+static FILE * sendmail_pipe (gchar **cmd,
+ GPid *pid);
+#endif
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static m_info mail_info =
+{
+ "", "", "", "", ""
+};
+
+static gchar *mesg_body = NULL;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ gchar *email_bin;
+
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "to-address", "The email address to send to" },
+ { GIMP_PDB_STRING, "from-address", "The email address for the From: field" },
+ { GIMP_PDB_STRING, "subject", "The subject" },
+ { GIMP_PDB_STRING, "comment", "The Comment" },
+ { GIMP_PDB_INT32, "encapsulation", "ignored" }
+ };
+
+ /* Check if xdg-email or sendmail is installed.
+ * TODO: allow setting the location of the executable in preferences.
+ */
+#ifdef SENDMAIL
+ if (strlen (SENDMAIL) == 0)
+ {
+ email_bin = g_find_program_in_path ("sendmail");
+ }
+ else
+ {
+ /* If a directory has been set at build time, we assume that sendmail
+ * can only be in this directory. */
+ email_bin = g_build_filename (SENDMAIL, "sendmail", NULL);
+ if (! g_file_test (email_bin, G_FILE_TEST_IS_EXECUTABLE))
+ {
+ g_free (email_bin);
+ email_bin = NULL;
+ }
+ }
+#else
+ email_bin = g_find_program_in_path ("xdg-email");
+#endif
+
+ if (email_bin == NULL)
+ return;
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Send the image by email"),
+#ifdef SENDMAIL
+ "Sendmail is used to send emails and must be properly configured.",
+#else /* xdg-email */
+ "The preferred email composer is used to send emails and must be properly configured.",
+#endif
+ "Adrian Likins, Reagan Blundell",
+ "Adrian Likins, Reagan Blundell, Daniel Risacher, "
+ "Spencer Kimball and Peter Mattis",
+ "1995-1997",
+ N_("Send by E_mail..."),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Send");
+ gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_EDIT);
+
+ g_free (email_bin);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_image;
+ drawable_ID = param[2].data.d_drawable;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, PLUG_IN_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &mail_info);
+ {
+ gchar *filename = gimp_image_get_filename (image_ID);
+
+ if (filename)
+ {
+ gchar *basename = g_filename_display_basename (filename);
+
+ g_strlcpy (mail_info.filename, basename, BUFFER_SIZE);
+ g_free (basename);
+ g_free (filename);
+ }
+ }
+
+ if (! send_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams < 8)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ g_strlcpy (mail_info.filename,
+ param[3].data.d_string, BUFFER_SIZE);
+ g_strlcpy (mail_info.receipt,
+ param[4].data.d_string, BUFFER_SIZE);
+ g_strlcpy (mail_info.from,
+ param[5].data.d_string, BUFFER_SIZE);
+ g_strlcpy (mail_info.subject,
+ param[6].data.d_string, BUFFER_SIZE);
+ g_strlcpy (mail_info.comment,
+ param[7].data.d_string, BUFFER_SIZE);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &mail_info);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ status = send_image (mail_info.filename,
+ image_ID,
+ drawable_ID,
+ run_mode);
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (mesg_body)
+ g_strlcpy (mail_info.comment, mesg_body, BUFFER_SIZE);
+
+ gimp_set_data (PLUG_IN_PROC, &mail_info, sizeof (m_info));
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static GimpPDBStatusType
+send_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 run_mode)
+{
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gchar *ext;
+ gchar *tmpname;
+#ifndef SENDMAIL /* xdg-email */
+ gchar *mailcmd[9];
+ gchar *filepath = NULL;
+ GFile *tmp_dir = NULL;
+ GFileEnumerator *enumerator;
+ gint i;
+#else /* SENDMAIL */
+ gchar *mailcmd[3];
+ GPid mailpid;
+ FILE *mailpipe = NULL;
+#endif
+ GError *error = NULL;
+
+ ext = find_extension (filename);
+
+ if (ext == NULL)
+ return GIMP_PDB_CALLING_ERROR;
+
+ /* get a temp name with the right extension and save into it. */
+ tmpname = gimp_temp_name (ext + 1);
+
+ if (! (gimp_file_save (run_mode,
+ image_ID,
+ drawable_ID,
+ tmpname,
+ tmpname) && valid_file (tmpname)))
+ {
+ goto error;
+ }
+
+#ifndef SENDMAIL /* xdg-email */
+ /* From xdg-email doc:
+ * "Some e-mail applications require the file to remain present
+ * after xdg-email returns."
+ * As a consequence, the file cannot be removed at the end of the
+ * function. We actually have no way to ever know *when* the file can
+ * be removed since the caller could leave the email window opened for
+ * hours. Yet we still want to clean sometimes and not have temporary
+ * images piling up.
+ * So I use a known directory that we control under $GIMP_DIRECTORY/tmp/,
+ * and clean it out each time the plugin runs. This means that *if* you
+ * are in the above case (your email client requires the file to stay
+ * alive), * you cannot run twice the plugin at the same time.
+ */
+ tmp_dir = gimp_directory_file ("tmp", PLUG_IN_PROC, NULL);
+
+ if (g_mkdir_with_parents (gimp_file_get_utf8_name (tmp_dir),
+ S_IRUSR | S_IWUSR | S_IXUSR) == -1)
+ {
+ g_message ("Temporary directory %s could not be created.",
+ gimp_file_get_utf8_name (tmp_dir));
+ g_error_free (error);
+ goto error;
+ }
+
+ enumerator = g_file_enumerate_children (tmp_dir,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL, NULL);
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator,
+ NULL, NULL)))
+ {
+ if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
+ {
+ GFile *file = g_file_enumerator_get_child (enumerator, info);
+ g_file_delete (file, NULL, NULL);
+ g_object_unref (file);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+
+ filepath = g_build_filename (gimp_file_get_utf8_name (tmp_dir),
+ mail_info.filename, NULL);
+ g_rename (tmpname, filepath);
+
+ mailcmd[0] = g_strdup ("xdg-email");
+ mailcmd[1] = "--attach";
+ mailcmd[2] = filepath;
+ i = 3;
+ if (strlen (mail_info.subject) > 0)
+ {
+ mailcmd[i++] = "--subject";
+ mailcmd[i++] = mail_info.subject;
+ }
+ if (strlen (mail_info.comment) > 0)
+ {
+ mailcmd[i++] = "--body";
+ mailcmd[i++] = mail_info.comment;
+ }
+ if (strlen (mail_info.receipt) > 0)
+ {
+ mailcmd[i++] = mail_info.receipt;
+ }
+ mailcmd[i] = NULL;
+
+ if (! g_spawn_async (NULL, mailcmd, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &error))
+ {
+ g_message ("%s", error->message);
+ g_error_free (error);
+ goto error;
+ }
+
+#else /* SENDMAIL */
+ /* construct the "sendmail user@location" line */
+ if (strlen (SENDMAIL) == 0)
+ mailcmd[0] = g_strdup ("sendmail");
+ else
+ mailcmd[0] = g_build_filename (SENDMAIL, "sendmail", NULL);
+
+ mailcmd[1] = mail_info.receipt;
+ mailcmd[2] = NULL;
+
+ /* create a pipe to sendmail */
+ mailpipe = sendmail_pipe (mailcmd, &mailpid);
+
+ if (mailpipe == NULL)
+ return GIMP_PDB_EXECUTION_ERROR;
+
+ sendmail_create_headers (mailpipe);
+
+ fflush (mailpipe);
+
+ if (! sendmail_to64 (tmpname, mailpipe, &error))
+ {
+ g_message ("%s", error->message);
+ g_error_free (error);
+ goto error;
+ }
+
+ fprintf (mailpipe, "\n--GUMP-MIME-boundary--\n");
+#endif
+
+ goto cleanup;
+
+error:
+ /* stop sendmail from doing anything */
+#ifdef SENDMAIL
+ kill (mailpid, SIGINT);
+#endif
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+cleanup:
+ /* close out the sendmail process */
+#ifdef SENDMAIL
+ if (mailpipe)
+ {
+ fclose (mailpipe);
+ waitpid (mailpid, NULL, 0);
+ g_spawn_close_pid (mailpid);
+ }
+
+ /* delete the tmpfile that was generated */
+ g_unlink (tmpname);
+#else
+ if (tmp_dir)
+ g_object_unref (tmp_dir);
+ if (filepath)
+ g_free (filepath);
+#endif
+
+ g_free (mailcmd[0]);
+ g_free (tmpname);
+
+ return status;
+}
+
+
+static gboolean
+send_dialog (void)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *entry;
+ GtkWidget *table;
+#ifdef SENDMAIL
+ GtkWidget *scrolled_window;
+ GtkWidget *text_view;
+ GtkTextBuffer *text_buffer;
+#endif
+ gchar *gump_from;
+ gint row = 0;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ /* check gimprc for a preferred "From:" address */
+ gump_from = gimp_gimprc_query ("gump-from");
+
+ if (gump_from)
+ {
+ g_strlcpy (mail_info.from, gump_from, BUFFER_SIZE);
+ g_free (gump_from);
+ }
+
+ dlg = gimp_dialog_new (_("Send by Email"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Send"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* table */
+ table = gtk_table_new (5, 2, FALSE);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 12);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+
+ /* Filename entry */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
+ gtk_entry_set_text (GTK_ENTRY (entry), mail_info.filename);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("_Filename:"), 0.0, 0.5,
+ entry, 1, FALSE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (mail_entry_callback),
+ mail_info.filename);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+
+#ifdef SENDMAIL
+ /* To entry */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
+ gtk_entry_set_text (GTK_ENTRY (entry), mail_info.receipt);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ C_("email-address", "_To:"), 0.0, 0.5,
+ entry, 1, FALSE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (mail_entry_callback),
+ mail_info.receipt);
+
+ gtk_widget_grab_focus (entry);
+
+ /* From entry */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
+ gtk_entry_set_text (GTK_ENTRY (entry), mail_info.from);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ C_("email-address", "_From:"), 0.0, 0.5,
+ entry, 1, FALSE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (mail_entry_callback),
+ mail_info.from);
+
+ /* Subject entry */
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 200, -1);
+ gtk_entry_set_max_length (GTK_ENTRY (entry), BUFFER_SIZE - 1);
+ gtk_entry_set_text (GTK_ENTRY (entry), mail_info.subject);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("S_ubject:"), 0.0, 0.5,
+ entry, 1, FALSE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (mail_entry_callback),
+ mail_info.subject);
+
+ /* Body */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (main_vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ text_buffer = gtk_text_buffer_new (NULL);
+
+ g_signal_connect (text_buffer, "changed",
+ G_CALLBACK (mesg_body_callback),
+ NULL);
+
+ gtk_text_buffer_set_text (text_buffer, mail_info.comment, -1);
+
+ text_view = gtk_text_view_new_with_buffer (text_buffer);
+ g_object_unref (text_buffer);
+
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
+ gtk_widget_show (text_view);
+#endif
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+static gboolean
+valid_file (const gchar *filename)
+{
+ GStatBuf buf;
+
+ return g_stat (filename, &buf) == 0 && buf.st_size > 0;
+}
+
+static gchar *
+find_extension (const gchar *filename)
+{
+ gchar *filename_copy;
+ gchar *ext;
+
+ /* we never free this copy - aren't we evil! */
+ filename_copy = g_strdup (filename);
+
+ /* find the extension, boy! */
+ ext = strrchr (filename_copy, '.');
+
+ while (TRUE)
+ {
+ if (!ext || ext[1] == '\0' || strchr (ext, G_DIR_SEPARATOR))
+ {
+ g_message (_("some sort of error with the file extension "
+ "or lack thereof"));
+
+ return NULL;
+ }
+
+ if (0 != g_ascii_strcasecmp (ext, ".gz") &&
+ 0 != g_ascii_strcasecmp (ext, ".bz2"))
+ {
+ return ext;
+ }
+ else
+ {
+ /* we found something, loop back, and look again */
+ *ext = 0;
+ ext = strrchr (filename_copy, '.');
+ }
+ }
+
+ g_free (filename_copy);
+
+ return ext;
+}
+
+static void
+mail_entry_callback (GtkWidget *widget,
+ gchar *data)
+{
+ g_strlcpy (data, gtk_entry_get_text (GTK_ENTRY (widget)), BUFFER_SIZE);
+}
+
+#ifdef SENDMAIL
+static void
+mesg_body_callback (GtkTextBuffer *buffer,
+ gpointer data)
+{
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
+
+ g_free (mesg_body);
+ mesg_body = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
+}
+
+static gchar *
+sendmail_content_type (const gchar *filename)
+{
+ /* This function returns a MIME Content-type: value based on the
+ filename it is given. */
+ const gchar *type_mappings[20] =
+ {
+ "gif" , "image/gif",
+ "jpg" , "image/jpeg",
+ "jpeg", "image/jpeg",
+ "tif" , "image/tiff",
+ "tiff", "image/tiff",
+ "png" , "image/png",
+ "g3" , "image/g3fax",
+ "ps" , "application/postscript",
+ "eps" , "application/postscript",
+ NULL, NULL
+ };
+
+ gchar *ext;
+ gint i;
+
+ ext = find_extension (filename);
+
+ if (!ext)
+ {
+ return g_strdup ("application/octet-stream");
+ }
+
+ i = 0;
+ ext += 1;
+
+ while (type_mappings[i])
+ {
+ if (g_ascii_strcasecmp (ext, type_mappings[i]) == 0)
+ {
+ return g_strdup (type_mappings[i + 1]);
+ }
+
+ i += 2;
+ }
+
+ return g_strdup_printf ("image/x-%s", ext);
+}
+
+static void
+sendmail_create_headers (FILE *mailpipe)
+{
+ /* create all the mail header stuff. Feel free to add your own */
+ /* It is advisable to leave the X-Mailer header though, as */
+ /* there is a possibility of a Gimp mail scanner/reader in the */
+ /* future. It will probabaly need that header. */
+
+ fprintf (mailpipe, "To: %s \n", mail_info.receipt);
+ fprintf (mailpipe, "Subject: %s \n", mail_info.subject);
+ if (strlen (mail_info.from) > 0)
+ fprintf (mailpipe, "From: %s \n", mail_info.from);
+
+ fprintf (mailpipe, "X-Mailer: GIMP Useless Mail plug-in %s\n", GIMP_VERSION);
+
+ fprintf (mailpipe, "MIME-Version: 1.0\n");
+ fprintf (mailpipe, "Content-type: multipart/mixed; "
+ "boundary=GUMP-MIME-boundary\n");
+
+ fprintf (mailpipe, "\n\n");
+
+ fprintf (mailpipe, "--GUMP-MIME-boundary\n");
+ fprintf (mailpipe, "Content-type: text/plain; charset=UTF-8\n\n");
+
+ if (mesg_body)
+ fprintf (mailpipe, "%s", mesg_body);
+
+ fprintf (mailpipe, "\n\n");
+
+ {
+ gchar *content = sendmail_content_type (mail_info.filename);
+
+ fprintf (mailpipe, "--GUMP-MIME-boundary\n");
+ fprintf (mailpipe, "Content-type: %s\n", content);
+ fprintf (mailpipe, "Content-transfer-encoding: base64\n");
+ fprintf (mailpipe, "Content-disposition: attachment; filename=\"%s\"\n",
+ mail_info.filename);
+ fprintf (mailpipe, "Content-description: %s\n\n", mail_info.filename);
+
+ g_free (content);
+ }
+}
+
+static gboolean
+sendmail_to64 (const gchar *filename,
+ FILE *outfile,
+ GError **error)
+{
+ GMappedFile *infile;
+ const guchar *in;
+ gchar out[2048];
+ gint state = 0;
+ gint save = 0;
+ gsize len;
+ gsize bytes;
+ gsize c;
+
+ infile = g_mapped_file_new (filename, FALSE, error);
+ if (! infile)
+ return FALSE;
+
+ in = (const guchar *) g_mapped_file_get_contents (infile);
+ len = g_mapped_file_get_length (infile);
+
+ for (c = 0; c < len;)
+ {
+ gsize step = MIN (1024, len - c);
+
+ bytes = g_base64_encode_step (in + c, step, TRUE, out, &state, &save);
+ fwrite (out, 1, bytes, outfile);
+
+ c += step;
+ }
+
+ bytes = g_base64_encode_close (TRUE, out, &state, &save);
+ fwrite (out, 1, bytes, outfile);
+
+ g_mapped_file_unref (infile);
+
+ return TRUE;
+}
+
+static FILE *
+sendmail_pipe (gchar **cmd,
+ GPid *pid)
+{
+ gint fd;
+ GError *err = NULL;
+
+ if (! g_spawn_async_with_pipes (NULL, cmd, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD |
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, pid, &fd, NULL, NULL, &err))
+ {
+ g_message (_("Could not start sendmail (%s)"), err->message);
+ g_error_free (err);
+
+ *pid = -1;
+ return NULL;
+ }
+
+ return fdopen (fd, "wb");
+}
+#endif
diff --git a/plug-ins/common/max-rgb.c b/plug-ins/common/max-rgb.c
new file mode 100644
index 0000000..1261548
--- /dev/null
+++ b/plug-ins/common/max-rgb.c
@@ -0,0 +1,373 @@
+/* max_rgb.c -- This is a plug-in for GIMP
+ * Author: Shuji Narazaki <narazaki@InetQ.or.jp>
+ * Time-stamp: <2000-02-08 16:26:24 yasuhiro>
+ * Version: 0.35
+ *
+ * Copyright (C) 1997 Shuji Narazaki <narazaki@InetQ.or.jp>
+ *
+ * May 2000 - tim copperfield [timecop@japan.co.jp]
+ *
+ * Added a preview mode. After adding preview mode realised just exactly
+ * how useless this plugin is :)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-max-rgb"
+#define PLUG_IN_BINARY "max-rgb"
+#define PLUG_IN_ROLE "gimp-max-rgb"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType main_function (GimpDrawable *drawable,
+ GimpPreview *preview);
+
+static gint max_rgb_dialog (GimpDrawable *drawable);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+enum
+{
+ MIN_CHANNELS = 0,
+ MAX_CHANNELS = 1
+};
+
+typedef struct
+{
+ gint max_p;
+} ValueType;
+
+static ValueType pvals =
+{
+ MAX_CHANNELS
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args [] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (not used)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "max-p", "{ MINIMIZE (0), MAXIMIZE (1) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Reduce image to pure red, green, and blue"),
+ "There's no help yet.",
+ "Shuji Narazaki (narazaki@InetQ.or.jp)",
+ "Shuji Narazaki",
+ "May 2000",
+ N_("Maxim_um RGB..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpDrawable *drawable;
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+ /* Since a channel might be selected, we must check whether RGB or not. */
+ if (!gimp_drawable_is_rgb (drawable->drawable_id))
+ {
+ g_message (_("Can only operate on RGB drawables."));
+ return;
+ }
+ if (! max_rgb_dialog (drawable))
+ return;
+ break;
+ case GIMP_RUN_NONINTERACTIVE:
+ /* You must copy the values of parameters to pvals or dialog variables. */
+ pvals.max_p = param[3].data.d_int32;
+ break;
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+ break;
+ }
+
+ status = main_function (drawable, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ if (run_mode == GIMP_RUN_INTERACTIVE && status == GIMP_PDB_SUCCESS)
+ gimp_set_data (PLUG_IN_PROC, &pvals, sizeof (ValueType));
+
+ values[0].data.d_status = status;
+}
+
+typedef struct
+{
+ gint init_value;
+ gint flag;
+ gboolean has_alpha;
+} MaxRgbParam_t;
+
+static void
+max_rgb_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gpointer data)
+{
+ MaxRgbParam_t *param = (MaxRgbParam_t*) data;
+ gint ch, max_ch = 0;
+ guchar max, tmp_value;
+
+ max = param->init_value;
+ for (ch = 0; ch < 3; ch++)
+ if (param->flag * max <= param->flag * (tmp_value = (*src++)))
+ {
+ if (max == tmp_value)
+ {
+ max_ch += 1 << ch;
+ }
+ else
+ {
+ max_ch = 1 << ch; /* clear memories of old channels */
+ max = tmp_value;
+ }
+ }
+
+ dest[0] = (max_ch & (1 << 0)) ? max : 0;
+ dest[1] = (max_ch & (1 << 1)) ? max : 0;
+ dest[2] = (max_ch & (1 << 2)) ? max : 0;
+ if (param->has_alpha)
+ dest[3] = *src;
+}
+
+static GimpPDBStatusType
+main_function (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ MaxRgbParam_t param;
+
+ param.init_value = (pvals.max_p > 0) ? 0 : 255;
+ param.flag = (0 < pvals.max_p) ? 1 : -1;
+ param.has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ if (preview)
+ {
+ gint i;
+ guchar *buffer;
+ guchar *src;
+ gint width, height, bpp;
+
+ src = gimp_zoom_preview_get_source (GIMP_ZOOM_PREVIEW (preview),
+ &width, &height, &bpp);
+
+ buffer = g_new (guchar, width * height * bpp);
+
+ for (i = 0; i < width * height; i++)
+ {
+ max_rgb_func (src + i * bpp,
+ buffer + i * bpp,
+ bpp,
+ &param);
+ }
+
+ gimp_preview_draw_buffer (preview, buffer, width * bpp);
+ g_free (buffer);
+ g_free (src);
+ }
+ else
+ {
+ GimpPixelRgn srcPR, destPR;
+ gint x1, y1, x2, y2;
+ gpointer pr;
+ gint total_area;
+ gint area_so_far;
+ gint count;
+
+ gimp_progress_init (_("Max RGB"));
+
+ gimp_drawable_mask_bounds (drawable->drawable_id, &x1, &y1, &x2, &y2);
+
+ total_area = (x2 - x1) * (y2 - y1);
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ goto out;
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&srcPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ FALSE, FALSE);
+ gimp_pixel_rgn_init (&destPR, drawable, x1, y1, (x2 - x1), (y2 - y1),
+ TRUE, TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &srcPR, &destPR), count = 0;
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr), count++)
+ {
+ const guchar *src = srcPR.data;
+ guchar *dest = destPR.data;
+ gint row;
+
+ for (row = 0; row < srcPR.h; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint pixels = srcPR.w;
+
+ while (pixels--)
+ {
+ max_rgb_func (s, d, srcPR.bpp, &param);
+
+ s += srcPR.bpp;
+ d += destPR.bpp;
+ }
+
+ src += srcPR.rowstride;
+ dest += destPR.rowstride;
+ }
+
+ area_so_far += srcPR.w * srcPR.h;
+
+ if ((count % 16) == 0)
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ /* update the processed region */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+
+ out:
+ gimp_drawable_detach (drawable);
+ }
+
+ return GIMP_PDB_SUCCESS;
+}
+
+
+/* dialog stuff */
+static gint
+max_rgb_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *frame;
+ GtkWidget *max;
+ GtkWidget *min;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Maximum RGB Value"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_zoom_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (main_function),
+ drawable);
+
+ frame = gimp_int_radio_group_new (FALSE, NULL,
+ G_CALLBACK (gimp_radio_button_update),
+ &pvals.max_p, pvals.max_p,
+
+ _("_Hold the maximal channels"),
+ MAX_CHANNELS, &max,
+
+ _("Ho_ld the minimal channels"),
+ MIN_CHANNELS, &min,
+
+ NULL);
+
+ g_signal_connect_swapped (max, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (min, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/mkgen.pl b/plug-ins/common/mkgen.pl
new file mode 100755
index 0000000..1e95284
--- /dev/null
+++ b/plug-ins/common/mkgen.pl
@@ -0,0 +1,234 @@
+#!/usr/bin/perl -w
+
+use lib '../../pdb';
+
+require 'util.pl';
+
+*write_file = \&Gimp::CodeGen::util::write_file;
+*FILE_EXT = \$Gimp::CodeGen::util::FILE_EXT;
+
+$destdir = ".";
+$builddir = ".";
+
+$ignorefile = ".gitignore";
+$rcfile = "gimprc.common";
+
+$outmk = "$builddir/Makefile.am$FILE_EXT";
+$outignore = "$builddir/$ignorefile$FILE_EXT";
+$outrc = "$builddir/$rcfile$FILE_EXT";
+
+open MK, "> $outmk";
+open IGNORE, "> $outignore";
+open RC, "> $outrc";
+
+require './plugin-defs.pl';
+
+$bins = ""; $opts = ""; $dirs = "";
+
+foreach (sort keys %plugins) {
+ my $makename = $_;
+ $makename =~ s/-/_/g;
+
+ if (exists $plugins{$_}->{optional}) {
+ $bins .= "${makename}_libexec_PROGRAMS = \$(\U$makename\E)\n";
+ $opts .= "\t$_ \\\n";
+ }
+ else {
+ $bins .= "${makename}_libexec_PROGRAMS = $_\n";
+ }
+
+ $dirs .= "${makename}_libexecdir = \$(gimpplugindir)/plug-ins/$_\n";
+}
+
+$extra = "";
+foreach (@extra) { $extra .= "\t$_\t\\\n" }
+if ($extra) {
+ $extra =~ s/\t\\\n$//s;
+ $extra = "\t\\\n$extra";
+}
+
+foreach ($bins, $opts) { s/ \\\n$//s }
+
+print MK <<EOT;
+
+
+## ---------------------------------------------------------
+## This file is autogenerated by mkgen.pl and plugin-defs.pl
+## ---------------------------------------------------------
+
+## Modify those two files instead of this one; for most
+## plug-ins you should only need to modify plugin-defs.pl.
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+framework_cocoa = -framework Cocoa
+endif
+
+if HAVE_WINDRES
+include \$(top_srcdir)/build/windows/gimprc-plug-ins.rule
+include $rcfile
+endif
+
+libgimp = \$(top_builddir)/libgimp/libgimp-\$(GIMP_API_VERSION).la
+libgimpbase = \$(top_builddir)/libgimpbase/libgimpbase-\$(GIMP_API_VERSION).la
+libgimpcolor = \$(top_builddir)/libgimpcolor/libgimpcolor-\$(GIMP_API_VERSION).la
+libgimpconfig = \$(top_builddir)/libgimpconfig/libgimpconfig-\$(GIMP_API_VERSION).la
+libgimpmath = \$(top_builddir)/libgimpmath/libgimpmath-\$(GIMP_API_VERSION).la \$(libm)
+libgimpmodule = \$(top_builddir)/libgimpmodule/libgimpmodule-\$(GIMP_API_VERSION).la
+libgimpui = \$(top_builddir)/libgimp/libgimpui-\$(GIMP_API_VERSION).la
+libgimpwidgets = \$(top_builddir)/libgimpwidgets/libgimpwidgets-\$(GIMP_API_VERSION).la
+
+
+AM_LDFLAGS = \$(mwindows)
+
+EXTRA_DIST = \\
+ mkgen.pl \\
+ plugin-defs.pl$extra \\
+ $rcfile
+
+AM_CPPFLAGS = \\
+ -I\$(top_srcdir) \\
+ \$(GTK_CFLAGS) \\
+ \$(GEGL_CFLAGS) \\
+ -I\$(includedir)
+
+$dirs
+
+$bins
+
+EXTRA_PROGRAMS = \\
+$opts
+
+install-\%: \%
+ \@\$(NORMAL_INSTALL)
+ \$(mkinstalldirs) \$(DESTDIR)\$(gimpplugindir)/plug-ins/\$<
+ \@p=\$<; p1=`echo \$\$p|sed 's/\$(EXEEXT)\$\$//'`; \\
+ if test -f \$\$p \\
+ || test -f \$\$p1 \\
+ ; then \\
+ f=`echo "\$\$p1" | sed 's,^.*/,,;\$(transform);s/\$\$/\$(EXEEXT)/'`; \\
+ echo " \$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(gimpplugindir)/plug-ins/\$\$p/\$\$f"; \\
+ \$(INSTALL_PROGRAM_ENV) \$(LIBTOOL) --mode=install \$(INSTALL_PROGRAM) \$\$p \$(DESTDIR)\$(gimpplugindir)/plug-ins/\$\$p/\$\$f || exit 1; \\
+ else :; fi
+EOT
+
+print IGNORE <<EOT;
+/.deps
+/.libs
+/Makefile
+/Makefile.in
+EOT
+
+foreach (sort keys %plugins) {
+ my $makename = $_;
+ $makename =~ s/-/_/g;
+
+ my $libgimp = "";
+
+ if (exists $plugins{$_}->{ui}) {
+ $libgimp .= "\$(libgimpui)";
+ $libgimp .= "\t\t\\\n\t\$(libgimpwidgets)";
+ $libgimp .= "\t\\\n\t\$(libgimpmodule)";
+ $libgimp .= "\t\\\n\t";
+ }
+
+ $libgimp .= "\$(libgimp)";
+ $libgimp .= "\t\t\\\n\t\$(libgimpmath)";
+ $libgimp .= "\t\t\\\n\t\$(libgimpconfig)";
+ $libgimp .= "\t\\\n\t\$(libgimpcolor)";
+ $libgimp .= "\t\t\\\n\t\$(libgimpbase)";
+
+ my $glib;
+ if (exists $plugins{$_}->{ui}) {
+ $glib = "\$(GTK_LIBS)\t\t\\";
+ } else {
+ $glib = "\$(CAIRO_LIBS)\t\t\\\n\t\$(GDK_PIXBUF_LIBS)\t\\";
+
+ if (exists $plugins{$_}->{gio} &&
+ ! exists $plugins{$_}->{gegl}) {
+ $glib .= "\n\t\$(GIO_LIBS)\t\t\\";
+ }
+ }
+
+ if (exists $plugins{$_}->{gegl}) {
+ $glib .= "\n\t\$(GEGL_LIBS)\t\t\\";
+ }
+
+ my $optlib = "";
+
+ if (exists $plugins{$_}->{libs}) {
+ $optlib = "\n\t\$(" . $plugins{$_}->{libs} . ")\t\t\\";
+ }
+
+ if (exists $plugins{$_}->{ldflags}) {
+ my $ldflags = $plugins{$_}->{ldflags};
+
+ print MK <<EOT;
+
+${makename}_LDFLAGS = $ldflags
+EOT
+ }
+
+ if (exists $plugins{$_}->{cflags}) {
+ my $cflags = $plugins{$_}->{cflags};
+ my $cflagsvalue = $cflags =~ /FLAGS/ ? "\$($cflags)" : $cflags;
+
+ print MK <<EOT;
+
+${makename}_CFLAGS = $cflagsvalue
+EOT
+ }
+
+ if (exists $plugins{$_}->{cppflags}) {
+ my $cppflags = $plugins{$_}->{cppflags};
+
+ print MK <<EOT;
+
+${makename}_CPPFLAGS = $cppflags
+EOT
+ }
+
+ my $deplib = "\$(RT_LIBS)\t\t\\\n\t\$(INTLLIBS)";
+ if (exists $plugins{$_}->{libdep}) {
+ my @lib = split(/:/, $plugins{$_}->{libdep});
+ foreach $lib (@lib) {
+ $deplib = "\$(\U$lib\E_LIBS)\t\t\\\n\t$deplib";
+ }
+ }
+
+ my $rclib = "\$(${makename}_RC)";
+
+ print MK <<EOT;
+
+${makename}_SOURCES = \\
+ $_.c
+
+${makename}_LDADD = \\
+ $libgimp \\
+ $glib$optlib
+ $deplib \\
+ $rclib
+EOT
+
+ print RC <<EOT;
+${makename}_RC = $_.rc.o
+EOT
+
+ print IGNORE "/$_\n";
+ print IGNORE "/$_.exe\n";
+}
+
+close RC;
+close MK;
+close IGNORE;
+
+&write_file($outmk, $destdir);
+&write_file($outignore, $destdir);
+&write_file($outrc, $destdir);
+
diff --git a/plug-ins/common/nl-filter.c b/plug-ins/common/nl-filter.c
new file mode 100644
index 0000000..3f4f302
--- /dev/null
+++ b/plug-ins/common/nl-filter.c
@@ -0,0 +1,1149 @@
+/**************************************************
+ * file: nlfilt/nlfilt.c
+ *
+ * Copyright (c) 1997 Eric L. Hernes (erich@rrnet.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Algorithm fixes, V2.0 compatibility by David Hodson hodsond@ozemail.com.au
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-nlfilt"
+#define PLUG_IN_BINARY "nl-filter"
+#define PLUG_IN_ROLE "gimp-nl-filter"
+
+
+typedef struct
+{
+ gdouble alpha;
+ gdouble radius;
+ gint filter;
+} NLFilterValues;
+
+typedef enum
+{
+ filter_alpha_trim,
+ filter_opt_est,
+ filter_edge_enhance
+} FilterType;
+
+static NLFilterValues nlfvals =
+{
+ 0.3,
+ 0.3,
+ 0
+};
+
+/* function protos */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparam,
+ const GimpParam *param,
+ gint *nretvals,
+ GimpParam **retvals);
+
+static void nlfilter (gint32 drawable_id,
+ GimpPreview *preview);
+static void nlfilter_preview (gpointer drawable_id,
+ GimpPreview *preview);
+
+static gboolean nlfilter_dialog (gint32 drawable_id);
+
+static gint nlfiltInit (gdouble alpha,
+ gdouble radius,
+ FilterType filter);
+
+static void nlfiltRow (guchar *srclast,
+ guchar *srcthis,
+ guchar *srcnext,
+ guchar *dst,
+ gint width,
+ gint bpp,
+ gint filtno);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "img", "The Image to Filter" },
+ { GIMP_PDB_DRAWABLE, "drw", "The Drawable" },
+ { GIMP_PDB_FLOAT, "alpha", "The amount of the filter to apply" },
+ { GIMP_PDB_FLOAT, "radius", "The filter radius" },
+ { GIMP_PDB_INT32, "filter", "The Filter to Run, "
+ "0 - alpha trimmed mean; "
+ "1 - optimal estimation (alpha controls noise variance); "
+ "2 - edge enhancement" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Nonlinear swiss army knife filter"),
+ "This is the pnmnlfilt, in gimp's clothing. "
+ "See the pnmnlfilt manpage for details.",
+ "Graeme W. Gill, gimp 0.99 plug-in by Eric L. Hernes",
+ "Graeme W. Gill, Eric L. Hernes",
+ "1997",
+ N_("_NL Filter..."),
+ "RGB,GRAY",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Enhance");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &nlfvals);
+
+ if (! nlfilter_dialog (drawable_id))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ nlfvals.alpha = param[3].data.d_float;
+ nlfvals.radius = param[4].data.d_float;
+ nlfvals.filter = param[5].data.d_int32;
+ }
+
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &nlfvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ nlfilter (drawable_id, NULL);
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &nlfvals, sizeof (NLFilterValues));
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* pnmnlfilt.c - 4 in 1 (2 non-linear) filter
+** - smooth an anyimage
+** - do alpha trimmed mean filtering on an anyimage
+** - do optimal estimation smoothing on an anyimage
+** - do edge enhancement on an anyimage
+**
+** Version 1.0
+**
+** The implementation of an alpha-trimmed mean filter
+** is based on the description in IEEE CG&A May 1990
+** Page 23 by Mark E. Lee and Richard A. Redner.
+**
+** The paper recommends using a hexagon sampling region around each
+** pixel being processed, allowing an effective sub pixel radius to be
+** specified. The hexagon values are sythesised by area sampling the
+** rectangular pixels with a hexagon grid. The seven hexagon values
+** obtained from the 3x3 pixel grid are used to compute the alpha
+** trimmed mean. Note that an alpha value of 0.0 gives a conventional
+** mean filter (where the radius controls the contribution of
+** surrounding pixels), while a value of 0.5 gives a median filter.
+** Although there are only seven values to trim from before finding
+** the mean, the algorithm has been extended from that described in
+** CG&A by using interpolation, to allow a continuous selection of
+** alpha value between and including 0.0 to 0.5 The useful values
+** for radius are between 0.3333333 (where the filter will have no
+** effect because only one pixel is sampled), to 1.0, where all
+** pixels in the 3x3 grid are sampled.
+**
+** The optimal estimation filter is taken from an article "Converting Dithered
+** Images Back to Gray Scale" by Allen Stenger, Dr Dobb's Journal, November
+** 1992, and this article references "Digital Image Enhancement andNoise Filtering by
+** Use of Local Statistics", Jong-Sen Lee, IEEE Transactions on Pattern Analysis and
+** Machine Intelligence, March 1980.
+**
+** Also borrow the technique used in pgmenhance(1) to allow edge
+** enhancement if the alpha value is negative.
+**
+** Author:
+** Graeme W. Gill, 30th Jan 1993
+** graeme@labtam.oz.au
+**
+** Permission is hereby granted, to use, copy, modify, distribute,
+** and sell this software and its associated documentation files
+** (the "Software") for any purpose without fee, provided
+** that:
+**
+** 1) The above copyright notices and this permission notice
+** accompany all source code copies of the Software and
+** related documentation.
+** and
+**
+** 2) If executable code based on the Software only is distributed,
+** then the accompanying documentation must acknowledge that
+** "this software is based in part on the work of Graeme W. Gill".
+** and
+**
+** 3) It is accepted that Graeme W. Gill (the "Author") accepts
+** NO LIABILITY for damages of any kind. The Software is
+** provided without fee by the Author "AS-IS" and without
+** warranty of any kind, express, implied or otherwise,
+** including without limitation, any warranty of merchantability
+** or fitness for a particular purpose.
+** and
+**
+** 4) These conditions apply to any software derived from or based
+** on the Software, not just to the unmodified library.
+**
+*/
+
+/* ************************************************** */
+/* Hexagon intersecting square area functions */
+/* Compute the area of the intersection of a triangle */
+/* and a rectangle */
+
+static gdouble triang_area(gdouble, gdouble, gdouble, gdouble, gdouble,
+ gdouble, gdouble, gdouble, gint);
+static gdouble rectang_area(gdouble, gdouble, gdouble, gdouble,
+ gdouble, gdouble, gdouble, gdouble);
+static gdouble hex_area(gdouble, gdouble, gdouble, gdouble, gdouble);
+
+static gint atfilt0 (gint *p);
+static gint atfilt1 (gint *p);
+static gint atfilt2 (gint *p);
+static gint atfilt3 (gint *p);
+static gint atfilt4 (gint *p);
+static gint atfilt5 (gint *p);
+
+gint (*atfuncs[6])(gint *) =
+{
+ atfilt0,
+ atfilt1,
+ atfilt2,
+ atfilt3,
+ atfilt4,
+ atfilt5
+};
+
+static gint noisevariance;
+
+#define MXIVAL 255 /* maximum input value */
+#define NOIVAL (MXIVAL + 1) /* number of possible input values */
+
+#define SCALEB 8 /* scale bits */
+#define SCALE (1 << SCALEB) /* scale factor */
+
+#define CSCALEB 2 /* coarse scale bits */
+#define CSCALE (1 << CSCALEB) /* coarse scale factor */
+#define MXCSVAL (MXIVAL * CSCALE) /* maximum coarse scaled values */
+#define NOCSVAL (MXCSVAL + 1) /* number of coarse scaled values */
+#define SCTOCSC(x) ((x) >> (SCALEB - CSCALEB)) /* convert from scaled to coarse scaled */
+#define CSCTOSC(x) ((x) << (SCALEB - CSCALEB)) /* convert from course scaled to scaled */
+
+/* round and scale floating point to scaled integer */
+#define SROUND(x) ((gint)(((x) * (gdouble)SCALE) + 0.5))
+/* round and un-scale scaled integer value */
+#define RUNSCALE(x) (((x) + (1 << (SCALEB-1))) >> SCALEB) /* rounded un-scale */
+#define UNSCALE(x) ((x) >> SCALEB)
+
+/* Note: modified by David Hodson, nlfiltRow now accesses
+ * srclast, srcthis, and srcnext from [-bpp] to [width*bpp-1].
+ * Beware if you use this code anywhere else!
+ */
+static void
+nlfiltRow (guchar *srclast, guchar *srcthis, guchar *srcnext, guchar *dst,
+ gint width, gint bpp, gint filtno)
+{
+ gint pf[9];
+ guchar *ip0, *ip1, *ip2, *or, *orend;
+
+ orend = dst + width * bpp;
+ ip0 = srclast;
+ ip1 = srcthis;
+ ip2 = srcnext;
+
+ for (or = dst; or < orend; ip0++, ip1++, ip2++, or++)
+ {
+ pf[0] = *ip1;
+ pf[1] = *(ip1 - bpp);
+ pf[2] = *(ip2 - bpp);
+ pf[3] = *(ip2);
+ pf[4] = *(ip2 + bpp);
+ pf[5] = *(ip1 + bpp);
+ pf[6] = *(ip0 + bpp);
+ pf[7] = *(ip0);
+ pf[8] = *(ip0 - bpp);
+ *or=(atfuncs[filtno])(pf);
+ }
+}
+
+/* We restrict radius to the values: 0.333333 <= radius <= 1.0 */
+/* so that no fewer and no more than a 3x3 grid of pixels around */
+/* the pixel in question needs to be read. Given this, we only */
+/* need 3 or 4 weightings per hexagon, as follows: */
+/* _ _ */
+/* Virtical hex: |_|_| 1 2 */
+/* |X|_| 0 3 */
+/* _ */
+/* _ _|_| 1 */
+/* Middle hex: |_| 1 Horizontal hex: |X|_| 0 2 */
+/* |X| 0 |_| 3 */
+/* |_| 2 */
+
+/* all filters */
+gint V0[NOIVAL],V1[NOIVAL],V2[NOIVAL],V3[NOIVAL]; /* vertical hex */
+gint M0[NOIVAL],M1[NOIVAL],M2[NOIVAL]; /* middle hex */
+gint H0[NOIVAL],H1[NOIVAL],H2[NOIVAL],H3[NOIVAL]; /* horizontal hex */
+
+/* alpha trimmed and edge enhancement only */
+gint ALFRAC[NOIVAL * 8]; /* fractional alpha divider table */
+
+/* optimal estimation only */
+gint AVEDIV[7 * NOCSVAL]; /* divide by 7 to give average value */
+gint SQUARE[2 * NOCSVAL]; /* scaled square lookup table */
+
+/* Table initialisation function - return alpha range */
+static gint
+nlfiltInit (gdouble alpha, gdouble radius, FilterType filter)
+{
+ gint alpharange; /* alpha range value 0 - 3 */
+ gdouble meanscale; /* scale for finding mean */
+ gdouble mmeanscale; /* scale for finding mean - midle hex */
+ gdouble alphafraction; /* fraction of next largest/smallest
+ * to subtract from sum
+ */
+ switch (filter)
+ {
+ case filter_alpha_trim:
+ {
+ gdouble noinmean;
+ /* alpha only makes sense in range 0.0 - 0.5 */
+ alpha /= 2.0;
+ /* number of elements (out of a possible 7) used in the mean */
+ noinmean = ((0.5 - alpha) * 12.0) + 1.0;
+ mmeanscale = meanscale = 1.0/noinmean;
+ if (alpha == 0.0) { /* mean filter */
+ alpharange = 0;
+ alphafraction = 0.0; /* not used */
+ } else if (alpha < (1.0/6.0)) { /* mean of 5 to 7 middle values */
+ alpharange = 1;
+ alphafraction = (7.0 - noinmean)/2.0;
+ } else if (alpha < (1.0/3.0)) { /* mean of 3 to 5 middle values */
+ alpharange = 2;
+ alphafraction = (5.0 - noinmean)/2.0;
+ } else { /* mean of 1 to 3 middle values */
+ /* alpha==0.5 => median filter */
+ alpharange = 3;
+ alphafraction = (3.0 - noinmean)/2.0;
+ }
+ }
+ break;
+ case filter_opt_est: {
+ gint i;
+ gdouble noinmean = 7.0;
+
+ /* edge enhancement function */
+ alpharange = 5;
+
+ /* compute scaled hex values */
+ mmeanscale=meanscale=1.0;
+
+ /* Set up 1:1 division lookup - not used */
+ alphafraction=1.0/noinmean;
+
+ /* estimate of noise variance */
+ noisevariance = alpha * (gdouble)255;
+ noisevariance = noisevariance * noisevariance / 8.0;
+
+ /* set yp optimal estimation specific stuff */
+
+ for (i=0;i<(7*NOCSVAL);i++) { /* divide scaled value by 7 lookup */
+ AVEDIV[i] = CSCTOSC(i)/7; /* scaled divide by 7 */
+ }
+ /* compute square and rescale by
+ * (val >> (2 * SCALEB + 2)) table
+ */
+ for (i=0;i<(2*NOCSVAL);i++) {
+ gint val;
+ /* NOCSVAL offset to cope with -ve input values */
+ val = CSCTOSC(i - NOCSVAL);
+ SQUARE[i] = (val * val) >> (2 * SCALEB + 2);
+ }
+ }
+ break;
+ case filter_edge_enhance: {
+ if (alpha == 1.0) alpha = 0.99;
+ alpharange = 4;
+ /* mean of 7 and scaled by -alpha/(1-alpha) */
+ meanscale = 1.0 * (-alpha/((1.0 - alpha) * 7.0));
+
+ /* middle pixel has 1/(1-alpha) as well */
+ mmeanscale = 1.0 * (1.0/(1.0 - alpha) + meanscale);
+ alphafraction = 0.0; /* not used */
+ }
+ break;
+ default:
+ g_printerr ("unknown filter %d\n", filter);
+ return -1;
+ }
+ /*
+ * Setup pixel weighting tables -
+ * note we pre-compute mean division here too.
+ */
+ {
+ gint i;
+ gdouble hexhoff,hexvoff;
+ gdouble tabscale,mtabscale;
+ gdouble v0,v1,v2,v3,m0,m1,m2,h0,h1,h2,h3;
+
+ /* horizontal offset of virtical hex centers */
+ hexhoff = radius/2;
+
+ /* vertical offset of virtical hex centers */
+ hexvoff = 3.0 * radius/sqrt(12.0);
+
+ /*
+ * scale tables to normalise by hexagon
+ * area, and number of hexes used in mean
+ */
+ tabscale = meanscale / (radius * hexvoff);
+ mtabscale = mmeanscale / (radius * hexvoff);
+ v0 = hex_area(0.0,0.0,hexhoff,hexvoff,radius) * tabscale;
+ v1 = hex_area(0.0,1.0,hexhoff,hexvoff,radius) * tabscale;
+ v2 = hex_area(1.0,1.0,hexhoff,hexvoff,radius) * tabscale;
+ v3 = hex_area(1.0,0.0,hexhoff,hexvoff,radius) * tabscale;
+ m0 = hex_area(0.0,0.0,0.0,0.0,radius) * mtabscale;
+ m1 = hex_area(0.0,1.0,0.0,0.0,radius) * mtabscale;
+ m2 = hex_area(0.0,-1.0,0.0,0.0,radius) * mtabscale;
+ h0 = hex_area(0.0,0.0,radius,0.0,radius) * tabscale;
+ h1 = hex_area(1.0,1.0,radius,0.0,radius) * tabscale;
+ h2 = hex_area(1.0,0.0,radius,0.0,radius) * tabscale;
+ h3 = hex_area(1.0,-1.0,radius,0.0,radius) * tabscale;
+
+ for (i=0; i <= MXIVAL; i++) {
+ gdouble fi;
+ fi = (gdouble)i;
+ V0[i] = SROUND(fi * v0);
+ V1[i] = SROUND(fi * v1);
+ V2[i] = SROUND(fi * v2);
+ V3[i] = SROUND(fi * v3);
+ M0[i] = SROUND(fi * m0);
+ M1[i] = SROUND(fi * m1);
+ M2[i] = SROUND(fi * m2);
+ H0[i] = SROUND(fi * h0);
+ H1[i] = SROUND(fi * h1);
+ H2[i] = SROUND(fi * h2);
+ H3[i] = SROUND(fi * h3);
+ }
+ /* set up alpha fraction lookup table used on big/small */
+ for (i=0; i < (NOIVAL * 8); i++) {
+ ALFRAC[i] = SROUND((gdouble)i * alphafraction);
+ }
+ }
+ return alpharange;
+}
+
+/* Core pixel processing function - hand it 3x3 pixels and return result. */
+/* Mean filter */
+static gint
+atfilt0(gint32 *p)
+{
+ gint retv;
+ /* map to scaled hexagon values */
+ retv = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ retv += H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ retv += V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ retv += V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ retv += H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ retv += V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ retv += V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ return UNSCALE(retv);
+}
+
+/* Mean of 5 - 7 middle values */
+static gint
+atfilt1 (gint32 *p)
+{
+ gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
+ /* 1 0 4 */
+ /* 6 5 */
+ gint big,small;
+ /* map to scaled hexagon values */
+ h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ /* sum values and also discover the largest and smallest */
+ big = small = h0;
+#define CHECK(xx) \
+ h0 += xx; \
+ if (xx > big) \
+ big = xx; \
+ else if (xx < small) \
+ small = xx;
+ CHECK(h1)
+ CHECK(h2)
+ CHECK(h3)
+ CHECK(h4)
+ CHECK(h5)
+ CHECK(h6)
+#undef CHECK
+ /* Compute mean of middle 5-7 values */
+ return UNSCALE(h0 -ALFRAC[(big + small)>>SCALEB]);
+}
+
+/* Mean of 3 - 5 middle values */
+static gint
+atfilt2 (gint32 *p)
+{
+ gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
+ /* 1 0 4 */
+ /* 6 5 */
+ gint big0,big1,small0,small1;
+ /* map to scaled hexagon values */
+ h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ /* sum values and also discover the 2 largest and 2 smallest */
+ big0 = small0 = h0;
+ small1 = G_MAXINT;
+ big1 = 0;
+#define CHECK(xx) \
+ h0 += xx; \
+ if (xx > big1) \
+ { \
+ if (xx > big0) \
+ { \
+ big1 = big0; \
+ big0 = xx; \
+ } \
+ else \
+ big1 = xx; \
+ } \
+ if (xx < small1) \
+ { \
+ if (xx < small0) \
+ { \
+ small1 = small0; \
+ small0 = xx; \
+ } \
+ else \
+ small1 = xx; \
+ }
+ CHECK(h1)
+ CHECK(h2)
+ CHECK(h3)
+ CHECK(h4)
+ CHECK(h5)
+ CHECK(h6)
+#undef CHECK
+ /* Compute mean of middle 3-5 values */
+ return UNSCALE(h0 -big0 -small0 -ALFRAC[(big1 + small1)>>SCALEB]);
+}
+
+/*
+ * Mean of 1 - 3 middle values.
+ * If only 1 value, then this is a median filter.
+ */
+static gint32
+atfilt3(gint32 *p)
+{
+ gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
+ /* 1 0 4 */
+ /* 6 5 */
+ gint big0,big1,big2,small0,small1,small2;
+ /* map to scaled hexagon values */
+ h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ /* sum values and also discover the 3 largest and 3 smallest */
+ big0 = small0 = h0;
+ small1 = small2 = G_MAXINT;
+ big1 = big2 = 0;
+#define CHECK(xx) \
+ h0 += xx; \
+ if (xx > big2) \
+ { \
+ if (xx > big1) \
+ { \
+ if (xx > big0) \
+ { \
+ big2 = big1; \
+ big1 = big0; \
+ big0 = xx; \
+ } \
+ else \
+ { \
+ big2 = big1; \
+ big1 = xx; \
+ } \
+ } \
+ else \
+ big2 = xx; \
+ } \
+ if (xx < small2) \
+ { \
+ if (xx < small1) \
+ { \
+ if (xx < small0) \
+ { \
+ small2 = small1; \
+ small1 = small0; \
+ small0 = xx; \
+ } \
+ else \
+ { \
+ small2 = small1; \
+ small1 = xx; \
+ } \
+ } \
+ else \
+ small2 = xx; \
+ }
+ CHECK(h1)
+ CHECK(h2)
+ CHECK(h3)
+ CHECK(h4)
+ CHECK(h5)
+ CHECK(h6)
+#undef CHECK
+ /* Compute mean of middle 1-3 values */
+ return UNSCALE(h0-big0-big1-small0-small1-ALFRAC[(big2+small2)>>SCALEB]);
+}
+
+/* Edge enhancement */
+static gint
+atfilt4 (gint *p)
+{
+ gint hav;
+ /* map to scaled hexagon values and compute enhance value */
+ hav = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ hav += H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ hav += V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ hav += V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ hav += H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ hav += V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ hav += V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ if (hav < 0)
+ hav = 0;
+ hav = UNSCALE(hav);
+ if (hav > (gdouble)255)
+ hav = (gdouble)255;
+ return hav;
+}
+
+/* Optimal estimation - do smoothing in inverse proportion */
+/* to the local variance. */
+/* notice we use the globals noisevariance */
+gint
+atfilt5(gint *p) {
+ gint mean,variance,temp;
+ gint h0,h1,h2,h3,h4,h5,h6; /* hexagon values 2 3 */
+ /* 1 0 4 */
+ /* 6 5 */
+ /* map to scaled hexagon values */
+ h0 = M0[p[0]] + M1[p[3]] + M2[p[7]];
+ h1 = H0[p[0]] + H1[p[2]] + H2[p[1]] + H3[p[8]];
+ h2 = V0[p[0]] + V1[p[3]] + V2[p[2]] + V3[p[1]];
+ h3 = V0[p[0]] + V1[p[3]] + V2[p[4]] + V3[p[5]];
+ h4 = H0[p[0]] + H1[p[4]] + H2[p[5]] + H3[p[6]];
+ h5 = V0[p[0]] + V1[p[7]] + V2[p[6]] + V3[p[5]];
+ h6 = V0[p[0]] + V1[p[7]] + V2[p[8]] + V3[p[1]];
+ mean = h0 + h1 + h2 + h3 + h4 + h5 + h6;
+ /* compute scaled mean by dividing by 7 */
+ mean = AVEDIV[SCTOCSC(mean)];
+
+ /* compute scaled variance */
+ temp = (h1 - mean); variance = SQUARE[NOCSVAL + SCTOCSC(temp)];
+
+ /* and rescale to keep */
+ temp = (h2 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+
+ /* within 32 bit limits */
+ temp = (h3 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+ temp = (h4 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+ temp = (h5 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+ temp = (h6 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+ /* (temp = h0 - mean) */
+ temp = (h0 - mean); variance += SQUARE[NOCSVAL + SCTOCSC(temp)];
+ if (variance != 0) /* avoid possible divide by 0 */
+ /* optimal estimate */
+ temp = mean + (variance * temp) / (variance + noisevariance);
+ else temp = h0;
+ if (temp < 0)
+ temp = 0;
+ temp = RUNSCALE(temp);
+ if (temp > (gdouble)255) temp = (gdouble)255;
+ return temp;
+}
+
+
+/* Triangle orientation is per geometric axes (not graphical axies) */
+
+#define NW 0 /* North west triangle /| */
+#define NE 1 /* North east triangle |\ */
+#define SW 2 /* South west triangle \| */
+#define SE 3 /* South east triangle |/ */
+#define STH 2
+#define EST 1
+
+#define SWAPI(a,b) (t = a, a = -b, b = -t)
+
+/* compute the area of overlap of a hexagon diameter d, */
+/* centered at hx,hy, with a unit square of center sx,sy. */
+static gdouble
+hex_area (gdouble sx, gdouble sy, gdouble hx, gdouble hy, gdouble d)
+{
+ gdouble hx0,hx1,hx2,hy0,hy1,hy2,hy3;
+ gdouble sx0,sx1,sy0,sy1;
+
+ /* compute square co-ordinates */
+ sx0 = sx - 0.5;
+ sy0 = sy - 0.5;
+ sx1 = sx + 0.5;
+ sy1 = sy + 0.5;
+ /* compute hexagon co-ordinates */
+ hx0 = hx - d/2.0;
+ hx1 = hx;
+ hx2 = hx + d/2.0;
+ hy0 = hy - 0.5773502692 * d; /* d / sqrt(3) */
+ hy1 = hy - 0.2886751346 * d; /* d / sqrt(12) */
+ hy2 = hy + 0.2886751346 * d; /* d / sqrt(12) */
+ hy3 = hy + 0.5773502692 * d; /* d / sqrt(3) */
+
+ return
+ triang_area(sx0,sy0,sx1,sy1,hx0,hy2,hx1,hy3,NW) +
+ triang_area(sx0,sy0,sx1,sy1,hx1,hy2,hx2,hy3,NE) +
+ rectang_area(sx0,sy0,sx1,sy1,hx0,hy1,hx2,hy2) +
+ triang_area(sx0,sy0,sx1,sy1,hx0,hy0,hx1,hy1,SW) +
+ triang_area(sx0,sy0,sx1,sy1,hx1,hy0,hx2,hy1,SE);
+}
+
+static gdouble
+triang_area (gdouble rx0, gdouble ry0, gdouble rx1, gdouble ry1, gdouble tx0,
+ gdouble ty0, gdouble tx1, gdouble ty1, gint tt)
+{
+ gdouble a,b,c,d;
+ gdouble lx0,ly0,lx1,ly1;
+ /* Convert everything to a NW triangle */
+ if (tt & STH) {
+ gdouble t;
+ SWAPI(ry0,ry1);
+ SWAPI(ty0,ty1);
+ } if (tt & EST) {
+ gdouble t;
+ SWAPI(rx0,rx1);
+ SWAPI(tx0,tx1);
+ }
+ /* Compute overlapping box */
+ if (tx0 > rx0)
+ rx0 = tx0;
+ if (ty0 > ry0)
+ ry0 = ty0;
+ if (tx1 < rx1)
+ rx1 = tx1;
+ if (ty1 < ry1)
+ ry1 = ty1;
+ if (rx1 <= rx0 || ry1 <= ry0)
+ return 0.0;
+ /* Need to compute diagonal line intersection with the box */
+ /* First compute co-efficients to formulas x = a + by and y = c + dx */
+ b = (tx1 - tx0)/(ty1 - ty0);
+ a = tx0 - b * ty0;
+ d = (ty1 - ty0)/(tx1 - tx0);
+ c = ty0 - d * tx0;
+
+ /* compute top or right intersection */
+ tt = 0;
+ ly1 = ry1;
+ lx1 = a + b * ly1;
+ if (lx1 <= rx0)
+ return (rx1 - rx0) * (ry1 - ry0);
+ else if (lx1 > rx1) { /* could be right hand side */
+ lx1 = rx1;
+ ly1 = c + d * lx1;
+ if (ly1 <= ry0)
+ return (rx1 - rx0) * (ry1 - ry0);
+ tt = 1; /* right hand side intersection */
+ }
+ /* compute left or bottom intersection */
+ lx0 = rx0;
+ ly0 = c + d * lx0;
+ if (ly0 >= ry1)
+ return (rx1 - rx0) * (ry1 - ry0);
+ else if (ly0 < ry0) { /* could be right hand side */
+ ly0 = ry0;
+ lx0 = a + b * ly0;
+ if (lx0 >= rx1)
+ return (rx1 - rx0) * (ry1 - ry0);
+ tt |= 2; /* bottom intersection */
+ }
+
+ if (tt == 0) { /* top and left intersection */
+ /* rectangle minus triangle */
+ return ((rx1 - rx0) * (ry1 - ry0))
+ - (0.5 * (lx1 - rx0) * (ry1 - ly0));
+ }
+ else if (tt == 1) { /* right and left intersection */
+ return ((rx1 - rx0) * (ly0 - ry0))
+ + (0.5 * (rx1 - rx0) * (ly1 - ly0));
+ } else if (tt == 2) { /* top and bottom intersection */
+ return ((rx1 - lx1) * (ry1 - ry0))
+ + (0.5 * (lx1 - lx0) * (ry1 - ry0));
+ } else { /* tt == 3 */ /* right and bottom intersection */
+ /* triangle */
+ return (0.5 * (rx1 - lx0) * (ly1 - ry0));
+ }
+}
+
+/* Compute rectangle area */
+static gdouble
+rectang_area (gdouble rx0, gdouble ry0, gdouble rx1, gdouble ry1, gdouble tx0,
+ gdouble ty0, gdouble tx1, gdouble ty1)
+{
+ /* Compute overlapping box */
+ if (tx0 > rx0)
+ rx0 = tx0;
+ if (ty0 > ry0)
+ ry0 = ty0;
+ if (tx1 < rx1)
+ rx1 = tx1;
+ if (ty1 < ry1)
+ ry1 = ty1;
+ if (rx1 <= rx0 || ry1 <= ry0)
+ return 0.0;
+ return (rx1 - rx0) * (ry1 - ry0);
+}
+
+static void
+nlfilter (gint32 drawable_id,
+ GimpPreview *preview)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ guchar *srcbuf, *dstbuf;
+ guchar *lastrow, *thisrow, *nextrow, *temprow;
+ gint x1, y1, y2;
+ gint width, height, bpp;
+ gint filtno, y, rowsize, exrowsize, p_update;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+ y2 = y1 + height;
+ }
+ else
+ {
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ y2 = y1 + height;
+ }
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ if (preview)
+ dest_buffer = gegl_buffer_new (gegl_buffer_get_extent (src_buffer), format);
+ else
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ rowsize = width * bpp;
+ exrowsize = (width + 2) * bpp;
+ p_update = width / 20 + 1;
+
+ /* source buffer gives one pixel margin all around destination buffer */
+ srcbuf = g_new0 (guchar, exrowsize * 3);
+ dstbuf = g_new0 (guchar, rowsize);
+
+ /* pointers to second pixel in each source row */
+ lastrow = srcbuf + bpp;
+ thisrow = lastrow + exrowsize;
+ nextrow = thisrow + exrowsize;
+
+ filtno = nlfiltInit (nlfvals.alpha, nlfvals.radius, nlfvals.filter);
+
+ if (!preview)
+ gimp_progress_init (_("NL Filter"));
+
+ /* first row */
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1, width, 1), 1.0,
+ format, thisrow,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* copy thisrow[0] to thisrow[-1], thisrow[width-1] to thisrow[width] */
+ memcpy (thisrow - bpp, thisrow, bpp);
+ memcpy (thisrow + rowsize, thisrow + rowsize - bpp, bpp);
+ /* copy whole thisrow to lastrow */
+ memcpy (lastrow - bpp, thisrow - bpp, exrowsize);
+
+ for (y = y1; y < y2 - 1; y++)
+ {
+ if (((y % p_update) == 0) && !preview)
+ gimp_progress_update ((gdouble) y / (gdouble) height);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y + 1, width, 1), 1.0,
+ format, nextrow,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ memcpy (nextrow - bpp, nextrow, bpp);
+ memcpy (nextrow + rowsize, nextrow + rowsize - bpp, bpp);
+ nlfiltRow (lastrow, thisrow, nextrow, dstbuf, width, bpp, filtno);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, y, width, 1), 0,
+ format, dstbuf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* rotate row buffers */
+ temprow = lastrow; lastrow = thisrow;
+ thisrow = nextrow; nextrow = temprow;
+ }
+
+ /* last row */
+ memcpy (nextrow - bpp, thisrow - bpp, exrowsize);
+ nlfiltRow (lastrow, thisrow, nextrow, dstbuf, width, bpp, filtno);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, y2 - 1, width, 1), 0,
+ format, dstbuf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ g_free (srcbuf);
+ g_free (dstbuf);
+
+ if (preview)
+ {
+ guchar *buf = g_new (guchar, width * height * bpp);
+
+ gegl_buffer_get (dest_buffer, GEGL_RECTANGLE (x1, y1, width, height), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gimp_preview_draw_buffer (GIMP_PREVIEW (preview), buf, width * bpp);
+
+ g_free (buf);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ if (! preview)
+ {
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x1, y1, width, height);
+ gimp_displays_flush ();
+ }
+}
+
+static void
+nlfilter_preview (gpointer drawable_id,
+ GimpPreview *preview)
+{
+ nlfilter (GPOINTER_TO_INT (drawable_id), preview);
+}
+
+static gboolean
+nlfilter_dialog (gint32 drawable_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *frame;
+ GtkWidget *alpha_trim;
+ GtkWidget *opt_est;
+ GtkWidget *edge_enhance;
+ GtkWidget *table;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("NL Filter"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (nlfilter_preview),
+ GINT_TO_POINTER (drawable_id));
+
+ frame = gimp_int_radio_group_new (TRUE, _("Filter"),
+ G_CALLBACK (gimp_radio_button_update),
+ &nlfvals.filter, nlfvals.filter,
+
+ _("_Alpha trimmed mean"),
+ filter_alpha_trim, &alpha_trim,
+ _("Op_timal estimation"),
+ filter_opt_est, &opt_est,
+ _("_Edge enhancement"),
+ filter_edge_enhance, &edge_enhance,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ g_signal_connect_swapped (alpha_trim, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (opt_est, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (edge_enhance, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("A_lpha:"), 0, 0,
+ nlfvals.alpha, 0.0, 1.0, 0.05, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &nlfvals.alpha);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Radius:"), 0, 0,
+ nlfvals.radius, 1.0 / 3.0, 1.0, 0.05, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &nlfvals.radius);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/photocopy.c b/plug-ins/common/photocopy.c
new file mode 100644
index 0000000..5a2c7f2
--- /dev/null
+++ b/plug-ins/common/photocopy.c
@@ -0,0 +1,935 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Photocopy filter for GIMP for BIPS
+ * -Spencer Kimball
+ *
+ * This filter propagates dark values in an image based on
+ * each pixel's relative darkness to a neighboring average
+ * and sets the remaining pixels to white.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+
+#define PLUG_IN_PROC "plug-in-photocopy"
+#define PLUG_IN_BINARY "photocopy"
+#define PLUG_IN_ROLE "gimp-photocopy"
+#define TILE_CACHE_SIZE 48
+#define GAMMA 1.0
+#define EPSILON 2
+
+
+typedef struct
+{
+ gdouble mask_radius;
+ gdouble sharpness;
+ gdouble threshold;
+ gdouble pct_black;
+ gdouble pct_white;
+} PhotocopyVals;
+
+
+/*
+ * Function prototypes.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void photocopy (GimpDrawable *drawable,
+ GimpPreview *preview);
+static gboolean photocopy_dialog (GimpDrawable *drawable);
+
+static gdouble compute_ramp (guchar *dest1,
+ guchar *dest2,
+ gint length,
+ gdouble pct_black,
+ gint under_threshold);
+
+/*
+ * Gaussian blur helper functions
+ */
+static void find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev);
+static void transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint width);
+
+/***** Local vars *****/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run, /* run */
+};
+
+static PhotocopyVals pvals =
+{
+ 8.0, /* mask_radius */
+ 0.8, /* sharpness */
+ 0.75, /* threshold */
+ 0.2, /* pct_black */
+ 0.2 /* pct_white */
+};
+
+
+/***** Functions *****/
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "mask-radius", "Photocopy mask radius (radius of pixel neighborhood)" },
+ { GIMP_PDB_FLOAT, "sharpness", "Sharpness (detail level) (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "pct-black", "Percentage of darkened pixels to set to black (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "pct-white", "Percentage of non-darkened pixels left white (0.0 - 1.0)" }
+ };
+
+ gchar *help_string =
+ "Propagates dark values in an image based on "
+ "each pixel's relative darkness to a neighboring average. The idea behind "
+ "this filter is to give the look of a photocopied version of the image, "
+ "with toner transferred based on the relative darkness of a particular "
+ "region. This is achieved by darkening areas of the image which are "
+ "measured to be darker than a neighborhood average and setting other "
+ "pixels to white. In this way, sufficiently large shifts in intensity "
+ "are darkened to black. The rate at which they are darkened to black is "
+ "determined by the second pct_black parameter. The mask_radius parameter "
+ "controls the size of the pixel neighborhood over which the average "
+ "intensity is computed and then compared to each pixel in the neighborhood "
+ "to decide whether or not to darken it to black. Large values for "
+ "mask_radius result in very thick black areas bordering the regions "
+ "of white and much less detail for black areas everywhere including "
+ "inside regions of color. Small values result in less toner overall "
+ "and more detail everywhere. Small values for the pct_black make the "
+ "blend from the white regions to the black border lines smoother and "
+ "the toner regions themselves thinner and less noticeable; larger values "
+ "achieve the opposite effect.";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simulate color distortion produced by a copy machine"),
+ help_string,
+ "Spencer Kimball",
+ "Bit Specialists, Inc.",
+ "2001",
+ N_("_Photocopy (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpDrawable *drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ run_mode = param[0].data.d_int32;
+
+ /* Get the specified drawable */
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ /* set the tile cache size */
+ gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ INIT_I18N();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+
+ /* First acquire information with a dialog */
+ if (! photocopy_dialog (drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ pvals.mask_radius = param[3].data.d_float;
+ pvals.sharpness = param[4].data.d_float;
+ pvals.pct_black = param[5].data.d_float;
+ pvals.pct_white = param[6].data.d_float;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGB or GRAY color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init ("Photocopy");
+
+ photocopy (drawable, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &pvals, sizeof (PhotocopyVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("Cannot operate on indexed color images.");
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ gimp_drawable_detach (drawable);
+}
+
+/*
+ * Photocopy algorithm
+ * -----------------
+ * Mask radius = radius of pixel neighborhood for intensity comparison
+ * Threshold = relative intensity difference which will result in darkening
+ * Ramp = amount of relative intensity difference before total black
+ * Blur radius = mask radius / 3.0
+ *
+ * Algorithm:
+ * For each pixel, calculate pixel intensity value to be: avg (blur radius)
+ * relative diff = pixel intensity / avg (mask radius)
+ * If relative diff < Threshold
+ * intensity mult = (Ramp - MIN (Ramp, (Threshold - relative diff))) / Ramp
+ * pixel intensity *= intensity mult
+ * Else
+ * pixel intensity = white
+ */
+static void
+photocopy (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ GimpPixelRgn src_rgn, dest_rgn;
+ GimpPixelRgn *pr;
+ gint x, y, width, height;
+ gint bytes;
+ gboolean has_alpha;
+ guchar *dest1;
+ guchar *dest2;
+ guchar *src1, *sp_p1, *sp_m1;
+ guchar *src2, *sp_p2, *sp_m2;
+ gdouble n_p1[5], n_m1[5];
+ gdouble n_p2[5], n_m2[5];
+ gdouble d_p1[5], d_m1[5];
+ gdouble d_p2[5], d_m2[5];
+ gdouble bd_p1[5], bd_m1[5];
+ gdouble bd_p2[5], bd_m2[5];
+ gdouble *val_p1, *val_m1, *vp1, *vm1;
+ gdouble *val_p2, *val_m2, *vp2, *vm2;
+ gint i, j;
+ gint row, col;
+ gint terms;
+ gint progress, max_progress;
+ gint initial_p1[4];
+ gint initial_p2[4];
+ gint initial_m1[4];
+ gint initial_m2[4];
+ gdouble radius;
+ gdouble val;
+ gdouble std_dev1;
+ gdouble std_dev2;
+ gdouble ramp_down;
+ gdouble ramp_up;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x, &y);
+ gimp_preview_get_size (preview, &width, &height);
+ }
+ else
+ {
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x, &y, &width, &height))
+ return;
+ }
+
+ bytes = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ val_p1 = g_new (gdouble, MAX (width, height));
+ val_p2 = g_new (gdouble, MAX (width, height));
+ val_m1 = g_new (gdouble, MAX (width, height));
+ val_m2 = g_new (gdouble, MAX (width, height));
+
+ dest1 = g_new0 (guchar, width * height);
+ dest2 = g_new0 (guchar, width * height);
+
+ progress = 0;
+ max_progress = width * height * 3;
+
+ gimp_pixel_rgn_init (&src_rgn, drawable,
+ x, y, width, height, FALSE, FALSE);
+
+ for (pr = gimp_pixel_rgns_register (1, &src_rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ guchar *src_ptr = src_rgn.data;
+ guchar *dest_ptr = dest1 + (src_rgn.y - y) * width + (src_rgn.x - x);
+
+ for (row = 0; row < src_rgn.h; row++)
+ {
+ for (col = 0; col < src_rgn.w; col++)
+ {
+ /* desaturate */
+ if (bytes > 2)
+ dest_ptr[col] = (guchar) gimp_rgb_to_l_int (src_ptr[col * bytes + 0],
+ src_ptr[col * bytes + 1],
+ src_ptr[col * bytes + 2]);
+ else
+ dest_ptr[col] = (guchar) src_ptr[col * bytes];
+
+ /* compute transfer */
+ val = pow (dest_ptr[col], (1.0 / GAMMA));
+ dest_ptr[col] = (guchar) CLAMP (val, 0, 255);
+ }
+
+ src_ptr += src_rgn.rowstride;
+ dest_ptr += width;
+ }
+
+ if (!preview)
+ {
+ progress += src_rgn.w * src_rgn.h;
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ /* Calculate the standard deviations */
+ radius = MAX (1.0, 10 * (1.0 - pvals.sharpness));
+ radius = fabs (radius) + 1.0;
+ std_dev1 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ radius = fabs (pvals.mask_radius) + 1.0;
+ std_dev2 = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ /* derive the constants for calculating the gaussian from the std dev */
+ find_constants (n_p1, n_m1, d_p1, d_m1, bd_p1, bd_m1, std_dev1);
+ find_constants (n_p2, n_m2, d_p2, d_m2, bd_p2, bd_m2, std_dev2);
+
+ /* First the vertical pass */
+ for (col = 0; col < width; col++)
+ {
+ memset (val_p1, 0, height * sizeof (gdouble));
+ memset (val_p2, 0, height * sizeof (gdouble));
+ memset (val_m1, 0, height * sizeof (gdouble));
+ memset (val_m2, 0, height * sizeof (gdouble));
+
+ src1 = dest1 + col;
+ sp_p1 = src1;
+ sp_m1 = src1 + (height - 1) * width;
+ vp1 = val_p1;
+ vp2 = val_p2;
+ vm1 = val_m1 + (height - 1);
+ vm2 = val_m2 + (height - 1);
+
+ /* Set up the first vals */
+ initial_p1[0] = sp_p1[0];
+ initial_m1[0] = sp_m1[0];
+
+ for (row = 0; row < height; row++)
+ {
+ gdouble *vpptr1, *vmptr1;
+ gdouble *vpptr2, *vmptr2;
+
+ terms = (row < 4) ? row : 4;
+
+ vpptr1 = vp1; vmptr1 = vm1;
+ vpptr2 = vp2; vmptr2 = vm2;
+
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr1 += n_p1[i] * sp_p1[-i * width] - d_p1[i] * vp1[-i];
+ *vmptr1 += n_m1[i] * sp_m1[i * width] - d_m1[i] * vm1[i];
+
+ *vpptr2 += n_p2[i] * sp_p1[-i * width] - d_p2[i] * vp2[-i];
+ *vmptr2 += n_m2[i] * sp_m1[i * width] - d_m2[i] * vm2[i];
+ }
+
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0];
+ *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0];
+
+ *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p1[0];
+ *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m1[0];
+ }
+
+ sp_p1 += width;
+ sp_m1 -= width;
+ vp1 += 1;
+ vp2 += 1;
+ vm1 -= 1;
+ vm2 -= 1;
+ }
+
+ transfer_pixels (val_p1, val_m1, dest1 + col, width, height);
+ transfer_pixels (val_p2, val_m2, dest2 + col, width, height);
+
+ if (!preview)
+ {
+ progress += height;
+ if ((col % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ for (row = 0; row < height; row++)
+ {
+ memset (val_p1, 0, width * sizeof (gdouble));
+ memset (val_p2, 0, width * sizeof (gdouble));
+ memset (val_m1, 0, width * sizeof (gdouble));
+ memset (val_m2, 0, width * sizeof (gdouble));
+
+ src1 = dest1 + row * width;
+ src2 = dest2 + row * width;
+
+ sp_p1 = src1;
+ sp_p2 = src2;
+ sp_m1 = src1 + width - 1;
+ sp_m2 = src2 + width - 1;
+ vp1 = val_p1;
+ vp2 = val_p2;
+ vm1 = val_m1 + width - 1;
+ vm2 = val_m2 + width - 1;
+
+ /* Set up the first vals */
+ initial_p1[0] = sp_p1[0];
+ initial_p2[0] = sp_p2[0];
+ initial_m1[0] = sp_m1[0];
+ initial_m2[0] = sp_m2[0];
+
+ for (col = 0; col < width; col++)
+ {
+ gdouble *vpptr1, *vmptr1;
+ gdouble *vpptr2, *vmptr2;
+
+ terms = (col < 4) ? col : 4;
+
+ vpptr1 = vp1; vmptr1 = vm1;
+ vpptr2 = vp2; vmptr2 = vm2;
+
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr1 += n_p1[i] * sp_p1[-i] - d_p1[i] * vp1[-i];
+ *vmptr1 += n_m1[i] * sp_m1[i] - d_m1[i] * vm1[i];
+
+ *vpptr2 += n_p2[i] * sp_p2[-i] - d_p2[i] * vp2[-i];
+ *vmptr2 += n_m2[i] * sp_m2[i] - d_m2[i] * vm2[i];
+ }
+
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr1 += (n_p1[j] - bd_p1[j]) * initial_p1[0];
+ *vmptr1 += (n_m1[j] - bd_m1[j]) * initial_m1[0];
+
+ *vpptr2 += (n_p2[j] - bd_p2[j]) * initial_p2[0];
+ *vmptr2 += (n_m2[j] - bd_m2[j]) * initial_m2[0];
+ }
+
+ sp_p1 ++;
+ sp_p2 ++;
+ sp_m1 --;
+ sp_m2 --;
+ vp1 ++;
+ vp2 ++;
+ vm1 --;
+ vm2 --;
+ }
+
+ transfer_pixels (val_p1, val_m1, dest1 + row * width, 1, width);
+ transfer_pixels (val_p2, val_m2, dest2 + row * width, 1, width);
+
+ if (!preview)
+ {
+ progress += width;
+ if ((row % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ /* Compute the ramp value which sets 'pct_black' % of the darkened pixels black */
+ ramp_down = compute_ramp (dest1, dest2, width * height, pvals.pct_black, 1);
+ ramp_up = compute_ramp (dest1, dest2, width * height, 1.0 - pvals.pct_white, 0);
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&src_rgn, drawable, x, y, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&dest_rgn, drawable, x, y, width, height,
+ (preview == NULL), TRUE);
+
+ pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
+
+ while (pr)
+ {
+ guchar *src_ptr = src_rgn.data;
+ guchar *dest_ptr = dest_rgn.data;
+ guchar *blur_ptr = dest1 + (src_rgn.y - y) * width + (src_rgn.x - x);
+ guchar *avg_ptr = dest2 + (src_rgn.y - y) * width + (src_rgn.x - x);
+ gdouble diff, mult;
+ gdouble lightness = 0.0;
+
+ for (row = 0; row < src_rgn.h; row++)
+ {
+ for (col = 0; col < src_rgn.w; col++)
+ {
+ if (avg_ptr[col] > EPSILON)
+ {
+ diff = (gdouble) blur_ptr[col] / (gdouble) avg_ptr[col];
+
+ if (diff < pvals.threshold)
+ {
+ if (ramp_down == 0.0)
+ mult = 0.0;
+ else
+ mult = (ramp_down - MIN (ramp_down,
+ (pvals.threshold - diff))) / ramp_down;
+ lightness = CLAMP (blur_ptr[col] * mult, 0, 255);
+ }
+ else
+ {
+ if (ramp_up == 0.0)
+ mult = 1.0;
+ else
+ mult = MIN (ramp_up,
+ (diff - pvals.threshold)) / ramp_up;
+
+ lightness = 255 - (1.0 - mult) * (255 - blur_ptr[col]);
+ lightness = CLAMP (lightness, 0, 255);
+ }
+ }
+ else
+ {
+ lightness = 0;
+ }
+
+ if (bytes < 3)
+ {
+ dest_ptr[col * bytes] = (guchar) lightness;
+ if (has_alpha)
+ dest_ptr[col * bytes + 1] = src_ptr[col * src_rgn.bpp + 1];
+ }
+ else
+ {
+ dest_ptr[col * bytes + 0] = lightness;
+ dest_ptr[col * bytes + 1] = lightness;
+ dest_ptr[col * bytes + 2] = lightness;
+
+ if (has_alpha)
+ dest_ptr[col * bytes + 3] = src_ptr[col * src_rgn.bpp + 3];
+ }
+ }
+
+ src_ptr += src_rgn.rowstride;
+ dest_ptr += dest_rgn.rowstride;
+ blur_ptr += width;
+ avg_ptr += width;
+ }
+
+ if (preview)
+ {
+ gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
+ &dest_rgn);
+ }
+ else
+ {
+ progress += src_rgn.w * src_rgn.h;
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+
+ pr = gimp_pixel_rgns_process (pr);
+ }
+
+ if (! preview)
+ {
+ gimp_progress_update (1.0);
+ /* merge the shadow, update the drawable */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id, x, y, width, height);
+ }
+
+ /* free up buffers */
+ g_free (val_p1);
+ g_free (val_p2);
+ g_free (val_m1);
+ g_free (val_m2);
+ g_free (dest1);
+ g_free (dest2);
+}
+
+static gdouble
+compute_ramp (guchar *dest1,
+ guchar *dest2,
+ gint length,
+ gdouble pct,
+ gint under_threshold)
+{
+ gint hist[2000];
+ gdouble diff;
+ gint count;
+ gint i;
+ gint sum;
+
+ memset (hist, 0, sizeof (int) * 2000);
+ count = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ if (*dest2 != 0)
+ {
+ diff = (gdouble) *dest1 / (gdouble) *dest2;
+
+ if (under_threshold)
+ {
+ if (diff < pvals.threshold)
+ {
+ hist[(int) (diff * 1000)] += 1;
+ count += 1;
+ }
+ }
+ else
+ {
+ if (diff >= pvals.threshold && diff < 2.0)
+ {
+ hist[(int) (diff * 1000)] += 1;
+ count += 1;
+ }
+ }
+ }
+
+ dest1++;
+ dest2++;
+ }
+
+ if (pct == 0.0 || count == 0)
+ return (under_threshold ? 1.0 : 0.0);
+
+ sum = 0;
+ for (i = 0; i < 2000; i++)
+ {
+ sum += hist[i];
+ if (((gdouble) sum / (gdouble) count) > pct)
+ {
+ if (under_threshold)
+ return (pvals.threshold - (gdouble) i / 1000.0);
+ else
+ return ((gdouble) i / 1000.0 - pvals.threshold);
+ }
+ }
+
+ return (under_threshold ? 0.0 : 1.0);
+}
+
+
+/*
+ * Gaussian blur helper functions
+ */
+
+static void
+transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint width)
+{
+ gint i;
+ gdouble sum;
+
+ for(i = 0; i < width; i++)
+ {
+ sum = src1[i] + src2[i];
+ if (sum > 255) sum = 255;
+ else if(sum < 0) sum = 0;
+
+ *dest = (guchar) sum;
+
+ dest += jump;
+ }
+}
+
+static void
+find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev)
+{
+ gint i;
+ gdouble constants [8];
+ gdouble div;
+
+ /* The constants used in the implementation of a casual sequence
+ * using a 4th order approximation of the gaussian operator
+ */
+
+ div = sqrt (2 * G_PI) * std_dev;
+ constants [0] = -1.783 / std_dev;
+ constants [1] = -1.723 / std_dev;
+ constants [2] = 0.6318 / std_dev;
+ constants [3] = 1.997 / std_dev;
+ constants [4] = 1.6803 / div;
+ constants [5] = 3.735 / div;
+ constants [6] = -0.6803 / div;
+ constants [7] = -0.2598 / div;
+
+ n_p [0] = constants[4] + constants[6];
+ n_p [1] = exp (constants[1]) *
+ (constants[7] * sin (constants[3]) -
+ (constants[6] + 2 * constants[4]) * cos (constants[3])) +
+ exp (constants[0]) *
+ (constants[5] * sin (constants[2]) -
+ (2 * constants[6] + constants[4]) * cos (constants[2]));
+ n_p [2] = 2 * exp (constants[0] + constants[1]) *
+ ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
+ constants[5] * cos (constants[3]) * sin (constants[2]) -
+ constants[7] * cos (constants[2]) * sin (constants[3])) +
+ constants[6] * exp (2 * constants[0]) +
+ constants[4] * exp (2 * constants[1]);
+ n_p [3] = exp (constants[1] + 2 * constants[0]) *
+ (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
+ exp (constants[0] + 2 * constants[1]) *
+ (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
+ n_p [4] = 0.0;
+
+ d_p [0] = 0.0;
+ d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
+ 2 * exp (constants[0]) * cos (constants[2]);
+ d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
+ exp (2 * constants[1]) + exp (2 * constants[0]);
+ d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
+ 2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
+ d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
+
+#ifndef ORIGINAL_READABLE_CODE
+ memcpy(d_m, d_p, 5 * sizeof(gdouble));
+#else
+ for (i = 0; i <= 4; i++)
+ d_m [i] = d_p [i];
+#endif
+
+ n_m[0] = 0.0;
+ for (i = 1; i <= 4; i++)
+ n_m [i] = n_p[i] - d_p[i] * n_p[0];
+
+ {
+ gdouble sum_n_p, sum_n_m, sum_d;
+ gdouble a, b;
+
+ sum_n_p = 0.0;
+ sum_n_m = 0.0;
+ sum_d = 0.0;
+
+ for (i = 0; i <= 4; i++)
+ {
+ sum_n_p += n_p[i];
+ sum_n_m += n_m[i];
+ sum_d += d_p[i];
+ }
+
+#ifndef ORIGINAL_READABLE_CODE
+ sum_d++;
+ a = sum_n_p / sum_d;
+ b = sum_n_m / sum_d;
+#else
+ a = sum_n_p / (1 + sum_d);
+ b = sum_n_m / (1 + sum_d);
+#endif
+
+ for (i = 0; i <= 4; i++)
+ {
+ bd_p[i] = d_p[i] * a;
+ bd_m[i] = d_m[i] * b;
+ }
+ }
+}
+
+/*******************************************************/
+/* Dialog */
+/*******************************************************/
+
+static gboolean
+photocopy_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkObject *scale_data;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Photocopy"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (photocopy),
+ drawable);
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Label, scale, entry for pvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Mask radius:"), 100, 5,
+ pvals.mask_radius, 3.0, 50.0, 1, 5.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.mask_radius);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for pvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Sharpness:"), 50, 5,
+ pvals.sharpness, 0.0, 1.0, 0.01, 0.1, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.sharpness);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for pvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Percent _black:"), 50, 5,
+ pvals.pct_black, 0.0, 1.0, 0.01, 0.1, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.pct_black);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for pvals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Percent _white:"), 50, 5,
+ pvals.pct_white, 0.0, 1.0, 0.01, 0.1, 3,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.pct_white);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/plugin-browser.c b/plug-ins/common/plugin-browser.c
new file mode 100644
index 0000000..202cc97
--- /dev/null
+++ b/plug-ins/common/plugin-browser.c
@@ -0,0 +1,918 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Copyright (C) 1999 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * Note some portions of the UI comes from the dbbrowser plugin.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <time.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-plug-in-details"
+#define PLUG_IN_BINARY "plugin-browser"
+#define PLUG_IN_ROLE "gimp-plugin-browser"
+#define DBL_LIST_WIDTH 250
+#define DBL_WIDTH (DBL_LIST_WIDTH + 400)
+#define DBL_HEIGHT 250
+
+
+enum
+{
+ LIST_COLUMN_NAME,
+ LIST_COLUMN_DATE,
+ LIST_COLUMN_DATE_STRING,
+ LIST_COLUMN_PATH,
+ LIST_COLUMN_IMAGE_TYPES,
+ LIST_COLUMN_PINFO,
+ N_LIST_COLUMNS
+};
+
+enum
+{
+ TREE_COLUMN_PATH_NAME,
+ TREE_COLUMN_DATE,
+ TREE_COLUMN_DATE_STRING,
+ TREE_COLUMN_IMAGE_TYPES,
+ TREE_COLUMN_MPATH,
+ TREE_COLUMN_PINFO,
+ N_TREE_OLUMNS
+};
+
+typedef struct
+{
+ GtkWidget *dialog;
+
+ GtkWidget *browser;
+
+ GtkTreeView *list_view;
+ GtkTreeView *tree_view;
+} PluginBrowser;
+
+typedef struct
+{
+ gchar *menu;
+ gchar *accel;
+ gchar *prog;
+ gchar *types;
+ gchar *realname;
+ gint instime;
+} PInfo;
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+static GtkWidget * browser_dialog_new (void);
+static void browser_dialog_response (GtkWidget *widget,
+ gint response_id,
+ PluginBrowser *browser);
+static void browser_list_selection_changed (GtkTreeSelection *selection,
+ PluginBrowser *browser);
+static void browser_tree_selection_changed (GtkTreeSelection *selection,
+ PluginBrowser *browser);
+static void browser_show_plugin (PluginBrowser *browser,
+ PInfo *pinfo);
+
+static gboolean find_existing_mpath (GtkTreeModel *model,
+ const gchar *mpath,
+ GtkTreeIter *return_iter);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Display information about plug-ins"),
+ "Allows one to browse the plug-in menus system. You "
+ "can search for plug-in names, sort by name or menu "
+ "location and you can view a tree representation "
+ "of the plug-in menus. Can also be of help to find "
+ "where new plug-ins have installed themselves in "
+ "the menus.",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1999",
+ N_("_Plug-in Browser"),
+ "",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Help/Programming");
+ gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_PLUGIN);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ INIT_I18N ();
+
+ if (strcmp (name, PLUG_IN_PROC) == 0)
+ {
+ *nreturn_vals = 1;
+
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ browser_dialog_new ();
+ gtk_main ();
+ }
+}
+
+static gboolean
+find_existing_mpath_helper (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ const gchar *mpath,
+ GtkTreeIter *return_iter)
+{
+ do
+ {
+ GtkTreeIter child;
+ gchar *picked_mpath;
+
+ gtk_tree_model_get (model, iter,
+ TREE_COLUMN_MPATH, &picked_mpath,
+ -1);
+
+ if (! strcmp (mpath, picked_mpath))
+ {
+ *return_iter = *iter;
+ g_free (picked_mpath);
+ return TRUE;
+ }
+
+ if (gtk_tree_model_iter_children (model, &child, iter))
+ {
+ gtk_tree_path_down (path);
+
+ if (find_existing_mpath_helper (model, &child, path,
+ mpath, return_iter))
+ {
+ g_free (picked_mpath);
+ return TRUE;
+ }
+
+ gtk_tree_path_up (path);
+ }
+
+ gtk_tree_path_next (path);
+ g_free (picked_mpath);
+ }
+ while (gtk_tree_model_iter_next (model, iter));
+
+ return FALSE;
+}
+
+static gboolean
+find_existing_mpath (GtkTreeModel *model,
+ const gchar *mpath,
+ GtkTreeIter *return_iter)
+{
+ GtkTreePath *path = gtk_tree_path_new_first ();
+ GtkTreeIter parent;
+ gboolean found;
+
+ if (! gtk_tree_model_get_iter (model, &parent, path))
+ {
+ gtk_tree_path_free (path);
+ return FALSE;
+ }
+
+ found = find_existing_mpath_helper (model, &parent, path, mpath, return_iter);
+
+ gtk_tree_path_free (path);
+
+ return found;
+}
+
+static void
+get_parent (PluginBrowser *browser,
+ const gchar *mpath,
+ GtkTreeIter *parent)
+{
+ GtkTreeIter last_parent;
+ gchar *tmp_ptr;
+ gchar *str_ptr;
+ GtkTreeStore *tree_store;
+
+ if (! mpath)
+ return;
+
+ tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
+
+ /* Lookup for existing mpath */
+ if (find_existing_mpath (GTK_TREE_MODEL (tree_store), mpath, parent))
+ return;
+
+ tmp_ptr = g_strdup (mpath);
+
+ /* Strip off trailing ellipsis */
+ str_ptr = strstr (mpath, "...");
+ if (str_ptr && str_ptr == (mpath + strlen (mpath) - 3))
+ *str_ptr = '\0';
+
+ str_ptr = strrchr (tmp_ptr, '/');
+
+ if (str_ptr == NULL)
+ {
+ gtk_tree_store_append (tree_store, parent, NULL);
+ gtk_tree_store_set (tree_store, parent,
+ TREE_COLUMN_MPATH, mpath,
+ TREE_COLUMN_PATH_NAME, mpath,
+ -1);
+ }
+ else
+ {
+ gchar *leaf_ptr;
+
+ leaf_ptr = g_strdup (str_ptr + 1);
+ *str_ptr = '\0';
+
+ get_parent (browser, tmp_ptr, &last_parent);
+ gtk_tree_store_append (tree_store, parent, &last_parent);
+ gtk_tree_store_set (tree_store, parent,
+ TREE_COLUMN_MPATH, mpath,
+ TREE_COLUMN_PATH_NAME, leaf_ptr,
+ -1);
+
+ g_free (leaf_ptr);
+ }
+}
+
+static void
+insert_into_tree_view (PluginBrowser *browser,
+ const gchar *name,
+ gint64 xtime,
+ const gchar *xtimestr,
+ const gchar *menu_str,
+ const gchar *types_str,
+ PInfo *pinfo)
+{
+ gchar *str_ptr;
+ gchar *tmp_ptr;
+ GtkTreeIter parent, iter;
+ GtkTreeStore *tree_store;
+
+ /* Find all nodes */
+ /* Last one is the leaf part */
+
+ tmp_ptr = g_strdup (menu_str);
+
+ str_ptr = strrchr (tmp_ptr, '/');
+
+ if (str_ptr == NULL)
+ {
+ g_free (tmp_ptr);
+ return; /* No node */
+ }
+
+ *str_ptr = '\0';
+
+ /* printf("inserting %s...\n",menu_str); */
+
+ get_parent (browser, tmp_ptr, &parent);
+
+ tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
+ gtk_tree_store_append (tree_store, &iter, &parent);
+ gtk_tree_store_set (tree_store, &iter,
+ TREE_COLUMN_MPATH, menu_str,
+ TREE_COLUMN_PATH_NAME, name,
+ TREE_COLUMN_IMAGE_TYPES, types_str,
+ TREE_COLUMN_DATE, xtime,
+ TREE_COLUMN_DATE_STRING, xtimestr,
+ TREE_COLUMN_PINFO, pinfo,
+ -1);
+}
+
+static void
+browser_search (GimpBrowser *gimp_browser,
+ const gchar *search_text,
+ gint search_type,
+ PluginBrowser *browser)
+{
+ GimpParam *return_vals;
+ gint nreturn_vals;
+ gint num_plugins;
+ gchar *str;
+ GtkListStore *list_store;
+ GtkTreeStore *tree_store;
+
+ gimp_browser_show_message (GIMP_BROWSER (browser->browser),
+ _("Searching by name"));
+
+ return_vals = gimp_run_procedure ("gimp-plugins-query",
+ &nreturn_vals,
+ GIMP_PDB_STRING, search_text,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ num_plugins = return_vals[1].data.d_int32;
+ else
+ num_plugins = 0;
+
+ if (! search_text || strlen (search_text) == 0)
+ {
+ str = g_strdup_printf (ngettext ("%d plug-in", "%d plug-ins",
+ num_plugins),
+ num_plugins);
+ }
+ else
+ {
+ switch (num_plugins)
+ {
+ case 0:
+ str = g_strdup (_("No matches for your query"));
+ break;
+ default:
+ str = g_strdup_printf (ngettext ("%d plug-in matches your query",
+ "%d plug-ins match your query",
+ num_plugins), num_plugins);
+ break;
+ }
+ }
+
+ gtk_label_set_text (GTK_LABEL (gimp_browser->count_label), str);
+ g_free (str);
+
+ list_store = GTK_LIST_STORE (gtk_tree_view_get_model (browser->list_view));
+ gtk_list_store_clear (list_store);
+
+ tree_store = GTK_TREE_STORE (gtk_tree_view_get_model (browser->tree_view));
+ gtk_tree_store_clear (tree_store);
+
+ if (num_plugins > 0)
+ {
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ gchar **menu_strs;
+ gchar **accel_strs;
+ gchar **prog_strs;
+ gchar **types_strs;
+ gchar **realname_strs;
+ gint *time_ints;
+ gint i;
+
+ menu_strs = return_vals[2].data.d_stringarray;
+ accel_strs = return_vals[4].data.d_stringarray;
+ prog_strs = return_vals[6].data.d_stringarray;
+ types_strs = return_vals[8].data.d_stringarray;
+ time_ints = return_vals[10].data.d_int32array;
+ realname_strs = return_vals[12].data.d_stringarray;
+
+ for (i = 0; i < num_plugins; i++)
+ {
+ PInfo *pinfo;
+ gchar *name;
+ gchar xtimestr[50];
+ struct tm *x;
+ time_t tx;
+ gint ret;
+
+ /* Strip off trailing ellipsis */
+ name = strstr (menu_strs[i], "...");
+ if (name && name == (menu_strs[i] + strlen (menu_strs[i]) - 3))
+ *name = '\0';
+
+ name = strrchr (menu_strs[i], '/');
+
+ if (name)
+ {
+ *name = '\0';
+ name = name + 1;
+ }
+ else
+ {
+ name = menu_strs[i];
+ }
+
+ tx = time_ints[i];
+ if (tx)
+ {
+ const gchar *format = "%c"; /* gcc workaround to avoid warning */
+ gchar *utf8;
+
+ x = localtime (&tx);
+ ret = strftime (xtimestr, sizeof (xtimestr), format, x);
+ xtimestr[ret] = 0;
+
+ if ((utf8 = g_locale_to_utf8 (xtimestr, -1, NULL, NULL, NULL)))
+ {
+ strncpy (xtimestr, utf8, sizeof (xtimestr));
+ xtimestr[sizeof (xtimestr) - 1] = 0;
+ g_free (utf8);
+ }
+ }
+ else
+ {
+ strcpy (xtimestr, "");
+ }
+
+ pinfo = g_new0 (PInfo, 1);
+
+ pinfo->menu = g_strdup (menu_strs[i]);
+ pinfo->accel = g_strdup (accel_strs[i]);
+ pinfo->prog = g_strdup (prog_strs[i]);
+ pinfo->types = g_strdup (types_strs[i]);
+ pinfo->instime = time_ints[i];
+ pinfo->realname = g_strdup (realname_strs[i]);
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ LIST_COLUMN_NAME, name,
+ LIST_COLUMN_DATE, (gint64) tx,
+ LIST_COLUMN_DATE_STRING, xtimestr,
+ LIST_COLUMN_PATH, menu_strs[i],
+ LIST_COLUMN_IMAGE_TYPES, types_strs[i],
+ LIST_COLUMN_PINFO, pinfo,
+ -1);
+
+ /* Now do the tree view.... */
+ insert_into_tree_view (browser,
+ name,
+ (gint64) tx,
+ xtimestr,
+ menu_strs[i],
+ types_strs[i],
+ pinfo);
+ }
+
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->list_view));
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (browser->tree_view));
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store),
+ LIST_COLUMN_NAME,
+ GTK_SORT_ASCENDING);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (tree_store),
+ TREE_COLUMN_PATH_NAME,
+ GTK_SORT_ASCENDING);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (browser->list_view));
+
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store),
+ &iter);
+ gtk_tree_selection_select_iter (sel, &iter);
+ }
+ else
+ {
+ gimp_browser_show_message (GIMP_BROWSER (browser->browser),
+ _("No matches"));
+ }
+
+ gimp_destroy_params (return_vals, nreturn_vals);
+}
+
+static GtkWidget *
+browser_dialog_new (void)
+{
+ PluginBrowser *browser;
+ GtkWidget *label, *notebook;
+ GtkWidget *scrolled_window;
+ GtkListStore *list_store;
+ GtkTreeStore *tree_store;
+ GtkWidget *list_view;
+ GtkWidget *tree_view;
+ GtkWidget *parent;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ browser = g_new0 (PluginBrowser, 1);
+
+ browser->dialog = gimp_dialog_new (_("Plug-in Browser"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ g_signal_connect (browser->dialog, "response",
+ G_CALLBACK (browser_dialog_response),
+ browser);
+
+ browser->browser = gimp_browser_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (browser->browser), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (browser->dialog))),
+ browser->browser, TRUE, TRUE, 0);
+ gtk_widget_show (browser->browser);
+
+ g_signal_connect (browser->browser, "search",
+ G_CALLBACK (browser_search),
+ browser);
+
+ /* left = notebook */
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (GIMP_BROWSER (browser->browser)->left_vbox),
+ notebook, TRUE, TRUE, 0);
+
+ /* list : list in a scrolled_win */
+ list_store = gtk_list_store_new (N_LIST_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_INT64,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+
+ list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+ g_object_unref (list_store);
+
+ browser->list_view = GTK_TREE_VIEW (list_view);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Name"),
+ renderer,
+ "text", LIST_COLUMN_NAME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_NAME);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
+ renderer,
+ "text", LIST_COLUMN_PATH,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_PATH);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
+ renderer,
+ "text",
+ LIST_COLUMN_IMAGE_TYPES,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_IMAGE_TYPES);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
+ renderer,
+ "text",
+ LIST_COLUMN_DATE_STRING,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, LIST_COLUMN_DATE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list_view), column);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_set_size_request (list_view, DBL_LIST_WIDTH, DBL_HEIGHT);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (browser_list_selection_changed),
+ browser);
+
+ label = gtk_label_new (_("List View"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), list_view);
+ gtk_widget_show (list_view);
+ gtk_widget_show (scrolled_window);
+
+ /* notebook->ctree */
+ tree_store = gtk_tree_store_new (N_LIST_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_INT64,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+
+ tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
+ g_object_unref (tree_store);
+
+ browser->tree_view = GTK_TREE_VIEW (tree_view);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Menu Path"),
+ renderer,
+ "text",
+ TREE_COLUMN_PATH_NAME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_PATH_NAME);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Image Types"),
+ renderer,
+ "text",
+ TREE_COLUMN_IMAGE_TYPES,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_IMAGE_TYPES);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Installation Date"),
+ renderer,
+ "text",
+ TREE_COLUMN_DATE_STRING,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_DATE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 2);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request (tree_view, DBL_LIST_WIDTH, DBL_HEIGHT);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (browser_tree_selection_changed),
+ browser);
+
+ label = gtk_label_new (_("Tree View"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), scrolled_window, label);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+
+ gtk_widget_show (tree_view);
+ gtk_widget_show (scrolled_window);
+ gtk_widget_show (notebook);
+
+ parent = gtk_widget_get_parent (GIMP_BROWSER (browser->browser)->right_vbox);
+ parent = gtk_widget_get_parent (parent);
+
+ gtk_widget_set_size_request (parent, DBL_WIDTH - DBL_LIST_WIDTH, -1);
+
+ /* now build the list */
+ browser_search (GIMP_BROWSER (browser->browser), "", 0, browser);
+
+ gtk_widget_show (browser->dialog);
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view)),
+ &iter);
+
+ return browser->dialog;
+}
+
+static void
+browser_dialog_response (GtkWidget *widget,
+ gint response_id,
+ PluginBrowser *browser)
+{
+ gtk_widget_destroy (browser->dialog);
+ gtk_main_quit ();
+}
+
+static void
+browser_list_selection_changed (GtkTreeSelection *selection,
+ PluginBrowser *browser)
+{
+ PInfo *pinfo = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *mpath = NULL;
+
+ g_return_if_fail (browser != NULL);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ LIST_COLUMN_PINFO, &pinfo,
+ LIST_COLUMN_PATH, &mpath,
+ -1);
+ }
+
+ if (!pinfo || !mpath)
+ return;
+
+ model = gtk_tree_view_get_model (browser->tree_view);
+
+ if (find_existing_mpath (model, mpath, &iter))
+ {
+ GtkTreeSelection *tree_selection;
+ GtkTreePath *tree_path;
+
+ tree_path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_expand_to_path (browser->tree_view, tree_path);
+ tree_selection = gtk_tree_view_get_selection (browser->tree_view);
+
+ g_signal_handlers_block_by_func (tree_selection,
+ browser_tree_selection_changed,
+ browser);
+ gtk_tree_selection_select_iter (tree_selection, &iter);
+ g_signal_handlers_unblock_by_func (tree_selection,
+ browser_tree_selection_changed,
+ browser);
+
+ gtk_tree_view_scroll_to_cell (browser->tree_view,
+ tree_path, NULL,
+ TRUE, 0.5, 0.0);
+ }
+ else
+ {
+ g_warning ("Failed to find node in tree");
+ }
+
+ g_free (mpath);
+
+ browser_show_plugin (browser, pinfo);
+}
+
+static void
+browser_tree_selection_changed (GtkTreeSelection *selection,
+ PluginBrowser *browser)
+{
+ PInfo *pinfo = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *mpath = NULL;
+ gboolean valid, found;
+
+ g_return_if_fail (browser != NULL);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ TREE_COLUMN_PINFO, &pinfo,
+ TREE_COLUMN_MPATH, &mpath,
+ -1);
+ }
+
+ if (!pinfo || !mpath)
+ return;
+
+ /* Get the first iter in the list */
+ model = gtk_tree_view_get_model (browser->list_view);
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+ found = FALSE;
+
+ while (valid)
+ {
+ /* Walk through the list, reading each row */
+ gchar *picked_mpath;
+
+ gtk_tree_model_get (model, &iter,
+ LIST_COLUMN_PATH, &picked_mpath,
+ -1);
+ if (picked_mpath && !strcmp (mpath, picked_mpath))
+ {
+ found = TRUE;
+ break;
+ }
+
+ g_free (picked_mpath);
+ valid = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ g_free (mpath);
+
+ if (found)
+ {
+ GtkTreeSelection *list_selection;
+ GtkTreePath *tree_path;
+
+ tree_path = gtk_tree_model_get_path (model, &iter);
+ list_selection = gtk_tree_view_get_selection (browser->list_view);
+
+ g_signal_handlers_block_by_func (list_selection,
+ browser_list_selection_changed,
+ browser);
+ gtk_tree_selection_select_iter (list_selection, &iter);
+ g_signal_handlers_unblock_by_func (list_selection,
+ browser_list_selection_changed,
+ browser);
+
+ gtk_tree_view_scroll_to_cell (browser->list_view,
+ tree_path, NULL,
+ TRUE, 0.5, 0.0);
+ }
+ else
+ {
+ g_warning ("Failed to find node in list");
+ }
+
+ browser_show_plugin (browser, pinfo);
+}
+
+static void
+browser_show_plugin (PluginBrowser *browser,
+ PInfo *pinfo)
+{
+ gchar *blurb = NULL;
+ gchar *help = NULL;
+ gchar *author = NULL;
+ gchar *copyright = NULL;
+ gchar *date = NULL;
+ GimpPDBProcType type = 0;
+ gint n_params = 0;
+ gint n_return_vals = 0;
+ GimpParamDef *params = NULL;
+ GimpParamDef *return_vals = NULL;
+
+ g_return_if_fail (browser != NULL);
+ g_return_if_fail (pinfo != NULL);
+
+ gimp_procedural_db_proc_info (pinfo->realname,
+ &blurb,
+ &help,
+ &author,
+ &copyright,
+ &date,
+ &type,
+ &n_params,
+ &n_return_vals,
+ &params,
+ &return_vals);
+
+ gimp_browser_set_widget (GIMP_BROWSER (browser->browser),
+ gimp_proc_view_new (pinfo->realname,
+ pinfo->menu,
+ blurb,
+ help,
+ author,
+ copyright,
+ date,
+ type,
+ n_params,
+ n_return_vals,
+ params,
+ return_vals));
+
+ g_free (blurb);
+ g_free (help);
+ g_free (author);
+ g_free (copyright);
+ g_free (date);
+
+ gimp_destroy_paramdefs (params, n_params);
+ gimp_destroy_paramdefs (return_vals, n_return_vals);
+}
diff --git a/plug-ins/common/plugin-defs.pl b/plug-ins/common/plugin-defs.pl
new file mode 100644
index 0000000..b4294d7
--- /dev/null
+++ b/plug-ins/common/plugin-defs.pl
@@ -0,0 +1,92 @@
+%plugins = (
+ 'align-layers' => { ui => 1 },
+ 'animation-optimize' => { gegl => 1},
+ 'animation-play' => { ui => 1, gegl => 1 },
+ 'blinds' => { ui => 1, gegl => 1 },
+ 'blur' => {},
+ 'border-average' => { ui => 1, gegl => 1 },
+ 'busy-dialog' => { ui => 1, gegl => 1 },
+ 'cartoon' => { ui => 1 },
+ 'checkerboard' => { ui => 1, gegl => 1 },
+ 'cml-explorer' => { ui => 1, gegl => 1 },
+ 'color-cube-analyze' => { ui => 1 },
+ 'color-enhance' => { ui => 1 },
+ 'colorify' => { ui => 1 },
+ 'colormap-remap' => { ui => 1, gegl => 1 },
+ 'compose' => { ui => 1, gegl => 1 },
+ 'contrast-retinex' => { ui => 1, gegl => 1 },
+ 'crop-zealous' => { gegl => 1 },
+ 'curve-bend' => { ui => 1, gegl => 1 },
+ 'decompose' => { ui => 1, gegl => 1 },
+ 'depth-merge' => { ui => 1, gegl => 1 },
+ 'despeckle' => { ui => 1, gegl => 1 },
+ 'destripe' => { ui => 1, gegl => 1 },
+ 'edge-dog' => { ui => 1 },
+ 'emboss' => { ui => 1 },
+ 'file-aa' => { ui => 1, gegl => 1, optional => 1, libs => 'AA_LIBS' },
+ 'file-cel' => { ui => 1, gegl => 1 },
+ 'file-csource' => { ui => 1, gegl => 1 },
+ 'file-compressor' => { gio => 1, libdep => 'Z:BZIP2:LZMA', cflags => 'LZMA_CFLAGS' },
+ 'file-desktop-link' => {},
+ 'file-dicom' => { ui => 1, gegl => 1, cflags => '-fno-strict-aliasing' },
+ 'file-gbr' => { ui => 1, gegl => 1 },
+ 'file-gegl' => { ui => 1, gegl => 1 },
+ 'file-gif-load' => { gegl => 1 },
+ 'file-gif-save' => { ui => 1, gegl => 1 },
+ 'file-gih' => { ui => 1, gegl => 1 },
+ 'file-glob' => {},
+ 'file-header' => { ui => 1, gegl => 1 },
+ 'file-heif' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:LCMS', libs => 'LIBHEIF_LIBS', cflags => 'LIBHEIF_CFLAGS' },
+ 'file-html-table' => { ui => 1, gegl => 1 },
+ 'file-jp2-load' => { ui => 1, optional => 1, gegl => 1, libs => 'OPENJPEG_LIBS', cflags => 'OPENJPEG_CFLAGS' },
+ 'file-jpegxl' => { ui => 1, optional => 1, gegl => 1, libdep => 'GEXIV2:JXL:JXL_THREADS', cflags => 'JXL_CFLAGS' },
+ 'file-mng' => { ui => 1, gegl => 1, optional => 1, libs => 'MNG_LIBS', cflags => 'MNG_CFLAGS' },
+ 'file-pat' => { ui => 1, gegl => 1 },
+ 'file-pcx' => { ui => 1, gegl => 1 },
+ 'file-pix' => { ui => 1, gegl => 1 },
+ 'file-png' => { ui => 1, gegl => 1, libs => 'PNG_LIBS', cflags => 'PNG_CFLAGS' },
+ 'file-pnm' => { ui => 1, gegl => 1 },
+ 'file-pdf-load' => { ui => 1, gegl => 1, libs => 'POPPLER_LIBS', cflags => 'POPPLER_CFLAGS' },
+ 'file-pdf-save' => { ui => 1, gegl => 1, optional => 1, libs => 'CAIRO_PDF_LIBS', cflags => 'CAIRO_PDF_CFLAGS' },
+ 'file-ps' => { ui => 1, gegl => 1, optional => 1, libs => 'GS_LIBS' },
+ 'file-psp' => { ui => 1, gegl => 1, libs => 'Z_LIBS' },
+ 'file-raw-data' => { ui => 1, gegl => 1 },
+ 'file-sunras' => { ui => 1, gegl => 1 },
+ 'file-svg' => { ui => 1, libs => 'SVG_LIBS', cflags => 'SVG_CFLAGS' },
+ 'file-tga' => { ui => 1, gegl => 1 },
+ 'file-wmf' => { ui => 1, gegl => 1, optional => 1, libs => 'WMF_LIBS', cflags => 'WMF_CFLAGS' },
+ 'file-xbm' => { ui => 1, gegl => 1 },
+ 'file-xmc' => { ui => 1, gegl => 1, optional => 1, libs => 'XMC_LIBS' },
+ 'file-xpm' => { ui => 1, gegl => 1, optional => 1, libs => 'XPM_LIBS' },
+ 'file-xwd' => { ui => 1, gegl => 1 },
+ 'film' => { ui => 1, gegl => 1 },
+ 'filter-pack' => { ui => 1 },
+ 'fractal-trace' => { ui => 1 },
+ 'goat-exercise' => { gegl => 1 },
+ 'gradient-map' => { gegl => 1 },
+ 'grid' => { ui => 1, gegl => 1 },
+ 'guillotine' => {},
+ 'hot' => { ui => 1, gegl => 1 },
+ 'jigsaw' => { ui => 1, gegl => 1 },
+ 'mail' => { ui => 1, optional => 1 },
+ 'max-rgb' => { ui => 1 },
+ 'nl-filter' => { ui => 1, gegl => 1 },
+ 'photocopy' => { ui => 1 },
+ 'plugin-browser' => { ui => 1 },
+ 'procedure-browser' => { ui => 1 },
+ 'qbist' => { ui => 1, gegl => 1 },
+ 'sample-colorize' => { ui => 1, gegl => 1 },
+ 'sharpen' => { ui => 1 },
+ 'smooth-palette' => { ui => 1, gegl => 1 },
+ 'softglow' => { ui => 1 },
+ 'sparkle' => { ui => 1, gegl => 1 },
+ 'sphere-designer' => { ui => 1, gegl => 1 },
+ 'tile' => { ui => 1, gegl => 1 },
+ 'tile-small' => { ui => 1, gegl => 1 },
+ 'unit-editor' => { ui => 1 },
+ 'van-gogh-lic' => { ui => 1, gegl => 1 },
+ 'warp' => { ui => 1, gegl => 1 },
+ 'wavelet-decompose' => { ui => 1, gegl => 1 },
+ 'web-browser' => { ui => 1, ldflags => '$(framework_cocoa)', cppflags => '$(AM_CPPFLAGS) $(xobjective_c)' },
+ 'web-page' => { ui => 1, optional => 1, libs => 'WEBKIT_LIBS', cflags => 'WEBKIT_CFLAGS' }
+);
diff --git a/plug-ins/common/procedure-browser.c b/plug-ins/common/procedure-browser.c
new file mode 100644
index 0000000..1651aa4
--- /dev/null
+++ b/plug-ins/common/procedure-browser.c
@@ -0,0 +1,147 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * dbbrowser
+ * 0.08 26th sept 97 by Thomas NOEL <thomas@minet.net>
+ */
+
+/*
+ * This plugin gives you the list of available procedure, with the
+ * name, description and parameters for each procedure.
+ * You can do regexp search (by name and by description)
+ * Useful for scripts development.
+ *
+ * NOTE :
+ * this is only a exercice for me (my first "plug-in" (extension))
+ * so it's very (very) dirty.
+ * Btw, hope it gives you some ideas about gimp possibilities.
+ *
+ * The core of the plugin is not here. See dbbrowser_utils (shared
+ * with script-fu-console).
+ *
+ * TODO
+ * - bug fixes... (my method : rewrite from scratch :)
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-dbbrowser"
+#define PLUG_IN_BINARY "procedure-browser"
+#define PLUG_IN_ROLE "gimp-procedure-browser"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("List available procedures in the PDB"),
+ "",
+ "Thomas Noel",
+ "Thomas Noel",
+ "23th june 1997",
+ N_("Procedure _Browser"),
+ "",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Help/Programming");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ {
+ GtkWidget *dialog;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog =
+ gimp_proc_browser_dialog_new (_("Procedure Browser"), PLUG_IN_BINARY,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ case GIMP_RUN_NONINTERACTIVE:
+ g_warning (PLUG_IN_PROC " allows only interactive invocation");
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/plug-ins/common/qbist.c b/plug-ins/common/qbist.c
new file mode 100644
index 0000000..5b4a6ed
--- /dev/null
+++ b/plug-ins/common/qbist.c
@@ -0,0 +1,912 @@
+/*
+ * Written 1997 Jens Ch. Restemeier <jrestemeier@currantbun.com>
+ * This program is based on an algorithm / article by
+ * Jörn Loviscach.
+ *
+ * It appeared in c't 10/95, page 326 and is called
+ * "Ausgewürfelt - Moderne Kunst algorithmisch erzeugen"
+ * (~modern art created with algorithms).
+ *
+ * It generates one main formula (the middle button) and 8 variations of it.
+ * If you select a variation it becomes the new main formula. If you
+ * press "OK" the main formula will be applied to the image.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <limits.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/** qbist renderer ***********************************************************/
+
+#define MAX_TRANSFORMS 36
+#define NUM_REGISTERS 6
+#define PREVIEW_SIZE 64
+
+#define PLUG_IN_PROC "plug-in-qbist"
+#define PLUG_IN_BINARY "qbist"
+#define PLUG_IN_ROLE "gimp-qbist"
+#define PLUG_IN_VERSION "January 2001, 1.12"
+
+/** types *******************************************************************/
+
+/* experiment with this */
+typedef gfloat vreg[3];
+
+typedef enum
+{
+ PROJECTION,
+ SHIFT,
+ SHIFTBACK,
+ ROTATE,
+ ROTATE2,
+ MULTIPLY,
+ SINE,
+ CONDITIONAL,
+ COMPLEMENT
+} TransformType;
+
+#define NUM_TRANSFORMS (COMPLEMENT + 1)
+
+
+typedef struct
+{
+ TransformType transformSequence[MAX_TRANSFORMS];
+ gint source[MAX_TRANSFORMS];
+ gint control[MAX_TRANSFORMS];
+ gint dest[MAX_TRANSFORMS];
+} ExpInfo;
+
+typedef struct
+{
+ ExpInfo info;
+ gint oversampling;
+ gchar path[PATH_MAX];
+} QbistInfo;
+
+
+/** prototypes **************************************************************/
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean dialog_run (void);
+static void dialog_new_variations (GtkWidget *widget,
+ gpointer data);
+static void dialog_update_previews (GtkWidget *widget,
+ gpointer data);
+static void dialog_select_preview (GtkWidget *widget,
+ ExpInfo *n_info);
+
+static QbistInfo qbist_info;
+static GRand *gr = NULL;
+
+
+/** qbist functions *********************************************************/
+
+static void
+create_info (ExpInfo *info)
+{
+ gint k;
+
+ for (k = 0; k < MAX_TRANSFORMS; k++)
+ {
+ info->transformSequence[k] = g_rand_int_range (gr, 0, NUM_TRANSFORMS);
+ info->source[k] = g_rand_int_range (gr, 0, NUM_REGISTERS);
+ info->control[k] = g_rand_int_range (gr, 0, NUM_REGISTERS);
+ info->dest[k] = g_rand_int_range (gr, 0, NUM_REGISTERS);
+ }
+}
+
+static void
+modify_info (ExpInfo *o_info,
+ ExpInfo *n_info)
+{
+ gint k, n;
+
+ *n_info = *o_info;
+ n = g_rand_int_range (gr, 0, MAX_TRANSFORMS);
+ for (k = 0; k < n; k++)
+ {
+ switch (g_rand_int_range (gr, 0, 4))
+ {
+ case 0:
+ n_info->transformSequence[g_rand_int_range (gr, 0, MAX_TRANSFORMS)] =
+ g_rand_int_range (gr, 0, NUM_TRANSFORMS);
+ break;
+
+ case 1:
+ n_info->source[g_rand_int_range (gr, 0, MAX_TRANSFORMS)] =
+ g_rand_int_range (gr, 0, NUM_REGISTERS);
+ break;
+
+ case 2:
+ n_info->control[g_rand_int_range (gr, 0, MAX_TRANSFORMS)] =
+ g_rand_int_range (gr, 0, NUM_REGISTERS);
+ break;
+
+ case 3:
+ n_info->dest[g_rand_int_range (gr, 0, MAX_TRANSFORMS)] =
+ g_rand_int_range (gr, 0, NUM_REGISTERS);
+ break;
+ }
+ }
+}
+
+/*
+ * Optimizer
+ */
+static gint used_trans_flag[MAX_TRANSFORMS];
+static gint used_reg_flag[NUM_REGISTERS];
+
+static void
+check_last_modified (ExpInfo *info,
+ gint p,
+ gint n)
+{
+ p--;
+ while ((p >= 0) && (info->dest[p] != n))
+ p--;
+ if (p < 0)
+ used_reg_flag[n] = 1;
+ else
+ {
+ used_trans_flag[p] = 1;
+ check_last_modified (info, p, info->source[p]);
+ check_last_modified (info, p, info->control[p]);
+ }
+}
+
+static void
+optimize (ExpInfo *info)
+{
+ gint i;
+
+ /* double-arg fix: */
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ {
+ used_trans_flag[i] = 0;
+ if (i < NUM_REGISTERS)
+ used_reg_flag[i] = 0;
+ /* double-arg fix: */
+ switch (info->transformSequence[i])
+ {
+ case ROTATE:
+ case ROTATE2:
+ case COMPLEMENT:
+ info->control[i] = info->dest[i];
+ break;
+
+ default:
+ break;
+ }
+ }
+ /* check for last modified item */
+ check_last_modified (info, MAX_TRANSFORMS, 0);
+}
+
+static void
+qbist (ExpInfo *info,
+ gfloat *buffer,
+ gint xp,
+ gint yp,
+ gint num,
+ gint width,
+ gint height,
+ gint oversampling)
+{
+ gint gx;
+
+ vreg reg[NUM_REGISTERS];
+
+ for (gx = 0; gx < num; gx++)
+ {
+ gfloat accum[3];
+ gint yy, i;
+
+ for (i = 0; i < 3; i++)
+ {
+ accum[i] = 0.0;
+ }
+
+ for (yy = 0; yy < oversampling; yy++)
+ {
+ gint xx;
+
+ for (xx = 0; xx < oversampling; xx++)
+ {
+ for (i = 0; i < NUM_REGISTERS; i++)
+ {
+ if (used_reg_flag[i])
+ {
+ reg[i][0] = ((gfloat) ((gx + xp) * oversampling + xx)) / ((gfloat) (width * oversampling));
+ reg[i][1] = ((gfloat) (yp * oversampling + yy)) / ((gfloat) (height * oversampling));
+ reg[i][2] = ((gfloat) i) / ((gfloat) NUM_REGISTERS);
+ }
+ }
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ {
+ gushort sr, cr, dr;
+
+ sr = info->source[i];
+ cr = info->control[i];
+ dr = info->dest[i];
+
+ if (used_trans_flag[i])
+ switch (info->transformSequence[i])
+ {
+ case PROJECTION:
+ {
+ gfloat scalarProd;
+
+ scalarProd = (reg[sr][0] * reg[cr][0]) +
+ (reg[sr][1] * reg[cr][1]) + (reg[sr][2] * reg[cr][2]);
+
+ reg[dr][0] = scalarProd * reg[sr][0];
+ reg[dr][1] = scalarProd * reg[sr][1];
+ reg[dr][2] = scalarProd * reg[sr][2];
+ break;
+ }
+
+ case SHIFT:
+ reg[dr][0] = reg[sr][0] + reg[cr][0];
+ if (reg[dr][0] >= 1.0)
+ reg[dr][0] -= 1.0;
+ reg[dr][1] = reg[sr][1] + reg[cr][1];
+ if (reg[dr][1] >= 1.0)
+ reg[dr][1] -= 1.0;
+ reg[dr][2] = reg[sr][2] + reg[cr][2];
+ if (reg[dr][2] >= 1.0)
+ reg[dr][2] -= 1.0;
+ break;
+
+ case SHIFTBACK:
+ reg[dr][0] = reg[sr][0] - reg[cr][0];
+ if (reg[dr][0] <= 0.0)
+ reg[dr][0] += 1.0;
+ reg[dr][1] = reg[sr][1] - reg[cr][1];
+ if (reg[dr][1] <= 0.0)
+ reg[dr][1] += 1.0;
+ reg[dr][2] = reg[sr][2] - reg[cr][2];
+ if (reg[dr][2] <= 0.0)
+ reg[dr][2] += 1.0;
+ break;
+
+ case ROTATE:
+ reg[dr][0] = reg[sr][1];
+ reg[dr][1] = reg[sr][2];
+ reg[dr][2] = reg[sr][0];
+ break;
+
+ case ROTATE2:
+ reg[dr][0] = reg[sr][2];
+ reg[dr][1] = reg[sr][0];
+ reg[dr][2] = reg[sr][1];
+ break;
+
+ case MULTIPLY:
+ reg[dr][0] = reg[sr][0] * reg[cr][0];
+ reg[dr][1] = reg[sr][1] * reg[cr][1];
+ reg[dr][2] = reg[sr][2] * reg[cr][2];
+ break;
+
+ case SINE:
+ reg[dr][0] = 0.5 + (0.5 * sin (20.0 * reg[sr][0] * reg[cr][0]));
+ reg[dr][1] = 0.5 + (0.5 * sin (20.0 * reg[sr][1] * reg[cr][1]));
+ reg[dr][2] = 0.5 + (0.5 * sin (20.0 * reg[sr][2] * reg[cr][2]));
+ break;
+
+ case CONDITIONAL:
+ if ((reg[cr][0] + reg[cr][1] + reg[cr][2]) > 0.5)
+ {
+ reg[dr][0] = reg[sr][0];
+ reg[dr][1] = reg[sr][1];
+ reg[dr][2] = reg[sr][2];
+ }
+ else
+ {
+ reg[dr][0] = reg[cr][0];
+ reg[dr][1] = reg[cr][1];
+ reg[dr][2] = reg[cr][2];
+ }
+ break;
+
+ case COMPLEMENT:
+ reg[dr][0] = 1.0 - reg[sr][0];
+ reg[dr][1] = 1.0 - reg[sr][1];
+ reg[dr][2] = 1.0 - reg[sr][2];
+ break;
+ }
+ }
+
+ accum[0] += reg[0][0];
+ accum[1] += reg[0][1];
+ accum[2] += reg[0][2];
+ }
+ }
+
+ buffer[0] = accum[0] / (gfloat) (oversampling * oversampling);
+ buffer[1] = accum[1] / (gfloat) (oversampling * oversampling);
+ buffer[2] = accum[2] / (gfloat) (oversampling * oversampling);
+ buffer[3] = 1.0;
+
+ buffer += 4;
+ }
+}
+
+/** Plugin interface *********************************************************/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Generate a huge variety of abstract patterns"),
+ "This Plug-in is based on an article by "
+ "Jörn Loviscach (appeared in c't 10/95, page 326). "
+ "It generates modern art pictures from a random "
+ "genetic formula.",
+ "Jörn Loviscach, Jens Ch. Restemeier",
+ "Jörn Loviscach, Jens Ch. Restemeier",
+ PLUG_IN_VERSION,
+ N_("_Qbist..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Pattern");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint sel_x1, sel_y1, sel_width, sel_height;
+ gint img_height, img_width;
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ status = GIMP_PDB_SUCCESS;
+
+ if (param[0].type != GIMP_PDB_INT32)
+ status = GIMP_PDB_CALLING_ERROR;
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ if (param[2].type != GIMP_PDB_DRAWABLE)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ drawable_id = param[2].data.d_drawable;
+
+ img_width = gimp_drawable_width (drawable_id);
+ img_height = gimp_drawable_height (drawable_id);
+
+ if (! gimp_drawable_is_rgb (drawable_id))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &sel_x1, &sel_y1,
+ &sel_width, &sel_height))
+ {
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ return;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gr = g_rand_new ();
+
+ memset (&qbist_info, 0, sizeof (qbist_info));
+ create_info (&qbist_info.info);
+ qbist_info.oversampling = 4;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &qbist_info);
+
+ /* Get information from the dialog */
+ if (dialog_run ())
+ {
+ status = GIMP_PDB_SUCCESS;
+ gimp_set_data (PLUG_IN_PROC, &qbist_info, sizeof (QbistInfo));
+ }
+ else
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &qbist_info);
+ status = GIMP_PDB_SUCCESS;
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GeglBuffer *buffer;
+ GeglBufferIterator *iter;
+ gint total_pixels = img_width * img_height;
+ gint done_pixels = 0;
+
+ buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, 0,
+ img_width, img_height),
+ 0, babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_READWRITE,
+ GEGL_ABYSS_NONE, 1);
+
+ optimize (&qbist_info.info);
+
+ gimp_progress_init (_("Qbist"));
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+ GeglRectangle roi = iter->items[0].roi;
+ gint row;
+
+ for (row = 0; row < roi.height; row++)
+ {
+ qbist (&qbist_info.info,
+ data + row * roi.width * 4,
+ roi.x,
+ roi.y + row,
+ roi.width,
+ sel_width,
+ sel_height,
+ qbist_info.oversampling);
+ }
+
+ done_pixels += roi.width * roi.height;
+
+ gimp_progress_update ((gdouble) done_pixels /
+ (gdouble) total_pixels);
+ }
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, sel_x1, sel_y1,
+ sel_width, sel_height);
+
+ gimp_displays_flush ();
+ }
+
+ g_rand_free (gr);
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+/** User interface ***********************************************************/
+
+static GtkWidget *preview[9];
+static ExpInfo info[9];
+static ExpInfo last_info[9];
+
+static void
+dialog_new_variations (GtkWidget *widget,
+ gpointer data)
+{
+ gint i;
+
+ for (i = 1; i < 9; i++)
+ modify_info (&(info[0]), &(info[i]));
+}
+
+static void
+dialog_update_previews (GtkWidget *widget,
+ gpointer data)
+{
+ static const Babl *fish = NULL;
+
+ gfloat buf[PREVIEW_SIZE * PREVIEW_SIZE * 4];
+ guchar u8_buf[PREVIEW_SIZE * PREVIEW_SIZE * 4];
+ gint i, j;
+
+ if (! fish)
+ fish = babl_fish (babl_format ("R'G'B'A float"),
+ babl_format ("R'G'B'A u8"));
+
+ for (j = 0; j < 9; j++)
+ {
+ optimize (&info[(j + 5) % 9]);
+
+ for (i = 0; i < PREVIEW_SIZE; i++)
+ {
+ qbist (&info[(j + 5) % 9], buf + i * PREVIEW_SIZE * 4,
+ 0, i, PREVIEW_SIZE, PREVIEW_SIZE, PREVIEW_SIZE, 1);
+ }
+
+ babl_process (fish, buf, u8_buf, PREVIEW_SIZE * PREVIEW_SIZE);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview[j]),
+ 0, 0, PREVIEW_SIZE, PREVIEW_SIZE,
+ GIMP_RGBA_IMAGE,
+ u8_buf,
+ PREVIEW_SIZE * 4);
+ }
+}
+
+static void
+dialog_select_preview (GtkWidget *widget,
+ ExpInfo *n_info)
+{
+ memcpy (last_info, info, sizeof (info));
+ info[0] = *n_info;
+ dialog_new_variations (widget, NULL);
+ dialog_update_previews (widget, NULL);
+}
+
+/* File I/O stuff */
+
+static guint16
+get_be16 (guint8 *buf)
+{
+ return (guint16) buf[0] << 8 | buf[1];
+}
+
+static void
+set_be16 (guint8 *buf,
+ guint16 val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val & 0xFF;
+}
+
+static gboolean
+load_data (gchar *name)
+{
+ gint i;
+ FILE *f;
+ guint8 buf[288];
+
+ f = g_fopen (name, "rb");
+ if (f == NULL)
+ {
+ return FALSE;
+ }
+ if (fread (buf, 1, sizeof (buf), f) != sizeof (buf))
+ {
+ fclose (f);
+ return FALSE;
+ }
+ fclose (f);
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ info[0].transformSequence[i] =
+ get_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 0) % NUM_TRANSFORMS;
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ info[0].source[i] = get_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 1) % NUM_REGISTERS;
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ info[0].control[i] = get_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 2) % NUM_REGISTERS;
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ info[0].dest[i] = get_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 3) % NUM_REGISTERS;
+
+ return TRUE;
+}
+
+static gboolean
+save_data (gchar *name)
+{
+ gint i = 0;
+ FILE *f;
+ guint8 buf[288];
+
+ f = g_fopen (name, "wb");
+ if (f == NULL)
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ set_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 0,
+ info[0].transformSequence[i]);
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ set_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 1, info[0].source[i]);
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ set_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 2, info[0].control[i]);
+
+ for (i = 0; i < MAX_TRANSFORMS; i++)
+ set_be16 (buf + i * 2 + MAX_TRANSFORMS * 2 * 3, info[0].dest[i]);
+
+ fwrite (buf, 1, sizeof (buf), f);
+ fclose (f);
+
+ return TRUE;
+}
+
+static void
+dialog_undo (GtkWidget *widget,
+ gpointer data)
+{
+ ExpInfo temp_info[9];
+
+ memcpy (temp_info, info, sizeof (info));
+ memcpy (info, last_info, sizeof (info));
+ dialog_update_previews (NULL, NULL);
+ memcpy (last_info, temp_info, sizeof (info));
+}
+
+static void
+dialog_load (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *parent;
+ GtkWidget *dialog;
+
+ parent = gtk_widget_get_toplevel (widget);
+
+ dialog = gtk_file_chooser_dialog_new (_("Load QBE File"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), qbist_info.path);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ strncpy (qbist_info.path, name, PATH_MAX - 1);
+ load_data (qbist_info.path);
+
+ g_free (name);
+
+ dialog_new_variations (NULL, NULL);
+ dialog_update_previews (NULL, NULL);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+dialog_save (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *parent;
+ GtkWidget *dialog;
+
+ parent = gtk_widget_get_toplevel (widget);
+
+ dialog = gtk_file_chooser_dialog_new (_("Save as QBE File"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), qbist_info.path);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
+ {
+ gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ strncpy (qbist_info.path, name, PATH_MAX - 1);
+ save_data (qbist_info.path);
+
+ g_free (name);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+dialog_toggle_antialaising (GtkWidget *widget,
+ gpointer data)
+{
+ qbist_info.oversampling =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)) ? 4 : 1;
+}
+
+static gboolean
+dialog_run (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *button;
+ GtkWidget *table;
+ gint i;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("G-Qbist"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ info[0] = qbist_info.info;
+ dialog_new_variations (NULL, NULL);
+ memcpy (last_info, info, sizeof (info));
+
+ for (i = 0; i < 9; i++)
+ {
+ button = gtk_button_new ();
+ gtk_table_attach (GTK_TABLE (table),
+ button, i % 3, (i % 3) + 1, i / 3, (i / 3) + 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_select_preview),
+ (gpointer) & (info[(i + 5) % 9]));
+
+ preview[i] = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview[i], PREVIEW_SIZE, PREVIEW_SIZE);
+ gtk_container_add (GTK_CONTAINER (button), preview[i]);
+ gtk_widget_show (preview[i]);
+ }
+
+ button = gtk_check_button_new_with_mnemonic (_("_Antialiasing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ qbist_info.oversampling > 1);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (dialog_toggle_antialaising),
+ NULL);
+
+ bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_START);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Undo"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_undo),
+ NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_load),
+ NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_save),
+ NULL);
+
+ gtk_widget_show (dialog);
+ dialog_update_previews (NULL, NULL);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ qbist_info.info = info[0];
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/sample-colorize.c b/plug-ins/common/sample-colorize.c
new file mode 100644
index 0000000..bae918b
--- /dev/null
+++ b/plug-ins/common/sample-colorize.c
@@ -0,0 +1,3113 @@
+/* sample_colorize.c
+ * A GIMP Plug-In by Wolfgang Hofer
+ */
+
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+#define RESPONSE_RESET 1
+#define RESPONSE_GET_COLORS 2
+
+#define PLUG_IN_PROC "plug-in-sample-colorize"
+#define PLUG_IN_BINARY "sample-colorize"
+#define PLUG_IN_ROLE "gimp-sample-colorize"
+#define NUMBER_IN_ARGS 13
+
+#define LUMINOSITY_0(X) ((X[0] * 30 + X[1] * 59 + X[2] * 11))
+#define LUMINOSITY_1(X) ((X[0] * 30 + X[1] * 59 + X[2] * 11) / 100)
+#define MIX_CHANNEL(a, b, m) (((a * m) + (b * (255 - m))) / 255)
+
+#define SMP_GRADIENT -444
+#define SMP_INV_GRADIENT -445
+
+
+#define PREVIEW_BPP 3
+#define PREVIEW_SIZE_X 256
+#define PREVIEW_SIZE_Y 256
+#define DA_WIDTH 256
+#define DA_HEIGHT 25
+#define GRADIENT_HEIGHT 15
+#define CONTROL_HEIGHT DA_HEIGHT - GRADIENT_HEIGHT
+#define LEVELS_DA_MASK (GDK_EXPOSURE_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON1_MOTION_MASK | \
+ GDK_POINTER_MOTION_HINT_MASK)
+
+#define LOW_INPUT 0x1
+#define GAMMA 0x2
+#define HIGH_INPUT 0x4
+#define LOW_OUTPUT 0x8
+#define HIGH_OUTPUT 0x10
+#define INPUT_LEVELS 0x20
+#define OUTPUT_LEVELS 0x40
+#define INPUT_SLIDERS 0x80
+#define OUTPUT_SLIDERS 0x100
+#define DRAW 0x200
+#define REFRESH_DST 0x400
+#define ALL 0xFFF
+
+#define MC_GET_SAMPLE_COLORS 1
+#define MC_DST_REMAP 2
+#define MC_ALL (MC_GET_SAMPLE_COLORS | MC_DST_REMAP)
+
+typedef struct
+{
+ gint32 dst_id;
+ gint32 sample_id;
+
+ gint32 hold_inten; /* TRUE or FALSE */
+ gint32 orig_inten; /* TRUE or FALSE */
+ gint32 rnd_subcolors; /* TRUE or FALSE */
+ gint32 guess_missing; /* TRUE or FALSE */
+ gint32 lvl_in_min; /* 0 up to 254 */
+ gint32 lvl_in_max; /* 1 up to 255 */
+ float lvl_in_gamma; /* 0.1 up to 10.0 (1.0 == linear) */
+ gint32 lvl_out_min; /* 0 up to 254 */
+ gint32 lvl_out_max; /* 1 up to 255 */
+
+ float tol_col_err; /* 0.0% up to 100.0%
+ * this is used to find out colors of the same
+ * colortone, while analyzing sample colors,
+ * It does not make much sense for the user to adjust this
+ * value. (I used a param file to find out a suitable value)
+ */
+} t_values;
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkWidget *sample_preview;
+ GtkWidget *dst_preview;
+ GtkWidget *sample_colortab_preview;
+ GtkWidget *sample_drawarea;
+ GtkWidget *in_lvl_gray_preview;
+ GtkWidget *in_lvl_drawarea;
+ GtkAdjustment *adj_lvl_in_min;
+ GtkAdjustment *adj_lvl_in_max;
+ GtkAdjustment *adj_lvl_in_gamma;
+ GtkAdjustment *adj_lvl_out_min;
+ GtkAdjustment *adj_lvl_out_max;
+ GtkWidget *orig_inten_button;
+ gint active_slider;
+ gint slider_pos[5]; /* positions for the five sliders */
+
+ gboolean enable_preview_update;
+ gboolean sample_show_selection;
+ gboolean dst_show_selection;
+ gboolean sample_show_color;
+ gboolean dst_show_color;
+} t_samp_interface;
+
+
+
+typedef struct
+{
+ guchar color[4]; /* R,G,B,A */
+ gint32 sum_color; /* nr. of sourcepixels with (nearly the same) color */
+ void *next;
+} t_samp_color_elem;
+
+
+
+typedef struct
+{
+ gint32 all_samples; /* number of all source pixels with this luminosity */
+ gint from_sample; /* TRUE: color found in sample, FALSE: interpolated color added */
+ t_samp_color_elem *col_ptr; /* List of sample colors at same luminosity */
+} t_samp_table_elem;
+
+
+typedef struct
+{
+ gint32 drawable_id;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+ void *sel_gdrw;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gint index_alpha; /* 0 == no alpha, 1 == GREYA, 3 == RGBA */
+ gint bpp;
+ gint tile_width;
+ gint tile_height;
+ gint shadow;
+ gint32 seldeltax;
+ gint32 seldeltay;
+} t_GDRW;
+
+/*
+ * Some globals
+ */
+
+static t_samp_interface g_di; /* global dialog interface variables */
+static t_values g_values = { -1, -1, 1, 1, 0, 1, 0, 255, 1.0, 0, 255, 5.5 };
+static t_samp_table_elem g_lum_tab[256];
+static guchar g_lvl_trans_tab[256];
+static guchar g_out_trans_tab[256];
+static guchar g_sample_color_tab[256 * 3];
+static guchar g_dst_preview_buffer[PREVIEW_SIZE_X * PREVIEW_SIZE_Y * 4 ]; /* color copy with mask of dst in previewsize */
+
+static gint32 g_tol_col_err;
+static gint32 g_max_col_err;
+static gint g_Sdebug = FALSE;
+static gint g_show_progress = FALSE;
+
+/* Declare a local function.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint main_colorize (gint mc_flags);
+static void get_filevalues (void);
+static void smp_dialog (void);
+static void refresh_dst_preview (GtkWidget *preview,
+ guchar *src_buffer);
+static void update_preview (gint32 *id_ptr);
+static void clear_tables (void);
+static void free_colors (void);
+static void levels_update (gint update);
+static gint level_in_events (GtkWidget *widget,
+ GdkEvent *event);
+static gint level_out_events (GtkWidget *widget,
+ GdkEvent *event);
+static void calculate_level_transfers (void);
+static void get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel);
+static void init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ gboolean shadow);
+static void end_gdrw (t_GDRW *gdrw);
+static gint32 is_layer_alive (gint32 drawable_id);
+static void remap_pixel (guchar *pixel,
+ const guchar *original,
+ gint bpp2);
+static void guess_missing_colors (void);
+static void fill_missing_colors (void);
+static void smp_get_colors (GtkWidget *dialog);
+static void get_gradient (gint mode);
+static void clear_preview (GtkWidget *preview);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[]=
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "dst-drawable", "The drawable to be colorized (Type GRAY* or RGB*)" },
+ { GIMP_PDB_DRAWABLE, "sample-drawable", "Sample drawable (should be of Type RGB or RGBA)" },
+ { GIMP_PDB_INT32, "hold-inten", "hold brightness intensity levels (TRUE, FALSE)" },
+ { GIMP_PDB_INT32, "orig-inten", "TRUE: hold brightness of original intensity levels. FALSE: Hold Intensity of input levels" },
+ { GIMP_PDB_INT32, "rnd-subcolors", "TRUE: Use all subcolors of same intensity, FALSE: use only one color per intensity" },
+ { GIMP_PDB_INT32, "guess-missing", "TRUE: guess samplecolors for the missing intensity values FALSE: use only colors found in the sample" },
+ { GIMP_PDB_INT32, "in-low", "intensity of lowest input (0 <= in_low <= 254)" },
+ { GIMP_PDB_INT32, "in-high", "intensity of highest input (1 <= in_high <= 255)" },
+ { GIMP_PDB_FLOAT, "gamma", "gamma adjustment factor (0.1 <= gamma <= 10) where 1.0 is linear" },
+ { GIMP_PDB_INT32, "out-low", "lowest sample color intensity (0 <= out_low <= 254)" },
+ { GIMP_PDB_INT32, "out-high", "highest sample color intensity (1 <= out_high <= 255)" }
+ };
+
+ static gchar *help_string =
+ "This plug-in colorizes the contents of the specified (gray) layer"
+ " with the help of a sample (color) layer."
+ " It analyzes all colors in the sample layer."
+ " The sample colors are sorted by brightness (== intentisty) and amount"
+ " and stored in a sample colortable (where brightness is the index)"
+ " The pixels of the destination layer are remapped with the help of the"
+ " sample colortable. If use_subcolors is TRUE, the remapping process uses"
+ " all sample colors of the corresponding brightness-intensity and"
+ " distributes the subcolors according to their amount in the sample"
+ " (If the sample has 5 green, 3 yellow, and 1 red pixel of the "
+ " intensity value 105, the destination pixels at intensity value 105"
+ " are randomly painted in green, yellow and red in a relation of 5:3:1"
+ " If use_subcolors is FALSE only one sample color per intensity is used."
+ " (green will be used in this example)"
+ " The brightness intensity value is transformed at the remapping process"
+ " according to the levels: out_lo, out_hi, in_lo, in_high and gamma"
+ " The in_low / in_high levels specify an initial mapping of the intensity."
+ " The gamma value determines how intensities are interpolated between"
+ " the in_lo and in_high levels. A gamma value of 1.0 results in linear"
+ " interpolation. Higher gamma values results in more high-level intensities"
+ " Lower gamma values results in more low-level intensities"
+ " The out_low/out_high levels constrain the resulting intensity index"
+ " The intensity index is used to pick the corresponding color"
+ " in the sample colortable. If hold_inten is FALSE the picked color"
+ " is used 1:1 as resulting remap_color."
+ " If hold_inten is TRUE The brightness of the picked color is adjusted"
+ " back to the origial intensity value (only hue and saturation are"
+ " taken from the picked sample color)"
+ " (or to the input level, if orig_inten is set FALSE)"
+ " Works on both Grayscale and RGB image with/without alpha channel."
+ " (the image with the dst_drawable is converted to RGB if necessary)"
+ " The sample_drawable should be of type RGB or RGBA";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Colorize image using a sample image as a guide"),
+ help_string,
+ "Wolfgang Hofer",
+ "hof@hotbot.com",
+ "02/2000",
+ N_("_Sample Colorize..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Map");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ const gchar *env;
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ env = g_getenv ("SAMPLE_COLORIZE_DEBUG");
+ if (env != NULL && (*env != 'n') && (*env != 'N'))
+ g_Sdebug = TRUE;
+
+ if (g_Sdebug)
+ g_printf ("sample colorize run\n");
+ g_show_progress = FALSE;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_PROC, &g_values);
+ if (g_values.sample_id == SMP_GRADIENT ||
+ g_values.sample_id == SMP_INV_GRADIENT)
+ g_values.sample_id = -1;
+
+ /* fix value */
+ g_values.tol_col_err = 5.5;
+
+ /* Get the specified dst_drawable */
+ g_values.dst_id = drawable_id;
+
+ clear_tables ();
+
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ smp_dialog ();
+ free_colors ();
+ gimp_set_data (PLUG_IN_PROC, &g_values, sizeof (t_values));
+ gimp_displays_flush ();
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams == NUMBER_IN_ARGS)
+ {
+ g_values.sample_id = param[3].data.d_drawable;
+ g_values.hold_inten = param[4].data.d_int32;
+ g_values.orig_inten = param[5].data.d_int32;
+ g_values.rnd_subcolors = param[6].data.d_int32;
+ g_values.guess_missing = param[7].data.d_int32;
+ g_values.lvl_in_min = param[8].data.d_int32;
+ g_values.lvl_in_max = param[9].data.d_int32;
+ g_values.lvl_in_gamma = param[10].data.d_float;
+ g_values.lvl_out_min = param[11].data.d_int32;
+ g_values.lvl_out_max = param[12].data.d_int32;
+ if (main_colorize (MC_GET_SAMPLE_COLORS) >= 0)
+ {
+ main_colorize (MC_DST_REMAP);
+ status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* ============================================================================
+ * callback and constraint procedures for the dialog
+ * ============================================================================
+ */
+
+static void
+smp_response_callback (GtkWidget *widget,
+ gint response_id)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+
+ levels_update (ALL);
+ break;
+
+ case RESPONSE_GET_COLORS:
+ smp_get_colors (widget);
+ break;
+
+ case GTK_RESPONSE_APPLY:
+ g_show_progress = TRUE;
+ if (main_colorize (MC_DST_REMAP) == 0)
+ {
+ gimp_displays_flush ();
+ g_show_progress = FALSE;
+ return;
+ }
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (widget),
+ GTK_RESPONSE_APPLY, FALSE);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ gtk_main_quit ();
+ break;
+ }
+}
+
+static void
+smp_toggle_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gboolean *toggle_val = (gboolean *)data;
+
+ *toggle_val = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ if ((data == &g_di.sample_show_selection) ||
+ (data == &g_di.sample_show_color))
+ {
+ update_preview (&g_values.sample_id);
+ return;
+ }
+
+ if ((data == &g_di.dst_show_selection) ||
+ (data == &g_di.dst_show_color))
+ {
+ update_preview (&g_values.dst_id);
+ return;
+ }
+
+ if ((data == &g_values.hold_inten) ||
+ (data == &g_values.orig_inten) ||
+ (data == &g_values.rnd_subcolors))
+ {
+ if (g_di.orig_inten_button)
+ gtk_widget_set_sensitive (g_di.orig_inten_button,g_values.hold_inten);
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ }
+
+ if (data == &g_values.guess_missing)
+ {
+ if (g_values.guess_missing)
+ guess_missing_colors ();
+ else
+ fill_missing_colors ();
+ smp_get_colors (NULL);
+ }
+}
+
+static void
+smp_sample_combo_callback (GtkWidget *widget)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ g_values.sample_id = value;
+
+ if (value == SMP_GRADIENT || value == SMP_INV_GRADIENT)
+ {
+ get_gradient (value);
+ smp_get_colors (NULL);
+
+ if (g_di.sample_preview)
+ clear_preview (g_di.sample_preview);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ GTK_RESPONSE_APPLY, TRUE);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, FALSE);
+ }
+ else
+ {
+ update_preview (&g_values.sample_id);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, TRUE);
+ }
+}
+
+static void
+smp_dest_combo_callback (GtkWidget *widget)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &g_values.dst_id);
+
+ update_preview (&g_values.dst_id);
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ RESPONSE_GET_COLORS, TRUE);
+}
+
+static gint
+smp_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ if (image_id < 0)
+ return FALSE;
+
+ /* don't accept layers from indexed images */
+ if (gimp_drawable_is_indexed (drawable_id))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+smp_adj_lvl_in_max_upd_callback (GtkAdjustment *adjustment)
+{
+ gint32 value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 1, 255);
+
+ if (value != g_values.lvl_in_max)
+ {
+ g_values.lvl_in_max = value;
+ upd_flags = INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_in_max < g_values.lvl_in_min)
+ {
+ g_values.lvl_in_min = g_values.lvl_in_max;
+ upd_flags |= LOW_INPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_adj_lvl_in_min_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0, 254);
+
+ if (value != g_values.lvl_in_min)
+ {
+ g_values.lvl_in_min = value;
+ upd_flags = INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_in_min > g_values.lvl_in_max)
+ {
+ g_values.lvl_in_max = g_values.lvl_in_min;
+ upd_flags |= HIGH_INPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_text_gamma_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0.1, 10.0);
+
+ if (value != g_values.lvl_in_gamma)
+ {
+ g_values.lvl_in_gamma = value;
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW | REFRESH_DST);
+ }
+}
+
+static void
+smp_adj_lvl_out_max_upd_callback (GtkAdjustment *adjustment)
+{
+ gint32 value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 1, 255);
+
+ if (value != g_values.lvl_out_max)
+ {
+ g_values.lvl_out_max = value;
+ upd_flags = OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_out_max < g_values.lvl_out_min)
+ {
+ g_values.lvl_out_min = g_values.lvl_out_max;
+ upd_flags |= LOW_OUTPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+static void
+smp_adj_lvl_out_min_upd_callback (GtkAdjustment *adjustment)
+{
+ double value;
+ gint upd_flags;
+
+ value = CLAMP ((gtk_adjustment_get_value (adjustment)), 0, 254);
+
+ if (value != g_values.lvl_out_min)
+ {
+ g_values.lvl_out_min = value;
+ upd_flags = OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW | REFRESH_DST;
+ if (g_values.lvl_out_min > g_values.lvl_out_max)
+ {
+ g_values.lvl_out_max = g_values.lvl_out_min;
+ upd_flags |= HIGH_OUTPUT;
+ }
+ levels_update (upd_flags);
+ }
+}
+
+/* ============================================================================
+ * DIALOG helper procedures
+ * (workers for the updates on the preview widgets)
+ * ============================================================================
+ */
+
+static void
+refresh_dst_preview (GtkWidget *preview,
+ guchar *src_buffer)
+{
+ guchar allrowsbuf[3 * PREVIEW_SIZE_X * PREVIEW_SIZE_Y];
+ guchar *ptr;
+ guchar *src_ptr;
+ guchar lum;
+ guchar maskbyte;
+ gint x, y;
+ gint preview_bpp;
+ gint src_bpp;
+
+ preview_bpp = PREVIEW_BPP;
+ src_bpp = PREVIEW_BPP +1; /* 3 colors + 1 maskbyte */
+ src_ptr = src_buffer;
+
+ ptr = allrowsbuf;
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ if ((maskbyte = src_ptr[3]) == 0)
+ {
+ ptr[0] = src_ptr[0];
+ ptr[1] = src_ptr[1];
+ ptr[2] = src_ptr[2];
+ }
+ else
+ {
+ if (g_di.dst_show_color)
+ {
+ remap_pixel (ptr, src_ptr, 3);
+ }
+ else
+ {
+ /* lum = g_out_trans_tab[g_lvl_trans_tab[LUMINOSITY_1(src_ptr)]]; */
+ /* get brightness from (uncolorized) original */
+
+ lum = g_lvl_trans_tab[LUMINOSITY_1 (src_ptr)];
+ /* get brightness from (uncolorized) original */
+
+ ptr[0] = lum;
+ ptr[1] = lum;
+ ptr[2] = lum;
+ }
+
+ if (maskbyte < 255)
+ {
+ ptr[0] = MIX_CHANNEL (ptr[0], src_ptr[0], maskbyte);
+ ptr[1] = MIX_CHANNEL (ptr[1], src_ptr[1], maskbyte);
+ ptr[2] = MIX_CHANNEL (ptr[2], src_ptr[2], maskbyte);
+ }
+ }
+ ptr += preview_bpp;
+ src_ptr += src_bpp;
+ }
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGB_IMAGE,
+ allrowsbuf,
+ PREVIEW_SIZE_X * 3);
+}
+
+static void
+clear_preview (GtkWidget *preview)
+{
+ gimp_preview_area_fill (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ 170, 170, 170);
+}
+
+static void
+update_pv (GtkWidget *preview,
+ gboolean show_selection,
+ t_GDRW *gdrw,
+ guchar *dst_buffer,
+ gboolean is_color)
+{
+ guchar allrowsbuf[4 * PREVIEW_SIZE_X * PREVIEW_SIZE_Y];
+ guchar pixel[4];
+ guchar *ptr;
+ gint x, y;
+ gint x2, y2;
+ gint ofx, ofy;
+ gint sel_width, sel_height;
+ double scale_x, scale_y;
+ guchar *buf_ptr;
+ guchar dummy[4];
+ guchar maskbytes[4];
+ gint dstep;
+ guchar alpha;
+
+
+ if (!preview)
+ return;
+
+ /* init gray pixel (if we are called without a sourceimage (gdwr == NULL) */
+ pixel[0] = pixel[1] =pixel[2] = pixel[3] = 127;
+
+ /* calculate scale factors and offsets */
+ if (show_selection)
+ {
+ sel_width = gdrw->x2 - gdrw->x1;
+ sel_height = gdrw->y2 - gdrw->y1;
+
+ if (sel_height > sel_width)
+ {
+ scale_y = (gfloat) sel_height / PREVIEW_SIZE_Y;
+ scale_x = scale_y;
+ ofx = (gdrw->x1 +
+ ((sel_width - (PREVIEW_SIZE_X * scale_x)) / 2));
+ ofy = gdrw->y1;
+ }
+ else
+ {
+ scale_x = (gfloat) sel_width / PREVIEW_SIZE_X;
+ scale_y = scale_x;
+ ofx = gdrw->x1;
+ ofy = (gdrw->y1 +
+ ((sel_height - (PREVIEW_SIZE_Y * scale_y)) / 2));
+ }
+ }
+ else
+ {
+ if (gdrw->height > gdrw->width)
+ {
+ scale_y = (gfloat) gdrw->height / PREVIEW_SIZE_Y;
+ scale_x = scale_y;
+ ofx = (gdrw->width - (PREVIEW_SIZE_X * scale_x)) / 2;
+ ofy = 0;
+ }
+ else
+ {
+ scale_x = (gfloat) gdrw->width / PREVIEW_SIZE_X;
+ scale_y = scale_x;
+ ofx = 0;
+ ofy = (gdrw->height - (PREVIEW_SIZE_Y * scale_y)) / 2;
+ }
+ }
+
+ /* check if output goes to previw widget or to dst_buffer */
+ if (dst_buffer)
+ {
+ buf_ptr = dst_buffer;
+ dstep = PREVIEW_BPP +1;
+ }
+ else
+ {
+ buf_ptr = &dummy[0];
+ dstep = 0;
+ }
+
+
+ /* render preview */
+ ptr = allrowsbuf;
+ for (y = 0; y < PREVIEW_SIZE_Y; y++)
+ {
+ for (x = 0; x < PREVIEW_SIZE_X; x++)
+ {
+ if (gdrw->drawable_id > 0)
+ {
+ x2 = ofx + (x * scale_x);
+ y2 = ofy + (y * scale_y);
+ get_pixel (gdrw, x2, y2, &pixel[0]);
+ if (gdrw->sel_gdrw)
+ {
+ get_pixel (gdrw->sel_gdrw,
+ x2 + gdrw->seldeltax,
+ y2 + gdrw->seldeltay,
+ &maskbytes[0]);
+ }
+ else
+ {
+ maskbytes[0] = 255;
+ }
+ }
+
+ alpha = pixel[gdrw->index_alpha];
+ if (is_color && (gdrw->bpp > 2))
+ {
+ buf_ptr[0] = ptr[0] = pixel[0];
+ buf_ptr[1] = ptr[1] = pixel[1];
+ buf_ptr[2] = ptr[2] = pixel[2];
+ }
+ else
+ {
+ if (gdrw->bpp > 2)
+ *ptr = LUMINOSITY_1 (pixel);
+ else
+ *ptr = pixel[0];
+
+ *buf_ptr = *ptr;
+ buf_ptr[1] = ptr[1] = *ptr;
+ buf_ptr[2] = ptr[2] = *ptr;
+ }
+ if (gdrw->index_alpha == 0) /* has no alpha channel */
+ buf_ptr[3] = ptr[3] = 255;
+ else
+ buf_ptr[3] = ptr[3] = MIN (maskbytes[0], alpha);
+ buf_ptr += dstep; /* advance (or stay at dummy byte) */
+ ptr += 4;
+ }
+
+ }
+
+ if (dst_buffer == NULL)
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEW_SIZE_X, PREVIEW_SIZE_Y,
+ GIMP_RGBA_IMAGE,
+ allrowsbuf,
+ PREVIEW_SIZE_X * 4);
+ gtk_widget_queue_draw (preview);
+ }
+}
+
+static void
+update_preview (gint32 *id_ptr)
+{
+ t_GDRW gdrw;
+ gboolean drawable = FALSE;
+
+ if (g_Sdebug)
+ g_printf ("UPD PREVIEWS ID:%d ENABLE_UPD:%d\n",
+ id_ptr ? (int) *id_ptr : -1, (int)g_di.enable_preview_update);
+
+ if (id_ptr == NULL || !g_di.enable_preview_update)
+ return;
+ if (is_layer_alive (*id_ptr) < 0)
+ {
+ /* clear preview on invalid drawable id
+ * (SMP_GRADIENT and SMP_INV_GRADIENT)
+ */
+ if (id_ptr == &g_values.sample_id)
+ clear_preview (g_di.sample_preview);
+ if (id_ptr == &g_values.dst_id)
+ clear_preview (g_di.dst_preview);
+ return;
+ }
+
+ if (id_ptr == &g_values.sample_id)
+ {
+ drawable = TRUE;
+
+ init_gdrw (&gdrw, *id_ptr, FALSE);
+ update_pv (g_di.sample_preview, g_di.sample_show_selection, &gdrw,
+ NULL, g_di.sample_show_color);
+ }
+ else if (id_ptr == &g_values.dst_id)
+ {
+ drawable = TRUE;
+
+ init_gdrw (&gdrw, *id_ptr, FALSE);
+ update_pv (g_di.dst_preview, g_di.dst_show_selection, &gdrw,
+ &g_dst_preview_buffer[0], g_di.dst_show_color);
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ }
+
+ if (drawable)
+ end_gdrw (&gdrw);
+}
+
+static void
+levels_draw_slider (cairo_t *cr,
+ GdkColor *border_color,
+ GdkColor *fill_color,
+ gint xpos)
+{
+ cairo_move_to (cr, xpos, 0);
+ cairo_line_to (cr, xpos - (CONTROL_HEIGHT - 1) / 2, CONTROL_HEIGHT - 1);
+ cairo_line_to (cr, xpos + (CONTROL_HEIGHT - 1) / 2, CONTROL_HEIGHT - 1);
+ cairo_line_to (cr, xpos, 0);
+
+ gdk_cairo_set_source_color (cr, fill_color);
+ cairo_fill_preserve (cr);
+
+ gdk_cairo_set_source_color (cr, border_color);
+ cairo_stroke (cr);
+}
+
+static void
+smp_get_colors (GtkWidget *dialog)
+{
+ gint i;
+ guchar buffer[3 * DA_WIDTH * GRADIENT_HEIGHT];
+
+ update_preview (&g_values.sample_id);
+
+ if (dialog && main_colorize (MC_GET_SAMPLE_COLORS) >= 0) /* do not colorize, just analyze sample colors */
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (g_di.dialog),
+ GTK_RESPONSE_APPLY, TRUE);
+ for (i = 0; i < GRADIENT_HEIGHT; i++)
+ memcpy (buffer + i * 3 * DA_WIDTH, g_sample_color_tab, 3 * DA_WIDTH);
+
+ update_preview (&g_values.dst_id);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (g_di.sample_colortab_preview),
+ 0, 0, DA_WIDTH, GRADIENT_HEIGHT,
+ GIMP_RGB_IMAGE,
+ buffer,
+ DA_WIDTH * 3);
+}
+
+
+static void
+levels_update (gint update)
+{
+ gint i;
+
+ if (g_Sdebug)
+ g_printf ("levels_update: update reques %x\n", update);
+
+ /* Recalculate the transfer array */
+ calculate_level_transfers ();
+ if (update & REFRESH_DST)
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+
+ /* update the spinbutton entry widgets */
+ if (update & LOW_INPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_min,
+ g_values.lvl_in_min);
+ if (update & GAMMA)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_gamma,
+ g_values.lvl_in_gamma);
+ if (update & HIGH_INPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_in_max,
+ g_values.lvl_in_max);
+ if (update & LOW_OUTPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_out_min,
+ g_values.lvl_out_min);
+ if (update & HIGH_OUTPUT)
+ gtk_adjustment_set_value (g_di.adj_lvl_out_max,
+ g_values.lvl_out_max);
+ if (update & INPUT_LEVELS)
+ {
+ guchar buffer[DA_WIDTH * GRADIENT_HEIGHT];
+ for (i = 0; i < GRADIENT_HEIGHT; i++)
+ memcpy (buffer + DA_WIDTH * i, g_lvl_trans_tab, DA_WIDTH);
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (g_di.in_lvl_gray_preview),
+ 0, 0, DA_WIDTH, GRADIENT_HEIGHT,
+ GIMP_GRAY_IMAGE,
+ buffer,
+ DA_WIDTH);
+ }
+
+ if (update & INPUT_SLIDERS)
+ {
+ GtkStyle *style = gtk_widget_get_style (g_di.in_lvl_drawarea);
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (g_di.in_lvl_drawarea));
+ gdouble width, mid, tmp;
+
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 1.0);
+
+ g_di.slider_pos[0] = DA_WIDTH * ((double) g_values.lvl_in_min / 255.0);
+ g_di.slider_pos[2] = DA_WIDTH * ((double) g_values.lvl_in_max / 255.0);
+
+ width = (double) (g_di.slider_pos[2] - g_di.slider_pos[0]) / 2.0;
+ mid = g_di.slider_pos[0] + width;
+ tmp = log10 (1.0 / g_values.lvl_in_gamma);
+ g_di.slider_pos[1] = (int) (mid + width * tmp + 0.5);
+
+ levels_draw_slider (cr,
+ &style->black,
+ &style->dark[GTK_STATE_NORMAL],
+ g_di.slider_pos[1]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[0]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->white,
+ g_di.slider_pos[2]);
+
+ cairo_destroy (cr);
+ }
+
+ if (update & OUTPUT_SLIDERS)
+ {
+ GtkStyle *style = gtk_widget_get_style (g_di.sample_drawarea);
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (g_di.sample_drawarea));
+
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_paint (cr);
+
+ cairo_translate (cr, 0.5, 0.5);
+ cairo_set_line_width (cr, 1.0);
+
+ g_di.slider_pos[3] = DA_WIDTH * ((double) g_values.lvl_out_min / 255.0);
+ g_di.slider_pos[4] = DA_WIDTH * ((double) g_values.lvl_out_max / 255.0);
+
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[3]);
+ levels_draw_slider (cr,
+ &style->black,
+ &style->black,
+ g_di.slider_pos[4]);
+
+ cairo_destroy (cr);
+ }
+}
+
+static gboolean
+level_in_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gdouble width, mid, tmp;
+ gint x, distance;
+ gint i;
+ gint update = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_EXPOSE\n");
+ if (widget == g_di.in_lvl_drawarea)
+ levels_update (INPUT_SLIDERS);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_BUTTON_PRESS\n");
+ gtk_grab_add (widget);
+ bevent = (GdkEventButton *) event;
+
+ distance = G_MAXINT;
+ for (i = 0; i < 3; i++)
+ {
+ if (fabs (bevent->x - g_di.slider_pos[i]) < distance)
+ {
+ g_di.active_slider = i;
+ distance = fabs (bevent->x - g_di.slider_pos[i]);
+ }
+ }
+ x = bevent->x;
+ update = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_BUTTON_RELEASE\n");
+ gtk_grab_remove (widget);
+ switch (g_di.active_slider)
+ {
+ case 0: /* low input */
+ levels_update (LOW_INPUT | GAMMA | DRAW);
+ break;
+
+ case 1: /* gamma */
+ levels_update (GAMMA);
+ break;
+
+ case 2: /* high input */
+ levels_update (HIGH_INPUT | GAMMA | DRAW);
+ break;
+ }
+
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (g_Sdebug)
+ g_printf ("EVENT: GDK_MOTION_NOTIFY\n");
+ mevent = (GdkEventMotion *) event;
+ x = mevent->x;
+ gdk_event_request_motions (mevent);
+ update = TRUE;
+ break;
+
+ default:
+ if (g_Sdebug)
+ g_printf ("EVENT: default\n");
+ break;
+ }
+
+ if (update)
+ {
+ if (g_Sdebug)
+ g_printf ("EVENT: ** update **\n");
+ switch (g_di.active_slider)
+ {
+ case 0: /* low input */
+ g_values.lvl_in_min = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_in_min = CLAMP (g_values.lvl_in_min,
+ 0, g_values.lvl_in_max);
+ break;
+
+ case 1: /* gamma */
+ width = (double) (g_di.slider_pos[2] - g_di.slider_pos[0]) / 2.0;
+ mid = g_di.slider_pos[0] + width;
+
+ x = CLAMP (x, g_di.slider_pos[0], g_di.slider_pos[2]);
+ tmp = (double) (x - mid) / width;
+ g_values.lvl_in_gamma = 1.0 / pow (10, tmp);
+
+ /* round the gamma value to the nearest 1/100th */
+ g_values.lvl_in_gamma =
+ floor (g_values.lvl_in_gamma * 100 + 0.5) / 100.0;
+ break;
+
+ case 2: /* high input */
+ g_values.lvl_in_max = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_in_max = CLAMP (g_values.lvl_in_max,
+ g_values.lvl_in_min, 255);
+ break;
+ }
+
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+level_out_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gint x, distance;
+ gint i;
+ gint update = FALSE;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_EXPOSE\n");
+ if (widget == g_di.sample_drawarea)
+ levels_update (OUTPUT_SLIDERS);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_BUTTON_PRESS\n");
+ bevent = (GdkEventButton *) event;
+
+ distance = G_MAXINT;
+ for (i = 3; i < 5; i++)
+ {
+ if (fabs (bevent->x - g_di.slider_pos[i]) < distance)
+ {
+ g_di.active_slider = i;
+ distance = fabs (bevent->x - g_di.slider_pos[i]);
+ }
+ }
+
+ x = bevent->x;
+ update = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_BUTTON_RELEASE\n");
+ switch (g_di.active_slider)
+ {
+ case 3: /* low output */
+ levels_update (LOW_OUTPUT | DRAW);
+ break;
+
+ case 4: /* high output */
+ levels_update (HIGH_OUTPUT | DRAW);
+ break;
+ }
+
+ refresh_dst_preview (g_di.dst_preview, &g_dst_preview_buffer[0]);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: GDK_MOTION_NOTIFY\n");
+ mevent = (GdkEventMotion *) event;
+ x = mevent->x;
+ gdk_event_request_motions (mevent);
+ update = TRUE;
+ break;
+
+ default:
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: default\n");
+ break;
+ }
+
+ if (update)
+ {
+ if (g_Sdebug)
+ g_printf ("OUT_EVENT: ** update **\n");
+ switch (g_di.active_slider)
+ {
+ case 3: /* low output */
+ g_values.lvl_out_min = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_out_min = CLAMP (g_values.lvl_out_min,
+ 0, g_values.lvl_out_max);
+ break;
+
+ case 4: /* high output */
+ g_values.lvl_out_max = ((double) x / (double) DA_WIDTH) * 255.0;
+ g_values.lvl_out_max = CLAMP (g_values.lvl_out_max,
+ g_values.lvl_out_min, 255);
+ break;
+ }
+
+ levels_update (OUTPUT_SLIDERS | OUTPUT_LEVELS | DRAW);
+ }
+
+ return FALSE;
+}
+
+
+/* ============================================================================
+ * smp_dialog
+ * The Interactive Dialog
+ * ============================================================================
+ */
+static void
+smp_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *vbox2;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *check_button;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *spinbutton;
+ GtkObject *data;
+ gint ty;
+
+ /* set flags for check buttons from mode value bits */
+ if (g_Sdebug)
+ g_print ("smp_dialog START\n");
+
+ /* init some dialog variables */
+ g_di.enable_preview_update = FALSE;
+ g_di.sample_show_selection = TRUE;
+ g_di.dst_show_selection = TRUE;
+ g_di.dst_show_color = TRUE;
+ g_di.sample_show_color = TRUE;
+ g_di.orig_inten_button = NULL;
+
+ /* Init GTK */
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /* Main Dialog */
+ g_di.dialog = dialog =
+ gimp_dialog_new (_("Sample Colorize"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("Get _Sample Colors"), RESPONSE_GET_COLORS,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+ _("_Apply"), GTK_RESPONSE_APPLY,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_GET_COLORS,
+ RESPONSE_RESET,
+ GTK_RESPONSE_APPLY,
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (smp_response_callback),
+ NULL);
+
+ /* table for values */
+ table = gtk_table_new (7, 5, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ table, TRUE, TRUE, 0);
+
+ ty = 0;
+ /* layer combo_box (Dst) */
+ label = gtk_label_new (_("Destination:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, ty, ty + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_layer_combo_box_new (smp_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), g_values.dst_id,
+ G_CALLBACK (smp_dest_combo_callback),
+ NULL);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, ty, ty + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ /* layer combo_box (Sample) */
+ label = gtk_label_new (_("Sample:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 3, 4, ty, ty + 1,
+ GTK_FILL, GTK_FILL, 4, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_layer_combo_box_new (smp_constrain, NULL);
+
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, SMP_INV_GRADIENT,
+ GIMP_INT_STORE_LABEL, _("From reverse gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, SMP_GRADIENT,
+ GIMP_INT_STORE_LABEL, _("From gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), g_values.sample_id,
+ G_CALLBACK (smp_sample_combo_callback),
+ NULL);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 4, 5, ty, ty + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ ty++;
+
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty + 1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Sho_w selection"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.dst_show_selection);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.dst_show_selection);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show co_lor"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.dst_show_color);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.dst_show_color);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty + 1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show selec_tion"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.sample_show_selection);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.sample_show_selection);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Show c_olor"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_di.sample_show_color);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_di.sample_show_color);
+
+ ty++;
+
+ /* Preview (Dst) */
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 0, 2, ty, ty + 1, 0, 0, 0, 0);
+ gtk_widget_show (frame);
+
+ g_di.dst_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.dst_preview,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), g_di.dst_preview);
+ gtk_widget_show (g_di.dst_preview);
+
+ /* Preview (Sample)*/
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 3, 5, ty, ty + 1, 0, 0, 0, 0);
+ gtk_widget_show (frame);
+
+ g_di.sample_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.sample_preview,
+ PREVIEW_SIZE_X, PREVIEW_SIZE_Y);
+ gtk_container_add (GTK_CONTAINER (frame), g_di.sample_preview);
+ gtk_widget_show (g_di.sample_preview);
+
+ ty++;
+
+ /* The levels graylevel prevev */
+ frame = gtk_frame_new (NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 0, 2, ty, ty + 1, 0, 0, 0, 0);
+
+ g_di.in_lvl_gray_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.in_lvl_gray_preview,
+ DA_WIDTH, GRADIENT_HEIGHT);
+ gtk_widget_set_events (g_di.in_lvl_gray_preview, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.in_lvl_gray_preview, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.in_lvl_gray_preview);
+
+ g_signal_connect (g_di.in_lvl_gray_preview, "event",
+ G_CALLBACK (level_in_events),
+ NULL);
+
+ /* The levels drawing area */
+ g_di.in_lvl_drawarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (g_di.in_lvl_drawarea,
+ DA_WIDTH, CONTROL_HEIGHT);
+ gtk_widget_set_events (g_di.in_lvl_drawarea, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.in_lvl_drawarea, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.in_lvl_drawarea);
+
+ g_signal_connect (g_di.in_lvl_drawarea, "event",
+ G_CALLBACK (level_in_events),
+ NULL);
+
+ gtk_widget_show (vbox2);
+ gtk_widget_show (frame);
+
+ /* The sample_colortable prevev */
+ frame = gtk_frame_new (NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox2);
+ gtk_table_attach (GTK_TABLE (table),
+ frame, 3, 5, ty, ty + 1, 0, 0, 0, 0);
+
+ g_di.sample_colortab_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (g_di.sample_colortab_preview,
+ DA_WIDTH, GRADIENT_HEIGHT);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.sample_colortab_preview, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.sample_colortab_preview);
+
+ /* The levels drawing area */
+ g_di.sample_drawarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (g_di.sample_drawarea,
+ DA_WIDTH, CONTROL_HEIGHT);
+ gtk_widget_set_events (g_di.sample_drawarea, LEVELS_DA_MASK);
+ gtk_box_pack_start (GTK_BOX (vbox2), g_di.sample_drawarea, FALSE, TRUE, 0);
+ gtk_widget_show (g_di.sample_drawarea);
+
+ g_signal_connect (g_di.sample_drawarea, "event",
+ G_CALLBACK (level_out_events),
+ NULL);
+
+ gtk_widget_show (vbox2);
+ gtk_widget_show (frame);
+
+
+ ty++;
+
+ /* Horizontal box for INPUT levels text widget */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new (_("Input levels:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* min input spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_min, 0.0, 254.0, 1, 10, 0);
+ g_di.adj_lvl_in_min = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_min, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_min, "value-changed",
+ G_CALLBACK (smp_adj_lvl_in_min_upd_callback),
+ &g_di);
+
+ /* input gamma spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_gamma, 0.1, 10.0, 0.02, 0.2, 0);
+ g_di.adj_lvl_in_gamma = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_gamma, 0.5, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_gamma, "value-changed",
+ G_CALLBACK (smp_text_gamma_upd_callback),
+ &g_di);
+
+ /* high input spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_in_max, 1.0, 255.0, 1, 10, 0);
+ g_di.adj_lvl_in_max = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_in_max, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_in_max, "value-changed",
+ G_CALLBACK (smp_adj_lvl_in_max_upd_callback),
+ &g_di);
+
+ gtk_widget_show (hbox);
+
+ /* Horizontal box for OUTPUT levels text widget */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+
+ label = gtk_label_new (_("Output levels:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* min output spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_out_min, 0.0, 254.0, 1, 10, 0);
+ g_di.adj_lvl_out_min = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_out_min, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_out_min, "value-changed",
+ G_CALLBACK (smp_adj_lvl_out_min_upd_callback),
+ &g_di);
+
+ /* high output spinbutton */
+ data = gtk_adjustment_new ((gfloat)g_values.lvl_out_max, 0.0, 255.0, 1, 10, 0);
+ g_di.adj_lvl_out_max = GTK_ADJUSTMENT (data);
+
+ spinbutton = gimp_spin_button_new (g_di.adj_lvl_out_max, 0.5, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (g_di.adj_lvl_out_max, "value-changed",
+ G_CALLBACK (smp_adj_lvl_out_max_upd_callback),
+ &g_di);
+
+ gtk_widget_show (hbox);
+
+ ty++;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), hbox, 0, 2, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Hold _intensity"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.hold_inten);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.hold_inten);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Original i_ntensity"));
+ g_di.orig_inten_button = check_button;
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive (g_di.orig_inten_button, g_values.hold_inten);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.orig_inten);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.orig_inten);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_table_attach (GTK_TABLE (table), hbox, 3, 5, ty, ty+1,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (hbox);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("Us_e subcolors"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.rnd_subcolors);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.rnd_subcolors);
+
+ /* check button */
+ check_button = gtk_check_button_new_with_mnemonic (_("S_mooth samples"));
+ gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, FALSE, 0);
+ gtk_widget_show (check_button);
+
+ g_signal_connect (check_button, "toggled",
+ G_CALLBACK (smp_toggle_callback),
+ &g_values.guess_missing);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button),
+ g_values.guess_missing);
+
+ ty++;
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ /* set old_id's different (to force updates of the previews) */
+ g_di.enable_preview_update = TRUE;
+ smp_get_colors (NULL);
+ update_preview (&g_values.dst_id);
+ levels_update (INPUT_SLIDERS | INPUT_LEVELS | DRAW);
+
+ gtk_main ();
+}
+
+/* -----------------------------
+ * DEBUG print procedures START
+ * -----------------------------
+ */
+
+static void
+print_ppm (const gchar *ppm_name)
+{
+ FILE *fp;
+ gint idx;
+ gint cnt;
+ gint r;
+ gint g;
+ gint b;
+ t_samp_color_elem *col_ptr;
+
+ if (ppm_name == NULL)
+ return;
+
+ fp = g_fopen (ppm_name, "w");
+ if (fp)
+ {
+ fprintf (fp, "P3\n# CREATOR: Gimp sample coloros\n256 256\n255\n");
+ for (idx = 0; idx < 256; idx++)
+ {
+ col_ptr = g_lum_tab[idx].col_ptr;
+
+ for (cnt = 0; cnt < 256; cnt++)
+ {
+ r = g = b = 0;
+ if (col_ptr)
+ {
+ if ((col_ptr->sum_color > 0) && (cnt != 20))
+ {
+ r = (gint) col_ptr->color[0];
+ g = (gint) col_ptr->color[1];
+ b = (gint) col_ptr->color[2];
+ }
+
+ if (cnt > 20)
+ col_ptr = col_ptr->next;
+ }
+ fprintf (fp, "%d %d %d\n", r, g, b);
+ }
+ }
+ fclose (fp);
+ }
+}
+
+static void
+print_color_list (FILE *fp,
+ t_samp_color_elem *col_ptr)
+{
+ if (fp == NULL)
+ return;
+
+ while (col_ptr)
+ {
+ fprintf (fp, " RGBA: %03d %03d %03d %03d sum: [%d]\n",
+ (gint)col_ptr->color[0],
+ (gint)col_ptr->color[1],
+ (gint)col_ptr->color[2],
+ (gint)col_ptr->color[3],
+ (gint)col_ptr->sum_color);
+
+ col_ptr = col_ptr->next;
+ }
+}
+
+static void
+print_table (FILE *fp)
+{
+ gint idx;
+
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "---------------------------\n");
+ fprintf (fp, "print_table\n");
+ fprintf (fp, "---------------------------\n");
+
+ for (idx = 0; idx < 256; idx++)
+ {
+ fprintf (fp, "LUM [%03d] pixcount:%d\n",
+ idx, (int)g_lum_tab[idx].all_samples);
+ print_color_list (fp, g_lum_tab[idx].col_ptr);
+ }
+}
+
+static void
+print_transtable (FILE *fp)
+{
+ gint idx;
+
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "---------------------------\n");
+ fprintf (fp, "print_transtable\n");
+ fprintf (fp, "---------------------------\n");
+
+ for (idx = 0; idx < 256; idx++)
+ {
+ fprintf (fp, "LVL_TRANS [%03d] in_lvl: %3d out_lvl: %3d\n",
+ idx, (int)g_lvl_trans_tab[idx], (int)g_out_trans_tab[idx]);
+ }
+}
+
+static void
+print_values (FILE *fp)
+{
+ if (fp == NULL)
+ return;
+
+ fprintf (fp, "sample_colorize: params\n");
+ fprintf (fp, "g_values.hold_inten :%d\n", (int)g_values.hold_inten);
+ fprintf (fp, "g_values.orig_inten :%d\n", (int)g_values.orig_inten);
+ fprintf (fp, "g_values.rnd_subcolors :%d\n", (int)g_values.rnd_subcolors);
+ fprintf (fp, "g_values.guess_missing :%d\n", (int)g_values.guess_missing);
+ fprintf (fp, "g_values.lvl_in_min :%d\n", (int)g_values.lvl_in_min);
+ fprintf (fp, "g_values.lvl_in_max :%d\n", (int)g_values.lvl_in_max);
+ fprintf (fp, "g_values.lvl_in_gamma :%f\n", g_values.lvl_in_gamma);
+ fprintf (fp, "g_values.lvl_out_min :%d\n", (int)g_values.lvl_out_min);
+ fprintf (fp, "g_values.lvl_out_max :%d\n", (int)g_values.lvl_out_max);
+
+ fprintf (fp, "g_values.tol_col_err :%f\n", g_values.tol_col_err);
+}
+
+/* -----------------------------
+ * DEBUG print procedures END
+ * -----------------------------
+ */
+
+/* DEBUG: read values from file */
+static void
+get_filevalues (void)
+{
+ FILE *fp;
+ gchar buf[1000];
+
+/*
+ g_values.lvl_out_min = 0;
+ g_values.lvl_out_max = 255;
+ g_values.lvl_in_min = 0;
+ g_values.lvl_in_max = 255;
+ g_values.lvl_in_gamma = 1.0;
+*/
+ g_values.tol_col_err = 5.5;
+
+ fp = g_fopen ("sample_colorize.values", "r");
+ if (fp != NULL)
+ {
+ fgets (buf, 999, fp);
+ sscanf (buf, "%f", &g_values.tol_col_err);
+ fclose (fp);
+ }
+
+ g_printf ("g_values.tol_col_err :%f\n", g_values.tol_col_err);
+}
+
+static gint32
+color_error (guchar ref_red, guchar ref_green, guchar ref_blue,
+ guchar cmp_red, guchar cmp_green, guchar cmp_blue)
+{
+ glong ff;
+ glong fs;
+ glong cmp_h, ref_h;
+
+ /* 1. Brightness differences */
+ cmp_h = (3 * cmp_red + 6 * cmp_green + cmp_blue) / 10;
+ ref_h = (3 * ref_red + 6 * ref_green + ref_blue) / 10;
+
+ fs = labs (ref_h - cmp_h);
+ ff = fs * fs;
+
+ /* 2. add Red Color differences */
+ fs = abs (ref_red - cmp_red);
+ ff += (fs * fs);
+
+ /* 3. add Green Color differences */
+ fs = abs (ref_green - cmp_green);
+ ff += (fs * fs);
+
+ /* 4. add Blue Color differences */
+ fs = abs (ref_blue - cmp_blue);
+ ff += (fs * fs);
+
+ return ((gint32)(ff));
+}
+
+/* get pixel value
+ * return light gray transparent pixel if out of bounds
+ * (should occur in the previews only)
+ */
+static void
+get_pixel (t_GDRW *gdrw,
+ gint32 x,
+ gint32 y,
+ guchar *pixel)
+{
+ gegl_buffer_sample (gdrw->buffer, x, y, NULL, pixel, gdrw->format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+}
+
+/* clear table */
+static void
+clear_tables (void)
+{
+ guint i;
+
+ for (i = 0; i < 256; i++)
+ {
+ g_lum_tab[i].col_ptr = NULL;
+ g_lum_tab[i].all_samples = 0;
+ g_lvl_trans_tab[i] = i;
+ g_out_trans_tab[i] = i;
+ g_sample_color_tab[3 * i + 0] = i;
+ g_sample_color_tab[3 * i + 1] = i;
+ g_sample_color_tab[3 * i + 2] = i;
+ }
+}
+
+/* free all allocated sample colors in table g_lum_tab */
+static void
+free_colors (void)
+{
+ gint lum;
+ t_samp_color_elem *col_ptr;
+ t_samp_color_elem *next_ptr;
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = next_ptr)
+ {
+ next_ptr = (t_samp_color_elem *)col_ptr->next;
+ g_free (col_ptr);
+ }
+
+ g_lum_tab[lum].col_ptr = NULL;
+ g_lum_tab[lum].all_samples = 0;
+ }
+}
+
+/* setup lum transformer table according to input_levels, gamma and output levels
+ * (uses sam algorithm as GIMP Level Tool)
+ */
+static void
+calculate_level_transfers (void)
+{
+ double inten;
+ gint i;
+ gint in_min, in_max;
+ gint out_min, out_max;
+
+ if (g_values.lvl_in_max >= g_values.lvl_in_min)
+ {
+ in_max = g_values.lvl_in_max;
+ in_min = g_values.lvl_in_min;
+ }
+ else
+ {
+ in_max = g_values.lvl_in_min;
+ in_min = g_values.lvl_in_max;
+ }
+ if (g_values.lvl_out_max >= g_values.lvl_out_min)
+ {
+ out_max = g_values.lvl_out_max;
+ out_min = g_values.lvl_out_min;
+ }
+ else
+ {
+ out_max = g_values.lvl_out_min;
+ out_min = g_values.lvl_out_max;
+ }
+
+ /* Recalculate the levels arrays */
+ for (i = 0; i < 256; i++)
+ {
+ /* determine input intensity */
+ inten = (double) i / 255.0;
+ if (g_values.lvl_in_gamma != 0.0)
+ {
+ inten = pow (inten, (1.0 / g_values.lvl_in_gamma));
+ }
+ inten = (double) (inten * (in_max - in_min) + in_min);
+ inten = CLAMP (inten, 0.0, 255.0);
+ g_lvl_trans_tab[i] = (guchar) (inten + 0.5);
+
+ /* determine the output intensity */
+ inten = (double) i / 255.0;
+ inten = (double) (inten * (out_max - out_min) + out_min);
+ inten = CLAMP (inten, 0.0, 255.0);
+ g_out_trans_tab[i] = (guchar) (inten + 0.5);
+ }
+}
+
+
+
+/* alloc and init new col Element */
+static t_samp_color_elem *
+new_samp_color (const guchar *color)
+{
+ t_samp_color_elem *col_ptr;
+
+ col_ptr = g_new0 (t_samp_color_elem, 1);
+
+ memcpy (&col_ptr->color[0], color, 4);
+
+ col_ptr->sum_color = 1;
+ col_ptr->next = NULL;
+
+ return col_ptr;
+}
+
+
+/* store color in g_lum_tab */
+static void
+add_color (const guchar *color)
+{
+ gint32 lum;
+ t_samp_color_elem *col_ptr;
+
+ lum = LUMINOSITY_1(color);
+
+ g_lum_tab[lum].all_samples++;
+ g_lum_tab[lum].from_sample = TRUE;
+
+ /* check if exactly the same color is already in the list */
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ if ((color[0] == col_ptr->color[0]) &&
+ (color[1] == col_ptr->color[1]) &&
+ (color[2] == col_ptr->color[2]))
+ {
+ col_ptr->sum_color++;
+ return;
+ }
+ }
+
+ /* alloc and init element for the new color */
+ col_ptr = new_samp_color (color);
+
+ if (col_ptr != NULL)
+ {
+ /* add new color element as 1.st of the list */
+ col_ptr->next = g_lum_tab[lum].col_ptr;
+ g_lum_tab[lum].col_ptr = col_ptr;
+ }
+}
+
+/* sort Sublists (color) by descending sum_color in g_lum_tab
+ */
+static void
+sort_color (gint32 lum)
+{
+ t_samp_color_elem *col_ptr;
+ t_samp_color_elem *next_ptr;
+ t_samp_color_elem *prev_ptr;
+ t_samp_color_elem *sorted_col_ptr;
+ gint32 min;
+ gint32 min_next;
+
+ sorted_col_ptr = NULL;
+ min_next = 0;
+
+ while (g_lum_tab[lum].col_ptr != NULL)
+ {
+ min = min_next;
+ next_ptr = NULL;
+ prev_ptr = NULL;
+
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = next_ptr)
+ {
+ next_ptr = col_ptr->next;
+ if (col_ptr->sum_color > min)
+ {
+ /* check min value for next loop */
+ if ((col_ptr->sum_color < min_next) || (min == min_next))
+ {
+ min_next = col_ptr->sum_color;
+ }
+ prev_ptr = col_ptr;
+ }
+ else
+ {
+ /* add element at head of sorted list */
+ col_ptr->next = sorted_col_ptr;
+ sorted_col_ptr = col_ptr;
+
+ /* remove element from list */
+ if (prev_ptr == NULL)
+ {
+ g_lum_tab[lum].col_ptr = next_ptr; /* remove 1.st element */
+ }
+ else
+ {
+ prev_ptr->next = next_ptr;
+ }
+ }
+ }
+ }
+
+ g_lum_tab[lum].col_ptr = sorted_col_ptr;
+}
+
+static void
+cnt_same_sample_colortones (t_samp_color_elem *ref_ptr,
+ guchar *prev_color,
+ guchar *color_tone,
+ gint *csum)
+{
+ gint32 col_error, ref_error;
+ t_samp_color_elem *col_ptr;
+
+ ref_error = 0;
+ if (prev_color != NULL)
+ {
+ ref_error = color_error (ref_ptr->color[0], ref_ptr->color[1], ref_ptr->color[2],
+ prev_color[0], prev_color[1], prev_color[2]);
+ }
+
+ /* collect colors that are (nearly) the same */
+ for (col_ptr = ref_ptr->next;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ if (col_ptr->sum_color < 1)
+ continue;
+ col_error = color_error (ref_ptr->color[0], ref_ptr->color[1], ref_ptr->color[2],
+ col_ptr->color[0], col_ptr->color[1], col_ptr->color[2]);
+
+ if (col_error <= g_tol_col_err)
+ {
+ /* cout color of the same colortone */
+ *csum += col_ptr->sum_color;
+ /* mark the already checked color with negative sum_color value */
+ col_ptr->sum_color = 0 - col_ptr->sum_color;
+
+ if (prev_color != NULL)
+ {
+ col_error = color_error (col_ptr->color[0], col_ptr->color[1], col_ptr->color[2],
+ prev_color[0], prev_color[1], prev_color[2]);
+ if (col_error < ref_error)
+ {
+ /* use the color that is closest to prev_color */
+ memcpy (color_tone, &col_ptr->color[0], 3);
+ ref_error = col_error;
+ }
+ }
+ }
+ }
+}
+
+/* find the dominant colortones (out of all sample colors)
+ * for each available brightness intensity value.
+ * and store them in g_sample_color_tab
+ */
+static void
+ideal_samples (void)
+{
+ gint32 lum;
+ t_samp_color_elem *col_ptr;
+ guchar *color;
+
+ guchar color_tone[4];
+ guchar color_ideal[4];
+ gint csum, maxsum;
+
+ color = NULL;
+ for (lum = 0; lum < 256; lum++)
+ {
+ if (g_lum_tab[lum].col_ptr == NULL)
+ continue;
+
+ sort_color (lum);
+ col_ptr = g_lum_tab[lum].col_ptr;
+ memcpy (&color_ideal[0], &col_ptr->color[0], 3);
+
+ maxsum = 0;
+
+ /* collect colors that are (nearly) the same */
+ for (;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ csum = 0;
+ if (col_ptr->sum_color > 0)
+ {
+ memcpy (&color_tone[0], &col_ptr->color[0], 3);
+ cnt_same_sample_colortones (col_ptr, color,
+ &color_tone[0], &csum);
+ if (csum > maxsum)
+ {
+ maxsum = csum;
+ memcpy (&color_ideal[0], &color_tone[0], 3);
+ }
+ }
+ else
+ col_ptr->sum_color = abs (col_ptr->sum_color);
+ }
+
+ /* store ideal color and keep track of the color */
+ color = &g_sample_color_tab[3 * lum];
+ memcpy (color, &color_ideal[0], 3);
+ }
+}
+
+static void
+guess_missing_colors (void)
+{
+ gint32 lum;
+ gint32 idx;
+ gfloat div;
+
+ guchar lo_color[4];
+ guchar hi_color[4];
+ guchar new_color[4];
+
+ lo_color[0] = 0;
+ lo_color[1] = 0;
+ lo_color[2] = 0;
+ lo_color[3] = 255;
+
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ hi_color[3] = 255;
+
+ new_color[0] = 0;
+ new_color[1] = 0;
+ new_color[2] = 0;
+ new_color[3] = 255;
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ if ((g_lum_tab[lum].col_ptr == NULL) ||
+ (g_lum_tab[lum].from_sample == FALSE))
+ {
+ if (lum > 0)
+ {
+ for (idx = lum; idx < 256; idx++)
+ {
+ if ((g_lum_tab[idx].col_ptr != NULL) &&
+ g_lum_tab[idx].from_sample)
+ {
+ memcpy (&hi_color[0],
+ &g_sample_color_tab[3 * idx], 3);
+ break;
+ }
+ if (idx == 255)
+ {
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ break;
+ }
+ }
+
+ div = idx - (lum -1);
+ new_color[0] = lo_color[0] + ((float)(hi_color[0] - lo_color[0]) / div);
+ new_color[1] = lo_color[1] + ((float)(hi_color[1] - lo_color[1]) / div);
+ new_color[2] = lo_color[2] + ((float)(hi_color[2] - lo_color[2]) / div);
+
+/*
+ * printf ("LO: %03d %03d %03d HI: %03d %03d %03d NEW: %03d %03d %03d\n",
+ * (int)lo_color[0], (int)lo_color[1], (int)lo_color[2],
+ * (int)hi_color[0], (int)hi_color[1], (int)hi_color[2],
+ * (int)new_color[0], (int)new_color[1], (int)new_color[2]);
+ */
+
+ }
+ g_lum_tab[lum].col_ptr = new_samp_color (&new_color[0]);
+ g_lum_tab[lum].from_sample = FALSE;
+ memcpy (&g_sample_color_tab [3 * lum], &new_color[0], 3);
+ }
+ memcpy (&lo_color[0], &g_sample_color_tab [3 * lum], 3);
+ }
+}
+
+static void
+fill_missing_colors (void)
+{
+ gint32 lum;
+ gint32 idx;
+ gint32 lo_idx;
+
+ guchar lo_color[4];
+ guchar hi_color[4];
+ guchar new_color[4];
+
+ lo_color[0] = 0;
+ lo_color[1] = 0;
+ lo_color[2] = 0;
+ lo_color[3] = 255;
+
+ hi_color[0] = 255;
+ hi_color[1] = 255;
+ hi_color[2] = 255;
+ hi_color[3] = 255;
+
+ new_color[0] = 0;
+ new_color[1] = 0;
+ new_color[2] = 0;
+ new_color[3] = 255;
+
+ lo_idx = 0;
+ for (lum = 0; lum < 256; lum++)
+ {
+ if ((g_lum_tab[lum].col_ptr == NULL) ||
+ (g_lum_tab[lum].from_sample == FALSE))
+ {
+ if (lum > 0)
+ {
+ for (idx = lum; idx < 256; idx++)
+ {
+ if ((g_lum_tab[idx].col_ptr != NULL) &&
+ (g_lum_tab[idx].from_sample))
+ {
+ memcpy (&hi_color[0],
+ &g_sample_color_tab[3 * idx], 3);
+ break;
+ }
+
+ if (idx == 255)
+ {
+/*
+ * hi_color[0] = 255;
+ * hi_color[1] = 255;
+ * hi_color[2] = 255;
+ */
+ memcpy (&hi_color[0], &lo_color[0], 3);
+ break;
+ }
+ }
+
+ if ((lum > (lo_idx + ((idx - lo_idx ) / 2))) ||
+ (lo_idx == 0))
+ {
+ new_color[0] = hi_color[0];
+ new_color[1] = hi_color[1];
+ new_color[2] = hi_color[2];
+ }
+ else
+ {
+ new_color[0] = lo_color[0];
+ new_color[1] = lo_color[1];
+ new_color[2] = lo_color[2];
+ }
+ }
+
+ g_lum_tab[lum].col_ptr = new_samp_color (&new_color[0]);
+ g_lum_tab[lum].from_sample = FALSE;
+ memcpy (&g_sample_color_tab[3 * lum], &new_color[0], 3);
+ }
+ else
+ {
+ lo_idx = lum;
+ memcpy (&lo_color[0], &g_sample_color_tab[3 * lum], 3);
+ }
+ }
+}
+
+/* get 256 samples of active gradient (optional in inverse order) */
+static void
+get_gradient (gint mode)
+{
+ gchar *name;
+ gint n_f_samples;
+ gdouble *f_samples;
+ gdouble *f_samp; /* float samples */
+ gint lum;
+
+ free_colors ();
+
+ name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (name, 256 /* n_samples */,
+ mode == SMP_INV_GRADIENT,
+ &n_f_samples, &f_samples);
+
+ g_free (name);
+
+ for (lum = 0; lum < 256; lum++)
+ {
+ f_samp = &f_samples[lum * 4];
+
+ g_sample_color_tab[3 * lum + 0] = f_samp[0] * 255;
+ g_sample_color_tab[3 * lum + 1] = f_samp[1] * 255;
+ g_sample_color_tab[3 * lum + 2] = f_samp[2] * 255;
+
+ g_lum_tab[lum].col_ptr =
+ new_samp_color (&g_sample_color_tab[3 * lum]);
+ g_lum_tab[lum].from_sample = TRUE;
+ g_lum_tab[lum].all_samples = 1;
+ }
+
+ g_free (f_samples);
+}
+
+static gint32
+is_layer_alive (gint32 drawable_id)
+{
+ /* return -1 if layer has become invalid */
+ if (drawable_id < 0)
+ return -1;
+
+ if (gimp_item_get_image (drawable_id) < 0)
+ {
+ g_printf ("sample colorize: unknown layer_id %d (Image closed?)\n",
+ (int)drawable_id);
+ return -1;
+ }
+
+ return drawable_id;
+}
+
+static void
+end_gdrw (t_GDRW *gdrw)
+{
+ t_GDRW *sel_gdrw = (t_GDRW *) gdrw->sel_gdrw;
+
+ if (sel_gdrw && sel_gdrw->buffer)
+ {
+ g_object_unref (sel_gdrw->buffer);
+ sel_gdrw->buffer = NULL;
+ }
+
+ g_object_unref (gdrw->buffer);
+ gdrw->buffer = NULL;
+}
+
+static void
+init_gdrw (t_GDRW *gdrw,
+ gint32 drawable_id,
+ gboolean shadow)
+{
+ gint32 image_id;
+ gint32 sel_channel_id;
+ gint32 x1, x2, y1, y2;
+ gint offsetx, offsety;
+ gint w, h;
+ gint sel_offsetx, sel_offsety;
+ t_GDRW *sel_gdrw;
+ gint32 non_empty;
+
+ if (g_Sdebug)
+ g_printf ("\np_init_gdrw: drawable_ID: %d\n", drawable_id);
+
+ gdrw->drawable_id = drawable_id;
+
+ if (shadow)
+ gdrw->buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ else
+ gdrw->buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gdrw->width = gimp_drawable_width (drawable_id);
+ gdrw->height = gimp_drawable_height (drawable_id);
+ gdrw->tile_width = gimp_tile_width ();
+ gdrw->tile_height = gimp_tile_height ();
+ gdrw->shadow = shadow;
+ gdrw->seldeltax = 0;
+ gdrw->seldeltay = 0;
+ /* get offsets within the image */
+ gimp_drawable_offsets (gdrw->drawable_id, &offsetx, &offsety);
+
+ if (! gimp_drawable_mask_intersect (gdrw->drawable_id,
+ &gdrw->x1, &gdrw->y1, &w, &h))
+ return;
+
+ gdrw->x2 = gdrw->x1 + w;
+ gdrw->y2 = gdrw->y1 + h;
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ gdrw->format = babl_format ("R'G'B'A u8");
+ else
+ gdrw->format = babl_format ("R'G'B' u8");
+
+ gdrw->bpp = babl_format_get_bytes_per_pixel (gdrw->format);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ {
+ /* index of the alpha channelbyte {1|3} */
+ gdrw->index_alpha = gdrw->bpp -1;
+ }
+ else
+ {
+ gdrw->index_alpha = 0; /* there is no alpha channel */
+ }
+
+ image_id = gimp_item_get_image (gdrw->drawable_id);
+
+ /* check and see if we have a selection mask */
+ sel_channel_id = gimp_image_get_selection (image_id);
+
+ if (g_Sdebug)
+ {
+ g_printf ("init_gdrw: image_id %d sel_channel_id: %d\n",
+ (int)image_id, (int)sel_channel_id);
+ g_printf ("init_gdrw: BOUNDS x1: %d y1: %d x2:%d y2: %d\n",
+ (int)gdrw->x1, (int)gdrw->y1, (int)gdrw->x2,(int)gdrw->y2);
+ g_printf ("init_gdrw: OFFS x: %d y: %d\n",
+ (int)offsetx, (int)offsety );
+ }
+
+ gimp_selection_bounds (image_id, &non_empty, &x1, &y1, &x2, &y2);
+
+ if (non_empty && (sel_channel_id >= 0))
+ {
+ /* selection is TRUE */
+ sel_gdrw = g_new0 (t_GDRW, 1);
+ sel_gdrw->drawable_id = sel_channel_id;
+
+ sel_gdrw->buffer = gimp_drawable_get_buffer (sel_channel_id);
+ sel_gdrw->format = babl_format ("Y u8");
+
+ sel_gdrw->width = gimp_drawable_width (sel_channel_id);
+ sel_gdrw->height = gimp_drawable_height (sel_channel_id);
+
+ sel_gdrw->tile_width = gimp_tile_width ();
+ sel_gdrw->tile_height = gimp_tile_height ();
+ sel_gdrw->shadow = shadow;
+ sel_gdrw->x1 = x1;
+ sel_gdrw->y1 = y1;
+ sel_gdrw->x2 = x2;
+ sel_gdrw->y2 = y2;
+ sel_gdrw->seldeltax = 0;
+ sel_gdrw->seldeltay = 0;
+ sel_gdrw->bpp = babl_format_get_bytes_per_pixel (sel_gdrw->format);
+ sel_gdrw->index_alpha = 0; /* there is no alpha channel */
+ sel_gdrw->sel_gdrw = NULL;
+
+ /* offset delta between drawable and selection
+ * (selection always has image size and should always have offsets of 0 )
+ */
+ gimp_drawable_offsets (sel_channel_id, &sel_offsetx, &sel_offsety);
+ gdrw->seldeltax = offsetx - sel_offsetx;
+ gdrw->seldeltay = offsety - sel_offsety;
+
+ gdrw->sel_gdrw = (t_GDRW *) sel_gdrw;
+
+ if (g_Sdebug)
+ {
+ g_printf ("init_gdrw: SEL_BOUNDS x1: %d y1: %d x2:%d y2: %d\n",
+ (int)sel_gdrw->x1, (int)sel_gdrw->y1,
+ (int)sel_gdrw->x2, (int)sel_gdrw->y2);
+ g_printf ("init_gdrw: SEL_OFFS x: %d y: %d\n",
+ (int)sel_offsetx, (int)sel_offsety );
+ g_printf ("init_gdrw: SEL_DELTA x: %d y: %d\n",
+ (int)gdrw->seldeltax, (int)gdrw->seldeltay );
+ }
+ }
+ else
+ gdrw->sel_gdrw = NULL; /* selection is FALSE */
+}
+
+/* analyze the colors in the sample_drawable */
+static int
+sample_analyze (t_GDRW *sample_gdrw)
+{
+ gint32 sample_pixels;
+ gint32 row, col;
+ gint32 first_row, first_col, last_row, last_col;
+ gint32 x, y;
+ gint32 x2, y2;
+ float progress_step;
+ float progress_max;
+ float progress;
+ guchar color[4];
+ FILE *prot_fp;
+
+ sample_pixels = 0;
+
+ /* init progress */
+ progress_max = (sample_gdrw->x2 - sample_gdrw->x1);
+ progress_step = 1.0 / progress_max;
+ progress = 0.0;
+ if (g_show_progress)
+ gimp_progress_init (_("Sample analyze"));
+
+ prot_fp = NULL;
+ if (g_Sdebug)
+ prot_fp = g_fopen ("sample_colors.dump", "w");
+ print_values (prot_fp);
+
+ /* ------------------------------------------------
+ * foreach pixel in the SAMPLE_drawable:
+ * calculate brightness intensity LUM
+ * ------------------------------------------------
+ * the inner loops (x/y) are designed to process
+ * all pixels of one tile in the sample drawable, the outer loops (row/col) do step
+ * to the next tiles. (this was done to reduce tile swapping)
+ */
+
+ first_row = sample_gdrw->y1 / sample_gdrw->tile_height;
+ last_row = (sample_gdrw->y2 / sample_gdrw->tile_height);
+ first_col = sample_gdrw->x1 / sample_gdrw->tile_width;
+ last_col = (sample_gdrw->x2 / sample_gdrw->tile_width);
+
+ for (row = first_row; row <= last_row; row++)
+ {
+ for (col = first_col; col <= last_col; col++)
+ {
+ if (col == first_col)
+ x = sample_gdrw->x1;
+ else
+ x = col * sample_gdrw->tile_width;
+ if (col == last_col)
+ x2 = sample_gdrw->x2;
+ else
+ x2 = (col +1) * sample_gdrw->tile_width;
+
+ for ( ; x < x2; x++)
+ {
+ if (row == first_row)
+ y = sample_gdrw->y1;
+ else
+ y = row * sample_gdrw->tile_height;
+ if (row == last_row)
+ y2 = sample_gdrw->y2;
+ else
+ y2 = (row +1) * sample_gdrw->tile_height ;
+
+ /* printf ("X: %4d Y:%4d Y2:%4d\n", (int)x, (int)y, (int)y2); */
+
+ for ( ; y < y2; y++)
+ {
+ /* check if the pixel is in the selection */
+ if (sample_gdrw->sel_gdrw)
+ {
+ get_pixel (sample_gdrw->sel_gdrw,
+ (x + sample_gdrw->seldeltax),
+ (y + sample_gdrw->seldeltay),
+ &color[0]);
+
+ if (color[0] == 0)
+ continue;
+ }
+ get_pixel (sample_gdrw, x, y, &color[0]);
+
+ /* if this is a visible (non-transparent) pixel */
+ if ((sample_gdrw->index_alpha < 1) ||
+ (color[sample_gdrw->index_alpha] != 0))
+ {
+ /* store color in the sublists of g_lum_tab */
+ add_color (&color[0]);
+ sample_pixels++;
+ }
+ }
+
+ if (g_show_progress)
+ gimp_progress_update (progress += progress_step);
+ }
+ }
+ }
+
+ if (g_show_progress)
+ gimp_progress_update (1.0);
+
+ if (g_Sdebug)
+ g_printf ("ROWS: %d - %d COLS: %d - %d\n",
+ (int)first_row, (int)last_row,
+ (int)first_col, (int)last_col);
+
+ print_table (prot_fp);
+
+ if (g_Sdebug)
+ print_ppm ("sample_color_all.ppm");
+
+ /* find out ideal sample colors for each brightness intensity (lum)
+ * and set g_sample_color_tab to the ideal colors.
+ */
+ ideal_samples ();
+ calculate_level_transfers ();
+
+ if (g_values.guess_missing)
+ guess_missing_colors ();
+ else
+ fill_missing_colors ();
+
+ print_table (prot_fp);
+ if (g_Sdebug)
+ print_ppm ("sample_color_2.ppm");
+ print_transtable (prot_fp);
+ if (prot_fp)
+ fclose (prot_fp);
+
+ /* check if there was at least one visible pixel */
+ if (sample_pixels == 0)
+ {
+ g_printf ("Error: Source sample has no visible Pixel\n");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+rnd_remap (gint32 lum,
+ guchar *mapped_color)
+{
+ t_samp_color_elem *col_ptr;
+ gint rnd;
+ gint ct;
+ gint idx;
+
+ if (g_lum_tab[lum].all_samples > 1)
+ {
+ rnd = g_random_int_range (0, g_lum_tab[lum].all_samples);
+ ct = 0;
+ idx = 0;
+
+ for (col_ptr = g_lum_tab[lum].col_ptr;
+ col_ptr != NULL;
+ col_ptr = (t_samp_color_elem *)col_ptr->next)
+ {
+ ct += col_ptr->sum_color;
+
+ if (rnd < ct)
+ {
+ /* printf ("RND_remap: rnd: %d all:%d ct:%d idx:%d\n",
+ * rnd, (int)g_lum_tab[lum].all_samples, ct, idx);
+ */
+ memcpy (mapped_color, &col_ptr->color[0], 3);
+ return;
+ }
+ idx++;
+ }
+ }
+
+ memcpy (mapped_color, &g_sample_color_tab[lum + lum + lum], 3);
+}
+
+static void
+remap_pixel (guchar *pixel,
+ const guchar *original,
+ gint bpp2)
+{
+ guchar mapped_color[4];
+ gint lum;
+ double orig_lum, mapped_lum;
+ double grn, red, blu;
+ double mg, mr, mb;
+ double dg, dr, db;
+ double dlum;
+
+ /* get brightness from (uncolorized) original */
+ lum = g_out_trans_tab[g_lvl_trans_tab[LUMINOSITY_1 (original)]];
+ if (g_values.rnd_subcolors)
+ rnd_remap (lum, mapped_color);
+ else
+ memcpy (mapped_color, &g_sample_color_tab[3 * lum], 3);
+
+ if (g_values.hold_inten)
+ {
+ if (g_values.orig_inten)
+ orig_lum = LUMINOSITY_0(original);
+ else
+ orig_lum = 100.0 * g_lvl_trans_tab[LUMINOSITY_1 (original)];
+
+ mapped_lum = LUMINOSITY_0 (mapped_color);
+
+ if (mapped_lum == 0)
+ {
+ /* convert black to greylevel with desired brightness value */
+ mapped_color[0] = orig_lum / 100.0;
+ mapped_color[1] = mapped_color[0];
+ mapped_color[2] = mapped_color[0];
+ }
+ else
+ {
+ /* Calculate theoretical RGB to reach given intensity LUM
+ * value (orig_lum)
+ */
+ mr = mapped_color[0];
+ mg = mapped_color[1];
+ mb = mapped_color[2];
+
+ if (mr > 0.0)
+ {
+ red =
+ orig_lum / (30 + (59 * mg / mr) + (11 * mb / mr));
+ grn = mg * red / mr;
+ blu = mb * red / mr;
+ }
+ else if (mg > 0.0)
+ {
+ grn =
+ orig_lum / ((30 * mr / mg) + 59 + (11 * mb / mg));
+ red = mr * grn / mg;
+ blu = mb * grn / mg;
+ }
+ else
+ {
+ blu =
+ orig_lum / ((30 * mr / mb) + (59 * mg / mb) + 11);
+ grn = mg * blu / mb;
+ red = mr * blu / mb;
+ }
+
+ /* on overflow: Calculate real RGB values
+ * (this may change the hue and saturation,
+ * more and more into white)
+ */
+
+ if (red > 255)
+ {
+ if ((blu < 255) && (grn < 255))
+ {
+ /* overflow in the red channel (compensate with green and blue) */
+ dlum = (red - 255.0) * 30.0;
+ if (mg > 0)
+ {
+ dg = dlum / (59.0 + (11.0 * mb / mg));
+ db = dg * mb / mg;
+ }
+ else if (mb > 0)
+ {
+ db = dlum / (11.0 + (59.0 * mg / mb));
+ dg = db * mg / mb;
+ }
+ else
+ {
+ db = dlum / (11.0 + 59.0);
+ dg = dlum / (59.0 + 11.0);
+ }
+
+ grn += dg;
+ blu += db;
+ }
+
+ red = 255.0;
+
+ if (grn > 255)
+ {
+ grn = 255.0;
+ blu = (orig_lum - 22695) / 11; /* 22695 = (255 * 30) + (255 * 59) */
+ }
+ if (blu > 255)
+ {
+ blu = 255.0;
+ grn = (orig_lum - 10455) / 59; /* 10455 = (255 * 30) + (255 * 11) */
+ }
+ }
+ else if (grn > 255)
+ {
+ if ((blu < 255) && (red < 255))
+ {
+ /* overflow in the green channel (compensate with red and blue) */
+ dlum = (grn - 255.0) * 59.0;
+
+ if (mr > 0)
+ {
+ dr = dlum / (30.0 + (11.0 * mb / mr));
+ db = dr * mb / mr;
+ }
+ else if (mb > 0)
+ {
+ db = dlum / (11.0 + (30.0 * mr / mb));
+ dr = db * mr / mb;
+ }
+ else
+ {
+ db = dlum / (11.0 + 30.0);
+ dr = dlum / (30.0 + 11.0);
+ }
+
+ red += dr;
+ blu += db;
+ }
+
+ grn = 255.0;
+
+ if (red > 255)
+ {
+ red = 255.0;
+ blu = (orig_lum - 22695) / 11; /* 22695 = (255*59) + (255*30) */
+ }
+ if (blu > 255)
+ {
+ blu = 255.0;
+ red = (orig_lum - 17850) / 30; /* 17850 = (255*59) + (255*11) */
+ }
+ }
+ else if (blu > 255)
+ {
+ if ((red < 255) && (grn < 255))
+ {
+ /* overflow in the blue channel (compensate with green and red) */
+ dlum = (blu - 255.0) * 11.0;
+
+ if (mg > 0)
+ {
+ dg = dlum / (59.0 + (30.0 * mr / mg));
+ dr = dg * mr / mg;
+ }
+ else if (mr > 0)
+ {
+ dr = dlum / (30.0 + (59.0 * mg / mr));
+ dg = dr * mg / mr;
+ }
+ else
+ {
+ dr = dlum / (30.0 + 59.0);
+ dg = dlum / (59.0 + 30.0);
+ }
+
+ grn += dg;
+ red += dr;
+ }
+
+ blu = 255.0;
+
+ if (grn > 255)
+ {
+ grn = 255.0;
+ red = (orig_lum - 17850) / 30; /* 17850 = (255*11) + (255*59) */
+ }
+ if (red > 255)
+ {
+ red = 255.0;
+ grn = (orig_lum - 10455) / 59; /* 10455 = (255*11) + (255*30) */
+ }
+ }
+
+ mapped_color[0] = CLAMP0255 (red + 0.5);
+ mapped_color[1] = CLAMP0255 (grn + 0.5);
+ mapped_color[2] = CLAMP0255 (blu + 0.5);
+ }
+ }
+
+ /* set colorized pixel in shadow pr */
+ memcpy (pixel, &mapped_color[0], bpp2);
+}
+
+static void
+colorize_func (const guchar *src,
+ guchar *dest,
+ gint bpp,
+ gboolean has_alpha)
+{
+ if (has_alpha)
+ {
+ bpp--;
+ dest[bpp] = src[bpp];
+ }
+
+ remap_pixel (dest, src, bpp);
+}
+
+static void
+colorize_drawable (gint32 drawable_id)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gboolean has_alpha;
+ gint bpp;
+ gint x, y, w, h;
+ gint total_area;
+ gint area_so_far;
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &x, &y, &w, &h))
+ return;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ has_alpha = gimp_drawable_has_alpha (drawable_id);
+
+ if (has_alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ if (g_show_progress)
+ gimp_progress_init (_("Remap colorized"));
+
+
+ total_area = w * h;
+ area_so_far = 0;
+
+ if (total_area <= 0)
+ goto out;
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x, y, w, h), 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (x, y, w, h), 0 , format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->items[0].data;
+ guchar *dest = iter->items[1].data;
+ gint row;
+
+ for (row = 0; row < iter->items[0].roi.height; row++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint pixels = iter->items[0].roi.width;
+
+ while (pixels--)
+ {
+ colorize_func (s, d, bpp, has_alpha);
+
+ s += bpp;
+ d += bpp;
+ }
+
+ src += iter->items[0].roi.width * bpp;
+ dest += iter->items[1].roi.width * bpp;
+ }
+
+ area_so_far += iter->items[0].roi.width * iter->items[0].roi.height;
+
+ gimp_progress_update ((gdouble) area_so_far / (gdouble) total_area);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x, y, w, h);
+
+ out:
+ if (g_show_progress)
+ gimp_progress_update (0.0);
+}
+
+/* colorize dst_drawable like sample_drawable */
+static int
+main_colorize (gint mc_flags)
+{
+ t_GDRW sample_gdrw;
+ gboolean sample_drawable = FALSE;
+ gint32 max;
+ gint32 id;
+ gint rc;
+
+ if (g_Sdebug)
+ get_filevalues (); /* for debugging: read values from file */
+
+ /* calculate value of tolerable color error */
+ max = color_error (0,0, 0, 255, 255, 255); /* 260100 */
+ g_tol_col_err = (((float)max * (g_values.tol_col_err * g_values.tol_col_err)) / (100.0 *100.0));
+ g_max_col_err = max;
+
+ rc = 0;
+
+ if (mc_flags & MC_GET_SAMPLE_COLORS)
+ {
+ id = g_values.sample_id;
+ if ((id == SMP_GRADIENT) || (id == SMP_INV_GRADIENT))
+ {
+ get_gradient (id);
+ }
+ else
+ {
+ if (is_layer_alive (id) < 0)
+ return -1;
+
+ sample_drawable = TRUE;
+
+ init_gdrw (&sample_gdrw, id, FALSE);
+ free_colors ();
+ rc = sample_analyze (&sample_gdrw);
+ }
+ }
+
+ if ((mc_flags & MC_DST_REMAP) && (rc == 0))
+ {
+ if (is_layer_alive (g_values.dst_id) < 0)
+ return -1;
+
+ if (gimp_drawable_is_gray (g_values.dst_id) &&
+ (mc_flags & MC_DST_REMAP))
+ {
+ gimp_image_convert_rgb (gimp_item_get_image (g_values.dst_id));
+ }
+
+ colorize_drawable (g_values.dst_id);
+ }
+
+ if (sample_drawable)
+ end_gdrw (&sample_gdrw);
+
+ return rc;
+}
diff --git a/plug-ins/common/sharpen.c b/plug-ins/common/sharpen.c
new file mode 100644
index 0000000..eeb0f64
--- /dev/null
+++ b/plug-ins/common/sharpen.c
@@ -0,0 +1,800 @@
+/*
+ * Sharpen filters for GIMP - The GNU Image Manipulation Program
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/*
+ * Constants...
+ */
+
+#define PLUG_IN_PROC "plug-in-sharpen"
+#define PLUG_IN_BINARY "sharpen"
+#define PLUG_IN_ROLE "gimp-sharpen"
+#define PLUG_IN_VERSION "1.4.2 - 3 June 1998"
+#define SCALE_WIDTH 100
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **returm_vals);
+
+static void compute_luts (void);
+static void sharpen (GimpDrawable *drawable);
+
+static gboolean sharpen_dialog (GimpDrawable *drawable);
+
+static void preview_update (GimpPreview *preview,
+ GimpDrawable *drawable);
+
+typedef gint32 intneg;
+typedef gint32 intpos;
+
+static void gray_filter (int width, guchar *src, guchar *dst, intneg *neg0,
+ intneg *neg1, intneg *neg2);
+static void graya_filter (int width, guchar *src, guchar *dst, intneg *neg0,
+ intneg *neg1, intneg *neg2);
+static void rgb_filter (int width, guchar *src, guchar *dst, intneg *neg0,
+ intneg *neg1, intneg *neg2);
+static void rgba_filter (int width, guchar *src, guchar *dst, intneg *neg0,
+ intneg *neg1, intneg *neg2);
+
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+typedef struct
+{
+ gint sharpen_percent;
+} SharpenParams;
+
+static SharpenParams sharpen_params =
+{
+ 10
+};
+
+static intneg neg_lut[256]; /* Negative coefficient LUT */
+static intpos pos_lut[256]; /* Positive coefficient LUT */
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "percent", "Percent sharpening (default = 10)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Make image sharper "
+ "(less powerful than Unsharp Mask)"),
+ "This plug-in selectively performs a convolution "
+ "filter on an image.",
+ "Michael Sweet <mike@easysw.com>",
+ "Copyright 1997-1998 by Michael Sweet",
+ PLUG_IN_VERSION,
+ N_("_Sharpen..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1]; /* Return values */
+ GimpRunMode run_mode; /* Current run mode */
+ GimpPDBStatusType status; /* Return status */
+ GimpDrawable *drawable; /* Current image */
+
+ /*
+ * Initialize parameter data...
+ */
+
+ status = GIMP_PDB_SUCCESS;
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ /*
+ * Get drawable information...
+ */
+
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+ gimp_tile_cache_ntiles (2 * drawable->ntile_cols);
+
+
+ /*
+ * See how we will run
+ */
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (PLUG_IN_PROC, &sharpen_params);
+
+ /*
+ * Get information from the dialog...
+ */
+ if (!sharpen_dialog (drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are present...
+ */
+ if (nparams != 4)
+ status = GIMP_PDB_CALLING_ERROR;
+ else
+ sharpen_params.sharpen_percent = param[3].data.d_int32;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (PLUG_IN_PROC, &sharpen_params);
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ /*
+ * Sharpen the image...
+ */
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if ((gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id)))
+ {
+ /*
+ * Run!
+ */
+ sharpen (drawable);
+
+ /*
+ * If run mode is interactive, flush displays...
+ */
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /*
+ * Store data...
+ */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC,
+ &sharpen_params, sizeof (SharpenParams));
+ }
+ else
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /*
+ * Reset the current run status...
+ */
+ values[0].data.d_status = status;
+
+ /*
+ * Detach from the drawable...
+ */
+ gimp_drawable_detach (drawable);
+}
+
+
+static void
+compute_luts (void)
+{
+ gint i; /* Looping var */
+ gint fact; /* 1 - sharpness */
+
+ fact = 100 - sharpen_params.sharpen_percent;
+ if (fact < 1)
+ fact = 1;
+
+ for (i = 0; i < 256; i ++)
+ {
+ pos_lut[i] = 800 * i / fact;
+ neg_lut[i] = (4 + pos_lut[i] - (i << 3)) >> 3;
+ }
+}
+
+/*
+ * 'sharpen()' - Sharpen an image using a convolution filter.
+ */
+
+static void
+sharpen (GimpDrawable *drawable)
+{
+ GimpPixelRgn src_rgn; /* Source image region */
+ GimpPixelRgn dst_rgn; /* Destination image region */
+ guchar *src_rows[4]; /* Source pixel rows */
+ guchar *src_ptr; /* Current source pixel */
+ guchar *dst_row; /* Destination pixel row */
+ intneg *neg_rows[4]; /* Negative coefficient rows */
+ intneg *neg_ptr; /* Current negative coefficient */
+ gint i; /* Looping vars */
+ gint y; /* Current location in image */
+ gint row; /* Current row in src_rows */
+ gint count; /* Current number of filled src_rows */
+ gint width; /* Byte width of the image */
+ gint x1; /* Selection bounds */
+ gint y1;
+ gint y2;
+ gint sel_width; /* Selection width */
+ gint sel_height; /* Selection height */
+ gint img_bpp; /* Bytes-per-pixel in image */
+ void (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
+
+ filter = NULL;
+
+ if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &sel_width, &sel_height))
+ return;
+
+ y2 = y1 + sel_height;
+
+ img_bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+ /*
+ * Let the user know what we're doing...
+ */
+ gimp_progress_init (_("Sharpening"));
+
+ /*
+ * Setup for filter...
+ */
+
+ gimp_pixel_rgn_init (&src_rgn, drawable,
+ x1, y1, sel_width, sel_height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&dst_rgn, drawable,
+ x1, y1, sel_width, sel_height, TRUE, TRUE);
+
+ compute_luts ();
+
+ width = sel_width * img_bpp;
+
+ for (row = 0; row < 4; row ++)
+ {
+ src_rows[row] = g_new (guchar, width);
+ neg_rows[row] = g_new (intneg, width);
+ }
+
+ dst_row = g_new (guchar, width);
+
+ /*
+ * Pre-load the first row for the filter...
+ */
+
+ gimp_pixel_rgn_get_row (&src_rgn, src_rows[0], x1, y1, sel_width);
+
+ for (i = width, src_ptr = src_rows[0], neg_ptr = neg_rows[0];
+ i > 0;
+ i --, src_ptr ++, neg_ptr ++)
+ *neg_ptr = neg_lut[*src_ptr];
+
+ row = 1;
+ count = 1;
+
+ /*
+ * Select the filter...
+ */
+
+ switch (img_bpp)
+ {
+ case 1 :
+ filter = gray_filter;
+ break;
+ case 2 :
+ filter = graya_filter;
+ break;
+ case 3 :
+ filter = rgb_filter;
+ break;
+ case 4 :
+ filter = rgba_filter;
+ break;
+ };
+
+ /*
+ * Sharpen...
+ */
+
+ for (y = y1; y < y2; y ++)
+ {
+ /*
+ * Load the next pixel row...
+ */
+
+ if ((y + 1) < y2)
+ {
+ /*
+ * Check to see if our src_rows[] array is overflowing yet...
+ */
+
+ if (count >= 3)
+ count --;
+
+ /*
+ * Grab the next row...
+ */
+
+ gimp_pixel_rgn_get_row (&src_rgn, src_rows[row],
+ x1, y + 1, sel_width);
+ for (i = width, src_ptr = src_rows[row], neg_ptr = neg_rows[row];
+ i > 0;
+ i --, src_ptr ++, neg_ptr ++)
+ *neg_ptr = neg_lut[*src_ptr];
+
+ count ++;
+ row = (row + 1) & 3;
+ }
+ else
+ {
+ /*
+ * No more pixels at the bottom... Drop the oldest samples...
+ */
+
+ count --;
+ }
+
+ /*
+ * Now sharpen pixels and save the results...
+ */
+
+ if (count == 3)
+ {
+ (* filter) (sel_width, src_rows[(row + 2) & 3], dst_row,
+ neg_rows[(row + 1) & 3] + img_bpp,
+ neg_rows[(row + 2) & 3] + img_bpp,
+ neg_rows[(row + 3) & 3] + img_bpp);
+
+ /*
+ * Set the row...
+ */
+
+ gimp_pixel_rgn_set_row (&dst_rgn, dst_row, x1, y, sel_width);
+ }
+ else if (count == 2)
+ {
+ if (y == y1) /* first row */
+ gimp_pixel_rgn_set_row (&dst_rgn, src_rows[0],
+ x1, y, sel_width);
+ else /* last row */
+ gimp_pixel_rgn_set_row (&dst_rgn, src_rows[(sel_height - 1) & 3],
+ x1, y, sel_width);
+ }
+
+ if ((y & 15) == 0)
+ gimp_progress_update ((gdouble) (y - y1) / (gdouble) sel_height);
+ }
+
+ /*
+ * OK, we're done. Free all memory used...
+ */
+
+ for (row = 0; row < 4; row ++)
+ {
+ g_free (src_rows[row]);
+ g_free (neg_rows[row]);
+ }
+
+ g_free (dst_row);
+
+ /*
+ * Update the screen...
+ */
+
+ gimp_progress_update (1.0);
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id,
+ x1, y1, sel_width, sel_height);
+}
+
+
+/*
+ * 'sharpen_dialog()' - Popup a dialog window for the filter box size...
+ */
+
+static gboolean
+sharpen_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Sharpen"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect (preview, "invalidated",
+ G_CALLBACK (preview_update),
+ drawable);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Sharpness:"), SCALE_WIDTH, 0,
+ sharpen_params.sharpen_percent,
+ 1, 99, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &sharpen_params.sharpen_percent);
+ g_signal_connect_swapped (adj, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+preview_update (GimpPreview *preview,
+ GimpDrawable *drawable)
+{
+ GimpPixelRgn src_rgn; /* Source image region */
+ guchar *src_ptr; /* Current source pixel */
+ guchar *dst_ptr; /* Current destination pixel */
+ intneg *neg_ptr; /* Current negative pixel */
+ gint i; /* Looping var */
+ gint y; /* Current location in image */
+ gint width; /* Byte width of the image */
+ gint x1, y1;
+ gint preview_width, preview_height;
+ guchar *preview_src, *preview_dst;
+ intneg *preview_neg;
+ gint img_bpp; /* Bytes-per-pixel in image */
+
+ void (*filter)(int, guchar *, guchar *, intneg *, intneg *, intneg *);
+
+ filter = NULL;
+
+ compute_luts();
+
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &preview_width, &preview_height);
+
+ img_bpp = gimp_drawable_bpp (drawable->drawable_id);
+
+
+ preview_src = g_new (guchar, preview_width * preview_height * img_bpp);
+ preview_neg = g_new (intneg, preview_width * preview_height * img_bpp);
+ preview_dst = g_new (guchar, preview_width * preview_height * img_bpp);
+
+ gimp_pixel_rgn_init (&src_rgn, drawable,
+ x1, y1, preview_width, preview_height,
+ FALSE, FALSE);
+
+ width = preview_width * img_bpp;
+
+ /*
+ * Load the preview area...
+ */
+
+ gimp_pixel_rgn_get_rect (&src_rgn, preview_src, x1, y1,
+ preview_width, preview_height);
+
+ for (i = width * preview_height, src_ptr = preview_src, neg_ptr = preview_neg;
+ i > 0;
+ i --)
+ *neg_ptr++ = neg_lut[*src_ptr++];
+
+ /*
+ * Select the filter...
+ */
+
+ switch (img_bpp)
+ {
+ case 1:
+ filter = gray_filter;
+ break;
+ case 2:
+ filter = graya_filter;
+ break;
+ case 3:
+ filter = rgb_filter;
+ break;
+ case 4:
+ filter = rgba_filter;
+ break;
+ default:
+ g_error ("Programmer stupidity error: img_bpp is %d\n",
+ img_bpp);
+ }
+
+ /*
+ * Sharpen...
+ */
+
+ memcpy (preview_dst, preview_src, width);
+ memcpy (preview_dst + width * (preview_height - 1),
+ preview_src + width * (preview_height - 1),
+ width);
+
+ for (y = preview_height - 2, src_ptr = preview_src + width,
+ neg_ptr = preview_neg + width + img_bpp,
+ dst_ptr = preview_dst + width;
+ y > 0;
+ y --, src_ptr += width, neg_ptr += width, dst_ptr += width)
+ (*filter)(preview_width, src_ptr, dst_ptr, neg_ptr - width,
+ neg_ptr, neg_ptr + width);
+
+ gimp_preview_draw_buffer (preview, preview_dst, preview_width * img_bpp);
+
+ g_free (preview_src);
+ g_free (preview_neg);
+ g_free (preview_dst);
+}
+
+/*
+ * 'gray_filter()' - Sharpen grayscale pixels.
+ */
+
+static void
+gray_filter (gint width, /* I - Width of line in pixels */
+ guchar *src, /* I - Source line */
+ guchar *dst, /* O - Destination line */
+ intneg *neg0, /* I - Top negative coefficient line */
+ intneg *neg1, /* I - Middle negative coefficient line */
+ intneg *neg2) /* I - Bottom negative coefficient line */
+{
+ intpos pixel; /* New pixel value */
+
+ *dst++ = *src++;
+ width -= 2;
+
+ while (width > 0)
+ {
+ pixel = (pos_lut[*src++] - neg0[-1] - neg0[0] - neg0[1] -
+ neg1[-1] - neg1[1] -
+ neg2[-1] - neg2[0] - neg2[1]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ neg0 ++;
+ neg1 ++;
+ neg2 ++;
+ width --;
+ }
+
+ *dst++ = *src++;
+}
+
+/*
+ * 'graya_filter()' - Sharpen grayscale+alpha pixels.
+ */
+
+static void
+graya_filter (gint width, /* I - Width of line in pixels */
+ guchar *src, /* I - Source line */
+ guchar *dst, /* O - Destination line */
+ intneg *neg0, /* I - Top negative coefficient line */
+ intneg *neg1, /* I - Middle negative coefficient line */
+ intneg *neg2) /* I - Bottom negative coefficient line */
+{
+ intpos pixel; /* New pixel value */
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+ width -= 2;
+
+ while (width > 0)
+ {
+ pixel = (pos_lut[*src++] - neg0[-2] - neg0[0] - neg0[2] -
+ neg1[-2] - neg1[2] -
+ neg2[-2] - neg2[0] - neg2[2]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ *dst++ = *src++;
+ neg0 += 2;
+ neg1 += 2;
+ neg2 += 2;
+ width --;
+ }
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+}
+
+/*
+ * 'rgb_filter()' - Sharpen RGB pixels.
+ */
+
+static void
+rgb_filter (gint width, /* I - Width of line in pixels */
+ guchar *src, /* I - Source line */
+ guchar *dst, /* O - Destination line */
+ intneg *neg0, /* I - Top negative coefficient line */
+ intneg *neg1, /* I - Middle negative coefficient line */
+ intneg *neg2) /* I - Bottom negative coefficient line */
+{
+ intpos pixel; /* New pixel value */
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ width -= 2;
+
+ while (width > 0)
+ {
+ pixel = (pos_lut[*src++] - neg0[-3] - neg0[0] - neg0[3] -
+ neg1[-3] - neg1[3] -
+ neg2[-3] - neg2[0] - neg2[3]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ pixel = (pos_lut[*src++] - neg0[-2] - neg0[1] - neg0[4] -
+ neg1[-2] - neg1[4] -
+ neg2[-2] - neg2[1] - neg2[4]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ pixel = (pos_lut[*src++] - neg0[-1] - neg0[2] - neg0[5] -
+ neg1[-1] - neg1[5] -
+ neg2[-1] - neg2[2] - neg2[5]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ neg0 += 3;
+ neg1 += 3;
+ neg2 += 3;
+ width --;
+ }
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+}
+
+/*
+ * 'rgba_filter()' - Sharpen RGBA pixels.
+ */
+
+static void
+rgba_filter (gint width, /* I - Width of line in pixels */
+ guchar *src, /* I - Source line */
+ guchar *dst, /* O - Destination line */
+ intneg *neg0, /* I - Top negative coefficient line */
+ intneg *neg1, /* I - Middle negative coefficient line */
+ intneg *neg2) /* I - Bottom negative coefficient line */
+{
+ intpos pixel; /* New pixel value */
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ width -= 2;
+
+ while (width > 0)
+ {
+ pixel = (pos_lut[*src++] - neg0[-4] - neg0[0] - neg0[4] -
+ neg1[-4] - neg1[4] -
+ neg2[-4] - neg2[0] - neg2[4]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ pixel = (pos_lut[*src++] - neg0[-3] - neg0[1] - neg0[5] -
+ neg1[-3] - neg1[5] -
+ neg2[-3] - neg2[1] - neg2[5]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ pixel = (pos_lut[*src++] - neg0[-2] - neg0[2] - neg0[6] -
+ neg1[-2] - neg1[6] -
+ neg2[-2] - neg2[2] - neg2[6]);
+ pixel = (pixel + 4) >> 3;
+ *dst++ = CLAMP0255 (pixel);
+
+ *dst++ = *src++;
+
+ neg0 += 4;
+ neg1 += 4;
+ neg2 += 4;
+ width --;
+ }
+
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = *src++;
+}
diff --git a/plug-ins/common/smooth-palette.c b/plug-ins/common/smooth-palette.c
new file mode 100644
index 0000000..e6916b5
--- /dev/null
+++ b/plug-ins/common/smooth-palette.c
@@ -0,0 +1,499 @@
+/*
+ * smooth palette - derive smooth palette from image
+ * Copyright (C) 1997 Scott Draves <spot@cs.cmu.edu>
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-smooth-palette"
+#define PLUG_IN_BINARY "smooth-palette"
+#define PLUG_IN_ROLE "gimp-smooth-palette"
+
+
+/* Declare local functions. */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean dialog (gint32 drawable_id);
+
+static gint32 smooth_palette (gint32 drawable_id,
+ gint32 *layer_id);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "width", "Width" },
+ { GIMP_PDB_INT32, "height", "Height" },
+ { GIMP_PDB_INT32, "ntries", "Search Depth" },
+ { GIMP_PDB_INT32, "show-image", "Show Image?" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new-image", "Output image" },
+ { GIMP_PDB_LAYER, "new-layer", "Output layer" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Derive a smooth color palette from the image"),
+ "help!",
+ "Scott Draves",
+ "Scott Draves",
+ "1997",
+ N_("Smoo_th Palette..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Colors/Info");
+}
+
+static struct
+{
+ gint width;
+ gint height;
+ gint ntries;
+ gint try_size;
+ gint show_image;
+} config =
+{
+ 256,
+ 64,
+ 50,
+ 10000,
+ 1
+};
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 3;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[2].type = GIMP_PDB_LAYER;
+
+ drawable_id = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &config);
+ if (! dialog (drawable_id))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 7)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ config.width = param[3].data.d_int32;
+ config.height = param[4].data.d_int32;
+ config.ntries = param[5].data.d_int32;
+ config.show_image = param[6].data.d_int32 ? TRUE : FALSE;
+ }
+
+ if (status == GIMP_PDB_SUCCESS &&
+ ((config.width <= 0) || (config.height <= 0) || config.ntries <= 0))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &config);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ gimp_progress_init (_("Deriving smooth palette"));
+
+ gegl_init (NULL, NULL);
+ values[1].data.d_image = smooth_palette (drawable_id,
+ &values[2].data.d_layer);
+ gegl_exit ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &config, sizeof (config));
+
+ if (config.show_image)
+ gimp_display_new (values[1].data.d_image);
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gfloat
+pix_diff (gfloat *pal,
+ guint bpp,
+ gint i,
+ gint j)
+{
+ gfloat r = 0.f;
+ guint k;
+
+ for (k = 0; k < bpp; k++)
+ {
+ gfloat p1 = pal[j * bpp + k];
+ gfloat p2 = pal[i * bpp + k];
+ r += (p1 - p2) * (p1 - p2);
+ }
+
+ return r;
+}
+
+static void
+pix_swap (gfloat *pal,
+ guint bpp,
+ gint i,
+ gint j)
+{
+ guint k;
+
+ for (k = 0; k < bpp; k++)
+ {
+ gfloat t = pal[j * bpp + k];
+ pal[j * bpp + k] = pal[i * bpp + k];
+ pal[i * bpp + k] = t;
+ }
+}
+
+static gint32
+smooth_palette (gint32 drawable_id,
+ gint32 *layer_id)
+{
+ gint32 new_image_id;
+ gint psize, i, j;
+ guint bpp;
+ gint sel_x1, sel_y1;
+ gint width, height;
+ GeglBuffer *buffer;
+ GeglSampler *sampler;
+ gfloat *pal;
+ GRand *gr;
+
+ const Babl *format = babl_format ("RGB float");
+
+ new_image_id = gimp_image_new_with_precision (config.width,
+ config.height,
+ GIMP_RGB,
+ GIMP_PRECISION_FLOAT_LINEAR);
+
+ gimp_image_undo_disable (new_image_id);
+
+ *layer_id = gimp_layer_new (new_image_id, _("Background"),
+ config.width, config.height,
+ gimp_drawable_type (drawable_id),
+ 100,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+
+ gimp_image_insert_layer (new_image_id, *layer_id, -1, 0);
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &sel_x1, &sel_y1, &width, &height))
+ return new_image_id;
+
+ gr = g_rand_new ();
+
+ psize = config.width;
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ sampler = gegl_buffer_sampler_new (buffer, format, GEGL_SAMPLER_NEAREST);
+
+ bpp = babl_format_get_n_components (gegl_buffer_get_format (buffer));
+
+ pal = g_new (gfloat, psize * bpp);
+
+ /* get initial palette */
+
+ for (i = 0; i < psize; i++)
+ {
+ gint x = sel_x1 + g_rand_int_range (gr, 0, width);
+ gint y = sel_y1 + g_rand_int_range (gr, 0, height);
+
+ gegl_sampler_get (sampler,
+ (gdouble) x, (gdouble) y, NULL, pal + i * bpp,
+ GEGL_ABYSS_NONE);
+ }
+
+ g_object_unref (sampler);
+ g_object_unref (buffer);
+
+ /* reorder */
+ if (1)
+ {
+ gfloat *pal_best;
+ gfloat *original;
+ gdouble len_best = 0;
+ gint try;
+
+ pal_best = g_memdup (pal, bpp * psize);
+ original = g_memdup (pal, bpp * psize);
+
+ for (try = 0; try < config.ntries; try++)
+ {
+ gdouble len;
+
+ if (!(try%5))
+ gimp_progress_update (try / (double) config.ntries);
+ memcpy (pal, original, bpp * psize);
+
+ /* scramble */
+ for (i = 1; i < psize; i++)
+ pix_swap (pal, bpp, i, g_rand_int_range (gr, 0, psize));
+
+ /* measure */
+ len = 0.0;
+ for (i = 1; i < psize; i++)
+ len += pix_diff (pal, bpp, i, i-1);
+
+ /* improve */
+ for (i = 0; i < config.try_size; i++)
+ {
+ gint i0 = 1 + g_rand_int_range (gr, 0, psize-2);
+ gint i1 = 1 + g_rand_int_range (gr, 0, psize-2);
+ gfloat as_is, swapd;
+
+ if (1 == (i0 - i1))
+ {
+ as_is = (pix_diff (pal, bpp, i1 - 1, i1) +
+ pix_diff (pal, bpp, i0, i0 + 1));
+ swapd = (pix_diff (pal, bpp, i1 - 1, i0) +
+ pix_diff (pal, bpp, i1, i0 + 1));
+ }
+ else if (1 == (i1 - i0))
+ {
+ as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
+ pix_diff (pal, bpp, i1, i1 + 1));
+ swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
+ pix_diff (pal, bpp, i0, i1 + 1));
+ }
+ else
+ {
+ as_is = (pix_diff (pal, bpp, i0, i0 + 1) +
+ pix_diff (pal, bpp, i0, i0 - 1) +
+ pix_diff (pal, bpp, i1, i1 + 1) +
+ pix_diff (pal, bpp, i1, i1 - 1));
+ swapd = (pix_diff (pal, bpp, i1, i0 + 1) +
+ pix_diff (pal, bpp, i1, i0 - 1) +
+ pix_diff (pal, bpp, i0, i1 + 1) +
+ pix_diff (pal, bpp, i0, i1 - 1));
+ }
+ if (swapd < as_is)
+ {
+ pix_swap (pal, bpp, i0, i1);
+ len += swapd - as_is;
+ }
+ }
+ /* best? */
+ if (0 == try || len < len_best)
+ {
+ memcpy (pal_best, pal, bpp * psize);
+ len_best = len;
+ }
+ }
+
+ gimp_progress_update (1.0);
+ memcpy (pal, pal_best, bpp * psize);
+ g_free (pal_best);
+ g_free (original);
+
+ /* clean */
+ for (i = 1; i < 4 * psize; i++)
+ {
+ gfloat as_is, swapd;
+ gint i0 = 1 + g_rand_int_range (gr, 0, psize - 2);
+ gint i1 = i0 + 1;
+
+ as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
+ pix_diff (pal, bpp, i1, i1 + 1));
+ swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
+ pix_diff (pal, bpp, i0, i1 + 1));
+
+ if (swapd < as_is)
+ {
+ pix_swap (pal, bpp, i0, i1);
+ len_best += swapd - as_is;
+ }
+ }
+ }
+
+ /* store smooth palette */
+
+ buffer = gimp_drawable_get_buffer (*layer_id);
+
+ for (j = 0; j < config.height; j++)
+ {
+ GeglRectangle row = {0, j, config.width, 1};
+ gegl_buffer_set (buffer, &row, 0, format, pal, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ gegl_buffer_flush (buffer);
+
+ gimp_drawable_update (*layer_id, 0, 0,
+ config.width, config.height);
+ gimp_image_undo_enable (new_image_id);
+
+ g_object_unref (buffer);
+ g_free (pal);
+ g_rand_free (gr);
+
+ return new_image_id;
+}
+
+static gboolean
+dialog (gint32 drawable_id)
+{
+ GtkWidget *dlg;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ GtkWidget *sizeentry;
+ guint32 image_id;
+ GimpUnit unit;
+ gdouble xres, yres;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dlg = gimp_dialog_new (_("Smooth Palette"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ image_id = gimp_item_get_image (drawable_id);
+ unit = gimp_image_get_unit (image_id);
+ gimp_image_get_resolution (image_id, &xres, &yres);
+
+ sizeentry = gimp_coordinates_new (unit, "%a", TRUE, FALSE, 6,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE,
+ FALSE, FALSE,
+
+ _("_Width:"),
+ config.width, xres,
+ 2, GIMP_MAX_IMAGE_SIZE,
+ 2, GIMP_MAX_IMAGE_SIZE,
+
+ _("_Height:"),
+ config.height, yres,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 1, GIMP_MAX_IMAGE_SIZE);
+ gtk_container_set_border_width (GTK_CONTAINER (sizeentry), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ sizeentry, FALSE, FALSE, 0);
+ gtk_widget_show (sizeentry);
+
+ adj = GTK_ADJUSTMENT (gtk_adjustment_new (config.ntries,
+ 1, 1024, 1, 10, 0));
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ gimp_table_attach_aligned (GTK_TABLE (sizeentry), 0, 2,
+ _("_Search depth:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &config.ntries);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ config.width = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (sizeentry),
+ 0);
+ config.height = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (sizeentry),
+ 1);
+ }
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
diff --git a/plug-ins/common/softglow.c b/plug-ins/common/softglow.c
new file mode 100644
index 0000000..9a718a0
--- /dev/null
+++ b/plug-ins/common/softglow.c
@@ -0,0 +1,713 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Softglow filter for GIMP for BIPS
+ * -Spencer Kimball
+ *
+ * This filter screens a desaturated, sigmoidally transferred
+ * and gaussian blurred version of the drawable over itself
+ * to create a "softglow" photographic effect.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+
+#define PLUG_IN_PROC "plug-in-softglow"
+#define PLUG_IN_BINARY "softglow"
+#define PLUG_IN_ROLE "gimp-softglow"
+#define TILE_CACHE_SIZE 48
+#define SIGMOIDAL_BASE 2
+#define SIGMOIDAL_RANGE 20
+
+#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
+
+typedef struct
+{
+ gdouble glow_radius;
+ gdouble brightness;
+ gdouble sharpness;
+} SoftglowVals;
+
+
+/*
+ * Function prototypes.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void softglow (GimpDrawable *drawable,
+ GimpPreview *preview);
+static gboolean softglow_dialog (GimpDrawable *drawable);
+
+/*
+ * Gaussian blur helper functions
+ */
+static void find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev);
+static void transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint width);
+
+/***** Local vars *****/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init */
+ NULL, /* quit */
+ query, /* query */
+ run, /* run */
+};
+
+static SoftglowVals svals =
+{
+ 10.0, /* glow_radius */
+ 0.75, /* brightness */
+ 0.85, /* sharpness */
+};
+
+
+/***** Functions *****/
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "glow-radius", "Glow radius (radius in pixels)" },
+ { GIMP_PDB_FLOAT, "brightness", "Glow brightness (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "sharpness", "Glow sharpness (0.0 - 1.0)" }
+ };
+
+ gchar *help_string =
+ "Gives an image a softglow effect by intensifying the highlights in the "
+ "image. This is done by screening a modified version of the drawable "
+ "with itself. The modified version is desaturated and then a sigmoidal "
+ "transfer function is applied to force the distribution of intensities "
+ "into very small and very large only. This desaturated version is then "
+ "blurred to give it a fuzzy 'vaseline-on-the-lens' effect. The glow "
+ "radius parameter controls the sharpness of the glow effect. The "
+ "brightness parameter controls the degree of intensification applied "
+ "to image highlights. The sharpness parameter controls how defined or "
+ "alternatively, diffuse, the glow effect should be.";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Simulate glow by making highlights intense and fuzzy"),
+ help_string,
+ "Spencer Kimball",
+ "Bit Specialists, Inc.",
+ "2001",
+ N_("_Softglow (legacy)..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpDrawable *drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ run_mode = param[0].data.d_int32;
+
+ /* Get the specified drawable */
+ drawable = gimp_drawable_get (param[2].data.d_drawable);
+
+ /* set the tile cache size */
+ gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ INIT_I18N();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &svals);
+
+ /* First acquire information with a dialog */
+ if (! softglow_dialog (drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ svals.glow_radius = param[3].data.d_float;
+ svals.brightness = param[4].data.d_float;
+ svals.sharpness = param[5].data.d_float;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &svals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGB or GRAY color */
+ if (gimp_drawable_is_rgb (drawable->drawable_id) ||
+ gimp_drawable_is_gray (drawable->drawable_id))
+ {
+ gimp_progress_init ("Softglow");
+
+ softglow (drawable, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &svals, sizeof (SoftglowVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("Cannot operate on indexed color images.");
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ gimp_drawable_detach (drawable);
+}
+
+static void
+softglow (GimpDrawable *drawable,
+ GimpPreview *preview)
+{
+ GimpPixelRgn src_rgn, dest_rgn;
+ GimpPixelRgn *pr;
+ gint width, height;
+ gint bytes;
+ gboolean has_alpha;
+ guchar *dest;
+ guchar *src, *sp_p, *sp_m;
+ gdouble n_p[5], n_m[5];
+ gdouble d_p[5], d_m[5];
+ gdouble bd_p[5], bd_m[5];
+ gdouble *val_p, *val_m, *vp, *vm;
+ gint x1, y1;
+ gint i, j;
+ gint row, col, b;
+ gint terms;
+ gint progress, max_progress;
+ gint initial_p[4];
+ gint initial_m[4];
+ gint tmp;
+ gdouble radius;
+ gdouble std_dev;
+ gdouble val;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+ }
+ else if (! gimp_drawable_mask_intersect (drawable->drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ bytes = drawable->bpp;
+ has_alpha = gimp_drawable_has_alpha (drawable->drawable_id);
+
+ val_p = g_new (gdouble, MAX (width, height));
+ val_m = g_new (gdouble, MAX (width, height));
+
+ dest = g_new0 (guchar, width * height);
+
+ progress = 0;
+ max_progress = width * height * 3;
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE);
+
+ for (pr = gimp_pixel_rgns_register (1, &src_rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ guchar *src_ptr = src_rgn.data;
+ guchar *dest_ptr = dest + (src_rgn.y - y1) * width + (src_rgn.x - x1);
+
+ for (row = 0; row < src_rgn.h; row++)
+ {
+ for (col = 0; col < src_rgn.w; col++)
+ {
+ /* desaturate */
+ if (bytes > 2)
+ dest_ptr[col] = (guchar) gimp_rgb_to_l_int (src_ptr[col * bytes + 0],
+ src_ptr[col * bytes + 1],
+ src_ptr[col * bytes + 2]);
+ else
+ dest_ptr[col] = (guchar) src_ptr[col * bytes];
+
+ /* compute sigmoidal transfer */
+ val = dest_ptr[col] / 255.0;
+ val = 255.0 / (1 + exp (-(SIGMOIDAL_BASE + (svals.sharpness * SIGMOIDAL_RANGE)) * (val - 0.5)));
+ val = val * svals.brightness;
+ dest_ptr[col] = (guchar) CLAMP (val, 0, 255);
+ }
+
+ src_ptr += src_rgn.rowstride;
+ dest_ptr += width;
+ }
+
+ if (!preview)
+ {
+ progress += src_rgn.w * src_rgn.h;
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ /* Calculate the standard deviations */
+ radius = fabs (svals.glow_radius) + 1.0;
+ std_dev = sqrt (-(radius * radius) / (2 * log (1.0 / 255.0)));
+
+ /* derive the constants for calculating the gaussian from the std dev */
+ find_constants (n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev);
+
+ /* First the vertical pass */
+ for (col = 0; col < width; col++)
+ {
+ memset (val_p, 0, height * sizeof (gdouble));
+ memset (val_m, 0, height * sizeof (gdouble));
+
+ src = dest + col;
+ sp_p = src;
+ sp_m = src + width * (height - 1);
+ vp = val_p;
+ vm = val_m + (height - 1);
+
+ /* Set up the first vals */
+ initial_p[0] = sp_p[0];
+ initial_m[0] = sp_m[0];
+
+ for (row = 0; row < height; row++)
+ {
+ gdouble *vpptr, *vmptr;
+
+ terms = (row < 4) ? row : 4;
+
+ vpptr = vp; vmptr = vm;
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr += n_p[i] * sp_p[-i * width] - d_p[i] * vp[-i];
+ *vmptr += n_m[i] * sp_m[i * width] - d_m[i] * vm[i];
+ }
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr += (n_p[j] - bd_p[j]) * initial_p[0];
+ *vmptr += (n_m[j] - bd_m[j]) * initial_m[0];
+ }
+
+ sp_p += width;
+ sp_m -= width;
+ vp ++;
+ vm --;
+ }
+
+ transfer_pixels (val_p, val_m, dest + col, width, height);
+
+ if (!preview)
+ {
+ progress += height;
+ if ((col % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ for (row = 0; row < height; row++)
+ {
+ memset (val_p, 0, width * sizeof (gdouble));
+ memset (val_m, 0, width * sizeof (gdouble));
+
+ src = dest + row * width;
+
+ sp_p = src;
+ sp_m = src + width - 1;
+ vp = val_p;
+ vm = val_m + width - 1;
+
+ /* Set up the first vals */
+ initial_p[0] = sp_p[0];
+ initial_m[0] = sp_m[0];
+
+ for (col = 0; col < width; col++)
+ {
+ gdouble *vpptr, *vmptr;
+
+ terms = (col < 4) ? col : 4;
+
+ vpptr = vp; vmptr = vm;
+
+ for (i = 0; i <= terms; i++)
+ {
+ *vpptr += n_p[i] * sp_p[-i] - d_p[i] * vp[-i];
+ *vmptr += n_m[i] * sp_m[i] - d_m[i] * vm[i];
+ }
+
+ for (j = i; j <= 4; j++)
+ {
+ *vpptr += (n_p[j] - bd_p[j]) * initial_p[0];
+ *vmptr += (n_m[j] - bd_m[j]) * initial_m[0];
+ }
+
+ sp_p ++;
+ sp_m --;
+ vp ++;
+ vm --;
+ }
+
+ transfer_pixels (val_p, val_m, dest + row * width, 1, width);
+
+ if (!preview)
+ {
+ progress += width;
+ if ((row % 5) == 0)
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ /* Initialize the pixel regions. */
+ gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, width, height, FALSE, FALSE);
+ gimp_pixel_rgn_init (&dest_rgn, drawable,
+ x1, y1, width, height, (preview == NULL), TRUE);
+
+ for (pr = gimp_pixel_rgns_register (2, &src_rgn, &dest_rgn);
+ pr != NULL;
+ pr = gimp_pixel_rgns_process (pr))
+ {
+ guchar *src_ptr = src_rgn.data;
+ guchar *dest_ptr = dest_rgn.data;
+ guchar *blur_ptr = dest + (src_rgn.y - y1) * width + (src_rgn.x - x1);
+
+ for (row = 0; row < src_rgn.h; row++)
+ {
+ for (col = 0; col < src_rgn.w; col++)
+ {
+ /* screen op */
+ for (b = 0; b < (has_alpha ? (bytes - 1) : bytes); b++)
+ dest_ptr[col * bytes + b] =
+ 255 - INT_MULT((255 - src_ptr[col * bytes + b]),
+ (255 - blur_ptr[col]), tmp);
+ if (has_alpha)
+ dest_ptr[col * bytes + b] = src_ptr[col * bytes + b];
+ }
+
+ src_ptr += src_rgn.rowstride;
+ dest_ptr += dest_rgn.rowstride;
+ blur_ptr += width;
+ }
+
+ if (preview)
+ {
+ gimp_drawable_preview_draw_region (GIMP_DRAWABLE_PREVIEW (preview),
+ &dest_rgn);
+ }
+ else
+ {
+ progress += src_rgn.w * src_rgn.h;
+ gimp_progress_update ((gdouble) progress / (gdouble) max_progress);
+ }
+ }
+
+ if (! preview)
+ {
+ gimp_progress_update (1.0);
+ /* merge the shadow, update the drawable */
+ gimp_drawable_flush (drawable);
+ gimp_drawable_merge_shadow (drawable->drawable_id, TRUE);
+ gimp_drawable_update (drawable->drawable_id,
+ x1, y1, width, height);
+ }
+
+ /* free up buffers */
+ g_free (val_p);
+ g_free (val_m);
+ g_free (dest);
+}
+
+/*
+ * Gaussian blur helper functions
+ */
+
+static void
+transfer_pixels (gdouble *src1,
+ gdouble *src2,
+ guchar *dest,
+ gint jump,
+ gint width)
+{
+ gint i;
+ gdouble sum;
+
+ for (i = 0; i < width; i++)
+ {
+ sum = src1[i] + src2[i];
+
+ sum = CLAMP0255 (sum);
+
+ *dest = (guchar) sum;
+ dest += jump;
+ }
+}
+
+static void
+find_constants (gdouble n_p[],
+ gdouble n_m[],
+ gdouble d_p[],
+ gdouble d_m[],
+ gdouble bd_p[],
+ gdouble bd_m[],
+ gdouble std_dev)
+{
+ gint i;
+ gdouble constants [8];
+ gdouble div;
+
+ /* The constants used in the implementation of a casual sequence
+ * using a 4th order approximation of the gaussian operator
+ */
+
+ div = sqrt(2 * G_PI) * std_dev;
+
+ constants [0] = -1.783 / std_dev;
+ constants [1] = -1.723 / std_dev;
+ constants [2] = 0.6318 / std_dev;
+ constants [3] = 1.997 / std_dev;
+ constants [4] = 1.6803 / div;
+ constants [5] = 3.735 / div;
+ constants [6] = -0.6803 / div;
+ constants [7] = -0.2598 / div;
+
+ n_p [0] = constants[4] + constants[6];
+ n_p [1] = exp (constants[1]) *
+ (constants[7] * sin (constants[3]) -
+ (constants[6] + 2 * constants[4]) * cos (constants[3])) +
+ exp (constants[0]) *
+ (constants[5] * sin (constants[2]) -
+ (2 * constants[6] + constants[4]) * cos (constants[2]));
+ n_p [2] = 2 * exp (constants[0] + constants[1]) *
+ ((constants[4] + constants[6]) * cos (constants[3]) * cos (constants[2]) -
+ constants[5] * cos (constants[3]) * sin (constants[2]) -
+ constants[7] * cos (constants[2]) * sin (constants[3])) +
+ constants[6] * exp (2 * constants[0]) +
+ constants[4] * exp (2 * constants[1]);
+ n_p [3] = exp (constants[1] + 2 * constants[0]) *
+ (constants[7] * sin (constants[3]) - constants[6] * cos (constants[3])) +
+ exp (constants[0] + 2 * constants[1]) *
+ (constants[5] * sin (constants[2]) - constants[4] * cos (constants[2]));
+ n_p [4] = 0.0;
+
+ d_p [0] = 0.0;
+ d_p [1] = -2 * exp (constants[1]) * cos (constants[3]) -
+ 2 * exp (constants[0]) * cos (constants[2]);
+ d_p [2] = 4 * cos (constants[3]) * cos (constants[2]) * exp (constants[0] + constants[1]) +
+ exp (2 * constants[1]) + exp (2 * constants[0]);
+ d_p [3] = -2 * cos (constants[2]) * exp (constants[0] + 2 * constants[1]) -
+ 2 * cos (constants[3]) * exp (constants[1] + 2 * constants[0]);
+ d_p [4] = exp (2 * constants[0] + 2 * constants[1]);
+
+#ifndef ORIGINAL_READABLE_CODE
+ memcpy(d_m, d_p, 5 * sizeof(gdouble));
+#else
+ for (i = 0; i <= 4; i++)
+ d_m [i] = d_p [i];
+#endif
+
+ n_m[0] = 0.0;
+ for (i = 1; i <= 4; i++)
+ n_m [i] = n_p[i] - d_p[i] * n_p[0];
+
+ {
+ gdouble sum_n_p, sum_n_m, sum_d;
+ gdouble a, b;
+
+ sum_n_p = 0.0;
+ sum_n_m = 0.0;
+ sum_d = 0.0;
+
+ for (i = 0; i <= 4; i++)
+ {
+ sum_n_p += n_p[i];
+ sum_n_m += n_m[i];
+ sum_d += d_p[i];
+ }
+
+#ifndef ORIGINAL_READABLE_CODE
+ sum_d++;
+ a = sum_n_p / sum_d;
+ b = sum_n_m / sum_d;
+#else
+ a = sum_n_p / (1 + sum_d);
+ b = sum_n_m / (1 + sum_d);
+#endif
+
+ for (i = 0; i <= 4; i++)
+ {
+ bd_p[i] = d_p[i] * a;
+ bd_m[i] = d_m[i] * b;
+ }
+ }
+}
+
+/*******************************************************/
+/* Dialog */
+/*******************************************************/
+
+static gboolean
+softglow_dialog (GimpDrawable *drawable)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *table;
+ GtkObject *scale_data;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Softglow"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable->drawable_id);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (softglow),
+ drawable);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Label, scale, entry for svals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Glow radius:"), 100, 5,
+ svals.glow_radius, 1.0, 50.0, 1, 5.0, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.glow_radius);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for svals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_Brightness:"), 100, 5,
+ svals.brightness, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.brightness);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* Label, scale, entry for svals.amount */
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Sharpness:"), 100, 5,
+ svals.sharpness, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.sharpness);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/sparkle.c b/plug-ins/common/sparkle.c
new file mode 100644
index 0000000..6af5044
--- /dev/null
+++ b/plug-ins/common/sparkle.c
@@ -0,0 +1,1189 @@
+/* Sparkle --- image filter plug-in for GIMP
+ * Copyright (C) 1996 by John Beale; ported to Gimp by Michael J. Hammel;
+ *
+ * It has been optimized a little, bugfixed and modified by Martin Weber
+ * for additional functionality. Also bugfixed by Seth Burgess (9/17/03)
+ * to take rowstrides into account when selections are present (bug #50911).
+ * Attempted reformatting.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * You can contact Michael at mjhammel@csn.net
+ * You can contact Martin at martweb@gmx.net
+ * You can contact Seth at sjburges@gimp.org
+ */
+
+/*
+ * Sparkle 1.27 - simulate pixel bloom and diffraction effects
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-sparkle"
+#define PLUG_IN_BINARY "sparkle"
+#define PLUG_IN_ROLE "gimp-sparkle"
+
+#define SCALE_WIDTH 175
+#define ENTRY_WIDTH 7
+#define MAX_CHANNELS 4
+#define PSV 2 /* point spread value */
+
+#define NATURAL 0
+#define FOREGROUND 1
+#define BACKGROUND 2
+
+
+typedef struct
+{
+ gdouble lum_threshold;
+ gdouble flare_inten;
+ gdouble spike_len;
+ gdouble spike_pts;
+ gdouble spike_angle;
+ gdouble density;
+ gdouble transparency;
+ gdouble random_hue;
+ gdouble random_saturation;
+ gboolean preserve_luminosity;
+ gboolean inverse;
+ gboolean border;
+ gint colortype;
+} SparkleVals;
+
+
+/* Declare local functions.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean sparkle_dialog (gint32 drawable_ID);
+
+static gint compute_luminosity (const guchar *pixel,
+ gboolean gray,
+ gboolean has_alpha);
+static gint compute_lum_threshold (gint32 drawable_ID,
+ gdouble percentile);
+static void sparkle (gint32 drawable_ID,
+ GimpPreview *preview);
+static void sparkle_preview (gpointer drawable_ID,
+ GimpPreview *preview);
+static void fspike (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer,
+ const Babl *format,
+ gint bytes,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint xr,
+ gint yr,
+ gdouble inten,
+ gdouble length,
+ gdouble angle,
+ GRand *gr,
+ guchar *dest_buf);
+static void rpnt (GeglBuffer *dest_buffer,
+ const Babl *format,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gdouble xr,
+ gdouble yr,
+ gint bytes,
+ gdouble inten,
+ guchar color[MAX_CHANNELS],
+ guchar *dest_buf);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static SparkleVals svals =
+{
+ 0.001, /* luminosity threshold */
+ 0.5, /* flare intensity */
+ 20.0, /* spike length */
+ 4.0, /* spike points */
+ 15.0, /* spike angle */
+ 1.0, /* spike density */
+ 0.0, /* transparency */
+ 0.0, /* random hue */
+ 0.0, /* random saturation */
+ FALSE, /* preserve_luminosity */
+ FALSE, /* inverse */
+ FALSE, /* border */
+ NATURAL /* colortype */
+};
+
+static gint num_sparkles;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "lum-threshold", "Luminosity threshold (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "flare-inten", "Flare intensity (0.0 - 1.0)" },
+ { GIMP_PDB_INT32, "spike-len", "Spike length (in pixels)" },
+ { GIMP_PDB_INT32, "spike-pts", "# of spike points" },
+ { GIMP_PDB_INT32, "spike-angle", "Spike angle (0-360 degrees, -1: random)" },
+ { GIMP_PDB_FLOAT, "density", "Spike density (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "transparency", "Transparency (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "random-hue", "Random hue (0.0 - 1.0)" },
+ { GIMP_PDB_FLOAT, "random-saturation", "Random saturation (0.0 - 1.0)" },
+ { GIMP_PDB_INT32, "preserve-luminosity", "Preserve luminosity (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "inverse", "Inverse (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "border", "Add border (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "color-type", "Color of sparkles: { NATURAL (0), FOREGROUND (1), BACKGROUND (2) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Turn bright spots into starry sparkles"),
+ "Uses a percentage based luminoisty threhsold to find "
+ "candidate pixels for adding some sparkles (spikes). ",
+ "John Beale, & (ported to GIMP v0.54) Michael "
+ "J. Hammel & ted to GIMP v1.0) & Seth Burgess & "
+ "Spencer Kimball",
+ "John Beale",
+ "Version 1.27, September 2003",
+ N_("_Sparkle..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC,
+ "<Image>/Filters/Light and Shadow/Light");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint x, y, w, h;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID, &x, &y, &w, &h))
+ {
+ g_message (_("Region selected for filter is empty"));
+ return;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &svals);
+
+ /* First acquire information with a dialog */
+ if (! sparkle_dialog (drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 16)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ svals.lum_threshold = param[3].data.d_float;
+ svals.flare_inten = param[4].data.d_float;
+ svals.spike_len = param[5].data.d_int32;
+ svals.spike_pts = param[6].data.d_int32;
+ svals.spike_angle = param[7].data.d_int32;
+ svals.density = param[8].data.d_float;
+ svals.transparency = param[9].data.d_float;
+ svals.random_hue = param[10].data.d_float;
+ svals.random_saturation = param[11].data.d_float;
+ svals.preserve_luminosity = (param[12].data.d_int32) ? TRUE : FALSE;
+ svals.inverse = (param[13].data.d_int32) ? TRUE : FALSE;
+ svals.border = (param[14].data.d_int32) ? TRUE : FALSE;
+ svals.colortype = param[15].data.d_int32;
+
+ if (svals.lum_threshold < 0.0 || svals.lum_threshold > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.flare_inten < 0.0 || svals.flare_inten > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.spike_len < 0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.spike_pts < 0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.spike_angle < -1 || svals.spike_angle > 360)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.density < 0.0 || svals.density > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.transparency < 0.0 || svals.transparency > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.random_hue < 0.0 || svals.random_hue > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.random_saturation < 0.0 ||
+ svals.random_saturation > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (svals.colortype < NATURAL || svals.colortype > BACKGROUND)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &svals);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ gimp_progress_init (_("Sparkling"));
+
+ sparkle (drawable_ID, NULL);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store mvals data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &svals, sizeof (SparkleVals));
+ }
+ else
+ {
+ /* gimp_message ("sparkle: cannot operate on indexed color images"); */
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+sparkle_dialog (gint32 drawable_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *preview;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *toggle;
+ GtkWidget *r1, *r2, *r3;
+ GtkObject *scale_data;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Sparkle"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ preview = gimp_drawable_preview_new_from_drawable_id (drawable_ID);
+ gtk_box_pack_start (GTK_BOX (main_vbox), preview, TRUE, TRUE, 0);
+ gtk_widget_show (preview);
+ g_signal_connect_swapped (preview, "invalidated",
+ G_CALLBACK (sparkle_preview),
+ GINT_TO_POINTER (drawable_ID));
+
+ table = gtk_table_new (9, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Luminosity _threshold:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.lum_threshold, 0.0, 0.1, 0.001, 0.01, 3,
+ TRUE, 0, 0,
+ _("Adjust the luminosity threshold"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.lum_threshold);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("F_lare intensity:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.flare_inten, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Adjust the flare intensity"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.flare_inten);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Spike length:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.spike_len, 1, 100, 1, 10, 0,
+ TRUE, 0, 0,
+ _("Adjust the spike length"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.spike_len);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Sp_ike points:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.spike_pts, 0, 16, 1, 4, 0,
+ TRUE, 0, 0,
+ _("Adjust the number of spikes"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.spike_pts);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Spi_ke angle (-1: random):"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.spike_angle, -1, 360, 1, 15, 0,
+ TRUE, 0, 0,
+ _("Adjust the spike angle "
+ "(-1 causes a random angle to be chosen)"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.spike_angle);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
+ _("Spik_e density:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.density, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Adjust the spike density"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.density);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
+ _("Tr_ansparency:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.transparency, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Adjust the opacity of the spikes"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.transparency);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 7,
+ _("_Random hue:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.random_hue, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Adjust how much the hue should be changed randomly"), NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.random_hue);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ scale_data =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 8,
+ _("Rando_m saturation:"), SCALE_WIDTH, ENTRY_WIDTH,
+ svals.random_saturation, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Adjust how much the saturation should be changed randomly"),
+ NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &svals.random_saturation);
+ g_signal_connect_swapped (scale_data, "value-changed",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Preserve luminosity"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ svals.preserve_luminosity);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Should the luminosity be preserved?"), NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &svals.preserve_luminosity);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("In_verse"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), svals.inverse);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Should the effect be inversed?"), NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &svals.inverse);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("A_dd border"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), svals.border);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Draw a border of spikes around the image"), NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &svals.border);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ /* colortype */
+ vbox = gimp_int_radio_group_new (FALSE, NULL,
+ G_CALLBACK (gimp_radio_button_update),
+ &svals.colortype, svals.colortype,
+
+ _("_Natural color"), NATURAL, &r1,
+ _("_Foreground color"), FOREGROUND, &r2,
+ _("_Background color"), BACKGROUND, &r3,
+
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ gimp_help_set_help_data (r1, _("Use the color of the image"), NULL);
+ gimp_help_set_help_data (r2, _("Use the foreground color"), NULL);
+ gimp_help_set_help_data (r3, _("Use the background color"), NULL);
+ g_signal_connect_swapped (r1, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (r2, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+ g_signal_connect_swapped (r3, "toggled",
+ G_CALLBACK (gimp_preview_invalidate),
+ preview);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static gint
+compute_luminosity (const guchar *pixel,
+ gboolean gray,
+ gboolean has_alpha)
+{
+ gint pixel0, pixel1, pixel2;
+
+ if (svals.inverse)
+ {
+ pixel0 = 255 - pixel[0];
+ pixel1 = 255 - pixel[1];
+ pixel2 = 255 - pixel[2];
+ }
+ else
+ {
+ pixel0 = pixel[0];
+ pixel1 = pixel[1];
+ pixel2 = pixel[2];
+ }
+
+ if (gray)
+ {
+ if (has_alpha)
+ return (pixel0 * pixel1) / 255;
+ else
+ return (pixel0);
+ }
+ else
+ {
+ gint min, max;
+
+ min = MIN (pixel0, pixel1);
+ min = MIN (min, pixel2);
+ max = MAX (pixel0, pixel1);
+ max = MAX (max, pixel2);
+
+ if (has_alpha)
+ return ((min + max) * pixel[3]) / 510;
+ else
+ return (min + max) / 2;
+ }
+}
+
+static gint
+compute_lum_threshold (gint32 drawable_ID,
+ gdouble percentile)
+{
+ GeglBuffer *src_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint bpp;
+ gint values[256];
+ gint total, sum;
+ gboolean gray;
+ gboolean has_alpha;
+ gint i;
+ gint x1, y1;
+ gint width, height;
+
+ /* zero out the luminosity values array */
+ memset (values, 0, sizeof (gint) * 256);
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x1, &y1, &width, &height))
+ return 0;
+
+ gray = gimp_drawable_is_gray (drawable_ID);
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (gray)
+ {
+ if (has_alpha)
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+ else
+ {
+ if (has_alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->items[0].data;
+ gint length = iter->length;
+
+ while (length--)
+ {
+ values [compute_luminosity (src, gray, has_alpha)]++;
+ src += bpp;
+ }
+ }
+
+ g_object_unref (src_buffer);
+
+ total = width * height;
+ sum = 0;
+
+ for (i = 255; i >= 0; i--)
+ {
+ sum += values[i];
+ if ((gdouble) sum > percentile * (gdouble) total)
+ {
+ num_sparkles = sum;
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static void
+sparkle (gint32 drawable_ID,
+ GimpPreview *preview)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint d_width, d_height;
+ gdouble nfrac, length, inten, spike_angle;
+ gint cur_progress, max_progress;
+ gint x1, y1, x2, y2;
+ gint width, height;
+ gint threshold;
+ gint lum, x, y, b;
+ gboolean gray, has_alpha;
+ gint alpha;
+ gint bytes;
+ GRand *gr;
+ guchar *dest_buf = NULL;
+
+ gray = gimp_drawable_is_gray (drawable_ID);
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (gray)
+ {
+ if (has_alpha)
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+ else
+ {
+ if (has_alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ alpha = (has_alpha) ? bytes - 1 : bytes;
+
+ if (preview)
+ {
+ gimp_preview_get_position (preview, &x1, &y1);
+ gimp_preview_get_size (preview, &width, &height);
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+ dest_buf = g_new0 (guchar, width * height * bytes);
+ }
+ else
+ {
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+ }
+
+ if (width < 1 || height < 1)
+ return;
+
+ d_width = gimp_drawable_width (drawable_ID);
+ d_height = gimp_drawable_height (drawable_ID);
+
+ gr = g_rand_new ();
+
+ if (svals.border)
+ {
+ num_sparkles = 2 * (width + height);
+ threshold = 255;
+ }
+ else
+ {
+ /* compute the luminosity which exceeds the luminosity threshold */
+ threshold = compute_lum_threshold (drawable_ID, svals.lum_threshold);
+ }
+
+ /* initialize the progress dialog */
+ cur_progress = 0;
+ max_progress = num_sparkles;
+
+ /* copy what is already there */
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[0].roi;
+ const guchar *src, *s;
+ guchar *dest, *d;
+
+ src = iter->items[0].data;
+ if (preview)
+ dest = dest_buf + (((roi.y - y1) * width) + (roi.x - x1)) * bytes;
+ else
+ dest = iter->items[1].data;
+
+ for (y = 0; y < roi.height; y++)
+ {
+ s = src;
+ d = dest;
+
+ for (x = 0; x < roi.width; x++)
+ {
+ if (has_alpha && s[alpha] == 0)
+ {
+ memset (d, 0, alpha);
+ }
+ else
+ {
+ for (b = 0; b < alpha; b++)
+ d[b] = s[b];
+ }
+
+ if (has_alpha)
+ d[alpha] = s[alpha];
+
+ s += bytes;
+ d += bytes;
+ }
+
+ src += roi.width * bytes;
+ if (preview)
+ dest += width * bytes;
+ else
+ dest += roi.width * bytes;
+ }
+ }
+
+ /* add effects to new image based on intensity of old pixels */
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[0].roi;
+ const guchar *src, *s;
+
+ src = iter->items[0].data;
+
+ for (y = 0; y < roi.height; y++)
+ {
+ s = src;
+
+ for (x = 0; x < roi.width; x++)
+ {
+ if (svals.border)
+ {
+ if (x + roi.x == 0 ||
+ y + roi.y == 0 ||
+ x + roi.x == d_width - 1 ||
+ y + roi.y == d_height - 1)
+ {
+ lum = 255;
+ }
+ else
+ {
+ lum = 0;
+ }
+ }
+ else
+ {
+ lum = compute_luminosity (s, gray, has_alpha);
+ }
+
+ if (lum >= threshold)
+ {
+ nfrac = fabs ((gdouble) (lum + 1 - threshold) /
+ (gdouble) (256 - threshold));
+ length = ((gdouble) svals.spike_len *
+ (gdouble) pow (nfrac, 0.8));
+ inten = svals.flare_inten * nfrac;
+
+ /* fspike im x,y intens rlength angle */
+ if (svals.spike_pts > 0)
+ {
+ /* major spikes */
+ if (svals.spike_angle == -1)
+ spike_angle = g_rand_double_range (gr, 0, 360.0);
+ else
+ spike_angle = svals.spike_angle;
+
+ if (g_rand_double (gr) <= svals.density)
+ {
+ fspike (src_buffer, dest_buffer, format, bytes,
+ x1, y1, x2, y2,
+ x + roi.x, y + roi.y,
+ inten, length, spike_angle, gr, dest_buf);
+
+ /* minor spikes */
+ fspike (src_buffer, dest_buffer, format, bytes,
+ x1, y1, x2, y2,
+ x + roi.x, y + roi.y,
+ inten * 0.7, length * 0.7,
+ ((gdouble)spike_angle+180.0/svals.spike_pts),
+ gr, dest_buf);
+ }
+ }
+ if (!preview)
+ {
+ cur_progress ++;
+
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((double) cur_progress /
+ (double) max_progress);
+ }
+ }
+
+ s += bytes;
+ }
+
+ src += roi.width * bytes;
+ }
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ if (preview)
+ {
+ gimp_preview_draw_buffer (preview, dest_buf, width * bytes);
+ g_free (dest_buf);
+ }
+ else
+ {
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, x1, y1, width, height);
+ }
+
+ g_rand_free (gr);
+}
+
+static void
+sparkle_preview (gpointer drawable_ID,
+ GimpPreview *preview)
+{
+ sparkle (GPOINTER_TO_INT (drawable_ID), preview);
+}
+
+static inline void
+rpnt (GeglBuffer *dest_buffer,
+ const Babl *format,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gdouble xr,
+ gdouble yr,
+ gint bytes,
+ gdouble inten,
+ guchar color[MAX_CHANNELS],
+ guchar *dest_buf)
+{
+ gint x, y, b;
+ gdouble dx, dy, rs, val;
+ guchar *pixel;
+ guchar pixel_buf[4];
+ gdouble new;
+
+ x = (int) (xr); /* integer coord. to upper left of real point */
+ y = (int) (yr);
+
+ if (x >= x1 && y >= y1 && x < x2 && y < y2)
+ {
+ if (dest_buf)
+ {
+ pixel = dest_buf + ((y - y1) * (x2 - x1) + (x - x1)) * bytes;
+ }
+ else
+ {
+ gegl_buffer_sample (dest_buffer, x, y, NULL,
+ pixel_buf, format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ pixel = pixel_buf;
+ }
+
+ dx = xr - x; dy = yr - y;
+ rs = dx * dx + dy * dy;
+ val = inten * exp (-rs / PSV);
+
+ for (b = 0; b < bytes; b++)
+ {
+ if (svals.inverse)
+ new = 255 - pixel[b];
+ else
+ new = pixel[b];
+
+ if (svals.preserve_luminosity)
+ {
+ if (new < color[b])
+ {
+ new *= (1.0 - val * (1.0 - svals.transparency));
+ }
+ else
+ {
+ new -= val * color[b] * (1.0 - svals.transparency);
+ if (new < 0.0)
+ new = 0.0;
+ }
+ }
+
+ new *= 1.0 - val * svals.transparency;
+ new += val * color[b];
+
+ if (new > 255)
+ new = 255;
+
+ if (svals.inverse)
+ pixel[b] = 255 - new;
+ else
+ pixel[b] = new;
+ }
+
+ if (! dest_buf)
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ format, pixel_buf,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+}
+
+static void
+fspike (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer,
+ const Babl *format,
+ gint bytes,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint xr,
+ gint yr,
+ gdouble inten,
+ gdouble length,
+ gdouble angle,
+ GRand *gr,
+ guchar *dest_buf)
+{
+ const gdouble efac = 2.0;
+ gdouble xrt, yrt, dx, dy;
+ gdouble rpos;
+ gdouble in;
+ gdouble theta;
+ gdouble sfac;
+ gint r, g, b;
+ gint i;
+ gboolean ok;
+ GimpRGB gimp_color;
+ guchar pixel[MAX_CHANNELS];
+ guchar chosen_color[MAX_CHANNELS];
+ guchar color[MAX_CHANNELS];
+
+ theta = angle;
+
+ switch (svals.colortype)
+ {
+ case NATURAL:
+ break;
+
+ case FOREGROUND:
+ gimp_context_get_foreground (&gimp_color);
+ gimp_rgb_get_uchar (&gimp_color, &chosen_color[0], &chosen_color[1],
+ &chosen_color[2]);
+ break;
+
+ case BACKGROUND:
+ gimp_context_get_background (&gimp_color);
+ gimp_rgb_get_uchar (&gimp_color, &chosen_color[0], &chosen_color[1],
+ &chosen_color[2]);
+ break;
+ }
+
+ /* draw the major spikes */
+ for (i = 0; i < svals.spike_pts; i++)
+ {
+ gegl_buffer_sample (dest_buffer, xr, yr, NULL, pixel, format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (svals.colortype == NATURAL)
+ {
+ color[0] = pixel[0];
+ color[1] = pixel[1];
+ color[2] = pixel[2];
+ }
+ else
+ {
+ color[0] = chosen_color[0];
+ color[1] = chosen_color[1];
+ color[2] = chosen_color[2];
+ }
+
+ color[3] = pixel[3];
+
+ if (svals.inverse)
+ {
+ color[0] = 255 - color[0];
+ color[1] = 255 - color[1];
+ color[2] = 255 - color[2];
+ }
+
+ if (svals.random_hue > 0.0 || svals.random_saturation > 0.0)
+ {
+ r = 255 - color[0];
+ g = 255 - color[1];
+ b = 255 - color[2];
+
+ gimp_rgb_to_hsv_int (&r, &g, &b);
+
+ r += svals.random_hue * g_rand_double_range (gr, -0.5, 0.5) * 255;
+
+ if (r >= 255)
+ r -= 255;
+ else if (r < 0)
+ r += 255;
+
+ b += (svals.random_saturation *
+ g_rand_double_range (gr, -1.0, 1.0)) * 255;
+
+ if (b > 255)
+ b = 255;
+
+ gimp_hsv_to_rgb_int (&r, &g, &b);
+
+ color[0] = 255 - r;
+ color[1] = 255 - g;
+ color[2] = 255 - b;
+ }
+
+ dx = 0.2 * cos (theta * G_PI / 180.0);
+ dy = 0.2 * sin (theta * G_PI / 180.0);
+ xrt = (gdouble) xr; /* (gdouble) is needed because some */
+ yrt = (gdouble) yr; /* compilers optimize too much otherwise */
+ rpos = 0.2;
+
+ do
+ {
+ sfac = inten * exp (-pow (rpos / length, efac));
+ ok = FALSE;
+
+ in = 0.2 * sfac;
+ if (in > 0.01)
+ ok = TRUE;
+
+ rpnt (dest_buffer, format, x1, y1, x2, y2,
+ xrt, yrt,
+ bytes, in, color, dest_buf);
+
+ rpnt (dest_buffer, format, x1, y1, x2, y2,
+ xrt + 1.0, yrt,
+ bytes, in, color, dest_buf);
+
+ rpnt (dest_buffer, format, x1, y1, x2, y2,
+ xrt + 1.0, yrt + 1.0,
+ bytes, in, color, dest_buf);
+
+ rpnt (dest_buffer, format, x1, y1, x2, y2,
+ xrt, yrt + 1.0,
+ bytes, in, color, dest_buf);
+
+ xrt += dx;
+ yrt += dy;
+ rpos += 0.2;
+
+ } while (ok);
+
+ theta += 360.0 / svals.spike_pts;
+ }
+}
diff --git a/plug-ins/common/sphere-designer.c b/plug-ins/common/sphere-designer.c
new file mode 100644
index 0000000..624e6ff
--- /dev/null
+++ b/plug-ins/common/sphere-designer.c
@@ -0,0 +1,3166 @@
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * SphereDesigner v0.4 - creates textured spheres
+ * by Vidar Madsen <vidar@prosalg.no>
+ *
+ * Status: Last updated 1999-09-11
+ *
+ * Known issues:
+ * - Might crash if you click OK or Cancel before first preview is rendered
+ * - Phong might look weird with transparent textures
+ *
+ * Todo:
+ * - Saving / Loading of presets needs an overhaul
+ * - Antialiasing
+ * - Global controls: Gamma, ++
+ * - Beautification of GUI
+ * - Clean up messy source (lots of Glade remnants)
+ * - (Probably more. ;-)
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-spheredesigner"
+#define PLUG_IN_BINARY "sphere-designer"
+#define PLUG_IN_ROLE "gimp-sphere-designer"
+
+#define RESPONSE_RESET 1
+
+#define PREVIEWSIZE 150
+
+/* These must be adjusted as more functionality is added */
+#define MAXOBJECT 5
+#define MAXLIGHT 5
+#define MAXTEXTURE 20
+#define MAXTEXTUREPEROBJ 20
+#define MAXNORMAL 20
+#define MAXNORMALPEROBJ 20
+#define MAXATMOS 1
+#define MAXCOLPERGRADIENT 5
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+enum
+{
+ TRIANGLE,
+ DISC,
+ PLANE,
+ SPHERE,
+ CYLINDER,
+ LIGHT
+};
+
+enum
+{
+ SOLID,
+ CHECKER,
+ MARBLE,
+ LIZARD,
+ IMAGE,
+ PHONG,
+ REFLECTION,
+ REFRACTION,
+ PERLIN,
+ WOOD,
+ TRANSPARENT,
+ SPIRAL,
+ SPOTS,
+ SMOKE
+};
+
+enum
+{
+ PERSPECTIVE,
+ ORTHOGONAL,
+ FISHEYE
+};
+
+enum
+{
+ FOG
+};
+
+enum
+{
+ TYPE,
+ TEXTURE,
+ NUM_COLUMNS
+};
+
+
+/* World-flags */
+#define SMARTAMBIENT 0x00000001
+
+/* Object-flags */
+#define NOSHADOW 0x00000001
+
+/* Texture-flags */
+#define GRADIENT 0x00000001
+
+typedef struct
+{
+ gshort xsize, ysize;
+ guchar *rgb;
+} image;
+
+typedef struct
+{
+ gshort numcol;
+ gdouble pos[MAXCOLPERGRADIENT];
+ GimpVector4 color[MAXCOLPERGRADIENT];
+} gradient;
+
+typedef struct
+{
+ gint majtype;
+ gint type;
+ gulong flags;
+ GimpVector4 color1, color2;
+ gradient gradient;
+ GimpVector4 ambient, diffuse;
+ gdouble oscale;
+ GimpVector4 scale, translate, rotate;
+ image image;
+ GimpVector4 reflection;
+ GimpVector4 refraction;
+ GimpVector4 transparent;
+ gdouble ior;
+ GimpVector4 phongcolor;
+ gdouble phongsize;
+ gdouble amount;
+ gdouble exp;
+ GimpVector4 turbulence;
+} texture;
+
+typedef struct
+{
+ gshort type;
+ gdouble density;
+ GimpVector4 color;
+ gdouble turbulence;
+} atmos;
+
+typedef struct
+{
+ gshort type;
+ gulong flags;
+ gshort numtexture;
+ texture texture[MAXTEXTUREPEROBJ];
+ gshort numnormal;
+ texture normal[MAXNORMALPEROBJ];
+} common;
+
+typedef struct
+{
+ common com;
+ GimpVector4 a, b, c;
+} triangle;
+
+typedef struct
+{
+ common com;
+ GimpVector4 a;
+ gdouble b, r;
+} disc;
+
+typedef struct
+{
+ common com;
+ GimpVector4 a;
+ gdouble r;
+} sphere;
+
+typedef struct
+{
+ common com;
+ GimpVector4 a, b, c;
+} cylinder;
+
+typedef struct
+{
+ common com;
+ GimpVector4 a;
+ gdouble b;
+} plane;
+
+typedef struct
+{
+ common com;
+ GimpVector4 color;
+ GimpVector4 a;
+} light;
+
+typedef struct
+{
+ GimpVector4 v1, v2;
+ gshort inside;
+ gdouble ior;
+} ray;
+
+typedef union
+{
+ common com;
+ triangle tri;
+ disc disc;
+ plane plane;
+ sphere sphere;
+ cylinder cylinder;
+} object;
+
+
+struct world_t
+{
+ gint numobj;
+ object obj[MAXOBJECT];
+ gint numlight;
+ light light[MAXLIGHT];
+ gint numtexture;
+ texture texture[MAXTEXTURE];
+ gulong flags;
+ gshort quality;
+ gdouble smartambient;
+ gshort numatmos;
+ atmos atmos[MAXATMOS];
+};
+
+struct camera_t
+{
+ GimpVector4 location, lookat, up, right;
+ short type;
+ double fov, tilt;
+};
+
+static GtkWidget *drawarea = NULL;
+
+static guchar *img;
+static gint img_stride;
+static cairo_surface_t *buffer;
+
+static guint idle_id = 0;
+
+static sphere s;
+
+struct textures_t
+{
+ gint index;
+ gchar *s;
+ glong n;
+};
+
+static struct textures_t textures[] =
+{
+ { 0, N_("Solid"), SOLID },
+ { 1, N_("Checker"), CHECKER },
+ { 2, N_("Marble"), MARBLE },
+ { 3, N_("Lizard"), LIZARD },
+ { 4, N_("Phong"), PHONG },
+ { 5, N_("Noise"), PERLIN },
+ { 6, N_("Wood"), WOOD },
+ { 7, N_("Spiral"), SPIRAL },
+ { 8, N_("Spots"), SPOTS },
+ { 0, NULL, 0 }
+};
+
+static inline void vset (GimpVector4 *v,
+ gdouble a,
+ gdouble b,
+ gdouble c);
+static void restartrender (void);
+static void drawcolor1 (GtkWidget *widget);
+static void drawcolor2 (GtkWidget *widget);
+static gboolean render (void);
+static void realrender (gint32 drawable_ID);
+static void fileselect (GtkFileChooserAction action,
+ GtkWidget *parent);
+static gint traceray (ray *r,
+ GimpVector4 *col,
+ gint level,
+ gdouble imp);
+static gdouble turbulence (gdouble *point,
+ gdouble lofreq,
+ gdouble hifreq);
+
+
+#define COLORBUTTONWIDTH 30
+#define COLORBUTTONHEIGHT 20
+
+static GtkTreeView *texturelist = NULL;
+
+static GtkObject *scalexscale, *scaleyscale, *scalezscale;
+static GtkObject *rotxscale, *rotyscale, *rotzscale;
+static GtkObject *posxscale, *posyscale, *poszscale;
+static GtkObject *scalescale;
+static GtkObject *turbulencescale;
+static GtkObject *amountscale;
+static GtkObject *expscale;
+static GtkWidget *typemenu;
+static GtkWidget *texturemenu;
+
+#define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])
+
+#define B 256
+
+static gint p[B + B + 2];
+static gdouble g[B + B + 2][3];
+static gboolean start = TRUE;
+static GRand *gr;
+
+
+static void
+init (void)
+{
+ gint i, j, k;
+ gdouble v[3], s;
+
+ /* Create an array of random gradient vectors uniformly on the unit sphere */
+
+ gr = g_rand_new ();
+ g_rand_set_seed (gr, 1); /* Use static seed, to get reproducible results */
+
+ for (i = 0; i < B; i++)
+ {
+ do
+ { /* Choose uniformly in a cube */
+ for (j = 0; j < 3; j++)
+ v[j] = g_rand_double_range (gr, -1, 1);
+ s = DOT (v, v);
+ }
+ while (s > 1.0); /* If not in sphere try again */
+ s = sqrt (s);
+ for (j = 0; j < 3; j++) /* Else normalize */
+ g[i][j] = v[j] / s;
+ }
+
+/* Create a pseudorandom permutation of [1..B] */
+
+ for (i = 0; i < B; i++)
+ p[i] = i;
+ for (i = B; i > 0; i -= 2)
+ {
+ k = p[i];
+ p[i] = p[j = g_rand_int_range (gr, 0, B)];
+ p[j] = k;
+ }
+
+ /* Extend g and p arrays to allow for faster indexing */
+
+ for (i = 0; i < B + 2; i++)
+ {
+ p[B + i] = p[i];
+ for (j = 0; j < 3; j++)
+ g[B + i][j] = g[i][j];
+ }
+ g_rand_free (gr);
+}
+
+#define setup(i,b0,b1,r0,r1) \
+ t = vec[i] + 10000.; \
+ b0 = ((int)t) & (B-1); \
+ b1 = (b0+1) & (B-1); \
+ r0 = t - (int)t; \
+ r1 = r0 - 1.;
+
+
+static gdouble
+noise3 (gdouble * vec)
+{
+ gint bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
+ gdouble rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v;
+ gint i, j;
+
+ if (start)
+ {
+ start = FALSE;
+ init ();
+ }
+
+ setup (0, bx0, bx1, rx0, rx1);
+ setup (1, by0, by1, ry0, ry1);
+ setup (2, bz0, bz1, rz0, rz1);
+
+ i = p[bx0];
+ j = p[bx1];
+
+ b00 = p[i + by0];
+ b10 = p[j + by0];
+ b01 = p[i + by1];
+ b11 = p[j + by1];
+
+#define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
+
+#define surve(t) ( t * t * (3. - 2. * t) )
+
+#define lerp(t, a, b) ( a + t * (b - a) )
+
+ sx = surve (rx0);
+ sy = surve (ry0);
+ sz = surve (rz0);
+
+
+ q = g[b00 + bz0];
+ u = at (rx0, ry0, rz0);
+ q = g[b10 + bz0];
+ v = at (rx1, ry0, rz0);
+ a = lerp (sx, u, v);
+
+ q = g[b01 + bz0];
+ u = at (rx0, ry1, rz0);
+ q = g[b11 + bz0];
+ v = at (rx1, ry1, rz0);
+ b = lerp (sx, u, v);
+
+ c = lerp (sy, a, b); /* interpolate in y at lo x */
+
+ q = g[b00 + bz1];
+ u = at (rx0, ry0, rz1);
+ q = g[b10 + bz1];
+ v = at (rx1, ry0, rz1);
+ a = lerp (sx, u, v);
+
+ q = g[b01 + bz1];
+ u = at (rx0, ry1, rz1);
+ q = g[b11 + bz1];
+ v = at (rx1, ry1, rz1);
+ b = lerp (sx, u, v);
+
+ d = lerp (sy, a, b); /* interpolate in y at hi x */
+
+ return 1.5 * lerp (sz, c, d); /* interpolate in z */
+}
+
+static double
+turbulence (gdouble * point, gdouble lofreq, gdouble hifreq)
+{
+ gdouble freq, t, p[3];
+
+ p[0] = point[0] + 123.456;
+ p[1] = point[1] + 234.567;
+ p[2] = point[2] + 345.678;
+
+ t = 0;
+ for (freq = lofreq; freq < hifreq; freq *= 2.)
+ {
+ t += noise3 (p) / freq;
+ p[0] *= 2.;
+ p[1] *= 2.;
+ p[2] *= 2.;
+ }
+ return t - 0.3; /* readjust to make mean value = 0.0 */
+}
+
+static struct world_t world;
+
+static inline void
+vcopy (GimpVector4 *a, GimpVector4 *b)
+{
+ *a = *b;
+}
+
+static inline void
+vcross (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b)
+{
+ r->x = a->y * b->z - a->z * b->y;
+ r->y = -(a->x * b->z - a->z * b->x);
+ r->z = a->x * b->y - a->y * b->x;
+}
+
+static inline gdouble
+vdot (GimpVector4 *a, GimpVector4 *b)
+{
+ return a->x * b->x + a->y * b->y + a->z * b->z;
+}
+
+static inline gdouble
+vdist (GimpVector4 *a, GimpVector4 *b)
+{
+ gdouble x, y, z;
+
+ x = a->x - b->x;
+ y = a->y - b->y;
+ z = a->z - b->z;
+
+ return sqrt (x * x + y * y + z * z);
+}
+
+static inline gdouble
+vdist2 (GimpVector4 *a, GimpVector4 *b)
+{
+ gdouble x, y, z;
+
+ x = a->x - b->x;
+ y = a->y - b->y;
+ z = a->z - b->z;
+
+ return x * x + y * y + z * z;
+}
+
+static inline gdouble
+vlen (GimpVector4 *a)
+{
+ return sqrt (a->x * a->x + a->y * a->y + a->z * a->z);
+}
+
+static inline void
+vnorm (GimpVector4 *a, gdouble v)
+{
+ gdouble d;
+
+ d = vlen (a);
+ a->x *= v / d;
+ a->y *= v / d;
+ a->z *= v / d;
+}
+
+static inline void
+vrotate (GimpVector4 *axis, gdouble ang, GimpVector4 *vector)
+{
+ gdouble rad = ang / 180.0 * G_PI;
+ gdouble ax = vector->x;
+ gdouble ay = vector->y;
+ gdouble az = vector->z;
+ gdouble x = axis->x;
+ gdouble y = axis->y;
+ gdouble z = axis->z;
+ gdouble c = cos (rad);
+ gdouble s = sin (rad);
+ gdouble c1 = 1.0 - c;
+ gdouble xx = c1 * x * x;
+ gdouble yy = c1 * y * y;
+ gdouble zz = c1 * z * z;
+ gdouble xy = c1 * x * y;
+ gdouble xz = c1 * x * z;
+ gdouble yz = c1 * y * z;
+ gdouble sx = s * x;
+ gdouble sy = s * y;
+ gdouble sz = s * z;
+
+ vector->x = (xx + c) * ax + (xy + sz) * ay + (xz - sy) * az;
+ vector->y = (xy - sz) * ax + (yy + c) * ay + (yz + sx) * az;
+ vector->z = (xz + sy) * ax + (yz - sx) * ay + (zz + c) * az;
+}
+
+static inline void
+vset (GimpVector4 *v, gdouble a, gdouble b, gdouble c)
+{
+ v->x = a;
+ v->y = b;
+ v->z = c;
+ v->w = 1.0;
+}
+
+static inline void
+vcset (GimpVector4 *v, gdouble a, gdouble b, gdouble c, gdouble d)
+{
+ v->x = a;
+ v->y = b;
+ v->z = c;
+ v->w = d;
+}
+
+static inline void
+vvrotate (GimpVector4 *p, GimpVector4 *rot)
+{
+ GimpVector4 axis;
+
+ if (rot->x != 0.0)
+ {
+ vset (&axis, 1, 0, 0);
+ vrotate (&axis, rot->x, p);
+ }
+ if (rot->y != 0.0)
+ {
+ vset (&axis, 0, 1, 0);
+ vrotate (&axis, rot->y, p);
+ }
+ if (rot->z != 0.0)
+ {
+ vset (&axis, 0, 0, 1);
+ vrotate (&axis, rot->z, p);
+ }
+}
+
+static inline void
+vsub (GimpVector4 *a, GimpVector4 *b)
+{
+ a->x -= b->x;
+ a->y -= b->y;
+ a->z -= b->z;
+ a->w -= b->w;
+}
+
+static inline void
+vadd (GimpVector4 *a, GimpVector4 *b)
+{
+ a->x += b->x;
+ a->y += b->y;
+ a->z += b->z;
+ a->w += b->w;
+}
+
+static inline void
+vneg (GimpVector4 *a)
+{
+ a->x = -a->x;
+ a->y = -a->y;
+ a->z = -a->z;
+ a->w = -a->w;
+}
+
+static inline void
+vmul (GimpVector4 *v, gdouble a)
+{
+ v->x *= a;
+ v->y *= a;
+ v->z *= a;
+ v->w *= a;
+}
+
+static inline void
+vvmul (GimpVector4 *a, GimpVector4 *b)
+{
+ a->x *= b->x;
+ a->y *= b->y;
+ a->z *= b->z;
+ a->w *= b->w;
+}
+
+static inline void
+vvdiv (GimpVector4 *a, GimpVector4 *b)
+{
+ a->x /= b->x;
+ a->y /= b->y;
+ a->z /= b->z;
+}
+
+static void
+vmix (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b, gdouble v)
+{
+ gdouble i = 1.0 - v;
+
+ r->x = a->x * v + b->x * i;
+ r->y = a->y * v + b->y * i;
+ r->z = a->z * v + b->z * i;
+ r->w = a->w * v + b->w * i;
+}
+
+static double
+vmax (GimpVector4 *a)
+{
+ gdouble max = fabs (a->x);
+
+ if (fabs (a->y) > max)
+ max = fabs (a->y);
+ if (fabs (a->z) > max)
+ max = fabs (a->z);
+ if (fabs (a->w) > max)
+ max = fabs (a->w);
+
+ return max;
+}
+
+#if 0
+static void
+vavg (GimpVector4 * a)
+{
+ gdouble s;
+
+ s = (a->x + a->y + a->z) / 3.0;
+ a->x = a->y = a->z = s;
+}
+#endif
+
+static void
+trianglenormal (GimpVector4 * n, gdouble *t, triangle * tri)
+{
+ triangle tmp;
+ vcopy (&tmp.b, &tri->b);
+ vcopy (&tmp.c, &tri->c);
+ vsub (&tmp.b, &tri->a);
+ vsub (&tmp.c, &tri->a);
+ vset (&tmp.a, 0, 0, 0);
+ vcross (n, &tmp.b, &tmp.c);
+ if (t)
+ *t = vdot (&tmp.b, &tmp.c);
+}
+
+static gdouble
+checkdisc (ray * r, disc * disc)
+{
+ GimpVector4 p, *v = &disc->a;
+ gdouble t, d2;
+ gdouble i, j, k;
+
+ i = r->v2.x - r->v1.x;
+ j = r->v2.y - r->v1.y;
+ k = r->v2.z - r->v1.z;
+
+ t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - disc->b) /
+ (v->x * i + v->y * j + v->z * k);
+
+ p.x = r->v1.x + i * t;
+ p.y = r->v1.y + j * t;
+ p.z = r->v1.z + k * t;
+
+ d2 = vdist2 (&p, v);
+
+ if (d2 > disc->r * disc->r)
+ t = 0.0;
+
+ return t;
+}
+
+static gdouble
+checksphere (ray * r, sphere * sphere)
+{
+ GimpVector4 cendir, rdir;
+ gdouble dirproj, cdlensq;
+ gdouble linear, constant, rsq, quadratic, discriminant;
+ gdouble smallzero, solmin, solmax, tolerance = 0.001;
+
+ vcopy (&rdir, &r->v2);
+ vsub (&rdir, &r->v1);
+
+ rsq = sphere->r * sphere->r;
+
+ vcopy (&cendir, &r->v1);
+ vsub (&cendir, &sphere->a);
+ dirproj = vdot (&rdir, &cendir);
+ cdlensq = vdot (&cendir, &cendir);
+
+ if ((cdlensq >= rsq) && (dirproj > 0.0))
+ return 0.0;
+
+ linear = 2.0 * dirproj;
+ constant = cdlensq - rsq;
+ quadratic = vdot (&rdir, &rdir);
+
+ smallzero = (constant / linear);
+ if ((smallzero < tolerance) && (smallzero > -tolerance))
+ {
+ solmin = -linear / quadratic;
+
+ if (solmin > tolerance)
+ {
+ return solmin;
+ /*
+ *hits = solmin;
+ return 1;
+ */
+ }
+ else
+ return 0.0;
+ }
+ discriminant = linear * linear - 4.0 * quadratic * constant;
+ if (discriminant < 0.0)
+ return 0.0;
+ quadratic *= 2.0;
+ discriminant = sqrt (discriminant);
+ solmax = (-linear + discriminant) / (quadratic);
+ solmin = (-linear - discriminant) / (quadratic);
+
+ if (solmax < tolerance)
+ return 0.0;
+
+ if (solmin < tolerance)
+ {
+ return solmax;
+ /*
+ * hits = solmax;
+ * return 1;
+ */
+ }
+ else
+ {
+ return solmin;
+ /*
+ * hits++ = solmin;
+ * hits = solmax;
+ * return 2;
+ */
+ }
+}
+
+static gdouble
+checkcylinder (ray * r, cylinder * cylinder)
+{
+ /* FIXME */
+ return 0.0;
+}
+
+
+static gdouble
+checkplane (ray * r, plane * plane)
+{
+ GimpVector4 *v = &plane->a;
+ gdouble t;
+ gdouble i, j, k;
+
+ i = r->v2.x - r->v1.x;
+ j = r->v2.y - r->v1.y;
+ k = r->v2.z - r->v1.z;
+
+ t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - plane->b) /
+ (v->x * i + v->y * j + v->z * k);
+
+ return t;
+}
+
+static gdouble
+checktri (ray * r, triangle * tri)
+{
+ GimpVector4 ed1, ed2;
+ GimpVector4 tvec, pvec, qvec;
+ gdouble det, idet, t, u, v;
+ GimpVector4 *orig, dir;
+
+ orig = &r->v1;
+ dir = r->v2;
+ vsub (&dir, orig);
+
+ ed1.x = tri->c.x - tri->a.x;
+ ed1.y = tri->c.y - tri->a.y;
+ ed1.z = tri->c.z - tri->a.z;
+ ed2.x = tri->b.x - tri->a.x;
+ ed2.y = tri->b.y - tri->a.y;
+ ed2.z = tri->b.z - tri->a.z;
+ vcross (&pvec, &dir, &ed2);
+ det = vdot (&ed1, &pvec);
+
+ idet = 1.0 / det;
+
+ tvec.x = orig->x;
+ tvec.y = orig->y;
+ tvec.z = orig->z;
+ vsub (&tvec, &tri->a);
+ u = vdot (&tvec, &pvec) * idet;
+
+ if (u < 0.0)
+ return 0;
+ if (u > 1.0)
+ return 0;
+
+ vcross (&qvec, &tvec, &ed1);
+ v = vdot (&dir, &qvec) * idet;
+
+ if ((v < 0.0) || (u + v > 1.0))
+ return 0;
+
+ t = vdot (&ed2, &qvec) * idet;
+
+ return t;
+}
+
+static void
+transformpoint (GimpVector4 * p, texture * t)
+{
+ gdouble point[3], f;
+
+ if ((t->rotate.x != 0.0) || (t->rotate.y != 0.0) || (t->rotate.z != 0.0))
+ vvrotate (p, &t->rotate);
+ vvdiv (p, &t->scale);
+
+ vsub (p, &t->translate);
+
+ if ((t->turbulence.x != 0.0) || (t->turbulence.y != 0.0) ||
+ (t->turbulence.z != 0.0))
+ {
+ point[0] = p->x;
+ point[1] = p->y;
+ point[2] = p->z;
+ f = turbulence (point, 1, 256);
+ p->x += t->turbulence.x * f;
+ p->y += t->turbulence.y * f;
+ p->z += t->turbulence.z * f;
+ }
+}
+
+static void
+checker (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gint c = 0;
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ vmul (&p, 0.25);
+
+ p.x += 0.00001;
+ p.y += 0.00001;
+ p.z += 0.00001;
+
+ if (p.x < 0.0)
+ p.x = 0.5 - p.x;
+ if (p.y < 0.0)
+ p.y = 0.5 - p.y;
+ if (p.z < 0.0)
+ p.z = 0.5 - p.z;
+
+ if ((p.x - (gint) p.x) < 0.5)
+ c ^= 1;
+ if ((p.y - (gint) p.y) < 0.5)
+ c ^= 1;
+ if ((p.z - (gint) p.z) < 0.5)
+ c ^= 1;
+
+ *col = (c) ? t->color1 : t->color2;
+}
+
+static void
+gradcolor (GimpVector4 *col, gradient *t, gdouble val)
+{
+ gint i;
+ gdouble d;
+ GimpVector4 tmpcol;
+
+ val = CLAMP (val, 0.0, 1.0);
+
+ for (i = 0; i < t->numcol; i++)
+ {
+ if (t->pos[i] == val)
+ {
+ *col = t->color[i];
+ return;
+ }
+ if (t->pos[i] > val)
+ {
+ d = (val - t->pos[i - 1]) / (t->pos[i] - t->pos[i - 1]);
+ vcopy (&tmpcol, &t->color[i]);
+ vmul (&tmpcol, d);
+ vcopy (col, &tmpcol);
+ vcopy (&tmpcol, &t->color[i - 1]);
+ vmul (&tmpcol, 1.0 - d);
+ vadd (col, &tmpcol);
+ return;
+ }
+ }
+ g_printerr ("Error in gradient!\n");
+ vset (col, 0, 1, 0);
+}
+
+static void
+marble (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gdouble f;
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ f = sin (p.x * 4) / 2 + 0.5;
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+lizard (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gdouble f;
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ f = fabs (sin (p.x * 4));
+ f += fabs (sin (p.y * 4));
+ f += fabs (sin (p.z * 4));
+ f /= 3.0;
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+wood (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gdouble f;
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ f = fabs (p.x);
+ f = f - (int) f;
+
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+spiral (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gdouble f;
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ f = fabs (atan2 (p.x, p.z) / G_PI / 2 + p.y + 99999);
+ f = f - (int) f;
+
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+spots (GimpVector4 *q, GimpVector4 *col, texture *t)
+{
+ gdouble f;
+ GimpVector4 p, r;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ p.x += 10000.0;
+ p.y += 10000.0;
+ p.z += 10000.0;
+
+ vset (&r, (gint) (p.x + 0.5), (gint) (p.y + 0.5), (gint) (p.z + 0.5));
+ f = vdist (&p, &r);
+ f = cos (f * G_PI);
+ f = CLAMP (f, 0.0, 1.0);
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+perlin (GimpVector4 * q, GimpVector4 * col, texture * t)
+{
+ gdouble f, point[3];
+ GimpVector4 p;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ point[0] = p.x;
+ point[1] = p.y;
+ point[2] = p.z;
+
+ f = turbulence (point, 1, 256) * 0.3 + 0.5;
+
+ f = pow (f, t->exp);
+
+ if (t->flags & GRADIENT)
+ gradcolor (col, &t->gradient, f);
+ else
+ vmix (col, &t->color1, &t->color2, f);
+}
+
+static void
+imagepixel (GimpVector4 * q, GimpVector4 * col, texture * t)
+{
+ GimpVector4 p;
+ gint x, y;
+ guchar *rgb;
+
+ p = *q;
+ transformpoint (&p, t);
+
+ x = (p.x * t->image.xsize);
+ y = (p.y * t->image.ysize);
+
+ x = (x % t->image.xsize + t->image.xsize) % t->image.xsize;
+ y = (y % t->image.ysize + t->image.ysize) % t->image.ysize;
+
+ rgb = &t->image.rgb[x * 3 + (t->image.ysize - 1 - y) * t->image.xsize * 3];
+ vset (col, rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0);
+}
+
+static void
+objcolor (GimpVector4 *col, GimpVector4 *p, common *obj)
+{
+ gint i;
+ texture *t;
+ GimpVector4 tmpcol;
+
+ vcset (col, 0, 0, 0, 0);
+
+ for (i = 0; i < obj->numtexture; i++)
+ {
+ t = &obj->texture[i];
+
+ if (world.quality < 1)
+ {
+ vadd (col, &t->color1);
+ continue;
+ }
+
+ vset (&tmpcol, 0, 0, 0);
+ switch (t->type)
+ {
+ case SOLID:
+ vcopy (&tmpcol, &t->color1);
+ break;
+ case CHECKER:
+ checker (p, &tmpcol, t);
+ break;
+ case MARBLE:
+ marble (p, &tmpcol, t);
+ break;
+ case LIZARD:
+ lizard (p, &tmpcol, t);
+ break;
+ case PERLIN:
+ perlin (p, &tmpcol, t);
+ break;
+ case WOOD:
+ wood (p, &tmpcol, t);
+ break;
+ case SPIRAL:
+ spiral (p, &tmpcol, t);
+ break;
+ case SPOTS:
+ spots (p, &tmpcol, t);
+ break;
+ case IMAGE:
+ imagepixel (p, &tmpcol, t);
+ break;
+ case PHONG:
+ case REFRACTION:
+ case REFLECTION:
+ case TRANSPARENT:
+ case SMOKE:
+ /* Silently ignore non-color textures */
+ continue;
+ break;
+ default:
+ g_printerr ("Warning: unknown texture %d\n", t->type);
+ break;
+ }
+ vmul (&tmpcol, t->amount);
+ vadd (col, &tmpcol);
+ }
+ if (!i)
+ {
+ g_printerr ("Warning: object %p has no textures\n", obj);
+ }
+}
+
+static void
+objnormal (GimpVector4 *res, common *obj, GimpVector4 *p)
+{
+ gint i;
+
+ switch (obj->type)
+ {
+ case TRIANGLE:
+ trianglenormal (res, NULL, (triangle *) obj);
+ break;
+ case DISC:
+ vcopy (res, &((disc *) obj)->a);
+ break;
+ case PLANE:
+ vcopy (res, &((plane *) obj)->a);
+ break;
+ case SPHERE:
+ vcopy (res, &((sphere *) obj)->a);
+ vsub (res, p);
+ break;
+ case CYLINDER:
+ vset (res, 1, 1, 1); /* fixme */
+ break;
+ default:
+ g_error ("objnormal(): Unsupported object type!?\n");
+ }
+ vnorm (res, 1.0);
+
+ for (i = 0; i < obj->numnormal; i++)
+ {
+ gint k;
+ GimpVector4 tmpcol[6];
+ GimpVector4 q[6], nres;
+ texture *t = &obj->normal[i];
+ gdouble nstep = 0.1;
+
+ vset (&nres, 0, 0, 0);
+ for (k = 0; k < 6; k++)
+ {
+ vcopy (&q[k], p);
+ }
+ q[0].x += nstep;
+ q[1].x -= nstep;
+ q[2].y += nstep;
+ q[3].y -= nstep;
+ q[4].z += nstep;
+ q[5].z -= nstep;
+
+ switch (t->type)
+ {
+ case MARBLE:
+ for (k = 0; k < 6; k++)
+ marble (&q[k], &tmpcol[k], t);
+ break;
+ case LIZARD:
+ for (k = 0; k < 6; k++)
+ lizard (&q[k], &tmpcol[k], t);
+ break;
+ case PERLIN:
+ for (k = 0; k < 6; k++)
+ perlin (&q[k], &tmpcol[k], t);
+ break;
+ case WOOD:
+ for (k = 0; k < 6; k++)
+ wood (&q[k], &tmpcol[k], t);
+ break;
+ case SPIRAL:
+ for (k = 0; k < 6; k++)
+ spiral (&q[k], &tmpcol[k], t);
+ break;
+ case SPOTS:
+ for (k = 0; k < 6; k++)
+ spots (&q[k], &tmpcol[k], t);
+ break;
+ case IMAGE:
+ for (k = 0; k < 6; k++)
+ imagepixel (&q[k], &tmpcol[k], t);
+ break;
+ case CHECKER:
+ case SOLID:
+ case PHONG:
+ case REFRACTION:
+ case REFLECTION:
+ case TRANSPARENT:
+ case SMOKE:
+ continue;
+ break;
+ default:
+ g_printerr ("Warning: unknown texture %d\n", t->type);
+ break;
+ }
+
+ nres.x = tmpcol[0].x - tmpcol[1].x;
+ nres.y = tmpcol[2].x - tmpcol[3].x;
+ nres.z = tmpcol[4].x - tmpcol[5].x;
+ vadd (&nres, res);
+ vnorm (&nres, 1.0);
+ vmul (&nres, t->amount);
+ vadd (res, &nres);
+ vnorm (res, 1.0);
+ }
+}
+
+/*
+ Quality:
+ 0 = Color only
+ 1 = Textures
+ 2 = Light + Normals
+ 3 = Shadows
+ 4 = Phong
+ 5 = Reflection + Refraction
+ */
+
+static void
+calclight (GimpVector4 * col, GimpVector4 * point, common * obj)
+{
+ gint i, j;
+ ray r;
+ gdouble b, a;
+ GimpVector4 lcol;
+ GimpVector4 norm;
+ GimpVector4 pcol;
+
+ vcset (col, 0, 0, 0, 0);
+
+ objcolor (&pcol, point, obj);
+ a = pcol.w;
+
+ if (world.quality < 2)
+ {
+ vcopy (col, &pcol);
+ return;
+ }
+
+ for (i = 0; i < obj->numtexture; i++)
+ {
+ if (obj->texture[i].type == PHONG)
+ continue;
+ if (obj->texture[i].type == REFLECTION)
+ continue;
+ if (obj->texture[i].type == REFRACTION)
+ continue;
+ if (obj->texture[i].type == TRANSPARENT)
+ continue;
+ if (obj->texture[i].type == SMOKE)
+ continue;
+ vcopy (&lcol, &pcol);
+ vvmul (&lcol, &obj->texture[i].ambient);
+ vadd (col, &lcol);
+ }
+
+ objnormal (&norm, obj, point);
+ vnorm (&norm, 1.0);
+
+ r.inside = -1;
+ r.ior = 1.0;
+
+ for (i = 0; i < world.numlight; i++)
+ {
+ vcopy (&r.v1, point);
+ vcopy (&r.v2, &world.light[i].a);
+ vmix (&r.v1, &r.v1, &r.v2, 0.9999);
+
+ vsub (&r.v1, &r.v2);
+ vnorm (&r.v1, 1.0);
+ b = vdot (&r.v1, &norm);
+
+ if (b < 0.0)
+ continue;
+
+ for (j = 0; j < obj->numtexture; j++)
+ {
+ if (obj->texture[j].type == PHONG)
+ continue;
+ if (obj->texture[j].type == REFLECTION)
+ continue;
+ if (obj->texture[j].type == REFRACTION)
+ continue;
+ if (obj->texture[j].type == TRANSPARENT)
+ continue;
+ if (obj->texture[j].type == SMOKE)
+ continue;
+ vcopy (&lcol, &pcol);
+ vvmul (&lcol, &world.light[i].color);
+ vvmul (&lcol, &obj->texture[j].diffuse);
+ vmul (&lcol, b);
+ vadd (col, &lcol);
+ }
+ }
+ col->w = a;
+}
+
+static void
+calcphong (common * obj, ray * r2, GimpVector4 * col)
+{
+ gint i, j;
+ ray r;
+ gdouble b;
+ GimpVector4 lcol;
+ GimpVector4 norm;
+ GimpVector4 pcol;
+ gdouble ps;
+
+ vcopy (&pcol, col);
+
+ vcopy (&norm, &r2->v2);
+ vsub (&norm, &r2->v1);
+ vnorm (&norm, 1.0);
+
+ r.inside = -1;
+ r.ior = 1.0;
+
+ for (i = 0; i < world.numlight; i++)
+ {
+ vcopy (&r.v1, &r2->v1);
+ vcopy (&r.v2, &world.light[i].a);
+ vmix (&r.v1, &r.v1, &r.v2, 0.9999);
+
+ if (traceray (&r, NULL, -1, 1.0))
+ continue;
+
+ /* OK, light is visible */
+
+ vsub (&r.v1, &r.v2);
+ vnorm (&r.v1, 1.0);
+ b = -vdot (&r.v1, &norm);
+
+ for (j = 0; j < obj->numtexture; j++)
+ {
+ if (obj->texture[j].type != PHONG)
+ continue;
+
+ ps = obj->texture[j].phongsize;
+
+ if (b < (1 - ps))
+ continue;
+ ps = (b - (1 - ps)) / ps;
+
+ vcopy (&lcol, &obj->texture[j].phongcolor);
+ vvmul (&lcol, &world.light[i].color);
+ vmul (&lcol, ps);
+ vadd (col, &lcol);
+ }
+ }
+}
+
+static int
+traceray (ray * r, GimpVector4 * col, gint level, gdouble imp)
+{
+ gint i, b = -1;
+ gdouble t = -1.0, min = 0.0;
+ common *obj, *bobj = NULL;
+ gint hits = 0;
+ GimpVector4 p;
+
+ if ((level == 0) || (imp < 0.005))
+ {
+ vset (col, 0, 1, 0);
+ return 0;
+ }
+
+ for (i = 0; i < world.numobj; i++)
+ {
+ obj = (common *) & world.obj[i];
+ switch (obj->type)
+ {
+ case TRIANGLE:
+ t = checktri (r, (triangle *) & world.obj[i]);
+ break;
+ case DISC:
+ t = checkdisc (r, (disc *) & world.obj[i]);
+ break;
+ case PLANE:
+ t = checkplane (r, (plane *) & world.obj[i]);
+ break;
+ case SPHERE:
+ t = checksphere (r, (sphere *) & world.obj[i]);
+ break;
+ case CYLINDER:
+ t = checkcylinder (r, (cylinder *) & world.obj[i]);
+ break;
+ default:
+ g_error ("Illegal object!!\n");
+ }
+ if (t <= 0.0)
+ continue;
+
+ if (!(obj->flags & NOSHADOW) && (level == -1))
+ {
+ return i + 1;
+ }
+
+ hits++;
+ if ((!bobj) || (t < min))
+ {
+
+ min = t;
+ b = i;
+ bobj = obj;
+ }
+ }
+ if (level == -1)
+ return 0;
+
+ if (bobj)
+ {
+ p.x = r->v1.x + (r->v2.x - r->v1.x) * min;
+ p.y = r->v1.y + (r->v2.y - r->v1.y) * min;
+ p.z = r->v1.z + (r->v2.z - r->v1.z) * min;
+
+ calclight (col, &p, bobj);
+
+ if (world.flags & SMARTAMBIENT)
+ {
+ gdouble ambient = 0.3 * exp (-min / world.smartambient);
+ GimpVector4 lcol;
+ objcolor (&lcol, &p, bobj);
+ vmul (&lcol, ambient);
+ vadd (col, &lcol);
+ }
+
+ for (i = 0; i < bobj->numtexture; i++)
+ {
+
+ if ((world.quality >= 4)
+ && ((bobj->texture[i].type == REFLECTION)
+ || (bobj->texture[i].type == PHONG)))
+ {
+
+ GimpVector4 refcol, norm, ocol;
+ ray ref;
+
+ objcolor (&ocol, &p, bobj);
+
+ vcopy (&ref.v1, &p);
+ vcopy (&ref.v2, &r->v1);
+ ref.inside = r->inside;
+ ref.ior = r->ior;
+
+ vmix (&ref.v1, &ref.v1, &ref.v2, 0.9999); /* push it a tad */
+
+ vsub (&ref.v2, &p);
+ objnormal (&norm, bobj, &p);
+ vnorm (&norm, 1.0);
+ vrotate (&norm, 180.0, &ref.v2);
+
+ vmul (&norm, -0.0001); /* push it a tad */
+ vadd (&ref.v1, &norm);
+
+ vnorm (&ref.v2, 1.0);
+ vadd (&ref.v2, &p);
+
+ if ((world.quality >= 5)
+ && (bobj->texture[i].type == REFLECTION))
+ {
+ traceray (&ref, &refcol, level - 1,
+ imp * vmax (&bobj->texture[i].reflection));
+ vvmul (&refcol, &bobj->texture[i].reflection);
+ refcol.w = ocol.w;
+ vadd (col, &refcol);
+ }
+ if (bobj->texture[i].type == PHONG)
+ {
+ vcset (&refcol, 0, 0, 0, 0);
+ calcphong (bobj, &ref, &refcol);
+ refcol.w = ocol.w;
+ vadd (col, &refcol);
+ }
+
+ }
+
+ if ((world.quality >= 5) && (col->w < 1.0))
+ {
+ GimpVector4 refcol;
+ ray ref;
+
+ vcopy (&ref.v1, &p);
+ vcopy (&ref.v2, &p);
+ vsub (&ref.v2, &r->v1);
+ vnorm (&ref.v2, 1.0);
+ vadd (&ref.v2, &p);
+
+ vmix (&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */
+ traceray (&ref, &refcol, level - 1, imp * (1.0 - col->w));
+ vmul (&refcol, (1.0 - col->w));
+ vadd (col, &refcol);
+ }
+
+ if ((world.quality >= 5) && (bobj->texture[i].type == TRANSPARENT))
+ {
+ GimpVector4 refcol;
+ ray ref;
+
+ vcopy (&ref.v1, &p);
+ vcopy (&ref.v2, &p);
+ vsub (&ref.v2, &r->v1);
+ vnorm (&ref.v2, 1.0);
+ vadd (&ref.v2, &p);
+
+ vmix (&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */
+
+ traceray (&ref, &refcol, level - 1,
+ imp * vmax (&bobj->texture[i].transparent));
+ vvmul (&refcol, &bobj->texture[i].transparent);
+
+ vadd (col, &refcol);
+ }
+
+ if ((world.quality >= 5) && (bobj->texture[i].type == SMOKE))
+ {
+ GimpVector4 smcol, raydir, norm;
+ double tran;
+ ray ref;
+
+ vcopy (&ref.v1, &p);
+ vcopy (&ref.v2, &p);
+ vsub (&ref.v2, &r->v1);
+ vnorm (&ref.v2, 1.0);
+ vadd (&ref.v2, &p);
+
+ objnormal (&norm, bobj, &p);
+ vcopy (&raydir, &r->v2);
+ vsub (&raydir, &r->v1);
+ vnorm (&raydir, 1.0);
+ tran = vdot (&norm, &raydir);
+ if (tran < 0.0)
+ continue;
+ tran *= tran;
+ vcopy (&smcol, &bobj->texture[i].color1);
+ vmul (&smcol, tran);
+ vadd (col, &smcol);
+ }
+
+ if ((world.quality >= 5) && (bobj->texture[i].type == REFRACTION))
+ {
+ GimpVector4 refcol, norm, tmpv;
+ ray ref;
+ double c1, c2, n1, n2, n;
+
+ vcopy (&ref.v1, &p);
+ vcopy (&ref.v2, &p);
+ vsub (&ref.v2, &r->v1);
+ vadd (&ref.v2, &r->v2);
+
+ vmix (&ref.v1, &ref.v1, &ref.v2, 0.999); /* push it a tad */
+
+ vsub (&ref.v2, &p);
+ objnormal (&norm, bobj, &p);
+
+ if (r->inside == b)
+ {
+ ref.inside = -1;
+ ref.ior = 1.0;
+ }
+ else
+ {
+ ref.inside = b;
+ ref.ior = bobj->texture[i].ior;
+ }
+
+ c1 = vdot (&norm, &ref.v2);
+
+ if (ref.inside < 0)
+ c1 = -c1;
+
+ n1 = r->ior; /* IOR of current media */
+ n2 = ref.ior; /* IOR of new media */
+ n = n1 / n2;
+ c2 = 1.0 - n * n * (1.0 - c1 * c1);
+
+ if (c2 < 0.0)
+ {
+ /* FIXME: Internal reflection should occur */
+ c2 = sqrt (-c2);
+
+ }
+ else
+ {
+ c2 = sqrt (c2);
+ }
+
+ vmul (&ref.v2, n);
+ vcopy (&tmpv, &norm);
+ vmul (&tmpv, n * c1 - c2);
+ vadd (&ref.v2, &tmpv);
+
+ vnorm (&ref.v2, 1.0);
+ vadd (&ref.v2, &p);
+
+ traceray (&ref, &refcol, level - 1,
+ imp * vmax (&bobj->texture[i].refraction));
+
+ vvmul (&refcol, &bobj->texture[i].refraction);
+ vadd (col, &refcol);
+ }
+ }
+ }
+ else
+ {
+ vcset (col, 0, 0, 0, 0);
+ min = 10000.0;
+ vcset (&p, 0, 0, 0, 0);
+ }
+
+ for (i = 0; i < world.numatmos; i++)
+ {
+ GimpVector4 tmpcol;
+ if (world.atmos[i].type == FOG)
+ {
+ gdouble v, pt[3];
+ pt[0] = p.x;
+ pt[1] = p.y;
+ pt[2] = p.z;
+ if ((v = world.atmos[i].turbulence) > 0.0)
+ v = turbulence (pt, 1, 256) * world.atmos[i].turbulence;
+ v = exp (-(min + v) / world.atmos[i].density);
+ vmul (col, v);
+ vcopy (&tmpcol, &world.atmos[i].color);
+ vmul (&tmpcol, 1.0 - v);
+ vadd (col, &tmpcol);
+ }
+ }
+
+ return hits;
+}
+
+static void
+setdefaults (texture * t)
+{
+ memset (t, 0, sizeof (texture));
+ t->type = SOLID;
+ vcset (&t->color1, 1, 1, 1, 1);
+ vcset (&t->color2, 0, 0, 0, 1);
+ vcset (&t->diffuse, 1, 1, 1, 1);
+ vcset (&t->ambient, 0, 0, 0, 1);
+ vset (&t->scale, 1, 1, 1);
+ vset (&t->rotate, 0, 0, 0);
+ vset (&t->translate, 0, 0, 0);
+ t->oscale = 1.0;
+ t->amount = 1.0;
+ t->exp = 1.0;
+}
+
+static gchar *
+mklabel (texture * t)
+{
+ struct textures_t *l;
+ static gchar tmps[100];
+
+ if (t->majtype == 0)
+ strcpy (tmps, _("Texture"));
+ else if (t->majtype == 1)
+ strcpy (tmps, _("Bumpmap"));
+ else if (t->majtype == 2)
+ strcpy (tmps, _("Light"));
+ else
+ strcpy (tmps, "<unknown>");
+ if ((t->majtype == 0) || (t->majtype == 1))
+ {
+ strcat (tmps, " / ");
+ l = textures;
+ while (l->s)
+ {
+ if (t->type == l->n)
+ {
+ strcat (tmps, gettext (l->s));
+ break;
+ }
+ l++;
+ }
+ }
+ return tmps;
+}
+
+static texture *
+currenttexture (void)
+{
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ texture *t = NULL;
+
+ sel = gtk_tree_view_get_selection (texturelist);
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ gtk_tree_model_get (gtk_tree_view_get_model (texturelist), &iter,
+ TEXTURE, &t,
+ -1);
+ }
+
+ return t;
+}
+
+static void
+relabel (void)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ texture *t = NULL;
+
+ sel = gtk_tree_view_get_selection (texturelist);
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ model = gtk_tree_view_get_model (texturelist);
+
+ gtk_tree_model_get (model, &iter,
+ TEXTURE, &t,
+ -1);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ TYPE, mklabel (t),
+ -1);
+ }
+}
+
+static gboolean noupdate = FALSE;
+
+static void
+setvals (texture *t)
+{
+ struct textures_t *l;
+
+ if (!t)
+ return;
+
+ noupdate = TRUE;
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (amountscale), t->amount);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (scalescale), t->oscale);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (scalexscale), t->scale.x);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (scaleyscale), t->scale.y);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (scalezscale), t->scale.z);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (rotxscale), t->rotate.x);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (rotyscale), t->rotate.y);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (rotzscale), t->rotate.z);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (posxscale), t->translate.x);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (posyscale), t->translate.y);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (poszscale), t->translate.z);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (turbulencescale),
+ t->turbulence.x);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (expscale), t->exp);
+
+ drawcolor1 (NULL);
+ drawcolor2 (NULL);
+
+ l = textures;
+ while (l->s)
+ {
+ if (l->n == t->type)
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (texturemenu),
+ l->index);
+ break;
+ }
+ l++;
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (typemenu), t->majtype);
+
+ noupdate = FALSE;
+}
+
+static void
+selectitem (GtkTreeSelection *treeselection,
+ gpointer data)
+{
+ setvals (currenttexture ());
+}
+
+static void
+addtexture (void)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ gint n = s.com.numtexture;
+
+ if (n == MAXTEXTUREPEROBJ - 1)
+ return;
+
+ setdefaults (&s.com.texture[n]);
+
+ list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ TYPE, mklabel (&s.com.texture[n]),
+ TEXTURE, &s.com.texture[n],
+ -1);
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
+ &iter);
+
+ s.com.numtexture++;
+
+ restartrender ();
+}
+
+static void
+duptexture (void)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ texture *t = currenttexture ();
+ gint n = s.com.numtexture;
+
+ if (n == MAXTEXTUREPEROBJ - 1)
+ return;
+ if (!t)
+ return;
+
+ s.com.texture[n] = *t;
+
+ list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ TYPE, mklabel (&s.com.texture[n]),
+ TEXTURE, &s.com.texture[n],
+ -1);
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
+ &iter);
+
+ s.com.numtexture++;
+
+ restartrender ();
+}
+
+static void
+rebuildlist (void)
+{
+ GtkListStore *list_store;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ gint n;
+
+ sel = gtk_tree_view_get_selection (texturelist);
+
+ for (n = 0; n < s.com.numtexture; n++)
+ {
+ if (s.com.numtexture && (s.com.texture[n].majtype < 0))
+ {
+ gint i;
+
+ for (i = n; i < s.com.numtexture - 1; i++)
+ s.com.texture[i] = s.com.texture[i + 1];
+
+ s.com.numtexture--;
+ n--;
+ }
+ }
+
+ list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));
+
+ for (n = 0; n < s.com.numtexture; n++)
+ {
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ TYPE, mklabel (&s.com.texture[n]),
+ TEXTURE, &s.com.texture[n],
+ -1);
+ }
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
+ gtk_tree_selection_select_iter (sel, &iter);
+
+ restartrender ();
+}
+
+static void
+deltexture (void)
+{
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ texture *t = NULL;
+
+ sel = gtk_tree_view_get_selection (texturelist);
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ model = gtk_tree_view_get_model (texturelist);
+
+ gtk_tree_model_get (model, &iter,
+ TEXTURE, &t,
+ -1);
+ t->majtype = -1;
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+ }
+
+ restartrender ();
+}
+
+static void
+loadit (const gchar * fn)
+{
+ FILE *f;
+ gchar endbuf[21 * (G_ASCII_DTOSTR_BUF_SIZE + 1)];
+ gchar *end = endbuf;
+ gchar line[1024];
+ gchar fmt_str[16];
+ gint i;
+ texture *t;
+ gint majtype, type;
+
+ f = g_fopen (fn, "rt");
+ if (! f)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (fn), g_strerror (errno));
+ return;
+ }
+
+ if (2 != fscanf (f, "%d %d", &majtype, &type) || majtype < 0 || majtype > 2)
+ {
+ g_message (_("File '%s' is not a valid save file."),
+ gimp_filename_to_utf8 (fn));
+ fclose (f);
+ return;
+ }
+
+ rewind (f);
+
+ s.com.numtexture = 0;
+
+ snprintf (fmt_str, sizeof (fmt_str), "%%d %%d %%%" G_GSIZE_FORMAT "s", sizeof (endbuf) - 1);
+
+ while (!feof (f))
+ {
+
+ if (!fgets (line, 1023, f))
+ break;
+
+ i = s.com.numtexture;
+ t = &s.com.texture[i];
+ setdefaults (t);
+
+ if (sscanf (line, fmt_str, &t->majtype, &t->type, end) != 3)
+ t->color1.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color1.y = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color1.z = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color1.w = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color2.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color2.y = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color2.z = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->color2.w = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->oscale = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->turbulence.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->amount = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->exp = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->scale.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->scale.y = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->scale.z = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->rotate.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->rotate.y = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->rotate.z = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->translate.x = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->translate.y = g_ascii_strtod (end, &end);
+ if (end && errno != ERANGE)
+ t->translate.z = g_ascii_strtod (end, &end);
+
+ s.com.numtexture++;
+ }
+
+ fclose (f);
+}
+
+static void
+loadpreset_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ GtkTreeModel *model = gtk_tree_view_get_model (texturelist);
+ gchar *name;
+
+ name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ gtk_list_store_clear (GTK_LIST_STORE (model));
+
+ loadit (name);
+ g_free (name);
+
+ rebuildlist ();
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+static void
+saveit (const gchar *fn)
+{
+ gint i;
+ FILE *f;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ f = g_fopen (fn, "wt");
+ if (!f)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (fn), g_strerror (errno));
+ return;
+ }
+
+ for (i = 0; i < s.com.numtexture; i++)
+ {
+ texture *t = &s.com.texture[i];
+
+ if (t->majtype < 0)
+ continue;
+
+ fprintf (f, "%d %d", t->majtype, t->type);
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.y));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.z));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.w));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.y));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.z));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.w));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->oscale));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->turbulence.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->amount));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->exp));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.y));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.z));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.y));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.z));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.x));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.y));
+ fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.z));
+ fprintf (f, "\n");
+ }
+
+ fclose (f);
+}
+
+static void
+savepreset_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ saveit (name);
+ g_free (name);
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+static void
+loadpreset (GtkWidget *widget,
+ GtkWidget *parent)
+{
+ fileselect (GTK_FILE_CHOOSER_ACTION_OPEN, parent);
+}
+
+static void
+savepreset (GtkWidget *widget,
+ GtkWidget *parent)
+{
+ fileselect (GTK_FILE_CHOOSER_ACTION_SAVE, parent);
+}
+
+static void
+fileselect (GtkFileChooserAction action,
+ GtkWidget *parent)
+{
+ static GtkWidget *windows[2] = { NULL, NULL };
+
+ gchar *titles[] = { N_("Open File"), N_("Save File") };
+ void *handlers[] = { loadpreset_response, savepreset_response };
+
+ if (! windows[action])
+ {
+ GtkWidget *dialog = windows[action] =
+ gtk_file_chooser_dialog_new (gettext (titles[action]),
+ GTK_WINDOW (parent),
+ action,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+
+ action == GTK_FILE_CHOOSER_ACTION_OPEN ?
+ _("_Open") : _("_Save"),
+ GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &windows[action]);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (handlers[action]),
+ NULL);
+ }
+
+ gtk_window_present (GTK_WINDOW (windows[action]));
+}
+
+static void
+initworld (void)
+{
+ gint i;
+
+ memset (&world, 0, sizeof (world));
+
+ s.com.type = SPHERE;
+ s.a.x = s.a.y = s.a.z = 0.0;
+ s.r = 4.0;
+
+ /* not: world.obj[0] = s;
+ * s is a sphere so error C2115: '=' : incompatible types
+ */
+ memcpy (&world.obj[0], &s, sizeof (s));
+ world.numobj = 1;
+
+ world.obj[0].com.numtexture = 0;
+ world.obj[0].com.numnormal = 0;
+
+ for (i = 0; i < s.com.numtexture; i++)
+ {
+ common *c = &s.com;
+ common *d = &world.obj[0].com;
+ texture *t = &c->texture[i];
+ if ((t->amount <= 0.0) || (t->majtype < 0))
+ continue;
+ if (t->majtype == 0)
+ { /* Normal texture */
+ if (t->type == PHONG)
+ {
+ t->phongcolor = t->color1;
+ t->phongsize = t->oscale / 25.0;
+ }
+ d->texture[d->numtexture] = *t;
+ vmul (&d->texture[d->numtexture].scale,
+ d->texture[d->numtexture].oscale);
+ d->numtexture++;
+ }
+ else if (t->majtype == 1)
+ { /* Bumpmap */
+ d->normal[d->numnormal] = *t;
+ vmul (&d->normal[d->numnormal].scale,
+ d->texture[d->numnormal].oscale);
+ d->numnormal++;
+ }
+ else if (t->majtype == 2)
+ { /* Lightsource */
+ light l;
+ vcopy (&l.a, &t->translate);
+ vcopy (&l.color, &t->color1);
+ vmul (&l.color, t->amount);
+ world.light[world.numlight] = l;
+ world.numlight++;
+ }
+ }
+
+ world.quality = 5;
+
+ world.flags |= SMARTAMBIENT;
+ world.smartambient = 40.0;
+}
+
+static gboolean
+expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ cairo_set_source_surface (cr, buffer, 0.0, 0.0);
+
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ return TRUE;
+}
+
+static void
+restartrender (void)
+{
+ if (idle_id)
+ g_source_remove (idle_id);
+
+ idle_id = g_idle_add ((GSourceFunc) render, NULL);
+}
+
+static void
+selecttexture (GtkWidget *widget,
+ gpointer data)
+{
+ texture *t;
+
+ if (noupdate)
+ return;
+
+ t = currenttexture ();
+ if (!t)
+ return;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->type);
+
+ relabel ();
+ restartrender ();
+}
+
+static void
+selecttype (GtkWidget *widget,
+ gpointer data)
+{
+ texture *t;
+
+ if (noupdate)
+ return;
+
+ t = currenttexture ();
+ if (!t)
+ return;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->majtype);
+
+ relabel ();
+ restartrender ();
+}
+
+static void
+getscales (GtkWidget *widget,
+ gpointer data)
+{
+ gdouble f;
+ texture *t;
+
+ if (noupdate)
+ return;
+
+ t = currenttexture ();
+ if (!t)
+ return;
+
+ t->amount = gtk_adjustment_get_value (GTK_ADJUSTMENT (amountscale));
+ t->exp = gtk_adjustment_get_value (GTK_ADJUSTMENT (expscale));
+
+ f = gtk_adjustment_get_value (GTK_ADJUSTMENT (turbulencescale));
+ vset (&t->turbulence, f, f, f);
+
+ t->oscale = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalescale));
+
+ t->scale.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalexscale));
+ t->scale.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (scaleyscale));
+ t->scale.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (scalezscale));
+
+ t->rotate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotxscale));
+ t->rotate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotyscale));
+ t->rotate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (rotzscale));
+
+ t->translate.x = gtk_adjustment_get_value (GTK_ADJUSTMENT (posxscale));
+ t->translate.y = gtk_adjustment_get_value (GTK_ADJUSTMENT (posyscale));
+ t->translate.z = gtk_adjustment_get_value (GTK_ADJUSTMENT (poszscale));
+
+ restartrender ();
+}
+
+
+static void
+color1_changed (GimpColorButton *button)
+{
+ texture *t = currenttexture ();
+
+ if (t)
+ {
+ GimpRGB color;
+
+ gimp_color_button_get_color (button, &color);
+
+ t->color1.x = color.r;
+ t->color1.y = color.g;
+ t->color1.z = color.b;
+ t->color1.w = color.a;
+
+ restartrender ();
+ }
+}
+
+static void
+color2_changed (GimpColorButton *button)
+{
+ texture *t = currenttexture ();
+
+ if (t)
+ {
+ GimpRGB color;
+
+ gimp_color_button_get_color (button, &color);
+
+ t->color2.x = color.r;
+ t->color2.y = color.g;
+ t->color2.z = color.b;
+ t->color2.w = color.a;
+
+ restartrender ();
+ }
+}
+
+static void
+drawcolor1 (GtkWidget *w)
+{
+ static GtkWidget *lastw = NULL;
+
+ GimpRGB color;
+ texture *t = currenttexture ();
+
+ if (w)
+ lastw = w;
+ else
+ w = lastw;
+
+ if (!w)
+ return;
+ if (!t)
+ return;
+
+ gimp_rgba_set (&color,
+ t->color1.x, t->color1.y, t->color1.z, t->color1.w);
+
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color);
+}
+
+static void
+drawcolor2 (GtkWidget *w)
+{
+ static GtkWidget *lastw = NULL;
+
+ GimpRGB color;
+ texture *t = currenttexture ();
+
+ if (w)
+ lastw = w;
+ else
+ w = lastw;
+
+ if (!w)
+ return;
+ if (!t)
+ return;
+
+ gimp_rgba_set (&color,
+ t->color2.x, t->color2.y, t->color2.z, t->color2.w);
+
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), &color);
+}
+
+static gboolean do_run = FALSE;
+
+static void
+sphere_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ s.com.numtexture = 3;
+
+ setdefaults (&s.com.texture[0]);
+ setdefaults (&s.com.texture[1]);
+ setdefaults (&s.com.texture[2]);
+
+ s.com.texture[1].majtype = 2;
+ vset (&s.com.texture[1].color1, 1, 1, 1);
+ vset (&s.com.texture[1].translate, -15, -15, -15);
+
+ s.com.texture[2].majtype = 2;
+ vset (&s.com.texture[2].color1, 0, 0.4, 0.4);
+ vset (&s.com.texture[2].translate, 15, 15, -15);
+
+ gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)));
+
+ rebuildlist ();
+ break;
+
+ case GTK_RESPONSE_OK:
+ if (idle_id)
+ {
+ g_source_remove (idle_id);
+ idle_id = 0;
+ }
+
+ do_run = TRUE;
+
+ default:
+ gtk_widget_hide (widget);
+ gtk_main_quit ();
+ break;
+ }
+}
+
+static GtkWidget *
+makewindow (void)
+{
+ GtkListStore *store;
+ GtkTreeViewColumn *col;
+ GtkTreeSelection *selection;
+ GtkWidget *window;
+ GtkWidget *main_hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *scrolled;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GtkWidget *list;
+ GimpRGB rgb = { 0, 0, 0, 0 };
+
+ window = gimp_dialog_new (_("Sphere Designer"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (window));
+
+ g_signal_connect (window, "response",
+ G_CALLBACK (sphere_response),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ drawarea = gtk_drawing_area_new ();
+ gtk_container_add (GTK_CONTAINER (frame), drawarea);
+ gtk_widget_set_size_request (drawarea, PREVIEWSIZE, PREVIEWSIZE);
+ gtk_widget_show (drawarea);
+
+ g_signal_connect (drawarea, "expose-event",
+ G_CALLBACK (expose_event), NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (loadpreset),
+ window);
+
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (savepreset),
+ window);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_end (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled);
+
+ store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ texturelist = GTK_TREE_VIEW (list);
+
+ selection = gtk_tree_view_get_selection (texturelist);
+
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (selectitem),
+ NULL);
+
+ gtk_widget_set_size_request (list, -1, 150);
+ gtk_container_add (GTK_CONTAINER (scrolled), list);
+ gtk_widget_show (list);
+
+ col = gtk_tree_view_column_new_with_attributes (_("Layers"),
+ gtk_cell_renderer_text_new (),
+ "text", TYPE,
+ NULL);
+ gtk_tree_view_append_column (texturelist, col);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("_New"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (addtexture), NULL);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_mnemonic (_("D_uplicate"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (duptexture), NULL);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_mnemonic (_("_Delete"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (deltexture), NULL);
+ gtk_widget_show (button);
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_hbox);
+
+ frame = gimp_frame_new (_("Properties"));
+ gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (7, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 2);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ typemenu = gimp_int_combo_box_new (_("Texture"), 0,
+ _("Bump"), 1,
+ _("Light"), 2,
+ NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (typemenu), 0,
+ G_CALLBACK (selecttype),
+ NULL);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Type:"), 0.0, 0.5,
+ typemenu, 2, FALSE);
+
+ texturemenu = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+ {
+ struct textures_t *t;
+
+ for (t = textures; t->s; t++)
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (texturemenu),
+ GIMP_INT_STORE_VALUE, t->n,
+ GIMP_INT_STORE_LABEL, gettext (t->s),
+ -1);
+ }
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (texturemenu), 0,
+ G_CALLBACK (selecttexture),
+ NULL);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Texture:"), 0.0, 0.5,
+ texturemenu, 2, FALSE);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Colors:"), 0.0, 0.5,
+ hbox, 2, FALSE);
+
+ button = gimp_color_button_new (_("Color Selection Dialog"),
+ COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+ drawcolor1 (button);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (color1_changed),
+ NULL);
+
+ button = gimp_color_button_new (_("Color Selection Dialog"),
+ COLORBUTTONWIDTH, COLORBUTTONHEIGHT, &rgb,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+ drawcolor2 (button);
+
+ g_signal_connect (button, "color-changed",
+ G_CALLBACK (color2_changed),
+ NULL);
+
+ scalescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Scale:"),
+ 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (scalescale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ turbulencescale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Turbulence:"),
+ 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (turbulencescale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ amountscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Amount:"),
+ 100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (amountscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ expscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Exp.:"),
+ 100, -1, 1.0, 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (expscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ frame = gimp_frame_new (_("Transformations"));
+ gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (9, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 2);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ scalexscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 0, _("Scale X:"),
+ 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (scalexscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ scaleyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 1, _("Scale Y:"),
+ 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (scaleyscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+ scalezscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 2, _("Scale Z:"),
+ 100, -1, 1.0, 0.0, 10.0, 0.1, 1.0, 2,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (scalezscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ rotxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 3, _("Rotate X:"),
+ 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (rotxscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ rotyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, _("Rotate Y:"),
+ 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (rotyscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ rotzscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, _("Rotate Z:"),
+ 100, -1, 0.0, 0.0, 360.0, 1.0, 10.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (rotzscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ posxscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, _("Position X:"),
+ 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (posxscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ posyscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 7, _("Position Y:"),
+ 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (posyscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ poszscale = gimp_scale_entry_new (GTK_TABLE (table), 0, 8, _("Position Z:"),
+ 100, -1, 0.0, -20.0, 20.0, 0.1, 1.0, 1,
+ TRUE, 0.0, 0.0, NULL, NULL);
+ g_signal_connect (poszscale, "value-changed",
+ G_CALLBACK (getscales),
+ NULL);
+
+ gtk_widget_show (window);
+
+ return window;
+}
+
+static guchar
+pixelval (gdouble v)
+{
+ v += 0.5;
+ if (v < 0.0)
+ return 0;
+ if (v > 255.0)
+ return 255;
+ return v;
+}
+
+static gboolean
+render (void)
+{
+ GimpVector4 col;
+ guchar *dest_row;
+ ray r;
+ gint x, y, p;
+ gint tx = PREVIEWSIZE;
+ gint ty = PREVIEWSIZE;
+ gint bpp = 4;
+
+ idle_id = 0;
+
+ initworld ();
+
+ r.v1.z = -10.0;
+ r.v2.z = 0.0;
+
+ if (world.obj[0].com.numtexture > 0)
+ {
+ cairo_surface_flush (buffer);
+
+ for (y = 0; y < ty; y++)
+ {
+ dest_row = img + y * img_stride;
+
+ for (x = 0; x < tx; x++)
+ {
+ gint g, gridsize = 16;
+
+ g = ((x / gridsize + y / gridsize) % 2) * 60 + 100;
+
+ r.v1.x = r.v2.x = 8.5 * (x / (float) (tx - 1) - 0.5);
+ r.v1.y = r.v2.y = 8.5 * (y / (float) (ty - 1) - 0.5);
+
+ p = x * bpp;
+
+ traceray (&r, &col, 10, 1.0);
+
+ if (col.w < 0.0)
+ col.w = 0.0;
+ else if (col.w > 1.0)
+ col.w = 1.0;
+
+ GIMP_CAIRO_RGB24_SET_PIXEL ((dest_row + p),
+ pixelval (255 * col.x) * col.w + g * (1.0 - col.w),
+ pixelval (255 * col.y) * col.w + g * (1.0 - col.w),
+ pixelval (255 * col.z) * col.w + g * (1.0 - col.w));
+ }
+ }
+
+ cairo_surface_mark_dirty (buffer);
+ }
+
+ gtk_widget_queue_draw (drawarea);
+
+ return FALSE;
+}
+
+static void
+realrender (gint32 drawable_ID)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ gint x, y;
+ ray r;
+ GimpVector4 rcol;
+ gint width, height;
+ gint x1, y1;
+ guchar *dest;
+ gint bpp;
+ guchar *buffer, *ibuffer;
+
+ initworld ();
+
+ r.v1.z = -10.0;
+ r.v2.z = 0.0;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &x1, &y1, &width, &height))
+ return;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("Y'A u8");
+ else
+ format = babl_format ("Y' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ buffer = g_malloc (width * 4);
+ ibuffer = g_malloc (width * 4);
+
+ gimp_progress_init (_("Rendering sphere"));
+
+ for (y = 0; y < height; y++)
+ {
+ dest = buffer;
+ for (x = 0; x < width; x++)
+ {
+ r.v1.x = r.v2.x = 8.1 * (x / (float) (width - 1) - 0.5);
+ r.v1.y = r.v2.y = 8.1 * (y / (float) (height - 1) - 0.5);
+
+ traceray (&r, &rcol, 10, 1.0);
+ dest[0] = pixelval (255 * rcol.x);
+ dest[1] = pixelval (255 * rcol.y);
+ dest[2] = pixelval (255 * rcol.z);
+ dest[3] = pixelval (255 * rcol.w);
+ dest += 4;
+ }
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 1.0,
+ format, ibuffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (x = 0; x < width; x++)
+ {
+ gint k, dx = x * 4, sx = x * bpp;
+ gfloat a = buffer[dx + 3] / 255.0;
+
+ for (k = 0; k < bpp; k++)
+ {
+ ibuffer[sx + k] =
+ buffer[dx + k] * a + ibuffer[sx + k] * (1.0 - a);
+ }
+ }
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, y1 + y, width, 1), 0,
+ format, ibuffer,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((gdouble) y / (gdouble) height);
+ }
+
+ gimp_progress_update (1.0);
+ g_free (buffer);
+ g_free (ibuffer);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, x1, y1, width, height);
+}
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create an image of a textured sphere"),
+ "This plug-in can be used to create textured and/or "
+ "bumpmapped spheres, and uses a small lightweight "
+ "raytracer to perform the task with good quality",
+ "Vidar Madsen",
+ "Vidar Madsen",
+ "1999",
+ N_("Sphere _Designer..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render");
+}
+
+static gboolean
+sphere_main (gint32 drawable_ID)
+{
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ img_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, PREVIEWSIZE);
+ img = g_malloc0 (img_stride * PREVIEWSIZE);
+
+ buffer = cairo_image_surface_create_for_data (img, CAIRO_FORMAT_RGB24,
+ PREVIEWSIZE,
+ PREVIEWSIZE,
+ img_stride);
+
+ makewindow ();
+
+ if (s.com.numtexture == 0)
+ {
+ /* Setup and use default list */
+ sphere_response (NULL, RESPONSE_RESET, NULL);
+ }
+ else
+ {
+ /* Reuse the list from a previous invocation */
+ rebuildlist ();
+ }
+
+ gtk_main ();
+
+ cairo_surface_destroy (buffer);
+ g_free (img);
+
+ return do_run;
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint x, y, w, h;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ if (! gimp_drawable_mask_intersect (drawable_ID, &x, &y, &w, &h))
+ {
+ g_message (_("Region selected for plug-in is empty"));
+ return;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ s.com.numtexture = 0;
+ gimp_get_data (PLUG_IN_PROC, &s);
+ if (! sphere_main (drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ s.com.numtexture = 0;
+ gimp_get_data (PLUG_IN_PROC, &s);
+ if (s.com.numtexture == 0)
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ default:
+ /* Not implemented yet... */
+ return;
+ }
+
+ gimp_set_data (PLUG_IN_PROC, &s, sizeof (s));
+
+ realrender (drawable_ID);
+ gimp_displays_flush ();
+
+ values[0].data.d_status = status;
+}
+
+MAIN ()
diff --git a/plug-ins/common/tile-small.c b/plug-ins/common/tile-small.c
new file mode 100644
index 0000000..534faab
--- /dev/null
+++ b/plug-ins/common/tile-small.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Tileit - This plugin take an image and makes repeated copies of it.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * A fair proprotion of this code was taken from the Whirl plug-in
+ * which was copyrighted by Federico Mena Quintero (as below).
+ *
+ * Whirl plug-in --- distort an image into a whirlpool
+ * Copyright (C) 1997 Federico Mena Quintero
+ *
+ */
+
+/* Change log:-
+ * 0.2 Added new functions to allow "editing" of the tile patten.
+ *
+ * 0.1 First version released.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PLUG_IN_PROC "plug-in-small-tiles"
+#define PLUG_IN_BINARY "tile-small"
+#define PLUG_IN_ROLE "gimp-tile-small"
+
+/***** Magic numbers *****/
+
+#define PREVIEW_SIZE 128
+#define SCALE_WIDTH 80
+
+#define MAX_SEGS 6
+
+#define PREVIEW_MASK GDK_EXPOSURE_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_MOTION_MASK
+
+/* Variables set in dialog box */
+typedef struct data
+{
+ gint numtiles;
+} TileItVals;
+
+typedef struct
+{
+ GtkWidget *preview;
+ guchar preview_row[PREVIEW_SIZE * 4];
+ gint img_bpp;
+ guchar *pv_cache;
+} TileItInterface;
+
+static TileItInterface tint =
+{
+ NULL, /* Preview */
+ {
+ '4',
+ 'u'
+ }, /* Preview_row */
+ 4, /* bpp of drawable */
+ NULL
+};
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean tileit_dialog (gint32 drawable_ID);
+
+static void tileit_scale_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+static void tileit_exp_update (GtkWidget *widget,
+ gpointer value);
+static void tileit_exp_update_f (GtkWidget *widget,
+ gpointer value);
+
+static void tileit_reset (GtkWidget *widget,
+ gpointer value);
+static void tileit_radio_update (GtkWidget *widget,
+ gpointer data);
+static void tileit_hvtoggle_update (GtkWidget *widget,
+ gpointer data);
+
+static void do_tiles (gint32 drawable_ID);
+static gint tiles_xy (gint width,
+ gint height,
+ gint x,
+ gint y,
+ gint *nx,
+ gint *ny);
+static void all_update (void);
+static void alt_update (void);
+static void explicit_update (gboolean);
+
+static void dialog_update_preview (void);
+static void cache_preview (gint32 drawable_ID);
+static gboolean tileit_preview_expose (GtkWidget *widget,
+ GdkEvent *event);
+static gboolean tileit_preview_events (GtkWidget *widget,
+ GdkEvent *event);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* Values when first invoked */
+static TileItVals itvals =
+{
+ 2
+};
+
+/* Structures for call backs... */
+/* The "explicit tile" & family */
+typedef enum
+{
+ ALL,
+ ALT,
+ EXPLICIT
+} AppliedTo;
+
+typedef struct
+{
+ AppliedTo type;
+
+ gint x; /* X - pos of tile */
+ gint y; /* Y - pos of tile */
+ GtkObject *r_adj; /* row adjustment */
+ GtkObject *c_adj; /* column adjustment */
+ GtkWidget *applybut; /* The apply button */
+} Exp_Call;
+
+static Exp_Call exp_call =
+{
+ ALL,
+ -1,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+};
+
+/* The reset button needs to know some toggle widgets.. */
+
+typedef struct
+{
+ GtkWidget *htoggle;
+ GtkWidget *vtoggle;
+} Reset_Call;
+
+static Reset_Call res_call =
+{
+ NULL,
+ NULL,
+};
+
+/* 2D - Array that holds the actions for each tile */
+/* Action type on cell */
+#define HORIZONTAL 0x1
+#define VERTICAL 0x2
+
+static gint tileactions[MAX_SEGS][MAX_SEGS];
+
+/* What actions buttons toggled */
+static gint do_horz = FALSE;
+static gint do_vert = FALSE;
+static gint opacity = 100;
+
+/* Stuff for the preview bit */
+static gint sel_x1, sel_y1, sel_x2, sel_y2;
+static gint sel_width, sel_height;
+static gint preview_width, preview_height;
+static gboolean has_alpha;
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "num-tiles", "Number of tiles to make" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Tile image into smaller versions of the original"),
+ "More here later",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1997",
+ N_("_Small Tiles..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint pwidth;
+ gint pheight;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &sel_x1, &sel_y1,
+ &sel_width, &sel_height))
+ {
+ g_message (_("Region selected for filter is empty."));
+ return;
+ }
+
+ sel_x2 = sel_x1 + sel_width;
+ sel_y2 = sel_y1 + sel_height;
+
+ /* Calculate preview size */
+
+ if (sel_width > sel_height)
+ {
+ pwidth = MIN (sel_width, PREVIEW_SIZE);
+ pheight = sel_height * pwidth / sel_width;
+ }
+ else
+ {
+ pheight = MIN (sel_height, PREVIEW_SIZE);
+ pwidth = sel_width * pheight / sel_height;
+ }
+
+ preview_width = MAX (pwidth, 2); /* Min size is 2 */
+ preview_height = MAX (pheight, 2);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &itvals);
+ if (! tileit_dialog (drawable_ID))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 4)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ itvals.numtiles = param[3].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &itvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ /* Set the tile cache size */
+
+ gimp_progress_init (_("Tiling"));
+
+ do_tiles (drawable_ID);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &itvals, sizeof (TileItVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+tileit_dialog (gint drawable_ID)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *table2;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *spinbutton;
+ GtkObject *adj;
+ GtkObject *scale;
+ GtkWidget *toggle;
+ GSList *orientation_group = NULL;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ cache_preview (drawable_ID); /* Get the preview image */
+
+ dlg = gimp_dialog_new (_("Small Tiles"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ tint.preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tint.preview, preview_width, preview_height);
+ gtk_widget_set_events (GTK_WIDGET (tint.preview), PREVIEW_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), tint.preview);
+ gtk_widget_show (tint.preview);
+
+ g_signal_connect_after (tint.preview, "expose-event",
+ G_CALLBACK (tileit_preview_expose),
+ NULL);
+ g_signal_connect (tint.preview, "event",
+ G_CALLBACK (tileit_preview_events),
+ NULL);
+
+ /* Area for buttons etc */
+
+ frame = gimp_frame_new (_("Flip"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Horizontal"));
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (tileit_hvtoggle_update),
+ &do_horz);
+
+ res_call.htoggle = toggle;
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Vertical"));
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (tileit_hvtoggle_update),
+ &do_vert);
+
+ res_call.vtoggle = toggle;
+
+ button = gtk_button_new_with_mnemonic (_("_Reset"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (tileit_reset),
+ &res_call);
+
+ /* Table for the inner widgets..*/
+ table = gtk_table_new (4, 4, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ toggle = gtk_radio_button_new_with_mnemonic (orientation_group,
+ _("A_ll tiles"));
+ orientation_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 4, 0, 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (ALL));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (tileit_radio_update),
+ &exp_call.type);
+
+ toggle = gtk_radio_button_new_with_mnemonic (orientation_group,
+ _("Al_ternate tiles"));
+ orientation_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 4, 1, 2,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (ALT));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (tileit_radio_update),
+ &exp_call.type);
+
+ toggle = gtk_radio_button_new_with_mnemonic (orientation_group,
+ _("_Explicit tile"));
+ orientation_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 4,
+ GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle);
+
+ label = gtk_label_new_with_mnemonic (_("Ro_w:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 2, 3,
+ GTK_FILL | GTK_SHRINK , GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ g_object_bind_property (toggle, "active",
+ label, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ spinbutton = gimp_spin_button_new (&adj, 2, 1, 6, 1, 1, 0, 1, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 2, 3,
+ GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (tileit_exp_update_f),
+ &exp_call);
+
+ exp_call.r_adj = adj;
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ label = gtk_label_new_with_mnemonic (_("Col_umn:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE (table), label, 1, 2, 3, 4,
+ GTK_FILL , GTK_FILL, 0, 0);
+
+ g_object_bind_property (toggle, "active",
+ label, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ spinbutton = gimp_spin_button_new (&adj, 2, 1, 6, 1, 1, 0, 1, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 3, 4,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (tileit_exp_update_f),
+ &exp_call);
+
+ exp_call.c_adj = adj;
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (EXPLICIT));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (tileit_radio_update),
+ &exp_call.type);
+
+ button = gtk_button_new_with_mnemonic (_("_Apply"));
+ gtk_table_attach (GTK_TABLE (table), button, 3, 4, 2, 4, 0, 0, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (tileit_exp_update),
+ &exp_call);
+
+ exp_call.applybut = button;
+
+ g_object_bind_property (toggle, "active",
+ spinbutton, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /* Widget for selecting the Opacity */
+
+ table2 = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table2), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table2, FALSE, FALSE, 0);
+ gtk_widget_show (table2);
+
+ scale = gimp_scale_entry_new (GTK_TABLE (table2), 0, 0,
+ _("O_pacity:"), SCALE_WIDTH, -1,
+ opacity, 0, 100, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale, "value-changed",
+ G_CALLBACK (tileit_scale_update),
+ &opacity);
+
+ /* Lower frame saying howmany segments */
+ frame = gimp_frame_new (_("Number of Segments"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ gtk_widget_set_sensitive (table2, gimp_drawable_has_alpha (drawable_ID));
+
+ scale = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ "_n²", SCALE_WIDTH, -1,
+ itvals.numtiles, 2, MAX_SEGS, 1, 1, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale, "value-changed",
+ G_CALLBACK (tileit_scale_update),
+ &itvals.numtiles);
+
+ gtk_widget_show (dlg);
+ dialog_update_preview ();
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+static void
+tileit_hvtoggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ switch (exp_call.type)
+ {
+ case ALL:
+ /* Clear current settings */
+ memset (tileactions, 0, sizeof (tileactions));
+ all_update ();
+ break;
+
+ case ALT:
+ /* Clear current settings */
+ memset (tileactions, 0, sizeof (tileactions));
+ alt_update ();
+ break;
+
+ case EXPLICIT:
+ break;
+ }
+
+ dialog_update_preview ();
+}
+
+static gboolean
+tileit_preview_expose (GtkWidget *widget,
+ GdkEvent *event)
+{
+ if (exp_call.type == EXPLICIT)
+ {
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (tint.preview));
+ gdouble width = (gdouble) preview_width / (gdouble) itvals.numtiles;
+ gdouble height = (gdouble) preview_height / (gdouble) itvals.numtiles;
+ gdouble x , y;
+
+ x = width * (exp_call.x - 1);
+ y = height * (exp_call.y - 1);
+
+ cairo_rectangle (cr, x + 1.5, y + 1.5, width - 2, height - 2);
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.6);
+ cairo_stroke_preserve (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8);
+ cairo_stroke_preserve (cr);
+
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+
+static void
+exp_need_update (gint nx,
+ gint ny)
+{
+ if (nx <= 0 || nx > itvals.numtiles || ny <= 0 || ny > itvals.numtiles)
+ return;
+
+ if (nx != exp_call.x || ny != exp_call.y)
+ {
+ exp_call.x = nx;
+ exp_call.y = ny;
+ gtk_widget_queue_draw (tint.preview);
+
+ g_signal_handlers_block_by_func (exp_call.c_adj,
+ tileit_exp_update_f,
+ &exp_call);
+ g_signal_handlers_block_by_func (exp_call.r_adj,
+ tileit_exp_update_f,
+ &exp_call);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (exp_call.c_adj), nx);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (exp_call.r_adj), ny);
+
+ g_signal_handlers_unblock_by_func (exp_call.c_adj,
+ tileit_exp_update_f,
+ &exp_call);
+ g_signal_handlers_unblock_by_func (exp_call.r_adj,
+ tileit_exp_update_f,
+ &exp_call);
+ }
+}
+
+static gboolean
+tileit_preview_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ gint nx, ny;
+ gint twidth = preview_width / itvals.numtiles;
+ gint theight = preview_height / itvals.numtiles;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ bevent = (GdkEventButton *) event;
+ nx = bevent->x/twidth + 1;
+ ny = bevent->y/theight + 1;
+ exp_need_update (nx, ny);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = (GdkEventMotion *) event;
+ if ( !mevent->state )
+ break;
+ if (mevent->x < 0 || mevent->y < 0)
+ break;
+ nx = mevent->x/twidth + 1;
+ ny = mevent->y/theight + 1;
+ exp_need_update (nx, ny);
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+explicit_update (gboolean settile)
+{
+ gint x,y;
+
+ /* Make sure bounds are OK */
+ y = ROUND (gtk_adjustment_get_value (GTK_ADJUSTMENT (exp_call.r_adj)));
+ if (y > itvals.numtiles || y <= 0)
+ {
+ y = itvals.numtiles;
+ }
+ x = ROUND (gtk_adjustment_get_value (GTK_ADJUSTMENT (exp_call.c_adj)));
+ if (x > itvals.numtiles || x <= 0)
+ {
+ x = itvals.numtiles;
+ }
+
+ /* Set it */
+ if (settile)
+ tileactions[x-1][y-1] = (((do_horz) ? HORIZONTAL : 0) |
+ ((do_vert) ? VERTICAL : 0));
+
+ exp_call.x = x;
+ exp_call.y = y;
+}
+
+static void
+all_update (void)
+{
+ gint x,y;
+
+ for (x = 0 ; x < MAX_SEGS; x++)
+ for (y = 0 ; y < MAX_SEGS; y++)
+ tileactions[x][y] |= (((do_horz) ? HORIZONTAL : 0) |
+ ((do_vert) ? VERTICAL : 0));
+}
+
+static void
+alt_update (void)
+{
+ gint x,y;
+
+ for (x = 0 ; x < MAX_SEGS; x++)
+ for (y = 0 ; y < MAX_SEGS; y++)
+ if (!((x + y) % 2))
+ tileactions[x][y] |= (((do_horz) ? HORIZONTAL : 0) |
+ ((do_vert) ? VERTICAL : 0));
+}
+
+static void
+tileit_radio_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_radio_button_update (widget, data);
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ switch (exp_call.type)
+ {
+ case ALL:
+ /* Clear current settings */
+ memset (tileactions, 0, sizeof (tileactions));
+ all_update ();
+ break;
+
+ case ALT:
+ /* Clear current settings */
+ memset (tileactions, 0, sizeof (tileactions));
+ alt_update ();
+ break;
+
+ case EXPLICIT:
+ explicit_update (FALSE);
+ break;
+ }
+
+ dialog_update_preview ();
+ }
+}
+
+
+static void
+tileit_scale_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_int_adjustment_update (adjustment, data);
+
+ dialog_update_preview ();
+}
+
+static void
+tileit_reset (GtkWidget *widget,
+ gpointer data)
+{
+ Reset_Call *r = (Reset_Call *) data;
+
+ memset (tileactions, 0, sizeof (tileactions));
+
+ g_signal_handlers_block_by_func (r->htoggle,
+ tileit_hvtoggle_update,
+ &do_horz);
+ g_signal_handlers_block_by_func (r->vtoggle,
+ tileit_hvtoggle_update,
+ &do_vert);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (r->htoggle), FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (r->vtoggle), FALSE);
+
+ g_signal_handlers_unblock_by_func (r->htoggle,
+ tileit_hvtoggle_update,
+ &do_horz);
+ g_signal_handlers_unblock_by_func (r->vtoggle,
+ tileit_hvtoggle_update,
+ &do_vert);
+
+ do_horz = do_vert = FALSE;
+
+ dialog_update_preview ();
+}
+
+
+/* Could avoid almost dup. functions by using a field in the data
+ * passed. Must still pass the data since used in sig blocking func.
+ */
+
+static void
+tileit_exp_update (GtkWidget *widget,
+ gpointer applied)
+{
+ explicit_update (TRUE);
+ dialog_update_preview ();
+}
+
+static void
+tileit_exp_update_f (GtkWidget *widget,
+ gpointer applied)
+{
+ explicit_update (FALSE);
+ dialog_update_preview ();
+}
+
+/* Cache the preview image - updates are a lot faster. */
+/* The preview_cache will contain the small image */
+
+static void
+cache_preview (gint32 drawable_ID)
+{
+ GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_ID);
+ const Babl *format;
+ gdouble scale;
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ tint.img_bpp = babl_format_get_bytes_per_pixel (format);
+
+ tint.pv_cache = g_new (guchar, preview_width * preview_height * 4);
+
+ scale = (gdouble) preview_width / (gdouble) sel_width;
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (scale * sel_x1, scale * sel_y1,
+ preview_width, preview_height),
+ scale,
+ format, tint.pv_cache,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+}
+
+static void
+do_tiles (gint32 drawable_ID)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gboolean has_alpha;
+ gint progress, max_progress;
+ gint bpp;
+ guchar pixel[4];
+ gint nc, nr;
+ gint i;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (has_alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ progress = 0;
+ max_progress = sel_width * sel_height;
+
+ iter = gegl_buffer_iterator_new (dest_buffer,
+ GEGL_RECTANGLE (sel_x1, sel_y1,
+ sel_width, sel_height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle dest_roi = iter->items[0].roi;
+ guchar *dest_row = iter->items[0].data;
+ gint row;
+
+ for (row = dest_roi.y; row < (dest_roi.y + dest_roi.height); row++)
+ {
+ guchar *dest = dest_row;
+ gint col;
+
+ for (col = dest_roi.x; col < (dest_roi.x + dest_roi.width); col++)
+ {
+ tiles_xy (sel_width,
+ sel_height,
+ col - sel_x1,
+ row - sel_y1,
+ &nc, &nr);
+
+ gegl_buffer_sample (src_buffer, nc + sel_x1, nr + sel_y1, NULL,
+ pixel, format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ for (i = 0; i < bpp; i++)
+ dest[i] = pixel[i];
+
+ if (has_alpha)
+ dest[bpp - 1] = (pixel[bpp - 1] * opacity) / 100;
+
+ dest += bpp;
+ }
+
+ dest_row += dest_roi.width * bpp;
+ }
+
+ progress += dest_roi.width * dest_roi.height;
+ gimp_progress_update ((double) progress / max_progress);
+ }
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID,
+ sel_x1, sel_y1, sel_width, sel_height);
+}
+
+
+/* Get the xy pos and any action */
+static gint
+tiles_xy (gint width,
+ gint height,
+ gint x,
+ gint y,
+ gint *nx,
+ gint *ny)
+{
+ gint px,py;
+ gint rnum,cnum;
+ gint actiontype;
+ gdouble rnd = 1 - (1.0 / (gdouble) itvals.numtiles) + 0.01;
+
+ rnum = y * itvals.numtiles / height;
+
+ py = (y * itvals.numtiles) % height;
+ px = (x * itvals.numtiles) % width;
+ cnum = x * itvals.numtiles / width;
+
+ if ((actiontype = tileactions[cnum][rnum]))
+ {
+ if (actiontype & VERTICAL)
+ {
+ gdouble pyr;
+
+ pyr = height - y - 1 + rnd;
+ py = ((gint) (pyr * (gdouble) itvals.numtiles)) % height;
+ }
+
+ if (actiontype & HORIZONTAL)
+ {
+ gdouble pxr;
+
+ pxr = width - x - 1 + rnd;
+ px = ((gint) (pxr * (gdouble) itvals.numtiles)) % width;
+ }
+ }
+
+ *nx = px;
+ *ny = py;
+
+ return(actiontype);
+}
+
+
+/* Given a row then shrink it down a bit */
+static void
+do_tiles_preview (guchar *dest_row,
+ guchar *src_rows,
+ gint width,
+ gint dh,
+ gint height,
+ gint bpp)
+{
+ gint x;
+ gint i;
+ gint px, py;
+ gint rnum,cnum;
+ gint actiontype;
+ gdouble rnd = 1 - (1.0 / (gdouble) itvals.numtiles) + 0.01;
+
+ rnum = dh * itvals.numtiles / height;
+
+ for (x = 0; x < width; x ++)
+ {
+
+ py = (dh*itvals.numtiles)%height;
+
+ px = (x*itvals.numtiles)%width;
+ cnum = x*itvals.numtiles/width;
+
+ if ((actiontype = tileactions[cnum][rnum]))
+ {
+ if (actiontype & VERTICAL)
+ {
+ gdouble pyr;
+ pyr = height - dh - 1 + rnd;
+ py = ((int)(pyr*(gdouble)itvals.numtiles))%height;
+ }
+
+ if (actiontype & HORIZONTAL)
+ {
+ gdouble pxr;
+ pxr = width - x - 1 + rnd;
+ px = ((int)(pxr*(gdouble)itvals.numtiles))%width;
+ }
+ }
+
+ for (i = 0 ; i < bpp; i++ )
+ dest_row[x*tint.img_bpp+i] =
+ src_rows[(px + (py*width))*bpp+i];
+
+ if (has_alpha)
+ dest_row[x*tint.img_bpp + (bpp - 1)] =
+ (dest_row[x*tint.img_bpp + (bpp - 1)]*opacity)/100;
+
+ }
+}
+
+static void
+dialog_update_preview (void)
+{
+ gint y;
+ guchar *buffer;
+
+ buffer = g_new (guchar, preview_width * preview_height * tint.img_bpp);
+
+ for (y = 0; y < preview_height; y++)
+ {
+ do_tiles_preview (tint.preview_row,
+ tint.pv_cache,
+ preview_width,
+ y,
+ preview_height,
+ tint.img_bpp);
+
+ memcpy (buffer + y* (preview_width * tint.img_bpp),
+ tint.preview_row,
+ preview_width * tint.img_bpp);
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (tint.preview),
+ 0, 0, preview_width, preview_height,
+ (tint.img_bpp>3)?GIMP_RGBA_IMAGE:GIMP_RGB_IMAGE,
+ buffer,
+ preview_width * tint.img_bpp);
+
+ g_free (buffer);
+
+ gtk_widget_queue_draw (tint.preview);
+}
diff --git a/plug-ins/common/tile.c b/plug-ins/common/tile.c
new file mode 100644
index 0000000..a74b7fe
--- /dev/null
+++ b/plug-ins/common/tile.c
@@ -0,0 +1,505 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This filter tiles an image to arbitrary width and height
+ */
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-tile"
+#define PLUG_IN_BINARY "tile"
+#define PLUG_IN_ROLE "gimp-tile"
+
+
+typedef struct
+{
+ gint new_width;
+ gint new_height;
+ gint constrain;
+ gint new_image;
+} TileVals;
+
+
+/* Declare local functions.
+ */
+static void query (void);
+
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void tile (gint32 image_id,
+ gint32 drawable_id,
+ gint32 *new_image_id,
+ gint32 *new_layer_id);
+
+static void tile_gegl (GeglBuffer *src,
+ gint src_width,
+ gint src_height,
+ GeglBuffer *dst,
+ gint dst_width,
+ gint dst_height);
+
+static gboolean tile_dialog (gint32 image_ID,
+ gint32 drawable_ID);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static TileVals tvals =
+{
+ 1, /* new_width */
+ 1, /* new_height */
+ TRUE, /* constrain */
+ TRUE /* new_image */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "new-width", "New (tiled) image width" },
+ { GIMP_PDB_INT32, "new-height", "New (tiled) image height" },
+ { GIMP_PDB_INT32, "new-image", "Create a new image?" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "new-image", "Output image (-1 if new-image == FALSE)" },
+ { GIMP_PDB_LAYER, "new-layer", "Output layer (-1 if new-image == FALSE)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create an array of copies of the image"),
+ "This function creates a new image with a single "
+ "layer sized to the specified 'new_width' and "
+ "'new_height' parameters. The specified drawable "
+ "is tiled into this layer. The new layer will have "
+ "the same type as the specified drawable and the "
+ "new image will have a corresponding base type.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1996-1997",
+ N_("_Tile..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Map");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[3];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 3;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[2].type = GIMP_PDB_LAYER;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &tvals);
+
+ /* First acquire information with a dialog */
+ if (! tile_dialog (param[1].data.d_image,
+ param[2].data.d_drawable))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ tvals.new_width = param[3].data.d_int32;
+ tvals.new_height = param[4].data.d_int32;
+ tvals.new_image = param[5].data.d_int32 ? TRUE : FALSE;
+
+ if (tvals.new_width < 1 || tvals.new_height < 1)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &tvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint32 new_layer_id;
+ gint32 new_image_id;
+
+ gimp_progress_init (_("Tiling"));
+
+ tile (param[1].data.d_image,
+ param[2].data.d_drawable,
+ &new_image_id,
+ &new_layer_id);
+
+ values[1].data.d_image = new_image_id;
+ values[2].data.d_layer = new_layer_id;
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &tvals, sizeof (TileVals));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ if (tvals.new_image)
+ gimp_display_new (values[1].data.d_image);
+ else
+ gimp_displays_flush ();
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ gegl_exit ();
+}
+
+static void
+tile_gegl (GeglBuffer *src,
+ gint src_width,
+ gint src_height,
+ GeglBuffer *dst,
+ gint dst_width,
+ gint dst_height)
+{
+ GeglNode *node;
+ GeglNode *buffer_src_node;
+ GeglNode *tile_node;
+ GeglNode *crop_src_node;
+ GeglNode *crop_dst_node;
+ GeglNode *buffer_dst_node;
+
+ GeglProcessor *processor;
+ gdouble progress;
+
+ node = gegl_node_new ();
+
+ buffer_src_node = gegl_node_new_child (node,
+ "operation", "gegl:buffer-source",
+ "buffer", src,
+ NULL);
+
+ crop_src_node = gegl_node_new_child (node,
+ "operation", "gegl:crop",
+ "width", (gdouble) src_width,
+ "height", (gdouble) src_height,
+ NULL);
+
+ tile_node = gegl_node_new_child (node,
+ "operation", "gegl:tile",
+ NULL);
+
+ crop_dst_node = gegl_node_new_child (node,
+ "operation", "gegl:crop",
+ "width", (gdouble) dst_width,
+ "height", (gdouble) dst_height,
+ NULL);
+
+ buffer_dst_node = gegl_node_new_child (node,
+ "operation", "gegl:write-buffer",
+ "buffer", dst,
+ NULL);
+
+ gegl_node_link_many (buffer_src_node,
+ crop_src_node,
+ tile_node,
+ crop_dst_node,
+ buffer_dst_node,
+ NULL);
+
+ processor = gegl_node_new_processor (buffer_dst_node, NULL);
+
+ while (gegl_processor_work (processor, &progress))
+ if (!((gint) (progress * 100.0) % 10))
+ gimp_progress_update (progress);
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (processor);
+ g_object_unref (node);
+}
+
+static void
+tile (gint32 image_id,
+ gint32 drawable_id,
+ gint32 *new_image_id,
+ gint32 *new_layer_id)
+{
+ gint32 dst_drawable_id;
+ GeglBuffer *dst_buffer;
+ GeglBuffer *src_buffer;
+ gint dst_width = tvals.new_width;
+ gint dst_height = tvals.new_height;
+ gint src_width = gimp_drawable_width (drawable_id);
+ gint src_height = gimp_drawable_height (drawable_id);
+
+ GimpImageBaseType image_type = GIMP_RGB;
+
+ /* sanity check parameters */
+ if (dst_width < 1 || dst_height < 1)
+ {
+ *new_image_id = -1;
+ *new_layer_id = -1;
+ return;
+ }
+
+ if (tvals.new_image)
+ {
+ /* create a new image */
+ gint32 precision = gimp_image_get_precision (image_id);
+
+ switch (gimp_drawable_type (drawable_id))
+ {
+ case GIMP_RGB_IMAGE:
+ case GIMP_RGBA_IMAGE:
+ image_type = GIMP_RGB;
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ case GIMP_GRAYA_IMAGE:
+ image_type = GIMP_GRAY;
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ image_type = GIMP_INDEXED;
+ break;
+ }
+
+ *new_image_id = gimp_image_new_with_precision (dst_width,
+ dst_height,
+ image_type,
+ precision);
+ gimp_image_undo_disable (*new_image_id);
+
+ /* copy the colormap, if necessary */
+ if (image_type == GIMP_INDEXED)
+ {
+ guchar *cmap;
+ gint ncols;
+
+ cmap = gimp_image_get_colormap (image_id, &ncols);
+ gimp_image_set_colormap (*new_image_id, cmap, ncols);
+ g_free (cmap);
+ }
+
+ *new_layer_id = gimp_layer_new (*new_image_id, _("Background"),
+ dst_width, dst_height,
+ gimp_drawable_type (drawable_id),
+ 100,
+ gimp_image_get_default_new_layer_mode (*new_image_id));
+
+ if (*new_layer_id == -1)
+ return;
+
+ gimp_image_insert_layer (*new_image_id, *new_layer_id, -1, 0);
+ dst_drawable_id = *new_layer_id;
+ }
+ else
+ {
+ *new_image_id = -1;
+ *new_layer_id = -1;
+
+ gimp_image_undo_group_start (image_id);
+ gimp_image_resize (image_id, dst_width, dst_height, 0, 0);
+
+ if (gimp_item_is_layer (drawable_id))
+ gimp_layer_resize (drawable_id, dst_width, dst_height, 0, 0);
+ else if (gimp_item_is_layer_mask (drawable_id))
+ {
+ gint32 layer_id = gimp_layer_from_mask (drawable_id);
+ gimp_layer_resize (layer_id, dst_width, dst_height, 0, 0);
+ }
+
+ dst_drawable_id = drawable_id;
+ }
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dst_buffer = gimp_drawable_get_buffer (dst_drawable_id);
+
+ tile_gegl (src_buffer, src_width, src_height,
+ dst_buffer, dst_width, dst_height);
+
+ gegl_buffer_flush (dst_buffer);
+ gimp_drawable_update (dst_drawable_id, 0, 0, dst_width, dst_height);
+
+ if (tvals.new_image)
+ {
+ gimp_image_undo_enable (*new_image_id);
+ }
+ else
+ {
+ gimp_image_undo_group_end (image_id);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dst_buffer);
+}
+
+static gboolean
+tile_dialog (gint32 image_ID,
+ gint32 drawable_ID)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *sizeentry;
+ GtkWidget *chainbutton;
+ GtkWidget *toggle;
+ gint width;
+ gint height;
+ gdouble xres;
+ gdouble yres;
+ GimpUnit unit;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+ unit = gimp_image_get_unit (image_ID);
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+
+ tvals.new_width = width;
+ tvals.new_height = height;
+
+ dlg = gimp_dialog_new (_("Tile"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Tile to New Size"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ sizeentry = gimp_coordinates_new (unit, "%a", TRUE, TRUE, 8,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE,
+
+ tvals.constrain, TRUE,
+
+ _("_Width:"), width, xres,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 0, width,
+
+ _("_Height:"), height, yres,
+ 1, GIMP_MAX_IMAGE_SIZE,
+ 0, height);
+ gtk_container_add (GTK_CONTAINER (frame), sizeentry);
+ gtk_table_set_row_spacing (GTK_TABLE (sizeentry), 1, 6);
+ gtk_widget_show (sizeentry);
+
+ chainbutton = GTK_WIDGET (GIMP_COORDINATES_CHAINBUTTON (sizeentry));
+
+ toggle = gtk_check_button_new_with_mnemonic (_("C_reate new image"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tvals.new_image);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tvals.new_image);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ tvals.new_width =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (sizeentry), 0));
+ tvals.new_height =
+ RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (sizeentry), 1));
+
+ tvals.constrain =
+ gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (chainbutton));
+ }
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
diff --git a/plug-ins/common/unit-editor.c b/plug-ins/common/unit-editor.c
new file mode 100644
index 0000000..4156fe6
--- /dev/null
+++ b/plug-ins/common/unit-editor.c
@@ -0,0 +1,700 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Copyright (C) 2000 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-unit-editor"
+#define PLUG_IN_BINARY "unit-editor"
+#define PLUG_IN_ROLE "gimp-unit-editor"
+#define RESPONSE_REFRESH 1
+
+enum
+{
+ SAVE,
+ IDENTIFIER,
+ FACTOR,
+ DIGITS,
+ SYMBOL,
+ ABBREVIATION,
+ SINGULAR,
+ PLURAL,
+ UNIT,
+ USER_UNIT,
+ BG_COLOR,
+ NUM_COLUMNS
+};
+
+typedef struct
+{
+ const gchar *title;
+ const gchar *help;
+
+} UnitColumn;
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *n_return_vals,
+ GimpParam **return_vals);
+
+static GimpUnit new_unit_dialog (GtkWidget *main_dialog,
+ GimpUnit template);
+static void unit_editor_dialog (void);
+static void unit_editor_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void new_callback (GtkAction *action,
+ GtkTreeView *tv);
+static void duplicate_callback (GtkAction *action,
+ GtkTreeView *tv);
+static void saved_toggled_callback (GtkCellRendererToggle *celltoggle,
+ gchar *path_string,
+ GtkListStore *list_store);
+static void unit_list_init (GtkTreeView *tv);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static const UnitColumn columns[] =
+{
+ { N_("Saved"), N_("A unit definition will only be saved before "
+ "GIMP exits if this column is checked.") },
+ { N_("ID"), N_("This string will be used to identify a "
+ "unit in GIMP's configuration files.") },
+ { N_("Factor"), N_("How many units make up an inch.") },
+ { N_("Digits"), N_("This field is a hint for numerical input "
+ "fields. It specifies how many decimal digits "
+ "the input field should provide to get "
+ "approximately the same accuracy as an "
+ "\"inch\" input field with two decimal digits.") },
+ { N_("Symbol"), N_("The unit's symbol if it has one (e.g. \" "
+ "for inches). The unit's abbreviation is used "
+ "if doesn't have a symbol.") },
+ { N_("Abbreviation"), N_("The unit's abbreviation (e.g. \"cm\" for "
+ "centimeters).") },
+ { N_("Singular"), N_("The unit's singular form.") },
+ { N_("Plural"), N_("The unit's plural form.") }
+};
+
+static GtkActionEntry actions[] =
+{
+ { "unit-editor-toolbar", NULL,
+ "Unit Editor Toolbar", NULL, NULL, NULL
+ },
+
+ { "unit-editor-new", GIMP_ICON_DOCUMENT_NEW,
+ NULL, "<control>N",
+ N_("Create a new unit from scratch"),
+ G_CALLBACK (new_callback)
+ },
+
+ { "unit-editor-duplicate", GIMP_ICON_OBJECT_DUPLICATE,
+ NULL, "<control>D",
+ N_("Create a new unit using the currently selected unit as template"),
+ G_CALLBACK (duplicate_callback)
+ }
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create or alter units used in GIMP"),
+ "The GIMP unit editor",
+ "Michael Natterer <mitch@gimp.org>",
+ "Michael Natterer <mitch@gimp.org>",
+ "2000",
+ N_("U_nits"),
+ "",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Edit/Preferences");
+ gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_TOOL_MEASURE);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ if (strcmp (name, PLUG_IN_PROC) == 0)
+ {
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ unit_editor_dialog ();
+ }
+}
+
+static GimpUnit
+new_unit_dialog (GtkWidget *main_dialog,
+ GimpUnit template)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *entry;
+ GtkWidget *spinbutton;
+
+ GtkWidget *identifier_entry;
+ GtkAdjustment *factor_adj;
+ GtkAdjustment *digits_adj;
+ GtkWidget *symbol_entry;
+ GtkWidget *abbreviation_entry;
+ GtkWidget *singular_entry;
+ GtkWidget *plural_entry;
+
+ GimpUnit unit = GIMP_UNIT_PIXEL;
+
+ dialog = gimp_dialog_new (_("Add a New Unit"), PLUG_IN_ROLE,
+ main_dialog, GTK_DIALOG_MODAL,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Add"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ table = gtk_table_new (7, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ entry = identifier_entry = gtk_entry_new ();
+ if (template != GIMP_UNIT_PIXEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ gimp_unit_get_identifier (template));
+ }
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_ID:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ gimp_help_set_help_data (entry, gettext (columns[IDENTIFIER].help), NULL);
+
+ factor_adj = (GtkAdjustment *)
+ gtk_adjustment_new ((template != GIMP_UNIT_PIXEL) ?
+ gimp_unit_get_factor (template) : 1.0,
+ GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION,
+ 0.01, 0.1, 0.0);
+ spinbutton = gimp_spin_button_new (factor_adj, 0.01, 5);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Factor:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ gimp_help_set_help_data (spinbutton, gettext (columns[FACTOR].help), NULL);
+
+ digits_adj = (GtkAdjustment *)
+ gtk_adjustment_new ((template != GIMP_UNIT_PIXEL) ?
+ gimp_unit_get_digits (template) : 2.0,
+ 0, 5, 1, 1, 0);
+ spinbutton = gimp_spin_button_new (digits_adj, 0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Digits:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ gimp_help_set_help_data (spinbutton, gettext (columns[DIGITS].help), NULL);
+
+ entry = symbol_entry = gtk_entry_new ();
+ if (template != GIMP_UNIT_PIXEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ gimp_unit_get_symbol (template));
+ }
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Symbol:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ gimp_help_set_help_data (entry, gettext (columns[SYMBOL].help), NULL);
+
+ entry = abbreviation_entry = gtk_entry_new ();
+ if (template != GIMP_UNIT_PIXEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ gimp_unit_get_abbreviation (template));
+ }
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("_Abbreviation:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ gimp_help_set_help_data (entry, gettext (columns[ABBREVIATION].help), NULL);
+
+ entry = singular_entry = gtk_entry_new ();
+ if (template != GIMP_UNIT_PIXEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ gimp_unit_get_singular (template));
+ }
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 5,
+ _("Si_ngular:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ gimp_help_set_help_data (entry, gettext (columns[SINGULAR].help), NULL);
+
+ entry = plural_entry = gtk_entry_new ();
+ if (template != GIMP_UNIT_PIXEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ gimp_unit_get_plural (template));
+ }
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
+ _("_Plural:"), 0.0, 0.5,
+ entry, 1, FALSE);
+
+ gimp_help_set_help_data (entry, gettext (columns[PLURAL].help), NULL);
+
+ gtk_widget_show (dialog);
+
+ while (TRUE)
+ {
+ gchar *identifier;
+ gdouble factor;
+ gint digits;
+ gchar *symbol;
+ gchar *abbreviation;
+ gchar *singular;
+ gchar *plural;
+
+ if (gimp_dialog_run (GIMP_DIALOG (dialog)) != GTK_RESPONSE_OK)
+ break;
+
+ identifier = g_strdup (gtk_entry_get_text (GTK_ENTRY (identifier_entry)));
+ factor = gtk_adjustment_get_value (GTK_ADJUSTMENT (factor_adj));
+ digits = gtk_adjustment_get_value (GTK_ADJUSTMENT (digits_adj));
+ symbol = g_strdup (gtk_entry_get_text (GTK_ENTRY (symbol_entry)));
+ abbreviation = g_strdup (gtk_entry_get_text (GTK_ENTRY (abbreviation_entry)));
+ singular = g_strdup (gtk_entry_get_text (GTK_ENTRY (singular_entry)));
+ plural = g_strdup (gtk_entry_get_text (GTK_ENTRY (plural_entry)));
+
+ identifier = g_strstrip (identifier);
+ symbol = g_strstrip (symbol);
+ abbreviation = g_strstrip (abbreviation);
+ singular = g_strstrip (singular);
+ plural = g_strstrip (plural);
+
+ if (! strlen (identifier) ||
+ ! strlen (symbol) ||
+ ! strlen (abbreviation) ||
+ ! strlen (singular) ||
+ ! strlen (plural))
+ {
+ GtkWidget *msg = gtk_message_dialog_new (GTK_WINDOW (dialog), 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Incomplete input"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msg),
+ _("Please fill in all text fields."));
+ gtk_dialog_run (GTK_DIALOG (msg));
+ gtk_widget_destroy (msg);
+
+ continue;
+ }
+
+ unit = gimp_unit_new (identifier,
+ factor, digits,
+ symbol, abbreviation, singular, plural);
+
+ g_free (identifier);
+ g_free (symbol);
+ g_free (abbreviation);
+ g_free (singular);
+ g_free (plural);
+
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return unit;
+}
+
+static void
+unit_editor_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *scrolled_win;
+ GtkUIManager *ui_manager;
+ GtkActionGroup *group;
+ GtkWidget *toolbar;
+ GtkListStore *list_store;
+ GtkWidget *tv;
+ GtkTreeViewColumn *col;
+ GtkWidget *col_widget;
+ GtkWidget *button;
+ GtkCellRenderer *rend;
+ gint i;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ list_store = gtk_list_store_new (NUM_COLUMNS,
+ G_TYPE_BOOLEAN, /* SAVE */
+ G_TYPE_STRING, /* IDENTIFIER */
+ G_TYPE_DOUBLE, /* FACTOR */
+ G_TYPE_INT, /* DIGITS */
+ G_TYPE_STRING, /* SYMBOL */
+ G_TYPE_STRING, /* ABBREVIATION */
+ G_TYPE_STRING, /* SINGULAR */
+ G_TYPE_STRING, /* PLURAL */
+ GIMP_TYPE_UNIT, /* UNIT */
+ G_TYPE_BOOLEAN, /* USER_UNIT */
+ GDK_TYPE_COLOR); /* BG_COLOR */
+
+ tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+ g_object_unref (list_store);
+
+ dialog = gimp_dialog_new (_("Unit Editor"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Refresh"), RESPONSE_REFRESH,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (unit_editor_response),
+ tv);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ /* the toolbar */
+ ui_manager = gtk_ui_manager_new ();
+
+ group = gtk_action_group_new ("unit-editor");
+
+ gtk_action_group_set_translation_domain (group, NULL);
+ gtk_action_group_add_actions (group, actions, G_N_ELEMENTS (actions), tv);
+
+ gtk_window_add_accel_group (GTK_WINDOW (dialog),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string
+ (ui_manager,
+ "<ui>\n"
+ " <toolbar action=\"unit-editor-toolbar\">\n"
+ " <toolitem action=\"unit-editor-new\" />\n"
+ " <toolitem action=\"unit-editor-duplicate\" />\n"
+ " </toolbar>\n"
+ "</ui>\n",
+ -1, NULL);
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/unit-editor-toolbar");
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_ALWAYS);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ scrolled_win, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_win);
+
+ gtk_widget_set_size_request (tv, -1, 220);
+ gtk_container_add (GTK_CONTAINER (scrolled_win), tv);
+ gtk_widget_show (tv);
+
+ rend = gtk_cell_renderer_toggle_new ();
+ col =
+ gtk_tree_view_column_new_with_attributes (gettext (columns[SAVE].title),
+ rend,
+ "active", SAVE,
+ "activatable", USER_UNIT,
+ "cell-background-gdk", BG_COLOR,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col);
+
+ col_widget = gtk_tree_view_column_get_widget (col);
+ if (col_widget)
+ {
+ button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
+
+ if (button)
+ gimp_help_set_help_data (button,
+ gettext (columns[SAVE].help), NULL);
+ }
+
+ g_signal_connect (rend, "toggled",
+ G_CALLBACK (saved_toggled_callback),
+ list_store);
+
+ for (i = 0; i < G_N_ELEMENTS (columns); i++)
+ {
+ if (i == SAVE)
+ continue;
+
+ col =
+ gtk_tree_view_column_new_with_attributes (gettext (columns[i].title),
+ gtk_cell_renderer_text_new (),
+ "text", i,
+ "cell-background-gdk", BG_COLOR,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tv), col);
+
+ col_widget = gtk_tree_view_column_get_widget (col);
+ if (col_widget)
+ {
+ button = gtk_widget_get_ancestor (col_widget, GTK_TYPE_BUTTON);
+
+ if (button)
+ gimp_help_set_help_data (button, gettext (columns[i].help), NULL);
+ }
+ }
+
+ unit_list_init (GTK_TREE_VIEW (tv));
+
+ gtk_widget_show (dialog);
+
+ gtk_main ();
+}
+
+static void
+unit_editor_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_REFRESH:
+ unit_list_init (GTK_TREE_VIEW (data));
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+new_callback (GtkAction *action,
+ GtkTreeView *tv)
+{
+ GimpUnit unit;
+
+ unit = new_unit_dialog (gtk_widget_get_toplevel (GTK_WIDGET (tv)),
+ GIMP_UNIT_PIXEL);
+
+ if (unit != GIMP_UNIT_PIXEL)
+ {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ unit_list_init (tv);
+
+ model = gtk_tree_view_get_model (tv);
+
+ if (gtk_tree_model_get_iter_first (model, &iter) &&
+ gtk_tree_model_iter_nth_child (model, &iter,
+ NULL, unit - GIMP_UNIT_INCH))
+ {
+ GtkAdjustment *adj;
+
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv),
+ &iter);
+
+ adj = gtk_tree_view_get_vadjustment (tv);
+ gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
+ }
+ }
+}
+
+static void
+duplicate_callback (GtkAction *action,
+ GtkTreeView *tv)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model (tv);
+ sel = gtk_tree_view_get_selection (tv);
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+ {
+ GimpUnit unit;
+
+ gtk_tree_model_get (model, &iter,
+ UNIT, &unit,
+ -1);
+
+ unit = new_unit_dialog (gtk_widget_get_toplevel (GTK_WIDGET (tv)),
+ unit);
+
+ if (unit != GIMP_UNIT_PIXEL)
+ {
+ GtkTreeIter iter;
+
+ unit_list_init (tv);
+
+ if (gtk_tree_model_get_iter_first (model, &iter) &&
+ gtk_tree_model_iter_nth_child (model, &iter,
+ NULL, unit - GIMP_UNIT_INCH))
+ {
+ GtkAdjustment *adj;
+
+ gtk_tree_selection_select_iter (sel, &iter);
+
+ adj = gtk_tree_view_get_vadjustment (tv);
+ gtk_adjustment_set_value (adj, gtk_adjustment_get_upper (adj));
+ }
+ }
+ }
+}
+
+static void
+saved_toggled_callback (GtkCellRendererToggle *celltoggle,
+ gchar *path_string,
+ GtkListStore *list_store)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean saved;
+ GimpUnit unit;
+
+ path = gtk_tree_path_new_from_string (path_string);
+
+ if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &iter, path))
+ {
+ g_warning ("%s: bad tree path?", G_STRLOC);
+ return;
+ }
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
+ SAVE, &saved,
+ UNIT, &unit,
+ -1);
+
+ if (unit >= gimp_unit_get_number_of_built_in_units ())
+ {
+ gimp_unit_set_deletion_flag (unit, saved);
+ gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
+ SAVE, ! saved,
+ -1);
+ }
+}
+
+static void
+unit_list_init (GtkTreeView *tv)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ gint num_units;
+ GimpUnit unit;
+ GdkColor color;
+
+ list_store = GTK_LIST_STORE (gtk_tree_view_get_model (tv));
+
+ gtk_list_store_clear (list_store);
+
+ num_units = gimp_unit_get_number_of_units ();
+
+ color.red = 0xdddd;
+ color.green = 0xdddd;
+ color.blue = 0xffff;
+
+ for (unit = GIMP_UNIT_INCH; unit < num_units; unit++)
+ {
+ gboolean user_unit = (unit >= gimp_unit_get_number_of_built_in_units ());
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter,
+ SAVE, ! gimp_unit_get_deletion_flag (unit),
+ IDENTIFIER, gimp_unit_get_identifier (unit),
+ FACTOR, gimp_unit_get_factor (unit),
+ DIGITS, gimp_unit_get_digits (unit),
+ SYMBOL, gimp_unit_get_symbol (unit),
+ ABBREVIATION, gimp_unit_get_abbreviation (unit),
+ SINGULAR, gimp_unit_get_singular (unit),
+ PLURAL, gimp_unit_get_plural (unit),
+ UNIT, unit,
+ USER_UNIT, user_unit,
+
+ user_unit ? -1 : BG_COLOR, &color,
+
+ -1);
+ }
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
+ gtk_tree_selection_select_iter (gtk_tree_view_get_selection (tv), &iter);
+}
diff --git a/plug-ins/common/van-gogh-lic.c b/plug-ins/common/van-gogh-lic.c
new file mode 100644
index 0000000..9bb5dc8
--- /dev/null
+++ b/plug-ins/common/van-gogh-lic.c
@@ -0,0 +1,904 @@
+/* LIC 0.14 -- image filter plug-in for GIMP
+ * Copyright (C) 1996 Tom Bech
+ *
+ * E-mail: tomb@gimp.org
+ * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * In other words, you can't sue me for whatever happens while using this ;)
+ *
+ * Changes (post 0.10):
+ * -> 0.11: Fixed a bug in the convolution kernels (Tom).
+ * -> 0.12: Added Quartic's bilinear interpolation stuff (Tom).
+ * -> 0.13 Changed some UI stuff causing trouble with the 0.60 release, added
+ * the (GIMP) tags and changed random() calls to rand() (Tom)
+ * -> 0.14 Ported to 0.99.11 (Tom)
+ *
+ * This plug-in implements the Line Integral Convolution (LIC) as described in
+ * Cabral et al. "Imaging vector fields using line integral convolution" in the
+ * Proceedings of ACM SIGGRAPH 93. Publ. by ACM, New York, NY, USA. p. 263-270.
+ * (See http://www8.cs.umu.se/kurser/TDBD13/VT00/extra/p263-cabral.pdf)
+ *
+ * Some of the code is based on code by Steinar Haugen (thanks!), the Perlin
+ * noise function is practically ripped as is :)
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/************/
+/* Typedefs */
+/************/
+
+#define numx 40 /* Pseudo-random vector grid size */
+#define numy 40
+
+#define PLUG_IN_PROC "plug-in-lic"
+#define PLUG_IN_BINARY "van-gogh-lic"
+#define PLUG_IN_ROLE "gimp-van-gogh-lic"
+
+typedef enum
+{
+ LIC_HUE,
+ LIC_SATURATION,
+ LIC_BRIGHTNESS
+} LICEffectChannel;
+
+
+/*****************************/
+/* Global variables and such */
+/*****************************/
+
+static gdouble G[numx][numy][2];
+
+typedef struct
+{
+ gdouble filtlen;
+ gdouble noisemag;
+ gdouble intsteps;
+ gdouble minv;
+ gdouble maxv;
+ gint effect_channel;
+ gint effect_operator;
+ gint effect_convolve;
+ gint32 effect_image_id;
+} LicValues;
+
+static LicValues licvals;
+
+static gdouble l = 10.0;
+static gdouble dx = 2.0;
+static gdouble dy = 2.0;
+static gdouble minv = -2.5;
+static gdouble maxv = 2.5;
+static gdouble isteps = 20.0;
+
+static gboolean source_drw_has_alpha = FALSE;
+
+static gint effect_width, effect_height;
+static gint border_x, border_y, border_w, border_h;
+
+static GtkWidget *dialog;
+
+/************************/
+/* Convenience routines */
+/************************/
+
+static void
+peek (GeglBuffer *buffer,
+ gint x,
+ gint y,
+ GimpRGB *color)
+{
+ gegl_buffer_sample (buffer, x, y, NULL,
+ color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+}
+
+static void
+poke (GeglBuffer *buffer,
+ gint x,
+ gint y,
+ GimpRGB *color)
+{
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ babl_format ("R'G'B'A double"), color,
+ GEGL_AUTO_ROWSTRIDE);
+}
+
+static gint
+peekmap (const guchar *image,
+ gint x,
+ gint y)
+{
+ while (x < 0)
+ x += effect_width;
+ x %= effect_width;
+
+ while (y < 0)
+ y += effect_height;
+ y %= effect_height;
+
+ return (gint) image[x + effect_width * y];
+}
+
+/*************/
+/* Main part */
+/*************/
+
+/***************************************************/
+/* Compute the derivative in the x and y direction */
+/* We use these convolution kernels: */
+/* |1 0 -1| | 1 2 1| */
+/* DX: |2 0 -2| DY: | 0 0 0| */
+/* |1 0 -1| | -1 -2 -1| */
+/* (It's a variation of the Sobel kernels, really) */
+/***************************************************/
+
+static gint
+gradx (const guchar *image,
+ gint x,
+ gint y)
+{
+ gint val = 0;
+
+ val = val + peekmap (image, x-1, y-1);
+ val = val - peekmap (image, x+1, y-1);
+
+ val = val + 2 * peekmap (image, x-1, y);
+ val = val - 2 * peekmap (image, x+1, y);
+
+ val = val + peekmap (image, x-1, y+1);
+ val = val - peekmap (image, x+1, y+1);
+
+ return val;
+}
+
+static gint
+grady (const guchar *image,
+ gint x,
+ gint y)
+{
+ gint val = 0;
+
+ val = val + peekmap (image, x-1, y-1);
+ val = val + 2 * peekmap (image, x, y-1);
+ val = val + peekmap (image, x+1, y-1);
+
+ val = val - peekmap (image, x-1, y+1);
+ val = val - 2 * peekmap (image, x, y+1);
+ val = val - peekmap (image, x+1, y+1);
+
+ return val;
+}
+
+/************************************/
+/* A nice 2nd order cubic spline :) */
+/************************************/
+
+static gdouble
+cubic (gdouble t)
+{
+ gdouble at = fabs (t);
+
+ return (at < 1.0) ? at * at * (2.0 * at - 3.0) + 1.0 : 0.0;
+}
+
+static gdouble
+omega (gdouble u,
+ gdouble v,
+ gint i,
+ gint j)
+{
+ while (i < 0)
+ i += numx;
+
+ while (j < 0)
+ j += numy;
+
+ i %= numx;
+ j %= numy;
+
+ return cubic (u) * cubic (v) * (G[i][j][0]*u + G[i][j][1]*v);
+}
+
+/*************************************************************/
+/* The noise function (2D variant of Perlins noise function) */
+/*************************************************************/
+
+static gdouble
+noise (gdouble x,
+ gdouble y)
+{
+ gint i, sti = (gint) floor (x / dx);
+ gint j, stj = (gint) floor (y / dy);
+
+ gdouble sum = 0.0;
+
+ /* Calculate the gdouble sum */
+ /* ======================== */
+
+ for (i = sti; i <= sti + 1; i++)
+ for (j = stj; j <= stj + 1; j++)
+ sum += omega ((x - (gdouble) i * dx) / dx,
+ (y - (gdouble) j * dy) / dy,
+ i, j);
+
+ return sum;
+}
+
+/*************************************************/
+/* Generates pseudo-random vectors with length 1 */
+/*************************************************/
+
+static void
+generatevectors (void)
+{
+ gdouble alpha;
+ gint i, j;
+ GRand *gr;
+
+ gr = g_rand_new();
+
+ for (i = 0; i < numx; i++)
+ {
+ for (j = 0; j < numy; j++)
+ {
+ alpha = g_rand_double_range (gr, 0, 2) * G_PI;
+ G[i][j][0] = cos (alpha);
+ G[i][j][1] = sin (alpha);
+ }
+ }
+
+ g_rand_free (gr);
+}
+
+/* A simple triangle filter */
+/* ======================== */
+
+static gdouble
+filter (gdouble u)
+{
+ gdouble f = 1.0 - fabs (u) / l;
+
+ return (f < 0.0) ? 0.0 : f;
+}
+
+/******************************************************/
+/* Compute the Line Integral Convolution (LIC) at x,y */
+/******************************************************/
+
+static gdouble
+lic_noise (gint x,
+ gint y,
+ gdouble vx,
+ gdouble vy)
+{
+ gdouble i = 0.0;
+ gdouble f1 = 0.0, f2 = 0.0;
+ gdouble u, step = 2.0 * l / isteps;
+ gdouble xx = (gdouble) x, yy = (gdouble) y;
+ gdouble c, s;
+
+ /* Get vector at x,y */
+ /* ================= */
+
+ c = vx;
+ s = vy;
+
+ /* Calculate integral numerically */
+ /* ============================== */
+
+ f1 = filter (-l) * noise (xx + l * c , yy + l * s);
+
+ for (u = -l + step; u <= l; u += step)
+ {
+ f2 = filter (u) * noise ( xx - u * c , yy - u * s);
+ i += (f1 + f2) * 0.5 * step;
+ f1 = f2;
+ }
+
+ i = (i - minv) / (maxv - minv);
+
+ i = CLAMP (i, 0.0, 1.0);
+
+ i = (i / 2.0) + 0.5;
+
+ return i;
+}
+
+static void
+getpixel (GeglBuffer *buffer,
+ GimpRGB *p,
+ gdouble u,
+ gdouble v)
+{
+ register gint x1, y1, x2, y2;
+ gint width, height;
+ static GimpRGB pp[4];
+
+ width = border_w;
+ height = border_h;
+
+ x1 = (gint)u;
+ y1 = (gint)v;
+
+ if (x1 < 0)
+ x1 = width - (-x1 % width);
+ else
+ x1 = x1 % width;
+
+ if (y1 < 0)
+ y1 = height - (-y1 % height);
+ else
+ y1 = y1 % height;
+
+ x2 = (x1 + 1) % width;
+ y2 = (y1 + 1) % height;
+
+ peek (buffer, x1, y1, &pp[0]);
+ peek (buffer, x2, y1, &pp[1]);
+ peek (buffer, x1, y2, &pp[2]);
+ peek (buffer, x2, y2, &pp[3]);
+
+ if (source_drw_has_alpha)
+ *p = gimp_bilinear_rgba (u, v, pp);
+ else
+ *p = gimp_bilinear_rgb (u, v, pp);
+}
+
+static void
+lic_image (GeglBuffer *buffer,
+ gint x,
+ gint y,
+ gdouble vx,
+ gdouble vy,
+ GimpRGB *color)
+{
+ gdouble u, step = 2.0 * l / isteps;
+ gdouble xx = (gdouble) x, yy = (gdouble) y;
+ gdouble c, s;
+ GimpRGB col = { 0, 0, 0, 0 };
+ GimpRGB col1, col2, col3;
+
+ /* Get vector at x,y */
+ /* ================= */
+
+ c = vx;
+ s = vy;
+
+ /* Calculate integral numerically */
+ /* ============================== */
+
+ getpixel (buffer, &col1, xx + l * c, yy + l * s);
+
+ if (source_drw_has_alpha)
+ gimp_rgba_multiply (&col1, filter (-l));
+ else
+ gimp_rgb_multiply (&col1, filter (-l));
+
+ for (u = -l + step; u <= l; u += step)
+ {
+ getpixel (buffer, &col2, xx - u * c, yy - u * s);
+
+ if (source_drw_has_alpha)
+ {
+ gimp_rgba_multiply (&col2, filter (u));
+
+ col3 = col1;
+ gimp_rgba_add (&col3, &col2);
+ gimp_rgba_multiply (&col3, 0.5 * step);
+ gimp_rgba_add (&col, &col3);
+ }
+ else
+ {
+ gimp_rgb_multiply (&col2, filter (u));
+
+ col3 = col1;
+ gimp_rgb_add (&col3, &col2);
+ gimp_rgb_multiply (&col3, 0.5 * step);
+ gimp_rgb_add (&col, &col3);
+ }
+ col1 = col2;
+ }
+ if (source_drw_has_alpha)
+ gimp_rgba_multiply (&col, 1.0 / l);
+ else
+ gimp_rgb_multiply (&col, 1.0 / l);
+ gimp_rgb_clamp (&col);
+
+ *color = col;
+}
+
+static guchar *
+rgb_to_hsl (gint32 drawable_ID,
+ LICEffectChannel effect_channel)
+{
+ GeglBuffer *buffer;
+ guchar *themap, data[4];
+ gint x, y;
+ GimpRGB color;
+ GimpHSL color_hsl;
+ gdouble val = 0.0;
+ glong maxc, index = 0;
+ GRand *gr;
+
+ gr = g_rand_new ();
+
+ maxc = gimp_drawable_width (drawable_ID) * gimp_drawable_height (drawable_ID);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ themap = g_new (guchar, maxc);
+
+ for (y = 0; y < border_h; y++)
+ {
+ for (x = 0; x < border_w; x++)
+ {
+ data[3] = 255;
+
+ gegl_buffer_sample (buffer, x, y, NULL,
+ data, babl_format ("R'G'B'A u8"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ gimp_rgba_set_uchar (&color, data[0], data[1], data[2], data[3]);
+ gimp_rgb_to_hsl (&color, &color_hsl);
+
+ switch (effect_channel)
+ {
+ case LIC_HUE:
+ val = color_hsl.h * 255;
+ break;
+ case LIC_SATURATION:
+ val = color_hsl.s * 255;
+ break;
+ case LIC_BRIGHTNESS:
+ val = color_hsl.l * 255;
+ break;
+ }
+
+ /* add some random to avoid unstructured areas. */
+ val += g_rand_double_range (gr, -1.0, 1.0);
+
+ themap[index++] = (guchar) CLAMP0255 (RINT (val));
+ }
+ }
+
+ g_object_unref (buffer);
+
+ g_rand_free (gr);
+
+ return themap;
+}
+
+
+static void
+compute_lic (gint32 drawable_ID,
+ const guchar *scalarfield,
+ gboolean rotate)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ gint xcount, ycount;
+ GimpRGB color;
+ gdouble vx, vy, tmp;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ for (ycount = 0; ycount < border_h; ycount++)
+ {
+ for (xcount = 0; xcount < border_w; xcount++)
+ {
+ /* Get derivative at (x,y) and normalize it */
+ /* ============================================================== */
+
+ vx = gradx (scalarfield, xcount, ycount);
+ vy = grady (scalarfield, xcount, ycount);
+
+ /* Rotate if needed */
+ if (rotate)
+ {
+ tmp = vy;
+ vy = -vx;
+ vx = tmp;
+ }
+
+ tmp = sqrt (vx * vx + vy * vy);
+ if (tmp >= 0.000001)
+ {
+ tmp = 1.0 / tmp;
+ vx *= tmp;
+ vy *= tmp;
+ }
+
+ /* Convolve with the LIC at (x,y) */
+ /* ============================== */
+
+ if (licvals.effect_convolve == 0)
+ {
+ peek (src_buffer, xcount, ycount, &color);
+
+ tmp = lic_noise (xcount, ycount, vx, vy);
+
+ if (source_drw_has_alpha)
+ gimp_rgba_multiply (&color, tmp);
+ else
+ gimp_rgb_multiply (&color, tmp);
+ }
+ else
+ {
+ lic_image (src_buffer, xcount, ycount, vx, vy, &color);
+ }
+
+ poke (dest_buffer, xcount, ycount, &color);
+ }
+
+ gimp_progress_update ((gfloat) ycount / (gfloat) border_h);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+}
+
+static void
+compute_image (gint32 drawable_ID)
+{
+ guchar *scalarfield = NULL;
+
+ /* Get some useful info on the input drawable */
+ /* ========================================== */
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &border_x, &border_y,
+ &border_w, &border_h))
+ return;
+
+ gimp_progress_init (_("Van Gogh (LIC)"));
+
+ if (licvals.effect_convolve == 0)
+ generatevectors ();
+
+ if (licvals.filtlen < 0.1)
+ licvals.filtlen = 0.1;
+
+ l = licvals.filtlen;
+ dx = dy = licvals.noisemag;
+ minv = licvals.minv / 10.0;
+ maxv = licvals.maxv / 10.0;
+ isteps = licvals.intsteps;
+
+ source_drw_has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ effect_width = gimp_drawable_width (licvals.effect_image_id);
+ effect_height = gimp_drawable_height (licvals.effect_image_id);
+
+ switch (licvals.effect_channel)
+ {
+ case 0:
+ scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_HUE);
+ break;
+ case 1:
+ scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_SATURATION);
+ break;
+ case 2:
+ scalarfield = rgb_to_hsl (licvals.effect_image_id, LIC_BRIGHTNESS);
+ break;
+ }
+
+ compute_lic (drawable_ID, scalarfield, licvals.effect_operator);
+
+ g_free (scalarfield);
+
+ /* Update image */
+ /* ============ */
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, border_x, border_y, border_w, border_h);
+
+ gimp_displays_flush ();
+}
+
+/**************************/
+/* Below is only UI stuff */
+/**************************/
+
+static gboolean
+effect_image_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ return gimp_drawable_is_rgb (drawable_id);
+}
+
+static gboolean
+create_main_dialog (void)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkObject *scale_data;
+ gint row;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Van Gogh (LIC)"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Effect Channel"),
+ G_CALLBACK (gimp_radio_button_update),
+ &licvals.effect_channel,
+ licvals.effect_channel,
+
+ _("_Hue"), 0, NULL,
+ _("_Saturation"), 1, NULL,
+ _("_Brightness"), 2, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Effect Operator"),
+ G_CALLBACK (gimp_radio_button_update),
+ &licvals.effect_operator,
+ licvals.effect_operator,
+
+ _("_Derivative"), 0, NULL,
+ _("_Gradient"), 1, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Convolve"),
+ G_CALLBACK (gimp_radio_button_update),
+ &licvals.effect_convolve,
+ licvals.effect_convolve,
+
+ _("_With white noise"), 0, NULL,
+ _("W_ith source image"), 1, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ /* Effect image menu */
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ combo = gimp_drawable_combo_box_new (effect_image_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ licvals.effect_image_id,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &licvals.effect_image_id);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Effect image:"), 0.0, 0.5, combo, 2, TRUE);
+
+ table = gtk_table_new (5, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Filter length:"), 0, 6,
+ licvals.filtlen, 0.1, 64, 1.0, 8.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &licvals.filtlen);
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Noise magnitude:"), 0, 6,
+ licvals.noisemag, 1, 5, 0.1, 1.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &licvals.noisemag);
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("In_tegration steps:"), 0, 6,
+ licvals.intsteps, 1, 40, 1.0, 5.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &licvals.intsteps);
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Minimum value:"), 0, 6,
+ licvals.minv, -100, 0, 1, 10, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &licvals.minv);
+
+ scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("M_aximum value:"), 0, 6,
+ licvals.maxv, 0, 100, 1, 10, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &licvals.maxv);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+/*************************************/
+/* Set parameters to standard values */
+/*************************************/
+
+static void
+set_default_settings (void)
+{
+ licvals.filtlen = 5;
+ licvals.noisemag = 2;
+ licvals.intsteps = 25;
+ licvals.minv = -25;
+ licvals.maxv = 25;
+ licvals.effect_channel = 2;
+ licvals.effect_operator = 1;
+ licvals.effect_convolve = 1;
+ licvals.effect_image_id = 0;
+}
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Special effects that nobody understands"),
+ "No help yet",
+ "Tom Bech & Federico Mena Quintero",
+ "Tom Bech & Federico Mena Quintero",
+ "Version 0.14, September 24 1997",
+ N_("_Van Gogh (LIC)..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_ID;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ /* Set default values */
+ /* ================== */
+
+ set_default_settings ();
+
+ /* Possibly retrieve data */
+ /* ====================== */
+
+ gimp_get_data (PLUG_IN_PROC, &licvals);
+
+ run_mode = param[0].data.d_int32;
+ drawable_ID = param[2].data.d_drawable;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGBA or RGB color */
+ /* ================================================ */
+
+ if (gimp_drawable_is_rgb (drawable_ID))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (create_main_dialog ())
+ compute_image (drawable_ID);
+
+ gimp_set_data (PLUG_IN_PROC, &licvals, sizeof (LicValues));
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ compute_image (drawable_ID);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
diff --git a/plug-ins/common/warp.c b/plug-ins/common/warp.c
new file mode 100644
index 0000000..1d66ac6
--- /dev/null
+++ b/plug-ins/common/warp.c
@@ -0,0 +1,1793 @@
+/* Warp --- image filter plug-in for GIMP
+ * Copyright (C) 1997 John P. Beale
+ * Much of the 'warp' is from the Displace plug-in: 1996 Stephen Robert Norris
+ * Much of the 'displace' code taken in turn from the pinch plug-in
+ * which is by 1996 Federico Mena Quintero
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * You can contact me (the warp author) at beale@best.com
+ * Please send me any patches or enhancements to this code.
+ * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
+ *
+ * --------------------------------------------------------------------
+ * Warp Program structure: after running the user interface and setting the
+ * parameters, warp generates a brand-new image (later to be deleted
+ * before the user ever sees it) which contains two grayscale layers,
+ * representing the X and Y gradients of the "control" image. For this
+ * purpose, all channels of the control image are summed for a scalar
+ * value at each pixel coordinate for the gradient operation.
+ *
+ * The X,Y components of the calculated gradient are then used to
+ * displace pixels from the source image into the destination
+ * image. The displacement vector is rotated a user-specified amount
+ * first. This displacement operation happens iteratively, generating
+ * a new displaced image from each prior image.
+ * -------------------------------------------------------------------
+ *
+ * Revision History:
+ * Version 0.37 12/19/98 Fixed Tooltips and freeing memory
+ * Version 0.36 11/9/97 Changed XY vector layers back to own image
+ * fixed 'undo' problem (hopefully)
+ *
+ * Version 0.35 11/3/97 Added vector-map, mag-map, grad-map to
+ * diff vector instead of separate operation
+ * further futzing with drawable updates
+ * starting adding tooltips
+ *
+ * Version 0.34 10/30/97 'Fixed' drawable update problem
+ * Added 16-bit resolution to differential map
+ * Added substep increments for finer control
+ *
+ * Version 0.33 10/26/97 Added 'angle increment' to user interface
+ *
+ * Version 0.32 10/25/97 Added magnitude control map (secondary control)
+ * Changed undo behavior to be one undo-step per warp call.
+ *
+ * Version 0.31 10/25/97 Fixed src/dest pixregions so program works
+ * with multiple-layer images. Still don't know
+ * exactly what I did to fix it :-/ Also, added 'color' option
+ * for border pixels to use the current selected foreground color.
+ *
+ * Version 0.3 10/20/97 Initial release for Gimp 0.99.xx
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Some useful macros */
+
+#define PLUG_IN_PROC "plug-in-warp"
+#define PLUG_IN_BINARY "warp"
+#define PLUG_IN_ROLE "gimp-warp"
+#define ENTRY_WIDTH 75
+#define MIN_ARGS 6 /* minimum number of arguments required */
+
+enum
+{
+ WRAP,
+ SMEAR,
+ BLACK,
+ COLOR
+};
+
+typedef struct
+{
+ gdouble amount;
+ gint warp_map;
+ gint iter;
+ gdouble dither;
+ gdouble angle;
+ gint wrap_type;
+ gint mag_map;
+ gint mag_use;
+ gint substeps;
+ gint grad_map;
+ gdouble grad_scale;
+ gint vector_map;
+ gdouble vector_scale;
+ gdouble vector_angle;
+} WarpVals;
+
+
+/*
+ * Function prototypes.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void blur16 (gint32 drawable_id);
+
+static void diff (gint32 drawable_id,
+ gint32 *xl_id,
+ gint32 *yl_id);
+
+static void diff_prepare_row (GeglBuffer *buffer,
+ const Babl *format,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w);
+
+static void warp_one (gint32 draw_id,
+ gint32 new_id,
+ gint32 map_x_id,
+ gint32 map_y_id,
+ gint32 mag_draw_id,
+ gboolean first_time,
+ gint step);
+
+static void warp (gint32 drawable_id);
+
+static gboolean warp_dialog (gint32 drawable_id);
+static void warp_pixel (GeglBuffer *buffer,
+ const Babl *format,
+ gint width,
+ gint height,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint x,
+ gint y,
+ guchar *pixel);
+
+static gboolean warp_map_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+static gdouble warp_map_mag_give_value (guchar *pt,
+ gint alpha,
+ gint bytes);
+
+/* -------------------------------------------------------------------------- */
+/* Variables global over entire plug-in scope */
+/* -------------------------------------------------------------------------- */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static WarpVals dvals =
+{
+ 10.0, /* amount */
+ -1, /* warp_map */
+ 5, /* iterations */
+ 0.0, /* dither */
+ 90.0, /* angle */
+ WRAP, /* wrap_type */
+ -1, /* mag_map */
+ FALSE, /* mag_use */
+ 1, /* substeps */
+ -1, /* grad_map */
+ 0.0, /* grad_scale */
+ -1, /* vector_map */
+ 0.0, /* vector_scale */
+ 0.0 /* vector_angle */
+};
+
+/* -------------------------------------------------------------------------- */
+
+static gint progress = 0; /* progress indicator bar */
+static GimpRunMode run_mode; /* interactive, non-, etc. */
+static guchar color_pixel[4] = {0, 0, 0, 255}; /* current fg color */
+
+/* -------------------------------------------------------------------------- */
+
+/***** Functions *****/
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_FLOAT, "amount", "Pixel displacement multiplier" },
+ { GIMP_PDB_DRAWABLE, "warp-map", "Displacement control map" },
+ { GIMP_PDB_INT32, "iter", "Iteration count (last required argument)" },
+ { GIMP_PDB_FLOAT, "dither", "Random dither amount (first optional argument)" },
+ { GIMP_PDB_FLOAT, "angle", "Angle of gradient vector rotation" },
+ { GIMP_PDB_INT32, "wrap-type", "Edge behavior: { WRAP (0), SMEAR (1), BLACK (2), COLOR (3) }" },
+ { GIMP_PDB_DRAWABLE, "mag-map", "Magnitude control map" },
+ { GIMP_PDB_INT32, "mag-use", "Use magnitude map: { FALSE (0), TRUE (1) }" },
+ { GIMP_PDB_INT32, "substeps", "Substeps between image updates" },
+ { GIMP_PDB_INT32, "grad-map", "Gradient control map" },
+ { GIMP_PDB_FLOAT, "grad-scale", "Scaling factor for gradient map (0=don't use)" },
+ { GIMP_PDB_INT32, "vector-map", "Fixed vector control map" },
+ { GIMP_PDB_FLOAT, "vector-scale", "Scaling factor for fixed vector map (0=don't use)" },
+ { GIMP_PDB_FLOAT, "vector-angle", "Angle for fixed vector map" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Twist or smear image in many different ways"),
+ "Smears an image along vector paths calculated as "
+ "the gradient of a separate control matrix. The "
+ "effect can look like brushstrokes of acrylic or "
+ "watercolor paint, in some cases.",
+ "John P. Beale",
+ "John P. Beale",
+ "1997",
+ N_("_Warp..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Map");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 drawable_id;
+ GimpRGB color;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ /* get currently selected foreground pixel color */
+ gimp_context_get_foreground (&color);
+ gimp_rgb_get_uchar (&color,
+ &color_pixel[0],
+ &color_pixel[1],
+ &color_pixel[2]);
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &dvals);
+
+ /* First acquire information with a dialog */
+ if (! warp_dialog (drawable_id))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure minimum args
+ * (mode, image, draw, amount, warp_map, iter) are there
+ */
+ if (nparams < MIN_ARGS)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gint pcnt = MIN_ARGS;
+
+ dvals.amount = param[3].data.d_float;
+ dvals.warp_map = param[4].data.d_int32;
+ dvals.iter = param[5].data.d_int32;
+
+ if (nparams > pcnt++) dvals.dither = param[6].data.d_float;
+ if (nparams > pcnt++) dvals.angle = param[7].data.d_float;
+ if (nparams > pcnt++) dvals.wrap_type = param[8].data.d_int32;
+ if (nparams > pcnt++) dvals.mag_map = param[9].data.d_int32;
+ if (nparams > pcnt++) dvals.mag_use = param[10].data.d_int32;
+ if (nparams > pcnt++) dvals.substeps = param[11].data.d_int32;
+ if (nparams > pcnt++) dvals.grad_map = param[12].data.d_int32;
+ if (nparams > pcnt++) dvals.grad_scale = param[13].data.d_float;
+ if (nparams > pcnt++) dvals.vector_map = param[14].data.d_int32;
+ if (nparams > pcnt++) dvals.vector_scale = param[15].data.d_float;
+ if (nparams > pcnt++) dvals.vector_angle = param[16].data.d_float;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &dvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* run the warp effect */
+ warp (drawable_id);
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &dvals, sizeof (WarpVals));
+ }
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+warp_dialog (gint32 drawable_id)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *toggle;
+ GtkWidget *toggle_hbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkObject *adj;
+ GtkWidget *combo;
+ GtkSizeGroup *label_group;
+ GtkSizeGroup *spin_group;
+ GSList *group = NULL;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dlg = gimp_dialog_new (_("Warp"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Basic Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ spin_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* amount, iter */
+ spinbutton = gimp_spin_button_new (&adj, dvals.amount,
+ -1000, 1000, /* ??? */
+ 1, 10, 0, 1, 2);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+ g_object_unref (spin_group);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Step size:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+ g_object_unref (label_group);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.amount);
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.iter,
+ 1, 100, 1, 5, 0, 1, 0);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Iterations:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &dvals.iter);
+
+ /* Displacement map menu */
+ label = gtk_label_new (_("Displacement map:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (warp_map_constrain,
+ GINT_TO_POINTER (drawable_id));
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.warp_map,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &dvals.warp_map);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 2, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ /* ======================================================================= */
+
+ /* Displacement Type */
+ label = gtk_label_new (_("On edges:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ toggle_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach (GTK_TABLE (table), toggle_hbox, 1, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle_hbox);
+
+ toggle = gtk_radio_button_new_with_label (group, _("Wrap"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (WRAP));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &dvals.wrap_type);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ dvals.wrap_type == WRAP);
+
+ toggle = gtk_radio_button_new_with_label (group, _("Smear"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (SMEAR));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &dvals.wrap_type);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ dvals.wrap_type == SMEAR);
+
+ toggle = gtk_radio_button_new_with_label (group, _("Black"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (BLACK));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &dvals.wrap_type);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ dvals.wrap_type == BLACK);
+
+ toggle = gtk_radio_button_new_with_label (group, _("Foreground color"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (COLOR));
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &dvals.wrap_type);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ dvals.wrap_type == COLOR);
+
+
+
+ /* -------------------------------------------------------------------- */
+ /* --------- The secondary table -------------------------- */
+
+ frame = gimp_frame_new (_("Advanced Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.dither,
+ 0, 100, 1, 10, 0, 1, 2);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Dither size:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.dither);
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.angle,
+ 0, 360, 1, 15, 0, 1, 1);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Rotation angle:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.angle);
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.substeps,
+ 1, 100, 1, 5, 0, 1, 0);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Substeps:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &dvals.substeps);
+
+ /* Magnitude map menu */
+ label = gtk_label_new (_("Magnitude map:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (warp_map_constrain,
+ GINT_TO_POINTER (drawable_id));
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.mag_map,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &dvals.mag_map);
+
+ gtk_table_attach (GTK_TABLE (table), combo, 2, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ /* Magnitude Usage */
+ toggle_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
+ gtk_container_set_border_width (GTK_CONTAINER (toggle_hbox), 1);
+ gtk_table_attach (GTK_TABLE (table), toggle_hbox, 2, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle_hbox);
+
+ toggle = gtk_check_button_new_with_label (_("Use magnitude map"));
+ gtk_box_pack_start (GTK_BOX (toggle_hbox), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dvals.mag_use);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &dvals.mag_use);
+
+
+ /* -------------------------------------------------------------------- */
+ /* --------- The "other" table -------------------------- */
+
+ frame = gimp_frame_new (_("More Advanced Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.grad_scale,
+ -1000, 1000, /* ??? */
+ 0.01, 0.1, 0, 1, 3);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Gradient scale:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.grad_scale);
+
+ /* --------- Gradient map menu ---------------- */
+
+ combo = gimp_drawable_combo_box_new (warp_map_constrain,
+ GINT_TO_POINTER (drawable_id));
+ gtk_table_attach (GTK_TABLE (table), combo, 2, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.grad_map,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &dvals.grad_map);
+
+ gimp_help_set_help_data (combo, _("Gradient map selection menu"), NULL);
+
+ /* ---------------------------------------------- */
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.vector_scale,
+ -1000, 1000, /* ??? */
+ 0.01, 0.1, 0, 1, 3);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Vector mag:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.vector_scale);
+
+ /* -------------------------------------------------------- */
+
+ spinbutton = gimp_spin_button_new (&adj, dvals.vector_angle,
+ 0, 360, 1, 15, 0, 1, 1);
+ gtk_size_group_add_widget (spin_group, spinbutton);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Angle:"), 0.0, 0.5,
+ spinbutton, 1, FALSE);
+ gtk_size_group_add_widget (label_group, label);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &dvals.vector_angle);
+
+ /* --------- Vector map menu ---------------- */
+ combo = gimp_drawable_combo_box_new (warp_map_constrain,
+ GINT_TO_POINTER (drawable_id));
+ gtk_table_attach (GTK_TABLE (table), combo, 2, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dvals.vector_map,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &dvals.vector_map);
+
+ gimp_help_set_help_data (combo,
+ _("Fixed-direction-vector map selection menu"),
+ NULL);
+
+ gtk_widget_show (dlg);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dlg);
+
+ return run;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const Babl *
+get_u8_format (gint32 drawable_id)
+{
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("R'G'B'A u8");
+ else
+ return babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("Y'A u8");
+ else
+ return babl_format ("Y' u8");
+ }
+}
+
+static void
+blur16 (gint32 drawable_id)
+{
+ /* blur a 2-or-more byte-per-pixel drawable,
+ * 1st 2 bytes interpreted as a 16-bit height field.
+ */
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ gint width, height;
+ gint src_bytes;
+ gint dest_bytes;
+ gint dest_bytes_inc;
+ gint offb, off1;
+
+ guchar *dest, *d; /* pointers to rows of X and Y diff. data */
+ guchar *prev_row, *pr;
+ guchar *cur_row, *cr;
+ guchar *next_row, *nr;
+ guchar *tmp;
+ gint row, col; /* relating to indexing into pixel row arrays */
+ gint x1, y1, x2, y2;
+ gdouble pval; /* average pixel value of pixel & neighbors */
+
+ /* --------------------------------------- */
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ width = gimp_drawable_width (drawable_id); /* size of input drawable*/
+ height = gimp_drawable_height (drawable_id);
+
+ format = get_u8_format (drawable_id);
+
+ /* bytes per pixel in SOURCE drawable, must be 2 or more */
+ src_bytes = babl_format_get_bytes_per_pixel (format);
+
+ dest_bytes = src_bytes; /* bytes per pixel in SOURCE drawable, >= 2 */
+ dest_bytes_inc = dest_bytes - 2; /* this is most likely zero, but I guess it's more conservative... */
+
+ /* allocate row buffers for source & dest. data */
+
+ prev_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ cur_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ next_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ dest = g_new (guchar, (x2 - x1) * src_bytes);
+
+ /* initialize the pixel regions (read from source, write into dest) */
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ pr = prev_row + src_bytes; /* row arrays are prepared for indexing to -1 (!) */
+ cr = cur_row + src_bytes;
+ nr = next_row + src_bytes;
+
+ diff_prepare_row (src_buffer, format, pr, x1, y1, (x2 - x1));
+ diff_prepare_row (src_buffer, format, cr, x1, y1+1, (x2 - x1));
+
+ /* loop through the rows, applying the smoothing function */
+ for (row = y1; row < y2; row++)
+ {
+ /* prepare the next row */
+ diff_prepare_row (src_buffer, format, nr, x1, row + 1, (x2 - x1));
+
+ d = dest;
+ for (col = 0; col < (x2 - x1); col++) /* over columns of pixels */
+ {
+ offb = col*src_bytes; /* base of byte pointer offset */
+ off1 = offb+1; /* offset into row arrays */
+
+ pval = (256.0 * pr[offb - src_bytes] + pr[off1 - src_bytes] +
+ 256.0 * pr[offb] + pr[off1] +
+ 256.0 * pr[offb + src_bytes] + pr[off1 + src_bytes] +
+ 256.0 * cr[offb - src_bytes] + cr[off1 - src_bytes] +
+ 256.0 * cr[offb] + cr[off1] +
+ 256.0 * cr[offb + src_bytes] + cr[off1 + src_bytes] +
+ 256.0 * nr[offb - src_bytes] + nr[off1 - src_bytes] +
+ 256.0 * nr[offb] + nr[off1] +
+ 256.0 * nr[offb + src_bytes]) + nr[off1 + src_bytes];
+
+ pval /= 9.0; /* take the average */
+ *d++ = (guchar) (((gint) pval) >> 8); /* high-order byte */
+ *d++ = (guchar) (((gint) pval) % 256); /* low-order byte */
+ d += dest_bytes_inc; /* move data pointer on to next destination pixel */
+ }
+
+ /* store the dest */
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x1, row, (x2 - x1), 1), 0,
+ format, dest,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* shuffle the row pointers */
+ tmp = pr;
+ pr = cr;
+ cr = nr;
+ nr = tmp;
+
+ if ((row % 8) == 0)
+ gimp_progress_update ((double) row / (double) (y2 - y1));
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x1, y1, (x2 - x1), (y2 - y1));
+
+ g_free (prev_row); /* row buffers allocated at top of fn. */
+ g_free (cur_row);
+ g_free (next_row);
+ g_free (dest);
+
+}
+
+
+/* ====================================================================== */
+/* Get one row of pixels from the PixelRegion and put them in 'data' */
+
+static void
+diff_prepare_row (GeglBuffer *buffer,
+ const Babl *format,
+ guchar *data,
+ gint x,
+ gint y,
+ gint w)
+{
+ gint bpp = babl_format_get_bytes_per_pixel (format);
+ gint b;
+
+ /* y = CLAMP (y, 0, pixel_rgn->h - 1); FIXME? */
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (x, y, w, 1), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* Fill in edge pixels */
+ for (b = 0; b < bpp; b++)
+ {
+ data[b - (gint) bpp] = data[b];
+ data[w * bpp + b] = data[(w - 1) * bpp + b];
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+/* 'diff' combines the input drawables to prepare the two */
+/* 16-bit (X,Y) vector displacement maps */
+/* -------------------------------------------------------------------------- */
+
+static void
+diff (gint32 drawable_id,
+ gint32 *xl_id,
+ gint32 *yl_id)
+{
+ gint32 draw_xd_id;
+ gint32 draw_yd_id; /* vector disp. drawables */
+ gint32 mdraw_id;
+ gint32 vdraw_id;
+ gint32 gdraw_id;
+ gint32 image_id; /* image holding X and Y diff. arrays */
+ gint32 new_image_id; /* image holding X and Y diff. layers */
+ gint32 layer_active; /* currently active layer */
+ gint32 xlayer_id, ylayer_id; /* individual X and Y layer ID numbers */
+ GeglBuffer *src_buffer;
+ GeglBuffer *destx_buffer;
+ const Babl *destx_format;
+ GeglBuffer *desty_buffer;
+ const Babl *desty_format;
+ GeglBuffer *vec_buffer;
+ GeglBuffer *mag_buffer = NULL;
+ GeglBuffer *grad_buffer;
+ gint width, height;
+ const Babl *src_format;
+ gint src_bytes;
+ const Babl *mformat = NULL;
+ gint mbytes = 0;
+ const Babl *vformat = NULL;
+ gint vbytes = 0;
+ const Babl *gformat = NULL;
+ gint gbytes = 0; /* bytes-per-pixel of various source drawables */
+ const Babl *dest_format;
+ gint dest_bytes;
+ gint dest_bytes_inc;
+ gint do_gradmap = FALSE; /* whether to add in gradient of gradmap to final diff. map */
+ gint do_vecmap = FALSE; /* whether to add in a fixed vector scaled by the vector map */
+ gint do_magmap = FALSE; /* whether to multiply result by the magnitude map */
+
+ guchar *destx, *dx, *desty, *dy; /* pointers to rows of X and Y diff. data */
+ guchar *tmp;
+ guchar *prev_row, *pr;
+ guchar *cur_row, *cr;
+ guchar *next_row, *nr;
+ guchar *prev_row_g, *prg = NULL; /* pointers to gradient map data */
+ guchar *cur_row_g, *crg = NULL;
+ guchar *next_row_g, *nrg = NULL;
+ guchar *cur_row_v, *crv = NULL; /* pointers to vector map data */
+ guchar *cur_row_m, *crm = NULL; /* pointers to magnitude map data */
+ gint row, col, offb, off, bytes; /* relating to indexing into pixel row arrays */
+ gint x1, y1, x2, y2;
+ gint dvalx, dvaly; /* differential value at particular pixel */
+ gdouble tx, ty; /* temporary x,y differential value increments from gradmap, etc. */
+ gdouble rdx, rdy; /* x,y differential values: real #s */
+ gdouble rscalefac; /* scaling factor for x,y differential of 'curl' map */
+ gdouble gscalefac; /* scaling factor for x,y differential of 'gradient' map */
+ gdouble r, theta, dtheta; /* rectangular<-> spherical coordinate transform for vector rotation */
+ gdouble scale_vec_x, scale_vec_y; /* fixed vector X,Y component scaling factors */
+
+ /* ----------------------------------------------------------------------- */
+
+ if (dvals.grad_scale != 0.0)
+ do_gradmap = TRUE; /* add in gradient of gradmap if scale != 0.000 */
+
+ if (dvals.vector_scale != 0.0) /* add in gradient of vectormap if scale != 0.000 */
+ do_vecmap = TRUE;
+
+ do_magmap = (dvals.mag_use == TRUE); /* multiply by magnitude map if so requested */
+
+ /* Get the input area. This is the bounding box of the selection in
+ * the image (or the entire image if there is no selection). Only
+ * operating on the input area is simply an optimization. It doesn't
+ * need to be done for correct operation. (It simply makes it go
+ * faster, since fewer pixels need to be operated on).
+ */
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ /* Get the size of the input image. (This will/must be the same
+ * as the size of the output image.
+ */
+ width = gimp_drawable_width (drawable_id);
+ height = gimp_drawable_height (drawable_id);
+
+ src_format = get_u8_format (drawable_id);
+ src_bytes = babl_format_get_bytes_per_pixel (src_format);
+
+ /* -- Add two layers: X and Y Displacement vectors -- */
+ /* -- I'm using a RGB drawable and using the first two bytes for a
+ 16-bit pixel value. This is either clever, or a kluge,
+ depending on your point of view. */
+
+ image_id = gimp_item_get_image (drawable_id);
+ layer_active = gimp_image_get_active_layer (image_id);
+
+ /* create new image for X,Y diff */
+ new_image_id = gimp_image_new (width, height, GIMP_RGB);
+
+ xlayer_id = gimp_layer_new (new_image_id, "Warp_X_Vectors",
+ width, height,
+ GIMP_RGB_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+
+ ylayer_id = gimp_layer_new (new_image_id, "Warp_Y_Vectors",
+ width, height,
+ GIMP_RGB_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+
+ draw_yd_id = ylayer_id;
+ draw_xd_id = xlayer_id;
+
+ gimp_image_insert_layer (new_image_id, xlayer_id, -1, 1);
+ gimp_image_insert_layer (new_image_id, ylayer_id, -1, 1);
+ gimp_drawable_fill (xlayer_id, GIMP_FILL_BACKGROUND);
+ gimp_drawable_fill (ylayer_id, GIMP_FILL_BACKGROUND);
+ gimp_image_set_active_layer (image_id, layer_active);
+
+ dest_format = get_u8_format (draw_xd_id);
+ dest_bytes = babl_format_get_bytes_per_pixel (dest_format);
+ /* for a GRAYA drawable, I would expect this to be two bytes; any more would be excess */
+ dest_bytes_inc = dest_bytes - 2;
+
+ /* allocate row buffers for source & dest. data */
+
+ prev_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ cur_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ next_row = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+
+ prev_row_g = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ cur_row_g = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+ next_row_g = g_new (guchar, (x2 - x1 + 2) * src_bytes);
+
+ cur_row_v = g_new (guchar, (x2 - x1 + 2) * src_bytes); /* vector map */
+ cur_row_m = g_new (guchar, (x2 - x1 + 2) * src_bytes); /* magnitude map */
+
+ destx = g_new (guchar, (x2 - x1) * dest_bytes);
+ desty = g_new (guchar, (x2 - x1) * dest_bytes);
+
+ /* initialize the source and destination pixel regions */
+
+ /* 'curl' vector-rotation input */
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ /* destination: X diff output */
+ destx_buffer = gimp_drawable_get_buffer (draw_xd_id);
+ destx_format = get_u8_format (draw_xd_id);
+
+ /* Y diff output */
+ desty_buffer = gimp_drawable_get_buffer (draw_yd_id);
+ desty_format = get_u8_format (draw_yd_id);
+
+ pr = prev_row + src_bytes;
+ cr = cur_row + src_bytes;
+ nr = next_row + src_bytes;
+
+ diff_prepare_row (src_buffer, src_format, pr, x1, y1, (x2 - x1));
+ diff_prepare_row (src_buffer, src_format, cr, x1, y1+1, (x2 - x1));
+
+ /* fixed-vector (x,y) component scale factors */
+ scale_vec_x = (dvals.vector_scale *
+ cos ((90 - dvals.vector_angle) * G_PI / 180.0) * 256.0 / 10);
+ scale_vec_y = (dvals.vector_scale *
+ sin ((90 - dvals.vector_angle) * G_PI / 180.0) * 256.0 / 10);
+
+ if (do_vecmap)
+ {
+ vdraw_id = dvals.vector_map;
+
+ /* bytes per pixel in SOURCE drawable */
+ vformat = get_u8_format (vdraw_id);
+ vbytes = babl_format_get_bytes_per_pixel (vformat);
+
+ /* fixed-vector scale-map */
+ vec_buffer = gimp_drawable_get_buffer (vdraw_id);
+
+ crv = cur_row_v + vbytes;
+ diff_prepare_row (vec_buffer, vformat, crv, x1, y1, (x2 - x1));
+ }
+
+ if (do_gradmap)
+ {
+ gdraw_id = dvals.grad_map;
+
+ gformat = get_u8_format (gdraw_id);
+ gbytes = babl_format_get_bytes_per_pixel (gformat);
+
+ /* fixed-vector scale-map */
+ grad_buffer = gimp_drawable_get_buffer (gdraw_id);
+
+ prg = prev_row_g + gbytes;
+ crg = cur_row_g + gbytes;
+ nrg = next_row_g + gbytes;
+ diff_prepare_row (grad_buffer, gformat, prg, x1, y1 - 1, (x2 - x1));
+ diff_prepare_row (grad_buffer, gformat, crg, x1, y1, (x2 - x1));
+ }
+
+ if (do_magmap)
+ {
+ mdraw_id = dvals.mag_map;
+
+ mformat = get_u8_format (mdraw_id);
+ mbytes = babl_format_get_bytes_per_pixel (mformat);
+
+ /* fixed-vector scale-map */
+ mag_buffer = gimp_drawable_get_buffer (mdraw_id);
+
+ crm = cur_row_m + mbytes;
+ diff_prepare_row (mag_buffer, mformat, crm, x1, y1, (x2 - x1));
+ }
+
+ dtheta = dvals.angle * G_PI / 180.0;
+ /* note that '3' is rather arbitrary here. */
+ rscalefac = 256.0 / (3 * src_bytes);
+ /* scale factor for gradient map components */
+ gscalefac = dvals.grad_scale * 256.0 / (3 * gbytes);
+
+ /* loop through the rows, applying the differential convolution */
+ for (row = y1; row < y2; row++)
+ {
+ /* prepare the next row */
+ diff_prepare_row (src_buffer, src_format, nr, x1, row + 1, (x2 - x1));
+
+ if (do_magmap)
+ diff_prepare_row (mag_buffer, mformat, crm, x1, row + 1, (x2 - x1));
+ if (do_vecmap)
+ diff_prepare_row (vec_buffer, vformat, crv, x1, row + 1, (x2 - x1));
+ if (do_gradmap)
+ diff_prepare_row (grad_buffer, gformat, crg, x1, row + 1, (x2 - x1));
+
+ dx = destx;
+ dy = desty;
+
+ for (col = 0; col < (x2 - x1); col++) /* over columns of pixels */
+ {
+ rdx = 0.0;
+ rdy = 0.0;
+ ty = 0.0;
+ tx = 0.0;
+
+ offb = col * src_bytes; /* base of byte pointer offset */
+ for (bytes=0; bytes < src_bytes; bytes++) /* add all channels together */
+ {
+ off = offb+bytes; /* offset into row arrays */
+ rdx += ((gint) -pr[off - src_bytes] + (gint) pr[off + src_bytes] +
+ (gint) -2*cr[off - src_bytes] + (gint) 2*cr[off + src_bytes] +
+ (gint) -nr[off - src_bytes] + (gint) nr[off + src_bytes]);
+
+ rdy += ((gint) -pr[off - src_bytes] - (gint)2*pr[off] - (gint) pr[off + src_bytes] +
+ (gint) nr[off - src_bytes] + (gint)2*nr[off] + (gint) nr[off + src_bytes]);
+ }
+
+ rdx *= rscalefac; /* take average, then reduce. Assume max. rdx now 65535 */
+ rdy *= rscalefac; /* take average, then reduce */
+
+ theta = atan2(rdy,rdx); /* convert to polar, then back to rectang. coords */
+ r = sqrt(rdy*rdy + rdx*rdx);
+ theta += dtheta; /* rotate gradient vector by this angle (radians) */
+ rdx = r * cos(theta);
+ rdy = r * sin(theta);
+
+ if (do_gradmap)
+ {
+ offb = col*gbytes; /* base of byte pointer offset into pixel values (R,G,B,Alpha, etc.) */
+ for (bytes=0; bytes < src_bytes; bytes++) /* add all channels together */
+ {
+ off = offb+bytes; /* offset into row arrays */
+ tx += ((gint) -prg[off - gbytes] + (gint) prg[off + gbytes] +
+ (gint) -2*crg[off - gbytes] + (gint) 2*crg[off + gbytes] +
+ (gint) -nrg[off - gbytes] + (gint) nrg[off + gbytes]);
+
+ ty += ((gint) -prg[off - gbytes] - (gint)2*prg[off] - (gint) prg[off + gbytes] +
+ (gint) nrg[off - gbytes] + (gint)2*nrg[off] + (gint) nrg[off + gbytes]);
+ }
+ tx *= gscalefac;
+ ty *= gscalefac;
+
+ rdx += tx; /* add gradient component in to the other one */
+ rdy += ty;
+
+ } /* if (do_gradmap) */
+
+ if (do_vecmap)
+ { /* add in fixed vector scaled by vec. map data */
+ tx = (gdouble) crv[col*vbytes]; /* use first byte only */
+ rdx += scale_vec_x * tx;
+ rdy += scale_vec_y * tx;
+ } /* if (do_vecmap) */
+
+ if (do_magmap)
+ { /* multiply result by mag. map data */
+ tx = (gdouble) crm[col*mbytes];
+ rdx = (rdx * tx)/(255.0);
+ rdy = (rdy * tx)/(255.0);
+ } /* if do_magmap */
+
+ dvalx = rdx + (2<<14); /* take zero point to be 2^15, since this is two bytes */
+ dvaly = rdy + (2<<14);
+
+ if (dvalx < 0)
+ dvalx = 0;
+
+ if (dvalx > 65535)
+ dvalx = 65535;
+
+ *dx++ = (guchar) (dvalx >> 8); /* store high order byte in value channel */
+ *dx++ = (guchar) (dvalx % 256); /* store low order byte in alpha channel */
+ dx += dest_bytes_inc; /* move data pointer on to next destination pixel */
+
+ if (dvaly < 0)
+ dvaly = 0;
+
+ if (dvaly > 65535)
+ dvaly = 65535;
+
+ *dy++ = (guchar) (dvaly >> 8);
+ *dy++ = (guchar) (dvaly % 256);
+ dy += dest_bytes_inc;
+
+ } /* ------------------------------- for (col...) ---------------- */
+
+ /* store the dest */
+ gegl_buffer_set (destx_buffer,
+ GEGL_RECTANGLE (x1, row, (x2 - x1), 1), 0,
+ destx_format, destx,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gegl_buffer_set (desty_buffer,
+ GEGL_RECTANGLE (x1, row, (x2 - x1), 1), 0,
+ desty_format, desty,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* swap around the pointers to row buffers */
+ tmp = pr;
+ pr = cr;
+ cr = nr;
+ nr = tmp;
+
+ if (do_gradmap)
+ {
+ tmp = prg;
+ prg = crg;
+ crg = nrg;
+ nrg = tmp;
+ }
+
+ if ((row % 8) == 0)
+ gimp_progress_update ((gdouble) row / (gdouble) (y2 - y1));
+
+ } /* for (row..) */
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (src_buffer);
+ g_object_unref (destx_buffer);
+ g_object_unref (desty_buffer);
+
+ gimp_drawable_update (draw_xd_id, x1, y1, (x2 - x1), (y2 - y1));
+ gimp_drawable_update (draw_yd_id, x1, y1, (x2 - x1), (y2 - y1));
+
+ gimp_displays_flush (); /* make sure layer is visible */
+
+ gimp_progress_init (_("Smoothing X gradient"));
+ blur16 (draw_xd_id);
+
+ gimp_progress_init (_("Smoothing Y gradient"));
+ blur16 (draw_yd_id);
+
+ g_free (prev_row); /* row buffers allocated at top of fn. */
+ g_free (cur_row);
+ g_free (next_row);
+ g_free (prev_row_g); /* row buffers allocated at top of fn. */
+ g_free (cur_row_g);
+ g_free (next_row_g);
+ g_free (cur_row_v);
+ g_free (cur_row_m);
+
+ g_free (destx);
+ g_free (desty);
+
+ *xl_id = xlayer_id; /* pass back the X and Y layer ID numbers */
+ *yl_id = ylayer_id;
+}
+
+/* -------------------------------------------------------------------------- */
+/* The Warp displacement is done here. */
+/* -------------------------------------------------------------------------- */
+
+static void
+warp (gint32 orig_draw_id)
+{
+ gint32 disp_map_id; /* Displacement map, ie, control array */
+ gint32 mag_draw_id; /* Magnitude multiplier factor map */
+ gint32 map_x_id = -1;
+ gint32 map_y_id = -1;
+ gboolean first_time = TRUE;
+ gint width;
+ gint height;
+ gint x1, y1, x2, y2;
+ gint32 image_ID;
+
+ /* index var. over all "warp" Displacement iterations */
+ gint warp_iter;
+
+ disp_map_id = dvals.warp_map;
+ mag_draw_id = dvals.mag_map;
+
+ /* calculate new X,Y Displacement image maps */
+
+ gimp_progress_init (_("Finding XY gradient"));
+
+ /* Get selection area */
+ if (! gimp_drawable_mask_intersect (orig_draw_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ width = gimp_drawable_width (orig_draw_id);
+ height = gimp_drawable_height (orig_draw_id);
+
+ /* generate x,y differential images (arrays) */
+ diff (disp_map_id, &map_x_id, &map_y_id);
+
+ for (warp_iter = 0; warp_iter < dvals.iter; warp_iter++)
+ {
+ gimp_progress_init_printf (_("Flow step %d"), warp_iter+1);
+ progress = 0;
+
+ warp_one (orig_draw_id, orig_draw_id,
+ map_x_id, map_y_id, mag_draw_id,
+ first_time, warp_iter);
+
+ gimp_drawable_update (orig_draw_id,
+ x1, y1, (x2 - x1), (y2 - y1));
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ first_time = FALSE;
+ }
+
+ image_ID = gimp_item_get_image (map_x_id);
+
+ gimp_image_delete (image_ID);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void
+warp_one (gint32 draw_id,
+ gint32 new_id,
+ gint32 map_x_id,
+ gint32 map_y_id,
+ gint32 mag_draw_id,
+ gboolean first_time,
+ gint step)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GeglBuffer *map_x_buffer;
+ GeglBuffer *map_y_buffer;
+ GeglBuffer *mag_buffer = NULL;
+
+ GeglBufferIterator *iter;
+
+ gint width;
+ gint height;
+
+ const Babl *src_format;
+ gint src_bytes;
+ const Babl *dest_format;
+ gint dest_bytes;
+
+ guchar pixel[4][4];
+ gint x1, y1, x2, y2;
+ gint x, y;
+ gint max_progress;
+
+ gdouble needx, needy;
+ gdouble xval=0; /* initialize to quiet compiler grumbles */
+ gdouble yval=0; /* interpolated vector displacement */
+ gdouble scalefac; /* multiplier for vector displacement scaling */
+ gdouble dscalefac; /* multiplier for incremental displacement vectors */
+ gint xi, yi;
+ gint substep; /* loop variable counting displacement vector substeps */
+
+ guchar values[4];
+ guint32 ivalues[4];
+ guchar val;
+
+ gint k;
+
+ gdouble dx, dy; /* X and Y Displacement, integer from GRAY map */
+
+ const Babl *map_x_format;
+ gint map_x_bytes;
+ const Babl *map_y_format;
+ gint map_y_bytes;
+ const Babl *mag_format;
+ gint mag_bytes = 1;
+ gboolean mag_alpha = FALSE;
+
+ GRand *gr;
+
+ gr = g_rand_new (); /* Seed Pseudo Random Number Generator */
+
+ /* ================ Outer Loop calculation ================================ */
+
+ /* Get selection area */
+
+ if (! gimp_drawable_mask_intersect (draw_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ x2 = x1 + width;
+ y2 = y1 + height;
+
+ width = gimp_drawable_width (draw_id);
+ height = gimp_drawable_height (draw_id);
+
+
+ max_progress = (x2 - x1) * (y2 - y1);
+
+
+ /* --------- Register the (many) pixel regions ---------- */
+
+ src_buffer = gimp_drawable_get_buffer (draw_id);
+
+ src_format = get_u8_format (draw_id);
+ src_bytes = babl_format_get_bytes_per_pixel (src_format);
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x1, y1, (x2 - x1), (y2 - y1)),
+ 0, src_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 5);
+
+
+ dest_buffer = gimp_drawable_get_shadow_buffer (new_id);
+
+ dest_format = get_u8_format (new_id);
+ dest_bytes = babl_format_get_bytes_per_pixel (dest_format);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (x1, y1, (x2 - x1), (y2 - y1)),
+ 0, dest_format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+
+ map_x_buffer = gimp_drawable_get_buffer (map_x_id);
+
+ map_x_format = get_u8_format (map_x_id);
+ map_x_bytes = babl_format_get_bytes_per_pixel (map_x_format);
+
+ gegl_buffer_iterator_add (iter, map_x_buffer,
+ GEGL_RECTANGLE (x1, y1, (x2 - x1), (y2 - y1)),
+ 0, map_x_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+
+ map_y_buffer = gimp_drawable_get_buffer (map_y_id);
+
+ map_y_format = get_u8_format (map_y_id);
+ map_y_bytes = babl_format_get_bytes_per_pixel (map_y_format);
+
+ gegl_buffer_iterator_add (iter, map_y_buffer,
+ GEGL_RECTANGLE (x1, y1, (x2 - x1), (y2 - y1)),
+ 0, map_y_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+
+ if (dvals.mag_use)
+ {
+ mag_buffer = gimp_drawable_get_buffer (mag_draw_id);
+
+ mag_format = get_u8_format (mag_draw_id);
+ mag_bytes = babl_format_get_bytes_per_pixel (mag_format);
+
+ mag_alpha = gimp_drawable_has_alpha (mag_draw_id);
+
+ gegl_buffer_iterator_add (iter, mag_buffer,
+ GEGL_RECTANGLE (x1, y1, (x2 - x1), (y2 - y1)),
+ 0, mag_format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ }
+
+ /* substep displacement vector scale factor */
+ dscalefac = dvals.amount / (256 * 127.5 * dvals.substeps);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[1].roi;
+ guchar *srcrow = iter->items[0].data;
+ guchar *destrow = iter->items[1].data;
+ guchar *mxrow = iter->items[2].data;
+ guchar *myrow = iter->items[3].data;
+ guchar *mmagrow = NULL;
+
+ if (dvals.mag_use)
+ mmagrow = iter->items[4].data;
+
+ /* loop over destination pixels */
+ for (y = roi.y; y < (roi.y + roi.height); y++)
+ {
+ guchar *dest = destrow;
+ guchar *mx = mxrow;
+ guchar *my = myrow;
+ guchar *mmag = NULL;
+
+ if (dvals.mag_use == TRUE)
+ mmag = mmagrow;
+
+ for (x = roi.x; x < (roi.x + roi.width); x++)
+ {
+ /* ----- Find displacement vector (amnt_x, amnt_y) ------------ */
+
+ dx = dscalefac * ((256.0 * mx[0]) + mx[1] -32768); /* 16-bit values */
+ dy = dscalefac * ((256.0 * my[0]) + my[1] -32768);
+
+ if (dvals.mag_use)
+ {
+ scalefac = warp_map_mag_give_value (mmag,
+ mag_alpha,
+ mag_bytes) / 255.0;
+ dx *= scalefac;
+ dy *= scalefac;
+ }
+
+ if (dvals.dither != 0.0)
+ { /* random dither is +/- dvals.dither pixels */
+ dx += g_rand_double_range (gr, -dvals.dither, dvals.dither);
+ dy += g_rand_double_range (gr, -dvals.dither, dvals.dither);
+ }
+
+ if (dvals.substeps != 1)
+ { /* trace (substeps) iterations of displacement vector */
+ for (substep = 1; substep < dvals.substeps; substep++)
+ {
+ /* In this (substep) loop, (x,y) remain fixed. (dx,dy) vary each step. */
+ needx = x + dx;
+ needy = y + dy;
+
+ if (needx >= 0.0)
+ xi = (gint) needx;
+ else
+ xi = -((gint) -needx + 1);
+
+ if (needy >= 0.0)
+ yi = (gint) needy;
+ else
+ yi = -((gint) -needy + 1);
+
+ /* get 4 neighboring DX values from DiffX drawable for linear interpolation */
+ warp_pixel (map_x_buffer, map_x_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi,
+ pixel[0]);
+ warp_pixel (map_x_buffer, map_x_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi,
+ pixel[1]);
+ warp_pixel (map_x_buffer, map_x_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi + 1,
+ pixel[2]);
+ warp_pixel (map_x_buffer, map_x_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi + 1,
+ pixel[3]);
+
+ ivalues[0] = 256 * pixel[0][0] + pixel[0][1];
+ ivalues[1] = 256 * pixel[1][0] + pixel[1][1];
+ ivalues[2] = 256 * pixel[2][0] + pixel[2][1];
+ ivalues[3] = 256 * pixel[3][0] + pixel[3][1];
+
+ xval = gimp_bilinear_32 (needx, needy, ivalues);
+
+ /* get 4 neighboring DY values from DiffY drawable for linear interpolation */
+ warp_pixel (map_y_buffer, map_y_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi,
+ pixel[0]);
+ warp_pixel (map_y_buffer, map_y_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi,
+ pixel[1]);
+ warp_pixel (map_y_buffer, map_y_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi + 1,
+ pixel[2]);
+ warp_pixel (map_y_buffer, map_y_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi + 1,
+ pixel[3]);
+
+ ivalues[0] = 256 * pixel[0][0] + pixel[0][1];
+ ivalues[1] = 256 * pixel[1][0] + pixel[1][1];
+ ivalues[2] = 256 * pixel[2][0] + pixel[2][1];
+ ivalues[3] = 256 * pixel[3][0] + pixel[3][1];
+
+ yval = gimp_bilinear_32 (needx, needy, ivalues);
+
+ /* move displacement vector to this new value */
+ dx += dscalefac * (xval - 32768);
+ dy += dscalefac * (yval - 32768);
+
+ } /* for (substep) */
+ } /* if (substeps != 0) */
+
+ /* --------------------------------------------------------- */
+
+ needx = x + dx;
+ needy = y + dy;
+
+ mx += map_x_bytes; /* pointers into x,y displacement maps */
+ my += map_y_bytes;
+
+ if (dvals.mag_use == TRUE)
+ mmag += mag_bytes;
+
+ /* Calculations complete; now copy the proper pixel */
+
+ if (needx >= 0.0)
+ xi = (gint) needx;
+ else
+ xi = -((gint) -needx + 1);
+
+ if (needy >= 0.0)
+ yi = (gint) needy;
+ else
+ yi = -((gint) -needy + 1);
+
+ /* get 4 neighboring pixel values from source drawable
+ * for linear interpolation
+ */
+ warp_pixel (src_buffer, src_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi,
+ pixel[0]);
+ warp_pixel (src_buffer, src_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi,
+ pixel[1]);
+ warp_pixel (src_buffer, src_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi, yi + 1,
+ pixel[2]);
+ warp_pixel (src_buffer, src_format,
+ width, height,
+ x1, y1, x2, y2,
+ xi + 1, yi + 1,
+ pixel[3]);
+
+ for (k = 0; k < dest_bytes; k++)
+ {
+ values[0] = pixel[0][k];
+ values[1] = pixel[1][k];
+ values[2] = pixel[2][k];
+ values[3] = pixel[3][k];
+
+ val = gimp_bilinear_8 (needx, needy, values);
+
+ *dest++ = val;
+ }
+ }
+
+ /* srcrow += src_rgn.rowstride; */
+ srcrow += src_bytes * roi.width;
+ destrow += dest_bytes * roi.width;
+ mxrow += map_x_bytes * roi.width;
+ myrow += map_y_bytes * roi.width;
+
+ if (dvals.mag_use == TRUE)
+ mmagrow += mag_bytes * roi.width;
+ }
+
+ progress += (roi.width * roi.height);
+ gimp_progress_update ((double) progress / (double) max_progress);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+ g_object_unref (map_x_buffer);
+ g_object_unref (map_y_buffer);
+
+ if (dvals.mag_use == TRUE)
+ g_object_unref (mag_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (draw_id, first_time);
+
+ g_rand_free (gr);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static gdouble
+warp_map_mag_give_value (guchar *pt,
+ gint alpha,
+ gint bytes)
+{
+ gdouble ret, val_alpha;
+
+ if (bytes >= 3)
+ ret = (pt[0] + pt[1] + pt[2])/3.0;
+ else
+ ret = (gdouble) *pt;
+
+ if (alpha)
+ {
+ val_alpha = pt[bytes - 1];
+ ret = (ret * val_alpha / 255.0);
+ };
+
+ return (ret);
+}
+
+
+static void
+warp_pixel (GeglBuffer *buffer,
+ const Babl *format,
+ gint width,
+ gint height,
+ gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint x,
+ gint y,
+ guchar *pixel)
+{
+ static guchar empty_pixel[4] = { 0, 0, 0, 0 };
+ guchar *data;
+
+ /* Tile the image. */
+ if (dvals.wrap_type == WRAP)
+ {
+ if (x < 0)
+ x = width - (-x % width);
+ else
+ x %= width;
+
+ if (y < 0)
+ y = height - (-y % height);
+ else
+ y %= height;
+ }
+ /* Smear out the edges of the image by repeating pixels. */
+ else if (dvals.wrap_type == SMEAR)
+ {
+ if (x < 0)
+ x = 0;
+ else if (x > width - 1)
+ x = width - 1;
+
+ if (y < 0)
+ y = 0;
+ else if (y > height - 1)
+ y = height - 1;
+ }
+
+ if (x >= x1 && y >= y1 && x < x2 && y < y2)
+ {
+ gegl_buffer_sample (buffer, x, y, NULL, pixel, format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+ }
+ else
+ {
+ gint bpp = babl_format_get_bytes_per_pixel (format);
+ gint b;
+
+ if (dvals.wrap_type == BLACK)
+ data = empty_pixel;
+ else
+ data = color_pixel; /* must have selected COLOR type */
+
+ for (b = 0; b < bpp; b++)
+ pixel[b] = data[b];
+ }
+}
+
+/* Warp interface functions */
+
+static gboolean
+warp_map_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ gint32 d_id = GPOINTER_TO_INT (data);
+
+ return (gimp_drawable_width (drawable_id) == gimp_drawable_width (d_id) &&
+ gimp_drawable_height (drawable_id) == gimp_drawable_height (d_id));
+}
diff --git a/plug-ins/common/wavelet-decompose.c b/plug-ins/common/wavelet-decompose.c
new file mode 100644
index 0000000..4b79c35
--- /dev/null
+++ b/plug-ins/common/wavelet-decompose.c
@@ -0,0 +1,422 @@
+/*
+ * Wavelet decompose plug-in by Miroslav Talasek, miroslav.talasek@seznam.cz
+ */
+
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-wavelet-decompose"
+#define PLUG_IN_ROLE "gimp-wavelet-decompose"
+#define PLUG_IN_BINARY "wavelet-decompose"
+
+#define SCALE_WIDTH 120
+#define ENTRY_WIDTH 5
+
+
+typedef struct
+{
+ gint scales;
+ gint create_group;
+ gint create_masks;
+} WaveletDecomposeParams;
+
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void wavelet_blur (gint32 drawable_id,
+ gint radius);
+
+
+static gboolean wavelet_decompose_dialog (void);
+
+
+/* create a few globals, set default values */
+static WaveletDecomposeParams wavelet_params =
+{
+ 5, /* default scales */
+ 1, /* create group */
+ 0 /* do not add mask by default */
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), "
+ "RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "scales", "Number of scales (1-7)" },
+ { GIMP_PDB_INT32, "create-group", "Create a layer group to store the "
+ "decomposition" },
+ { GIMP_PDB_INT32, "create-masks", "Add a layer mask to each scales "
+ "layers" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Wavelet decompose"),
+ "Compute and render wavelet scales",
+ "Miroslav Talasek <miroslav.talasek@seznam.cz>",
+ "Miroslav Talasek <miroslav.talasek@seznam.cz>",
+ "19january 2017",
+ N_("_Wavelet-decompose..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Enhance");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_id;
+ gint32 drawable_id;
+ GimpRunMode run_mode;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ image_id = param[1].data.d_image;
+ drawable_id = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (PLUG_IN_PROC, &wavelet_params);
+
+ if (! wavelet_decompose_dialog ())
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ wavelet_params.scales = param[3].data.d_int32;
+ wavelet_params.create_group = param[4].data.d_int32;
+ wavelet_params.create_masks = param[5].data.d_int32;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &wavelet_params);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint32 *scale_ids;
+ gint32 new_scale_id;
+ gint32 parent_id;
+ GimpLayerMode grain_extract_mode = GIMP_LAYER_MODE_GRAIN_EXTRACT;
+ GimpLayerMode grain_merge_mode = GIMP_LAYER_MODE_GRAIN_MERGE;
+ gint id;
+
+ gimp_progress_init (_("Wavelet-Decompose"));
+
+ gimp_image_undo_group_start (image_id);
+
+ gimp_image_freeze_layers (image_id);
+
+ if (wavelet_params.create_group)
+ {
+ gint32 group_id = gimp_layer_group_new (image_id);
+ gimp_item_set_name (group_id, _("Decomposition"));
+ gimp_item_set_visible (group_id, FALSE);
+ gimp_image_insert_layer (image_id, group_id,
+ gimp_item_get_parent (drawable_id),
+ gimp_image_get_item_position (image_id,
+ drawable_id));
+ parent_id = group_id;
+ }
+ else
+ parent_id = -1;
+
+ scale_ids = g_new (gint32, wavelet_params.scales);
+ new_scale_id = gimp_layer_copy (drawable_id);
+ gimp_image_insert_layer (image_id, new_scale_id, parent_id,
+ gimp_image_get_item_position (image_id,
+ drawable_id));
+
+ /* the exact result of the grain-extract and grain-merge modes depends on
+ * the choice of (gamma-corrected) midpoint intensity value. for the
+ * non-legacy modes, the midpoint value is 0.5, which isn't representable
+ * exactly using integer precision. for the legacy modes, the midpoint
+ * value is 128/255 (i.e., 0x80), which is representable exactly using
+ * (gamma-corrected) integer precision. we therefore use the legacy
+ * modes when the input image precision is integer, and only use the
+ * (preferable) non-legacy modes when the input image precision is
+ * floating point.
+ *
+ * this avoids imperfect reconstruction of the image when using integer
+ * precision. see bug #786844.
+ */
+ switch (gimp_image_get_precision (image_id))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ case GIMP_PRECISION_U16_LINEAR:
+ case GIMP_PRECISION_U16_GAMMA:
+ case GIMP_PRECISION_U32_LINEAR:
+ case GIMP_PRECISION_U32_GAMMA:
+ grain_extract_mode = GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY;
+ grain_merge_mode = GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY;
+ break;
+
+ case GIMP_PRECISION_HALF_LINEAR:
+ case GIMP_PRECISION_HALF_GAMMA:
+ case GIMP_PRECISION_FLOAT_LINEAR:
+ case GIMP_PRECISION_FLOAT_GAMMA:
+ case GIMP_PRECISION_DOUBLE_LINEAR:
+ case GIMP_PRECISION_DOUBLE_GAMMA:
+ grain_extract_mode = GIMP_LAYER_MODE_GRAIN_EXTRACT;
+ grain_merge_mode = GIMP_LAYER_MODE_GRAIN_MERGE;
+ break;
+ }
+
+ for (id = 0 ; id < wavelet_params.scales; ++id)
+ {
+ gint32 blur_id, tmp_id;
+ gchar scale_name[20];
+
+ gimp_progress_update ((gdouble) id / (gdouble) wavelet_params.scales);
+
+ scale_ids[id] = new_scale_id;
+
+ g_snprintf (scale_name, sizeof (scale_name), _("Scale %d"), id + 1);
+ gimp_item_set_name (new_scale_id, scale_name);
+
+ tmp_id = gimp_layer_copy (new_scale_id);
+ gimp_image_insert_layer (image_id, tmp_id, parent_id,
+ gimp_image_get_item_position (image_id,
+ new_scale_id));
+ wavelet_blur (tmp_id, pow(2.0, id));
+
+ blur_id = gimp_layer_copy (tmp_id);
+ gimp_image_insert_layer (image_id, blur_id, parent_id,
+ gimp_image_get_item_position (image_id,
+ tmp_id));
+
+ gimp_layer_set_mode (tmp_id, grain_extract_mode);
+ new_scale_id = gimp_image_merge_down (image_id, tmp_id,
+ GIMP_EXPAND_AS_NECESSARY);
+ scale_ids[id] = new_scale_id;
+
+ gimp_item_set_visible (new_scale_id, FALSE);
+
+ new_scale_id = blur_id;
+ }
+
+ gimp_item_set_name (new_scale_id, _("Residual"));
+
+ for (id = 0; id < wavelet_params.scales; id++)
+ {
+ gimp_image_reorder_item (image_id, scale_ids[id], parent_id,
+ gimp_image_get_item_position (image_id,
+ new_scale_id));
+ gimp_layer_set_mode (scale_ids[id], grain_merge_mode);
+
+ if (wavelet_params.create_masks)
+ {
+ gint32 mask_id = gimp_layer_create_mask (scale_ids[id],
+ GIMP_ADD_MASK_WHITE);
+ gimp_layer_add_mask (scale_ids[id], mask_id);
+ }
+
+ gimp_item_set_visible (scale_ids[id], TRUE);
+ }
+
+ if (wavelet_params.create_group)
+ gimp_item_set_visible (parent_id, TRUE);
+
+ g_free (scale_ids);
+
+ gimp_image_thaw_layers (image_id);
+
+ gimp_image_undo_group_end (image_id);
+
+ gimp_progress_update (1.0);
+
+ values[0].data.d_status = status;
+ gimp_displays_flush ();
+
+ /* set data for next use of filter */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC,
+ &wavelet_params, sizeof (WaveletDecomposeParams));
+ }
+
+ gegl_exit ();
+}
+
+static void
+wavelet_blur (gint32 drawable_id,
+ gint radius)
+{
+ gint x, y, width, height;
+
+ if (gimp_drawable_mask_intersect (drawable_id, &x, &y, &width, &height))
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_id);
+ GeglBuffer *shadow = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ gegl_render_op (buffer, shadow,
+ "gegl:wavelet-blur",
+ "radius", (gdouble) radius,
+ NULL);
+
+ gegl_buffer_flush (shadow);
+ gimp_drawable_merge_shadow (drawable_id, FALSE);
+ gimp_drawable_update (drawable_id, x, y, width, height);
+ g_object_unref (buffer);
+ g_object_unref (shadow);
+ }
+}
+
+static gboolean
+wavelet_decompose_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Wavelet decompose"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ table = gtk_table_new (3, 1, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* scales */
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Scales:"), SCALE_WIDTH, ENTRY_WIDTH,
+ wavelet_params.scales,
+ 1.0, 7.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &wavelet_params.scales);
+
+ /* create group layer */
+
+ button = gtk_check_button_new_with_mnemonic (_("Create a layer group to store the decomposition"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ wavelet_params.create_group);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &wavelet_params.create_group);
+
+ /* create layer masks */
+
+ button = gtk_check_button_new_with_mnemonic (_("Add a layer mask to each scales layers"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ wavelet_params.create_masks);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &wavelet_params.create_masks);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/common/web-browser.c b/plug-ins/common/web-browser.c
new file mode 100644
index 0000000..c751493
--- /dev/null
+++ b/plug-ins/common/web-browser.c
@@ -0,0 +1,214 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Web Browser Plug-in
+ * Copyright (C) 2003 Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h> /* strlen, strstr */
+
+#include <gtk/gtk.h>
+
+#ifdef PLATFORM_OSX
+#import <Cocoa/Cocoa.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-web-browser"
+#define PLUG_IN_BINARY "web-browser"
+#define PLUG_IN_ROLE "gimp-web-browser"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean browser_open_url (const gchar *url,
+ GError **error);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "url", "URL to open" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ "Open an URL in the user specified web browser",
+ "Opens the given URL in the user specified web browser.",
+ "Henrik Brix Andersen <brix@gimp.org>",
+ "2003",
+ "2003/09/16",
+ NULL, NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status;
+ GError *error = NULL;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+
+ if (nparams == 1 &&
+ param[0].data.d_string != NULL &&
+ strlen (param[0].data.d_string))
+ {
+ if (! browser_open_url (param[0].data.d_string, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static gboolean
+browser_open_url (const gchar *url,
+ GError **error)
+{
+#ifdef G_OS_WIN32
+
+ HINSTANCE hinst = ShellExecute (GetDesktopWindow(),
+ "open", url, NULL, NULL, SW_SHOW);
+
+ if ((gint) hinst <= 32)
+ {
+ const gchar *err;
+
+ switch ((gint) hinst)
+ {
+ case 0 :
+ err = _("The operating system is out of memory or resources.");
+ break;
+ case ERROR_FILE_NOT_FOUND :
+ err = _("The specified file was not found.");
+ break;
+ case ERROR_PATH_NOT_FOUND :
+ err = _("The specified path was not found.");
+ break;
+ case ERROR_BAD_FORMAT :
+ err = _("The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image).");
+ break;
+ case SE_ERR_ACCESSDENIED :
+ err = _("The operating system denied access to the specified file.");
+ break;
+ case SE_ERR_ASSOCINCOMPLETE :
+ err = _("The file name association is incomplete or invalid.");
+ break;
+ case SE_ERR_DDEBUSY :
+ err = _("DDE transaction busy");
+ break;
+ case SE_ERR_DDEFAIL :
+ err = _("The DDE transaction failed.");
+ break;
+ case SE_ERR_DDETIMEOUT :
+ err = _("The DDE transaction timed out.");
+ break;
+ case SE_ERR_DLLNOTFOUND :
+ err = _("The specified DLL was not found.");
+ break;
+ case SE_ERR_NOASSOC :
+ err = _("There is no application associated with the given file name extension.");
+ break;
+ case SE_ERR_OOM :
+ err = _("There was not enough memory to complete the operation.");
+ break;
+ case SE_ERR_SHARE:
+ err = _("A sharing violation occurred.");
+ break;
+ default :
+ err = _("Unknown Microsoft Windows error.");
+ }
+
+ g_set_error (error, 0, 0, _("Failed to open '%s': %s"), url, err);
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+#elif defined(PLATFORM_OSX)
+
+ NSURL *ns_url;
+ gboolean retval;
+
+ NSAutoreleasePool *arp = [NSAutoreleasePool new];
+ {
+ ns_url = [NSURL URLWithString: [NSString stringWithUTF8String: url]];
+ retval = [[NSWorkspace sharedWorkspace] openURL: ns_url];
+ }
+ [arp release];
+
+ return retval;
+
+#else
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ return gtk_show_uri (gdk_screen_get_default (),
+ url,
+ gtk_get_current_event_time(),
+ error);
+
+#endif
+}
diff --git a/plug-ins/common/web-page.c b/plug-ins/common/web-page.c
new file mode 100644
index 0000000..3ceef8a
--- /dev/null
+++ b/plug-ins/common/web-page.c
@@ -0,0 +1,551 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Webpage plug-in.
+ * Copyright (C) 2011 Mukund Sivaraman <muks@banu.com>.
+ * Portions are copyright of the author of the
+ * file-open-location-dialog.c code.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <webkit/webkit.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/* Defines */
+#define PLUG_IN_PROC "plug-in-web-page"
+#define PLUG_IN_BINARY "web-page"
+#define PLUG_IN_ROLE "gimp-web-page"
+#define MAX_URL_LEN 2048
+
+typedef struct
+{
+ char *url;
+ gint32 width;
+ gint font_size;
+ GdkPixbuf *pixbuf;
+ GError *error;
+} WebpageVals;
+
+static WebpageVals webpagevals;
+
+typedef struct
+{
+ char url[MAX_URL_LEN];
+ gint32 width;
+ gint font_size;
+} WebpageSaveVals;
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gboolean webpage_dialog (void);
+static gint32 webpage_capture (void);
+
+
+/* Global Variables */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+
+/* Functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "url", "URL of the webpage to screenshot" },
+ { GIMP_PDB_INT32, "width", "The width of the screenshot (in pixels)" },
+ { GIMP_PDB_INT32, "font-size", "The font size to use in the page (in pt)" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create an image of a webpage"),
+ "The plug-in allows you to take a screenshot "
+ "of a webpage.",
+ "Mukund Sivaraman <muks@banu.com>",
+ "2011",
+ "2011",
+ N_("From _Webpage..."),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Create/Acquire");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpRunMode run_mode = param[0].data.d_int32;
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ gint32 image_id = -1;
+ static GimpParam values[2];
+ WebpageSaveVals save = {"https://www.gimp.org/", 1024, 12};
+
+ INIT_I18N ();
+
+ /* initialize the return of the status */
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+
+ gimp_get_data (PLUG_IN_PROC, &save);
+
+ webpagevals.url = g_strdup (save.url);
+ webpagevals.width = save.width;
+ webpagevals.font_size = save.font_size;
+
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (webpage_dialog ())
+ status = GIMP_PDB_SUCCESS;
+ else
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* This is currently not supported. */
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ webpagevals.url = param[1].data.d_string;
+ webpagevals.width = param[2].data.d_int32;
+ webpagevals.font_size = param[3].data.d_int32;
+ status = GIMP_PDB_SUCCESS;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ image_id = webpage_capture ();
+
+ if (image_id == -1)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (webpagevals.error)
+ {
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = webpagevals.error->message;
+ }
+ }
+ else
+ {
+ save.width = webpagevals.width;
+ save.font_size = webpagevals.font_size;
+
+ if (strlen (webpagevals.url) < MAX_URL_LEN)
+ {
+ strncpy (save.url, webpagevals.url, MAX_URL_LEN);
+ save.url[MAX_URL_LEN - 1] = 0;
+ }
+ else
+ {
+ memset (save.url, 0, MAX_URL_LEN);
+ }
+
+ gimp_set_data (PLUG_IN_PROC, &save, sizeof save);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_display_new (image_id);
+
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_id;
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+webpage_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkSizeGroup *sizegroup;
+ GtkAdjustment *adjustment;
+ GtkWidget *spinbutton;
+ GtkWidget *combo;
+ gint active;
+ gint status;
+ gboolean ret = FALSE;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Create from webpage"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("Cre_ate"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_WEB,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
+ gtk_widget_show (image);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ label = gtk_label_new (_("Enter location (URI):"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ entry = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_widget_set_size_request (entry, 400, -1);
+ gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+ if (webpagevals.url)
+ gtk_entry_set_text (GTK_ENTRY (entry),
+ webpagevals.url);
+ gtk_widget_show (entry);
+
+ sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* Width */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new (_("Width (pixels):"));
+ gtk_size_group_add_widget (sizegroup, label);
+
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (webpagevals.width,
+ 1, 8192, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adjustment, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
+ gtk_widget_show (spinbutton);
+
+ /* Font size */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new (_("Font size:"));
+ gtk_size_group_add_widget (sizegroup, label);
+
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_int_combo_box_new (_("Huge"), 16,
+ _("Large"), 14,
+ C_("web-page", "Default"), 12,
+ _("Small"), 10,
+ _("Tiny"), 8,
+ NULL);
+
+ switch (webpagevals.font_size)
+ {
+ case 16:
+ case 14:
+ case 12:
+ case 10:
+ case 8:
+ active = webpagevals.font_size;
+ break;
+ default:
+ active = 12;
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), active);
+
+ gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ g_object_unref (sizegroup);
+
+ status = gimp_dialog_run (GIMP_DIALOG (dialog));
+ if (status == GTK_RESPONSE_OK)
+ {
+ g_free (webpagevals.url);
+ webpagevals.url = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+
+ webpagevals.width = (gint) gtk_adjustment_get_value
+ (GTK_ADJUSTMENT (adjustment));
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo),
+ &webpagevals.font_size);
+
+ ret = TRUE;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return ret;
+}
+
+static void
+notify_progress_cb (WebKitWebView *view,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ static gdouble old_progress = 0.0;
+ gdouble progress;
+
+ g_object_get (view,
+ "progress", &progress,
+ NULL);
+
+ if ((progress - old_progress) > 0.01)
+ {
+ gimp_progress_update (progress);
+ old_progress = progress;
+ }
+}
+
+static gboolean
+load_error_cb (WebKitWebView *view,
+ WebKitWebFrame *web_frame,
+ gchar *uri,
+ gpointer web_error,
+ gpointer user_data)
+{
+ webpagevals.error = g_error_copy ((GError *) web_error);
+
+ gtk_main_quit ();
+
+ return TRUE;
+}
+
+static void
+notify_load_status_cb (WebKitWebView *view,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ WebKitLoadStatus status;
+
+ g_object_get (view,
+ "load-status", &status,
+ NULL);
+
+ if (status == WEBKIT_LOAD_FINISHED)
+ {
+ if (!webpagevals.error)
+ {
+ webpagevals.pixbuf = gtk_offscreen_window_get_pixbuf
+ (GTK_OFFSCREEN_WINDOW (user_data));
+ }
+
+ gtk_main_quit ();
+ }
+}
+
+static gint32
+webpage_capture (void)
+{
+ gint32 image = -1;
+ gchar *scheme;
+ GtkWidget *window;
+ GtkWidget *view;
+ WebKitWebSettings *settings;
+ char *ua_old;
+ char *ua;
+
+ if (webpagevals.pixbuf)
+ {
+ g_object_unref (webpagevals.pixbuf);
+ webpagevals.pixbuf = NULL;
+ }
+ if (webpagevals.error)
+ {
+ g_error_free (webpagevals.error);
+ webpagevals.error = NULL;
+ }
+
+ if ((!webpagevals.url) ||
+ (strlen (webpagevals.url) == 0))
+ {
+ g_set_error (&webpagevals.error, 0, 0, _("No URL was specified"));
+ return -1;
+ }
+
+ scheme = g_uri_parse_scheme (webpagevals.url);
+ if (!scheme)
+ {
+ char *url;
+
+ /* If we were not given a well-formed URL, make one. */
+
+ url = g_strconcat ("http://", webpagevals.url, NULL);
+ g_free (webpagevals.url);
+ webpagevals.url = url;
+
+ g_free (scheme);
+ }
+
+ if (webpagevals.width < 32)
+ {
+ g_warning ("Width '%d' is too small. Clamped to 32.",
+ webpagevals.width);
+ webpagevals.width = 32;
+ }
+ else if (webpagevals.width > 8192)
+ {
+ g_warning ("Width '%d' is too large. Clamped to 8192.",
+ webpagevals.width);
+ webpagevals.width = 8192;
+ }
+
+ window = gtk_offscreen_window_new ();
+ gtk_widget_show (window);
+
+ view = webkit_web_view_new ();
+ gtk_widget_show (view);
+
+ gtk_widget_set_size_request (view, webpagevals.width, -1);
+ gtk_container_add (GTK_CONTAINER (window), view);
+
+ /* Append "GIMP/<GIMP_VERSION>" to the user agent string */
+ settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (view));
+ g_object_get (settings,
+ "user-agent", &ua_old,
+ NULL);
+ ua = g_strdup_printf ("%s GIMP/%s", ua_old, GIMP_VERSION);
+ g_object_set (settings,
+ "user-agent", ua,
+ NULL);
+ g_free (ua_old);
+ g_free (ua);
+
+ /* Set font size */
+ g_object_set (settings,
+ "default-font-size", webpagevals.font_size,
+ NULL);
+
+ g_signal_connect (view, "notify::progress",
+ G_CALLBACK (notify_progress_cb),
+ window);
+ g_signal_connect (view, "load-error",
+ G_CALLBACK (load_error_cb),
+ window);
+ g_signal_connect (view, "notify::load-status",
+ G_CALLBACK (notify_load_status_cb),
+ window);
+
+ gimp_progress_init_printf (_("Downloading webpage '%s'"), webpagevals.url);
+
+ webkit_web_view_open (WEBKIT_WEB_VIEW (view),
+ webpagevals.url);
+
+ gtk_main ();
+
+ gtk_widget_destroy (window);
+
+ gimp_progress_update (1.0);
+
+ if (webpagevals.pixbuf)
+ {
+ gint width;
+ gint height;
+ gint32 layer;
+
+ gimp_progress_init_printf (_("Transferring webpage image for '%s'"),
+ webpagevals.url);
+
+ width = gdk_pixbuf_get_width (webpagevals.pixbuf);
+ height = gdk_pixbuf_get_height (webpagevals.pixbuf);
+
+ image = gimp_image_new (width, height, GIMP_RGB);
+
+ gimp_image_undo_disable (image);
+ layer = gimp_layer_new_from_pixbuf (image, _("Webpage"),
+ webpagevals.pixbuf,
+ 100,
+ gimp_image_get_default_new_layer_mode (image),
+ 0.0, 1.0);
+ gimp_image_insert_layer (image, layer, -1, 0);
+ gimp_image_undo_enable (image);
+
+ g_object_unref (webpagevals.pixbuf);
+ webpagevals.pixbuf = NULL;
+
+ gimp_progress_update (1.0);
+ }
+
+ return image;
+}
diff --git a/plug-ins/file-bmp/Makefile.am b/plug-ins/file-bmp/Makefile.am
new file mode 100644
index 0000000..4dc09ec
--- /dev/null
+++ b/plug-ins/file-bmp/Makefile.am
@@ -0,0 +1,52 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_bmp_RC = file-bmp.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-bmp
+
+libexec_PROGRAMS = file-bmp
+
+file_bmp_SOURCES = \
+ bmp.c \
+ bmp.h \
+ bmp-load.c \
+ bmp-load.h \
+ bmp-save.c \
+ bmp-save.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_bmp_RC)
diff --git a/plug-ins/file-bmp/Makefile.in b/plug-ins/file-bmp/Makefile.in
new file mode 100644
index 0000000..dfe3334
--- /dev/null
+++ b/plug-ins/file-bmp/Makefile.in
@@ -0,0 +1,1012 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-bmp$(EXEEXT)
+subdir = plug-ins/file-bmp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_bmp_OBJECTS = bmp.$(OBJEXT) bmp-load.$(OBJEXT) \
+ bmp-save.$(OBJEXT)
+file_bmp_OBJECTS = $(am_file_bmp_OBJECTS)
+file_bmp_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_bmp_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_bmp_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/bmp-load.Po ./$(DEPDIR)/bmp-save.Po \
+ ./$(DEPDIR)/bmp.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_bmp_SOURCES)
+DIST_SOURCES = $(file_bmp_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-bmp
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_bmp_RC = file-bmp.rc.o
+AM_LDFLAGS = $(mwindows)
+file_bmp_SOURCES = \
+ bmp.c \
+ bmp.h \
+ bmp-load.c \
+ bmp-load.h \
+ bmp-save.c \
+ bmp-save.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_bmp_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-bmp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-bmp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-bmp$(EXEEXT): $(file_bmp_OBJECTS) $(file_bmp_DEPENDENCIES) $(EXTRA_file_bmp_DEPENDENCIES)
+ @rm -f file-bmp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_bmp_OBJECTS) $(file_bmp_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bmp-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bmp-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bmp.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/bmp-load.Po
+ -rm -f ./$(DEPDIR)/bmp-save.Po
+ -rm -f ./$(DEPDIR)/bmp.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/bmp-load.Po
+ -rm -f ./$(DEPDIR)/bmp-save.Po
+ -rm -f ./$(DEPDIR)/bmp.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-bmp/bmp-load.c b/plug-ins/file-bmp/bmp-load.c
new file mode 100644
index 0000000..e79b900
--- /dev/null
+++ b/plug-ins/file-bmp/bmp-load.c
@@ -0,0 +1,1026 @@
+/* bmpread.c reads any bitmap I could get for testing */
+/* Alexander.Schulz@stud.uni-karlsruhe.de */
+
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+
+#include "bmp.h"
+#include "bmp-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#if !defined(WIN32) || defined(__MINGW32__)
+#define BI_RGB 0
+#define BI_RLE8 1
+#define BI_RLE4 2
+#define BI_BITFIELDS 3
+#define BI_ALPHABITFIELDS 4
+#endif
+
+
+static gint32 ReadImage (FILE *fd,
+ const gchar *filename,
+ gint width,
+ gint height,
+ guchar cmap[256][3],
+ gint ncols,
+ gint bpp,
+ gint compression,
+ gint rowbytes,
+ gboolean gray,
+ const BitmapChannel *masks,
+ GError **error);
+
+
+static void
+setMasksDefault (gushort biBitCnt,
+ BitmapChannel *masks)
+{
+ switch (biBitCnt)
+ {
+ case 32:
+ masks[0].mask = 0x00ff0000;
+ masks[0].shiftin = 16;
+ masks[0].max_value = (gfloat)255.0;
+ masks[1].mask = 0x0000ff00;
+ masks[1].shiftin = 8;
+ masks[1].max_value = (gfloat)255.0;
+ masks[2].mask = 0x000000ff;
+ masks[2].shiftin = 0;
+ masks[2].max_value = (gfloat)255.0;
+ masks[3].mask = 0x00000000;
+ masks[3].shiftin = 0;
+ masks[3].max_value = (gfloat)0.0;
+ break;
+
+ case 24:
+ masks[0].mask = 0xff0000;
+ masks[0].shiftin = 16;
+ masks[0].max_value = (gfloat)255.0;
+ masks[1].mask = 0x00ff00;
+ masks[1].shiftin = 8;
+ masks[1].max_value = (gfloat)255.0;
+ masks[2].mask = 0x0000ff;
+ masks[2].shiftin = 0;
+ masks[2].max_value = (gfloat)255.0;
+ masks[3].mask = 0x0;
+ masks[3].shiftin = 0;
+ masks[3].max_value = (gfloat)0.0;
+ break;
+
+ case 16:
+ masks[0].mask = 0x7c00;
+ masks[0].shiftin = 10;
+ masks[0].max_value = (gfloat)31.0;
+ masks[1].mask = 0x03e0;
+ masks[1].shiftin = 5;
+ masks[1].max_value = (gfloat)31.0;
+ masks[2].mask = 0x001f;
+ masks[2].shiftin = 0;
+ masks[2].max_value = (gfloat)31.0;
+ masks[3].mask = 0x0;
+ masks[3].shiftin = 0;
+ masks[3].max_value = (gfloat)0.0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static gint32
+ToL (const guchar *buffer)
+{
+ return (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);
+}
+
+static gint16
+ToS (const guchar *buffer)
+{
+ return (buffer[0] | buffer[1] << 8);
+}
+
+static gboolean
+ReadColorMap (FILE *fd,
+ guchar buffer[256][3],
+ gint number,
+ gint size,
+ gboolean *gray)
+{
+ gint i;
+
+ *gray = (number > 2);
+
+ for (i = 0; i < number ; i++)
+ {
+ guchar rgb[4];
+
+ if (! ReadOK (fd, rgb, size))
+ {
+ g_message (_("Bad colormap"));
+ return FALSE;
+ }
+
+ /* Bitmap save the colors in another order! But change only once! */
+
+ buffer[i][0] = rgb[2];
+ buffer[i][1] = rgb[1];
+ buffer[i][2] = rgb[0];
+
+ *gray = ((*gray) && (rgb[0] == rgb[1]) && (rgb[1] == rgb[2]));
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ReadChannelMasks (guint32 *tmp,
+ BitmapChannel *masks,
+ guint channels)
+{
+ gint i;
+
+ for (i = 0; i < channels; i++)
+ {
+ guint32 mask;
+ gint nbits, offset, bit;
+
+ mask = tmp[i];
+ masks[i].mask = mask;
+ nbits = 0;
+ offset = -1;
+
+ for (bit = 0; bit < 32; bit++)
+ {
+ if (mask & 1)
+ {
+ nbits++;
+ if (offset == -1)
+ offset = bit;
+ }
+
+ mask = mask >> 1;
+ }
+
+ masks[i].shiftin = offset;
+ masks[i].max_value = (gfloat) ((1 << nbits) - 1);
+
+#ifdef DEBUG
+ g_print ("Channel %d mask %08x in %d max_val %d\n",
+ i, masks[i].mask, masks[i].shiftin, (gint)masks[i].max_value);
+#endif
+ }
+
+ return TRUE;
+}
+
+gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fd;
+ BitmapFileHead bitmap_file_head;
+ BitmapHead bitmap_head;
+ guchar buffer[124];
+ gint ColormapSize, rowbytes, Maps;
+ gboolean gray = FALSE;
+ guchar ColorMap[256][3];
+ gint32 image_ID = -1;
+ gchar magick[2];
+ BitmapChannel masks[4];
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fd = g_fopen (filename, "rb");
+
+ if (!fd)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ goto out;
+ }
+
+ /* It is a File. Now is it a Bitmap? Read the shortest possible header */
+
+ if (! ReadOK (fd, magick, 2) ||
+ ! (! strncmp (magick, "BA", 2) ||
+ ! strncmp (magick, "BM", 2) ||
+ ! strncmp (magick, "IC", 2) ||
+ ! strncmp (magick, "PT", 2) ||
+ ! strncmp (magick, "CI", 2) ||
+ ! strncmp (magick, "CP", 2)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ while (! strncmp (magick, "BA", 2))
+ {
+ if (! ReadOK (fd, buffer, 12))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (! ReadOK (fd, magick, 2))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+
+ if (! ReadOK (fd, buffer, 12))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* bring them to the right byteorder. Not too nice, but it should work */
+
+ bitmap_file_head.bfSize = ToL (&buffer[0x00]);
+ bitmap_file_head.zzHotX = ToS (&buffer[0x04]);
+ bitmap_file_head.zzHotY = ToS (&buffer[0x06]);
+ bitmap_file_head.bfOffs = ToL (&buffer[0x08]);
+
+ if (! ReadOK (fd, buffer, 4))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_file_head.biSize = ToL (&buffer[0x00]);
+
+ /* What kind of bitmap is it? */
+
+ if (bitmap_file_head.biSize == 12) /* OS/2 1.x ? */
+ {
+ if (! ReadOK (fd, buffer, 8))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_head.biWidth = ToS (&buffer[0x00]); /* 12 */
+ bitmap_head.biHeight = ToS (&buffer[0x02]); /* 14 */
+ bitmap_head.biPlanes = ToS (&buffer[0x04]); /* 16 */
+ bitmap_head.biBitCnt = ToS (&buffer[0x06]); /* 18 */
+ bitmap_head.biCompr = 0;
+ bitmap_head.biSizeIm = 0;
+ bitmap_head.biXPels = bitmap_head.biYPels = 0;
+ bitmap_head.biClrUsed = 0;
+ bitmap_head.biClrImp = 0;
+ bitmap_head.masks[0] = 0;
+ bitmap_head.masks[1] = 0;
+ bitmap_head.masks[2] = 0;
+ bitmap_head.masks[3] = 0;
+
+ memset (masks, 0, sizeof (masks));
+ Maps = 3;
+ }
+ else if (bitmap_file_head.biSize == 40) /* Windows 3.x */
+ {
+ if (! ReadOK (fd, buffer, 36))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_head.biWidth = ToL (&buffer[0x00]); /* 12 */
+ bitmap_head.biHeight = ToL (&buffer[0x04]); /* 16 */
+ bitmap_head.biPlanes = ToS (&buffer[0x08]); /* 1A */
+ bitmap_head.biBitCnt = ToS (&buffer[0x0A]); /* 1C */
+ bitmap_head.biCompr = ToL (&buffer[0x0C]); /* 1E */
+ bitmap_head.biSizeIm = ToL (&buffer[0x10]); /* 22 */
+ bitmap_head.biXPels = ToL (&buffer[0x14]); /* 26 */
+ bitmap_head.biYPels = ToL (&buffer[0x18]); /* 2A */
+ bitmap_head.biClrUsed = ToL (&buffer[0x1C]); /* 2E */
+ bitmap_head.biClrImp = ToL (&buffer[0x20]); /* 32 */
+ bitmap_head.masks[0] = 0;
+ bitmap_head.masks[1] = 0;
+ bitmap_head.masks[2] = 0;
+ bitmap_head.masks[3] = 0;
+
+ Maps = 4;
+ memset (masks, 0, sizeof (masks));
+
+ if (bitmap_head.biCompr == BI_BITFIELDS)
+ {
+#ifdef DEBUG
+ g_print ("Got BI_BITFIELDS compression\n");
+#endif
+
+ if (! ReadOK (fd, buffer, 3 * sizeof (guint32)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_head.masks[0] = ToL (&buffer[0x00]);
+ bitmap_head.masks[1] = ToL (&buffer[0x04]);
+ bitmap_head.masks[2] = ToL (&buffer[0x08]);
+
+ ReadChannelMasks (&bitmap_head.masks[0], masks, 3);
+ }
+ else if (bitmap_head.biCompr == BI_RGB)
+ {
+#ifdef DEBUG
+ g_print ("Got BI_RGB compression\n");
+#endif
+
+ setMasksDefault (bitmap_head.biBitCnt, masks);
+ }
+ else if ((bitmap_head.biCompr != BI_RLE4) &&
+ (bitmap_head.biCompr != BI_RLE8))
+ {
+ /* BI_ALPHABITFIELDS, etc. */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported compression (%u) in BMP file from '%s'"),
+ bitmap_head.biCompr,
+ gimp_filename_to_utf8 (filename));
+ }
+
+#ifdef DEBUG
+ g_print ("Got BI_RLE4 or BI_RLE8 compression\n");
+#endif
+ }
+ else if (bitmap_file_head.biSize >= 56 &&
+ bitmap_file_head.biSize <= 64)
+ {
+ /* enhanced Windows format with bit masks */
+
+ if (! ReadOK (fd, buffer, bitmap_file_head.biSize - 4))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_head.biWidth = ToL (&buffer[0x00]); /* 12 */
+ bitmap_head.biHeight = ToL (&buffer[0x04]); /* 16 */
+ bitmap_head.biPlanes = ToS (&buffer[0x08]); /* 1A */
+ bitmap_head.biBitCnt = ToS (&buffer[0x0A]); /* 1C */
+ bitmap_head.biCompr = ToL (&buffer[0x0C]); /* 1E */
+ bitmap_head.biSizeIm = ToL (&buffer[0x10]); /* 22 */
+ bitmap_head.biXPels = ToL (&buffer[0x14]); /* 26 */
+ bitmap_head.biYPels = ToL (&buffer[0x18]); /* 2A */
+ bitmap_head.biClrUsed = ToL (&buffer[0x1C]); /* 2E */
+ bitmap_head.biClrImp = ToL (&buffer[0x20]); /* 32 */
+ bitmap_head.masks[0] = ToL (&buffer[0x24]); /* 36 */
+ bitmap_head.masks[1] = ToL (&buffer[0x28]); /* 3A */
+ bitmap_head.masks[2] = ToL (&buffer[0x2C]); /* 3E */
+ bitmap_head.masks[3] = ToL (&buffer[0x30]); /* 42 */
+
+ Maps = 4;
+ ReadChannelMasks (&bitmap_head.masks[0], masks, 4);
+ }
+ else if (bitmap_file_head.biSize == 108 ||
+ bitmap_file_head.biSize == 124)
+ {
+ /* BMP Version 4 or 5 */
+
+ if (! ReadOK (fd, buffer, bitmap_file_head.biSize - 4))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ bitmap_head.biWidth = ToL (&buffer[0x00]);
+ bitmap_head.biHeight = ToL (&buffer[0x04]);
+ bitmap_head.biPlanes = ToS (&buffer[0x08]);
+ bitmap_head.biBitCnt = ToS (&buffer[0x0A]);
+ bitmap_head.biCompr = ToL (&buffer[0x0C]);
+ bitmap_head.biSizeIm = ToL (&buffer[0x10]);
+ bitmap_head.biXPels = ToL (&buffer[0x14]);
+ bitmap_head.biYPels = ToL (&buffer[0x18]);
+ bitmap_head.biClrUsed = ToL (&buffer[0x1C]);
+ bitmap_head.biClrImp = ToL (&buffer[0x20]);
+ bitmap_head.masks[0] = ToL (&buffer[0x24]);
+ bitmap_head.masks[1] = ToL (&buffer[0x28]);
+ bitmap_head.masks[2] = ToL (&buffer[0x2C]);
+ bitmap_head.masks[3] = ToL (&buffer[0x30]);
+
+ Maps = 4;
+
+ if (bitmap_head.biCompr == BI_BITFIELDS)
+ {
+#ifdef DEBUG
+ g_print ("Got BI_BITFIELDS compression\n");
+#endif
+
+ ReadChannelMasks (&bitmap_head.masks[0], masks, 4);
+ }
+ else if (bitmap_head.biCompr == BI_RGB)
+ {
+#ifdef DEBUG
+ g_print ("Got BI_RGB compression\n");
+#endif
+
+ setMasksDefault (bitmap_head.biBitCnt, masks);
+ }
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading BMP file header from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* Valid bit depth is 1, 4, 8, 16, 24, 32 */
+ /* 16 is awful, we should probably shoot whoever invented it */
+
+ switch (bitmap_head.biBitCnt)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* There should be some colors used! */
+
+ ColormapSize =
+ (bitmap_file_head.bfOffs - bitmap_file_head.biSize - 14) / Maps;
+
+ if ((bitmap_head.biClrUsed == 0) &&
+ (bitmap_head.biBitCnt <= 8))
+ {
+ ColormapSize = bitmap_head.biClrUsed = 1 << bitmap_head.biBitCnt;
+ }
+
+ if (ColormapSize > 256)
+ ColormapSize = 256;
+
+ /* Sanity checks */
+
+ if (bitmap_head.biHeight == 0 ||
+ bitmap_head.biWidth == 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* biHeight may be negative, but G_MININT32 is dangerous because:
+ G_MININT32 == -(G_MININT32) */
+ if (bitmap_head.biWidth < 0 ||
+ bitmap_head.biHeight == G_MININT32)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (bitmap_head.biPlanes != 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ if (bitmap_head.biClrUsed > 256 &&
+ bitmap_head.biBitCnt <= 8)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* protect against integer overflows caused by malicious BMPs */
+ /* use divisions in comparisons to avoid type overflows */
+
+ if (((guint64) bitmap_head.biWidth) > G_MAXINT32 / bitmap_head.biBitCnt ||
+ ((guint64) bitmap_head.biWidth) > (G_MAXINT32 / ABS (bitmap_head.biHeight)) / 4)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("'%s' is not a valid BMP file"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ /* Windows and OS/2 declare filler so that rows are a multiple of
+ * word length (32 bits == 4 bytes)
+ */
+
+ rowbytes = ((bitmap_head.biWidth * bitmap_head.biBitCnt - 1) / 32) * 4 + 4;
+
+#ifdef DEBUG
+ printf ("\nSize: %lu, Colors: %lu, Bits: %hu, Width: %ld, Height: %ld, "
+ "Comp: %lu, Zeile: %d\n",
+ bitmap_file_head.bfSize,
+ bitmap_head.biClrUsed,
+ bitmap_head.biBitCnt,
+ bitmap_head.biWidth,
+ bitmap_head.biHeight,
+ bitmap_head.biCompr,
+ rowbytes);
+#endif
+
+ if (bitmap_head.biBitCnt <= 8)
+ {
+#ifdef DEBUG
+ printf ("Colormap read\n");
+#endif
+ /* Get the Colormap */
+ if (! ReadColorMap (fd, ColorMap, ColormapSize, Maps, &gray))
+ goto out;
+ }
+
+ fseek (fd, bitmap_file_head.bfOffs, SEEK_SET);
+
+ /* Get the Image and return the ID or -1 on error*/
+ image_ID = ReadImage (fd,
+ filename,
+ bitmap_head.biWidth,
+ ABS (bitmap_head.biHeight),
+ ColorMap,
+ bitmap_head.biClrUsed,
+ bitmap_head.biBitCnt,
+ bitmap_head.biCompr,
+ rowbytes,
+ gray,
+ masks,
+ error);
+
+ if (image_ID < 0)
+ goto out;
+
+ if (bitmap_head.biXPels > 0 &&
+ bitmap_head.biYPels > 0)
+ {
+ /* Fixed up from scott@asofyet's changes last year, njl195 */
+ gdouble xresolution;
+ gdouble yresolution;
+
+ /* I don't agree with scott's feeling that Gimp should be trying
+ * to "fix" metric resolution translations, in the long term
+ * Gimp should be SI (metric) anyway, but we haven't told the
+ * Americans that yet
+ */
+
+ xresolution = bitmap_head.biXPels * 0.0254;
+ yresolution = bitmap_head.biYPels * 0.0254;
+
+ gimp_image_set_resolution (image_ID, xresolution, yresolution);
+ }
+
+ if (bitmap_head.biHeight < 0)
+ gimp_image_flip (image_ID, GIMP_ORIENTATION_VERTICAL);
+
+out:
+ if (fd)
+ fclose (fd);
+
+ return image_ID;
+}
+
+static gint32
+ReadImage (FILE *fd,
+ const gchar *filename,
+ gint width,
+ gint height,
+ guchar cmap[256][3],
+ gint ncols,
+ gint bpp,
+ gint compression,
+ gint rowbytes,
+ gboolean gray,
+ const BitmapChannel *masks,
+ GError **error)
+{
+ guchar v, n;
+ gint xpos = 0;
+ gint ypos = 0;
+ gint32 image;
+ gint32 layer;
+ GeglBuffer *buffer;
+ guchar *dest, *temp, *row_buf;
+ guchar gimp_cmap[768];
+ gushort rgb;
+ glong rowstride, channels;
+ gint i, i_max, j, cur_progress, max_progress;
+ gint total_bytes_read;
+ GimpImageBaseType base_type;
+ GimpImageType image_type;
+ guint32 px32;
+
+ if (! (compression == BI_RGB || compression == BI_BITFIELDS ||
+ (bpp == 8 && compression == BI_RLE8) ||
+ (bpp == 4 && compression == BI_RLE4)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s",
+ _("Unrecognized or invalid BMP compression format."));
+ return -1;
+ }
+
+ /* Make a new image in GIMP */
+
+ switch (bpp)
+ {
+ case 32:
+ case 24:
+ case 16:
+ base_type = GIMP_RGB;
+ if (masks[3].mask != 0)
+ {
+ image_type = GIMP_RGBA_IMAGE;
+ channels = 4;
+ }
+ else
+ {
+ image_type = GIMP_RGB_IMAGE;
+ channels = 3;
+ }
+ if (bpp == 24 && compression == BI_BITFIELDS)
+ g_printerr ("Loading BMP with invalid combination of 24 bpp and BI_BITFIELDS compression.\n");
+ break;
+
+ case 8:
+ case 4:
+ case 1:
+ if (gray)
+ {
+ base_type = GIMP_GRAY;
+ image_type = GIMP_GRAY_IMAGE;
+ }
+ else
+ {
+ base_type = GIMP_INDEXED;
+ image_type = GIMP_INDEXED_IMAGE;
+ }
+ if (compression == BI_BITFIELDS)
+ g_printerr ("Loading BMP with invalid combination of %d bpp and BI_BITFIELDS compression.\n",
+ bpp);
+
+ channels = 1;
+ break;
+
+ default:
+ g_message (_("Unsupported or invalid bitdepth."));
+ return -1;
+ }
+
+ if ((width < 0) || (width > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_message (_("Unsupported or invalid image width: %d"), width);
+ return -1;
+ }
+
+ if ((height < 0) || (height > GIMP_MAX_IMAGE_SIZE))
+ {
+ g_message (_("Unsupported or invalid image height: %d"), height);
+ return -1;
+ }
+
+ image = gimp_image_new (width, height, base_type);
+ layer = gimp_layer_new (image, _("Background"),
+ width, height,
+ image_type, 100,
+ gimp_image_get_default_new_layer_mode (image));
+
+ gimp_image_set_filename (image, filename);
+
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ /* use g_malloc0 to initialize the dest buffer so that unspecified
+ pixels in RLE bitmaps show up as the zeroth element in the palette.
+ */
+ dest = g_malloc0 (width * height * channels);
+ row_buf = g_malloc (rowbytes);
+ rowstride = width * channels;
+
+ ypos = height - 1; /* Bitmaps begin in the lower left corner */
+ cur_progress = 0;
+ max_progress = height;
+
+ switch (bpp)
+ {
+ case 32:
+ {
+ while (ReadOK (fd, row_buf, rowbytes))
+ {
+ temp = dest + (ypos * rowstride);
+
+ for (xpos= 0; xpos < width; ++xpos)
+ {
+ px32 = ToL(&row_buf[xpos*4]);
+ *(temp++) = ((px32 & masks[0].mask) >> masks[0].shiftin) * 255.0 / masks[0].max_value + 0.5;
+ *(temp++) = ((px32 & masks[1].mask) >> masks[1].shiftin) * 255.0 / masks[1].max_value + 0.5;
+ *(temp++) = ((px32 & masks[2].mask) >> masks[2].shiftin) * 255.0 / masks[2].max_value + 0.5;
+ if (channels > 3)
+ *(temp++) = ((px32 & masks[3].mask) >> masks[3].shiftin) * 255.0 / masks[3].max_value + 0.5;
+ }
+
+ if (ypos == 0)
+ break;
+
+ --ypos; /* next line */
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+ }
+ break;
+
+ case 24:
+ {
+ while (ReadOK (fd, row_buf, rowbytes))
+ {
+ temp = dest + (ypos * rowstride);
+
+ for (xpos= 0; xpos < width; ++xpos)
+ {
+ *(temp++) = row_buf[xpos * 3 + 2];
+ *(temp++) = row_buf[xpos * 3 + 1];
+ *(temp++) = row_buf[xpos * 3];
+ }
+
+ if (ypos == 0)
+ break;
+
+ --ypos; /* next line */
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+ }
+ break;
+
+ case 16:
+ {
+ while (ReadOK (fd, row_buf, rowbytes))
+ {
+ temp = dest + (ypos * rowstride);
+
+ for (xpos= 0; xpos < width; ++xpos)
+ {
+ rgb= ToS(&row_buf[xpos * 2]);
+ *(temp++) = ((rgb & masks[0].mask) >> masks[0].shiftin) * 255.0 / masks[0].max_value + 0.5;
+ *(temp++) = ((rgb & masks[1].mask) >> masks[1].shiftin) * 255.0 / masks[1].max_value + 0.5;
+ *(temp++) = ((rgb & masks[2].mask) >> masks[2].shiftin) * 255.0 / masks[2].max_value + 0.5;
+ if (channels > 3)
+ *(temp++) = ((rgb & masks[3].mask) >> masks[3].shiftin) * 255.0 / masks[3].max_value + 0.5;
+ }
+
+ if (ypos == 0)
+ break;
+
+ --ypos; /* next line */
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+ }
+ break;
+
+ case 8:
+ case 4:
+ case 1:
+ {
+ if (compression == BI_RGB || compression == BI_BITFIELDS)
+ /* no compression */
+ {
+ while (ReadOK (fd, &v, 1))
+ {
+ for (i = 1; (i <= (8 / bpp)) && (xpos < width); i++, xpos++)
+ {
+ temp = dest + (ypos * rowstride) + (xpos * channels);
+ *temp=( v & ( ((1<<bpp)-1) << (8-(i*bpp)) ) ) >> (8-(i*bpp));
+ if (gray)
+ *temp = cmap[*temp][0];
+ }
+
+ if (xpos == width)
+ {
+ fread (row_buf, rowbytes - 1 - (width * bpp - 1) / 8, 1, fd);
+
+ if (ypos == 0)
+ break;
+
+ ypos--;
+ xpos = 0;
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+
+ if (ypos < 0)
+ break;
+ }
+ break;
+ }
+ else
+ {
+ /* compressed image (either RLE8 or RLE4) */
+ while (ypos >= 0 && xpos <= width)
+ {
+ if (! ReadOK (fd, row_buf, 2))
+ {
+ g_message (_("The bitmap ends unexpectedly."));
+ break;
+ }
+
+ if (row_buf[0] != 0)
+ /* Count + Color - record */
+ {
+ /* encoded mode run -
+ * row_buf[0] == run_length
+ * row_buf[1] == pixel data
+ */
+ for (j = 0;
+ ((guchar) j < row_buf[0]) && (xpos < width);)
+ {
+#ifdef DEBUG2
+ printf("%u %u | ",xpos,width);
+#endif
+ for (i = 1;
+ ((i <= (8 / bpp)) &&
+ (xpos < width) &&
+ ((guchar) j < row_buf[0]));
+ i++, xpos++, j++)
+ {
+ temp = dest + (ypos * rowstride) + (xpos * channels);
+ *temp = (row_buf[1] &
+ (((1<<bpp)-1) << (8 - (i * bpp)))) >> (8 - (i * bpp));
+ if (gray)
+ *temp = cmap[*temp][0];
+ }
+ }
+ }
+
+ if ((row_buf[0] == 0) && (row_buf[1] > 2))
+ /* uncompressed record */
+ {
+ n = row_buf[1];
+ total_bytes_read = 0;
+
+ for (j = 0; j < n; j += (8 / bpp))
+ {
+ /* read the next byte in the record */
+ if (! ReadOK (fd, &v, 1))
+ {
+ g_message (_("The bitmap ends unexpectedly."));
+ break;
+ }
+
+ total_bytes_read++;
+
+ /* read all pixels from that byte */
+ i_max = 8 / bpp;
+ if (n - j < i_max)
+ {
+ i_max = n - j;
+ }
+
+ i = 1;
+ while ((i <= i_max) && (xpos < width))
+ {
+ temp =
+ dest + (ypos * rowstride) + (xpos * channels);
+ *temp = (v >> (8-(i*bpp))) & ((1<<bpp)-1);
+ if (gray)
+ *temp = cmap[*temp][0];
+ i++;
+ xpos++;
+ }
+ }
+
+ /* absolute mode runs are padded to 16-bit alignment */
+ if (total_bytes_read % 2)
+ fread (&v, 1, 1, fd);
+ }
+
+ if ((row_buf[0] == 0) && (row_buf[1] == 0))
+ /* Line end */
+ {
+ ypos--;
+ xpos = 0;
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+
+ if ((row_buf[0] == 0) && (row_buf[1] == 1))
+ /* Bitmap end */
+ {
+ break;
+ }
+
+ if ((row_buf[0] == 0) && (row_buf[1] == 2))
+ /* Deltarecord */
+ {
+ if (! ReadOK (fd, row_buf, 2))
+ {
+ g_message (_("The bitmap ends unexpectedly."));
+ break;
+ }
+
+ xpos += row_buf[0];
+ ypos -= row_buf[1];
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (bpp <= 8)
+ for (i = 0, j = 0; i < ncols; i++)
+ {
+ gimp_cmap[j++] = cmap[i][0];
+ gimp_cmap[j++] = cmap[i][1];
+ gimp_cmap[j++] = cmap[i][2];
+ }
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, dest, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ g_free (dest);
+
+ if ((! gray) && (bpp <= 8))
+ gimp_image_set_colormap (image, gimp_cmap, ncols);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
diff --git a/plug-ins/file-bmp/bmp-load.h b/plug-ins/file-bmp/bmp-load.h
new file mode 100644
index 0000000..7d80abe
--- /dev/null
+++ b/plug-ins/file-bmp/bmp-load.h
@@ -0,0 +1,27 @@
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BMP_LOAD_H__
+#define __BMP_LOAD_H__
+
+
+gint32 load_image (const gchar *filename,
+ GError **error);
+
+
+#endif /* __BMP_LOAD_H__ */
diff --git a/plug-ins/file-bmp/bmp-save.c b/plug-ins/file-bmp/bmp-save.c
new file mode 100644
index 0000000..6654f12
--- /dev/null
+++ b/plug-ins/file-bmp/bmp-save.c
@@ -0,0 +1,1104 @@
+/* bmpwrite.c Writes Bitmap files. Even RLE encoded ones. */
+/* (Windows (TM) doesn't read all of those, but who */
+/* cares? ;-) */
+/* I changed a few things over the time, so perhaps */
+/* it dos now, but now there's no Windows left on */
+/* my computer... */
+
+/* Alexander.Schulz@stud.uni-karlsruhe.de */
+
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "bmp.h"
+#include "bmp-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+typedef enum
+{
+ RGB_565,
+ RGBA_5551,
+ RGB_555,
+ RGB_888,
+ RGBA_8888,
+ RGBX_8888
+} RGBMode;
+
+
+static void write_image (FILE *f,
+ guchar *src,
+ gint width,
+ gint height,
+ gint use_run_length_encoding,
+ gint channels,
+ gint bpp,
+ gint spzeile,
+ gint MapSize,
+ RGBMode rgb_format,
+ gint mask_info_size,
+ gint color_space_size);
+
+static gboolean save_dialog (gint channels,
+ gint bpp);
+
+
+static struct
+{
+ RGBMode rgb_format;
+ gint use_run_length_encoding;
+
+ /* Whether or not to write BITMAPV5HEADER color space data */
+ gint dont_write_color_space_data;
+ gboolean overwrite_RGB_format;
+} BMPSaveData;
+
+
+static void
+write_color_map (FILE *f,
+ gint red[MAXCOLORS],
+ gint green[MAXCOLORS],
+ gint blue[MAXCOLORS],
+ gint size)
+{
+ gchar trgb[4];
+ gint i;
+
+ size /= 4;
+ trgb[3] = 0;
+ for (i = 0; i < size; i++)
+ {
+ trgb[0] = (guchar) blue[i];
+ trgb[1] = (guchar) green[i];
+ trgb[2] = (guchar) red[i];
+ Write (f, trgb, 4);
+ }
+}
+
+static gboolean
+warning_dialog (const gchar *primary,
+ const gchar *secondary)
+{
+ GtkWidget *dialog;
+ gboolean ok;
+
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
+ "%s", primary);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", secondary);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ ok = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return ok;
+}
+
+GimpPDBStatusType
+save_image (const gchar *filename,
+ gint32 image,
+ gint32 drawable_ID,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ FILE *outfile;
+ BitmapFileHead bitmap_file_head;
+ BitmapHead bitmap_head;
+ gint Red[MAXCOLORS];
+ gint Green[MAXCOLORS];
+ gint Blue[MAXCOLORS];
+ guchar *cmap;
+ gint rows, cols, Spcols, channels, MapSize, SpZeile;
+ glong BitsPerPixel;
+ gint colors;
+ guchar *pixels;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GimpImageType drawable_type;
+ gint drawable_width;
+ gint drawable_height;
+ gint i;
+ gint mask_info_size;
+ gint color_space_size;
+ guint32 Mask[4];
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+ drawable_width = gimp_drawable_width (drawable_ID);
+ drawable_height = gimp_drawable_height (drawable_ID);
+
+ switch (drawable_type)
+ {
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ colors = 0;
+ BitsPerPixel = 32;
+ MapSize = 0;
+ channels = 4;
+ if (!BMPSaveData.overwrite_RGB_format)
+ BMPSaveData.rgb_format = RGBA_8888;
+ break;
+
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ colors = 0;
+ BitsPerPixel = 24;
+ MapSize = 0;
+ channels = 3;
+ if (!BMPSaveData.overwrite_RGB_format)
+ BMPSaveData.rgb_format = RGB_888;
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ ! warning_dialog (_("Cannot export indexed image with "
+ "transparency in BMP file format."),
+ _("Alpha channel will be ignored.")))
+ return GIMP_PDB_CANCEL;
+
+ /* fallthrough */
+
+ case GIMP_GRAY_IMAGE:
+ colors = 256;
+ BitsPerPixel = 8;
+ MapSize = 1024;
+
+ if (drawable_type == GIMP_GRAYA_IMAGE)
+ {
+ format = babl_format ("Y'A u8");
+ channels = 2;
+ }
+ else
+ {
+ format = babl_format ("Y' u8");
+ channels = 1;
+ }
+
+ for (i = 0; i < colors; i++)
+ {
+ Red[i] = i;
+ Green[i] = i;
+ Blue[i] = i;
+ }
+ break;
+
+ case GIMP_INDEXEDA_IMAGE:
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ ! warning_dialog (_("Cannot export indexed image with "
+ "transparency in BMP file format."),
+ _("Alpha channel will be ignored.")))
+ return GIMP_PDB_CANCEL;
+
+ /* fallthrough */
+
+ case GIMP_INDEXED_IMAGE:
+ format = gimp_drawable_get_format (drawable_ID);
+ cmap = gimp_image_get_colormap (image, &colors);
+ MapSize = 4 * colors;
+
+ if (drawable_type == GIMP_INDEXEDA_IMAGE)
+ channels = 2;
+ else
+ channels = 1;
+
+ if (colors > 16)
+ BitsPerPixel = 8;
+ else if (colors > 2)
+ BitsPerPixel = 4;
+ else
+ BitsPerPixel = 1;
+
+ for (i = 0; i < colors; i++)
+ {
+ Red[i] = *cmap++;
+ Green[i] = *cmap++;
+ Blue[i] = *cmap++;
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Don't alter option data if already defined in non-interactive mode Script-fu */
+ if (BMPSaveData.use_run_length_encoding != 1)
+ BMPSaveData.use_run_length_encoding = 0;
+
+ if (BMPSaveData.dont_write_color_space_data != 1)
+ BMPSaveData.dont_write_color_space_data = 0;
+ mask_info_size = 0;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ gimp_get_data (SAVE_PROC, &BMPSaveData);
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ (BitsPerPixel == 8 ||
+ BitsPerPixel == 4 ||
+ BitsPerPixel == 1))
+ {
+ if (! save_dialog (1, BitsPerPixel))
+ return GIMP_PDB_CANCEL;
+ }
+ else if (BitsPerPixel == 24 ||
+ BitsPerPixel == 32)
+ {
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! save_dialog (channels, BitsPerPixel))
+ return GIMP_PDB_CANCEL;
+ }
+
+ /* mask_info_size is only set to non-zero for 16- and 32-bpp */
+ switch (BMPSaveData.rgb_format)
+ {
+ case RGB_888:
+ BitsPerPixel = 24;
+ break;
+ case RGBA_8888:
+ BitsPerPixel = 32;
+ mask_info_size = 16;
+ break;
+ case RGBX_8888:
+ BitsPerPixel = 32;
+ mask_info_size = 16;
+ break;
+ case RGB_565:
+ BitsPerPixel = 16;
+ mask_info_size = 16;
+ break;
+ case RGBA_5551:
+ BitsPerPixel = 16;
+ mask_info_size = 16;
+ break;
+ case RGB_555:
+ BitsPerPixel = 16;
+ mask_info_size = 16;
+ break;
+ default:
+ g_return_val_if_reached (GIMP_PDB_EXECUTION_ERROR);
+ }
+ }
+
+ gimp_set_data (SAVE_PROC, &BMPSaveData, sizeof (BMPSaveData));
+
+ /* Let's begin the progress */
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Let's take some file */
+ outfile = g_fopen (filename, "wb");
+ if (!outfile)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* fetch the image */
+ pixels = g_new (guchar, drawable_width * drawable_height * channels);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0,
+ drawable_width, drawable_height), 1.0,
+ format, pixels,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ /* Now, we need some further information ... */
+ cols = drawable_width;
+ rows = drawable_height;
+
+ /* ... that we write to our headers. */
+ if ((BitsPerPixel <= 8) && (cols % (8 / BitsPerPixel)))
+ Spcols = (((cols / (8 / BitsPerPixel)) + 1) * (8 / BitsPerPixel));
+ else
+ Spcols = cols;
+
+ if ((((Spcols * BitsPerPixel) / 8) % 4) == 0)
+ SpZeile = ((Spcols * BitsPerPixel) / 8);
+ else
+ SpZeile = ((gint) (((Spcols * BitsPerPixel) / 8) / 4) + 1) * 4;
+
+ if (! BMPSaveData.dont_write_color_space_data)
+ {
+ /* Always include color mask for BITMAPV5HEADER, see #4155. */
+ mask_info_size = 16;
+ color_space_size = 68;
+ }
+ else
+ {
+ color_space_size = 0;
+ }
+
+ bitmap_file_head.bfSize = (0x36 + MapSize + (rows * SpZeile) +
+ mask_info_size + color_space_size);
+ bitmap_file_head.zzHotX = 0;
+ bitmap_file_head.zzHotY = 0;
+ bitmap_file_head.bfOffs = (0x36 + MapSize +
+ mask_info_size + color_space_size);
+ bitmap_file_head.biSize = 40 + mask_info_size + color_space_size;
+
+ bitmap_head.biWidth = cols;
+ bitmap_head.biHeight = rows;
+ bitmap_head.biPlanes = 1;
+ bitmap_head.biBitCnt = BitsPerPixel;
+
+ if (BMPSaveData.use_run_length_encoding == 0)
+ {
+ /* The Microsoft specification for BITMAPV5HEADER says that
+ * BI_BITFIELDS is valid for 16 and 32-bits per pixel,
+ * Since it doesn't mention 24 bpp or other numbers
+ * use BI_RGB for that. See issue #6114. */
+ if (mask_info_size > 0 && (BitsPerPixel == 16 || BitsPerPixel == 32))
+ bitmap_head.biCompr = 3; /* BI_BITFIELDS */
+ else
+ bitmap_head.biCompr = 0; /* BI_RGB */
+ }
+ else if (BitsPerPixel == 8)
+ {
+ bitmap_head.biCompr = 1;
+ }
+ else if (BitsPerPixel == 4)
+ {
+ bitmap_head.biCompr = 2;
+ }
+ else
+ {
+ bitmap_head.biCompr = 0;
+ }
+
+ bitmap_head.biSizeIm = SpZeile * rows;
+
+ {
+ gdouble xresolution;
+ gdouble yresolution;
+ gimp_image_get_resolution (image, &xresolution, &yresolution);
+
+ if (xresolution > GIMP_MIN_RESOLUTION &&
+ yresolution > GIMP_MIN_RESOLUTION)
+ {
+ /*
+ * xresolution and yresolution are in dots per inch.
+ * the BMP spec says that biXPels and biYPels are in
+ * pixels per meter as long ints (actually, "DWORDS"),
+ * so...
+ * n dots inch 100 cm m dots
+ * ------ * ------- * ------ = ------
+ * inch 2.54 cm m inch
+ *
+ * We add 0.5 for proper rounding.
+ */
+ bitmap_head.biXPels = (long int) (xresolution * 100.0 / 2.54 + 0.5);
+ bitmap_head.biYPels = (long int) (yresolution * 100.0 / 2.54 + 0.5);
+ }
+ }
+
+ if (BitsPerPixel <= 8)
+ bitmap_head.biClrUsed = colors;
+ else
+ bitmap_head.biClrUsed = 0;
+
+ bitmap_head.biClrImp = bitmap_head.biClrUsed;
+
+#ifdef DEBUG
+ printf ("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n",
+ (int)bitmap_file_head.bfSize,
+ (int)bitmap_head.biClrUsed,
+ bitmap_head.biBitCnt,
+ (int)bitmap_head.biWidth,
+ (int)bitmap_head.biHeight,
+ (int)bitmap_head.biCompr,SpZeile);
+#endif
+
+ /* And now write the header and the colormap (if any) to disk */
+
+ Write (outfile, "BM", 2);
+
+ bitmap_file_head.bfSize = GUINT32_TO_LE (bitmap_file_head.bfSize);
+ bitmap_file_head.zzHotX = GUINT16_TO_LE (bitmap_file_head.zzHotX);
+ bitmap_file_head.zzHotY = GUINT16_TO_LE (bitmap_file_head.zzHotY);
+ bitmap_file_head.bfOffs = GUINT32_TO_LE (bitmap_file_head.bfOffs);
+ bitmap_file_head.biSize = GUINT32_TO_LE (bitmap_file_head.biSize);
+
+ Write (outfile, &bitmap_file_head.bfSize, 16);
+
+ bitmap_head.biWidth = GINT32_TO_LE (bitmap_head.biWidth);
+ bitmap_head.biHeight = GINT32_TO_LE (bitmap_head.biHeight);
+ bitmap_head.biPlanes = GUINT16_TO_LE (bitmap_head.biPlanes);
+ bitmap_head.biBitCnt = GUINT16_TO_LE (bitmap_head.biBitCnt);
+ bitmap_head.biCompr = GUINT32_TO_LE (bitmap_head.biCompr);
+ bitmap_head.biSizeIm = GUINT32_TO_LE (bitmap_head.biSizeIm);
+ bitmap_head.biXPels = GUINT32_TO_LE (bitmap_head.biXPels);
+ bitmap_head.biYPels = GUINT32_TO_LE (bitmap_head.biYPels);
+ bitmap_head.biClrUsed = GUINT32_TO_LE (bitmap_head.biClrUsed);
+ bitmap_head.biClrImp = GUINT32_TO_LE (bitmap_head.biClrImp);
+
+ Write (outfile, &bitmap_head, 36);
+
+ if (mask_info_size > 0)
+ {
+ switch (BMPSaveData.rgb_format)
+ {
+ default:
+ case RGB_888:
+ case RGBX_8888:
+ Mask[0] = 0x00ff0000;
+ Mask[1] = 0x0000ff00;
+ Mask[2] = 0x000000ff;
+ Mask[3] = 0x00000000;
+ break;
+
+ case RGBA_8888:
+ Mask[0] = 0x00ff0000;
+ Mask[1] = 0x0000ff00;
+ Mask[2] = 0x000000ff;
+ Mask[3] = 0xff000000;
+ break;
+
+ case RGB_565:
+ Mask[0] = 0xf800;
+ Mask[1] = 0x7e0;
+ Mask[2] = 0x1f;
+ Mask[3] = 0x0;
+ break;
+
+ case RGBA_5551:
+ Mask[0] = 0x7c00;
+ Mask[1] = 0x3e0;
+ Mask[2] = 0x1f;
+ Mask[3] = 0x8000;
+ break;
+
+ case RGB_555:
+ Mask[0] = 0x7c00;
+ Mask[1] = 0x3e0;
+ Mask[2] = 0x1f;
+ Mask[3] = 0x0;
+ break;
+ }
+
+ Mask[0] = GUINT32_TO_LE (Mask[0]);
+ Mask[1] = GUINT32_TO_LE (Mask[1]);
+ Mask[2] = GUINT32_TO_LE (Mask[2]);
+ Mask[3] = GUINT32_TO_LE (Mask[3]);
+
+ Write (outfile, &Mask, mask_info_size);
+ }
+
+ if (! BMPSaveData.dont_write_color_space_data)
+ {
+ guint32 buf[0x11];
+
+ /* Write V5 color space fields */
+
+ /* bV5CSType = LCS_sRGB */
+ buf[0x00] = GUINT32_TO_LE (0x73524742);
+
+ /* bV5Endpoints is set to 0 (ignored) */
+ for (i = 0; i < 0x09; i++)
+ buf[i + 1] = 0x00;
+
+ /* bV5GammaRed is set to 0 (ignored) */
+ buf[0x0a] = GUINT32_TO_LE (0x0);
+
+ /* bV5GammaGreen is set to 0 (ignored) */
+ buf[0x0b] = GUINT32_TO_LE (0x0);
+
+ /* bV5GammaBlue is set to 0 (ignored) */
+ buf[0x0c] = GUINT32_TO_LE (0x0);
+
+ /* bV5Intent = LCS_GM_GRAPHICS */
+ buf[0x0d] = GUINT32_TO_LE (0x00000002);
+
+ /* bV5ProfileData is set to 0 (ignored) */
+ buf[0x0e] = GUINT32_TO_LE (0x0);
+
+ /* bV5ProfileSize is set to 0 (ignored) */
+ buf[0x0f] = GUINT32_TO_LE (0x0);
+
+ /* bV5Reserved = 0 */
+ buf[0x10] = GUINT32_TO_LE (0x0);
+
+ Write (outfile, buf, color_space_size);
+ }
+
+ write_color_map (outfile, Red, Green, Blue, MapSize);
+
+ /* After that is done, we write the image ... */
+
+ write_image (outfile,
+ pixels, cols, rows,
+ BMPSaveData.use_run_length_encoding,
+ channels, BitsPerPixel, SpZeile,
+ MapSize, BMPSaveData.rgb_format,
+ mask_info_size, color_space_size);
+
+ /* ... and exit normally */
+
+ fclose (outfile);
+ g_free (pixels);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+/* Entry point for file-bmp-save2 */
+GimpPDBStatusType
+save_image2 (const gchar *filename,
+ gint32 image,
+ gint32 drawable_ID,
+ gint32 use_rle,
+ gint32 write_color_space,
+ gint32 rgb_format,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ BMPSaveData.use_run_length_encoding = use_rle;
+ BMPSaveData.dont_write_color_space_data = write_color_space;
+ BMPSaveData.rgb_format = (RGBMode) rgb_format;
+ /* Prevents save_image () from overwriting user's RGB format */
+ BMPSaveData.overwrite_RGB_format = TRUE;
+
+ return save_image (filename, image, drawable_ID,
+ run_mode, error);
+}
+
+static inline void
+Make565 (guchar r,
+ guchar g,
+ guchar b,
+ guchar *buf)
+{
+ gint p;
+
+ p = ((((gint) (r / 255.0 * 31.0 + 0.5)) << 11) |
+ (((gint) (g / 255.0 * 63.0 + 0.5)) << 5) |
+ (((gint) (b / 255.0 * 31.0 + 0.5))));
+
+ buf[0] = (guchar) (p & 0xff);
+ buf[1] = (guchar) (p >> 8);
+}
+
+static inline void
+Make5551 (guchar r,
+ guchar g,
+ guchar b,
+ guchar a,
+ guchar *buf)
+{
+ gint p;
+
+ p = ((((gint) (r / 255.0 * 31.0 + 0.5)) << 10) |
+ (((gint) (g / 255.0 * 31.0 + 0.5)) << 5) |
+ (((gint) (b / 255.0 * 31.0 + 0.5))) |
+ (((gint) (a / 255.0 + 0.5) << 15)));
+
+ buf[0] = (guchar) (p & 0xff);
+ buf[1] = (guchar) (p >> 8);
+}
+
+static void
+write_image (FILE *f,
+ guchar *src,
+ gint width,
+ gint height,
+ gint use_run_length_encoding,
+ gint channels,
+ gint bpp,
+ gint spzeile,
+ gint MapSize,
+ RGBMode rgb_format,
+ gint mask_info_size,
+ gint color_space_size)
+{
+ guchar buf[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 };
+ guint32 uint32buf;
+ guchar *temp, v;
+ guchar *row, *ketten;
+ gint xpos, ypos, i, j, rowstride, length, thiswidth;
+ gint breite, k;
+ guchar n, r, g, b, a;
+ gint cur_progress;
+ gint max_progress;
+
+ xpos = 0;
+ rowstride = width * channels;
+
+ cur_progress = 0;
+ max_progress = height;
+
+ /* We'll begin with the 16/24/32 bit Bitmaps, they are easy :-) */
+
+ if (bpp > 8)
+ {
+ for (ypos = height - 1; ypos >= 0; ypos--) /* for each row */
+ {
+ for (i = 0; i < width; i++) /* for each pixel */
+ {
+ temp = src + (ypos * rowstride) + (xpos * channels);
+ switch (rgb_format)
+ {
+ default:
+ case RGB_888:
+ buf[2] = *temp++;
+ buf[1] = *temp++;
+ buf[0] = *temp++;
+ xpos++;
+ if (channels > 3 && (guchar) *temp == 0)
+ buf[0] = buf[1] = buf[2] = 0xff;
+ Write (f, buf, 3);
+ break;
+ case RGBX_8888:
+ buf[2] = *temp++;
+ buf[1] = *temp++;
+ buf[0] = *temp++;
+ buf[3] = 0;
+ xpos++;
+ if (channels > 3 && (guchar) *temp == 0)
+ buf[0] = buf[1] = buf[2] = 0xff;
+ Write (f, buf, 4);
+ break;
+ case RGBA_8888:
+ buf[2] = *temp++;
+ buf[1] = *temp++;
+ buf[0] = *temp++;
+ buf[3] = *temp;
+ xpos++;
+ Write (f, buf, 4);
+ break;
+ case RGB_565:
+ r = *temp++;
+ g = *temp++;
+ b = *temp++;
+ if (channels > 3 && (guchar) *temp == 0)
+ r = g = b = 0xff;
+ Make565 (r, g, b, buf);
+ xpos++;
+ Write (f, buf, 2);
+ break;
+ case RGB_555:
+ r = *temp++;
+ g = *temp++;
+ b = *temp++;
+ if (channels > 3 && (guchar) *temp == 0)
+ r = g = b = 0xff;
+ Make5551 (r, g, b, 0x0, buf);
+ xpos++;
+ Write (f, buf, 2);
+ break;
+ case RGBA_5551:
+ r = *temp++;
+ g = *temp++;
+ b = *temp++;
+ a = *temp;
+ Make5551 (r, g, b, a, buf);
+ xpos++;
+ Write (f, buf, 2);
+ break;
+ }
+ }
+
+ Write (f, &buf[4], spzeile - (width * (bpp/8)));
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+
+ xpos = 0;
+ }
+ }
+ else
+ {
+ if (bpp == 1)
+ use_run_length_encoding = 0;
+ switch (use_run_length_encoding) /* now it gets more difficult */
+ { /* uncompressed 1,4 and 8 bit */
+ case 0:
+ {
+ thiswidth = (width / (8 / bpp));
+ if (width % (8 / bpp))
+ thiswidth++;
+
+ for (ypos = height - 1; ypos >= 0; ypos--) /* for each row */
+ {
+ for (xpos = 0; xpos < width;) /* for each _byte_ */
+ {
+ v = 0;
+ for (i = 1;
+ (i <= (8 / bpp)) && (xpos < width);
+ i++, xpos++) /* for each pixel */
+ {
+ temp = src + (ypos * rowstride) + (xpos * channels);
+ if (channels > 1 && *(temp+1) == 0) *temp = 0x0;
+ v=v | ((guchar) *temp << (8 - (i * bpp)));
+ }
+ Write (f, &v, 1);
+ }
+ Write (f, &buf[3], spzeile - thiswidth);
+ xpos = 0;
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+ break;
+ }
+ default:
+ { /* Save RLE encoded file, quite difficult */
+ length = 0;
+ buf[12] = 0;
+ buf[13] = 1;
+ buf[14] = 0;
+ buf[15] = 0;
+ row = g_new (guchar, width / (8 / bpp) + 10);
+ ketten = g_new (guchar, width / (8 / bpp) + 10);
+ for (ypos = height - 1; ypos >= 0; ypos--)
+ { /* each row separately */
+ j = 0;
+ /* first copy the pixels to a buffer,
+ * making one byte from two 4bit pixels
+ */
+ for (xpos = 0; xpos < width;)
+ {
+ v = 0;
+ for (i = 1;
+ (i <= (8 / bpp)) && (xpos < width);
+ i++, xpos++)
+ { /* for each pixel */
+ temp = src + (ypos * rowstride) + (xpos * channels);
+ if (channels > 1 && *(temp+1) == 0) *temp = 0x0;
+ v = v | ((guchar) * temp << (8 - (i * bpp)));
+ }
+ row[j++] = v;
+ }
+ breite = width / (8 / bpp);
+ if (width % (8 / bpp))
+ breite++;
+
+ /* then check for strings of equal bytes */
+ for (i = 0; i < breite; i += j)
+ {
+ j = 0;
+ while ((i + j < breite) &&
+ (j < (255 / (8 / bpp))) &&
+ (row[i + j] == row[i]))
+ j++;
+
+ ketten[i] = j;
+ }
+
+ /* then write the strings and the other pixels to the file */
+ for (i = 0; i < breite;)
+ {
+ if (ketten[i] < 3)
+ /* strings of different pixels ... */
+ {
+ j = 0;
+ while ((i + j < breite) &&
+ (j < (255 / (8 / bpp))) &&
+ (ketten[i + j] < 3))
+ j += ketten[i + j];
+
+ /* this can only happen if j jumps over
+ * the end with a 2 in ketten[i+j]
+ */
+ if (j > (255 / (8 / bpp)))
+ j -= 2;
+ /* 00 01 and 00 02 are reserved */
+ if (j > 2)
+ {
+ Write (f, &buf[12], 1);
+ n = j * (8 / bpp);
+ if (n + i * (8 / bpp) > width)
+ n--;
+ Write (f, &n, 1);
+ length += 2;
+ Write (f, &row[i], j);
+ length += j;
+ if ((j) % 2)
+ {
+ Write (f, &buf[12], 1);
+ length++;
+ }
+ }
+ else
+ {
+ for (k = i; k < i + j; k++)
+ {
+ n = (8 / bpp);
+ if (n + i * (8 / bpp) > width)
+ n--;
+ Write (f, &n, 1);
+ Write (f, &row[k], 1);
+ /*printf("%i.#|",n); */
+ length += 2;
+ }
+ }
+ i += j;
+ }
+ else
+ /* strings of equal pixels */
+ {
+ n = ketten[i] * (8 / bpp);
+ if (n + i * (8 / bpp) > width)
+ n--;
+ Write (f, &n, 1);
+ Write (f, &row[i], 1);
+ i += ketten[i];
+ length += 2;
+ }
+ }
+
+ Write (f, &buf[14], 2); /* End of row */
+ length += 2;
+
+ cur_progress++;
+ if ((cur_progress % 5) == 0)
+ gimp_progress_update ((gdouble) cur_progress /
+ (gdouble) max_progress);
+ }
+
+ fseek (f, -2, SEEK_CUR); /* Overwrite last End of row ... */
+ Write (f, &buf[12], 2); /* ... with End of file */
+
+ fseek (f, 0x22, SEEK_SET); /* Write length of image */
+ uint32buf = GUINT32_TO_LE (length);
+ Write (f, &uint32buf, 4);
+
+ fseek (f, 0x02, SEEK_SET); /* Write length of file */
+ length += (0x36 + MapSize + mask_info_size + color_space_size);
+ uint32buf = GUINT32_TO_LE (length);
+ Write (f, &uint32buf, 4);
+
+ g_free (ketten);
+ g_free (row);
+ break;
+ }
+ }
+ }
+
+ gimp_progress_update (1.0);
+}
+
+static void
+format_callback (GtkToggleButton *toggle,
+ gpointer data)
+{
+ if (gtk_toggle_button_get_active (toggle))
+ BMPSaveData.rgb_format = GPOINTER_TO_INT (data);
+}
+
+static gboolean
+save_dialog (gint channels, gint bpp)
+{
+ GtkWidget *dialog;
+ GtkWidget *toggle;
+ GtkWidget *vbox_main;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ GSList *group;
+ gboolean run;
+
+ /* Dialog init */
+ dialog = gimp_export_dialog_new ("BMP", PLUG_IN_BINARY, SAVE_PROC);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ vbox_main = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox_main), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox_main, TRUE, TRUE, 0);
+ gtk_widget_show (vbox_main);
+
+ /* Run-Length Encoded */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Run-Length Encoded"));
+ gtk_box_pack_start (GTK_BOX (vbox_main), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ BMPSaveData.use_run_length_encoding);
+ gtk_widget_show (toggle);
+ if (channels > 1 || bpp == 1)
+ gtk_widget_set_sensitive (toggle, FALSE);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &BMPSaveData.use_run_length_encoding);
+
+ /* Compatibility Options */
+ expander = gtk_expander_new_with_mnemonic (_("Co_mpatibility Options"));
+
+ gtk_box_pack_start (GTK_BOX (vbox_main), expander, TRUE, TRUE, 0);
+ gtk_widget_show (expander);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
+ gtk_container_add (GTK_CONTAINER (expander), vbox2);
+ gtk_widget_show (vbox2);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("_Do not write color space information"));
+ gimp_help_set_help_data (toggle,
+ _("Some applications can not read BMP images that "
+ "include color space information. GIMP writes "
+ "color space information by default. Enabling "
+ "this option will cause GIMP to not write color "
+ "space information to the file."),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ BMPSaveData.dont_write_color_space_data);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &BMPSaveData.dont_write_color_space_data);
+
+ /* Advanced Options */
+ expander = gtk_expander_new_with_mnemonic (_("_Advanced Options"));
+
+ gtk_box_pack_start (GTK_BOX (vbox_main), expander, TRUE, TRUE, 0);
+ gtk_widget_show (expander);
+
+ if (channels < 3)
+ gtk_widget_set_sensitive (expander, FALSE);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
+ gtk_container_add (GTK_CONTAINER (expander), vbox2);
+ gtk_widget_show (vbox2);
+
+ group = NULL;
+
+ frame = gimp_frame_new (_("16 bits"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_radio_button_new_with_mnemonic (group, "_R5 G6 B5");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGB_565));
+
+ toggle = gtk_radio_button_new_with_mnemonic (group, "_A1 R5 G5 B5");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+
+ if (channels < 4)
+ gtk_widget_set_sensitive (toggle, FALSE);
+
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGBA_5551));
+ toggle = gtk_radio_button_new_with_mnemonic (group, "_X1 R5 G5 B5");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGB_555));
+
+ frame = gimp_frame_new (_("24 bits"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_radio_button_new_with_mnemonic (group, "R_8 G8 B8");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON(toggle));
+ gtk_container_add (GTK_CONTAINER (frame), toggle);
+ gtk_widget_show (toggle);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGB_888));
+ if (channels < 4)
+ {
+ BMPSaveData.rgb_format = RGB_888;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
+ }
+
+ frame = gimp_frame_new (_("32 bits"));
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_radio_button_new_with_mnemonic (group, "A8 R8 G8 _B8");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGBA_8888));
+
+
+ if (channels < 4)
+ {
+ gtk_widget_set_sensitive (toggle, FALSE);
+ }
+ else
+ {
+ BMPSaveData.rgb_format = RGBA_8888;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
+ }
+
+ toggle = gtk_radio_button_new_with_mnemonic (group, "X8 R8 G8 _B8");
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (format_callback),
+ GINT_TO_POINTER (RGBX_8888));
+
+ /* Dialog show */
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/file-bmp/bmp-save.h b/plug-ins/file-bmp/bmp-save.h
new file mode 100644
index 0000000..1483104
--- /dev/null
+++ b/plug-ins/file-bmp/bmp-save.h
@@ -0,0 +1,39 @@
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BMP_SAVE_H__
+#define __BMP_SAVE_H__
+
+
+GimpPDBStatusType save_image (const gchar *filename,
+ gint32 image,
+ gint32 drawable_ID,
+ GimpRunMode run_mode,
+ GError **error);
+
+GimpPDBStatusType save_image2 (const gchar *filename,
+ gint32 image,
+ gint32 drawable_ID,
+ gint32 use_rle,
+ gint32 write_color_space,
+ gint32 rgb_format,
+ GimpRunMode run_mode,
+ GError **error);
+
+
+#endif /* __BMP_SAVE_H__ */
diff --git a/plug-ins/file-bmp/bmp.c b/plug-ins/file-bmp/bmp.c
new file mode 100644
index 0000000..02d7aeb
--- /dev/null
+++ b/plug-ins/file-bmp/bmp.c
@@ -0,0 +1,306 @@
+/* bmp.c */
+/* Version 0.52 */
+/* This is a File input and output filter for the */
+/* Gimp. It loads and saves images in windows(TM) */
+/* bitmap format. */
+/* Some Parts that deal with the interaction with */
+/* GIMP are taken from the GIF plugin by */
+/* Peter Mattis & Spencer Kimball and from the */
+/* PCX plugin by Francisco Bustamante. */
+/* */
+/* Alexander.Schulz@stud.uni-karlsruhe.de */
+
+/* Changes: 28.11.1997 Noninteractive operation */
+/* 16.03.1998 Endian-independent!! */
+/* 21.03.1998 Little Bug-fix */
+/* 06.04.1998 Bugfix in Padding */
+/* 11.04.1998 Arch. cleanup (-Wall) */
+/* Parses gtkrc */
+/* 14.04.1998 Another Bug in Padding */
+/* 28.04.1998 RLE-Encoding rewritten */
+/* 29.10.1998 Changes by Tor Lillqvist */
+/* <tml@iki.fi> to support */
+/* 16 and 32 bit images */
+/* 28.11.1998 Bug in RLE-read-padding */
+/* fixed. */
+/* 19.12.1999 Resolution support added */
+/* 06.05.2000 Overhaul for 16&24-bit */
+/* plus better OS/2 code */
+/* by njl195@zepler.org.uk */
+/* 29.06.2006 Full support for 16/32 */
+/* bits bitmaps and support */
+/* for alpha channel */
+/* by p.filiciak@zax.pl */
+
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "bmp.h"
+#include "bmp-load.h"
+#include "bmp-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef save_args2[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "use-rle", "Use run-length-encoding compression (only valid for 4 and 8-bit indexed images)" },
+ { GIMP_PDB_INT32, "write-color-space", "Whether or not to write BITMAPV5HEADER color space data" },
+ { GIMP_PDB_INT32, "rgb-format", "Export format for RGB images (0=RGB_565, 1=RGBA_5551, 2=RGB_555, 3=RGB_888, 4=RGBA_8888, 5=RGBX_8888)" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files of Windows BMP file format",
+ "Loads files of Windows BMP file format",
+ "Alexander Schulz",
+ "Alexander Schulz",
+ "1997",
+ N_("Windows BMP image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/bmp");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "bmp",
+ "",
+ "0,string,BM");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves files in Windows BMP file format",
+ "Saves files in Windows BMP file format",
+ "Alexander Schulz",
+ "Alexander Schulz",
+ "1997",
+ N_("Windows BMP image"),
+ "INDEXED, GRAY, RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/bmp");
+ gimp_register_save_handler (SAVE_PROC, "bmp", "");
+
+ gimp_install_procedure (SAVE_PROC2,
+ "Saves files in Windows BMP file format",
+ "Saves files in Windows BMP file format, "
+ "with RLE, color space information, and RGB format "
+ "options available non-interactively",
+ "Alexander Schulz",
+ "Alexander Schulz",
+ "1997",
+ N_("Windows BMP image"),
+ "INDEXED, GRAY, RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args2), 0,
+ save_args2, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC2, "image/bmp");
+ gimp_register_save_handler (SAVE_PROC2, "bmp", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint32 image_ID = load_image (param[1].data.d_string,
+ &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0 ||
+ strcmp (name, SAVE_PROC2) == 0)
+ {
+ gint32 image_ID = param[1].data.d_int32;
+ gint32 drawable_ID = param[2].data.d_int32;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "BMP",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if ((strcmp (name, SAVE_PROC) == 0 && nparams != 5) ||
+ (strcmp (name, SAVE_PROC2) == 0 && nparams != 8))
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (strcmp (name, SAVE_PROC) == 0)
+ status = save_image (param[3].data.d_string,
+ image_ID, drawable_ID,
+ run_mode,
+ &error);
+ else
+ status = save_image2 (param[3].data.d_string,
+ image_ID, drawable_ID,
+ param[5].data.d_int32,
+ param[6].data.d_int32,
+ param[7].data.d_int32,
+ run_mode,
+ &error);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/file-bmp/bmp.h b/plug-ins/file-bmp/bmp.h
new file mode 100644
index 0000000..259b025
--- /dev/null
+++ b/plug-ins/file-bmp/bmp.h
@@ -0,0 +1,71 @@
+/*
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BMP_H__
+#define __BMP_H__
+
+
+#define LOAD_PROC "file-bmp-load"
+#define SAVE_PROC "file-bmp-save"
+#define SAVE_PROC2 "file-bmp-save2"
+#define PLUG_IN_BINARY "file-bmp"
+#define PLUG_IN_ROLE "gimp-file-bmp"
+
+#define MAXCOLORS 256
+
+#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
+
+#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
+#define Write(file,buffer,len) fwrite(buffer, len, 1, file)
+#define WriteOK(file,buffer,len) (Write(buffer, len, file) != 0)
+
+
+typedef struct
+{
+ gchar zzMagic[2]; /* 00 "BM" */
+ guint32 bfSize; /* 02 */
+ guint16 zzHotX; /* 06 */
+ guint16 zzHotY; /* 08 */
+ guint32 bfOffs; /* 0A */
+ guint32 biSize; /* 0E */
+} BitmapFileHead;
+
+typedef struct
+{
+ gint32 biWidth; /* 12 */
+ gint32 biHeight; /* 16 */
+ guint16 biPlanes; /* 1A */
+ guint16 biBitCnt; /* 1C */
+ guint32 biCompr; /* 1E */
+ guint32 biSizeIm; /* 22 */
+ guint32 biXPels; /* 26 */
+ guint32 biYPels; /* 2A */
+ guint32 biClrUsed; /* 2E */
+ guint32 biClrImp; /* 32 */
+ guint32 masks[4]; /* 36 */
+} BitmapHead;
+
+typedef struct
+{
+ guint32 mask;
+ guint32 shiftin;
+ gfloat max_value;
+} BitmapChannel;
+
+
+#endif /* __BMP_H__ */
diff --git a/plug-ins/file-dds/COPYING b/plug-ins/file-dds/COPYING
new file mode 100644
index 0000000..6f17fd7
--- /dev/null
+++ b/plug-ins/file-dds/COPYING
@@ -0,0 +1,339 @@
+ 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 Library 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
+
+ Appendix: 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 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) 19yy 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.
+
+ <signature of Ty Coon>, 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 Library General
+Public License instead of this License.
diff --git a/plug-ins/file-dds/Makefile.am b/plug-ins/file-dds/Makefile.am
new file mode 100644
index 0000000..72fc5a7
--- /dev/null
+++ b/plug-ins/file-dds/Makefile.am
@@ -0,0 +1,68 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_dds_RC = file-dds.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+AM_CFLAGS = -fno-strict-aliasing
+
+libexecdir = $(gimpplugindir)/plug-ins/file-dds
+
+libexec_PROGRAMS = file-dds
+
+file_dds_SOURCES = \
+ dds.c \
+ dds.h \
+ color.c \
+ color.h \
+ ddsplugin.h \
+ ddsread.c \
+ ddswrite.c \
+ dxt.c \
+ dxt.h \
+ dxt_tables.h \
+ endian_rw.h \
+ imath.h \
+ mipmap.c \
+ mipmap.h \
+ misc.c \
+ misc.h \
+ mktables.c \
+ vec.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(libm) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_dds_RC)
diff --git a/plug-ins/file-dds/Makefile.in b/plug-ins/file-dds/Makefile.in
new file mode 100644
index 0000000..8cb3eb5
--- /dev/null
+++ b/plug-ins/file-dds/Makefile.in
@@ -0,0 +1,1046 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-dds$(EXEEXT)
+subdir = plug-ins/file-dds
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_dds_OBJECTS = dds.$(OBJEXT) color.$(OBJEXT) ddsread.$(OBJEXT) \
+ ddswrite.$(OBJEXT) dxt.$(OBJEXT) mipmap.$(OBJEXT) \
+ misc.$(OBJEXT) mktables.$(OBJEXT)
+file_dds_OBJECTS = $(am_file_dds_OBJECTS)
+file_dds_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_dds_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_dds_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/color.Po ./$(DEPDIR)/dds.Po \
+ ./$(DEPDIR)/ddsread.Po ./$(DEPDIR)/ddswrite.Po \
+ ./$(DEPDIR)/dxt.Po ./$(DEPDIR)/mipmap.Po ./$(DEPDIR)/misc.Po \
+ ./$(DEPDIR)/mktables.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_dds_SOURCES)
+DIST_SOURCES = $(file_dds_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp COPYING README TODO
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-dds
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_dds_RC = file-dds.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CFLAGS = -fno-strict-aliasing
+file_dds_SOURCES = \
+ dds.c \
+ dds.h \
+ color.c \
+ color.h \
+ ddsplugin.h \
+ ddsread.c \
+ ddswrite.c \
+ dxt.c \
+ dxt.h \
+ dxt_tables.h \
+ endian_rw.h \
+ imath.h \
+ mipmap.c \
+ mipmap.h \
+ misc.c \
+ misc.h \
+ mktables.c \
+ vec.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(libm) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_dds_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-dds/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-dds/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-dds$(EXEEXT): $(file_dds_OBJECTS) $(file_dds_DEPENDENCIES) $(EXTRA_file_dds_DEPENDENCIES)
+ @rm -f file-dds$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_dds_OBJECTS) $(file_dds_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dds.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddsread.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ddswrite.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dxt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mipmap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mktables.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/color.Po
+ -rm -f ./$(DEPDIR)/dds.Po
+ -rm -f ./$(DEPDIR)/ddsread.Po
+ -rm -f ./$(DEPDIR)/ddswrite.Po
+ -rm -f ./$(DEPDIR)/dxt.Po
+ -rm -f ./$(DEPDIR)/mipmap.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/mktables.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/color.Po
+ -rm -f ./$(DEPDIR)/dds.Po
+ -rm -f ./$(DEPDIR)/ddsread.Po
+ -rm -f ./$(DEPDIR)/ddswrite.Po
+ -rm -f ./$(DEPDIR)/dxt.Po
+ -rm -f ./$(DEPDIR)/mipmap.Po
+ -rm -f ./$(DEPDIR)/misc.Po
+ -rm -f ./$(DEPDIR)/mktables.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-dds/README b/plug-ins/file-dds/README
new file mode 100644
index 0000000..4090343
--- /dev/null
+++ b/plug-ins/file-dds/README
@@ -0,0 +1,29 @@
+DDS plugin for The GIMP
+(C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+==========================================
+
+This is a plugin for GIMP version 2.4.x. It allows you to load and save
+images in Direct Draw Surface (DDS) format.
+
+Features
+==========================================
+* Load/Save DDS files using DXT texture compression
+* Automatic mipmap generation on save
+* Load mipmaps into separate layers
+* Load cube map faces and volume map slices into separate layers
+* Cube and volume map saving
+* Pixel conversion selection for custom formats (RGBA4, R5G6B5, RGB10A2, etc.)
+* Load/save DDS files, optionally using DirectX texture compression (DXT)
+* Optional automatic mipmap generation when saving
+* Load mipmaps into separate layers
+* Load cube map faces and volume map slices into separate layers
+* Save cube maps and volume maps with automatic mipmap generation support
+* Save image with a custom pixel format
+* Non-power-of-two image loading and saving support with automatic mipmap generation support
+* Compliant with DirectX 10 compressed formats
+
+
+Installation
+==========================================
+See the file INSTALL for installation instructions
diff --git a/plug-ins/file-dds/TODO b/plug-ins/file-dds/TODO
new file mode 100644
index 0000000..94b0411
--- /dev/null
+++ b/plug-ins/file-dds/TODO
@@ -0,0 +1,7 @@
+TODO list for future releases of gimp-dds:
+
+* Add support for DX10 DDS extensions
+* BC6H and BC7 compression support
+* Volume map compression support (VTC)
+* Add support for GIMP 2.6.x GEGL for reading and writing higher precision
+pixel formats
diff --git a/plug-ins/file-dds/color.c b/plug-ins/file-dds/color.c
new file mode 100644
index 0000000..0c1be40
--- /dev/null
+++ b/plug-ins/file-dds/color.c
@@ -0,0 +1,58 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <math.h>
+#include "color.h"
+
+int
+linear_to_sRGB(int c)
+{
+ float v = (float)c / 255.0f;
+
+ if(v < 0)
+ v = 0;
+ else if(v > 1)
+ v = 1;
+ else if(v <= 0.0031308f)
+ v = 12.92f * v;
+ else
+ v = 1.055f * powf(v, 0.41666f) - 0.055f;
+
+ return (int)floorf(255.0f * v + 0.5f);
+}
+
+int
+sRGB_to_linear(int c)
+{
+ float v = (float)c / 255.0f;
+
+ if(v < 0)
+ v = 0;
+ else if(v > 1)
+ v = 1;
+ else if(v <= 0.04045f)
+ v /= 12.92f;
+ else
+ v = powf((v + 0.055f) / 1.055f, 2.4f);
+
+ return (int)floorf(255.0f * v + 0.5f);
+}
diff --git a/plug-ins/file-dds/color.h b/plug-ins/file-dds/color.h
new file mode 100644
index 0000000..70bdb3a
--- /dev/null
+++ b/plug-ins/file-dds/color.h
@@ -0,0 +1,96 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COLOR_H__
+#define __COLOR_H__
+
+#include "imath.h"
+
+/* sRGB encoding/decoding */
+int linear_to_sRGB(int c);
+int sRGB_to_linear(int c);
+
+/* YCoCg encoding */
+static inline void
+RGB_to_YCoCg (unsigned char *dst, int r, int g, int b)
+{
+ int y = ((r + (g << 1) + b) + 2) >> 2;
+ int co = ((((r << 1) - (b << 1)) + 2) >> 2) + 128;
+ int cg = (((-r + (g << 1) - b) + 2) >> 2) + 128;
+
+ dst[0] = 255;
+ dst[1] = (cg > 255 ? 255 : (cg < 0 ? 0 : cg));
+ dst[2] = (co > 255 ? 255 : (co < 0 ? 0 : co));
+ dst[3] = (y > 255 ? 255 : (y < 0 ? 0 : y));
+}
+
+/* other color conversions */
+
+static inline int
+rgb_to_luminance (int r, int g, int b)
+{
+ /* ITU-R BT.709 luma coefficients, scaled by 256 */
+ return ((r * 54 + g * 182 + b * 20) + 128) >> 8;
+}
+
+static inline unsigned short
+pack_r5g6b5 (int r, int g, int b)
+{
+ return (mul8bit(r, 31) << 11) |
+ (mul8bit(g, 63) << 5) |
+ (mul8bit(b, 31) );
+}
+
+static inline unsigned short
+pack_rgba4 (int r, int g, int b, int a)
+{
+ return (mul8bit(a, 15) << 12) |
+ (mul8bit(r, 15) << 8) |
+ (mul8bit(g, 15) << 4) |
+ (mul8bit(b, 15) );
+}
+
+static inline unsigned short
+pack_rgb5a1 (int r, int g, int b, int a)
+{
+ return (((a >> 7) & 0x01) << 15) |
+ (mul8bit(r, 31) << 10) |
+ (mul8bit(g, 31) << 5) |
+ (mul8bit(b, 31) );
+}
+
+static inline unsigned char
+pack_r3g3b2(int r, int g, int b)
+{
+ return (mul8bit(r, 7) << 5) |
+ (mul8bit(g, 7) << 2) |
+ (mul8bit(b, 3) );
+}
+
+static inline unsigned int
+pack_rgb10a2 (int r, int g, int b, int a)
+{
+ return ((unsigned int)((a >> 6) & 0x003) << 30) |
+ ((unsigned int)((b << 2) & 0x3ff) << 20) |
+ ((unsigned int)((g << 2) & 0x3ff) << 10) |
+ ((unsigned int)((r << 2) & 0x3ff) );
+}
+
+#endif /* __COLOR_H__ */
diff --git a/plug-ins/file-dds/dds.c b/plug-ins/file-dds/dds.c
new file mode 100644
index 0000000..421f8bd
--- /dev/null
+++ b/plug-ins/file-dds/dds.c
@@ -0,0 +1,482 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <libgimp/stdplugins-intl.h>
+
+#include "ddsplugin.h"
+#include "dds.h"
+#include "misc.h"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ 0,
+ 0,
+ query,
+ run
+};
+
+DDSWriteVals dds_write_vals =
+{
+ DDS_COMPRESS_NONE,
+ DDS_MIPMAP_NONE,
+ DDS_SAVE_SELECTED_LAYER,
+ DDS_FORMAT_DEFAULT,
+ -1,
+ DDS_MIPMAP_FILTER_DEFAULT,
+ DDS_MIPMAP_WRAP_DEFAULT,
+ 0,
+ 0,
+ 0.0,
+ 0,
+ 0,
+ 0,
+ 0.5
+};
+
+DDSReadVals dds_read_vals =
+{
+ 1,
+ 1
+};
+
+static GimpParamDef load_args[] =
+{
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_STRING, "filename", "The name of the file to load"},
+ { GIMP_PDB_STRING, "raw_filename", "The name entered"},
+ { GIMP_PDB_INT32, "load_mipmaps", "Load mipmaps if present"},
+ { GIMP_PDB_INT32, "decode_images", "Decode YCoCg/AExp images when detected"}
+};
+
+static GimpParamDef load_return_vals[] =
+{
+ { GIMP_PDB_IMAGE, "image", "Output image"}
+};
+
+static GimpParamDef save_args[] =
+{
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image"},
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"},
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image as"},
+ { GIMP_PDB_STRING, "raw_filename", "The name entered"},
+ { GIMP_PDB_INT32, "compression_format", "Compression format (0 = None, 1 = BC1/DXT1, 2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, 5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), 8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), 10 = YCoCg scaled (DXT5))"},
+ { GIMP_PDB_INT32, "mipmaps", "How to handle mipmaps (0 = No mipmaps, 1 = Generate mipmaps, 2 = Use existing mipmaps (layers)"},
+ { GIMP_PDB_INT32, "savetype", "How to save the image (0 = selected layer, 1 = cube map, 2 = volume map, 3 = texture array, 4 = all visible layers"},
+ { GIMP_PDB_INT32, "format", "Custom pixel format (0 = default, 1 = R5G6B5, 2 = RGBA4, 3 = RGB5A1, 4 = RGB10A2)"},
+ { GIMP_PDB_INT32, "transparent_index", "Index of transparent color or -1 to disable (for indexed images only)."},
+ { GIMP_PDB_INT32, "mipmap_filter", "Filtering to use when generating mipmaps (0 = default, 1 = nearest, 2 = box, 3 = triangle, 4 = quadratic, 5 = bspline, 6 = mitchell, 7 = lanczos, 8 = kaiser)"},
+ { GIMP_PDB_INT32, "mipmap_wrap", "Wrap mode to use when generating mipmaps (0 = default, 1 = mirror, 2 = repeat, 3 = clamp)"},
+ { GIMP_PDB_INT32, "gamma_correct", "Use gamma correct mipmap filtering"},
+ { GIMP_PDB_INT32, "srgb", "Use sRGB colorspace for gamma correction"},
+ { GIMP_PDB_FLOAT, "gamma", "Gamma value to use for gamma correction (i.e. 2.2)"},
+ { GIMP_PDB_INT32, "perceptual_metric", "Use a perceptual error metric during compression"},
+ { GIMP_PDB_INT32, "preserve_alpha_coverage", "Preserve alpha test converage for alpha channel maps"},
+ { GIMP_PDB_FLOAT, "alpha_test_threshold", "Alpha test threshold value for which alpha test converage should be preserved"}
+};
+
+static GimpParamDef save_args2[] =
+{
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image"},
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"},
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image as"},
+ { GIMP_PDB_STRING, "raw_filename", "The name entered"},
+ { GIMP_PDB_INT32, "compression_format", "Compression format (0 = None, 1 = BC1/DXT1, 2 = BC2/DXT3, 3 = BC3/DXT5, 4 = BC3n/DXT5nm, 5 = BC4/ATI1N, 6 = BC5/ATI2N, 7 = RXGB (DXT5), 8 = Alpha Exponent (DXT5), 9 = YCoCg (DXT5), 10 = YCoCg scaled (DXT5))"},
+ { GIMP_PDB_INT32, "mipmaps", "How to handle mipmaps (0 = No mipmaps, 1 = Generate mipmaps, 2 = Use existing mipmaps (layers)"},
+ { GIMP_PDB_INT32, "savetype", "How to save the image (0 = selected layer, 1 = cube map, 2 = volume map, 3 = texture array, 4 = all visible layers"},
+ { GIMP_PDB_INT32, "format", "Custom pixel format (0 = default, 1 = R5G6B5, 2 = RGBA4, 3 = RGB5A1, 4 = RGB10A2)"},
+ { GIMP_PDB_INT32, "transparent_index", "Index of transparent color or -1 to disable (for indexed images only)."},
+ { GIMP_PDB_INT32, "mipmap_filter", "Filtering to use when generating mipmaps (0 = default, 1 = nearest, 2 = box, 3 = triangle, 4 = quadratic, 5 = bspline, 6 = mitchell, 7 = lanczos, 8 = kaiser)"},
+ { GIMP_PDB_INT32, "mipmap_wrap", "Wrap mode to use when generating mipmaps (0 = default, 1 = mirror, 2 = repeat, 3 = clamp)"},
+ { GIMP_PDB_INT32, "gamma_correct", "Use gamma correct mipmap filtering"},
+ { GIMP_PDB_INT32, "srgb", "Use sRGB colorspace for gamma correction"},
+ { GIMP_PDB_FLOAT, "gamma", "Gamma value to use for gamma correction (i.e. 2.2)"},
+ { GIMP_PDB_INT32, "perceptual_metric", "Use a perceptual error metric during compression"},
+ { GIMP_PDB_INT32, "preserve_alpha_coverage", "Preserve alpha test converage for alpha channel maps"},
+ { GIMP_PDB_FLOAT, "alpha_test_threshold", "Alpha test threshold value for which alpha test converage should be preserved"},
+ { GIMP_PDB_INT32, "flip_image", "Flip image vertically on export"}
+};
+
+#if 0
+static GimpParamDef decode_args[] =
+{
+ { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ { GIMP_PDB_IMAGE, "image", "Input image"},
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save"}
+};
+#endif
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in DDS image format",
+ "Loads files in DDS image format",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("DDS image"),
+ 0,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/dds");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "dds",
+ "",
+ "0,string,DDS");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves files in DDS image format",
+ "Saves files in DDS image format",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("DDS image"),
+ "INDEXED, GRAY, RGB",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, 0);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/dds");
+ gimp_register_save_handler (SAVE_PROC,
+ "dds",
+ "");
+
+ gimp_install_procedure (SAVE_PROC2,
+ "Saves files in DDS image format "
+ "with additional export options",
+ "Saves files in DDS image format "
+ "with additional export options",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("DDS image"),
+ "INDEXED, GRAY, RGB",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args2), 0,
+ save_args2, 0);
+
+ gimp_register_file_handler_mime (SAVE_PROC2, "image/dds");
+ gimp_register_save_handler (SAVE_PROC2,
+ "dds",
+ "");
+#if 0
+ gimp_install_procedure (DECODE_YCOCG_PROC,
+ "Converts YCoCg encoded pixels to RGB",
+ "Converts YCoCg encoded pixels to RGB",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("Decode YCoCg"),
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (decode_args), 0,
+ decode_args, 0);
+ /*gimp_plugin_menu_register (DECODE_YCOCG_PROC, "<Image>/Filters/Colors");*/
+
+ gimp_install_procedure (DECODE_YCOCG_SCALED_PROC,
+ "Converts YCoCg (scaled) encoded pixels to RGB",
+ "Converts YCoCg (scaled) encoded pixels to RGB",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("Decode YCoCg (scaled)"),
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (decode_args), 0,
+ decode_args, 0);
+ /*gimp_plugin_menu_register (DECODE_YCOCG_SCALED_PROC, "<Image>/Filters/Colors");*/
+
+ gimp_install_procedure (DECODE_ALPHA_EXP_PROC,
+ "Converts alpha exponent encoded pixels to RGB",
+ "Converts alpha exponent encoded pixels to RGB",
+ "Shawn Kirst",
+ "Shawn Kirst",
+ "2008",
+ N_("Decode Alpha exponent"),
+ "RGBA",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (decode_args), 0,
+ decode_args, 0);
+ /*gimp_plugin_menu_register (DECODE_ALPHA_EXP_PROC, "<Image>/Filters/Colors");*/
+#endif
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 imageID;
+ gint32 drawableID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (! strcmp (name, LOAD_PROC))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_ui_init ("dds", 0);
+ gimp_get_data (LOAD_PROC, &dds_read_vals);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ dds_read_vals.mipmaps = param[3].data.d_int32;
+ dds_read_vals.decode_images = param[4].data.d_int32;
+ if (nparams != G_N_ELEMENTS (load_args))
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ status = read_dds (param[1].data.d_string, &imageID,
+ run_mode == GIMP_RUN_INTERACTIVE);
+ if (status == GIMP_PDB_SUCCESS && imageID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = imageID;
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (LOAD_PROC, &dds_read_vals, sizeof (dds_read_vals));
+ }
+ else if (status != GIMP_PDB_CANCEL)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (! strcmp (name, SAVE_PROC) ||
+ ! strcmp (name, SAVE_PROC2))
+ {
+ imageID = param[1].data.d_int32;
+ drawableID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init ("dds", 0);
+ export = gimp_export_image (&imageID, &drawableID, "DDS",
+ (GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS));
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ gimp_get_data (SAVE_PROC, &dds_write_vals);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != G_N_ELEMENTS (save_args))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ dds_write_vals.compression = param[5].data.d_int32;
+ dds_write_vals.mipmaps = param[6].data.d_int32;
+ dds_write_vals.savetype = param[7].data.d_int32;
+ dds_write_vals.format = param[8].data.d_int32;
+ dds_write_vals.transindex = param[9].data.d_int32;
+ dds_write_vals.mipmap_filter = param[10].data.d_int32;
+ dds_write_vals.mipmap_wrap = param[11].data.d_int32;
+ dds_write_vals.gamma_correct = param[12].data.d_int32;
+ dds_write_vals.srgb = param[13].data.d_int32;
+ dds_write_vals.gamma = param[14].data.d_float;
+ dds_write_vals.perceptual_metric = param[15].data.d_int32;
+ dds_write_vals.preserve_alpha_coverage = param[16].data.d_int32;
+ dds_write_vals.alpha_test_threshold = param[17].data.d_float;
+ if (nparams > 18)
+ dds_write_vals.flip_image = param[18].data.d_int32;
+ else
+ dds_write_vals.flip_image = FALSE;
+
+ if ((dds_write_vals.compression < DDS_COMPRESS_NONE) ||
+ (dds_write_vals.compression >= DDS_COMPRESS_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((dds_write_vals.mipmaps < DDS_MIPMAP_NONE) ||
+ (dds_write_vals.mipmaps >= DDS_MIPMAP_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((dds_write_vals.savetype < DDS_SAVE_SELECTED_LAYER) ||
+ (dds_write_vals.savetype >= DDS_SAVE_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((dds_write_vals.format < DDS_FORMAT_DEFAULT) ||
+ (dds_write_vals.format >= DDS_FORMAT_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((dds_write_vals.mipmap_filter < DDS_MIPMAP_FILTER_DEFAULT) ||
+ (dds_write_vals.mipmap_filter >= DDS_MIPMAP_FILTER_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if ((dds_write_vals.mipmap_wrap < DDS_MIPMAP_WRAP_DEFAULT) ||
+ (dds_write_vals.mipmap_wrap >= DDS_MIPMAP_WRAP_MAX))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (SAVE_PROC, &dds_write_vals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (dds_write_vals.gamma < 1e-04f)
+ /* gimp_gamma () got removed and was always returning 2.2 anyway.
+ * XXX Review this piece of code if we expect gamma value could
+ * be parameterized.
+ */
+ dds_write_vals.gamma = 2.2;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ status = write_dds (param[3].data.d_string, imageID, drawableID,
+ run_mode == GIMP_RUN_INTERACTIVE,
+ export == GIMP_EXPORT_EXPORT);
+ if (status == GIMP_PDB_SUCCESS)
+ gimp_set_data (SAVE_PROC, &dds_write_vals, sizeof (dds_write_vals));
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (imageID);
+ }
+#if 0
+ else if (! strcmp (name, DECODE_YCOCG_PROC))
+ {
+ imageID = param[1].data.d_int32;
+ drawableID = param[2].data.d_int32;
+
+ decode_ycocg_image (drawableID, TRUE);
+
+ status = GIMP_PDB_SUCCESS;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else if (! strcmp (name, DECODE_YCOCG_SCALED_PROC))
+ {
+ imageID = param[1].data.d_int32;
+ drawableID = param[2].data.d_int32;
+
+ decode_ycocg_scaled_image (drawableID, TRUE);
+
+ status = GIMP_PDB_SUCCESS;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+ else if (! strcmp (name, DECODE_ALPHA_EXP_PROC))
+ {
+ imageID = param[1].data.d_int32;
+ drawableID = param[2].data.d_int32;
+
+ decode_alpha_exp_image (drawableID, TRUE);
+
+ status = GIMP_PDB_SUCCESS;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ }
+#endif
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/file-dds/dds.h b/plug-ins/file-dds/dds.h
new file mode 100644
index 0000000..971b00a
--- /dev/null
+++ b/plug-ins/file-dds/dds.h
@@ -0,0 +1,326 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DDS_H__
+#define __DDS_H__
+
+#define FOURCC(a, b, c, d) \
+ ((unsigned int)((unsigned int)(a) ) | \
+ ((unsigned int)(b) << 8) | \
+ ((unsigned int)(c) << 16) | \
+ ((unsigned int)(d) << 24))
+
+typedef enum
+{
+ DDS_COMPRESS_NONE = 0,
+ DDS_COMPRESS_BC1, /* DXT1 */
+ DDS_COMPRESS_BC2, /* DXT3 */
+ DDS_COMPRESS_BC3, /* DXT5 */
+ DDS_COMPRESS_BC3N, /* DXT5n */
+ DDS_COMPRESS_BC4, /* ATI1 */
+ DDS_COMPRESS_BC5, /* ATI2 */
+ DDS_COMPRESS_RXGB, /* DXT5 */
+ DDS_COMPRESS_AEXP, /* DXT5 */
+ DDS_COMPRESS_YCOCG, /* DXT5 */
+ DDS_COMPRESS_YCOCGS, /* DXT5 */
+ DDS_COMPRESS_MAX
+} DDS_COMPRESSION_TYPE;
+
+typedef enum
+{
+ DDS_SAVE_SELECTED_LAYER = 0,
+ DDS_SAVE_CUBEMAP,
+ DDS_SAVE_VOLUMEMAP,
+ DDS_SAVE_ARRAY,
+ DDS_SAVE_VISIBLE_LAYERS,
+ DDS_SAVE_MAX
+} DDS_SAVE_TYPE;
+
+typedef enum
+{
+ DDS_FORMAT_DEFAULT = 0,
+ DDS_FORMAT_RGB8,
+ DDS_FORMAT_RGBA8,
+ DDS_FORMAT_BGR8,
+ DDS_FORMAT_ABGR8,
+ DDS_FORMAT_R5G6B5,
+ DDS_FORMAT_RGBA4,
+ DDS_FORMAT_RGB5A1,
+ DDS_FORMAT_RGB10A2,
+ DDS_FORMAT_R3G3B2,
+ DDS_FORMAT_A8,
+ DDS_FORMAT_L8,
+ DDS_FORMAT_L8A8,
+ DDS_FORMAT_AEXP,
+ DDS_FORMAT_YCOCG,
+ DDS_FORMAT_MAX
+} DDS_FORMAT_TYPE;
+
+typedef enum
+{
+ DDS_MIPMAP_NONE = 0,
+ DDS_MIPMAP_GENERATE,
+ DDS_MIPMAP_EXISTING,
+ DDS_MIPMAP_MAX
+} DDS_MIPMAP;
+
+typedef enum
+{
+ DDS_MIPMAP_FILTER_DEFAULT = 0,
+ DDS_MIPMAP_FILTER_NEAREST,
+ DDS_MIPMAP_FILTER_BOX,
+ DDS_MIPMAP_FILTER_TRIANGLE,
+ DDS_MIPMAP_FILTER_QUADRATIC,
+ DDS_MIPMAP_FILTER_BSPLINE,
+ DDS_MIPMAP_FILTER_MITCHELL,
+ DDS_MIPMAP_FILTER_LANCZOS,
+ DDS_MIPMAP_FILTER_KAISER,
+ DDS_MIPMAP_FILTER_MAX
+} DDS_MIPMAP_FILTER;
+
+typedef enum
+{
+ DDS_MIPMAP_WRAP_DEFAULT = 0,
+ DDS_MIPMAP_WRAP_MIRROR,
+ DDS_MIPMAP_WRAP_REPEAT,
+ DDS_MIPMAP_WRAP_CLAMP,
+ DDS_MIPMAP_WRAP_MAX
+} DDS_MIPMAP_WRAP;
+
+#define DDS_HEADERSIZE 128
+#define DDS_HEADERSIZE_DX10 20
+
+#define DDSD_CAPS 0x00000001
+#define DDSD_HEIGHT 0x00000002
+#define DDSD_WIDTH 0x00000004
+#define DDSD_PITCH 0x00000008
+#define DDSD_PIXELFORMAT 0x00001000
+#define DDSD_MIPMAPCOUNT 0x00020000
+#define DDSD_LINEARSIZE 0x00080000
+#define DDSD_DEPTH 0x00800000
+
+#define DDPF_ALPHAPIXELS 0x00000001
+#define DDPF_ALPHA 0x00000002
+#define DDPF_FOURCC 0x00000004
+#define DDPF_PALETTEINDEXED8 0x00000020
+#define DDPF_RGB 0x00000040
+#define DDPF_LUMINANCE 0x00020000
+#define DDPF_NORMAL 0x80000000 // nvidia specific
+
+#define DDSCAPS_COMPLEX 0x00000008
+#define DDSCAPS_TEXTURE 0x00001000
+#define DDSCAPS_MIPMAP 0x00400000
+
+#define DDSCAPS2_CUBEMAP 0x00000200
+#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
+#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
+#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
+#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
+#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
+#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
+#define DDSCAPS2_CUBEMAP_ALL_FACES \
+ (DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | \
+ DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | \
+ DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ)
+
+#define DDSCAPS2_VOLUME 0x00200000
+
+#define D3D10_RESOURCE_MISC_TEXTURECUBE 0x04
+#define D3D10_RESOURCE_DIMENSION_BUFFER 1
+#define D3D10_RESOURCE_DIMENSION_TEXTURE1D 2
+#define D3D10_RESOURCE_DIMENSION_TEXTURE2D 3
+#define D3D10_RESOURCE_DIMENSION_TEXTURE3D 4
+
+typedef struct
+{
+ unsigned int size;
+ unsigned int flags;
+ char fourcc[4];
+ unsigned int bpp;
+ unsigned int rmask;
+ unsigned int gmask;
+ unsigned int bmask;
+ unsigned int amask;
+} dds_pixel_format_t;
+
+typedef struct
+{
+ unsigned int caps1;
+ unsigned int caps2;
+ unsigned int reserved[2];
+} dds_caps_t;
+
+typedef struct
+{
+ unsigned int magic;
+ unsigned int size;
+ unsigned int flags;
+ unsigned int height;
+ unsigned int width;
+ unsigned int pitch_or_linsize;
+ unsigned int depth;
+ unsigned int num_mipmaps;
+ union
+ {
+ struct
+ {
+ unsigned int magic1; // FOURCC "GIMP"
+ unsigned int magic2; // FOURCC "-DDS"
+ unsigned int version;
+ unsigned int extra_fourcc;
+ } gimp_dds_special;
+ unsigned char pad[4 * 11];
+ } reserved;
+ dds_pixel_format_t pixelfmt;
+ dds_caps_t caps;
+ unsigned int reserved2;
+} dds_header_t;
+
+typedef enum
+{
+ DXGI_FORMAT_UNKNOWN = 0,
+ DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
+ DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
+ DXGI_FORMAT_R32G32B32A32_UINT = 3,
+ DXGI_FORMAT_R32G32B32A32_SINT = 4,
+ DXGI_FORMAT_R32G32B32_TYPELESS = 5,
+ DXGI_FORMAT_R32G32B32_FLOAT = 6,
+ DXGI_FORMAT_R32G32B32_UINT = 7,
+ DXGI_FORMAT_R32G32B32_SINT = 8,
+ DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
+ DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
+ DXGI_FORMAT_R16G16B16A16_UNORM = 11,
+ DXGI_FORMAT_R16G16B16A16_UINT = 12,
+ DXGI_FORMAT_R16G16B16A16_SNORM = 13,
+ DXGI_FORMAT_R16G16B16A16_SINT = 14,
+ DXGI_FORMAT_R32G32_TYPELESS = 15,
+ DXGI_FORMAT_R32G32_FLOAT = 16,
+ DXGI_FORMAT_R32G32_UINT = 17,
+ DXGI_FORMAT_R32G32_SINT = 18,
+ DXGI_FORMAT_R32G8X24_TYPELESS = 19,
+ DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
+ DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
+ DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
+ DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
+ DXGI_FORMAT_R10G10B10A2_UNORM = 24,
+ DXGI_FORMAT_R10G10B10A2_UINT = 25,
+ DXGI_FORMAT_R11G11B10_FLOAT = 26,
+ DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
+ DXGI_FORMAT_R8G8B8A8_UNORM = 28,
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
+ DXGI_FORMAT_R8G8B8A8_UINT = 30,
+ DXGI_FORMAT_R8G8B8A8_SNORM = 31,
+ DXGI_FORMAT_R8G8B8A8_SINT = 32,
+ DXGI_FORMAT_R16G16_TYPELESS = 33,
+ DXGI_FORMAT_R16G16_FLOAT = 34,
+ DXGI_FORMAT_R16G16_UNORM = 35,
+ DXGI_FORMAT_R16G16_UINT = 36,
+ DXGI_FORMAT_R16G16_SNORM = 37,
+ DXGI_FORMAT_R16G16_SINT = 38,
+ DXGI_FORMAT_R32_TYPELESS = 39,
+ DXGI_FORMAT_D32_FLOAT = 40,
+ DXGI_FORMAT_R32_FLOAT = 41,
+ DXGI_FORMAT_R32_UINT = 42,
+ DXGI_FORMAT_R32_SINT = 43,
+ DXGI_FORMAT_R24G8_TYPELESS = 44,
+ DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
+ DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
+ DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
+ DXGI_FORMAT_R8G8_TYPELESS = 48,
+ DXGI_FORMAT_R8G8_UNORM = 49,
+ DXGI_FORMAT_R8G8_UINT = 50,
+ DXGI_FORMAT_R8G8_SNORM = 51,
+ DXGI_FORMAT_R8G8_SINT = 52,
+ DXGI_FORMAT_R16_TYPELESS = 53,
+ DXGI_FORMAT_R16_FLOAT = 54,
+ DXGI_FORMAT_D16_UNORM = 55,
+ DXGI_FORMAT_R16_UNORM = 56,
+ DXGI_FORMAT_R16_UINT = 57,
+ DXGI_FORMAT_R16_SNORM = 58,
+ DXGI_FORMAT_R16_SINT = 59,
+ DXGI_FORMAT_R8_TYPELESS = 60,
+ DXGI_FORMAT_R8_UNORM = 61,
+ DXGI_FORMAT_R8_UINT = 62,
+ DXGI_FORMAT_R8_SNORM = 63,
+ DXGI_FORMAT_R8_SINT = 64,
+ DXGI_FORMAT_A8_UNORM = 65,
+ DXGI_FORMAT_R1_UNORM = 66,
+ DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
+ DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
+ DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
+ DXGI_FORMAT_BC1_TYPELESS = 70,
+ DXGI_FORMAT_BC1_UNORM = 71,
+ DXGI_FORMAT_BC1_UNORM_SRGB = 72,
+ DXGI_FORMAT_BC2_TYPELESS = 73,
+ DXGI_FORMAT_BC2_UNORM = 74,
+ DXGI_FORMAT_BC2_UNORM_SRGB = 75,
+ DXGI_FORMAT_BC3_TYPELESS = 76,
+ DXGI_FORMAT_BC3_UNORM = 77,
+ DXGI_FORMAT_BC3_UNORM_SRGB = 78,
+ DXGI_FORMAT_BC4_TYPELESS = 79,
+ DXGI_FORMAT_BC4_UNORM = 80,
+ DXGI_FORMAT_BC4_SNORM = 81,
+ DXGI_FORMAT_BC5_TYPELESS = 82,
+ DXGI_FORMAT_BC5_UNORM = 83,
+ DXGI_FORMAT_BC5_SNORM = 84,
+ DXGI_FORMAT_B5G6R5_UNORM = 85,
+ DXGI_FORMAT_B5G5R5A1_UNORM = 86,
+ DXGI_FORMAT_B8G8R8A8_UNORM = 87,
+ DXGI_FORMAT_B8G8R8X8_UNORM = 88,
+ DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
+ DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
+ DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
+ DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
+ DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
+ DXGI_FORMAT_BC6H_TYPELESS = 94,
+ DXGI_FORMAT_BC6H_UF16 = 95,
+ DXGI_FORMAT_BC6H_SF16 = 96,
+ DXGI_FORMAT_BC7_TYPELESS = 97,
+ DXGI_FORMAT_BC7_UNORM = 98,
+ DXGI_FORMAT_BC7_UNORM_SRGB = 99,
+ DXGI_FORMAT_AYUV = 100,
+ DXGI_FORMAT_Y410 = 101,
+ DXGI_FORMAT_Y416 = 102,
+ DXGI_FORMAT_NV12 = 103,
+ DXGI_FORMAT_P010 = 104,
+ DXGI_FORMAT_P016 = 105,
+ DXGI_FORMAT_420_OPAQUE = 106,
+ DXGI_FORMAT_YUY2 = 107,
+ DXGI_FORMAT_Y210 = 108,
+ DXGI_FORMAT_Y216 = 109,
+ DXGI_FORMAT_NV11 = 110,
+ DXGI_FORMAT_AI44 = 111,
+ DXGI_FORMAT_IA44 = 112,
+ DXGI_FORMAT_P8 = 113,
+ DXGI_FORMAT_A8P8 = 114,
+ DXGI_FORMAT_B4G4R4A4_UNORM = 115,
+ DXGI_FORMAT_FORCE_UINT = 0xffffffffUL
+} DXGI_FORMAT;
+
+typedef struct
+{
+ DXGI_FORMAT dxgiFormat;
+ unsigned int resourceDimension;
+ unsigned int miscFlag;
+ unsigned int arraySize;
+ unsigned int reserved;
+} dds_header_dx10_t;
+
+#endif /* __DDS_H__ */
diff --git a/plug-ins/file-dds/ddsplugin.h b/plug-ins/file-dds/ddsplugin.h
new file mode 100644
index 0000000..96257c9
--- /dev/null
+++ b/plug-ins/file-dds/ddsplugin.h
@@ -0,0 +1,79 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DDSPLUGIN_H__
+#define __DDSPLUGIN_H__
+
+#define DDS_PLUGIN_VERSION_MAJOR 3
+#define DDS_PLUGIN_VERSION_MINOR 9
+#define DDS_PLUGIN_VERSION_REVISION 92
+
+#define DDS_PLUGIN_VERSION \
+ ((unsigned int)(DDS_PLUGIN_VERSION_MAJOR << 16) | \
+ (unsigned int)(DDS_PLUGIN_VERSION_MINOR << 8) | \
+ (unsigned int)(DDS_PLUGIN_VERSION_REVISION))
+
+typedef struct
+{
+ int compression;
+ int mipmaps;
+ int savetype;
+ int format;
+ int transindex;
+ int mipmap_filter;
+ int mipmap_wrap;
+ int gamma_correct;
+ int srgb;
+ float gamma;
+ int perceptual_metric;
+ int show_adv_opt;
+ int preserve_alpha_coverage;
+ float alpha_test_threshold;
+ gboolean flip_image;
+} DDSWriteVals;
+
+typedef struct
+{
+ int mipmaps;
+ int decode_images;
+} DDSReadVals;
+
+extern DDSWriteVals dds_write_vals;
+extern DDSReadVals dds_read_vals;
+
+extern GimpPDBStatusType read_dds (gchar *filename,
+ gint32 *imageID,
+ gboolean interactive_dds);
+extern GimpPDBStatusType write_dds (gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ gboolean interactive_dds,
+ gboolean is_duplicate_image);
+
+
+#define LOAD_PROC "file-dds-load"
+#define SAVE_PROC "file-dds-save"
+#define SAVE_PROC2 "file-dds-save2"
+
+#define DECODE_YCOCG_PROC "color-decode-ycocg"
+#define DECODE_YCOCG_SCALED_PROC "color-decode-ycocg-scaled"
+#define DECODE_ALPHA_EXP_PROC "color-decode-alpha-exp"
+
+#endif /* __DDSPLUGIN_H__ */
diff --git a/plug-ins/file-dds/ddsread.c b/plug-ins/file-dds/ddsread.c
new file mode 100644
index 0000000..a8eb8b8
--- /dev/null
+++ b/plug-ins/file-dds/ddsread.c
@@ -0,0 +1,1401 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ ** !!! COPYRIGHT NOTICE !!!
+ **
+ ** The following is based on code (C) 2003 Arne Reuter <homepage@arnereuter.de>
+ ** URL: http://www.dr-reuter.de/arne/dds.html
+ **
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <libgimp/stdplugins-intl.h>
+
+#include "ddsplugin.h"
+#include "dds.h"
+#include "dxt.h"
+#include "endian_rw.h"
+#include "misc.h"
+#include "imath.h"
+
+typedef struct
+{
+ unsigned char rshift, gshift, bshift, ashift;
+ unsigned char rbits, gbits, bbits, abits;
+ unsigned int rmask, gmask, bmask, amask;
+ unsigned int bpp, gimp_bpp;
+ unsigned int gimp_bps; /* bytes per sample */
+ int tile_height;
+ unsigned char *palette;
+} dds_load_info_t;
+
+static int read_header (dds_header_t *hdr,
+ FILE *fp);
+static int read_header_dx10 (dds_header_dx10_t *hdr,
+ FILE *fp);
+static int validate_header (dds_header_t *hdr);
+static int setup_dxgi_format (dds_header_t *hdr,
+ dds_header_dx10_t *dx10hdr);
+static int load_layer (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ unsigned int level,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static int load_mipmaps (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static int load_face (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf);
+static unsigned char color_bits (unsigned int mask);
+static unsigned char color_shift (unsigned int mask);
+static int load_dialog (void);
+
+static gboolean runme = FALSE;
+
+GimpPDBStatusType
+read_dds (gchar *filename,
+ gint32 *imageID,
+ gboolean interactive_dds)
+{
+ gint32 image = 0;
+ unsigned char *buf;
+ unsigned int l = 0;
+ guchar *pixels;
+ gchar *tmp;
+ FILE *fp;
+ dds_header_t hdr;
+ dds_header_dx10_t dx10hdr;
+ dds_load_info_t d;
+ gint *layers, layer_count;
+ GimpImageBaseType type;
+ GimpPrecision precision;
+ int i, j;
+
+ if (interactive_dds)
+ {
+ if (!load_dialog ())
+ return GIMP_PDB_CANCEL;
+ }
+
+ fp = g_fopen (filename, "rb");
+ if (fp == 0)
+ {
+ g_message ("Error opening file.\n");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (strrchr (filename, '/'))
+ tmp = g_strdup_printf ("Loading %s:", strrchr (filename, '/') + 1);
+ else
+ tmp = g_strdup_printf ("Loading %s:", filename);
+ gimp_progress_init (tmp);
+ g_free (tmp);
+
+ /* read header */
+ read_header (&hdr, fp);
+
+ memset (&dx10hdr, 0, sizeof (dds_header_dx10_t));
+
+ /* read DX10 header if necessary */
+ if (GETL32(hdr.pixelfmt.fourcc) == FOURCC ('D','X','1','0'))
+ {
+ read_header_dx10(&dx10hdr, fp);
+
+ if (!setup_dxgi_format (&hdr, &dx10hdr))
+ {
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (!validate_header (&hdr))
+ {
+ fclose (fp);
+ g_message ("Invalid DDS header!\n");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* a lot of DDS images out there don't have this for some reason -_- */
+ if (hdr.pitch_or_linsize == 0)
+ {
+ if (hdr.pixelfmt.flags & DDPF_FOURCC) /* assume linear size */
+ {
+ hdr.pitch_or_linsize = ((hdr.width + 3) >> 2) * ((hdr.height + 3) >> 2);
+ switch (GETL32(hdr.pixelfmt.fourcc))
+ {
+ case FOURCC ('D','X','T','1'):
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ hdr.pitch_or_linsize *= 8;
+ break;
+ default:
+ hdr.pitch_or_linsize *= 16;
+ break;
+ }
+ }
+ else /* assume pitch */
+ {
+ hdr.pitch_or_linsize = hdr.height * hdr.width * (hdr.pixelfmt.bpp >> 3);
+ }
+ }
+
+ if (hdr.pixelfmt.flags & DDPF_FOURCC)
+ {
+ /* fourcc is dXt* or rXgb */
+ if (hdr.pixelfmt.fourcc[1] == 'X')
+ hdr.pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ }
+
+ d.gimp_bps = 1; /* Most formats will be converted to 1 byte per sample */
+ if (hdr.pixelfmt.flags & DDPF_FOURCC)
+ {
+ switch (GETL32(hdr.pixelfmt.fourcc))
+ {
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ d.bpp = d.gimp_bpp = 1;
+ type = GIMP_GRAY;
+ break;
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'):
+ d.bpp = d.gimp_bpp = 3;
+ type = GIMP_RGB;
+ break;
+ default:
+ d.bpp = d.gimp_bpp = 4;
+ type = GIMP_RGB;
+ break;
+ }
+ }
+ else
+ {
+ d.bpp = hdr.pixelfmt.bpp >> 3;
+
+ if (d.bpp == 2)
+ {
+ if (hdr.pixelfmt.amask == 0xf000) // RGBA4
+ {
+ d.gimp_bpp = 4;
+ type = GIMP_RGB;
+ }
+ else if (hdr.pixelfmt.amask == 0xff00) //L8A8
+ {
+ d.gimp_bpp = 2;
+ type = GIMP_GRAY;
+ }
+ else if (hdr.pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1
+ {
+ if (hdr.pixelfmt.amask == 0x8000) // RGB5A1
+ d.gimp_bpp = 4;
+ else
+ d.gimp_bpp = 3;
+
+ type = GIMP_RGB;
+ }
+ else if (hdr.pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ d.gimp_bpp = 2;
+ d.gimp_bps = 2;
+ type = GIMP_GRAY;
+ }
+ else
+ {
+ g_message ("Unsupported uncompressed dds format: "
+ "bpp: %d, Rmask: %x, Gmask: %x, Bmask: %x, Amask: %x",
+ hdr.pixelfmt.bpp,
+ hdr.pixelfmt.rmask, hdr.pixelfmt.gmask,
+ hdr.pixelfmt.bmask, hdr.pixelfmt.amask);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ type = GIMP_INDEXED;
+ d.gimp_bpp = 1;
+ }
+ else if (hdr.pixelfmt.rmask == 0xe0) // R3G3B2
+ {
+ type = GIMP_RGB;
+ d.gimp_bpp = 3;
+ }
+ else
+ {
+ /* test alpha only image */
+ if (d.bpp == 1 && (hdr.pixelfmt.flags & DDPF_ALPHA))
+ {
+ d.gimp_bpp = 2;
+ type = GIMP_GRAY;
+ }
+ else
+ {
+ d.gimp_bpp = d.bpp;
+ type = (d.bpp == 1) ? GIMP_GRAY : GIMP_RGB;
+ }
+ }
+ }
+ }
+
+ if (d.gimp_bps == 2)
+ {
+ precision = GIMP_PRECISION_U16_GAMMA;
+ }
+ else
+ {
+ precision = GIMP_PRECISION_U8_GAMMA;
+ }
+
+ image = gimp_image_new_with_precision (hdr.width, hdr.height, type, precision);
+
+ if (image == -1)
+ {
+ g_message ("Can't allocate new image.\n");
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_set_filename (image, filename);
+
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ d.palette = g_malloc (256 * 4);
+ if (fread (d.palette, 1, 1024, fp) != 1024)
+ {
+ g_message ("Error reading palette.\n");
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ for (i = j = 0; i < 768; i += 3, j += 4)
+ {
+ d.palette[i + 0] = d.palette[j + 0];
+ d.palette[i + 1] = d.palette[j + 1];
+ d.palette[i + 2] = d.palette[j + 2];
+ }
+ gimp_image_set_colormap (image, d.palette, 256);
+ }
+
+ d.tile_height = gimp_tile_height ();
+
+ pixels = g_new (guchar, d.tile_height * hdr.width * d.gimp_bpp);
+ buf = g_malloc (hdr.pitch_or_linsize);
+
+ d.rshift = color_shift (hdr.pixelfmt.rmask);
+ d.gshift = color_shift (hdr.pixelfmt.gmask);
+ d.bshift = color_shift (hdr.pixelfmt.bmask);
+ d.ashift = color_shift (hdr.pixelfmt.amask);
+ d.rbits = color_bits (hdr.pixelfmt.rmask);
+ d.gbits = color_bits (hdr.pixelfmt.gmask);
+ d.bbits = color_bits (hdr.pixelfmt.bmask);
+ d.abits = color_bits (hdr.pixelfmt.amask);
+ d.rmask = (hdr.pixelfmt.rmask >> d.rshift) << (8 - d.rbits);
+ d.gmask = (hdr.pixelfmt.gmask >> d.gshift) << (8 - d.gbits);
+ d.bmask = (hdr.pixelfmt.bmask >> d.bshift) << (8 - d.bbits);
+ d.amask = (hdr.pixelfmt.amask >> d.ashift) << (8 - d.abits);
+
+ if (!(hdr.caps.caps2 & DDSCAPS2_CUBEMAP) &&
+ !(hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
+ dx10hdr.arraySize == 0)
+ {
+ if (!load_layer (fp, &hdr, &d, image, 0, "", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if (!load_mipmaps (fp, &hdr, &d, image, "", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (hdr.caps.caps2 & DDSCAPS2_CUBEMAP)
+ {
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) &&
+ !load_face (fp, &hdr, &d, image, "(positive x)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) &&
+ !load_face (fp, &hdr, &d, image, "(negative x)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) &&
+ !load_face (fp, &hdr, &d, image, "(positive y)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) &&
+ !load_face (fp, &hdr, &d, image, "(negative y)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) &&
+ !load_face (fp, &hdr, &d, image, "(positive z)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if ((hdr.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) &&
+ !load_face (fp, &hdr, &d, image, "(negative z)", &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if ((hdr.caps.caps2 & DDSCAPS2_VOLUME) &&
+ (hdr.flags & DDSD_DEPTH))
+ {
+ unsigned int i, level;
+ char *plane;
+ for (i = 0; i < hdr.depth; ++i)
+ {
+ plane = g_strdup_printf ("(z = %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, 0, plane, &l, pixels, buf))
+ {
+ g_free (plane);
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (plane);
+ }
+
+ if ((hdr.flags & DDSD_MIPMAPCOUNT) &&
+ (hdr.caps.caps1 & DDSCAPS_MIPMAP) &&
+ (dds_read_vals.mipmaps != 0))
+ {
+ for (level = 1; level < hdr.num_mipmaps; ++level)
+ {
+ int n = hdr.depth >> level;
+ if (n < 1) n = 1;
+ for (i = 0; i < n; ++i)
+ {
+ plane = g_strdup_printf ("(z = %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, level, plane, &l, pixels, buf))
+ {
+ g_free (plane);
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (plane);
+ }
+ }
+ }
+ }
+ else if (dx10hdr.arraySize > 0)
+ {
+ unsigned int i;
+ char *elem;
+
+ for (i = 0; i < dx10hdr.arraySize; ++i)
+ {
+ elem = g_strdup_printf ("(array element %d)", i);
+ if (!load_layer (fp, &hdr, &d, image, 0, elem, &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ if (!load_mipmaps (fp, &hdr, &d, image, elem, &l, pixels, buf))
+ {
+ fclose (fp);
+ gimp_image_delete (image);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ g_free (elem);
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ if (hdr.pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ g_free (d.palette);
+
+ g_free (buf);
+ g_free (pixels);
+ fclose (fp);
+
+ layers = gimp_image_get_layers (image, &layer_count);
+
+ if (layers == NULL || layer_count == 0)
+ {
+ g_message ("Oops! NULL image read! Please report this!");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_set_active_layer (image, layers[0]);
+ g_free (layers);
+
+ *imageID = image;
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static int
+read_header (dds_header_t *hdr,
+ FILE *fp)
+{
+ unsigned char buf[DDS_HEADERSIZE];
+
+ memset (hdr, 0, sizeof (dds_header_t));
+
+ if (fread (buf, 1, DDS_HEADERSIZE, fp) != DDS_HEADERSIZE)
+ return 0;
+
+ hdr->magic = GETL32(buf);
+
+ hdr->size = GETL32(buf + 4);
+ hdr->flags = GETL32(buf + 8);
+ hdr->height = GETL32(buf + 12);
+ hdr->width = GETL32(buf + 16);
+ hdr->pitch_or_linsize = GETL32(buf + 20);
+ hdr->depth = GETL32(buf + 24);
+ hdr->num_mipmaps = GETL32(buf + 28);
+
+ hdr->pixelfmt.size = GETL32(buf + 76);
+ hdr->pixelfmt.flags = GETL32(buf + 80);
+ hdr->pixelfmt.fourcc[0] = buf[84];
+ hdr->pixelfmt.fourcc[1] = buf[85];
+ hdr->pixelfmt.fourcc[2] = buf[86];
+ hdr->pixelfmt.fourcc[3] = buf[87];
+ hdr->pixelfmt.bpp = GETL32(buf + 88);
+ hdr->pixelfmt.rmask = GETL32(buf + 92);
+ hdr->pixelfmt.gmask = GETL32(buf + 96);
+ hdr->pixelfmt.bmask = GETL32(buf + 100);
+ hdr->pixelfmt.amask = GETL32(buf + 104);
+
+ hdr->caps.caps1 = GETL32(buf + 108);
+ hdr->caps.caps2 = GETL32(buf + 112);
+
+ /* GIMP-DDS special info */
+ if (GETL32(buf + 32) == FOURCC ('G','I','M','P') &&
+ GETL32(buf + 36) == FOURCC ('-','D','D','S'))
+ {
+ hdr->reserved.gimp_dds_special.magic1 = GETL32(buf + 32);
+ hdr->reserved.gimp_dds_special.magic2 = GETL32(buf + 36);
+ hdr->reserved.gimp_dds_special.version = GETL32(buf + 40);
+ hdr->reserved.gimp_dds_special.extra_fourcc = GETL32(buf + 44);
+ }
+
+ return 1;
+}
+
+static int
+read_header_dx10 (dds_header_dx10_t *hdr,
+ FILE *fp)
+{
+ char buf[DDS_HEADERSIZE_DX10];
+
+ memset (hdr, 0, sizeof (dds_header_dx10_t));
+
+ if (fread (buf, 1, DDS_HEADERSIZE_DX10, fp) != DDS_HEADERSIZE_DX10)
+ return 0;
+
+ hdr->dxgiFormat = GETL32(buf);
+ hdr->resourceDimension = GETL32(buf + 4);
+ hdr->miscFlag = GETL32(buf + 8);
+ hdr->arraySize = GETL32(buf + 12);
+ hdr->reserved = GETL32(buf + 16);
+
+ return 1;
+}
+
+static int
+validate_header (dds_header_t *hdr)
+{
+ unsigned int fourcc;
+
+ if (hdr->magic != FOURCC ('D','D','S',' '))
+ {
+ g_message ("Invalid DDS file.\n");
+ return 0;
+ }
+
+ if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ if (hdr->flags & DDSD_PITCH)
+ {
+ g_message ("Warning: DDSD_PITCH is incorrectly set for DDPF_FOURCC!");
+ hdr->flags &= DDSD_PITCH;
+ }
+ if (! (hdr->flags & DDSD_LINEARSIZE))
+ {
+ g_message ("Warning: DDSD_LINEARSIZE is incorrectly not set for DDPF_FOURCC!");
+ hdr->flags |= DDSD_LINEARSIZE;
+ }
+ }
+ else
+ {
+ if (! (hdr->flags & DDSD_PITCH))
+ {
+ g_printerr ("Warning: DDSD_PITCH is incorrectly not set for an uncompressed texture! (recovered)\n");
+ hdr->flags |= DDSD_PITCH;
+ }
+ if ((hdr->flags & DDSD_LINEARSIZE))
+ {
+ g_printerr ("Warning: DDSD_LINEARSIZE is incorrectly set for an uncompressed texture! (recovered)\n");
+ hdr->flags &= DDSD_LINEARSIZE;
+ }
+ }
+
+ /*
+ if ((hdr->pixelfmt.flags & DDPF_FOURCC) ==
+ (hdr->pixelfmt.flags & DDPF_RGB))
+ {
+ g_message ("Invalid pixel format.\n");
+ return 0;
+ }
+ */
+ fourcc = GETL32(hdr->pixelfmt.fourcc);
+
+ if ((hdr->pixelfmt.flags & DDPF_FOURCC) &&
+ fourcc != FOURCC ('D','X','T','1') &&
+ fourcc != FOURCC ('D','X','T','2') &&
+ fourcc != FOURCC ('D','X','T','3') &&
+ fourcc != FOURCC ('D','X','T','4') &&
+ fourcc != FOURCC ('D','X','T','5') &&
+ fourcc != FOURCC ('R','X','G','B') &&
+ fourcc != FOURCC ('A','T','I','1') &&
+ fourcc != FOURCC ('B','C','4','U') &&
+ fourcc != FOURCC ('B','C','4','S') &&
+ fourcc != FOURCC ('A','T','I','2') &&
+ fourcc != FOURCC ('B','C','5','U') &&
+ fourcc != FOURCC ('B','C','5','S') &&
+ fourcc != FOURCC ('D','X','1','0'))
+ {
+ g_message ("Unsupported format (FOURCC: %c%c%c%c, hex: %08x).\n",
+ hdr->pixelfmt.fourcc[0],
+ hdr->pixelfmt.fourcc[1],
+ hdr->pixelfmt.fourcc[2],
+ hdr->pixelfmt.fourcc[3],
+ GETL32(hdr->pixelfmt.fourcc));
+ return 0;
+ }
+
+ if (hdr->pixelfmt.flags & DDPF_RGB)
+ {
+ if ((hdr->pixelfmt.bpp != 8) &&
+ (hdr->pixelfmt.bpp != 16) &&
+ (hdr->pixelfmt.bpp != 24) &&
+ (hdr->pixelfmt.bpp != 32))
+ {
+ g_message ("Invalid BPP.\n");
+ return 0;
+ }
+ }
+ else if (hdr->pixelfmt.flags & DDPF_LUMINANCE)
+ {
+ if ((hdr->pixelfmt.bpp != 8) &&
+ (hdr->pixelfmt.bpp != 16))
+ {
+ g_message ("Invalid BPP.\n");
+ return 0;
+ }
+
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ }
+ else if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ }
+
+ if (!(hdr->pixelfmt.flags & DDPF_RGB) &&
+ !(hdr->pixelfmt.flags & DDPF_ALPHA) &&
+ !(hdr->pixelfmt.flags & DDPF_FOURCC) &&
+ !(hdr->pixelfmt.flags & DDPF_LUMINANCE))
+ {
+ g_message ("Unknown pixel format! Taking a guess, expect trouble!");
+ switch (fourcc)
+ {
+ case FOURCC ('D','X','T','1'):
+ case FOURCC ('D','X','T','2'):
+ case FOURCC ('D','X','T','3'):
+ case FOURCC ('D','X','T','4'):
+ case FOURCC ('D','X','T','5'):
+ case FOURCC ('R','X','G','B'):
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'):
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'):
+ hdr->pixelfmt.flags |= DDPF_FOURCC;
+ break;
+ default:
+ switch (hdr->pixelfmt.bpp)
+ {
+ case 8:
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ hdr->pixelfmt.flags |= DDPF_ALPHA;
+ else
+ hdr->pixelfmt.flags |= DDPF_LUMINANCE;
+ break;
+ case 16:
+ case 24:
+ case 32:
+ hdr->pixelfmt.flags |= DDPF_RGB;
+ break;
+ default:
+ g_message ("Invalid pixel format.");
+ return 0;
+ }
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * This function will set the necessary flags and attributes in the standard
+ * dds header using the information found in the DX10 header.
+ */
+static int
+setup_dxgi_format (dds_header_t *hdr,
+ dds_header_dx10_t *dx10hdr)
+{
+ if ((dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE2D) &&
+ (dx10hdr->miscFlag & D3D10_RESOURCE_MISC_TEXTURECUBE))
+ {
+ hdr->caps.caps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES;
+ }
+ else if (dx10hdr->resourceDimension == D3D10_RESOURCE_DIMENSION_TEXTURE3D)
+ {
+ hdr->flags |= DDSD_DEPTH;
+ hdr->caps.caps2 |= DDSCAPS2_VOLUME;
+ }
+
+ if ((dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE1D) &&
+ (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D) &&
+ (dx10hdr->resourceDimension != D3D10_RESOURCE_DIMENSION_TEXTURE3D))
+ return 0;
+
+ // check for a compressed DXGI format
+ if ((dx10hdr->dxgiFormat >= DXGI_FORMAT_BC1_TYPELESS) &&
+ (dx10hdr->dxgiFormat <= DXGI_FORMAT_BC5_SNORM))
+ {
+ // set flag and replace FOURCC
+ hdr->pixelfmt.flags |= DDPF_FOURCC;
+
+ switch (dx10hdr->dxgiFormat)
+ {
+ case DXGI_FORMAT_BC1_TYPELESS:
+ case DXGI_FORMAT_BC1_UNORM:
+ case DXGI_FORMAT_BC1_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','1'));
+ break;
+ case DXGI_FORMAT_BC2_TYPELESS:
+ case DXGI_FORMAT_BC2_UNORM:
+ case DXGI_FORMAT_BC2_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','3'));
+ break;
+ case DXGI_FORMAT_BC3_TYPELESS:
+ case DXGI_FORMAT_BC3_UNORM:
+ case DXGI_FORMAT_BC3_UNORM_SRGB:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('D','X','T','5'));
+ break;
+ case DXGI_FORMAT_BC4_TYPELESS:
+ case DXGI_FORMAT_BC4_UNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','1'));
+ break;
+ case DXGI_FORMAT_BC4_SNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','4','S'));
+ break;
+ case DXGI_FORMAT_BC5_TYPELESS:
+ case DXGI_FORMAT_BC5_UNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('A','T','I','2'));
+ break;
+ case DXGI_FORMAT_BC5_SNORM:
+ PUTL32(hdr->pixelfmt.fourcc, FOURCC ('B','C','5','S'));
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* unset the FOURCC flag */
+ hdr->pixelfmt.flags &= ~DDPF_FOURCC;
+
+ switch (dx10hdr->dxgiFormat)
+ {
+ case DXGI_FORMAT_B8G8R8A8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8A8_UNORM:
+ case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00ff0000;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x000000ff;
+ hdr->pixelfmt.amask = 0xff000000;
+ break;
+ case DXGI_FORMAT_B8G8R8X8_TYPELESS:
+ case DXGI_FORMAT_B8G8R8X8_UNORM:
+ case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00ff0000;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x000000ff;
+ hdr->pixelfmt.amask = 0x00000000;
+ break;
+ case DXGI_FORMAT_R8G8B8A8_TYPELESS:
+ case DXGI_FORMAT_R8G8B8A8_UNORM:
+ case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
+ case DXGI_FORMAT_R8G8B8A8_UINT:
+ case DXGI_FORMAT_R8G8B8A8_SNORM:
+ case DXGI_FORMAT_R8G8B8A8_SINT:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x000000ff;
+ hdr->pixelfmt.gmask = 0x0000ff00;
+ hdr->pixelfmt.bmask = 0x00ff0000;
+ hdr->pixelfmt.amask = 0xff000000;
+ break;
+ case DXGI_FORMAT_B5G6R5_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.rmask = 0x0000f800;
+ hdr->pixelfmt.gmask = 0x000007e0;
+ hdr->pixelfmt.bmask = 0x0000001f;
+ hdr->pixelfmt.amask = 0x00000000;
+ break;
+ case DXGI_FORMAT_B5G5R5A1_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.rmask = 0x00007c00;
+ hdr->pixelfmt.gmask = 0x000003e0;
+ hdr->pixelfmt.bmask = 0x0000001f;
+ hdr->pixelfmt.amask = 0x00008000;
+ break;
+ case DXGI_FORMAT_R10G10B10A2_TYPELESS:
+ case DXGI_FORMAT_R10G10B10A2_UNORM:
+ case DXGI_FORMAT_R10G10B10A2_UINT:
+ hdr->pixelfmt.bpp = 32;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x000003ff;
+ hdr->pixelfmt.gmask = 0x000ffc00;
+ hdr->pixelfmt.bmask = 0x3ff00000;
+ hdr->pixelfmt.amask = 0xc0000000;
+ break;
+ case DXGI_FORMAT_A8_UNORM:
+ hdr->pixelfmt.bpp = 8;
+ hdr->pixelfmt.flags |= DDPF_ALPHA | DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = 0;
+ hdr->pixelfmt.amask = 0x000000ff;
+ break;
+ case DXGI_FORMAT_R8_TYPELESS:
+ case DXGI_FORMAT_R8_UNORM:
+ case DXGI_FORMAT_R8_UINT:
+ case DXGI_FORMAT_R8_SNORM:
+ case DXGI_FORMAT_R8_SINT:
+ hdr->pixelfmt.bpp = 8;
+ hdr->pixelfmt.rmask = 0x000000ff;
+ hdr->pixelfmt.gmask = hdr->pixelfmt.bmask = hdr->pixelfmt.amask = 0;
+ break;
+ case DXGI_FORMAT_B4G4R4A4_UNORM:
+ hdr->pixelfmt.bpp = 16;
+ hdr->pixelfmt.flags |= DDPF_ALPHAPIXELS;
+ hdr->pixelfmt.rmask = 0x00000f00;
+ hdr->pixelfmt.gmask = 0x000000f0;
+ hdr->pixelfmt.bmask = 0x0000000f;
+ hdr->pixelfmt.amask = 0x0000f000;
+ break;
+ case DXGI_FORMAT_UNKNOWN:
+ g_message ("Unknown DXGI format. Expect problems...");
+ break;
+ default: /* unsupported DXGI format */
+ g_message ("Unsupported DXGI format (%d)", dx10hdr->dxgiFormat);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+static const Babl*
+premultiplied_variant (const Babl* format)
+{
+ if (format == babl_format ("R'G'B'A u8"))
+ return babl_format ("R'aG'aB'aA u8");
+ else
+ g_printerr ("Add format %s to premultiplied_variant () %s: %d\n", babl_get_name (format), __FILE__, __LINE__);
+ return format;
+}
+
+static int
+load_layer (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ unsigned int level,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ GeglBuffer *buffer;
+ const Babl *bablfmt = NULL;
+ GimpImageType type = GIMP_RGBA_IMAGE;
+ gchar *layer_name;
+ gint x, y, z, n;
+ gint32 layer;
+ unsigned int width = hdr->width >> level;
+ unsigned int height = hdr->height >> level;
+ unsigned int size = hdr->pitch_or_linsize >> (2 * level);
+ unsigned int layerw;
+ int format = DDS_COMPRESS_NONE;
+
+ if (width < 1) width = 1;
+ if (height < 1) height = 1;
+
+ switch (d->bpp)
+ {
+ case 1:
+ if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ type = GIMP_INDEXED_IMAGE;
+ }
+ else if (hdr->pixelfmt.rmask == 0xe0)
+ {
+ type = GIMP_RGB_IMAGE;
+ bablfmt = babl_format ("R'G'B' u8");
+ }
+ else if (hdr->pixelfmt.flags & DDPF_ALPHA)
+ {
+ type = GIMP_GRAYA_IMAGE;
+ bablfmt = babl_format ("Y'A u8");
+ }
+ else
+ {
+ type = GIMP_GRAY_IMAGE;
+ bablfmt = babl_format ("Y' u8");
+ }
+ break;
+ case 2:
+ if ((hdr->pixelfmt.flags & (DDPF_PALETTEINDEXED8 + DDPF_ALPHA)) ==
+ DDPF_PALETTEINDEXED8 + DDPF_ALPHA)
+ {
+ type = GIMP_INDEXEDA_IMAGE;
+ }
+ else if (hdr->pixelfmt.amask == 0xf000) /* RGBA4 */
+ {
+ type = GIMP_RGBA_IMAGE;
+ bablfmt = babl_format ("R'G'B'A u8");
+ }
+ else if (hdr->pixelfmt.amask == 0xff00) /* L8A8 */
+ {
+ type = GIMP_GRAYA_IMAGE;
+ bablfmt = babl_format ("Y'A u8");
+ }
+ else if (hdr->pixelfmt.bmask == 0x1f) /* R5G6B5 or RGB5A1 */
+ {
+ type = (hdr->pixelfmt.amask == 0x8000) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ bablfmt = (hdr->pixelfmt.amask == 0x8000) ? babl_format ("R'G'B'A u8") : babl_format ("R'G'B' u8");
+ }
+ else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ type = GIMP_GRAY_IMAGE;
+ bablfmt = babl_format ("Y' u16");
+ }
+ break;
+ case 3: type = GIMP_RGB_IMAGE; bablfmt = babl_format ("R'G'B' u8"); break;
+ case 4: type = GIMP_RGBA_IMAGE; bablfmt = babl_format ("R'G'B'A u8"); break;
+ }
+
+ layer_name = (level) ? g_strdup_printf ("mipmap %d %s", level, prefix) :
+ g_strdup_printf ("main surface %s", prefix);
+
+ layer = gimp_layer_new (image, layer_name, width, height, type, 100,
+ GIMP_LAYER_MODE_NORMAL);
+ g_free (layer_name);
+
+ gimp_image_insert_layer (image, layer, 0, *l);
+
+ if (type == GIMP_INDEXED_IMAGE || type == GIMP_INDEXEDA_IMAGE)
+ bablfmt = gimp_drawable_get_format (layer);
+
+ if ((*l)++) gimp_item_set_visible (layer, FALSE);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ layerw = gegl_buffer_get_width (buffer);
+
+ if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ unsigned int w = (width + 3) >> 2;
+ unsigned int h = (height + 3) >> 2;
+
+ switch (GETL32(hdr->pixelfmt.fourcc))
+ {
+ case FOURCC ('D','X','T','1'): format = DDS_COMPRESS_BC1; break;
+ case FOURCC ('D','X','T','2'): bablfmt = premultiplied_variant (bablfmt);
+ case FOURCC ('D','X','T','3'): format = DDS_COMPRESS_BC2; break;
+ case FOURCC ('D','X','T','4'): bablfmt = premultiplied_variant (bablfmt);
+ case FOURCC ('D','X','T','5'): format = DDS_COMPRESS_BC3; break;
+ case FOURCC ('R','X','G','B'): format = DDS_COMPRESS_BC3; break;
+ case FOURCC ('A','T','I','1'):
+ case FOURCC ('B','C','4','U'):
+ case FOURCC ('B','C','4','S'): format = DDS_COMPRESS_BC4; break;
+ case FOURCC ('A','T','I','2'):
+ case FOURCC ('B','C','5','U'):
+ case FOURCC ('B','C','5','S'): format = DDS_COMPRESS_BC5; break;
+ }
+
+ size = w * h;
+ if ((format == DDS_COMPRESS_BC1) || (format == DDS_COMPRESS_BC4))
+ size *= 8;
+ else
+ size *= 16;
+ }
+
+ if ((hdr->flags & DDSD_LINEARSIZE) &&
+ !fread (buf, size, 1, fp))
+ {
+ g_message ("Unexpected EOF.\n");
+ return 0;
+ }
+
+ if ((hdr->pixelfmt.flags & DDPF_RGB) ||
+ (hdr->pixelfmt.flags & DDPF_ALPHA))
+ {
+ guint ired = 0;
+ guint iblue = 2;
+
+ if (hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.version <= 199003 &&
+ hdr->reserved.gimp_dds_special.version > 0 &&
+ d->bpp >= 3 && hdr->pixelfmt.amask == 0xc0000000)
+ {
+ /* GIMP dds plug-in versions before or equal to 199003 (3.9.91) wrote
+ * the red and green channels reversed for RGB10A2. We will fix that here.
+ */
+ g_printerr ("Switching incorrect red and green channels in RGB10A2 dds "
+ "written by an older version of GIMP's dds plug-in.\n");
+ ired = 2;
+ iblue = 0;
+ }
+
+ z = 0;
+ for (y = 0, n = 0; y < height; ++y, ++n)
+ {
+ if (n >= d->tile_height)
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ n = 0;
+ gimp_progress_update ((double)y / (double)hdr->height);
+ }
+
+ if ((hdr->flags & DDSD_PITCH) &&
+ !fread (buf, width * d->bpp, 1, fp))
+ {
+ g_message ("Unexpected EOF.\n");
+ return 0;
+ }
+
+ if (!(hdr->flags & DDSD_LINEARSIZE)) z = 0;
+
+ for (x = 0; x < layerw; ++x)
+ {
+ unsigned int pixel = buf[z];
+ unsigned int pos = (n * layerw + x) * d->gimp_bpp;
+
+ if (d->bpp > 1) pixel += ((unsigned int)buf[z + 1] << 8);
+ if (d->bpp > 2) pixel += ((unsigned int)buf[z + 2] << 16);
+ if (d->bpp > 3) pixel += ((unsigned int)buf[z + 3] << 24);
+
+ if (d->bpp >= 3)
+ {
+ if (hdr->pixelfmt.amask == 0xc0000000) // handle RGB10A2
+ {
+ pixels[pos + ired] = (pixel >> d->rshift) >> 2;
+ pixels[pos + 1] = (pixel >> d->gshift) >> 2;
+ pixels[pos + iblue] = (pixel >> d->bshift) >> 2;
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ pixels[pos + 3] = (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ if (hdr->pixelfmt.flags & DDPF_ALPHAPIXELS)
+ {
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ }
+ }
+ else if (d->bpp == 2)
+ {
+ if (hdr->pixelfmt.amask == 0xf000) //RGBA4
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else if (hdr->pixelfmt.amask == 0xff00) //L8A8
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ else if (hdr->pixelfmt.bmask == 0x1f) //R5G6B5 or RGB5A1
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ if (hdr->pixelfmt.amask == 0x8000)
+ {
+ pixels[pos + 3] =
+ (pixel >> d->ashift << (8 - d->abits) & d->amask) * 255 / d->amask;
+ }
+ }
+ else if (hdr->pixelfmt.rmask == 0xffff) /* L16 */
+ {
+ guint16 *pixels16 = (guint16 *) &pixels[pos];
+
+ *pixels16 = (guint16) (pixel & 0xffff);
+ }
+ }
+ else
+ {
+ if (hdr->pixelfmt.flags & DDPF_PALETTEINDEXED8)
+ {
+ pixels[pos] = pixel & 0xff;
+ }
+ else if (hdr->pixelfmt.rmask == 0xe0) // R3G3B2
+ {
+ pixels[pos] =
+ (pixel >> d->rshift << (8 - d->rbits) & d->rmask) * 255 / d->rmask;
+ pixels[pos + 1] =
+ (pixel >> d->gshift << (8 - d->gbits) & d->gmask) * 255 / d->gmask;
+ pixels[pos + 2] =
+ (pixel >> d->bshift << (8 - d->bbits) & d->bmask) * 255 / d->bmask;
+ }
+ else if (hdr->pixelfmt.flags & DDPF_ALPHA)
+ {
+ pixels[pos + 0] = 255;
+ pixels[pos + 1] = pixel & 0xff;
+ }
+ else // LUMINANCE
+ {
+ pixels[pos] = pixel & 0xff;
+ }
+ }
+
+ z += d->bpp;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ }
+ else if (hdr->pixelfmt.flags & DDPF_FOURCC)
+ {
+ unsigned char *dst;
+
+ dst = g_malloc (width * height * d->gimp_bpp);
+ memset (dst, 0, width * height * d->gimp_bpp);
+
+ if (d->gimp_bpp == 4)
+ {
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ dst[y * (width * 4) + (x * 4) + 3] = 255;
+ }
+
+ dxt_decompress (dst, buf, format, size, width, height, d->gimp_bpp,
+ hdr->pixelfmt.flags & DDPF_NORMAL);
+
+ if (format == DDS_COMPRESS_BC5 &&
+ hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.version > 0 &&
+ hdr->reserved.gimp_dds_special.version <= 199002)
+ {
+ /* GIMP dds plug-in versions before 199002 == 3.9.90 wrote
+ * the red and green channels reversed. We will fix that here.
+ */
+ g_printerr ("Switching incorrect red and green channels in BC5 dds "
+ "written by an older version of GIMP's dds plug-in.\n");
+
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ {
+ guchar tmpG;
+ guint pix_width = width * d->gimp_bpp;
+ guint x_width = x * d->gimp_bpp;
+
+ tmpG = dst[y * pix_width + x_width];
+ dst[y * pix_width + x_width] = dst[y * pix_width + x_width + 1];
+ dst[y * pix_width + x_width + 1] = tmpG;
+ }
+ }
+
+ z = 0;
+ for (y = 0, n = 0; y < height; ++y, ++n)
+ {
+ if (n >= d->tile_height)
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+ n = 0;
+ gimp_progress_update ((double)y / (double)hdr->height);
+ }
+
+ memcpy (pixels + n * layerw * d->gimp_bpp,
+ dst + y * layerw * d->gimp_bpp,
+ width * d->gimp_bpp);
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - n, layerw, n), 0,
+ bablfmt, pixels, GEGL_AUTO_ROWSTRIDE);
+
+ g_free (dst);
+ }
+
+ gegl_buffer_flush (buffer);
+
+ g_object_unref (buffer);
+
+ /* gimp dds specific. decode encoded images */
+ if (dds_read_vals.decode_images &&
+ hdr->reserved.gimp_dds_special.magic1 == FOURCC ('G','I','M','P') &&
+ hdr->reserved.gimp_dds_special.magic2 == FOURCC ('-','D','D','S'))
+ {
+ switch (hdr->reserved.gimp_dds_special.extra_fourcc)
+ {
+ case FOURCC ('A','E','X','P'):
+ decode_alpha_exp_image (layer, FALSE);
+ break;
+ case FOURCC ('Y','C','G','1'):
+ decode_ycocg_image (layer, FALSE);
+ break;
+ case FOURCC ('Y','C','G','2'):
+ decode_ycocg_scaled_image (layer, FALSE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 1;
+}
+
+static int
+load_mipmaps (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ unsigned int level;
+
+ if ((hdr->flags & DDSD_MIPMAPCOUNT) &&
+ (hdr->caps.caps1 & DDSCAPS_MIPMAP) &&
+ (dds_read_vals.mipmaps != 0))
+ {
+ for (level = 1; level < hdr->num_mipmaps; ++level)
+ {
+ if (!load_layer (fp, hdr, d, image, level, prefix, l, pixels, buf))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+load_face (FILE *fp,
+ dds_header_t *hdr,
+ dds_load_info_t *d,
+ gint32 image,
+ char *prefix,
+ unsigned int *l,
+ guchar *pixels,
+ unsigned char *buf)
+{
+ if (!load_layer (fp, hdr, d, image, 0, prefix, l, pixels, buf))
+ return 0;
+
+ return load_mipmaps (fp, hdr, d, image, prefix, l, pixels, buf);
+}
+
+static unsigned char
+color_bits (unsigned int mask)
+{
+ unsigned char i = 0;
+
+ while (mask)
+ {
+ if (mask & 1) ++i;
+ mask >>= 1;
+ }
+
+ return i;
+}
+
+static unsigned char
+color_shift (unsigned int mask)
+{
+ guchar i = 0;
+
+ if (! mask)
+ return 0;
+
+ while (!((mask >> i) & 1))
+ ++i;
+
+ return i;
+}
+
+static void
+load_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ runme = TRUE;
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+toggle_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ int *flag = (int*) data;
+ (*flag) = !(*flag);
+}
+
+static int
+load_dialog (void)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *check;
+
+ dlg = gimp_dialog_new (_("Load DDS"), "dds", NULL, GTK_WIN_POS_MOUSE,
+ gimp_standard_help_func, LOAD_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+ NULL);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (load_dialog_response),
+ 0);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, 1, 1, 0);
+ gtk_widget_show (vbox);
+
+ check = gtk_check_button_new_with_mnemonic (_("_Load mipmaps"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.mipmaps);
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked), &dds_read_vals.mipmaps);
+ gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0);
+ gtk_widget_show (check);
+
+ check = gtk_check_button_new_with_mnemonic (_("_Automatically decode YCoCg/AExp images when detected"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), dds_read_vals.decode_images);
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked), &dds_read_vals.decode_images);
+ gtk_box_pack_start (GTK_BOX (vbox), check, 1, 1, 0);
+ gtk_widget_show (check);
+
+ gtk_widget_show (dlg);
+
+ runme = FALSE;
+
+ gtk_main ();
+
+ return runme;
+}
diff --git a/plug-ins/file-dds/ddswrite.c b/plug-ins/file-dds/ddswrite.c
new file mode 100644
index 0000000..5fb658a
--- /dev/null
+++ b/plug-ins/file-dds/ddswrite.c
@@ -0,0 +1,2278 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <libgimp/stdplugins-intl.h>
+
+#include "ddsplugin.h"
+#include "dds.h"
+#include "dxt.h"
+#include "mipmap.h"
+#include "endian_rw.h"
+#include "imath.h"
+#include "color.h"
+
+
+enum
+{
+ COMBO_VALUE,
+ COMBO_STRING,
+ COMBO_SENSITIVE
+};
+
+
+static gint save_dialog (gint32 image_id,
+ gint32 drawable);
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static gboolean write_image (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id);
+
+
+static gboolean runme = FALSE;
+
+static const char *cubemap_face_names[4][6] =
+{
+ {
+ "positive x", "negative x",
+ "positive y", "negative y",
+ "positive z", "negative z"
+ },
+ {
+ "pos x", "neg x",
+ "pos y", "neg y",
+ "pos z", "neg z",
+ },
+ {
+ "+x", "-x",
+ "+y", "-y",
+ "+z", "-z"
+ },
+ {
+ "right", "left",
+ "top", "bottom",
+ "back", "front"
+ }
+};
+
+static gint cubemap_faces[6];
+static gboolean is_cubemap = FALSE;
+static gboolean is_volume = FALSE;
+static gboolean is_array = FALSE;
+static gboolean is_mipmap_chain_valid = FALSE;
+
+static GtkWidget *compress_opt;
+static GtkWidget *format_opt;
+static GtkWidget *mipmap_opt;
+static GtkWidget *mipmap_filter_opt;
+static GtkWidget *mipmap_wrap_opt;
+static GtkWidget *srgb_chk;
+static GtkWidget *gamma_chk;
+static GtkWidget *gamma_spin;
+static GtkWidget *pm_chk;
+static GtkWidget *alpha_coverage_chk;
+static GtkWidget *alpha_test_threshold_spin;
+
+typedef struct string_value_s
+{
+ gint value;
+ gchar *string;
+} string_value_t;
+
+static string_value_t compression_strings[] =
+{
+ { DDS_COMPRESS_NONE, "None" },
+ { DDS_COMPRESS_BC1, "BC1 / DXT1" },
+ { DDS_COMPRESS_BC2, "BC2 / DXT3" },
+ { DDS_COMPRESS_BC3, "BC3 / DXT5" },
+ { DDS_COMPRESS_BC3N, "BC3nm / DXT5nm" },
+ { DDS_COMPRESS_BC4, "BC4 / ATI1 (3Dc+)" },
+ { DDS_COMPRESS_BC5, "BC5 / ATI2 (3Dc)" },
+ { DDS_COMPRESS_RXGB, "RXGB (DXT5)" },
+ { DDS_COMPRESS_AEXP, "Alpha Exponent (DXT5)" },
+ { DDS_COMPRESS_YCOCG, "YCoCg (DXT5)" },
+ { DDS_COMPRESS_YCOCGS, "YCoCg scaled (DXT5)" },
+ { -1, 0}
+};
+
+static string_value_t format_strings[] =
+{
+ { DDS_FORMAT_DEFAULT, "Default" },
+ { DDS_FORMAT_RGB8, "RGB8" },
+ { DDS_FORMAT_RGBA8, "RGBA8" },
+ { DDS_FORMAT_BGR8, "BGR8" },
+ { DDS_FORMAT_ABGR8, "ABGR8" },
+ { DDS_FORMAT_R5G6B5, "R5G6B5" },
+ { DDS_FORMAT_RGBA4, "RGBA4" },
+ { DDS_FORMAT_RGB5A1, "RGB5A1" },
+ { DDS_FORMAT_RGB10A2, "RGB10A2" },
+ { DDS_FORMAT_R3G3B2, "R3G3B2" },
+ { DDS_FORMAT_A8, "A8" },
+ { DDS_FORMAT_L8, "L8" },
+ { DDS_FORMAT_L8A8, "L8A8" },
+ { DDS_FORMAT_AEXP, "AExp" },
+ { DDS_FORMAT_YCOCG, "YCoCg" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_strings[] =
+{
+ { DDS_MIPMAP_NONE, "No mipmaps" },
+ { DDS_MIPMAP_GENERATE, "Generate mipmaps" },
+ { DDS_MIPMAP_EXISTING, "Use existing mipmaps" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_filter_strings[] =
+{
+ { DDS_MIPMAP_FILTER_DEFAULT, "Default" },
+ { DDS_MIPMAP_FILTER_NEAREST, "Nearest" },
+ { DDS_MIPMAP_FILTER_BOX, "Box" },
+ { DDS_MIPMAP_FILTER_TRIANGLE, "Triangle" },
+ { DDS_MIPMAP_FILTER_QUADRATIC, "Quadratic" },
+ { DDS_MIPMAP_FILTER_BSPLINE, "B-Spline" },
+ { DDS_MIPMAP_FILTER_MITCHELL, "Mitchell" },
+ { DDS_MIPMAP_FILTER_LANCZOS, "Lanczos" },
+ { DDS_MIPMAP_FILTER_KAISER, "Kaiser" },
+ { -1, 0}
+};
+
+static string_value_t mipmap_wrap_strings[] =
+{
+ { DDS_MIPMAP_WRAP_DEFAULT, "Default" },
+ { DDS_MIPMAP_WRAP_MIRROR, "Mirror" },
+ { DDS_MIPMAP_WRAP_REPEAT, "Repeat" },
+ { DDS_MIPMAP_WRAP_CLAMP, "Clamp" },
+ { -1, 0}
+};
+
+static string_value_t save_type_strings[] =
+{
+ { DDS_SAVE_SELECTED_LAYER, "Selected layer" },
+ { DDS_SAVE_CUBEMAP, "As cube map" },
+ { DDS_SAVE_VOLUMEMAP, "As volume map" },
+ { DDS_SAVE_ARRAY, "As texture array" },
+ { DDS_SAVE_VISIBLE_LAYERS, "All visible layers" },
+ { -1, 0}
+};
+
+static struct
+{
+ int format;
+ DXGI_FORMAT dxgi_format;
+ int bpp;
+ int alpha;
+ unsigned int rmask;
+ unsigned int gmask;
+ unsigned int bmask;
+ unsigned int amask;
+} format_info[] =
+{
+ { DDS_FORMAT_RGB8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
+ { DDS_FORMAT_RGBA8, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
+ { DDS_FORMAT_BGR8, DXGI_FORMAT_UNKNOWN, 3, 0, 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000},
+ { DDS_FORMAT_ABGR8, DXGI_FORMAT_R8G8B8A8_UNORM, 4, 1, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
+ { DDS_FORMAT_R5G6B5, DXGI_FORMAT_B5G6R5_UNORM, 2, 0, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000},
+ { DDS_FORMAT_RGBA4, DXGI_FORMAT_B4G4R4A4_UNORM, 2, 1, 0x00000f00, 0x000000f0, 0x0000000f, 0x0000f000},
+ { DDS_FORMAT_RGB5A1, DXGI_FORMAT_B5G5R5A1_UNORM, 2, 1, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000},
+ { DDS_FORMAT_RGB10A2, DXGI_FORMAT_R10G10B10A2_UNORM, 4, 1, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000},
+ { DDS_FORMAT_R3G3B2, DXGI_FORMAT_UNKNOWN, 1, 0, 0x000000e0, 0x0000001c, 0x00000003, 0x00000000},
+ { DDS_FORMAT_A8, DXGI_FORMAT_A8_UNORM, 1, 0, 0x00000000, 0x00000000, 0x00000000, 0x000000ff},
+ { DDS_FORMAT_L8, DXGI_FORMAT_R8_UNORM, 1, 0, 0x000000ff, 0x000000ff, 0x000000ff, 0x00000000},
+ { DDS_FORMAT_L8A8, DXGI_FORMAT_UNKNOWN, 2, 1, 0x000000ff, 0x000000ff, 0x000000ff, 0x0000ff00},
+ { DDS_FORMAT_AEXP, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
+ { DDS_FORMAT_YCOCG, DXGI_FORMAT_B8G8R8A8_UNORM, 4, 1, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}
+};
+
+
+static gboolean
+check_mipmaps (gint32 image_id,
+ gint savetype)
+{
+ gint *layers;
+ gint num_layers;
+ gint i, j, w, h, mipw, miph;
+ gint num_mipmaps;
+ gint num_surfaces = 0;
+ gint min_surfaces = 1;
+ gint max_surfaces = 1;
+ gboolean valid = TRUE;
+ GimpImageType type;
+
+ /* not handling volume maps for the moment... */
+ if (savetype == DDS_SAVE_VOLUMEMAP)
+ return 0;
+
+ if (savetype == DDS_SAVE_CUBEMAP)
+ {
+ min_surfaces = 6;
+ max_surfaces = 6;
+ }
+ else if (savetype == DDS_SAVE_ARRAY)
+ {
+ min_surfaces = 2;
+ max_surfaces = INT_MAX;
+ }
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ num_mipmaps = get_num_mipmaps (w, h);
+
+ type = gimp_drawable_type (layers[0]);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if (type != gimp_drawable_type (layers[i]))
+ return 0;
+
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ ++num_surfaces;
+ }
+
+ if ((num_surfaces < min_surfaces) ||
+ (num_surfaces > max_surfaces) ||
+ (num_layers != (num_surfaces * num_mipmaps)))
+ return 0;
+
+ for (i = 0; valid && i < num_layers; i += num_mipmaps)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ {
+ valid = FALSE;
+ break;
+ }
+
+ for (j = 1; j < num_mipmaps; ++j)
+ {
+ mipw = w >> j;
+ miph = h >> j;
+ if (mipw < 1) mipw = 1;
+ if (miph < 1) miph = 1;
+ if ((gimp_drawable_width (layers[i + j]) != mipw) ||
+ (gimp_drawable_height (layers[i + j]) != miph))
+ {
+ valid = FALSE;
+ break;
+ }
+ }
+ }
+
+ return valid;
+}
+
+static gboolean
+check_cubemap (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean cubemap = TRUE;
+ gint i, j, k, w, h;
+ gchar *layer_name;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers < 6)
+ return FALSE;
+
+ /* check for a valid cubemap with mipmap layers */
+ if (num_layers > 6)
+ {
+ /* check that mipmap layers are in order for a cubemap */
+ if (! check_mipmaps (image_id, DDS_SAVE_CUBEMAP))
+ return FALSE;
+
+ /* invalidate cubemap faces */
+ for (i = 0; i < 6; ++i)
+ cubemap_faces[i] = -1;
+
+ /* find the mipmap level 0 layers */
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ continue;
+
+ layer_name = (char*)gimp_item_get_name (layers[i]);
+ for (j = 0; j < 6; ++j)
+ {
+ for (k = 0; k < 4; ++k)
+ {
+ if (strstr (layer_name, cubemap_face_names[k][j]))
+ {
+ if (cubemap_faces[j] == -1)
+ {
+ cubemap_faces[j] = layers[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* check for 6 valid faces */
+ for (i = 0; i < 6; ++i)
+ {
+ if (cubemap_faces[i] == -1)
+ {
+ cubemap = FALSE;
+ break;
+ }
+ }
+
+ /* make sure they are all the same type */
+ if (cubemap)
+ {
+ type = gimp_drawable_type (cubemap_faces[0]);
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if (gimp_drawable_type (cubemap_faces[i]) != type)
+ cubemap = FALSE;
+ }
+ }
+ }
+
+ if (num_layers == 6)
+ {
+ /* invalidate cubemap faces */
+ for (i = 0; i < 6; ++i)
+ cubemap_faces[i] = -1;
+
+ for (i = 0; i < 6; ++i)
+ {
+ layer_name = (char*)gimp_item_get_name (layers[i]);
+ for (j = 0; j < 6; ++j)
+ {
+ for (k = 0; k < 4; ++k)
+ {
+ if (strstr (layer_name, cubemap_face_names[k][j]))
+ {
+ if (cubemap_faces[j] == -1)
+ {
+ cubemap_faces[j] = layers[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* check for 6 valid faces */
+ for (i = 0; i < 6; ++i)
+ {
+ if (cubemap_faces[i] == -1)
+ {
+ cubemap = FALSE;
+ break;
+ }
+ }
+
+ /* make sure they are all the same size */
+ if (cubemap)
+ {
+ w = gimp_drawable_width (cubemap_faces[0]);
+ h = gimp_drawable_height (cubemap_faces[0]);
+
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if ((gimp_drawable_width (cubemap_faces[i]) != w) ||
+ (gimp_drawable_height (cubemap_faces[i]) != h))
+ cubemap = FALSE;
+ }
+ }
+
+ /* make sure they are all the same type */
+ if (cubemap)
+ {
+ type = gimp_drawable_type (cubemap_faces[0]);
+ for (i = 1; i < 6 && cubemap; ++i)
+ {
+ if (gimp_drawable_type (cubemap_faces[i]) != type)
+ cubemap = FALSE;
+ }
+ }
+ }
+
+ return cubemap;
+}
+
+static gboolean
+check_volume (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean volume = FALSE;
+ gint i, w, h;
+ GimpImageType type;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers > 1)
+ {
+ volume = TRUE;
+
+ /* make sure all layers are the same size */
+ w = gimp_drawable_width (layers[0]);
+ h = gimp_drawable_height (layers[0]);
+
+ for (i = 1; i < num_layers && volume; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ volume = FALSE;
+ }
+
+ if (volume)
+ {
+ /* make sure all layers are the same type */
+ type = gimp_drawable_type (layers[0]);
+ for (i = 1; i < num_layers && volume; ++i)
+ {
+ if (gimp_drawable_type (layers[i]) != type)
+ volume = FALSE;
+ }
+ }
+ }
+
+ return volume;
+}
+
+static gboolean
+check_array (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gboolean array = FALSE;
+ gint i, w, h;
+ GimpImageType type;
+
+ if (check_mipmaps (image_id, DDS_SAVE_ARRAY))
+ return 1;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (num_layers > 1)
+ {
+ array = TRUE;
+
+ /* make sure all layers are the same size */
+ w = gimp_drawable_width (layers[0]);
+ h = gimp_drawable_height (layers[0]);
+
+ for (i = 1; i < num_layers && array; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) != w) ||
+ (gimp_drawable_height (layers[i]) != h))
+ array = FALSE;
+ }
+
+ if (array)
+ {
+ /* make sure all layers are the same type */
+ type = gimp_drawable_type (layers[0]);
+ for (i = 1; i < num_layers; ++i)
+ {
+ if (gimp_drawable_type (layers[i]) != type)
+ {
+ array = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ return array;
+}
+
+static int
+get_array_size (gint32 image_id)
+{
+ gint *layers;
+ gint num_layers;
+ gint i, w, h;
+ gint elements = 0;
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ {
+ elements++;
+ }
+ }
+
+ return elements;
+}
+
+GimpPDBStatusType
+write_dds (gchar *filename,
+ gint32 image_id,
+ gint32 drawable_id,
+ gboolean interactive_dds,
+ gboolean is_duplicate_image)
+{
+ FILE *fp;
+ gchar *tmp;
+ int rc = 0;
+
+ is_mipmap_chain_valid = check_mipmaps (image_id, dds_write_vals.savetype);
+
+ is_cubemap = check_cubemap (image_id);
+ is_volume = check_volume (image_id);
+ is_array = check_array (image_id);
+
+ if (interactive_dds)
+ {
+ if (! is_mipmap_chain_valid &&
+ dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
+ dds_write_vals.mipmaps = DDS_MIPMAP_NONE;
+
+ if (! save_dialog (image_id, drawable_id))
+ return GIMP_PDB_CANCEL;
+ }
+ else
+ {
+ if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP && ! is_cubemap)
+ {
+ g_message ("DDS: Cannot save image as cube map");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP && ! is_volume)
+ {
+ g_message ("DDS: Cannot save image as volume map");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP &&
+ dds_write_vals.compression != DDS_COMPRESS_NONE)
+ {
+ g_message ("DDS: Cannot save volume map with compression");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING &&
+ ! is_mipmap_chain_valid)
+ {
+ g_message ("DDS: Cannot save with existing mipmaps as the mipmap chain is incomplete");
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ fp = g_fopen (filename, "wb");
+ if (fp == 0)
+ {
+ g_message ("Error opening %s", filename);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (strrchr (filename, '/'))
+ tmp = g_strdup_printf ("Saving %s:", strrchr (filename, '/') + 1);
+ else
+ tmp = g_strdup_printf ("Saving %s:", filename);
+ gimp_progress_init (tmp);
+ g_free (tmp);
+
+ /* If destructive changes are going to happen to the image,
+ * make sure we send a duplicate of it to write_image
+ */
+ if (! is_duplicate_image)
+ {
+ gint32 duplicate_image = gimp_image_duplicate (image_id);
+ rc = write_image (fp, duplicate_image, drawable_id);
+ gimp_image_delete (duplicate_image);
+ }
+ else
+ {
+ rc = write_image (fp, image_id, drawable_id);
+ }
+
+
+ fclose (fp);
+
+ return rc ? GIMP_PDB_SUCCESS : GIMP_PDB_EXECUTION_ERROR;
+}
+
+static void
+swap_rb (unsigned char *pixels,
+ unsigned int n,
+ int bpp)
+{
+ unsigned int i;
+ unsigned char t;
+
+ for (i = 0; i < n; ++i)
+ {
+ t = pixels[bpp * i + 0];
+ pixels[bpp * i + 0] = pixels[bpp * i + 2];
+ pixels[bpp * i + 2] = t;
+ }
+}
+
+static void
+alpha_exp (unsigned char *dst,
+ int r,
+ int g,
+ int b,
+ int a)
+{
+ float ar, ag, ab, aa;
+
+ ar = (float)r / 255.0f;
+ ag = (float)g / 255.0f;
+ ab = (float)b / 255.0f;
+
+ aa = MAX (ar, MAX (ag, ab));
+
+ if (aa < 1e-04f)
+ {
+ dst[0] = b;
+ dst[1] = g;
+ dst[2] = r;
+ dst[3] = 255;
+ return;
+ }
+
+ ar /= aa;
+ ag /= aa;
+ ab /= aa;
+
+ r = (int)floorf (255.0f * ar + 0.5f);
+ g = (int)floorf (255.0f * ag + 0.5f);
+ b = (int)floorf (255.0f * ab + 0.5f);
+ a = (int)floorf (255.0f * aa + 0.5f);
+
+ dst[0] = MAX (0, MIN (255, b));
+ dst[1] = MAX (0, MIN (255, g));
+ dst[2] = MAX (0, MIN (255, r));
+ dst[3] = MAX (0, MIN (255, a));
+}
+
+static void
+convert_pixels (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ int w,
+ int h,
+ int d,
+ int bpp,
+ unsigned char *palette,
+ int mipmaps)
+{
+ unsigned int i, num_pixels;
+ unsigned char r, g, b, a;
+
+ if (d > 0)
+ num_pixels = get_volume_mipmapped_size (w, h, d, 1, 0, mipmaps, DDS_COMPRESS_NONE);
+ else
+ num_pixels = get_mipmapped_size (w, h, 1, 0, mipmaps, DDS_COMPRESS_NONE);
+
+ for (i = 0; i < num_pixels; ++i)
+ {
+ if (bpp == 1)
+ {
+ if (palette)
+ {
+ r = palette[3 * src[i] + 0];
+ g = palette[3 * src[i] + 1];
+ b = palette[3 * src[i] + 2];
+ }
+ else
+ r = g = b = src[i];
+
+ if (format == DDS_FORMAT_A8)
+ a = src[i];
+ else
+ a = 255;
+ }
+ else if (bpp == 2)
+ {
+ r = g = b = src[2 * i];
+ a = src[2 * i + 1];
+ }
+ else if (bpp == 3)
+ {
+ b = src[3 * i + 0];
+ g = src[3 * i + 1];
+ r = src[3 * i + 2];
+ a = 255;
+ }
+ else
+ {
+ b = src[4 * i + 0];
+ g = src[4 * i + 1];
+ r = src[4 * i + 2];
+ a = src[4 * i + 3];
+ }
+
+ switch (format)
+ {
+ case DDS_FORMAT_RGB8:
+ dst[3 * i + 0] = b;
+ dst[3 * i + 1] = g;
+ dst[3 * i + 2] = r;
+ break;
+ case DDS_FORMAT_RGBA8:
+ dst[4 * i + 0] = b;
+ dst[4 * i + 1] = g;
+ dst[4 * i + 2] = r;
+ dst[4 * i + 3] = a;
+ break;
+ case DDS_FORMAT_BGR8:
+ dst[3 * i + 0] = r;
+ dst[3 * i + 1] = g;
+ dst[3 * i + 2] = b;
+ break;
+ case DDS_FORMAT_ABGR8:
+ dst[4 * i + 0] = r;
+ dst[4 * i + 1] = g;
+ dst[4 * i + 2] = b;
+ dst[4 * i + 3] = a;
+ break;
+ case DDS_FORMAT_R5G6B5:
+ PUTL16(&dst[2 * i], pack_r5g6b5(r, g, b));
+ break;
+ case DDS_FORMAT_RGBA4:
+ PUTL16(&dst[2 * i], pack_rgba4(r, g, b, a));
+ break;
+ case DDS_FORMAT_RGB5A1:
+ PUTL16(&dst[2 * i], pack_rgb5a1(r, g, b, a));
+ break;
+ case DDS_FORMAT_RGB10A2:
+ PUTL32(&dst[4 * i], pack_rgb10a2(r, g, b, a));
+ break;
+ case DDS_FORMAT_R3G3B2:
+ dst[i] = pack_r3g3b2(r, g, b);
+ break;
+ case DDS_FORMAT_A8:
+ dst[i] = a;
+ break;
+ case DDS_FORMAT_L8:
+ dst[i] = rgb_to_luminance (r, g, b);
+ break;
+ case DDS_FORMAT_L8A8:
+ dst[2 * i + 0] = rgb_to_luminance (r, g, b);
+ dst[2 * i + 1] = a;
+ break;
+ case DDS_FORMAT_YCOCG:
+ dst[4 * i] = a;
+ RGB_to_YCoCg (&dst[4 * i], r, g, b);
+ break;
+ case DDS_FORMAT_AEXP:
+ alpha_exp (&dst[4 * i], r, g, b, a);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+get_mipmap_chain (unsigned char *dst,
+ int w,
+ int h,
+ int bpp,
+ gint32 image_id,
+ gint drawable_id)
+{
+ gint *layers, num_layers;
+ GeglBuffer *buffer;
+ const Babl *format = 0;
+ int i, idx = 0, offset, mipw, miph;
+
+ if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ for (i = 0; i < num_layers; ++i)
+ {
+ if (layers[i] == drawable_id)
+ {
+ idx = i;
+ break;
+ }
+ }
+
+ if (i == num_layers) return;
+
+ offset = 0;
+
+ while (get_next_mipmap_dimensions (&mipw, &miph, w, h))
+ {
+ buffer = gimp_drawable_get_buffer (layers[++idx]);
+
+ if ((gegl_buffer_get_width (buffer) != mipw) ||
+ (gegl_buffer_get_height (buffer) != miph))
+ {
+ g_object_unref (buffer);
+ return;
+ }
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, mipw, miph), 1.0, format,
+ dst + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ g_object_unref (buffer);
+
+ /* we need BGRX or BGRA */
+ if (bpp >= 3)
+ swap_rb (dst + offset, mipw * miph, bpp);
+
+ offset += (mipw * miph * bpp);
+ w = mipw;
+ h = miph;
+ }
+}
+
+static void
+write_layer (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id,
+ int w,
+ int h,
+ int bpp,
+ int fmtbpp,
+ int mipmaps)
+{
+ GeglBuffer *buffer;
+ const Babl *format = 0;
+ GimpImageBaseType basetype;
+ GimpImageType type;
+ unsigned char *src, *dst, *fmtdst, *tmp;
+ unsigned char *palette = NULL;
+ int i, c, x, y, size, fmtsize, offset, colors;
+ int compression = dds_write_vals.compression;
+ int flags = 0;
+
+ basetype = gimp_image_base_type (image_id);
+ type = gimp_drawable_type (drawable_id);
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ src = g_malloc (w * h * bpp);
+
+ if (basetype == GIMP_INDEXED)
+ format = gimp_drawable_get_format (drawable_id);
+ else if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (basetype == GIMP_INDEXED)
+ {
+ palette = gimp_image_get_colormap (image_id, &colors);
+
+ if (type == GIMP_INDEXEDA_IMAGE)
+ {
+ tmp = g_malloc (w * h);
+ for (i = 0; i < w * h; ++i)
+ tmp[i] = src[2 * i];
+ g_free (src);
+ src = tmp;
+ bpp = 1;
+ }
+ }
+
+ /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
+ onwards */
+ if (bpp >= 3)
+ swap_rb (src, w * h, bpp);
+
+ if (compression == DDS_COMPRESS_BC3N)
+ {
+ if (bpp != 4)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ for (y = 0; y < h; ++y)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ /* set alpha to red (x) */
+ src[y * (w * 4) + (x * 4) + 3] =
+ src[y * (w * 4) + (x * 4) + 2];
+ /* set red to 1 */
+ src[y * (w * 4) + (x * 4) + 2] = 255;
+ }
+ }
+ }
+
+ /* RXGB (Doom3) */
+ if (compression == DDS_COMPRESS_RXGB)
+ {
+ if (bpp != 4)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGBA8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ for (y = 0; y < h; ++y)
+ {
+ for (x = 0; x < w; ++x)
+ {
+ /* swap red and alpha */
+ c = src[y * (w * 4) + (x * 4) + 3];
+ src[y * (w * 4) + (x * 4) + 3] =
+ src[y * (w * 4) + (x * 4) + 2];
+ src[y * (w * 4) + (x * 4) + 2] = c;
+ }
+ }
+ }
+
+ if (compression == DDS_COMPRESS_YCOCG ||
+ compression == DDS_COMPRESS_YCOCGS) /* convert to YCoCG */
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_YCOCG, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ if (compression == DDS_COMPRESS_AEXP)
+ {
+ fmtsize = w * h * 4;
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_AEXP, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 4;
+ }
+
+ if (compression == DDS_COMPRESS_NONE)
+ {
+ if (mipmaps > 1)
+ {
+ /* pre-convert indexed images to RGB for better quality mipmaps
+ if a pixel format conversion is requested */
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT && basetype == GIMP_INDEXED)
+ {
+ fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps, DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = 3;
+ palette = NULL;
+ }
+
+ size = get_mipmapped_size (w, h, bpp, 0, mipmaps, DDS_COMPRESS_NONE);
+ dst = g_malloc (size);
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
+ {
+ generate_mipmaps (dst, src, w, h, bpp, palette != NULL,
+ mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma,
+ dds_write_vals.preserve_alpha_coverage,
+ dds_write_vals.alpha_test_threshold);
+ }
+ else
+ {
+ memcpy (dst, src, w * h * bpp);
+ get_mipmap_chain (dst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
+ }
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ fmtsize = get_mipmapped_size (w, h, fmtbpp, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+
+ convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, 0, bpp,
+ palette, mipmaps);
+
+ g_free (dst);
+ dst = fmtdst;
+ bpp = fmtbpp;
+ }
+
+ offset = 0;
+
+ for (i = 0; i < mipmaps; ++i)
+ {
+ size = get_mipmapped_size (w, h, bpp, i, 1, DDS_COMPRESS_NONE);
+ fwrite (dst + offset, 1, size, fp);
+ offset += size;
+ }
+
+ g_free (dst);
+ }
+ else
+ {
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ fmtdst = g_malloc (h * w * fmtbpp);
+ convert_pixels (fmtdst, src, dds_write_vals.format, w, h, 0, bpp,
+ palette, 1);
+ g_free (src);
+ src = fmtdst;
+ bpp = fmtbpp;
+ }
+
+ fwrite (src, 1, h * w * bpp, fp);
+ }
+ }
+ else
+ {
+ size = get_mipmapped_size (w, h, bpp, 0, mipmaps, compression);
+
+ dst = g_malloc (size);
+
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtsize = get_mipmapped_size (w, h, 3, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ convert_pixels (fmtdst, src, DDS_FORMAT_RGB8, w, h, 0, bpp,
+ palette, mipmaps);
+ g_free (src);
+ src = fmtdst;
+ bpp = 3;
+ }
+
+ if (mipmaps > 1)
+ {
+ fmtsize = get_mipmapped_size (w, h, bpp, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ fmtdst = g_malloc (fmtsize);
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE)
+ {
+ generate_mipmaps (fmtdst, src, w, h, bpp, 0, mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma,
+ dds_write_vals.preserve_alpha_coverage,
+ dds_write_vals.alpha_test_threshold);
+ }
+ else
+ {
+ memcpy (fmtdst, src, w * h * bpp);
+ get_mipmap_chain (fmtdst + (w * h * bpp), w, h, bpp, image_id, drawable_id);
+ }
+
+ g_free (src);
+ src = fmtdst;
+ }
+
+ flags = 0;
+ if (dds_write_vals.perceptual_metric) flags |= DXT_PERCEPTUAL;
+
+ dxt_compress (dst, src, compression, w, h, bpp, mipmaps, flags);
+
+ fwrite (dst, 1, size, fp);
+
+ g_free (dst);
+ }
+
+ g_free (src);
+
+ g_object_unref (buffer);
+}
+
+static void
+write_volume_mipmaps (FILE *fp,
+ gint32 image_id,
+ gint32 *layers,
+ int w,
+ int h,
+ int d,
+ int bpp,
+ int fmtbpp,
+ int mipmaps)
+{
+ int i, size, offset, colors;
+ unsigned char *src, *dst, *tmp, *fmtdst;
+ unsigned char *palette = 0;
+ GeglBuffer *buffer;
+ const Babl *format;
+ GimpImageBaseType type;
+
+ type = gimp_image_base_type (image_id);
+
+ if (dds_write_vals.compression != DDS_COMPRESS_NONE) return;
+
+ src = g_malloc (w * h * bpp * d);
+
+ if (bpp == 1)
+ format = babl_format ("Y' u8");
+ else if (bpp == 2)
+ format = babl_format ("Y'A u8");
+ else if (bpp == 3)
+ format = babl_format ("R'G'B' u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+
+ if (gimp_image_base_type (image_id) == GIMP_INDEXED)
+ palette = gimp_image_get_colormap (image_id, &colors);
+
+ offset = 0;
+ for (i = 0; i < d; ++i)
+ {
+ buffer = gimp_drawable_get_buffer (layers[i]);
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0, format,
+ src + offset, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ offset += (w * h * bpp);
+ g_object_unref (buffer);
+ }
+
+ if (gimp_drawable_type (layers[0]) == GIMP_INDEXEDA_IMAGE)
+ {
+ tmp = g_malloc (w * h * d);
+ for (i = 0; i < w * h * d; ++i)
+ tmp[i] = src[2 * i];
+ g_free (src);
+ src = tmp;
+ bpp = 1;
+ }
+
+ /* we want and assume BGRA ordered pixels for bpp >= 3 from here and
+ onwards */
+ if (bpp >= 3)
+ swap_rb (src, w * h * d, bpp);
+
+ /* pre-convert indexed images to RGB for better mipmaps if a
+ pixel format conversion is requested */
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT && type == GIMP_INDEXED)
+ {
+ size = get_volume_mipmapped_size (w, h, d, 3, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ dst = g_malloc (size);
+ convert_pixels (dst, src, DDS_FORMAT_RGB8, w, h, d, bpp, palette, 1);
+ g_free (src);
+ src = dst;
+ bpp = 3;
+ palette = NULL;
+ }
+
+ size = get_volume_mipmapped_size (w, h, d, bpp, 0, mipmaps,
+ dds_write_vals.compression);
+
+ dst = g_malloc (size);
+
+ offset = get_volume_mipmapped_size (w, h, d, bpp, 0, 1,
+ dds_write_vals.compression);
+
+ generate_volume_mipmaps (dst, src, w, h, d, bpp,
+ palette != NULL, mipmaps,
+ dds_write_vals.mipmap_filter,
+ dds_write_vals.mipmap_wrap,
+ dds_write_vals.gamma_correct + dds_write_vals.srgb,
+ dds_write_vals.gamma);
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ size = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, mipmaps,
+ dds_write_vals.compression);
+ offset = get_volume_mipmapped_size (w, h, d, fmtbpp, 0, 1,
+ dds_write_vals.compression);
+ fmtdst = g_malloc (size);
+
+ convert_pixels (fmtdst, dst, dds_write_vals.format, w, h, d, bpp,
+ palette, mipmaps);
+ g_free (dst);
+ dst = fmtdst;
+ }
+
+ fwrite (dst + offset, 1, size, fp);
+
+ g_free (src);
+ g_free (dst);
+}
+
+static gboolean
+write_image (FILE *fp,
+ gint32 image_id,
+ gint32 drawable_id)
+{
+ GimpImageType drawable_type;
+ GimpImageBaseType basetype;
+ gint i, w, h;
+ gint bpp = 0;
+ gint fmtbpp = 0;
+ gint has_alpha = 0;
+ gint num_mipmaps;
+ guchar hdr[DDS_HEADERSIZE];
+ guchar hdr10[DDS_HEADERSIZE_DX10];
+ guint flags = 0, pflags = 0, caps = 0, caps2 = 0, size = 0;
+ guint rmask = 0, gmask = 0, bmask = 0, amask = 0;
+ guint fourcc = 0;
+ DXGI_FORMAT dxgi_format = DXGI_FORMAT_UNKNOWN;
+ gint32 num_layers;
+ gint32 *layers;
+ guchar *cmap;
+ gint colors;
+ guchar zero[4] = {0, 0, 0, 0};
+ gint is_dx10 = 0;
+ gint array_size = 1;
+
+ if (dds_write_vals.flip_image)
+ {
+ gimp_image_flip (image_id, GIMP_ORIENTATION_VERTICAL);
+ drawable_id = gimp_image_get_active_drawable (image_id);
+ }
+
+ layers = gimp_image_get_layers (image_id, &num_layers);
+
+ if (dds_write_vals.mipmaps == DDS_MIPMAP_EXISTING)
+ drawable_id = layers[0];
+
+ if (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER)
+ {
+ w = gimp_drawable_width (drawable_id);
+ h = gimp_drawable_height (drawable_id);
+ }
+ else
+ {
+ w = gimp_image_width (image_id);
+ h = gimp_image_height (image_id);
+ }
+
+ basetype = gimp_image_base_type (image_id);
+ drawable_type = gimp_drawable_type (drawable_id);
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE: bpp = 3; break;
+ case GIMP_RGBA_IMAGE: bpp = 4; break;
+ case GIMP_GRAY_IMAGE: bpp = 1; break;
+ case GIMP_GRAYA_IMAGE: bpp = 2; break;
+ case GIMP_INDEXED_IMAGE: bpp = 1; break;
+ case GIMP_INDEXEDA_IMAGE: bpp = 2; break;
+ default:
+ break;
+ }
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ for (i = 0; ; ++i)
+ {
+ if (format_info[i].format == dds_write_vals.format)
+ {
+ fmtbpp = format_info[i].bpp;
+ has_alpha = format_info[i].alpha;
+ rmask = format_info[i].rmask;
+ gmask = format_info[i].gmask;
+ bmask = format_info[i].bmask;
+ amask = format_info[i].amask;
+ dxgi_format = format_info[i].dxgi_format;
+ break;
+ }
+ }
+ }
+ else if (bpp == 1)
+ {
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = bmask = gmask = amask = 0;
+ }
+ else
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = 0x000000ff;
+ gmask = bmask = amask = 0;
+ dxgi_format = DXGI_FORMAT_R8_UNORM;
+ }
+ }
+ else if (bpp == 2)
+ {
+ if (basetype == GIMP_INDEXED)
+ {
+ fmtbpp = 1;
+ has_alpha = 0;
+ rmask = gmask = bmask = amask = 0;
+ }
+ else
+ {
+ fmtbpp = 2;
+ has_alpha = 1;
+ rmask = 0x000000ff;
+ gmask = 0x000000ff;
+ bmask = 0x000000ff;
+ amask = 0x0000ff00;
+ }
+ }
+ else if (bpp == 3)
+ {
+ fmtbpp = 3;
+ rmask = 0x00ff0000;
+ gmask = 0x0000ff00;
+ bmask = 0x000000ff;
+ amask = 0x00000000;
+ }
+ else
+ {
+ fmtbpp = 4;
+ has_alpha = 1;
+ rmask = 0x00ff0000;
+ gmask = 0x0000ff00;
+ bmask = 0x000000ff;
+ amask = 0xff000000;
+ dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ }
+
+ memset (hdr, 0, DDS_HEADERSIZE);
+
+ PUTL32(hdr, FOURCC ('D','D','S',' '));
+ PUTL32(hdr + 4, 124);
+ PUTL32(hdr + 12, h);
+ PUTL32(hdr + 16, w);
+ PUTL32(hdr + 76, 32);
+
+ if (dds_write_vals.compression == DDS_COMPRESS_NONE)
+ {
+ PUTL32(hdr + 88, fmtbpp << 3);
+ PUTL32(hdr + 92, rmask);
+ PUTL32(hdr + 96, gmask);
+ PUTL32(hdr + 100, bmask);
+ PUTL32(hdr + 104, amask);
+ }
+
+ /*
+ put some information in the reserved area to identify the origin
+ of the image
+ */
+ PUTL32(hdr + 32, FOURCC ('G','I','M','P'));
+ PUTL32(hdr + 36, FOURCC ('-','D','D','S'));
+ PUTL32(hdr + 40, DDS_PLUGIN_VERSION);
+
+ flags = DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT;
+
+ caps = DDSCAPS_TEXTURE;
+ if (dds_write_vals.mipmaps)
+ {
+ flags |= DDSD_MIPMAPCOUNT;
+ caps |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
+ num_mipmaps = get_num_mipmaps (w, h);
+ }
+ else
+ {
+ num_mipmaps = 1;
+ }
+
+ if ((dds_write_vals.savetype == DDS_SAVE_CUBEMAP) && is_cubemap)
+ {
+ caps |= DDSCAPS_COMPLEX;
+ caps2 |= (DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES);
+ }
+ else if ((dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP) && is_volume)
+ {
+ PUTL32(hdr + 24, num_layers); /* depth */
+ flags |= DDSD_DEPTH;
+ caps |= DDSCAPS_COMPLEX;
+ caps2 |= DDSCAPS2_VOLUME;
+ }
+
+ PUTL32(hdr + 28, num_mipmaps);
+ PUTL32(hdr + 108, caps);
+ PUTL32(hdr + 112, caps2);
+
+ if (dds_write_vals.compression == DDS_COMPRESS_NONE)
+ {
+ flags |= DDSD_PITCH;
+
+ if (dds_write_vals.format > DDS_FORMAT_DEFAULT)
+ {
+ if (dds_write_vals.format == DDS_FORMAT_A8)
+ pflags |= DDPF_ALPHA;
+ else
+ {
+ if (((fmtbpp == 1) || (dds_write_vals.format == DDS_FORMAT_L8A8)) &&
+ (dds_write_vals.format != DDS_FORMAT_R3G3B2))
+ pflags |= DDPF_LUMINANCE;
+ else
+ pflags |= DDPF_RGB;
+ }
+ }
+ else
+ {
+ if (bpp == 1)
+ {
+ if (basetype == GIMP_INDEXED)
+ pflags |= DDPF_PALETTEINDEXED8;
+ else
+ pflags |= DDPF_LUMINANCE;
+ }
+ else if ((bpp == 2) && (basetype == GIMP_INDEXED))
+ {
+ pflags |= DDPF_PALETTEINDEXED8;
+ }
+ else
+ {
+ pflags |= DDPF_RGB;
+ }
+ }
+
+ if (has_alpha)
+ pflags |= DDPF_ALPHAPIXELS;
+
+ PUTL32 (hdr + 8, flags);
+ PUTL32 (hdr + 20, w * fmtbpp); /* pitch */
+ PUTL32 (hdr + 80, pflags);
+
+ /*
+ * write extra fourcc info - this is special to GIMP DDS. When the image
+ * is read by the plugin, we can detect the added information to decode
+ * the pixels
+ */
+ if (dds_write_vals.format == DDS_FORMAT_AEXP)
+ {
+ PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
+ }
+ else if (dds_write_vals.format == DDS_FORMAT_YCOCG)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
+ }
+ }
+ else
+ {
+ flags |= DDSD_LINEARSIZE;
+ pflags = DDPF_FOURCC;
+
+ switch (dds_write_vals.compression)
+ {
+ case DDS_COMPRESS_BC1:
+ fourcc = FOURCC ('D','X','T','1');
+ dxgi_format = DXGI_FORMAT_BC1_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC2:
+ fourcc = FOURCC ('D','X','T','3');
+ dxgi_format = DXGI_FORMAT_BC2_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC3:
+ case DDS_COMPRESS_BC3N:
+ case DDS_COMPRESS_YCOCG:
+ case DDS_COMPRESS_YCOCGS:
+ case DDS_COMPRESS_AEXP:
+ fourcc = FOURCC ('D','X','T','5');
+ dxgi_format = DXGI_FORMAT_BC3_UNORM;
+ break;
+
+ case DDS_COMPRESS_RXGB:
+ fourcc = FOURCC ('R','X','G','B');
+ dxgi_format = DXGI_FORMAT_BC3_UNORM;
+ break;
+
+ case DDS_COMPRESS_BC4:
+ fourcc = FOURCC ('A','T','I','1');
+ dxgi_format = DXGI_FORMAT_BC4_UNORM;
+ //is_dx10 = 1;
+ break;
+
+ case DDS_COMPRESS_BC5:
+ fourcc = FOURCC ('A','T','I','2');
+ dxgi_format = DXGI_FORMAT_BC5_UNORM;
+ //is_dx10 = 1;
+ break;
+ }
+
+ if ((dds_write_vals.compression == DDS_COMPRESS_BC3N) ||
+ (dds_write_vals.compression == DDS_COMPRESS_RXGB))
+ {
+ pflags |= DDPF_NORMAL;
+ }
+
+ PUTL32 (hdr + 8, flags);
+ PUTL32 (hdr + 80, pflags);
+ PUTL32 (hdr + 84, fourcc);
+
+ size = ((w + 3) >> 2) * ((h + 3) >> 2);
+ if ((dds_write_vals.compression == DDS_COMPRESS_BC1) ||
+ (dds_write_vals.compression == DDS_COMPRESS_BC4))
+ size *= 8;
+ else
+ size *= 16;
+
+ PUTL32 (hdr + 20, size); /* linear size */
+
+ /*
+ * write extra fourcc info - this is special to GIMP DDS. When the image
+ * is read by the plugin, we can detect the added information to decode
+ * the pixels
+ */
+ if (dds_write_vals.compression == DDS_COMPRESS_AEXP)
+ {
+ PUTL32 (hdr + 44, FOURCC ('A','E','X','P'));
+ }
+ else if (dds_write_vals.compression == DDS_COMPRESS_YCOCG)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','1'));
+ }
+ else if (dds_write_vals.compression == DDS_COMPRESS_YCOCGS)
+ {
+ PUTL32 (hdr + 44, FOURCC ('Y','C','G','2'));
+ }
+ }
+
+ /* texture arrays require a DX10 header */
+ if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
+ is_dx10 = 1;
+
+ if (is_dx10)
+ {
+ array_size = (dds_write_vals.savetype == DDS_SAVE_SELECTED_LAYER ||
+ dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS) ? 1 : get_array_size (image_id);
+
+ PUTL32 (hdr10 + 0, dxgi_format);
+ PUTL32 (hdr10 + 4, D3D10_RESOURCE_DIMENSION_TEXTURE2D);
+ PUTL32 (hdr10 + 8, 0);
+ PUTL32 (hdr10 + 12, array_size);
+ PUTL32 (hdr10 + 16, 0);
+
+ /* update main header accordingly */
+ PUTL32 (hdr + 80, pflags | DDPF_FOURCC);
+ PUTL32 (hdr + 84, FOURCC ('D','X','1','0'));
+ }
+
+ fwrite (hdr, DDS_HEADERSIZE, 1, fp);
+
+ if (is_dx10)
+ fwrite (hdr10, DDS_HEADERSIZE_DX10, 1, fp);
+
+ /* write palette for indexed images */
+ if ((basetype == GIMP_INDEXED) &&
+ (dds_write_vals.format == DDS_FORMAT_DEFAULT) &&
+ (dds_write_vals.compression == DDS_COMPRESS_NONE))
+ {
+ cmap = gimp_image_get_colormap (image_id, &colors);
+
+ for (i = 0; i < colors; ++i)
+ {
+ fwrite (&cmap[3 * i], 1, 3, fp);
+ if (i == dds_write_vals.transindex)
+ fputc (0, fp);
+ else
+ fputc (255, fp);
+ }
+
+ for (; i < 256; ++i)
+ fwrite (zero, 1, 4, fp);
+ }
+
+ if (dds_write_vals.savetype == DDS_SAVE_CUBEMAP)
+ {
+ for (i = 0; i < 6; ++i)
+ {
+ write_layer (fp, image_id, cubemap_faces[i], w, h, bpp, fmtbpp,
+ num_mipmaps);
+ gimp_progress_update ((float)(i + 1) / 6.0);
+ }
+ }
+ else if (dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
+ {
+ for (i = 0; i < num_layers; ++i)
+ {
+ write_layer (fp, image_id, layers[i], w, h, bpp, fmtbpp, 1);
+ gimp_progress_update ((float)i / (float)num_layers);
+ }
+
+ if (num_mipmaps > 1)
+ write_volume_mipmaps (fp, image_id, layers, w, h, num_layers,
+ bpp, fmtbpp, num_mipmaps);
+ }
+ else if (dds_write_vals.savetype == DDS_SAVE_ARRAY)
+ {
+ for (i = 0; i < num_layers; ++i)
+ {
+ if ((gimp_drawable_width (layers[i]) == w) &&
+ (gimp_drawable_height (layers[i]) == h))
+ {
+ write_layer (fp, image_id, layers[i],
+ w, h, bpp, fmtbpp, num_mipmaps);
+ }
+
+ gimp_progress_update ((float)i / (float)num_layers);
+ }
+ }
+ else
+ {
+ if (dds_write_vals.savetype == DDS_SAVE_VISIBLE_LAYERS)
+ drawable_id = gimp_image_merge_visible_layers (image_id, 1);
+ write_layer (fp, image_id, drawable_id, w, h, bpp, fmtbpp, num_mipmaps);
+ }
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+static GtkWidget *
+string_value_combo_new (string_value_t *strings,
+ int active_value)
+{
+ GtkWidget *opt;
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gint i;
+ gint active = 0;
+
+ store = gtk_list_store_new (3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN);
+ for (i = 0; strings[i].string; ++i)
+ {
+ if (strings[i].value == active_value) active = i;
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ 0, strings[i].value,
+ 1, strings[i].string,
+ 2, 1,
+ -1);
+ }
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ opt = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (opt), renderer, 1);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (opt), renderer,
+ "text", COMBO_STRING,
+ "sensitive", COMBO_SENSITIVE,
+ NULL);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (opt), active);
+
+ g_object_unref (store);
+
+ return opt;
+}
+
+static void
+string_value_combo_selected (GtkWidget *widget,
+ gpointer data)
+{
+ gint value;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &value, -1);
+
+ *((int *)data) = value;
+}
+
+static void
+string_value_combo_set_item_sensitive (GtkWidget *widget,
+ gint value,
+ gboolean sensitive)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gint val;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_tree_model_get_iter_first (model, &iter);
+ do
+ {
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
+ if (val == value)
+ {
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ COMBO_SENSITIVE, sensitive, -1);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+string_value_combo_set_active (GtkWidget *widget,
+ gint value)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ int val;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_tree_model_get_iter_first (model, &iter);
+ do
+ {
+ gtk_tree_model_get (model, &iter, COMBO_VALUE, &val, -1);
+ if (val == value)
+ {
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ runme = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+compression_selected (GtkWidget *widget,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE,
+ &dds_write_vals.compression,
+ -1);
+
+ gtk_widget_set_sensitive (format_opt,
+ dds_write_vals.compression == DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (pm_chk,
+ dds_write_vals.compression != DDS_COMPRESS_NONE);
+}
+
+static void
+savetype_selected (GtkWidget *widget,
+ gpointer data)
+{
+ gint32 image_id = *((gint32 *)data);
+
+ dds_write_vals.savetype = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+
+ switch (dds_write_vals.savetype)
+ {
+ case DDS_SAVE_SELECTED_LAYER:
+ case DDS_SAVE_VISIBLE_LAYERS:
+ case DDS_SAVE_CUBEMAP:
+ case DDS_SAVE_ARRAY:
+ gtk_widget_set_sensitive (compress_opt, TRUE);
+ break;
+
+ case DDS_SAVE_VOLUMEMAP:
+ dds_write_vals.compression = DDS_COMPRESS_NONE;
+ gtk_combo_box_set_active (GTK_COMBO_BOX (compress_opt),
+ DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (compress_opt, FALSE);
+ break;
+ }
+
+ string_value_combo_set_item_sensitive (mipmap_opt, DDS_MIPMAP_EXISTING,
+ check_mipmaps (image_id, dds_write_vals.savetype));
+}
+
+static void
+mipmaps_selected (GtkWidget *widget,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter);
+ gtk_tree_model_get (model, &iter, COMBO_VALUE,
+ &dds_write_vals.mipmaps, -1);
+
+ gtk_widget_set_sensitive (mipmap_filter_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (mipmap_wrap_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (gamma_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (srgb_chk,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct &&
+ !dds_write_vals.srgb);
+ gtk_widget_set_sensitive (alpha_coverage_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.preserve_alpha_coverage);
+}
+
+static void
+toggle_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ gint *flag = (int *)data;
+
+ (*flag) = !(*flag);
+}
+
+static void
+transindex_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *spin = GTK_WIDGET (g_object_get_data (G_OBJECT (widget), "spin"));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ dds_write_vals.transindex = 0;
+ gtk_widget_set_sensitive (spin, TRUE);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 0);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (spin, FALSE);
+ dds_write_vals.transindex = -1;
+ }
+}
+
+static void
+transindex_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.transindex =
+ gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (widget));
+}
+
+static void
+gamma_correct_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.gamma_correct =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (srgb_chk,
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ dds_write_vals.gamma_correct &&
+ ! dds_write_vals.srgb);
+}
+
+static void
+srgb_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.srgb =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (gamma_spin, !dds_write_vals.srgb);
+}
+
+static void
+gamma_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.gamma =
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+}
+
+static void
+alpha_coverage_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.preserve_alpha_coverage =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ dds_write_vals.preserve_alpha_coverage);
+}
+
+static void
+alpha_test_threshold_changed (GtkWidget *widget,
+ gpointer data)
+{
+ dds_write_vals.alpha_test_threshold =
+ gtk_spin_button_get_value (GTK_SPIN_BUTTON (widget));
+}
+
+static gint
+save_dialog (gint32 image_id,
+ gint32 drawable_id)
+{
+ GtkWidget *dlg;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *opt;
+ GtkWidget *check;
+ GtkWidget *spin;
+ GtkWidget *frame;
+ GimpImageBaseType basetype;
+
+ if (is_cubemap || is_volume || is_array)
+ dds_write_vals.savetype = DDS_SAVE_SELECTED_LAYER;
+
+ basetype = gimp_image_base_type (image_id);
+
+ dlg = gimp_dialog_new (_("Export as DDS"), "dds", NULL, GTK_WIN_POS_MOUSE,
+ gimp_standard_help_func, SAVE_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Export"), GTK_RESPONSE_OK,
+ NULL);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (save_dialog_response),
+ NULL);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dlg), FALSE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (6, 2, 0);
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+
+ opt = string_value_combo_new (compression_strings,
+ dds_write_vals.compression);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Compression:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (compression_selected),
+ NULL);
+
+ compress_opt = opt;
+
+ check = gtk_check_button_new_with_mnemonic (_("Use _perceptual error metric"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.perceptual_metric);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 1, 2,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked),
+ &dds_write_vals.perceptual_metric);
+
+ pm_chk = check;
+
+ opt = string_value_combo_new (format_strings, dds_write_vals.format);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Format:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.format);
+
+ gtk_widget_set_sensitive (opt, dds_write_vals.compression == DDS_COMPRESS_NONE);
+
+ format_opt = opt;
+
+ opt = string_value_combo_new (save_type_strings, dds_write_vals.savetype);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Save:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (savetype_selected),
+ &image_id);
+
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_CUBEMAP, is_cubemap);
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_VOLUMEMAP, is_volume);
+ string_value_combo_set_item_sensitive (opt, DDS_SAVE_ARRAY, is_array);
+
+ check = gtk_check_button_new_with_mnemonic (_("Flip the image _vertically on export"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.flip_image);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 4, 5,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (toggle_clicked),
+ &dds_write_vals.flip_image);
+
+ opt = string_value_combo_new (mipmap_strings, dds_write_vals.mipmaps);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 5,
+ _("_Mipmaps:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (mipmaps_selected),
+ &image_id);
+
+ string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
+ check_mipmaps (image_id,
+ dds_write_vals.savetype));
+
+ mipmap_opt = opt;
+
+ string_value_combo_set_item_sensitive (opt, DDS_MIPMAP_EXISTING,
+ ! (is_volume || is_cubemap) &&
+ is_mipmap_chain_valid);
+
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ check = gtk_check_button_new_with_label (_("Transparent index:"));
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (transindex_clicked),
+ NULL);
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 255, 1, 1, 0)), 1, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), spin, TRUE, TRUE, 0);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin),
+ GTK_UPDATE_IF_VALID);
+ gtk_widget_show (spin);
+
+ g_signal_connect (spin, "value-changed",
+ G_CALLBACK (transindex_changed),
+ NULL);
+
+ g_object_set_data (G_OBJECT (check), "spin", spin);
+
+ if (basetype != GIMP_INDEXED)
+ {
+ gtk_widget_set_sensitive (check, FALSE);
+ gtk_widget_set_sensitive (spin, FALSE);
+ }
+ else if (dds_write_vals.transindex < 0)
+ {
+ gtk_widget_set_sensitive (spin, FALSE);
+ }
+ else if (dds_write_vals.transindex >= 0)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), TRUE);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin),
+ dds_write_vals.transindex);
+ }
+
+ if (is_volume && dds_write_vals.savetype == DDS_SAVE_VOLUMEMAP)
+ {
+ dds_write_vals.compression = DDS_COMPRESS_NONE;
+ string_value_combo_set_active (compress_opt, DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (compress_opt, FALSE);
+ }
+
+ frame = gimp_frame_new (_("Mipmap Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (7, 2, 0);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 8);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ opt = string_value_combo_new (mipmap_filter_strings,
+ dds_write_vals.mipmap_filter);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("F_ilter:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.mipmap_filter);
+
+ mipmap_filter_opt = opt;
+
+ opt = string_value_combo_new (mipmap_wrap_strings,
+ dds_write_vals.mipmap_wrap);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Wrap mode:"),
+ 0.0, 0.5,
+ opt, 1, FALSE);
+
+ g_signal_connect (opt, "changed",
+ G_CALLBACK (string_value_combo_selected),
+ &dds_write_vals.mipmap_wrap);
+
+ mipmap_wrap_opt = opt;
+
+ check = gtk_check_button_new_with_mnemonic (_("Appl_y gamma correction"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.gamma_correct &&
+ dds_write_vals.mipmaps);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 2, 3,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (gamma_correct_clicked),
+ NULL);
+
+ gamma_chk = check;
+
+ check = gtk_check_button_new_with_mnemonic (_("Use s_RGB colorspace"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.gamma_correct &&
+ dds_write_vals.srgb);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 3, 4,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (srgb_clicked),
+ NULL);
+
+ srgb_chk = check;
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.gamma,
+ 1e-05, 100, 0.1, 0.5, 0)), 1, 1);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
+ _("_Gamma:"),
+ 0.0, 0.5,
+ spin, 1, FALSE);
+
+ g_signal_connect (spin, "value_changed",
+ G_CALLBACK (gamma_changed),
+ NULL);
+
+ gamma_spin = spin;
+
+ check = gtk_check_button_new_with_mnemonic (_("Preserve alpha _test coverage"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
+ dds_write_vals.preserve_alpha_coverage &&
+ dds_write_vals.mipmaps);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 2, 5, 6,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (check);
+
+ g_signal_connect (check, "clicked",
+ G_CALLBACK (alpha_coverage_clicked),
+ NULL);
+
+ alpha_coverage_chk = check;
+
+ spin = gimp_spin_button_new
+ (GTK_ADJUSTMENT (gtk_adjustment_new (dds_write_vals.alpha_test_threshold,
+ 0, 1, 0.01, 0.1, 0)), 1, 2);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin), GTK_UPDATE_IF_VALID);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
+ _("_Alpha test threshold:"),
+ 0.0, 0.5,
+ spin, 1, FALSE);
+
+ g_signal_connect (spin, "value_changed",
+ G_CALLBACK (alpha_test_threshold_changed),
+ NULL);
+
+ alpha_test_threshold_spin = spin;
+
+ gtk_widget_set_sensitive (mipmap_filter_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (mipmap_wrap_opt,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (gamma_chk
+ , dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (srgb_chk,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct);
+ gtk_widget_set_sensitive (gamma_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.gamma_correct &&
+ !dds_write_vals.srgb);
+ gtk_widget_set_sensitive (pm_chk,
+ dds_write_vals.compression != DDS_COMPRESS_NONE);
+ gtk_widget_set_sensitive (alpha_coverage_chk,
+ dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE);
+ gtk_widget_set_sensitive (alpha_test_threshold_spin,
+ (dds_write_vals.mipmaps == DDS_MIPMAP_GENERATE) &&
+ dds_write_vals.preserve_alpha_coverage);
+
+ gtk_widget_show (dlg);
+
+ runme = FALSE;
+
+ gtk_main ();
+
+ return runme;
+}
diff --git a/plug-ins/file-dds/dxt.c b/plug-ins/file-dds/dxt.c
new file mode 100644
index 0000000..28d407a
--- /dev/null
+++ b/plug-ins/file-dds/dxt.c
@@ -0,0 +1,1526 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * Parts of this code have been generously released in the public domain
+ * by Fabian 'ryg' Giesen. The original code can be found (at the time
+ * of writing) here: http://mollyrocket.com/forums/viewtopic.php?t=392
+ *
+ * For more information about this code, see the README.dxt file that
+ * came with the source.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+
+#include "dds.h"
+#include "dxt.h"
+#include "endian_rw.h"
+#include "mipmap.h"
+#include "imath.h"
+#include "vec.h"
+
+#include "dxt_tables.h"
+
+#define SWAP(a, b) do { typeof(a) t; t = a; a = b; b = t; } while(0)
+
+/* SIMD constants */
+static const vec4_t V4ZERO = VEC4_CONST1(0.0f);
+static const vec4_t V4ONE = VEC4_CONST1(1.0f);
+static const vec4_t V4HALF = VEC4_CONST1(0.5f);
+static const vec4_t V4ONETHIRD = VEC4_CONST3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f);
+static const vec4_t V4TWOTHIRDS = VEC4_CONST3(2.0f / 3.0f, 2.0f / 3.0f, 2.0f / 3.0f);
+static const vec4_t V4GRID = VEC4_CONST3(31.0f, 63.0f, 31.0f);
+static const vec4_t V4GRIDRCP = VEC4_CONST3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f);
+static const vec4_t V4EPSILON = VEC4_CONST1(1e-04f);
+
+typedef struct
+{
+ unsigned int single;
+ unsigned int alphamask;
+ vec4_t points[16];
+ vec4_t palette[4];
+ vec4_t max;
+ vec4_t min;
+ vec4_t metric;
+} dxtblock_t;
+
+/* extract 4x4 BGRA block */
+static void
+extract_block (const unsigned char *src,
+ int x,
+ int y,
+ int w,
+ int h,
+ unsigned char *block)
+{
+ int i, j;
+ int bw = MIN(w - x, 4);
+ int bh = MIN(h - y, 4);
+ int bx, by;
+ const int rem[] =
+ {
+ 0, 0, 0, 0,
+ 0, 1, 0, 1,
+ 0, 1, 2, 0,
+ 0, 1, 2, 3
+ };
+
+ for (i = 0; i < 4; ++i)
+ {
+ by = rem[(bh - 1) * 4 + i] + y;
+ for (j = 0; j < 4; ++j)
+ {
+ bx = rem[(bw - 1) * 4 + j] + x;
+ block[(i * 4 * 4) + (j * 4) + 0] =
+ src[(by * (w * 4)) + (bx * 4) + 0];
+ block[(i * 4 * 4) + (j * 4) + 1] =
+ src[(by * (w * 4)) + (bx * 4) + 1];
+ block[(i * 4 * 4) + (j * 4) + 2] =
+ src[(by * (w * 4)) + (bx * 4) + 2];
+ block[(i * 4 * 4) + (j * 4) + 3] =
+ src[(by * (w * 4)) + (bx * 4) + 3];
+ }
+ }
+}
+
+/* pack BGR8 to RGB565 */
+static inline unsigned short
+pack_rgb565 (const unsigned char *c)
+{
+ return (mul8bit(c[2], 31) << 11) |
+ (mul8bit(c[1], 63) << 5) |
+ (mul8bit(c[0], 31) );
+}
+
+/* unpack RGB565 to BGR */
+static void
+unpack_rgb565 (unsigned char *dst,
+ unsigned short v)
+{
+ int r = (v >> 11) & 0x1f;
+ int g = (v >> 5) & 0x3f;
+ int b = (v ) & 0x1f;
+
+ dst[0] = (b << 3) | (b >> 2);
+ dst[1] = (g << 2) | (g >> 4);
+ dst[2] = (r << 3) | (r >> 2);
+}
+
+/* linear interpolation at 1/3 point between a and b */
+static void
+lerp_rgb13 (unsigned char *dst,
+ unsigned char *a,
+ unsigned char *b)
+{
+#if 0
+ dst[0] = blerp(a[0], b[0], 0x55);
+ dst[1] = blerp(a[1], b[1], 0x55);
+ dst[2] = blerp(a[2], b[2], 0x55);
+#else
+ /*
+ * according to the S3TC/DX10 specs, this is the correct way to do the
+ * interpolation (with no rounding bias)
+ *
+ * dst = (2 * a + b) / 3;
+ */
+ dst[0] = (2 * a[0] + b[0]) / 3;
+ dst[1] = (2 * a[1] + b[1]) / 3;
+ dst[2] = (2 * a[2] + b[2]) / 3;
+#endif
+}
+
+static void
+vec4_endpoints_to_565 (int *start,
+ int *end,
+ const vec4_t a,
+ const vec4_t b)
+{
+ int c[8] __attribute__((aligned(16)));
+ vec4_t ta = a * V4GRID + V4HALF;
+ vec4_t tb = b * V4GRID + V4HALF;
+
+#ifdef USE_SSE
+# ifdef __SSE2__
+ const __m128i C565 = _mm_setr_epi16(31, 63, 31, 0, 31, 63, 31, 0);
+ __m128i ia = _mm_cvttps_epi32(ta);
+ __m128i ib = _mm_cvttps_epi32(tb);
+ __m128i zero = _mm_setzero_si128();
+ __m128i words = _mm_packs_epi32(ia, ib);
+ words = _mm_min_epi16(C565, _mm_max_epi16(zero, words));
+ *((__m128i *)&c[0]) = _mm_unpacklo_epi16(words, zero);
+ *((__m128i *)&c[4]) = _mm_unpackhi_epi16(words, zero);
+# else
+ const __m64 C565 = _mm_setr_pi16(31, 63, 31, 0);
+ __m64 lo, hi, c0, c1;
+ __m64 zero = _mm_setzero_si64();
+ lo = _mm_cvttps_pi32(ta);
+ hi = _mm_cvttps_pi32(_mm_movehl_ps(ta, ta));
+ c0 = _mm_packs_pi32(lo, hi);
+ lo = _mm_cvttps_pi32(tb);
+ hi = _mm_cvttps_pi32(_mm_movehl_ps(tb, tb));
+ c1 = _mm_packs_pi32(lo, hi);
+ c0 = _mm_min_pi16(C565, _mm_max_pi16(zero, c0));
+ c1 = _mm_min_pi16(C565, _mm_max_pi16(zero, c1));
+ *((__m64 *)&c[0]) = _mm_unpacklo_pi16(c0, zero);
+ *((__m64 *)&c[2]) = _mm_unpackhi_pi16(c0, zero);
+ *((__m64 *)&c[4]) = _mm_unpacklo_pi16(c1, zero);
+ *((__m64 *)&c[6]) = _mm_unpackhi_pi16(c1, zero);
+ _mm_empty();
+# endif
+#else
+ c[0] = (int)ta[0]; c[4] = (int)tb[0];
+ c[1] = (int)ta[1]; c[5] = (int)tb[1];
+ c[2] = (int)ta[2]; c[6] = (int)tb[2];
+ c[0] = MIN(31, MAX(0, c[0]));
+ c[1] = MIN(63, MAX(0, c[1]));
+ c[2] = MIN(31, MAX(0, c[2]));
+ c[4] = MIN(31, MAX(0, c[4]));
+ c[5] = MIN(63, MAX(0, c[5]));
+ c[6] = MIN(31, MAX(0, c[6]));
+#endif
+
+ *start = ((c[2] << 11) | (c[1] << 5) | c[0]);
+ *end = ((c[6] << 11) | (c[5] << 5) | c[4]);
+}
+
+static void
+dxtblock_init (dxtblock_t *dxtb,
+ const unsigned char *block,
+ int flags)
+{
+ int i, c0, c;
+ int bc1 = (flags & DXT_BC1);
+ float x, y, z;
+ vec4_t min, max, center, t, cov, inset;
+
+ dxtb->single = 1;
+ dxtb->alphamask = 0;
+
+ if(flags & DXT_PERCEPTUAL)
+ /* ITU-R BT.709 luma coefficients */
+ dxtb->metric = vec4_set(0.2126f, 0.7152f, 0.0722f, 0.0f);
+ else
+ dxtb->metric = vec4_set(1.0f, 1.0f, 1.0f, 0.0f);
+
+ c0 = GETL24(block);
+
+ for (i = 0; i < 16; ++i)
+ {
+ if (bc1 && (block[4 * i + 3] < 128))
+ dxtb->alphamask |= (3 << (2 * i));
+
+ x = (float)block[4 * i + 0] / 255.0f;
+ y = (float)block[4 * i + 1] / 255.0f;
+ z = (float)block[4 * i + 2] / 255.0f;
+
+ dxtb->points[i] = vec4_set(x, y, z, 0);
+
+ c = GETL24(&block[4 * i]);
+ dxtb->single = dxtb->single && (c == c0);
+ }
+
+ // no need to continue if this is a single color block
+ if (dxtb->single)
+ return;
+
+ min = vec4_set1(1.0f);
+ max = vec4_zero();
+
+ // get bounding box extents
+ for (i = 0; i < 16; ++i)
+ {
+ min = vec4_min(min, dxtb->points[i]);
+ max = vec4_max(max, dxtb->points[i]);
+ }
+
+ // select diagonal
+ center = (max + min) * V4HALF;
+ cov = vec4_zero();
+ for (i = 0; i < 16; ++i)
+ {
+ t = dxtb->points[i] - center;
+ cov += t * vec4_splatz(t);
+ }
+
+#ifdef USE_SSE
+ {
+ __m128 mask, tmp;
+ // get mask
+ mask = _mm_cmplt_ps(cov, _mm_setzero_ps());
+ // clear high bits (z, w)
+ mask = _mm_movelh_ps(mask, _mm_setzero_ps());
+ // mask and combine
+ tmp = _mm_or_ps(_mm_and_ps(mask, min), _mm_andnot_ps(mask, max));
+ min = _mm_or_ps(_mm_and_ps(mask, max), _mm_andnot_ps(mask, min));
+ max = tmp;
+ }
+#else
+ {
+ float x0, x1, y0, y1;
+ x0 = max[0];
+ y0 = max[1];
+ x1 = min[0];
+ y1 = min[1];
+
+ if (cov[0] < 0) SWAP(x0, x1);
+ if (cov[1] < 0) SWAP(y0, y1);
+
+ max[0] = x0;
+ max[1] = y0;
+ min[0] = x1;
+ min[1] = y1;
+ }
+#endif
+
+ // inset bounding box and clamp to [0,1]
+ inset = (max - min) * vec4_set1(1.0f / 16.0f) - vec4_set1((8.0f / 255.0f) / 16.0f);
+ max = vec4_min(V4ONE, vec4_max(V4ZERO, max - inset));
+ min = vec4_min(V4ONE, vec4_max(V4ZERO, min + inset));
+
+ // clamp to color space and save
+ dxtb->max = vec4_trunc(V4GRID * max + V4HALF) * V4GRIDRCP;
+ dxtb->min = vec4_trunc(V4GRID * min + V4HALF) * V4GRIDRCP;
+}
+
+static void
+construct_palette3 (dxtblock_t *dxtb)
+{
+ dxtb->palette[0] = dxtb->max;
+ dxtb->palette[1] = dxtb->min;
+ dxtb->palette[2] = (dxtb->max * V4HALF) + (dxtb->min * V4HALF);
+ dxtb->palette[3] = vec4_zero();
+}
+
+static void
+construct_palette4 (dxtblock_t *dxtb)
+{
+ dxtb->palette[0] = dxtb->max;
+ dxtb->palette[1] = dxtb->min;
+ dxtb->palette[2] = (dxtb->max * V4TWOTHIRDS) + (dxtb->min * V4ONETHIRD );
+ dxtb->palette[3] = (dxtb->max * V4ONETHIRD ) + (dxtb->min * V4TWOTHIRDS);
+}
+
+/*
+ * from nvidia-texture-tools; see LICENSE.nvtt for copyright information
+ */
+static void
+optimize_endpoints3 (dxtblock_t *dxtb,
+ unsigned int indices,
+ vec4_t *max,
+ vec4_t *min)
+{
+ float alpha, beta;
+ vec4_t alpha2_sum, alphax_sum;
+ vec4_t beta2_sum, betax_sum;
+ vec4_t alphabeta_sum, a, b, factor;
+ int i, bits;
+
+ alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero();
+ alphax_sum = vec4_zero();
+ betax_sum = vec4_zero();
+
+ for (i = 0; i < 16; ++i)
+ {
+ bits = indices >> (2 * i);
+
+ // skip alpha pixels
+ if ((bits & 3) == 3)
+ continue;
+
+ beta = (float)(bits & 1);
+ if (bits & 2)
+ beta = 0.5f;
+ alpha = 1.0f - beta;
+
+ a = vec4_set1(alpha);
+ b = vec4_set1(beta);
+ alpha2_sum += a * a;
+ beta2_sum += b * b;
+ alphabeta_sum += a * b;
+ alphax_sum += dxtb->points[i] * a;
+ betax_sum += dxtb->points[i] * b;
+ }
+
+ factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
+ if (vec4_cmplt(factor, V4EPSILON))
+ return;
+ factor = vec4_rcp(factor);
+
+ a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;
+ b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;
+
+ // clamp to the color space
+ a = vec4_min(V4ONE, vec4_max(V4ZERO, a));
+ b = vec4_min(V4ONE, vec4_max(V4ZERO, b));
+ a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP;
+ b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP;
+
+ *max = a;
+ *min = b;
+}
+
+/*
+ * from nvidia-texture-tools; see LICENSE.nvtt for copyright information
+ */
+static void
+optimize_endpoints4 (dxtblock_t *dxtb,
+ unsigned int indices,
+ vec4_t *max,
+ vec4_t *min)
+{
+ float alpha, beta;
+ vec4_t alpha2_sum, alphax_sum;
+ vec4_t beta2_sum, betax_sum;
+ vec4_t alphabeta_sum, a, b, factor;
+ int i, bits;
+
+ alpha2_sum = beta2_sum = alphabeta_sum = vec4_zero();
+ alphax_sum = vec4_zero();
+ betax_sum = vec4_zero();
+
+ for (i = 0; i < 16; ++i)
+ {
+ bits = indices >> (2 * i);
+
+ beta = (float)(bits & 1);
+ if (bits & 2)
+ beta = (1.0f + beta) / 3.0f;
+ alpha = 1.0f - beta;
+
+ a = vec4_set1(alpha);
+ b = vec4_set1(beta);
+ alpha2_sum += a * a;
+ beta2_sum += b * b;
+ alphabeta_sum += a * b;
+ alphax_sum += dxtb->points[i] * a;
+ betax_sum += dxtb->points[i] * b;
+ }
+
+ factor = alpha2_sum * beta2_sum - alphabeta_sum * alphabeta_sum;
+ if (vec4_cmplt(factor, V4EPSILON))
+ return;
+ factor = vec4_rcp(factor);
+
+ a = (alphax_sum * beta2_sum - betax_sum * alphabeta_sum) * factor;
+ b = (betax_sum * alpha2_sum - alphax_sum * alphabeta_sum) * factor;
+
+ // clamp to the color space
+ a = vec4_min(V4ONE, vec4_max(V4ZERO, a));
+ b = vec4_min(V4ONE, vec4_max(V4ZERO, b));
+ a = vec4_trunc(V4GRID * a + V4HALF) * V4GRIDRCP;
+ b = vec4_trunc(V4GRID * b + V4HALF) * V4GRIDRCP;
+
+ *max = a;
+ *min = b;
+}
+
+static unsigned int
+match_colors3 (dxtblock_t *dxtb)
+{
+ int i, idx;
+ unsigned int indices = 0;
+ vec4_t t0, t1, t2;
+#ifdef USE_SSE
+ vec4_t d, bits, zero = _mm_setzero_ps();
+ int mask;
+#else
+ float d0, d1, d2;
+#endif
+
+ // match each point to the closest color
+ for (i = 0; i < 16; ++i)
+ {
+ // skip alpha pixels
+ if (((dxtb->alphamask >> (2 * i)) & 3) == 3)
+ {
+ indices |= (3 << (2 * i));
+ continue;
+ }
+
+ t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric;
+ t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric;
+ t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric;
+
+#ifdef USE_SSE
+ _MM_TRANSPOSE4_PS(t0, t1, t2, zero);
+ d = t0 * t0 + t1 * t1 + t2 * t2;
+ bits = _mm_cmplt_ps(_mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 1, 0, 0)),
+ _mm_shuffle_ps(d, d, _MM_SHUFFLE(3, 2, 2, 1)));
+ mask = _mm_movemask_ps(bits);
+ if((mask & 3) == 3) idx = 0;
+ else if(mask & 4) idx = 1;
+ else idx = 2;
+#else
+ d0 = vec4_dot(t0, t0);
+ d1 = vec4_dot(t1, t1);
+ d2 = vec4_dot(t2, t2);
+
+ if ((d0 < d1) && (d0 < d2))
+ idx = 0;
+ else if (d1 < d2)
+ idx = 1;
+ else
+ idx = 2;
+#endif
+
+ indices |= (idx << (2 * i));
+ }
+
+ return indices;
+}
+
+static unsigned int
+match_colors4 (dxtblock_t *dxtb)
+{
+ int i;
+ unsigned int idx, indices = 0;
+ unsigned int b0, b1, b2, b3, b4;
+ unsigned int x0, x1, x2;
+ vec4_t t0, t1, t2, t3;
+#ifdef USE_SSE
+ vec4_t d;
+#else
+ float d[4];
+#endif
+
+ // match each point to the closest color
+ for (i = 0; i < 16; ++i)
+ {
+ t0 = (dxtb->points[i] - dxtb->palette[0]) * dxtb->metric;
+ t1 = (dxtb->points[i] - dxtb->palette[1]) * dxtb->metric;
+ t2 = (dxtb->points[i] - dxtb->palette[2]) * dxtb->metric;
+ t3 = (dxtb->points[i] - dxtb->palette[3]) * dxtb->metric;
+
+#ifdef USE_SSE
+ _MM_TRANSPOSE4_PS(t0, t1, t2, t3);
+ d = t0 * t0 + t1 * t1 + t2 * t2;
+#else
+ d[0] = vec4_dot(t0, t0);
+ d[1] = vec4_dot(t1, t1);
+ d[2] = vec4_dot(t2, t2);
+ d[3] = vec4_dot(t3, t3);
+#endif
+
+ b0 = d[0] > d[3];
+ b1 = d[1] > d[2];
+ b2 = d[0] > d[2];
+ b3 = d[1] > d[3];
+ b4 = d[2] > d[3];
+
+ x0 = b1 & b2;
+ x1 = b0 & b3;
+ x2 = b0 & b4;
+
+ idx = x2 | ((x0 | x1) << 1);
+
+ indices |= (idx << (2 * i));
+ }
+
+ return indices;
+}
+
+static float
+compute_error3 (dxtblock_t *dxtb,
+ unsigned int indices)
+{
+ int i, idx;
+ float error = 0;
+ vec4_t t;
+
+ // compute error
+ for (i = 0; i < 16; ++i)
+ {
+ idx = (indices >> (2 * i)) & 3;
+ // skip alpha pixels
+ if(idx == 3)
+ continue;
+ t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric;
+ error += vec4_dot(t, t);
+ }
+
+ return error;
+}
+
+static float
+compute_error4 (dxtblock_t *dxtb,
+ unsigned int indices)
+{
+ int i, idx;
+ float error = 0;
+
+#ifdef USE_SSE
+ vec4_t a0, a1, a2, a3;
+ vec4_t b0, b1, b2, b3;
+ vec4_t d;
+
+ for (i = 0; i < 4; ++i)
+ {
+ idx = indices >> (8 * i);
+ a0 = dxtb->points[4 * i + 0];
+ a1 = dxtb->points[4 * i + 1];
+ a2 = dxtb->points[4 * i + 2];
+ a3 = dxtb->points[4 * i + 3];
+ b0 = dxtb->palette[(idx ) & 3];
+ b1 = dxtb->palette[(idx >> 2) & 3];
+ b2 = dxtb->palette[(idx >> 4) & 3];
+ b3 = dxtb->palette[(idx >> 6) & 3];
+ a0 = (a0 - b0) * dxtb->metric;
+ a1 = (a1 - b1) * dxtb->metric;
+ a2 = (a2 - b2) * dxtb->metric;
+ a3 = (a3 - b3) * dxtb->metric;
+ _MM_TRANSPOSE4_PS(a0, a1, a2, a3);
+ d = a0 * a0 + a1 * a1 + a2 * a2;
+ error += vec4_accum(d);
+ }
+#else
+ vec4_t t;
+
+ // compute error
+ for (i = 0; i < 16; ++i)
+ {
+ idx = (indices >> (2 * i)) & 3;
+ t = (dxtb->points[i] - dxtb->palette[idx]) * dxtb->metric;
+ error += vec4_dot(t, t);
+ }
+#endif
+
+ return error;
+}
+
+static unsigned int
+compress3 (dxtblock_t *dxtb)
+{
+ const int MAX_ITERATIONS = 8;
+ int i;
+ unsigned int indices, bestindices;
+ float error, besterror = FLT_MAX;
+ vec4_t oldmax, oldmin;
+
+ construct_palette3(dxtb);
+
+ indices = match_colors3(dxtb);
+ bestindices = indices;
+
+ for (i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ oldmax = dxtb->max;
+ oldmin = dxtb->min;
+
+ optimize_endpoints3(dxtb, indices, &dxtb->max, &dxtb->min);
+ construct_palette3(dxtb);
+ indices = match_colors3(dxtb);
+ error = compute_error3(dxtb, indices);
+
+ if (error < besterror)
+ {
+ besterror = error;
+ bestindices = indices;
+ }
+ else
+ {
+ dxtb->max = oldmax;
+ dxtb->min = oldmin;
+ break;
+ }
+ }
+
+ return bestindices;
+}
+
+static unsigned int
+compress4 (dxtblock_t *dxtb)
+{
+ const int MAX_ITERATIONS = 8;
+ int i;
+ unsigned int indices, bestindices;
+ float error, besterror = FLT_MAX;
+ vec4_t oldmax, oldmin;
+
+ construct_palette4(dxtb);
+
+ indices = match_colors4(dxtb);
+ bestindices = indices;
+
+ for (i = 0; i < MAX_ITERATIONS; ++i)
+ {
+ oldmax = dxtb->max;
+ oldmin = dxtb->min;
+
+ optimize_endpoints4(dxtb, indices, &dxtb->max, &dxtb->min);
+ construct_palette4(dxtb);
+ indices = match_colors4(dxtb);
+ error = compute_error4(dxtb, indices);
+
+ if (error < besterror)
+ {
+ besterror = error;
+ bestindices = indices;
+ }
+ else
+ {
+ dxtb->max = oldmax;
+ dxtb->min = oldmin;
+ break;
+ }
+ }
+
+ return bestindices;
+}
+
+static void
+encode_color_block (unsigned char *dst,
+ unsigned char *block,
+ int flags)
+{
+ dxtblock_t dxtb;
+ int max16, min16;
+ unsigned int indices, mask;
+
+ dxtblock_init(&dxtb, block, flags);
+
+ if (dxtb.single) // single color block
+ {
+ max16 = (omatch5[block[2]][0] << 11) |
+ (omatch6[block[1]][0] << 5) |
+ (omatch5[block[0]][0] );
+ min16 = (omatch5[block[2]][1] << 11) |
+ (omatch6[block[1]][1] << 5) |
+ (omatch5[block[0]][1] );
+
+ indices = 0xaaaaaaaa; // 101010...
+
+ if ((flags & DXT_BC1) && dxtb.alphamask)
+ {
+ // DXT1 compression, non-opaque block. Add alpha indices.
+ indices |= dxtb.alphamask;
+ if (max16 > min16)
+ SWAP(max16, min16);
+ }
+ else if (max16 < min16)
+ {
+ SWAP(max16, min16);
+ indices ^= 0x55555555; // 010101...
+ }
+ }
+ else if ((flags & DXT_BC1) && dxtb.alphamask) // DXT1 compression, non-opaque block
+ {
+ indices = compress3(&dxtb);
+
+ vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min);
+
+ if (max16 > min16)
+ {
+ SWAP(max16, min16);
+ // remap indices 0 -> 1, 1 -> 0
+ mask = indices & 0xaaaaaaaa;
+ mask = mask | (mask >> 1);
+ indices = (indices & mask) | ((indices ^ 0x55555555) & ~mask);
+ }
+ }
+ else
+ {
+ indices = compress4(&dxtb);
+
+ vec4_endpoints_to_565(&max16, &min16, dxtb.max, dxtb.min);
+
+ if (max16 < min16)
+ {
+ SWAP(max16, min16);
+ indices ^= 0x55555555; // 010101...
+ }
+ }
+
+ PUTL16(dst + 0, max16);
+ PUTL16(dst + 2, min16);
+ PUTL32(dst + 4, indices);
+}
+
+static void
+get_min_max_YCoCg (const unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ int i;
+
+ mincolor[2] = mincolor[1] = 255;
+ maxcolor[2] = maxcolor[1] = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ if (block[4 * i + 2] < mincolor[2]) mincolor[2] = block[4 * i + 2];
+ if (block[4 * i + 1] < mincolor[1]) mincolor[1] = block[4 * i + 1];
+ if (block[4 * i + 2] > maxcolor[2]) maxcolor[2] = block[4 * i + 2];
+ if (block[4 * i + 1] > maxcolor[1]) maxcolor[1] = block[4 * i + 1];
+ }
+}
+
+static void
+scale_YCoCg (unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ const int s0 = 128 / 2 - 1;
+ const int s1 = 128 / 4 - 1;
+ int m0, m1, m2, m3;
+ int mask0, mask1, scale;
+ int i;
+
+ m0 = abs(mincolor[2] - 128);
+ m1 = abs(mincolor[1] - 128);
+ m2 = abs(maxcolor[2] - 128);
+ m3 = abs(maxcolor[1] - 128);
+
+ if (m1 > m0) m0 = m1;
+ if (m3 > m2) m2 = m3;
+ if (m2 > m0) m0 = m2;
+
+ mask0 = -(m0 <= s0);
+ mask1 = -(m0 <= s1);
+ scale = 1 + (1 & mask0) + (2 & mask1);
+
+ mincolor[2] = (mincolor[2] - 128) * scale + 128;
+ mincolor[1] = (mincolor[1] - 128) * scale + 128;
+ mincolor[0] = (scale - 1) << 3;
+
+ maxcolor[2] = (maxcolor[2] - 128) * scale + 128;
+ maxcolor[1] = (maxcolor[1] - 128) * scale + 128;
+ maxcolor[0] = (scale - 1) << 3;
+
+ for (i = 0; i < 16; ++i)
+ {
+ block[i * 4 + 2] = (block[i * 4 + 2] - 128) * scale + 128;
+ block[i * 4 + 1] = (block[i * 4 + 1] - 128) * scale + 128;
+ }
+}
+
+#define INSET_SHIFT 4
+
+static void
+inset_bbox_YCoCg (unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ int inset[4], mini[4], maxi[4];
+
+ inset[2] = (maxcolor[2] - mincolor[2]) - ((1 << (INSET_SHIFT - 1)) - 1);
+ inset[1] = (maxcolor[1] - mincolor[1]) - ((1 << (INSET_SHIFT - 1)) - 1);
+
+ mini[2] = ((mincolor[2] << INSET_SHIFT) + inset[2]) >> INSET_SHIFT;
+ mini[1] = ((mincolor[1] << INSET_SHIFT) + inset[1]) >> INSET_SHIFT;
+
+ maxi[2] = ((maxcolor[2] << INSET_SHIFT) - inset[2]) >> INSET_SHIFT;
+ maxi[1] = ((maxcolor[1] << INSET_SHIFT) - inset[1]) >> INSET_SHIFT;
+
+ mini[2] = (mini[2] >= 0) ? mini[2] : 0;
+ mini[1] = (mini[1] >= 0) ? mini[1] : 0;
+
+ maxi[2] = (maxi[2] <= 255) ? maxi[2] : 255;
+ maxi[1] = (maxi[1] <= 255) ? maxi[1] : 255;
+
+ mincolor[2] = (mini[2] & 0xf8) | (mini[2] >> 5);
+ mincolor[1] = (mini[1] & 0xfc) | (mini[1] >> 6);
+
+ maxcolor[2] = (maxi[2] & 0xf8) | (maxi[2] >> 5);
+ maxcolor[1] = (maxi[1] & 0xfc) | (maxi[1] >> 6);
+}
+
+static void
+select_diagonal_YCoCg (const unsigned char *block,
+ unsigned char *mincolor,
+ unsigned char *maxcolor)
+{
+ unsigned char mid0, mid1, side, mask, b0, b1, c0, c1;
+ int i;
+
+ mid0 = ((int)mincolor[2] + maxcolor[2] + 1) >> 1;
+ mid1 = ((int)mincolor[1] + maxcolor[1] + 1) >> 1;
+
+ side = 0;
+ for (i = 0; i < 16; ++i)
+ {
+ b0 = block[i * 4 + 2] >= mid0;
+ b1 = block[i * 4 + 1] >= mid1;
+ side += (b0 ^ b1);
+ }
+
+ mask = -(side > 8);
+ mask &= -(mincolor[2] != maxcolor[2]);
+
+ c0 = mincolor[1];
+ c1 = maxcolor[1];
+
+ c0 ^= c1;
+ c1 ^= c0 & mask;
+ c0 ^= c1;
+
+ mincolor[1] = c0;
+ maxcolor[1] = c1;
+}
+
+static void
+encode_YCoCg_block (unsigned char *dst,
+ unsigned char *block)
+{
+ unsigned char colors[4][3], *maxcolor, *mincolor;
+ unsigned int mask;
+ int c0, c1, d0, d1, d2, d3;
+ int b0, b1, b2, b3, b4;
+ int x0, x1, x2;
+ int i, idx;
+
+ maxcolor = &colors[0][0];
+ mincolor = &colors[1][0];
+
+ get_min_max_YCoCg(block, mincolor, maxcolor);
+ scale_YCoCg(block, mincolor, maxcolor);
+ inset_bbox_YCoCg(mincolor, maxcolor);
+ select_diagonal_YCoCg(block, mincolor, maxcolor);
+
+ lerp_rgb13(&colors[2][0], maxcolor, mincolor);
+ lerp_rgb13(&colors[3][0], mincolor, maxcolor);
+
+ mask = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ c0 = block[4 * i + 2];
+ c1 = block[4 * i + 1];
+
+ d0 = abs(colors[0][2] - c0) + abs(colors[0][1] - c1);
+ d1 = abs(colors[1][2] - c0) + abs(colors[1][1] - c1);
+ d2 = abs(colors[2][2] - c0) + abs(colors[2][1] - c1);
+ d3 = abs(colors[3][2] - c0) + abs(colors[3][1] - c1);
+
+ b0 = d0 > d3;
+ b1 = d1 > d2;
+ b2 = d0 > d2;
+ b3 = d1 > d3;
+ b4 = d2 > d3;
+
+ x0 = b1 & b2;
+ x1 = b0 & b3;
+ x2 = b0 & b4;
+
+ idx = (x2 | ((x0 | x1) << 1));
+
+ mask |= idx << (2 * i);
+ }
+
+ PUTL16(dst + 0, pack_rgb565(maxcolor));
+ PUTL16(dst + 2, pack_rgb565(mincolor));
+ PUTL32(dst + 4, mask);
+}
+
+/* write DXT3 alpha block */
+static void
+encode_alpha_block_BC2 (unsigned char *dst,
+ const unsigned char *block)
+{
+ int i, a1, a2;
+
+ block += 3;
+
+ for (i = 0; i < 8; ++i)
+ {
+ a1 = mul8bit(block[8 * i + 0], 0x0f);
+ a2 = mul8bit(block[8 * i + 4], 0x0f);
+ *dst++ = (a2 << 4) | a1;
+ }
+}
+
+/* Write DXT5 alpha block */
+static void
+encode_alpha_block_BC3 (unsigned char *dst,
+ const unsigned char *block,
+ const int offset)
+{
+ int i, v, mn, mx;
+ int dist, bias, dist2, dist4, bits, mask;
+ int a, idx, t;
+
+ block += offset;
+ block += 3;
+
+ /* find min/max alpha pair */
+ mn = mx = block[0];
+ for (i = 0; i < 16; ++i)
+ {
+ v = block[4 * i];
+ if(v > mx) mx = v;
+ if(v < mn) mn = v;
+ }
+
+ /* encode them */
+ *dst++ = mx;
+ *dst++ = mn;
+
+ /*
+ * determine bias and emit indices
+ * given the choice of mx/mn, these indices are optimal:
+ * http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
+ */
+ dist = mx - mn;
+ dist4 = dist * 4;
+ dist2 = dist * 2;
+ bias = (dist < 8) ? (dist - 1) : (dist / 2 + 2);
+ bias -= mn * 7;
+ bits = 0;
+ mask = 0;
+
+ for (i = 0; i < 16; ++i)
+ {
+ a = block[4 * i] * 7 + bias;
+
+ /* select index. this is a "linear scale" lerp factor between 0
+ (val=min) and 7 (val=max). */
+ t = (a >= dist4) ? -1 : 0; idx = t & 4; a -= dist4 & t;
+ t = (a >= dist2) ? -1 : 0; idx += t & 2; a -= dist2 & t;
+ idx += (a >= dist);
+
+ /* turn linear scale into DXT index (0/1 are extremal pts) */
+ idx = -idx & 7;
+ idx ^= (2 > idx);
+
+ /* write index */
+ mask |= idx << bits;
+ if ((bits += 3) >= 8)
+ {
+ *dst++ = mask;
+ mask >>= 8;
+ bits -= 8;
+ }
+ }
+}
+
+#define BLOCK_COUNT(w, h) ((((h) + 3) >> 2) * (((w) + 3) >> 2))
+#define BLOCK_OFFSET(x, y, w, bs) (((y) >> 2) * ((bs) * (((w) + 3) >> 2)) + ((bs) * ((x) >> 2)))
+
+static void
+compress_BC1 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 8);
+ extract_block(src, x, y, w, h, block);
+ encode_color_block(p, block, DXT_BC1 | flags);
+ }
+}
+
+static void
+compress_BC2 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC2(p, block);
+ encode_color_block(p + 8, block, DXT_BC2 | flags);
+ }
+}
+
+static void
+compress_BC3 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h,
+ int flags)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, 0);
+ encode_color_block(p + 8, block, DXT_BC3 | flags);
+ }
+}
+
+static void
+compress_BC4 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 8);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, -1);
+ }
+}
+
+static void
+compress_BC5 (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ /* Pixels are ordered as BGRA (see write_layer)
+ * First we encode red -1+3: channel 2;
+ * then we encode green -2+3: channel 1.
+ */
+ encode_alpha_block_BC3(p, block, -1);
+ encode_alpha_block_BC3(p + 8, block, -2);
+ }
+}
+
+static void
+compress_YCoCg (unsigned char *dst,
+ const unsigned char *src,
+ int w,
+ int h)
+{
+ const unsigned int block_count = BLOCK_COUNT(w, h);
+ unsigned int i;
+ unsigned char block[64], *p;
+ int x, y;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic, 256) private(block, p, x, y)
+#endif
+ for (i = 0; i < block_count; ++i)
+ {
+ x = (i % ((w + 3) >> 2)) << 2;
+ y = (i / ((w + 3) >> 2)) << 2;
+ p = dst + BLOCK_OFFSET(x, y, w, 16);
+ extract_block(src, x, y, w, h, block);
+ encode_alpha_block_BC3(p, block, 0);
+ encode_YCoCg_block(p + 8, block);
+ }
+}
+
+int
+dxt_compress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int mipmaps,
+ int flags)
+{
+ int i, size, w, h;
+ unsigned int offset;
+ unsigned char *tmp = NULL;
+ int j;
+ unsigned char *s;
+
+ if (bpp == 1)
+ {
+ /* grayscale promoted to BGRA */
+
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; ++i, j += 4)
+ {
+ tmp[j + 0] = src[i];
+ tmp[j + 1] = src[i];
+ tmp[j + 2] = src[i];
+ tmp[j + 3] = 255;
+ }
+
+ bpp = 4;
+ }
+ else if (bpp == 2)
+ {
+ /* gray-alpha promoted to BGRA */
+
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; i += 2, j += 4)
+ {
+ tmp[j + 0] = src[i];
+ tmp[j + 1] = src[i];
+ tmp[j + 2] = src[i];
+ tmp[j + 3] = src[i + 1];
+ }
+
+ bpp = 4;
+ }
+ else if (bpp == 3)
+ {
+ size = get_mipmapped_size(width, height, 4, 0, mipmaps,
+ DDS_COMPRESS_NONE);
+ tmp = g_malloc(size);
+
+ for (i = j = 0; j < size; i += 3, j += 4)
+ {
+ tmp[j + 0] = src[i + 0];
+ tmp[j + 1] = src[i + 1];
+ tmp[j + 2] = src[i + 2];
+ tmp[j + 3] = 255;
+ }
+
+ bpp = 4;
+ }
+
+ offset = 0;
+ w = width;
+ h = height;
+ s = tmp ? tmp : src;
+
+ for (i = 0; i < mipmaps; ++i)
+ {
+ switch (format)
+ {
+ case DDS_COMPRESS_BC1:
+ compress_BC1(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC2:
+ compress_BC2(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC3:
+ case DDS_COMPRESS_BC3N:
+ case DDS_COMPRESS_RXGB:
+ case DDS_COMPRESS_AEXP:
+ case DDS_COMPRESS_YCOCG:
+ compress_BC3(dst + offset, s, w, h, flags);
+ break;
+ case DDS_COMPRESS_BC4:
+ compress_BC4(dst + offset, s, w, h);
+ break;
+ case DDS_COMPRESS_BC5:
+ compress_BC5(dst + offset, s, w, h);
+ break;
+ case DDS_COMPRESS_YCOCGS:
+ compress_YCoCg(dst + offset, s, w, h);
+ break;
+ default:
+ compress_BC3(dst + offset, s, w, h, flags);
+ break;
+ }
+ s += (w * h * bpp);
+ offset += get_mipmapped_size(w, h, 0, 0, 1, format);
+ w = MAX(1, w >> 1);
+ h = MAX(1, h >> 1);
+ }
+
+ if (tmp)
+ g_free(tmp);
+
+ return 1;
+}
+
+static void
+decode_color_block (unsigned char *block,
+ unsigned char *src,
+ int format)
+{
+ int i, x, y;
+ unsigned char *d = block;
+ unsigned int indices, idx;
+ unsigned char colors[4][3];
+ unsigned short c0, c1;
+
+ c0 = GETL16(&src[0]);
+ c1 = GETL16(&src[2]);
+
+ unpack_rgb565(colors[0], c0);
+ unpack_rgb565(colors[1], c1);
+
+ if ((c0 > c1) || (format == DDS_COMPRESS_BC3))
+ {
+ lerp_rgb13(colors[2], colors[0], colors[1]);
+ lerp_rgb13(colors[3], colors[1], colors[0]);
+ }
+ else
+ {
+ for (i = 0; i < 3; ++i)
+ {
+ colors[2][i] = (colors[0][i] + colors[1][i] + 1) >> 1;
+ colors[3][i] = 255;
+ }
+ }
+
+ src += 4;
+ for (y = 0; y < 4; ++y)
+ {
+ indices = src[y];
+ for (x = 0; x < 4; ++x)
+ {
+ idx = indices & 0x03;
+ d[0] = colors[idx][2];
+ d[1] = colors[idx][1];
+ d[2] = colors[idx][0];
+ if (format == DDS_COMPRESS_BC1)
+ d[3] = ((c0 <= c1) && idx == 3) ? 0 : 255;
+ indices >>= 2;
+ d += 4;
+ }
+ }
+}
+
+static void
+decode_alpha_block_BC2 (unsigned char *block,
+ unsigned char *src)
+{
+ int x, y;
+ unsigned char *d = block;
+ unsigned int bits;
+
+ for (y = 0; y < 4; ++y)
+ {
+ bits = GETL16(&src[2 * y]);
+ for (x = 0; x < 4; ++x)
+ {
+ d[0] = (bits & 0x0f) * 17;
+ bits >>= 4;
+ d += 4;
+ }
+ }
+}
+
+static void
+decode_alpha_block_BC3 (unsigned char *block,
+ unsigned char *src,
+ int w)
+{
+ int x, y, code;
+ unsigned char *d = block;
+ unsigned char a0 = src[0];
+ unsigned char a1 = src[1];
+ unsigned long long bits = GETL64(src) >> 16;
+
+ for (y = 0; y < 4; ++y)
+ {
+ for (x = 0; x < 4; ++x)
+ {
+ code = ((unsigned int)bits) & 0x07;
+ if (code == 0)
+ d[0] = a0;
+ else if (code == 1)
+ d[0] = a1;
+ else if (a0 > a1)
+ d[0] = ((8 - code) * a0 + (code - 1) * a1) / 7;
+ else if (code >= 6)
+ d[0] = (code == 6) ? 0 : 255;
+ else
+ d[0] = ((6 - code) * a0 + (code - 1) * a1) / 5;
+ bits >>= 3;
+ d += 4;
+ }
+
+ if (w < 4)
+ bits >>= (3 * (4 - w));
+ }
+}
+
+static void
+make_normal (unsigned char *dst,
+ unsigned char x,
+ unsigned char y)
+{
+ float nx = 2.0f * ((float)x / 255.0f) - 1.0f;
+ float ny = 2.0f * ((float)y / 255.0f) - 1.0f;
+ float nz = 0.0f;
+ float d = 1.0f - nx * nx + ny * ny;
+ int z;
+
+ if (d > 0)
+ nz = sqrtf(d);
+
+ z = (int)(255.0f * (nz + 1) / 2.0f);
+ z = MAX(0, MIN(255, z));
+
+ dst[0] = x;
+ dst[1] = y;
+ dst[2] = z;
+}
+
+static void
+normalize_block (unsigned char *block,
+ int format)
+{
+ int x, y, tmp;
+
+ for (y = 0; y < 4; ++y)
+ {
+ for (x = 0; x < 4; ++x)
+ {
+ if (format == DDS_COMPRESS_BC3)
+ {
+ tmp = block[y * 16 + (x * 4)];
+ make_normal(&block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4) + 3],
+ block[y * 16 + (x * 4) + 1]);
+ block[y * 16 + (x * 4) + 3] = tmp;
+ }
+ else if (format == DDS_COMPRESS_BC5)
+ {
+ make_normal(&block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4)],
+ block[y * 16 + (x * 4) + 1]);
+ }
+ }
+ }
+}
+
+static void
+put_block (unsigned char *dst,
+ unsigned char *block,
+ unsigned int bx,
+ unsigned int by,
+ unsigned int width,
+ unsigned height,
+ int bpp)
+{
+ int x, y, i;
+ unsigned char *d;
+
+ for (y = 0; y < 4 && ((by + y) < height); ++y)
+ {
+ d = dst + ((y + by) * width + bx) * bpp;
+ for (x = 0; x < 4 && ((bx + x) < width); ++x)
+ {
+ for (i = 0; i < bpp; ++ i)
+ *d++ = block[y * 16 + (x * 4) + i];
+ }
+ }
+}
+
+int
+dxt_decompress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int size,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int normals)
+{
+ unsigned char *s;
+ unsigned int x, y;
+ unsigned char block[16 * 4];
+
+ s = src;
+
+ for (y = 0; y < height; y += 4)
+ {
+ for (x = 0; x < width; x += 4)
+ {
+ memset(block, 0, 16 * 4);
+
+ if (format == DDS_COMPRESS_BC1)
+ {
+ decode_color_block(block, s, format);
+ s += 8;
+ }
+ else if (format == DDS_COMPRESS_BC2)
+ {
+ decode_alpha_block_BC2(block + 3, s);
+ decode_color_block(block, s + 8, format);
+ s += 16;
+ }
+ else if (format == DDS_COMPRESS_BC3)
+ {
+ decode_alpha_block_BC3(block + 3, s, width);
+ decode_color_block(block, s + 8, format);
+ s += 16;
+ }
+ else if (format == DDS_COMPRESS_BC4)
+ {
+ decode_alpha_block_BC3(block, s, width);
+ s += 8;
+ }
+ else if (format == DDS_COMPRESS_BC5)
+ {
+ decode_alpha_block_BC3(block, s, width);
+ decode_alpha_block_BC3(block + 1, s + 8, width);
+ s += 16;
+ }
+
+ if (normals)
+ normalize_block(block, format);
+
+ put_block(dst, block, x, y, width, height, bpp);
+ }
+ }
+
+ return 1;
+}
diff --git a/plug-ins/file-dds/dxt.h b/plug-ins/file-dds/dxt.h
new file mode 100644
index 0000000..5364bbe
--- /dev/null
+++ b/plug-ins/file-dds/dxt.h
@@ -0,0 +1,49 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DXT_H__
+#define __DXT_H__
+
+typedef enum dxt_flags_e
+{
+ DXT_BC1 = 1 << 0,
+ DXT_BC2 = 1 << 1,
+ DXT_BC3 = 1 << 2,
+ DXT_PERCEPTUAL = 1 << 3,
+} dxt_flags_t;
+
+int dxt_compress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int mipmaps,
+ int flags);
+int dxt_decompress (unsigned char *dst,
+ unsigned char *src,
+ int format,
+ unsigned int size,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int normals);
+
+#endif /* __DXT_H__ */
diff --git a/plug-ins/file-dds/dxt_tables.h b/plug-ins/file-dds/dxt_tables.h
new file mode 100644
index 0000000..e30bb6a
--- /dev/null
+++ b/plug-ins/file-dds/dxt_tables.h
@@ -0,0 +1,216 @@
+#ifndef __DXT_TABLES_H__
+#define __DXT_TABLES_H__
+
+static const unsigned char quantRB[256 + 16] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x29, 0x29,
+ 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x52,
+ 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x5a,
+ 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b,
+ 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b,
+ 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84,
+ 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xef, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static const unsigned char quantG[256 + 16] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x08,
+ 0x08, 0x08, 0x08, 0x0c, 0x0c, 0x0c, 0x0c, 0x10,
+ 0x10, 0x10, 0x10, 0x14, 0x14, 0x14, 0x14, 0x18,
+ 0x18, 0x18, 0x18, 0x1c, 0x1c, 0x1c, 0x1c, 0x20,
+ 0x20, 0x20, 0x20, 0x24, 0x24, 0x24, 0x24, 0x28,
+ 0x28, 0x28, 0x28, 0x2c, 0x2c, 0x2c, 0x2c, 0x30,
+ 0x30, 0x30, 0x30, 0x34, 0x34, 0x34, 0x34, 0x38,
+ 0x38, 0x38, 0x38, 0x3c, 0x3c, 0x3c, 0x3c, 0x41,
+ 0x41, 0x41, 0x41, 0x45, 0x45, 0x45, 0x45, 0x49,
+ 0x49, 0x49, 0x49, 0x4d, 0x4d, 0x4d, 0x4d, 0x51,
+ 0x51, 0x51, 0x51, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x59, 0x59, 0x59, 0x59, 0x5d, 0x5d, 0x5d, 0x5d,
+ 0x61, 0x61, 0x61, 0x61, 0x65, 0x65, 0x65, 0x65,
+ 0x69, 0x69, 0x69, 0x69, 0x6d, 0x6d, 0x6d, 0x6d,
+ 0x71, 0x71, 0x71, 0x71, 0x75, 0x75, 0x75, 0x75,
+ 0x79, 0x79, 0x79, 0x79, 0x7d, 0x7d, 0x7d, 0x7d,
+ 0x82, 0x82, 0x82, 0x82, 0x86, 0x86, 0x86, 0x86,
+ 0x8a, 0x8a, 0x8a, 0x8a, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x92, 0x92, 0x92, 0x92, 0x96, 0x96, 0x96, 0x96,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xae, 0xae, 0xae,
+ 0xae, 0xb2, 0xb2, 0xb2, 0xb2, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xba, 0xba, 0xba, 0xba, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xcb, 0xcb, 0xcb, 0xcb, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd3, 0xd3, 0xd3, 0xd3, 0xd7, 0xd7, 0xd7,
+ 0xd7, 0xdb, 0xdb, 0xdb, 0xdb, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe3, 0xe3, 0xe3, 0xe3, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xeb, 0xeb, 0xeb, 0xeb, 0xef, 0xef, 0xef,
+ 0xef, 0xf3, 0xf3, 0xf3, 0xf3, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xfb, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static const unsigned char omatch5[256][2] =
+{
+ {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x01}, {0x00, 0x01},
+ {0x01, 0x00}, {0x01, 0x00}, {0x01, 0x00}, {0x01, 0x01},
+ {0x01, 0x01}, {0x01, 0x01}, {0x01, 0x02}, {0x00, 0x04},
+ {0x02, 0x01}, {0x02, 0x01}, {0x02, 0x01}, {0x02, 0x02},
+ {0x02, 0x02}, {0x02, 0x02}, {0x02, 0x03}, {0x01, 0x05},
+ {0x03, 0x02}, {0x03, 0x02}, {0x04, 0x00}, {0x03, 0x03},
+ {0x03, 0x03}, {0x03, 0x03}, {0x03, 0x04}, {0x03, 0x04},
+ {0x03, 0x04}, {0x03, 0x05}, {0x04, 0x03}, {0x04, 0x03},
+ {0x05, 0x02}, {0x04, 0x04}, {0x04, 0x04}, {0x04, 0x05},
+ {0x04, 0x05}, {0x05, 0x04}, {0x05, 0x04}, {0x05, 0x04},
+ {0x06, 0x03}, {0x05, 0x05}, {0x05, 0x05}, {0x05, 0x06},
+ {0x04, 0x08}, {0x06, 0x05}, {0x06, 0x05}, {0x06, 0x05},
+ {0x06, 0x06}, {0x06, 0x06}, {0x06, 0x06}, {0x06, 0x07},
+ {0x05, 0x09}, {0x07, 0x06}, {0x07, 0x06}, {0x08, 0x04},
+ {0x07, 0x07}, {0x07, 0x07}, {0x07, 0x07}, {0x07, 0x08},
+ {0x07, 0x08}, {0x07, 0x08}, {0x07, 0x09}, {0x08, 0x07},
+ {0x08, 0x07}, {0x09, 0x06}, {0x08, 0x08}, {0x08, 0x08},
+ {0x08, 0x09}, {0x08, 0x09}, {0x09, 0x08}, {0x09, 0x08},
+ {0x09, 0x08}, {0x0a, 0x07}, {0x09, 0x09}, {0x09, 0x09},
+ {0x09, 0x0a}, {0x08, 0x0c}, {0x0a, 0x09}, {0x0a, 0x09},
+ {0x0a, 0x09}, {0x0a, 0x0a}, {0x0a, 0x0a}, {0x0a, 0x0a},
+ {0x0a, 0x0b}, {0x09, 0x0d}, {0x0b, 0x0a}, {0x0b, 0x0a},
+ {0x0c, 0x08}, {0x0b, 0x0b}, {0x0b, 0x0b}, {0x0b, 0x0b},
+ {0x0b, 0x0c}, {0x0b, 0x0c}, {0x0b, 0x0c}, {0x0b, 0x0d},
+ {0x0c, 0x0b}, {0x0c, 0x0b}, {0x0d, 0x0a}, {0x0c, 0x0c},
+ {0x0c, 0x0c}, {0x0c, 0x0d}, {0x0c, 0x0d}, {0x0d, 0x0c},
+ {0x0d, 0x0c}, {0x0d, 0x0c}, {0x0e, 0x0b}, {0x0d, 0x0d},
+ {0x0d, 0x0d}, {0x0d, 0x0e}, {0x0c, 0x10}, {0x0e, 0x0d},
+ {0x0e, 0x0d}, {0x0e, 0x0d}, {0x0e, 0x0e}, {0x0e, 0x0e},
+ {0x0e, 0x0e}, {0x0e, 0x0f}, {0x0d, 0x11}, {0x0f, 0x0e},
+ {0x0f, 0x0e}, {0x10, 0x0c}, {0x0f, 0x0f}, {0x0f, 0x0f},
+ {0x0f, 0x0f}, {0x0f, 0x10}, {0x0f, 0x10}, {0x0f, 0x10},
+ {0x0f, 0x11}, {0x10, 0x0f}, {0x10, 0x0f}, {0x11, 0x0e},
+ {0x10, 0x10}, {0x10, 0x10}, {0x10, 0x11}, {0x10, 0x11},
+ {0x11, 0x10}, {0x11, 0x10}, {0x11, 0x10}, {0x12, 0x0f},
+ {0x11, 0x11}, {0x11, 0x11}, {0x11, 0x12}, {0x10, 0x14},
+ {0x12, 0x11}, {0x12, 0x11}, {0x12, 0x11}, {0x12, 0x12},
+ {0x12, 0x12}, {0x12, 0x12}, {0x12, 0x13}, {0x11, 0x15},
+ {0x13, 0x12}, {0x13, 0x12}, {0x14, 0x10}, {0x13, 0x13},
+ {0x13, 0x13}, {0x13, 0x13}, {0x13, 0x14}, {0x13, 0x14},
+ {0x13, 0x14}, {0x13, 0x15}, {0x14, 0x13}, {0x14, 0x13},
+ {0x15, 0x12}, {0x14, 0x14}, {0x14, 0x14}, {0x14, 0x15},
+ {0x14, 0x15}, {0x15, 0x14}, {0x15, 0x14}, {0x15, 0x14},
+ {0x16, 0x13}, {0x15, 0x15}, {0x15, 0x15}, {0x15, 0x16},
+ {0x14, 0x18}, {0x16, 0x15}, {0x16, 0x15}, {0x16, 0x15},
+ {0x16, 0x16}, {0x16, 0x16}, {0x16, 0x16}, {0x16, 0x17},
+ {0x15, 0x19}, {0x17, 0x16}, {0x17, 0x16}, {0x18, 0x14},
+ {0x17, 0x17}, {0x17, 0x17}, {0x17, 0x17}, {0x17, 0x18},
+ {0x17, 0x18}, {0x17, 0x18}, {0x17, 0x19}, {0x18, 0x17},
+ {0x18, 0x17}, {0x19, 0x16}, {0x18, 0x18}, {0x18, 0x18},
+ {0x18, 0x19}, {0x18, 0x19}, {0x19, 0x18}, {0x19, 0x18},
+ {0x19, 0x18}, {0x1a, 0x17}, {0x19, 0x19}, {0x19, 0x19},
+ {0x19, 0x1a}, {0x18, 0x1c}, {0x1a, 0x19}, {0x1a, 0x19},
+ {0x1a, 0x19}, {0x1a, 0x1a}, {0x1a, 0x1a}, {0x1a, 0x1a},
+ {0x1a, 0x1b}, {0x19, 0x1d}, {0x1b, 0x1a}, {0x1b, 0x1a},
+ {0x1c, 0x18}, {0x1b, 0x1b}, {0x1b, 0x1b}, {0x1b, 0x1b},
+ {0x1b, 0x1c}, {0x1b, 0x1c}, {0x1b, 0x1c}, {0x1b, 0x1d},
+ {0x1c, 0x1b}, {0x1c, 0x1b}, {0x1d, 0x1a}, {0x1c, 0x1c},
+ {0x1c, 0x1c}, {0x1c, 0x1d}, {0x1c, 0x1d}, {0x1d, 0x1c},
+ {0x1d, 0x1c}, {0x1d, 0x1c}, {0x1e, 0x1b}, {0x1d, 0x1d},
+ {0x1d, 0x1d}, {0x1d, 0x1e}, {0x1d, 0x1e}, {0x1e, 0x1d},
+ {0x1e, 0x1d}, {0x1e, 0x1d}, {0x1e, 0x1e}, {0x1e, 0x1e},
+ {0x1e, 0x1e}, {0x1e, 0x1f}, {0x1e, 0x1f}, {0x1f, 0x1e},
+ {0x1f, 0x1e}, {0x1f, 0x1e}, {0x1f, 0x1f}, {0x1f, 0x1f},
+};
+
+static const unsigned char omatch6[256][2] =
+{
+ {0x00, 0x00}, {0x00, 0x01}, {0x01, 0x00}, {0x01, 0x01},
+ {0x01, 0x01}, {0x01, 0x02}, {0x02, 0x01}, {0x02, 0x02},
+ {0x02, 0x02}, {0x02, 0x03}, {0x03, 0x02}, {0x03, 0x03},
+ {0x03, 0x03}, {0x03, 0x04}, {0x04, 0x03}, {0x04, 0x04},
+ {0x04, 0x04}, {0x04, 0x05}, {0x05, 0x04}, {0x05, 0x05},
+ {0x05, 0x05}, {0x05, 0x06}, {0x06, 0x05}, {0x00, 0x11},
+ {0x06, 0x06}, {0x06, 0x07}, {0x07, 0x06}, {0x02, 0x10},
+ {0x07, 0x07}, {0x07, 0x08}, {0x08, 0x07}, {0x03, 0x11},
+ {0x08, 0x08}, {0x08, 0x09}, {0x09, 0x08}, {0x05, 0x10},
+ {0x09, 0x09}, {0x09, 0x0a}, {0x0a, 0x09}, {0x06, 0x11},
+ {0x0a, 0x0a}, {0x0a, 0x0b}, {0x0b, 0x0a}, {0x08, 0x10},
+ {0x0b, 0x0b}, {0x0b, 0x0c}, {0x0c, 0x0b}, {0x09, 0x11},
+ {0x0c, 0x0c}, {0x0c, 0x0d}, {0x0d, 0x0c}, {0x0b, 0x10},
+ {0x0d, 0x0d}, {0x0d, 0x0e}, {0x0e, 0x0d}, {0x0c, 0x11},
+ {0x0e, 0x0e}, {0x0e, 0x0f}, {0x0f, 0x0e}, {0x0e, 0x10},
+ {0x0f, 0x0f}, {0x0f, 0x10}, {0x10, 0x0e}, {0x10, 0x0f},
+ {0x11, 0x0e}, {0x10, 0x10}, {0x10, 0x11}, {0x11, 0x10},
+ {0x12, 0x0f}, {0x11, 0x11}, {0x11, 0x12}, {0x12, 0x11},
+ {0x14, 0x0e}, {0x12, 0x12}, {0x12, 0x13}, {0x13, 0x12},
+ {0x15, 0x0f}, {0x13, 0x13}, {0x13, 0x14}, {0x14, 0x13},
+ {0x17, 0x0e}, {0x14, 0x14}, {0x14, 0x15}, {0x15, 0x14},
+ {0x18, 0x0f}, {0x15, 0x15}, {0x15, 0x16}, {0x16, 0x15},
+ {0x1a, 0x0e}, {0x16, 0x16}, {0x16, 0x17}, {0x17, 0x16},
+ {0x1b, 0x0f}, {0x17, 0x17}, {0x17, 0x18}, {0x18, 0x17},
+ {0x13, 0x21}, {0x18, 0x18}, {0x18, 0x19}, {0x19, 0x18},
+ {0x15, 0x20}, {0x19, 0x19}, {0x19, 0x1a}, {0x1a, 0x19},
+ {0x16, 0x21}, {0x1a, 0x1a}, {0x1a, 0x1b}, {0x1b, 0x1a},
+ {0x18, 0x20}, {0x1b, 0x1b}, {0x1b, 0x1c}, {0x1c, 0x1b},
+ {0x19, 0x21}, {0x1c, 0x1c}, {0x1c, 0x1d}, {0x1d, 0x1c},
+ {0x1b, 0x20}, {0x1d, 0x1d}, {0x1d, 0x1e}, {0x1e, 0x1d},
+ {0x1c, 0x21}, {0x1e, 0x1e}, {0x1e, 0x1f}, {0x1f, 0x1e},
+ {0x1e, 0x20}, {0x1f, 0x1f}, {0x1f, 0x20}, {0x20, 0x1e},
+ {0x20, 0x1f}, {0x21, 0x1e}, {0x20, 0x20}, {0x20, 0x21},
+ {0x21, 0x20}, {0x22, 0x1f}, {0x21, 0x21}, {0x21, 0x22},
+ {0x22, 0x21}, {0x24, 0x1e}, {0x22, 0x22}, {0x22, 0x23},
+ {0x23, 0x22}, {0x25, 0x1f}, {0x23, 0x23}, {0x23, 0x24},
+ {0x24, 0x23}, {0x27, 0x1e}, {0x24, 0x24}, {0x24, 0x25},
+ {0x25, 0x24}, {0x28, 0x1f}, {0x25, 0x25}, {0x25, 0x26},
+ {0x26, 0x25}, {0x2a, 0x1e}, {0x26, 0x26}, {0x26, 0x27},
+ {0x27, 0x26}, {0x2b, 0x1f}, {0x27, 0x27}, {0x27, 0x28},
+ {0x28, 0x27}, {0x23, 0x31}, {0x28, 0x28}, {0x28, 0x29},
+ {0x29, 0x28}, {0x25, 0x30}, {0x29, 0x29}, {0x29, 0x2a},
+ {0x2a, 0x29}, {0x26, 0x31}, {0x2a, 0x2a}, {0x2a, 0x2b},
+ {0x2b, 0x2a}, {0x28, 0x30}, {0x2b, 0x2b}, {0x2b, 0x2c},
+ {0x2c, 0x2b}, {0x29, 0x31}, {0x2c, 0x2c}, {0x2c, 0x2d},
+ {0x2d, 0x2c}, {0x2b, 0x30}, {0x2d, 0x2d}, {0x2d, 0x2e},
+ {0x2e, 0x2d}, {0x2c, 0x31}, {0x2e, 0x2e}, {0x2e, 0x2f},
+ {0x2f, 0x2e}, {0x2e, 0x30}, {0x2f, 0x2f}, {0x2f, 0x30},
+ {0x30, 0x2e}, {0x30, 0x2f}, {0x31, 0x2e}, {0x30, 0x30},
+ {0x30, 0x31}, {0x31, 0x30}, {0x32, 0x2f}, {0x31, 0x31},
+ {0x31, 0x32}, {0x32, 0x31}, {0x34, 0x2e}, {0x32, 0x32},
+ {0x32, 0x33}, {0x33, 0x32}, {0x35, 0x2f}, {0x33, 0x33},
+ {0x33, 0x34}, {0x34, 0x33}, {0x37, 0x2e}, {0x34, 0x34},
+ {0x34, 0x35}, {0x35, 0x34}, {0x38, 0x2f}, {0x35, 0x35},
+ {0x35, 0x36}, {0x36, 0x35}, {0x3a, 0x2e}, {0x36, 0x36},
+ {0x36, 0x37}, {0x37, 0x36}, {0x3b, 0x2f}, {0x37, 0x37},
+ {0x37, 0x38}, {0x38, 0x37}, {0x3d, 0x2e}, {0x38, 0x38},
+ {0x38, 0x39}, {0x39, 0x38}, {0x3e, 0x2f}, {0x39, 0x39},
+ {0x39, 0x3a}, {0x3a, 0x39}, {0x3a, 0x3a}, {0x3a, 0x3a},
+ {0x3a, 0x3b}, {0x3b, 0x3a}, {0x3b, 0x3b}, {0x3b, 0x3b},
+ {0x3b, 0x3c}, {0x3c, 0x3b}, {0x3c, 0x3c}, {0x3c, 0x3c},
+ {0x3c, 0x3d}, {0x3d, 0x3c}, {0x3d, 0x3d}, {0x3d, 0x3d},
+ {0x3d, 0x3e}, {0x3e, 0x3d}, {0x3e, 0x3e}, {0x3e, 0x3e},
+ {0x3e, 0x3f}, {0x3f, 0x3e}, {0x3f, 0x3f}, {0x3f, 0x3f},
+};
+
+#endif /* __DXT_TABLES_H__ */
diff --git a/plug-ins/file-dds/endian_rw.h b/plug-ins/file-dds/endian_rw.h
new file mode 100644
index 0000000..1d0b5fc
--- /dev/null
+++ b/plug-ins/file-dds/endian_rw.h
@@ -0,0 +1,69 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ENDIAN_RW_H__
+#define __ENDIAN_RW_H__
+
+#define GETL64(buf) \
+ (((unsigned long long)(buf)[0] ) | \
+ ((unsigned long long)(buf)[1] << 8) | \
+ ((unsigned long long)(buf)[2] << 16) | \
+ ((unsigned long long)(buf)[3] << 24) | \
+ ((unsigned long long)(buf)[4] << 32) | \
+ ((unsigned long long)(buf)[5] << 40) | \
+ ((unsigned long long)(buf)[6] << 48) | \
+ ((unsigned long long)(buf)[7] << 56))
+
+#define GETL32(buf) \
+ (((unsigned int)(buf)[0] ) | \
+ ((unsigned int)(buf)[1] << 8) | \
+ ((unsigned int)(buf)[2] << 16) | \
+ ((unsigned int)(buf)[3] << 24))
+
+#define GETL24(buf) \
+ (((unsigned int)(buf)[0] ) | \
+ ((unsigned int)(buf)[1] << 8) | \
+ ((unsigned int)(buf)[2] << 16))
+
+#define GETL16(buf) \
+ (((unsigned short)(buf)[0] ) | \
+ ((unsigned short)(buf)[1] << 8))
+
+#define PUTL16(buf, s) \
+ (buf)[0] = ((s) ) & 0xff; \
+ (buf)[1] = ((s) >> 8) & 0xff;
+
+#define PUTL32(buf, l) \
+ (buf)[0] = ((l) ) & 0xff; \
+ (buf)[1] = ((l) >> 8) & 0xff; \
+ (buf)[2] = ((l) >> 16) & 0xff; \
+ (buf)[3] = ((l) >> 24) & 0xff;
+
+#define PUTL64(buf, ll) \
+ (buf)[0] = ((ll) ) & 0xff; \
+ (buf)[1] = ((ll) >> 8) & 0xff; \
+ (buf)[2] = ((ll) >> 16) & 0xff; \
+ (buf)[3] = ((ll) >> 24) & 0xff; \
+ (buf)[4] = ((ll) >> 32) & 0xff; \
+ (buf)[5] = ((ll) >> 40) & 0xff; \
+ (buf)[6] = ((ll) >> 48) & 0xff; \
+ (buf)[7] = ((ll) >> 56) & 0xff;
+
+#endif /* __ENDIAN_RW_H__ */
diff --git a/plug-ins/file-dds/imath.h b/plug-ins/file-dds/imath.h
new file mode 100644
index 0000000..785c16d
--- /dev/null
+++ b/plug-ins/file-dds/imath.h
@@ -0,0 +1,77 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __IMATH_H__
+#define __IMATH_H__
+
+#ifndef MIN
+# ifdef __GNUC__
+# define MIN(a, b) ({typeof(a) _a=(a); typeof(b) _b=(b); _a < _b ? _a : _b;})
+# else
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+# endif
+#endif
+
+#ifndef MAX
+# ifdef __GNUC__
+# define MAX(a, b) ({typeof(a) _a=(a); typeof(b) _b=(b); _a > _b ? _a : _b;})
+# else
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+# endif
+#endif
+
+#define IS_POW2(x) (!((x) & ((x) - 1)))
+#define IS_MUL4(x) (((x) & 3) == 0)
+
+/* round integer x up to next multiple of 4 */
+#define RND_MUL4(x) ((x) + (4 - ((x) & 3)))
+
+static inline int
+mul8bit (int a,
+ int b)
+{
+ int t = a * b + 128;
+
+ return (t + (t >> 8)) >> 8;
+}
+
+static inline int
+blerp (int a,
+ int b,
+ int x)
+{
+ return a + mul8bit(b - a, x);
+}
+
+static inline int
+icerp (int a,
+ int b,
+ int c,
+ int d,
+ int x)
+{
+ int p = (d - c) - (a - b);
+ int q = (a - b) - p;
+ int r = c - a;
+
+ return (x * (x * (x * p + (q << 7)) + (r << 14)) + (b << 21)) >> 21;
+}
+
+#endif /* __IMATH_H__ */
diff --git a/plug-ins/file-dds/mipmap.c b/plug-ins/file-dds/mipmap.c
new file mode 100644
index 0000000..a1ad0c1
--- /dev/null
+++ b/plug-ins/file-dds/mipmap.c
@@ -0,0 +1,1132 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+#include <gtk/gtk.h>
+
+#ifdef _OPENMP
+#include <omp.h>
+#endif
+
+#include "dds.h"
+#include "mipmap.h"
+#include "imath.h"
+#include "color.h"
+
+typedef float (*filterfunc_t)(float);
+typedef int (*wrapfunc_t)(int, int);
+typedef void (*mipmapfunc_t)(unsigned char *, int, int, unsigned char *, int, int, int, filterfunc_t, float, wrapfunc_t, int, float);
+typedef void (*volmipmapfunc_t)(unsigned char *, int, int, int, unsigned char *, int, int, int, int, filterfunc_t, float, wrapfunc_t, int, float);
+
+/******************************************************************************
+ * size functions *
+ ******************************************************************************/
+
+int
+get_num_mipmaps (int width,
+ int height)
+{
+ int w = width << 1;
+ int h = height << 1;
+ int n = 0;
+
+ while (w != 1 || h != 1)
+ {
+ if (w > 1) w >>= 1;
+ if (h > 1) h >>= 1;
+ ++n;
+ }
+
+ return n;
+}
+
+unsigned int
+get_mipmapped_size (int width,
+ int height,
+ int bpp,
+ int level,
+ int num,
+ int format)
+{
+ int w, h, n = 0;
+ unsigned int size = 0;
+
+ w = width >> level;
+ h = height >> level;
+ w = MAX(1, w);
+ h = MAX(1, h);
+ w <<= 1;
+ h <<= 1;
+
+ while (n < num && (w != 1 || h != 1))
+ {
+ if (w > 1) w >>= 1;
+ if (h > 1) h >>= 1;
+ if (format == DDS_COMPRESS_NONE)
+ size += (w * h);
+ else
+ size += ((w + 3) >> 2) * ((h + 3) >> 2);
+ ++n;
+ }
+
+ if (format == DDS_COMPRESS_NONE)
+ {
+ size *= bpp;
+ }
+ else
+ {
+ if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4)
+ size *= 8;
+ else
+ size *= 16;
+ }
+
+ return size;
+}
+
+unsigned int
+get_volume_mipmapped_size (int width,
+ int height,
+ int depth,
+ int bpp,
+ int level,
+ int num,
+ int format)
+{
+ int w, h, d, n = 0;
+ unsigned int size = 0;
+
+ w = width >> level;
+ h = height >> level;
+ d = depth >> level;
+ w = MAX(1, w);
+ h = MAX(1, h);
+ d = MAX(1, d);
+ w <<= 1;
+ h <<= 1;
+ d <<= 1;
+
+ while (n < num && (w != 1 || h != 1))
+ {
+ if (w > 1) w >>= 1;
+ if (h > 1) h >>= 1;
+ if (d > 1) d >>= 1;
+ if (format == DDS_COMPRESS_NONE)
+ size += (w * h * d);
+ else
+ size += (((w + 3) >> 2) * ((h + 3) >> 2) * d);
+ ++n;
+ }
+
+ if (format == DDS_COMPRESS_NONE)
+ {
+ size *= bpp;
+ }
+ else
+ {
+ if (format == DDS_COMPRESS_BC1 || format == DDS_COMPRESS_BC4)
+ size *= 8;
+ else
+ size *= 16;
+ }
+
+ return size;
+}
+
+int
+get_next_mipmap_dimensions (int *next_w,
+ int *next_h,
+ int curr_w,
+ int curr_h)
+{
+ if (curr_w == 1 || curr_h == 1)
+ return 0;
+
+ if (next_w) *next_w = curr_w >> 1;
+ if (next_h) *next_h = curr_h >> 1;
+
+ return 1;
+}
+
+/******************************************************************************
+ * wrap modes *
+ ******************************************************************************/
+
+static int
+wrap_mirror (int x,
+ int max)
+{
+ if (max == 1) x = 0;
+ x = abs(x);
+ while (x >= max)
+ x = abs(max + max - x - 2);
+
+ return x;
+}
+
+static int
+wrap_repeat (int x,
+ int max)
+{
+ if (x >= 0)
+ return x % max;
+
+ return (x + 1) % max + max - 1;
+}
+
+static int
+wrap_clamp (int x,
+ int max)
+{
+ return MAX(0, MIN(max - 1, x));
+}
+
+/******************************************************************************
+ * gamma-correction *
+ ******************************************************************************/
+
+static int
+linear_to_gamma (int gc,
+ int v,
+ float gamma)
+{
+ if (gc == 1)
+ {
+ v = (int)(powf((float)v / 255.0f, gamma) * 255);
+ if (v > 255) v = 255;
+ }
+ else if (gc == 2)
+ {
+ v = linear_to_sRGB(v);
+ }
+
+ return v;
+}
+
+static int
+gamma_to_linear (int gc,
+ int v,
+ float gamma)
+{
+ if (gc == 1)
+ {
+ v = (int)(powf((float)v / 255.0f, 1.0f / gamma) * 255);
+ if(v > 255) v = 255;
+ }
+ else if (gc == 2)
+ {
+ v = sRGB_to_linear(v);
+ }
+
+ return v;
+}
+
+/******************************************************************************
+ * filters *
+ ******************************************************************************/
+
+static float
+box_filter (float t)
+{
+ if ((t >= -0.5f) && (t < 0.5f))
+ return 1.0f;
+
+ return 0.0f;
+}
+
+static float
+triangle_filter (float t)
+{
+ if (t < 0.0f) t = -t;
+ if (t < 1.0f) return 1.0f - t;
+
+ return 0.0f;
+}
+
+static float
+quadratic_filter (float t)
+{
+ if (t < 0.0f) t = -t;
+ if (t < 0.5f) return 0.75f - t * t;
+ if (t < 1.5f)
+ {
+ t -= 1.5f;
+ return 0.5f * t * t;
+ }
+
+ return 0.0f;
+}
+
+static float
+bspline_filter (float t)
+{
+ float tt;
+
+ if (t < 0.0f)
+ t = -t;
+
+ if (t < 1.0f)
+ {
+ tt = t * t;
+ return ((0.5f * tt * t) - tt + (2.0f / 3.0f));
+ }
+ else if (t < 2.0f)
+ {
+ t = 2.0f - t;
+ return (1.0f / 6.0f) * (t * t * t);
+ }
+
+ return 0.0f;
+}
+
+static float
+mitchell (float t,
+ const float B,
+ const float C)
+{
+ float tt;
+
+ tt = t * t;
+ if (t < 0.0f)
+ t = -t;
+
+ if (t < 1.0f)
+ {
+ t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt)) +
+ ((-18.0f + 12.0f * B + 6.0f * C) * tt) +
+ (6.0f - 2.0f * B));
+
+ return t / 6.0f;
+ }
+ else if (t < 2.0f)
+ {
+ t = (((-1.0f * B - 6.0f * C) * (t * tt)) +
+ ((6.0f * B + 30.0f * C) * tt) +
+ ((-12.0f * B - 48.0f * C) * t) +
+ (8.0f * B + 24.0f * C));
+
+ return t / 6.0f;
+ }
+
+ return 0.0f;
+}
+
+static float
+mitchell_filter (float t)
+{
+ return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f);
+}
+
+static float
+sinc (float x)
+{
+ x = (x * M_PI);
+ if (fabsf(x) < 1e-04f)
+ return 1.0f + x * x * (-1.0f / 6.0f + x * x * 1.0f / 120.0f);
+
+ return sinf(x) / x;
+}
+
+static float
+lanczos_filter (float t)
+{
+ if (t < 0.0f) t = -t;
+ if (t < 3.0f) return sinc(t) * sinc(t / 3.0f);
+
+ return 0.0f;
+}
+
+static float
+bessel0 (float x)
+{
+ const float EPSILON = 1e-6f;
+ float xh, sum, pow, ds;
+ int k;
+
+ xh = 0.5f * x;
+ sum = 1.0f;
+ pow = 1.0f;
+ k = 0;
+ ds = 1.0f;
+
+ while (ds > sum * EPSILON)
+ {
+ ++k;
+ pow = pow * (xh / k);
+ ds = pow * pow;
+ sum += ds;
+ }
+
+ return sum;
+}
+
+static float
+kaiser_filter (float t)
+{
+ if (t < 0.0f) t = -t;
+
+ if (t < 3.0f)
+ {
+ const float alpha = 4.0f;
+ const float rb04 = 0.0884805322f; // 1.0f / bessel0(4.0f);
+ const float ratio = t / 3.0f;
+ if ((1.0f - ratio * ratio) >= 0)
+ return sinc(t) * bessel0(alpha * sqrtf(1.0f - ratio * ratio)) * rb04;
+ }
+
+ return 0.0f;
+}
+
+/******************************************************************************
+ * 2D image scaling *
+ ******************************************************************************/
+
+static void
+scale_image_nearest (unsigned char *dst,
+ int dw,
+ int dh,
+ unsigned char *src,
+ int sw,
+ int sh,
+ int bpp,
+ filterfunc_t filter,
+ float support,
+ wrapfunc_t wrap,
+ int gc,
+ float gamma)
+{
+ int n, x, y;
+ int ix, iy;
+ int srowbytes = sw * bpp;
+ int drowbytes = dw * bpp;
+
+ for (y = 0; y < dh; ++y)
+ {
+ iy = (y * sh + sh / 2) / dh;
+ for (x = 0; x < dw; ++x)
+ {
+ ix = (x * sw + sw / 2) / dw;
+ for (n = 0; n < bpp; ++n)
+ {
+ dst[y * drowbytes + (x * bpp) + n] =
+ src[iy * srowbytes + (ix * bpp) + n];
+ }
+ }
+ }
+}
+
+static void
+scale_image (unsigned char *dst,
+ int dw,
+ int dh,
+ unsigned char *src,
+ int sw,
+ int sh,
+ int bpp,
+ filterfunc_t filter,
+ float support,
+ wrapfunc_t wrap,
+ int gc,
+ float gamma)
+{
+ const float blur = 1.0f;
+ const float xfactor = (float)dw / (float)sw;
+ const float yfactor = (float)dh / (float)sh;
+
+ int x, y, start, stop, nmax, n, i;
+ int sstride = sw * bpp;
+ float center, contrib, density, s, r, t;
+
+ unsigned char *d, *row, *col;
+
+ float xscale = MIN(xfactor, 1.0f) / blur;
+ float yscale = MIN(yfactor, 1.0f) / blur;
+ float xsupport = support / xscale;
+ float ysupport = support / yscale;
+ unsigned char *tmp;
+
+ if (xsupport <= 0.5f)
+ {
+ xsupport = 0.5f + 1e-10f;
+ xscale = 1.0f;
+ }
+
+ if (ysupport <= 0.5f)
+ {
+ ysupport = 0.5f + 1e-10f;
+ yscale = 1.0f;
+ }
+
+#ifdef _OPENMP
+ tmp = g_malloc(sw * bpp * omp_get_max_threads());
+#else
+ tmp = g_malloc(sw * bpp);
+#endif
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic) \
+ private(x, y, d, row, col, center, start, stop, nmax, s, i, n, density, r, t, contrib)
+#endif
+ for (y = 0; y < dh; ++y)
+ {
+ /* resample in Y direction to temp buffer */
+ d = tmp;
+#ifdef _OPENMP
+ d += (sw * bpp * omp_get_thread_num());
+#endif
+
+ center = ((float)y + 0.5f) / yfactor;
+ start = (int)(center - ysupport + 0.5f);
+ stop = (int)(center + ysupport + 0.5f);
+ nmax = stop - start;
+ s = (float)start - center + 0.5f;
+
+ for (x = 0; x < sw; ++x)
+ {
+ col = src + (x * bpp);
+
+ for (i = 0; i < bpp; ++i)
+ {
+ density = 0.0f;
+ r = 0.0f;
+
+ for (n = 0; n < nmax; ++n)
+ {
+ contrib = filter((s + n) * yscale);
+ density += contrib;
+ if (i == 3)
+ t = col[(wrap(start + n, sh) * sstride) + i];
+ else
+ t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma);
+ r += t * contrib;
+ }
+
+ if (density != 0.0f && density != 1.0f)
+ r /= density;
+
+ r = MIN(255, MAX(0, r));
+
+ if (i != 3)
+ r = gamma_to_linear(gc, r, gamma);
+
+ d[(x * bpp) + i] = (unsigned char)r;
+ }
+ }
+
+ /* resample in X direction using temp buffer */
+ row = d;
+ d = dst;
+
+ for (x = 0; x < dw; ++x)
+ {
+ center = ((float)x + 0.5f) / xfactor;
+ start = (int)(center - xsupport + 0.5f);
+ stop = (int)(center + xsupport + 0.5f);
+ nmax = stop - start;
+ s = (float)start - center + 0.5f;
+
+ for (i = 0; i < bpp; ++i)
+ {
+ density = 0.0f;
+ r = 0.0f;
+
+ for (n = 0; n < nmax; ++n)
+ {
+ contrib = filter((s + n) * xscale);
+ density += contrib;
+ if (i == 3)
+ t = row[(wrap(start + n, sw) * bpp) + i];
+ else
+ t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma);
+ r += t * contrib;
+ }
+
+ if (density != 0.0f && density != 1.0f)
+ r /= density;
+
+ r = MIN(255, MAX(0, r));
+
+ if (i != 3)
+ r = gamma_to_linear(gc, r, gamma);
+
+ d[(y * (dw * bpp)) + (x * bpp) + i] = (unsigned char)r;
+ }
+ }
+ }
+
+ g_free (tmp);
+}
+
+/******************************************************************************
+ * 3D image scaling *
+ ******************************************************************************/
+
+static void
+scale_volume_image_nearest (unsigned char *dst,
+ int dw,
+ int dh,
+ int dd,
+ unsigned char *src,
+ int sw,
+ int sh,
+ int sd,
+ int bpp,
+ filterfunc_t filter,
+ float support,
+ wrapfunc_t wrap,
+ int gc,
+ float gamma)
+{
+ int n, x, y, z;
+ int ix, iy, iz;
+
+ for (z = 0; z < dd; ++z)
+ {
+ iz = (z * sd + sd / 2) / dd;
+ for (y = 0; y < dh; ++y)
+ {
+ iy = (y * sh + sh / 2) / dh;
+ for (x = 0; x < dw; ++x)
+ {
+ ix = (x * sw + sw / 2) / dw;
+ for (n = 0; n < bpp; ++n)
+ {
+ dst[(z * (dw * dh)) + (y * dw) + (x * bpp) + n] =
+ src[(iz * (sw * sh)) + (iy * sw) + (ix * bpp) + n];
+ }
+ }
+ }
+ }
+}
+
+static void
+scale_volume_image (unsigned char *dst,
+ int dw,
+ int dh,
+ int dd,
+ unsigned char *src,
+ int sw,
+ int sh,
+ int sd,
+ int bpp,
+ filterfunc_t filter,
+ float support,
+ wrapfunc_t wrap,
+ int gc,
+ float gamma)
+{
+ const float blur = 1.0f;
+ const float xfactor = (float)dw / (float)sw;
+ const float yfactor = (float)dh / (float)sh;
+ const float zfactor = (float)dd / (float)sd;
+
+ int x, y, z, start, stop, nmax, n, i;
+ int sstride = sw * bpp;
+ int zstride = sh * sw * bpp;
+ float center, contrib, density, s, r, t;
+
+ unsigned char *d, *row, *col, *slice;
+
+ float xscale = MIN(xfactor, 1.0f) / blur;
+ float yscale = MIN(yfactor, 1.0f) / blur;
+ float zscale = MIN(zfactor, 1.0f) / blur;
+ float xsupport = support / xscale;
+ float ysupport = support / yscale;
+ float zsupport = support / zscale;
+ unsigned char *tmp1, *tmp2;
+
+ /* down to a 2D image, use the faster 2D image resampler */
+ if (dd == 1 && sd == 1)
+ {
+ scale_image(dst, dw, dh, src, sw, sh, bpp, filter, support, wrap, gc, gamma);
+ return;
+ }
+
+ if (xsupport <= 0.5f)
+ {
+ xsupport = 0.5f + 1e-10f;
+ xscale = 1.0f;
+ }
+
+ if (ysupport <= 0.5f)
+ {
+ ysupport = 0.5f + 1e-10f;
+ yscale = 1.0f;
+ }
+
+ if (zsupport <= 0.5f)
+ {
+ zsupport = 0.5f + 1e-10f;
+ zscale = 1.0f;
+ }
+
+ tmp1 = g_malloc(sh * sw * bpp);
+ tmp2 = g_malloc(dh * sw * bpp);
+
+ for (z = 0; z < dd; ++z)
+ {
+ /* resample in Z direction */
+ d = tmp1;
+
+ center = ((float)z + 0.5f) / zfactor;
+ start = (int)(center - zsupport + 0.5f);
+ stop = (int)(center + zsupport + 0.5f);
+ nmax = stop - start;
+ s = (float)start - center + 0.5f;
+
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic) \
+ private(x, y, slice, i, n, density, r, t, contrib)
+#endif
+ for (y = 0; y < sh; ++y)
+ {
+ for (x = 0; x < sw; ++x)
+ {
+ slice = src + (y * (sw * bpp)) + (x * bpp);
+
+ for (i = 0; i < bpp; ++i)
+ {
+ density = 0.0f;
+ r = 0.0f;
+
+ for (n = 0; n < nmax; ++n)
+ {
+ contrib = filter((s + n) * zscale);
+ density += contrib;
+ if (i == 3)
+ t = slice[(wrap(start + n, sd) * zstride) + i];
+ else
+ t = linear_to_gamma(gc, slice[(wrap(start + n, sd) * zstride) + i], gamma);
+ r += t * contrib;
+ }
+
+ if (density != 0.0f && density != 1.0f)
+ r /= density;
+
+ r = MIN(255, MAX(0, r));
+
+ if (i != 3)
+ r = gamma_to_linear(gc, r, gamma);
+
+ d[((y * sw) + x) * bpp + i] = (unsigned char)r;
+ }
+ }
+ }
+
+ /* resample in Y direction */
+ d = tmp2;
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic) \
+ private(x, y, col, center, start, stop, nmax, s, i, n, density, r, t, contrib)
+#endif
+ for (y = 0; y < dh; ++y)
+ {
+ center = ((float)y + 0.5f) / yfactor;
+ start = (int)(center - ysupport + 0.5f);
+ stop = (int)(center + ysupport + 0.5f);
+ nmax = stop - start;
+ s = (float)start - center + 0.5f;
+
+ for (x = 0; x < sw; ++x)
+ {
+ col = tmp1 + (x * bpp);
+
+ for (i = 0; i < bpp; ++i)
+ {
+ density = 0.0f;
+ r = 0.0f;
+
+ for (n = 0; n < nmax; ++n)
+ {
+ contrib = filter((s + n) * yscale);
+ density += contrib;
+ if (i == 3)
+ t = col[(wrap(start + n, sh) * sstride) + i];
+ else
+ t = linear_to_gamma(gc, col[(wrap(start + n, sh) * sstride) + i], gamma);
+ r += t * contrib;
+ }
+
+ if (density != 0.0f && density != 1.0f)
+ r /= density;
+
+ r = MIN(255, MAX(0, r));
+
+ if (i != 3)
+ r = gamma_to_linear(gc, r, gamma);
+
+ d[((y * sw) + x) * bpp + i] = (unsigned char)r;
+ }
+ }
+ }
+
+ /* resample in X direction */
+ d = dst;
+#ifdef _OPENMP
+#pragma omp parallel for schedule(dynamic) \
+ private(x, y, row, center, start, stop, nmax, s, i, n, density, r, t, contrib)
+#endif
+ for (y = 0; y < dh; ++y)
+ {
+ row = tmp2 + (y * sstride);
+
+ for (x = 0; x < dw; ++x)
+ {
+ center = ((float)x + 0.5f) / xfactor;
+ start = (int)(center - xsupport + 0.5f);
+ stop = (int)(center + xsupport + 0.5f);
+ nmax = stop - start;
+ s = (float)start - center + 0.5f;
+
+ for (i = 0; i < bpp; ++i)
+ {
+ density = 0.0f;
+ r = 0.0f;
+
+ for (n = 0; n < nmax; ++n)
+ {
+ contrib = filter((s + n) * xscale);
+ density += contrib;
+ if (i == 3)
+ t = row[(wrap(start + n, sw) * bpp) + i];
+ else
+ t = linear_to_gamma(gc, row[(wrap(start + n, sw) * bpp) + i], gamma);
+ r += t * contrib;
+ }
+
+ if (density != 0.0f && density != 1.0f)
+ r /= density;
+
+ r = MIN(255, MAX(0, r));
+
+ if (i != 3)
+ r = gamma_to_linear(gc, r, gamma);
+
+ d[((z * dh * dw) + (y * dw) + x) * bpp + i] = (unsigned char)r;
+ }
+ }
+ }
+ }
+
+ g_free (tmp1);
+ g_free (tmp2);
+}
+
+/******************************************************************************
+ * filter lookup table *
+ ******************************************************************************/
+
+static struct
+{
+ int filter;
+ filterfunc_t func;
+ float support;
+} filters[] =
+{
+ { DDS_MIPMAP_FILTER_BOX, box_filter, 0.5f },
+ { DDS_MIPMAP_FILTER_TRIANGLE, triangle_filter, 1.0f },
+ { DDS_MIPMAP_FILTER_QUADRATIC, quadratic_filter, 1.5f },
+ { DDS_MIPMAP_FILTER_BSPLINE, bspline_filter, 2.0f },
+ { DDS_MIPMAP_FILTER_MITCHELL, mitchell_filter, 2.0f },
+ { DDS_MIPMAP_FILTER_LANCZOS, lanczos_filter, 3.0f },
+ { DDS_MIPMAP_FILTER_KAISER, kaiser_filter, 3.0f },
+ { DDS_MIPMAP_FILTER_MAX, NULL, 0.0f }
+};
+
+/*
+ * Alpha test coverage - portion of visible texels after alpha test:
+ * if (texel_alpha < alpha_test_threshold)
+ * discard;
+ */
+static float
+calc_alpha_test_coverage (unsigned char *src,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ float alpha_test_threshold,
+ float alpha_scale)
+{
+ unsigned int x, y;
+ int rowbytes = width * bpp;
+ int coverage = 0;
+ const int alpha_channel_idx = 3;
+
+ if (bpp <= alpha_channel_idx)
+ {
+ /* No alpha channel */
+ return 1.f;
+ }
+
+ for (y = 0; y < height; ++y)
+ {
+ for (x = 0; x < width; ++x)
+ {
+ const float alpha = src[y * rowbytes + (x * bpp) + alpha_channel_idx];
+ if ((alpha * alpha_scale) >= (alpha_test_threshold * 255))
+ {
+ ++coverage;
+ }
+ }
+ }
+
+ return (float)coverage / (width * height);
+}
+
+static void
+scale_alpha_to_coverage (unsigned char *img,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ float desired_coverage,
+ float alpha_test_threshold)
+{
+ int i;
+ unsigned int x, y;
+ const int rowbytes = width * bpp;
+ const int alpha_channel_idx = 3;
+ float min_alpha_scale = 0.0f;
+ float max_alpha_scale = 4.0f;
+ float alpha_scale = 1.0f;
+
+ if (bpp <= alpha_channel_idx)
+ {
+ /* No alpha channel */
+ return;
+ }
+
+ /* Binary search */
+ for (i = 0; i < 10; i++)
+ {
+ float cur_coverage = calc_alpha_test_coverage(img, width, height, bpp, alpha_test_threshold, alpha_scale);
+
+ if (cur_coverage < desired_coverage)
+ {
+ min_alpha_scale = alpha_scale;
+ }
+ else if (cur_coverage > desired_coverage)
+ {
+ max_alpha_scale = alpha_scale;
+ }
+ else
+ {
+ break;
+ }
+
+ alpha_scale = (min_alpha_scale + max_alpha_scale) / 2;
+ }
+
+ /* Scale alpha channel */
+ for (y = 0; y < height; ++y)
+ {
+ for (x = 0; x < width; ++x)
+ {
+ float new_alpha = img[y * rowbytes + (x * bpp) + alpha_channel_idx] * alpha_scale;
+ if (new_alpha > 255.0f)
+ {
+ new_alpha = 255.0f;
+ }
+
+ img[y * rowbytes + (x * bpp) + alpha_channel_idx] = (unsigned char)new_alpha;
+ }
+ }
+}
+
+/******************************************************************************
+ * mipmap generation *
+ ******************************************************************************/
+
+int
+generate_mipmaps (unsigned char *dst,
+ unsigned char *src,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int indexed,
+ int mipmaps,
+ int filter,
+ int wrap,
+ int gc,
+ float gamma,
+ int preserve_alpha_coverage,
+ float alpha_test_threshold)
+{
+ int i;
+ unsigned int sw, sh, dw, dh;
+ unsigned char *s, *d;
+ mipmapfunc_t mipmap_func = NULL;
+ filterfunc_t filter_func = NULL;
+ wrapfunc_t wrap_func = NULL;
+ float support = 0.0f;
+ const int has_alpha = (bpp >= 3);
+ float alpha_test_coverage = 1;
+
+ if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST)
+ {
+ mipmap_func = scale_image_nearest;
+ }
+ else
+ {
+ if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) ||
+ (filter >= DDS_MIPMAP_FILTER_MAX))
+ filter = DDS_MIPMAP_FILTER_BOX;
+
+ mipmap_func = scale_image;
+
+ for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i)
+ {
+ if (filter == filters[i].filter)
+ {
+ filter_func = filters[i].func;
+ support = filters[i].support;
+ break;
+ }
+ }
+ }
+
+ switch (wrap)
+ {
+ case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break;
+ case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break;
+ case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break;
+ default: wrap_func = wrap_clamp; break;
+ }
+
+ if (has_alpha && preserve_alpha_coverage)
+ {
+ alpha_test_coverage = calc_alpha_test_coverage(src, width, height, bpp,
+ alpha_test_threshold,
+ 1.0f);
+ }
+
+ memcpy (dst, src, width * height * bpp);
+
+ s = dst;
+ d = dst + (width * height * bpp);
+
+ sw = width;
+ sh = height;
+
+ for (i = 1; i < mipmaps; ++i)
+ {
+ dw = MAX(1, sw >> 1);
+ dh = MAX(1, sh >> 1);
+
+ mipmap_func(d, dw, dh, s, sw, sh, bpp, filter_func, support, wrap_func, gc, gamma);
+
+ if (has_alpha && preserve_alpha_coverage)
+ {
+ scale_alpha_to_coverage(d, dw, dh, bpp, alpha_test_coverage, alpha_test_threshold);
+ }
+
+ s = d;
+ sw = dw;
+ sh = dh;
+ d += (dw * dh * bpp);
+ }
+
+ return 1;
+}
+
+int
+generate_volume_mipmaps (unsigned char *dst,
+ unsigned char *src,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ int bpp,
+ int indexed,
+ int mipmaps,
+ int filter,
+ int wrap,
+ int gc,
+ float gamma)
+{
+ int i;
+ unsigned int sw, sh, sd;
+ unsigned int dw, dh, dd;
+ unsigned char *s, *d;
+ volmipmapfunc_t mipmap_func = NULL;
+ filterfunc_t filter_func = NULL;
+ wrapfunc_t wrap_func = NULL;
+ float support = 0.0f;
+
+ if (indexed || filter == DDS_MIPMAP_FILTER_NEAREST)
+ {
+ mipmap_func = scale_volume_image_nearest;
+ }
+ else
+ {
+ if ((filter <= DDS_MIPMAP_FILTER_DEFAULT) ||
+ (filter >= DDS_MIPMAP_FILTER_MAX))
+ filter = DDS_MIPMAP_FILTER_BOX;
+
+ mipmap_func = scale_volume_image;
+
+ for (i = 0; filters[i].filter != DDS_MIPMAP_FILTER_MAX; ++i)
+ {
+ if (filter == filters[i].filter)
+ {
+ filter_func = filters[i].func;
+ support = filters[i].support;
+ break;
+ }
+ }
+ }
+
+ switch (wrap)
+ {
+ case DDS_MIPMAP_WRAP_MIRROR: wrap_func = wrap_mirror; break;
+ case DDS_MIPMAP_WRAP_REPEAT: wrap_func = wrap_repeat; break;
+ case DDS_MIPMAP_WRAP_CLAMP: wrap_func = wrap_clamp; break;
+ default: wrap_func = wrap_clamp; break;
+ }
+
+ memcpy (dst, src, width * height * depth * bpp);
+
+ s = dst;
+ d = dst + (width * height * depth * bpp);
+
+ sw = width;
+ sh = height;
+ sd = depth;
+
+ for (i = 1; i < mipmaps; ++i)
+ {
+ dw = MAX(1, sw >> 1);
+ dh = MAX(1, sh >> 1);
+ dd = MAX(1, sd >> 1);
+
+ mipmap_func (d, dw, dh, dd, s, sw, sh, sd, bpp, filter_func, support, wrap_func, gc, gamma);
+
+ s = d;
+ sw = dw;
+ sh = dh;
+ sd = dd;
+ d += (dw * dh * dd * bpp);
+ }
+
+ return 1;
+}
diff --git a/plug-ins/file-dds/mipmap.h b/plug-ins/file-dds/mipmap.h
new file mode 100644
index 0000000..166f326
--- /dev/null
+++ b/plug-ins/file-dds/mipmap.h
@@ -0,0 +1,75 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MIPMAP_H__
+#define __MIPMAP_H__
+
+int get_num_mipmaps (int width,
+ int height);
+unsigned int get_mipmapped_size (int width,
+ int height,
+ int bpp,
+ int level,
+ int num,
+ int format);
+unsigned int get_volume_mipmapped_size (int width,
+ int height,
+ int depth,
+ int bpp,
+ int level,
+ int num,
+ int format);
+int get_next_mipmap_dimensions (int *next_w,
+ int *next_h,
+ int curr_w,
+ int curr_h);
+
+float cubic_interpolate (float a,
+ float b,
+ float c,
+ float d,
+ float x);
+int generate_mipmaps (unsigned char *dst,
+ unsigned char *src,
+ unsigned int width,
+ unsigned int height,
+ int bpp,
+ int indexed,
+ int mipmaps,
+ int filter,
+ int wrap,
+ int gamma_correct,
+ float gamma,
+ int preserve_alpha_test_coverage,
+ float alpha_test_threshold);
+int generate_volume_mipmaps (unsigned char *dst,
+ unsigned char *src,
+ unsigned int width,
+ unsigned int height,
+ unsigned int depth,
+ int bpp,
+ int indexed,
+ int mipmaps,
+ int filter,
+ int wrap,
+ int gamma_correct,
+ float gamma);
+
+#endif /* __MIPMAP_H__ */
diff --git a/plug-ins/file-dds/misc.c b/plug-ins/file-dds/misc.c
new file mode 100644
index 0000000..c8c3593
--- /dev/null
+++ b/plug-ins/file-dds/misc.c
@@ -0,0 +1,261 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <libgimp/gimp.h>
+#include "misc.h"
+
+static inline float
+saturate (float a)
+{
+ if(a < 0) a = 0;
+ if(a > 1) a = 1;
+
+ return a;
+}
+
+void
+decode_ycocg_image (gint32 drawableID,
+ gboolean shadow)
+{
+ GeglBuffer *buffer, *sbuffer;
+ const Babl *format;
+ unsigned char *data;
+ unsigned int i, w, h, num_pixels;
+
+ const float offset = 0.5f * 256.0f / 255.0f;
+ float Y, Co, Cg, R, G, B;
+
+ buffer = gimp_drawable_get_buffer (drawableID);
+
+ if (shadow)
+ {
+ sbuffer = gimp_drawable_get_shadow_buffer (drawableID);
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
+ g_object_unref (buffer);
+ buffer = sbuffer;
+ }
+
+ format = babl_format ("R'G'B'A u8");
+
+ w = gegl_buffer_get_width (buffer);
+ h = gegl_buffer_get_height (buffer);
+ num_pixels = w * h;
+
+ data = g_malloc (num_pixels * 4);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gimp_progress_init ("Decoding YCoCg pixels...");
+
+ for (i = 0; i < num_pixels; ++i)
+ {
+ Y = (float)data[4 * i + 3] / 255.0f;
+ Co = (float)data[4 * i + 0] / 255.0f;
+ Cg = (float)data[4 * i + 1] / 255.0f;
+
+ /* convert YCoCg to RGB */
+ Co -= offset;
+ Cg -= offset;
+
+ R = saturate(Y + Co - Cg);
+ G = saturate(Y + Cg);
+ B = saturate(Y - Co - Cg);
+
+ /* copy new alpha from blue */
+ data[4 * i + 3] = data[4 * i + 2];
+
+ data[4 * i + 0] = (unsigned char)(R * 255.0f);
+ data[4 * i + 1] = (unsigned char)(G * 255.0f);
+ data[4 * i + 2] = (unsigned char)(B * 255.0f);
+
+ if ((i & 0x7fff) == 0)
+ gimp_progress_update ((float)i / (float)num_pixels);
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update (1.0);
+
+ gegl_buffer_flush (buffer);
+
+ if (shadow)
+ gimp_drawable_merge_shadow (drawableID, TRUE);
+
+ gimp_drawable_update (drawableID, 0, 0, w, h);
+
+ g_free (data);
+
+ g_object_unref (buffer);
+}
+
+void
+decode_ycocg_scaled_image (gint32 drawableID,
+ gboolean shadow)
+{
+ GeglBuffer *buffer, *sbuffer;
+ const Babl *format;
+ unsigned char *data;
+ unsigned int i, w, h, num_pixels;
+
+ const float offset = 0.5f * 256.0f / 255.0f;
+ float Y, Co, Cg, R, G, B, s;
+
+ buffer = gimp_drawable_get_buffer (drawableID);
+
+ if (shadow)
+ {
+ sbuffer = gimp_drawable_get_shadow_buffer(drawableID);
+ gegl_buffer_copy(buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
+ g_object_unref(buffer);
+ buffer = sbuffer;
+ }
+
+ format = babl_format ("R'G'B'A u8");
+
+ w = gegl_buffer_get_width (buffer);
+ h = gegl_buffer_get_height (buffer);
+ num_pixels = w * h;
+
+ data = g_malloc (num_pixels * 4);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gimp_progress_init ("Decoding YCoCg (scaled) pixels...");
+
+ for (i = 0; i < num_pixels; ++i)
+ {
+ Y = (float)data[4 * i + 3] / 255.0f;
+ Co = (float)data[4 * i + 0] / 255.0f;
+ Cg = (float)data[4 * i + 1] / 255.0f;
+ s = (float)data[4 * i + 2] / 255.0f;
+
+ /* convert YCoCg to RGB */
+ s = 1.0f / ((255.0f / 8.0f) * s + 1.0f);
+
+ Co = (Co - offset) * s;
+ Cg = (Cg - offset) * s;
+
+ R = saturate(Y + Co - Cg);
+ G = saturate(Y + Cg);
+ B = saturate(Y - Co - Cg);
+
+ data[4 * i + 0] = (unsigned char)(R * 255.0f);
+ data[4 * i + 1] = (unsigned char)(G * 255.0f);
+ data[4 * i + 2] = (unsigned char)(B * 255.0f);
+
+ /* set alpha to 1 */
+ data[4 * i + 3] = 255;
+
+ if ((i & 0x7fff) == 0)
+ gimp_progress_update ((float)i / (float)num_pixels);
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update (1.0);
+
+ gegl_buffer_flush (buffer);
+
+ if (shadow)
+ gimp_drawable_merge_shadow (drawableID, TRUE);
+
+ gimp_drawable_update (drawableID, 0, 0, w, h);
+
+ g_free (data);
+
+ g_object_unref (buffer);
+}
+
+void
+decode_alpha_exp_image (gint32 drawableID,
+ gboolean shadow)
+{
+ GeglBuffer *buffer, *sbuffer;
+ const Babl *format;
+ unsigned char *data;
+ unsigned int i, w, h, num_pixels;
+ int R, G, B, A;
+
+ buffer = gimp_drawable_get_buffer (drawableID);
+
+ if (shadow)
+ {
+ sbuffer = gimp_drawable_get_shadow_buffer (drawableID);
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, sbuffer, NULL);
+ g_object_unref (buffer);
+ buffer = sbuffer;
+ }
+
+ format = babl_format ("R'G'B'A u8");
+
+ w = gegl_buffer_get_width (buffer);
+ h = gegl_buffer_get_height (buffer);
+ num_pixels = w * h;
+
+ data = g_malloc (num_pixels * 4);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE(0, 0, w, h), 1.0, format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gimp_progress_init ("Decoding Alpha-exponent pixels...");
+
+ for (i = 0; i < num_pixels; ++i)
+ {
+ R = data[4 * i + 0];
+ G = data[4 * i + 1];
+ B = data[4 * i + 2];
+ A = data[4 * i + 3];
+
+ R = (R * A + 1) >> 8;
+ G = (G * A + 1) >> 8;
+ B = (B * A + 1) >> 8;
+ A = 255;
+
+ data[4 * i + 0] = R;
+ data[4 * i + 1] = G;
+ data[4 * i + 2] = B;
+ data[4 * i + 3] = A;
+
+ if ((i & 0x7fff) == 0)
+ gimp_progress_update ((float)i / (float)num_pixels);
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE(0, 0, w, h), 0, format, data,
+ GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update (1.0);
+
+ gegl_buffer_flush (buffer);
+
+ if (shadow)
+ gimp_drawable_merge_shadow (drawableID, TRUE);
+
+ gimp_drawable_update (drawableID, 0, 0, w, h);
+
+ g_free (data);
+
+ g_object_unref (buffer);
+}
diff --git a/plug-ins/file-dds/misc.h b/plug-ins/file-dds/misc.h
new file mode 100644
index 0000000..73656ee
--- /dev/null
+++ b/plug-ins/file-dds/misc.h
@@ -0,0 +1,31 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MISC_H__
+#define __MISC_H__
+
+void decode_ycocg_image (gint32 drawableID,
+ gboolean shadow);
+void decode_ycocg_scaled_image (gint32 drawableID,
+ gboolean shadow);
+void decode_alpha_exp_image (gint32 drawableID,
+ gboolean shadow);
+
+#endif /* __MISC_H__ */
diff --git a/plug-ins/file-dds/mktables.c b/plug-ins/file-dds/mktables.c
new file mode 100644
index 0000000..99eef6d
--- /dev/null
+++ b/plug-ins/file-dds/mktables.c
@@ -0,0 +1,130 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+static int mul8bit(int a, int b)
+{
+ int t = a * b + 128;
+ return((t + (t >> 8)) >> 8);
+}
+
+static int lerp13(int a, int b)
+{
+#if 0
+ return(a + mul8bit(b - a, 0x55));
+#else
+ return((2 * a + b) / 3);
+#endif
+}
+
+static void prepare_opt_table(unsigned char *tab,
+ const unsigned char *expand, int size)
+{
+ int i, mn, mx, bestE, minE, maxE, e;
+
+ for(i = 0; i < 256; ++i)
+ {
+ bestE = 256 * 100;
+
+ for(mn = 0; mn < size; ++mn)
+ {
+ for(mx = 0; mx < size; ++mx)
+ {
+ minE = expand[mn];
+ maxE = expand[mx];
+ e = abs(lerp13(maxE, minE) - i) * 100;
+
+ e += abs(mx - mn) * 3;
+
+ if(e < bestE)
+ {
+ tab[i * 2 + 0] = mx;
+ tab[i * 2 + 1] = mn;
+ bestE = e;
+ }
+ }
+ }
+ }
+}
+
+#if 0
+int main(void)
+{
+ FILE *fp;
+ int i, v;
+ unsigned char expand5[32];
+ unsigned char expand6[64];
+ unsigned char quantRB[256 + 16];
+ unsigned char quantG[256 + 16];
+ unsigned char omatch5[256][2];
+ unsigned char omatch6[256][2];
+
+ fp = fopen("dxt_tables.h", "w");
+ fprintf(fp,
+ "#ifndef DXT_TABLES_H\n"
+ "#define DXT_TABLES_H\n\n");
+
+ for(i = 0; i < 32; ++i)
+ expand5[i] = (i << 3) | (i >> 2);
+
+ for(i = 0; i < 64; ++i)
+ expand6[i] = (i << 2) | (i >> 4);
+
+ for(i = 0; i < 256 + 16; ++i)
+ {
+ v = i - 8;
+ if(v < 0) v = 0;
+ if(v > 255) v = 255;
+ quantRB[i] = expand5[mul8bit(v, 31)];
+ quantG[i] = expand6[mul8bit(v, 63)];
+ }
+
+ fprintf(fp,
+ "static const unsigned char quantRB[256 + 16] =\n"
+ "{");
+ for(i = 0; i < 256 + 16; ++i)
+ {
+ if(i % 8 == 0) fprintf(fp, "\n ");
+ fprintf(fp, "0x%02x, ", quantRB[i]);
+ }
+ fprintf(fp, "\n};\n\n");
+
+ fprintf(fp,
+ "static const unsigned char quantG[256 + 16] =\n"
+ "{");
+ for(i = 0; i < 256 + 16; ++i)
+ {
+ if(i % 8 == 0) fprintf(fp, "\n ");
+ fprintf(fp, "0x%02x, ", quantG[i]);
+ }
+ fprintf(fp, "\n};\n\n");
+
+ prepare_opt_table(&omatch5[0][0], expand5, 32);
+ prepare_opt_table(&omatch6[0][0], expand6, 64);
+
+ fprintf(fp,
+ "static const unsigned char omatch5[256][2] =\n"
+ "{");
+ for(i = 0; i < 256; ++i)
+ {
+ if(i % 4 == 0) fprintf(fp, "\n ");
+ fprintf(fp, "{0x%02x, 0x%02x}, ", omatch5[i][0], omatch5[i][1]);
+ }
+ fprintf(fp, "\n};\n\n");
+
+ fprintf(fp,
+ "static const unsigned char omatch6[256][2] =\n"
+ "{");
+ for(i = 0; i < 256; ++i)
+ {
+ if(i % 4 == 0) fprintf(fp, "\n ");
+ fprintf(fp, "{0x%02x, 0x%02x}, ", omatch6[i][0], omatch6[i][1]);
+ }
+ fprintf(fp, "\n};\n\n");
+
+ fprintf(fp, "#endif\n");
+
+ fclose(fp);
+
+ return(0);
+}
+#endif
diff --git a/plug-ins/file-dds/vec.h b/plug-ins/file-dds/vec.h
new file mode 100644
index 0000000..cc3c344
--- /dev/null
+++ b/plug-ins/file-dds/vec.h
@@ -0,0 +1,245 @@
+/*
+ * DDS GIMP plugin
+ *
+ * Copyright (C) 2004-2012 Shawn Kirst <skirst@gmail.com>,
+ * with parts (C) 2003 Arne Reuter <homepage@arnereuter.de> where specified.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VEC_H__
+#define __VEC_H__
+
+#include <math.h>
+
+#ifdef __SSE__
+#define USE_SSE 1
+#endif
+
+#ifdef USE_SSE
+#include <immintrin.h>
+#endif
+
+#include "imath.h"
+
+typedef float vec4_t __attribute__((vector_size(16)));
+typedef float sym3x3_t[6];
+
+#define VEC4_CONST4(x, y, z, w) {x, y, z, w}
+#define VEC4_CONST3(x, y, z) {x, y, z, 0.0f}
+#define VEC4_CONST1(x) {x, x, x, x}
+
+static inline vec4_t
+vec4_set (float x,
+ float y,
+ float z,
+ float w)
+{
+#ifdef USE_SSE
+ return _mm_setr_ps(x, y, z, w);
+#else
+ vec4_t v = { x, y, z, w };
+ return v;
+#endif
+}
+
+static inline vec4_t
+vec4_set1 (float f)
+{
+#ifdef USE_SSE
+ return _mm_set1_ps(f);
+#else
+ vec4_t v = { f, f, f, f };
+ return v;
+#endif
+}
+
+static inline vec4_t
+vec4_zero (void)
+{
+#ifdef USE_SSE
+ return _mm_setzero_ps();
+#else
+ vec4_t v = { 0, 0, 0, 0 };
+ return v;
+#endif
+}
+
+static inline void
+vec4_store (float *f,
+ const vec4_t v)
+{
+#ifdef USE_SSE
+ _mm_store_ps (f, v);
+#else
+ f[0] = v[0]; f[1] = v[1]; f[2] = v[2]; f[3] = v[3];
+#endif
+}
+
+static inline vec4_t
+vec4_splatx (const vec4_t v)
+{
+#ifdef USE_SSE
+ return _mm_shuffle_ps(v, v, 0x00);
+#else
+ vec4_t r = { v[0], v[0], v[0], v[0] };
+ return r;
+#endif
+}
+
+static inline vec4_t
+vec4_splaty (const vec4_t v)
+{
+#ifdef USE_SSE
+ return _mm_shuffle_ps(v, v, 0x55);
+#else
+ vec4_t r = { v[1], v[1], v[1], v[1] };
+ return r;
+#endif
+}
+
+static inline vec4_t
+vec4_splatz (const vec4_t v)
+{
+#ifdef USE_SSE
+ return _mm_shuffle_ps(v, v, 0xaa);
+#else
+ vec4_t r = { v[2], v[2], v[2], v[2] };
+ return r;
+#endif
+}
+
+static inline vec4_t
+vec4_splatw (const vec4_t v)
+{
+#ifdef USE_SSE
+ return _mm_shuffle_ps(v, v, 0xff);
+#else
+ vec4_t r = { v[3], v[3], v[3], v[3] };
+ return r;
+#endif
+}
+
+static inline vec4_t
+vec4_rcp (const vec4_t v)
+{
+#ifdef USE_SSE
+ __m128 est = _mm_rcp_ps (v);
+ __m128 diff = _mm_sub_ps (_mm_set1_ps(1.0f), _mm_mul_ps(est, v));
+ return _mm_add_ps(_mm_mul_ps(diff, est), est);
+#else
+ vec4_t one = { 1.0f, 1.0f, 1.0f, 1.0f };
+ return one / v;
+#endif
+}
+
+static inline vec4_t
+vec4_min (const vec4_t a,
+ const vec4_t b)
+{
+#ifdef USE_SSE
+ return _mm_min_ps(a, b);
+#else
+ return vec4_set (MIN(a[0], b[0]), MIN(a[1], b[1]), MIN(a[2], b[2]), MIN(a[3], b[3]));
+#endif
+}
+
+static inline vec4_t
+vec4_max (const vec4_t a,
+ const vec4_t b)
+{
+#ifdef USE_SSE
+ return _mm_max_ps (a, b);
+#else
+ return vec4_set (MAX(a[0], b[0]), MAX(a[1], b[1]), MAX(a[2], b[2]), MAX(a[3], b[3]));
+#endif
+}
+
+static inline vec4_t
+vec4_trunc (const vec4_t v)
+{
+#ifdef USE_SSE
+# ifdef __SSE4_1__
+ return _mm_round_ps(v, _MM_FROUND_TRUNC);
+# elif defined(__SSE2__)
+ return _mm_cvtepi32_ps(_mm_cvttps_epi32(v));
+# else
+ // convert to ints
+ __m128 in = v;
+ __m64 lo = _mm_cvttps_pi32(in);
+ __m64 hi = _mm_cvttps_pi32(_mm_movehl_ps(in, in));
+ // convert to floats
+ __m128 part = _mm_movelh_ps(in, _mm_cvtpi32_ps(in, hi));
+ __m128 trunc = _mm_cvtpi32_ps(part, lo);
+ // clear mmx state
+ _mm_empty ();
+ return trunc;
+# endif
+#else
+ vec4_t r = { v[0] > 0.0f ? floorf(v[0]) : ceil(v[0]),
+ v[1] > 0.0f ? floorf(v[1]) : ceil(v[1]),
+ v[2] > 0.0f ? floorf(v[2]) : ceil(v[2]),
+ v[3] > 0.0f ? floorf(v[3]) : ceil(v[3]), };
+ return r;
+#endif
+}
+
+static inline float
+vec4_accum (const vec4_t v)
+{
+#ifdef USE_SSE
+ float rv;
+ __m128 t;
+# ifdef __SSE3__
+ t = _mm_hadd_ps(v, v);
+ t = _mm_hadd_ps(t, t);
+# else
+ t = _mm_add_ps(v, _mm_movehl_ps(v, v));
+ t = _mm_add_ss(t, _mm_shuffle_ps(t, t, 0x01));
+# endif
+ _mm_store_ss(&rv, t);
+ return rv;
+#else
+ return v[0] + v[1] + v[2] + v[3];
+#endif
+}
+
+static inline float
+vec4_dot (const vec4_t a,
+ const vec4_t b)
+{
+#if defined(USE_SSE) && defined(__SSE4_1__)
+ float rv;
+ __m128 t = _mm_dp_ps(a, b, 0xff);
+ _mm_store_ss(&rv, t);
+ return rv;
+#else
+ return vec4_accum(a * b);
+#endif
+}
+
+static inline int
+vec4_cmplt (const vec4_t a,
+ const vec4_t b)
+{
+#ifdef USE_SSE
+ __m128 bits = _mm_cmplt_ps(a, b);
+ int val = _mm_movemask_ps(bits);
+ return val != 0;
+#else
+ return (a[0] < b[0]) || (a[1] < b[1]) || (a[2] < b[2]) || (a[3] < b[3]);
+#endif
+}
+
+#endif /* __VEC_H__ */
diff --git a/plug-ins/file-exr/Makefile.am b/plug-ins/file-exr/Makefile.am
new file mode 100644
index 0000000..5ad2b56
--- /dev/null
+++ b/plug-ins/file-exr/Makefile.am
@@ -0,0 +1,54 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+# if HAVE_WINDRES
+# include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+# file_exr_RC = file-exr.rc.o
+# endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-exr
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(OPENEXR_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-exr
+
+file_exr_SOURCES = \
+ exr-attribute-blob.h \
+ file-exr.c \
+ openexr-wrapper.cc \
+ openexr-wrapper.h
+
+file_exr_LDADD = \
+ $(OPENEXR_LIBS) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(LCMS_LIBS) \
+ $(file_exr_RC)
diff --git a/plug-ins/file-exr/Makefile.in b/plug-ins/file-exr/Makefile.in
new file mode 100644
index 0000000..811d55b
--- /dev/null
+++ b/plug-ins/file-exr/Makefile.in
@@ -0,0 +1,1036 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-exr$(EXEEXT)
+subdir = plug-ins/file-exr
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_exr_OBJECTS = file-exr.$(OBJEXT) openexr-wrapper.$(OBJEXT)
+file_exr_OBJECTS = $(am_file_exr_OBJECTS)
+am__DEPENDENCIES_1 =
+file_exr_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file-exr.Po \
+ ./$(DEPDIR)/openexr-wrapper.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(file_exr_SOURCES)
+DIST_SOURCES = $(file_exr_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-exr
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+
+# if HAVE_WINDRES
+# include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+# file_exr_RC = file-exr.rc.o
+# endif
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(OPENEXR_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ -I$(includedir)
+
+file_exr_SOURCES = \
+ exr-attribute-blob.h \
+ file-exr.c \
+ openexr-wrapper.cc \
+ openexr-wrapper.h
+
+file_exr_LDADD = \
+ $(OPENEXR_LIBS) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(LCMS_LIBS) \
+ $(file_exr_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-exr/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-exr/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-exr$(EXEEXT): $(file_exr_OBJECTS) $(file_exr_DEPENDENCIES) $(EXTRA_file_exr_DEPENDENCIES)
+ @rm -f file-exr$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(file_exr_OBJECTS) $(file_exr_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-exr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openexr-wrapper.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file-exr.Po
+ -rm -f ./$(DEPDIR)/openexr-wrapper.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file-exr.Po
+ -rm -f ./$(DEPDIR)/openexr-wrapper.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-exr/exr-attribute-blob.h b/plug-ins/file-exr/exr-attribute-blob.h
new file mode 100644
index 0000000..449f252
--- /dev/null
+++ b/plug-ins/file-exr/exr-attribute-blob.h
@@ -0,0 +1,97 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * exr-attribute-blob.h
+ * copyright (c) 2012 johannes hanika
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <ciso646>
+#include <inttypes.h>
+
+#if defined(_LIBCPP_VERSION)
+#include <memory>
+#else
+#include <tr1/memory>
+#endif
+
+#include <OpenEXR/ImfFrameBuffer.h>
+#include <OpenEXR/ImfTestFile.h>
+#include <OpenEXR/ImfInputFile.h>
+#include <OpenEXR/ImfTiledInputFile.h>
+#include <OpenEXR/ImfChannelList.h>
+#include <OpenEXR/ImfStandardAttributes.h>
+
+#ifdef OPENEXR_IMF_INTERNAL_NAMESPACE
+#define IMF_NS OPENEXR_IMF_INTERNAL_NAMESPACE
+#else
+#define IMF_NS Imf
+#endif
+
+// this stores our exif data as a blob.
+
+template <typename T> struct array_deleter
+{
+ void operator()(T const *p)
+ {
+ delete[] p;
+ }
+};
+
+namespace IMF_NS
+{
+class Blob
+{
+public:
+ Blob() : size(0), data((uint8_t *)NULL)
+ {
+ }
+
+ Blob(uint32_t _size, uint8_t *_data) : size(_size)
+ {
+ uint8_t *tmp_ptr = new uint8_t[_size];
+ memcpy(tmp_ptr, _data, _size);
+ data.reset(tmp_ptr, array_deleter<uint8_t>());
+ }
+
+ uint32_t size;
+#if defined(_LIBCPP_VERSION)
+ std::shared_ptr<uint8_t> data;
+#else
+ std::tr1::shared_ptr<uint8_t> data;
+#endif
+};
+
+
+typedef IMF_NS::TypedAttribute<IMF_NS::Blob> BlobAttribute;
+template <> const char *BlobAttribute::staticTypeName()
+{
+ return "blob";
+}
+template <> void BlobAttribute::writeValueTo(OStream &os, int version) const
+{
+ Xdr::write<StreamIO>(os, _value.size);
+ Xdr::write<StreamIO>(os, (char *)(_value.data.get()), _value.size);
+}
+
+template <> void BlobAttribute::readValueFrom(IStream &is, int size, int version)
+{
+ Xdr::read<StreamIO>(is, _value.size);
+ _value.data.reset(new uint8_t[_value.size], array_deleter<uint8_t>());
+ Xdr::read<StreamIO>(is, (char *)(_value.data.get()), _value.size);
+}
+}
diff --git a/plug-ins/file-exr/file-exr.c b/plug-ins/file-exr/file-exr.c
new file mode 100644
index 0000000..cac33cd
--- /dev/null
+++ b/plug-ins/file-exr/file-exr.c
@@ -0,0 +1,393 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "openexr-wrapper.h"
+
+#define LOAD_PROC "file-exr-load"
+#define PLUG_IN_BINARY "file-exr"
+#define PLUG_IN_VERSION "0.0.0"
+
+
+/*
+ * Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ gboolean interactive,
+ GError **error);
+
+static void sanitize_comment (gchar *comment);
+
+
+/*
+ * Some global variables.
+ */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in the OpenEXR file format",
+ "This plug-in loads OpenEXR files. ",
+ "Dominik Ernst <dernst@gmx.de>, "
+ "Mukund Sivaraman <muks@banu.com>",
+ "Dominik Ernst <dernst@gmx.de>, "
+ "Mukund Sivaraman <muks@banu.com>",
+ PLUG_IN_VERSION,
+ N_("OpenEXR image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-exr");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "exr",
+ "",
+ "0,long,0x762f3101");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ run_mode = param[0].data.d_int32;
+
+ image_ID = load_image (param[1].data.d_string,
+ run_mode == GIMP_RUN_INTERACTIVE, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ gboolean interactive,
+ GError **error)
+{
+ EXRLoader *loader;
+ gint width;
+ gint height;
+ gboolean has_alpha;
+ GimpImageBaseType image_type;
+ GimpPrecision image_precision;
+ gint32 image = -1;
+ GimpImageType layer_type;
+ gint32 layer;
+ const Babl *format;
+ GeglBuffer *buffer = NULL;
+ gint bpp;
+ gint tile_height;
+ gchar *pixels = NULL;
+ gint begin;
+ gint32 success = FALSE;
+ gchar *comment = NULL;
+ GimpColorProfile *profile = NULL;
+ guchar *exif_data;
+ guint exif_size;
+ guchar *xmp_data;
+ guint xmp_size;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ loader = exr_loader_new (filename);
+
+ if (! loader)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error opening file '%s' for reading"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ width = exr_loader_get_width (loader);
+ height = exr_loader_get_height (loader);
+
+ if ((width < 1) || (height < 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error querying image dimensions from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ has_alpha = exr_loader_has_alpha (loader) ? TRUE : FALSE;
+
+ switch (exr_loader_get_precision (loader))
+ {
+ case PREC_UINT:
+ image_precision = GIMP_PRECISION_U32_LINEAR;
+ break;
+ case PREC_HALF:
+ image_precision = GIMP_PRECISION_HALF_LINEAR;
+ break;
+ case PREC_FLOAT:
+ image_precision = GIMP_PRECISION_FLOAT_LINEAR;
+ break;
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error querying image precision from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ switch (exr_loader_get_image_type (loader))
+ {
+ case IMAGE_TYPE_RGB:
+ image_type = GIMP_RGB;
+ layer_type = has_alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ break;
+ case IMAGE_TYPE_GRAY:
+ image_type = GIMP_GRAY;
+ layer_type = has_alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
+ break;
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error querying image type from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+
+ image = gimp_image_new_with_precision (width, height,
+ image_type, image_precision);
+ if (image == -1)
+ {
+ g_set_error (error, 0, 0,
+ _("Could not create new image for '%s': %s"),
+ gimp_filename_to_utf8 (filename), gimp_get_pdb_error ());
+ goto out;
+ }
+
+ gimp_image_set_filename (image, filename);
+
+ /* try to load an icc profile, it will be generated on the fly if
+ * chromaticities are given
+ */
+ if (image_type == GIMP_RGB)
+ {
+ profile = exr_loader_get_profile (loader);
+
+ if (profile)
+ gimp_image_set_color_profile (image, profile);
+ }
+
+ layer = gimp_layer_new (image, _("Background"), width, height,
+ layer_type, 100,
+ gimp_image_get_default_new_layer_mode (image));
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+ format = gimp_drawable_get_format (layer);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ tile_height = gimp_tile_height ();
+ pixels = g_new0 (gchar, tile_height * width * bpp);
+
+ for (begin = 0; begin < height; begin += tile_height)
+ {
+ gint end;
+ gint num;
+ gint i;
+
+ end = MIN (begin + tile_height, height);
+ num = end - begin;
+
+ for (i = 0; i < num; i++)
+ {
+ gint retval;
+
+ retval = exr_loader_read_pixel_row (loader,
+ pixels + (i * width * bpp),
+ bpp, begin + i);
+ if (retval < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error reading pixel data from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto out;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, begin, width, num),
+ 0, NULL, pixels, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update ((gdouble) begin / (gdouble) height);
+ }
+
+ /* try to read the file comment */
+ comment = exr_loader_get_comment (loader);
+ if (comment)
+ {
+ GimpParasite *parasite;
+
+ sanitize_comment (comment);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1,
+ comment);
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ /* check if the image contains Exif or Xmp data and read it */
+ exif_data = exr_loader_get_exif (loader, &exif_size);
+ xmp_data = exr_loader_get_xmp (loader, &xmp_size);
+
+ if (exif_data || xmp_data)
+ {
+ GimpMetadata *metadata = gimp_metadata_new ();
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (exif_data)
+ {
+ gimp_metadata_set_from_exif (metadata, exif_data, exif_size, NULL);
+ g_free (exif_data);
+ }
+
+ if (xmp_data)
+ {
+ gimp_metadata_set_from_xmp (metadata, xmp_data, xmp_size, NULL);
+ g_free (xmp_data);
+ }
+
+ if (comment)
+ flags &= ~GIMP_METADATA_LOAD_COMMENT;
+
+ if (profile)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image, "image/exr",
+ metadata, flags, interactive);
+ g_object_unref (metadata);
+ }
+
+ gimp_progress_update (1.0);
+
+ success = TRUE;
+
+ out:
+ g_clear_object (&profile);
+ g_clear_object (&buffer);
+ g_clear_pointer (&pixels, g_free);
+ g_clear_pointer (&comment, g_free);
+ g_clear_pointer (&loader, exr_loader_unref);
+
+ if (success)
+ return image;
+
+ if (image != -1)
+ gimp_image_delete (image);
+
+ return -1;
+}
+
+/* copy & pasted from file-jpeg/jpeg-load.c */
+static void
+sanitize_comment (gchar *comment)
+{
+ const gchar *start_invalid;
+
+ if (! g_utf8_validate (comment, -1, &start_invalid))
+ {
+ guchar *c;
+
+ for (c = (guchar *) start_invalid; *c; c++)
+ {
+ if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
+ *c = '?';
+ }
+ }
+}
diff --git a/plug-ins/file-exr/openexr-wrapper.cc b/plug-ins/file-exr/openexr-wrapper.cc
new file mode 100644
index 0000000..c678646
--- /dev/null
+++ b/plug-ins/file-exr/openexr-wrapper.cc
@@ -0,0 +1,518 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string>
+
+#include <lcms2.h>
+
+/* These libgimp includes are not needed here at all, but this is a
+ * convenient place to make sure the public libgimp headers are
+ * C++-clean. The C++ compiler will choke on stuff like naming
+ * a struct member or parameter "private".
+ */
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+#include "libgimpbase/gimpbase.h"
+#include "libgimpmath/gimpmath.h"
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpconfig/gimpconfig.h"
+#include "libgimpmodule/gimpmodule.h"
+#include "libgimpthumb/gimpthumb.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#if defined(__MINGW32__)
+#ifndef FLT_EPSILON
+#define FLT_EPSILON __FLT_EPSILON__
+#endif
+#ifndef DBL_EPSILON
+#define DBL_EPSILON __DBL_EPSILON__
+#endif
+#ifndef LDBL_EPSILON
+#define LDBL_EPSILON __LDBL_EPSILON__
+#endif
+#endif
+
+#include <ImfInputFile.h>
+#include <ImfChannelList.h>
+#include <ImfRgbaFile.h>
+#include <ImfRgbaYca.h>
+#include <ImfStandardAttributes.h>
+
+#include "exr-attribute-blob.h"
+#include "openexr-wrapper.h"
+
+using namespace Imf;
+using namespace Imf::RgbaYca;
+using namespace Imath;
+
+static bool XYZ_equal(cmsCIEXYZ *a, cmsCIEXYZ *b)
+{
+ static const double epsilon = 0.0001;
+ // Y is encoding the luminance, we normalize that for comparison
+ return fabs ((a->X / a->Y * b->Y) - b->X) < epsilon &&
+ fabs ((a->Y / a->Y * b->Y) - b->Y) < epsilon &&
+ fabs ((a->Z / a->Y * b->Y) - b->Z) < epsilon;
+}
+
+struct _EXRLoader
+{
+ _EXRLoader(const char* filename) :
+ refcount_(1),
+ file_(filename),
+ data_window_(file_.header().dataWindow()),
+ channels_(file_.header().channels())
+ {
+ const Channel* chan;
+
+ if (channels_.findChannel("R") ||
+ channels_.findChannel("G") ||
+ channels_.findChannel("B"))
+ {
+ format_string_ = "RGB";
+ image_type_ = IMAGE_TYPE_RGB;
+
+ if ((chan = channels_.findChannel("R")))
+ pt_ = chan->type;
+ else if ((chan = channels_.findChannel("G")))
+ pt_ = chan->type;
+ else
+ pt_ = channels_.findChannel("B")->type;
+ }
+ else if (channels_.findChannel("Y") &&
+ (channels_.findChannel("RY") ||
+ channels_.findChannel("BY")))
+ {
+ format_string_ = "RGB";
+ image_type_ = IMAGE_TYPE_RGB;
+
+ pt_ = channels_.findChannel("Y")->type;
+
+ // FIXME: no chroma handling for now.
+ throw;
+ }
+ else if (channels_.findChannel("Y"))
+ {
+ format_string_ = "Y";
+ image_type_ = IMAGE_TYPE_GRAY;
+
+ pt_ = channels_.findChannel("Y")->type;
+ }
+ else
+ {
+ throw;
+ }
+
+ if (channels_.findChannel("A"))
+ {
+ format_string_.append("A");
+ has_alpha_ = true;
+ }
+ else
+ {
+ has_alpha_ = false;
+ }
+
+ switch (pt_)
+ {
+ case UINT:
+ format_string_.append(" u32");
+ bpc_ = 4;
+ break;
+ case HALF:
+ format_string_.append(" half");
+ bpc_ = 2;
+ break;
+ case FLOAT:
+ default:
+ format_string_.append(" float");
+ bpc_ = 4;
+ }
+ }
+
+ int readPixelRow(char* pixels,
+ int bpp,
+ int row)
+ {
+ const int actual_row = data_window_.min.y + row;
+ FrameBuffer fb;
+ // This is necessary because OpenEXR expects the buffer to begin at
+ // (0, 0). Though it probably results in some unmapped address,
+ // hopefully OpenEXR will not make use of it. :/
+ char* base = pixels - (data_window_.min.x * bpp);
+
+ switch (image_type_)
+ {
+ case IMAGE_TYPE_GRAY:
+ fb.insert("Y", Slice(pt_, base, bpp, 0, 1, 1, 0.5));
+ if (hasAlpha())
+ {
+ fb.insert("A", Slice(pt_, base + bpc_, bpp, 0, 1, 1, 1.0));
+ }
+ break;
+
+ case IMAGE_TYPE_RGB:
+ default:
+ fb.insert("R", Slice(pt_, base + (bpc_ * 0), bpp, 0, 1, 1, 0.0));
+ fb.insert("G", Slice(pt_, base + (bpc_ * 1), bpp, 0, 1, 1, 0.0));
+ fb.insert("B", Slice(pt_, base + (bpc_ * 2), bpp, 0, 1, 1, 0.0));
+ if (hasAlpha())
+ {
+ fb.insert("A", Slice(pt_, base + (bpc_ * 3), bpp, 0, 1, 1, 1.0));
+ }
+ }
+
+ file_.setFrameBuffer(fb);
+ file_.readPixels(actual_row);
+
+ return 0;
+ }
+
+ int getWidth() const {
+ return data_window_.max.x - data_window_.min.x + 1;
+ }
+
+ int getHeight() const {
+ return data_window_.max.y - data_window_.min.y + 1;
+ }
+
+ EXRPrecision getPrecision() const {
+ EXRPrecision prec;
+
+ switch (pt_)
+ {
+ case UINT:
+ prec = PREC_UINT;
+ break;
+ case HALF:
+ prec = PREC_HALF;
+ break;
+ case FLOAT:
+ default:
+ prec = PREC_FLOAT;
+ }
+
+ return prec;
+ }
+
+ EXRImageType getImageType() const {
+ return image_type_;
+ }
+
+ int hasAlpha() const {
+ return has_alpha_ ? 1 : 0;
+ }
+
+ GimpColorProfile *getProfile() const {
+ Chromaticities chromaticities;
+ float whiteLuminance = 1.0;
+
+ GimpColorProfile *linear_srgb_profile;
+ cmsHPROFILE linear_srgb_lcms;
+
+ GimpColorProfile *profile;
+ cmsHPROFILE lcms_profile;
+
+ cmsCIEXYZ *gimp_r_XYZ, *gimp_g_XYZ, *gimp_b_XYZ, *gimp_w_XYZ;
+ cmsCIEXYZ exr_r_XYZ, exr_g_XYZ, exr_b_XYZ, exr_w_XYZ;
+
+ // get the color information from the EXR
+ if (hasChromaticities (file_.header ()))
+ chromaticities = Imf::chromaticities (file_.header ());
+ else
+ return NULL;
+
+ if (Imf::hasWhiteLuminance (file_.header ()))
+ whiteLuminance = Imf::whiteLuminance (file_.header ());
+ else
+ return NULL;
+
+#if 0
+ std::cout << "hasChromaticities: "
+ << hasChromaticities (file_.header ())
+ << std::endl;
+ std::cout << "hasWhiteLuminance: "
+ << hasWhiteLuminance (file_.header ())
+ << std::endl;
+ std::cout << whiteLuminance << std::endl;
+ std::cout << chromaticities.red << std::endl;
+ std::cout << chromaticities.green << std::endl;
+ std::cout << chromaticities.blue << std::endl;
+ std::cout << chromaticities.white << std::endl;
+ std::cout << std::endl;
+#endif
+
+ cmsCIExyY whitePoint = { chromaticities.white.x,
+ chromaticities.white.y,
+ whiteLuminance };
+ cmsCIExyYTRIPLE CameraPrimaries = { { chromaticities.red.x,
+ chromaticities.red.y,
+ whiteLuminance },
+ { chromaticities.green.x,
+ chromaticities.green.y,
+ whiteLuminance },
+ { chromaticities.blue.x,
+ chromaticities.blue.y,
+ whiteLuminance } };
+
+ // get the primaries + wp from GIMP's internal linear sRGB profile
+ linear_srgb_profile = gimp_color_profile_new_rgb_srgb_linear ();
+ linear_srgb_lcms = gimp_color_profile_get_lcms_profile (linear_srgb_profile);
+
+ gimp_r_XYZ = (cmsCIEXYZ *) cmsReadTag (linear_srgb_lcms, cmsSigRedColorantTag);
+ gimp_g_XYZ = (cmsCIEXYZ *) cmsReadTag (linear_srgb_lcms, cmsSigGreenColorantTag);
+ gimp_b_XYZ = (cmsCIEXYZ *) cmsReadTag (linear_srgb_lcms, cmsSigBlueColorantTag);
+ gimp_w_XYZ = (cmsCIEXYZ *) cmsReadTag (linear_srgb_lcms, cmsSigMediaWhitePointTag);
+
+ cmsxyY2XYZ(&exr_r_XYZ, &CameraPrimaries.Red);
+ cmsxyY2XYZ(&exr_g_XYZ, &CameraPrimaries.Green);
+ cmsxyY2XYZ(&exr_b_XYZ, &CameraPrimaries.Blue);
+ cmsxyY2XYZ(&exr_w_XYZ, &whitePoint);
+
+ // ... and check if the data stored in the EXR matches GIMP's internal profile
+ bool exr_is_linear_srgb = XYZ_equal (&exr_r_XYZ, gimp_r_XYZ) &&
+ XYZ_equal (&exr_g_XYZ, gimp_g_XYZ) &&
+ XYZ_equal (&exr_b_XYZ, gimp_b_XYZ) &&
+ XYZ_equal (&exr_w_XYZ, gimp_w_XYZ);
+
+ // using GIMP's linear sRGB profile allows to skip the conversion popup
+ if (exr_is_linear_srgb)
+ return linear_srgb_profile;
+
+ // nope, it's something else. Clean up and build a new profile
+ g_object_unref (linear_srgb_profile);
+
+ // TODO: maybe factor this out into libgimpcolor/gimpcolorprofile.h ?
+ double Parameters[2] = { 1.0, 0.0 };
+ cmsToneCurve *Gamma[3];
+ Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildParametricToneCurve(0,
+ 1,
+ Parameters);
+ lcms_profile = cmsCreateRGBProfile (&whitePoint, &CameraPrimaries, Gamma);
+ cmsFreeToneCurve (Gamma[0]);
+ if (lcms_profile == NULL) return NULL;
+
+// cmsSetProfileVersion (lcms_profile, 2.1);
+ cmsMLU *mlu0 = cmsMLUalloc (NULL, 1);
+ cmsMLUsetASCII (mlu0, "en", "US", "(GIMP internal)");
+ cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
+ cmsMLUsetASCII (mlu1, "en", "US", "color profile from EXR chromaticities");
+ cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
+ cmsMLUsetASCII (mlu2, "en", "US", "color profile from EXR chromaticities");
+ cmsWriteTag (lcms_profile, cmsSigDeviceMfgDescTag, mlu0);
+ cmsWriteTag (lcms_profile, cmsSigDeviceModelDescTag, mlu1);
+ cmsWriteTag (lcms_profile, cmsSigProfileDescriptionTag, mlu2);
+ cmsMLUfree (mlu0);
+ cmsMLUfree (mlu1);
+ cmsMLUfree (mlu2);
+
+ profile = gimp_color_profile_new_from_lcms_profile (lcms_profile,
+ NULL);
+ cmsCloseProfile (lcms_profile);
+
+ return profile;
+ }
+
+ gchar *getComment() const {
+ char *result = NULL;
+ const Imf::StringAttribute *comment = file_.header().findTypedAttribute<Imf::StringAttribute>("comment");
+ if (comment)
+ result = g_strdup (comment->value().c_str());
+ return result;
+ }
+
+ guchar *getExif(guint *size) const {
+ guchar jpeg_exif[] = "Exif\0\0";
+ guchar *exif_data = NULL;
+ *size = 0;
+
+ const Imf::BlobAttribute *exif = file_.header().findTypedAttribute<Imf::BlobAttribute>("exif");
+
+ if (exif)
+ {
+ exif_data = (guchar *)(exif->value().data.get());
+ *size = exif->value().size;
+ // darktable appends a jpg-compatible exif00 string, so get rid of that again:
+ if ( ! memcmp (jpeg_exif, exif_data, sizeof(jpeg_exif)))
+ {
+ *size -= 6;
+ exif_data += 6;
+ }
+ }
+
+ return (guchar *)g_memdup (exif_data, *size);
+ }
+
+ guchar *getXmp(guint *size) const {
+ guchar *result = NULL;
+ *size = 0;
+ const Imf::StringAttribute *xmp = file_.header().findTypedAttribute<Imf::StringAttribute>("xmp");
+ if (xmp)
+ {
+ *size = xmp->value().size();
+ result = (guchar *) g_memdup (xmp->value().data(), *size);
+ }
+ return result;
+ }
+
+ size_t refcount_;
+ InputFile file_;
+ const Box2i data_window_;
+ const ChannelList& channels_;
+ PixelType pt_;
+ int bpc_;
+ EXRImageType image_type_;
+ bool has_alpha_;
+ std::string format_string_;
+};
+
+EXRLoader*
+exr_loader_new (const char *filename)
+{
+ EXRLoader* file;
+
+ // Don't let any exceptions propagate to the C layer.
+ try
+ {
+ Imf::BlobAttribute::registerAttributeType();
+ file = new EXRLoader(filename);
+ }
+ catch (...)
+ {
+ file = NULL;
+ }
+
+ return file;
+}
+
+EXRLoader*
+exr_loader_ref (EXRLoader *loader)
+{
+ ++loader->refcount_;
+ return loader;
+}
+
+void
+exr_loader_unref (EXRLoader *loader)
+{
+ if (--loader->refcount_ == 0)
+ {
+ delete loader;
+ }
+}
+
+int
+exr_loader_get_width (EXRLoader *loader)
+{
+ int width;
+ // Don't let any exceptions propagate to the C layer.
+ try
+ {
+ width = loader->getWidth();
+ }
+ catch (...)
+ {
+ width = -1;
+ }
+
+ return width;
+}
+
+int
+exr_loader_get_height (EXRLoader *loader)
+{
+ int height;
+ // Don't let any exceptions propagate to the C layer.
+ try
+ {
+ height = loader->getHeight();
+ }
+ catch (...)
+ {
+ height = -1;
+ }
+
+ return height;
+}
+
+EXRImageType
+exr_loader_get_image_type (EXRLoader *loader)
+{
+ // This does not throw.
+ return loader->getImageType();
+}
+
+EXRPrecision
+exr_loader_get_precision (EXRLoader *loader)
+{
+ // This does not throw.
+ return loader->getPrecision();
+}
+
+int
+exr_loader_has_alpha (EXRLoader *loader)
+{
+ // This does not throw.
+ return loader->hasAlpha();
+}
+
+GimpColorProfile *
+exr_loader_get_profile (EXRLoader *loader)
+{
+ return loader->getProfile ();
+}
+
+gchar *
+exr_loader_get_comment (EXRLoader *loader)
+{
+ return loader->getComment ();
+}
+
+guchar *
+exr_loader_get_exif (EXRLoader *loader,
+ guint *size)
+{
+ return loader->getExif (size);
+}
+
+guchar *
+exr_loader_get_xmp (EXRLoader *loader,
+ guint *size)
+{
+ return loader->getXmp (size);
+}
+
+int
+exr_loader_read_pixel_row (EXRLoader *loader,
+ char *pixels,
+ int bpp,
+ int row)
+{
+ int retval = -1;
+ // Don't let any exceptions propagate to the C layer.
+ try
+ {
+ retval = loader->readPixelRow(pixels, bpp, row);
+ }
+ catch (...)
+ {
+ retval = -1;
+ }
+
+ return retval;
+}
diff --git a/plug-ins/file-exr/openexr-wrapper.h b/plug-ins/file-exr/openexr-wrapper.h
new file mode 100644
index 0000000..13f252d
--- /dev/null
+++ b/plug-ins/file-exr/openexr-wrapper.h
@@ -0,0 +1,68 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OPENEXR_WRAPPER_H__
+#define __OPENEXR_WRAPPER_H__
+
+G_BEGIN_DECLS
+
+/* This is fully opaque on purpose, as the calling C code must not be
+ * exposed to more than this.
+ */
+typedef struct _EXRLoader EXRLoader;
+
+typedef enum
+{
+ PREC_UINT,
+ PREC_HALF,
+ PREC_FLOAT
+} EXRPrecision;
+
+typedef enum
+{
+ IMAGE_TYPE_RGB,
+ IMAGE_TYPE_GRAY
+} EXRImageType;
+
+
+EXRLoader * exr_loader_new (const char *filename);
+
+EXRLoader * exr_loader_ref (EXRLoader *loader);
+void exr_loader_unref (EXRLoader *loader);
+
+int exr_loader_get_width (EXRLoader *loader);
+int exr_loader_get_height (EXRLoader *loader);
+
+EXRPrecision exr_loader_get_precision (EXRLoader *loader);
+EXRImageType exr_loader_get_image_type (EXRLoader *loader);
+int exr_loader_has_alpha (EXRLoader *loader);
+
+GimpColorProfile * exr_loader_get_profile (EXRLoader *loader);
+gchar * exr_loader_get_comment (EXRLoader *loader);
+guchar * exr_loader_get_exif (EXRLoader *loader,
+ guint *size);
+guchar * exr_loader_get_xmp (EXRLoader *loader,
+ guint *size);
+
+int exr_loader_read_pixel_row (EXRLoader *loader,
+ char *pixels,
+ int bpp,
+ int row);
+
+G_END_DECLS
+
+#endif /* __OPENEXR_WRAPPER_H__ */
diff --git a/plug-ins/file-faxg3/Makefile.am b/plug-ins/file-faxg3/Makefile.am
new file mode 100644
index 0000000..b528d6a
--- /dev/null
+++ b/plug-ins/file-faxg3/Makefile.am
@@ -0,0 +1,49 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_faxg3_RC = file-faxg3.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-faxg3
+
+libexec_PROGRAMS = file-faxg3
+
+file_faxg3_SOURCES = \
+ faxg3.c \
+ g3.c \
+ g3.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimp) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmath) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_faxg3_RC)
diff --git a/plug-ins/file-faxg3/Makefile.in b/plug-ins/file-faxg3/Makefile.in
new file mode 100644
index 0000000..2f63348
--- /dev/null
+++ b/plug-ins/file-faxg3/Makefile.in
@@ -0,0 +1,1004 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-faxg3$(EXEEXT)
+subdir = plug-ins/file-faxg3
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_faxg3_OBJECTS = faxg3.$(OBJEXT) g3.$(OBJEXT)
+file_faxg3_OBJECTS = $(am_file_faxg3_OBJECTS)
+file_faxg3_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_faxg3_DEPENDENCIES = $(libgimp) $(libgimpconfig) $(libgimpcolor) \
+ $(libgimpbase) $(libgimpmath) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_faxg3_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/faxg3.Po ./$(DEPDIR)/g3.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_faxg3_SOURCES)
+DIST_SOURCES = $(file_faxg3_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-faxg3
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_faxg3_RC = file-faxg3.rc.o
+AM_LDFLAGS = $(mwindows)
+file_faxg3_SOURCES = \
+ faxg3.c \
+ g3.c \
+ g3.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(CAIRO_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimp) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmath) \
+ $(CAIRO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_faxg3_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-faxg3/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-faxg3/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-faxg3$(EXEEXT): $(file_faxg3_OBJECTS) $(file_faxg3_DEPENDENCIES) $(EXTRA_file_faxg3_DEPENDENCIES)
+ @rm -f file-faxg3$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_faxg3_OBJECTS) $(file_faxg3_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/faxg3.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/g3.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/faxg3.Po
+ -rm -f ./$(DEPDIR)/g3.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/faxg3.Po
+ -rm -f ./$(DEPDIR)/g3.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-faxg3/faxg3.c b/plug-ins/file-faxg3/faxg3.c
new file mode 100644
index 0000000..07ef436
--- /dev/null
+++ b/plug-ins/file-faxg3/faxg3.c
@@ -0,0 +1,626 @@
+/* This is a plugin for GIMP.
+ *
+ * Copyright (C) 1997 Jochen Friedrich
+ * Parts Copyright (C) 1995 Gert Doering
+ * Parts Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <glib.h> /* For G_OS_WIN32 */
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#include <libgimp/gimp.h>
+
+#include "g3.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-faxg3-load"
+#define VERSION "0.6"
+
+/* Declare local functions.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+
+static gint32 emitgimp (gint hcol,
+ gint row,
+ const gchar *bitmap,
+ gint bperrow,
+ const gchar *filename,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads g3 fax files",
+ "This plug-in loads Fax G3 Image files.",
+ "Jochen Friedrich",
+ "Jochen Friedrich, Gert Doering, Spencer Kimball & Peter Mattis",
+ VERSION,
+ N_("G3 fax image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/g3-fax");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "g3",
+ "",
+ "4,string,Research");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ gint32 image_ID;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+}
+
+#ifdef DEBUG
+void
+putbin (unsigned long d)
+{
+ unsigned long i = 0x80000000;
+
+ while (i != 0)
+ {
+ putc((d & i) ? '1' : '0', stderr);
+ i >>= 1;
+ }
+ putc('\n', stderr);
+}
+#endif
+
+static int byte_tab[256];
+/* static int o_stretch; */ /* -stretch: double each line */
+/* static int o_stretch_force=-1; */ /* -1: guess from filename */
+/* static int o_lj; */ /* -l: LJ output */
+/* static int o_turn; */ /* -t: turn 90 degrees right */
+
+struct g3_tree * black, * white;
+
+#define CHUNK 2048;
+static char rbuf[2048]; /* read buffer */
+static int rp; /* read pointer */
+static int rs; /* read buffer size */
+
+#define MAX_ROWS 4300
+#define MAX_COLS 1728 /* !! FIXME - command line parameter */
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ int data;
+ int hibit;
+ struct g3_tree *p;
+ int nr_pels;
+ int fd;
+ int color;
+ int i, rr, rsize;
+ int cons_eol;
+ int last_eol_row;
+
+ gint32 image_id;
+ gint bperrow = MAX_COLS/8; /* bytes per bit row */
+ gchar *bitmap; /* MAX_ROWS by (bperrow) bytes */
+ gchar *bp; /* bitmap pointer */
+ gint row;
+ gint max_rows; /* max. rows allocated */
+ gint col, hcol; /* column, highest column ever used */
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* initialize lookup trees */
+ build_tree (&white, t_white);
+ build_tree (&white, m_white);
+ build_tree (&black, t_black);
+ build_tree (&black, m_black);
+
+ init_byte_tab (0, byte_tab);
+
+ fd = g_open (filename, O_RDONLY | _O_BINARY, 0);
+
+ if (fd < 0)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ hibit = 0;
+ data = 0;
+
+ cons_eol = 0; /* consecutive EOLs read - zero yet */
+ last_eol_row = 0;
+
+ color = 0; /* start with white */
+ rr = 0;
+
+ rsize = lseek (fd, 0L, SEEK_END);
+ lseek (fd, 0L, 0);
+
+ rs = read (fd, rbuf, sizeof (rbuf));
+ if (rs < 0)
+ {
+ perror ("read");
+ close (fd);
+ gimp_quit ();
+ }
+
+ rr += rs;
+ gimp_progress_update ((float) rr / rsize / 2.0);
+
+ /* skip GhostScript header */
+ rp = (rs >= 64 && strcmp (rbuf + 1, "PC Research, Inc") == 0) ? 64 : 0;
+
+ /* initialize bitmap */
+
+ row = col = hcol = 0;
+
+ bitmap = g_new0 (gchar, (max_rows = MAX_ROWS) * MAX_COLS / 8);
+ if (! bitmap)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not create buffer to process image data."));
+ return -1;
+ }
+
+ bp = &bitmap[row * MAX_COLS / 8];
+
+ while (rs > 0 && cons_eol < 10) /* i.e., while (!EOF) */
+ {
+#ifdef DEBUG
+ g_printerr ("hibit=%2d, data=", hibit);
+ putbin (data);
+#endif
+
+ while (hibit < 20)
+ {
+ data |= (byte_tab[(int) (unsigned char) rbuf[rp++]] << hibit);
+ hibit += 8;
+
+ if (rp >= rs)
+ {
+ rs = read (fd, rbuf, sizeof (rbuf));
+ if (rs < 0)
+ { perror ("read2");
+ break;
+ }
+ rr += rs;
+ gimp_progress_update ((float) rr / rsize / 2.0);
+ rp = 0;
+ if (rs == 0)
+ goto do_write;
+ }
+
+#ifdef DEBUG
+ g_printerr ("hibit=%2d, data=", hibit);
+ putbin (data);
+#endif
+ }
+
+ if (color == 0) /* white */
+ p = white->nextb[data & BITM];
+ else /* black */
+ p = black->nextb[data & BITM];
+
+ while (p != NULL && ! (p->nr_bits))
+ {
+ data >>= FBITS;
+ hibit -= FBITS;
+ p = p->nextb[data & BITM];
+ }
+
+ if (p == NULL) /* invalid code */
+ {
+ g_printerr ("invalid code, row=%d, col=%d, file offset=%lx, skip to eol\n",
+ row, col, (unsigned long) lseek (fd, 0, 1) - rs + rp);
+
+ while ((data & 0x03f) != 0)
+ {
+ data >>= 1; hibit--;
+
+ if ( hibit < 20 )
+ {
+ data |= (byte_tab[(int) (unsigned char) rbuf[rp++]] << hibit);
+ hibit += 8;
+
+ if (rp >= rs) /* buffer underrun */
+ {
+ rs = read (fd, rbuf, sizeof (rbuf));
+
+ if (rs < 0)
+ { perror ("read4");
+ break;
+ }
+
+ rr += rs;
+ gimp_progress_update ((float) rr / rsize / 2.0);
+ rp = 0;
+ if (rs == 0)
+ goto do_write;
+ }
+ }
+ }
+ nr_pels = -1; /* handle as if eol */
+ }
+ else /* p != NULL <-> valid code */
+ {
+ data >>= p->nr_bits;
+ hibit -= p->nr_bits;
+
+ nr_pels = ((struct g3_leaf *) p)->nr_pels;
+#ifdef DEBUG
+ g_printerr ("PELs: %d (%c)\n", nr_pels, '0' + color);
+#endif
+ }
+
+ /* handle EOL (including fill bits) */
+ if (nr_pels == -1)
+ {
+#ifdef DEBUG
+ g_printerr ("hibit=%2d, data=", hibit);
+ putbin (data);
+#endif
+ /* skip filler 0bits -> seek for "1"-bit */
+ while ((data & 0x01) != 1)
+ {
+ if ((data & 0xf) == 0) /* nibble optimization */
+ {
+ hibit-= 4;
+ data >>= 4;
+ }
+ else
+ {
+ hibit--;
+ data >>= 1;
+ }
+
+ /* fill higher bits */
+ if (hibit < 20)
+ {
+ data |= ( byte_tab[(int) (unsigned char) rbuf[ rp++]] << hibit);
+ hibit += 8;
+
+ if (rp >= rs) /* buffer underrun */
+ {
+ rs = read (fd, rbuf, sizeof (rbuf));
+ if ( rs < 0 )
+ {
+ perror ("read3");
+ break;
+ }
+ rr += rs;
+ gimp_progress_update ((float) rr / rsize / 2.0);
+ rp = 0;
+ if (rs == 0)
+ goto do_write;
+ }
+ }
+#ifdef DEBUG
+ g_printerr ("hibit=%2d, data=", hibit );
+ putbin(data);
+#endif
+ } /* end skip 0bits */
+ hibit--;
+ data >>=1;
+
+ color = 0;
+
+ if (col == 0)
+ {
+ if (last_eol_row != row)
+ {
+ cons_eol++; /* consecutive EOLs */
+ last_eol_row = row;
+ }
+ }
+ else
+ {
+ if (col > hcol && col <= MAX_COLS)
+ hcol = col;
+ row++;
+
+ /* bitmap memory full? make it larger! */
+ if (row >= max_rows)
+ {
+ gchar *p = g_try_realloc (bitmap,
+ (max_rows += 500) * MAX_COLS / 8);
+ if (p == NULL)
+ {
+ perror ("realloc() failed, page truncated");
+ rs = 0;
+ }
+ else
+ {
+ bitmap = p;
+ memset (&bitmap[ row * MAX_COLS / 8 ], 0,
+ (max_rows - row) * MAX_COLS / 8);
+ }
+ }
+
+ col=0; bp = &bitmap[row * MAX_COLS / 8];
+ cons_eol = 0;
+ }
+ }
+ else /* not eol */
+ {
+ if (col + nr_pels > MAX_COLS)
+ nr_pels = MAX_COLS - col;
+
+ if (color == 0) /* white */
+ {
+ col += nr_pels;
+ }
+ else /* black */
+ {
+ register int bit = (0x80 >> (col & 07));
+ register char *w = & bp[col >> 3];
+
+ for (i = nr_pels; i > 0; i--)
+ {
+ *w |= bit;
+ bit >>=1;
+ if (bit == 0)
+ {
+ bit = 0x80;
+ w++;
+ }
+ col++;
+ }
+ }
+
+ if (nr_pels < 64)
+ color = !color; /* terminating code */
+ }
+ } /* end main loop */
+
+ do_write: /* write pbm (or whatever) file */
+
+ if (fd != 0)
+ close (fd); /* close input file */
+
+#ifdef DEBUG
+ g_printerr ("consecutive EOLs: %d, max columns: %d\n", cons_eol, hcol);
+#endif
+
+ image_id = emitgimp (hcol, row, bitmap, bperrow, filename, error);
+
+ g_free (bitmap);
+
+ return image_id;
+}
+
+/* hcol is the number of columns, row the number of rows
+ * bperrow is the number of bytes actually used by hcol, which may
+ * be greater than (hcol+7)/8 [in case of an unscaled g3 image less
+ * than 1728 pixels wide]
+ */
+
+static gint32
+emitgimp (gint hcol,
+ gint row,
+ const gchar *bitmap,
+ gint bperrow,
+ const gchar *filename,
+ GError **error)
+{
+ GeglBuffer *buffer;
+ gint32 image_ID;
+ gint32 layer_ID;
+ guchar *buf;
+ guchar tmp;
+ gint x, y;
+ gint xx, yy;
+ gint tile_height;
+
+ /* initialize */
+
+ tmp = 0;
+
+#ifdef DEBUG
+ g_printerr ("emit gimp: %d x %d\n", hcol, row);
+#endif
+
+ if (hcol > GIMP_MAX_IMAGE_SIZE || hcol <= 0 ||
+ row > GIMP_MAX_IMAGE_SIZE || row <= 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid image dimensions (%d x %d). "
+ "Image may be corrupt."),
+ hcol, row);
+ return -1;
+ }
+
+ image_ID = gimp_image_new (hcol, row, GIMP_GRAY);
+ if (image_ID == -1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not create image."));
+ return -1;
+ }
+ gimp_image_set_filename (image_ID, filename);
+
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ hcol,
+ row,
+ GIMP_GRAY_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ tile_height = gimp_tile_height ();
+#ifdef DEBUG
+ g_printerr ("tile height: %d\n", tile_height);
+#endif
+
+ buf = g_new (guchar, hcol * tile_height);
+ if (! buf)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not create buffer to process image data."));
+ g_object_unref (buffer);
+ gimp_image_delete(image_ID);
+ return -1;
+ }
+
+ xx = 0;
+ yy = 0;
+
+ for (y = 0; y < row; y++)
+ {
+ for (x = 0; x < hcol; x++)
+ {
+ if ((x & 7) == 0)
+ tmp = bitmap[y * bperrow + (x >> 3)];
+
+ buf[xx++] = tmp&(128 >> (x & 7)) ? 0 : 255;
+ }
+
+ if ((y - yy) == tile_height - 1)
+ {
+#ifdef DEBUG
+ g_printerr ("update tile height: %d\n", tile_height);
+#endif
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, yy, hcol, tile_height), 0,
+ NULL, buf, GEGL_AUTO_ROWSTRIDE);
+
+ gimp_progress_update (0.5 + (float) y / row / 2.0);
+
+ xx = 0;
+ yy += tile_height;
+ }
+ }
+
+ if (row - yy)
+ {
+#ifdef DEBUG
+ g_printerr ("update rest: %d\n", row-yy);
+#endif
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, yy, hcol, row - yy), 0,
+ NULL, buf, GEGL_AUTO_ROWSTRIDE);
+ }
+
+ gimp_progress_update (1.0);
+
+ g_free (buf);
+
+ g_object_unref (buffer);
+
+ return image_ID;
+}
diff --git a/plug-ins/file-faxg3/g3.c b/plug-ins/file-faxg3/g3.c
new file mode 100644
index 0000000..1a476e8
--- /dev/null
+++ b/plug-ins/file-faxg3/g3.c
@@ -0,0 +1,277 @@
+/* #ident "@(#)g3.c 3.1 95/08/30 Copyright (c) Gert Doering" */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "g3.h"
+
+struct g3code t_white[66] = {
+{ 0, 0, 0x0ac, 8 },
+{ 0, 1, 0x038, 6 },
+{ 0, 2, 0x00e, 4 },
+{ 0, 3, 0x001, 4 },
+{ 0, 4, 0x00d, 4 },
+{ 0, 5, 0x003, 4 },
+{ 0, 6, 0x007, 4 },
+{ 0, 7, 0x00f, 4 },
+{ 0, 8, 0x019, 5 },
+{ 0, 9, 0x005, 5 },
+{ 0, 10, 0x01c, 5 },
+{ 0, 11, 0x002, 5 },
+{ 0, 12, 0x004, 6 },
+{ 0, 13, 0x030, 6 },
+{ 0, 14, 0x00b, 6 },
+{ 0, 15, 0x02b, 6 },
+{ 0, 16, 0x015, 6 },
+{ 0, 17, 0x035, 6 },
+{ 0, 18, 0x072, 7 },
+{ 0, 19, 0x018, 7 },
+{ 0, 20, 0x008, 7 },
+{ 0, 21, 0x074, 7 },
+{ 0, 22, 0x060, 7 },
+{ 0, 23, 0x010, 7 },
+{ 0, 24, 0x00a, 7 },
+{ 0, 25, 0x06a, 7 },
+{ 0, 26, 0x064, 7 },
+{ 0, 27, 0x012, 7 },
+{ 0, 28, 0x00c, 7 },
+{ 0, 29, 0x040, 8 },
+{ 0, 30, 0x0c0, 8 },
+{ 0, 31, 0x058, 8 },
+{ 0, 32, 0x0d8, 8 },
+{ 0, 33, 0x048, 8 },
+{ 0, 34, 0x0c8, 8 },
+{ 0, 35, 0x028, 8 },
+{ 0, 36, 0x0a8, 8 },
+{ 0, 37, 0x068, 8 },
+{ 0, 38, 0x0e8, 8 },
+{ 0, 39, 0x014, 8 },
+{ 0, 40, 0x094, 8 },
+{ 0, 41, 0x054, 8 },
+{ 0, 42, 0x0d4, 8 },
+{ 0, 43, 0x034, 8 },
+{ 0, 44, 0x0b4, 8 },
+{ 0, 45, 0x020, 8 },
+{ 0, 46, 0x0a0, 8 },
+{ 0, 47, 0x050, 8 },
+{ 0, 48, 0x0d0, 8 },
+{ 0, 49, 0x04a, 8 },
+{ 0, 50, 0x0ca, 8 },
+{ 0, 51, 0x02a, 8 },
+{ 0, 52, 0x0aa, 8 },
+{ 0, 53, 0x024, 8 },
+{ 0, 54, 0x0a4, 8 },
+{ 0, 55, 0x01a, 8 },
+{ 0, 56, 0x09a, 8 },
+{ 0, 57, 0x05a, 8 },
+{ 0, 58, 0x0da, 8 },
+{ 0, 59, 0x052, 8 },
+{ 0, 60, 0x0d2, 8 },
+{ 0, 61, 0x04c, 8 },
+{ 0, 62, 0x0cc, 8 },
+{ 0, 63, 0x02c, 8 },
+{ 0, -1, 0, 11 }, /* 11 0-bits == EOL, special handling */
+{ 0, -1, 0, 0 }}; /* end of table */
+
+/* make-up codes white */
+struct g3code m_white[28] = {
+{ 0, 64, 0x01b, 5 },
+{ 0, 128, 0x009, 5 },
+{ 0, 192, 0x03a, 6 },
+{ 0, 256, 0x076, 7 },
+{ 0, 320, 0x06c, 8 },
+{ 0, 384, 0x0ec, 8 },
+{ 0, 448, 0x026, 8 },
+{ 0, 512, 0x0a6, 8 },
+{ 0, 576, 0x016, 8 },
+{ 0, 640, 0x0e6, 8 },
+{ 0, 704, 0x066, 9 },
+{ 0, 768, 0x166, 9 },
+{ 0, 832, 0x096, 9 },
+{ 0, 896, 0x196, 9 },
+{ 0, 960, 0x056, 9 },
+{ 0,1024, 0x156, 9 },
+{ 0,1088, 0x0d6, 9 },
+{ 0,1152, 0x1d6, 9 },
+{ 0,1216, 0x036, 9 },
+{ 0,1280, 0x136, 9 },
+{ 0,1344, 0x0b6, 9 },
+{ 0,1408, 0x1b6, 9 },
+{ 0,1472, 0x032, 9 },
+{ 0,1536, 0x132, 9 },
+{ 0,1600, 0x0b2, 9 },
+{ 0,1664, 0x006, 6 },
+{ 0,1728, 0x1b2, 9 },
+{ 0, -1, 0, 0} };
+
+
+struct g3code t_black[66] = {
+{ 0, 0, 0x3b0, 10 },
+{ 0, 1, 0x002, 3 },
+{ 0, 2, 0x003, 2 },
+{ 0, 3, 0x001, 2 },
+{ 0, 4, 0x006, 3 },
+{ 0, 5, 0x00c, 4 },
+{ 0, 6, 0x004, 4 },
+{ 0, 7, 0x018, 5 },
+{ 0, 8, 0x028, 6 },
+{ 0, 9, 0x008, 6 },
+{ 0, 10, 0x010, 7 },
+{ 0, 11, 0x050, 7 },
+{ 0, 12, 0x070, 7 },
+{ 0, 13, 0x020, 8 },
+{ 0, 14, 0x0e0, 8 },
+{ 0, 15, 0x030, 9 },
+{ 0, 16, 0x3a0, 10 },
+{ 0, 17, 0x060, 10 },
+{ 0, 18, 0x040, 10 },
+{ 0, 19, 0x730, 11 },
+{ 0, 20, 0x0b0, 11 },
+{ 0, 21, 0x1b0, 11 },
+{ 0, 22, 0x760, 11 },
+{ 0, 23, 0x0a0, 11 },
+{ 0, 24, 0x740, 11 },
+{ 0, 25, 0x0c0, 11 },
+{ 0, 26, 0x530, 12 },
+{ 0, 27, 0xd30, 12 },
+{ 0, 28, 0x330, 12 },
+{ 0, 29, 0xb30, 12 },
+{ 0, 30, 0x160, 12 },
+{ 0, 31, 0x960, 12 },
+{ 0, 32, 0x560, 12 },
+{ 0, 33, 0xd60, 12 },
+{ 0, 34, 0x4b0, 12 },
+{ 0, 35, 0xcb0, 12 },
+{ 0, 36, 0x2b0, 12 },
+{ 0, 37, 0xab0, 12 },
+{ 0, 38, 0x6b0, 12 },
+{ 0, 39, 0xeb0, 12 },
+{ 0, 40, 0x360, 12 },
+{ 0, 41, 0xb60, 12 },
+{ 0, 42, 0x5b0, 12 },
+{ 0, 43, 0xdb0, 12 },
+{ 0, 44, 0x2a0, 12 },
+{ 0, 45, 0xaa0, 12 },
+{ 0, 46, 0x6a0, 12 },
+{ 0, 47, 0xea0, 12 },
+{ 0, 48, 0x260, 12 },
+{ 0, 49, 0xa60, 12 },
+{ 0, 50, 0x4a0, 12 },
+{ 0, 51, 0xca0, 12 },
+{ 0, 52, 0x240, 12 },
+{ 0, 53, 0xec0, 12 },
+{ 0, 54, 0x1c0, 12 },
+{ 0, 55, 0xe40, 12 },
+{ 0, 56, 0x140, 12 },
+{ 0, 57, 0x1a0, 12 },
+{ 0, 58, 0x9a0, 12 },
+{ 0, 59, 0xd40, 12 },
+{ 0, 60, 0x340, 12 },
+{ 0, 61, 0x5a0, 12 },
+{ 0, 62, 0x660, 12 },
+{ 0, 63, 0xe60, 12 },
+{ 0, -1, 0x000, 11 },
+{ 0, -1, 0, 0 } };
+
+struct g3code m_black[28] = {
+{ 0, 64, 0x3c0, 10 },
+{ 0, 128, 0x130, 12 },
+{ 0, 192, 0x930, 12 },
+{ 0, 256, 0xda0, 12 },
+{ 0, 320, 0xcc0, 12 },
+{ 0, 384, 0x2c0, 12 },
+{ 0, 448, 0xac0, 12 },
+{ 0, 512, 0x6c0, 13 },
+{ 0, 576,0x16c0, 13 },
+{ 0, 640, 0xa40, 13 },
+{ 0, 704,0x1a40, 13 },
+{ 0, 768, 0x640, 13 },
+{ 0, 832,0x1640, 13 },
+{ 0, 896, 0x9c0, 13 },
+{ 0, 960,0x19c0, 13 },
+{ 0,1024, 0x5c0, 13 },
+{ 0,1088,0x15c0, 13 },
+{ 0,1152, 0xdc0, 13 },
+{ 0,1216,0x1dc0, 13 },
+{ 0,1280, 0x940, 13 },
+{ 0,1344,0x1940, 13 },
+{ 0,1408, 0x540, 13 },
+{ 0,1472,0x1540, 13 },
+{ 0,1536, 0xb40, 13 },
+{ 0,1600,0x1b40, 13 },
+{ 0,1664, 0x4c0, 13 },
+{ 0,1728,0x14c0, 13 },
+{ 0, -1, 0, 0 } };
+
+void tree_add_node( struct g3_tree *p, struct g3code * g3c,
+ int bit_code, int bit_length )
+{
+int i;
+
+ if ( bit_length <= FBITS ) /* leaf (multiple bits) */
+ {
+ g3c->nr_bits = bit_length; /* leaf tag */
+
+ if ( bit_length == FBITS ) /* full width */
+ {
+ p->nextb[ bit_code ] = (struct g3_tree *) g3c;
+ }
+ else /* fill bits */
+ for ( i=0; i< ( 1 << (FBITS-bit_length)); i++ )
+ {
+ p->nextb[ bit_code + ( i << bit_length ) ] = (struct g3_tree *) g3c;
+ }
+ }
+ else /* node */
+ {
+ struct g3_tree *p2;
+
+ p2 = p->nextb[ bit_code & BITM ];
+ if ( p2 == 0 ) /* no sub-node exists */
+ {
+ p2 = p->nextb[ bit_code & BITM ] =
+ ( struct g3_tree * ) calloc( 1, sizeof( struct g3_tree ));
+ if ( p2 == NULL ) { perror( "malloc 3" ); exit(11); }
+ p2->nr_bits = 0; /* node tag */
+
+ }
+ if ( p2->nr_bits != 0 )
+ {
+ g_printerr ("internal table setup error\n" ); exit(6);
+ }
+ tree_add_node( p2, g3c, bit_code >> FBITS, bit_length - FBITS );
+ }
+}
+
+void build_tree (struct g3_tree ** p, struct g3code * c )
+{
+ if ( *p == NULL )
+ {
+ (*p) = (struct g3_tree *) calloc( 1, sizeof(struct g3_tree) );
+ if ( *p == NULL ) { perror( "malloc(1)" ); exit(10); }
+
+ (*p)->nr_bits=0;
+ }
+
+ while ( c->bit_length != 0 )
+ {
+ tree_add_node( *p, c, c->bit_code, c->bit_length );
+ c++;
+ }
+}
+
+void init_byte_tab (int reverse, int byte_tab[] )
+{
+int i;
+ if ( reverse ) for ( i=0; i<256; i++ ) byte_tab[i] = i;
+ else
+ for ( i=0; i<256; i++ )
+ byte_tab[i] = ( ((i & 0x01) << 7) | ((i & 0x02) << 5) |
+ ((i & 0x04) << 3) | ((i & 0x08) << 1) |
+ ((i & 0x10) >> 1) | ((i & 0x20) >> 3) |
+ ((i & 0x40) >> 5) | ((i & 0x80) >> 7) );
+}
diff --git a/plug-ins/file-faxg3/g3.h b/plug-ins/file-faxg3/g3.h
new file mode 100644
index 0000000..50c7e16
--- /dev/null
+++ b/plug-ins/file-faxg3/g3.h
@@ -0,0 +1,55 @@
+/* #ident "@(#)g3.h 3.1 95/08/30 Copyright (c) Gert Doering" */
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+/* nr_bits is set to ( bit_length MOD FBITS ) by build_g3_tree,
+ * nr_pels is the number of pixels to write for that code,
+ * bit_code is the code itself (msb2lsb), and bit_length its length
+ */
+
+struct g3code { int nr_bits, nr_pels, bit_code, bit_length; };
+
+/* tables for makeup / terminal codes white / black, extended m_codes */
+extern struct g3code t_white[], m_white[], t_black[], m_black[], m_ext[];
+
+/* The number of bits looked up simultaneously determines the amount
+ * of memory used by the program - some values:
+ * 10 bits : 87 Kbytes, 8 bits : 20 Kbytes
+ * 5 bits : 6 Kbytes, 1 bit : 4 Kbytes
+ * - naturally, using less bits is also slower...
+ */
+
+/*
+#define FBITS 5
+#define BITM 0x1f
+*/
+
+#define FBITS 8
+#define BITM 0xff
+
+/*
+#define FBITS 12
+#define BITM 0xfff
+*/
+
+#define BITN 1<<FBITS
+
+struct g3_tree { int nr_bits;
+ struct g3_tree * nextb[ BITN ];
+ };
+
+#define g3_leaf g3code
+
+extern void tree_add_node ( struct g3_tree *p, struct g3code * g3c,
+ int bit_code, int bit_length );
+extern void build_tree ( struct g3_tree ** p, struct g3code * c );
+
+#ifdef DEBUG
+extern void print_g3_tree ( char * t, struct g3_tree * p );
+#endif
+
+extern void init_byte_tab ( int reverse, int byte_tab[] );
+
+
diff --git a/plug-ins/file-fits/Makefile.am b/plug-ins/file-fits/Makefile.am
new file mode 100644
index 0000000..0ea58fe
--- /dev/null
+++ b/plug-ins/file-fits/Makefile.am
@@ -0,0 +1,50 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_fits_RC = file-fits.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+AM_CFLAGS = -fno-strict-aliasing
+
+libexecdir = $(gimpplugindir)/plug-ins/file-fits
+
+libexec_PROGRAMS = file-fits
+
+file_fits_SOURCES = \
+ fits.c \
+ fits-io.c \
+ fits-io.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_fits_RC)
diff --git a/plug-ins/file-fits/Makefile.in b/plug-ins/file-fits/Makefile.in
new file mode 100644
index 0000000..562e880
--- /dev/null
+++ b/plug-ins/file-fits/Makefile.in
@@ -0,0 +1,1005 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-fits$(EXEEXT)
+subdir = plug-ins/file-fits
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_fits_OBJECTS = fits.$(OBJEXT) fits-io.$(OBJEXT)
+file_fits_OBJECTS = $(am_file_fits_OBJECTS)
+file_fits_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_fits_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_fits_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/fits-io.Po ./$(DEPDIR)/fits.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_fits_SOURCES)
+DIST_SOURCES = $(file_fits_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-fits
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_fits_RC = file-fits.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CFLAGS = -fno-strict-aliasing
+file_fits_SOURCES = \
+ fits.c \
+ fits-io.c \
+ fits-io.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_fits_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-fits/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-fits/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-fits$(EXEEXT): $(file_fits_OBJECTS) $(file_fits_DEPENDENCIES) $(EXTRA_file_fits_DEPENDENCIES)
+ @rm -f file-fits$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_fits_OBJECTS) $(file_fits_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fits-io.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fits.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/fits-io.Po
+ -rm -f ./$(DEPDIR)/fits.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/fits-io.Po
+ -rm -f ./$(DEPDIR)/fits.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-fits/fits-io.c b/plug-ins/file-fits/fits-io.c
new file mode 100644
index 0000000..9d429b7
--- /dev/null
+++ b/plug-ins/file-fits/fits-io.c
@@ -0,0 +1,2506 @@
+/******************************************************************************/
+/* Peter Kirchgessner */
+/* e-mail: peter@kirchgessner.net */
+/* WWW : http://www.kirchgessner.net */
+/******************************************************************************/
+/* #BEG-HDR */
+/* */
+/* Package : FITS reading/writing library */
+/* Modul-Name : fitsrw.c */
+/* Description : Support of reading/writing FITS-files */
+/* Function(s) : fits_new_filestruct - (local) initialize file structure*/
+/* fits_new_hdulist - (local) initialize hdulist struct*/
+/* fits_delete_filestruct - (local) delete file structure */
+/* fits_delete_recordlist - (local) delete record list */
+/* fits_delete_hdulist - (local) delete hdu list */
+/* fits_nan_32 - (local) check IEEE NaN values */
+/* fits_nan_64 - (local) check IEEE NaN values */
+/* fits_get_error - get error message */
+/* fits_set_error - (local) set error message */
+/* fits_drop_error - (local) remove an error message */
+/* fits_open - open a FITS file */
+/* fits_close - close a FITS file */
+/* fits_add_hdu - add a HDU to a FITS file */
+/* fits_add_card - add a card to the HDU */
+/* fits_print_header - print a single FITS header */
+/* fits_read_header - (local) read in FITS header */
+/* fits_write_header - write a FITS header */
+/* fits_decode_header - (local) decode a header */
+/* fits_eval_pixrange - (local) evaluate range of pixels */
+/* fits_decode_card - decode a card */
+/* fits_search_card - search a card in a record list */
+/* fits_image_info - get information about image */
+/* fits_seek_image - position to an image */
+/* fits_read_pixel - read pixel values from file */
+/* fits_to_pgmraw - convert FITS-file to PGM-file */
+/* pgmraw_to_fits - convert PGM-file to FITS-file */
+/* */
+/* Author : P. Kirchgessner */
+/* Date of Gen. : 12-Apr-97 */
+/* Last modified : 25-Aug-06 */
+/* Version : 0.12 */
+/* Compiler Opt. : */
+/* Changes : */
+/* #MOD-0001, nn, 20-Dec-97, Initialize some variables */
+/* #MOD-0002, pk, 16-Aug-06, Fix problems with internationalization */
+/* */
+/* #END-HDR */
+/******************************************************************************/
+/* References: */
+/* - NOST, Definition of the Flexible Image Transport System (FITS), */
+/* September 29, 1995, Standard, NOST 100-1.1 */
+/* (ftp://nssdc.gsfc.nasa.gov/pub/fits/fits_standard_ps.Z) */
+/* - The FITS IMAGE Extension. A Proposal. J.D. Ponz, R.W. Thompson, */
+/* J.R. Munoz, Feb. 7, 1992 */
+/* (ftp://www.cv.nrao.edu/fits/documents/standards/image.ps.gz) */
+/* */
+/******************************************************************************/
+
+#define VERSIO 0.12
+
+/******************************************************************************/
+/* FITS reading/writing library */
+/* Copyright (C) 1997 Peter Kirchgessner */
+/* (email: peter@kirchgessner.net, WWW: http://www.kirchgessner.net) */
+/* The library was developed for a FITS-plug-in to GIMP, the GNU Image */
+/* Manipulation Program. But it is completely independent to that (beside use */
+/* of glib). If someone finds it useful for other purposes, try to keep it */
+/* independent from your application. */
+/******************************************************************************/
+/* */
+/* This program is free software: you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either version 3 of the License, or */
+/* (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program. If not, see <https://www.gnu.org/licenses/>. */
+/******************************************************************************/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "fits-io.h"
+
+
+/* Declaration of local functions */
+
+static FitsFile * fits_new_filestruct (void);
+static FitsHduList * fits_new_hdulist (void);
+static void fits_delete_filestruct (FitsFile *ff);
+static void fits_delete_recordlist (FitsRecordList *rl);
+static void fits_delete_hdulist (FitsHduList *hl);
+static gint fits_nan_32 (guchar *value);
+static gint fits_nan_64 (guchar *value);
+static void fits_set_error (const char *errmsg);
+static void fits_drop_error (void);
+static FitsRecordList * fits_read_header (FILE *fp,
+ gint *nrec);
+static FitsHduList * fits_decode_header (FitsRecordList *hdr,
+ glong hdr_offset,
+ glong dat_offset);
+static gint fits_eval_pixrange (FILE *fp,
+ FitsHduList *hdu);
+
+
+/* Error handling like a FIFO */
+#define FITS_MAX_ERROR 16
+#define FITS_ERROR_LENGTH 256
+
+static gint fits_n_error = 0;
+static gchar fits_error[FITS_MAX_ERROR][FITS_ERROR_LENGTH];
+
+/* What byte ordering for IEEE-format we are running on ? */
+static gint fits_ieee32_intel = 0;
+static gint fits_ieee32_motorola = 0;
+static gint fits_ieee64_intel = 0;
+static gint fits_ieee64_motorola = 0;
+
+/* Macros */
+#define FITS_RETURN(msg, val) { fits_set_error (msg); return (val); }
+#define FITS_VRETURN(msg) { fits_set_error (msg); return; }
+
+/* Get pixel values from memory. p must be an (guchar *) */
+#define FITS_GETBITPIX16(p,val) val = ((p[0] << 8) | (p[1]))
+#define FITS_GETBITPIX32(p,val) val = \
+ ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3])
+
+/* Get floating point values from memory. p must be an (guchar *). */
+/* The floating point values must directly correspond */
+/* to machine representation. Otherwise it does not work. */
+#define FITS_GETBITPIXM32(p,val) \
+ { if (fits_ieee32_intel) {guchar uc[4]; \
+ uc[0] = p[3]; uc[1] = p[2]; uc[2] = p[1]; uc[3] = p[0]; \
+ val = *(FitsBitpixM32 *)uc; } \
+ else if (fits_ieee32_motorola) { val = *(FitsBitpixM32 *)p; } \
+ else if (fits_ieee64_motorola) {FitsBitpixM64 m64; \
+ guchar *uc= (guchar *)&m64; \
+ uc[0]=p[0]; uc[1]=p[1]; uc[2]=p[2]; uc[3]=p[3]; uc[4]=uc[5]=uc[6]=uc[7]=0; \
+ val = (FitsBitpixM32)m64; } \
+ else if (fits_ieee64_intel) {FitsBitpixM64 i64; \
+ guchar *uc= (guchar *)&i64; \
+ uc[0]=uc[1]=uc[2]=uc[3]=0; uc[7]=p[0]; uc[6]=p[1]; uc[5]=p[2]; uc[4]=p[3]; \
+ val = (FitsBitpixM32)i64;} }
+
+#define FITS_GETBITPIXM64(p,val) \
+ { if (fits_ieee64_intel) {guchar uc[8]; \
+ uc[0] = p[7]; uc[1] = p[6]; uc[2] = p[5]; uc[3] = p[4]; \
+ uc[4] = p[3]; uc[5] = p[2]; uc[6] = p[1]; uc[7] = p[0]; \
+ val = *(FitsBitpixM64 *)uc; } else val = *(FitsBitpixM64 *)p; }
+
+#define FITS_WRITE_BOOLCARD(fp,key,value) \
+{ gchar card[81]; \
+ g_snprintf (card, sizeof (card), \
+ "%-8.8s= %20s%50s", key, value ? "T" : "F", " "); \
+ fwrite (card, 1, 80, fp); }
+
+#define FITS_WRITE_LONGCARD(fp,key,value) \
+{ gchar card[81]; \
+ g_snprintf (card, sizeof (card), \
+ "%-8.8s= %20ld%50s", key, (long)value, " "); \
+ fwrite (card, 1, 80, fp); }
+
+#define FITS_WRITE_DOUBLECARD(fp,key,value) \
+{ gchar card[81], dbl[21], *istr; \
+ g_ascii_dtostr (dbl, sizeof(dbl), (gdouble)value); \
+ istr = strstr (dbl, "e"); \
+ if (istr) *istr = 'E'; \
+ g_snprintf (card, sizeof (card), \
+ "%-8.8s= %20.20s%50s", key, dbl, " "); \
+ fwrite (card, 1, 80, fp); }
+
+#define FITS_WRITE_STRINGCARD(fp,key,value) \
+{ gchar card[81]; int k;\
+ g_snprintf (card, sizeof (card), \
+ "%-8.8s= \'%s", key, value); \
+ for (k = strlen (card); k < 81; k++) card[k] = ' '; \
+ k = strlen (key); if (k < 8) card[19] = '\''; else card[11+k] = '\''; \
+ fwrite (card, 1, 80, fp); }
+
+#define FITS_WRITE_CARD(fp,value) \
+{ gchar card[81]; \
+ g_snprintf (card, sizeof (card), \
+ "%-80.80s", value); \
+ fwrite (card, 1, 80, fp); }
+
+
+/* Macro to convert a double value to a string using '.' as decimal point */
+#define FDTOSTR(buf,val) g_ascii_dtostr (buf, sizeof(buf), (gdouble)(val))
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_scanfdouble - (local) scan a string for a double value */
+/* */
+/* Parameters: */
+/* const char *buf [I] : the string to scan */
+/* double *value [O] : where to write the double value to */
+/* */
+/* Scan a string for a double value represented in "C" locale. */
+/* On success 1 is returned. On failure 0 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static gboolean
+fits_scanfdouble (const gchar *buf,
+ gdouble *value)
+{
+ gboolean retval = FALSE;
+ gchar *bufcopy = g_strdup (buf);
+
+ /* We should use g_ascii_strtod. This also allows scanning of hexadecimal */
+ /* values like 0x02. But we want the behaviour of sscanf ("0x02","%lf",...*/
+ /* that gives 0.0 in this case. So check the string if we have a hex-value*/
+
+ if (bufcopy)
+ {
+ gchar *bufptr = bufcopy;
+
+ /* Remove leading white space */
+ g_strchug (bufcopy);
+
+ /* Skip leading sign character */
+ if ((*bufptr == '-') || (*bufptr == '+'))
+ bufptr++;
+
+ /* Start of hex value ? Take this as 0.0 */
+ if ((bufptr[0] == '0') && (g_ascii_toupper (bufptr[1]) == 'X'))
+ {
+ *value = 0.0;
+ retval = TRUE;
+ }
+ else
+ {
+ if (*bufptr == '.') /* leading decimal point ? Skip it */
+ bufptr++;
+
+ if (g_ascii_isdigit (*bufptr)) /* Expect the complete string is decimal */
+ {
+ gchar *endptr;
+ gdouble gvalue = g_ascii_strtod (bufcopy, &endptr);
+
+ if (errno == 0)
+ {
+ *value = gvalue;
+ retval = TRUE;
+ }
+ }
+ }
+
+ g_free (bufcopy);
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_new_filestruct - (local) initialize file structure */
+/* */
+/* Parameters: */
+/* -none- */
+/* */
+/* Returns a pointer to an initialized fits file structure. */
+/* On failure, a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static FitsFile *
+fits_new_filestruct (void)
+{
+ return g_new0 (FitsFile, 1);
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_new_hdulist - (local) initialize hdulist structure */
+/* */
+/* Parameters: */
+/* -none- */
+/* */
+/* Returns a pointer to an initialized hdulist structure. */
+/* On failure, a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static FitsHduList *
+fits_new_hdulist (void)
+{
+ return g_new0 (FitsHduList, 1);
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_delete_filestruct - (local) delete file structure */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : pointer to fits file structure */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Frees all memory allocated by the file structure. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static void
+fits_delete_filestruct (FitsFile *ff)
+{
+ if (ff == NULL)
+ return;
+
+ fits_delete_hdulist (ff->hdu_list);
+ ff->hdu_list = NULL;
+
+ g_free (ff);
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_delete_recordlist - (local) delete record list */
+/* */
+/* Parameters: */
+/* FitsRecordList *rl [I] : record list to delete */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static void
+fits_delete_recordlist (FitsRecordList *rl)
+{
+ while (rl != NULL)
+ {
+ FitsRecordList *next;
+
+ next = rl->next_record;
+ rl->next_record = NULL;
+ g_free (rl);
+
+ rl = next;
+ }
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_delete_hdulist - (local) delete hdu list */
+/* */
+/* Parameters: */
+/* FitsHduList *hl [I] : hdu list to delete */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static void
+fits_delete_hdulist (FitsHduList *hl)
+{
+ while (hl != NULL)
+ {
+ FitsHduList *next;
+
+ fits_delete_recordlist (hl->header_record_list);
+ next = hl->next_hdu;
+ hl->next_hdu = NULL;
+ g_free (hl);
+
+ hl = next;
+ }
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_nan_32 - (local) check for IEEE NaN values (32 bit) */
+/* */
+/* Parameters: */
+/* unsigned char *v [I] : value to check */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function returns 1 if the value is a NaN. Otherwise 0 is returned. */
+/* The byte sequence at v must start with the sign/eponent byte. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static int
+fits_nan_32 (guchar *v)
+{
+ register gulong k;
+
+ k = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
+ k &= 0x7fffffff; /* Don't care about the sign bit */
+
+ /* See NOST Definition of the Flexible Image Transport System (FITS), */
+ /* Appendix F, IEEE special formats. */
+ return (((k >= 0x7f7fffff) && (k <= 0x7fffffff)) ||
+ ((k >= 0x00000001) && (k <= 0x00800000)));
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_nan_64 - (local) check for IEEE NaN values (64 bit) */
+/* */
+/* Parameters: */
+/* unsigned char *v [I] : value to check */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function returns 1 if the value is a NaN. Otherwise 0 is returned. */
+/* The byte sequence at v must start with the sign/eponent byte. */
+/* (currently we ignore the low order 4 bytes of the mantissa. Therefore */
+/* this function is the same as for 32 bits). */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static int
+fits_nan_64 (guchar *v)
+{
+ register gulong k;
+
+ k = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
+ k &= 0x7fffffff; /* Don't care about the sign bit */
+
+ /* See NOST Definition of the Flexible Image Transport System (FITS), */
+ /* Appendix F, IEEE special formats. */
+ return (((k >= 0x7f7fffff) && (k <= 0x7fffffff)) ||
+ ((k >= 0x00000001) && (k <= 0x00800000)));
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_get_error - get an error message */
+/* */
+/* Parameters: */
+/* -none- */
+/* */
+/* If an error message has been set, a pointer to the message is returned. */
+/* Otherwise a NULL pointer is returned. */
+/* An inquired error message is removed from the error FIFO. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gchar *
+fits_get_error (void)
+{
+ static gchar errmsg[FITS_ERROR_LENGTH];
+ gint k;
+
+ if (fits_n_error <= 0)
+ return NULL;
+
+ strcpy (errmsg, fits_error[0]);
+
+ for (k = 1; k < fits_n_error; k++)
+ strcpy (fits_error[k-1], fits_error[k]);
+
+ fits_n_error--;
+
+ return errmsg;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_set_error - (local) set an error message */
+/* */
+/* Parameters: */
+/* char *errmsg [I] : Error message to set */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Places the error message in the FIFO. If the FIFO is full, */
+/* the message is discarded. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static void
+fits_set_error (const gchar *errmsg)
+{
+ if (fits_n_error < FITS_MAX_ERROR)
+ {
+ strncpy (fits_error[fits_n_error], errmsg, FITS_ERROR_LENGTH);
+ fits_error[fits_n_error++][FITS_ERROR_LENGTH-1] = '\0';
+ }
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_drop_error - (local) remove an error message */
+/* */
+/* Parameters: */
+/* -none- */
+/* */
+/* Removes the last error message from the error message FIFO */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static void
+fits_drop_error (void)
+{
+ if (fits_n_error > 0)
+ fits_n_error--;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_open - open a FITS file */
+/* */
+/* Parameters: */
+/* char *filename [I] : name of file to open */
+/* char *openmode [I] : mode to open the file ("r", "w") */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* On success, a FitsFile-pointer is returned. On failure, a NULL- */
+/* pointer is returned. */
+/* The functions scans through the file loading each header and analyzing */
+/* them. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+FitsFile *
+fits_open (const gchar *filename,
+ const gchar *openmode)
+
+{
+ gint reading, writing, n_rec, n_hdr;
+ glong fpos_header, fpos_data;
+ FILE *fp;
+ FitsFile *ff;
+ FitsRecordList *hdrlist;
+ FitsHduList *hdulist = NULL;
+ FitsHduList *last_hdulist = NULL;
+
+ /* Check the IEEE-format we are running on */
+ {
+ gfloat one32 = 1.0;
+ gdouble one64 = 1.0;
+ guchar *op32 = (guchar *) &one32;
+ guchar *op64 = (guchar *) &one64;
+
+ if (sizeof (float) == 4)
+ {
+ fits_ieee32_intel = (op32[3] == 0x3f);
+ fits_ieee32_motorola = (op32[0] == 0x3f);
+ }
+
+ if (sizeof (double) == 8)
+ {
+ fits_ieee64_intel = (op64[7] == 0x3f);
+ fits_ieee64_motorola = (op64[0] == 0x3f);
+ }
+ }
+
+ if ((filename == NULL) || (*filename == '\0') || (openmode == NULL))
+ FITS_RETURN ("fits_open: Invalid parameters", NULL);
+
+ reading = (strcmp (openmode, "r") == 0);
+ writing = (strcmp (openmode, "w") == 0);
+ if ((!reading) && (!writing))
+ FITS_RETURN ("fits_open: Invalid openmode", NULL);
+
+ fp = g_fopen (filename, reading ? "rb" : "wb");
+ if (fp == NULL)
+ FITS_RETURN ("fits_open: fopen() failed", NULL);
+
+ ff = fits_new_filestruct ();
+ if (ff == NULL)
+ {
+ fclose (fp);
+ FITS_RETURN ("fits_open: No more memory", NULL);
+ }
+
+ ff->fp = fp;
+ ff->openmode = *openmode;
+
+ if (writing)
+ return ff;
+
+ for (n_hdr = 0; ; n_hdr++) /* Read through all HDUs */
+ {
+ fpos_header = ftell (fp); /* Save file position of header */
+ hdrlist = fits_read_header (fp, &n_rec);
+
+ if (hdrlist == NULL)
+ {
+ if (n_hdr > 0) /* At least one header must be present. */
+ fits_drop_error (); /* If we got a header already, drop the error */
+ break;
+ }
+ fpos_data = ftell (fp); /* Save file position of data */
+
+ /* Decode the header */
+ hdulist = fits_decode_header (hdrlist, fpos_header, fpos_data);
+ if (hdulist == NULL)
+ {
+ fits_delete_recordlist (hdrlist);
+ break;
+ }
+ ff->n_hdu++;
+ ff->n_pic += hdulist->numpic;
+
+ if (hdulist->used.blank_value)
+ ff->blank_used = TRUE;
+
+ if (hdulist->used.nan_value)
+ ff->nan_used = TRUE;
+
+ if (n_hdr == 0)
+ ff->hdu_list = hdulist;
+ else
+ last_hdulist->next_hdu = hdulist;
+
+ last_hdulist = hdulist;
+
+ /* Evaluate the range of pixel data */
+ fits_eval_pixrange (fp, hdulist);
+
+ /* Reposition to start of next header */
+ if (fseek (fp, hdulist->data_offset + hdulist->data_size, SEEK_SET) < 0)
+ break;
+ }
+
+ return ff;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_close - close a FITS file */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS file pointer */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+void
+fits_close (FitsFile *ff)
+{
+ if (ff == NULL)
+ FITS_VRETURN ("fits_close: Invalid parameter");
+
+ fclose (ff->fp);
+
+ fits_delete_filestruct (ff);
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_add_hdu - add a HDU to the file */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS file pointer */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Adds a new HDU to the list kept in ff. A pointer to the new HDU is */
+/* returned. On failure, a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+FitsHduList *
+fits_add_hdu (FitsFile *ff)
+{
+ FitsHduList *newhdu, *hdu;
+
+ if (ff->openmode != 'w')
+ FITS_RETURN ("fits_add_hdu: file not open for writing", NULL);
+
+ newhdu = fits_new_hdulist ();
+ if (newhdu == NULL)
+ return NULL;
+
+ if (ff->hdu_list == NULL)
+ {
+ ff->hdu_list = newhdu;
+ }
+ else
+ {
+ hdu = ff->hdu_list;
+ while (hdu->next_hdu != NULL)
+ hdu = hdu->next_hdu;
+ hdu->next_hdu = newhdu;
+ }
+
+ return newhdu;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_add_card - add a card to the HDU */
+/* */
+/* Parameters: */
+/* FitsHduList *hdulist [I] : HDU listr */
+/* char *card [I] : card to add */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The card must follow the standards of FITS. The card must not use a */
+/* keyword that is written using *hdulist itself. On success 0 is returned. */
+/* On failure -1 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gint
+fits_add_card (FitsHduList *hdulist,
+ const gchar *card)
+{
+ gint k;
+
+ if (hdulist->naddcards >= FITS_NADD_CARDS)
+ return -1;
+
+ k = strlen (card);
+ if (k < FITS_CARD_SIZE)
+ {
+ memset (&(hdulist->addcards[hdulist->naddcards][k]), ' ',
+ FITS_CARD_SIZE - k);
+ memcpy (hdulist->addcards[(hdulist->naddcards)++], card, k);
+ }
+ else
+ {
+ memcpy (hdulist->addcards[(hdulist->naddcards)++], card, FITS_CARD_SIZE);
+ }
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_print_header - print the internal representation */
+/* of a single header */
+/* Parameters: */
+/* FitsHduList *hdr [I] : pointer to the header */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+void
+fits_print_header (FitsHduList *hdr)
+{
+ gint k;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+ if (hdr->used.simple)
+ printf ("Content of SIMPLE-header:\n");
+ else
+ printf ("Content of XTENSION-header %s:\n", hdr->xtension);
+
+ printf ("header_offset : %ld\n", hdr->header_offset);
+ printf ("data_offset : %ld\n", hdr->data_offset);
+ printf ("data_size : %ld\n", hdr->data_size);
+ printf ("used data_size: %ld\n", hdr->udata_size);
+ printf ("bytes p.pixel : %d\n", hdr->bpp);
+ printf ("pixmin : %s\n", FDTOSTR (buf, hdr->pixmin));
+ printf ("pixmax : %s\n", FDTOSTR (buf, hdr->pixmax));
+
+ printf ("naxis : %d\n", hdr->naxis);
+ for (k = 1; k <= hdr->naxis; k++)
+ printf ("naxis%-3d : %d\n", k, hdr->naxisn[k-1]);
+
+ printf ("bitpix : %d\n", hdr->bitpix);
+
+ if (hdr->used.blank)
+ printf ("blank : %ld\n", hdr->blank);
+ else
+ printf ("blank : not used\n");
+
+ if (hdr->used.datamin)
+ printf ("datamin : %s\n", FDTOSTR (buf, hdr->datamin));
+ else
+ printf ("datamin : not used\n");
+
+ if (hdr->used.datamax)
+ printf ("datamax : %s\n", FDTOSTR (buf, hdr->datamax));
+ else
+ printf ("datamax : not used\n");
+
+ if (hdr->used.gcount)
+ printf ("gcount : %ld\n", hdr->gcount);
+ else
+ printf ("gcount : not used\n");
+
+ if (hdr->used.pcount)
+ printf ("pcount : %ld\n", hdr->pcount);
+ else
+ printf ("pcount : not used\n");
+
+ if (hdr->used.bscale)
+ printf ("bscale : %s\n", FDTOSTR (buf, hdr->bscale));
+ else
+ printf ("bscale : not used\n");
+
+ if (hdr->used.bzero)
+ printf ("bzero : %s\n", FDTOSTR (buf, hdr->bzero));
+ else
+ printf ("bzero : not used\n");
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_read_header - (local) read FITS header */
+/* */
+/* Parameters: */
+/* FILE *fp [I] : file pointer */
+/* int *nrec [O] : number of records read */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Reads in all header records up to the record that keeps the END-card. */
+/* A pointer to the record list is returned on success. */
+/* On failure, a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static FitsRecordList *
+fits_read_header (FILE *fp,
+ gint *nrec)
+{
+ gchar record[FITS_RECORD_SIZE];
+ FitsRecordList *start_list = NULL;
+ FitsRecordList *cu_record = NULL;
+ FitsRecordList *new_record;
+ FitsData *fdat;
+ gint k, simple, xtension;
+
+ *nrec = 0;
+
+ k = fread (record, 1, FITS_RECORD_SIZE, fp);
+ if (k != FITS_RECORD_SIZE)
+ FITS_RETURN ("fits_read_header: Error in read of first record", NULL);
+
+ simple = (strncmp (record, "SIMPLE ", 8) == 0);
+ xtension = (strncmp (record, "XTENSION", 8) == 0);
+ if ((!simple) && (!xtension))
+ FITS_RETURN ("fits_read_header: Missing keyword SIMPLE or XTENSION", NULL);
+
+ if (simple)
+ {
+ fdat = fits_decode_card (record, FITS_DATA_TYPE_FBOOL);
+ if (fdat && !fdat->fbool)
+ fits_set_error ("fits_read_header (warning): keyword SIMPLE does not "
+ "have value T");
+ }
+
+ for (;;) /* Process all header records */
+ {
+ new_record = g_new0 (FitsRecordList, 1);
+ memcpy (new_record->data, record, FITS_RECORD_SIZE);
+ new_record->next_record = NULL;
+ (*nrec)++;
+
+ if (start_list == NULL) /* Add new record to the list */
+ start_list = new_record;
+ else
+ cu_record->next_record = new_record;
+
+ cu_record = new_record;
+
+ /* Was this the last record ? */
+ if (fits_search_card (cu_record, "END") != NULL)
+ break;
+
+ k = fread (record, 1, FITS_RECORD_SIZE, fp);
+ if (k != FITS_RECORD_SIZE)
+ FITS_RETURN ("fits_read_header: Error in read of record", NULL);
+ }
+
+ return start_list;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_write_header - write a FITS header */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS-file pointer */
+/* FitsHduList [I] : pointer to header */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Writes a header to the file. On success, 0 is returned. On failure, */
+/* -1 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gint
+fits_write_header (FitsFile *ff,
+ FitsHduList *hdulist)
+{
+ gint numcards;
+ gint k;
+
+ if (ff->openmode != 'w')
+ FITS_RETURN ("fits_write_header: file not open for writing", -1);
+
+ numcards = 0;
+
+ if (hdulist->used.simple)
+ {
+ FITS_WRITE_BOOLCARD (ff->fp, "SIMPLE", 1);
+ numcards++;
+ }
+ else if (hdulist->used.xtension)
+ {
+ FITS_WRITE_STRINGCARD (ff->fp, "XTENSION", hdulist->xtension);
+ numcards++;
+ }
+
+ FITS_WRITE_LONGCARD (ff->fp, "BITPIX", hdulist->bitpix);
+ numcards++;
+
+ FITS_WRITE_LONGCARD (ff->fp, "NAXIS", hdulist->naxis);
+ numcards++;
+
+ for (k = 0; k < hdulist->naxis; k++)
+ {
+ gchar naxisn[16];
+
+ g_snprintf (naxisn, sizeof (naxisn), "NAXIS%d", k+1);
+ FITS_WRITE_LONGCARD (ff->fp, naxisn, hdulist->naxisn[k]);
+ numcards++;
+ }
+
+ if (hdulist->used.extend)
+ {
+ FITS_WRITE_BOOLCARD (ff->fp, "EXTEND", hdulist->extend);
+ numcards++;
+ }
+
+ if (hdulist->used.groups)
+ {
+ FITS_WRITE_BOOLCARD (ff->fp, "GROUPS", hdulist->groups);
+ numcards++;
+ }
+
+ if (hdulist->used.pcount)
+ {
+ FITS_WRITE_LONGCARD (ff->fp, "PCOUNT", hdulist->pcount);
+ numcards++;
+ }
+
+ if (hdulist->used.gcount)
+ {
+ FITS_WRITE_LONGCARD (ff->fp, "GCOUNT", hdulist->gcount);
+ numcards++;
+ }
+
+ if (hdulist->used.bzero)
+ {
+ FITS_WRITE_DOUBLECARD (ff->fp, "BZERO", hdulist->bzero);
+ numcards++;
+ }
+
+ if (hdulist->used.bscale)
+ {
+ FITS_WRITE_DOUBLECARD (ff->fp, "BSCALE", hdulist->bscale);
+ numcards++;
+ }
+
+ if (hdulist->used.datamin)
+ {
+ FITS_WRITE_DOUBLECARD (ff->fp, "DATAMIN", hdulist->datamin);
+ numcards++;
+ }
+
+ if (hdulist->used.datamax)
+ {
+ FITS_WRITE_DOUBLECARD (ff->fp, "DATAMAX", hdulist->datamax);
+ numcards++;
+ }
+
+ if (hdulist->used.blank)
+ {
+ FITS_WRITE_LONGCARD (ff->fp, "BLANK", hdulist->blank);
+ numcards++;
+ }
+
+ /* Write additional cards */
+ if (hdulist->naddcards > 0)
+ {
+ fwrite (hdulist->addcards, FITS_CARD_SIZE, hdulist->naddcards, ff->fp);
+ numcards += hdulist->naddcards;
+ }
+
+ FITS_WRITE_CARD (ff->fp, "END");
+ numcards++;
+
+ k = (numcards * FITS_CARD_SIZE) % FITS_RECORD_SIZE;
+ if (k) /* Must the record be filled up ? */
+ {
+ while (k++ < FITS_RECORD_SIZE)
+ putc (' ', ff->fp);
+ }
+
+ return ferror (ff->fp) ? -1 : 0;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_decode_header - (local) decode a header */
+/* */
+/* Parameters: */
+/* FitsRecordList *hdr [I] : the header record list */
+/* long hdr_offset [I] : fileposition of header */
+/* long dat_offset [I] : fileposition of data (end of header) */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function decodes the mostly used data within the header and generates */
+/* a FitsHduList-entry. On failure, a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static FitsHduList *
+fits_decode_header (FitsRecordList *hdr,
+ glong hdr_offset,
+ glong dat_offset)
+{
+ FitsHduList *hdulist;
+ FitsData *fdat;
+ gchar errmsg[80], key[9];
+ gint k, bpp, random_groups;
+ glong mul_axis, data_size, bitpix_supported;
+
+#define FITS_DECODE_CARD(mhdr,mkey,mfdat,mtyp) \
+ { strcpy (key, mkey); \
+ mfdat = fits_decode_card (fits_search_card (mhdr, mkey), mtyp); \
+ if (mfdat == NULL) goto err_missing; }
+
+#define FITS_TRY_CARD(mhdr,mhdu,mkey,mvar,mtyp,unionvar) \
+ { FitsData *mfdat = fits_decode_card (fits_search_card (mhdr,mkey), mtyp); \
+ mhdu->used.mvar = (mfdat != NULL); \
+ if (mhdu->used.mvar) mhdu->mvar = mfdat->unionvar; }
+
+ hdulist = fits_new_hdulist ();
+ if (hdulist == NULL)
+ FITS_RETURN ("fits_decode_header: Not enough memory", NULL);
+
+ /* Initialize the header data */
+ hdulist->header_offset = hdr_offset;
+ hdulist->data_offset = dat_offset;
+
+ hdulist->used.simple = (strncmp (hdr->data, "SIMPLE ", 8) == 0);
+ hdulist->used.xtension = (strncmp (hdr->data, "XTENSION", 8) == 0);
+
+ if (hdulist->used.xtension)
+ {
+ fdat = fits_decode_card (fits_search_card (hdr, "XTENSION"),
+ FITS_DATA_TYPE_FSTRING);
+ if (fdat != NULL)
+ {
+ strcpy (hdulist->xtension, fdat->fstring);
+ }
+ else
+ {
+ strcpy (errmsg, "No valid XTENSION header found.");
+ goto err_return;
+ }
+ }
+
+ FITS_DECODE_CARD (hdr, "NAXIS", fdat, FITS_DATA_TYPE_FLONG);
+ hdulist->naxis = fdat->flong;
+
+ FITS_DECODE_CARD (hdr, "BITPIX", fdat, FITS_DATA_TYPE_FLONG);
+ bpp = hdulist->bitpix = (gint) fdat->flong;
+ if ((bpp != 8) && (bpp != 16) && (bpp != 32) &&
+ (bpp != -32) && (bpp != -64))
+ {
+ strcpy (errmsg, "fits_decode_header: Invalid BITPIX-value");
+ goto err_return;
+ }
+
+ if (bpp < 0)
+ bpp = -bpp;
+
+ bpp /= 8;
+ hdulist->bpp = bpp;
+
+ FITS_TRY_CARD (hdr, hdulist, "GCOUNT", gcount, FITS_DATA_TYPE_FLONG, flong);
+ FITS_TRY_CARD (hdr, hdulist, "PCOUNT", pcount, FITS_DATA_TYPE_FLONG, flong);
+
+ FITS_TRY_CARD (hdr, hdulist, "GROUPS", groups, FITS_DATA_TYPE_FLONG, fbool);
+ random_groups = hdulist->used.groups && hdulist->groups;
+
+ FITS_TRY_CARD (hdr, hdulist, "EXTEND", extend, FITS_DATA_TYPE_FBOOL, fbool);
+
+ if (hdulist->used.xtension) /* Extension requires GCOUNT and PCOUNT */
+ {
+ if (! hdulist->used.gcount || ! hdulist->used.pcount)
+ {
+ strcpy (errmsg, "fits_decode_header: Missing GCOUNT/PCOUNT for XTENSION");
+ goto err_return;
+ }
+ }
+
+ mul_axis = 1;
+
+ /* Find all NAXISx-cards */
+ for (k = 1; k <= FITS_MAX_AXIS; k++)
+ {
+ gchar naxisn[16];
+
+ g_snprintf (naxisn, sizeof (naxisn), "NAXIS%-3d", k);
+ fdat = fits_decode_card (fits_search_card (hdr, naxisn),
+ FITS_DATA_TYPE_FLONG);
+ if (fdat == NULL)
+ {
+ k--; /* Save the last NAXISk read */
+ break;
+ }
+
+ hdulist->naxisn[k-1] = (int)fdat->flong;
+
+ if (hdulist->naxisn[k-1] < 0)
+ {
+ strcpy (errmsg, "fits_decode_header: Negative value in NAXISn");
+ goto err_return;
+ }
+
+ if ((k == 1) && random_groups)
+ {
+ if (hdulist->naxisn[0] != 0)
+ {
+ strcpy (errmsg, "fits_decode_header: Random groups with NAXIS1 != 0");
+ goto err_return;
+ }
+ }
+ else
+ {
+ mul_axis *= hdulist->naxisn[k - 1];
+ }
+ }
+
+ if ((hdulist->naxis > 0) && (k < hdulist->naxis))
+ {
+ strcpy (errmsg, "fits_decode_card: Not enough NAXISn-cards");
+ goto err_return;
+ }
+
+ /* If we have only one dimension, just set the second to size one. */
+ /* So we don't have to check for naxis < 2 in some places. */
+ if (hdulist->naxis < 2)
+ hdulist->naxisn[1] = 1;
+
+ if (hdulist->naxis < 1)
+ {
+ mul_axis = 0;
+ hdulist->naxisn[0] = 1;
+ }
+
+ if (hdulist->used.xtension)
+ data_size = bpp * hdulist->gcount * (hdulist->pcount + mul_axis);
+ else
+ data_size = bpp * mul_axis;
+
+ hdulist->udata_size = data_size; /* Used data size without padding */
+
+ /* Datasize must be a multiple of the FITS logical record size */
+ data_size = (data_size + FITS_RECORD_SIZE - 1) / FITS_RECORD_SIZE;
+ data_size *= FITS_RECORD_SIZE;
+ hdulist->data_size = data_size;
+
+ FITS_TRY_CARD (hdr, hdulist, "BLANK", blank, FITS_DATA_TYPE_FLONG, flong);
+
+ FITS_TRY_CARD (hdr, hdulist, "DATAMIN", datamin, FITS_DATA_TYPE_FDOUBLE, fdouble);
+ FITS_TRY_CARD (hdr, hdulist, "DATAMAX", datamax, FITS_DATA_TYPE_FDOUBLE, fdouble);
+
+ FITS_TRY_CARD (hdr, hdulist, "BZERO", bzero, FITS_DATA_TYPE_FDOUBLE, fdouble);
+ FITS_TRY_CARD (hdr, hdulist, "BSCALE", bscale, FITS_DATA_TYPE_FDOUBLE, fdouble);
+
+ /* Evaluate number of interpretable images for this HDU */
+ hdulist->numpic = 0;
+
+ /* We must support this format */
+ bitpix_supported = (hdulist->bitpix > 0)
+ || ( (hdulist->bitpix == -64)
+ && (fits_ieee64_intel || fits_ieee64_motorola))
+ || ( (hdulist->bitpix == -32)
+ && ( fits_ieee32_intel || fits_ieee32_motorola
+ || fits_ieee64_intel || fits_ieee64_motorola));
+
+ if (bitpix_supported)
+ {
+ if (hdulist->used.simple)
+ {
+ if (hdulist->naxis > 0)
+ {
+ hdulist->numpic = 1;
+ for (k = 3; k <= hdulist->naxis; k++)
+ hdulist->numpic *= hdulist->naxisn[k - 1];
+ }
+ }
+ else if (hdulist->used.xtension &&
+ (strncmp (hdulist->xtension, "IMAGE", 5) == 0))
+ {
+ if (hdulist->naxis > 0)
+ {
+ hdulist->numpic = 1;
+ for (k = 3; k <= hdulist->naxis; k++)
+ hdulist->numpic *= hdulist->naxisn[k - 1];
+ }
+ }
+ }
+ else
+ {
+ gchar msg[160];
+
+ g_snprintf (msg, sizeof (msg),
+ "fits_decode_header: IEEE floating point format required for "
+ "BITPIX=%d\nis not supported on this machine",
+ hdulist->bitpix);
+ fits_set_error (msg);
+ }
+
+ hdulist->header_record_list = hdr; /* Add header records to the list */
+
+ return hdulist;
+
+ err_missing:
+ g_snprintf (errmsg, sizeof (errmsg),
+ "fits_decode_header: missing/invalid %s card", key);
+
+ err_return:
+ fits_delete_hdulist (hdulist);
+ fits_set_error (errmsg);
+
+ return NULL;
+
+#undef FITS_DECODE_CARD
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_eval_pixrange - (local) evaluate range of pixel data */
+/* */
+/* Parameters: */
+/* FILE *fp [I] : file pointer */
+/* FitsHduList *hdu [I] : pointer to header */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The Function sets the values hdu->pixmin and hdu->pixmax. On success 0 */
+/* is returned. On failure, -1 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+static gint
+fits_eval_pixrange (FILE *fp,
+ FitsHduList *hdu)
+{
+ register gint maxelem;
+#define FITSNPIX 4096
+ guchar pixdat[FITSNPIX];
+ gint nelem, bpp;
+ gboolean blank_found = FALSE;
+ gboolean nan_found = FALSE;
+
+ if (fseek (fp, hdu->data_offset, SEEK_SET) < 0)
+ FITS_RETURN ("fits_eval_pixrange: can't position file", -1);
+
+ bpp = hdu->bpp; /* Number of bytes per pixel */
+ nelem = hdu->udata_size / bpp; /* Number of data elements */
+
+ switch (hdu->bitpix)
+ {
+ case 8:
+ {
+ register FitsBitpix8 pixval;
+ register guchar *ptr;
+ FitsBitpix8 minval = 255;
+ FitsBitpix8 maxval = 0;
+
+ while (nelem > 0)
+ {
+ maxelem = sizeof (pixdat) / bpp;
+ if (nelem < maxelem)
+ maxelem = nelem;
+
+ nelem -= maxelem;
+ if (fread ((gchar *) pixdat, bpp, maxelem, fp) != maxelem)
+ FITS_RETURN ("fits_eval_pixrange: error on read bitpix 8 data", -1);
+
+ ptr = pixdat;
+ if (hdu->used.blank)
+ {
+ FitsBitpix8 blankval = (FitsBitpix8) hdu->blank;
+
+ while (maxelem-- > 0)
+ {
+ pixval = (FitsBitpix8)*(ptr++);
+
+ if (pixval != blankval)
+ {
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ else
+ {
+ blank_found = TRUE;
+ }
+ }
+ }
+ else
+ {
+ while (maxelem-- > 0)
+ {
+ pixval = (FitsBitpix8)*(ptr++);
+
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ }
+ }
+
+ hdu->pixmin = minval;
+ hdu->pixmax = maxval;
+ break;
+ }
+
+ case 16:
+ {
+ register FitsBitpix16 pixval;
+ register guchar *ptr;
+ FitsBitpix16 minval = 0x7fff;
+ FitsBitpix16 maxval = ~0x7fff;
+
+ while (nelem > 0)
+ {
+ maxelem = sizeof (pixdat) / bpp;
+ if (nelem < maxelem)
+ maxelem = nelem;
+
+ nelem -= maxelem;
+ if (fread ((gchar *) pixdat, bpp, maxelem, fp) != maxelem)
+ FITS_RETURN ("fits_eval_pixrange: error on read bitpix 16 data", -1);
+
+ ptr = pixdat;
+ if (hdu->used.blank)
+ {
+ FitsBitpix16 blankval = (FitsBitpix16) hdu->blank;
+
+ while (maxelem-- > 0)
+ {
+ FITS_GETBITPIX16 (ptr, pixval);
+ ptr += 2;
+
+ if (pixval != blankval)
+ {
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ else
+ {
+ blank_found = TRUE;
+ }
+ }
+ }
+ else
+ {
+ while (maxelem-- > 0)
+ {
+ FITS_GETBITPIX16 (ptr, pixval);
+ ptr += 2;
+
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ }
+ }
+
+ hdu->pixmin = minval;
+ hdu->pixmax = maxval;
+ break;
+ }
+
+ case 32:
+ {
+ register FitsBitpix32 pixval;
+ register guchar *ptr;
+ FitsBitpix32 minval = 0x7fffffff;
+ FitsBitpix32 maxval = ~0x7fffffff;
+
+ while (nelem > 0)
+ {
+ maxelem = sizeof (pixdat)/bpp;
+ if (nelem < maxelem)
+ maxelem = nelem;
+
+ nelem -= maxelem;
+ if (fread ((gchar *) pixdat, bpp, maxelem, fp) != maxelem)
+ FITS_RETURN ("fits_eval_pixrange: error on read bitpix 32 data", -1);
+
+ ptr = pixdat;
+ if (hdu->used.blank)
+ {
+ FitsBitpix32 blankval = (FitsBitpix32)hdu->blank;
+
+ while (maxelem-- > 0)
+ {
+ FITS_GETBITPIX32 (ptr, pixval);
+ ptr += 4;
+
+ if (pixval != blankval)
+ {
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ else
+ {
+ blank_found = TRUE;
+ }
+ }
+ }
+ else
+ {
+ while (maxelem-- > 0)
+ {
+ FITS_GETBITPIX32 (ptr, pixval);
+ ptr += 4;
+
+ if (pixval < minval)
+ minval = pixval;
+ else if (pixval > maxval)
+ maxval = pixval;
+ }
+ }
+ }
+
+ hdu->pixmin = minval;
+ hdu->pixmax = maxval;
+ break;
+ }
+
+ case -32:
+ {
+ register FitsBitpixM32 pixval = 0;
+ register guchar *ptr;
+ FitsBitpixM32 minval = 0;
+ FitsBitpixM32 maxval = 0;
+ gboolean first = TRUE;
+
+ while (nelem > 0)
+ {
+ maxelem = sizeof (pixdat) / bpp;
+ if (nelem < maxelem)
+ maxelem = nelem;
+
+ nelem -= maxelem;
+ if (fread ((gchar *) pixdat, bpp, maxelem, fp) != maxelem)
+ FITS_RETURN ("fits_eval_pixrange: error on read bitpix -32 data", -1);
+
+ ptr = pixdat;
+ while (maxelem-- > 0)
+ {
+ if (! fits_nan_32 (ptr))
+ {
+ FITS_GETBITPIXM32 (ptr, pixval);
+ ptr += 4;
+
+ if (first)
+ {
+ first = FALSE;
+ minval = maxval = pixval;
+ }
+ else if (pixval < minval)
+ {
+ minval = pixval;
+ }
+ else if (pixval > maxval)
+ {
+ maxval = pixval;
+ }
+ }
+ else
+ {
+ nan_found = TRUE;
+ }
+ }
+ }
+
+ hdu->pixmin = minval;
+ hdu->pixmax = maxval;
+ break;
+ }
+
+ case -64:
+ {
+ register FitsBitpixM64 pixval;
+ register guchar *ptr;
+ FitsBitpixM64 minval = 0;
+ FitsBitpixM64 maxval = 0;
+ gboolean first = TRUE;
+
+ while (nelem > 0)
+ {
+ maxelem = sizeof (pixdat) / bpp;
+ if (nelem < maxelem)
+ maxelem = nelem;
+
+ nelem -= maxelem;
+ if (fread ((gchar *) pixdat, bpp, maxelem, fp) != maxelem)
+ FITS_RETURN ("fits_eval_pixrange: error on read bitpix -64 data", -1);
+
+ ptr = pixdat;
+ while (maxelem-- > 0)
+ {
+ if (! fits_nan_64 (ptr))
+ {
+ FITS_GETBITPIXM64 (ptr, pixval);
+ ptr += 8;
+
+ if (first)
+ {
+ first = FALSE;
+ minval = maxval = pixval;
+ }
+ else if (pixval < minval)
+ {
+ minval = pixval;
+ }
+ else if (pixval > maxval)
+ {
+ maxval = pixval;
+ }
+ }
+ else
+ {
+ nan_found = TRUE;
+ }
+ }
+ }
+
+ hdu->pixmin = minval;
+ hdu->pixmax = maxval;
+ break;
+ }
+ }
+
+ if (nan_found)
+ hdu->used.nan_value = TRUE;
+
+ if (blank_found)
+ hdu->used.blank_value = TRUE;
+
+ return 0;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_decode_card - decode a card */
+/* */
+/* Parameters: */
+/* const char *card [I] : pointer to card image */
+/* FitsDataType data_type [I] : datatype to decode */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* Decodes a card and returns a pointer to the union, keeping the data. */
+/* If card is NULL or on failure, a NULL-pointer is returned. */
+/* If the card does not have the value indicator, an error is generated, */
+/* but its tried to decode the card. The data is only valid up to the next */
+/* call of the function. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+FitsData *
+fits_decode_card (const gchar *card,
+ FitsDataType data_type)
+{
+ static FitsData data;
+ glong l_long;
+ gdouble l_double;
+ gchar l_card[FITS_CARD_SIZE + 1];
+ gchar msg[256];
+ gchar *cp, *dst, *end;
+ gint ErrCount = 0;
+
+ if (card == NULL)
+ return NULL;
+
+ memcpy (l_card, card, FITS_CARD_SIZE);
+ l_card[FITS_CARD_SIZE] = '\0';
+
+ if (strncmp (card+8, "= ", 2) != 0)
+ {
+ g_snprintf (msg, sizeof (msg),
+ "fits_decode_card (warning): Missing value indicator "
+ "'= ' for %8.8s", l_card);
+ fits_set_error (msg);
+ ErrCount++;
+ }
+
+ switch (data_type)
+ {
+ case FITS_DATA_TYPE_BITPIX_8:
+ data.bitpix8 = (FitsBitpix8) (l_card[10]);
+ break;
+
+ case FITS_DATA_TYPE_BITPIX_16:
+ if (sscanf (l_card + 10, "%ld", &l_long) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_BITPIX_16");
+ ErrCount++;
+ break;
+ }
+ data.bitpix16 = (FitsBitpix16) l_long;
+ break;
+
+ case FITS_DATA_TYPE_BITPIX_32:
+ if (sscanf (l_card + 10, "%ld", &l_long) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_BITPIX_32");
+ ErrCount++;
+ break;
+ }
+ data.bitpix32 = (FitsBitpix32) l_long;
+ break;
+
+ case FITS_DATA_TYPE_BITPIX_M32:
+ if (fits_scanfdouble (l_card + 10, &l_double) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_BITPIX_M32");
+ ErrCount++;
+ break;
+ }
+ data.bitpixm32 = (FitsBitpixM32) l_double;
+ break;
+
+ case FITS_DATA_TYPE_BITPIX_M64:
+ if (fits_scanfdouble (l_card + 10, &l_double) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_BITPIX_M64");
+ ErrCount++;
+ break;
+ }
+ data.bitpixm64 = (FitsBitpixM64) l_double;
+ break;
+
+ case FITS_DATA_TYPE_FBOOL:
+ cp = l_card + 10;
+ while (*cp == ' ')
+ cp++;
+
+ if (*cp == 'T')
+ {
+ data.fbool = 1;
+ }
+ else if (*cp == 'F')
+ {
+ data.fbool = 0;
+ }
+ else
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_FBOOL");
+ ErrCount++;
+ break;
+ }
+ break;
+
+ case FITS_DATA_TYPE_FLONG:
+ if (sscanf (l_card + 10, "%ld", &l_long) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_FLONG");
+ ErrCount++;
+ break;
+ }
+ data.flong = (FitsBitpix32) l_long;
+ break;
+
+ case FITS_DATA_TYPE_FDOUBLE:
+ if (fits_scanfdouble (l_card + 10, &l_double) != 1)
+ {
+ fits_set_error ("fits_decode_card: error decoding FITS_DATA_TYPE_FDOUBLE");
+ ErrCount++;
+ break;
+ }
+ data.fdouble = (FitsBitpixM32) l_double;
+ break;
+
+ case FITS_DATA_TYPE_FSTRING:
+ cp = l_card + 10;
+ if (*cp != '\'')
+ {
+ fits_set_error ("fits_decode_card: missing \' decoding FITS_DATA_TYPE_FSTRING");
+ ErrCount++;
+ break;
+ }
+
+ dst = data.fstring;
+ cp++;
+ end = l_card + FITS_CARD_SIZE - 1;
+ for (;;) /* Search for trailing quote */
+ {
+ if (*cp != '\'') /* All characters but quote are used. */
+ {
+ *(dst++) = *cp;
+ }
+ else /* Maybe there is a quote in the string */
+ {
+ if (cp >= end)
+ break; /* End of card ? finished */
+
+ if (*(cp+1) != '\'')
+ break;
+
+ *(dst++) = *(cp++);
+ }
+
+ if (cp >= end)
+ break;
+
+ cp++;
+ }
+
+ *dst = '\0';
+ break;
+ }
+
+ return (ErrCount == 0) ? &data : NULL;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_search_card - search a card in the record list */
+/* */
+/* Parameters: */
+/* FitsRecordList *rl [I] : record list to search */
+/* char *keyword [I] : keyword identifying the card */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* A card is searched in the record list. Only the first eight characters of */
+/* keyword are significant. If keyword is less than 8 characters, its filled */
+/* with blanks. */
+/* If the card is found, a pointer to the card is returned. */
+/* The pointer does not point to a null-terminated string. Only the next */
+/* 80 bytes are allowed to be read. */
+/* On failure a NULL-pointer is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gchar *
+fits_search_card (FitsRecordList *rl,
+ gchar *keyword)
+{
+ gint key_len, k;
+ gchar key[9];
+
+ key_len = strlen (keyword);
+ if (key_len > 8)
+ key_len = 8;
+ if (key_len == 0)
+ FITS_RETURN ("fits_search_card: Invalid parameter", NULL);
+
+ strcpy (key, " ");
+ memcpy (key, keyword, key_len);
+
+ while (rl != NULL)
+ {
+ gchar *card = (gchar *) rl->data;
+
+ for (k = 0; k < FITS_RECORD_SIZE / FITS_CARD_SIZE; k++)
+ {
+ if (strncmp (card, key, 8) == 0)
+ return (card);
+
+ card += FITS_CARD_SIZE;
+ }
+
+ rl = rl->next_record;
+ }
+
+ return NULL;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_image_info - get information about an image */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS file structure */
+/* int picind [I] : Index of picture in file (1,2,...) */
+/* int *hdupicind [O] : Index of picture in HDU (1,2,...) */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function returns on success a pointer to a FitsHduList. hdupicind */
+/* then gives the index of the image within the HDU. */
+/* On failure, NULL is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+FitsHduList *
+fits_image_info (FitsFile *ff,
+ gint picind,
+ gint *hdupicind)
+{
+ FitsHduList *hdulist;
+ gint firstpic, lastpic;
+
+ if (ff == NULL)
+ FITS_RETURN ("fits_image_info: ff is NULL", NULL);
+
+ if (ff->openmode != 'r')
+ FITS_RETURN ("fits_image_info: file not open for reading", NULL);
+
+ if ((picind < 1) || (picind > ff->n_pic))
+ FITS_RETURN ("fits_image_info: picind out of range", NULL);
+
+ firstpic = 1;
+ for (hdulist = ff->hdu_list; hdulist != NULL; hdulist = hdulist->next_hdu)
+ {
+ if (hdulist->numpic <= 0)
+ continue;
+
+ lastpic = firstpic + hdulist->numpic - 1;
+
+ if (picind <= lastpic) /* Found image in current HDU ? */
+ break;
+
+ firstpic = lastpic + 1;
+ }
+
+ *hdupicind = picind - firstpic + 1;
+
+ return hdulist;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_seek_image - position to a specific image */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS file structure */
+/* int picind [I] : Index of picture to seek (1,2,...) */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function positions the file pointer to a specified image. */
+/* The function returns on success a pointer to a FitsHduList. This pointer */
+/* must also be used when reading data from the image. */
+/* On failure, NULL is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+FitsHduList *
+fits_seek_image (FitsFile *ff,
+ gint picind)
+{
+ FitsHduList *hdulist;
+ gint hdupicind;
+ glong offset, pic_size;
+
+ hdulist = fits_image_info (ff, picind, &hdupicind);
+ if (hdulist == NULL)
+ return NULL;
+
+ pic_size = hdulist->bpp * hdulist->naxisn[0] * hdulist->naxisn[1];
+ offset = hdulist->data_offset + (hdupicind - 1) * pic_size;
+ if (fseek (ff->fp, offset, SEEK_SET) < 0)
+ FITS_RETURN ("fits_seek_image: Unable to position to image", NULL);
+
+ return hdulist;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_read_pixel - read pixel values from a file */
+/* */
+/* Parameters: */
+/* FitsFile *ff [I] : FITS file structure */
+/* FitsHduList *hdulist [I] : pointer to hdulist that describes image */
+/* int npix [I] : number of pixel values to read */
+/* FitsPixTransform *trans [I]: pixel transformation */
+/* void *buf [O] : buffer where to place transformed pixels */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function reads npix pixel values from the file, transforms them */
+/* checking for blank/NaN pixels and stores the transformed values in buf. */
+/* The number of transformed pixels is returned. If the returned value is */
+/* less than npix (or even -1), an error has occurred. */
+/* hdulist must be a pointer returned by fits_seek_image(). Before starting */
+/* to read an image, fits_seek_image() must be called. Even for successive */
+/* images. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gint
+fits_read_pixel (FitsFile *ff,
+ FitsHduList *hdulist,
+ gint npix,
+ FitsPixTransform *trans,
+ void *buf)
+{
+ gdouble offs, scale;
+ gdouble datadiff, pixdiff;
+ guchar pixbuffer[4096];
+ guchar *pix;
+ glong creplace;
+ gint transcount = 0;
+ glong tdata, tmin, tmax;
+ gint maxelem;
+
+ if (ff->openmode != 'r')
+ return -1; /* Not open for reading */
+
+ if (trans->dsttyp != 'k')
+ return -1; /* Currently we only return types equivalent to the image format */
+
+ if (npix <= 0)
+ return npix;
+
+ datadiff = trans->datamax - trans->datamin;
+ pixdiff = trans->pixmax - trans->pixmin;
+
+ offs = trans->datamin - trans->pixmin * datadiff / pixdiff;
+ scale = datadiff / pixdiff;
+
+ tmin = (glong) trans->datamin;
+ tmax = (glong) trans->datamax;
+
+ creplace = (glong) trans->replacement;
+
+ switch (hdulist->bitpix)
+ {
+ case 8:
+ {
+ guchar *cdata = (guchar *) buf;
+ while (npix > 0) /* For all pixels to read */
+ {
+ FitsBitpix8 bp8, bp8blank;
+
+ maxelem = sizeof (pixbuffer) / hdulist->bpp;
+ if (maxelem > npix)
+ maxelem = npix;
+
+ if (fread ((gchar *) pixbuffer, hdulist->bpp,
+ maxelem, ff->fp) != maxelem)
+ return -1;
+
+ npix -= maxelem;
+
+ pix = pixbuffer;
+
+ if (hdulist->used.blank)
+ {
+ bp8blank = (FitsBitpix8) hdulist->blank;
+
+ while (maxelem--)
+ {
+ bp8 = (FitsBitpix8) * (pix++);
+
+ if (bp8 == bp8blank) /* Is it a blank pixel ? */
+ {
+ *(cdata++) = (guchar) creplace;
+ }
+ else /* Do transform */
+ {
+ tdata = (glong) (bp8 * scale + offs);
+ tdata = CLAMP (tdata, tmin, tmax);
+
+ *(cdata++) = (guchar) tdata;
+ }
+
+ transcount++;
+ }
+ }
+ else
+ {
+ while (maxelem--)
+ {
+ bp8 = (FitsBitpix8) * (pix++);
+
+ tdata = (glong) (bp8 * scale + offs);
+ tdata = CLAMP (tdata, tmin, tmax);
+
+ *(cdata++) = (guchar) tdata;
+
+ transcount++;
+ }
+ }
+ }
+ }
+ break;
+
+ case 16:
+ {
+ FitsBitpix16 *cdata = (FitsBitpix16 *) buf;
+ while (npix > 0) /* For all pixels to read */
+ {
+ FitsBitpix16 bp16, bp16blank;
+
+ maxelem = sizeof (pixbuffer) / hdulist->bpp;
+ if (maxelem > npix)
+ maxelem = npix;
+
+ if (fread ((gchar *) pixbuffer, hdulist->bpp,
+ maxelem, ff->fp) != maxelem)
+ return -1;
+
+ npix -= maxelem;
+
+ pix = pixbuffer;
+ if (hdulist->used.blank)
+ {
+ bp16blank = (FitsBitpix16) hdulist->blank;
+
+ while (maxelem--)
+ {
+ FITS_GETBITPIX16 (pix, bp16);
+
+ if (bp16 == bp16blank)
+ {
+ *(cdata++) = (FitsBitpix16) creplace;
+ }
+ else
+ {
+ tdata = (glong) (bp16 * scale + offs);
+
+ if (tdata < tmin)
+ tdata = tmin;
+ else if (tdata > tmax)
+ tdata = tmax;
+
+ *(cdata++) = (FitsBitpix16) tdata;
+ }
+
+ transcount++;
+ pix += 2;
+ }
+ }
+ else
+ {
+ while (maxelem--)
+ {
+ FITS_GETBITPIX16 (pix, bp16);
+
+ tdata = (glong) (bp16 * scale + offs);
+ if (tdata < tmin)
+ tdata = tmin;
+ else if (tdata > tmax)
+ tdata = tmax;
+
+ *(cdata++) = (FitsBitpix16) tdata;
+
+ transcount++;
+ pix += 2;
+ }
+ }
+ }
+ }
+ break;
+
+ case 32:
+ {
+ FitsBitpix32 *cdata = (FitsBitpix32 *) buf;
+ while (npix > 0) /* For all pixels to read */
+ {
+ FitsBitpix32 bp32, bp32blank;
+
+ maxelem = sizeof (pixbuffer) / hdulist->bpp;
+ if (maxelem > npix)
+ maxelem = npix;
+
+ if (fread ((gchar *) pixbuffer, hdulist->bpp,
+ maxelem, ff->fp) != maxelem)
+ return -1;
+
+ npix -= maxelem;
+
+ pix = pixbuffer;
+ if (hdulist->used.blank)
+ {
+ bp32blank = (FitsBitpix32) hdulist->blank;
+
+ while (maxelem--)
+ {
+ FITS_GETBITPIX32 (pix, bp32);
+
+ if (bp32 == bp32blank)
+ {
+ *(cdata++) = (FitsBitpix32) creplace;
+ }
+ else
+ {
+ tdata = (glong) (bp32 * scale + offs);
+ if (tdata < tmin)
+ tdata = tmin;
+ else if (tdata > tmax)
+ tdata = tmax;
+
+ *(cdata++) = (FitsBitpix32) tdata;
+ }
+
+ transcount++;
+ pix += 4;
+ }
+ }
+ else
+ {
+ while (maxelem--)
+ {
+ FITS_GETBITPIX32 (pix, bp32);
+
+ tdata = (glong) (bp32 * scale + offs);
+ tdata = CLAMP (tdata, tmin, tmax);
+
+ *(cdata++) = (FitsBitpix32) tdata;
+
+ transcount++;
+ pix += 4;
+ }
+ }
+ }
+ }
+ break;
+
+ case -32:
+ {
+ FitsBitpixM32 *cdata = (FitsBitpixM32 *) buf;
+ while (npix > 0) /* For all pixels to read */
+ {
+ FitsBitpixM32 bpm32 = 0;
+
+ maxelem = sizeof (pixbuffer) / hdulist->bpp;
+ if (maxelem > npix)
+ maxelem = npix;
+
+ if (fread ((gchar *) pixbuffer, hdulist->bpp,
+ maxelem, ff->fp) != maxelem)
+ return -1;
+
+ npix -= maxelem;
+
+ pix = pixbuffer;
+ while (maxelem--)
+ {
+ if (fits_nan_32 (pix)) /* An IEEE special value ? */
+ {
+ *(cdata++) = trans->replacement;
+ }
+ else /* Do transform */
+ {
+ FITS_GETBITPIXM32 (pix, bpm32);
+
+ bpm32 = bpm32 * scale + offs;
+ bpm32 = CLAMP (bpm32, trans->datamin, trans->datamax);
+
+ *(cdata++) = bpm32;
+ }
+
+ transcount++;
+ pix += 4;
+ }
+ }
+ }
+ break;
+
+ case -64:
+ {
+ FitsBitpixM64 *cdata = (FitsBitpixM64 *) buf;
+ while (npix > 0) /* For all pixels to read */
+ {
+ FitsBitpixM64 bpm64;
+
+ maxelem = sizeof (pixbuffer) / hdulist->bpp;
+ if (maxelem > npix)
+ maxelem = npix;
+
+ if (fread ((gchar *) pixbuffer, hdulist->bpp,
+ maxelem, ff->fp) != maxelem)
+ return -1;
+
+ npix -= maxelem;
+
+ pix = pixbuffer;
+ while (maxelem--)
+ {
+ if (fits_nan_64 (pix))
+ {
+ *(cdata++) = trans->replacement;
+ }
+ else
+ {
+ FITS_GETBITPIXM64 (pix, bpm64);
+
+ bpm64 = bpm64 * scale + offs;
+ bpm64 = CLAMP (bpm64, trans->datamin, trans->datamax);
+
+ *(cdata++) = bpm64;
+ }
+
+ transcount++;
+ pix += 8;
+ }
+ }
+ }
+ break;
+ }
+
+ return transcount;
+}
+
+
+#ifndef FITS_NO_DEMO
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : fits_to_pgmraw - convert FITS-file to raw PGM-file */
+/* */
+/* Parameters: */
+/* char *fitsfile [I] : name of fitsfile */
+/* char *pgmfile [I] : name of pgmfile */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function converts a FITS-file to a raw PGM-file. The PGM-file will */
+/* be upside down, because the orientation for storing the image is */
+/* different. On success, 0 is returned. On failure, -1 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gint
+fits_to_pgmraw (gchar *fitsfile,
+ gchar *pgmfile)
+{
+ FitsFile *fitsin = NULL;
+ FILE *pgmout = NULL;
+ FitsHduList *hdu;
+ FitsPixTransform trans;
+ gint retval = -1, nbytes, maxbytes;
+ gchar buffer[1024];
+
+ fitsin = fits_open (fitsfile, "r"); /* Open FITS-file for reading */
+ if (fitsin == NULL)
+ goto err_return;
+
+ if (fitsin->n_pic < 1)
+ goto err_return; /* Any picture in it ? */
+
+ hdu = fits_seek_image (fitsin, 1); /* Position to the first image */
+ if (hdu == NULL)
+ goto err_return;
+
+ if (hdu->naxis < 2)
+ goto err_return; /* Enough dimensions ? */
+
+ pgmout = g_fopen (pgmfile, "wb");
+ if (pgmout == NULL)
+ goto err_return;
+
+ /* Write PGM header with width/height */
+ fprintf (pgmout, "P5\n%d %d\n255\n", hdu->naxisn[0], hdu->naxisn[1]);
+
+ /* Set up transformation for FITS pixel values to 0...255 */
+ /* It maps trans.pixmin to trans.datamin and trans.pixmax to trans.datamax. */
+ /* Values out of range [datamin, datamax] are clamped */
+ trans.pixmin = hdu->pixmin;
+ trans.pixmax = hdu->pixmax;
+ trans.datamin = 0.0;
+ trans.datamax = 255.0;
+ trans.replacement = 0.0; /* Blank/NaN replacement value */
+ trans.dsttyp = 'c'; /* Output type is character */
+
+ nbytes = hdu->naxisn[0]*hdu->naxisn[1];
+ while (nbytes > 0)
+ {
+ maxbytes = sizeof (buffer);
+ if (maxbytes > nbytes)
+ maxbytes = nbytes;
+
+ /* Read pixels and transform them */
+ if (fits_read_pixel (fitsin, hdu, maxbytes, &trans, buffer) != maxbytes)
+ goto err_return;
+
+ if (fwrite (buffer, 1, maxbytes, pgmout) != maxbytes)
+ goto err_return;
+
+ nbytes -= maxbytes;
+ }
+
+ retval = 0;
+
+ err_return:
+
+ if (fitsin)
+ fits_close (fitsin);
+
+ if (pgmout)
+ fclose (pgmout);
+
+ return retval;
+}
+
+
+/*****************************************************************************/
+/* #BEG-PAR */
+/* */
+/* Function : pgmraw to fits - convert raw PGM-file to FITS-file */
+/* */
+/* Parameters: */
+/* char *pgmfile [I] : name of pgmfile */
+/* char *fitsfile [I] : name of fitsfile */
+/* ( mode : I=input, O=output, I/O=input/output ) */
+/* */
+/* The function converts a raw PGM-file to a FITS-file. The FITS-file will */
+/* be upside down, because the orientation for storing the image is */
+/* different. On success, 0 is returned. On failure, -1 is returned. */
+/* */
+/* #END-PAR */
+/*****************************************************************************/
+
+gint
+pgmraw_to_fits (gchar *pgmfile,
+ gchar *fitsfile)
+{
+ FitsFile *fitsout = NULL;
+ FILE *pgmin = NULL;
+ FitsHduList *hdu;
+ gchar buffer[1024];
+ gint width, height, numbytes, maxbytes;
+ gint retval = -1;
+
+ fitsout = fits_open (fitsfile, "w");
+ if (fitsout == NULL)
+ goto err_return;
+
+ pgmin = g_fopen (pgmfile, "r");
+ if (pgmin == NULL)
+ goto err_return;
+
+ /* Read signature of PGM file */
+ if (fgets (buffer, sizeof (buffer), pgmin) == NULL)
+ goto err_return;
+
+ if ((buffer[0] != 'P') || (buffer[1] != '5'))
+ goto err_return;
+
+ /* Skip comments up to width/height */
+ do
+ {
+ if (fgets (buffer, sizeof (buffer), pgmin) == NULL)
+ goto err_return;
+ }
+ while (buffer[0] == '#');
+
+ if (sscanf (buffer, "%d%d", &width, &height) != 2)
+ goto err_return;
+
+ if ((width < 1) || (height < 1))
+ goto err_return;
+
+ /* Skip comments up to maxval */
+ do
+ {
+ if (fgets (buffer, sizeof (buffer), pgmin) == NULL)
+ goto err_return;
+ }
+ while (buffer[0] == '#');
+
+ /* Ignore maxval */
+
+ hdu = fits_add_hdu (fitsout); /* Create a HDU for the FITS file */
+ if (hdu == NULL)
+ goto err_return;
+
+ hdu->used.simple = 1; /* Set proper values */
+ hdu->bitpix = 8;
+ hdu->naxis = 2;
+ hdu->naxisn[0] = width;
+ hdu->naxisn[1] = height;
+ hdu->used.datamin = 1;
+ hdu->datamin = 0.0;
+ hdu->used.datamax = 1;
+ hdu->datamax = 255.0;
+ hdu->used.bzero = 1;
+ hdu->bzero = 0.0;
+ hdu->used.bscale = 1;
+ hdu->bscale = 1.0;
+
+ fits_add_card (hdu, "");
+ fits_add_card (hdu,
+ "HISTORY THIS FITS FILE WAS GENERATED BY FITSRW "
+ "USING PGMRAW_TO_FITS");
+
+ /* Write the header. Blocking is done automatically */
+ if (fits_write_header (fitsout, hdu) < 0)
+ goto err_return;
+
+ /* The primary array plus blocking must be written manually */
+ numbytes = width * height;
+
+ while (numbytes > 0)
+ {
+ maxbytes = sizeof (buffer);
+ if (maxbytes > numbytes)
+ maxbytes = numbytes;
+
+ if (fread (buffer, 1, maxbytes, pgmin) != maxbytes)
+ goto err_return;
+
+ if (fwrite (buffer, 1, maxbytes, fitsout->fp) != maxbytes)
+ goto err_return;
+
+ numbytes -= maxbytes;
+ }
+
+ /* Do blocking */
+ numbytes = (width * height) % FITS_RECORD_SIZE;
+ if (numbytes)
+ {
+ while (numbytes++ < FITS_RECORD_SIZE)
+ if (putc (0, fitsout->fp) == EOF)
+ goto err_return;
+ }
+
+ retval = 0;
+
+ err_return:
+
+ if (fitsout)
+ fits_close (fitsout);
+
+ if (pgmin)
+ fclose (pgmin);
+
+ return retval;
+}
+
+#endif /* ! FITS_NO_DEMO */
diff --git a/plug-ins/file-fits/fits-io.h b/plug-ins/file-fits/fits-io.h
new file mode 100644
index 0000000..8732db1
--- /dev/null
+++ b/plug-ins/file-fits/fits-io.h
@@ -0,0 +1,199 @@
+/******************************************************************************/
+/* Peter Kirchgessner */
+/* e-mail: pkirchg@aol.com */
+/* WWW : http://members.aol.com/pkirchg */
+/******************************************************************************/
+/* #BEG-HDR */
+/* */
+/* Package : FITS reading/writing library */
+/* Modul-Name : fitsrw.h */
+/* Description : Include file for FITS-r/w-library */
+/* Function(s) : */
+/* Author : P. Kirchgessner */
+/* Date of Gen. : 12-Apr-97 */
+/* Last modified : 17-May-97 */
+/* Version : 0.10 */
+/* Compiler Opt. : */
+/* Changes : */
+/* */
+/* #END-HDR */
+/******************************************************************************/
+
+#ifndef __FITS_IO_H__
+#define __FITS_IO_H__
+
+#define FITS_CARD_SIZE 80
+#define FITS_RECORD_SIZE 2880
+#define FITS_MAX_AXIS 999
+
+#define FITS_NADD_CARDS 128
+
+/* Data representations */
+
+typedef guchar FitsBitpix8;
+typedef gint16 FitsBitpix16;
+typedef gint32 FitsBitpix32;
+typedef float FitsBitpixM32;
+typedef double FitsBitpixM64;
+typedef gint32 FitsBool;
+typedef gint32 FitsLong;
+typedef double FitsDouble;
+typedef char FitsString[FITS_CARD_SIZE];
+
+typedef enum
+{
+ FITS_DATA_TYPE_BITPIX_8,
+ FITS_DATA_TYPE_BITPIX_16,
+ FITS_DATA_TYPE_BITPIX_32,
+ FITS_DATA_TYPE_BITPIX_M32,
+ FITS_DATA_TYPE_BITPIX_M64,
+ FITS_DATA_TYPE_FBOOL,
+ FITS_DATA_TYPE_FLONG,
+ FITS_DATA_TYPE_FDOUBLE,
+ FITS_DATA_TYPE_FSTRING
+} FitsDataType;
+
+typedef union
+{
+ FitsBitpix8 bitpix8;
+ FitsBitpix16 bitpix16;
+ FitsBitpix32 bitpix32;
+ FitsBitpixM32 bitpixm32;
+ FitsBitpixM64 bitpixm64;
+ FitsBool fbool;
+ FitsLong flong;
+ FitsDouble fdouble;
+ FitsString fstring;
+} FitsData;
+
+
+/* How to transform FITS pixel values */
+
+typedef struct _FitsPixTransform FitsPixTransform;
+
+struct _FitsPixTransform
+{
+ gdouble pixmin, pixmax; /* Pixel values [pixmin,pixmax] should be mapped */
+ gdouble datamin, datamax; /* to [datamin,datamax] */
+ gdouble replacement; /* datavalue to use for blank or NaN pixels */
+ gchar dsttyp; /* Destination typ ('c' = char) */
+};
+
+
+/* Record list */
+
+typedef struct _FitsRecordList FitsRecordList;
+
+struct _FitsRecordList
+{
+ gchar data[FITS_RECORD_SIZE];
+ FitsRecordList *next_record;
+};
+
+
+/* Header and Data Unit List */
+
+typedef struct _FitsHduList FitsHduList;
+
+struct _FitsHduList
+{
+ glong header_offset; /* Offset of header in the file */
+ glong data_offset; /* Offset of data in the file */
+ glong data_size; /* Size of data in the HDU (including pad)*/
+ glong udata_size; /* Size of used data in the HDU (excl. pad) */
+ gint bpp; /* Bytes per pixel */
+ gint numpic; /* Number of interpretable images in HDU */
+ gint naddcards; /* Number of additional cards */
+ gchar addcards[FITS_NADD_CARDS][FITS_CARD_SIZE];
+ struct
+ {
+ gboolean nan_value; /* NaN's found in data ? */
+ gboolean blank_value; /* Blanks found in data ? */
+ /* Flags specifying if some cards are used */
+ gchar blank; /* The corresponding data below is only */
+ gchar datamin; /* valid, if the flag is set. */
+ gchar datamax;
+ gchar simple; /* This indicates a simple HDU */
+ gchar xtension; /* This indicates an extension */
+ gchar gcount;
+ gchar pcount;
+ gchar bzero;
+ gchar bscale;
+ gchar groups;
+ gchar extend;
+ } used;
+ gdouble pixmin, pixmax; /* Minimum/Maximum pixel values */
+ /* Some decoded data of the HDU: */
+ gint naxis; /* Number of axes */
+ gint naxisn[FITS_MAX_AXIS]; /* Sizes of axes (NAXIS1 --> naxisn[0]) */
+ gint bitpix; /* Data representation (8,16,32,-16,-32) */
+ /* When using the following data, */
+ /* the used-flags must be checked before. */
+ glong blank; /* Blank value */
+ gdouble datamin, datamax; /* Minimum/Maximum physical data values */
+ gchar xtension[FITS_CARD_SIZE];/* Type of extension */
+ glong gcount, pcount; /* Used by XTENSION */
+ gdouble bzero, bscale; /* Transformation values */
+ gint groups; /* Random groups indicator */
+ gint extend; /* Extend flag */
+
+ FitsRecordList *header_record_list; /* Header records read in */
+
+ FitsHduList *next_hdu;
+};
+
+
+typedef struct _FitsFile FitsFile;
+
+struct _FitsFile
+{
+ FILE *fp; /* File pointer to fits file */
+ gchar openmode; /* Mode the file was opened (0, 'r', 'w') */
+
+ gint n_hdu; /* Number of HDUs in file */
+ gint n_pic; /* Total number of interpretable pictures */
+ gboolean nan_used; /* NaN's used in the file ? */
+ gboolean blank_used; /* Blank's used in the file ? */
+
+ FitsHduList *hdu_list; /* Header and Data Unit List */
+};
+
+
+/* User callable functions of the FITS-library */
+
+FitsFile * fits_open (const gchar *filename,
+ const gchar *openmode);
+void fits_close (FitsFile *ff);
+FitsHduList * fits_add_hdu (FitsFile *ff);
+gint fits_add_card (FitsHduList *hdulist,
+ const gchar *card);
+gint fits_write_header (FitsFile *ff,
+ FitsHduList *hdulist);
+FitsHduList * fits_image_info (FitsFile *ff,
+ gint picind,
+ gint *hdupicind);
+FitsHduList * fits_seek_image (FitsFile *ff,
+ gint picind);
+void fits_print_header (FitsHduList *hdr);
+FitsData * fits_decode_card (const gchar *card,
+ FitsDataType data_type);
+gchar * fits_search_card (FitsRecordList *rl,
+ gchar *keyword);
+gint fits_read_pixel (FitsFile *ff,
+ FitsHduList *hdulist,
+ gint npix,
+ FitsPixTransform *trans,
+ void *buf);
+
+gchar * fits_get_error (void);
+
+
+/* Demo functions */
+
+#define FITS_NO_DEMO
+gint fits_to_pgmraw (gchar *fitsfile,
+ gchar *pgmfile);
+gint pgmraw_to_fits (gchar *pgmfile,
+ gchar *fitsfile);
+
+#endif /* __FITS_IO_H__ */
diff --git a/plug-ins/file-fits/fits.c b/plug-ins/file-fits/fits.c
new file mode 100644
index 0000000..b843a68
--- /dev/null
+++ b/plug-ins/file-fits/fits.c
@@ -0,0 +1,1222 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * FITS file plugin
+ * reading and writing code Copyright (C) 1997 Peter Kirchgessner
+ * e-mail: peter@kirchgessner.net, WWW: http://www.kirchgessner.net
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Event history:
+ * V 1.00, PK, 05-May-97: Creation
+ * V 1.01, PK, 19-May-97: Problem with compilation on Irix fixed
+ * V 1.02, PK, 08-Jun-97: Bug with saving gray images fixed
+ * V 1.03, PK, 05-Oct-97: Parse rc-file
+ * V 1.04, PK, 12-Oct-97: No progress bars for non-interactive mode
+ * V 1.05, nn, 20-Dec-97: Initialize image_ID in run()
+ * V 1.06, PK, 21-Nov-99: Internationalization
+ * Fix bug with gimp_export_image()
+ * (moved it from load to save)
+ * V 1.07, PK, 16-Aug-06: Fix problems with internationalization
+ * (writing 255,0 instead of 255.0)
+ * Fix problem with not filling up properly last record
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "fits-io.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-fits-load"
+#define SAVE_PROC "file-fits-save"
+#define PLUG_IN_BINARY "file-fits"
+#define PLUG_IN_ROLE "gimp-file-fits"
+
+
+/* Load info */
+typedef struct
+{
+ gint replace; /* replacement for blank/NaN-values */
+ gint use_datamin; /* Use DATAMIN/MAX-scaling if possible */
+ gint compose; /* compose images with naxis==3 */
+} FITSLoadVals;
+
+
+/* Declare some local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gint save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static FitsHduList * create_fits_header (FitsFile *ofp,
+ guint width,
+ guint height,
+ guint channels,
+ guint bitpix);
+
+static gint save_fits (FitsFile *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID);
+
+static gint32 create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType itype,
+ GimpImageType dtype,
+ GimpPrecision iprecision,
+ gint32 *layer_ID,
+ GeglBuffer **buffer);
+
+static void check_load_vals (void);
+
+static gint32 load_fits (const gchar *filename,
+ FitsFile *ifp,
+ guint picnum,
+ guint ncompose);
+
+static gboolean load_dialog (void);
+static void show_fits_errors (void);
+
+
+static FITSLoadVals plvals =
+{
+ 0, /* Replace with black */
+ 0, /* Do autoscale on pixel-values */
+ 0 /* Don't compose images */
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* The run mode */
+static GimpRunMode l_run_mode;
+
+
+MAIN ()
+
+static void
+query (void)
+
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "load file of the FITS file format",
+ "load file of the FITS file format "
+ "(Flexible Image Transport System)",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1997",
+ N_("Flexible Image Transport System"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-fits");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "fit,fits",
+ "",
+ "0,string,SIMPLE");
+
+ gimp_install_procedure (SAVE_PROC,
+ "export file in the FITS file format",
+ "FITS exporting handles all image types except "
+ "those with alpha channels.",
+ "Peter Kirchgessner",
+ "Peter Kirchgessner (peter@kirchgessner.net)",
+ "1997",
+ N_("Flexible Image Transport System"),
+ "RGB, GRAY, INDEXED",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-fits");
+ gimp_register_save_handler (SAVE_PROC, "fit,fits", "");
+}
+
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ l_run_mode = run_mode = (GimpRunMode)param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PROC, &plvals);
+
+ if (!load_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (LOAD_PROC, &plvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ check_load_vals ();
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ /* Write out error messages of FITS-Library */
+ show_fits_errors ();
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* Store plvals data */
+ if (status == GIMP_PDB_SUCCESS)
+ gimp_set_data (LOAD_PROC, &plvals, sizeof (FITSLoadVals));
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "FITS",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint32 image_ID, *image_list, *nl;
+ guint picnum;
+ gint k, n_images, max_images, hdu_picnum;
+ gint compose;
+ FILE *fp;
+ FitsFile *ifp;
+ FitsHduList *hdu;
+
+ fp = g_fopen (filename, "rb");
+ if (!fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+ fclose (fp);
+
+ ifp = fits_open (filename, "r");
+ if (ifp == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("Error during open of FITS file"));
+ return -1;
+ }
+ if (ifp->n_pic <= 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("FITS file keeps no displayable images"));
+ fits_close (ifp);
+ return -1;
+ }
+
+ image_list = g_new (gint32, 10);
+ n_images = 0;
+ max_images = 10;
+
+ for (picnum = 1; picnum <= ifp->n_pic; )
+ {
+ /* Get image info to see if we can compose them */
+ hdu = fits_image_info (ifp, picnum, &hdu_picnum);
+ if (hdu == NULL) break;
+
+ /* Get number of FITS-images to compose */
+ compose = ( plvals.compose && (hdu_picnum == 1) && (hdu->naxis == 3)
+ && (hdu->naxisn[2] > 1) && (hdu->naxisn[2] <= 4));
+ if (compose)
+ compose = hdu->naxisn[2];
+ else
+ compose = 1; /* Load as GRAY */
+
+ image_ID = load_fits (filename, ifp, picnum, compose);
+
+ /* Write out error messages of FITS-Library */
+ show_fits_errors ();
+
+ if (image_ID == -1) break;
+ if (n_images == max_images)
+ {
+ nl = (gint32 *)g_realloc (image_list, (max_images+10)*sizeof (gint32));
+ if (nl == NULL) break;
+ image_list = nl;
+ max_images += 10;
+ }
+ image_list[n_images++] = image_ID;
+
+ picnum += compose;
+ }
+
+ /* Write out error messages of FITS-Library */
+ show_fits_errors ();
+
+ fits_close (ifp);
+
+ /* Display images in reverse order. The last will be displayed by GIMP itself*/
+ if (l_run_mode != GIMP_RUN_NONINTERACTIVE)
+ {
+ for (k = n_images-1; k >= 1; k--)
+ {
+ gimp_image_undo_enable (image_list[k]);
+ gimp_image_clean_all (image_list[k]);
+ gimp_display_new (image_list[k]);
+ }
+ }
+
+ image_ID = (n_images > 0) ? image_list[0] : -1;
+ g_free (image_list);
+
+ return (image_ID);
+}
+
+
+static gint
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ FitsFile *ofp;
+ GimpImageType drawable_type;
+ gint retval;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+
+ /* Make sure we're not exporting an image with an alpha channel */
+ if (gimp_drawable_has_alpha (drawable_ID))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s",
+ _("FITS export cannot handle images with alpha channels"));
+ return FALSE;
+ }
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE:
+ case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE:
+ case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE:
+ break;
+ default:
+ g_message (_("Cannot operate on unknown image types."));
+ return (FALSE);
+ break;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Open the output file. */
+ ofp = fits_open (filename, "w");
+ if (!ofp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return (FALSE);
+ }
+
+ retval = save_fits (ofp,image_ID, drawable_ID);
+
+ fits_close (ofp);
+
+ return (retval);
+}
+
+
+/* Check (and correct) the load values plvals */
+static void
+check_load_vals (void)
+{
+ if (plvals.replace > 255) plvals.replace = 255;
+}
+
+
+/* Create an image. Sets layer_ID, drawable and rgn. Returns image_ID */
+static gint32
+create_new_image (const gchar *filename,
+ guint pagenum,
+ guint width,
+ guint height,
+ GimpImageBaseType itype,
+ GimpImageType dtype,
+ GimpPrecision iprecision,
+ gint32 *layer_ID,
+ GeglBuffer **buffer)
+{
+ gint32 image_ID;
+ char *tmp;
+
+ image_ID = gimp_image_new_with_precision (width, height, itype, iprecision);
+
+ if ((tmp = g_malloc (strlen (filename) + 64)) != NULL)
+ {
+ sprintf (tmp, "%s-img%ld", filename, (long)pagenum);
+ gimp_image_set_filename (image_ID, tmp);
+ g_free (tmp);
+ }
+ else
+ gimp_image_set_filename (image_ID, filename);
+
+ gimp_image_undo_disable (image_ID);
+ *layer_ID = gimp_layer_new (image_ID, _("Background"), width, height,
+ dtype, 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ gimp_image_insert_layer (image_ID, *layer_ID, -1, 0);
+
+ *buffer = gimp_drawable_get_buffer (*layer_ID);
+
+ return image_ID;
+}
+
+
+/* Load FITS image. ncompose gives the number of FITS-images which have
+ * to be composed together. This will result in different GIMP image types:
+ * 1: GRAY, 2: GRAYA, 3: RGB, 4: RGBA
+ */
+static gint32
+load_fits (const gchar *filename,
+ FitsFile *ifp,
+ guint picnum,
+ guint ncompose)
+{
+ register guchar *dest, *src;
+ guchar *data, *data_end, *linebuf;
+ int width, height, tile_height, scan_lines;
+ int i, j, max_scan;
+ double a, b;
+ gint32 layer_ID, image_ID;
+ GeglBuffer *buffer;
+ GimpImageBaseType itype;
+ GimpImageType dtype;
+ GimpPrecision iprecision;
+ gint err = 0;
+ FitsHduList *hdulist;
+ FitsPixTransform trans;
+ double datamax, replacetransform;
+ const Babl *type, *format;
+
+ hdulist = fits_seek_image (ifp, (int)picnum);
+ if (hdulist == NULL)
+ return -1;
+
+ width = hdulist->naxisn[0]; /* Set the size of the FITS image */
+ height = hdulist->naxisn[1];
+
+ switch (hdulist->bitpix)
+ {
+ case 8:
+ iprecision = GIMP_PRECISION_U8_GAMMA;
+ type = babl_type ("u8");
+ datamax = 255.0;
+ replacetransform = 1.0;
+ break;
+ case 16:
+ iprecision = GIMP_PRECISION_U16_GAMMA; /* FIXME precision */
+ type = babl_type ("u16");
+ datamax = 65535.0;
+ replacetransform = 257;
+ break;
+ case 32:
+ iprecision = GIMP_PRECISION_U32_LINEAR;
+ type = babl_type ("u32");
+ datamax = 4294967295.0;
+ replacetransform = 16843009;
+ break;
+ case -32:
+ iprecision = GIMP_PRECISION_FLOAT_LINEAR;
+ type = babl_type ("float");
+ datamax = 1.0;
+ replacetransform = 1.0 / 255.0;
+ break;
+ case -64:
+ iprecision = GIMP_PRECISION_DOUBLE_LINEAR;
+ type = babl_type ("double");
+ datamax = 1.0;
+ replacetransform = 1.0 / 255.0;
+ break;
+ default:
+ return -1;
+ }
+
+ if (ncompose == 2)
+ {
+ itype = GIMP_GRAY;
+ dtype = GIMP_GRAYA_IMAGE;
+ format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ }
+ else if (ncompose == 3)
+ {
+ itype = GIMP_RGB;
+ dtype = GIMP_RGB_IMAGE;
+ format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ }
+ else if (ncompose == 4)
+ {
+ itype = GIMP_RGB;
+ dtype = GIMP_RGBA_IMAGE;
+ format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ ncompose = 1;
+ itype = GIMP_GRAY;
+ dtype = GIMP_GRAY_IMAGE;
+ format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+
+ image_ID = create_new_image (filename, picnum, width, height,
+ itype, dtype, iprecision,
+ &layer_ID, &buffer);
+
+ tile_height = gimp_tile_height ();
+
+ data = g_malloc (tile_height * width * ncompose * hdulist->bpp);
+ if (data == NULL)
+ return -1;
+
+ data_end = data + tile_height * width * ncompose * hdulist->bpp;
+
+ /* If the transformation from pixel value to data value has been
+ * specified, use it
+ */
+ if (plvals.use_datamin &&
+ hdulist->used.datamin && hdulist->used.datamax &&
+ hdulist->used.bzero && hdulist->used.bscale)
+ {
+ a = (hdulist->datamin - hdulist->bzero) / hdulist->bscale;
+ b = (hdulist->datamax - hdulist->bzero) / hdulist->bscale;
+
+ if (a < b)
+ trans.pixmin = a, trans.pixmax = b;
+ else
+ trans.pixmin = b, trans.pixmax = a;
+ }
+ else
+ {
+ trans.pixmin = hdulist->pixmin;
+ trans.pixmax = hdulist->pixmax;
+ }
+
+ trans.datamin = 0.0;
+ trans.datamax = datamax;
+ trans.replacement = plvals.replace * replacetransform;
+ trans.dsttyp = 'k';
+
+ /* FITS stores images with bottom row first. Therefore we have to
+ * fill the image from bottom to top.
+ */
+
+ if (ncompose == 1)
+ {
+ dest = data + tile_height * width * hdulist->bpp;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ /* Read FITS line */
+ dest -= width * hdulist->bpp;
+ if (fits_read_pixel (ifp, hdulist, width, &trans, dest) != width)
+ {
+ err = 1;
+ break;
+ }
+
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + 1) / (gdouble) height);
+
+ if ((scan_lines == tile_height) || ((i + 1) == height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, height - i - 1,
+ width, scan_lines), 0,
+ format, dest, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data + tile_height * width * hdulist->bpp;
+ }
+
+ if (err)
+ break;
+ }
+ }
+ else /* multiple images to compose */
+ {
+ gint channel;
+
+ linebuf = g_malloc (width * hdulist->bpp);
+ if (linebuf == NULL)
+ return -1;
+
+ for (channel = 0; channel < ncompose; channel++)
+ {
+ dest = data + tile_height * width * hdulist->bpp * ncompose + channel * hdulist->bpp;
+ scan_lines = 0;
+
+ for (i = 0; i < height; i++)
+ {
+ if ((channel > 0) && ((i % tile_height) == 0))
+ {
+ /* Reload a region for follow up channels */
+ max_scan = tile_height;
+
+ if (i + tile_height > height)
+ max_scan = height - i;
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, height - i - max_scan,
+ width, max_scan), 1.0,
+ format, data_end - max_scan * width * hdulist->bpp * ncompose,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ /* Read FITS scanline */
+ dest -= width * ncompose * hdulist->bpp;
+ if (fits_read_pixel (ifp, hdulist, width, &trans, linebuf) != width)
+ {
+ err = 1;
+ break;
+ }
+ j = width;
+ src = linebuf;
+ while (j--)
+ {
+ memcpy (dest, src, hdulist->bpp);
+ src += hdulist->bpp;
+ dest += ncompose * hdulist->bpp;
+ }
+ dest -= width * ncompose * hdulist->bpp;
+ scan_lines++;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (channel * height + i + 1) /
+ (gdouble) (height * ncompose));
+
+ if ((scan_lines == tile_height) || ((i + 1) == height))
+ {
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, height - i - 1,
+ width, scan_lines), 0,
+ format, dest - channel * hdulist->bpp, GEGL_AUTO_ROWSTRIDE);
+
+ scan_lines = 0;
+ dest = data + tile_height * width * ncompose * hdulist->bpp + channel * hdulist->bpp;
+ }
+
+ if (err)
+ break;
+ }
+ }
+
+ g_free (linebuf);
+ }
+
+ g_free (data);
+
+ if (err)
+ g_message (_("EOF encountered on reading"));
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return err ? -1 : image_ID;
+}
+
+
+static FitsHduList *
+create_fits_header (FitsFile *ofp,
+ guint width,
+ guint height,
+ guint channels,
+ guint bitpix)
+{
+ FitsHduList *hdulist;
+ gint print_ctype3 = 0; /* The CTYPE3-card may not be FITS-conforming */
+
+ static const char *ctype3_card[] =
+ {
+ NULL, NULL, NULL, /* bpp = 0: no additional card */
+ "COMMENT Image type within GIMP: GIMP_GRAY_IMAGE",
+ NULL,
+ NULL,
+ "COMMENT Image type within GIMP: GIMP_GRAYA_IMAGE (gray with alpha channel)",
+ "COMMENT Sequence for NAXIS3 : GRAY, ALPHA",
+ "CTYPE3 = 'GRAYA ' / GRAY IMAGE WITH ALPHA CHANNEL",
+ "COMMENT Image type within GIMP: GIMP_RGB_IMAGE",
+ "COMMENT Sequence for NAXIS3 : RED, GREEN, BLUE",
+ "CTYPE3 = 'RGB ' / RGB IMAGE",
+ "COMMENT Image type within GIMP: GIMP_RGBA_IMAGE (rgb with alpha channel)",
+ "COMMENT Sequence for NAXIS3 : RED, GREEN, BLUE, ALPHA",
+ "CTYPE3 = 'RGBA ' / RGB IMAGE WITH ALPHA CHANNEL"
+ };
+
+ hdulist = fits_add_hdu (ofp);
+ if (hdulist == NULL)
+ return NULL;
+
+ hdulist->used.simple = 1;
+ hdulist->bitpix = bitpix;
+ hdulist->naxis = (channels == 1) ? 2 : 3;
+ hdulist->naxisn[0] = width;
+ hdulist->naxisn[1] = height;
+ hdulist->naxisn[2] = channels;
+ hdulist->used.datamin = 1;
+ hdulist->datamin = 0.0;
+ hdulist->used.datamax = 1;
+ hdulist->used.bzero = 1;
+ hdulist->bzero = 0.0;
+ hdulist->used.bscale = 1;
+ hdulist->bscale = 1.0;
+
+ switch (bitpix)
+ {
+ case 8:
+ hdulist->datamax = 255;
+ break;
+ case 16:
+ hdulist->datamax = 65535;
+ break;
+ case 32:
+ hdulist->datamax = 4294967295.0; /* .0 to silence gcc */
+ break;
+ case -32:
+ hdulist->datamax = 1.0;
+ break;
+ case -64:
+ hdulist->datamax = 1.0;
+ break;
+ default:
+ return NULL;
+ }
+
+ fits_add_card (hdulist, "");
+ fits_add_card (hdulist,
+ "HISTORY THIS FITS FILE WAS GENERATED BY GIMP USING FITSRW");
+ fits_add_card (hdulist, "");
+ fits_add_card (hdulist,
+ "COMMENT FitsRW is (C) Peter Kirchgessner (peter@kirchgessner.net), but available");
+ fits_add_card (hdulist,
+ "COMMENT under the GNU general public licence.");
+ fits_add_card (hdulist,
+ "COMMENT For sources see http://www.kirchgessner.net");
+ fits_add_card (hdulist, "");
+ fits_add_card (hdulist, ctype3_card[channels * 3]);
+
+ if (ctype3_card[channels * 3 + 1] != NULL)
+ fits_add_card (hdulist, ctype3_card[channels * 3 + 1]);
+
+ if (print_ctype3 && (ctype3_card[channels * 3 + 2] != NULL))
+ fits_add_card (hdulist, ctype3_card[channels * 3 + 2]);
+
+ fits_add_card (hdulist, "");
+
+ return hdulist;
+}
+
+
+/* Save direct colors (GRAY, GRAYA, RGB, RGBA) */
+static gint
+save_fits (FitsFile *ofp,
+ gint32 image_ID,
+ gint32 drawable_ID)
+{
+ gint height, width, i, j, channel, channelnum;
+ gint tile_height, bpp, bpsl, bitpix, bpc;
+ long nbytes;
+ guchar *data, *src;
+ GeglBuffer *buffer;
+ const Babl *format, *type;
+ FitsHduList *hdu;
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ format = gegl_buffer_get_format (buffer);
+ type = babl_format_get_type (format, 0);
+
+ if (type == babl_type ("u8"))
+ {
+ bitpix = 8;
+ }
+ else if (type == babl_type ("u16"))
+ {
+ bitpix = 16;
+ }
+ else if (type == babl_type ("u32"))
+ {
+ bitpix = 32;
+ }
+ else if (type == babl_type ("half"))
+ {
+ bitpix = -32;
+ type = babl_type ("float");
+ }
+ else if (type == babl_type ("float"))
+ {
+ bitpix = -32;
+ }
+ else if (type == babl_type ("double"))
+ {
+ bitpix = -64;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ switch (gimp_drawable_type (drawable_ID))
+ {
+ case GIMP_GRAY_IMAGE:
+ format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ break;
+
+ case GIMP_RGB_IMAGE:
+ case GIMP_INDEXED_IMAGE:
+ format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ break;
+ }
+
+ channelnum = babl_format_get_n_components (format);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ bpc = bpp / channelnum; /* Bytes per channel */
+ bpsl = width * bpp; /* Bytes per scanline */
+
+ tile_height = gimp_tile_height ();
+
+ /* allocate a buffer for retrieving information from the pixel region */
+ src = data = (guchar *) g_malloc (width * height * bpp);
+
+ hdu = create_fits_header (ofp, width, height, channelnum, bitpix);
+ if (hdu == NULL)
+ return FALSE;
+
+ if (fits_write_header (ofp, hdu) < 0)
+ return FALSE;
+
+ nbytes = 0;
+ for (channel = 0; channel < channelnum; channel++)
+ {
+ for (i = 0; i < height; i++)
+ {
+ if ((i % tile_height) == 0)
+ {
+ gint scan_lines;
+
+ scan_lines = (i + tile_height-1 < height) ?
+ tile_height : (height - i);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, height - i - scan_lines,
+ width, scan_lines), 1.0,
+ format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = data + bpsl * (scan_lines - 1) + channel * bpc;
+ }
+
+ if (channelnum == 1 && bitpix == 8) /* One channel and 8 bit? Write the scanline */
+ {
+ fwrite (src, bpc, width, ofp->fp);
+ src += bpsl;
+ }
+ else /* Multiple channels or high bit depth */
+ {
+ /* Write out bytes for current channel */
+ /* FIXME: Don't assume a little endian arch */
+ switch (bitpix)
+ {
+ case 8:
+ for (j = 0; j < width; j++)
+ {
+ putc (*src, ofp->fp);
+ src += bpp;
+ }
+ break;
+ case 16:
+ for (j = 0; j < width; j++)
+ {
+ *((guint16*)src) += 32768;
+ putc (*(src + 1), ofp->fp);
+ putc (*(src + 0), ofp->fp);
+ src += bpp;
+ }
+ break;
+ case 32:
+ for (j = 0; j < width; j++)
+ {
+ *((guint32*)src) += 2147483648.0; /* .0 to silence gcc */
+ putc (*(src + 3), ofp->fp);
+ putc (*(src + 2), ofp->fp);
+ putc (*(src + 1), ofp->fp);
+ putc (*(src + 0), ofp->fp);
+ src += bpp;
+ }
+ break;
+ case -32:
+ for (j = 0; j < width; j++)
+ {
+ putc (*(src + 3), ofp->fp);
+ putc (*(src + 2), ofp->fp);
+ putc (*(src + 1), ofp->fp);
+ putc (*(src + 0), ofp->fp);
+ src += bpp;
+ }
+ break;
+ case -64:
+ for (j = 0; j < width; j++)
+ {
+ putc (*(src + 7), ofp->fp);
+ putc (*(src + 6), ofp->fp);
+ putc (*(src + 5), ofp->fp);
+ putc (*(src + 4), ofp->fp);
+ putc (*(src + 3), ofp->fp);
+ putc (*(src + 2), ofp->fp);
+ putc (*(src + 1), ofp->fp);
+ putc (*(src + 0), ofp->fp);
+ src += bpp;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ nbytes += width * bpc;
+ src -= 2 * bpsl;
+
+ if ((i % 20) == 0)
+ gimp_progress_update ((gdouble) (i + channel * height) /
+ (gdouble) (height * channelnum));
+ }
+ }
+
+ nbytes = nbytes % FITS_RECORD_SIZE;
+ if (nbytes)
+ {
+ while (nbytes++ < FITS_RECORD_SIZE)
+ putc (0, ofp->fp);
+ }
+
+ g_free (data);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ if (ferror (ofp->fp))
+ {
+ g_message (_("Write error occurred"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Load interface functions */
+
+static gboolean
+load_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Load FITS File"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Replacement for undefined pixels"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.replace, plvals.replace,
+
+ _("_Black"), 0, NULL,
+ _("_White"), 255, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame =
+ gimp_int_radio_group_new (TRUE, _("Pixel value scaling"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.use_datamin, plvals.use_datamin,
+
+ _("_Automatic"), FALSE, NULL,
+ _("By _DATAMIN/DATAMAX"), TRUE, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ frame =
+ gimp_int_radio_group_new (TRUE, _("Image Composing"),
+ G_CALLBACK (gimp_radio_button_update),
+ &plvals.compose, plvals.compose,
+
+ C_("composing", "_None"), FALSE, NULL,
+ "NA_XIS=3, NAXIS3=2,...,4", TRUE, NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+show_fits_errors (void)
+{
+ const gchar *msg;
+
+ /* Write out error messages of FITS-Library */
+ while ((msg = fits_get_error ()) != NULL)
+ g_message ("%s", msg);
+}
diff --git a/plug-ins/file-fli/Makefile.am b/plug-ins/file-fli/Makefile.am
new file mode 100644
index 0000000..55e6322
--- /dev/null
+++ b/plug-ins/file-fli/Makefile.am
@@ -0,0 +1,49 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_fli_RC = file-fli.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-fli
+
+libexec_PROGRAMS = file-fli
+
+file_fli_SOURCES = \
+ fli.h \
+ fli.c \
+ fli-gimp.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_fli_RC)
diff --git a/plug-ins/file-fli/Makefile.in b/plug-ins/file-fli/Makefile.in
new file mode 100644
index 0000000..937979d
--- /dev/null
+++ b/plug-ins/file-fli/Makefile.in
@@ -0,0 +1,1004 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-fli$(EXEEXT)
+subdir = plug-ins/file-fli
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_fli_OBJECTS = fli.$(OBJEXT) fli-gimp.$(OBJEXT)
+file_fli_OBJECTS = $(am_file_fli_OBJECTS)
+file_fli_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_fli_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_fli_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/fli-gimp.Po ./$(DEPDIR)/fli.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_fli_SOURCES)
+DIST_SOURCES = $(file_fli_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-fli
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_fli_RC = file-fli.rc.o
+AM_LDFLAGS = $(mwindows)
+file_fli_SOURCES = \
+ fli.h \
+ fli.c \
+ fli-gimp.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_fli_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-fli/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-fli/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-fli$(EXEEXT): $(file_fli_OBJECTS) $(file_fli_DEPENDENCIES) $(EXTRA_file_fli_DEPENDENCIES)
+ @rm -f file-fli$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_fli_OBJECTS) $(file_fli_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fli-gimp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fli.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/fli-gimp.Po
+ -rm -f ./$(DEPDIR)/fli.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/fli-gimp.Po
+ -rm -f ./$(DEPDIR)/fli.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-fli/fli-gimp.c b/plug-ins/file-fli/fli-gimp.c
new file mode 100644
index 0000000..fb14649
--- /dev/null
+++ b/plug-ins/file-fli/fli-gimp.c
@@ -0,0 +1,984 @@
+/*
+ * GFLI 1.3
+ *
+ * A gimp plug-in to read and write FLI and FLC movies.
+ *
+ * Copyright (C) 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * This is a first loader for FLI and FLC movies. It uses as the same method as
+ * the gif plug-in to store the animation (i.e. 1 layer/frame).
+ *
+ * Current disadvantages:
+ * - Generates A LOT OF warnings.
+ * - Consumes a lot of memory (See wish-list: use the original data or
+ * compression).
+ * - doesn't support palette changes between two frames.
+ *
+ * Wish-List:
+ * - I'd like to have a different format for storing animations, so I can use
+ * Layers and Alpha-Channels for effects. An older version of
+ * this plug-in created one image per frame, and went real soon out of
+ * memory.
+ * - I'd like a method that requests unmodified frames from the original
+ * image, and stores modified without destroying the original file.
+ * - I'd like a way to store additional information about a image to it, for
+ * example copyright stuff or a timecode.
+ * - I've thought about a small utility to mix MIDI events as custom chunks
+ * between the FLI chunks. Anyone interested in implementing this ?
+ */
+
+/*
+ * History:
+ * 1.0 first release
+ * 1.1 first support for FLI saving (BRUN and LC chunks)
+ * 1.2 support for load/save ranges, fixed SGI & SUN problems (I hope...), fixed FLC
+ * 1.3 made saving actually work, alpha channel is silently ignored;
+ loading was broken too, fixed it --Sven
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "fli.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define LOAD_PROC "file-fli-load"
+#define SAVE_PROC "file-fli-save"
+#define INFO_PROC "file-fli-info"
+#define PLUG_IN_BINARY "file-fli"
+#define PLUG_IN_ROLE "gimp-file-fli"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* return the image-ID of the new image, or -1 in case of an error */
+static gint32 load_image (const gchar *filename,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error);
+static gboolean load_dialog (const gchar *filename);
+
+static gboolean save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error);
+static gboolean save_dialog (gint32 image_id);
+
+static gboolean get_info (const gchar *filename,
+ gint32 *width,
+ gint32 *height,
+ gint32 *frames,
+ GError **error);
+
+/*
+ * GIMP interface
+ */
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static const GimpParamDef load_args[] =
+{
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "from-frame", "Load beginning from this frame" },
+ { GIMP_PDB_INT32, "to-frame", "End loading with this frame" }
+};
+
+static const GimpParamDef load_return_vals[] =
+{
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+};
+
+static const GimpParamDef save_args[] =
+{
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "from-frame", "Export beginning from this frame" },
+ { GIMP_PDB_INT32, "to-frame", "End exporting with this frame" },
+};
+
+static const GimpParamDef info_args[] =
+{
+ { GIMP_PDB_STRING, "filename", "The name of the file to get info" },
+};
+static const GimpParamDef info_return_vals[] =
+{
+ { GIMP_PDB_INT32, "width", "Width of one frame" },
+ { GIMP_PDB_INT32, "height", "Height of one frame" },
+ { GIMP_PDB_INT32, "frames", "Number of Frames" },
+};
+
+
+static gint32 from_frame;
+static gint32 to_frame;
+
+MAIN ()
+
+static void
+query (void)
+{
+ /*
+ * Load/export procedures
+ */
+ gimp_install_procedure (LOAD_PROC,
+ "load FLI-movies",
+ "This is an experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ N_("AutoDesk FLIC animation"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args) - 2,
+ G_N_ELEMENTS (load_return_vals),
+ load_args,
+ load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-flic");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "fli,flc",
+ "",
+ "");
+
+ gimp_install_procedure (SAVE_PROC,
+ "export FLI-movies",
+ "This is an experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ N_("AutoDesk FLIC animation"),
+ "INDEXED,GRAY",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-flic");
+ gimp_register_save_handler (SAVE_PROC,
+ "fli,flc",
+ "");
+
+ /*
+ * Utility functions:
+ */
+ gimp_install_procedure (INFO_PROC,
+ "Get information about a Fli movie",
+ "This is a experimantal plug-in to handle FLI movies",
+ "Jens Ch. Restemeier",
+ "Jens Ch. Restemeier",
+ "1997",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (info_args),
+ G_N_ELEMENTS (info_return_vals),
+ info_args,
+ info_return_vals);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[5];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint32 pc;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * check for valid parameters:
+ * (Or can I trust GIMP ?)
+ */
+ if ((nparams < G_N_ELEMENTS (load_args) - 2) ||
+ (G_N_ELEMENTS (load_args) < nparams))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ for (pc = 0; pc < G_N_ELEMENTS (load_args) - 2; pc++)
+ {
+ if (load_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ for (pc = G_N_ELEMENTS (load_args) - 2; pc < nparams; pc++)
+ {
+ if (load_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+
+ to_frame = ((nparams < G_N_ELEMENTS (load_args) - 1) ?
+ 1 : param[3].data.d_int32);
+ from_frame = ((nparams < G_N_ELEMENTS (load_args)) ?
+ -1 : param[4].data.d_int32);
+
+ image_ID = load_image (param[1].data.d_string,
+ from_frame, to_frame, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ if (load_dialog (param[1].data.d_string))
+ {
+ image_ID = load_image (param[1].data.d_string,
+ from_frame, to_frame, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = orig_image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != G_N_ELEMENTS (save_args))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ for (pc = 0; pc < G_N_ELEMENTS (save_args); pc++)
+ {
+ if (save_args[pc].type!=param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ if (! save_image (param[3].data.d_string, image_ID,
+ param[5].data.d_int32,
+ param[6].data.d_int32, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "FLI",
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+
+ if (save_dialog (param[1].data.d_image))
+ {
+ if (! save_image (param[3].data.d_string,
+ image_ID, from_frame, to_frame, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else if (strcmp (name, INFO_PROC) == 0)
+ {
+ gint32 width, height, frames;
+
+ /*
+ * check for valid parameters;
+ */
+ if (nparams != G_N_ELEMENTS (info_args))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ for (pc = 0; pc < G_N_ELEMENTS (save_args); pc++)
+ {
+ if (info_args[pc].type != param[pc].type)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (get_info (param[0].data.d_string,
+ &width, &height, &frames, &error))
+ {
+ *nreturn_vals = 4;
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = width;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = height;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = frames;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/*
+ * Open FLI animation and return header-info
+ */
+static gboolean
+get_info (const gchar *filename,
+ gint32 *width,
+ gint32 *height,
+ gint32 *frames,
+ GError **error)
+{
+ FILE *file;
+ s_fli_header fli_header;
+
+ *width = 0; *height = 0; *frames = 0;
+
+ file = g_fopen (filename ,"rb");
+
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ fli_read_header (file, &fli_header);
+ fclose (file);
+
+ *width = fli_header.width;
+ *height = fli_header.height;
+ *frames = fli_header.frames;
+
+ return TRUE;
+}
+
+/*
+ * load fli animation and store as framestack
+ */
+static gint32
+load_image (const gchar *filename,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error)
+{
+ FILE *file;
+ GeglBuffer *buffer;
+ gint32 image_id, layer_ID;
+ guchar *fb, *ofb, *fb_x;
+ guchar cm[768], ocm[768];
+ s_fli_header fli_header;
+ gint cnt;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ file = g_fopen (filename ,"rb");
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ fli_read_header (file, &fli_header);
+ if (fli_header.magic == NO_HEADER)
+ {
+ fclose (file);
+ return -1;
+ }
+ else
+ {
+ fseek (file, 128, SEEK_SET);
+ }
+
+ /*
+ * Fix parameters
+ */
+ if ((from_frame==-1) && (to_frame==-1))
+ {
+ /* to make scripting easier: */
+ from_frame=1; to_frame=fli_header.frames;
+ }
+ if (to_frame<from_frame)
+ {
+ to_frame = fli_header.frames;
+ }
+ if (from_frame < 1)
+ {
+ from_frame = 1;
+ }
+ if (to_frame < 1)
+ {
+ /* nothing to do ... */
+ fclose (file);
+ return -1;
+ }
+ if (from_frame >= fli_header.frames)
+ {
+ /* nothing to do ... */
+ fclose (file);
+ return -1;
+ }
+ if (to_frame>fli_header.frames)
+ {
+ to_frame = fli_header.frames;
+ }
+
+ image_id = gimp_image_new (fli_header.width, fli_header.height, GIMP_INDEXED);
+ gimp_image_set_filename (image_id, filename);
+
+ fb = g_malloc (fli_header.width * fli_header.height);
+ ofb = g_malloc (fli_header.width * fli_header.height);
+
+ /*
+ * Skip to the beginning of requested frames:
+ */
+ for (cnt = 1; cnt < from_frame; cnt++)
+ {
+ fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
+ memcpy (ocm, cm, 768);
+ fb_x = fb; fb = ofb; ofb = fb_x;
+ }
+ /*
+ * Load range
+ */
+ for (cnt = from_frame; cnt <= to_frame; cnt++)
+ {
+ gchar *name_buf = g_strdup_printf (_("Frame (%i)"), cnt);
+
+ layer_ID = gimp_layer_new (image_id, name_buf,
+ fli_header.width, fli_header.height,
+ GIMP_INDEXED_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+ g_free (name_buf);
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ fli_read_frame (file, &fli_header, ofb, ocm, fb, cm);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0,
+ fli_header.width,
+ fli_header.height), 0,
+ NULL, fb, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ if (cnt > 0)
+ gimp_layer_add_alpha (layer_ID);
+
+ gimp_image_insert_layer (image_id, layer_ID, -1, 0);
+
+ if (cnt < to_frame)
+ {
+ memcpy (ocm, cm, 768);
+ fb_x = fb; fb = ofb; ofb = fb_x;
+ }
+
+ gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
+ }
+
+ gimp_image_set_colormap (image_id, cm, 256);
+
+ fclose (file);
+
+ g_free (fb);
+ g_free (ofb);
+
+ gimp_progress_update (1.0);
+
+ return image_id;
+}
+
+
+#define MAXDIFF 195075 /* 3 * SQR (255) + 1 */
+
+/*
+ * get framestack and store as fli animation
+ * (some code was taken from the GIF plugin.)
+ */
+static gboolean
+save_image (const gchar *filename,
+ gint32 image_id,
+ gint32 from_frame,
+ gint32 to_frame,
+ GError **error)
+{
+ FILE *file;
+ gint32 *framelist;
+ gint nframes;
+ gint colors, i;
+ guchar *cmap;
+ guchar bg;
+ guchar red, green, blue;
+ gint diff, sum, max;
+ gint offset_x, offset_y, xc, yc, xx, yy;
+ guint rows, cols, bytes;
+ guchar *src_row;
+ guchar *fb, *ofb;
+ guchar cm[768];
+ GimpRGB background;
+ s_fli_header fli_header;
+ gint cnt;
+
+ framelist = gimp_image_get_layers (image_id, &nframes);
+
+ if ((from_frame == -1) && (to_frame == -1))
+ {
+ /* to make scripting easier: */
+ from_frame = 0; to_frame = nframes;
+ }
+ if (to_frame < from_frame)
+ {
+ to_frame = nframes;
+ }
+ if (from_frame < 1)
+ {
+ from_frame = 1;
+ }
+ if (to_frame < 1)
+ {
+ /* nothing to do ... */
+ return FALSE;
+ }
+ if (from_frame > nframes)
+ {
+ /* nothing to do ... */
+ return FALSE;
+ }
+ if (to_frame > nframes)
+ {
+ to_frame = nframes;
+ }
+
+ gimp_context_get_background (&background);
+ gimp_rgb_get_uchar (&background, &red, &green, &blue);
+
+ switch (gimp_image_base_type (image_id))
+ {
+ case GIMP_GRAY:
+ /* build grayscale palette */
+ for (i = 0; i < 256; i++)
+ {
+ cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
+ }
+ bg = GIMP_RGB_LUMINANCE (red, green, blue) + 0.5;
+ break;
+
+ case GIMP_INDEXED:
+ max = MAXDIFF;
+ bg = 0;
+ cmap = gimp_image_get_colormap (image_id, &colors);
+ for (i = 0; i < MIN (colors, 256); i++)
+ {
+ cm[i*3+0] = cmap[i*3+0];
+ cm[i*3+1] = cmap[i*3+1];
+ cm[i*3+2] = cmap[i*3+2];
+
+ diff = red - cm[i*3+0];
+ sum = SQR (diff);
+ diff = green - cm[i*3+1];
+ sum += SQR (diff);
+ diff = blue - cm[i*3+2];
+ sum += SQR (diff);
+
+ if (sum < max)
+ {
+ bg = i;
+ max = sum;
+ }
+ }
+ for (i = colors; i < 256; i++)
+ {
+ cm[i*3+0] = cm[i*3+1] = cm[i*3+2] = i;
+ }
+ break;
+
+ default:
+ g_message (_("Sorry, I can export only INDEXED and GRAY images."));
+ return FALSE;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /*
+ * First build the fli header.
+ */
+ fli_header.filesize = 0; /* will be fixed when writing the header */
+ fli_header.frames = 0; /* will be fixed during the write */
+ fli_header.width = gimp_image_width (image_id);
+ fli_header.height = gimp_image_height (image_id);
+
+ if ((fli_header.width == 320) && (fli_header.height == 200))
+ {
+ fli_header.magic = HEADER_FLI;
+ }
+ else
+ {
+ fli_header.magic = HEADER_FLC;
+ }
+ fli_header.depth = 8; /* I've never seen a depth != 8 */
+ fli_header.flags = 3;
+ fli_header.speed = 1000 / 25;
+ fli_header.created = 0; /* program ID. not necessary... */
+ fli_header.updated = 0; /* date in MS-DOS format. ignore...*/
+ fli_header.aspect_x = 1; /* aspect ratio. Will be added as soon.. */
+ fli_header.aspect_y = 1; /* ... as GIMP supports it. */
+ fli_header.oframe1 = fli_header.oframe2 = 0; /* will be fixed during the write */
+
+ file = g_fopen (filename ,"wb");
+ if (!file)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+ fseek (file, 128, SEEK_SET);
+
+ fb = g_malloc (fli_header.width * fli_header.height);
+ ofb = g_malloc (fli_header.width * fli_header.height);
+
+ /* initialize with bg color */
+ memset (fb, bg, fli_header.width * fli_header.height);
+
+ /*
+ * Now write all frames
+ */
+ for (cnt = from_frame; cnt <= to_frame; cnt++)
+ {
+ GeglBuffer *buffer;
+ const Babl *format = NULL;
+
+ buffer = gimp_drawable_get_buffer (framelist[nframes-cnt]);
+
+ if (gimp_drawable_is_gray (framelist[nframes-cnt]))
+ {
+ if (gimp_drawable_has_alpha (framelist[nframes-cnt]))
+ format = babl_format ("Y' u8");
+ else
+ format = babl_format ("Y'A u8");
+ }
+ else
+ {
+ format = gegl_buffer_get_format (buffer);
+ }
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+
+ gimp_drawable_offsets (framelist[nframes-cnt], &offset_x, &offset_y);
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ src_row = g_malloc (cols * bytes);
+
+ /* now paste it into the framebuffer, with the necessary offset */
+ for (yc = 0, yy = offset_y; yc < rows; yc++, yy++)
+ {
+ if (yy >= 0 && yy < fli_header.height)
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, yc, cols, 1), 1.0,
+ format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (xc = 0, xx = offset_x; xc < cols; xc++, xx++)
+ {
+ if (xx >= 0 && xx < fli_header.width)
+ fb[yy * fli_header.width + xx] = src_row[xc * bytes];
+ }
+ }
+ }
+
+ g_free (src_row);
+ g_object_unref (buffer);
+
+ /* save the frame */
+ if (cnt > from_frame)
+ {
+ /* save frame, allow all codecs */
+ fli_write_frame (file, &fli_header, ofb, cm, fb, cm, W_ALL);
+ }
+ else
+ {
+ /* save first frame, no delta information, allow all codecs */
+ fli_write_frame (file, &fli_header, NULL, NULL, fb, cm, W_ALL);
+ }
+
+ if (cnt < to_frame)
+ memcpy (ofb, fb, fli_header.width * fli_header.height);
+
+ gimp_progress_update ((double) cnt + 1 / (double)(to_frame - from_frame));
+ }
+
+ /*
+ * finish fli
+ */
+ fli_write_header (file, &fli_header);
+ fclose (file);
+
+ g_free (fb);
+ g_free (ofb);
+ g_free (framelist);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+/*
+ * Dialogs for interactive usage
+ */
+static gboolean
+load_dialog (const gchar *filename)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint32 width, height, nframes;
+ gboolean run;
+
+ get_info (filename, &width, &height, &nframes, NULL);
+
+ from_frame = 1;
+ to_frame = nframes;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("GFLI 1.3 - Load framestack"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, LOAD_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
+ * But for now you can set a start- and a end-frame:
+ */
+ adj = (GtkAdjustment *) gtk_adjustment_new (from_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ C_("frame-range", "_From:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &from_frame);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (to_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ C_("frame-range", "_To:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &to_frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static gboolean
+save_dialog (gint32 image_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+ gint nframes;
+ gboolean run;
+
+ g_free (gimp_image_get_layers (image_id, &nframes));
+
+ from_frame = 1;
+ to_frame = nframes;
+
+ dialog = gimp_export_dialog_new (_("GFLI 1.3"), PLUG_IN_BINARY, SAVE_PROC);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /*
+ * Maybe I add on-the-fly RGB conversion, to keep palettechanges...
+ * But for now you can set a start- and a end-frame:
+ */
+ adj = (GtkAdjustment *) gtk_adjustment_new (from_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ C_("frame-range", "_From:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &from_frame);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (to_frame, 1, nframes, 1, 10, 0);
+ spinbutton = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ C_("frame-range", "_To:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &to_frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/file-fli/fli.c b/plug-ins/file-fli/fli.c
new file mode 100644
index 0000000..c2e28e4
--- /dev/null
+++ b/plug-ins/file-fli/fli.c
@@ -0,0 +1,1033 @@
+
+/*
+ * Written 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This code can be used to read and write FLI movies. It is currently
+ * only used for the GIMP fli plug-in, but it can be used for other
+ * programs, too.
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "fli.h"
+
+/*
+ * To avoid endian-problems I wrote these functions:
+ */
+static unsigned char
+fli_read_char (FILE *f)
+{
+ unsigned char b;
+
+ fread (&b, 1, 1, f);
+ return b;
+}
+
+static unsigned short
+fli_read_short (FILE *f)
+{
+ unsigned char b[2];
+
+ fread (&b, 1, 2, f);
+ return (unsigned short) (b[1]<<8) | b[0];
+}
+
+static unsigned long
+fli_read_long (FILE *f)
+{
+ unsigned char b[4];
+
+ fread (&b, 1, 4, f);
+ return (unsigned long) (b[3]<<24) | (b[2]<<16) | (b[1]<<8) | b[0];
+}
+
+static void
+fli_write_char (FILE *f,
+ unsigned char b)
+{
+ fwrite (&b, 1, 1, f);
+}
+
+static void
+fli_write_short (FILE *f,
+ unsigned short w)
+{
+ unsigned char b[2];
+
+ b[0] = w & 255;
+ b[1] = (w >> 8) & 255;
+ fwrite (&b, 1, 2, f);
+}
+
+static void
+fli_write_long (FILE *f,
+ unsigned long l)
+{
+ unsigned char b[4];
+
+ b[0] = l & 255;
+ b[1] = (l >> 8) & 255;
+ b[2] = (l >> 16) & 255;
+ b[3] = (l >> 24) & 255;
+
+ fwrite (&b, 1, 4, f);
+}
+
+void
+fli_read_header (FILE *f,
+ s_fli_header *fli_header)
+{
+ fli_header->filesize = fli_read_long (f); /* 0 */
+ fli_header->magic = fli_read_short (f); /* 4 */
+ fli_header->frames = fli_read_short (f); /* 6 */
+ fli_header->width = fli_read_short (f); /* 8 */
+ fli_header->height = fli_read_short (f); /* 10 */
+ fli_header->depth = fli_read_short (f); /* 12 */
+ fli_header->flags = fli_read_short (f); /* 14 */
+
+ if (fli_header->magic == HEADER_FLI)
+ {
+ /* FLI saves speed in 1/70s */
+ fli_header->speed = fli_read_short (f) * 14; /* 16 */
+ }
+ else
+ {
+ if (fli_header->magic == HEADER_FLC)
+ {
+ /* FLC saves speed in 1/1000s */
+ fli_header->speed = fli_read_long (f); /* 16 */
+ }
+ else
+ {
+ fprintf (stderr, "error: magic number is wrong !\n");
+ fli_header->magic = NO_HEADER;
+ }
+ }
+
+ if (fli_header->width == 0)
+ fli_header->width = 320;
+
+ if (fli_header->height == 0)
+ fli_header->height = 200;
+}
+
+void
+fli_write_header (FILE *f,
+ s_fli_header *fli_header)
+{
+ fli_header->filesize = ftell (f);
+ fseek (f, 0, SEEK_SET);
+ fli_write_long (f, fli_header->filesize); /* 0 */
+ fli_write_short (f, fli_header->magic); /* 4 */
+ fli_write_short (f, fli_header->frames); /* 6 */
+ fli_write_short (f, fli_header->width); /* 8 */
+ fli_write_short (f, fli_header->height); /* 10 */
+ fli_write_short (f, fli_header->depth); /* 12 */
+ fli_write_short (f, fli_header->flags); /* 14 */
+ if (fli_header->magic == HEADER_FLI)
+ {
+ /* FLI saves speed in 1/70s */
+ fli_write_short (f, fli_header->speed / 14); /* 16 */
+ }
+ else
+ {
+ if (fli_header->magic == HEADER_FLC)
+ {
+ /* FLC saves speed in 1/1000s */
+ fli_write_long (f, fli_header->speed); /* 16 */
+ fseek (f, 80, SEEK_SET);
+ fli_write_long (f, fli_header->oframe1); /* 80 */
+ fli_write_long (f, fli_header->oframe2); /* 84 */
+ }
+ else
+ {
+ fprintf (stderr, "error: magic number in header is wrong !\n");
+ }
+ }
+}
+
+void
+fli_read_frame (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *old_cmap,
+ unsigned char *framebuf,
+ unsigned char *cmap)
+{
+ s_fli_frame fli_frame;
+ unsigned long framepos;
+ int c;
+
+ framepos = ftell (f);
+
+ fli_frame.size = fli_read_long (f);
+ fli_frame.magic = fli_read_short (f);
+ fli_frame.chunks = fli_read_short (f);
+
+ if (fli_frame.magic == FRAME)
+ {
+ fseek (f, framepos + 16, SEEK_SET);
+ for (c = 0; c < fli_frame.chunks; c++)
+ {
+ s_fli_chunk chunk;
+ unsigned long chunkpos;
+
+ chunkpos = ftell (f);
+ chunk.size = fli_read_long (f);
+ chunk.magic = fli_read_short (f);
+ switch (chunk.magic)
+ {
+ case FLI_COLOR:
+ fli_read_color (f, fli_header, old_cmap, cmap);
+ break;
+ case FLI_COLOR_2:
+ fli_read_color_2 (f, fli_header, old_cmap, cmap);
+ break;
+ case FLI_BLACK:
+ fli_read_black (f, fli_header, framebuf);
+ break;
+ case FLI_BRUN:
+ fli_read_brun (f, fli_header, framebuf);
+ break;
+ case FLI_COPY:
+ fli_read_copy (f, fli_header, framebuf);
+ break;
+ case FLI_LC:
+ fli_read_lc (f, fli_header, old_framebuf, framebuf);
+ break;
+ case FLI_LC_2:
+ fli_read_lc_2 (f, fli_header, old_framebuf, framebuf);
+ break;
+ case FLI_MINI:
+ /* unused, skip */
+ break;
+ default:
+ /* unknown, skip */
+ break;
+ }
+ if (chunk.size & 1)
+ chunk.size++;
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+ }
+ }
+ /* else: unknown, skip */
+
+ fseek (f, framepos + fli_frame.size, SEEK_SET);
+}
+
+void
+fli_write_frame (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *old_cmap,
+ unsigned char *framebuf,
+ unsigned char *cmap,
+ unsigned short codec_mask)
+{
+ s_fli_frame fli_frame;
+ unsigned long framepos, frameend;
+
+ framepos = ftell (f);
+ fseek (f, framepos + 16, SEEK_SET);
+
+ switch (fli_header->frames)
+ {
+ case 0:
+ fli_header->oframe1 = framepos;
+ break;
+ case 1:
+ fli_header->oframe2 = framepos;
+ break;
+ }
+
+ fli_frame.size = 0;
+ fli_frame.magic = FRAME;
+ fli_frame.chunks = 0;
+
+ /*
+ * create color chunk
+ */
+ if (fli_header->magic == HEADER_FLI)
+ {
+ if (fli_write_color (f, fli_header, old_cmap, cmap))
+ fli_frame.chunks++;
+ }
+ else
+ {
+ if (fli_header->magic == HEADER_FLC)
+ {
+ if (fli_write_color_2 (f, fli_header, old_cmap, cmap))
+ fli_frame.chunks++;
+ }
+ else
+ {
+ fprintf (stderr, "error: magic number in header is wrong !\n");
+ }
+ }
+
+#if 0
+ if (codec_mask & W_COLOR)
+ {
+ if (fli_write_color (f, fli_header, old_cmap, cmap))
+ fli_frame.chunks++;
+ }
+ if (codec_mask & W_COLOR_2)
+ {
+ if (fli_write_color_2 (f, fli_header, old_cmap, cmap))
+ fli_frame.chunks++;
+ }
+#endif
+ /* create bitmap chunk */
+ if (old_framebuf == NULL)
+ {
+ fli_write_brun (f, fli_header, framebuf);
+ }
+ else
+ {
+ fli_write_lc (f, fli_header, old_framebuf, framebuf);
+ }
+ fli_frame.chunks++;
+
+ frameend = ftell (f);
+ fli_frame.size = frameend - framepos;
+ fseek (f, framepos, SEEK_SET);
+ fli_write_long (f, fli_frame.size);
+ fli_write_short (f, fli_frame.magic);
+ fli_write_short (f, fli_frame.chunks);
+ fseek (f, frameend, SEEK_SET);
+ fli_header->frames++;
+}
+
+/*
+ * palette chunks from the classical Autodesk Animator.
+ */
+void
+fli_read_color (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap)
+{
+ unsigned short num_packets, cnt_packets, col_pos;
+
+ col_pos = 0;
+ num_packets = fli_read_short (f);
+ for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
+ {
+ unsigned short skip_col, num_col, col_cnt;
+ skip_col = fli_read_char (f);
+ num_col = fli_read_char (f);
+ if (num_col == 0)
+ {
+ for (col_pos = 0; col_pos < 768; col_pos++)
+ {
+ cmap[col_pos] = fli_read_char (f) << 2;
+ }
+ return;
+ }
+ for (col_cnt = skip_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
+ {
+ cmap[col_pos] = old_cmap[col_pos];col_pos++;
+ cmap[col_pos] = old_cmap[col_pos];col_pos++;
+ cmap[col_pos] = old_cmap[col_pos];col_pos++;
+ }
+ for (col_cnt = num_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
+ {
+ cmap[col_pos++] = fli_read_char (f) << 2;
+ cmap[col_pos++] = fli_read_char (f) << 2;
+ cmap[col_pos++] = fli_read_char (f) << 2;
+ }
+ }
+}
+
+int
+fli_write_color (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap)
+{
+ unsigned long chunkpos;
+ unsigned short num_packets;
+ s_fli_chunk chunk;
+
+ chunkpos = ftell (f);
+ fseek (f, chunkpos + 8, SEEK_SET);
+ num_packets = 0;
+ if (old_cmap == NULL)
+ {
+ unsigned short col_pos;
+
+ num_packets = 1;
+ fli_write_char (f, 0); /* skip no color */
+ fli_write_char (f, 0); /* 256 color */
+ for (col_pos = 0; col_pos < 768; col_pos++)
+ {
+ fli_write_char (f, cmap[col_pos] >> 2);
+ }
+ }
+ else
+ {
+ unsigned short cnt_skip, cnt_col, col_pos, col_start;
+
+ col_pos = 0;
+ do
+ {
+ cnt_skip = 0;
+ while ((col_pos < 256) &&
+ (old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
+ (old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
+ (old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2]))
+ {
+ cnt_skip++;
+ col_pos++;
+ }
+ col_start = col_pos * 3;
+ cnt_col = 0;
+ while ((col_pos < 256) &&
+ !((old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
+ (old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
+ (old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2])))
+ {
+ cnt_col++;
+ col_pos++;
+ }
+ if (cnt_col > 0)
+ {
+ num_packets++;
+
+ fli_write_char (f, cnt_skip & 255);
+ fli_write_char (f, cnt_col & 255);
+ while (cnt_col > 0)
+ {
+ fli_write_char (f, cmap[col_start++] >> 2);
+ fli_write_char (f, cmap[col_start++] >> 2);
+ fli_write_char (f, cmap[col_start++] >> 2);
+ cnt_col--;
+ }
+ }
+ } while (col_pos < 256);
+ }
+
+ if (num_packets > 0)
+ {
+ chunk.size = ftell (f) - chunkpos;
+ chunk.magic = FLI_COLOR;
+
+ fseek (f, chunkpos, SEEK_SET);
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+ fli_write_short (f, num_packets);
+
+ if (chunk.size & 1)
+ chunk.size++;
+
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+ return 1;
+ }
+
+ fseek (f, chunkpos, SEEK_SET);
+ return 0;
+}
+
+/*
+ * palette chunks from Autodesk Animator pro
+ */
+void
+fli_read_color_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap)
+{
+ unsigned short num_packets, cnt_packets, col_pos;
+
+ num_packets = fli_read_short (f);
+ col_pos = 0;
+ for (cnt_packets = num_packets; cnt_packets > 0; cnt_packets--)
+ {
+ unsigned short skip_col, num_col, col_cnt;
+
+ skip_col = fli_read_char (f);
+ num_col = fli_read_char (f);
+ if (num_col == 0)
+ {
+ for (col_pos = 0; col_pos < 768; col_pos++)
+ {
+ cmap[col_pos] = fli_read_char (f);
+ }
+ return;
+ }
+ for (col_cnt = skip_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
+ {
+ cmap[col_pos] = old_cmap[col_pos];
+ col_pos++;
+ cmap[col_pos] = old_cmap[col_pos];
+ col_pos++;
+ cmap[col_pos] = old_cmap[col_pos];
+ col_pos++;
+ }
+ for (col_cnt = num_col; (col_cnt > 0) && (col_pos < 768); col_cnt--)
+ {
+ cmap[col_pos++] = fli_read_char (f);
+ cmap[col_pos++] = fli_read_char (f);
+ cmap[col_pos++] = fli_read_char (f);
+ }
+ }
+}
+
+int
+fli_write_color_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap)
+{
+ unsigned long chunkpos;
+ unsigned short num_packets;
+ s_fli_chunk chunk;
+
+ chunkpos = ftell (f);
+ fseek (f, chunkpos + 8, SEEK_SET);
+ num_packets = 0;
+ if (old_cmap == NULL)
+ {
+ unsigned short col_pos;
+ num_packets = 1;
+ fli_write_char (f, 0); /* skip no color */
+ fli_write_char (f, 0); /* 256 color */
+ for (col_pos = 0; col_pos < 768; col_pos++)
+ {
+ fli_write_char (f, cmap[col_pos]);
+ }
+ }
+ else
+ {
+ unsigned short cnt_skip, cnt_col, col_pos, col_start;
+ col_pos = 0;
+ do {
+ cnt_skip = 0;
+ while ((col_pos < 256) &&
+ (old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
+ (old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
+ (old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2]))
+ {
+ cnt_skip++; col_pos++;
+ }
+ col_start = col_pos * 3;
+ cnt_col = 0;
+ while ((col_pos < 256) &&
+ !((old_cmap[col_pos * 3 + 0] == cmap[col_pos * 3 + 0]) &&
+ (old_cmap[col_pos * 3 + 1] == cmap[col_pos * 3 + 1]) &&
+ (old_cmap[col_pos * 3 + 2] == cmap[col_pos * 3 + 2])))
+ {
+ cnt_col++;
+ col_pos++;
+ }
+ if (cnt_col > 0)
+ {
+ num_packets++;
+ fli_write_char (f, cnt_skip);
+ fli_write_char (f, cnt_col);
+
+ while (cnt_col > 0)
+ {
+ fli_write_char (f, cmap[col_start++]);
+ fli_write_char (f, cmap[col_start++]);
+ fli_write_char (f, cmap[col_start++]);
+ cnt_col--;
+ }
+ }
+ } while (col_pos < 256);
+ }
+
+ if (num_packets > 0)
+ {
+ chunk.size = ftell (f) - chunkpos;
+ chunk.magic = FLI_COLOR_2;
+
+ fseek (f, chunkpos, SEEK_SET);
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+ fli_write_short (f, num_packets);
+
+ if (chunk.size & 1)
+ chunk.size++;
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+ return 1;
+ }
+ fseek (f, chunkpos, SEEK_SET);
+ return 0;
+}
+
+/*
+ * completely black frame
+ */
+void
+fli_read_black (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ memset (framebuf, 0, fli_header->width * fli_header->height);
+}
+
+void
+fli_write_black (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ s_fli_chunk chunk;
+
+ chunk.size = 6;
+ chunk.magic = FLI_BLACK;
+
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+}
+
+/*
+ * Uncompressed frame
+ */
+void
+fli_read_copy (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ fread (framebuf, fli_header->width, fli_header->height, f);
+}
+
+void
+fli_write_copy (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ unsigned long chunkpos;
+ s_fli_chunk chunk;
+
+ chunkpos = ftell (f);
+ fseek (f, chunkpos + 6, SEEK_SET);
+ fwrite (framebuf, fli_header->width, fli_header->height, f);
+ chunk.size = ftell (f) - chunkpos;
+ chunk.magic = FLI_COPY;
+
+ fseek (f, chunkpos, SEEK_SET);
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+
+ if (chunk.size & 1)
+ chunk.size++;
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+}
+
+/*
+ * This is a RLE algorithm, used for the first image of an animation
+ */
+void
+fli_read_brun (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ unsigned short yc;
+ unsigned char *pos;
+
+ for (yc = 0; yc < fli_header->height; yc++)
+ {
+ unsigned short pc, pcnt;
+ size_t n, xc;
+
+ pc = fli_read_char (f);
+ xc = 0;
+ pos = framebuf + (fli_header->width * yc);
+ n = (size_t) fli_header->width * (fli_header->height - yc);
+ for (pcnt = pc; pcnt > 0; pcnt--)
+ {
+ unsigned short ps;
+
+ ps = fli_read_char (f);
+ if (ps & 0x80)
+ {
+ unsigned short len;
+
+ for (len = -(signed char) ps; len > 0 && xc < n; len--)
+ {
+ pos[xc++] = fli_read_char (f);
+ }
+ }
+ else
+ {
+ unsigned char val;
+ size_t len;
+
+ len = MIN (n - xc, ps);
+ val = fli_read_char (f);
+ memset (&(pos[xc]), val, len);
+ xc+=len;
+ }
+ }
+ }
+}
+
+void
+fli_write_brun (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf)
+{
+ unsigned long chunkpos;
+ s_fli_chunk chunk;
+ unsigned short yc;
+ unsigned char *linebuf;
+
+ chunkpos = ftell (f);
+ fseek (f, chunkpos + 6, SEEK_SET);
+
+ for (yc = 0; yc < fli_header->height; yc++)
+ {
+ unsigned short xc, t1, pc, tc;
+ unsigned long linepos, lineend, bc;
+
+ linepos = ftell (f); bc = 0;
+ fseek (f, 1, SEEK_CUR);
+ linebuf = framebuf + (yc * fli_header->width);
+ xc = 0; tc = 0; t1 = 0;
+ while (xc < fli_header->width)
+ {
+ pc = 1;
+ while ((pc < 120) &&
+ ((xc + pc) < fli_header->width) &&
+ (linebuf[xc + pc] == linebuf[xc]))
+ {
+ pc++;
+ }
+ if (pc > 2)
+ {
+ if (tc > 0)
+ {
+ bc++;
+ fli_write_char (f, (tc - 1)^0xFF);
+ fwrite (linebuf + t1, 1, tc, f);
+ tc = 0;
+ }
+ bc++;
+ fli_write_char (f, pc);
+ fli_write_char (f, linebuf[xc]);
+ t1 = xc + pc;
+ }
+ else
+ {
+ tc+=pc;
+ if (tc > 120)
+ {
+ bc++;
+ fli_write_char (f, (tc - 1)^0xFF);
+ fwrite (linebuf + t1, 1, tc, f);
+ tc = 0;
+ t1 = xc + pc;
+ }
+ }
+ xc+=pc;
+ }
+ if (tc > 0)
+ {
+ bc++;
+ fli_write_char (f, (tc - 1)^0xFF);
+ fwrite (linebuf + t1, 1, tc, f);
+ tc = 0;
+ }
+ lineend = ftell (f);
+ fseek (f, linepos, SEEK_SET);
+ fli_write_char (f, bc);
+ fseek (f, lineend, SEEK_SET);
+ }
+
+ chunk.size = ftell (f) - chunkpos;
+ chunk.magic = FLI_BRUN;
+
+ fseek (f, chunkpos, SEEK_SET);
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+
+ if (chunk.size & 1)
+ chunk.size++;
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+}
+
+/*
+ * This is the delta-compression method from the classic Autodesk
+ * Animator. It's basically the RLE method from above, but it
+ * supports skipping unchanged lines at the beginning and end of an
+ * image, and unchanged pixels in a line. This chunk is used in FLI
+ * files.
+ */
+void
+fli_read_lc (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf)
+{
+ unsigned short yc, firstline, numline;
+ unsigned char *pos;
+
+ memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
+ firstline = fli_read_short (f);
+ numline = fli_read_short (f);
+ if (numline > fli_header->height || fli_header->height - numline < firstline)
+ return;
+
+ for (yc = 0; yc < numline; yc++)
+ {
+ unsigned short pc, pcnt;
+ size_t n, xc;
+
+ pc = fli_read_char (f);
+ xc = 0;
+ pos = framebuf + (fli_header->width * (firstline + yc));
+ n = (size_t) fli_header->width * (fli_header->height - firstline - yc);
+ for (pcnt = pc; pcnt > 0; pcnt--)
+ {
+ unsigned short ps, skip;
+
+ skip = fli_read_char (f);
+ ps = fli_read_char (f);
+ xc+=MIN (n - xc, skip);
+ if (ps & 0x80)
+ {
+ unsigned char val;
+ size_t len;
+ ps = -(signed char) ps;
+ val = fli_read_char (f);
+ len = MIN (n - xc, ps);
+ memset (&(pos[xc]), val, len);
+ xc+=len;
+ }
+ else
+ {
+ size_t len;
+ len = MIN (n - xc, ps);
+ fread (&(pos[xc]), len, 1, f);
+ xc+=len;
+ }
+ }
+ }
+}
+
+void
+fli_write_lc (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf)
+{
+ unsigned long chunkpos;
+ s_fli_chunk chunk;
+ unsigned short yc, firstline, numline, lastline;
+ unsigned char *linebuf, *old_linebuf;
+
+ chunkpos = ftell (f);
+ fseek (f, chunkpos + 6, SEEK_SET);
+
+ /* first check, how many lines are unchanged at the beginning */
+ firstline = 0;
+ while ((memcmp (old_framebuf + (firstline * fli_header->width),
+ framebuf + (firstline * fli_header->width),
+ fli_header->width) == 0) &&
+ (firstline < fli_header->height))
+ firstline++;
+
+ /* then check from the end, how many lines are unchanged */
+ if (firstline < fli_header->height)
+ {
+ lastline = fli_header->height - 1;
+ while ((memcmp (old_framebuf + (lastline * fli_header->width),
+ framebuf + (lastline * fli_header->width),
+ fli_header->width) == 0) &&
+ (lastline > firstline))
+ lastline--;
+ numline = (lastline - firstline) + 1;
+ }
+ else
+ {
+ numline = 0;
+ }
+ if (numline == 0)
+ firstline = 0;
+
+ fli_write_short (f, firstline);
+ fli_write_short (f, numline);
+
+ for (yc = 0; yc < numline; yc++)
+ {
+ unsigned short xc, sc, cc, tc;
+ unsigned long linepos, lineend, bc;
+
+ linepos = ftell (f); bc = 0;
+ fseek (f, 1, SEEK_CUR);
+
+ linebuf = framebuf + ((firstline + yc)*fli_header->width);
+ old_linebuf = old_framebuf + ((firstline + yc)*fli_header->width);
+ xc = 0;
+ while (xc < fli_header->width)
+ {
+ sc = 0;
+ while ((xc < fli_header->width) &&
+ (linebuf[xc] == old_linebuf[xc]) &&
+ (sc < 255))
+ {
+ xc++;
+ sc++;
+ }
+ fli_write_char (f, sc);
+ cc = 1;
+ while ((linebuf[xc] == linebuf[xc + cc]) &&
+ ((xc + cc)<fli_header->width) &&
+ (cc < 120))
+ {
+ cc++;
+ }
+ if (cc > 2)
+ {
+ bc++;
+ fli_write_char (f, (cc - 1)^0xFF);
+ fli_write_char (f, linebuf[xc]);
+ xc+=cc;
+ }
+ else
+ {
+ tc = 0;
+ do {
+ sc = 0;
+ while ((linebuf[tc + xc + sc] == old_linebuf[tc + xc + sc]) &&
+ ((tc + xc + sc)<fli_header->width) &&
+ (sc < 5))
+ {
+ sc++;
+ }
+ cc = 1;
+ while ((linebuf[tc + xc] == linebuf[tc + xc + cc]) &&
+ ((tc + xc + cc)<fli_header->width) &&
+ (cc < 10))
+ {
+ cc++;
+ }
+ tc++;
+ } while ((tc < 120) &&
+ (cc < 9) &&
+ (sc < 4) &&
+ ((xc + tc) < fli_header->width));
+ bc++;
+ fli_write_char (f, tc);
+ fwrite (linebuf + xc, tc, 1, f);
+ xc+=tc;
+ }
+ }
+ lineend = ftell (f);
+ fseek (f, linepos, SEEK_SET);
+ fli_write_char (f, bc);
+ fseek (f, lineend, SEEK_SET);
+ }
+
+ chunk.size = ftell (f) - chunkpos;
+ chunk.magic = FLI_LC;
+
+ fseek (f, chunkpos, SEEK_SET);
+ fli_write_long (f, chunk.size);
+ fli_write_short (f, chunk.magic);
+
+ if (chunk.size & 1)
+ chunk.size++;
+ fseek (f, chunkpos + chunk.size, SEEK_SET);
+}
+
+
+/*
+ * This is an enhanced version of the old delta-compression used by
+ * the autodesk animator pro. It's word-oriented, and supports
+ * skipping larger parts of the image. This chunk is used in FLC
+ * files.
+ */
+void
+fli_read_lc_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf)
+{
+ unsigned short yc, lc, numline;
+ unsigned char *pos;
+
+ memcpy (framebuf, old_framebuf, fli_header->width * fli_header->height);
+ yc = 0;
+ numline = fli_read_short (f);
+ for (lc = 0; lc < numline; lc++)
+ {
+ unsigned short pc, pcnt, lpf, lpn;
+ size_t n, xc;
+
+ pc = fli_read_short (f);
+ lpf = 0; lpn = 0;
+ while (pc & 0x8000)
+ {
+ if (pc & 0x4000)
+ {
+ yc += -(signed short) pc;
+ }
+ else
+ {
+ lpf = 1;
+ lpn = pc & 0xFF;
+ }
+ pc = fli_read_short (f);
+ }
+ yc = MIN (yc, fli_header->height);
+ xc = 0;
+ pos = framebuf + (fli_header->width * yc);
+ n = (size_t) fli_header->width * (fli_header->height - yc);
+ for (pcnt = pc; pcnt > 0; pcnt--)
+ {
+ unsigned short ps, skip;
+
+ skip = fli_read_char (f);
+ ps = fli_read_char (f);
+ xc += MIN (n - xc, skip);
+ if (ps & 0x80)
+ {
+ unsigned char v1, v2;
+
+ ps = -(signed char) ps;
+ v1 = fli_read_char (f);
+ v2 = fli_read_char (f);
+ while (ps > 0 && xc + 1 < n)
+ {
+ pos[xc++] = v1;
+ pos[xc++] = v2;
+ ps--;
+ }
+ }
+ else
+ {
+ size_t len;
+
+ len = MIN ((n - xc)/2, ps);
+ fread (&(pos[xc]), len, 2, f);
+ xc += len << 1;
+ }
+ }
+ if (lpf)
+ pos[xc] = lpn;
+ yc++;
+ }
+}
diff --git a/plug-ins/file-fli/fli.h b/plug-ins/file-fli/fli.h
new file mode 100644
index 0000000..40f0f76
--- /dev/null
+++ b/plug-ins/file-fli/fli.h
@@ -0,0 +1,153 @@
+/*
+ * Written 1998 Jens Ch. Restemeier <jchrr@hrz.uni-bielefeld.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _FLI_H
+#define _FLI_H
+
+/** structures */
+
+typedef struct _fli_header
+{
+ unsigned long filesize;
+ unsigned short magic;
+ unsigned short frames;
+ unsigned short width;
+ unsigned short height;
+ unsigned short depth;
+ unsigned short flags;
+ unsigned long speed;
+ unsigned long created;
+ unsigned long creator;
+ unsigned long updated;
+ unsigned short aspect_x, aspect_y;
+ unsigned long oframe1, oframe2;
+} s_fli_header;
+
+typedef struct _fli_frame
+{
+ unsigned long size;
+ unsigned short magic;
+ unsigned short chunks;
+} s_fli_frame;
+
+typedef struct _fli_chunk
+{
+ unsigned long size;
+ unsigned short magic;
+ unsigned char *data;
+} s_fli_chunk;
+
+/** chunk magics */
+#define NO_HEADER 0
+#define HEADER_FLI 0xAF11
+#define HEADER_FLC 0xAF12
+#define FRAME 0xF1FA
+
+/** codec magics */
+#define FLI_COLOR 11
+#define FLI_BLACK 13
+#define FLI_BRUN 15
+#define FLI_COPY 16
+#define FLI_LC 12
+#define FLI_LC_2 7
+#define FLI_COLOR_2 4
+#define FLI_MINI 18
+
+/** codec masks */
+#define W_COLOR 0x0001
+#define W_BLACK 0x0002
+#define W_BRUN 0x0004
+#define W_COPY 0x0008
+#define W_LC 0x0010
+#define W_LC_2 0x0020
+#define W_COLOR_2 0x0040
+#define W_MINI 0x0080
+#define W_ALL 0xFFFF
+
+/** functions */
+void fli_read_header (FILE *f,
+ s_fli_header *fli_header);
+void fli_read_frame (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *old_cmap,
+ unsigned char *framebuf,
+ unsigned char *cmap);
+
+void fli_read_color (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap);
+void fli_read_color_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap);
+void fli_read_black (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_read_brun (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_read_copy (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_read_lc (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf);
+void fli_read_lc_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf);
+
+void fli_write_header (FILE *f,
+ s_fli_header *fli_header);
+void fli_write_frame (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *old_cmap,
+ unsigned char *framebuf,
+ unsigned char *cmap,
+ unsigned short codec_mask);
+
+int fli_write_color (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap);
+int fli_write_color_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_cmap,
+ unsigned char *cmap);
+void fli_write_black (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_write_brun (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_write_copy (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *framebuf);
+void fli_write_lc (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf);
+void fli_write_lc_2 (FILE *f,
+ s_fli_header *fli_header,
+ unsigned char *old_framebuf,
+ unsigned char *framebuf);
+
+#endif
diff --git a/plug-ins/file-ico/Makefile.am b/plug-ins/file-ico/Makefile.am
new file mode 100644
index 0000000..0bc5bbf
--- /dev/null
+++ b/plug-ins/file-ico/Makefile.am
@@ -0,0 +1,57 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_ico_RC = file-ico.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-ico
+
+libexec_PROGRAMS = file-ico
+
+file_ico_CFLAGS = $(PNG_CFLAGS)
+
+file_ico_SOURCES = \
+ ico.c \
+ ico.h \
+ ico-dialog.c \
+ ico-dialog.h \
+ ico-load.c \
+ ico-load.h \
+ ico-save.c \
+ ico-save.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(PNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_ico_RC)
diff --git a/plug-ins/file-ico/Makefile.in b/plug-ins/file-ico/Makefile.in
new file mode 100644
index 0000000..093fb19
--- /dev/null
+++ b/plug-ins/file-ico/Makefile.in
@@ -0,0 +1,1081 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-ico$(EXEEXT)
+subdir = plug-ins/file-ico
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_ico_OBJECTS = file_ico-ico.$(OBJEXT) \
+ file_ico-ico-dialog.$(OBJEXT) file_ico-ico-load.$(OBJEXT) \
+ file_ico-ico-save.$(OBJEXT)
+file_ico_OBJECTS = $(am_file_ico_OBJECTS)
+file_ico_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_ico_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_ico_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+file_ico_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(file_ico_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file_ico-ico-dialog.Po \
+ ./$(DEPDIR)/file_ico-ico-load.Po \
+ ./$(DEPDIR)/file_ico-ico-save.Po ./$(DEPDIR)/file_ico-ico.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_ico_SOURCES)
+DIST_SOURCES = $(file_ico_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-ico
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_ico_RC = file-ico.rc.o
+AM_LDFLAGS = $(mwindows)
+file_ico_CFLAGS = $(PNG_CFLAGS)
+file_ico_SOURCES = \
+ ico.c \
+ ico.h \
+ ico-dialog.c \
+ ico-dialog.h \
+ ico-load.c \
+ ico-load.h \
+ ico-save.c \
+ ico-save.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(PNG_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_ico_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-ico/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-ico/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-ico$(EXEEXT): $(file_ico_OBJECTS) $(file_ico_DEPENDENCIES) $(EXTRA_file_ico_DEPENDENCIES)
+ @rm -f file-ico$(EXEEXT)
+ $(AM_V_CCLD)$(file_ico_LINK) $(file_ico_OBJECTS) $(file_ico_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_ico-ico-dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_ico-ico-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_ico-ico-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_ico-ico.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+file_ico-ico.o: ico.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico.o -MD -MP -MF $(DEPDIR)/file_ico-ico.Tpo -c -o file_ico-ico.o `test -f 'ico.c' || echo '$(srcdir)/'`ico.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico.Tpo $(DEPDIR)/file_ico-ico.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico.c' object='file_ico-ico.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico.o `test -f 'ico.c' || echo '$(srcdir)/'`ico.c
+
+file_ico-ico.obj: ico.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico.obj -MD -MP -MF $(DEPDIR)/file_ico-ico.Tpo -c -o file_ico-ico.obj `if test -f 'ico.c'; then $(CYGPATH_W) 'ico.c'; else $(CYGPATH_W) '$(srcdir)/ico.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico.Tpo $(DEPDIR)/file_ico-ico.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico.c' object='file_ico-ico.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico.obj `if test -f 'ico.c'; then $(CYGPATH_W) 'ico.c'; else $(CYGPATH_W) '$(srcdir)/ico.c'; fi`
+
+file_ico-ico-dialog.o: ico-dialog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-dialog.o -MD -MP -MF $(DEPDIR)/file_ico-ico-dialog.Tpo -c -o file_ico-ico-dialog.o `test -f 'ico-dialog.c' || echo '$(srcdir)/'`ico-dialog.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-dialog.Tpo $(DEPDIR)/file_ico-ico-dialog.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-dialog.c' object='file_ico-ico-dialog.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-dialog.o `test -f 'ico-dialog.c' || echo '$(srcdir)/'`ico-dialog.c
+
+file_ico-ico-dialog.obj: ico-dialog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-dialog.obj -MD -MP -MF $(DEPDIR)/file_ico-ico-dialog.Tpo -c -o file_ico-ico-dialog.obj `if test -f 'ico-dialog.c'; then $(CYGPATH_W) 'ico-dialog.c'; else $(CYGPATH_W) '$(srcdir)/ico-dialog.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-dialog.Tpo $(DEPDIR)/file_ico-ico-dialog.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-dialog.c' object='file_ico-ico-dialog.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-dialog.obj `if test -f 'ico-dialog.c'; then $(CYGPATH_W) 'ico-dialog.c'; else $(CYGPATH_W) '$(srcdir)/ico-dialog.c'; fi`
+
+file_ico-ico-load.o: ico-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-load.o -MD -MP -MF $(DEPDIR)/file_ico-ico-load.Tpo -c -o file_ico-ico-load.o `test -f 'ico-load.c' || echo '$(srcdir)/'`ico-load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-load.Tpo $(DEPDIR)/file_ico-ico-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-load.c' object='file_ico-ico-load.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-load.o `test -f 'ico-load.c' || echo '$(srcdir)/'`ico-load.c
+
+file_ico-ico-load.obj: ico-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-load.obj -MD -MP -MF $(DEPDIR)/file_ico-ico-load.Tpo -c -o file_ico-ico-load.obj `if test -f 'ico-load.c'; then $(CYGPATH_W) 'ico-load.c'; else $(CYGPATH_W) '$(srcdir)/ico-load.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-load.Tpo $(DEPDIR)/file_ico-ico-load.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-load.c' object='file_ico-ico-load.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-load.obj `if test -f 'ico-load.c'; then $(CYGPATH_W) 'ico-load.c'; else $(CYGPATH_W) '$(srcdir)/ico-load.c'; fi`
+
+file_ico-ico-save.o: ico-save.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-save.o -MD -MP -MF $(DEPDIR)/file_ico-ico-save.Tpo -c -o file_ico-ico-save.o `test -f 'ico-save.c' || echo '$(srcdir)/'`ico-save.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-save.Tpo $(DEPDIR)/file_ico-ico-save.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-save.c' object='file_ico-ico-save.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-save.o `test -f 'ico-save.c' || echo '$(srcdir)/'`ico-save.c
+
+file_ico-ico-save.obj: ico-save.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -MT file_ico-ico-save.obj -MD -MP -MF $(DEPDIR)/file_ico-ico-save.Tpo -c -o file_ico-ico-save.obj `if test -f 'ico-save.c'; then $(CYGPATH_W) 'ico-save.c'; else $(CYGPATH_W) '$(srcdir)/ico-save.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/file_ico-ico-save.Tpo $(DEPDIR)/file_ico-ico-save.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ico-save.c' object='file_ico-ico-save.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(file_ico_CFLAGS) $(CFLAGS) -c -o file_ico-ico-save.obj `if test -f 'ico-save.c'; then $(CYGPATH_W) 'ico-save.c'; else $(CYGPATH_W) '$(srcdir)/ico-save.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file_ico-ico-dialog.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico-load.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico-save.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file_ico-ico-dialog.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico-load.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico-save.Po
+ -rm -f ./$(DEPDIR)/file_ico-ico.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-ico/ico-dialog.c b/plug-ins/file-ico/ico-dialog.c
new file mode 100644
index 0000000..84e9909
--- /dev/null
+++ b/plug-ins/file-ico/ico-dialog.c
@@ -0,0 +1,531 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#include <config.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-dialog.h"
+#include "ico-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void ico_dialog_bpp_changed (GtkWidget *combo,
+ GObject *hbox);
+static void ico_dialog_toggle_compress (GtkWidget *checkbox,
+ GObject *hbox);
+static void ico_dialog_check_compat (GtkWidget *dialog,
+ IcoSaveInfo *info);
+
+
+GtkWidget *
+ico_dialog_new (IcoSaveInfo *info)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *scrolled_window;
+ GtkWidget *viewport;
+ GtkWidget *warning;
+
+ dialog = gimp_export_dialog_new (_("Windows Icon"),
+ PLUG_IN_BINARY,
+ "plug-in-winicon");
+
+ /* We store an array that holds each icon's requested bit depth
+ with the dialog. It's queried when the dialog is closed so the
+ save routine knows what colormaps etc to generate in the saved
+ file. We store twice the number necessary because in the second
+ set, the color depths that are automatically suggested are stored
+ for later comparison.
+ */
+
+ g_object_set_data (G_OBJECT (dialog), "save_info", info);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ frame = gimp_frame_new (_("Icon Details"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
+ gtk_widget_show (scrolled_window);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
+ gtk_widget_show (viewport);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ g_object_set_data (G_OBJECT (dialog), "icons_vbox", vbox);
+ gtk_container_add (GTK_CONTAINER (viewport), vbox);
+ gtk_widget_show (vbox);
+
+ warning = g_object_new (GIMP_TYPE_HINT_BOX,
+ "icon-name", GIMP_ICON_DIALOG_WARNING,
+ "hint",
+ _("Large icons and compression are not supported "
+ "by all programs. Older applications may not "
+ "open this file correctly."),
+ NULL);
+ gtk_box_pack_end (GTK_BOX (main_vbox), warning, FALSE, FALSE, 0);
+ /* don't show the warning here */
+
+ g_object_set_data (G_OBJECT (dialog), "warning", warning);
+
+ return dialog;
+}
+
+static GtkWidget *
+ico_preview_new (gint32 layer)
+{
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+ gint width = gimp_drawable_width (layer);
+ gint height = gimp_drawable_height (layer);
+
+ pixbuf = gimp_drawable_get_thumbnail (layer,
+ MIN (width, 128), MIN (height, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+
+ return image;
+}
+
+/* This function creates and returns an hbox for an icon,
+ which then gets added to the dialog's main vbox. */
+static GtkWidget *
+ico_create_icon_hbox (GtkWidget *icon_preview,
+ gint32 layer,
+ gint layer_num,
+ IcoSaveInfo *info)
+{
+ static GtkSizeGroup *size = NULL;
+
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *alignment;
+ GtkWidget *combo;
+ GtkWidget *checkbox;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ alignment = gtk_alignment_new (1.0, 0.5, 0, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), alignment, FALSE, FALSE, 0);
+ gtk_widget_show (alignment);
+
+ /* To make life easier for the callbacks, we store the
+ layer's ID and stacking number with the hbox. */
+
+ g_object_set_data (G_OBJECT (hbox),
+ "icon_layer", GINT_TO_POINTER (layer));
+ g_object_set_data (G_OBJECT (hbox),
+ "icon_layer_num", GINT_TO_POINTER (layer_num));
+
+ g_object_set_data (G_OBJECT (hbox), "icon_preview", icon_preview);
+ gtk_container_add (GTK_CONTAINER (alignment), icon_preview);
+ gtk_widget_show (icon_preview);
+
+ if (! size)
+ size = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ gtk_size_group_add_widget (size, alignment);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ combo = gimp_int_combo_box_new (_("1 bpp, 1-bit alpha, 2-slot palette"), 1,
+ _("4 bpp, 1-bit alpha, 16-slot palette"), 4,
+ _("8 bpp, 1-bit alpha, 256-slot palette"), 8,
+ _("24 bpp, 1-bit alpha, no palette"), 24,
+ _("32 bpp, 8-bit alpha, no palette"), 32,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ info->depths[layer_num]);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (ico_dialog_bpp_changed),
+ hbox);
+
+ g_object_set_data (G_OBJECT (hbox), "icon_menu", combo);
+
+ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ checkbox = gtk_check_button_new_with_label (_("Compressed (PNG)"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
+ info->compress[layer_num]);
+ g_signal_connect (checkbox, "toggled",
+ G_CALLBACK (ico_dialog_toggle_compress), hbox);
+ gtk_box_pack_start (GTK_BOX (vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show (checkbox);
+
+ return hbox;
+}
+
+static GtkWidget *
+ico_dialog_get_layer_preview (GtkWidget *dialog,
+ gint32 layer)
+{
+ GtkWidget *preview;
+ GtkWidget *icon_hbox;
+ gchar key[ICO_MAXBUF];
+
+ g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
+ icon_hbox = g_object_get_data (G_OBJECT (dialog), key);
+
+ if (!icon_hbox)
+ {
+ D(("Something's wrong -- couldn't look up hbox by layer ID\n"));
+ return NULL;
+ }
+
+ preview = g_object_get_data (G_OBJECT (icon_hbox), "icon_preview");
+
+ if (!icon_hbox)
+ {
+ D(("Something's wrong -- couldn't look up preview from hbox\n"));
+ return NULL;
+ }
+
+ return preview;
+}
+
+static void
+ico_dialog_update_icon_preview (GtkWidget *dialog,
+ gint32 layer,
+ gint bpp)
+{
+ GtkWidget *preview = ico_dialog_get_layer_preview (dialog, layer);
+ GdkPixbuf *pixbuf;
+ const Babl *format;
+ gint w = gimp_drawable_width (layer);
+ gint h = gimp_drawable_height (layer);
+
+ if (! preview)
+ return;
+
+ switch (gimp_drawable_type (layer))
+ {
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = gimp_drawable_get_format (layer);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ if (bpp <= 8)
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *tmp;
+ gint32 image;
+ gint32 tmp_image;
+ gint32 tmp_layer;
+ guchar *buf;
+ guchar *cmap;
+ gint num_colors;
+
+ image = gimp_item_get_image (layer);
+
+ tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+ gimp_image_undo_disable (tmp_image);
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ g_free (cmap);
+ }
+
+ tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h,
+ gimp_drawable_type (layer),
+ 100,
+ gimp_image_get_default_new_layer_mode (tmp_image));
+ gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ buf = g_malloc (w * h * 4);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
+
+ g_object_unref (tmp);
+ g_object_unref (buffer);
+
+ if (gimp_drawable_is_indexed (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 1 << bpp, TRUE, FALSE, "dummy");
+
+ cmap = gimp_image_get_colormap (tmp_image, &num_colors);
+
+ if (num_colors == (1 << bpp) &&
+ ! ico_cmap_contains_black (cmap, num_colors))
+ {
+ /* Windows icons with color maps need the color black.
+ * We need to eliminate one more color to make room for black.
+ */
+ if (gimp_drawable_is_indexed (layer))
+ {
+ g_free (cmap);
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ }
+ else if (gimp_drawable_is_gray (layer))
+ {
+ gimp_image_convert_grayscale (tmp_image);
+ }
+ else
+ {
+ gimp_image_convert_rgb (tmp_image);
+ }
+
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
+ format, buf, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (tmp);
+
+ if (!gimp_drawable_is_rgb (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ (1 << bpp) - 1, TRUE, FALSE, "dummy");
+ }
+
+ g_free (cmap);
+ g_free (buf);
+
+ pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gimp_image_delete (tmp_image);
+ }
+ else if (bpp == 24)
+ {
+ GeglBuffer *buffer;
+ GeglBuffer *tmp;
+ gint32 image;
+ gint32 tmp_image;
+ gint32 tmp_layer;
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+ image = gimp_item_get_image (layer);
+
+ tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+ gimp_image_undo_disable (tmp_image);
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ guchar *cmap;
+ gint num_colors;
+
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ g_free (cmap);
+ }
+
+ tmp_layer = gimp_layer_new (tmp_image, "temporary", w, h,
+ gimp_drawable_type (layer),
+ 100,
+ gimp_image_get_default_new_layer_mode (tmp_image));
+ gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer);
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
+
+ g_object_unref (tmp);
+ g_object_unref (buffer);
+
+ if (gimp_drawable_is_indexed (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ return_vals =
+ gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, tmp_image,
+ GIMP_PDB_DRAWABLE, tmp_layer,
+ GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
+ GIMP_PDB_END);
+ gimp_destroy_params (return_vals, n_return_vals);
+
+ pixbuf = gimp_drawable_get_thumbnail (tmp_layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+
+ gimp_image_delete (tmp_image);
+ }
+ else
+ {
+ pixbuf = gimp_drawable_get_thumbnail (layer,
+ MIN (w, 128), MIN (h, 128),
+ GIMP_PIXBUF_SMALL_CHECKS);
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf);
+ g_object_unref (pixbuf);
+}
+
+void
+ico_dialog_add_icon (GtkWidget *dialog,
+ gint32 layer,
+ gint layer_num)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *preview;
+ gchar key[ICO_MAXBUF];
+ IcoSaveInfo *info;
+
+ vbox = g_object_get_data (G_OBJECT (dialog), "icons_vbox");
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+
+ preview = ico_preview_new (layer);
+ hbox = ico_create_icon_hbox (preview, layer, layer_num, info);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* Let's make the hbox accessible through the layer ID */
+ g_snprintf (key, sizeof (key), "layer_%i_hbox", layer);
+ g_object_set_data (G_OBJECT (dialog), key, hbox);
+
+ ico_dialog_update_icon_preview (dialog, layer, info->depths[layer_num]);
+
+ ico_dialog_check_compat (dialog, info);
+}
+
+static void
+ico_dialog_bpp_changed (GtkWidget *combo,
+ GObject *hbox)
+{
+ GtkWidget *dialog;
+ gint32 layer;
+ gint layer_num;
+ gint bpp;
+ IcoSaveInfo *info;
+
+ dialog = gtk_widget_get_toplevel (combo);
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &bpp);
+
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+ g_assert (info);
+
+ layer = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer"));
+ layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
+
+ /* Update vector entry for later when we're actually saving,
+ and update the preview right away ... */
+ info->depths[layer_num] = bpp;
+ ico_dialog_update_icon_preview (dialog, layer, bpp);
+}
+
+static void
+ico_dialog_toggle_compress (GtkWidget *checkbox,
+ GObject *hbox)
+{
+ GtkWidget *dialog;
+ gint layer_num;
+ IcoSaveInfo *info;
+
+ dialog = gtk_widget_get_toplevel (checkbox);
+
+ info = g_object_get_data (G_OBJECT (dialog), "save_info");
+ g_assert (info);
+ layer_num = GPOINTER_TO_INT (g_object_get_data (hbox, "icon_layer_num"));
+
+ /* Update vector entry for later when we're actually saving */
+ info->compress[layer_num] =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbox));
+
+ ico_dialog_check_compat (dialog, info);
+}
+
+static void
+ico_dialog_check_compat (GtkWidget *dialog,
+ IcoSaveInfo *info)
+{
+ GtkWidget *warning;
+ gboolean warn = FALSE;
+ gint i;
+
+ for (i = 0; i < info->num_icons; i++)
+ {
+ if (gimp_drawable_width (info->layers[i]) > 255 ||
+ gimp_drawable_height (info->layers[i]) > 255 ||
+ info->compress[i])
+ {
+ warn = TRUE;
+ break;
+ }
+ }
+
+ warning = g_object_get_data (G_OBJECT (dialog), "warning");
+
+ gtk_widget_set_visible (warning, warn);
+}
diff --git a/plug-ins/file-ico/ico-dialog.h b/plug-ins/file-ico/ico-dialog.h
new file mode 100644
index 0000000..19ddee4
--- /dev/null
+++ b/plug-ins/file-ico/ico-dialog.h
@@ -0,0 +1,30 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ICO_DIALOG_H__
+#define __ICO_DIALOG_H__
+
+
+GtkWidget * ico_dialog_new (IcoSaveInfo *info);
+void ico_dialog_add_icon (GtkWidget *dialog,
+ gint32 layer,
+ gint layer_num);
+
+#endif /* __ICO_DIALOG_H__ */
diff --git a/plug-ins/file-ico/ico-load.c b/plug-ins/file-ico/ico-load.c
new file mode 100644
index 0000000..f44b805
--- /dev/null
+++ b/plug-ins/file-ico/ico-load.c
@@ -0,0 +1,813 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <png.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define A_VAL(p) ((guchar *)(p))[3]
+#define R_VAL(p) ((guchar *)(p))[2]
+#define G_VAL(p) ((guchar *)(p))[1]
+#define B_VAL(p) ((guchar *)(p))[0]
+
+#define A_VAL_GIMP(p) ((guchar *)(p))[3]
+#define R_VAL_GIMP(p) ((guchar *)(p))[0]
+#define G_VAL_GIMP(p) ((guchar *)(p))[1]
+#define B_VAL_GIMP(p) ((guchar *)(p))[2]
+
+
+static gint ico_read_int8 (FILE *fp,
+ guint8 *data,
+ gint count);
+static gint ico_read_int16 (FILE *fp,
+ guint16 *data,
+ gint count);
+static gint ico_read_int32 (FILE *fp,
+ guint32 *data,
+ gint count);
+
+static gint
+ico_read_int32 (FILE *fp,
+ guint32 *data,
+ gint count)
+{
+ gint i, total;
+
+ total = count;
+ if (count > 0)
+ {
+ ico_read_int8 (fp, (guint8 *) data, count * 4);
+ for (i = 0; i < count; i++)
+ data[i] = GUINT32_FROM_LE (data[i]);
+ }
+
+ return total * 4;
+}
+
+
+static gint
+ico_read_int16 (FILE *fp,
+ guint16 *data,
+ gint count)
+{
+ gint i, total;
+
+ total = count;
+ if (count > 0)
+ {
+ ico_read_int8 (fp, (guint8 *) data, count * 2);
+ for (i = 0; i < count; i++)
+ data[i] = GUINT16_FROM_LE (data[i]);
+ }
+
+ return total * 2;
+}
+
+
+static gint
+ico_read_int8 (FILE *fp,
+ guint8 *data,
+ gint count)
+{
+ gint total;
+ gint bytes;
+
+ total = count;
+ while (count > 0)
+ {
+ bytes = fread ((gchar *) data, sizeof (gchar), count, fp);
+ if (bytes <= 0) /* something bad happened */
+ break;
+
+ count -= bytes;
+ data += bytes;
+ }
+
+ return total;
+}
+
+
+static guint32
+ico_read_init (FILE *fp)
+{
+ IcoFileHeader header;
+
+ /* read and check file header */
+ if (! ico_read_int16 (fp, &header.reserved, 1) ||
+ ! ico_read_int16 (fp, &header.resource_type, 1) ||
+ ! ico_read_int16 (fp, &header.icon_count, 1) ||
+ header.reserved != 0 ||
+ header.resource_type != 1)
+ {
+ return 0;
+ }
+
+ return header.icon_count;
+}
+
+
+static gboolean
+ico_read_size (FILE *fp,
+ IcoLoadInfo *info)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 w, h;
+ gint32 bpp;
+ gint32 color_type;
+ guint32 magic;
+
+ if (fseek (fp, info->offset, SEEK_SET) < 0)
+ return FALSE;
+
+ ico_read_int32 (fp, &magic, 1);
+
+ if (magic == ICO_PNG_MAGIC)
+ {
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL,
+ NULL);
+ if (! png_ptr)
+ return FALSE;
+
+ info_ptr = png_create_info_struct (png_ptr);
+ if (! info_ptr)
+ {
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
+ return FALSE;
+ }
+
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
+ return FALSE;
+ }
+ png_init_io (png_ptr, fp);
+ png_set_sig_bytes (png_ptr, 4);
+ png_read_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &w, &h, &bpp, &color_type,
+ NULL, NULL, NULL);
+ png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
+ info->width = w;
+ info->height = h;
+ D(("ico_read_size: PNG: %ix%i\n", info->width, info->height));
+ return TRUE;
+ }
+ else if (magic == 40)
+ {
+ if (ico_read_int32 (fp, &info->width, 1) &&
+ ico_read_int32 (fp, &info->height, 1))
+ {
+ info->height /= 2;
+ D(("ico_read_size: ICO: %ix%i\n", info->width, info->height));
+ return TRUE;
+ }
+ else
+ {
+ info->width = 0;
+ info->height = 0;
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static IcoLoadInfo*
+ico_read_info (FILE *fp,
+ gint icon_count,
+ GError **error)
+{
+ gint i;
+ IcoFileEntry *entries;
+ IcoLoadInfo *info;
+
+ /* read icon entries */
+ entries = g_new (IcoFileEntry, icon_count);
+ if (fread (entries, sizeof (IcoFileEntry), icon_count, fp) <= 0)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Could not read '%lu' bytes"),
+ sizeof (IcoFileEntry));
+ g_free (entries);
+ return NULL;
+ }
+
+ info = g_new (IcoLoadInfo, icon_count);
+ for (i = 0; i < icon_count; i++)
+ {
+ info[i].width = entries[i].width;
+ info[i].height = entries[i].height;
+ info[i].bpp = GUINT16_FROM_LE (entries[i].bpp);
+ info[i].size = GUINT32_FROM_LE (entries[i].size);
+ info[i].offset = GUINT32_FROM_LE (entries[i].offset);
+
+ if (info[i].width == 0 || info[i].height == 0)
+ {
+ ico_read_size (fp, info + i);
+ }
+
+ D(("ico_read_info: %ix%i (%i bits, size: %i, offset: %i)\n",
+ info[i].width, info[i].height, info[i].bpp,
+ info[i].size, info[i].offset));
+
+ if (info[i].width == 0 || info[i].height == 0)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Icon #%d has zero width or height"), i);
+ g_free (info);
+ g_free (entries);
+ return NULL;
+ }
+ }
+
+ g_free (entries);
+
+ return info;
+}
+
+static gboolean
+ico_read_png (FILE *fp,
+ guint32 header,
+ guchar *buf,
+ gint maxsize,
+ gint *width,
+ gint *height)
+{
+ png_structp png_ptr;
+ png_infop info;
+ png_uint_32 w;
+ png_uint_32 h;
+ gint32 bit_depth;
+ gint32 color_type;
+ guint32 **rows;
+ gint i;
+
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (! png_ptr)
+ return FALSE;
+ info = png_create_info_struct (png_ptr);
+ if (! info)
+ {
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
+ return FALSE;
+ }
+
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ png_destroy_read_struct (&png_ptr, &info, NULL);
+ return FALSE;
+ }
+
+ png_init_io (png_ptr, fp);
+ png_set_sig_bytes (png_ptr, 4);
+ png_read_info (png_ptr, info);
+ png_get_IHDR (png_ptr, info, &w, &h, &bit_depth, &color_type,
+ NULL, NULL, NULL);
+ if (w*h*4 > maxsize)
+ {
+ png_destroy_read_struct (&png_ptr, &info, NULL);
+ return FALSE;
+ }
+ D(("ico_read_png: %ix%i, %i bits, %i type\n", (gint)w, (gint)h,
+ bit_depth, color_type));
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_GRAY:
+ png_set_expand_gray_1_2_4_to_8 (png_ptr);
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ png_set_gray_to_rgb (png_ptr);
+ png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+ break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ png_set_expand_gray_1_2_4_to_8 (png_ptr);
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ png_set_gray_to_rgb (png_ptr);
+ break;
+ case PNG_COLOR_TYPE_PALETTE:
+ png_set_palette_to_rgb (png_ptr);
+ if (png_get_valid (png_ptr, info, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha (png_ptr);
+ else
+ png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+ break;
+ case PNG_COLOR_TYPE_RGB:
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ png_set_add_alpha (png_ptr, 0xff, PNG_FILLER_AFTER);
+ break;
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ if (bit_depth == 16)
+ png_set_strip_16 (png_ptr);
+ break;
+ }
+
+ *width = w;
+ *height = h;
+ rows = g_new (guint32*, h);
+ rows[0] = (guint32*) buf;
+ for (i = 1; i < h; i++)
+ rows[i] = rows[i-1] + w;
+ png_read_image (png_ptr, (png_bytepp) rows);
+ png_destroy_read_struct (&png_ptr, &info, NULL);
+ g_free (rows);
+ return TRUE;
+}
+
+gint
+ico_get_bit_from_data (const guint8 *data,
+ gint line_width,
+ gint bit)
+{
+ gint line;
+ gint width32;
+ gint offset;
+ gint result;
+
+ /* width per line in multiples of 32 bits */
+ width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);
+ line = bit / line_width;
+ offset = bit % line_width;
+
+ result = (data[line * width32 * 4 + offset/8] & (1 << (7 - (offset % 8))));
+
+ return (result ? 1 : 0);
+}
+
+
+gint
+ico_get_nibble_from_data (const guint8 *data,
+ gint line_width,
+ gint nibble)
+{
+ gint line;
+ gint width32;
+ gint offset;
+ gint result;
+
+ /* width per line in multiples of 32 bits */
+ width32 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);
+ line = nibble / line_width;
+ offset = nibble % line_width;
+
+ result =
+ (data[line * width32 * 4 + offset/2] & (0x0F << (4 * (1 - offset % 2))));
+
+ if (offset % 2 == 0)
+ result = result >> 4;
+
+ return result;
+}
+
+gint
+ico_get_byte_from_data (const guint8 *data,
+ gint line_width,
+ gint byte)
+{
+ gint line;
+ gint width32;
+ gint offset;
+
+ /* width per line in multiples of 32 bits */
+ width32 = (line_width % 4 == 0 ? line_width / 4 : line_width / 4 + 1);
+ line = byte / line_width;
+ offset = byte % line_width;
+
+ return data[line * width32 * 4 + offset];
+}
+
+static gboolean
+ico_read_icon (FILE *fp,
+ guint32 header_size,
+ guchar *buf,
+ gint maxsize,
+ gint *width,
+ gint *height)
+{
+ IcoFileDataHeader data;
+ gint length;
+ gint x, y, w, h;
+ guchar *xor_map, *and_map;
+ guint32 *palette;
+ guint32 *dest_vec;
+ guchar *row;
+ gint rowstride;
+
+ palette = NULL;
+
+ data.header_size = header_size;
+ ico_read_int32 (fp, &data.width, 1);
+ ico_read_int32 (fp, &data.height, 1);
+ ico_read_int16 (fp, &data.planes, 1);
+ ico_read_int16 (fp, &data.bpp, 1);
+ ico_read_int32 (fp, &data.compression, 1);
+ ico_read_int32 (fp, &data.image_size, 1);
+ ico_read_int32 (fp, &data.x_res, 1);
+ ico_read_int32 (fp, &data.y_res, 1);
+ ico_read_int32 (fp, &data.used_clrs, 1);
+ ico_read_int32 (fp, &data.important_clrs, 1);
+
+ D((" header size %i, "
+ "w %i, h %i, planes %i, size %i, bpp %i, used %i, imp %i.\n",
+ data.header_size, data.width, data.height,
+ data.planes, data.image_size, data.bpp,
+ data.used_clrs, data.important_clrs));
+
+ if (data.planes != 1 ||
+ data.compression != 0)
+ {
+ D(("skipping image: invalid header\n"));
+ return FALSE;
+ }
+
+ if (data.bpp != 1 &&
+ data.bpp != 4 &&
+ data.bpp != 8 &&
+ data.bpp != 24 &&
+ data.bpp != 32)
+ {
+ D(("skipping image: invalid depth: %i\n", data.bpp));
+ return FALSE;
+ }
+
+ if (data.width * data.height * 2 > maxsize)
+ {
+ D(("skipping image: too large\n"));
+ return FALSE;
+ }
+
+ w = data.width;
+ h = data.height / 2;
+
+ if (data.bpp <= 8)
+ {
+ if (data.used_clrs == 0)
+ data.used_clrs = (1 << data.bpp);
+
+ D((" allocating a %i-slot palette for %i bpp.\n",
+ data.used_clrs, data.bpp));
+
+ palette = g_new0 (guint32, data.used_clrs);
+ ico_read_int8 (fp, (guint8 *) palette, data.used_clrs * 4);
+ }
+
+ xor_map = ico_alloc_map (w, h, data.bpp, &length);
+ ico_read_int8 (fp, xor_map, length);
+ D((" length of xor_map: %i\n", length));
+
+ /* Read in and_map. It's padded out to 32 bits per line: */
+ and_map = ico_alloc_map (w, h, 1, &length);
+ ico_read_int8 (fp, and_map, length);
+ D((" length of and_map: %i\n", length));
+
+ dest_vec = (guint32 *) buf;
+ switch (data.bpp)
+ {
+ case 1:
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ {
+ guint32 color = palette[ico_get_bit_from_data (xor_map,
+ w, y * w + x)];
+ guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+ R_VAL_GIMP (dest) = R_VAL (&color);
+ G_VAL_GIMP (dest) = G_VAL (&color);
+ B_VAL_GIMP (dest) = B_VAL (&color);
+
+ if (ico_get_bit_from_data (and_map, w, y * w + x))
+ A_VAL_GIMP (dest) = 0;
+ else
+ A_VAL_GIMP (dest) = 255;
+ }
+ break;
+
+ case 4:
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ {
+ guint32 color = palette[ico_get_nibble_from_data (xor_map,
+ w, y * w + x)];
+ guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+ R_VAL_GIMP (dest) = R_VAL (&color);
+ G_VAL_GIMP (dest) = G_VAL (&color);
+ B_VAL_GIMP (dest) = B_VAL (&color);
+
+ if (ico_get_bit_from_data (and_map, w, y * w + x))
+ A_VAL_GIMP (dest) = 0;
+ else
+ A_VAL_GIMP (dest) = 255;
+ }
+ break;
+
+ case 8:
+ for (y = 0; y < h; y++)
+ for (x = 0; x < w; x++)
+ {
+ guint32 color = palette[ico_get_byte_from_data (xor_map,
+ w, y * w + x)];
+ guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+ R_VAL_GIMP (dest) = R_VAL (&color);
+ G_VAL_GIMP (dest) = G_VAL (&color);
+ B_VAL_GIMP (dest) = B_VAL (&color);
+
+ if (ico_get_bit_from_data (and_map, w, y * w + x))
+ A_VAL_GIMP (dest) = 0;
+ else
+ A_VAL_GIMP (dest) = 255;
+ }
+ break;
+
+ default:
+ {
+ gint bytespp = data.bpp / 8;
+
+ rowstride = ico_rowstride (w, data.bpp);
+
+ for (y = 0; y < h; y++)
+ {
+ row = xor_map + rowstride * y;
+
+ for (x = 0; x < w; x++)
+ {
+ guint32 *dest = dest_vec + (h - 1 - y) * w + x;
+
+ B_VAL_GIMP (dest) = row[0];
+ G_VAL_GIMP (dest) = row[1];
+ R_VAL_GIMP (dest) = row[2];
+
+ if (data.bpp < 32)
+ {
+ if (ico_get_bit_from_data (and_map, w, y * w + x))
+ A_VAL_GIMP (dest) = 0;
+ else
+ A_VAL_GIMP (dest) = 255;
+ }
+ else
+ {
+ A_VAL_GIMP (dest) = row[3];
+ }
+
+ row += bytespp;
+ }
+ }
+ }
+ }
+ if (palette)
+ g_free (palette);
+ g_free (xor_map);
+ g_free (and_map);
+ *width = w;
+ *height = h;
+ return TRUE;
+}
+
+static gint32
+ico_load_layer (FILE *fp,
+ gint32 image,
+ gint32 icon_num,
+ guchar *buf,
+ gint maxsize,
+ IcoLoadInfo *info)
+{
+ gint width, height;
+ gint32 layer;
+ guint32 first_bytes;
+ GeglBuffer *buffer;
+ gchar name[ICO_MAXBUF];
+
+ if (fseek (fp, info->offset, SEEK_SET) < 0 ||
+ ! ico_read_int32 (fp, &first_bytes, 1))
+ return -1;
+
+ if (first_bytes == ICO_PNG_MAGIC)
+ {
+ if (!ico_read_png (fp, first_bytes, buf, maxsize, &width, &height))
+ return -1;
+ }
+ else if (first_bytes == 40)
+ {
+ if (!ico_read_icon (fp, first_bytes, buf, maxsize, &width, &height))
+ return -1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ /* read successfully. add to image */
+ g_snprintf (name, sizeof (name), _("Icon #%i"), icon_num+1);
+ layer = gimp_layer_new (image, name, width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ gimp_image_insert_layer (image, layer, -1, icon_num);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ NULL, buf, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+
+ return layer;
+}
+
+
+gint32
+ico_load_image (const gchar *filename,
+ GError **error)
+{
+ FILE *fp;
+ IcoLoadInfo *info;
+ gint max_width, max_height;
+ gint i;
+ gint32 image;
+ guchar *buf;
+ guint icon_count;
+ gint maxsize;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "rb");
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ icon_count = ico_read_init (fp);
+ if (!icon_count)
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ info = ico_read_info (fp, icon_count, error);
+ if (! info)
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ /* find width and height of image */
+ max_width = 0;
+ max_height = 0;
+ for (i = 0; i < icon_count; i++)
+ {
+ if (info[i].width > max_width)
+ max_width = info[i].width;
+ if (info[i].height > max_height)
+ max_height = info[i].height;
+ }
+ if (max_width <= 0 || max_height <= 0)
+ {
+ g_free (info);
+ fclose (fp);
+ return -1;
+ }
+ D(("image size: %ix%i\n", max_width, max_height));
+
+ image = gimp_image_new (max_width, max_height, GIMP_RGB);
+ gimp_image_set_filename (image, filename);
+
+ maxsize = max_width * max_height * 4;
+ buf = g_new (guchar, max_width * max_height * 4);
+ for (i = 0; i < icon_count; i++)
+ {
+ ico_load_layer (fp, image, i, buf, maxsize, info+i);
+ }
+ g_free (buf);
+ g_free (info);
+ fclose (fp);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
+
+gint32
+ico_load_thumbnail_image (const gchar *filename,
+ gint *width,
+ gint *height,
+ GError **error)
+{
+ FILE *fp;
+ IcoLoadInfo *info;
+ gint32 image;
+ gint w = 0;
+ gint h = 0;
+ gint bpp = 0;
+ gint match = 0;
+ gint i, icon_count;
+ guchar *buf;
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ fp = g_fopen (filename, "rb");
+ if (! fp)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ icon_count = ico_read_init (fp);
+ if (! icon_count)
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ D(("*** %s: Microsoft icon file, containing %i icon(s)\n",
+ filename, icon_count));
+
+ info = ico_read_info (fp, icon_count, error);
+ if (! info)
+ {
+ fclose (fp);
+ return -1;
+ }
+
+ /* Do a quick scan of the icons in the file to find the best match */
+ for (i = 0; i < icon_count; i++)
+ {
+ if ((info[i].width > w && w < *width) ||
+ (info[i].height > h && h < *height))
+ {
+ w = info[i].width;
+ h = info[i].height;
+ bpp = info[i].bpp;
+
+ match = i;
+ }
+ else if (w == info[i].width &&
+ h == info[i].height &&
+ info[i].bpp > bpp)
+ {
+ /* better quality */
+ bpp = info[i].bpp;
+ match = i;
+ }
+ }
+
+ if (w <= 0 || h <= 0)
+ return -1;
+
+ image = gimp_image_new (w, h, GIMP_RGB);
+ buf = g_new (guchar, w*h*4);
+ ico_load_layer (fp, image, match, buf, w*h*4, info+match);
+ g_free (buf);
+
+ *width = w;
+ *height = h;
+
+ D(("*** thumbnail successfully loaded.\n\n"));
+
+ gimp_progress_update (1.0);
+
+ g_free (info);
+ fclose (fp);
+
+ return image;
+}
diff --git a/plug-ins/file-ico/ico-load.h b/plug-ins/file-ico/ico-load.h
new file mode 100644
index 0000000..8749fd9
--- /dev/null
+++ b/plug-ins/file-ico/ico-load.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ICO_LOAD_H__
+#define __ICO_LOAD_H__
+
+
+gint32 ico_load_image (const gchar *filename,
+ GError **error);
+gint32 ico_load_thumbnail_image (const gchar *filename,
+ gint *width,
+ gint *height,
+ GError **error);
+
+gint ico_get_bit_from_data (const guint8 *data,
+ gint line_width,
+ gint bit);
+gint ico_get_nibble_from_data (const guint8 *data,
+ gint line_width,
+ gint nibble);
+gint ico_get_byte_from_data (const guint8 *data,
+ gint line_width,
+ gint byte);
+
+
+#endif /* __ICO_LOAD_H__ */
diff --git a/plug-ins/file-ico/ico-save.c b/plug-ins/file-ico/ico-save.c
new file mode 100644
index 0000000..f8ce431
--- /dev/null
+++ b/plug-ins/file-ico/ico-save.c
@@ -0,0 +1,1176 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <png.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-load.h"
+#include "ico-save.h"
+#include "ico-dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static gint ico_write_int8 (FILE *fp,
+ guint8 *data,
+ gint count);
+static gint ico_write_int16 (FILE *fp,
+ guint16 *data,
+ gint count);
+static gint ico_write_int32 (FILE *fp,
+ guint32 *data,
+ gint count);
+
+/* Helpers to set bits in a *cleared* data chunk */
+static void ico_set_bit_in_data (guint8 *data,
+ gint line_width,
+ gint bit_num,
+ gint bit_val);
+static void ico_set_nibble_in_data (guint8 *data,
+ gint line_width,
+ gint nibble_num,
+ gint nibble_val);
+static void ico_set_byte_in_data (guint8 *data,
+ gint line_width,
+ gint byte_num,
+ gint byte_val);
+
+static gint ico_get_layer_num_colors (gint32 layer,
+ gboolean *uses_alpha_levels);
+static void ico_image_get_reduced_buf (guint32 layer,
+ gint bpp,
+ gint *num_colors,
+ guchar **cmap_out,
+ guchar **buf_out);
+
+
+static gint
+ico_write_int32 (FILE *fp,
+ guint32 *data,
+ gint count)
+{
+ gint total;
+
+ total = count;
+ if (count > 0)
+ {
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ gint i;
+
+ for (i = 0; i < count; i++)
+ data[i] = GUINT32_FROM_LE (data[i]);
+#endif
+
+ ico_write_int8 (fp, (guint8 *) data, count * 4);
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ /* Put it back like we found it */
+ for (i = 0; i < count; i++)
+ data[i] = GUINT32_FROM_LE (data[i]);
+#endif
+ }
+
+ return total * 4;
+}
+
+
+static gint
+ico_write_int16 (FILE *fp,
+ guint16 *data,
+ gint count)
+{
+ gint total;
+
+ total = count;
+ if (count > 0)
+ {
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ gint i;
+
+ for (i = 0; i < count; i++)
+ data[i] = GUINT16_FROM_LE (data[i]);
+#endif
+
+ ico_write_int8 (fp, (guint8 *) data, count * 2);
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+ /* Put it back like we found it */
+ for (i = 0; i < count; i++)
+ data[i] = GUINT16_FROM_LE (data[i]);
+#endif
+ }
+
+ return total * 2;
+}
+
+
+static gint
+ico_write_int8 (FILE *fp,
+ guint8 *data,
+ gint count)
+{
+ gint total;
+ gint bytes;
+
+ total = count;
+ while (count > 0)
+ {
+ bytes = fwrite ((gchar *) data, sizeof (gchar), count, fp);
+ if (bytes <= 0) /* something bad happened */
+ break;
+ count -= bytes;
+ data += bytes;
+ }
+
+ return total;
+}
+
+
+static void
+ico_save_init (gint32 image_ID,
+ IcoSaveInfo *info)
+{
+ gint *layers;
+ gint i, num_colors;
+ gboolean uses_alpha_values = FALSE;
+
+ layers = gimp_image_get_layers (image_ID, &info->num_icons);
+ info->layers = layers;
+ info->depths = g_new (gint, info->num_icons);
+ info->default_depths = g_new (gint, info->num_icons);
+ info->compress = g_new (gboolean, info->num_icons);
+
+ /* Limit the color depths to values that don't cause any color loss --
+ the user should pick these anyway, so we can save her some time.
+ If the user wants to lose some colors, the settings can always be changed
+ in the dialog: */
+ for (i = 0; i < info->num_icons; i++)
+ {
+ num_colors = ico_get_layer_num_colors (layers[i], &uses_alpha_values);
+
+ if (!uses_alpha_values)
+ {
+ if (num_colors <= 2)
+ {
+ /* Let's suggest monochrome */
+ info->default_depths [i] = 1;
+ }
+ else if (num_colors <= 16)
+ {
+ /* Let's suggest 4bpp */
+ info->default_depths [i] = 4;
+ }
+ else if (num_colors <= 256)
+ {
+ /* Let's suggest 8bpp */
+ info->default_depths [i] = 8;
+ }
+ else
+ {
+ /* Let's suggest 24bpp */
+ info->default_depths [i] = 24;
+ }
+ }
+ else
+ {
+ /* Otherwise, or if real alpha levels are used, stick with 32bpp */
+ info->default_depths [i] = 32;
+ }
+
+ /* vista icons */
+ if (gimp_drawable_width (layers[i]) > 255
+ || gimp_drawable_height (layers[i]) > 255 )
+ {
+ info->compress[i] = TRUE;
+ }
+ else
+ {
+ info->compress[i] = FALSE;
+ }
+ }
+
+ /* set with default values */
+ memcpy (info->depths, info->default_depths,
+ sizeof (gint) * info->num_icons);
+}
+
+
+
+static gboolean
+ico_save_dialog (gint32 image_ID,
+ IcoSaveInfo *info)
+{
+ GtkWidget *dialog;
+ gint i;
+ gint response;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = ico_dialog_new (info);
+ for (i = 0; i < info->num_icons; i++)
+ {
+ /* if (gimp_layer_get_visible(layers[i])) */
+ ico_dialog_add_icon (dialog, info->layers[i], i);
+ }
+
+ /* Scale the thing to approximately fit its content, but not too large ... */
+ gtk_window_set_default_size (GTK_WINDOW (dialog),
+ -1,
+ 200 + (info->num_icons > 4 ?
+ 500 : info->num_icons * 120));
+
+ gtk_widget_show (dialog);
+
+ response = gimp_dialog_run (GIMP_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ return (response == GTK_RESPONSE_OK);
+}
+
+static void
+ico_set_bit_in_data (guint8 *data,
+ gint line_width,
+ gint bit_num,
+ gint bit_val)
+{
+ gint line;
+ gint width32;
+ gint offset;
+
+ /* width per line in multiples of 32 bits */
+ width32 = (line_width % 32 == 0 ? line_width/32 : line_width/32 + 1);
+
+ line = bit_num / line_width;
+ offset = bit_num % line_width;
+ bit_val = bit_val & 0x00000001;
+
+ data[line * width32 * 4 + offset/8] |= (bit_val << (7 - (offset % 8)));
+}
+
+
+static void
+ico_set_nibble_in_data (guint8 *data,
+ gint line_width,
+ gint nibble_num,
+ gint nibble_val)
+{
+ gint line;
+ gint width8;
+ gint offset;
+
+ /* width per line in multiples of 32 bits */
+ width8 = (line_width % 8 == 0 ? line_width/8 : line_width/8 + 1);
+
+ line = nibble_num / line_width;
+ offset = nibble_num % line_width;
+ nibble_val = nibble_val & 0x0000000F;
+
+ data[line * width8 * 4 + offset/2] |=
+ (nibble_val << (4 * (1 - (offset % 2))));
+}
+
+
+static void
+ico_set_byte_in_data (guint8 *data,
+ gint line_width,
+ gint byte_num,
+ gint byte_val)
+{
+ gint line;
+ gint width4;
+ gint offset;
+ gint byte;
+
+ /* width per line in multiples of 32 bits */
+ width4 = (line_width % 4 == 0 ? line_width/4 : line_width/4 + 1);
+
+ line = byte_num / line_width;
+ offset = byte_num % line_width;
+ byte = byte_val & 0x000000FF;
+
+ data[line * width4 * 4 + offset] = byte;
+}
+
+
+/* Create a colormap from the given buffer data */
+static guint32 *
+ico_create_palette (const guchar *cmap,
+ gint num_colors,
+ gint num_colors_used,
+ gint *black_slot)
+{
+ guchar *palette;
+ gint i;
+
+ g_return_val_if_fail (cmap != NULL || num_colors_used == 0, NULL);
+ g_return_val_if_fail (num_colors_used <= num_colors, NULL);
+
+ palette = g_new0 (guchar, num_colors * 4);
+ *black_slot = -1;
+
+ for (i = 0; i < num_colors_used; i++)
+ {
+ palette[i * 4 + 2] = cmap[i * 3];
+ palette[i * 4 + 1] = cmap[i * 3 + 1];
+ palette[i * 4] = cmap[i * 3 + 2];
+
+ if ((cmap[i*3] == 0) &&
+ (cmap[i*3 + 1] == 0) &&
+ (cmap[i*3 + 2] == 0))
+ {
+ *black_slot = i;
+ }
+ }
+
+ if (*black_slot == -1)
+ {
+ if (num_colors_used == num_colors)
+ {
+ D(("WARNING -- no room for black, this shouldn't happen.\n"));
+ *black_slot = num_colors - 1;
+
+ palette[(num_colors-1) * 4] = 0;
+ palette[(num_colors-1) * 4 + 1] = 0;
+ palette[(num_colors-1) * 4 + 2] = 0;
+ }
+ else
+ {
+ *black_slot = num_colors_used;
+ }
+ }
+
+ return (guint32 *) palette;
+}
+
+
+static GHashTable *
+ico_create_color_to_palette_map (const guint32 *palette,
+ gint num_colors)
+{
+ GHashTable *hash;
+ gint i;
+
+ hash = g_hash_table_new_full (g_int_hash, g_int_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ for (i = 0; i < num_colors; i++)
+ {
+ const guint8 *pixel = (const guint8 *) &palette[i];
+ gint *color;
+ gint *slot;
+
+ color = g_new (gint, 1);
+ slot = g_new (gint, 1);
+
+ *color = (pixel[2] << 16 | pixel[1] << 8 | pixel[0]);
+ *slot = i;
+
+ g_hash_table_insert (hash, color, slot);
+ }
+
+ return hash;
+}
+
+static gint
+ico_get_palette_index (GHashTable *hash,
+ gint red,
+ gint green,
+ gint blue)
+{
+ gint color = 0;
+ gint *slot;
+
+ color = (red << 16 | green << 8 | blue);
+ slot = g_hash_table_lookup (hash, &color);
+
+ if (!slot)
+ {
+ return 0;
+ }
+
+ return *slot;
+}
+
+static gint
+ico_get_layer_num_colors (gint32 layer,
+ gboolean *uses_alpha_levels)
+{
+ gint w, h;
+ gint bpp;
+ gint num_colors = 0;
+ guint num_pixels;
+ guchar *buf;
+ guchar *src;
+ guint32 *colors;
+ guint32 *c;
+ GHashTable *hash;
+ GeglBuffer *buffer = gimp_drawable_get_buffer (layer);
+ const Babl *format;
+
+ w = gegl_buffer_get_width (buffer);
+ h = gegl_buffer_get_height (buffer);
+
+ num_pixels = w * h;
+
+ switch (gimp_drawable_type (layer))
+ {
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = gegl_buffer_get_format (buffer);
+ /* It is possible to count the colors of indexed image more easily
+ * with gimp_image_get_colormap(), but counting only the colors
+ * actually used will allow more efficient bpp if possible. */
+ break;
+
+ default:
+ g_return_val_if_reached (0);
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ buf = src = g_new (guchar, num_pixels * bpp);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ hash = g_hash_table_new (g_int_hash, g_int_equal);
+ *uses_alpha_levels = FALSE;
+
+ colors = c = g_new (guint32, num_pixels);
+
+ switch (bpp)
+ {
+ case 1:
+ while (num_pixels--)
+ {
+ *c = *src;
+ g_hash_table_insert (hash, c, c);
+ src++;
+ c++;
+ }
+ break;
+
+ case 2:
+ while (num_pixels--)
+ {
+ *c = (src[1] << 8) | src[0];
+ if (src[1] != 0 && src[1] != 255)
+ *uses_alpha_levels = TRUE;
+ g_hash_table_insert (hash, c, c);
+ src += 2;
+ c++;
+ }
+ break;
+
+ case 3:
+ while (num_pixels--)
+ {
+ *c = (src[2] << 16) | (src[1] << 8) | src[0];
+ g_hash_table_insert (hash, c, c);
+ src += 3;
+ c++;
+ }
+ break;
+
+ case 4:
+ while (num_pixels--)
+ {
+ *c = (src[3] << 24) | (src[2] << 16) | (src[1] << 8) | src[0];
+ if (src[3] != 0 && src[3] != 255)
+ *uses_alpha_levels = TRUE;
+ g_hash_table_insert (hash, c, c);
+ src += 4;
+ c++;
+ }
+ break;
+ }
+
+ num_colors = g_hash_table_size (hash);
+
+ g_hash_table_destroy (hash);
+
+ g_free (colors);
+ g_free (buf);
+
+ return num_colors;
+}
+
+gboolean
+ico_cmap_contains_black (const guchar *cmap,
+ gint num_colors)
+{
+ gint i;
+
+ for (i = 0; i < num_colors; i++)
+ {
+ if ((cmap[3 * i ] == 0) &&
+ (cmap[3 * i + 1] == 0) &&
+ (cmap[3 * i + 2] == 0))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+ico_image_get_reduced_buf (guint32 layer,
+ gint bpp,
+ gint *num_colors,
+ guchar **cmap_out,
+ guchar **buf_out)
+{
+ gint32 tmp_image;
+ gint32 tmp_layer;
+ gint w, h;
+ guchar *buf;
+ guchar *cmap = NULL;
+ GeglBuffer *buffer = gimp_drawable_get_buffer (layer);
+ const Babl *format;
+
+ w = gegl_buffer_get_width (buffer);
+ h = gegl_buffer_get_height (buffer);
+
+ switch (gimp_drawable_type (layer))
+ {
+ case GIMP_RGB_IMAGE:
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = gegl_buffer_get_format (buffer);
+ break;
+
+ default:
+ g_return_if_reached ();
+ }
+
+ *num_colors = 0;
+
+ buf = g_new (guchar, w * h * 4);
+
+ if (bpp <= 8 || bpp == 24 || babl_format_get_bytes_per_pixel (format) != 4)
+ {
+ gint32 image = gimp_item_get_image (layer);
+ GeglBuffer *tmp;
+
+ tmp_image = gimp_image_new (w, h, gimp_image_base_type (image));
+ gimp_image_undo_disable (tmp_image);
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ guchar *cmap;
+ gint num_colors;
+
+ cmap = gimp_image_get_colormap (image, &num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, num_colors);
+ g_free (cmap);
+ }
+
+ tmp_layer = gimp_layer_new (tmp_image, "tmp", w, h,
+ gimp_drawable_type (layer),
+ 100,
+ gimp_image_get_default_new_layer_mode (tmp_image));
+ gimp_image_insert_layer (tmp_image, tmp_layer, -1, 0);
+
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, tmp, NULL);
+
+ g_object_unref (tmp);
+
+ if (! gimp_drawable_is_rgb (tmp_layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ if (bpp <= 8)
+ {
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ 1 << bpp, TRUE, FALSE, "dummy");
+
+ cmap = gimp_image_get_colormap (tmp_image, num_colors);
+
+ if (*num_colors == (1 << bpp) &&
+ ! ico_cmap_contains_black (cmap, *num_colors))
+ {
+ /* Windows icons with color maps need the color black.
+ * We need to eliminate one more color to make room for black.
+ */
+
+ if (gimp_drawable_is_indexed (layer))
+ {
+ g_free (cmap);
+ cmap = gimp_image_get_colormap (image, num_colors);
+ gimp_image_set_colormap (tmp_image, cmap, *num_colors);
+ }
+ else if (gimp_drawable_is_gray (layer))
+ {
+ gimp_image_convert_grayscale (tmp_image);
+ }
+ else
+ {
+ gimp_image_convert_rgb (tmp_image);
+ }
+
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_set (tmp, GEGL_RECTANGLE (0, 0, w, h), 0,
+ format, buf, GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (tmp);
+
+ if (! gimp_drawable_is_rgb (layer))
+ gimp_image_convert_rgb (tmp_image);
+
+ gimp_image_convert_indexed (tmp_image,
+ GIMP_CONVERT_DITHER_FS,
+ GIMP_CONVERT_PALETTE_GENERATE,
+ (1<<bpp) - 1, TRUE, FALSE, "dummy");
+ g_free (cmap);
+ cmap = gimp_image_get_colormap (tmp_image, num_colors);
+ }
+
+ gimp_image_convert_rgb (tmp_image);
+ }
+ else if (bpp == 24)
+ {
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+ return_vals =
+ gimp_run_procedure ("plug-in-threshold-alpha", &n_return_vals,
+ GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
+ GIMP_PDB_IMAGE, tmp_image,
+ GIMP_PDB_DRAWABLE, tmp_layer,
+ GIMP_PDB_INT32, ICO_ALPHA_THRESHOLD,
+ GIMP_PDB_END);
+ gimp_destroy_params (return_vals, n_return_vals);
+ }
+
+ gimp_layer_add_alpha (tmp_layer);
+
+ tmp = gimp_drawable_get_buffer (tmp_layer);
+
+ gegl_buffer_get (tmp, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ NULL, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (tmp);
+
+ gimp_image_delete (tmp_image);
+ }
+ else
+ {
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, w, h), 1.0,
+ format, buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ }
+
+ g_object_unref (buffer);
+
+ *cmap_out = cmap;
+ *buf_out = buf;
+}
+
+static gboolean
+ico_write_png (FILE *fp,
+ gint32 layer,
+ gint32 depth)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_byte **row_pointers;
+ gint i, rowstride;
+ gint width, height;
+ gint num_colors_used;
+ guchar *palette;
+ guchar *buf;
+
+ row_pointers = NULL;
+ palette = NULL;
+ buf = NULL;
+
+ width = gimp_drawable_width (layer);
+ height = gimp_drawable_height (layer);
+
+ png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if ( !png_ptr )
+ return FALSE;
+
+ info_ptr = png_create_info_struct (png_ptr);
+ if ( !info_ptr )
+ {
+ png_destroy_write_struct (&png_ptr, NULL);
+ return FALSE;
+ }
+
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+ if ( row_pointers )
+ g_free (row_pointers);
+ if (palette)
+ g_free (palette);
+ if (buf)
+ g_free (buf);
+ return FALSE;
+ }
+
+ ico_image_get_reduced_buf (layer, depth, &num_colors_used,
+ &palette, &buf);
+
+ png_init_io (png_ptr, fp);
+ png_set_IHDR (png_ptr, info_ptr, width, height,
+ 8,
+ PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ png_write_info (png_ptr, info_ptr);
+
+ rowstride = ico_rowstride (width, 32);
+ row_pointers = g_new (png_byte*, height);
+ for (i = 0; i < height; i++)
+ {
+ row_pointers[i] = buf + rowstride * i;
+ }
+ png_write_image (png_ptr, row_pointers);
+
+ row_pointers = NULL;
+
+ png_write_end (png_ptr, info_ptr);
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+
+ g_free (row_pointers);
+ g_free (palette);
+ g_free (buf);
+ return TRUE;
+}
+
+static gboolean
+ico_write_icon (FILE *fp,
+ gint32 layer,
+ gint32 depth)
+{
+ IcoFileDataHeader header;
+ gint and_len, xor_len, palette_index, x, y;
+ gint num_colors = 0, num_colors_used = 0, black_index = 0;
+ gint width, height;
+ guchar *buf = NULL, *pixel;
+ guint32 *buf32;
+ guchar *palette;
+ GHashTable *color_to_slot = NULL;
+ guchar *xor_map, *and_map;
+
+ guint32 *palette32 = NULL;
+ gint palette_len = 0;
+
+ guint8 alpha_threshold;
+
+ D(("Creating data structures for icon %i ------------------------\n",
+ num_icon));
+
+ width = gimp_drawable_width (layer);
+ height = gimp_drawable_height (layer);
+
+ header.header_size = 40;
+ header.width = width;
+ header.height = 2 * height;
+ header.planes = 1;
+ header.bpp = depth;
+ header.compression = 0;
+ header.image_size = 0;
+ header.x_res = 0;
+ header.y_res = 0;
+ header.used_clrs = 0;
+ header.important_clrs = 0;
+
+ num_colors = (1L << header.bpp);
+
+ D((" header size %i, w %i, h %i, planes %i, bpp %i\n",
+ header.header_size, header.width, header.height, header.planes,
+ header.bpp));
+
+ /* Reduce colors in copy of image */
+ ico_image_get_reduced_buf (layer, header.bpp, &num_colors_used,
+ &palette, &buf);
+ buf32 = (guint32 *) buf;
+
+ /* Set up colormap and and_map when necessary: */
+ if (header.bpp <= 8)
+ {
+ /* Create a colormap */
+ palette32 = ico_create_palette (palette,
+ num_colors, num_colors_used,
+ &black_index);
+ palette_len = num_colors * 4;
+
+ color_to_slot = ico_create_color_to_palette_map (palette32,
+ num_colors_used);
+ D((" created %i-slot colormap with %i colors, black at slot %i\n",
+ num_colors, num_colors_used, black_index));
+ }
+
+ /* Create and_map. It's padded out to 32 bits per line: */
+ and_map = ico_alloc_map (width, height, 1, &and_len);
+
+ /* 32-bit bitmaps have an alpha channel as well as a mask. Any partially or
+ * fully opaque pixel should have an opaque mask (some ICO code in Windows
+ * draws pixels as black if they have a transparent mask but a non-transparent
+ * alpha value).
+ *
+ * For bitmaps without an alpha channel, we use the normal threshold to build
+ * the mask, so that the mask is as close as possible to the original alpha
+ * channel.
+ */
+ alpha_threshold = header.bpp < 32 ? ICO_ALPHA_THRESHOLD : 0;
+
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+
+ ico_set_bit_in_data (and_map, width,
+ (height - y -1) * width + x,
+ (pixel[3] > alpha_threshold ? 0 : 1));
+ }
+
+ xor_map = ico_alloc_map (width, height, header.bpp, &xor_len);
+
+ /* Now fill in the xor map */
+ switch (header.bpp)
+ {
+ case 1:
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+ palette_index = ico_get_palette_index (color_to_slot, pixel[0],
+ pixel[1], pixel[2]);
+
+ if (ico_get_bit_from_data (and_map, width,
+ (height - y - 1) * width + x))
+ {
+ ico_set_bit_in_data (xor_map, width,
+ (height - y -1) * width + x,
+ black_index);
+ }
+ else
+ {
+ ico_set_bit_in_data (xor_map, width,
+ (height - y -1) * width + x,
+ palette_index);
+ }
+ }
+ break;
+
+ case 4:
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+ palette_index = ico_get_palette_index(color_to_slot, pixel[0],
+ pixel[1], pixel[2]);
+
+ if (ico_get_bit_from_data (and_map, width,
+ (height - y - 1) * width + x))
+ {
+ ico_set_nibble_in_data (xor_map, width,
+ (height - y -1) * width + x,
+ black_index);
+ }
+ else
+ {
+ ico_set_nibble_in_data (xor_map, width,
+ (height - y - 1) * width + x,
+ palette_index);
+ }
+ }
+ break;
+
+ case 8:
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+ palette_index = ico_get_palette_index (color_to_slot,
+ pixel[0],
+ pixel[1],
+ pixel[2]);
+
+ if (ico_get_bit_from_data (and_map, width,
+ (height - y - 1) * width + x))
+ {
+ ico_set_byte_in_data (xor_map, width,
+ (height - y - 1) * width + x,
+ black_index);
+ }
+ else
+ {
+ ico_set_byte_in_data (xor_map, width,
+ (height - y - 1) * width + x,
+ palette_index);
+ }
+
+ }
+ break;
+
+ case 24:
+ for (y = 0; y < height; y++)
+ {
+ guchar *row = xor_map + (xor_len * (height - y - 1) / height);
+
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+
+ row[0] = pixel[2];
+ row[1] = pixel[1];
+ row[2] = pixel[0];
+
+ row += 3;
+ }
+ }
+ break;
+
+ default:
+ for (y = 0; y < height; y++)
+ for (x = 0; x < width; x++)
+ {
+ pixel = (guint8 *) &buf32[y * width + x];
+
+ ((guint32 *) xor_map)[(height - y -1) * width + x] =
+ GUINT32_TO_LE ((pixel[0] << 16) |
+ (pixel[1] << 8) |
+ (pixel[2]) |
+ (pixel[3] << 24));
+ }
+ }
+
+ D((" filled and_map of length %i, xor_map of length %i\n",
+ and_len, xor_len));
+
+ if (color_to_slot)
+ g_hash_table_destroy (color_to_slot);
+
+ g_free (palette);
+ g_free (buf);
+
+ ico_write_int32 (fp, (guint32*) &header, 3);
+ ico_write_int16 (fp, &header.planes, 2);
+ ico_write_int32 (fp, &header.compression, 6);
+
+ if (palette_len)
+ ico_write_int8 (fp, (guint8 *) palette32, palette_len);
+
+ ico_write_int8 (fp, xor_map, xor_len);
+ ico_write_int8 (fp, and_map, and_len);
+
+ g_free (palette32);
+ g_free (xor_map);
+ g_free (and_map);
+
+ return TRUE;
+}
+
+static void
+ico_save_info_free (IcoSaveInfo *info)
+{
+ g_free (info->depths);
+ g_free (info->default_depths);
+ g_free (info->compress);
+ g_free (info->layers);
+ memset (info, 0, sizeof (IcoSaveInfo));
+}
+
+GimpPDBStatusType
+ico_save_image (const gchar *filename,
+ gint32 image,
+ gint32 run_mode,
+ GError **error)
+{
+ FILE *fp;
+
+ gint i;
+ gint width, height;
+ IcoSaveInfo info;
+ IcoFileHeader header;
+ IcoFileEntry *entries;
+ gboolean saved;
+
+ D(("*** Exporting Microsoft icon file %s\n", filename));
+
+ ico_save_init (image, &info);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ /* Allow user to override default values */
+ if ( !ico_save_dialog (image, &info))
+ return GIMP_PDB_CANCEL;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if (! (fp = g_fopen (filename, "wb")))
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ header.reserved = 0;
+ header.resource_type = 1;
+ header.icon_count = info.num_icons;
+ if ( !ico_write_int16 (fp, &header.reserved, 1)
+ || !ico_write_int16 (fp, &header.resource_type, 1)
+ || !ico_write_int16 (fp, &header.icon_count, 1) )
+ {
+ ico_save_info_free (&info);
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ entries = g_new0 (IcoFileEntry, info.num_icons);
+ if (fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+ {
+ ico_save_info_free (&info);
+ g_free (entries);
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ for (i = 0; i < info.num_icons; i++)
+ {
+ gimp_progress_update ((gdouble)i / (gdouble)info.num_icons);
+
+ width = gimp_drawable_width (info.layers[i]);
+ height = gimp_drawable_height (info.layers[i]);
+ if (width <= 255 && height <= 255)
+ {
+ entries[i].width = width;
+ entries[i].height = height;
+ }
+ else
+ {
+ entries[i].width = 0;
+ entries[i].height = 0;
+ }
+ if ( info.depths[i] <= 8 )
+ entries[i].num_colors = 1 << info.depths[i];
+ else
+ entries[i].num_colors = 0;
+ entries[i].reserved = 0;
+ entries[i].planes = 1;
+ entries[i].bpp = info.depths[i];
+ entries[i].offset = ftell (fp);
+
+ if (info.compress[i])
+ saved = ico_write_png (fp, info.layers[i], info.depths[i]);
+ else
+ saved = ico_write_icon (fp, info.layers[i], info.depths[i]);
+
+ if (!saved)
+ {
+ ico_save_info_free (&info);
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ entries[i].size = ftell (fp) - entries[i].offset;
+ }
+
+ for (i = 0; i < info.num_icons; i++)
+ {
+ entries[i].planes = GUINT16_TO_LE (entries[i].planes);
+ entries[i].bpp = GUINT16_TO_LE (entries[i].bpp);
+ entries[i].size = GUINT32_TO_LE (entries[i].size);
+ entries[i].offset = GUINT32_TO_LE (entries[i].offset);
+ }
+
+ if (fseek (fp, sizeof(IcoFileHeader), SEEK_SET) < 0
+ || fwrite (entries, sizeof (IcoFileEntry), info.num_icons, fp) <= 0)
+ {
+ ico_save_info_free (&info);
+ fclose (fp);
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_progress_update (1.0);
+
+ ico_save_info_free (&info);
+ fclose (fp);
+ g_free (entries);
+
+ return GIMP_PDB_SUCCESS;
+}
diff --git a/plug-ins/file-ico/ico-save.h b/plug-ins/file-ico/ico-save.h
new file mode 100644
index 0000000..46d6ff5
--- /dev/null
+++ b/plug-ins/file-ico/ico-save.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ICO_SAVE_H__
+#define __ICO_SAVE_H__
+
+
+GimpPDBStatusType ico_save_image (const gchar *file_name,
+ gint32 image_ID,
+ gint32 run_mode,
+ GError **error);
+
+gboolean ico_cmap_contains_black (const guchar *cmap,
+ gint num_colors);
+
+
+#endif /* __ICO_SAVE_H__ */
diff --git a/plug-ins/file-ico/ico.c b/plug-ins/file-ico/ico.c
new file mode 100644
index 0000000..6c15c22
--- /dev/null
+++ b/plug-ins/file-ico/ico.c
@@ -0,0 +1,345 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+/* #define ICO_DBG */
+
+#include "ico.h"
+#include "ico-load.h"
+#include "ico-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define LOAD_PROC "file-ico-load"
+#define LOAD_THUMB_PROC "file-ico-load-thumb"
+#define SAVE_PROC "file-ico-save"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files of Windows ICO file format",
+ "Loads files of Windows ICO file format",
+ "Christian Kreibich <christian@whoop.org>",
+ "Christian Kreibich <christian@whoop.org>",
+ "2002",
+ N_("Microsoft Windows icon"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-ico");
+ /* We do not set magics here, since that interferes with certain types
+ of TGA images. */
+ gimp_register_load_handler (LOAD_PROC,
+ "ico",
+ "");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads a preview from an Windows ICO file",
+ "",
+ "Dom Lachowicz, Sven Neumann",
+ "Sven Neumann <sven@gimp.org>",
+ "2005",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves files in Windows ICO file format",
+ "Saves files in Windows ICO file format",
+ "Christian Kreibich <christian@whoop.org>",
+ "Christian Kreibich <christian@whoop.org>",
+ "2002",
+ N_("Microsoft Windows icon"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-ico");
+ gimp_register_save_handler (SAVE_PROC, "ico", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint32 image_ID;
+
+ image_ID = ico_load_image (param[1].data.d_string, &error);
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ const gchar *filename = param[0].data.d_string;
+ gint width = param[1].data.d_int32;
+ gint height = param[1].data.d_int32;
+ gint32 image_ID;
+
+ image_ID = ico_load_thumbnail_image (filename,
+ &width, &height, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 4;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gchar *file_name;
+ gint32 image_ID;
+
+ image_ID = param[1].data.d_int32;
+ file_name = param[3].data.d_string;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams < 5)
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ status = ico_save_image (file_name, image_ID, run_mode, &error);
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+gint
+ico_rowstride (gint width,
+ gint bpp)
+{
+ switch (bpp)
+ {
+ case 1:
+ if ((width % 32) == 0)
+ return width / 8;
+ else
+ return 4 * (width/32 + 1);
+ break;
+
+ case 4:
+ if ((width % 8) == 0)
+ return width / 2;
+ else
+ return 4 * (width/8 + 1);
+ break;
+
+ case 8:
+ if ((width % 4) == 0)
+ return width;
+ else
+ return 4 * (width/4 + 1);
+ break;
+
+ case 24:
+ if (((width*3) % 4) == 0)
+ return width * 3;
+ else
+ return 4 * (width*3/4+1);
+
+ case 32:
+ return width * 4;
+
+ default:
+ g_warning ("invalid bitrate: %d\n", bpp);
+ g_assert_not_reached ();
+ return width * (bpp/8);
+ }
+}
+
+guint8 *
+ico_alloc_map (gint width,
+ gint height,
+ gint bpp,
+ gint *length)
+{
+ gint len = 0;
+ guint8 *map = NULL;
+
+ len = ico_rowstride (width, bpp) * height;
+
+ *length = len;
+ map = g_new0 (guint8, len);
+
+ return map;
+}
diff --git a/plug-ins/file-ico/ico.h b/plug-ins/file-ico/ico.h
new file mode 100644
index 0000000..65a217c
--- /dev/null
+++ b/plug-ins/file-ico/ico.h
@@ -0,0 +1,110 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Plug-in for Windows Icon files.
+ * Copyright (C) 2002 Christian Kreibich <christian@whoop.org>.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ICO_H__
+#define __ICO_H__
+
+
+#ifdef ICO_DBG
+#define D(x) \
+{ \
+ printf("ICO plugin: "); \
+ printf x; \
+}
+#else
+#define D(x)
+#endif
+
+#define PLUG_IN_BINARY "file-ico"
+#define PLUG_IN_ROLE "gimp-file-ico"
+
+#define ICO_PNG_MAGIC 0x474e5089
+#define ICO_ALPHA_THRESHOLD 127
+#define ICO_MAXBUF 4096
+
+
+typedef struct _IcoFileHeader
+{
+ guint16 reserved;
+ guint16 resource_type;
+ guint16 icon_count;
+} IcoFileHeader;
+
+typedef struct _IcoFileEntry
+{
+ guint8 width; /* Width of icon in pixels */
+ guint8 height; /* Height of icon in pixels */
+ guint8 num_colors; /* Number of colors of paletted image */
+ guint8 reserved; /* Must be 0 */
+ guint16 planes; /* Must be 1 */
+ guint16 bpp; /* 1, 4, 8, 24 or 32 bits per pixel */
+ guint32 size; /* Size of icon (including data header) */
+ guint32 offset; /* Absolute offset of data in a file */
+ } IcoFileEntry;
+
+typedef struct _IcoFileDataHeader
+{
+ guint32 header_size; /* 40 bytes */
+ guint32 width; /* Width of image in pixels */
+ guint32 height; /* Height of image in pixels */
+ guint16 planes; /* Must be 1 */
+ guint16 bpp;
+ guint32 compression; /* Not used for icons */
+ guint32 image_size; /* Size of image (without this header) */
+ guint32 x_res;
+ guint32 y_res;
+ guint32 used_clrs;
+ guint32 important_clrs;
+} IcoFileDataHeader;
+
+
+typedef struct _IcoLoadInfo
+{
+ guint width;
+ guint height;
+ gint bpp;
+ gint offset;
+ gint size;
+} IcoLoadInfo;
+
+typedef struct _IcoSaveInfo
+{
+ gint *depths;
+ gint *default_depths;
+ gboolean *compress;
+ gint *layers;
+ gint num_icons;
+} IcoSaveInfo;
+
+
+/* Miscellaneous helper functions below: */
+
+gint ico_rowstride (gint width,
+ gint bpp);
+
+/* Allocates a 32-bit padded bitmap for various color depths.
+ Returns the allocated array directly, and the length of the
+ array in the len pointer */
+guint8 * ico_alloc_map (gint width,
+ gint height,
+ gint bpp,
+ gint *len);
+
+#endif /* __ICO_H__ */
diff --git a/plug-ins/file-jpeg/Makefile.am b/plug-ins/file-jpeg/Makefile.am
new file mode 100644
index 0000000..b443475
--- /dev/null
+++ b/plug-ins/file-jpeg/Makefile.am
@@ -0,0 +1,77 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_jpeg_RC = file-jpeg.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-jpeg
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-jpeg
+
+file_jpeg_SOURCES = \
+ jpeg.c \
+ jpeg.h \
+ jpeg-icc.c \
+ jpeg-icc.h \
+ jpeg-load.c \
+ jpeg-load.h \
+ jpeg-save.c \
+ jpeg-save.h \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpeg-settings.c \
+ jpeg-settings.h
+
+file_jpeg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(LCMS_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpeg_RC)
+
+noinst_PROGRAMS = jpegqual
+
+jpegqual_SOURCES = \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpegqual.c
+
+jpegqual_LDADD = \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
diff --git a/plug-ins/file-jpeg/Makefile.in b/plug-ins/file-jpeg/Makefile.in
new file mode 100644
index 0000000..ab5f989
--- /dev/null
+++ b/plug-ins/file-jpeg/Makefile.in
@@ -0,0 +1,1069 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-jpeg$(EXEEXT)
+noinst_PROGRAMS = jpegqual$(EXEEXT)
+subdir = plug-ins/file-jpeg
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS)
+am_file_jpeg_OBJECTS = jpeg.$(OBJEXT) jpeg-icc.$(OBJEXT) \
+ jpeg-load.$(OBJEXT) jpeg-save.$(OBJEXT) jpeg-quality.$(OBJEXT) \
+ jpeg-settings.$(OBJEXT)
+file_jpeg_OBJECTS = $(am_file_jpeg_OBJECTS)
+am__DEPENDENCIES_1 =
+file_jpeg_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_jpeg_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_jpegqual_OBJECTS = jpeg-quality.$(OBJEXT) jpegqual.$(OBJEXT)
+jpegqual_OBJECTS = $(am_jpegqual_OBJECTS)
+jpegqual_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/jpeg-icc.Po ./$(DEPDIR)/jpeg-load.Po \
+ ./$(DEPDIR)/jpeg-quality.Po ./$(DEPDIR)/jpeg-save.Po \
+ ./$(DEPDIR)/jpeg-settings.Po ./$(DEPDIR)/jpeg.Po \
+ ./$(DEPDIR)/jpegqual.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_jpeg_SOURCES) $(jpegqual_SOURCES)
+DIST_SOURCES = $(file_jpeg_SOURCES) $(jpegqual_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-jpeg
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_jpeg_RC = file-jpeg.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+file_jpeg_SOURCES = \
+ jpeg.c \
+ jpeg.h \
+ jpeg-icc.c \
+ jpeg-icc.h \
+ jpeg-load.c \
+ jpeg-load.h \
+ jpeg-save.c \
+ jpeg-save.h \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpeg-settings.c \
+ jpeg-settings.h
+
+file_jpeg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(LCMS_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpeg_RC)
+
+jpegqual_SOURCES = \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpegqual.c
+
+jpegqual_LDADD = \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-jpeg/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-jpeg/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-jpeg$(EXEEXT): $(file_jpeg_OBJECTS) $(file_jpeg_DEPENDENCIES) $(EXTRA_file_jpeg_DEPENDENCIES)
+ @rm -f file-jpeg$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_jpeg_OBJECTS) $(file_jpeg_LDADD) $(LIBS)
+
+jpegqual$(EXEEXT): $(jpegqual_OBJECTS) $(jpegqual_DEPENDENCIES) $(EXTRA_jpegqual_DEPENDENCIES)
+ @rm -f jpegqual$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(jpegqual_OBJECTS) $(jpegqual_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-icc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-quality.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpegqual.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/jpeg-icc.Po
+ -rm -f ./$(DEPDIR)/jpeg-load.Po
+ -rm -f ./$(DEPDIR)/jpeg-quality.Po
+ -rm -f ./$(DEPDIR)/jpeg-save.Po
+ -rm -f ./$(DEPDIR)/jpeg-settings.Po
+ -rm -f ./$(DEPDIR)/jpeg.Po
+ -rm -f ./$(DEPDIR)/jpegqual.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/jpeg-icc.Po
+ -rm -f ./$(DEPDIR)/jpeg-load.Po
+ -rm -f ./$(DEPDIR)/jpeg-quality.Po
+ -rm -f ./$(DEPDIR)/jpeg-save.Po
+ -rm -f ./$(DEPDIR)/jpeg-settings.Po
+ -rm -f ./$(DEPDIR)/jpeg.Po
+ -rm -f ./$(DEPDIR)/jpegqual.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libexecPROGRAMS install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-jpeg/jpeg-icc.c b/plug-ins/file-jpeg/jpeg-icc.c
new file mode 100644
index 0000000..88f75d4
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-icc.c
@@ -0,0 +1,263 @@
+/* jpeg-icc.c
+ *
+ * This file provides code to read and write International Color
+ * Consortium (ICC) device profiles embedded in JFIF JPEG image files.
+ * The ICC has defined a standard format for including such data in
+ * JPEG "APP2" markers. The code given here does not know anything
+ * about the internal structure of the ICC profile data; it just knows
+ * how to put the profile data into a JPEG file being written, or get
+ * it back out when reading.
+ *
+ * This code depends on new features added to the IJG JPEG library as of
+ * IJG release 6b; it will not compile or work with older IJG versions.
+ */
+
+/* This code was originally copied from the jpegicc tool as found in
+ * the lcms source code. This code comes with the following copyright
+ * notice:
+ *
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <jpeglib.h>
+
+#include <glib.h>
+
+#include "jpeg-icc.h"
+
+
+/*
+ * Since an ICC profile can be larger than the maximum size of a JPEG
+ * marker (64K), we need provisions to split it into multiple markers.
+ * The format defined by the ICC specifies one or more APP2 markers
+ * containing the following data:
+ *
+ * Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
+ * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
+ * Number of markers Total number of APP2's used (1 byte)
+ * Profile data (remainder of APP2 data)
+
+ * Decoders should use the marker sequence numbers to reassemble the
+ * profile, rather than assuming that the APP2 markers appear in the
+ * correct sequence.
+ */
+
+#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
+#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
+#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
+#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
+
+
+/*
+ * This routine writes the given ICC profile data into a JPEG file.
+ * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
+ * the first call to jpeg_write_scanlines().
+ * (This ordering ensures that the APP2 marker(s) will appear after the
+ * SOI and JFIF or Adobe markers, but before all else.)
+ */
+
+void
+jpeg_icc_write_profile (j_compress_ptr cinfo,
+ const guchar *icc_data_ptr,
+ guint icc_data_len)
+{
+ unsigned int num_markers; /* total number of markers we'll write */
+ int cur_marker = 1; /* per spec, counting starts at 1 */
+ unsigned int length; /* number of bytes to write in this marker */
+
+ /* Calculate the number of markers we'll need, rounding up of course */
+ num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
+ if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
+ num_markers++;
+
+ while (icc_data_len > 0) {
+ /* length of profile to put in this marker */
+ length = icc_data_len;
+ if (length > MAX_DATA_BYTES_IN_MARKER)
+ length = MAX_DATA_BYTES_IN_MARKER;
+ icc_data_len -= length;
+
+ /* Write the JPEG marker header (APP2 code and marker length) */
+ jpeg_write_m_header(cinfo, ICC_MARKER,
+ (unsigned int) (length + ICC_OVERHEAD_LEN));
+
+ /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
+ * We code it in this less-than-transparent way so that the code works
+ * even if the local character set is not ASCII.
+ */
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x5F);
+ jpeg_write_m_byte(cinfo, 0x50);
+ jpeg_write_m_byte(cinfo, 0x52);
+ jpeg_write_m_byte(cinfo, 0x4F);
+ jpeg_write_m_byte(cinfo, 0x46);
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x4C);
+ jpeg_write_m_byte(cinfo, 0x45);
+ jpeg_write_m_byte(cinfo, 0x0);
+
+ /* Add the sequencing info */
+ jpeg_write_m_byte(cinfo, cur_marker);
+ jpeg_write_m_byte(cinfo, (int) num_markers);
+
+ /* Add the profile data */
+ while (length--) {
+ jpeg_write_m_byte(cinfo, *icc_data_ptr);
+ icc_data_ptr++;
+ }
+ cur_marker++;
+ }
+}
+
+
+/*
+ * Handy subroutine to test whether a saved marker is an ICC profile marker.
+ */
+
+static boolean
+marker_is_icc (jpeg_saved_marker_ptr marker)
+{
+ return
+ marker->marker == ICC_MARKER &&
+ marker->data_length >= ICC_OVERHEAD_LEN &&
+ /* verify the identifying string */
+ GETJOCTET(marker->data[0]) == 0x49 &&
+ GETJOCTET(marker->data[1]) == 0x43 &&
+ GETJOCTET(marker->data[2]) == 0x43 &&
+ GETJOCTET(marker->data[3]) == 0x5F &&
+ GETJOCTET(marker->data[4]) == 0x50 &&
+ GETJOCTET(marker->data[5]) == 0x52 &&
+ GETJOCTET(marker->data[6]) == 0x4F &&
+ GETJOCTET(marker->data[7]) == 0x46 &&
+ GETJOCTET(marker->data[8]) == 0x49 &&
+ GETJOCTET(marker->data[9]) == 0x4C &&
+ GETJOCTET(marker->data[10]) == 0x45 &&
+ GETJOCTET(marker->data[11]) == 0x0;
+}
+
+
+/*
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * NOTE: if the file contains invalid ICC APP2 markers, we just silently
+ * return FALSE. You might want to issue an error message instead.
+ */
+
+gboolean
+jpeg_icc_read_profile (j_decompress_ptr cinfo,
+ guchar **icc_data_ptr,
+ guint *icc_data_len)
+{
+ jpeg_saved_marker_ptr marker;
+ int num_markers = 0;
+ int seq_no;
+ JOCTET *icc_data;
+ unsigned int total_length;
+#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
+ char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */
+ unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
+ unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
+
+ *icc_data_ptr = NULL; /* avoid confusion if FALSE return */
+ *icc_data_len = 0;
+
+ /* This first pass over the saved markers discovers whether there are
+ * any ICC markers and verifies the consistency of the marker numbering.
+ */
+
+ for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
+ marker_present[seq_no] = 0;
+
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ if (num_markers == 0)
+ num_markers = GETJOCTET(marker->data[13]);
+ else if (num_markers != GETJOCTET(marker->data[13]))
+ return FALSE; /* inconsistent num_markers fields */
+ seq_no = GETJOCTET(marker->data[12]);
+ if (seq_no <= 0 || seq_no > num_markers)
+ return FALSE; /* bogus sequence number */
+ if (marker_present[seq_no])
+ return FALSE; /* duplicate sequence numbers */
+ marker_present[seq_no] = 1;
+ data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
+ }
+ }
+
+ if (num_markers == 0)
+ return FALSE;
+
+ /* Check for missing markers, count total space needed,
+ * compute offset of each marker's part of the data.
+ */
+
+ total_length = 0;
+ for (seq_no = 1; seq_no <= num_markers; seq_no++) {
+ if (marker_present[seq_no] == 0)
+ return FALSE; /* missing sequence number */
+ data_offset[seq_no] = total_length;
+ total_length += data_length[seq_no];
+ }
+
+ if (total_length <= 0)
+ return FALSE; /* found only empty markers? */
+
+ /* Allocate space for assembled data */
+ icc_data = g_try_malloc (total_length * sizeof(JOCTET));
+ if (icc_data == NULL)
+ return FALSE; /* oops, out of memory */
+
+ /* and fill it in */
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ JOCTET FAR *src_ptr;
+ JOCTET *dst_ptr;
+ unsigned int length;
+ seq_no = GETJOCTET(marker->data[12]);
+ dst_ptr = icc_data + data_offset[seq_no];
+ src_ptr = marker->data + ICC_OVERHEAD_LEN;
+ length = data_length[seq_no];
+ while (length--) {
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+
+ *icc_data_ptr = icc_data;
+ *icc_data_len = total_length;
+
+ return TRUE;
+}
diff --git a/plug-ins/file-jpeg/jpeg-icc.h b/plug-ins/file-jpeg/jpeg-icc.h
new file mode 100644
index 0000000..b5306a8
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-icc.h
@@ -0,0 +1,59 @@
+/* jpeg-icc.h
+ *
+ * This code was originally copied from the jpegicc tool as found in
+ * the lcms source code. This code comes with the following copyright
+ * notice:
+ *
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+#ifndef __JPEG_ICC_H__
+#define __JPEG_ICC_H__
+
+void jpeg_icc_write_profile (j_compress_ptr cinfo,
+ const guchar *icc_data_ptr,
+ guint icc_data_len);
+
+
+/*
+ * Reading a JPEG file that may contain an ICC profile requires two steps:
+ *
+ * 1. After jpeg_create_decompress() but before jpeg_read_header(),
+ * ask the IJG library to save in memory any APP2 markers it may find
+ * in the file.
+ *
+ * 2. After jpeg_read_header(), call jpeg_icc_read_profile() to find
+ * out whether there was a profile and obtain it if so.
+ *
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+ * and must be freed by the caller with free() when the caller no longer
+ * needs it. (Alternatively, we could write this routine to use the
+ * IJG library's memory allocator, so that the data would be freed implicitly
+ * at jpeg_finish_decompress() time. But it seems likely that many apps
+ * will prefer to have the data stick around after decompression finishes.)
+ */
+
+gboolean jpeg_icc_read_profile (j_decompress_ptr cinfo,
+ guchar **icc_data_ptr,
+ guint *icc_data_len);
+
+#endif /* __JPEG_ICC_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
new file mode 100644
index 0000000..df53327
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -0,0 +1,719 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <gexiv2/gexiv2.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <lcms2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-icc.h"
+#include "jpeg-settings.h"
+#include "jpeg-load.h"
+
+static gboolean jpeg_load_resolution (gint32 image_ID,
+ struct jpeg_decompress_struct
+ *cinfo);
+
+static void jpeg_load_sanitize_comment (gchar *comment);
+
+static gpointer jpeg_load_cmyk_transform (guint8 *profile_data,
+ gsize profile_len);
+static void jpeg_load_cmyk_to_rgb (guchar *buf,
+ glong pixels,
+ gpointer transform);
+
+gint32 volatile preview_image_ID;
+gint32 preview_layer_ID;
+
+gint32
+load_image (const gchar *filename,
+ GimpRunMode runmode,
+ gboolean preview,
+ gboolean *resolution_loaded,
+ GError **error)
+{
+ gint32 volatile image_ID;
+ gint32 layer_ID;
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ jpeg_saved_marker_ptr marker;
+ FILE *infile;
+ guchar *buf;
+ guchar **rowbuf;
+ GimpImageBaseType image_type;
+ GimpImageType layer_type;
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint tile_height;
+ gint i;
+ cmsHTRANSFORM cmyk_transform = NULL;
+
+ /* We set up the normal JPEG error routines. */
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+
+ if (!preview)
+ {
+ jerr.pub.output_message = my_output_message;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+ }
+
+ if ((infile = g_fopen (filename, "rb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ image_ID = -1;
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_decompress (&cinfo);
+ if (infile)
+ fclose (infile);
+
+ if (image_ID != -1 && !preview)
+ gimp_image_delete (image_ID);
+
+ if (preview)
+ destroy_preview ();
+
+ if (buffer)
+ g_object_unref (buffer);
+
+ return -1;
+ }
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress (&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_stdio_src (&cinfo, infile);
+
+ if (! preview)
+ {
+ /* - step 2.1: tell the lib to save the comments */
+ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff);
+
+ /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */
+ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff);
+
+ /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */
+ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff);
+ }
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* We can ignore the return value from jpeg_read_header since
+ * (a) suspension is not possible with the stdio data source, and
+ * (b) we passed TRUE to reject a tables-only JPEG file as an error.
+ * See libjpeg.doc for more info.
+ */
+
+ /* Step 4: set parameters for decompression */
+
+ /* In this example, we don't need to change any of the defaults set by
+ * jpeg_read_header(), so we do nothing here, except set the DCT
+ * method.
+ */
+
+ cinfo.dct_method = JDCT_FLOAT;
+
+ /* Step 5: Start decompressor */
+
+ jpeg_start_decompress (&cinfo);
+
+ /* We may need to do some setup of our own at this point before reading
+ * the data. After jpeg_start_decompress() we have the correct scaled
+ * output image dimensions available, as well as the output colormap
+ * if we asked for color quantization.
+ */
+
+ /* temporary buffer */
+ tile_height = gimp_tile_height ();
+ buf = g_new (guchar,
+ tile_height * cinfo.output_width * cinfo.output_components);
+
+ rowbuf = g_new (guchar *, tile_height);
+
+ for (i = 0; i < tile_height; i++)
+ rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
+
+ switch (cinfo.output_components)
+ {
+ case 1:
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAY_IMAGE;
+ break;
+
+ case 3:
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+
+ case 4:
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+ }
+ /*fallthrough*/
+
+ default:
+ g_message ("Don't know how to load JPEG images "
+ "with %d color channels, using colorspace %d (%d).",
+ cinfo.output_components, cinfo.out_color_space,
+ cinfo.jpeg_color_space);
+ return -1;
+ break;
+ }
+
+ if (preview)
+ {
+ image_ID = preview_image_ID;
+ }
+ else
+ {
+ image_ID = gimp_image_new_with_precision (cinfo.output_width,
+ cinfo.output_height,
+ image_type,
+ GIMP_PRECISION_U8_GAMMA);
+
+ gimp_image_undo_disable (image_ID);
+ gimp_image_set_filename (image_ID, filename);
+ }
+
+ if (preview)
+ {
+ preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"),
+ cinfo.output_width,
+ cinfo.output_height,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (preview_image_ID));
+ layer_ID = preview_layer_ID;
+ }
+ else
+ {
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ cinfo.output_width,
+ cinfo.output_height,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ }
+
+ if (! preview)
+ {
+ GString *comment_buffer = NULL;
+ guint8 *icc_data = NULL;
+ guint icc_length = 0;
+
+ /* Step 5.0: save the original JPEG settings in a parasite */
+ jpeg_detect_original_settings (&cinfo, image_ID);
+
+ /* Step 5.1: check for comments, or Exif metadata in APP1 markers */
+ for (marker = cinfo.marker_list; marker; marker = marker->next)
+ {
+ const gchar *data = (const gchar *) marker->data;
+ gsize len = marker->data_length;
+
+ if (marker->marker == JPEG_COM)
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-load: found image comment (%d bytes)\n",
+ marker->data_length);
+#endif
+
+ if (! comment_buffer)
+ {
+ comment_buffer = g_string_new_len (data, len);
+ }
+ else
+ {
+ /* concatenate multiple comments, separate them with LF */
+ g_string_append_c (comment_buffer, '\n');
+ g_string_append_len (comment_buffer, data, len);
+ }
+ }
+ else if ((marker->marker == JPEG_APP0 + 1)
+ && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8)
+ && ! strcmp (JPEG_APP_HEADER_EXIF, data))
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-load: found Exif block (%d bytes)\n",
+ (gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
+#endif
+ }
+ }
+
+ if (jpeg_load_resolution (image_ID, &cinfo))
+ {
+ if (resolution_loaded)
+ *resolution_loaded = TRUE;
+ }
+
+ /* if we found any comments, then make a parasite for them */
+ if (comment_buffer && comment_buffer->len)
+ {
+ GimpParasite *parasite;
+
+ jpeg_load_sanitize_comment (comment_buffer->str);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment_buffer->str) + 1,
+ comment_buffer->str);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ g_string_free (comment_buffer, TRUE);
+ }
+
+ /* Step 5.3: check for an embedded ICC profile in APP2 markers */
+ jpeg_icc_read_profile (&cinfo, &icc_data, &icc_length);
+
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ cmyk_transform = jpeg_load_cmyk_transform (icc_data, icc_length);
+ }
+ else if (icc_data) /* don't attach the profile if we are transforming */
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_icc_profile (icc_data,
+ icc_length,
+ NULL);
+ if (profile)
+ {
+ gimp_image_set_color_profile (image_ID, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ g_free (icc_data);
+
+ /* Do not attach the "jpeg-save-options" parasite to the image
+ * because this conflicts with the global defaults (bug #75398).
+ */
+ }
+
+ /* Step 6: while (scan lines remain to be read) */
+ /* jpeg_read_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.output_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ */
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+ format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8");
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ gint start, end;
+ gint scanlines;
+ gboolean image_truncated = FALSE;
+
+ start = cinfo.output_scanline;
+ end = cinfo.output_scanline + tile_height;
+ end = MIN (end, cinfo.output_height);
+
+ scanlines = end - start;
+
+ /* in case of error we now jump here, so pertially loaded imaged
+ * don't get discarded
+ */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ image_truncated = TRUE;
+
+ goto set_buffer;
+ }
+
+ for (i = 0; i < scanlines; i++)
+ jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
+
+ if (cinfo.out_color_space == JCS_CMYK)
+ jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines,
+ cmyk_transform);
+
+ set_buffer:
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines),
+ 0,
+ format,
+ buf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (image_truncated)
+ /* jumping to finish skips jpeg_finish_decompress(), its state
+ * might be broken by whatever caused the loading failure
+ */
+ goto finish;
+
+ if (! preview && (cinfo.output_scanline % 32) == 0)
+ gimp_progress_update ((gdouble) cinfo.output_scanline /
+ (gdouble) cinfo.output_height);
+ }
+
+ /* Step 7: Finish decompression */
+
+ jpeg_finish_decompress (&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ finish:
+
+ if (cmyk_transform)
+ cmsDeleteTransform (cmyk_transform);
+
+ /* Step 8: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_decompress (&cinfo);
+
+ g_object_unref (buffer);
+
+ /* free up the temporary buffers */
+ g_free (rowbuf);
+ g_free (buf);
+
+ /* After finish_decompress, we can close the input file.
+ * Here we postpone it until after no more JPEG errors are possible,
+ * so as to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume anything...)
+ */
+ fclose (infile);
+
+ /* At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.num_warnings is nonzero).
+ */
+
+ /* Detach from the drawable and add it to the image.
+ */
+ if (! preview)
+ {
+ gimp_progress_update (1.0);
+ }
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+static gboolean
+jpeg_load_resolution (gint32 image_ID,
+ struct jpeg_decompress_struct *cinfo)
+{
+ if (cinfo->saw_JFIF_marker && cinfo->X_density != 0 && cinfo->Y_density != 0)
+ {
+ gdouble xresolution = cinfo->X_density;
+ gdouble yresolution = cinfo->Y_density;
+ gdouble asymmetry = 1.0;
+
+ switch (cinfo->density_unit)
+ {
+ case 0: /* unknown -> set the aspect ratio but use the default
+ * image resolution
+ */
+ asymmetry = xresolution / yresolution;
+
+ gimp_image_get_resolution (image_ID, &xresolution, &yresolution);
+
+ xresolution *= asymmetry;
+ break;
+
+ case 1: /* dots per inch */
+ break;
+
+ case 2: /* dots per cm */
+ xresolution *= 2.54;
+ yresolution *= 2.54;
+ gimp_image_set_unit (image_ID, GIMP_UNIT_MM);
+ break;
+
+ default:
+ g_message ("Unknown density unit %d, assuming dots per inch.",
+ cinfo->density_unit);
+ break;
+ }
+
+ gimp_image_set_resolution (image_ID, xresolution, yresolution);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * A number of JPEG files have comments written in a local character set
+ * instead of UTF-8. Some of these files may have been saved by older
+ * versions of GIMP. It is not possible to reliably detect the character
+ * set used, but it is better to keep all characters in the ASCII range
+ * and replace the non-ASCII characters instead of discarding the whole
+ * comment. This is especially useful if the comment contains only a few
+ * non-ASCII characters such as a copyright sign, a soft hyphen, etc.
+ */
+static void
+jpeg_load_sanitize_comment (gchar *comment)
+{
+ const gchar *start_invalid;
+
+ if (! g_utf8_validate (comment, -1, &start_invalid))
+ {
+ guchar *c;
+
+ for (c = (guchar *) start_invalid; *c; c++)
+ {
+ if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
+ *c = '?';
+ }
+ }
+}
+
+gint32
+load_thumbnail_image (GFile *file,
+ gint *width,
+ gint *height,
+ GimpImageType *type,
+ GError **error)
+{
+ gint32 volatile image_ID = -1;
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ FILE *infile = NULL;
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ g_file_get_parse_name (file));
+
+ image_ID = gimp_image_metadata_load_thumbnail (file, error);
+ if (image_ID < 1)
+ return -1;
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+ jerr.pub.output_message = my_output_message;
+
+ if ((infile = g_fopen (g_file_get_path (file), "rb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ g_file_get_parse_name (file), g_strerror (errno));
+
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+
+ return -1;
+ }
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error. We
+ * need to clean up the JPEG object, close the input file,
+ * and return.
+ */
+ jpeg_destroy_decompress (&cinfo);
+
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+
+ return -1;
+ }
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress (&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_stdio_src (&cinfo, infile);
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ jpeg_start_decompress (&cinfo);
+
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
+
+ switch (cinfo.output_components)
+ {
+ case 1:
+ *type = GIMP_GRAY_IMAGE;
+ break;
+
+ case 3:
+ *type = GIMP_RGB_IMAGE;
+ break;
+
+ case 4:
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ *type = GIMP_RGB_IMAGE;
+ break;
+ }
+ /*fallthrough*/
+
+ default:
+ g_message ("Don't know how to load JPEG images "
+ "with %d color channels, using colorspace %d (%d).",
+ cinfo.output_components, cinfo.out_color_space,
+ cinfo.jpeg_color_space);
+
+ gimp_image_delete (image_ID);
+ image_ID = -1;
+ break;
+ }
+
+ /* Step 4: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal
+ * of memory.
+ */
+ jpeg_destroy_decompress (&cinfo);
+
+ fclose (infile);
+
+ return image_ID;
+}
+
+static gpointer
+jpeg_load_cmyk_transform (guint8 *profile_data,
+ gsize profile_len)
+{
+ GimpColorConfig *config = gimp_get_color_configuration ();
+ GimpColorProfile *cmyk_profile = NULL;
+ GimpColorProfile *rgb_profile = NULL;
+ cmsHPROFILE cmyk_lcms;
+ cmsHPROFILE rgb_lcms;
+ cmsUInt32Number flags = 0;
+ cmsHTRANSFORM transform;
+
+ /* try to load the embedded CMYK profile */
+ if (profile_data)
+ {
+ cmyk_profile = gimp_color_profile_new_from_icc_profile (profile_data,
+ profile_len,
+ NULL);
+
+ if (cmyk_profile && ! gimp_color_profile_is_cmyk (cmyk_profile))
+ {
+ g_object_unref (cmyk_profile);
+ cmyk_profile = NULL;
+ }
+ }
+
+ /* if that fails, try to load the CMYK profile configured in the prefs */
+ if (! cmyk_profile)
+ cmyk_profile = gimp_color_config_get_cmyk_color_profile (config, NULL);
+
+ /* bail out if we can't load any CMYK profile */
+ if (! cmyk_profile)
+ {
+ g_object_unref (config);
+ return NULL;
+ }
+
+ /* always convert to sRGB */
+ rgb_profile = gimp_color_profile_new_rgb_srgb ();
+
+ cmyk_lcms = gimp_color_profile_get_lcms_profile (cmyk_profile);
+ rgb_lcms = gimp_color_profile_get_lcms_profile (rgb_profile);
+
+ if (config->display_intent ==
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC)
+ {
+ flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+ }
+
+ transform = cmsCreateTransform (cmyk_lcms, TYPE_CMYK_8_REV,
+ rgb_lcms, TYPE_RGB_8,
+ config->display_intent,
+ flags);
+
+ g_object_unref (cmyk_profile);
+ g_object_unref (rgb_profile);
+
+ g_object_unref (config);
+
+ return transform;
+}
+
+
+static void
+jpeg_load_cmyk_to_rgb (guchar *buf,
+ glong pixels,
+ gpointer transform)
+{
+ const guchar *src = buf;
+ guchar *dest = buf;
+
+ if (transform)
+ {
+ cmsDoTransform (transform, buf, buf, pixels);
+ return;
+ }
+
+ /* NOTE: The following code assumes inverted CMYK values, even when an
+ APP14 marker doesn't exist. This is the behavior of recent versions
+ of PhotoShop as well. */
+
+ while (pixels--)
+ {
+ guint c = src[0];
+ guint m = src[1];
+ guint y = src[2];
+ guint k = src[3];
+
+ dest[0] = (c * k) / 255;
+ dest[1] = (m * k) / 255;
+ dest[2] = (y * k) / 255;
+
+ src += 4;
+ dest += 3;
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
new file mode 100644
index 0000000..3844097
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_LOAD_H__
+#define __JPEG_LOAD_H__
+
+gint32 load_image (const gchar *filename,
+ GimpRunMode runmode,
+ gboolean preview,
+ gboolean *resolution_loaded,
+ GError **error);
+
+gint32 load_thumbnail_image (GFile *file,
+ gint *width,
+ gint *height,
+ GimpImageType *type,
+ GError **error);
+
+#endif /* __JPEG_LOAD_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-quality.c b/plug-ins/file-jpeg/jpeg-quality.c
new file mode 100644
index 0000000..52bd2c7
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-quality.c
@@ -0,0 +1,148 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-quality.c
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include "jpeg-quality.h"
+
+/*
+ * Note that although 0 is a valid quality for IJG's JPEG library, the
+ * baseline quantization tables for quality 0 and 1 are identical (all
+ * divisors are set to the maximum value 255). So 0 can be used here
+ * to flag unusual settings.
+ */
+
+/* sum of the luminance divisors for quality = 0..100 (IJG standard tables) */
+static gint std_luminance_sum[101] =
+{
+ G_MAXINT,
+ 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859, 12560, 12240,
+ 11861, 11456, 11081, 10714, 10360, 10027, 9679, 9368, 9056, 8680, 8331,
+ 7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, 5939, 5756, 5571,
+ 5421, 5240, 5086, 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166,
+ 4092, 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396, 3323,
+ 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727, 2657, 2583, 2509,
+ 2437, 2362, 2290, 2211, 2136, 2068, 1996, 1915, 1858, 1773, 1692,
+ 1620, 1552, 1477, 1398, 1326, 1251, 1179, 1109, 1031, 961, 884,
+ 814, 736, 667, 592, 518, 441, 369, 292, 221, 151, 86,
+ 64
+};
+
+/* sum of the chrominance divisors for quality = 0..100 (IJG standard tables) */
+static gint std_chrominance_sum[101] =
+{
+ G_MAXINT,
+ 16320, 16320, 16320, 16218, 16010, 15731, 15523, 15369, 15245, 15110, 14985,
+ 14864, 14754, 14635, 14526, 14429, 14346, 14267, 14204, 13790, 13121, 12511,
+ 11954, 11453, 11010, 10567, 10175, 9787, 9455, 9122, 8844, 8565, 8288,
+ 8114, 7841, 7616, 7447, 7227, 7060, 6897, 6672, 6562, 6396, 6226,
+ 6116, 5948, 5838, 5729, 5614, 5505, 5396, 5281, 5172, 5062, 4947,
+ 4837, 4726, 4614, 4506, 4395, 4282, 4173, 4061, 3950, 3839, 3727,
+ 3617, 3505, 3394, 3284, 3169, 3060, 2949, 2836, 2780, 2669, 2556,
+ 2445, 2336, 2221, 2111, 2000, 1888, 1778, 1666, 1555, 1444, 1332,
+ 1223, 1110, 999, 891, 779, 668, 558, 443, 333, 224, 115,
+ 64
+};
+
+/**
+ * jpeg_detect_quality:
+ * @cinfo: a pointer to a JPEG decompressor info.
+ *
+ * Returns the exact or estimated quality value that was used to save
+ * the JPEG image by analyzing the quantization table divisors.
+ *
+ * If an exact match for the IJG quantization tables is found, then a
+ * quality setting in the range 1..100 is returned. If the quality
+ * can only be estimated, then a negative number in the range -1..-100
+ * is returned; its absolute value represents the maximum IJG quality
+ * setting to use. If the quality cannot be reliably determined, then
+ * 0 is returned.
+ *
+ * This function must be called after jpeg_read_header() so that
+ * @cinfo contains the quantization tables read from the DQT markers
+ * in the file.
+ *
+ * Return Value: the JPEG quality setting in the range 1..100, -1..-100 or 0.
+ */
+gint
+jpeg_detect_quality (struct jpeg_decompress_struct *cinfo)
+{
+ gint t;
+ gint i;
+ gint sum[3];
+ gint q;
+
+ /* files using CMYK or having 4 quantization tables are unusual */
+ if (!cinfo || cinfo->output_components > 3 || cinfo->quant_tbl_ptrs[3])
+ return 0;
+
+ /* Most files use table 0 for luminance divisors (Y) and table 1 for
+ * chrominance divisors (Cb and Cr). Some files use separate tables
+ * for Cb and Cr, so table 2 may also be used.
+ */
+ for (t = 0; t < 3; t++)
+ {
+ sum[t] = 0;
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ sum[t] += cinfo->quant_tbl_ptrs[t]->quantval[i];
+ }
+
+ if (cinfo->output_components > 1)
+ {
+ gint sums;
+
+ if (sum[0] < 64 || sum[1] < 64)
+ return 0;
+
+ /* compare with the chrominance table having the lowest sum */
+ if (sum[1] < sum[2] || sum[2] <= 0)
+ sums = sum[0] + sum[1];
+ else
+ sums = sum[0] + sum[2];
+
+ q = 100;
+ while (sums > std_luminance_sum[q] + std_chrominance_sum[q])
+ q--;
+
+ if (sum[0] == std_luminance_sum[q] && sum[1] == std_chrominance_sum[q])
+ return q;
+ else
+ return -q;
+ }
+ else
+ {
+ if (sum[0] < 64)
+ return 0;
+
+ q = 100;
+ while (sum[0] > std_luminance_sum[q])
+ q--;
+
+ if (sum[0] == std_luminance_sum[q])
+ return q;
+ else
+ return -q;
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-quality.h b/plug-ins/file-jpeg/jpeg-quality.h
new file mode 100644
index 0000000..d20600b
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-quality.h
@@ -0,0 +1,26 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-quality.h
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_QUALITY_H__
+#define __JPEG_QUALITY_H__
+
+gint jpeg_detect_quality (struct jpeg_decompress_struct *cinfo);
+
+#endif /* __JPEG_QUALITY_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c
new file mode 100644
index 0000000..4af4d1d
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-save.c
@@ -0,0 +1,1524 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-icc.h"
+#include "jpeg-load.h"
+#include "jpeg-save.h"
+#include "jpeg-settings.h"
+
+#ifdef C_ARITH_CODING_SUPPORTED
+static gboolean arithc_supported = TRUE;
+#else
+static gboolean arithc_supported = FALSE;
+#endif
+
+#define SCALE_WIDTH 125
+
+/* See bugs #63610 and #61088 for a discussion about the quality settings */
+#define DEFAULT_IJG_QUALITY 90.0
+#define DEFAULT_SMOOTHING 0.0
+#define DEFAULT_OPTIMIZE TRUE
+#define DEFAULT_ARITHMETIC_CODING FALSE
+#define DEFAULT_PROGRESSIVE TRUE
+#define DEFAULT_BASELINE TRUE
+#define DEFAULT_SUBSMP JPEG_SUBSAMPLING_1x1_1x1_1x1
+#define DEFAULT_RESTART 0
+#define DEFAULT_RESTART_MCU_ROWS 16
+#define DEFAULT_DCT 0
+#define DEFAULT_PREVIEW FALSE
+#define DEFAULT_EXIF FALSE
+#define DEFAULT_XMP FALSE
+#define DEFAULT_IPTC FALSE
+#define DEFAULT_THUMBNAIL FALSE
+#define DEFAULT_PROFILE TRUE
+#define DEFAULT_USE_ORIG_QUALITY FALSE
+
+#define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults"
+
+
+typedef struct
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ gint tile_height;
+ FILE *outfile;
+ gboolean has_alpha;
+ gint rowstride;
+ guchar *data;
+ guchar *src;
+ GeglBuffer *buffer;
+ const Babl *format;
+ const gchar *file_name;
+ gboolean abort_me;
+ guint source_id;
+} PreviewPersistent;
+
+/*le added : struct containing pointers to export dialog*/
+typedef struct
+{
+ gboolean run;
+ GtkWidget *use_restart_markers; /*checkbox setting use restart markers*/
+ GtkTextBuffer *text_buffer;
+ GtkAdjustment *scale_data; /*for restart markers*/
+ gulong handler_id_restart;
+
+ GtkAdjustment *quality; /*quality slidebar*/
+ GtkAdjustment *smoothing; /*smoothing slidebar*/
+ GtkWidget *optimize; /*optimize toggle*/
+ GtkWidget *arithmetic_coding; /*arithmetic coding toggle*/
+ GtkWidget *progressive; /*progressive toggle*/
+ GtkWidget *subsmp; /*subsampling side select*/
+ GtkWidget *restart; /*spinner for setting frequency restart markers*/
+ GtkWidget *dct; /*DCT side select*/
+ GtkWidget *preview; /*show preview toggle checkbox*/
+ GtkWidget *save_exif;
+ GtkWidget *save_xmp;
+ GtkWidget *save_iptc;
+ GtkWidget *save_thumbnail;
+ GtkWidget *save_profile;
+ GtkWidget *use_orig_quality; /*quant tables toggle*/
+} JpegSaveGui;
+
+static void make_preview (void);
+
+static void save_restart_update (GtkAdjustment *adjustment,
+ GtkWidget *toggle);
+static void subsampling_changed (GtkWidget *combo,
+ GtkAdjustment *entry);
+static void quality_changed (GtkAdjustment *scale_entry,
+ GtkWidget *toggle);
+static void subsampling_changed2 (GtkWidget *combo,
+ GtkWidget *toggle);
+static void use_orig_qual_changed (GtkWidget *toggle,
+ GtkAdjustment *scale_entry);
+static void use_orig_qual_changed2 (GtkWidget *toggle,
+ GtkWidget *combo);
+
+
+static GtkWidget *restart_markers_scale = NULL;
+static GtkWidget *restart_markers_label = NULL;
+static GtkWidget *preview_size = NULL;
+static PreviewPersistent *prev_p = NULL;
+
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+
+static void load_gui_defaults (JpegSaveGui *pg);
+static void save_defaults (void);
+
+
+/*
+ * sg - This is the best I can do, I'm afraid... I think it will fail
+ * if something bad really happens (but it might not). If you have a
+ * better solution, send it ;-)
+ */
+static void
+background_error_exit (j_common_ptr cinfo)
+{
+ if (prev_p)
+ prev_p->abort_me = TRUE;
+ (*cinfo->err->output_message) (cinfo);
+}
+
+static gboolean
+background_jpeg_save (PreviewPersistent *pp)
+{
+ gint yend;
+
+ if (pp->abort_me || (pp->cinfo.next_scanline >= pp->cinfo.image_height))
+ {
+ /* clean up... */
+ if (pp->abort_me)
+ {
+ jpeg_abort_compress (&(pp->cinfo));
+ }
+ else
+ {
+ jpeg_finish_compress (&(pp->cinfo));
+ }
+
+ fclose (pp->outfile);
+ jpeg_destroy_compress (&(pp->cinfo));
+
+ g_free (pp->data);
+
+ if (pp->buffer)
+ g_object_unref (pp->buffer);
+
+ /* display the preview stuff */
+ if (!pp->abort_me)
+ {
+ GFile *file = g_file_new_for_path (pp->file_name);
+ GFileInfo *info;
+ gchar *text;
+ GError *error = NULL;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+
+ if (info)
+ {
+ goffset size = g_file_info_get_size (info);
+ gchar *size_text;
+
+ size_text = g_format_size (size);
+ text = g_strdup_printf (_("File size: %s"), size_text);
+ g_free (size_text);
+
+ g_object_unref (info);
+ }
+ else
+ {
+ text = g_strdup_printf (_("File size: %s"), error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_label_set_text (GTK_LABEL (preview_size), text);
+ g_free (text);
+
+ g_object_unref (file);
+
+ /* and load the preview */
+ load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL);
+ }
+
+ /* we cleanup here (load_image doesn't run in the background) */
+ g_unlink (pp->file_name);
+
+ g_free (pp);
+ prev_p = NULL;
+
+ gimp_displays_flush ();
+ gdk_flush ();
+
+ return FALSE;
+ }
+ else
+ {
+ if ((pp->cinfo.next_scanline % pp->tile_height) == 0)
+ {
+ yend = pp->cinfo.next_scanline + pp->tile_height;
+ yend = MIN (yend, pp->cinfo.image_height);
+ gegl_buffer_get (pp->buffer,
+ GEGL_RECTANGLE (0, pp->cinfo.next_scanline,
+ pp->cinfo.image_width,
+ (yend - pp->cinfo.next_scanline)),
+ 1.0,
+ pp->format,
+ pp->data,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ pp->src = pp->data;
+ }
+
+ jpeg_write_scanlines (&(pp->cinfo), (JSAMPARRAY) &(pp->src), 1);
+ pp->src += pp->rowstride;
+
+ return TRUE;
+ }
+}
+
+gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ gboolean preview,
+ GError **error)
+{
+ static struct jpeg_compress_struct cinfo;
+ static struct my_error_mgr jerr;
+
+ GimpImageType drawable_type;
+ GeglBuffer *buffer;
+ const Babl *format;
+ JpegSubsampling subsampling;
+ FILE * volatile outfile;
+ guchar *data;
+ guchar *src;
+ GimpColorProfile *profile = NULL;
+
+ gboolean has_alpha;
+ gboolean out_linear = FALSE;
+ gint rowstride, yend;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ if (! preview)
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Step 1: allocate and initialize JPEG compression object */
+
+ /* We have to set up the error handler first, in case the initialization
+ * step fails. (Unlikely, but it could happen if you are out of memory.)
+ * This routine fills in the contents of struct jerr, and returns jerr's
+ * address which we place into the link field in cinfo.
+ */
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+
+ outfile = NULL;
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_compress (&cinfo);
+ if (outfile)
+ fclose (outfile);
+ if (buffer)
+ g_object_unref (buffer);
+
+ return FALSE;
+ }
+
+ /* Now we can initialize the JPEG compression object. */
+ jpeg_create_compress (&cinfo);
+
+ /* Step 2: specify data destination (eg, a file) */
+ /* Note: steps 2 and 3 can be done in either order. */
+
+ /* Here we use the library-supplied code to send compressed data to a
+ * stdio stream. You can also write your own code to do something else.
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+ * requires it in order to write binary files.
+ */
+ if ((outfile = g_fopen (filename, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ /* When we don't save profiles, we convert data to sRGB because
+ * that's what most/all readers expect on a no-profile JPEG.
+ * If we save an assigned profile, let's just follow its TRC.
+ * If we save the default linear profile (i.e. no assigned
+ * profile), we convert it to sRGB, except when it is 8-bit linear.
+ */
+ if (jsvals.save_profile)
+ {
+ profile = gimp_image_get_color_profile (orig_image_ID);
+
+ /* If a profile is explicitly set, follow its TRC, whatever the
+ * storage format.
+ */
+ if (profile && gimp_color_profile_is_linear (profile))
+ out_linear = TRUE;
+
+ if (! profile)
+ {
+ /* There is always an effective profile. */
+ profile = gimp_image_get_effective_color_profile (orig_image_ID);
+
+ if (gimp_color_profile_is_linear (profile))
+ {
+ if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR)
+ {
+ GimpColorProfile *saved_profile;
+
+ saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile);
+ g_object_unref (profile);
+ profile = saved_profile;
+ }
+ else
+ {
+ /* Keep linear profile as-is for 8-bit linear image. */
+ out_linear = TRUE;
+ }
+ }
+ }
+ }
+
+ jpeg_stdio_dest (&cinfo, outfile);
+
+ /* Get the input image and a pointer to its data.
+ */
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ /* # of color components per pixel */
+ cinfo.input_components = 3;
+ has_alpha = FALSE;
+
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ /* # of color components per pixel */
+ cinfo.input_components = 1;
+ has_alpha = FALSE;
+
+ if (out_linear)
+ format = babl_format ("Y u8");
+ else
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ /* # of color components per pixel (minus the GIMP alpha channel) */
+ cinfo.input_components = 4 - 1;
+ has_alpha = TRUE;
+
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ /* # of color components per pixel (minus the GIMP alpha channel) */
+ cinfo.input_components = 2 - 1;
+ has_alpha = TRUE;
+ if (out_linear)
+ format = babl_format ("Y u8");
+ else
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ default:
+ return FALSE;
+ }
+
+ /* Step 3: set parameters for compression */
+
+ /* First we supply a description of the input image.
+ * Four fields of the cinfo struct must be filled in:
+ */
+ /* image width and height, in pixels */
+ cinfo.image_width = gegl_buffer_get_width (buffer);
+ cinfo.image_height = gegl_buffer_get_height (buffer);
+ /* colorspace of input image */
+ cinfo.in_color_space = (drawable_type == GIMP_RGB_IMAGE ||
+ drawable_type == GIMP_RGBA_IMAGE)
+ ? JCS_RGB : JCS_GRAYSCALE;
+ /* Now use the library's routine to set default compression parameters.
+ * (You must set at least cinfo.in_color_space before calling this,
+ * since the defaults depend on the source color space.)
+ */
+ jpeg_set_defaults (&cinfo);
+
+ jpeg_set_quality (&cinfo, (gint) (jsvals.quality + 0.5), jsvals.baseline);
+
+ if (jsvals.use_orig_quality && num_quant_tables > 0)
+ {
+ guint **quant_tables;
+ gint t;
+
+ /* override tables generated by jpeg_set_quality() with custom tables */
+ quant_tables = jpeg_restore_original_tables (image_ID, num_quant_tables);
+ if (quant_tables)
+ {
+ for (t = 0; t < num_quant_tables; t++)
+ {
+ jpeg_add_quant_table (&cinfo, t, quant_tables[t],
+ 100, jsvals.baseline);
+ g_free (quant_tables[t]);
+ }
+ g_free (quant_tables);
+ }
+ }
+
+ if (arithc_supported)
+ {
+ cinfo.arith_code = jsvals.arithmetic_coding;
+ if (!jsvals.arithmetic_coding)
+ cinfo.optimize_coding = jsvals.optimize;
+ }
+ else
+ cinfo.optimize_coding = jsvals.optimize;
+
+ subsampling = (gimp_drawable_is_rgb (drawable_ID) ?
+ jsvals.subsmp : JPEG_SUBSAMPLING_1x1_1x1_1x1);
+
+ /* smoothing is not supported with nonstandard sampling ratios */
+ if (subsampling != JPEG_SUBSAMPLING_2x1_1x1_1x1 &&
+ subsampling != JPEG_SUBSAMPLING_1x2_1x1_1x1)
+ {
+ cinfo.smoothing_factor = (gint) (jsvals.smoothing * 100);
+ }
+
+ if (jsvals.progressive)
+ {
+ jpeg_simple_progression (&cinfo);
+ }
+
+ switch (subsampling)
+ {
+ case JPEG_SUBSAMPLING_2x2_1x1_1x1:
+ default:
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_2x1_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_1x1_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_1x2_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+ }
+
+ cinfo.restart_interval = 0;
+ cinfo.restart_in_rows = jsvals.restart;
+
+ switch (jsvals.dct)
+ {
+ case 0:
+ default:
+ cinfo.dct_method = JDCT_ISLOW;
+ break;
+
+ case 1:
+ cinfo.dct_method = JDCT_IFAST;
+ break;
+
+ case 2:
+ cinfo.dct_method = JDCT_FLOAT;
+ break;
+ }
+
+ {
+ gdouble xresolution;
+ gdouble yresolution;
+
+ gimp_image_get_resolution (orig_image_ID, &xresolution, &yresolution);
+
+ if (xresolution > 1e-5 && yresolution > 1e-5)
+ {
+ gdouble factor;
+
+ factor = gimp_unit_get_factor (gimp_image_get_unit (orig_image_ID));
+
+ if (factor == 2.54 /* cm */ ||
+ factor == 25.4 /* mm */)
+ {
+ cinfo.density_unit = 2; /* dots per cm */
+
+ xresolution /= 2.54;
+ yresolution /= 2.54;
+ }
+ else
+ {
+ cinfo.density_unit = 1; /* dots per inch */
+ }
+
+ cinfo.X_density = xresolution;
+ cinfo.Y_density = yresolution;
+ }
+ }
+
+ /* Step 4: Start compressor */
+
+ /* TRUE ensures that we will write a complete interchange-JPEG file.
+ * Pass TRUE unless you are very sure of what you're doing.
+ */
+ jpeg_start_compress (&cinfo, TRUE);
+
+ /* Step 4.1: Write the comment out - pw */
+ if (image_comment && *image_comment)
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-save: saving image comment (%d bytes)\n",
+ (int) strlen (image_comment));
+#endif
+ jpeg_write_marker (&cinfo, JPEG_COM,
+ (guchar *) image_comment, strlen (image_comment));
+ }
+
+ /* Step 4.2: store the color profile */
+ if (jsvals.save_profile)
+ {
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ jpeg_icc_write_profile (&cinfo, icc_data, icc_length);
+
+ g_object_unref (profile);
+ }
+
+ /* Step 5: while (scan lines remain to be written) */
+ /* jpeg_write_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.next_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ * To keep things simple, we pass one scanline per call; you can pass
+ * more if you wish, though.
+ */
+ /* JSAMPLEs per row in image_buffer */
+ rowstride = cinfo.input_components * cinfo.image_width;
+ data = g_new (guchar, rowstride * gimp_tile_height ());
+
+ /* fault if cinfo.next_scanline isn't initially a multiple of
+ * gimp_tile_height */
+ src = NULL;
+
+ /*
+ * sg - if we preview, we want this to happen in the background -- do
+ * not duplicate code in the future; for now, it's OK
+ */
+
+ if (preview)
+ {
+ PreviewPersistent *pp = g_new (PreviewPersistent, 1);
+
+ /* pass all the information we need */
+ pp->cinfo = cinfo;
+ pp->tile_height = gimp_tile_height();
+ pp->data = data;
+ pp->outfile = outfile;
+ pp->has_alpha = has_alpha;
+ pp->rowstride = rowstride;
+ pp->data = data;
+ pp->buffer = buffer;
+ pp->format = format;
+ pp->src = NULL;
+ pp->file_name = filename;
+ pp->abort_me = FALSE;
+
+ g_warn_if_fail (prev_p == NULL);
+ prev_p = pp;
+
+ pp->cinfo.err = jpeg_std_error(&(pp->jerr));
+ pp->jerr.error_exit = background_error_exit;
+
+ gtk_label_set_text (GTK_LABEL (preview_size),
+ _("Calculating file size..."));
+
+ pp->source_id = g_idle_add ((GSourceFunc) background_jpeg_save, pp);
+
+ /* background_jpeg_save() will cleanup as needed */
+ return TRUE;
+ }
+
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ if ((cinfo.next_scanline % gimp_tile_height ()) == 0)
+ {
+ yend = cinfo.next_scanline + gimp_tile_height ();
+ yend = MIN (yend, cinfo.image_height);
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, cinfo.next_scanline,
+ cinfo.image_width,
+ (yend - cinfo.next_scanline)),
+ 1.0,
+ format,
+ data,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ src = data;
+ }
+
+ jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &src, 1);
+ src += rowstride;
+
+ if ((cinfo.next_scanline % 32) == 0)
+ gimp_progress_update ((gdouble) cinfo.next_scanline /
+ (gdouble) cinfo.image_height);
+ }
+
+ /* Step 6: Finish compression */
+ jpeg_finish_compress (&cinfo);
+ /* After finish_compress, we can close the output file. */
+ fclose (outfile);
+
+ /* Step 7: release JPEG compression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_compress (&cinfo);
+
+ /* free the temporary buffer */
+ g_free (data);
+
+ /* And we're done! */
+ gimp_progress_update (1.0);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+}
+
+static void
+make_preview (void)
+{
+ destroy_preview ();
+
+ if (jsvals.preview)
+ {
+ gchar *tn = gimp_temp_name ("jpeg");
+
+ if (! undo_touched)
+ {
+ /* we freeze undo saving so that we can avoid sucking up
+ * tile cache with our unneeded preview steps. */
+ gimp_image_undo_freeze (preview_image_ID);
+
+ undo_touched = TRUE;
+ }
+
+ save_image (tn,
+ preview_image_ID,
+ drawable_ID_global,
+ orig_image_ID_global,
+ TRUE, NULL);
+
+ if (display_ID == -1)
+ display_ID = gimp_display_new (preview_image_ID);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (preview_size), _("File size: unknown"));
+
+ gimp_displays_flush ();
+ }
+}
+
+void
+destroy_preview (void)
+{
+ if (prev_p && !prev_p->abort_me)
+ {
+ guint id = prev_p->source_id;
+ prev_p->abort_me = TRUE; /* signal the background save to stop */
+ background_jpeg_save (prev_p);
+ g_source_remove (id);
+ }
+
+ if (gimp_image_is_valid (preview_image_ID) &&
+ gimp_item_is_valid (preview_layer_ID))
+ {
+ /* assuming that reference counting is working correctly,
+ we do not need to delete the layer, removing it from
+ the image should be sufficient */
+ gimp_image_remove_layer (preview_image_ID, preview_layer_ID);
+
+ preview_layer_ID = -1;
+ }
+}
+
+static void
+toggle_arithmetic_coding (GtkToggleButton *togglebutton,
+ gpointer user_data)
+{
+ GtkWidget *optimize = GTK_WIDGET (user_data);
+
+ gtk_widget_set_sensitive (optimize,
+ !gtk_toggle_button_get_active (togglebutton));
+}
+
+gboolean
+save_dialog (void)
+{
+ JpegSaveGui pg;
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkAdjustment *entry;
+ GtkWidget *table;
+ GtkWidget *table2;
+ GtkWidget *tabledefaults;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *text_view;
+ GtkTextBuffer *text_buffer;
+ GtkWidget *scrolled_window;
+ GtkWidget *button;
+ gchar *text;
+ gint row;
+
+ dialog = gimp_export_dialog_new (_("JPEG"), PLUG_IN_BINARY, SAVE_PROC);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_dialog_response),
+ &pg);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ pg.quality = entry = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Quality:"),
+ SCALE_WIDTH, 0, jsvals.quality,
+ 0.0, 100.0, 1.0, 10.0, 0,
+ TRUE, 0.0, 0.0,
+ _("JPEG quality parameter"),
+ "file-jpeg-save-quality");
+
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &jsvals.quality);
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* custom quantization tables - now used also for original quality */
+ pg.use_orig_quality = toggle =
+ gtk_check_button_new_with_mnemonic (_("_Use quality settings from original "
+ "image"));
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("If the original image was loaded from a JPEG "
+ "file using non-standard quality settings "
+ "(quantization tables), enable this option to "
+ "get almost the same quality and file size."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.use_orig_quality);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.use_orig_quality
+ && (orig_quality > 0)
+ && (orig_subsmp == jsvals.subsmp)
+ );
+ gtk_widget_set_sensitive (toggle, (orig_quality > 0));
+
+ /* changing quality disables custom quantization tables, and vice-versa */
+ g_signal_connect (pg.quality, "value-changed",
+ G_CALLBACK (quality_changed),
+ pg.use_orig_quality);
+ g_signal_connect (pg.use_orig_quality, "toggled",
+ G_CALLBACK (use_orig_qual_changed),
+ pg.quality);
+
+ /* File size */
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ preview_size = gtk_label_new (_("File size: unknown"));
+ gtk_label_set_xalign (GTK_LABEL (preview_size), 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (preview_size), PANGO_ELLIPSIZE_END);
+ gimp_label_set_attributes (GTK_LABEL (preview_size),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox2), preview_size, FALSE, FALSE, 0);
+ gtk_widget_show (preview_size);
+
+ gimp_help_set_help_data (preview_size,
+ _("Enable preview to obtain the file size."), NULL);
+
+ pg.preview = toggle =
+ gtk_check_button_new_with_mnemonic (_("Sho_w preview in image window"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.preview);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.preview);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ /* Save EXIF data */
+ pg.save_exif = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _Exif data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_exif);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_exif);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save XMP metadata */
+ pg.save_xmp = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _XMP data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_xmp);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_xmp);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save IPTC metadata */
+ pg.save_iptc = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _IPTC data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_iptc);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_iptc);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save thumbnail */
+ pg.save_thumbnail = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _thumbnail"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_thumbnail);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_thumbnail);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save color profile */
+ pg.save_profile = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save color _profile"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_profile);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_profile);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Comment */
+ frame = gimp_frame_new (_("Comment"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request (scrolled_window, 250, 50);
+ gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
+ gtk_widget_show (scrolled_window);
+
+ pg.text_buffer = text_buffer = gtk_text_buffer_new (NULL);
+ if (image_comment)
+ gtk_text_buffer_set_text (text_buffer, image_comment, -1);
+
+ text_view = gtk_text_view_new_with_buffer (text_buffer);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
+ gtk_widget_show (text_view);
+
+ g_object_unref (text_buffer);
+
+ /* Advanced expander */
+ text = g_strdup_printf ("<b>%s</b>", _("_Advanced Options"));
+ expander = gtk_expander_new_with_mnemonic (text);
+ gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE);
+ g_free (text);
+
+ gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
+ gtk_widget_show (expander);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (expander), vbox);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new ("<expander>");
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (4, 8, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ table2 = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table2), 6);
+ gtk_table_attach (GTK_TABLE (table), table2,
+ 2, 6, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (table2);
+
+ pg.smoothing = entry = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 0,
+ _("S_moothing:"),
+ 100, 0, jsvals.smoothing,
+ 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0.0, 0.0,
+ NULL,
+ "file-jpeg-save-smoothing");
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &jsvals.smoothing);
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ restart_markers_label = gtk_label_new (_("Interval (MCU rows):"));
+ gtk_label_set_xalign (GTK_LABEL (restart_markers_label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), restart_markers_label, 4, 5, 1, 2,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (restart_markers_label);
+
+ pg.scale_data = (GtkAdjustment *)
+ gtk_adjustment_new (((jsvals.restart == 0) ?
+ DEFAULT_RESTART_MCU_ROWS : jsvals.restart),
+ 1.0, 64.0, 1.0, 1.0, 0);
+ pg.restart = restart_markers_scale = spinbutton =
+ gimp_spin_button_new (pg.scale_data, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 5, 6, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ pg.use_restart_markers = toggle =
+ gtk_check_button_new_with_mnemonic (_("Use _restart markers"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 2, 4, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.restart);
+
+ gtk_widget_set_sensitive (restart_markers_label, jsvals.restart);
+ gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart);
+
+ g_signal_connect (pg.scale_data, "value-changed",
+ G_CALLBACK (save_restart_update),
+ toggle);
+ pg.handler_id_restart = g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (save_restart_update),
+ pg.scale_data);
+
+ row = 0;
+
+ /* Optimize */
+ pg.optimize = toggle = gtk_check_button_new_with_mnemonic (_("_Optimize"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.optimize);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.optimize);
+
+ if (arithc_supported)
+ gtk_widget_set_sensitive (toggle, !jsvals.arithmetic_coding);
+
+ row++;
+
+ if (arithc_supported)
+ {
+ /* Arithmetic coding */
+ pg.arithmetic_coding = toggle = gtk_check_button_new_with_mnemonic
+ (_("Use arithmetic _coding"));
+ gtk_widget_set_tooltip_text
+ (toggle, _("Older software may have trouble opening "
+ "arithmetic-coded images"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.arithmetic_coding);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_arithmetic_coding),
+ pg.optimize);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.arithmetic_coding);
+
+ row++;
+ }
+
+ /* Progressive */
+ pg.progressive = toggle =
+ gtk_check_button_new_with_mnemonic (_("_Progressive"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.progressive);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.progressive);
+
+ row++;
+
+ /* Subsampling */
+ label = gtk_label_new_with_mnemonic (_("Su_bsampling:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ pg.subsmp =
+ combo = gimp_int_combo_box_new (_("4:4:4 (best quality)"),
+ JPEG_SUBSAMPLING_1x1_1x1_1x1,
+ _("4:2:2 horizontal (chroma halved)"),
+ JPEG_SUBSAMPLING_2x1_1x1_1x1,
+ _("4:2:2 vertical (chroma halved)"),
+ JPEG_SUBSAMPLING_1x2_1x1_1x1,
+ _("4:2:0 (chroma quartered)"),
+ JPEG_SUBSAMPLING_2x2_1x1_1x1,
+ NULL);
+ gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 2, 3,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ if (gimp_drawable_is_rgb (drawable_ID_global))
+ {
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ jsvals.subsmp,
+ G_CALLBACK (subsampling_changed),
+ entry);
+ g_signal_connect (pg.subsmp, "changed",
+ G_CALLBACK (subsampling_changed2),
+ pg.use_orig_quality);
+ g_signal_connect (pg.use_orig_quality, "toggled",
+ G_CALLBACK (use_orig_qual_changed2),
+ pg.subsmp);
+ }
+ else
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ JPEG_SUBSAMPLING_1x1_1x1_1x1);
+
+ gtk_widget_set_sensitive (combo, FALSE);
+ }
+
+
+ /* DCT method */
+ label = gtk_label_new_with_mnemonic (_("_DCT method:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ pg.dct = combo = gimp_int_combo_box_new (_("Fast Integer"), 1,
+ _("Integer"), 0,
+ _("Floating-Point"), 2,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), jsvals.dct);
+ gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 3, 4,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &jsvals.dct);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Load/Save defaults */
+ tabledefaults = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (tabledefaults), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (tabledefaults), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ tabledefaults, FALSE, FALSE, 0);
+ gtk_widget_show (tabledefaults);
+
+ button = gtk_button_new_with_mnemonic (_("_Load Defaults"));
+ gtk_table_attach (GTK_TABLE (tabledefaults),
+ button, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (load_gui_defaults),
+ &pg);
+
+ button = gtk_button_new_with_mnemonic (_("Sa_ve Defaults"));
+ gtk_table_attach (GTK_TABLE (tabledefaults),
+ button, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (save_defaults),
+ &pg);
+
+ gtk_widget_show (dialog);
+
+ make_preview ();
+
+ pg.run = FALSE;
+
+ gtk_main ();
+
+ destroy_preview ();
+
+ return pg.run;
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ JpegSaveGui *pg = data;
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ gtk_text_buffer_get_bounds (pg->text_buffer, &start_iter, &end_iter);
+ image_comment = gtk_text_buffer_get_text (pg->text_buffer,
+ &start_iter, &end_iter, FALSE);
+ pg->run = TRUE;
+ /* fallthrough */
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+void
+load_defaults (void)
+{
+ jsvals.quality = DEFAULT_IJG_QUALITY;
+ jsvals.smoothing = DEFAULT_SMOOTHING;
+ jsvals.optimize = DEFAULT_OPTIMIZE;
+ jsvals.arithmetic_coding= DEFAULT_ARITHMETIC_CODING;
+ jsvals.progressive = DEFAULT_PROGRESSIVE;
+ jsvals.baseline = DEFAULT_BASELINE;
+ jsvals.subsmp = DEFAULT_SUBSMP;
+ jsvals.restart = DEFAULT_RESTART;
+ jsvals.dct = DEFAULT_DCT;
+ jsvals.preview = DEFAULT_PREVIEW;
+ jsvals.save_exif = DEFAULT_EXIF;
+ jsvals.save_xmp = DEFAULT_XMP;
+ jsvals.save_iptc = DEFAULT_IPTC;
+ jsvals.save_thumbnail = DEFAULT_THUMBNAIL;
+ jsvals.save_profile = DEFAULT_PROFILE;
+ jsvals.use_orig_quality = DEFAULT_USE_ORIG_QUALITY;
+}
+
+void
+load_parasite (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+ JpegSaveVals tmpvals;
+ gint num_fields;
+ gint subsampling;
+
+ parasite = gimp_get_parasite (JPEG_DEFAULTS_PARASITE);
+
+ if (! parasite)
+ return;
+
+ def_str = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+
+ gimp_parasite_free (parasite);
+
+ /* Initialize tmpvals in case fewer fields exist in the parasite
+ (e.g., when importing from a previous version of GIMP). */
+ memcpy(&tmpvals, &jsvals, sizeof jsvals);
+
+ num_fields = sscanf (def_str,
+ "%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ &tmpvals.quality,
+ &tmpvals.smoothing,
+ &tmpvals.optimize,
+ &tmpvals.progressive,
+ &subsampling,
+ &tmpvals.baseline,
+ &tmpvals.restart,
+ &tmpvals.dct,
+ &tmpvals.preview,
+ &tmpvals.save_exif,
+ &tmpvals.save_thumbnail,
+ &tmpvals.save_xmp,
+ &tmpvals.use_orig_quality,
+ &tmpvals.save_iptc,
+ &tmpvals.arithmetic_coding,
+ &tmpvals.save_profile);
+
+ tmpvals.subsmp = subsampling;
+
+ if (num_fields == 13 || num_fields == 15 || num_fields == 16)
+ {
+ memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
+ }
+
+ g_free (def_str);
+}
+
+static void
+save_defaults (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+
+ def_str = g_strdup_printf ("%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ jsvals.quality,
+ jsvals.smoothing,
+ jsvals.optimize,
+ jsvals.progressive,
+ (gint) jsvals.subsmp,
+ jsvals.baseline,
+ jsvals.restart,
+ jsvals.dct,
+ jsvals.preview,
+ jsvals.save_exif,
+ jsvals.save_thumbnail,
+ jsvals.save_xmp,
+ jsvals.use_orig_quality,
+ jsvals.save_iptc,
+ jsvals.arithmetic_coding,
+ jsvals.save_profile);
+ parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE,
+ GIMP_PARASITE_PERSISTENT,
+ strlen (def_str), def_str);
+
+ gimp_attach_parasite (parasite);
+
+ gimp_parasite_free (parasite);
+ g_free (def_str);
+}
+
+static void
+load_gui_defaults (JpegSaveGui *pg)
+{
+ GtkAdjustment *restart_markers;
+
+ load_defaults ();
+ load_parasite ();
+
+#define SET_ACTIVE_BTTN(field) \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->field), jsvals.field)
+
+ SET_ACTIVE_BTTN (optimize);
+ SET_ACTIVE_BTTN (progressive);
+ SET_ACTIVE_BTTN (use_orig_quality);
+ SET_ACTIVE_BTTN (preview);
+ SET_ACTIVE_BTTN (save_exif);
+ SET_ACTIVE_BTTN (save_xmp);
+ SET_ACTIVE_BTTN (save_iptc);
+ SET_ACTIVE_BTTN (save_thumbnail);
+ SET_ACTIVE_BTTN (save_profile);
+
+#undef SET_ACTIVE_BTTN
+
+/*spin button stuff*/
+ g_signal_handler_block (pg->use_restart_markers, pg->handler_id_restart);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->use_restart_markers),
+ jsvals.restart);
+ restart_markers = GTK_ADJUSTMENT (pg->scale_data);
+ gtk_adjustment_set_value (restart_markers, jsvals.restart);
+ g_signal_handler_unblock (pg->use_restart_markers, pg->handler_id_restart);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->smoothing),
+ jsvals.smoothing);
+
+ /* Don't override quality and subsampling setting if we already set it from original */
+ if (!jsvals.use_orig_quality)
+ {
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->quality),
+ jsvals.quality);
+
+ if (gimp_drawable_is_rgb (drawable_ID_global))
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->subsmp),
+ jsvals.subsmp);
+ }
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->dct),
+ jsvals.dct);
+}
+
+static void
+save_restart_update (GtkAdjustment *adjustment,
+ GtkWidget *toggle)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
+ jsvals.restart = gtk_adjustment_get_value (adjustment);
+ else
+ jsvals.restart = 0;
+
+ gtk_widget_set_sensitive (restart_markers_label, jsvals.restart);
+ gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart);
+
+ make_preview ();
+}
+
+static void
+subsampling_changed (GtkWidget *combo,
+ GtkAdjustment *entry)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value);
+
+ jsvals.subsmp = value;
+
+ /* smoothing is not supported with nonstandard sampling ratios */
+ gimp_scale_entry_set_sensitive ((gpointer) entry,
+ jsvals.subsmp != JPEG_SUBSAMPLING_2x1_1x1_1x1 &&
+ jsvals.subsmp != JPEG_SUBSAMPLING_1x2_1x1_1x1);
+
+ make_preview ();
+}
+
+static void
+quality_changed (GtkAdjustment *scale_entry,
+ GtkWidget *toggle)
+{
+ if (jsvals.use_orig_quality)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+}
+
+static void
+subsampling_changed2 (GtkWidget *combo,
+ GtkWidget *toggle)
+{
+ if (jsvals.use_orig_quality && orig_subsmp != jsvals.subsmp)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+}
+
+static void
+use_orig_qual_changed (GtkWidget *toggle,
+ GtkAdjustment *scale_entry)
+{
+ if (jsvals.use_orig_quality && orig_quality > 0)
+ {
+ g_signal_handlers_block_by_func (scale_entry, quality_changed, toggle);
+ gtk_adjustment_set_value (scale_entry, orig_quality);
+ g_signal_handlers_unblock_by_func (scale_entry, quality_changed, toggle);
+ }
+}
+
+static void
+use_orig_qual_changed2 (GtkWidget *toggle,
+ GtkWidget *combo)
+{
+ /* the test is (orig_quality > 0), not (orig_subsmp > 0) - this is normal */
+ if (jsvals.use_orig_quality && orig_quality > 0)
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), orig_subsmp);
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-save.h b/plug-ins/file-jpeg/jpeg-save.h
new file mode 100644
index 0000000..34f0eb5
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-save.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_SAVE_H__
+#define __JPEG_SAVE_H__
+
+typedef struct
+{
+ gdouble quality;
+ gdouble smoothing;
+ gboolean optimize;
+ gboolean arithmetic_coding;
+ gboolean progressive;
+ gboolean baseline;
+ JpegSubsampling subsmp;
+ gint restart;
+ gint dct;
+ gboolean preview;
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_thumbnail;
+ gboolean save_profile;
+ gboolean use_orig_quality;
+} JpegSaveVals;
+
+extern JpegSaveVals jsvals;
+
+extern gint32 orig_image_ID_global;
+extern gint32 drawable_ID_global;
+
+
+gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ gboolean preview,
+ GError **error);
+gboolean save_dialog (void);
+void load_defaults (void);
+void load_parasite (void);
+
+#endif /* __JPEG_SAVE_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-settings.c b/plug-ins/file-jpeg/jpeg-settings.c
new file mode 100644
index 0000000..df6ace2
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-settings.c
@@ -0,0 +1,397 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-settings.c
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Structure of the "jpeg-settings" parasite:
+ * 1 byte - JPEG color space (JCS_YCbCr, JCS_GRAYSCALE, JCS_CMYK, ...)
+ * 1 byte - quality (1..100 according to the IJG scale, or 0)
+ * 1 byte - number of components (0..4)
+ * 1 byte - number of quantization tables (0..4)
+ * C * 2 bytes - sampling factors for each component (1..4)
+ * T * 128 bytes - quantization tables (only if different from IJG tables)
+ *
+ * Additional data following the quantization tables is currently
+ * ignored and can be used for future extensions.
+ *
+ * In order to improve the compatibility with future versions of the
+ * plug-in that may support more subsampling types ("subsmp"), the
+ * parasite contains the original subsampling for each component
+ * instead of saving only one byte containing the subsampling type as
+ * used by the jpeg plug-in. The same applies to the other settings:
+ * for example, up to 4 quantization tables will be saved in the
+ * parasite even if the current code cannot restore more than 2 of
+ * them (4 tables may be needed by unusual JPEG color spaces such as
+ * JCS_CMYK or JCS_YCCK).
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <setjmp.h>
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-quality.h"
+#include "jpeg-settings.h"
+
+/**
+ * jpeg_detect_original_settings:
+ * @cinfo: a pointer to a JPEG decompressor info.
+ * @image_ID: the image to which the parasite should be attached.
+ *
+ * Analyze the image being decompressed (@cinfo) and extract the
+ * sampling factors, quantization tables and overall image quality.
+ * Store this information in a parasite and attach it to @image_ID.
+ *
+ * This function must be called after jpeg_read_header() so that
+ * @cinfo contains the quantization tables and the sampling factors
+ * for each component.
+ *
+ * Return Value: TRUE if a parasite has been attached to @image_ID.
+ */
+gboolean
+jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
+ gint32 image_ID)
+{
+ guint parasite_size;
+ guchar *parasite_data;
+ GimpParasite *parasite;
+ guchar *dest;
+ gint quality;
+ gint num_quant_tables = 0;
+ gint t;
+ gint i;
+
+ g_return_val_if_fail (cinfo != NULL, FALSE);
+ if (cinfo->jpeg_color_space == JCS_UNKNOWN
+ || cinfo->out_color_space == JCS_UNKNOWN)
+ return FALSE;
+
+ quality = jpeg_detect_quality (cinfo);
+ /* no need to attach quantization tables if they are the ones from IJG */
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ num_quant_tables++;
+ }
+
+ parasite_size = 4 + cinfo->num_components * 2 + num_quant_tables * 128;
+ parasite_data = g_new (guchar, parasite_size);
+ dest = parasite_data;
+
+ *dest++ = CLAMP0255 (cinfo->jpeg_color_space);
+ *dest++ = ABS (quality);
+ *dest++ = CLAMP0255 (cinfo->num_components);
+ *dest++ = num_quant_tables;
+
+ for (i = 0; i < cinfo->num_components; i++)
+ {
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].h_samp_factor);
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].v_samp_factor);
+ }
+
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ {
+ guint16 c = cinfo->quant_tbl_ptrs[t]->quantval[i];
+ *dest++ = c / 256;
+ *dest++ = c & 255;
+ }
+ }
+
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ parasite_size,
+ parasite_data);
+ g_free (parasite_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ return TRUE;
+}
+
+
+/*
+ * TODO: compare the JPEG color space found in the parasite with the
+ * GIMP color space of the drawable to be saved. If one of them is
+ * grayscale and the other isn't, then the quality setting may be used
+ * but the subsampling parameters and quantization tables should be
+ * ignored. The drawable_ID needs to be passed around because the
+ * color space of the drawable may be different from that of the image
+ * (e.g., when saving a mask or channel).
+ */
+
+/**
+ * jpeg_restore_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @quality: where to store the original jpeg quality.
+ * @subsmp: where to store the original subsampling type.
+ * @num_quant_tables: where to store the number of quantization tables found.
+ *
+ * Retrieve the original JPEG settings (quality, type of subsampling
+ * and number of quantization tables) from the parasite attached to
+ * @image_ID. If the number of quantization tables is greater than
+ * zero, then these tables can be retrieved from the parasite by
+ * calling jpeg_restore_original_tables().
+ *
+ * Return Value: TRUE if a valid parasite was attached to the image
+ */
+gboolean
+jpeg_restore_original_settings (gint32 image_ID,
+ gint *quality,
+ JpegSubsampling *subsmp,
+ gint *num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint color_space;
+ gint q;
+ gint num_components;
+ gint num_tables;
+ guchar h[3];
+ guchar v[3];
+
+ g_return_val_if_fail (quality != NULL, FALSE);
+ g_return_val_if_fail (subsmp != NULL, FALSE);
+ g_return_val_if_fail (num_quant_tables != NULL, FALSE);
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src = gimp_parasite_data (parasite);
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ color_space = *src++;
+ q = *src++;
+ num_components = *src++;
+ num_tables = *src++;
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && q <= 100 && num_tables <= 4)
+ {
+ *quality = q;
+
+ /* the current plug-in can only create grayscale or YCbCr JPEGs */
+ if (color_space == JCS_GRAYSCALE || color_space == JCS_YCbCr)
+ *num_quant_tables = num_tables;
+ else
+ *num_quant_tables = -1;
+
+ /* the current plug-in can only use subsampling for YCbCr (3) */
+ *subsmp = -1;
+ if (num_components == 3)
+ {
+ h[0] = *src++;
+ v[0] = *src++;
+ h[1] = *src++;
+ v[1] = *src++;
+ h[2] = *src++;
+ v[2] = *src++;
+
+ if (h[1] == 1 && v[1] == 1 && h[2] == 1 && v[2] == 1)
+ {
+ if (h[0] == 1 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_1x1_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_2x1_1x1_1x1;
+ else if (h[0] == 1 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_1x2_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ return TRUE;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ }
+
+ *quality = -1;
+ *subsmp = -1;
+ *num_quant_tables = 0;
+
+ return FALSE;
+}
+
+
+/**
+ * jpeg_restore_original_tables:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @num_quant_tables: the number of quantization tables to restore.
+ *
+ * Retrieve the original quantization tables from the parasite
+ * attached to @image_ID. Each table is an array of coefficients that
+ * can be associated with a component of a JPEG image when saving it.
+ *
+ * An array of newly allocated tables is returned if @num_quant_tables
+ * matches the number of tables saved in the parasite. These tables
+ * are returned as arrays of unsigned integers even if they will never
+ * use more than 16 bits (8 bits in most cases) because the IJG JPEG
+ * library expects arrays of unsigned integers. When these tables are
+ * not needed anymore, the caller should free them using g_free(). If
+ * no parasite exists or if it cannot be used, this function returns
+ * NULL.
+ *
+ * Return Value: an array of quantization tables, or NULL.
+ */
+guint **
+jpeg_restore_original_tables (gint32 image_ID,
+ gint num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guint **quant_tables;
+ gint t;
+ gint i;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && num_tables == num_quant_tables)
+ {
+ src += 4 + num_components * 2;
+ quant_tables = g_new (guint *, num_tables);
+
+ for (t = 0; t < num_tables; t++)
+ {
+ quant_tables[t] = g_new (guint, 128);
+ for (i = 0; i < 64; i++)
+ {
+ guint c;
+
+ c = *src++ * 256;
+ c += *src++;
+ quant_tables[t][i] = c;
+ }
+ }
+ gimp_parasite_free (parasite);
+ return quant_tables;
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+ return NULL;
+}
+
+
+/**
+ * jpeg_swap_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ *
+ * Swap the horizontal and vertical axis for the saved subsampling
+ * parameters and quantization tables. This should be done if the
+ * image has been rotated by +90 or -90 degrees or if it has been
+ * mirrored along its diagonal.
+ */
+void
+jpeg_swap_original_settings (gint32 image_ID)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guchar *new_data;
+ guchar *dest;
+ gint t;
+ gint i;
+ gint j;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128))
+ {
+ new_data = g_new (guchar, src_size);
+ dest = new_data;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ for (i = 0; i < num_components; i++)
+ {
+ dest[0] = src[1];
+ dest[1] = src[0];
+ dest += 2;
+ src += 2;
+ }
+ for (t = 0; t < num_tables; t++)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ dest[i * 16 + j * 2] = src[j * 16 + i * 2];
+ dest[i * 16 + j * 2 + 1] = src[j * 16 + i * 2 + 1];
+ }
+ }
+ dest += 128;
+ src += 128;
+ if (src_size > (4 + num_components * 2 + num_tables * 128))
+ {
+ memcpy (dest, src, src_size - (4 + num_components * 2
+ + num_tables * 128));
+ }
+ }
+ gimp_parasite_free (parasite);
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ src_size,
+ new_data);
+ g_free (new_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-settings.h b/plug-ins/file-jpeg/jpeg-settings.h
new file mode 100644
index 0000000..0399b84
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-settings.h
@@ -0,0 +1,37 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-settings.h
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_SETTINGS_H__
+#define __JPEG_SETTINGS_H__
+
+gboolean jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
+ gint32 image_ID);
+
+gboolean jpeg_restore_original_settings (gint32 image_ID,
+ gint *quality,
+ JpegSubsampling *subsmp,
+ gint *num_quant_tables);
+
+guint **jpeg_restore_original_tables (gint32 image_ID,
+ gint num_quant_tables);
+
+void jpeg_swap_original_settings (gint32 image_ID);
+
+#endif /* __JPEG_SETTINGS_H__ */
diff --git a/plug-ins/file-jpeg/jpeg.c b/plug-ins/file-jpeg/jpeg.c
new file mode 100644
index 0000000..bd2f460
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg.c
@@ -0,0 +1,643 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-settings.h"
+#include "jpeg-load.h"
+#include "jpeg-save.h"
+
+/* Declare local functions.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+gboolean undo_touched;
+gboolean load_interactive;
+gchar *image_comment;
+gint32 display_ID;
+JpegSaveVals jsvals;
+gint32 orig_image_ID_global;
+gint32 drawable_ID_global;
+gint orig_quality;
+JpegSubsampling orig_subsmp;
+gint num_quant_tables;
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
+ { GIMP_PDB_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
+ { GIMP_PDB_INT32, "optimize", "Use optimized tables during Huffman coding (0/1)" },
+ { GIMP_PDB_INT32, "progressive", "Create progressive JPEG images (0/1)" },
+ { GIMP_PDB_STRING, "comment", "Image comment" },
+ { GIMP_PDB_INT32, "subsmp", "Sub-sampling type { 0, 1, 2, 3 } 0 == 4:2:0 (chroma quartered), 1 == 4:2:2 Horizontal (chroma halved), 2 == 4:4:4 (best quality), 3 == 4:2:2 Vertical (chroma halved)" },
+ { GIMP_PDB_INT32, "baseline", "Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1)" },
+ { GIMP_PDB_INT32, "restart", "Interval of restart markers (in MCU rows, 0 = no restart markers)" },
+ { GIMP_PDB_INT32, "dct", "DCT method to use { INTEGER (0), FIXED (1), FLOAT (2) }" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads files in the JPEG file format",
+ "loads files in the JPEG file format",
+ "Spencer Kimball, Peter Mattis & others",
+ "Spencer Kimball & Peter Mattis",
+ "1995-2007",
+ N_("JPEG image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/jpeg");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "jpg,jpeg,jpe",
+ "",
+ "0,string,\xff\xd8\xff");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads a thumbnail from a JPEG image",
+ "Loads a thumbnail from a JPEG image (only if it exists)",
+ "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
+ "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
+ "November 15, 2004",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PROC,
+ "saves files in the JPEG file format",
+ "saves files in the lossy, widely supported JPEG format",
+ "Spencer Kimball, Peter Mattis & others",
+ "Spencer Kimball & Peter Mattis",
+ "1995-2007",
+ N_("JPEG image"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/jpeg");
+ gimp_register_save_handler (SAVE_PROC, "jpg,jpeg,jpe", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpParasite *parasite;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ preview_image_ID = -1;
+ preview_layer_ID = -1;
+
+ orig_quality = 0;
+ orig_subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
+ num_quant_tables = 0;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gboolean resolution_loaded = FALSE;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ load_interactive = TRUE;
+ break;
+ default:
+ load_interactive = FALSE;
+ break;
+ }
+
+ image_ID = load_image (param[1].data.d_string, run_mode, FALSE,
+ &resolution_loaded, &error);
+
+ if (image_ID != -1)
+ {
+ GFile *file = g_file_new_for_path (param[1].data.d_string);
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/jpeg",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ gimp_image_metadata_load_finish (image_ID, "image/jpeg",
+ metadata, flags,
+ load_interactive);
+
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ GFile *file = g_file_new_for_path (param[0].data.d_string);
+ gint width = 0;
+ gint height = 0;
+ GimpImageType type = -1;
+
+ image_ID = load_thumbnail_image (file, &width, &height, &type,
+ &error);
+
+ g_object_unref (file);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 6;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = type;
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = 1; /* num_layers */
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "JPEG",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+
+ switch (export)
+ {
+ case GIMP_EXPORT_EXPORT:
+ {
+ gchar *tmp = g_filename_from_utf8 (_("Export Preview"), -1,
+ NULL, NULL, NULL);
+ if (tmp)
+ {
+ gimp_image_set_filename (image_ID, tmp);
+ g_free (tmp);
+ }
+
+ display_ID = -1;
+ }
+ break;
+
+ case GIMP_EXPORT_IGNORE:
+ break;
+
+ case GIMP_EXPORT_CANCEL:
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Initialize with hardcoded defaults */
+ load_defaults ();
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (orig_image_ID,
+ "image/jpeg",
+ &metadata_flags);
+ jsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ jsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ jsvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ jsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+ jsvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+
+ parasite = gimp_image_get_parasite (orig_image_ID, "gimp-comment");
+ if (parasite)
+ {
+ image_comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ /* Override preferences from JPG export defaults (if saved). */
+ load_parasite ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ /* pw - added two more progressive and comment */
+ /* sg - added subsampling, preview, baseline, restarts and DCT */
+ if (nparams != 14)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ /* Once the PDB gets default parameters, remove this hack */
+ if (param[5].data.d_float >= 0.01)
+ {
+ jsvals.quality = 100.0 * param[5].data.d_float;
+ jsvals.smoothing = param[6].data.d_float;
+ jsvals.optimize = param[7].data.d_int32;
+ jsvals.progressive = param[8].data.d_int32;
+ jsvals.baseline = param[11].data.d_int32;
+ jsvals.subsmp = param[10].data.d_int32;
+ jsvals.restart = param[12].data.d_int32;
+ jsvals.dct = param[13].data.d_int32;
+
+ /* free up the default -- wasted some effort earlier */
+ g_free (image_comment);
+ image_comment = g_strdup (param[9].data.d_string);
+ }
+
+ jsvals.preview = FALSE;
+
+ if (jsvals.quality < 0.0 || jsvals.quality > 100.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.smoothing < 0.0 || jsvals.smoothing > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.subsmp < 0 || jsvals.subsmp > 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.dct < 0 || jsvals.dct > 2)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* restore the values found when loading the file (if available) */
+ jpeg_restore_original_settings (orig_image_ID,
+ &orig_quality,
+ &orig_subsmp,
+ &num_quant_tables);
+
+ /* load up the previously used values (if file was saved once) */
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "jpeg-save-options");
+ if (parasite)
+ {
+ const JpegSaveVals *save_vals = gimp_parasite_data (parasite);
+
+ jsvals.quality = save_vals->quality;
+ jsvals.smoothing = save_vals->smoothing;
+ jsvals.optimize = save_vals->optimize;
+ jsvals.progressive = save_vals->progressive;
+ jsvals.baseline = save_vals->baseline;
+ jsvals.subsmp = save_vals->subsmp;
+ jsvals.restart = save_vals->restart;
+ jsvals.dct = save_vals->dct;
+ jsvals.preview = save_vals->preview;
+ jsvals.save_exif = save_vals->save_exif;
+ jsvals.save_thumbnail = save_vals->save_thumbnail;
+ jsvals.save_xmp = save_vals->save_xmp;
+ jsvals.save_iptc = save_vals->save_iptc;
+ jsvals.use_orig_quality = save_vals->use_orig_quality;
+
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ /* We are called with GIMP_RUN_WITH_LAST_VALS but this image
+ * doesn't have a "jpeg-save-options" parasite. It's better
+ * to prompt the user with a dialog now so that she has control
+ * over the JPEG encoding parameters.
+ */
+ run_mode = GIMP_RUN_INTERACTIVE;
+
+ /* If this image was loaded from a JPEG file and has not been
+ * saved yet, try to use some of the settings from the
+ * original file if they are better than the default values.
+ */
+ if (orig_quality > jsvals.quality)
+ {
+ jsvals.quality = orig_quality;
+ }
+
+ /* Skip changing subsampling to original if we already have best
+ * setting or if original have worst setting */
+ if (!(jsvals.subsmp == JPEG_SUBSAMPLING_1x1_1x1_1x1 ||
+ orig_subsmp == JPEG_SUBSAMPLING_2x2_1x1_1x1))
+ {
+ jsvals.subsmp = orig_subsmp;
+ }
+
+ if (orig_quality == jsvals.quality &&
+ orig_subsmp == jsvals.subsmp)
+ {
+ jsvals.use_orig_quality = TRUE;
+ }
+ }
+ break;
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (jsvals.preview)
+ {
+ /* we freeze undo saving so that we can avoid sucking up
+ * tile cache with our unneeded preview steps. */
+ gimp_image_undo_freeze (image_ID);
+
+ undo_touched = TRUE;
+ }
+
+ /* prepare for the preview */
+ preview_image_ID = image_ID;
+ orig_image_ID_global = orig_image_ID;
+ drawable_ID_global = drawable_ID;
+
+ /* First acquire information with a dialog */
+ status = (save_dialog () ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL);
+
+ if (undo_touched)
+ {
+ /* thaw undo saving and flush the displays to have them
+ * reflect the current shortcuts */
+ gimp_image_undo_thaw (image_ID);
+ gimp_displays_flush ();
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string,
+ image_ID, drawable_ID, orig_image_ID, FALSE,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ {
+ /* If the image was exported, delete the new display. */
+ /* This also deletes the image.
+ */
+
+ if (display_ID != -1)
+ gimp_display_delete (display_ID);
+ else
+ gimp_image_delete (image_ID);
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* pw - now we need to change the defaults to be whatever
+ * was used to save this image. Dump the old parasites
+ * and add new ones.
+ */
+
+ gimp_image_detach_parasite (orig_image_ID, "gimp-comment");
+ if (image_comment && strlen (image_comment))
+ {
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (image_comment) + 1,
+ image_comment);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_parasite_new ("jpeg-save-options",
+ 0, sizeof (jsvals), &jsvals);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ /* write metadata */
+
+ if (metadata)
+ {
+ GFile *file;
+
+ gimp_metadata_set_bits_per_sample (metadata, 8);
+
+ if (jsvals.save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (jsvals.save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (jsvals.save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ if (jsvals.save_thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (jsvals.save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ file = g_file_new_for_path (param[3].data.d_string);
+ if (! gimp_image_metadata_save_finish (orig_image_ID,
+ "image/jpeg",
+ metadata, metadata_flags,
+ file, &error))
+ {
+ if (error)
+ {
+ /* Even though a failure to write metadata is not enough
+ reason to say we failed to save the image, we should
+ still notify the user about the problem. */
+ g_message ("%s: saving metadata failed: %s",
+ G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ }
+ g_object_unref (file);
+ }
+ }
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+void
+my_error_exit (j_common_ptr cinfo)
+{
+ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
+ my_error_ptr myerr = (my_error_ptr) cinfo->err;
+
+ /* Always display the message. */
+ /* We could postpone this until after returning, if we chose. */
+ (*cinfo->err->output_message) (cinfo);
+
+ /* Return control to the setjmp point */
+ longjmp (myerr->setjmp_buffer, 1);
+}
+
+
+void
+my_output_message (j_common_ptr cinfo)
+{
+ gchar buffer[JMSG_LENGTH_MAX + 1];
+
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+ g_message ("%s", buffer);
+}
diff --git a/plug-ins/file-jpeg/jpeg.h b/plug-ins/file-jpeg/jpeg.h
new file mode 100644
index 0000000..0bf9af0
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg.h
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_H__
+#define __JPEG_H__
+
+#define LOAD_PROC "file-jpeg-load"
+#define LOAD_THUMB_PROC "file-jpeg-load-thumb"
+#define SAVE_PROC "file-jpeg-save"
+#define PLUG_IN_BINARY "file-jpeg"
+#define PLUG_IN_ROLE "gimp-file-jpeg"
+
+/* headers used in some APPn markers */
+#define JPEG_APP_HEADER_EXIF "Exif\0\0"
+#define JPEG_APP_HEADER_XMP "http://ns.adobe.com/xap/1.0/"
+
+typedef struct my_error_mgr
+{
+ struct jpeg_error_mgr pub; /* "public" fields */
+
+#ifdef __ia64__
+ /* Ugh, the jmp_buf field needs to be 16-byte aligned on ia64 and some
+ * glibc/icc combinations don't guarantee this. So we pad. See bug #138357
+ * for details.
+ */
+ long double dummy;
+#endif
+
+ jmp_buf setjmp_buffer; /* for return to caller */
+} *my_error_ptr;
+
+typedef enum
+{
+ JPEG_SUBSAMPLING_2x2_1x1_1x1 = 0, /* smallest file */
+ JPEG_SUBSAMPLING_2x1_1x1_1x1 = 1, /* 4:2:2 */
+ JPEG_SUBSAMPLING_1x1_1x1_1x1 = 2,
+ JPEG_SUBSAMPLING_1x2_1x1_1x1 = 3
+} JpegSubsampling;
+
+extern gint32 volatile preview_image_ID;
+extern gint32 preview_layer_ID;
+extern gboolean undo_touched;
+extern gboolean load_interactive;
+extern gint32 display_ID;
+extern gchar *image_comment;
+extern gint orig_quality;
+extern JpegSubsampling orig_subsmp;
+extern gint num_quant_tables;
+
+
+void destroy_preview (void);
+
+void my_error_exit (j_common_ptr cinfo);
+void my_emit_message (j_common_ptr cinfo,
+ int msg_level);
+void my_output_message (j_common_ptr cinfo);
+
+
+#endif /* __JPEG_H__ */
diff --git a/plug-ins/file-jpeg/jpegqual.c b/plug-ins/file-jpeg/jpegqual.c
new file mode 100644
index 0000000..9dd5d9e
--- /dev/null
+++ b/plug-ins/file-jpeg/jpegqual.c
@@ -0,0 +1,1082 @@
+/* jpegqual.c - analyze quality settings of existing JPEG files
+ *
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This program analyzes the quantization tables of the JPEG files
+ * given on the command line and displays their quality settings.
+ *
+ * It is useful for developers or maintainers of the JPEG plug-in
+ * because it can be used to validate the formula used in
+ * jpeg_detect_quality(), by comparing the quality reported for
+ * different JPEG files.
+ *
+ * It can also dump quantization tables so that they can be integrated
+ * into this program and recognized later. This can be used to identify
+ * which device or which program saved a JPEG file.
+ */
+
+/*
+ * TODO:
+ * - rename this program!
+ * - update quant_info[].
+ * - reorganize the options: 2 groups for print options and for selection.
+ * - re-add the code to compare different formulas for approx. IJG quality.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include <glib.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include "jpeg-quality.h"
+
+/* command-line options */
+static gboolean option_summary = FALSE;
+static gboolean option_ctable = FALSE;
+static gboolean option_table_2cols = FALSE;
+static gboolean option_unknown = FALSE;
+static gboolean option_ignore_err = FALSE;
+static gchar **filenames = NULL;
+
+static const GOptionEntry option_entries[] =
+{
+ {
+ "ignore-errors", 'i', 0, G_OPTION_ARG_NONE, &option_ignore_err,
+ "Continue processing other files after a JPEG error", NULL
+ },
+ {
+ "summary", 's', 0, G_OPTION_ARG_NONE, &option_summary,
+ "Print summary information and closest IJG quality", NULL
+ },
+ {
+ "tables", 't', 0, G_OPTION_ARG_NONE, &option_table_2cols,
+ "Dump quantization tables", NULL
+ },
+ {
+ "c-tables", 'c', 0, G_OPTION_ARG_NONE, &option_ctable,
+ "Dump quantization tables as C code", NULL
+ },
+ {
+ "ctables", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &option_ctable,
+ NULL, NULL
+ },
+ {
+ "unknown", 'u', 0, G_OPTION_ARG_NONE, &option_unknown,
+ "Only print information about files with unknown tables", NULL
+ },
+ {
+ G_OPTION_REMAINING, 0, 0,
+ G_OPTION_ARG_FILENAME_ARRAY, &filenames,
+ NULL, NULL
+ },
+ { NULL }
+};
+
+/* information about known JPEG quantization tables */
+typedef struct
+{
+ const guint hash; /* hash of luminance/chrominance tables */
+ const gint lum_sum; /* sum of luminance table divisors */
+ const gint chrom_sum; /* sum of chrominance table divisors */
+ const gint subsmp_h; /* horizontal subsampling (1st component) */
+ const gint subsmp_v; /* vertical subsampling (1st component) */
+ const gint num_quant_tables; /* number of tables (< 0 if no grayscale) */
+ const gchar *source_name; /* name of software of device */
+ const gchar *setting_name; /* name of quality setting */
+ const gint ijg_qual; /* closest IJG quality setting */
+} QuantInfo;
+
+static const QuantInfo quant_info[] =
+{
+ { 0x0a82648b, 64, 64, 0, 0, 2, "IJG JPEG Library", "quality 100", 100 },
+ { 0x4d981764, 86, 115, 0, 0, 2, "IJG JPEG Library", "quality 99", 99 },
+ { 0x62b71702, 151, 224, 0, 0, 2, "IJG JPEG Library", "quality 98", 98 },
+ { 0x29e095c5, 221, 333, 0, 0, 2, "IJG JPEG Library", "quality 97", 97 },
+ { 0xb62c754a, 292, 443, 0, 0, 2, "IJG JPEG Library", "quality 96", 96 },
+ { 0x8e55c78a, 369, 558, 0, 0, 2, "IJG JPEG Library", "quality 95", 95 },
+ { 0x0664d770, 441, 668, 0, 0, 2, "IJG JPEG Library", "quality 94", 94 },
+ { 0x59e5c5bc, 518, 779, 0, 0, 2, "IJG JPEG Library", "quality 93", 93 },
+ { 0xd6f26606, 592, 891, 0, 0, 2, "IJG JPEG Library", "quality 92", 92 },
+ { 0x8aa986ad, 667, 999, 0, 0, 2, "IJG JPEG Library", "quality 91", 91 },
+ { 0x17816eb1, 736, 1110, 0, 0, 2, "IJG JPEG Library", "quality 90", 90 },
+ { 0x75de9350, 814, 1223, 0, 0, 2, "IJG JPEG Library", "quality 89", 89 },
+ { 0x88fdf223, 884, 1332, 0, 0, 2, "IJG JPEG Library", "quality 88", 88 },
+ { 0xf40a6a50, 961, 1444, 0, 0, 2, "IJG JPEG Library", "quality 87", 87 },
+ { 0xe9f2c235, 1031, 1555, 0, 0, 2, "IJG JPEG Library", "quality 86", 86 },
+ { 0x82683892, 1109, 1666, 0, 0, 2, "IJG JPEG Library", "quality 85", 85 },
+ { 0xb1aecce8, 1179, 1778, 0, 0, 2, "IJG JPEG Library", "quality 84", 84 },
+ { 0x83375efe, 1251, 1888, 0, 0, 2, "IJG JPEG Library", "quality 83", 83 },
+ { 0x1e99f479, 1326, 2000, 0, 0, 2, "IJG JPEG Library", "quality 82", 82 },
+ { 0x1a02d360, 1398, 2111, 0, 0, 2, "IJG JPEG Library", "quality 81", 81 },
+ { 0x96129a0d, 1477, 2221, 0, 0, 2, "IJG JPEG Library", "quality 80", 80 },
+ { 0x64d4144b, 1552, 2336, 0, 0, 2, "IJG JPEG Library", "quality 79", 79 },
+ { 0x48a344ac, 1620, 2445, 0, 0, 2, "IJG JPEG Library", "quality 78", 78 },
+ { 0x16e820e3, 1692, 2556, 0, 0, 2, "IJG JPEG Library", "quality 77", 77 },
+ { 0x246b2e95, 1773, 2669, 0, 0, 2, "IJG JPEG Library", "quality 76", 76 },
+ { 0x10b035e9, 1858, 2780, 0, 0, 2, "IJG JPEG Library", "quality 75", 75 },
+ { 0xd5c653da, 1915, 2836, 0, 0, 2, "IJG JPEG Library", "quality 74", 74 },
+ { 0xe349618c, 1996, 2949, 0, 0, 2, "IJG JPEG Library", "quality 73", 73 },
+ { 0xb18e3dc3, 2068, 3060, 0, 0, 2, "IJG JPEG Library", "quality 72", 72 },
+ { 0x955d6e24, 2136, 3169, 0, 0, 2, "IJG JPEG Library", "quality 71", 71 },
+ { 0x641ee862, 2211, 3284, 0, 0, 2, "IJG JPEG Library", "quality 70", 70 },
+ { 0xe02eaf0f, 2290, 3394, 0, 0, 2, "IJG JPEG Library", "quality 69", 69 },
+ { 0xdb978df6, 2362, 3505, 0, 0, 2, "IJG JPEG Library", "quality 68", 68 },
+ { 0x76fa2371, 2437, 3617, 0, 0, 2, "IJG JPEG Library", "quality 67", 67 },
+ { 0x4882b587, 2509, 3727, 0, 0, 2, "IJG JPEG Library", "quality 66", 66 },
+ { 0x25556ae1, 2583, 3839, 0, 0, 2, "IJG JPEG Library", "quality 65", 65 },
+ { 0x103ec03a, 2657, 3950, 0, 0, 2, "IJG JPEG Library", "quality 64", 64 },
+ { 0x0627181f, 2727, 4061, 0, 0, 2, "IJG JPEG Library", "quality 63", 63 },
+ { 0x7133904c, 2804, 4173, 0, 0, 2, "IJG JPEG Library", "quality 62", 62 },
+ { 0x8452ef1f, 2874, 4282, 0, 0, 2, "IJG JPEG Library", "quality 61", 61 },
+ { 0xe2b013be, 2952, 4395, 0, 0, 2, "IJG JPEG Library", "quality 60", 60 },
+ { 0x6f87fbc2, 3021, 4506, 0, 0, 2, "IJG JPEG Library", "quality 59", 59 },
+ { 0x233f1c69, 3096, 4614, 0, 0, 2, "IJG JPEG Library", "quality 58", 58 },
+ { 0xa04bbcb3, 3170, 4726, 0, 0, 2, "IJG JPEG Library", "quality 57", 57 },
+ { 0xf3ccaaff, 3247, 4837, 0, 0, 2, "IJG JPEG Library", "quality 56", 56 },
+ { 0x1967dbe9, 3323, 4947, 0, 0, 2, "IJG JPEG Library", "quality 55", 55 },
+ { 0x44050d25, 3396, 5062, 0, 0, 2, "IJG JPEG Library", "quality 54", 54 },
+ { 0xd050ecaa, 3467, 5172, 0, 0, 2, "IJG JPEG Library", "quality 53", 53 },
+ { 0x9e99f8f1, 3541, 5281, 0, 0, 2, "IJG JPEG Library", "quality 52", 52 },
+ { 0xdf2423f4, 3621, 5396, 0, 0, 2, "IJG JPEG Library", "quality 51", 51 },
+ { 0xe0f48a64, 3688, 5505, 0, 0, 2, "IJG JPEG Library", "quality 50", 50 },
+ { 0xe2c4f0d4, 3755, 5614, 0, 0, 2, "IJG JPEG Library", "quality 49", 49 },
+ { 0x234f1bd7, 3835, 5729, 0, 0, 2, "IJG JPEG Library", "quality 48", 48 },
+ { 0xf198281e, 3909, 5838, 0, 0, 2, "IJG JPEG Library", "quality 47", 47 },
+ { 0x7de407a3, 3980, 5948, 0, 0, 2, "IJG JPEG Library", "quality 46", 46 },
+ { 0xb3aa597b, 4092, 6116, 0, 0, 2, "IJG JPEG Library", "quality 45", 45 },
+ { 0x32b48093, 4166, 6226, 0, 0, 2, "IJG JPEG Library", "quality 44", 44 },
+ { 0x9ea9f85f, 4280, 6396, 0, 0, 2, "IJG JPEG Library", "quality 43", 43 },
+ { 0x335d6006, 4393, 6562, 0, 0, 2, "IJG JPEG Library", "quality 42", 42 },
+ { 0xa727ea4a, 4463, 6672, 0, 0, 2, "IJG JPEG Library", "quality 41", 41 },
+ { 0x1889cfc4, 4616, 6897, 0, 0, 2, "IJG JPEG Library", "quality 40", 40 },
+ { 0xb1aa548e, 4719, 7060, 0, 0, 2, "IJG JPEG Library", "quality 39", 39 },
+ { 0x99bebdd3, 4829, 7227, 0, 0, 2, "IJG JPEG Library", "quality 38", 38 },
+ { 0xf728d062, 4976, 7447, 0, 0, 2, "IJG JPEG Library", "quality 37", 37 },
+ { 0xe1ba65b9, 5086, 7616, 0, 0, 2, "IJG JPEG Library", "quality 36", 36 },
+ { 0x2c8ba6a4, 5240, 7841, 0, 0, 2, "IJG JPEG Library", "quality 35", 35 },
+ { 0x03f7963a, 5421, 8114, 0, 0, 2, "IJG JPEG Library", "quality 34", 34 },
+ { 0xa19bed1e, 5571, 8288, 0, 0, 2, "IJG JPEG Library", "quality 33", 33 },
+ { 0x7945d01c, 5756, 8565, 0, 0, 2, "IJG JPEG Library", "quality 32", 32 },
+ { 0xcc36df1a, 5939, 8844, 0, 0, 2, "IJG JPEG Library", "quality 31", 31 },
+ { 0x3eb1b5ca, 6125, 9122, 0, 0, 2, "IJG JPEG Library", "quality 30", 30 },
+ { 0xd7f65293, 6345, 9455, 0, 0, 2, "IJG JPEG Library", "quality 29", 29 },
+ { 0x4c0a8178, 6562, 9787, 0, 0, 2, "IJG JPEG Library", "quality 28", 28 },
+ { 0x8281d1a1, 6823, 10175, 0, 0, 2, "IJG JPEG Library", "quality 27", 27 },
+ { 0x0bbc9f7e, 7084, 10567, 0, 0, 2, "IJG JPEG Library", "quality 26", 26 },
+ { 0xa8ac1cbd, 7376, 11010, 0, 0, 2, "IJG JPEG Library", "quality 25", 25 },
+ { 0x459b99fc, 7668, 11453, 0, 0, 2, "IJG JPEG Library", "quality 24", 24 },
+ { 0xda09c178, 7995, 11954, 0, 0, 2, "IJG JPEG Library", "quality 23", 23 },
+ { 0x1c651f15, 8331, 12511, 0, 0, 2, "IJG JPEG Library", "quality 22", 22 },
+ { 0x59025244, 8680, 13121, 0, 0, 2, "IJG JPEG Library", "quality 21", 21 },
+ { 0xa130f919, 9056, 13790, 0, 0, 2, "IJG JPEG Library", "quality 20", 20 },
+ { 0x109756cf, 9368, 14204, 0, 0, 2, "IJG JPEG Library", "quality 19", 19 },
+ { 0xe929cab5, 9679, 14267, 0, 0, 2, "IJG JPEG Library", "quality 18", 18 },
+ { 0xcddca370, 10027, 14346, 0, 0, 2, "IJG JPEG Library", "quality 17", 17 },
+ { 0xd5fc76c0, 10360, 14429, 0, 0, 2, "IJG JPEG Library", "quality 16", 16 },
+ { 0x533a1a03, 10714, 14526, 0, 0, 2, "IJG JPEG Library", "quality 15", 15 },
+ { 0x0d8adaff, 11081, 14635, 0, 0, 2, "IJG JPEG Library", "quality 14", 14 },
+ { 0x0d2ee95d, 11456, 14754, 0, 0, 2, "IJG JPEG Library", "quality 13", 13 },
+ { 0x3a1d59a0, 11861, 14864, 0, 0, 2, "IJG JPEG Library", "quality 12", 12 },
+ { 0x66555d04, 12240, 14985, 0, 0, 2, "IJG JPEG Library", "quality 11", 11 },
+ { 0x7fa051b1, 12560, 15110, 0, 0, 2, "IJG JPEG Library", "quality 10", 10 },
+ { 0x7b668ca3, 12859, 15245, 0, 0, 2, "IJG JPEG Library", "quality 9", 9 },
+ { 0xb44d7082, 13230, 15369, 0, 0, 2, "IJG JPEG Library", "quality 8", 8 },
+ { 0xe838d325, 13623, 15523, 0, 0, 2, "IJG JPEG Library", "quality 7", 7 },
+ { 0xb6f58977, 14073, 15731, 0, 0, 2, "IJG JPEG Library", "quality 6", 6 },
+ { 0xfd3e9fc4, 14655, 16010, 0, 0, 2, "IJG JPEG Library", "quality 5", 5 },
+ { 0x7782b922, 15277, 16218, 0, 0, 2, "IJG JPEG Library", "quality 4", 4 },
+ { 0x5a03ac45, 15946, 16320, 0, 0, 2, "IJG JPEG Library", "quality 3", 3 },
+ { 0xe0afaa36, 16315, 16320, 0, 0, 2, "IJG JPEG Library", "quality 2", 2 },
+ { 0x6d640b8b, 16320, 16320, 0, 0, 2, "IJG JPEG Library", "quality 1", 1 },
+ { 0x6d640b8b, 16320, 16320, 0, 0, 2, "IJG JPEG Library", "quality 0", 1 },
+ { 0x4b1d5895, 8008, 11954, 0, 0, 2, "IJG JPEG Library", "not baseline 23", -22 },
+ { 0x36c32c2c, 8370, 12511, 0, 0, 2, "IJG JPEG Library", "not baseline 22", -21 },
+ { 0xa971f812, 8774, 13121, 0, 0, 2, "IJG JPEG Library", "not baseline 21", -20 },
+ { 0xa01f5a9b, 9234, 13790, 0, 0, 2, "IJG JPEG Library", "not baseline 20", -19 },
+ { 0x0e45ab9a, 9700, 14459, 0, 0, 2, "IJG JPEG Library", "not baseline 19", -17 },
+ { 0x5e654320, 10209, 15236, 0, 0, 2, "IJG JPEG Library", "not baseline 18", -14 },
+ { 0x5fc0115c, 10843, 16182, 0, 0, 2, "IJG JPEG Library", "not baseline 17", -11 },
+ { 0x5d8b8e7b, 11505, 17183, 0, 0, 2, "IJG JPEG Library", "not baseline 16", -7 },
+ { 0x63f8b8c1, 12279, 18351, 0, 0, 2, "IJG JPEG Library", "not baseline 15", -5 },
+ { 0x675ecd7a, 13166, 19633, 0, 0, 2, "IJG JPEG Library", "not baseline 14", 0 },
+ { 0x7a65d374, 14160, 21129, 0, 0, 2, "IJG JPEG Library", "not baseline 13", 0 },
+ { 0xf5d0af6a, 15344, 22911, 0, 0, 2, "IJG JPEG Library", "not baseline 12", 0 },
+ { 0x0227aaf0, 16748, 24969, 0, 0, 2, "IJG JPEG Library", "not baseline 11", 0 },
+ { 0xffd2d3c8, 18440, 27525, 0, 0, 2, "IJG JPEG Library", "not baseline 10", 0 },
+ { 0x27f48623, 20471, 30529, 0, 0, 2, "IJG JPEG Library", "not baseline 9", 0 },
+ { 0xff1fab81, 23056, 34422, 0, 0, 2, "IJG JPEG Library", "not baseline 8", 0 },
+ { 0xcfeac62b, 26334, 39314, 0, 0, 2, "IJG JPEG Library", "not baseline 7", 0 },
+ { 0x4a8e947e, 30719, 45876, 0, 0, 2, "IJG JPEG Library", "not baseline 6", 0 },
+ { 0xe668af85, 36880, 55050, 0, 0, 2, "IJG JPEG Library", "not baseline 5", 0 },
+ { 0x6d4b1215, 46114, 68840, 0, 0, 2, "IJG JPEG Library", "not baseline 4", 0 },
+ { 0xf2734901, 61445, 91697, 0, 0, 2, "IJG JPEG Library", "not baseline 3", 0 },
+ { 0x9a2a42bc, 92200, 137625, 0, 0, 2, "IJG JPEG Library", "not baseline 2", 0 },
+ { 0x1b178d6d, 184400, 275250, 0, 0, 2, "IJG JPEG Library", "not baseline 1", 0 },
+ { 0x1b178d6d, 184400, 275250, 0, 0, 2, "IJG JPEG Library", "not baseline 0", 0 },
+
+ /* FIXME: the following entries are incomplete and need to be verified */
+
+ { 0x31258383, 319, 665, 2, 1, -2, "ACD ?", "?", -94 },
+ { 0x91d018a3, 436, 996, 2, 1, -2, "ACD ?", "?", -92 },
+ { 0x954ee70e, 664, 1499, 2, 1, -2, "ACD ?", "?", -88 },
+ { 0xe351bb55, 1590, 3556, 2, 2, -2, "ACD ?", "?", -71 },
+ { 0x5a81e2c0, 95, 166, 1, 1, 2, "Adobe Photoshop CS2", "quality 12", -98 },
+ { 0xcd0d41ae, 232, 443, 1, 1, 2, "Adobe Photoshop CS2", "quality 11", -96 },
+ { 0x1b141cb3, 406, 722, 1, 1, 2, "Adobe Photoshop CS2", "quality 10", -93 },
+ { 0xc84c0187, 539, 801, 1, 1, 2, "Adobe Photoshop CS2", "quality 9", -92 },
+ { 0x1e822409, 649, 853, 1, 1, 2, "Adobe Photoshop CS2", "quality 8", -91 },
+ { 0x3104202b, 786, 926, 1, 1, 2, "Adobe Photoshop CS2", "quality 7", -90 },
+ { 0xcd21f666, 717, 782, 2, 2, 2, "Adobe Photoshop CS2", "quality 6", -91 },
+ { 0x1b74e018, 844, 849, 2, 2, 2, "Adobe Photoshop CS2", "quality 5", -90 },
+ { 0xde39ed89, 962, 892, 2, 2, 2, "Adobe Photoshop CS2", "quality 4", -89 },
+ { 0xbdef8414, 1068, 941, 2, 2, 2, "Adobe Photoshop CS2", "quality 3", -89 },
+ { 0xfedf6432, 1281, 998, 2, 2, 2, "Adobe Photoshop CS2", "quality 2", -87 },
+ { 0x5d6afd92, 1484, 1083, 2, 2, 2, "Adobe Photoshop CS2", "quality 1", -86 },
+ { 0x4c7d2f7d, 1582, 1108, 2, 2, 2, "Adobe Photoshop CS2", "quality 0", -85 },
+ { 0x68e798b2, 95, 168, 1, 1, 2, "Adobe Photoshop CS2", "save for web 100", -98 },
+ { 0x9f3456f2, 234, 445, 1, 1, 2, "Adobe Photoshop CS2", "save for web 90", -96 },
+ { 0xda807dd5, 406, 724, 1, 1, 2, "Adobe Photoshop CS2", "save for web 80", -93 },
+ { 0xf70a37ce, 646, 1149, 1, 1, 2, "Adobe Photoshop CS2", "save for web 70", -90 },
+ { 0xf36979d2, 974, 1769, 1, 1, 2, "Adobe Photoshop CS2", "save for web 60", -85 },
+ { 0x4966f484, 1221, 1348, 2, 2, 2, "Adobe Photoshop CS2", "save for web 50", -86 },
+ { 0xaddf6d45, 1821, 1997, 2, 2, 2, "Adobe Photoshop CS2", "save for web 40", -79 },
+ { 0xeffa362a, 2223, 2464, 2, 2, 2, "Adobe Photoshop CS2", "save for web 30", -74 },
+ { 0x7aa980c1, 2575, 2903, 2, 2, 2, "Adobe Photoshop CS2", "save for web 20", -70 },
+ { 0x489e344f, 3514, 3738, 2, 2, 2, "Adobe Photoshop CS2", "save for web 10", -60 },
+ { 0x1a2cffe0, 535, 750, 1, 1, 2, "Adobe Photoshop 7.0", "quality 10", -93 },
+ { 0x1e96d5d3, 109, 171, 1, 1, 2, "Adobe Photoshop CS", "quality 12", -98 },
+ { 0x6771042c, 303, 466, 1, 1, 2, "Adobe Photoshop CS, Camera Raw 3", "quality 11", -95 },
+ { 0xd4553f25, 668, 830, 1, 1, 2, "Adobe Photoshop 7.0, CS", "quality 9", -91 },
+ { 0xd3b24cb4, 794, 895, 1, 1, 2, "Adobe Photoshop 7.0, CS", "quality 8", -90 },
+ { 0x4ad5990c, 971, 950, 1, 1, 2, "Adobe Photoshop CS", "quality 7", -89 },
+ { 0x4293dfde, 884, 831, 2, 2, 2, "Adobe Photoshop CS", "quality 6", -90 },
+ { 0xba0212ec, 1032, 889, 2, 2, 2, "Adobe Photoshop CS", "quality 5", -89 },
+ { 0x4b50947d, 1126, 940, 2, 2, 2, "Adobe Photoshop CS", "quality 4", -88 },
+ { 0xad0f8e5c, 1216, 977, 2, 2, 2, "Adobe Photoshop CS", "quality 3", -88 },
+ { 0x560b5f0c, 339, 670, 1, 1, 2, "Adobe Photoshop ?", "save for web 85", -94 },
+ { 0x9539b14b, 427, 613, 2, 2, 2, "Adobe Photoshop ?", "?", -94 },
+ { 0x841f2655, 525, 941, 1, 1, 2, "Adobe Photoshop ?", "save for web 75", -92 },
+ { 0xaa2161e2, 803, 1428, 1, 1, 2, "Adobe Photoshop ?", "save for web 65", -87 },
+ { 0x743feb84, 1085, 1996, 1, 1, 2, "Adobe Photoshop ?", "save for web 55", -83 },
+ { 0xe9f14743, 1156, 2116, 1, 1, 2, "Adobe Photoshop ?", "save for web 52", -82 },
+ { 0x1003c8fb, 1175, 2169, 1, 1, 2, "Adobe Photoshop ?", "save for web 51", -81 },
+ { 0xd7804c45, 2272, 2522, 2, 2, 2, "Adobe Photoshop ?", "save for web 29", -73 },
+ { 0xcb5aa8ad, 2515, 2831, 2, 2, 2, "Adobe ImageReady", "save for web 22", -70 },
+ { 0x956d2a00, 3822, 3975, 2, 2, 2, "Adobe ImageReady", "save for web 6", -57 },
+ { 0xba53e0c5, 4028, 4174, 2, 2, 2, "Adobe Photoshop ?", "save for web 3", -55 },
+ { 0x13c0c8bc, 513, 0, 1, 1, 1, "Adobe Photoshop ?", "?", -93 },
+ { 0x3fad5c43, 255, 393, 2, 1, 2, "Apple Quicktime 7.1 or 7.2", "?", -96 },
+ { 0x6529bd03, 513, 775, 2, 2, 2, "Apple Quicktime 7.2", "?", -93 },
+ { 0x354e610a, 543, 784, 2, 1, -2, "Apple Quicktime 7.1", "?", -92 },
+ { 0xd596795e, 361, 506, 2, 1, 2, "Apple ?", "?", -95 },
+ { 0x74da8ba7, 1511, 2229, 2, 2, 2, "Apple ?", "?", -79 },
+ { 0x6391ca2b, 188, 276, 2, 1, -2, "Canon EOS 300D, 350D or 400D", "Fine", -97 },
+ { 0x00474eb0, 708, 1057, 2, 1, -2, "Canon EOS 10D", "Normal", -90 },
+ { 0x535174bd, 533, 1325, 2, 1, -2, "Canon Digital Ixus v2", "Fine", -92 },
+ { 0x535174bd, 533, 1325, 2, 1, -2, "Canon PowerShot A95, S1, S2, SD400 or SD630", "Fine", -89 },
+ { 0xb7be6b97, 192, 556, 2, 1, -2, "Canon PowerShot S5 IS, A300, A430, S200, SD500, SD700, Ixus 700 or 800", "Superfine", -95 },
+ { 0xb5b5c61d, 533, 1325, 1, 2, -2, "Canon Digital Ixus 400", "Fine", -89 },
+ { 0xa7a2c471, 288, 443, 2, 1, -3, "FujiFilm MX-2700", "?", -96 },
+ { 0x8db061f0, 389, 560, 2, 1, -3, "FujiFilm FinePix S700", "Fine", -94 },
+ { 0xbb7b97ba, 515, 774, 2, 1, -3, "FujiFilm FinePix 2600 Zoom", "Fine", -93 },
+ { 0x71bcdf92, 167, 240, 2, 2, -3, "HP PhotoSmart C850, C935", "?", -97 },
+ { 0x9542cc81, 1970, 1970, 2, 2, -3, "HP PhotoSmart C812", "?", -78 },
+ { 0xdb7b71d8, 369, 558, 2, 1, -3, "Kodak P880", "?", 95 },
+ { 0x82e461f8, 566, 583, 2, 2, -2, "Kodak V610", "Fine", -93 },
+ { 0x17816eb1, 736, 1110, 2, 2, -2, "Kodak DC240", "?", 90 },
+ { 0x17816eb1, 736, 1110, 1, 1, -2, "Kodak Imaging", "High (high res.)", 90 },
+ { 0x17816eb1, 736, 1110, 2, 1, -2, "Kodak Imaging", "High (medium res.)", 90 },
+ { 0x17816eb1, 736, 1110, 4, 1, -2, "Kodak Imaging", "High (low res.)", 90 },
+ { 0x3841f91b, 736, 0, 1, 1, 1, "Kodak Imaging", "High (grayscale)", 90 },
+ { 0xe0f48a64, 3688, 5505, 1, 1, -2, "Kodak Imaging", "Medium (high res.)", 50 },
+ { 0xe0f48a64, 3688, 5505, 2, 1, -2, "Kodak Imaging", "Medium (medium res.)", 50 },
+ { 0xe0f48a64, 3688, 5505, 4, 1, -2, "Kodak Imaging", "Medium (low res.)", 50 },
+ { 0x9ebccf53, 3688, 0, 1, 1, 1, "Kodak Imaging", "Medium (grayscale)", 50 },
+ { 0xa130f919, 9056, 13790, 1, 1, -2, "Kodak Imaging", "Low (high res.)", 20 },
+ { 0xa130f919, 9056, 13790, 2, 1, -2, "Kodak Imaging", "Low (medium res.)", 20 },
+ { 0xa130f919, 9056, 13790, 4, 1, -2, "Kodak Imaging", "Low (low res.)", 20 },
+ { 0x34216b8b, 9056, 0, 1, 1, 1, "Kodak Imaging", "Low (grayscale)", 20 },
+ { 0x403b528f, 161, 179, 1, 1, -2, "Lead ?", "?", -98 },
+ { 0x8550a881, 711, 1055, 1, 1, -2, "Lead ?", "?", -90 },
+ { 0x98fb09fc, 1079, 1610, 1, 1, -2, "Lead ?", "?", -85 },
+ { 0xfbb88fb8, 2031, 3054, 1, 1, -2, "Lead ?", "?", -72 },
+ { 0x5fa57f78, 4835, 7226, 1, 1, -2, "Lead ?", "?", -37 },
+ { 0x85b97881, 8199, 12287, 1, 1, -2, "Lead ?", "?", -22 },
+ { 0xd3cd4ad0, 96, 117, 2, 1, -2, "Leica Digilux 3", "?", -98 },
+ { 0x29e095c5, 221, 333, 2, 1, -2, "Leica M8", "?", 97 },
+ { 0xee344795, 582, 836, 2, 1, -2, "Medion ?", "?", -92 },
+ { 0x991408d7, 433, 667, 2, 1, -2, "Medion ?", "?", -94 },
+ { 0x10b035e9, 1858, 2780, 2, 2, 2, "Microsoft Office", "Default", 75 },
+ { 0x20fcfcb8, 116, 169, 2, 1, -2, "Nikon D50, D70, D70s, D80", "Fine", -98 },
+ { 0x2530fec2, 218, 333, 2, 1, -2, "Nikon D70 or D70s", "Normal", -97 },
+ { 0xe5dbee70, 616, 941, 2, 1, -2, "Nikon D70 or D70s", "Basic", -91 },
+ { 0x0e082d61, 671, 999, 2, 1, -2, "Nikon D70 or D70s", "Basic + raw", -90 },
+ { 0xcc6c9703, 127, 169, 2, 1, -2, "Nikon D70 v1.0", "Fine", -98 },
+ { 0x8cdfa365, 302, 444, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x23246639, 315, 499, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x978378a8, 329, 500, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x748a8379, 346, 500, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0xa85255cd, 372, 558, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0x016406e0, 389, 560, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0xda3a50f1, 419, 611, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0xd8e45108, 449, 668, 2, 1, -2, "Nikon D70 v1.0", "Fine", -93 },
+ { 0x8a62bf3c, 506, 775, 2, 1, -2, "Nikon D70 v1.0", "Fine", -93 },
+ { 0xc3108c99, 529, 781, 2, 1, -2, "Nikon D70 v1.0", "Fine", -92 },
+ { 0xeabc51a5, 261, 389, 2, 1, -2, "Nikon D50", "Normal", -96 },
+ { 0x0cddf617, 345, 499, 2, 1, -2, "Nikon D50", "Normal", -95 },
+ { 0x2b3b6401, 855, 1279, 2, 1, -2, "Nikon D40", "?", -88 },
+ { 0x5d1ca944, 667, 999, 2, 1, -3, "Nikon E4300", "Normal", 91 },
+ { 0xabcbdc47, 736, 1110, 2, 1, -3, "Nikon E4300", "Normal", 90 },
+ { 0x10b2ad77, 884, 1332, 2, 1, -3, "Nikon E4300", "Normal", 88 },
+ { 0x0a82648b, 64, 64, 1, 1, -2, "Nikon Browser 6", "High quality", 100 },
+ { 0xb091eaf2, 779, 1164, 1, 1, -2, "Nikon Browser 6 or PictureProject 1.6", "Standard quality", -89 },
+ { 0x1a856066, 1697, 2554, 2, 1, -2, "Nikon Browser 6", "Standard eq", -76 },
+ { 0xdf0774bd, 2746, 5112, 2, 2, -2, "Nikon Browser 6", "Standard compression", -57 },
+ { 0xe2fd6fb9, 8024, 12006, 2, 2, -2, "Nikon Browser 6", "Maximum compression", -22 },
+ { 0x17816eb1, 736, 1110, 2, 2, -2, "Olympus Camedia Master", "High quality?", 90 },
+ { 0x96129a0d, 1477, 2221, 2, 2, -2, "Olympus u710,S710", "Super high quality?", 80 },
+ { 0x824f84b9, 437, 617, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -94 },
+ { 0x1b050d58, 447, 670, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -93 },
+ { 0x1b050d58, 447, 670, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -93 },
+ { 0x68058c37, 814, 1223, 2, 1, -3, "Olympus C960Z,D460Z", "Standard quality", 89 },
+ { 0x10b2ad77, 884, 1332, 2, 1, -3, "Olympus C211Z", "Standard quality", 88 },
+ { 0x0f5fa4cb, 1552, 2336, 2, 1, -3, "Olympus C990Z,D490Z", "High quality", 79 },
+ { 0xf51554a8, 261, 392, 2, 1, -2, "Panasonic DMC-FZ5", "High", -96 },
+ { 0xf01efe6e, 251, 392, 2, 1, -2, "Panasonic DMC-FZ30", "High", -96 },
+ { 0x08064360, 280, 445, 2, 1, -2, "Panasonic DMC-FZ30", "High", -96 },
+ { 0x05831bbb, 304, 448, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xe6c08bea, 316, 499, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xcb5f5f7d, 332, 550, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xb53cf359, 355, 555, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xdbcd2690, 375, 606, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0x594a3212, 400, 615, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0xde23f16a, 420, 667, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0xc0a43b37, 501, 775, 2, 1, -2, "Panasonic DMC-FZ30", "High", -93 },
+ { 0xc298e887, 577, 891, 2, 1, -2, "Panasonic DMC-FZ30", "High", -92 },
+ { 0x039b6bc2, 324, 499, 2, 1, 2, "Ricoh Caplio R1V", "?", -95 },
+ { 0xf60dc348, 274, 443, 2, 1, 2, "Roxio PhotoSuite", "?", -96 },
+ { 0xc6f47fa4, 634, 943, 2, 1, -2, "Samsung Digimax V3", "?", -91 },
+ { 0xb9284f39, 1313, 1997, 2, 1, -2, "Samsung Digimax V3", "?", -82 },
+ { 0x5dedca50, 218, 331, 2, 1, -2, "Samsung Digimax S600", "?", -97 },
+ { 0x095451e2, 258, 389, 2, 1, -2, "Sony Cybershot", "?", -96 },
+ { 0x4d981764, 86, 115, 2, 1, -2, "Sony DSC-W55", "Fine", 99 },
+ { 0x6d2b20ce, 122, 169, 2, 1, -2, "Sony DSC-F828, DSC-F88", "?", -98 },
+ { 0x29e095c5, 221, 333, 2, 1, -2, "Sony DSC-W30, DSC-W50, DSC-H2, DSC-H5", "?", 97 },
+ { 0x59e5c5bc, 518, 779, 2, 1, -2, "Sony DSC-W70", "?", 93 },
+ { 0x96129a0d, 1477, 2221, 2, 2, -2, "Sony DSC-W30, DSC-P43, DSC-S600", "?", 80 },
+ { 0xa4d9a6d9, 324, 682, 2, 1, -2, "Sony DSLR-A100", "?", -94 },
+ { 0x17816eb1, 736, 1110, 2, 1, -2, "SonyEricsson K750i", "Fine", 90 },
+ { 0x10b035e9, 1858, 2780, 2, 1, -2, "SonyEricsson K750i or W200i", "Normal", 75 },
+ { 0x1b0ad9d5, 836, 1094, 2, 2, -2, "SonyEricsson K750i", "Panorama fine", -89 },
+ { 0x1cd8bb9f, 1672, 2188, 2, 2, -2, "SonyEricsson K750i", "Panorama normal", -79 },
+ { 0x81d174af, 361, 555, 2, 1, -2, "SonyEricsson K750i", "?", -95 },
+ { 0x991408d7, 433, 667, 2, 1, -2, "SonyEricsson K750i", "?", -94 },
+ { 0x00034978, 954, 1443, 2, 1, -2, "SonyEricsson K750i", "?", -87 },
+ { 0xd27667ab, 1024, 1504, 2, 1, -2, "SonyEricsson K750i", "?", -86 },
+ { 0x94e96153, 1097, 1615, 2, 1, -2, "SonyEricsson K750i", "?", -85 },
+ { 0xf524688a, 1168, 1727, 2, 1, -2, "SonyEricsson K750i", "?", -84 },
+ { 0x5e5e4237, 1324, 2000, 2, 1, -2, "SonyEricsson K750i", "?", -82 },
+ { 0x2e94a836, 1473, 2170, 2, 1, -2, "SonyEricsson K750i", "?", -80 },
+ { 0xdd957ed4, 1615, 2394, 2, 1, -2, "SonyEricsson K750i", "?", -78 },
+ { 0x4147561e, 1759, 2612, 2, 1, -2, "SonyEricsson K750i", "?", -76 },
+ { 0x6f5af2b1, 1491, 1491, 2, 1, -2, "SonyEricsson Z600", "Default", -83 },
+ { 0x641ee862, 2211, 3284, 2, 1, -2, "Trust 760 Powerc@m", "?", 70 },
+ { 0x0bd95282, 2211, 3284, 1, 2, -2, "Trust 760 Powerc@m", "?", 70 },
+ { 0xe9814c86, 1830, 2725, 1, 1, 2, "Xing VT-Compress", "?", -75 },
+};
+
+typedef struct
+{
+ guint32 hashval;
+ gint subsmp_h;
+ gint subsmp_v;
+ gint num_quant_tables;
+ gint ijg_qual;
+ GSList *files;
+ guint16 luminance[DCTSIZE2];
+ guint16 chrominance[DCTSIZE2];
+} QuantTableData;
+
+static GSList *found_tables = NULL;
+
+#if 0 /* FIXME ---v-v-v---------------------------------------------v-v-v--- */
+
+static guint16 **ijg_luminance = NULL; /* luminance, baseline */
+static guint16 **ijg_chrominance = NULL; /* chrominance, baseline */
+static guint16 **ijg_luminance_nb = NULL; /* luminance, not baseline */
+static guint16 **ijg_chrominance_nb = NULL; /* chrominance, not baseline */
+
+/*
+ * Initialize the IJG quantization tables for each quality setting.
+ */
+static void
+init_ijg_tables (void)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ gint q, i;
+
+ ijg_luminance = g_new (guint16 *, 101);
+ ijg_chrominance = g_new (guint16 *, 101);
+ for (q = 0; q <= 100; q++)
+ {
+ ijg_luminance[q] = g_new (guint16, DCTSIZE2);
+ ijg_chrominance[q] = g_new (guint16, DCTSIZE2);
+ }
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_compress (&cinfo);
+
+ for (q = 0; q <= 100; q++)
+ {
+ jpeg_set_quality (&cinfo, q, TRUE);
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_luminance[q][i] = cinfo.quant_tbl_ptrs[0]->quantval[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_chrominance[q][i] = cinfo.quant_tbl_ptrs[1]->quantval[i];
+ }
+ for (q = 0; q <= 100; q++)
+ {
+ jpeg_set_quality (&cinfo, q, FALSE);
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_luminance_nb[q][i] = cinfo.quant_tbl_ptrs[0]->quantval[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_chrominance_nb[q][i] = cinfo.quant_tbl_ptrs[1]->quantval[i];
+ }
+ jpeg_destroy_compress (&cinfo);
+}
+
+/*
+ * Check if two quantization tables are identical.
+ */
+static gboolean
+compare_tables (const guint16 *quant_table1,
+ const guint16 *quant_table2)
+{
+ gint i;
+
+ g_return_val_if_fail (quant_table1 != NULL, FALSE);
+ g_return_val_if_fail (quant_table2 != NULL, FALSE);
+
+ for (i = 0; i < DCTSIZE2; i++)
+ if (quant_table1[i] != quant_table2[i])
+ return FALSE;
+ return TRUE;
+}
+
+#endif /* FIXME ---^-^-^--------------------------------------------^-^-^--- */
+
+/*
+ * Trivial hash function (simple, but good enough for 1 to 4 * 64 short ints).
+ */
+static guint32
+hash_quant_tables (struct jpeg_decompress_struct *cinfo)
+{
+ guint32 hashval;
+ gint t;
+ gint i;
+
+ hashval = 11;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ hashval = hashval * 4177 + cinfo->quant_tbl_ptrs[t]->quantval[i];
+ return hashval;
+}
+
+static guint32
+hash_transposed_quant_tables (struct jpeg_decompress_struct *cinfo)
+{
+ guint32 hashval;
+ gint t;
+ gint i;
+ gint j;
+
+ hashval = 11;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ hashval = hashval * 4177 + cinfo->quant_tbl_ptrs[t]->quantval[j * 8
+ + i];
+ return hashval;
+}
+
+static void
+add_unknown_table (struct jpeg_decompress_struct *cinfo,
+ gchar *filename)
+{
+ guint32 hashval;
+ GSList *list;
+ QuantTableData *table_data;
+ gint num_quant_tables;
+ gint t;
+ gint i;
+
+ hashval = hash_quant_tables (cinfo);
+
+ /* linear search - the number of unknown tables is usually very small */
+ for (list = found_tables; list; list = list->next)
+ {
+ table_data = list->data;
+ if (table_data->hashval == hashval
+ && table_data->subsmp_h == cinfo->comp_info[0].h_samp_factor
+ && table_data->subsmp_v == cinfo->comp_info[0].v_samp_factor)
+ {
+ /* this unknown table has already been seen in previous files */
+ table_data->files = g_slist_prepend (table_data->files, filename);
+ return;
+ }
+ }
+
+ /* not found => new table */
+ table_data = g_new (QuantTableData, 1);
+ table_data->hashval = hashval;
+ table_data->subsmp_h = cinfo->comp_info[0].h_samp_factor;
+ table_data->subsmp_v = cinfo->comp_info[0].v_samp_factor;
+
+ num_quant_tables = 0;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ num_quant_tables++;
+
+ table_data->num_quant_tables = num_quant_tables;
+ table_data->ijg_qual = jpeg_detect_quality (cinfo);
+ table_data->files = g_slist_prepend (NULL, filename);
+
+ if (cinfo->quant_tbl_ptrs[0])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->luminance[i] = cinfo->quant_tbl_ptrs[0]->quantval[i];
+ }
+ else
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->luminance[i] = 0;
+ }
+
+ if (cinfo->quant_tbl_ptrs[1])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->chrominance[i] = cinfo->quant_tbl_ptrs[1]->quantval[i];
+ }
+ else
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->chrominance[i] = 0;
+ }
+
+ found_tables = g_slist_prepend (found_tables, table_data);
+}
+
+/*
+ * Analyze the JPEG quantization tables and return a list of devices or
+ * software that can generate the same tables and subsampling factors.
+ */
+static GSList *
+detect_source (struct jpeg_decompress_struct *cinfo,
+ gint num_quant_tables)
+{
+ guint lum_sum;
+ guint chrom_sum;
+ gint i;
+ GSList *source_list;
+
+ /* compute sum of luminance and chrominance quantization tables */
+ lum_sum = 0;
+ chrom_sum = 0;
+ if (cinfo->quant_tbl_ptrs[0])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ lum_sum += cinfo->quant_tbl_ptrs[0]->quantval[i];
+ }
+ if (cinfo->quant_tbl_ptrs[1])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ chrom_sum += cinfo->quant_tbl_ptrs[1]->quantval[i];
+ }
+
+ /* there can be more than one match (if sampling factors are compatible) */
+ source_list = NULL;
+ if (chrom_sum == 0 && num_quant_tables == 1)
+ {
+ /* grayscale */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == cinfo->comp_info[0].h_samp_factor)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == cinfo->comp_info[0].v_samp_factor)
+ && quant_info[i].num_quant_tables > 0)
+ {
+ source_list = g_slist_append (source_list,
+ (gpointer) (quant_info + i));
+ }
+ }
+ }
+ else
+ {
+ /* RGB and other color spaces */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && quant_info[i].chrom_sum == chrom_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == cinfo->comp_info[0].h_samp_factor)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == cinfo->comp_info[0].v_samp_factor)
+ && (quant_info[i].num_quant_tables == num_quant_tables
+ || quant_info[i].num_quant_tables == -num_quant_tables))
+ {
+ source_list = g_slist_append (source_list,
+ (gpointer) (quant_info + i));
+ }
+ }
+ }
+
+ return source_list;
+}
+
+/*
+ * ... FIXME: docs
+ */
+static void
+print_summary (struct jpeg_decompress_struct *cinfo,
+ gint num_quant_tables)
+{
+ gint quality;
+ gint i;
+ GSList *source_list;
+
+ /* detect JPEG quality - test the formula used in the jpeg plug-in */
+ quality = jpeg_detect_quality (cinfo);
+ if (quality > 0)
+ g_print ("\tQuality: %02d (exact)\n", quality);
+ else if (quality < 0)
+ g_print ("\tQuality: %02d (approx)\n", -quality);
+ else
+ g_print ("\tQuality: unknown\n");
+
+ /* JPEG sampling factors */
+ g_print ("\tSampling: %dx%d",
+ cinfo->comp_info[0].h_samp_factor,
+ cinfo->comp_info[0].v_samp_factor);
+ if ((cinfo->num_components > 1 && cinfo->num_components != 3)
+ || cinfo->comp_info[1].h_samp_factor != 1
+ || cinfo->comp_info[1].v_samp_factor != 1
+ || cinfo->comp_info[2].h_samp_factor != 1
+ || cinfo->comp_info[2].v_samp_factor != 1)
+ {
+ for (i = 1; i < cinfo->num_components; i++)
+ g_print (",%dx%d",
+ cinfo->comp_info[i].h_samp_factor,
+ cinfo->comp_info[i].v_samp_factor);
+ }
+ g_print ("\n");
+
+ /* Number of quantization tables */
+ g_print ("\tQ.tables: %d\n", num_quant_tables);
+
+ source_list = detect_source (cinfo, num_quant_tables);
+ if (source_list)
+ {
+ GSList *l;
+ guint32 hash;
+ guint32 hash_t;
+
+ hash = hash_quant_tables (cinfo);
+ hash_t = hash_transposed_quant_tables (cinfo);
+
+ for (l = source_list; l; l = l->next)
+ {
+ QuantInfo *source_info = l->data;
+ const gchar *comment = "";
+
+ if (source_info->hash == hash)
+ comment = "";
+ else if (source_info->hash == hash_t)
+ comment = " (rotated)";
+ else if (num_quant_tables == 1)
+ comment = " (grayscale)";
+ else
+ comment = " (FALSE MATCH)";
+
+ g_print ("\tSource: %s - %s%s\n",
+ source_info->source_name,
+ source_info->setting_name,
+ comment);
+ }
+ g_slist_free (source_list);
+ }
+ else
+ g_print ("\tSource: unknown\n");
+}
+
+/*
+ * Print a quantization table as a C array.
+ */
+static void
+print_ctable (gint table_id,
+ const guint16 *quant_table,
+ gboolean more)
+{
+ gint i;
+
+ g_return_if_fail (quant_table != NULL);
+ if (table_id >= 0)
+ g_print (" { /* table %d */\n ", table_id);
+ else
+ g_print (" {\n ");
+ for (i = 0; i < DCTSIZE2; i++)
+ {
+ if (i == DCTSIZE2 - 1)
+ g_print ("%3d\n", quant_table[i]);
+ else if ((i + 1) % DCTSIZE == 0)
+ g_print ("%3d,\n ", quant_table[i]);
+ else
+ g_print ("%3d, ", quant_table[i]);
+ }
+ if (more)
+ g_print (" },\n");
+ else
+ g_print (" }\n");
+}
+
+/*
+ * Print one or two quantization tables, two columns.
+ */
+static void
+print_table_2cols (gint table1_id,
+ const guint16 *quant_table1,
+ gint table2_id,
+ const guint16 *quant_table2)
+{
+ gint i;
+ gint j;
+
+ if (quant_table2)
+ g_print ("\tQuantization table %d: Quantization table %d:\n\t",
+ table1_id, table2_id);
+ else
+ g_print ("\tQuantization table %d:\n\t", table1_id);
+ for (i = 0; i < DCTSIZE; i++)
+ {
+ if (quant_table1)
+ {
+ for (j = 0; j < DCTSIZE; j++)
+ {
+ if (j != DCTSIZE - 1)
+ g_print ("%3d ", quant_table1[i * DCTSIZE + j]);
+ else
+ {
+ if (quant_table2)
+ g_print ("%3d | ", quant_table1[i * DCTSIZE + j]);
+ else if (i != DCTSIZE - 1)
+ g_print ("%3d\n\t", quant_table1[i * DCTSIZE + j]);
+ else
+ g_print ("%3d\n", quant_table1[i * DCTSIZE + j]);
+ }
+ }
+ }
+ else
+ {
+ g_print (" | ");
+ }
+ if (quant_table2)
+ {
+ for (j = 0; j < DCTSIZE; j++)
+ {
+ if (j != DCTSIZE - 1)
+ g_print ("%3d ", quant_table2[i * DCTSIZE + j]);
+ else if (i != DCTSIZE - 1)
+ g_print ("%3d\n\t", quant_table2[i * DCTSIZE + j]);
+ else
+ g_print ("%3d\n", quant_table2[i * DCTSIZE + j]);
+ }
+ }
+ }
+}
+
+/*
+ * Error handling as in the IJG libjpeg example.
+ */
+typedef struct my_error_mgr
+{
+ struct jpeg_error_mgr pub; /* "public" fields */
+#ifdef __ia64__
+ long double dummy; /* bug #138357 */
+#endif
+ jmp_buf setjmp_buffer; /* for return to caller */
+} *my_error_ptr;
+
+static void
+my_error_exit (j_common_ptr cinfo)
+{
+ my_error_ptr myerr = (my_error_ptr) cinfo->err;
+ (*cinfo->err->output_message) (cinfo);
+ longjmp (myerr->setjmp_buffer, 1);
+}
+
+/*
+ * Analyze a JPEG file according to the command-line options.
+ */
+static gboolean
+analyze_file (gchar *filename)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ FILE *f;
+ gint i;
+ gint num_quant_tables;
+ GSList *source_list;
+
+ if ((f = fopen (filename, "rb")) == NULL)
+ {
+ g_printerr ("Cannot open '%s'\n", filename);
+ return FALSE;
+ }
+
+ if (option_summary)
+ g_print ("%s:\n", filename);
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* if we get here, the JPEG code has signaled an error. */
+ jpeg_destroy_decompress (&cinfo);
+ fclose (f);
+ return FALSE;
+ }
+ jpeg_create_decompress (&cinfo);
+
+ jpeg_stdio_src (&cinfo, f);
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ num_quant_tables = 0;
+ for (i = 0; i < 4; i++)
+ if (cinfo.quant_tbl_ptrs[i])
+ num_quant_tables++;
+
+ source_list = detect_source (&cinfo, num_quant_tables);
+ if (! source_list)
+ {
+ add_unknown_table (&cinfo, filename);
+ }
+
+ if (! option_unknown)
+ {
+ if (option_summary)
+ print_summary (&cinfo, num_quant_tables);
+
+ if (option_ctable)
+ {
+ g_print (" {\n /* %s */\n \"?\", \"?\",\n %d, %d,\n %d,\n",
+ filename,
+ cinfo.comp_info[0].h_samp_factor,
+ cinfo.comp_info[0].v_samp_factor,
+ num_quant_tables);
+ for (i = 0; i < 4; i++)
+ if (cinfo.quant_tbl_ptrs[i])
+ print_ctable (i, cinfo.quant_tbl_ptrs[i]->quantval,
+ (i < 3) && cinfo.quant_tbl_ptrs[i + 1]);
+ g_print (" },\n");
+ }
+
+ if (option_table_2cols)
+ {
+ print_table_2cols (0, cinfo.quant_tbl_ptrs[0]->quantval,
+ 1, cinfo.quant_tbl_ptrs[1]->quantval);
+ if (cinfo.quant_tbl_ptrs[2] || cinfo.quant_tbl_ptrs[3])
+ print_table_2cols (2, cinfo.quant_tbl_ptrs[2]->quantval,
+ 3, cinfo.quant_tbl_ptrs[3]->quantval);
+ }
+ }
+
+ if (source_list)
+ g_slist_free (source_list);
+
+ jpeg_destroy_decompress (&cinfo);
+ fclose (f);
+
+ return TRUE;
+}
+
+/*
+ * ... FIXME: docs
+ */
+static void
+print_unknown_tables (void)
+{
+ GSList *list;
+ GSList *flist;
+ QuantTableData *table_data;
+ gint num_files;
+ gint total_files = 0;
+
+ for (list = found_tables; list; list = list->next)
+ {
+ table_data = list->data;
+
+ if (option_ctable)
+ {
+ g_print (" {\n");
+ num_files = 0;
+ for (flist = table_data->files; flist; flist = flist->next)
+ {
+ g_print(" /* %s */\n", (gchar *)(flist->data));
+ num_files++;
+ }
+
+ { /* FIXME */
+ guint lum_sum;
+ guint chrom_sum;
+ gint i;
+
+ total_files += num_files;
+ lum_sum = 0;
+ chrom_sum = 0;
+ for (i = 0; i < DCTSIZE2; i++)
+ lum_sum += table_data->luminance[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ chrom_sum += table_data->chrominance[i];
+ g_print (" /* hash 0x%x, IJG %d, lum %d, chrom %d, files: %d */\n",
+ table_data->hashval,
+ table_data->ijg_qual,
+ lum_sum, chrom_sum,
+ num_files);
+
+ if (chrom_sum == 0 && table_data->num_quant_tables == 1)
+ {
+ /* grayscale */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == table_data->subsmp_h)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == table_data->subsmp_v)
+ && quant_info[i].num_quant_tables > 0)
+ {
+ g_print(" XXX \"%s\", \"%s\",\n",
+ quant_info[i].source_name,
+ quant_info[i].setting_name);
+ }
+ }
+ }
+ else
+ {
+ /* RGB and other color spaces */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && quant_info[i].chrom_sum == chrom_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == table_data->subsmp_h)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == table_data->subsmp_v)
+ && (quant_info[i].num_quant_tables == table_data->num_quant_tables
+ || quant_info[i].num_quant_tables == -table_data->num_quant_tables))
+ {
+ g_print(" XXX \"%s\", \"%s\",\n",
+ quant_info[i].source_name,
+ quant_info[i].setting_name);
+ }
+ }
+ }
+ } /* FIXME */
+
+ g_print (" \"?\", \"? (hash %x)\",\n"
+ " %d, %d,\n %d,\n",
+ table_data->hashval,
+ table_data->subsmp_h,
+ table_data->subsmp_v,
+ -table_data->num_quant_tables);
+ print_ctable (-1, table_data->luminance, TRUE);
+ print_ctable (-1, table_data->chrominance, FALSE);
+ g_print (" },\n");
+ }
+ }
+ g_print ("/* TOTAL FILES: %d */\n", total_files);
+}
+
+/*
+ * Some compiler told me that it needed a function called main()...
+ */
+int
+main (int argc,
+ char *argv[])
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ gint i;
+
+ g_set_prgname ("jpegqual");
+
+ context =
+ g_option_context_new ("FILE [...] - analyzes JPEG quantization tables");
+ g_option_context_add_main_entries (context, option_entries,
+ NULL /* skip i18n? */);
+
+ if (! g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ return EXIT_FAILURE;
+ }
+
+ if (! filenames)
+ {
+ g_printerr ("Missing file name. Try the option --help for help\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!option_summary && !option_ctable && !option_table_2cols)
+ {
+ g_printerr ("Missing output option. Assuming that you wanted --summary.\n");
+ option_summary = TRUE;
+ }
+
+ for (i = 0; filenames[i]; i++)
+ {
+ if (! analyze_file (filenames[i]) && ! option_ignore_err)
+ return EXIT_FAILURE;
+ }
+
+ if (option_unknown && found_tables)
+ {
+ print_unknown_tables ();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/plug-ins/file-psd/Makefile.am b/plug-ins/file-psd/Makefile.am
new file mode 100644
index 0000000..10ce648
--- /dev/null
+++ b/plug-ins/file-psd/Makefile.am
@@ -0,0 +1,74 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_psd_RC = file-psd.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-psd
+
+libexec_PROGRAMS = \
+ file-psd
+
+file_psd_SOURCES = \
+ psd.c \
+ psd.h \
+ psd-util.c \
+ psd-util.h \
+ psd-load.c \
+ psd-load.h \
+ psd-save.c \
+ psd-save.h \
+ psd-thumb-load.c \
+ psd-thumb-load.h \
+ psd-image-res-load.c \
+ psd-image-res-load.h \
+ psd-layer-res-load.c \
+ psd-layer-res-load.h
+
+EXTRA_DIST = \
+ TODO.txt \
+ new-resource-ids.txt
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"file-psd\" \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+file_psd_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(EXIF_LIBS) \
+ $(IPTCDATA_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(Z_LIBS) \
+ $(file_psd_RC)
diff --git a/plug-ins/file-psd/Makefile.in b/plug-ins/file-psd/Makefile.in
new file mode 100644
index 0000000..54d5598
--- /dev/null
+++ b/plug-ins/file-psd/Makefile.in
@@ -0,0 +1,1048 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-psd$(EXEEXT)
+subdir = plug-ins/file-psd
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_psd_OBJECTS = psd.$(OBJEXT) psd-util.$(OBJEXT) \
+ psd-load.$(OBJEXT) psd-save.$(OBJEXT) psd-thumb-load.$(OBJEXT) \
+ psd-image-res-load.$(OBJEXT) psd-layer-res-load.$(OBJEXT)
+file_psd_OBJECTS = $(am_file_psd_OBJECTS)
+am__DEPENDENCIES_1 =
+file_psd_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(file_psd_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/psd-image-res-load.Po \
+ ./$(DEPDIR)/psd-layer-res-load.Po ./$(DEPDIR)/psd-load.Po \
+ ./$(DEPDIR)/psd-save.Po ./$(DEPDIR)/psd-thumb-load.Po \
+ ./$(DEPDIR)/psd-util.Po ./$(DEPDIR)/psd.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_psd_SOURCES)
+DIST_SOURCES = $(file_psd_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-psd
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_psd_RC = file-psd.rc.o
+AM_LDFLAGS = $(mwindows)
+file_psd_SOURCES = \
+ psd.c \
+ psd.h \
+ psd-util.c \
+ psd-util.h \
+ psd-load.c \
+ psd-load.h \
+ psd-save.c \
+ psd-save.h \
+ psd-thumb-load.c \
+ psd-thumb-load.h \
+ psd-image-res-load.c \
+ psd-image-res-load.h \
+ psd-layer-res-load.c \
+ psd-layer-res-load.h
+
+EXTRA_DIST = \
+ TODO.txt \
+ new-resource-ids.txt
+
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"file-psd\" \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+file_psd_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(EXIF_LIBS) \
+ $(IPTCDATA_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(Z_LIBS) \
+ $(file_psd_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-psd/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-psd/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-psd$(EXEEXT): $(file_psd_OBJECTS) $(file_psd_DEPENDENCIES) $(EXTRA_file_psd_DEPENDENCIES)
+ @rm -f file-psd$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_psd_OBJECTS) $(file_psd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-image-res-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-layer-res-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-thumb-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd-util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psd.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/psd-image-res-load.Po
+ -rm -f ./$(DEPDIR)/psd-layer-res-load.Po
+ -rm -f ./$(DEPDIR)/psd-load.Po
+ -rm -f ./$(DEPDIR)/psd-save.Po
+ -rm -f ./$(DEPDIR)/psd-thumb-load.Po
+ -rm -f ./$(DEPDIR)/psd-util.Po
+ -rm -f ./$(DEPDIR)/psd.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/psd-image-res-load.Po
+ -rm -f ./$(DEPDIR)/psd-layer-res-load.Po
+ -rm -f ./$(DEPDIR)/psd-load.Po
+ -rm -f ./$(DEPDIR)/psd-save.Po
+ -rm -f ./$(DEPDIR)/psd-thumb-load.Po
+ -rm -f ./$(DEPDIR)/psd-util.Po
+ -rm -f ./$(DEPDIR)/psd.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-psd/TODO.txt b/plug-ins/file-psd/TODO.txt
new file mode 100644
index 0000000..f02ff74
--- /dev/null
+++ b/plug-ins/file-psd/TODO.txt
@@ -0,0 +1,54 @@
+Load
+====
+Photoshop 2.0 and lower files are not supported due to lack of
+file specs and test files.
+
+Add text names for color modes
+
+Parasite for layer blending ranges per channel?
+
+Read & use global mask data
+
+Check for block over-run in read_channel_data
+
+Process errors from readers in read_channel_data
+
+Invert layer mask - channel_set_show_masked or invert mask + parasite
+
+decode more image resources
+
+Read in layer resources and process adjustment layers etc.
+add parasites as required - should be similar to image resources
+
+add layer support for:
+
+Text layers
+
+vector masks
+
+Image resources:
+================
+
+1005 - resolution
+ width and height units dropped.
+
+1007 - Display info
+ save color space & color as parasite if unable to display.
+
+1008 - caption - Add to XMP data block.
+
+1025 - Working path (not saved) - Load
+
+1026 - Layers Group Info - ?Load
+
+1033 1036
+ Better error handling for Jpeg decompression. Check for JPEG being included
+ Custom error function.
+
+1041 - Honour ICC untagged option
+
+2000-2998 - Paths
+ Add initial fill rule and clipboard parasites.
+
+2999 - Clipping path
+ Add as parasite to path record.
diff --git a/plug-ins/file-psd/new-resource-ids.txt b/plug-ins/file-psd/new-resource-ids.txt
new file mode 100644
index 0000000..561559a
--- /dev/null
+++ b/plug-ins/file-psd/new-resource-ids.txt
@@ -0,0 +1,23 @@
+Unknown resource ID's seen in PSD files but not yet identified.
+
+8BIM 1061 (0x0425)
+8BIM 1062 (0x0426)
+
+8BIM 1064 (0x0428)
+
+8BIM 1069 (0x042d)
+
+8BIM 1072 (0x0430)
+
+8BIM 1077 (0x0435)
+
+MeSa 7002 (0x165a)
+MeSa 7003 (0x165b)
+
+8BIM 4000 (0x0fa0)
+8BIM 4001 (0x0fa1)
+8BIM 4002 (0x0fa2)
+8BIM 4003 (0x0fa3)
+8BIM 4004 (0x0fa4)
+8BIM 4005 (0x0fa5)
+8BIM 4006 (0x0fa6)
diff --git a/plug-ins/file-psd/psd-image-res-load.c b/plug-ins/file-psd/psd-image-res-load.c
new file mode 100644
index 0000000..a634bc6
--- /dev/null
+++ b/plug-ins/file-psd/psd-image-res-load.c
@@ -0,0 +1,1611 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* ----- Known Image Resource Block Types -----
+ All image resources not otherwise handled, including unknown types
+ are added as image parasites.
+ The data is attached as-is from the file (i.e. in big endian order).
+
+ PSD_PS2_IMAGE_INFO = 1000, Dropped * 0x03e8 - Obsolete - ps 2.0 image info *
+ PSD_MAC_PRINT_INFO = 1001, PS Only * 0x03e9 - Optional - Mac print manager print info record *
+ PSD_PS2_COLOR_TAB = 1003, Dropped * 0x03eb - Obsolete - ps 2.0 indexed color table *
+ PSD_RESN_INFO = 1005, Loaded * 0x03ed - ResolutionInfo structure *
+ PSD_ALPHA_NAMES = 1006, Loaded * 0x03ee - Alpha channel names *
+ PSD_DISPLAY_INFO = 1007, Loaded * 0x03ef - DisplayInfo structure *
+ PSD_CAPTION = 1008, Loaded * 0x03f0 - Optional - Caption string *
+ PSD_BORDER_INFO = 1009, * 0x03f1 - Border info *
+ PSD_BACKGROUND_COL = 1010, * 0x03f2 - Background color *
+ PSD_PRINT_FLAGS = 1011, * 0x03f3 - Print flags *
+ PSD_GREY_HALFTONE = 1012, * 0x03f4 - Greyscale and multichannel halftoning info *
+ PSD_COLOR_HALFTONE = 1013, * 0x03f5 - Color halftoning info *
+ PSD_DUOTONE_HALFTONE = 1014, * 0x03f6 - Duotone halftoning info *
+ PSD_GREY_XFER = 1015, * 0x03f7 - Greyscale and multichannel transfer functions *
+ PSD_COLOR_XFER = 1016, * 0x03f8 - Color transfer functions *
+ PSD_DUOTONE_XFER = 1017, * 0x03f9 - Duotone transfer functions *
+ PSD_DUOTONE_INFO = 1018, * 0x03fa - Duotone image information *
+ PSD_EFFECTIVE_BW = 1019, * 0x03fb - Effective black & white values for dot range *
+ PSD_OBSOLETE_01 = 1020, Dropped * 0x03fc - Obsolete *
+ PSD_EPS_OPT = 1021, * 0x03fd - EPS options *
+ PSD_QUICK_MASK = 1022, Loaded * 0x03fe - Quick mask info *
+ PSD_OBSOLETE_02 = 1023, Dropped * 0x03ff - Obsolete *
+ PSD_LAYER_STATE = 1024, Loaded * 0x0400 - Layer state info *
+ PSD_WORKING_PATH = 1025, * 0x0401 - Working path (not saved) *
+ PSD_LAYER_GROUP = 1026, * 0x0402 - Layers group info *
+ PSD_OBSOLETE_03 = 1027, Dropped * 0x0403 - Obsolete *
+ PSD_IPTC_NAA_DATA = 1028, Loaded * 0x0404 - IPTC-NAA record (IMV4.pdf) *
+ PSD_IMAGE_MODE_RAW = 1029, * 0x0405 - Image mode for raw format files *
+ PSD_JPEG_QUAL = 1030, PS Only * 0x0406 - JPEG quality *
+ PSD_GRID_GUIDE = 1032, Loaded * 0x0408 - Grid & guide info *
+ PSD_THUMB_RES = 1033, Special * 0x0409 - Thumbnail resource *
+ PSD_COPYRIGHT_FLG = 1034, * 0x040a - Copyright flag *
+ PSD_URL = 1035, * 0x040b - URL string *
+ PSD_THUMB_RES2 = 1036, Special * 0x040c - Thumbnail resource *
+ PSD_GLOBAL_ANGLE = 1037, * 0x040d - Global angle *
+ PSD_COLOR_SAMPLER = 1038, * 0x040e - Color samplers resource *
+ PSD_ICC_PROFILE = 1039, Loaded * 0x040f - ICC Profile *
+ PSD_WATERMARK = 1040, * 0x0410 - Watermark *
+ PSD_ICC_UNTAGGED = 1041, * 0x0411 - Do not use ICC profile flag *
+ PSD_EFFECTS_VISIBLE = 1042, * 0x0412 - Show hide all effects layers *
+ PSD_SPOT_HALFTONE = 1043, * 0x0413 - Spot halftone *
+ PSD_DOC_IDS = 1044, * 0x0414 - Document specific IDs *
+ PSD_ALPHA_NAMES_UNI = 1045, Loaded * 0x0415 - Unicode alpha names *
+ PSD_IDX_COL_TAB_CNT = 1046, Loaded * 0x0416 - Indexed color table count *
+ PSD_IDX_TRANSPARENT = 1047, * 0x0417 - Index of transparent color (if any) *
+ PSD_GLOBAL_ALT = 1049, * 0x0419 - Global altitude *
+ PSD_SLICES = 1050, * 0x041a - Slices *
+ PSD_WORKFLOW_URL_UNI = 1051, * 0x041b - Workflow URL - Unicode string *
+ PSD_JUMP_TO_XPEP = 1052, * 0x041c - Jump to XPEP (?) *
+ PSD_ALPHA_ID = 1053, Loaded * 0x041d - Alpha IDs *
+ PSD_URL_LIST_UNI = 1054, * 0x041e - URL list - unicode *
+ PSD_VERSION_INFO = 1057, * 0x0421 - Version info *
+ PSD_EXIF_DATA = 1058, Loaded * 0x0422 - Exif data block 1 *
+ PSD_EXIF_DATA_3 = 1059 * 0X0423 - Exif data block 3 (?) *
+ PSD_XMP_DATA = 1060, Loaded * 0x0424 - XMP data block *
+ PSD_CAPTION_DIGEST = 1061, * 0x0425 - Caption digest *
+ PSD_PRINT_SCALE = 1062, * 0x0426 - Print scale *
+ PSD_PIXEL_AR = 1064, * 0x0428 - Pixel aspect ratio *
+ PSD_LAYER_COMPS = 1065, * 0x0429 - Layer comps *
+ PSD_ALT_DUOTONE_COLOR = 1066, * 0x042A - Alternative Duotone colors *
+ PSD_ALT_SPOT_COLOR = 1067, * 0x042B - Alternative Spot colors *
+ PSD_LAYER_SELECT_ID = 1069, * 0x042D - Layer selection ID *
+ PSD_HDR_TONING_INFO = 1070, * 0x042E - HDR toning information *
+ PSD_PRINT_INFO_SCALE = 1071, * 0x042F - Print scale *
+ PSD_LAYER_GROUP_E_ID = 1072, * 0x0430 - Layer group(s) enabled ID *
+ PSD_COLOR_SAMPLER_NEW = 1073, * 0x0431 - Color sampler resource for ps CS3 and higher PSD files *
+ PSD_MEASURE_SCALE = 1074, * 0x0432 - Measurement scale *
+ PSD_TIMELINE_INFO = 1075, * 0x0433 - Timeline information *
+ PSD_SHEET_DISCLOSE = 1076, * 0x0434 - Sheet discloser *
+ PSD_DISPLAY_INFO_NEW = 1077, Loaded * 0x0435 - DisplayInfo structure for ps CS3 and higher PSD files *
+ PSD_ONION_SKINS = 1078, * 0x0436 - Onion skins *
+ PSD_COUNT_INFO = 1080, * 0x0438 - Count information*
+ PSD_PRINT_INFO = 1082, * 0x043A - Print information added in ps CS5*
+ PSD_PRINT_STYLE = 1083, * 0x043B - Print style *
+ PSD_MAC_NSPRINTINFO = 1084, * 0x043C - Mac NSPrintInfo*
+ PSD_WIN_DEVMODE = 1085, * 0x043D - Windows DEVMODE *
+ PSD_AUTO_SAVE_PATH = 1086, * 0x043E - Auto save file path *
+ PSD_AUTO_SAVE_FORMAT = 1087, * 0x043F - Auto save format *
+ PSD_PATH_INFO_FIRST = 2000, Loaded * 0x07d0 - First path info block *
+ PSD_PATH_INFO_LAST = 2998, Loaded * 0x0bb6 - Last path info block *
+ PSD_CLIPPING_PATH = 2999, * 0x0bb7 - Name of clipping path *
+ PSD_PLUGIN_R_FIRST = 4000, * 0x0FA0 - First plugin resource *
+ PSD_PLUGIN_R_LAST = 4999, * 0x1387 - Last plugin resource *
+ PSD_IMAGEREADY_VARS = 7000, PS Only * 0x1B58 - Imageready variables *
+ PSD_IMAGEREADY_DATA = 7001, PS Only * 0x1B59 - Imageready data sets *
+ PSD_LIGHTROOM_WORK = 8000, PS Only * 0x1F40 - Lightroom workflow *
+ PSD_PRINT_FLAGS_2 = 10000 * 0x2710 - Print flags *
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <libgimp/gimp.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#ifdef HAVE_IPTCDATA
+#include <libiptcdata/iptc-data.h>
+#endif /* HAVE_IPTCDATA */
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-image-res-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define EXIF_HEADER_SIZE 8
+
+/* Local function prototypes */
+static gint load_resource_unknown (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_ps_only (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1005 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1006 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1007 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1008 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1022 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1024 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1028 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1032 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1033 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1039 (const PSDimageres *res_a,
+ PSDimage *img_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1045 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1046 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1053 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1058 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_1077 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_2000 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+/* Public Functions */
+gint
+get_image_resource_header (PSDimageres *res_a,
+ FILE *f,
+ GError **error)
+{
+ gint32 read_len;
+ gint32 write_len;
+ gchar *name;
+
+ if (fread (&res_a->type, 4, 1, f) < 1
+ || fread (&res_a->id, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ res_a->id = GUINT16_FROM_BE (res_a->id);
+ name = fread_pascal_string (&read_len, &write_len, 2, f, error);
+ if (*error)
+ return -1;
+ if (name != NULL)
+ g_strlcpy (res_a->name, name, write_len + 1);
+ else
+ res_a->name[0] = 0x0;
+ g_free (name);
+ if (fread (&res_a->data_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ res_a->data_len = GUINT32_FROM_BE (res_a->data_len);
+ res_a->data_start = ftell (f);
+
+ IFDBG(2) g_debug ("Type: %.4s, id: %d, start: %d, len: %d",
+ res_a->type, res_a->id, res_a->data_start, res_a->data_len);
+
+ return 0;
+}
+
+gint
+load_image_resource (PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ gint pad;
+
+ /* Set file position to start of image resource data block */
+ if (fseek (f, res_a->data_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Process image resource blocks */
+ if (memcmp (res_a->type, "8BIM", 4) != 0 &&
+ memcmp (res_a->type, "MeSa", 4) !=0)
+ {
+ IFDBG(1) g_debug ("Unknown image resource type signature %.4s",
+ res_a->type);
+ }
+ else
+ {
+ switch (res_a->id)
+ {
+ case PSD_PS2_IMAGE_INFO:
+ case PSD_PS2_COLOR_TAB:
+ case PSD_OBSOLETE_01:
+ case PSD_OBSOLETE_02:
+ case PSD_OBSOLETE_03:
+ /* Drop obsolete image resource blocks */
+ IFDBG(2) g_debug ("Obsolete image resource block: %d",
+ res_a->id);
+ break;
+
+ case PSD_THUMB_RES:
+ case PSD_THUMB_RES2:
+ /* Drop thumbnails from standard file load */
+ IFDBG(2) g_debug ("Thumbnail resource block: %d",
+ res_a->id);
+ break;
+
+ case PSD_MAC_PRINT_INFO:
+ case PSD_JPEG_QUAL:
+ /* Save photoshop resources with no meaning for GIMP
+ as image parasites */
+ load_resource_ps_only (res_a, image_id, f, error);
+ break;
+
+ case PSD_RESN_INFO:
+ if (! load_resource_1005 (res_a, image_id, f, error))
+ *resolution_loaded = TRUE;
+ break;
+
+ case PSD_ALPHA_NAMES:
+ if (! img_a->merged_image_only)
+ load_resource_1006 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_DISPLAY_INFO:
+ load_resource_1007 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_CAPTION:
+ load_resource_1008 (res_a, image_id, f, error);
+ break;
+
+ case PSD_QUICK_MASK:
+ if (! img_a->merged_image_only)
+ load_resource_1022 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_LAYER_STATE:
+ if (! img_a->merged_image_only)
+ load_resource_1024 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_WORKING_PATH:
+ if (! img_a->merged_image_only)
+ load_resource_2000 (res_a, image_id, f, error);
+ break;
+
+ case PSD_IPTC_NAA_DATA:
+ load_resource_1028 (res_a, image_id, f, error);
+ break;
+
+ case PSD_GRID_GUIDE:
+ if (! img_a->merged_image_only)
+ load_resource_1032 (res_a, image_id, f, error);
+ break;
+
+ case PSD_ICC_PROFILE:
+ if (! load_resource_1039 (res_a, img_a, image_id, f, error))
+ *profile_loaded = TRUE;
+ break;
+
+ case PSD_ALPHA_NAMES_UNI:
+ if (! img_a->merged_image_only)
+ load_resource_1045 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_IDX_COL_TAB_CNT:
+ load_resource_1046 (res_a, image_id, f, error);
+ break;
+
+ case PSD_ALPHA_ID:
+ if (! img_a->merged_image_only)
+ load_resource_1053 (res_a, image_id, img_a, f, error);
+ break;
+
+ case PSD_EXIF_DATA:
+ load_resource_1058 (res_a, image_id, f, error);
+ break;
+
+ case PSD_XMP_DATA:
+ break;
+
+ case PSD_DISPLAY_INFO_NEW:
+ load_resource_1077 (res_a, image_id, img_a, f, error);
+ break;
+
+ default:
+ if (res_a->id >= 2000 &&
+ res_a->id < 2999)
+ load_resource_2000 (res_a, image_id, f, error);
+ else
+ load_resource_unknown (res_a, image_id, f, error);
+ }
+ }
+
+ /* Image blocks are null padded to even length */
+ if (res_a->data_len % 2 == 0)
+ pad = 0;
+ else
+ pad = 1;
+
+ /* Set file position to end of image resource block */
+ if (fseek (f, res_a->data_start + res_a->data_len + pad, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+gint
+load_thumbnail_resource (PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ gint rtn = 0;
+ gint pad;
+
+ /* Set file position to start of image resource data block */
+ if (fseek (f, res_a->data_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Process image resource blocks */
+ if (res_a->id == PSD_THUMB_RES
+ || res_a->id == PSD_THUMB_RES2)
+ {
+ /* Load thumbnails from standard file load */
+ load_resource_1033 (res_a, image_id, f, error);
+ rtn = 1;
+ }
+
+ /* Image blocks are null padded to even length */
+ if (res_a->data_len % 2 == 0)
+ pad = 0;
+ else
+ pad = 1;
+
+ /* Set file position to end of image resource block */
+ if (fseek (f, res_a->data_start + res_a->data_len + pad, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return rtn;
+}
+
+/* Private Functions */
+
+static gint
+load_resource_unknown (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Unknown image resources attached as parasites to re-save later */
+ GimpParasite *parasite;
+ gchar *data;
+ gchar *name;
+
+ IFDBG(2) g_debug ("Process unknown image resource block: %d", res_a->id);
+
+ data = g_malloc (res_a->data_len);
+ if (res_a->data_len > 0 && fread (data, res_a->data_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (data);
+ return -1;
+ }
+
+ name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+ res_a->type, res_a->id);
+ IFDBG(2) g_debug ("Parasite name: %s", name);
+
+ parasite = gimp_parasite_new (name, 0, res_a->data_len, data);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ g_free (data);
+ g_free (name);
+
+ return 0;
+}
+
+static gint
+load_resource_ps_only (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Save photoshop resources with no meaning for GIMP as image parasites
+ to re-save later */
+ GimpParasite *parasite;
+ gchar *data;
+ gchar *name;
+
+ IFDBG(3) g_debug ("Process image resource block: %d", res_a->id);
+
+ data = g_malloc (res_a->data_len);
+ if (fread (data, res_a->data_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (data);
+ return -1;
+ }
+
+ name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+ res_a->type, res_a->id);
+ IFDBG(2) g_debug ("Parasite name: %s", name);
+
+ parasite = gimp_parasite_new (name, 0, res_a->data_len, data);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ g_free (data);
+ g_free (name);
+
+ return 0;
+}
+
+static gint
+load_resource_1005 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load image resolution and unit of measure */
+
+ /* FIXME width unit and height unit unused at present */
+
+ ResolutionInfo res_info;
+ GimpUnit image_unit;
+
+ IFDBG(2) g_debug ("Process image resource block 1005: Resolution Info");
+
+ if (fread (&res_info.hRes, 4, 1, f) < 1
+ || fread (&res_info.hResUnit, 2, 1, f) < 1
+ || fread (&res_info.widthUnit, 2, 1, f) < 1
+ || fread (&res_info.vRes, 4, 1, f) < 1
+ || fread (&res_info.vResUnit, 2, 1, f) < 1
+ || fread (&res_info.heightUnit, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ res_info.hRes = GINT32_FROM_BE (res_info.hRes);
+ res_info.hResUnit = GINT16_FROM_BE (res_info.hResUnit);
+ res_info.widthUnit = GINT16_FROM_BE (res_info.widthUnit);
+ res_info.vRes = GINT32_FROM_BE (res_info.vRes);
+ res_info.vResUnit = GINT16_FROM_BE (res_info.vResUnit);
+ res_info.heightUnit = GINT16_FROM_BE (res_info.heightUnit);
+
+ IFDBG(3) g_debug ("Resolution: %d, %d, %d, %d, %d, %d",
+ res_info.hRes,
+ res_info.hResUnit,
+ res_info.widthUnit,
+ res_info.vRes,
+ res_info.vResUnit,
+ res_info.heightUnit);
+
+ /* Resolution always recorded as pixels / inch in a fixed point implied
+ decimal int32 with 16 bits before point and 16 after (i.e. cast as
+ double and divide resolution by 2^16 */
+ gimp_image_set_resolution (image_id,
+ res_info.hRes / 65536.0, res_info.vRes / 65536.0);
+
+ /* GIMP only has one display unit so use ps horizontal resolution unit */
+ switch (res_info.hResUnit)
+ {
+ case PSD_RES_INCH:
+ image_unit = GIMP_UNIT_INCH;
+ break;
+ case PSD_RES_CM:
+ image_unit = GIMP_UNIT_MM;
+ break;
+ default:
+ image_unit = GIMP_UNIT_INCH;
+ }
+
+ gimp_image_set_unit (image_id, image_unit);
+
+ return 0;
+}
+
+static gint
+load_resource_1006 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load alpha channel names stored as a series of pascal strings
+ unpadded between strings */
+
+ gchar *str;
+ gint32 block_rem;
+ gint32 read_len;
+ gint32 write_len;
+
+ IFDBG(2) g_debug ("Process image resource block 1006: Alpha Channel Names");
+
+ if (img_a->alpha_names)
+ {
+ IFDBG(3) g_debug ("Alpha names loaded from unicode resource block");
+ return 0;
+ }
+
+ img_a->alpha_names = g_ptr_array_new ();
+
+ block_rem = res_a->data_len;
+ while (block_rem > 1)
+ {
+ str = fread_pascal_string (&read_len, &write_len, 1, f, error);
+ if (*error)
+ return -1;
+ IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len);
+ if (write_len >= 0)
+ {
+ g_ptr_array_add (img_a->alpha_names, (gpointer) str);
+ }
+ block_rem -= read_len;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_1007 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load alpha channel display info */
+
+ DisplayInfo dsp_info;
+ CMColor ps_color;
+ GimpRGB gimp_rgb;
+ GimpHSV gimp_hsv;
+ GimpCMYK gimp_cmyk;
+ gint16 tot_rec;
+ gint cidx;
+
+ IFDBG(2) g_debug ("Process image resource block 1007: Display Info");
+ tot_rec = res_a->data_len / 14;
+ if (tot_rec == 0)
+ return 0;
+
+ img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec);
+ img_a->alpha_display_count = tot_rec;
+ for (cidx = 0; cidx < tot_rec; ++cidx)
+ {
+ if (fread (&dsp_info.colorSpace, 2, 1, f) < 1
+ || fread (&dsp_info.color, 8, 1, f) < 1
+ || fread (&dsp_info.opacity, 2, 1, f) < 1
+ || fread (&dsp_info.kind, 1, 1, f) < 1
+ || fread (&dsp_info.padding, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace);
+ ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]);
+ ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]);
+ ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]);
+ ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]);
+ dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity);
+
+ switch (dsp_info.colorSpace)
+ {
+ case PSD_CS_RGB:
+ gimp_rgb_set (&gimp_rgb, ps_color.rgb.red / 65535.0,
+ ps_color.rgb.green / 65535.0,
+ ps_color.rgb.blue / 65535.0);
+ break;
+
+ case PSD_CS_HSB:
+ gimp_hsv_set (&gimp_hsv, ps_color.hsv.hue / 65535.0,
+ ps_color.hsv.saturation / 65535.0,
+ ps_color.hsv.value / 65535.0);
+ gimp_hsv_to_rgb (&gimp_hsv, &gimp_rgb);
+ break;
+
+ case PSD_CS_CMYK:
+ gimp_cmyk_set (&gimp_cmyk, 1.0 - ps_color.cmyk.cyan / 65535.0,
+ 1.0 - ps_color.cmyk.magenta / 65535.0,
+ 1.0 - ps_color.cmyk.yellow / 65535.0,
+ 1.0 - ps_color.cmyk.black / 65535.0);
+ gimp_cmyk_to_rgb (&gimp_cmyk, &gimp_rgb);
+ break;
+
+ case PSD_CS_GRAYSCALE:
+ gimp_rgb_set (&gimp_rgb, ps_color.gray.gray / 10000.0,
+ ps_color.gray.gray / 10000.0,
+ ps_color.gray.gray / 10000.0);
+ break;
+
+ case PSD_CS_FOCOLTONE:
+ case PSD_CS_TRUMATCH:
+ case PSD_CS_HKS:
+ case PSD_CS_LAB:
+ case PSD_CS_PANTONE:
+ case PSD_CS_TOYO:
+ case PSD_CS_DIC:
+ case PSD_CS_ANPA:
+ default:
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported color space: %d",
+ dsp_info.colorSpace);
+ gimp_rgb_set (&gimp_rgb, 1.0, 0.0, 0.0);
+ }
+
+ gimp_rgb_set_alpha (&gimp_rgb, 1.0);
+
+ IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, kind: %d",
+ dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta,
+ ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity,
+ dsp_info.kind);
+
+ IFDBG(2) g_debug ("cSpace: %d, col: %g %g %g, opacity: %d, kind: %d",
+ dsp_info.colorSpace, gimp_rgb.r * 255 , gimp_rgb.g * 255,
+ gimp_rgb.b * 255, dsp_info.opacity, dsp_info.kind);
+
+ img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
+ img_a->alpha_display_info[cidx]->gimp_color = gimp_rgb;
+ img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity;
+ img_a->alpha_display_info[cidx]->ps_kind = dsp_info.kind;
+ img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace;
+ img_a->alpha_display_info[cidx]->ps_color = ps_color;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_1008 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load image caption */
+ GimpParasite *parasite;
+ gchar *caption;
+ gint32 read_len;
+ gint32 write_len;
+
+ IFDBG(2) g_debug ("Process image resource block: 1008: Caption");
+ caption = fread_pascal_string (&read_len, &write_len, 1, f, error);
+ if (*error)
+ return -1;
+
+ IFDBG(3) g_debug ("Caption: %s", caption);
+ parasite = gimp_parasite_new (GIMP_PARASITE_COMMENT, GIMP_PARASITE_PERSISTENT,
+ write_len, caption);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ g_free (caption);
+
+ return 0;
+}
+
+static gint
+load_resource_1022 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load quick mask info */
+ gboolean quick_mask_empty; /* Quick mask initially empty */
+
+ IFDBG(2) g_debug ("Process image resource block: 1022: Quick Mask");
+
+ if (fread (&img_a->quick_mask_id, 2, 1, f) < 1
+ || fread (&quick_mask_empty, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->quick_mask_id = GUINT16_FROM_BE (img_a->quick_mask_id);
+
+ IFDBG(3) g_debug ("Quick mask channel: %d, empty: %d",
+ img_a->quick_mask_id,
+ quick_mask_empty);
+
+ return 0;
+}
+
+static gint
+load_resource_1024 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load image layer state - current active layer counting from bottom up */
+ IFDBG(2) g_debug ("Process image resource block: 1024: Layer State");
+
+ if (fread (&img_a->layer_state, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->layer_state = GUINT16_FROM_BE (img_a->layer_state);
+
+ return 0;
+}
+
+static gint
+load_resource_1028 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load IPTC data block */
+
+#ifdef HAVE_IPTCDATA
+ IptcData *iptc_data;
+ guchar *iptc_buf;
+ guint iptc_buf_len;
+#else
+ gchar *name;
+#endif /* HAVE_IPTCDATA */
+
+ GimpParasite *parasite;
+ gchar *res_data;
+
+ IFDBG(2) g_debug ("Process image resource block: 1028: IPTC data");
+
+ res_data = g_malloc (res_a->data_len);
+ if (fread (res_data, res_a->data_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (res_data);
+ return -1;
+ }
+
+#ifdef HAVE_IPTCDATA
+ /* Load IPTC data structure */
+ iptc_data = iptc_data_new_from_data (res_data, res_a->data_len);
+ IFDBG (3) iptc_data_dump (iptc_data, 0);
+
+ /* Store resource data as a GIMP IPTC parasite */
+ IFDBG (2) g_debug ("Processing IPTC data as GIMP IPTC parasite");
+ /* Serialize IPTC data */
+ iptc_data_save (iptc_data, &iptc_buf, &iptc_buf_len);
+ if (iptc_buf_len > 0)
+ {
+ parasite = gimp_parasite_new (GIMP_PARASITE_IPTC,
+ GIMP_PARASITE_PERSISTENT,
+ iptc_buf_len, iptc_buf);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ iptc_data_unref (iptc_data);
+ g_free (iptc_buf);
+
+#else
+ /* Store resource data as a standard psd parasite */
+ IFDBG (2) g_debug ("Processing IPTC data as psd parasite");
+ name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+ res_a->type, res_a->id);
+ IFDBG(3) g_debug ("Parasite name: %s", name);
+
+ parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ g_free (name);
+
+#endif /* HAVE_IPTCDATA */
+
+ g_free (res_data);
+ return 0;
+}
+
+static gint
+load_resource_1032 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load grid and guides */
+
+ /* Grid info is not used (CS2 or earlier) */
+
+ GuideHeader hdr;
+ GuideResource guide;
+ gint i;
+
+ IFDBG(2) g_debug ("Process image resource block 1032: Grid and Guide Info");
+
+ if (fread (&hdr.fVersion, 4, 1, f) < 1
+ || fread (&hdr.fGridCycleV, 4, 1, f) < 1
+ || fread (&hdr.fGridCycleH, 4, 1, f) < 1
+ || fread (&hdr.fGuideCount, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ hdr.fVersion = GUINT32_FROM_BE (hdr.fVersion);
+ hdr.fGridCycleV = GUINT32_FROM_BE (hdr.fGridCycleV);
+ hdr.fGridCycleH = GUINT32_FROM_BE (hdr.fGridCycleH);
+ hdr.fGuideCount = GUINT32_FROM_BE (hdr.fGuideCount);
+
+ IFDBG(3) g_debug ("Grids & Guides: %d, %d, %d, %d",
+ hdr.fVersion,
+ hdr.fGridCycleV,
+ hdr.fGridCycleH,
+ hdr.fGuideCount);
+
+ for (i = 0; i < hdr.fGuideCount; ++i)
+ {
+ if (fread (&guide.fLocation, 4, 1, f) < 1
+ || fread (&guide.fDirection, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ guide.fLocation = GUINT32_FROM_BE (guide.fLocation);
+ guide.fLocation /= 32;
+
+ IFDBG(3) g_debug ("Guide: %d px, %d",
+ guide.fLocation,
+ guide.fDirection);
+
+ if (guide.fDirection == PSD_VERTICAL)
+ gimp_image_add_vguide (image_id, guide.fLocation);
+ else
+ gimp_image_add_hguide (image_id, guide.fLocation);
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_1033 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load thumbnail image */
+
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ ThumbnailInfo thumb_info;
+ GeglBuffer *buffer;
+ const Babl *format;
+ gint32 layer_id;
+ guchar *buf;
+ guchar *rgb_buf;
+ guchar **rowbuf;
+ gint i;
+
+ IFDBG(2) g_debug ("Process image resource block %d: Thumbnail Image", res_a->id);
+
+ /* Read thumbnail resource header info */
+ if (fread (&thumb_info.format, 4, 1, f) < 1
+ || fread (&thumb_info.width, 4, 1, f) < 1
+ || fread (&thumb_info.height, 4, 1, f) < 1
+ || fread (&thumb_info.widthbytes, 4, 1, f) < 1
+ || fread (&thumb_info.size, 4, 1, f) < 1
+ || fread (&thumb_info.compressedsize, 4, 1, f) < 1
+ || fread (&thumb_info.bitspixel, 2, 1, f) < 1
+ || fread (&thumb_info.planes, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ thumb_info.format = GINT32_FROM_BE (thumb_info.format);
+ thumb_info.width = GINT32_FROM_BE (thumb_info.width);
+ thumb_info.height = GINT32_FROM_BE (thumb_info.height);
+ thumb_info.widthbytes = GINT32_FROM_BE (thumb_info.widthbytes);
+ thumb_info.size = GINT32_FROM_BE (thumb_info.size);
+ thumb_info.compressedsize = GINT32_FROM_BE (thumb_info.compressedsize);
+ thumb_info.bitspixel = GINT16_FROM_BE (thumb_info.bitspixel);
+ thumb_info.planes = GINT16_FROM_BE (thumb_info.planes);
+
+ IFDBG(2) g_debug ("\nThumbnail:\n"
+ "\tFormat: %d\n"
+ "\tDimensions: %d x %d\n",
+ thumb_info.format,
+ thumb_info.width,
+ thumb_info.height);
+
+ if (thumb_info.format != 1)
+ {
+ IFDBG(1) g_debug ("Unknown thumbnail format %d", thumb_info.format);
+ return -1;
+ }
+
+ /* Load Jpeg RGB thumbnail info */
+
+ /* Step 1: Allocate and initialize JPEG decompression object */
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_decompress (&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+ jpeg_stdio_src(&cinfo, f);
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* Step 4: set parameters for decompression */
+
+
+ /* Step 5: Start decompressor */
+ jpeg_start_decompress (&cinfo);
+
+ /* temporary buffers */
+ buf = g_new (guchar, cinfo.output_height * cinfo.output_width
+ * cinfo.output_components);
+ if (res_a->id == PSD_THUMB_RES)
+ rgb_buf = g_new (guchar, cinfo.output_height * cinfo.output_width
+ * cinfo.output_components);
+ else
+ rgb_buf = NULL;
+ rowbuf = g_new (guchar *, cinfo.output_height);
+
+ for (i = 0; i < cinfo.output_height; ++i)
+ rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
+
+ /* Create image layer */
+ gimp_image_resize (image_id, cinfo.output_width, cinfo.output_height, 0, 0);
+ layer_id = gimp_layer_new (image_id, _("Background"),
+ cinfo.output_width,
+ cinfo.output_height,
+ GIMP_RGB_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+ buffer = gimp_drawable_get_buffer (layer_id);
+ format = babl_format ("R'G'B' u8");
+
+ /* Step 6: while (scan lines remain to be read) */
+ /* jpeg_read_scanlines(...); */
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines (&cinfo,
+ (JSAMPARRAY) &rowbuf[cinfo.output_scanline], 1);
+ }
+
+ if (res_a->id == PSD_THUMB_RES) /* Order is BGR for resource 1033 */
+ {
+ guchar *dst = rgb_buf;
+ guchar *src = buf;
+
+ for (i = 0; i < gegl_buffer_get_width (buffer) * gegl_buffer_get_height (buffer); ++i)
+ {
+ guchar r, g, b;
+
+ r = *(src++);
+ g = *(src++);
+ b = *(src++);
+ *(dst++) = b;
+ *(dst++) = g;
+ *(dst++) = r;
+ }
+ }
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0,
+ gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)),
+ 0, format, rgb_buf ? rgb_buf : buf, GEGL_AUTO_ROWSTRIDE);
+
+ /* Step 7: Finish decompression */
+ jpeg_finish_decompress (&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ /* Step 8: Release JPEG decompression object */
+ jpeg_destroy_decompress (&cinfo);
+
+ /* free up the temporary buffers */
+ g_free (rowbuf);
+ g_free (buf);
+ g_free (rgb_buf);
+
+ /* At this point you may want to check to see whether any
+ * corrupt-data warnings occurred (test whether
+ * jerr.num_warnings is nonzero).
+ */
+ gimp_image_insert_layer (image_id, layer_id, -1, 0);
+ g_object_unref (buffer);
+
+ return 0;
+}
+
+static gint
+load_resource_1039 (const PSDimageres *res_a,
+ PSDimage *img_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load ICC profile */
+ GimpColorProfile *profile;
+ gchar *icc_profile;
+
+ IFDBG(2) g_debug ("Process image resource block: 1039: ICC Profile");
+
+ icc_profile = g_malloc (res_a->data_len);
+ if (fread (icc_profile, res_a->data_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (icc_profile);
+ return -1;
+ }
+
+ profile = gimp_color_profile_new_from_icc_profile ((guint8 *) icc_profile,
+ res_a->data_len,
+ NULL);
+ if (profile)
+ {
+ if (img_a->color_mode == PSD_CMYK &&
+ gimp_color_profile_is_cmyk (profile))
+ {
+ img_a->cmyk_profile = profile;
+ }
+ else
+ {
+ gimp_image_set_color_profile (image_id, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ g_free (icc_profile);
+
+ return 0;
+}
+
+static gint
+load_resource_1045 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load alpha channel names stored as a series of unicode strings
+ in a GPtrArray */
+
+ gchar *str;
+ gint32 block_rem;
+ gint32 read_len;
+ gint32 write_len;
+
+ IFDBG(2) g_debug ("Process image resource block 1045: Unicode Alpha Channel Names");
+
+ if (img_a->alpha_names)
+ {
+ gint i;
+ IFDBG(3) g_debug ("Deleting localised alpha channel names");
+ for (i = 0; i < img_a->alpha_names->len; ++i)
+ {
+ str = g_ptr_array_index (img_a->alpha_names, i);
+ g_free (str);
+ }
+ g_ptr_array_free (img_a->alpha_names, TRUE);
+ }
+
+ img_a->alpha_names = g_ptr_array_new ();
+
+ block_rem = res_a->data_len;
+ while (block_rem > 1)
+ {
+ str = fread_unicode_string (&read_len, &write_len, 1, f, error);
+ if (*error)
+ return -1;
+
+ IFDBG(3) g_debug ("String: %s, %d, %d", str, read_len, write_len);
+ if (write_len >= 0)
+ {
+ g_ptr_array_add (img_a->alpha_names, (gpointer) str);
+ }
+ block_rem -= read_len;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_1046 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ /* Load indexed color table count */
+ guchar *cmap;
+ gint32 cmap_count = 0;
+ gint16 index_count = 0;
+
+ IFDBG(2) g_debug ("Process image resource block: 1046: Indexed Color Table Count");
+
+ if (fread (&index_count, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ index_count = GINT16_FROM_BE (index_count);
+
+ IFDBG(3) g_debug ("Indexed color table count: %d", index_count);
+ /* FIXME - check that we have indexed image */
+ if (index_count && index_count < 256)
+ {
+ cmap = gimp_image_get_colormap (image_id, &cmap_count);
+ if (cmap && index_count < cmap_count)
+ gimp_image_set_colormap (image_id, cmap, index_count);
+ g_free (cmap);
+ }
+ return 0;
+}
+
+static gint
+load_resource_1053 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load image alpha channel ids (tattoos) */
+ gint16 tot_rec;
+ gint16 cidx;
+
+ IFDBG(2) g_debug ("Process image resource block: 1053: Channel ID");
+
+ tot_rec = res_a->data_len / 4;
+ if (tot_rec ==0)
+ return 0;
+
+ img_a->alpha_id = g_malloc (sizeof (img_a->alpha_id) * tot_rec);
+ img_a->alpha_id_count = tot_rec;
+ for (cidx = 0; cidx < tot_rec; ++cidx)
+ {
+ if (fread (&img_a->alpha_id[cidx], 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->alpha_id[cidx] = GUINT32_FROM_BE (img_a->alpha_id[cidx]);
+
+ IFDBG(3) g_debug ("Channel id: %d", img_a->alpha_id[cidx]);
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_1058 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ gchar *name;
+
+ GimpParasite *parasite;
+ gchar *res_data;
+
+ IFDBG(2) g_debug ("Process image resource block: 1058: Exif data");
+
+ res_data = g_malloc (res_a->data_len);
+ if (fread (res_data, res_a->data_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (res_data);
+ return -1;
+ }
+
+ /* Store resource data as a standard psd parasite */
+ IFDBG (2) g_debug ("Processing exif data as psd parasite");
+ name = g_strdup_printf ("psd-image-resource-%.4s-%.4x",
+ res_a->type, res_a->id);
+ IFDBG(3) g_debug ("Parasite name: %s", name);
+
+ parasite = gimp_parasite_new (name, 0, res_a->data_len, res_data);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ g_free (name);
+
+ g_free (res_data);
+ return 0;
+}
+
+static gint
+load_resource_1077 (const PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load alpha channel display info */
+
+ DisplayInfoNew dsp_info;
+ CMColor ps_color;
+ GimpRGB gimp_rgb;
+ GimpHSV gimp_hsv;
+ GimpCMYK gimp_cmyk;
+ gint16 tot_rec;
+ gint cidx;
+
+ IFDBG(2) g_debug ("Process image resource block 1077: Display Info New");
+
+ /* For now, skip first 4 bytes since intention is unclear. Seems to be
+ a version number that is always one, but who knows. */
+ fseek (f, 4, SEEK_CUR);
+
+ tot_rec = res_a->data_len / 13;
+ if (tot_rec == 0)
+ return 0;
+
+ img_a->alpha_display_info = g_new (PSDchanneldata *, tot_rec);
+ img_a->alpha_display_count = tot_rec;
+ for (cidx = 0; cidx < tot_rec; ++cidx)
+ {
+ if (fread (&dsp_info.colorSpace, 2, 1, f) < 1
+ || fread (&dsp_info.color, 8, 1, f) < 1
+ || fread (&dsp_info.opacity, 2, 1, f) < 1
+ || fread (&dsp_info.mode, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ dsp_info.colorSpace = GINT16_FROM_BE (dsp_info.colorSpace);
+ ps_color.cmyk.cyan = GUINT16_FROM_BE (dsp_info.color[0]);
+ ps_color.cmyk.magenta = GUINT16_FROM_BE (dsp_info.color[1]);
+ ps_color.cmyk.yellow = GUINT16_FROM_BE (dsp_info.color[2]);
+ ps_color.cmyk.black = GUINT16_FROM_BE (dsp_info.color[3]);
+ dsp_info.opacity = GINT16_FROM_BE (dsp_info.opacity);
+
+ switch (dsp_info.colorSpace)
+ {
+ case PSD_CS_RGB:
+ gimp_rgb_set (&gimp_rgb, ps_color.rgb.red / 65535.0,
+ ps_color.rgb.green / 65535.0,
+ ps_color.rgb.blue / 65535.0);
+ break;
+
+ case PSD_CS_HSB:
+ gimp_hsv_set (&gimp_hsv, ps_color.hsv.hue / 65535.0,
+ ps_color.hsv.saturation / 65535.0,
+ ps_color.hsv.value / 65535.0);
+ gimp_hsv_to_rgb (&gimp_hsv, &gimp_rgb);
+ break;
+
+ case PSD_CS_CMYK:
+ gimp_cmyk_set (&gimp_cmyk, 1.0 - ps_color.cmyk.cyan / 65535.0,
+ 1.0 - ps_color.cmyk.magenta / 65535.0,
+ 1.0 - ps_color.cmyk.yellow / 65535.0,
+ 1.0 - ps_color.cmyk.black / 65535.0);
+ gimp_cmyk_to_rgb (&gimp_cmyk, &gimp_rgb);
+ break;
+
+ case PSD_CS_GRAYSCALE:
+ gimp_rgb_set (&gimp_rgb, ps_color.gray.gray / 10000.0,
+ ps_color.gray.gray / 10000.0,
+ ps_color.gray.gray / 10000.0);
+ break;
+
+ case PSD_CS_FOCOLTONE:
+ case PSD_CS_TRUMATCH:
+ case PSD_CS_HKS:
+ case PSD_CS_LAB:
+ case PSD_CS_PANTONE:
+ case PSD_CS_TOYO:
+ case PSD_CS_DIC:
+ case PSD_CS_ANPA:
+ default:
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported color space: %d",
+ dsp_info.colorSpace);
+ gimp_rgb_set (&gimp_rgb, 1.0, 0.0, 0.0);
+ }
+
+ gimp_rgb_set_alpha (&gimp_rgb, 1.0);
+
+ IFDBG(2) g_debug ("PS cSpace: %d, col: %d %d %d %d, opacity: %d, mode: %d",
+ dsp_info.colorSpace, ps_color.cmyk.cyan, ps_color.cmyk.magenta,
+ ps_color.cmyk.yellow, ps_color.cmyk.black, dsp_info.opacity,
+ dsp_info.mode);
+
+ IFDBG(2) g_debug ("cSpace: %d, col: %g %g %g, opacity: %d, mode: %d",
+ dsp_info.colorSpace, gimp_rgb.r * 255 , gimp_rgb.g * 255,
+ gimp_rgb.b * 255, dsp_info.opacity, dsp_info.mode);
+
+ img_a->alpha_display_info[cidx] = g_malloc0 (sizeof (PSDchanneldata));
+ img_a->alpha_display_info[cidx]->gimp_color = gimp_rgb;
+ img_a->alpha_display_info[cidx]->opacity = dsp_info.opacity;
+ img_a->alpha_display_info[cidx]->ps_mode = dsp_info.mode;
+ img_a->alpha_display_info[cidx]->ps_cspace = dsp_info.colorSpace;
+ img_a->alpha_display_info[cidx]->ps_color = ps_color;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_2000 (const PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error)
+{
+ gdouble *controlpoints;
+ gint32 x[3];
+ gint32 y[3];
+ gint32 vector_id = -1;
+ gint16 type;
+ gint16 init_fill;
+ gint16 num_rec;
+ gint16 path_rec;
+ gint16 cntr;
+ gint image_width;
+ gint image_height;
+ gint i;
+ gboolean closed;
+
+ /* Load path data from image resources 2000-2998 */
+
+ IFDBG(2) g_debug ("Process image resource block: %d :Path data", res_a->id);
+ path_rec = res_a->data_len / 26;
+ if (path_rec ==0)
+ return 0;
+
+ if (fread (&type, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ type = GINT16_FROM_BE (type);
+ if (type != PSD_PATH_FILL_RULE)
+ {
+ IFDBG(1) g_debug ("Unexpected path record type: %d", type);
+ return -1;
+ }
+
+ if (fseek (f, 24, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ path_rec--;
+ if (path_rec ==0)
+ return 0;
+
+ image_width = gimp_image_width (image_id);
+ image_height = gimp_image_height (image_id);
+
+ /* Create path */
+ if (res_a->id == PSD_WORKING_PATH)
+ {
+ /* use "Working Path" for the path name to match the Photoshop display */
+ vector_id = gimp_vectors_new (image_id, "Working Path");
+ }
+ else
+ {
+ /* Use the name stored in the PSD to name the path */
+ vector_id = gimp_vectors_new (image_id, res_a->name);
+ }
+
+ gimp_image_insert_vectors (image_id, vector_id, -1, -1);
+
+ while (path_rec > 0)
+ {
+ if (fread (&type, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ type = GINT16_FROM_BE (type);
+ IFDBG(3) g_debug ("Path record type %d", type);
+
+ if (type == PSD_PATH_FILL_RULE)
+ {
+ if (fseek (f, 24, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+
+ else if (type == PSD_PATH_FILL_INIT)
+ {
+ if (fread (&init_fill, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ if (fseek (f, 22, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+
+ else if (type == PSD_PATH_CL_LEN
+ || type == PSD_PATH_OP_LEN)
+ {
+ if (fread (&num_rec, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ num_rec = GINT16_FROM_BE (num_rec);
+ if (num_rec > path_rec)
+ {
+ psd_set_error (feof (f), errno, error);
+ return - 1;
+ }
+ IFDBG(3) g_debug ("Num path records %d", num_rec);
+
+ if (type == PSD_PATH_CL_LEN)
+ closed = TRUE;
+ else
+ closed = FALSE;
+ cntr = 0;
+ controlpoints = g_malloc (sizeof (gdouble) * num_rec * 6);
+ if (fseek (f, 22, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (controlpoints);
+ return -1;
+ }
+
+ while (num_rec > 0)
+ {
+ if (fread (&type, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ type = GINT16_FROM_BE (type);
+ IFDBG(3) g_debug ("Path record type %d", type);
+
+ if (type == PSD_PATH_CL_LNK
+ || type == PSD_PATH_CL_UNLNK
+ || type == PSD_PATH_OP_LNK
+ || type == PSD_PATH_OP_UNLNK)
+ {
+ if (fread (&y[0], 4, 1, f) < 1
+ || fread (&x[0], 4, 1, f) < 1
+ || fread (&y[1], 4, 1, f) < 1
+ || fread (&x[1], 4, 1, f) < 1
+ || fread (&y[2], 4, 1, f) < 1
+ || fread (&x[2], 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ x[i] = GINT32_FROM_BE (x[i]);
+ controlpoints[cntr] = x[i] / 16777216.0 * image_width;
+ cntr++;
+ y[i] = GINT32_FROM_BE (y[i]);
+ controlpoints[cntr] = y[i] / 16777216.0 * image_height;
+ cntr++;
+ }
+ IFDBG(3) g_debug ("Path points (%d,%d), (%d,%d), (%d,%d)",
+ x[0], y[0], x[1], y[1], x[2], y[2]);
+ }
+ else
+ {
+ IFDBG(1) g_debug ("Unexpected path type record %d", type);
+ if (fseek (f, 24, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ path_rec--;
+ num_rec--;
+ }
+ /* Add sub-path */
+ gimp_vectors_stroke_new_from_points (vector_id,
+ GIMP_VECTORS_STROKE_TYPE_BEZIER,
+ cntr, controlpoints, closed);
+ g_free (controlpoints);
+ }
+
+ else
+ {
+ if (fseek (f, 24, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+
+ path_rec--;
+ }
+
+ return 0;
+}
diff --git a/plug-ins/file-psd/psd-image-res-load.h b/plug-ins/file-psd/psd-image-res-load.h
new file mode 100644
index 0000000..c981a6b
--- /dev/null
+++ b/plug-ins/file-psd/psd-image-res-load.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_IMAGE_RES_LOAD_H__
+#define __PSD_IMAGE_RES_LOAD_H__
+
+
+gint get_image_resource_header (PSDimageres *res_a,
+ FILE *f,
+ GError **error);
+
+gint load_image_resource (PSDimageres *res_a,
+ gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+gint load_thumbnail_resource (PSDimageres *res_a,
+ gint32 image_id,
+ FILE *f,
+ GError **error);
+
+
+#endif /* __PSD_IMAGE_RES_LOAD_H__ */
diff --git a/plug-ins/file-psd/psd-layer-res-load.c b/plug-ins/file-psd/psd-layer-res-load.c
new file mode 100644
index 0000000..d2207b3
--- /dev/null
+++ b/plug-ins/file-psd/psd-layer-res-load.c
@@ -0,0 +1,944 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* ----- Known Layer Resource Block Types -----
+ All layer resources not otherwise handled, including unknown types
+ are dropped with a warning.
+
+ * Adjustment layer IDs *
+ PSD_LADJ_LEVEL "levl" Drop Layer * Adjustment layer - levels (PS4) *
+ PSD_LADJ_CURVE "curv" Drop Layer * Adjustment layer - curves (PS4) *
+ PSD_LADJ_BRIGHTNESS "brit" Drop Layer * Adjustment layer - brightness contrast (PS4) *
+ PSD_LADJ_BALANCE "blnc" Drop Layer * Adjustment layer - color balance (PS4) *
+ PSD_LADJ_BLACK_WHITE "blwh" Drop Layer * Adjustment layer - black & white (PS10) *
+ PSD_LADJ_HUE "hue " Drop Layer * Adjustment layer - old hue saturation (PS4) *
+ PSD_LADJ_HUE2 "hue2" Drop Layer * Adjustment layer - hue saturation (PS5) *
+ PSD_LADJ_SELECTIVE "selc" Drop Layer * Adjustment layer - selective color (PS4) *
+ PSD_LADJ_MIXER "mixr" Drop Layer * Adjustment layer - channel mixer (PS9) *
+ PSD_LADJ_GRAD_MAP "grdm" Drop Layer * Adjustment layer - gradient map (PS9) *
+ PSD_LADJ_PHOTO_FILT "phfl" Drop Layer * Adjustment layer - photo filter (PS9) *
+ PSD_LADJ_EXPOSURE "expA" Drop Layer * Adjustment layer - exposure (PS10) *
+ PSD_LADJ_INVERT "nvrt" Drop Layer * Adjustment layer - invert (PS4) *
+ PSD_LADJ_THRESHOLD "thrs" Drop Layer * Adjustment layer - threshold (PS4) *
+ PSD_LADJ_POSTERIZE "post" Drop Layer * Adjustment layer - posterize (PS4) *
+ PSD_LADJ_VIBRANCE "vibA" - * Adjustment layer - vibrance (PS10) *
+ PSD_LADJ_COLOR_LOOKUP "clrL" - * Adjustment layer - color lookup (PS13) *
+
+ * Fill Layer IDs *
+ PSD_LFIL_SOLID "SoCo" - * Solid color sheet setting (PS6) *
+ PSD_LFIL_PATTERN "PtFl" - * Pattern fill setting (PS6) *
+ PSD_LFIL_GRADIENT "GdFl" - * Gradient fill setting (PS6) *
+
+ * Effects Layer IDs *
+ PSD_LFX_FX "lrFX" - * Effects layer info (PS5) *
+ PSD_LFX_FX2 "lfx2" - * Object based effects layer info (PS6) *
+
+ * Type Tool Layers *
+ PSD_LTYP_TYPE "tySh" - * Type tool layer (PS5) *
+ PSD_LTYP_TYPE2 "TySh" - * Type tool object setting (PS6) *
+
+ * Layer Properties *
+ PSD_LPRP_UNICODE "luni" Loaded * Unicode layer name (PS5) *
+ PSD_LPRP_SOURCE "lnsr" Loaded * Layer name source setting (PS6) *
+ PSD_LPRP_ID "lyid" Loaded * Layer ID (PS5) *
+ PSD_LPRP_BLEND_CLIP "clbl" - * Blend clipping elements (PS6) *
+ PSD_LPRP_BLEND_INT "infx" - * Blend interior elements (PS6) *
+ PSD_LPRP_KNOCKOUT "knko" - * Knockout setting (PS6) *
+ PSD_LPRP_PROTECT "lspf" - * Protected setting (PS6) *
+ PSD_LPRP_COLOR "lclr" - * Sheet color setting (PS6) *
+ PSD_LPRP_REF_POINT "fxrp" - * Reference point (PS6) *
+ PSD_LPRP_VERSION "lyvr" Loaded * Layer version (PS7) *
+
+ * Vector mask *
+ PSD_LMSK_VMASK "vmsk" - * Vector mask setting (PS6) *
+
+ * Parasites *
+ PSD_LPAR_ANNOTATE "Anno" - * Annotation (PS6) *
+
+ * Other *
+ PSD_LOTH_PATTERN "Patt" - * Patterns (PS6) *
+ PSD_LOTH_GRADIENT "grdm" - * Gradient settings (PS6) *
+ PSD_LOTH_SECTION "lsct" Loaded * Section divider setting (PS6) (Layer Groups) *
+ PSD_LOTH_SECTION2 "lsdk" Loaded * Nested section divider setting (CS5) (Layer Groups) *
+ PSD_LOTH_RESTRICT "brst" - * Channel blending restriction setting (PS6) *
+ PSD_LOTH_FOREIGN_FX "ffxi" - * Foreign effect ID (PS6) *
+ PSD_LOTH_PATT_DATA "shpa" - * Pattern data (PS6) *
+ PSD_LOTH_META_DATA "shmd" - * Meta data setting (PS6) *
+ PSD_LOTH_LAYER_DATA "layr" - * Layer data (PS6) *
+ PSD_LOTH_CONTENT_GEN "CgEd" - * Content generator extra data (PS12) *
+ PSD_LOTH_TEXT_ENGINE "Txt2" - * Text engine data (PS10) *
+ PSD_LOTH_PATH_NAME "pths" - * Unicode path name (PS13) *
+ PSD_LOTH_ANIMATION_FX "anFX" - * Animation effects (PS13) *
+ PSD_LOTH_FILTER_MASK "FMsk" - * Filter mask (PS10) *
+ PSD_LOTH_VECTOR_STROKE "vscg" - * Vector stroke data (PS13) *
+ PSD_LOTH_ALIGN_RENDER "sn2P" - * Aligned rendering flag (?) *
+ PSD_LOTH_USER_MASK "LMsk" - * User mask (?) *
+
+ * Effects layer resource IDs *
+ PSD_LFX_COMMON "cmnS" - * Effects layer - common state (PS5) *
+ PSD_LFX_DROP_SDW "dsdw" - * Effects layer - drop shadow (PS5) *
+ PSD_LFX_INNER_SDW "isdw" - * Effects layer - inner shadow (PS5) *
+ PSD_LFX_OUTER_GLW "oglw" - * Effects layer - outer glow (PS5) *
+ PSD_LFX_INNER_GLW "iglw" - * Effects layer - inner glow (PS5) *
+ PSD_LFX_BEVEL "bevl" - * Effects layer - bevel (PS5) *
+
+ * New stuff temporarily until I can get them sorted out *
+
+ * Placed Layer *
+ PSD_LPL_PLACE_LAYER "plLd" - * Placed layer (?) *
+ PSD_LPL_PLACE_LAYER_NEW "SoLd" - * Placed layer (PS10) *
+
+ * Linked Layer *
+ PSD_LLL_LINKED_LAYER "lnkD" - * Linked layer (?) *
+ PSD_LLL_LINKED_LAYER_2 "lnk2" - * Linked layer 2nd key *
+ PSD_LLL_LINKED_LAYER_3 "lnk3" - * Linked layer 3rd key *
+
+ * Merged Transparency *
+ PSD_LMT_MERGE_TRANS "Mtrn" - * Merged transparency save flag (?) *
+ PSD_LMT_MERGE_TRANS_16 "Mt16" - * Merged transparency save flag 2 *
+ PSD_LMT_MERGE_TRANS_32 "Mt32" - * Merged transparency save flag 3 *
+
+ * Filter Effects *
+ PSD_LFFX_FILTER_FX "FXid" - * Filter effects (?) *
+ PSD_LFFX_FILTER_FX_2 "FEid" - * Filter effects 2 *
+*/
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <libgimp/gimp.h>
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-layer-res-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+/* Local function prototypes */
+static gint load_resource_unknown (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_ladj (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lfil (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lfx (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_ltyp (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_luni (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lyid (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lclr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lsct (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lrfx (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lyvr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint load_resource_lnsr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+/* Public Functions */
+gint
+get_layer_resource_header (PSDlayerres *res_a,
+ FILE *f,
+ GError **error)
+{
+ if (fread (res_a->sig, 4, 1, f) < 1
+ || fread (res_a->key, 4, 1, f) < 1
+ || fread (&res_a->data_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ res_a->data_len = GUINT32_FROM_BE (res_a->data_len);
+ res_a->data_start = ftell (f);
+
+ IFDBG(2) g_debug ("Sig: %.4s, key: %.4s, start: %d, len: %d",
+ res_a->sig, res_a->key, res_a->data_start, res_a->data_len);
+
+ return 0;
+}
+
+gint
+load_layer_resource (PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Set file position to start of layer resource data block */
+ if (fseek (f, res_a->data_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Process layer resource blocks */
+ if (memcmp (res_a->sig, "8BIM", 4) != 0)
+ {
+ IFDBG(1) g_debug ("Unknown layer resource signature %.4s", res_a->sig);
+ }
+ else
+ {
+ if (memcmp (res_a->key, PSD_LADJ_LEVEL, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_CURVE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_BRIGHTNESS, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_BALANCE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_BLACK_WHITE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_HUE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_HUE2, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_SELECTIVE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_MIXER, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_GRAD_MAP, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_PHOTO_FILT, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_EXPOSURE, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_THRESHOLD, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_INVERT, 4) == 0
+ || memcmp (res_a->key, PSD_LADJ_POSTERIZE, 4) == 0)
+ load_resource_ladj (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LFIL_SOLID, 4) == 0
+ || memcmp (res_a->key, PSD_LFIL_PATTERN, 4) == 0
+ || memcmp (res_a->key, PSD_LFIL_GRADIENT, 4) == 0)
+ load_resource_lfil (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LFX_FX, 4) == 0
+ || memcmp (res_a->key, PSD_LFX_FX2, 4) == 0)
+ load_resource_lfx (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LTYP_TYPE, 4) == 0
+ || memcmp (res_a->key, PSD_LTYP_TYPE2, 4) == 0)
+ load_resource_ltyp (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LPRP_UNICODE, 4) == 0)
+ load_resource_luni (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LPRP_ID, 4) == 0)
+ load_resource_lyid (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LPRP_COLOR, 4) == 0)
+ load_resource_lclr (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LOTH_SECTION, 4) == 0
+ || memcmp (res_a->key, PSD_LOTH_SECTION2, 4) == 0) /* bug #789981 */
+ load_resource_lsct (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LFX_FX, 4) == 0)
+ load_resource_lrfx (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LPRP_VERSION, 4) == 0)
+ load_resource_lyvr (res_a, lyr_a, f, error);
+
+ else if (memcmp (res_a->key, PSD_LPRP_SOURCE, 4) == 0)
+ load_resource_lnsr (res_a, lyr_a, f, error);
+
+ else
+ load_resource_unknown (res_a, lyr_a, f, error);
+ }
+
+ /* Set file position to end of layer resource block */
+ if (fseek (f, res_a->data_start + res_a->data_len, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Private Functions */
+
+static gint
+load_resource_unknown (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ IFDBG(2) g_debug ("Process unknown layer resource block: %.4s", res_a->key);
+
+ return 0;
+}
+
+static gint
+load_resource_ladj (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load adjustment layer */
+ static gboolean msg_flag = FALSE;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Adjustment layer", res_a->key);
+ lyr_a->drop = TRUE;
+ if (! msg_flag && CONVERSION_WARNINGS)
+ {
+ g_message ("Warning:\n"
+ "The image file contains adjustment layers. "
+ "These are not supported by the GIMP and will "
+ "be dropped.");
+ msg_flag = TRUE;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_lfil (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load fill layer */
+ static gboolean msg_flag = FALSE;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Fill layer", res_a->key);
+ if (! msg_flag && CONVERSION_WARNINGS)
+ {
+ g_message ("Warning:\n"
+ "The image file contains fill layers. "
+ "These are not supported by the GIMP and will "
+ "be rasterized.");
+ msg_flag = TRUE;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_lfx (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load layer effects */
+ static gboolean msg_flag = FALSE;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Layer effects", res_a->key);
+ if (! msg_flag && CONVERSION_WARNINGS)
+ {
+ g_message ("Warning:\n"
+ "The image file contains layer effects. "
+ "These are not supported by the GIMP and will "
+ "be dropped.");
+ msg_flag = TRUE;
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_ltyp (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load type tool layer */
+ gint16 version;
+ gint16 text_desc_vers;
+ gint32 desc_version;
+ gint32 read_len;
+ gint32 write_len;
+ guint64 t_xx;
+ guint64 t_xy;
+ guint64 t_yx;
+ guint64 t_yy;
+ guint64 t_tx;
+ guint64 t_ty;
+ gchar *classID;
+
+ static gboolean msg_flag = FALSE;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Type tool layer", res_a->key);
+ if (! msg_flag && CONVERSION_WARNINGS)
+ {
+ g_message ("Warning:\n"
+ "The image file contains type tool layers. "
+ "These are not supported by the GIMP and will "
+ "be dropped.");
+ msg_flag = TRUE;
+ }
+
+ /* New style type tool layers (ps6) */
+ if (memcmp (res_a->key, PSD_LTYP_TYPE2, 4) == 0)
+ {
+ if (fread (&version, 2, 1, f) < 1
+ || fread (&t_xx, 8, 1, f) < 1
+ || fread (&t_xy, 8, 1, f) < 1
+ || fread (&t_yx, 8, 1, f) < 1
+ || fread (&t_yy, 8, 1, f) < 1
+ || fread (&t_tx, 8, 1, f) < 1
+ || fread (&t_ty, 8, 1, f) < 1
+ || fread (&text_desc_vers, 2, 1, f) < 1
+ || fread (&desc_version, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ version = GINT16_FROM_BE (version);
+ text_desc_vers = GINT16_FROM_BE (text_desc_vers);
+ desc_version = GINT32_FROM_BE (desc_version);
+ // t_xx = GUINT64_FROM_BE (t_xx);
+ // t_xy = GUINT64_FROM_BE (t_xy);
+ // t_yx = GUINT64_FROM_BE (t_yx);
+ // t_yy = GUINT64_FROM_BE (t_yy);
+ // t_tx = GUINT64_FROM_BE (t_tx);
+ // t_ty = GUINT64_FROM_BE (t_ty);
+
+ lyr_a->text.xx = t_xx >> 11;
+ lyr_a->text.xy = t_xy >> 11;
+ lyr_a->text.yx = t_yx >> 11;
+ lyr_a->text.yy = t_yy >> 11;
+ lyr_a->text.tx = t_tx >> 11;
+ lyr_a->text.ty = t_ty >> 11;
+
+ IFDBG(2) g_debug ("Version: %d, Text desc. vers.: %d, Desc. vers.: %d",
+ version, text_desc_vers, desc_version);
+
+ IFDBG(2) g_debug ("Transform\n\txx: %f\n\txy: %f\n\tyx: %f"
+ "\n\tyy: %f\n\ttx: %f\n\tty: %f",
+ lyr_a->text.xx, lyr_a->text.xy, lyr_a->text.yx,
+ lyr_a->text.yy, lyr_a->text.tx, lyr_a->text.ty);
+
+ classID = fread_unicode_string (&read_len, &write_len, 4, f, error);
+ IFDBG(2) g_debug ("Unicode name: %s", classID);
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_luni (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load layer name in unicode (length padded to multiple of 4 bytes) */
+ gint32 read_len;
+ gint32 write_len;
+
+ IFDBG(2) g_debug ("Process layer resource block luni: Unicode Name");
+ if (lyr_a->name)
+ g_free (lyr_a->name);
+
+ lyr_a->name = fread_unicode_string (&read_len, &write_len, 4, f, error);
+ if (*error)
+ return -1;
+ IFDBG(3) g_debug ("Unicode name: %s", lyr_a->name);
+
+ return 0;
+}
+
+static gint
+load_resource_lyid (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load layer id (tattoo) */
+
+ IFDBG(2) g_debug ("Process layer resource block lyid: Layer ID");
+ if (fread (&lyr_a->id, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ lyr_a->id = GUINT32_FROM_BE (lyr_a->id);
+ IFDBG(3) g_debug ("Layer id: %i", lyr_a->id);
+
+ return 0;
+}
+
+static gint
+load_resource_lclr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load layer sheet color code */
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Sheet color",
+ res_a->key);
+
+ if (fread (lyr_a->color_tag, 8, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Photoshop only uses color_tag[0] to store a color code */
+ lyr_a->color_tag[0] = GUINT16_FROM_BE(lyr_a->color_tag[0]);
+
+ IFDBG(3) g_debug ("Layer sheet color: %i", lyr_a->color_tag[0]);
+
+ return 0;
+}
+
+static gint
+load_resource_lsct (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ /* Load layer group & type information
+ * Type 0: not a group
+ * Type 1: Open folder
+ * Type 2: Closed folder
+ * Type 3: End of most recent group */
+ guint32 type;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Section divider", res_a->key);
+ if (fread (&type, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ type = GUINT32_FROM_BE (type);
+ IFDBG(3) g_debug ("Section divider type: %i", type);
+
+ lyr_a->group_type = type;
+
+ if (res_a->data_len >= 12)
+ {
+ gchar signature[4];
+ gchar blend_mode[4];
+
+ if (fread (signature, 4, 1, f) < 1 ||
+ fread (blend_mode, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ if (memcmp (signature, "8BIM", 4) == 0)
+ {
+ memcpy (lyr_a->blend_mode, blend_mode, 4);
+ IFDBG(3) g_debug ("Section divider layer mode sig: %.4s, blend mode: %.4s",
+ signature, blend_mode);
+ }
+ else
+ {
+ IFDBG(1) g_debug ("Incorrect layer mode signature %.4s", signature);
+ }
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_lrfx (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ gint16 version;
+ gint16 count;
+ gchar signature[4];
+ gchar effectname[4];
+ gint i;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: Layer effects", res_a->key);
+
+ if (fread (&version, 2, 1, f) < 1
+ || fread (&count, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (fread (&signature, 4, 1, f) < 1
+ || fread(&effectname, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ if (memcmp (signature, "8BIM", 4) != 0)
+ {
+ IFDBG(1) g_debug ("Unknown layer resource signature %.4s", signature);
+ }
+ else
+ {
+ if (memcmp (effectname, "cmnS", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gchar visible;
+ gint16 unused;
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&visible, 1, 1, f) < 1
+ || fread(&unused, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ else if (memcmp (effectname, "dsdw", 4) == 0
+ || memcmp (effectname, "isdw", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gint32 blur;
+ gint32 intensity;
+ gint32 angle;
+ gint32 distance;
+ gint16 color[5];
+ gint32 blendsig;
+ gint32 effect;
+ gchar effecton;
+ gchar anglefx;
+ gchar opacity;
+ gint16 natcolor[5];
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&blur, 4, 1, f) < 1
+ || fread(&intensity, 4, 1, f) < 1
+ || fread(&angle, 4, 1, f) < 1
+ || fread(&distance, 4, 1, f) < 1
+ || fread(&color[0], 2, 1, f) < 1
+ || fread(&color[1], 2, 1, f) < 1
+ || fread(&color[2], 2, 1, f) < 1
+ || fread(&color[3], 2, 1, f) < 1
+ || fread(&color[4], 2, 1, f) < 1
+ || fread(&blendsig, 4, 1, f) < 1
+ || fread(&effect, 4, 1, f) < 1
+ || fread(&effecton, 1, 1, f) < 1
+ || fread(&anglefx, 1, 1, f) < 1
+ || fread(&opacity, 1, 1, f) < 1
+ || fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[1], 2, 1, f) < 1
+ || fread(&natcolor[2], 2, 1, f) < 1
+ || fread(&natcolor[3], 2, 1, f) < 1
+ || fread(&natcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ else if (memcmp (effectname, "oglw", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gint32 blur;
+ gint32 intensity;
+ gint16 color[5];
+ gint32 blendsig;
+ gint32 effect;
+ gchar effecton;
+ gchar opacity;
+ gint16 natcolor[5];
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&blur, 4, 1, f) < 1
+ || fread(&intensity, 4, 1, f) < 1
+ || fread(&color[0], 2, 1, f) < 1
+ || fread(&color[1], 2, 1, f) < 1
+ || fread(&color[2], 2, 1, f) < 1
+ || fread(&color[3], 2, 1, f) < 1
+ || fread(&color[4], 2, 1, f) < 1
+ || fread(&blendsig, 4, 1, f) < 1
+ || fread(&effect, 4, 1, f) < 1
+ || fread(&effecton, 1, 1, f) < 1
+ || fread(&opacity, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ if (size == 42)
+ {
+ if (fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[1], 2, 1, f) < 1
+ || fread(&natcolor[2], 2, 1, f) < 1
+ || fread(&natcolor[3], 2, 1, f) < 1
+ || fread(&natcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ }
+ else if (memcmp (effectname, "iglw", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gint32 blur;
+ gint32 intensity;
+ gint32 angle;
+ gint32 distance;
+ gint16 color[5];
+ gint32 blendsig;
+ gint32 effect;
+ gchar effecton;
+ gchar anglefx;
+ gchar opacity;
+ gchar invert;
+ gint16 natcolor[5];
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&blur, 4, 1, f) < 1
+ || fread(&intensity, 4, 1, f) < 1
+ || fread(&angle, 4, 1, f) < 1
+ || fread(&distance, 4, 1, f) < 1
+ || fread(&color[0], 2, 1, f) < 1
+ || fread(&color[1], 2, 1, f) < 1
+ || fread(&color[2], 2, 1, f) < 1
+ || fread(&color[3], 2, 1, f) < 1
+ || fread(&color[4], 2, 1, f) < 1
+ || fread(&blendsig, 4, 1, f) < 1
+ || fread(&effect, 4, 1, f) < 1
+ || fread(&effecton, 1, 1, f) < 1
+ || fread(&anglefx, 1, 1, f) < 1
+ || fread(&opacity, 1, 1, f) < 1
+ || fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[1], 2, 1, f) < 1
+ || fread(&natcolor[2], 2, 1, f) < 1
+ || fread(&natcolor[3], 2, 1, f) < 1
+ || fread(&natcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ if (size == 43)
+ {
+ if (fread (&invert, 1, 1, f) < 1
+ || fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[1], 2, 1, f) < 1
+ || fread(&natcolor[2], 2, 1, f) < 1
+ || fread(&natcolor[3], 2, 1, f) < 1
+ || fread(&natcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ }
+ else if (memcmp (effectname, "bevl", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gint32 angle;
+ gint32 strength;
+ gint32 blur;
+ gint32 highlightsig;
+ gint32 highlighteffect;
+ gint32 shadowsig;
+ gint32 shadoweffect;
+ gint16 highlightcolor[5];
+ gint16 shadowcolor[5];
+ gchar style;
+ gchar highlightopacity;
+ gchar shadowopacity;
+ gchar enabled;
+ gchar global;
+ gchar direction;
+ gint16 highlightnatcolor[5];
+ gint16 shadownatcolor[5];
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&angle, 4, 1, f) < 1
+ || fread(&strength, 4, 1, f) < 1
+ || fread(&blur, 4, 1, f) < 1
+ || fread(&highlightsig, 4, 1, f) < 1
+ || fread(&highlighteffect, 4, 1, f) < 1
+ || fread(&shadowsig, 4, 1, f) < 1
+ || fread(&highlightcolor[0], 2, 1, f) < 1
+ || fread(&shadoweffect, 4, 1, f) < 1
+ || fread(&highlightcolor[1], 2, 1, f) < 1
+ || fread(&highlightcolor[2], 2, 1, f) < 1
+ || fread(&highlightcolor[3], 2, 1, f) < 1
+ || fread(&highlightcolor[4], 2, 1, f) < 1
+ || fread(&shadowcolor[0], 2, 1, f) < 1
+ || fread(&shadowcolor[1], 2, 1, f) < 1
+ || fread(&shadowcolor[2], 2, 1, f) < 1
+ || fread(&shadowcolor[3], 2, 1, f) < 1
+ || fread(&shadowcolor[4], 2, 1, f) < 1
+ || fread(&style, 1, 1, f) < 1
+ || fread(&highlightopacity, 1, 1, f) < 1
+ || fread(&shadowopacity, 1, 1, f) < 1
+ || fread(&enabled, 1, 1, f) < 1
+ || fread(&global, 1, 1, f) < 1
+ || fread(&direction, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ if (size == 78)
+ {
+ if (fread(&highlightnatcolor[0], 2, 1, f) < 1
+ || fread(&highlightnatcolor[0], 2, 1, f) < 1
+ || fread(&highlightnatcolor[1], 2, 1, f) < 1
+ || fread(&highlightnatcolor[2], 2, 1, f) < 1
+ || fread(&highlightnatcolor[3], 2, 1, f) < 1
+ || fread(&highlightnatcolor[4], 2, 1, f) < 1
+ || fread(&shadownatcolor[0], 2, 1, f) < 1
+ || fread(&shadownatcolor[0], 2, 1, f) < 1
+ || fread(&shadownatcolor[1], 2, 1, f) < 1
+ || fread(&shadownatcolor[2], 2, 1, f) < 1
+ || fread(&shadownatcolor[3], 2, 1, f) < 1
+ || fread(&shadownatcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ }
+ else if (memcmp (effectname, "sofi", 4) == 0)
+ {
+ gint32 size;
+ gint32 ver;
+ gint32 key;
+ gint16 color[5];
+ gchar opacity;
+ gchar enabled;
+ gint16 natcolor[5];
+
+ if (fread (&size, 4, 1, f) < 1
+ || fread(&ver, 4, 1, f) < 1
+ || fread(&key, 4, 1, f) < 1
+ || fread(&color[0], 2, 1, f) < 1
+ || fread(&color[1], 2, 1, f) < 1
+ || fread(&color[2], 2, 1, f) < 1
+ || fread(&color[3], 2, 1, f) < 1
+ || fread(&color[4], 2, 1, f) < 1
+ || fread(&opacity, 1, 1, f) < 1
+ || fread(&enabled, 1, 1, f) < 1
+ || fread(&natcolor[0], 2, 1, f) < 1
+ || fread(&natcolor[1], 2, 1, f) < 1
+ || fread(&natcolor[2], 2, 1, f) < 1
+ || fread(&natcolor[3], 2, 1, f) < 1
+ || fread(&natcolor[4], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ else
+ {
+ IFDBG(1) g_debug ("Unknown layer effect signature %.4s", effectname);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_lyvr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ gint32 version;
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: layer version",
+ res_a->key);
+
+ if (fread (&version, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ version = GINT32_FROM_BE(version);
+
+ /* minimum value is 70 according to specs but there's no reason to
+ * stop the loading
+ */
+ if (version < 70)
+ {
+ g_message ("Invalid version layer");
+ }
+
+ return 0;
+}
+
+static gint
+load_resource_lnsr (const PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error)
+{
+ gchar layername[4];
+
+ IFDBG(2) g_debug ("Process layer resource block %.4s: layer source name",
+ res_a->key);
+
+ if (fread (&layername, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* nowadays psd files, layer name are encoded in unicode, cf "luni"
+ * moreover lnsr info is encoded in MacRoman, see
+ * https://bugzilla.gnome.org/show_bug.cgi?id=753986#c4
+ */
+
+ return 0;
+}
diff --git a/plug-ins/file-psd/psd-layer-res-load.h b/plug-ins/file-psd/psd-layer-res-load.h
new file mode 100644
index 0000000..516ed95
--- /dev/null
+++ b/plug-ins/file-psd/psd-layer-res-load.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_LAYER_RES_LOAD_H__
+#define __PSD_LAYER_RES_LOAD_H__
+
+
+gint get_layer_resource_header (PSDlayerres *res_a,
+ FILE *f,
+ GError **error);
+
+gint load_layer_resource (PSDlayerres *res_a,
+ PSDlayer *lyr_a,
+ FILE *f,
+ GError **error);
+
+
+#endif /* __PSD_LAYER_RES_LOAD_H__ */
diff --git a/plug-ins/file-psd/psd-load.c b/plug-ins/file-psd/psd-load.c
new file mode 100644
index 0000000..b03f8cf
--- /dev/null
+++ b/plug-ins/file-psd/psd-load.c
@@ -0,0 +1,2807 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <zlib.h>
+#include <libgimp/gimp.h>
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-image-res-load.h"
+#include "psd-layer-res-load.h"
+#include "psd-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define COMP_MODE_SIZE sizeof(guint16)
+
+
+/* Local function prototypes */
+static gint read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static PSDlayer ** read_layer_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_merged_image_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint32 create_gimp_image (PSDimage *img_a,
+ const gchar *filename);
+
+static gint add_color_map (gint32 image_id,
+ PSDimage *img_a);
+
+static gint add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+static gint add_layers (gint32 image_id,
+ PSDimage *img_a,
+ PSDlayer **lyr_a,
+ FILE *f,
+ GError **error);
+
+static gint add_merged_image (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+/* Local utility function prototypes */
+static gint32 add_clipping_group (gint32 image_id,
+ gint32 parent_id);
+
+static gchar * get_psd_color_mode_name (PSDColorMode mode);
+
+static void psd_to_gimp_color_map (guchar *map256);
+
+static GimpImageType get_gimp_image_type (GimpImageBaseType image_base_type,
+ gboolean alpha);
+
+static gint read_channel_data (PSDchannel *channel,
+ guint16 bps,
+ guint16 compression,
+ const guint16 *rle_pack_len,
+ FILE *f,
+ guint32 comp_len,
+ GError **error);
+
+static void convert_1_bit (const gchar *src,
+ gchar *dst,
+ guint32 rows,
+ guint32 columns);
+
+static const Babl* get_layer_format (PSDimage *img_a,
+ gboolean alpha);
+static const Babl* get_channel_format (PSDimage *img_a);
+static const Babl* get_mask_format (PSDimage *img_a);
+
+
+/* Main file load function */
+gint32
+load_image (const gchar *filename,
+ gboolean merged_image_only,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **load_error)
+{
+ FILE *f;
+ GStatBuf st;
+ PSDimage img_a;
+ PSDlayer **lyr_a;
+ gint32 image_id = -1;
+ GError *error = NULL;
+
+ img_a.cmyk_transform = img_a.cmyk_transform_alpha = NULL;
+ img_a.cmyk_profile = NULL;
+ /* ----- Open PSD file ----- */
+ if (g_stat (filename, &st) == -1)
+ return -1;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ IFDBG(1) g_debug ("Open file %s", gimp_filename_to_utf8 (filename));
+ f = g_fopen (filename, "rb");
+ if (f == NULL)
+ {
+ g_set_error (load_error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ img_a.merged_image_only = merged_image_only;
+
+ /* ----- Read the PSD file Header block ----- */
+ IFDBG(2) g_debug ("Read header block");
+ if (read_header_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.1);
+
+ /* ----- Read the PSD file Color Mode block ----- */
+ IFDBG(2) g_debug ("Read color mode block");
+ if (read_color_mode_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.2);
+
+ /* ----- Read the PSD file Image Resource block ----- */
+ IFDBG(2) g_debug ("Read image resource block");
+ if (read_image_resource_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.3);
+
+ /* ----- Read the PSD file Layer & Mask block ----- */
+ IFDBG(2) g_debug ("Read layer & mask block");
+ lyr_a = read_layer_block (&img_a, f, &error);
+ if (! img_a.merged_image_only && img_a.num_layers != 0 && lyr_a == NULL)
+ goto load_error;
+ gimp_progress_update (0.4);
+
+ /* ----- Read the PSD file Merged Image Data block ----- */
+ IFDBG(2) g_debug ("Read merged image and extra alpha channel block");
+ if (read_merged_image_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.5);
+
+ /* ----- Create GIMP image ----- */
+ IFDBG(2) g_debug ("Create GIMP image");
+ image_id = create_gimp_image (&img_a, filename);
+ if (image_id < 0)
+ goto load_error;
+ gimp_progress_update (0.6);
+
+ /* ----- Add color map ----- */
+ IFDBG(2) g_debug ("Add color map");
+ if (add_color_map (image_id, &img_a) < 0)
+ goto load_error;
+ gimp_progress_update (0.7);
+
+ /* ----- Add image resources ----- */
+ IFDBG(2) g_debug ("Add image resources");
+ if (add_image_resources (image_id, &img_a, f,
+ resolution_loaded, profile_loaded,
+ &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.8);
+
+ /* ----- Add layers -----*/
+ IFDBG(2) g_debug ("Add layers");
+ if (add_layers (image_id, &img_a, lyr_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.9);
+
+ /* ----- Add merged image data and extra alpha channels ----- */
+ IFDBG(2) g_debug ("Add merged image data and extra alpha channels");
+ if (add_merged_image (image_id, &img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (1.0);
+
+ IFDBG(2) g_debug ("Close file & return, image id: %d", image_id);
+ IFDBG(1) g_debug ("\n----------------------------------------"
+ "----------------------------------------\n");
+
+ gimp_image_clean_all (image_id);
+ gimp_image_undo_enable (image_id);
+ fclose (f);
+ return image_id;
+
+ /* ----- Process load errors ----- */
+ load_error:
+ if (error)
+ {
+ g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error loading PSD file: %s"), error->message);
+ g_error_free (error);
+ }
+
+ /* Delete partially loaded image */
+ if (image_id > 0)
+ gimp_image_delete (image_id);
+
+ /* Close file if Open */
+ if (! (f == NULL))
+ fclose (f);
+
+ return -1;
+}
+
+
+/* Local functions */
+
+static gint
+read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint16 version;
+ gchar sig[4];
+ gchar buf[6];
+
+ if (fread (sig, 4, 1, f) < 1
+ || fread (&version, 2, 1, f) < 1
+ || fread (buf, 6, 1, f) < 1
+ || fread (&img_a->channels, 2, 1, f) < 1
+ || fread (&img_a->rows, 4, 1, f) < 1
+ || fread (&img_a->columns, 4, 1, f) < 1
+ || fread (&img_a->bps, 2, 1, f) < 1
+ || fread (&img_a->color_mode, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ version = GUINT16_FROM_BE (version);
+ img_a->channels = GUINT16_FROM_BE (img_a->channels);
+ img_a->rows = GUINT32_FROM_BE (img_a->rows);
+ img_a->columns = GUINT32_FROM_BE (img_a->columns);
+ img_a->bps = GUINT16_FROM_BE (img_a->bps);
+ img_a->color_mode = GUINT16_FROM_BE (img_a->color_mode);
+
+ IFDBG(1) g_debug ("\n\n\tSig: %.4s\n\tVer: %d\n\tChannels: "
+ "%d\n\tSize: %dx%d\n\tBPS: %d\n\tMode: %d\n",
+ sig, version, img_a->channels,
+ img_a->columns, img_a->rows,
+ img_a->bps, img_a->color_mode);
+
+ if (memcmp (sig, "8BPS", 4) != 0)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Not a valid Photoshop document file"));
+ return -1;
+ }
+
+ if (version != 1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported file format version: %d"), version);
+ return -1;
+ }
+
+ if (img_a->channels > MAX_CHANNELS)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Too many channels in file: %d"), img_a->channels);
+ return -1;
+ }
+
+ /* Photoshop CS (version 8) supports 300000 x 300000, but this
+ is currently larger than GIMP_MAX_IMAGE_SIZE */
+
+ if (img_a->rows < 1 || img_a->rows > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image height: %d"),
+ img_a->rows);
+ return -1;
+ }
+
+ if (img_a->columns < 1 || img_a->columns > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image width: %d"),
+ img_a->columns);
+ return -1;
+ }
+
+ /* img_a->rows is sanitized above, so a division by zero is avoided here */
+ if (img_a->columns > G_MAXINT32 / img_a->rows)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid image size: %dx%d"),
+ img_a->columns, img_a->rows);
+ return -1;
+ }
+
+ if (img_a->color_mode != PSD_BITMAP
+ && img_a->color_mode != PSD_GRAYSCALE
+ && img_a->color_mode != PSD_INDEXED
+ && img_a->color_mode != PSD_RGB
+ && img_a->color_mode != PSD_MULTICHANNEL
+ && img_a->color_mode != PSD_CMYK
+ && img_a->color_mode != PSD_DUOTONE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported color mode: %s"),
+ get_psd_color_mode_name (img_a->color_mode));
+ return -1;
+ }
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ if (img_a->bps != 8)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported color mode: %s"),
+ get_psd_color_mode_name (img_a->color_mode));
+ return -1;
+ }
+ }
+
+ /* Warning for unsupported bit depth */
+ switch (img_a->bps)
+ {
+ case 32:
+ IFDBG(3) g_debug ("32 Bit Data");
+ break;
+
+ case 16:
+ IFDBG(3) g_debug ("16 Bit Data");
+ break;
+
+ case 8:
+ IFDBG(3) g_debug ("8 Bit Data");
+ break;
+
+ case 1:
+ IFDBG(3) g_debug ("1 Bit Data");
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported bit depth: %d"), img_a->bps);
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static gint
+read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ static guchar cmap[] = { 0, 0, 0, 255, 255, 255 };
+ guint32 block_len;
+
+ img_a->color_map_entries = 0;
+ img_a->color_map_len = 0;
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ block_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Color map block size = %d", block_len);
+
+ if (block_len == 0)
+ {
+ if (img_a->color_mode == PSD_INDEXED ||
+ img_a->color_mode == PSD_DUOTONE )
+ {
+ IFDBG(1) g_debug ("No color block for indexed or duotone image");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return -1;
+ }
+ }
+ else if (img_a->color_mode == PSD_INDEXED)
+ {
+ if (block_len != 768)
+ {
+ IFDBG(1) g_debug ("Invalid color block size for indexed image");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return -1;
+ }
+ else
+ {
+ img_a->color_map_len = block_len;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ if (fread (img_a->color_map, block_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ else
+ {
+ psd_to_gimp_color_map (img_a->color_map);
+ img_a->color_map_entries = 256;
+ }
+ }
+ }
+ else if (img_a->color_mode == PSD_DUOTONE)
+ {
+ img_a->color_map_len = block_len;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ if (fread (img_a->color_map, block_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+
+ /* Create color map for bitmap image */
+ if (img_a->color_mode == PSD_BITMAP)
+ {
+ img_a->color_map_len = 6;
+ img_a->color_map = g_malloc (img_a->color_map_len);
+ memcpy (img_a->color_map, cmap, img_a->color_map_len);
+ img_a->color_map_entries = 2;
+ }
+ IFDBG(2) g_debug ("Color map data length %d", img_a->color_map_len);
+
+ return 0;
+}
+
+static gint
+read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint32 block_len;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->image_res_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Image resource block size = %d", (int)img_a->image_res_len);
+
+ img_a->image_res_start = ftell (f);
+ block_end = img_a->image_res_start + img_a->image_res_len;
+
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PSDlayer **
+read_layer_info (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDlayer **lyr_a = NULL;
+ guint32 block_len;
+ guint32 block_rem;
+ gint32 read_len;
+ gint32 write_len;
+ gint lidx; /* Layer index */
+ gint cidx; /* Channel index */
+
+ /* Get number of layers */
+ if (fread (&img_a->num_layers, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ img_a->num_layers = -1;
+ return NULL;
+ }
+
+ img_a->num_layers = GINT16_FROM_BE (img_a->num_layers);
+ IFDBG(2) g_debug ("Number of layers: %d", img_a->num_layers);
+
+ if (img_a->num_layers < 0)
+ {
+ img_a->transparency = TRUE;
+ img_a->num_layers = -img_a->num_layers;
+ }
+
+ if (! img_a->merged_image_only && img_a->num_layers)
+ {
+ /* Read layer records */
+ PSDlayerres res_a;
+
+ /* Create pointer array for the layer records */
+ lyr_a = g_new (PSDlayer *, img_a->num_layers);
+
+ for (lidx = 0; lidx < img_a->num_layers; ++lidx)
+ {
+ /* Allocate layer record */
+ lyr_a[lidx] = (PSDlayer *) g_malloc (sizeof (PSDlayer) );
+
+ /* Initialise record */
+ lyr_a[lidx]->drop = FALSE;
+ lyr_a[lidx]->id = 0;
+ lyr_a[lidx]->group_type = 0;
+
+ if (fread (&lyr_a[lidx]->top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->right, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->num_channels, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ lyr_a[lidx]->top = GINT32_FROM_BE (lyr_a[lidx]->top);
+ lyr_a[lidx]->left = GINT32_FROM_BE (lyr_a[lidx]->left);
+ lyr_a[lidx]->bottom = GINT32_FROM_BE (lyr_a[lidx]->bottom);
+ lyr_a[lidx]->right = GINT32_FROM_BE (lyr_a[lidx]->right);
+ lyr_a[lidx]->num_channels = GUINT16_FROM_BE (lyr_a[lidx]->num_channels);
+
+ if (lyr_a[lidx]->num_channels > MAX_CHANNELS)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Too many channels in layer: %d"),
+ lyr_a[lidx]->num_channels);
+ return NULL;
+ }
+ if (lyr_a[lidx]->bottom < lyr_a[lidx]->top ||
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer height: %d"),
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top);
+ return NULL;
+ }
+ if (lyr_a[lidx]->right < lyr_a[lidx]->left ||
+ lyr_a[lidx]->right - lyr_a[lidx]->left > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer width: %d"),
+ lyr_a[lidx]->right - lyr_a[lidx]->left);
+ return NULL;
+ }
+
+ if ((lyr_a[lidx]->right - lyr_a[lidx]->left) >
+ G_MAXINT32 / MAX (lyr_a[lidx]->bottom - lyr_a[lidx]->top, 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer size: %dx%d"),
+ lyr_a[lidx]->right - lyr_a[lidx]->left,
+ lyr_a[lidx]->bottom - lyr_a[lidx]->top);
+ return NULL;
+ }
+
+ IFDBG(2) g_debug ("Layer %d, Coords %d %d %d %d, channels %d, ",
+ lidx, lyr_a[lidx]->left, lyr_a[lidx]->top,
+ lyr_a[lidx]->right, lyr_a[lidx]->bottom,
+ lyr_a[lidx]->num_channels);
+
+ lyr_a[lidx]->chn_info = g_new (ChannelLengthInfo, lyr_a[lidx]->num_channels);
+
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (fread (&lyr_a[lidx]->chn_info[cidx].channel_id, 2, 1, f) < 1
+ || fread (&lyr_a[lidx]->chn_info[cidx].data_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ lyr_a[lidx]->chn_info[cidx].channel_id =
+ GINT16_FROM_BE (lyr_a[lidx]->chn_info[cidx].channel_id);
+ lyr_a[lidx]->chn_info[cidx].data_len =
+ GUINT32_FROM_BE (lyr_a[lidx]->chn_info[cidx].data_len);
+ img_a->layer_data_len += lyr_a[lidx]->chn_info[cidx].data_len;
+ IFDBG(3) g_debug ("Channel ID %d, data len %d",
+ lyr_a[lidx]->chn_info[cidx].channel_id,
+ lyr_a[lidx]->chn_info[cidx].data_len);
+ }
+
+ if (fread (lyr_a[lidx]->mode_key, 4, 1, f) < 1
+ || fread (lyr_a[lidx]->blend_mode, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->opacity, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->clipping, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->flags, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->filler, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->extra_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ if (memcmp (lyr_a[lidx]->mode_key, "8BIM", 4) != 0)
+ {
+ IFDBG(1) g_debug ("Incorrect layer mode signature %.4s",
+ lyr_a[lidx]->mode_key);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return NULL;
+ }
+
+ lyr_a[lidx]->layer_flags.trans_prot = lyr_a[lidx]->flags & 1 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_flags.visible = lyr_a[lidx]->flags & 2 ? FALSE : TRUE;
+
+ if (lyr_a[lidx]->flags & 8)
+ lyr_a[lidx]->layer_flags.irrelevant = lyr_a[lidx]->flags & 16 ? TRUE : FALSE;
+ else
+ lyr_a[lidx]->layer_flags.irrelevant = FALSE;
+
+ lyr_a[lidx]->extra_len = GUINT32_FROM_BE (lyr_a[lidx]->extra_len);
+ block_rem = lyr_a[lidx]->extra_len;
+ IFDBG(2) g_debug ("\n\tLayer mode sig: %.4s\n\tBlend mode: %.4s\n\t"
+ "Opacity: %d\n\tClipping: %d\n\tExtra data len: %d\n\t"
+ "Alpha lock: %d\n\tVisible: %d\n\tIrrelevant: %d",
+ lyr_a[lidx]->mode_key,
+ lyr_a[lidx]->blend_mode,
+ lyr_a[lidx]->opacity,
+ lyr_a[lidx]->clipping,
+ lyr_a[lidx]->extra_len,
+ lyr_a[lidx]->layer_flags.trans_prot,
+ lyr_a[lidx]->layer_flags.visible,
+ lyr_a[lidx]->layer_flags.irrelevant);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ /* Layer mask data */
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(3) g_debug ("Layer mask record size %u", block_len);
+ if (block_len + 4 > block_rem)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid mask info size: %d"),
+ block_len);
+ return NULL;
+ }
+
+ block_rem -= (block_len + 4);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ lyr_a[lidx]->layer_mask_extra.top = 0;
+ lyr_a[lidx]->layer_mask_extra.left = 0;
+ lyr_a[lidx]->layer_mask_extra.bottom = 0;
+ lyr_a[lidx]->layer_mask_extra.right = 0;
+ lyr_a[lidx]->layer_mask.top = 0;
+ lyr_a[lidx]->layer_mask.left = 0;
+ lyr_a[lidx]->layer_mask.bottom = 0;
+ lyr_a[lidx]->layer_mask.right = 0;
+ lyr_a[lidx]->layer_mask.def_color = 0;
+ lyr_a[lidx]->layer_mask.extra_def_color = 0;
+ lyr_a[lidx]->layer_mask.flags = 0;
+ lyr_a[lidx]->layer_mask.extra_flags = 0;
+ lyr_a[lidx]->layer_mask.mask_params = 0;
+ lyr_a[lidx]->layer_mask.mask_flags.relative_pos = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.disabled = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.invert = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.rendered = FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.params_present = FALSE;
+
+ if (block_len > 0)
+ {
+ if (fread (&lyr_a[lidx]->layer_mask.top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.right, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.def_color, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.flags, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ lyr_a[lidx]->layer_mask.top =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.top);
+ lyr_a[lidx]->layer_mask.left =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.left);
+ lyr_a[lidx]->layer_mask.bottom =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.bottom);
+ lyr_a[lidx]->layer_mask.right =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask.right);
+ lyr_a[lidx]->layer_mask.mask_flags.relative_pos =
+ lyr_a[lidx]->layer_mask.flags & 1 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.disabled =
+ lyr_a[lidx]->layer_mask.flags & 2 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.invert =
+ lyr_a[lidx]->layer_mask.flags & 4 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.rendered =
+ lyr_a[lidx]->layer_mask.flags & 8 ? TRUE : FALSE;
+ lyr_a[lidx]->layer_mask.mask_flags.params_present =
+ lyr_a[lidx]->layer_mask.flags & 16 ? TRUE : FALSE;
+ IFDBG(3)
+ {
+ if (lyr_a[lidx]->layer_mask.flags & 32) g_debug ("Layer mask flags bit 5 set.");
+ if (lyr_a[lidx]->layer_mask.flags & 64) g_debug ("Layer mask flags bit 6 set.");
+ if (lyr_a[lidx]->layer_mask.flags & 128) g_debug ("Layer mask flags bit 7 set.");
+ }
+
+ block_len -= 18;
+
+ /* According to psd-tools this part comes before reading the
+ * mask parameters. However if all mask parameter flags are
+ * set the size of that would also be more than 18. I'm not
+ * sure if all those flags could be set at the same time or
+ * how to distinguish them. */
+ if (block_len >= 18)
+ {
+ if (fread (&lyr_a[lidx]->layer_mask.extra_flags, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask.extra_def_color, 1, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.top, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.left, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.bottom, 4, 1, f) < 1
+ || fread (&lyr_a[lidx]->layer_mask_extra.right, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len -= 18;
+
+ lyr_a[lidx]->layer_mask_extra.top =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.top);
+ lyr_a[lidx]->layer_mask_extra.left =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.left);
+ lyr_a[lidx]->layer_mask_extra.bottom =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.bottom);
+ lyr_a[lidx]->layer_mask_extra.right =
+ GINT32_FROM_BE (lyr_a[lidx]->layer_mask_extra.right);
+
+ IFDBG(2) g_debug ("Rect enclosing Layer mask %d %d %d %d",
+ lyr_a[lidx]->layer_mask_extra.left,
+ lyr_a[lidx]->layer_mask_extra.top,
+ lyr_a[lidx]->layer_mask_extra.right,
+ lyr_a[lidx]->layer_mask_extra.bottom);
+ }
+
+ if (block_len > 2 && lyr_a[lidx]->layer_mask.mask_flags.params_present)
+ {
+ gint extra_bytes = 0;
+
+ if (fread (&lyr_a[lidx]->layer_mask.mask_params, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len--;
+ IFDBG(3) g_debug ("Mask params: %u", lyr_a[lidx]->layer_mask.mask_params);
+
+ /* FIXME Extra bytes with user/vector mask density and feather follow.
+ * We currently can't handle that so we will skip it. */
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 1 ? 1 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 2 ? 8 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 4 ? 1 : 0);
+ extra_bytes += (lyr_a[lidx]->layer_mask.mask_params & 8 ? 8 : 0);
+ IFDBG(3) g_debug ("Extra bytes according to mask parameters %d", extra_bytes);
+ if (fseek (f, extra_bytes, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ block_len -= extra_bytes;
+ }
+
+ if (block_len > 0)
+ {
+ /* We have some remaining unknown mask data.
+ * If size is less than 4 it is most likely padding. */
+ IFDBG(1)
+ {
+ if (block_len > 3)
+ g_debug ("Skipping %u bytes of unknown layer mask data", block_len);
+ }
+
+ if (fseek (f, block_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ IFDBG(2) g_debug ("Layer mask coords %d %d %d %d",
+ lyr_a[lidx]->layer_mask.left,
+ lyr_a[lidx]->layer_mask.top,
+ lyr_a[lidx]->layer_mask.right,
+ lyr_a[lidx]->layer_mask.bottom);
+
+ IFDBG(3) g_debug ("Default mask color %d, real color %d",
+ lyr_a[lidx]->layer_mask.def_color,
+ lyr_a[lidx]->layer_mask.extra_def_color);
+
+ IFDBG(3) g_debug ("Mask flags %u, real flags %u, mask params %u",
+ lyr_a[lidx]->layer_mask.flags,
+ lyr_a[lidx]->layer_mask.extra_flags,
+ lyr_a[lidx]->layer_mask.mask_params);
+
+ /* Rendered masks can have invalid dimensions: 0, 0, 0, -1 */
+ if (! lyr_a[lidx]->layer_mask.mask_flags.rendered)
+ {
+ /* sanity checks */
+ if (lyr_a[lidx]->layer_mask.bottom < lyr_a[lidx]->layer_mask.top ||
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask height: %d"),
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top);
+ return NULL;
+ }
+ if (lyr_a[lidx]->layer_mask.right < lyr_a[lidx]->layer_mask.left ||
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left > GIMP_MAX_IMAGE_SIZE)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask width: %d"),
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left);
+ return NULL;
+ }
+
+ if ((lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left) >
+ G_MAXINT32 / MAX (lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top, 1))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid layer mask size: %dx%d"),
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left,
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top);
+ return NULL;
+ }
+ }
+ }
+
+ /* Layer blending ranges */ /* FIXME */
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ block_len = GUINT32_FROM_BE (block_len);
+ block_rem -= (block_len + 4);
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ if (block_len > 0)
+ {
+ if (fseek (f, block_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ lyr_a[lidx]->name = fread_pascal_string (&read_len, &write_len,
+ 4, f, error);
+ if (*error)
+ return NULL;
+
+ block_rem -= read_len;
+ IFDBG(3) g_debug ("Remaining length %d", block_rem);
+
+ /* Adjustment layer info */ /* FIXME */
+
+ while (block_rem > 7)
+ {
+ if (get_layer_resource_header (&res_a, f, error) < 0)
+ return NULL;
+
+ block_rem -= 12;
+
+ if (res_a.data_len % 2 != 0)
+ {
+ /* Warn the user about an invalid length value but
+ * try to recover graciously. See bug #771558.
+ */
+ g_printerr ("psd-load: Layer extra data length should "
+ "be even, but it is %d.", res_a.data_len);
+ }
+
+ if (res_a.data_len > block_rem)
+ {
+ IFDBG(1) g_debug ("Unexpected end of layer resource data");
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("The file is corrupt!"));
+ return NULL;
+ }
+
+ if (load_layer_resource (&res_a, lyr_a[lidx], f, error) < 0)
+ return NULL;
+ block_rem -= res_a.data_len;
+ }
+ if (block_rem > 0)
+ {
+ if (fseek (f, block_rem, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+ }
+
+ img_a->layer_data_start = ftell(f);
+ if (fseek (f, img_a->layer_data_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+
+ IFDBG(1) g_debug ("Layer image data block size %d",
+ img_a->layer_data_len);
+ }
+
+ return lyr_a;
+}
+
+
+static PSDlayer **
+read_layer_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDlayer **lyr_a = NULL;
+ guint32 block_len;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ img_a->num_layers = -1;
+ return NULL;
+ }
+
+ img_a->mask_layer_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Layer and mask block size = %d", img_a->mask_layer_len);
+
+ img_a->transparency = FALSE;
+ img_a->layer_data_len = 0;
+
+ if (!img_a->mask_layer_len)
+ {
+ img_a->num_layers = 0;
+ return NULL;
+ }
+ else
+ {
+ guint32 total_len = img_a->mask_layer_len;
+
+ img_a->mask_layer_start = ftell (f);
+ block_end = img_a->mask_layer_start + img_a->mask_layer_len;
+
+ /* Layer info */
+ if (fread (&block_len, 4, 1, f) == 1 && block_len)
+ {
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(1) g_debug ("Layer info size = %d", block_len);
+
+ lyr_a = read_layer_info (img_a, f, error);
+
+ total_len -= block_len;
+ }
+ else
+ {
+ img_a->num_layers = 0;
+ lyr_a = NULL;
+ }
+
+ /* Global layer mask info */
+ if (fread (&block_len, 4, 1, f) == 1 && block_len)
+ {
+ block_len = GUINT32_FROM_BE (block_len);
+ IFDBG(1) g_debug ("Global layer mask info size = %d", block_len);
+
+ /* read_global_layer_mask_info (img_a, f, error); */
+ fseek (f, block_len, SEEK_CUR);
+
+ total_len -= block_len;
+ }
+
+ /* Additional Layer Information */
+ if (total_len > 12)
+ {
+ gchar signature_key[8];
+
+ if (fread (&signature_key, 4, 2, f) == 2 &&
+ (memcmp (signature_key, "8BIMLr16", 8) == 0 ||
+ memcmp (signature_key, "8BIMLr32", 8) == 0) &&
+ fread (&block_len, 4, 1, f) == 1 && block_len)
+ lyr_a = read_layer_info (img_a, f, error);
+ }
+
+ /* Skip to end of block */
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ }
+
+ return lyr_a;
+}
+
+static gint
+read_merged_image_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ img_a->merged_image_start = ftell(f);
+ if (fseek (f, 0, SEEK_END) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ img_a->merged_image_len = ftell(f) - img_a->merged_image_start;
+
+ IFDBG(1) g_debug ("Merged image data block: Start: %d, len: %d",
+ img_a->merged_image_start, img_a->merged_image_len);
+
+ return 0;
+}
+
+static gint32
+create_gimp_image (PSDimage *img_a,
+ const gchar *filename)
+{
+ gint32 image_id = -1;
+ GimpPrecision precision;
+
+ switch (img_a->color_mode)
+ {
+ case PSD_MULTICHANNEL:
+ case PSD_GRAYSCALE:
+ case PSD_DUOTONE:
+ img_a->base_type = GIMP_GRAY;
+ break;
+
+ case PSD_BITMAP:
+ case PSD_INDEXED:
+ img_a->base_type = GIMP_INDEXED;
+ break;
+
+ case PSD_CMYK:
+ case PSD_RGB:
+ img_a->base_type = GIMP_RGB;
+ break;
+
+ default:
+ /* Color mode already validated - should not be here */
+ g_warning ("Invalid color mode");
+ return -1;
+ break;
+ }
+
+ switch (img_a->bps)
+ {
+ case 32:
+ precision = GIMP_PRECISION_U32_GAMMA;
+ break;
+
+ case 16:
+ precision = GIMP_PRECISION_U16_GAMMA;
+ break;
+
+ case 8:
+ case 1:
+ if (img_a->color_mode == PSD_CMYK)
+ precision = GIMP_PRECISION_FLOAT_GAMMA;
+ else
+ precision = GIMP_PRECISION_U8_GAMMA;
+ break;
+
+ default:
+ /* Precision not supported */
+ g_warning ("Invalid precision");
+ return -1;
+ break;
+ }
+
+ /* Create gimp image */
+ IFDBG(2) g_debug ("Create image");
+ image_id = gimp_image_new_with_precision (img_a->columns, img_a->rows,
+ img_a->base_type, precision);
+ gimp_image_set_filename (image_id, filename);
+ gimp_image_undo_disable (image_id);
+
+ return image_id;
+}
+
+static gint
+add_color_map (gint32 image_id,
+ PSDimage *img_a)
+{
+ GimpParasite *parasite;
+
+ if (img_a->color_map_len)
+ {
+ if (img_a->color_mode != PSD_DUOTONE)
+ {
+ gimp_image_set_colormap (image_id, img_a->color_map,
+ img_a->color_map_entries);
+ }
+ else
+ {
+ /* Add parasite for Duotone color data */
+ IFDBG(2) g_debug ("Add Duotone color data parasite");
+ parasite = gimp_parasite_new (PSD_PARASITE_DUOTONE_DATA, 0,
+ img_a->color_map_len, img_a->color_map);
+ gimp_image_attach_parasite (image_id, parasite);
+ gimp_parasite_free (parasite);
+ }
+ g_free (img_a->color_map);
+ }
+
+ return 0;
+}
+
+static gint
+add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ PSDimageres res_a;
+
+ if (fseek (f, img_a->image_res_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ /* Initialise image resource variables */
+ img_a->no_icc = FALSE;
+ img_a->layer_state = 0;
+ img_a->alpha_names = NULL;
+ img_a->alpha_display_info = NULL;
+ img_a->alpha_display_count = 0;
+ img_a->alpha_id = NULL;
+ img_a->alpha_id_count = 0;
+ img_a->quick_mask_id = 0;
+
+ while (ftell (f) < img_a->image_res_start + img_a->image_res_len)
+ {
+ if (get_image_resource_header (&res_a, f, error) < 0)
+ return -1;
+
+ if (res_a.data_start + res_a.data_len >
+ img_a->image_res_start + img_a->image_res_len)
+ {
+ IFDBG(1) g_debug ("Unexpected end of image resource data");
+ return 0;
+ }
+
+ if (load_image_resource (&res_a, image_id, img_a, f,
+ resolution_loaded, profile_loaded,
+ error) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static guchar *
+psd_convert_cmyk_to_srgb (PSDimage *img_a,
+ guchar *dst,
+ guchar *src,
+ gint width,
+ gint height,
+ gboolean alpha)
+{
+ if (img_a->cmyk_profile)
+ {
+ if (alpha)
+ {
+ if (! img_a->cmyk_transform_alpha)
+ {
+ GimpColorProfile *srgb = gimp_color_profile_new_rgb_srgb ();
+
+ img_a->cmyk_transform_alpha = gimp_color_transform_new (img_a->cmyk_profile, babl_format ("cmykA u8"),
+ srgb, babl_format ("R'G'B'A float"),
+ 0, 0);
+
+ g_object_unref (srgb);
+ }
+
+ gimp_color_transform_process_pixels (img_a->cmyk_transform_alpha,
+ babl_format ("cmykA u8"),
+ src,
+ babl_format ("R'G'B'A float"),
+ dst,
+ width * height);
+ }
+ else
+ {
+ if (! img_a->cmyk_transform)
+ {
+ GimpColorProfile *srgb = gimp_color_profile_new_rgb_srgb ();
+
+ img_a->cmyk_transform = gimp_color_transform_new (img_a->cmyk_profile, babl_format ("cmyk u8"),
+ srgb, babl_format ("R'G'B' float"),
+ 0, 0);
+
+ g_object_unref (srgb);
+ }
+
+ gimp_color_transform_process_pixels (img_a->cmyk_transform,
+ babl_format ("cmyk u8"),
+ src,
+ babl_format ("R'G'B' float"),
+ dst,
+ width * height);
+ }
+ }
+ else
+ {
+ const Babl *fish;
+
+ if (alpha)
+ fish = babl_fish ("cmykA u8", "R'G'B'A float");
+ else
+ fish = babl_fish ("cmyk u8", "R'G'B' float");
+
+ babl_process (fish, src, dst, width * height);
+ }
+
+ return (guchar*) dst;
+}
+
+static gint
+add_layers (gint32 image_id,
+ PSDimage *img_a,
+ PSDlayer **lyr_a,
+ FILE *f,
+ GError **error)
+{
+ PSDchannel **lyr_chn;
+ GArray *parent_group_stack;
+ gint32 parent_group_id = -1;
+ gint32 clipping_group_id = -1;
+ guint16 alpha_chn;
+ guint16 user_mask_chn;
+ guint16 layer_channels, base_channels;
+ guint16 channel_idx[MAX_CHANNELS];
+ guint16 *rle_pack_len;
+ guint16 bps;
+ gint32 l_x; /* Layer x */
+ gint32 l_y; /* Layer y */
+ gint32 l_w; /* Layer width */
+ gint32 l_h; /* Layer height */
+ gint32 lm_x; /* Layer mask x */
+ gint32 lm_y; /* Layer mask y */
+ gint32 lm_w; /* Layer mask width */
+ gint32 lm_h; /* Layer mask height */
+ gint32 layer_id = -1;
+ gint32 mask_id = -1;
+ gint32 active_layer_id = -1;
+ gint lidx; /* Layer index */
+ gint cidx; /* Channel index */
+ gint gidx; /* Clipping group start index */
+ gint rowi; /* Row index */
+ gboolean alpha;
+ gboolean user_mask;
+ gboolean empty;
+ gboolean empty_mask;
+ gboolean use_clipping_group;
+ GeglBuffer *buffer;
+ GimpImageType image_type;
+ LayerModeInfo mode_info;
+
+
+ IFDBG(2) g_debug ("Number of layers: %d", img_a->num_layers);
+
+ if (img_a->merged_image_only || img_a->num_layers == 0)
+ {
+ IFDBG(2) g_debug ("No layers to process");
+ return 0;
+ }
+
+ /* Layered image - Photoshop 3 style */
+ if (fseek (f, img_a->layer_data_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ IFDBG(3) g_debug ("Pre process layers...");
+ use_clipping_group = FALSE;
+ gidx = -1;
+ for (lidx = 0; lidx < img_a->num_layers; ++lidx)
+ {
+ if (lyr_a[lidx]->clipping == 1)
+ {
+ /* Photoshop handles layers with clipping differently than GIMP does.
+ * To correctly show these layers we need to make a new group
+ * starting with the first non-clipping layer and including all
+ * the clipping layers above it.
+ */
+ if (lidx > 0)
+ {
+ if (gidx == -1)
+ {
+ use_clipping_group = TRUE;
+
+ /* Looking at the results we should ignore layer groups */
+ if (lyr_a[lidx-1]->group_type == 0)
+ gidx = lidx - 1;
+ else
+ gidx = lidx;
+
+ lyr_a[gidx]->clipping_group_type = 1; /* start clipping group */
+ IFDBG(3) g_debug ("Layer: %s - start of clipping group", lyr_a[gidx]->name);
+ }
+ else if (lidx + 1 == img_a->num_layers && use_clipping_group)
+ {
+ /* end clipping group at the top of the layer stack */
+ lyr_a[lidx]->clipping_group_type = 2; /* end clipping group */
+ IFDBG(3) g_debug ("Layer: %s - end of clipping group", lyr_a[lidx]->name);
+
+ use_clipping_group = FALSE;
+ gidx = -1;
+ }
+ else
+ {
+ lyr_a[lidx]->clipping_group_type = 0;
+ }
+ }
+ }
+ else if (use_clipping_group)
+ {
+ /* end clipping group */
+ lyr_a[lidx-1]->clipping_group_type = 2;
+ IFDBG(3) g_debug ("Layer: %s - end of clipping group", lyr_a[lidx-1]->name);
+
+ use_clipping_group = FALSE;
+ gidx = -1;
+ }
+ else
+ {
+ lyr_a[lidx]->clipping_group_type = 0;
+ }
+ }
+
+ /* set the root of the group hierarchy */
+ parent_group_stack = g_array_new (FALSE, FALSE, sizeof (gint32));
+ g_array_append_val (parent_group_stack, parent_group_id);
+
+ for (lidx = 0; lidx < img_a->num_layers; ++lidx)
+ {
+ IFDBG(2) g_debug ("Process Layer No %d (%s).", lidx, lyr_a[lidx]->name);
+
+ if (lyr_a[lidx]->drop)
+ {
+ IFDBG(2) g_debug ("Drop layer %d", lidx);
+
+ /* Step past layer data */
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (fseek (f, lyr_a[lidx]->chn_info[cidx].data_len, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ /* Empty layer */
+ if (lyr_a[lidx]->bottom - lyr_a[lidx]->top == 0
+ || lyr_a[lidx]->right - lyr_a[lidx]->left == 0)
+ empty = TRUE;
+ else
+ empty = FALSE;
+
+ /* Empty mask */
+ if (lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top == 0
+ || lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left == 0)
+ empty_mask = TRUE;
+ else
+ empty_mask = FALSE;
+
+ IFDBG(3) g_debug ("Empty mask %d, size %d %d", empty_mask,
+ lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top,
+ lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left);
+
+ /* Load layer channel data */
+ IFDBG(2) g_debug ("Number of channels: %d", lyr_a[lidx]->num_channels);
+ /* Create pointer array for the channel records */
+ lyr_chn = g_new (PSDchannel *, lyr_a[lidx]->num_channels);
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ guint16 comp_mode = PSD_COMP_RAW;
+
+ /* Allocate channel record */
+ lyr_chn[cidx] = g_malloc (sizeof (PSDchannel) );
+
+ lyr_chn[cidx]->id = lyr_a[lidx]->chn_info[cidx].channel_id;
+ lyr_chn[cidx]->rows = lyr_a[lidx]->bottom - lyr_a[lidx]->top;
+ lyr_chn[cidx]->columns = lyr_a[lidx]->right - lyr_a[lidx]->left;
+ lyr_chn[cidx]->data = NULL;
+
+ if (lyr_chn[cidx]->id == PSD_CHANNEL_EXTRA_MASK)
+ {
+ if (fseek (f, lyr_a[lidx]->chn_info[cidx].data_len, SEEK_CUR) != 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ continue;
+ }
+ else if (lyr_chn[cidx]->id == PSD_CHANNEL_MASK)
+ {
+ /* Works around a bug in panotools psd files where the layer mask
+ size is given as 0 but data exists. Set mask size to layer size.
+ */
+ if (empty_mask && lyr_a[lidx]->chn_info[cidx].data_len - 2 > 0)
+ {
+ empty_mask = FALSE;
+ if (lyr_a[lidx]->layer_mask.top == lyr_a[lidx]->layer_mask.bottom)
+ {
+ lyr_a[lidx]->layer_mask.top = lyr_a[lidx]->top;
+ lyr_a[lidx]->layer_mask.bottom = lyr_a[lidx]->bottom;
+ }
+ if (lyr_a[lidx]->layer_mask.right == lyr_a[lidx]->layer_mask.left)
+ {
+ lyr_a[lidx]->layer_mask.right = lyr_a[lidx]->right;
+ lyr_a[lidx]->layer_mask.left = lyr_a[lidx]->left;
+ }
+ }
+ lyr_chn[cidx]->rows = (lyr_a[lidx]->layer_mask.bottom -
+ lyr_a[lidx]->layer_mask.top);
+ lyr_chn[cidx]->columns = (lyr_a[lidx]->layer_mask.right -
+ lyr_a[lidx]->layer_mask.left);
+ }
+
+ IFDBG(3) g_debug ("Channel id %d, %dx%d",
+ lyr_chn[cidx]->id,
+ lyr_chn[cidx]->columns,
+ lyr_chn[cidx]->rows);
+
+ /* Only read channel data if there is any channel
+ * data. Note that the channel data can contain a
+ * compression method but no actual data.
+ */
+ if (lyr_a[lidx]->chn_info[cidx].data_len >= COMP_MODE_SIZE)
+ {
+ if (fread (&comp_mode, COMP_MODE_SIZE, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ comp_mode = GUINT16_FROM_BE (comp_mode);
+ IFDBG(3) g_debug ("Compression mode: %d", comp_mode);
+ }
+ if (lyr_a[lidx]->chn_info[cidx].data_len > COMP_MODE_SIZE)
+ {
+ switch (comp_mode)
+ {
+ case PSD_COMP_RAW: /* Planar raw data */
+ IFDBG(3) g_debug ("Raw data length: %d",
+ lyr_a[lidx]->chn_info[cidx].data_len - 2);
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ PSD_COMP_RAW, NULL, f, 0,
+ error) < 1)
+ return -1;
+ break;
+
+ case PSD_COMP_RLE: /* Packbits */
+ IFDBG(3) g_debug ("RLE channel length %d, RLE length data: %d, "
+ "RLE data block: %d",
+ lyr_a[lidx]->chn_info[cidx].data_len - 2,
+ lyr_chn[cidx]->rows * 2,
+ (lyr_a[lidx]->chn_info[cidx].data_len - 2 -
+ lyr_chn[cidx]->rows * 2));
+ rle_pack_len = g_malloc (lyr_chn[cidx]->rows * 2);
+ for (rowi = 0; rowi < lyr_chn[cidx]->rows; ++rowi)
+ {
+ if (fread (&rle_pack_len[rowi], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (rle_pack_len);
+ return -1;
+ }
+ rle_pack_len[rowi] = GUINT16_FROM_BE (rle_pack_len[rowi]);
+ }
+
+ IFDBG(3) g_debug ("RLE decode - data");
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ PSD_COMP_RLE, rle_pack_len, f, 0,
+ error) < 1)
+ return -1;
+
+ g_free (rle_pack_len);
+ break;
+
+ case PSD_COMP_ZIP: /* ? */
+ case PSD_COMP_ZIP_PRED:
+ if (read_channel_data (lyr_chn[cidx], img_a->bps,
+ comp_mode, NULL, f,
+ lyr_a[lidx]->chn_info[cidx].data_len - 2,
+ error) < 1)
+ return -1;
+ break;
+
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported compression mode: %d"), comp_mode);
+ return -1;
+ break;
+ }
+ }
+ }
+
+ /* Draw layer */
+
+ alpha = FALSE;
+ alpha_chn = -1;
+ user_mask = FALSE;
+ user_mask_chn = -1;
+ layer_channels = 0;
+ l_x = 0;
+ l_y = 0;
+ l_w = img_a->columns;
+ l_h = img_a->rows;
+ if (parent_group_stack->len > 0)
+ parent_group_id = g_array_index (parent_group_stack, gint32,
+ parent_group_stack->len - 1);
+ else
+ parent_group_id = -1; /* root */
+
+ if (img_a->color_mode == PSD_CMYK)
+ base_channels = 4;
+ else if (img_a->color_mode == PSD_RGB || img_a->color_mode == PSD_LAB)
+ base_channels = 3;
+ else
+ base_channels = 1;
+
+ IFDBG(3) g_debug ("Re-hash channel indices");
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ {
+ if (lyr_chn[cidx]->id == PSD_CHANNEL_MASK)
+ {
+ user_mask = TRUE;
+ user_mask_chn = cidx;
+ }
+ else if (lyr_chn[cidx]->id == PSD_CHANNEL_ALPHA)
+ {
+ alpha = TRUE;
+ alpha_chn = cidx;
+ }
+ else if (lyr_chn[cidx]->data)
+ {
+ if (layer_channels < base_channels)
+ {
+ channel_idx[layer_channels] = cidx; /* Assumes in sane order */
+ layer_channels++; /* RGB, Lab, CMYK etc. */
+ }
+ else
+ {
+ /* channel_idx[base_channels] is reserved for alpha channel,
+ * but this layer apparently has extra channels.
+ * From the one example I have (see #8411) it looks like
+ * that channel is the same as the alpha channel. */
+ IFDBG(2) g_debug ("This layer has an extra channel (id: %d)", lyr_chn[cidx]->id);
+ channel_idx[layer_channels+1] = cidx; /* Assumes in sane order */
+ layer_channels += 2; /* RGB, Lab, CMYK etc. */
+ }
+ }
+ else
+ {
+ IFDBG(4) g_debug ("Channel %d (id: %d) has no data", cidx, lyr_chn[cidx]->id);
+ }
+ }
+
+ if (alpha)
+ {
+ if (layer_channels <= base_channels)
+ {
+ channel_idx[layer_channels] = alpha_chn;
+ layer_channels++;
+ }
+ else
+ {
+ channel_idx[base_channels] = alpha_chn;
+ }
+ base_channels++;
+ }
+
+ IFDBG(4) g_debug ("Create the layer (group type: %d, clipping group type: %d)",
+ lyr_a[lidx]->group_type, lyr_a[lidx]->clipping_group_type);
+
+ /* Create the layer */
+ if (lyr_a[lidx]->group_type != 0)
+ {
+ if (lyr_a[lidx]->group_type == 3)
+ {
+ /* the </Layer group> marker layers are used to
+ * assemble the layer structure in a single pass
+ */
+ IFDBG(2) g_debug ("Create placeholder group layer");
+ layer_id = gimp_layer_group_new (image_id);
+ /* add this group layer as the new parent */
+ g_array_append_val (parent_group_stack, layer_id);
+ }
+ else /* group-type == 1 || group_type == 2 */
+ {
+ if (parent_group_stack->len)
+ {
+ layer_id = g_array_index (parent_group_stack, gint32,
+ parent_group_stack->len - 1);
+ IFDBG(2) g_debug ("End group layer id %d.", layer_id);
+ /* since the layers are stored in reverse, the group
+ * layer start marker actually means we're done with
+ * that layer group
+ */
+ g_array_remove_index (parent_group_stack,
+ parent_group_stack->len - 1);
+
+ gimp_drawable_offsets (layer_id, &l_x, &l_y);
+
+ l_w = gimp_drawable_width (layer_id);
+ l_h = gimp_drawable_height (layer_id);
+ }
+ else
+ {
+ IFDBG(1) g_debug ("WARNING: Unmatched group layer start marker.");
+ layer_id = -1;
+ }
+ }
+ }
+ else
+ {
+
+ if (lyr_a[lidx]->clipping_group_type == 1)
+ {
+ clipping_group_id = add_clipping_group (image_id, parent_group_id);
+ }
+
+ if (empty)
+ {
+ IFDBG(2) g_debug ("Create blank layer");
+ }
+ else
+ {
+ IFDBG(2) g_debug ("Create normal layer");
+ l_x = lyr_a[lidx]->left;
+ l_y = lyr_a[lidx]->top;
+ l_w = lyr_a[lidx]->right - lyr_a[lidx]->left;
+ l_h = lyr_a[lidx]->bottom - lyr_a[lidx]->top;
+ }
+
+ image_type = get_gimp_image_type (img_a->base_type, TRUE);
+ IFDBG(3) g_debug ("Layer type %d", image_type);
+
+ layer_id = gimp_layer_new (image_id, lyr_a[lidx]->name,
+ l_w, l_h, image_type,
+ 100, GIMP_LAYER_MODE_NORMAL);
+ }
+
+ if (layer_id != -1)
+ {
+ /* Set the layer name. Note that we do this even for group-end
+ * markers, to avoid having the default group name collide with
+ * subsequent layers; the real group name is set by the group
+ * start marker.
+ */
+ gimp_item_set_name (layer_id, lyr_a[lidx]->name);
+
+ /* Set the layer properties (skip this for layer group end
+ * markers; we set their properties when processing the start
+ * marker.)
+ */
+ if (lyr_a[lidx]->group_type != 3)
+ {
+ /* Mode */
+ psd_to_gimp_blend_mode (lyr_a[lidx], &mode_info);
+ gimp_layer_set_mode (layer_id, mode_info.mode);
+ gimp_layer_set_blend_space (layer_id, mode_info.blend_space);
+ gimp_layer_set_composite_space (layer_id, mode_info.composite_space);
+ gimp_layer_set_composite_mode (layer_id, mode_info.composite_mode);
+
+ /* Opacity */
+ gimp_layer_set_opacity (layer_id,
+ lyr_a[lidx]->opacity * 100.0 / 255.0);
+
+ /* Flags */
+ gimp_layer_set_lock_alpha (layer_id, lyr_a[lidx]->layer_flags.trans_prot);
+ gimp_item_set_visible (layer_id, lyr_a[lidx]->layer_flags.visible);
+#if 0
+ /* according to the spec, the 'irrelevant' flag indicates
+ * that the layer's "pixel data is irrelevant to the
+ * appearance of the document". what this seems to mean is
+ * not that the layer doesn't contribute to the image, but
+ * rather that its appearance can be entirely derived from
+ * sources other than the pixel data, such as vector data.
+ * in particular, this doesn't mean that the layer is
+ * invisible. since we only support raster layers atm, we can
+ * just ignore this flag.
+ */
+ if (lyr_a[lidx]->layer_flags.irrelevant &&
+ lyr_a[lidx]->group_type == 0)
+ {
+ gimp_item_set_visible (layer_id, FALSE);
+ }
+#endif
+
+ /* Position */
+ if (l_x != 0 || l_y != 0)
+ gimp_layer_set_offsets (layer_id, l_x, l_y);
+
+ /* Color tag */
+ gimp_item_set_color_tag (layer_id,
+ psd_to_gimp_layer_color_tag (lyr_a[lidx]->color_tag[0]));
+
+ /* Tattoo */
+ if (lyr_a[lidx]->id)
+ gimp_item_set_tattoo (layer_id, lyr_a[lidx]->id);
+
+ /* For layer groups, expand or collapse the group */
+ if (lyr_a[lidx]->group_type != 0)
+ {
+ gimp_item_set_expanded (layer_id,
+ lyr_a[lidx]->group_type == 1);
+ }
+ }
+
+ /* Remember the active layer ID */
+ if (lidx == img_a->layer_state)
+ {
+ active_layer_id = layer_id;
+ }
+
+ /* Set the layer data */
+ if (lyr_a[lidx]->group_type == 0)
+ {
+ IFDBG(3) g_debug ("Draw layer");
+
+ if (empty)
+ {
+ gimp_drawable_fill (layer_id, GIMP_FILL_TRANSPARENT);
+ }
+ else
+ {
+ GeglBufferIterator *iter;
+
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ buffer = gimp_drawable_get_buffer (layer_id);
+
+ iter = gegl_buffer_iterator_new (
+ buffer, NULL, 0, get_layer_format (img_a, alpha),
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const GeglRectangle *roi = &iter->items[0].roi;
+ guint8 *dst0 = iter->items[0].data;
+ gint src_step = bps;
+ gint dst_step = bps * base_channels;
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ dst0 = gegl_scratch_alloc (base_channels *
+ iter->length);
+ }
+
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ {
+ gint b;
+
+ if (roi->x == 0 && roi->y == 0)
+ IFDBG(3) g_debug ("Start channel %d", channel_idx[cidx]);
+
+ for (b = 0; b < bps; ++b)
+ {
+ guint8 *dst;
+ gint y;
+
+ dst = &dst0[cidx * bps + b];
+
+ for (y = 0; y < roi->height; y++)
+ {
+ const guint8 *src;
+ gint x;
+
+ src = (const guint8 *)
+ &lyr_chn[channel_idx[cidx]]->data[
+ ((roi->y + y) * l_w +
+ roi->x) * bps +
+ b];
+
+ for (x = 0; x < roi->width; ++x)
+ {
+ *dst = *src;
+
+ src += src_step;
+ dst += dst_step;
+ }
+ }
+ }
+ }
+
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ psd_convert_cmyk_to_srgb (
+ img_a,
+ iter->items[0].data, dst0,
+ roi->width, roi->height,
+ alpha);
+
+ gegl_scratch_free (dst0);
+ }
+ }
+
+ for (cidx = 0; cidx < layer_channels; ++cidx)
+ g_free (lyr_chn[channel_idx[cidx]]->data);
+
+ g_object_unref (buffer);
+ }
+ }
+
+ /* Layer mask */
+ if (user_mask && lyr_a[lidx]->group_type != 3)
+ {
+ if (empty_mask)
+ {
+ IFDBG(3) g_debug ("Create empty mask");
+ if (lyr_a[lidx]->layer_mask.def_color == 255)
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_WHITE);
+ else
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_BLACK);
+ gimp_layer_add_mask (layer_id, mask_id);
+ gimp_layer_set_apply_mask (layer_id,
+ ! lyr_a[lidx]->layer_mask.mask_flags.disabled);
+ }
+ else
+ {
+ GeglRectangle mask_rect;
+
+ /* Load layer mask data */
+ lm_x = lyr_a[lidx]->layer_mask.left - l_x;
+ lm_y = lyr_a[lidx]->layer_mask.top - l_y;
+ lm_w = lyr_a[lidx]->layer_mask.right - lyr_a[lidx]->layer_mask.left;
+ lm_h = lyr_a[lidx]->layer_mask.bottom - lyr_a[lidx]->layer_mask.top;
+ IFDBG(3) g_debug ("Mask channel index %d", user_mask_chn);
+ IFDBG(3) g_debug ("Original Mask %d %d %d %d", lm_x, lm_y, lm_w, lm_h);
+ /* Crop mask at layer boundary, and draw layer mask data,
+ * if any
+ */
+ if (gegl_rectangle_intersect (
+ &mask_rect,
+ GEGL_RECTANGLE (0, 0, l_w, l_h),
+ GEGL_RECTANGLE (lm_x, lm_y, lm_w, lm_h)))
+ {
+ IFDBG(3) g_debug ("Layer %d %d %d %d", l_x, l_y, l_w, l_h);
+ IFDBG(3) g_debug ("Mask %d %d %d %d",
+ mask_rect.x, mask_rect.y,
+ mask_rect.width, mask_rect.height);
+
+ if (lyr_a[lidx]->layer_mask.def_color == 255)
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_WHITE);
+ else
+ mask_id = gimp_layer_create_mask (layer_id,
+ GIMP_ADD_MASK_BLACK);
+
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ IFDBG(3) g_debug ("New layer mask %d", mask_id);
+ gimp_layer_add_mask (layer_id, mask_id);
+ buffer = gimp_drawable_get_buffer (mask_id);
+ gegl_buffer_set (buffer,
+ &mask_rect,
+ 0, get_mask_format (img_a),
+ lyr_chn[user_mask_chn]->data + (
+ (mask_rect.y - lm_y) * lm_w +
+ (mask_rect.x - lm_x)) * bps,
+ lm_w * bps);
+ g_object_unref (buffer);
+ gimp_layer_set_apply_mask (layer_id,
+ ! lyr_a[lidx]->layer_mask.mask_flags.disabled);
+ }
+ g_free (lyr_chn[user_mask_chn]->data);
+ }
+ }
+
+ /* Insert the layer */
+ if (lyr_a[lidx]->group_type == 0 || /* normal layer */
+ lyr_a[lidx]->group_type == 3 /* group layer end marker */)
+ {
+ if (clipping_group_id != -1)
+ {
+ gimp_image_insert_layer (image_id, layer_id, clipping_group_id, 0);
+
+ if (lyr_a[lidx]->clipping_group_type == 2)
+ {
+ /* End of our clipping group. */
+ clipping_group_id = -1;
+ }
+ }
+ else
+ {
+ gimp_image_insert_layer (image_id, layer_id, parent_group_id, 0);
+ }
+
+ }
+ }
+
+ for (cidx = 0; cidx < lyr_a[lidx]->num_channels; ++cidx)
+ if (lyr_chn[cidx])
+ g_free (lyr_chn[cidx]);
+ g_free (lyr_chn);
+ }
+ g_free (lyr_a[lidx]->chn_info);
+ g_free (lyr_a[lidx]->name);
+ g_free (lyr_a[lidx]);
+ }
+ g_free (lyr_a);
+ g_array_free (parent_group_stack, FALSE);
+
+ /* Set the active layer */
+ if (active_layer_id >= 0)
+ gimp_image_set_active_layer (image_id, active_layer_id);
+
+ return 0;
+}
+
+static gint
+add_merged_image (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDchannel chn_a[MAX_CHANNELS];
+ gchar *alpha_name;
+ guchar *pixels;
+ guint16 comp_mode;
+ guint16 base_channels;
+ guint16 extra_channels;
+ guint16 total_channels;
+ guint16 bps;
+ guint16 *rle_pack_len[MAX_CHANNELS];
+ guint32 alpha_id;
+ gint32 layer_size;
+ gint32 layer_id = -1;
+ gint32 channel_id = -1;
+ gint16 alpha_opacity;
+ gint cidx; /* Channel index */
+ gint rowi; /* Row index */
+ gint offset;
+ gint i;
+ gboolean alpha_visible;
+ gboolean alpha_channel = FALSE;
+ GeglBuffer *buffer;
+ GimpImageType image_type;
+ GimpRGB alpha_rgb;
+
+ total_channels = img_a->channels;
+ extra_channels = 0;
+ bps = img_a->bps / 8;
+ if (bps == 0)
+ bps++;
+
+ if (img_a->num_layers > 0 && img_a->color_mode == PSD_CMYK)
+ {
+ /* In this case there is no conversion. Merged image is RGB. */
+ img_a->color_mode = PSD_RGB;
+ if (! img_a->transparency)
+ total_channels--;
+ }
+
+ if ((img_a->color_mode == PSD_BITMAP ||
+ img_a->color_mode == PSD_MULTICHANNEL ||
+ img_a->color_mode == PSD_GRAYSCALE ||
+ img_a->color_mode == PSD_DUOTONE ||
+ img_a->color_mode == PSD_INDEXED) &&
+ total_channels > 1)
+ {
+ extra_channels = total_channels - 1;
+ }
+ else if ((img_a->color_mode == PSD_RGB ||
+ img_a->color_mode == PSD_LAB) &&
+ total_channels > 3)
+ {
+ extra_channels = total_channels - 3;
+ }
+ else if ((img_a->color_mode == PSD_CMYK) &&
+ total_channels > 4)
+ {
+ extra_channels = total_channels - 4;
+ }
+ if (img_a->transparency && extra_channels > 0)
+ extra_channels--;
+ base_channels = total_channels - extra_channels;
+
+ if (img_a->merged_image_only)
+ {
+ if (! img_a->transparency && extra_channels > 0)
+ {
+ alpha_channel = TRUE;
+ base_channels += 1;
+ }
+ extra_channels = 0;
+ total_channels = base_channels;
+ }
+
+ /* ----- Read merged image & extra channel pixel data ----- */
+ if (img_a->merged_image_only ||
+ img_a->num_layers == 0 ||
+ extra_channels > 0)
+ {
+ guint32 block_len;
+ guint32 block_start;
+
+ block_start = img_a->merged_image_start;
+ block_len = img_a->merged_image_len;
+
+ fseek (f, block_start, SEEK_SET);
+
+ if (fread (&comp_mode, COMP_MODE_SIZE, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ comp_mode = GUINT16_FROM_BE (comp_mode);
+
+ switch (comp_mode)
+ {
+ case PSD_COMP_RAW: /* Planar raw data */
+ IFDBG(3) g_debug ("Raw data length: %d", block_len);
+ for (cidx = 0; cidx < total_channels; ++cidx)
+ {
+ chn_a[cidx].columns = img_a->columns;
+ chn_a[cidx].rows = img_a->rows;
+ if (read_channel_data (&chn_a[cidx], img_a->bps,
+ PSD_COMP_RAW, NULL, f, 0,
+ error) < 1)
+ return -1;
+ }
+ break;
+
+ case PSD_COMP_RLE: /* Packbits */
+ /* Image data is stored as packed scanlines in planar order
+ with all compressed length counters stored first */
+ IFDBG(3) g_debug ("RLE length data: %d, RLE data block: %d",
+ total_channels * img_a->rows * 2,
+ block_len - (total_channels * img_a->rows * 2));
+ for (cidx = 0; cidx < total_channels; ++cidx)
+ {
+ chn_a[cidx].columns = img_a->columns;
+ chn_a[cidx].rows = img_a->rows;
+ rle_pack_len[cidx] = g_malloc (img_a->rows * 2);
+ for (rowi = 0; rowi < img_a->rows; ++rowi)
+ {
+ if (fread (&rle_pack_len[cidx][rowi], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ rle_pack_len[cidx][rowi] = GUINT16_FROM_BE (rle_pack_len[cidx][rowi]);
+ }
+ }
+
+ /* Skip channel length data for unloaded channels */
+ if (fseek (f, (img_a->channels - total_channels) * img_a->rows * 2,
+ SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ IFDBG(3) g_debug ("RLE decode - data");
+ for (cidx = 0; cidx < total_channels; ++cidx)
+ {
+ if (read_channel_data (&chn_a[cidx], img_a->bps,
+ PSD_COMP_RLE, rle_pack_len[cidx], f, 0,
+ error) < 1)
+ return -1;
+ g_free (rle_pack_len[cidx]);
+ }
+ break;
+
+ case PSD_COMP_ZIP: /* ? */
+ case PSD_COMP_ZIP_PRED:
+ default:
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported compression mode: %d"), comp_mode);
+ return -1;
+ break;
+ }
+ }
+
+ /* ----- Draw merged image ----- */
+ if (img_a->merged_image_only ||
+ img_a->num_layers == 0) /* Merged image - Photoshop 2 style */
+ {
+ image_type = get_gimp_image_type (img_a->base_type,
+ img_a->transparency || alpha_channel);
+
+ layer_size = img_a->columns * img_a->rows;
+ pixels = g_malloc (layer_size * base_channels * bps);
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ {
+ for (i = 0; i < layer_size; ++i)
+ {
+ memcpy (&pixels[((i * base_channels) + cidx) * bps],
+ &chn_a[cidx].data[i * bps], bps);
+ }
+ g_free (chn_a[cidx].data);
+ }
+
+ /* Add background layer */
+ IFDBG(2) g_debug ("Draw merged image");
+ layer_id = gimp_layer_new (image_id, _("Background"),
+ img_a->columns, img_a->rows,
+ image_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+ gimp_image_insert_layer (image_id, layer_id, -1, 0);
+
+ buffer = gimp_drawable_get_buffer (layer_id);
+ if (img_a->color_mode == PSD_CMYK)
+ {
+ guchar *dst0;
+
+ dst0 = g_malloc (base_channels * layer_size * sizeof(float));
+ psd_convert_cmyk_to_srgb ( img_a,
+ dst0, pixels,
+ img_a->columns, img_a->rows,
+ alpha_channel);
+ g_free (pixels);
+ pixels = dst0;
+ }
+
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)),
+ 0, get_layer_format (img_a,
+ img_a->transparency ||
+ alpha_channel),
+ pixels, GEGL_AUTO_ROWSTRIDE);
+
+ if (img_a->color_mode == PSD_CMYK)
+ img_a->color_mode = PSD_RGB;
+
+ /* Merged image data is blended against white. Unblend it. */
+ if (img_a->transparency)
+ {
+ GeglBufferIterator *iter;
+
+ iter = gegl_buffer_iterator_new (buffer, NULL, 0,
+ babl_format ("R'G'B'A float"),
+ GEGL_ACCESS_READWRITE,
+ GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *data = iter->items[0].data;
+
+ for (i = 0; i < iter->length; i++)
+ {
+ gint c;
+
+ if (data[3])
+ {
+ for (c = 0; c < 3; c++)
+ data[c] = (data[c] + data[3] - 1.0f) / data[3];
+ }
+
+ data += 4;
+ }
+ }
+ }
+
+ g_object_unref (buffer);
+ g_free (pixels);
+ }
+ else
+ {
+ /* Free merged image data for layered image */
+ if (extra_channels)
+ for (cidx = 0; cidx < base_channels; ++cidx)
+ g_free (chn_a[cidx].data);
+ }
+
+ if (img_a->transparency)
+ {
+ /* Free "Transparency" channel name */
+ if (img_a->alpha_names)
+ {
+ alpha_name = g_ptr_array_index (img_a->alpha_names, 0);
+ if (alpha_name)
+ g_free (alpha_name);
+ }
+ }
+
+ /* ----- Draw extra alpha channels ----- */
+ if (extra_channels /* Extra alpha channels */
+ && image_id > -1)
+ {
+ IFDBG(2) g_debug ("Add extra channels");
+ pixels = g_malloc(0);
+
+ /* Get channel resource data */
+ if (img_a->transparency)
+ offset = 1;
+ else
+ offset = 0;
+
+ /* Draw channels */
+ IFDBG(2) g_debug ("Number of channels: %d", extra_channels);
+ for (i = 0; i < extra_channels; ++i)
+ {
+ /* Alpha channel name */
+ alpha_name = NULL;
+ alpha_visible = FALSE;
+ /* Quick mask channel*/
+ if (img_a->quick_mask_id)
+ if (i == img_a->quick_mask_id - base_channels + offset)
+ {
+ /* Free "Quick Mask" channel name */
+ alpha_name = g_ptr_array_index (img_a->alpha_names, i + offset);
+ if (alpha_name)
+ g_free (alpha_name);
+ alpha_name = g_strdup (GIMP_IMAGE_QUICK_MASK_NAME);
+ alpha_visible = TRUE;
+ }
+ if (! alpha_name && img_a->alpha_names)
+ if (offset < img_a->alpha_names->len
+ && i + offset <= img_a->alpha_names->len)
+ alpha_name = g_ptr_array_index (img_a->alpha_names, i + offset);
+ if (! alpha_name)
+ alpha_name = g_strdup (_("Extra"));
+
+ if (offset < img_a->alpha_id_count &&
+ offset + i <= img_a->alpha_id_count)
+ alpha_id = img_a->alpha_id[i + offset];
+ else
+ alpha_id = 0;
+ if (offset < img_a->alpha_display_count &&
+ i + offset <= img_a->alpha_display_count)
+ {
+ alpha_rgb = img_a->alpha_display_info[i + offset]->gimp_color;
+ alpha_opacity = img_a->alpha_display_info[i + offset]->opacity;
+ }
+ else
+ {
+ gimp_rgba_set (&alpha_rgb, 1.0, 0.0, 0.0, 1.0);
+ alpha_opacity = 50;
+ }
+
+ cidx = base_channels + i;
+ pixels = g_realloc (pixels, chn_a[cidx].columns * chn_a[cidx].rows * bps);
+ memcpy (pixels, chn_a[cidx].data, chn_a[cidx].columns * chn_a[cidx].rows * bps);
+ channel_id = gimp_channel_new (image_id, alpha_name,
+ chn_a[cidx].columns, chn_a[cidx].rows,
+ alpha_opacity, &alpha_rgb);
+ gimp_image_insert_channel (image_id, channel_id, -1, i);
+ g_free (alpha_name);
+ buffer = gimp_drawable_get_buffer (channel_id);
+ if (alpha_id)
+ gimp_item_set_tattoo (channel_id, alpha_id);
+ gimp_item_set_visible (channel_id, alpha_visible);
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gegl_buffer_get_width (buffer),
+ gegl_buffer_get_height (buffer)),
+ 0, get_channel_format (img_a),
+ pixels, GEGL_AUTO_ROWSTRIDE);
+ g_object_unref (buffer);
+ g_free (chn_a[cidx].data);
+ }
+
+ g_free (pixels);
+ if (img_a->alpha_names)
+ g_ptr_array_free (img_a->alpha_names, TRUE);
+
+ if (img_a->alpha_id)
+ g_free (img_a->alpha_id);
+
+ if (img_a->alpha_display_info)
+ {
+ for (cidx = 0; cidx < img_a->alpha_display_count; ++cidx)
+ g_free (img_a->alpha_display_info[cidx]);
+ g_free (img_a->alpha_display_info);
+ }
+ }
+
+ /* FIXME gimp image tattoo state */
+
+ return 0;
+}
+
+
+/* Local utility functions */
+static gint32
+add_clipping_group (gint32 image_id,
+ gint32 parent_id)
+{
+ gint32 clipping_group_id = -1;
+
+ /* We need to create a group because GIMP handles clipping and
+ * composition mode in a different manner than PS. */
+ IFDBG(2) g_debug ("Creating a layer group to handle PS transparency clipping correctly.");
+
+ clipping_group_id = gimp_layer_group_new (image_id);
+
+ gimp_item_set_name (clipping_group_id, "Group added by GIMP");
+ gimp_layer_set_blend_space (clipping_group_id, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
+ gimp_layer_set_composite_space (clipping_group_id, GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL);
+ gimp_layer_set_composite_mode (clipping_group_id, GIMP_LAYER_COMPOSITE_UNION);
+
+ gimp_image_insert_layer (image_id, clipping_group_id, parent_id, 0);
+
+ return clipping_group_id;
+}
+
+static gchar *
+get_psd_color_mode_name (PSDColorMode mode)
+{
+ static gchar * const psd_color_mode_names[] =
+ {
+ "BITMAP",
+ "GRAYSCALE",
+ "INDEXED",
+ "RGB",
+ "CMYK",
+ "UNKNOWN (5)",
+ "UNKNOWN (6)",
+ "MULTICHANNEL",
+ "DUOTONE",
+ "LAB"
+ };
+
+ static gchar *err_name = NULL;
+
+ if (mode >= PSD_BITMAP && mode <= PSD_LAB)
+ return psd_color_mode_names[mode];
+
+ g_free (err_name);
+ err_name = g_strdup_printf ("UNKNOWN (%d)", mode);
+
+ return err_name;
+}
+
+static void
+psd_to_gimp_color_map (guchar *map256)
+{
+ guchar *tmpmap;
+ gint i;
+
+ tmpmap = g_malloc (3 * 256);
+
+ for (i = 0; i < 256; ++i)
+ {
+ tmpmap[i*3 ] = map256[i];
+ tmpmap[i*3+1] = map256[i+256];
+ tmpmap[i*3+2] = map256[i+512];
+ }
+
+ memcpy (map256, tmpmap, 3 * 256);
+ g_free (tmpmap);
+}
+
+static GimpImageType
+get_gimp_image_type (GimpImageBaseType image_base_type,
+ gboolean alpha)
+{
+ GimpImageType image_type;
+
+ switch (image_base_type)
+ {
+ case GIMP_GRAY:
+ image_type = (alpha) ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
+ break;
+
+ case GIMP_INDEXED:
+ image_type = (alpha) ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+ break;
+
+ case GIMP_RGB:
+ image_type = (alpha) ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ break;
+
+ default:
+ image_type = -1;
+ break;
+ }
+
+ return image_type;
+}
+
+static voidpf
+zzalloc (voidpf opaque, uInt items, uInt size)
+{
+ /* overflow check missing */
+ return g_malloc (items * size);
+}
+
+static void
+zzfree (voidpf opaque, voidpf address)
+{
+ g_free (address);
+}
+
+static gint
+read_channel_data (PSDchannel *channel,
+ guint16 bps,
+ guint16 compression,
+ const guint16 *rle_pack_len,
+ FILE *f,
+ guint32 comp_len,
+ GError **error)
+{
+ gchar *raw_data;
+ gchar *src;
+ guint32 readline_len;
+ gint i, j;
+
+ if (bps == 1)
+ readline_len = ((channel->columns + 7) / 8);
+ else
+ readline_len = (channel->columns * bps / 8);
+
+ IFDBG(3) g_debug ("raw data size %d x %d = %d", readline_len,
+ channel->rows, readline_len * channel->rows);
+
+ /* sanity check, int overflow check (avoid divisions by zero) */
+ if ((channel->rows == 0) || (channel->columns == 0) ||
+ (channel->rows > G_MAXINT32 / channel->columns / MAX (bps / 8, 1)))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unsupported or invalid channel size"));
+ return -1;
+ }
+
+ raw_data = g_malloc (readline_len * channel->rows);
+ switch (compression)
+ {
+ case PSD_COMP_RAW:
+ if (fread (raw_data, readline_len, channel->rows, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ break;
+
+ case PSD_COMP_RLE:
+ for (i = 0; i < channel->rows; ++i)
+ {
+ src = gegl_scratch_alloc (rle_pack_len[i]);
+/* FIXME check for over-run
+ if (ftell (f) + rle_pack_len[i] > block_end)
+ {
+ psd_set_error (TRUE, errno, error);
+ return -1;
+ }
+*/
+ if (fread (src, rle_pack_len[i], 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ gegl_scratch_free (src);
+ return -1;
+ }
+ /* FIXME check for errors returned from decode packbits */
+ decode_packbits (src, raw_data + i * readline_len,
+ rle_pack_len[i], readline_len);
+ gegl_scratch_free (src);
+ }
+ break;
+ case PSD_COMP_ZIP:
+ case PSD_COMP_ZIP_PRED:
+ {
+ z_stream zs;
+
+ src = g_malloc (comp_len);
+ if (fread (src, comp_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (src);
+ return -1;
+ }
+
+ zs.next_in = (guchar*) src;
+ zs.avail_in = comp_len;
+ zs.next_out = (guchar*) raw_data;
+ zs.avail_out = readline_len * channel->rows;
+ zs.zalloc = zzalloc;
+ zs.zfree = zzfree;
+
+ if (inflateInit (&zs) == Z_OK &&
+ inflate (&zs, Z_FINISH) == Z_STREAM_END)
+ {
+ inflateEnd (&zs);
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Failed to decompress data"));
+ g_free (src);
+ return -1;
+ }
+
+ g_free (src);
+ break;
+ }
+ }
+
+ /* Convert channel data to GIMP format */
+ switch (bps)
+ {
+ case 32:
+ {
+ guint32 *data = (guint32*) raw_data;
+
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ for (i = 0; i < channel->rows * channel->columns; ++i)
+ data[i] = GUINT32_FROM_BE (data[i]);
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ data[i * channel->columns + j] += data[i * channel->columns + j - 1];
+ }
+ break;
+ }
+
+ case 16:
+ {
+ guint16 *data = (guint16*) raw_data;
+
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ for (i = 0; i < channel->rows * channel->columns; ++i)
+ data[i] = GUINT16_FROM_BE (data[i]);
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ data[i * channel->columns + j] += data[i * channel->columns + j - 1];
+ }
+ break;
+ }
+
+ case 8:
+ channel->data = raw_data;
+ raw_data = NULL;
+
+ if (compression == PSD_COMP_ZIP_PRED)
+ {
+ for (i = 0; i < channel->rows; ++i)
+ for (j = 1; j < channel->columns; ++j)
+ channel->data[i * channel->columns + j] += channel->data[i * channel->columns + j - 1];
+ }
+ break;
+
+ case 1:
+ channel->data = (gchar *) g_malloc (channel->rows * channel->columns);
+ convert_1_bit (raw_data, channel->data, channel->rows, channel->columns);
+ break;
+
+ default:
+ return -1;
+ break;
+ }
+
+ g_free (raw_data);
+
+ return 1;
+}
+
+static void
+convert_1_bit (const gchar *src,
+ gchar *dst,
+ guint32 rows,
+ guint32 columns)
+{
+/* Convert bits to bytes left to right by row.
+ Rows are padded out to a byte boundary.
+*/
+ guint32 row_pos = 0;
+ gint i, j;
+
+ IFDBG(3) g_debug ("Start 1 bit conversion");
+
+ for (i = 0; i < rows * ((columns + 7) / 8); ++i)
+ {
+ guchar mask = 0x80;
+ for (j = 0; j < 8 && row_pos < columns; ++j)
+ {
+ *dst = (*src & mask) ? 0 : 1;
+ IFDBG(3) g_debug ("byte %d, bit %d, offset %d, src %d, dst %d",
+ i , j, row_pos, *src, *dst);
+ dst++;
+ mask >>= 1;
+ row_pos++;
+ }
+ if (row_pos >= columns)
+ row_pos = 0;
+ src++;
+ }
+ IFDBG(3) g_debug ("End 1 bit conversion");
+}
+
+static const Babl*
+get_layer_format (PSDimage *img_a,
+ gboolean alpha)
+{
+ const Babl *format = NULL;
+
+ switch (get_gimp_image_type (img_a->base_type, alpha))
+ {
+ case GIMP_GRAY_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y' u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y' u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y'A u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y'A u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y'A u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_RGB_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("R'G'B' u32");
+ break;
+
+ case 16:
+ format = babl_format ("R'G'B' u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B' float" : "R'G'B' u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("R'G'B'A u32");
+ break;
+
+ case 16:
+ format = babl_format ("R'G'B'A u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format (img_a->color_mode == PSD_CMYK ? "R'G'B'A float" : "R'G'B'A u8");
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+ break;
+
+ default:
+ return NULL;
+ break;
+ }
+
+ return format;
+}
+
+static const Babl*
+get_channel_format (PSDimage *img_a)
+{
+ const Babl *format = NULL;
+
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y u16");
+ break;
+
+ case 8:
+ case 1:
+ /* see gimp_image_get_channel_format() */
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ break;
+ }
+
+ return format;
+}
+
+static const Babl*
+get_mask_format (PSDimage *img_a)
+{
+ const Babl *format = NULL;
+
+ switch (img_a->bps)
+ {
+ case 32:
+ format = babl_format ("Y u32");
+ break;
+
+ case 16:
+ format = babl_format ("Y u16");
+ break;
+
+ case 8:
+ case 1:
+ format = babl_format ("Y u8");
+ break;
+
+ default:
+ break;
+ }
+
+ return format;
+}
diff --git a/plug-ins/file-psd/psd-load.h b/plug-ins/file-psd/psd-load.h
new file mode 100644
index 0000000..42eb516
--- /dev/null
+++ b/plug-ins/file-psd/psd-load.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_LOAD_H__
+#define __PSD_LOAD_H__
+
+
+gint32 load_image (const gchar *filename,
+ gboolean merged_image_only,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+
+#endif /* __PSD_LOAD_H__ */
diff --git a/plug-ins/file-psd/psd-save.c b/plug-ins/file-psd/psd-save.c
new file mode 100644
index 0000000..e506610
--- /dev/null
+++ b/plug-ins/file-psd/psd-save.c
@@ -0,0 +1,2097 @@
+/*
+ * PSD Export Plugin version 1.0 (BETA)
+ * This GIMP plug-in is designed to export Adobe Photoshop(tm) files (.PSD)
+ *
+ * Monigotes
+ *
+ * If this plug-in fails to export a file which you think it should,
+ * please tell me what seemed to go wrong, and anything you know
+ * about the image you tried to export. Please don't send big PSD
+ * files to me without asking first.
+ *
+ * Copyright (C) 2000 Monigotes
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adobe and Adobe Photoshop are trademarks of Adobe Systems
+ * Incorporated that may be registered in certain jurisdictions.
+ */
+
+/*
+ * Revision history:
+ *
+ * 2000.02 / v1.0 / Monigotes
+ * First version.
+ *
+ * 2003-05-10 Pedro Gimeno <pggimeno@wanadoo.es>
+ * - Cleaned up and GNUstylized.
+ * - Translated all comments and vars in Spanish to English.
+ *
+ * 2005-2-11 Jay Cox <jaycox@gimp.org>
+ * Rewrote all the code that deals with pixels to be stingy with
+ * memory and operate on tile-size chunks. Create a flattened
+ * copy of the image when necessary. Fixes file corruption bug
+ * #167139 and memory bug #121871
+ *
+ * 2006-03-29 Guillermo S. Romero <gsr.bugs@infernal-iceberg.com>
+ * - Added/enabled basic support for layer masks based in psd.c
+ * and whatever was already here.
+ * Layers that are not canvas sized need investigation, here
+ * or in the import plugin something seems wrong.
+ * - Cosmetic changes about the debug messages, more like psd.c.
+ */
+
+/*
+ * TODO:
+ * Export preview
+ */
+
+/*
+ * BUGS:
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimpmath/gimpmath.h"
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* set to TRUE if you want debugging, FALSE otherwise */
+#define DEBUG FALSE
+
+/* 1: Normal debuggin, 2: Deep debuggin */
+#define DEBUG_LEVEL 2
+
+#undef IFDBG /* previously defined in psd.h */
+
+#define IFDBG if (DEBUG)
+#define IF_DEEP_DBG if (DEBUG && DEBUG_LEVEL == 2)
+
+#define PSD_UNIT_INCH 1
+#define PSD_UNIT_CM 2
+
+
+/* Local types etc
+ */
+
+typedef enum PsdLayerType
+{
+ PSD_LAYER_TYPE_LAYER,
+ PSD_LAYER_TYPE_GROUP_START,
+ PSD_LAYER_TYPE_GROUP_END
+} PSD_Layer_Type;
+
+typedef struct PsdLayer
+{
+ gint id;
+ PSD_Layer_Type type;
+} PSD_Layer;
+
+typedef struct PsdImageData
+{
+ gboolean compression;
+
+ gint32 image_height;
+ gint32 image_width;
+
+ GimpImageBaseType baseType;
+
+ gint32 merged_layer;/* Merged image,
+ to be used for the image data section */
+
+ gint nChannels; /* Number of user channels in the image */
+ gint32 *lChannels; /* User channels in the image */
+
+ gint nLayers; /* Number of layers in the image */
+ PSD_Layer *lLayers; /* Layer list */
+} PSD_Image_Data;
+
+static PSD_Image_Data PSDImageData;
+
+/* Declare some local functions.
+ */
+
+static const gchar * psd_lmode_layer (gint32 idLayer,
+ gboolean section_divider);
+
+static void reshuffle_cmap_write (guchar *mapGimp);
+
+static void save_header (FILE *fd,
+ gint32 image_id);
+
+static void save_color_mode_data (FILE *fd,
+ gint32 image_id);
+
+static void save_resources (FILE *fd,
+ gint32 image_id);
+
+static void save_paths (FILE *fd,
+ gint32 image_id);
+
+static void save_layer_and_mask (FILE *fd,
+ gint32 image_id);
+
+static void save_data (FILE *fd,
+ gint32 image_id);
+
+static void double_to_psd_fixed (gdouble value,
+ gchar *target);
+
+static void xfwrite (FILE *fd,
+ gconstpointer buf,
+ glong len,
+ const gchar *why);
+
+static void write_pascalstring (FILE *fd,
+ const gchar *val,
+ gint padding,
+ const gchar *why);
+
+static void write_string (FILE *fd,
+ const gchar *val,
+ const gchar *why);
+
+static void write_gchar (FILE *fd,
+ guchar val,
+ const gchar *why);
+
+static void write_gint16 (FILE *fd,
+ gint16 val,
+ const gchar *why);
+
+static void write_gint32 (FILE *fd,
+ gint32 val,
+ const gchar *why);
+
+static void write_datablock_luni (FILE *fd,
+ const gchar *val,
+ const gchar *why);
+
+
+static void write_pixel_data (FILE *fd,
+ gint32 drawableID,
+ glong *ChanLenPosition,
+ gint32 rowlenOffset,
+ gboolean write_mask);
+
+static gint32 create_merged_image (gint32 imageID);
+
+static gint get_bpc (gint32 imageID);
+static const Babl * get_pixel_format (gint32 drawableID);
+static const Babl * get_channel_format (gint32 drawableID);
+static const Babl * get_mask_format (gint32 drawableID);
+
+static PSD_Layer * image_get_all_layers (gint32 imageID,
+ gint *n_layers);
+
+static const gchar *
+psd_lmode_layer (gint32 idLayer,
+ gboolean section_divider)
+{
+ LayerModeInfo mode_info;
+
+ mode_info.mode = gimp_layer_get_mode (idLayer);
+ mode_info.blend_space = gimp_layer_get_blend_space (idLayer);
+ mode_info.composite_space = gimp_layer_get_composite_space (idLayer);
+ mode_info.composite_mode = gimp_layer_get_composite_mode (idLayer);
+
+ /* pass-through groups use normal mode in their layer record; the
+ * pass-through mode is specified in their section divider resource.
+ */
+ if (mode_info.mode == GIMP_LAYER_MODE_PASS_THROUGH && ! section_divider)
+ mode_info.mode = GIMP_LAYER_MODE_NORMAL;
+
+ return gimp_to_psd_blend_mode (&mode_info);
+}
+
+static void
+write_string (FILE *fd,
+ const gchar *val,
+ const gchar *why)
+{
+ write_gchar (fd, strlen (val), why);
+ xfwrite (fd, val, strlen (val), why);
+}
+
+static void
+write_pascalstring (FILE *fd,
+ const gchar *val,
+ gint padding,
+ const gchar *why)
+{
+ guchar len;
+ gint i;
+
+ /* Calculate string length to write and limit it to 255 */
+
+ len = (strlen (val) > 255) ? 255 : (guchar) strlen (val);
+
+ /* Perform actual writing */
+
+ if (len != 0)
+ {
+ write_gchar (fd, len, why);
+ xfwrite (fd, val, len, why);
+ }
+ else
+ {
+ write_gchar (fd, 0, why);
+ }
+
+ /* If total length (length byte + content) is not a multiple of PADDING,
+ add zeros to pad it. */
+
+ len++; /* Add the length field */
+
+ if ((len % padding) == 0)
+ return;
+
+ for (i = 0; i < (padding - (len % padding)); i++)
+ write_gchar (fd, 0, why);
+}
+
+static void
+xfwrite (FILE *fd,
+ gconstpointer buf,
+ glong len,
+ const gchar *why)
+{
+ if (len == 0)
+ return;
+
+ if (fwrite (buf, len, 1, fd) == 0)
+ {
+ g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
+ gimp_quit ();
+ }
+}
+
+static void
+write_gchar (FILE *fd,
+ guchar val,
+ const gchar *why)
+{
+ guchar b[2];
+ glong pos;
+
+ b[0] = val;
+ b[1] = 0;
+
+ pos = ftell (fd);
+ if (fwrite (&b, 1, 2, fd) == 0)
+ {
+ g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
+ gimp_quit ();
+ }
+ fseek (fd, pos + 1, SEEK_SET);
+}
+
+static void
+write_gint16 (FILE *fd,
+ gint16 val,
+ const gchar *why)
+{
+ guchar b[2];
+ /* b[0] = val & 255;
+ b[1] = (val >> 8) & 255;*/
+
+ b[1] = val & 255;
+ b[0] = (val >> 8) & 255;
+
+ if (fwrite (&b, 1, 2, fd) == 0)
+ {
+ g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
+ gimp_quit ();
+ }
+}
+
+static void
+write_gint32 (FILE *fd,
+ gint32 val,
+ const gchar *why)
+{
+ guchar b[4];
+
+ b[3] = val & 255;
+ b[2] = (val >> 8) & 255;
+ b[1] = (val >> 16) & 255;
+ b[0] = (val >> 24) & 255;
+
+ if (fwrite (&b, 1, 4, fd) == 0)
+ {
+ g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
+ gimp_quit ();
+ }
+}
+
+static void
+write_datablock_luni (FILE *fd,
+ const gchar *val,
+ const gchar *why)
+{
+ if (val)
+ {
+ guint32 count;
+ guint32 xdBlockSize;
+ glong numchars;
+ gunichar2 *luniName;
+
+ luniName = g_utf8_to_utf16 (val, -1, NULL, &numchars, NULL);
+
+ if (luniName)
+ {
+ guchar len = MIN (numchars, 255);
+
+ /* Only pad to even num of chars */
+ if( len % 2 )
+ xdBlockSize = len + 1;
+ else
+ xdBlockSize = len;
+
+ /* 2 bytes / char + 4 bytes for pascal num chars */
+ xdBlockSize = (xdBlockSize * 2) + 4;
+
+ xfwrite (fd, "8BIMluni", 8, "luni xdb signature");
+ write_gint32 (fd, xdBlockSize, "luni xdb size");
+ write_gint32 (fd, len, "luni xdb pascal string");
+
+ for (count = 0; count < len; count++)
+ write_gint16 (fd, luniName[count], "luni xdb pascal string");
+
+ /* Pad to an even number of chars */
+ if (len % 2)
+ write_gint16 (fd, 0x0000, "luni xdb pascal string padding");
+ }
+ }
+}
+
+static gint32
+pack_pb_line (guchar *start,
+ gint32 length,
+ guchar *dest_ptr)
+{
+ gint32 remaining = length;
+ gint i, j;
+
+ length = 0;
+ while (remaining > 0)
+ {
+ /* Look for characters matching the first */
+
+ i = 0;
+ while ((i < 128) &&
+ (remaining - i > 0) &&
+ (start[0] == start[i]))
+ i++;
+
+ if (i > 1) /* Match found */
+ {
+
+ *dest_ptr++ = -(i - 1);
+ *dest_ptr++ = *start;
+
+ start += i;
+ remaining -= i;
+ length += 2;
+ }
+ else /* Look for characters different from the previous */
+ {
+ i = 0;
+ while ((i < 128) &&
+ (remaining - (i + 1) > 0) &&
+ (start[i] != start[i + 1] ||
+ remaining - (i + 2) <= 0 || start[i] != start[i+2]))
+ i++;
+
+ /* If there's only 1 remaining, the previous WHILE stmt doesn't
+ catch it */
+
+ if (remaining == 1)
+ {
+ i = 1;
+ }
+
+ if (i > 0) /* Some distinct ones found */
+ {
+ *dest_ptr++ = i - 1;
+ for (j = 0; j < i; j++)
+ {
+ *dest_ptr++ = start[j];
+ }
+ start += i;
+ remaining -= i;
+ length += i + 1;
+ }
+
+ }
+ }
+ return length;
+}
+
+static gint
+gimpBaseTypeToPsdMode (GimpImageBaseType gimpBaseType)
+{
+ switch (gimpBaseType)
+ {
+ case GIMP_RGB:
+ return 3; /* RGB */
+ case GIMP_GRAY:
+ return 1; /* Grayscale */
+ case GIMP_INDEXED:
+ return 2; /* Indexed */
+ default:
+ g_message (_("Error: Can't convert GIMP base imagetype to PSD mode"));
+ IFDBG printf ("PSD Export: gimpBaseType value is %d, "
+ "can't convert to PSD mode", gimpBaseType);
+ gimp_quit ();
+ return 3; /* Return RGB by default */
+ }
+}
+
+static gint
+nChansLayer (gint gimpBaseType,
+ gint hasAlpha,
+ gint hasMask)
+{
+ gint incAlpha = 0;
+ gint incMask = 0;
+
+ incAlpha = (hasAlpha == 0) ? 0 : 1;
+ incMask = (hasMask == 0) ? 0 : 1;
+
+ switch (gimpBaseType)
+ {
+ case GIMP_RGB:
+ return 3 + incAlpha + incMask; /* R,G,B & Alpha & Mask (if any) */
+ case GIMP_GRAY:
+ return 1 + incAlpha + incMask; /* G & Alpha & Mask (if any) */
+ case GIMP_INDEXED:
+ return 1 + incAlpha + incMask; /* I & Alpha & Mask (if any) */
+ default:
+ return 0; /* Return 0 channels by default */
+ }
+}
+
+static void
+reshuffle_cmap_write (guchar *mapGimp)
+{
+ guchar *mapPSD;
+ gint i;
+
+ mapPSD = g_malloc (768);
+
+ for (i = 0; i < 256; i++)
+ {
+ mapPSD[i] = mapGimp[i * 3];
+ mapPSD[i + 256] = mapGimp[i * 3 + 1];
+ mapPSD[i + 512] = mapGimp[i * 3 + 2];
+ }
+
+ for (i = 0; i < 768; i++)
+ {
+ mapGimp[i] = mapPSD[i];
+ }
+
+ g_free (mapPSD);
+}
+
+static void
+save_header (FILE *fd,
+ gint32 image_id)
+{
+ IFDBG printf (" Function: save_header\n");
+ IFDBG printf ("\tRows: %d\n", PSDImageData.image_height);
+ IFDBG printf ("\tColumns: %d\n", PSDImageData.image_width);
+ IFDBG printf ("\tBase type: %d\n", PSDImageData.baseType);
+ IFDBG printf ("\tNumber of channels: %d\n", PSDImageData.nChannels);
+
+ xfwrite (fd, "8BPS", 4, "signature");
+ write_gint16 (fd, 1, "version");
+ write_gint32 (fd, 0, "reserved 1"); /* 6 for the 'reserved' field + 4 bytes for a long */
+ write_gint16 (fd, 0, "reserved 1"); /* and 2 bytes for a short */
+ write_gint16 (fd, (PSDImageData.nChannels +
+ nChansLayer (PSDImageData.baseType,
+ gimp_drawable_has_alpha (PSDImageData.merged_layer), 0)),
+ "channels");
+ write_gint32 (fd, PSDImageData.image_height, "rows");
+ write_gint32 (fd, PSDImageData.image_width, "columns");
+ write_gint16 (fd, 8 * get_bpc (image_id), "depth");
+ write_gint16 (fd, gimpBaseTypeToPsdMode (PSDImageData.baseType), "mode");
+}
+
+static void
+save_color_mode_data (FILE *fd,
+ gint32 image_id)
+{
+ guchar *cmap;
+ guchar *cmap_modified;
+ gint i;
+ gint32 nColors;
+
+ IFDBG printf (" Function: save_color_mode_data\n");
+
+ switch (PSDImageData.baseType)
+ {
+ case GIMP_INDEXED:
+ IFDBG printf ("\tImage type: INDEXED\n");
+
+ cmap = gimp_image_get_colormap (image_id, &nColors);
+ IFDBG printf ("\t\tLength of colormap returned by gimp_image_get_colormap: %d\n", nColors);
+
+ if (nColors == 0)
+ {
+ IFDBG printf ("\t\tThe indexed image lacks a colormap\n");
+ write_gint32 (fd, 0, "color data length");
+ }
+ else if (nColors != 256)
+ {
+ IFDBG printf ("\t\tThe indexed image has %d!=256 colors\n", nColors);
+ IFDBG printf ("\t\tPadding with zeros up to 256\n");
+ write_gint32 (fd, 768, "color data length");
+ /* For this type, length is always 768 */
+
+ cmap_modified = g_malloc (768);
+ for (i = 0; i < nColors * 3; i++)
+ cmap_modified[i] = cmap[i];
+
+ for (i = nColors * 3; i < 768; i++)
+ cmap_modified[i] = 0;
+
+ reshuffle_cmap_write (cmap_modified);
+ xfwrite (fd, cmap_modified, 768, "colormap"); /* Write readjusted colormap */
+
+ g_free (cmap_modified);
+ }
+ else /* nColors equals 256 */
+ {
+ write_gint32 (fd, 768, "color data length"); /* For this type, length is always 768 */
+ reshuffle_cmap_write (cmap);
+ xfwrite (fd, cmap, 768, "colormap"); /* Write readjusted colormap */
+ }
+ break;
+
+ default:
+ IFDBG printf ("\tImage type: Not INDEXED\n");
+ write_gint32 (fd, 0, "color data length");
+ }
+}
+
+static void
+save_resources (FILE *fd,
+ gint32 image_id)
+{
+ gint i;
+ gchar *fileName; /* Image file name */
+ gint32 idActLayer; /* Id of the active layer */
+ guint nActiveLayer = 0; /* Number of the active layer */
+ gboolean ActiveLayerPresent; /* TRUE if there's an active layer */
+
+ glong eof_pos; /* Position for End of file */
+ glong rsc_pos; /* Position for Lengths of Resources section */
+ glong name_sec; /* Position for Lengths of Channel Names */
+
+
+ /* Only relevant resources in GIMP are: 0x03EE, 0x03F0 & 0x0400 */
+ /* For Adobe Photoshop version 4.0 these can also be considered:
+ 0x0408, 0x040A & 0x040B (1006, 1008, 1024, 1032, 1034, and 1035) */
+
+ IFDBG printf (" Function: save_resources\n");
+
+
+ /* Get the image title from its filename */
+
+ fileName = gimp_image_get_filename (image_id);
+ IFDBG printf ("\tImage title: %s\n", fileName);
+
+ /* Get the active layer number id */
+
+ idActLayer = gimp_image_get_active_layer (image_id);
+ IFDBG printf ("\tCurrent layer id: %d\n", idActLayer);
+
+ ActiveLayerPresent = FALSE;
+ for (i = 0; i < PSDImageData.nLayers; i++)
+ if (idActLayer == PSDImageData.lLayers[i].id)
+ {
+ nActiveLayer = PSDImageData.nLayers - i - 1;
+ ActiveLayerPresent = TRUE;
+ break;
+ }
+
+ if (ActiveLayerPresent)
+ {
+ IFDBG printf ("\t\tActive layer is number %d\n", nActiveLayer);
+ }
+ else
+ {
+ IFDBG printf ("\t\tNo active layer\n");
+ }
+
+
+ /* Here's where actual writing starts */
+
+ rsc_pos = ftell (fd);
+ write_gint32 (fd, 0, "image resources length");
+
+
+ /* --------------- Write Channel names --------------- */
+
+ if (PSDImageData.nChannels > 0 ||
+ gimp_drawable_has_alpha (PSDImageData.merged_layer))
+ {
+ xfwrite (fd, "8BIM", 4, "imageresources signature");
+ write_gint16 (fd, 0x03EE, "0x03EE Id"); /* 1006 */
+ /* write_pascalstring (fd, Name, "Id name"); */
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+
+ /* Mark current position in the file */
+
+ name_sec = ftell (fd);
+ write_gint32 (fd, 0, "0x03EE resource size");
+
+ /* Write all strings */
+
+ /* if the merged_image contains transparency, write a name for it first */
+ if (gimp_drawable_has_alpha (PSDImageData.merged_layer))
+ write_string (fd, "Transparency", "channel name");
+
+ for (i = 0; i < PSDImageData.nChannels; i++)
+ {
+ char *chName = gimp_item_get_name (PSDImageData.lChannels[i]);
+ write_string (fd, chName, "channel name");
+ g_free (chName);
+ }
+ /* Calculate and write actual resource's length */
+
+ eof_pos = ftell (fd);
+
+ fseek (fd, name_sec, SEEK_SET);
+ write_gint32 (fd, eof_pos - name_sec - sizeof (gint32), "0x03EE resource size");
+ IFDBG printf ("\tTotal length of 0x03EE resource: %d\n",
+ (int) (eof_pos - name_sec - sizeof (gint32)));
+
+ /* Return to EOF to continue writing */
+
+ fseek (fd, eof_pos, SEEK_SET);
+
+ /* Pad if length is odd */
+
+ if ((eof_pos - name_sec - sizeof (gint32)) & 1)
+ write_gchar (fd, 0, "pad byte");
+ }
+
+ /* --------------- Write Channel properties --------------- */
+
+ if (PSDImageData.nChannels > 0 ||
+ gimp_drawable_has_alpha (PSDImageData.merged_layer))
+ {
+ xfwrite (fd, "8BIM", 4, "imageresources signature");
+ write_gint16 (fd, 0x0435, "0x0435 Id"); /* 1077 */
+ /* write_pascalstring (fd, Name, "Id name"); */
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+ write_gint32 (fd,
+ 4 +
+ 13 * (gimp_drawable_has_alpha (PSDImageData.merged_layer) +
+ PSDImageData.nChannels),
+ "0x0435 resource size");
+
+ /* The function of the first 4 bytes is unclear. As per
+ * load_resource_1077() in psd-image-res-load.c, it seems to be a version
+ * number that is always one.
+ */
+ write_gint32 (fd, 1, "0x0435 version");
+
+ /* Write all channel properties */
+
+ #define DOUBLE_TO_INT16(x) ROUND (SAFE_CLAMP (x, 0.0, 1.0) * 0xffff)
+
+ /* if the merged_image contains transparency, write its properties first */
+ if (gimp_drawable_has_alpha (PSDImageData.merged_layer))
+ {
+ write_gint16 (fd, PSD_CS_RGB, "channel color space");
+ write_gint16 (fd, DOUBLE_TO_INT16 (1.0), "channel color r");
+ write_gint16 (fd, DOUBLE_TO_INT16 (0.0), "channel color g");
+ write_gint16 (fd, DOUBLE_TO_INT16 (0.0), "channel color b");
+ write_gint16 (fd, 0, "channel color padding");
+ write_gint16 (fd, 100, "channel opacity");
+ write_gchar (fd, 1, "channel mode");
+ }
+
+ for (i = 0; i < PSDImageData.nChannels; i++)
+ {
+ GimpRGB color;
+ gdouble opacity;
+
+ gimp_channel_get_color (PSDImageData.lChannels[i], &color);
+ opacity = gimp_channel_get_opacity (PSDImageData.lChannels[i]);
+
+ write_gint16 (fd, PSD_CS_RGB, "channel color space");
+ write_gint16 (fd, DOUBLE_TO_INT16 (color.r), "channel color r");
+ write_gint16 (fd, DOUBLE_TO_INT16 (color.g), "channel color g");
+ write_gint16 (fd, DOUBLE_TO_INT16 (color.b), "channel color b");
+ write_gint16 (fd, 0, "channel color padding");
+ write_gint16 (fd, ROUND (opacity), "channel opacity");
+ write_gchar (fd, 1, "channel mode");
+ }
+
+ #undef DOUBLE_TO_INT16
+
+ /* Pad if length is odd */
+
+ if (ftell (fd) & 1)
+ write_gchar (fd, 0, "pad byte");
+ }
+
+ /* --------------- Write Guides --------------- */
+ if (gimp_image_find_next_guide(image_id, 0))
+ {
+ gint n_guides = 0;
+ gint guide_id =0;
+
+ /* Count the guides */
+ while ((guide_id = gimp_image_find_next_guide(image_id, guide_id)))
+ n_guides++;
+
+ xfwrite (fd, "8BIM", 4, "imageresources signature");
+ write_gint16 (fd, 0x0408, "0x0408 Id (Guides)"); /* 1032 */
+ /* write_pascalstring (fd, Name, "Id name"); */
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+ write_gint32 (fd, 16 + 5 * n_guides, "0x0408 resource size");
+ /* Save grid and guide header */
+ write_gint32 (fd, 1, "grid/guide header version");
+ write_gint32 (fd, 576, "grid custom spacing horizontal");/* dpi*32/4??*/
+ write_gint32 (fd, 576, "grid custom spacing vertical"); /* dpi*32/4??*/
+ write_gint32 (fd, n_guides, "number of guides");
+
+ /* write the guides */
+ while ((guide_id = gimp_image_find_next_guide(image_id, guide_id)))
+ {
+ gchar orientation;
+ gint32 position;
+ orientation = gimp_image_get_guide_orientation(image_id, guide_id);
+ position = 32 * gimp_image_get_guide_position(image_id, guide_id);
+ orientation ^= 1; /* in the psd vert =0 , horiz = 1 */
+ write_gint32 (fd, position, "Position of guide");
+ write_gchar (fd, orientation, "Orientation of guide");
+ n_guides--;
+ }
+ if ((ftell(fd) & 1))
+ write_gchar(fd, 0, "pad byte");
+ if (n_guides != 0)
+ g_warning("Screwed up guide resource:: wrong number of guides\n");
+ IFDBG printf ("\tTotal length of 0x0400 resource: %d\n", (int) sizeof (gint16));
+ }
+
+ /* --------------- Write paths ------------------- */
+ save_paths (fd, image_id);
+
+ /* --------------- Write resolution data ------------------- */
+ {
+ gdouble xres = 0, yres = 0;
+ guint32 xres_fix, yres_fix;
+ GimpUnit g_unit;
+ gint16 psd_unit;
+
+ g_unit = gimp_image_get_unit (image_id);
+ gimp_image_get_resolution (image_id, &xres, &yres);
+
+ if (g_unit == GIMP_UNIT_MM)
+ {
+ psd_unit = PSD_UNIT_CM;
+ }
+ else
+ {
+ psd_unit = PSD_UNIT_INCH;
+ }
+
+ /* Don't convert resolution based on g_unit which is a display unit.
+ * PSD resolution is always in pixels/inch. */
+ xres_fix = xres * 65536.0 + .5; /* Convert to 16.16 fixed point */
+ yres_fix = yres * 65536.0 + .5; /* Convert to 16.16 fixed point */
+
+ xfwrite (fd, "8BIM", 4, "imageresources signature (for resolution)");
+ write_gint16(fd, 0x03ed, "0x03ed Id (resolution)"); /* 1005 */
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+ write_gint32 (fd, 16, "0x0400 resource size");
+ write_gint32 (fd, xres_fix, "hRes (16.16 fixed point)");
+ write_gint16 (fd, psd_unit, "hRes unit");
+ write_gint16 (fd, psd_unit, "width unit");
+ write_gint32 (fd, yres_fix, "vRes (16.16 fixed point)");
+ write_gint16 (fd, psd_unit, "vRes unit");
+ write_gint16 (fd, psd_unit, "height unit");
+ }
+
+ /* --------------- Write Active Layer Number --------------- */
+
+ if (ActiveLayerPresent)
+ {
+ xfwrite (fd, "8BIM", 4, "imageresources signature");
+ write_gint16 (fd, 0x0400, "0x0400 Id"); /* 1024 */
+ /* write_pascalstring (fd, Name, "Id name"); */
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+ write_gint32 (fd, sizeof (gint16), "0x0400 resource size");
+
+ /* Save title as gint16 (length always even) */
+
+ write_gint16 (fd, nActiveLayer, "active layer");
+
+ IFDBG printf ("\tTotal length of 0x0400 resource: %d\n", (int) sizeof (gint16));
+ }
+
+ /* --------------- Write ICC profile data ------------------- */
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_image_get_effective_color_profile (image_id);
+
+ if (profile)
+ {
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+
+ xfwrite (fd, "8BIM", 4, "imageresources signature");
+ write_gint16 (fd, 0x040f, "0x040f Id");
+ write_gint16 (fd, 0, "Id name"); /* Set to null string (two zeros) */
+ write_gint32 (fd, icc_length, "0x040f resource size");
+ xfwrite (fd, icc_data, icc_length, "ICC profile");
+
+ g_object_unref (profile);
+ }
+ }
+
+ /* --------------- Write Total Section Length --------------- */
+
+ eof_pos = ftell (fd);
+
+ fseek (fd, rsc_pos, SEEK_SET);
+ write_gint32 (fd, eof_pos - rsc_pos - sizeof (gint32), "image resources length");
+ IFDBG printf ("\tResource section total length: %d\n",
+ (int) (eof_pos - rsc_pos - sizeof (gint32)));
+
+ /* Return to EOF to continue writing */
+
+ fseek (fd, eof_pos, SEEK_SET);
+}
+
+static int
+get_compress_channel_data (guchar *channel_data,
+ gint32 channel_cols,
+ gint32 channel_rows,
+ gint32 stride,
+ gint32 bpc,
+ gint16 *LengthsTable,
+ guchar *remdata)
+{
+ gint i;
+ gint32 len; /* Length of compressed data */
+ guchar *start; /* Starting position of a row in channel_data */
+
+ stride /= bpc;
+
+ /* Pack channel data, and perform byte-order conversion */
+ switch (bpc)
+ {
+ case 1:
+ {
+ if (stride > 1)
+ {
+ const guint8 *src = (const guint8 *) channel_data;
+ guint8 *dest = (guint8 *) channel_data;
+
+ for (i = 0; i < channel_rows * channel_cols; i++)
+ {
+ *dest = *src;
+
+ dest++;
+ src += stride;
+ }
+ }
+ }
+ break;
+
+ case 2:
+ {
+ const guint16 *src = (const guint16 *) channel_data;
+ guint16 *dest = (guint16 *) channel_data;
+
+ for (i = 0; i < channel_rows * channel_cols; i++)
+ {
+ *dest = GUINT16_TO_BE (*src);
+
+ dest++;
+ src += stride;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ const guint32 *src = (const guint32 *) channel_data;
+ guint32 *dest = (guint32 *) channel_data;
+
+ for (i = 0; i < channel_rows * channel_cols; i++)
+ {
+ *dest = GUINT32_TO_BE (*src);
+
+ dest++;
+ src += stride;
+ }
+ }
+ break;
+
+ default:
+ g_return_val_if_reached (0);
+ }
+
+ /* For every row in the channel */
+
+ len = 0;
+ for (i = 0; i < channel_rows; i++)
+ {
+ start = channel_data + i * channel_cols * bpc;
+
+ /* Create packed data for this row */
+ LengthsTable[i] = pack_pb_line (start, channel_cols * bpc,
+ &remdata[len]);
+ len += LengthsTable[i];
+ }
+
+ /* return((len + channel_rows * sizeof (gint16)) + sizeof (gint16));*/
+ return len;
+}
+
+/* Ported /from plug-ins/file-tiff/file-tiff-save.c */
+static void
+double_to_psd_fixed (gdouble value,
+ gchar *target)
+{
+ gdouble in, frac;
+ gint i, f;
+
+ frac = modf (value, &in);
+ if (frac < 0)
+ {
+ in -= 1;
+ frac += 1;
+ }
+
+ i = (gint) CLAMP (in, -16, 15);
+ f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF);
+
+ target[0] = i & 0xFF;
+ target[1] = (f >> 16) & 0xFF;
+ target[2] = (f >> 8) & 0xFF;
+ target[3] = f & 0xFF;
+}
+
+/* Ported from /plug-ins/file-tiff/file-tiff-save.c */
+static void
+save_paths (FILE *fd,
+ gint32 image_id)
+{
+ gshort id = 0x07D0; /* Photoshop paths have IDs >= 2000 */
+ gdouble width = gimp_image_width (image_id);
+ gdouble height = gimp_image_height (image_id);
+ gint num_vectors;
+ gint *vectors;
+ gint v;
+ gint num_strokes;
+ gint *strokes;
+ gint s;
+
+ vectors = gimp_image_get_vectors (image_id, &num_vectors);
+
+ if (num_vectors <= 0)
+ return;
+
+ /* Only up to 997 paths supported */
+ for (v = 0; v < MIN (num_vectors, 1000); v++)
+ {
+ GString *data;
+ gchar *name, *nameend;
+ gsize len;
+ gint lenpos;
+ gchar pointrecord[26] = { 0, };
+ gchar *tmpname;
+ GError *err = NULL;
+
+ data = g_string_new ("8BIM");
+ g_string_append_c (data, id / 256);
+ g_string_append_c (data, id % 256);
+
+ /*
+ * - use iso8859-1 if possible
+ * - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark)
+ */
+ name = gimp_item_get_name (vectors[v]);
+ tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err);
+
+ if (tmpname && err == NULL)
+ {
+ g_string_append_c (data, MIN (len, 255));
+ g_string_append_len (data, tmpname, MIN (len, 255));
+ g_free (tmpname);
+ }
+ else
+ {
+ /* conversion failed, we fall back to UTF-8 */
+ len = g_utf8_strlen (name, 255 - 3); /* need three marker-bytes */
+
+ nameend = g_utf8_offset_to_pointer (name, len);
+ len = nameend - name; /* in bytes */
+ g_assert (len + 3 <= 255);
+
+ g_string_append_c (data, len + 3);
+ g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */
+ g_string_append_len (data, name, len);
+
+ if (tmpname)
+ g_free (tmpname);
+ }
+
+ if (data->len % 2) /* padding to even size */
+ g_string_append_c (data, 0);
+ g_free (name);
+
+ lenpos = data->len;
+ g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */
+ len = data->len; /* to calculate the data size later */
+
+ pointrecord[1] = 6; /* fill rule record */
+ g_string_append_len (data, pointrecord, 26);
+
+ strokes = gimp_vectors_get_strokes (vectors[v], &num_strokes);
+
+ for (s = 0; s < num_strokes; s++)
+ {
+ GimpVectorsStrokeType type;
+ gdouble *points;
+ gint num_points;
+ gboolean closed;
+ gint p = 0;
+
+ type = gimp_vectors_stroke_get_points (vectors[v], strokes[s],
+ &num_points, &points, &closed);
+
+ if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER ||
+ num_points > 65535 ||
+ num_points % 6)
+ {
+ g_printerr ("psd-save: unsupported stroke type: "
+ "%d (%d points)\n", type, num_points);
+ continue;
+ }
+
+ memset (pointrecord, 0, 26);
+ pointrecord[1] = closed ? 0 : 3;
+ pointrecord[2] = (num_points / 6) / 256;
+ pointrecord[3] = (num_points / 6) % 256;
+ g_string_append_len (data, pointrecord, 26);
+
+ for (p = 0; p < num_points; p += 6)
+ {
+ pointrecord[1] = closed ? 2 : 5;
+
+ double_to_psd_fixed (points[p+1] / height, pointrecord + 2);
+ double_to_psd_fixed (points[p+0] / width, pointrecord + 6);
+ double_to_psd_fixed (points[p+3] / height, pointrecord + 10);
+ double_to_psd_fixed (points[p+2] / width, pointrecord + 14);
+ double_to_psd_fixed (points[p+5] / height, pointrecord + 18);
+ double_to_psd_fixed (points[p+4] / width, pointrecord + 22);
+
+ g_string_append_len (data, pointrecord, 26);
+ }
+ }
+
+ g_free (strokes);
+
+ /* fix up the length */
+
+ len = data->len - len;
+ data->str[lenpos + 0] = (len & 0xFF000000) >> 24;
+ data->str[lenpos + 1] = (len & 0x00FF0000) >> 16;
+ data->str[lenpos + 2] = (len & 0x0000FF00) >> 8;
+ data->str[lenpos + 3] = (len & 0x000000FF) >> 0;
+
+ xfwrite (fd, data->str, data->len, "path resources data");
+ g_string_free (data, TRUE);
+ id += 0x01;
+ }
+
+ g_free (vectors);
+}
+
+static void
+save_layer_and_mask (FILE *fd,
+ gint32 image_id)
+{
+ gint i,j;
+ gint idChannel;
+ gint offset_x; /* X offset for each layer */
+ gint offset_y; /* Y offset for each layer */
+ gint32 layerWidth; /* Width of each layer */
+ gint32 layerHeight; /* Height of each layer */
+ const gchar *blendMode; /* Blending mode of the layer */
+ guchar layerOpacity; /* Opacity of the layer */
+ guchar flags; /* Layer flags */
+ gint nChannelsLayer; /* Number of channels of a layer */
+ gint32 ChanSize; /* Data length for a channel */
+ gchar *layerName; /* Layer name */
+ gint mask; /* Layer mask */
+ gint depth; /* Layer group nesting depth */
+ gint bpc; /* Image BPC */
+
+ glong eof_pos; /* Position: End of file */
+ glong ExtraDataPos; /* Position: Extra data length */
+ glong LayerMaskPos; /* Position: Layer & Mask section length */
+ glong LayerInfoPos; /* Position: Layer info section length*/
+ glong **ChannelLengthPos; /* Position: Channel length */
+
+
+ IFDBG printf (" Function: save_layer_and_mask\n");
+
+ /* Create first array dimension (layers, channels) */
+
+ ChannelLengthPos = g_newa (glong *, PSDImageData.nLayers);
+
+ /* Layer and mask information section */
+
+ LayerMaskPos = ftell (fd);
+ write_gint32 (fd, 0, "layers & mask information length");
+
+ /* Layer info section */
+
+ LayerInfoPos = ftell (fd);
+ write_gint32 (fd, 0, "layers info section length");
+
+ /* Layer structure section */
+
+ if (gimp_drawable_has_alpha (PSDImageData.merged_layer))
+ write_gint16 (fd, -PSDImageData.nLayers, "Layer structure count");
+ else
+ write_gint16 (fd, PSDImageData.nLayers, "Layer structure count");
+
+ depth = 0;
+
+ bpc = get_bpc (image_id);
+
+ /* Layer records section */
+ /* GIMP layers must be written in reverse order */
+
+ for (i = PSDImageData.nLayers - 1; i >= 0; i--)
+ {
+ gint hasMask = 0;
+
+ if (PSDImageData.lLayers[i].type == PSD_LAYER_TYPE_LAYER)
+ {
+ gimp_drawable_offsets (PSDImageData.lLayers[i].id, &offset_x, &offset_y);
+ layerWidth = gimp_drawable_width (PSDImageData.lLayers[i].id);
+ layerHeight = gimp_drawable_height (PSDImageData.lLayers[i].id);
+ }
+ else
+ {
+ /* groups don't specify their dimensions, and have empty channel
+ * data
+ */
+ offset_x = 0;
+ offset_y = 0;
+ layerWidth = 0;
+ layerHeight = 0;
+ }
+
+ IFDBG printf ("\tLayer number: %d\n", i);
+ IFDBG
+ {
+ const gchar *type;
+
+ switch (PSDImageData.lLayers[i].type)
+ {
+ case PSD_LAYER_TYPE_LAYER: type = "normal layer"; break;
+ case PSD_LAYER_TYPE_GROUP_START: type = "group start marker"; break;
+ case PSD_LAYER_TYPE_GROUP_END: type = "group end marker"; break;
+ }
+
+ printf ("\t\tType: %s\n", type);
+ }
+ IFDBG printf ("\t\tX offset: %d\n", offset_x);
+ IFDBG printf ("\t\tY offset: %d\n", offset_y);
+ IFDBG printf ("\t\tWidth: %d\n", layerWidth);
+ IFDBG printf ("\t\tHeight: %d\n", layerHeight);
+
+ write_gint32 (fd, offset_y, "Layer top");
+ write_gint32 (fd, offset_x, "Layer left");
+ write_gint32 (fd, offset_y + layerHeight, "Layer bottom");
+ write_gint32 (fd, offset_x + layerWidth, "Layer right");
+
+ hasMask = (PSDImageData.lLayers[i].type != PSD_LAYER_TYPE_GROUP_END &&
+ gimp_layer_get_mask (PSDImageData.lLayers[i].id) != -1);
+ nChannelsLayer = nChansLayer (PSDImageData.baseType,
+ gimp_drawable_has_alpha (PSDImageData.lLayers[i].id),
+ hasMask);
+
+
+ write_gint16 (fd, nChannelsLayer, "Number channels in the layer");
+ IFDBG printf ("\t\tNumber of channels: %d\n", nChannelsLayer);
+
+ /* Create second array dimension (layers, channels) */
+
+ ChannelLengthPos[i] = g_new (glong, nChannelsLayer);
+
+ /* Try with gimp_drawable_bpp() */
+
+ for (j = 0; j < nChannelsLayer; j++)
+ {
+ if (gimp_drawable_has_alpha (PSDImageData.lLayers[i].id))
+ idChannel = j - 1;
+ else
+ idChannel = j;
+ if (hasMask && (j+1 == nChannelsLayer)) /* Last channel ... */
+ idChannel = -2; /* ... will be layer mask */
+
+ write_gint16 (fd, idChannel, "Channel ID");
+ IFDBG printf ("\t\t\tChannel Identifier: %d\n", idChannel);
+
+ /* Write the length assuming no compression. In case there is,
+ will modify it later when writing data. */
+
+ ChannelLengthPos[i][j] = ftell (fd);
+ ChanSize = sizeof (gint16) + (layerWidth * layerHeight * bpc);
+
+ write_gint32 (fd, ChanSize, "Channel Size");
+ IFDBG printf ("\t\t\tLength: %d\n", ChanSize);
+ }
+
+ xfwrite (fd, "8BIM", 4, "blend mode signature");
+
+ blendMode = psd_lmode_layer (PSDImageData.lLayers[i].id, FALSE);
+ IFDBG printf ("\t\tBlend mode: %s\n", blendMode);
+ xfwrite (fd, blendMode, 4, "blend mode key");
+
+ layerOpacity = RINT ((gimp_layer_get_opacity (PSDImageData.lLayers[i].id) * 255.0) / 100.0);
+ IFDBG printf ("\t\tOpacity: %u\n", layerOpacity);
+ write_gchar (fd, layerOpacity, "Opacity");
+
+ if (gimp_layer_get_composite_mode (PSDImageData.lLayers[i].id) == GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP)
+ write_gchar (fd, 1, "Clipping");
+ else
+ write_gchar (fd, 0, "Clipping");
+
+ flags = 0;
+ if (gimp_layer_get_lock_alpha (PSDImageData.lLayers[i].id)) flags |= 1;
+ if (! gimp_item_get_visible (PSDImageData.lLayers[i].id)) flags |= 2;
+ if (PSDImageData.lLayers[i].type != PSD_LAYER_TYPE_LAYER) flags |= 0x18;
+ IFDBG printf ("\t\tFlags: %u\n", flags);
+ write_gchar (fd, flags, "Flags");
+
+ /* Padding byte to make the length even */
+ write_gchar (fd, 0, "Filler");
+
+ ExtraDataPos = ftell (fd); /* Position of Extra Data size */
+ write_gint32 (fd, 0, "Extra data size");
+
+ if (hasMask)
+ {
+ gint maskOffset_x;
+ gint maskOffset_y;
+ gint maskWidth;
+ gint maskHeight;
+ gboolean apply;
+
+ mask = gimp_layer_get_mask (PSDImageData.lLayers[i].id);
+
+ gimp_drawable_offsets (mask, &maskOffset_x, &maskOffset_y);
+
+ maskWidth = gimp_drawable_width (mask);
+ maskHeight = gimp_drawable_height (mask);
+ apply = gimp_layer_get_apply_mask (PSDImageData.lLayers[i].id);
+
+ IFDBG printf ("\t\tLayer mask size: %d\n", 20);
+ write_gint32 (fd, 20, "Layer mask size");
+ write_gint32 (fd, maskOffset_y, "Layer mask top");
+ write_gint32 (fd, maskOffset_x, "Layer mask left");
+ write_gint32 (fd, maskOffset_y + maskHeight, "Layer mask bottom");
+ write_gint32 (fd, maskOffset_x + maskWidth, "Layer mask right");
+ write_gchar (fd, 0, "Layer mask default color");
+ flags = (0 | /* position relative to layer */
+ (apply ? 0 : 1) << 1 | /* layer mask disabled */
+ 0 << 2); /* invert layer mask */
+ write_gchar (fd, flags, "Layer mask flags");
+ write_gint16 (fd, 0, "Layer mask Padding");
+ }
+ else
+ {
+ /* NOTE Writing empty Layer mask / adjustment layer data */
+ write_gint32 (fd, 0, "Layer mask size");
+ IFDBG printf ("\t\tLayer mask size: %d\n", 0);
+ }
+
+ /* NOTE Writing empty Layer blending ranges data */
+ write_gint32 (fd, 0, "Layer blending size");
+ IFDBG printf ("\t\tLayer blending size: %d\n", 0);
+
+ if (PSDImageData.lLayers[i].type != PSD_LAYER_TYPE_GROUP_END)
+ layerName = gimp_item_get_name (PSDImageData.lLayers[i].id);
+ else
+ layerName = g_strdup ("</Layer group>");
+ write_pascalstring (fd, layerName, 4, "layer name");
+ IFDBG printf ("\t\tLayer name: %s\n", layerName);
+
+ /* Additional layer information blocks */
+ /* Unicode layer name */
+ write_datablock_luni(fd, layerName, "luni extra data block");
+
+ g_free (layerName);
+
+ /* Layer color tag */
+ xfwrite (fd, "8BIMlclr", 8, "sheet color signature");
+ write_gint32 (fd, 8, "sheet color size");
+ write_gint16 (fd,
+ gimp_to_psd_layer_color_tag(gimp_item_get_color_tag(PSDImageData.lLayers[i].id)),
+ "sheet color code");
+ write_gint16 (fd, 0, "sheet color unused value");
+ write_gint16 (fd, 0, "sheet color unused value");
+ write_gint16 (fd, 0, "sheet color unused value");
+
+ /* Group layer section divider */
+ if (PSDImageData.lLayers[i].type != PSD_LAYER_TYPE_LAYER)
+ {
+ gint32 size;
+ gint32 type;
+
+ size = 12;
+
+ if (PSDImageData.lLayers[i].type == PSD_LAYER_TYPE_GROUP_START)
+ {
+ type = gimp_item_get_expanded (PSDImageData.lLayers[i].id) ? 1 : 2;
+
+ depth--;
+ }
+ else
+ {
+ type = 3;
+
+ depth++;
+ }
+
+ blendMode = psd_lmode_layer (PSDImageData.lLayers[i].id, TRUE);
+
+ if (type < 3 || depth <= 5)
+ {
+ xfwrite (fd, "8BIMlsct", 8, "section divider");
+ }
+ else
+ {
+ /* layer groups whose nesting depth is above 5 are only supported
+ * by Photoshop CS5 and up, and their end markers use the
+ * (undocumented) "lsdk" key, instead of "lsct".
+ */
+ xfwrite (fd, "8BIMlsdk", 8, "nested section divider");
+ }
+ write_gint32 (fd, size, "section divider size");
+ write_gint32 (fd, type, "section divider type");
+ xfwrite (fd, "8BIM", 4, "section divider blend mode signature");
+ xfwrite (fd, blendMode, 4, "section divider blend mode key");
+ }
+
+ /* Write real length for: Extra data */
+
+ eof_pos = ftell (fd);
+
+ fseek (fd, ExtraDataPos, SEEK_SET);
+ write_gint32 (fd, eof_pos - ExtraDataPos - sizeof (gint32), "Extra data size");
+ IFDBG printf ("\t\tExtraData size: %d\n",
+ (int) (eof_pos - ExtraDataPos - sizeof (gint32)));
+
+ /* Return to EOF to continue writing */
+
+ fseek (fd, eof_pos, SEEK_SET);
+ }
+
+
+ /* Channel image data section */
+ /* Gimp layers must be written in reverse order */
+
+ for (i = PSDImageData.nLayers - 1; i >= 0; i--)
+ {
+ gimp_progress_update ((PSDImageData.nLayers - i - 1.0) / (PSDImageData.nLayers + 1.0));
+
+ IFDBG printf ("\t\tWriting pixel data for layer slot %d\n", i);
+ write_pixel_data (fd, PSDImageData.lLayers[i].id, ChannelLengthPos[i], 0,
+ PSDImageData.lLayers[i].type != PSD_LAYER_TYPE_GROUP_END);
+ g_free (ChannelLengthPos[i]);
+ }
+
+ gimp_progress_update (PSDImageData.nLayers / (PSDImageData.nLayers + 1.0));
+ eof_pos = ftell (fd);
+
+ /* Write actual size of Layer info section */
+
+ fseek (fd, LayerInfoPos, SEEK_SET);
+ write_gint32 (fd, eof_pos - LayerInfoPos - sizeof (gint32), "layers info section length");
+ IFDBG printf ("\t\tTotal layers info section length: %d\n",
+ (int) (eof_pos - LayerInfoPos - sizeof (gint32)));
+
+ /* Write actual size of Layer and mask information section */
+
+ fseek (fd, LayerMaskPos, SEEK_SET);
+ write_gint32 (fd, eof_pos - LayerMaskPos - sizeof (gint32), "layers & mask information length");
+ IFDBG printf ("\t\tTotal layers & mask information length: %d\n",
+ (int) (eof_pos - LayerMaskPos - sizeof (gint32)));
+
+ /* Return to EOF to continue writing */
+
+ fseek (fd, eof_pos, SEEK_SET);
+}
+
+static void
+write_pixel_data (FILE *fd,
+ gint32 drawableID,
+ glong *ChanLenPosition,
+ gint32 ltable_offset,
+ gboolean write_mask)
+{
+ GeglBuffer *buffer = gimp_drawable_get_buffer (drawableID);
+ const Babl *format;
+ gint32 maskID;
+ gint32 tile_height = gimp_tile_height ();
+ gint32 height = gegl_buffer_get_height (buffer);
+ gint32 width = gegl_buffer_get_width (buffer);
+ gint32 bytes;
+ gint32 components;
+ gint32 bpc;
+ gint32 colors;
+ gint32 y;
+ gint32 len; /* Length of compressed data */
+ gint16 *LengthsTable; /* Lengths of every compressed row */
+ guchar *rledata; /* Compressed data from a region */
+ guchar *data; /* Temporary copy of pixel data */
+ glong length_table_pos; /* position in file of the length table */
+ int i, j;
+
+ IFDBG printf (" Function: write_pixel_data, drw %d, lto %d\n",
+ drawableID, ltable_offset);
+
+ if (write_mask)
+ maskID = gimp_layer_get_mask (drawableID);
+ else
+ maskID = -1;
+
+ /* groups have empty channel data, but may have a mask */
+ if (gimp_item_is_group (drawableID) && maskID == -1)
+ {
+ width = 0;
+ height = 0;
+ }
+
+ if (gimp_item_is_channel (drawableID))
+ format = get_channel_format (drawableID);
+ else
+ format = get_pixel_format (drawableID);
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+ components = babl_format_get_n_components (format);
+ bpc = bytes / components;
+
+ colors = components;
+
+ if (gimp_drawable_has_alpha (drawableID) &&
+ ! gimp_drawable_is_indexed (drawableID))
+ colors -= 1;
+
+ LengthsTable = g_new (gint16, height);
+ rledata = g_new (guchar, (MIN (height, tile_height) *
+ (width + 10 + (width / 100))) * bpc);
+
+ data = g_new (guchar, MIN (height, tile_height) * width * bytes);
+
+ /* groups have empty channel data */
+ if (gimp_item_is_group (drawableID))
+ {
+ width = 0;
+ height = 0;
+ }
+
+ for (i = 0; i < components; i++)
+ {
+ gint chan;
+
+ len = 0;
+
+ if (components != colors && ltable_offset == 0) /* Need to write alpha channel first, except in image data section */
+ {
+ if (i == 0)
+ {
+ chan = components - 1;
+ }
+ else
+ {
+ chan = i - 1;
+ }
+ }
+ else
+ {
+ chan = i;
+ }
+
+ if (ChanLenPosition)
+ {
+ write_gint16 (fd, 1, "Compression type (RLE)");
+ len += 2;
+ }
+
+ if (ltable_offset > 0)
+ {
+ length_table_pos = ltable_offset + 2 * chan * height;
+ }
+ else
+ {
+ length_table_pos = ftell(fd);
+
+ xfwrite (fd, LengthsTable, height * sizeof(gint16),
+ "Dummy RLE length");
+ len += height * sizeof(gint16);
+ IF_DEEP_DBG printf ("\t\t\t\t. ltable, pos %ld len %d\n", length_table_pos, len);
+ }
+
+ for (y = 0; y < height; y += tile_height)
+ {
+ int tlen;
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, y,
+ width,
+ MIN (height - y, tile_height)),
+ 1.0, format, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ tlen = get_compress_channel_data (&data[chan * bpc],
+ width,
+ MIN(height - y, tile_height),
+ bytes, bpc,
+ &LengthsTable[y],
+ rledata);
+ len += tlen;
+ xfwrite (fd, rledata, tlen, "Compressed pixel data");
+ IF_DEEP_DBG printf ("\t\t\t\t. Writing compressed pixels, stream of %d\n", tlen);
+ }
+
+ /* Write compressed lengths table */
+ fseek (fd, length_table_pos, SEEK_SET);
+ for (j = 0; j < height; j++) /* write real length table */
+ write_gint16 (fd, LengthsTable[j], "RLE length");
+
+ if (ChanLenPosition) /* Update total compressed length */
+ {
+ fseek (fd, ChanLenPosition[i], SEEK_SET);
+ write_gint32 (fd, len, "channel data length");
+ IFDBG printf ("\t\tUpdating data len to %d\n", len);
+ }
+ fseek (fd, 0, SEEK_END);
+ IF_DEEP_DBG printf ("\t\t\t\t. Cur pos %ld\n", ftell(fd));
+ }
+
+ /* Write layer mask, as last channel, id -2 */
+ if (maskID != -1)
+ {
+ GeglBuffer *mbuffer = gimp_drawable_get_buffer (maskID);
+ const Babl *mformat = get_mask_format(maskID);
+
+ width = gegl_buffer_get_width (buffer);
+ height = gegl_buffer_get_height (buffer);
+
+ len = 0;
+
+ if (ChanLenPosition)
+ {
+ write_gint16 (fd, 1, "Compression type (RLE)");
+ len += 2;
+ IF_DEEP_DBG printf ("\t\t\t\t. ChanLenPos, len %d\n", len);
+ }
+
+ if (ltable_offset > 0)
+ {
+ length_table_pos = ltable_offset + 2 * (components+1) * height;
+ IF_DEEP_DBG printf ("\t\t\t\t. ltable, pos %ld\n",
+ length_table_pos);
+ }
+ else
+ {
+ length_table_pos = ftell(fd);
+
+ xfwrite (fd, LengthsTable, height * sizeof(gint16),
+ "Dummy RLE length");
+ len += height * sizeof(gint16);
+ IF_DEEP_DBG printf ("\t\t\t\t. ltable, pos %ld len %d\n",
+ length_table_pos, len);
+ }
+
+ for (y = 0; y < height; y += tile_height)
+ {
+ int tlen;
+ gegl_buffer_get (mbuffer,
+ GEGL_RECTANGLE (0, y,
+ width,
+ MIN (height - y, tile_height)),
+ 1.0, mformat, data,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+ tlen = get_compress_channel_data (&data[0],
+ width,
+ MIN(height - y, tile_height),
+ bpc, bpc,
+ &LengthsTable[y],
+ rledata);
+ len += tlen;
+ xfwrite (fd, rledata, tlen, "Compressed mask data");
+ IF_DEEP_DBG printf ("\t\t\t\t. Writing compressed mask, stream of %d\n", tlen);
+ }
+
+ /* Write compressed lengths table */
+ fseek (fd, length_table_pos, SEEK_SET); /*POS WHERE???*/
+ for (j = 0; j < height; j++) /* write real length table */
+ {
+ write_gint16 (fd, LengthsTable[j], "RLE length");
+ IF_DEEP_DBG printf ("\t\t\t\t. Updating RLE len %d\n",
+ LengthsTable[j]);
+ }
+
+ if (ChanLenPosition) /* Update total compressed length */
+ {
+ /* Mask follows other components so use that as offset. */
+ fseek (fd, ChanLenPosition[components], SEEK_SET);
+
+ write_gint32 (fd, len, "channel data length");
+ IFDBG printf ("\t\tUpdating data len to %d, at %ld\n", len, ftell(fd));
+ }
+ fseek (fd, 0, SEEK_END);
+ IF_DEEP_DBG printf ("\t\t\t\t. Cur pos %ld\n", ftell(fd));
+
+ g_object_unref (mbuffer);
+ }
+
+ g_object_unref (buffer);
+
+ g_free (data);
+ g_free (rledata);
+ g_free (LengthsTable);
+}
+
+static void
+save_data (FILE *fd,
+ gint32 image_id)
+{
+ gint ChanCount;
+ gint i, j;
+ gint32 imageHeight; /* Height of image */
+ glong offset; /* offset in file of rle lengths */
+ gint chan;
+
+ IFDBG printf (" Function: save_data\n");
+
+ ChanCount = (PSDImageData.nChannels +
+ nChansLayer (PSDImageData.baseType,
+ gimp_drawable_has_alpha (PSDImageData.merged_layer),
+ 0));
+
+ imageHeight = gimp_image_height (image_id);
+
+ write_gint16 (fd, 1, "RLE compression");
+
+ /* All line lengths go before the rle pixel data */
+
+ offset = ftell(fd); /* Offset in file of line lengths */
+
+ for (i = 0; i < ChanCount; i++)
+ for (j = 0; j < imageHeight; j++)
+ write_gint16 (fd, 0, "junk line lengths");
+
+ IFDBG printf ("\t\tWriting compressed image data\n");
+ write_pixel_data (fd, PSDImageData.merged_layer,
+ NULL, offset, FALSE);
+
+ chan = nChansLayer (PSDImageData.baseType,
+ gimp_drawable_has_alpha(PSDImageData.merged_layer), 0);
+
+ for (i = 0; i < PSDImageData.nChannels; i++)
+ {
+ IFDBG printf ("\t\tWriting compressed channel data for channel %d\n",
+ i);
+ write_pixel_data (fd, PSDImageData.lChannels[i], NULL,
+ offset + 2*imageHeight*chan, FALSE); //check how imgs are channels here
+ chan++;
+ }
+}
+
+static gint32
+create_merged_image (gint32 image_id)
+{
+ gint32 projection;
+
+ projection = gimp_layer_new_from_visible (image_id, image_id, "psd-save");
+
+ if (! gimp_drawable_has_alpha (projection))
+ return projection;
+
+ if (gimp_image_base_type (image_id) != GIMP_INDEXED)
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (projection);
+ const Babl *format = get_pixel_format (projection);
+ gboolean transparency_found = FALSE;
+ gint bpp = babl_format_get_bytes_per_pixel (format);
+ GeglBufferIterator *iter;
+
+ iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *d = iter->items[0].data;
+ gint i;
+
+ for (i = 0; i < iter->length; i++)
+ {
+ gint32 alpha = d[bpp - 1];
+
+ if (alpha < 255)
+ {
+ gint c;
+
+ transparency_found = TRUE;
+
+ /* blend against white, photoshop does this. */
+ for (c = 0; c < bpp - 1; c++)
+ d[c] = ((guint32) d[c] * alpha + 128) / 255 + 255 - alpha;
+ }
+
+ d += bpp;
+ }
+ }
+
+ g_object_unref (buffer);
+
+ if (! transparency_found)
+ gimp_layer_flatten (projection);
+ }
+ else
+ {
+ gimp_layer_flatten (projection); /* PSDs don't support transparency information in indexed images*/
+ }
+
+ return projection;
+}
+
+static void
+get_image_data (gint32 image_id)
+{
+ IFDBG printf (" Function: get_image_data\n");
+
+ PSDImageData.compression = FALSE;
+
+ PSDImageData.image_height = gimp_image_height (image_id);
+ IFDBG printf ("\tGot number of rows: %d\n", PSDImageData.image_height);
+
+ PSDImageData.image_width = gimp_image_width (image_id);
+ IFDBG printf ("\tGot number of cols: %d\n", PSDImageData.image_width);
+
+ PSDImageData.baseType = gimp_image_base_type (image_id);
+ IFDBG printf ("\tGot base type: %d\n", PSDImageData.baseType);
+
+ PSDImageData.merged_layer = create_merged_image (image_id);
+
+ PSDImageData.lChannels = gimp_image_get_channels (image_id,
+ &PSDImageData.nChannels);
+ IFDBG printf ("\tGot number of channels: %d\n", PSDImageData.nChannels);
+
+ PSDImageData.lLayers = image_get_all_layers (image_id,
+ &PSDImageData.nLayers);
+ IFDBG printf ("\tGot number of layers: %d\n", PSDImageData.nLayers);
+}
+
+static void
+clear_image_data (void)
+{
+ IFDBG printf (" Function: clear_image_data\n");
+
+ g_free (PSDImageData.lChannels);
+ PSDImageData.lChannels = NULL;
+
+ g_free (PSDImageData.lLayers);
+ PSDImageData.lLayers = NULL;
+}
+
+gboolean
+save_image (const gchar *filename,
+ gint32 image_id,
+ GError **error)
+{
+ FILE *fd;
+ gint i;
+ GeglBuffer *buffer;
+
+ IFDBG printf (" Function: save_image\n");
+
+ if (gimp_image_width (image_id) > 30000 ||
+ gimp_image_height (image_id) > 30000)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unable to export '%s'. The PSD file format does not "
+ "support images that are more than 30,000 pixels wide "
+ "or tall."),
+ gimp_filename_to_utf8 (filename));
+ return FALSE;
+ }
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ get_image_data (image_id);
+
+ /* Need to check each of the layers size individually also */
+ for (i = 0; i < PSDImageData.nLayers; i++)
+ {
+ if (PSDImageData.lLayers[i].type == PSD_LAYER_TYPE_LAYER)
+ {
+ buffer = gimp_drawable_get_buffer (PSDImageData.lLayers[i].id);
+ if (gegl_buffer_get_width (buffer) > 30000 || gegl_buffer_get_height (buffer) > 30000)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Unable to export '%s'. The PSD file format does not "
+ "support images with layers that are more than 30,000 "
+ "pixels wide or tall."),
+ gimp_filename_to_utf8 (filename));
+ clear_image_data ();
+ return FALSE;
+ }
+ g_object_unref (buffer);
+ }
+ }
+
+ fd = g_fopen (filename, "wb");
+ if (fd == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ clear_image_data ();
+ return FALSE;
+ }
+
+ IFDBG g_print ("\tFile '%s' has been opened\n",
+ gimp_filename_to_utf8 (filename));
+
+ save_header (fd, image_id);
+ save_color_mode_data (fd, image_id);
+ save_resources (fd, image_id);
+
+ /* PSD format does not support layers in indexed images */
+
+ if (PSDImageData.baseType == GIMP_INDEXED)
+ write_gint32 (fd, 0, "layers info section length");
+ else
+ save_layer_and_mask (fd, image_id);
+
+ /* If this is an indexed image, write now channel and layer info */
+
+ save_data (fd, image_id);
+
+ /* Delete merged image now */
+
+ gimp_item_delete (PSDImageData.merged_layer);
+
+ clear_image_data ();
+
+ IFDBG printf ("----- Closing PSD file, done -----\n\n");
+
+ fclose (fd);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+static gint
+get_bpc (gint32 image_id)
+{
+ switch (gimp_image_get_precision (image_id))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ return 1;
+
+ case GIMP_PRECISION_U16_LINEAR:
+ case GIMP_PRECISION_U16_GAMMA:
+ case GIMP_PRECISION_HALF_LINEAR:
+ case GIMP_PRECISION_HALF_GAMMA:
+ return 2;
+
+ case GIMP_PRECISION_U32_LINEAR:
+ case GIMP_PRECISION_U32_GAMMA:
+ case GIMP_PRECISION_FLOAT_LINEAR:
+ case GIMP_PRECISION_FLOAT_GAMMA:
+ default:
+ /* FIXME: we *should* encode the image as u32 in this case, but simply
+ * using the same code as for the other cases produces invalid psd files
+ * (they're rejected by photoshop, although they can be read by the
+ * corresponding psd-load.c code, which in turn can't actually read
+ * photoshop-generated u32 files.)
+ *
+ * simply encode the image as u16 for now.
+ */
+ /* return 4; */
+ return 2;
+ }
+}
+
+static const Babl *
+get_pixel_format (gint32 drawableID)
+{
+ gint32 image_id = gimp_item_get_image (drawableID);
+ const gchar *model;
+ gint bpc;
+ gchar format[32];
+
+ switch (gimp_drawable_type (drawableID))
+ {
+ case GIMP_GRAY_IMAGE:
+ model = "Y'";
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ model = "Y'A";
+ break;
+
+ case GIMP_RGB_IMAGE:
+ model = "R'G'B'";
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ model = "R'G'B'A";
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ return gimp_drawable_get_format (drawableID);
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ bpc = get_bpc (image_id);
+
+ sprintf (format, "%s u%d", model, 8 * bpc);
+
+ return babl_format (format);
+}
+
+static const Babl *
+get_channel_format (gint32 drawableID)
+{
+ gint32 image_id = gimp_item_get_image (drawableID);
+ gint bpc;
+ gchar format[32];
+
+ /* see gimp_image_get_channel_format() */
+ if (gimp_image_get_precision (image_id) == GIMP_PRECISION_U8_GAMMA)
+ return babl_format ("Y' u8");
+
+ bpc = get_bpc (image_id);
+
+ sprintf (format, "Y u%d", 8 * bpc);
+
+ return babl_format (format);
+}
+
+static const Babl *
+get_mask_format (gint32 drawableID)
+{
+ gint32 image_id = gimp_item_get_image (drawableID);
+ gint bpc;
+ gchar format[32];
+
+ bpc = get_bpc (image_id);
+
+ sprintf (format, "Y u%d", 8 * bpc);
+
+ return babl_format (format);
+}
+
+static void
+append_layers (const gint *layers,
+ gint n_layers,
+ GArray *array)
+{
+ gint i;
+
+ for (i = 0; i < n_layers; i++)
+ {
+ PSD_Layer layer = {};
+ gboolean is_group;
+
+ layer.id = layers[i];
+
+ is_group = gimp_item_is_group (layer.id);
+
+ if (! is_group)
+ layer.type = PSD_LAYER_TYPE_LAYER;
+ else
+ layer.type = PSD_LAYER_TYPE_GROUP_START;
+
+ g_array_append_val (array, layer);
+
+ if (is_group)
+ {
+ gint32 *group_layers;
+ gint n;
+
+ group_layers = gimp_item_get_children (layer.id, &n);
+ append_layers (group_layers, n, array);
+ g_free (group_layers);
+
+ layer.type = PSD_LAYER_TYPE_GROUP_END;
+ g_array_append_val (array, layer);
+ }
+ }
+}
+
+static PSD_Layer *
+image_get_all_layers (gint32 image_id,
+ gint *n_layers)
+{
+ GArray *array = g_array_new (FALSE, FALSE, sizeof (PSD_Layer));
+ gint32 *layers;
+ gint n;
+
+ layers = gimp_image_get_layers (image_id, &n);
+
+ append_layers (layers, n, array);
+
+ g_free (layers);
+
+ *n_layers = array->len;
+
+ return (PSD_Layer *) g_array_free (array, FALSE);
+}
diff --git a/plug-ins/file-psd/psd-save.h b/plug-ins/file-psd/psd-save.h
new file mode 100644
index 0000000..9308271
--- /dev/null
+++ b/plug-ins/file-psd/psd-save.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_SAVE_H__
+#define __PSD_SAVE_H__
+
+
+gboolean save_image (const gchar *filename,
+ gint32 image_id,
+ GError **error);
+
+
+#endif /* __PSD_SAVE_H__ */
diff --git a/plug-ins/file-psd/psd-thumb-load.c b/plug-ins/file-psd/psd-thumb-load.c
new file mode 100644
index 0000000..7ce646f
--- /dev/null
+++ b/plug-ins/file-psd/psd-thumb-load.c
@@ -0,0 +1,309 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <libgimp/gimp.h>
+
+#include "psd.h"
+#include "psd-util.h"
+#include "psd-image-res-load.h"
+#include "psd-thumb-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+/* Local function prototypes */
+static gint read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+static gint32 create_gimp_image (PSDimage *img_a,
+ const gchar *filename);
+
+static gint add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error);
+
+/* Main file load function */
+gint32
+load_thumbnail_image (const gchar *filename,
+ gint *width,
+ gint *height,
+ GError **load_error)
+{
+ FILE *f;
+ GStatBuf st;
+ PSDimage img_a;
+ gint32 image_id = -1;
+ GError *error = NULL;
+
+ /* ----- Open PSD file ----- */
+ if (g_stat (filename, &st) == -1)
+ return -1;
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ IFDBG(1) g_debug ("Open file %s", gimp_filename_to_utf8 (filename));
+ f = g_fopen (filename, "rb");
+ if (f == NULL)
+ {
+ g_set_error (load_error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ /* ----- Read the PSD file Header block ----- */
+ IFDBG(2) g_debug ("Read header block");
+ if (read_header_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.2);
+
+ /* ----- Read the PSD file Color Mode block ----- */
+ IFDBG(2) g_debug ("Read color mode block");
+ if (read_color_mode_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.4);
+
+ /* ----- Read the PSD file Image Resource block ----- */
+ IFDBG(2) g_debug ("Read image resource block");
+ if (read_image_resource_block (&img_a, f, &error) < 0)
+ goto load_error;
+ gimp_progress_update (0.6);
+
+ /* ----- Create GIMP image ----- */
+ IFDBG(2) g_debug ("Create GIMP image");
+ image_id = create_gimp_image (&img_a, filename);
+ if (image_id < 0)
+ goto load_error;
+
+ /* ----- Add image resources ----- */
+ IFDBG(2) g_debug ("Add image resources");
+ if (add_image_resources (image_id, &img_a, f, &error) < 1)
+ goto load_error;
+ gimp_progress_update (1.0);
+
+ gimp_image_clean_all (image_id);
+ gimp_image_undo_enable (image_id);
+ fclose (f);
+
+ *width = img_a.columns;
+ *height = img_a.rows;
+ return image_id;
+
+ /* ----- Process load errors ----- */
+ load_error:
+ if (error)
+ {
+ g_set_error (load_error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Error loading PSD file: %s"), error->message);
+ g_error_free (error);
+ }
+
+ /* Delete partially loaded image */
+ if (image_id > 0)
+ gimp_image_delete (image_id);
+
+ /* Close file if Open */
+ if (! (f == NULL))
+ fclose (f);
+
+ return -1;
+}
+
+
+/* Local functions */
+
+static gint
+read_header_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint16 version;
+ gchar sig[4];
+ gchar buf[6];
+
+ if (fread (sig, 4, 1, f) < 1
+ || fread (&version, 2, 1, f) < 1
+ || fread (buf, 6, 1, f) < 1
+ || fread (&img_a->channels, 2, 1, f) < 1
+ || fread (&img_a->rows, 4, 1, f) < 1
+ || fread (&img_a->columns, 4, 1, f) < 1
+ || fread (&img_a->bps, 2, 1, f) < 1
+ || fread (&img_a->color_mode, 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ version = GUINT16_FROM_BE (version);
+ img_a->channels = GUINT16_FROM_BE (img_a->channels);
+ img_a->rows = GUINT32_FROM_BE (img_a->rows);
+ img_a->columns = GUINT32_FROM_BE (img_a->columns);
+ img_a->bps = GUINT16_FROM_BE (img_a->bps);
+ img_a->color_mode = GUINT16_FROM_BE (img_a->color_mode);
+
+ IFDBG(1) g_debug ("\n\n\tSig: %.4s\n\tVer: %d\n\tChannels: "
+ "%d\n\tSize: %dx%d\n\tBPS: %d\n\tMode: %d\n",
+ sig, version, img_a->channels,
+ img_a->columns, img_a->rows,
+ img_a->bps, img_a->color_mode);
+
+ if (memcmp (sig, "8BPS", 4) != 0)
+ return -1;
+
+ if (version != 1)
+ return -1;
+
+ if (img_a->channels > MAX_CHANNELS)
+ return -1;
+
+ if (img_a->rows < 1 || img_a->rows > GIMP_MAX_IMAGE_SIZE)
+ return -1;
+
+ if (img_a->columns < 1 || img_a->columns > GIMP_MAX_IMAGE_SIZE)
+ return -1;
+
+ return 0;
+}
+
+static gint
+read_color_mode_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint32 block_len;
+ guint32 block_start;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ block_len = GUINT32_FROM_BE (block_len);
+
+ block_start = ftell (f);
+ block_end = block_start + block_len;
+
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static gint
+read_image_resource_block (PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ guint32 block_len;
+ guint32 block_end;
+
+ if (fread (&block_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ img_a->image_res_len = GUINT32_FROM_BE (block_len);
+
+ IFDBG(1) g_debug ("Image resource block size = %d", (int)img_a->image_res_len);
+
+ img_a->image_res_start = ftell (f);
+ block_end = img_a->image_res_start + img_a->image_res_len;
+
+ if (fseek (f, block_end, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static gint32
+create_gimp_image (PSDimage *img_a,
+ const gchar *filename)
+{
+ gint32 image_id = -1;
+
+ img_a->base_type = GIMP_RGB;
+
+ /* Create gimp image */
+ IFDBG(2) g_debug ("Create image");
+ image_id = gimp_image_new (img_a->columns, img_a->rows, img_a->base_type);
+
+ gimp_image_set_filename (image_id, filename);
+ gimp_image_undo_disable (image_id);
+
+ return image_id;
+}
+
+static gint
+add_image_resources (gint32 image_id,
+ PSDimage *img_a,
+ FILE *f,
+ GError **error)
+{
+ PSDimageres res_a;
+ gint status;
+
+ if (fseek (f, img_a->image_res_start, SEEK_SET) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+
+ while (ftell (f) < img_a->image_res_start + img_a->image_res_len)
+ {
+ if (get_image_resource_header (&res_a, f, error) < 0)
+ return -1;
+
+ if (res_a.data_start + res_a.data_len >
+ img_a->image_res_start + img_a->image_res_len)
+ return 0;
+
+ status = load_thumbnail_resource (&res_a, image_id, f, error);
+ /* Error */
+ if (status < 0)
+ return -1;
+ /* Thumbnail Loaded */
+ if (status > 0)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/plug-ins/file-psd/psd-thumb-load.h b/plug-ins/file-psd/psd-thumb-load.h
new file mode 100644
index 0000000..a5e1281
--- /dev/null
+++ b/plug-ins/file-psd/psd-thumb-load.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_THUMB_LOAD_H__
+#define __PSD_THUMB_LOAD_H__
+
+
+gint32 load_thumbnail_image (const gchar *filename,
+ gint *width,
+ gint *height,
+ GError **error);
+
+
+#endif /* __PSD_THUMB_LOAD_H__ */
diff --git a/plug-ins/file-psd/psd-util.c b/plug-ins/file-psd/psd-util.c
new file mode 100644
index 0000000..1eccdd6
--- /dev/null
+++ b/plug-ins/file-psd/psd-util.c
@@ -0,0 +1,933 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+#include <libgimp/gimp.h>
+
+#include "psd.h"
+#include "psd-util.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+/* Local constants */
+#define MIN_RUN 3
+
+/* Local types */
+typedef struct
+{
+ const gchar *name;
+ const gchar *psd_mode;
+ GimpLayerMode gimp_mode;
+ gboolean exact; /* does the modes behave (more-or-less) the same in
+ * Photoshop and in GIMP?
+ */
+} LayerModeMapping;
+
+/* Local function prototypes */
+static const gchar * get_enum_value_nick (GType type,
+ gint value);
+
+/* Local variables */
+
+/* mapping table between Photoshop and GIMP modes. in case a mode matches more
+ * than one entry (in either direction), the first entry wins.
+ */
+static const LayerModeMapping layer_mode_map[] =
+{
+/* Name PSD GIMP Exact? */
+
+ /* Normal (ps3) */
+ { "Normal", "norm", GIMP_LAYER_MODE_NORMAL, TRUE },
+ { "Normal", "norm", GIMP_LAYER_MODE_NORMAL_LEGACY, TRUE },
+
+ /* Dissolve (ps3) */
+ { "Dissolve", "diss", GIMP_LAYER_MODE_DISSOLVE, TRUE },
+
+ /* Multiply (ps3) */
+ { "Multiply", "mul ", GIMP_LAYER_MODE_MULTIPLY, TRUE },
+ { "Multiply", "mul ", GIMP_LAYER_MODE_MULTIPLY_LEGACY, TRUE },
+
+ /* Screen (ps3) */
+ { "Screen", "scrn", GIMP_LAYER_MODE_SCREEN, TRUE },
+ { "Screen", "scrn", GIMP_LAYER_MODE_SCREEN_LEGACY, TRUE },
+
+ /* Overlay (ps3) */
+ { "Overlay", "over", GIMP_LAYER_MODE_OVERLAY, TRUE },
+
+ /* Difference (ps3) */
+ { "Difference", "diff", GIMP_LAYER_MODE_DIFFERENCE, TRUE },
+ { "Difference", "diff", GIMP_LAYER_MODE_DIFFERENCE_LEGACY, TRUE },
+
+ /* Linear Dodge (cs2) */
+ { "Linear Dodge", "lddg", GIMP_LAYER_MODE_ADDITION, TRUE },
+ { "Linear Dodge", "lddg", GIMP_LAYER_MODE_ADDITION_LEGACY, TRUE },
+
+ /* Subtract (??) */
+ { "Subtract", "fsub", GIMP_LAYER_MODE_SUBTRACT, TRUE },
+ { "Subtract", "fsub", GIMP_LAYER_MODE_SUBTRACT_LEGACY, TRUE },
+
+ /* Darken (ps3) */
+ { "Darken", "dark", GIMP_LAYER_MODE_DARKEN_ONLY, TRUE },
+ { "Darken", "dark", GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, TRUE },
+
+ /* Lighten (ps3) */
+ { "Ligten", "lite", GIMP_LAYER_MODE_LIGHTEN_ONLY, TRUE },
+ { "Ligten", "lite", GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, TRUE },
+
+ /* Hue (ps3) */
+ { "Hue", "hue ", GIMP_LAYER_MODE_LCH_HUE, FALSE },
+ { "Hue", "hue ", GIMP_LAYER_MODE_HSV_HUE, FALSE },
+ { "Hue", "hue ", GIMP_LAYER_MODE_HSV_HUE_LEGACY, FALSE },
+
+ /* Stauration (ps3) */
+ { "Saturation", "sat ", GIMP_LAYER_MODE_LCH_CHROMA, FALSE },
+ { "Saturation", "sat ", GIMP_LAYER_MODE_HSV_SATURATION, FALSE },
+ { "Saturation", "sat ", GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, FALSE },
+
+ /* Color (ps3) */
+ { "Color", "colr", GIMP_LAYER_MODE_LCH_COLOR, FALSE },
+ { "Color", "colr", GIMP_LAYER_MODE_HSL_COLOR, FALSE },
+ { "Color", "colr", GIMP_LAYER_MODE_HSL_COLOR_LEGACY, FALSE },
+
+ /* Luminosity (ps3) */
+ { "Luminosity", "lum ", GIMP_LAYER_MODE_LCH_LIGHTNESS, FALSE },
+ { "Luminosity", "lum ", GIMP_LAYER_MODE_HSV_VALUE, FALSE },
+ { "Luminosity", "lum ", GIMP_LAYER_MODE_HSV_VALUE_LEGACY, FALSE },
+ { "Luminosity", "lum ", GIMP_LAYER_MODE_LUMINANCE, FALSE },
+
+ /* Divide (??) */
+ { "Divide", "fdiv", GIMP_LAYER_MODE_DIVIDE, TRUE },
+ { "Divide", "fdiv", GIMP_LAYER_MODE_DIVIDE_LEGACY, TRUE },
+
+ /* Color Dodge (ps6) */
+ { "Color Dodge", "div ", GIMP_LAYER_MODE_DODGE, TRUE },
+ { "Color Dodge", "div ", GIMP_LAYER_MODE_DODGE_LEGACY, TRUE },
+
+ /* Color Burn (ps6) */
+ { "Color Burn", "idiv", GIMP_LAYER_MODE_BURN, TRUE },
+ { "Color Burn", "idiv", GIMP_LAYER_MODE_BURN_LEGACY, TRUE },
+
+ /* Hard Light (ps3) */
+ { "Hard Light", "hLit", GIMP_LAYER_MODE_HARDLIGHT, TRUE },
+ { "Hard Light", "hLit", GIMP_LAYER_MODE_HARDLIGHT_LEGACY, TRUE },
+
+ /* Soft Light (ps3) */
+ { "Soft Light", "sLit", GIMP_LAYER_MODE_SOFTLIGHT, FALSE },
+ { "Soft Light", "sLit", GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, FALSE },
+ { "Soft Light", "sLit", GIMP_LAYER_MODE_OVERLAY_LEGACY, FALSE },
+
+ /* Vivid Light (ps7)*/
+ { "Vivid Light", "vLit", GIMP_LAYER_MODE_VIVID_LIGHT, TRUE },
+
+ /* Pin Light (ps7)*/
+ { "Pin Light", "pLit", GIMP_LAYER_MODE_PIN_LIGHT, TRUE },
+
+ /* Linear Light (ps7)*/
+ { "Linear Light", "lLit", GIMP_LAYER_MODE_LINEAR_LIGHT, TRUE },
+
+ /* Hard Mix (CS)*/
+ { "Hard Mix", "hMix", GIMP_LAYER_MODE_HARD_MIX, TRUE },
+
+ /* Exclusion (ps6) */
+ { "Exclusion", "smud", GIMP_LAYER_MODE_EXCLUSION, TRUE },
+
+ /* Linear Burn (ps7)*/
+ { "Linear Burn", "lbrn", GIMP_LAYER_MODE_LINEAR_BURN, TRUE },
+
+ /* Darker Color (??)*/
+ { "Darker Color", "dkCl", GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, FALSE },
+
+ /* Lighter Color (??)*/
+ { "Lighter Color", "lgCl", GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, FALSE },
+
+ /* Pass Through (CS)*/
+ { "Pass Through", "pass", GIMP_LAYER_MODE_PASS_THROUGH, TRUE },
+};
+
+
+/* Utility function */
+void
+psd_set_error (gboolean file_eof,
+ gint err_no,
+ GError **error)
+{
+ if (file_eof)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "%s", _("Unexpected end of file"));
+ }
+ else
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (err_no),
+ "%s", g_strerror (err_no));
+ }
+
+ return;
+}
+
+gchar *
+fread_pascal_string (gint32 *bytes_read,
+ gint32 *bytes_written,
+ guint16 mod_len,
+ FILE *f,
+ GError **error)
+{
+ /*
+ * Reads a pascal string from the file padded to a multiple of mod_len
+ * and returns a utf-8 string.
+ */
+
+ gchar *str;
+ gchar *utf8_str;
+ guchar len;
+ gint32 padded_len;
+
+ *bytes_read = 0;
+ *bytes_written = -1;
+
+ if (fread (&len, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ (*bytes_read)++;
+ IFDBG(3) g_debug ("Pascal string length %d", len);
+
+ if (len == 0)
+ {
+ if (fseek (f, mod_len - 1, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ *bytes_read += (mod_len - 1);
+ *bytes_written = 0;
+ return NULL;
+ }
+
+ str = g_malloc (len);
+ if (fread (str, len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (str);
+ return NULL;
+ }
+ *bytes_read += len;
+
+ if (mod_len > 0)
+ {
+ padded_len = len + 1;
+ while (padded_len % mod_len != 0)
+ {
+ if (fseek (f, 1, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (str);
+ return NULL;
+ }
+ (*bytes_read)++;
+ padded_len++;
+ }
+ }
+
+ utf8_str = gimp_any_to_utf8 (str, len, NULL);
+ *bytes_written = strlen (utf8_str);
+ g_free (str);
+
+ IFDBG(3) g_debug ("Pascal string: %s, bytes_read: %d, bytes_written: %d",
+ utf8_str, *bytes_read, *bytes_written);
+
+ return utf8_str;
+}
+
+gint32
+fwrite_pascal_string (const gchar *src,
+ guint16 mod_len,
+ FILE *f,
+ GError **error)
+{
+ /*
+ * Converts utf-8 string to current locale and writes as pascal
+ * string with padding to a multiple of mod_len.
+ */
+
+ gchar *str;
+ gchar *pascal_str;
+ gchar null_str = 0x0;
+ guchar pascal_len;
+ gint32 bytes_written = 0;
+ gsize len;
+
+ if (src == NULL)
+ {
+ /* Write null string as two null bytes (0x0) */
+ if (fwrite (&null_str, 1, 1, f) < 1
+ || fwrite (&null_str, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ bytes_written += 2;
+ }
+ else
+ {
+ str = g_locale_from_utf8 (src, -1, NULL, &len, NULL);
+ if (len > 255)
+ pascal_len = 255;
+ else
+ pascal_len = len;
+ pascal_str = g_strndup (str, pascal_len);
+ g_free (str);
+ if (fwrite (&pascal_len, 1, 1, f) < 1
+ || fwrite (pascal_str, pascal_len, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (pascal_str);
+ return -1;
+ }
+ bytes_written++;
+ bytes_written += pascal_len;
+ IFDBG(2) g_debug ("Pascal string: %s, bytes_written: %d",
+ pascal_str, bytes_written);
+ g_free (pascal_str);
+ }
+
+ /* Pad with nulls */
+ if (mod_len > 0)
+ {
+ while (bytes_written % mod_len != 0)
+ {
+ if (fwrite (&null_str, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ bytes_written++;
+ }
+ }
+
+ return bytes_written;
+}
+
+gchar *
+fread_unicode_string (gint32 *bytes_read,
+ gint32 *bytes_written,
+ guint16 mod_len,
+ FILE *f,
+ GError **error)
+{
+ /*
+ * Reads a utf-16 string from the file padded to a multiple of mod_len
+ * and returns a utf-8 string.
+ */
+
+ gchar *utf8_str;
+ gunichar2 *utf16_str;
+ gint32 len;
+ gint32 i;
+ gint32 padded_len;
+ glong utf8_str_len;
+
+ *bytes_read = 0;
+ *bytes_written = -1;
+
+ if (fread (&len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ *bytes_read += 4;
+ len = GINT32_FROM_BE (len);
+ IFDBG(3) g_debug ("Unicode string length %d", len);
+
+ if (len == 0)
+ {
+ if (fseek (f, mod_len - 1, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ return NULL;
+ }
+ *bytes_read += (mod_len - 1);
+ *bytes_written = 0;
+ return NULL;
+ }
+
+ utf16_str = g_malloc (len * 2);
+ for (i = 0; i < len; ++i)
+ {
+ if (fread (&utf16_str[i], 2, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (utf16_str);
+ return NULL;
+ }
+ *bytes_read += 2;
+ utf16_str[i] = GINT16_FROM_BE (utf16_str[i]);
+ }
+
+ if (mod_len > 0)
+ {
+ padded_len = len + 1;
+ while (padded_len % mod_len != 0)
+ {
+ if (fseek (f, 1, SEEK_CUR) < 0)
+ {
+ psd_set_error (feof (f), errno, error);
+ g_free (utf16_str);
+ return NULL;
+ }
+ (*bytes_read)++;
+ padded_len++;
+ }
+ }
+
+ utf8_str = g_utf16_to_utf8 (utf16_str, len, NULL, &utf8_str_len, NULL);
+ *bytes_written = utf8_str_len;
+ g_free (utf16_str);
+
+ IFDBG(3) g_debug ("Unicode string: %s, bytes_read: %d, bytes_written: %d",
+ utf8_str, *bytes_read, *bytes_written);
+
+ return utf8_str;
+}
+
+gint32
+fwrite_unicode_string (const gchar *src,
+ guint16 mod_len,
+ FILE *f,
+ GError **error)
+{
+ /*
+ * Converts utf-8 string to utf-16 and writes 4 byte length
+ * then string padding to multiple of mod_len.
+ */
+
+ gunichar2 *utf16_str;
+ gchar null_str = 0x0;
+ gint32 utf16_len = 0;
+ gint32 bytes_written = 0;
+ gint i;
+ glong len;
+
+ if (src == NULL)
+ {
+ /* Write null string as four byte 0 int32 */
+ if (fwrite (&utf16_len, 4, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ bytes_written += 4;
+ }
+ else
+ {
+ utf16_str = g_utf8_to_utf16 (src, -1, NULL, &len, NULL);
+ /* Byte swap as required */
+ utf16_len = len;
+ for (i = 0; i < utf16_len; ++i)
+ utf16_str[i] = GINT16_TO_BE (utf16_str[i]);
+ utf16_len = GINT32_TO_BE (utf16_len);
+
+ if (fwrite (&utf16_len, 4, 1, f) < 1
+ || fwrite (utf16_str, 2, utf16_len + 1, f) < utf16_len + 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ bytes_written += (4 + 2 * utf16_len + 2);
+ IFDBG(2) g_debug ("Unicode string: %s, bytes_written: %d",
+ src, bytes_written);
+ }
+
+ /* Pad with nulls */
+ if (mod_len > 0)
+ {
+ while (bytes_written % mod_len != 0)
+ {
+ if (fwrite (&null_str, 1, 1, f) < 1)
+ {
+ psd_set_error (feof (f), errno, error);
+ return -1;
+ }
+ bytes_written++;
+ }
+ }
+
+ return bytes_written;
+}
+
+gint
+decode_packbits (const gchar *src,
+ gchar *dst,
+ guint16 packed_len,
+ guint32 unpacked_len)
+{
+ /*
+ * Decode a PackBits chunk.
+ */
+
+ gint n;
+ gint32 unpack_left = unpacked_len;
+ gint32 pack_left = packed_len;
+ gint32 error_code = 0;
+ gint32 return_val = 0;
+
+ while (unpack_left > 0 && pack_left > 0)
+ {
+ n = *(const guchar *) src;
+ src++;
+ pack_left--;
+
+ if (n == 128) /* nop */
+ continue;
+ else if (n > 128)
+ n -= 256;
+
+ if (n < 0) /* replicate next gchar |n|+ 1 times */
+ {
+ n = 1 - n;
+ if (pack_left < 1)
+ {
+ IFDBG(2) g_debug ("Input buffer exhausted in replicate");
+ error_code = 1;
+ break;
+ }
+ if (unpack_left < n)
+ {
+ IFDBG(2) g_debug ("Overrun in packbits replicate of %d chars", n - unpack_left);
+ error_code = 2;
+ }
+ memset (dst, *src, n);
+ src++;
+ pack_left--;
+ dst += n;
+ unpack_left -= n;
+ }
+ else /* copy next n+1 gchars literally */
+ {
+ n++;
+ if (pack_left < n)
+ {
+ IFDBG(2) g_debug ("Input buffer exhausted in copy");
+ error_code = 3;
+ break;
+ }
+ if (unpack_left < n)
+ {
+ IFDBG(2) g_debug ("Output buffer exhausted in copy");
+ error_code = 4;
+ break;
+ }
+ memcpy (dst, src, n);
+ src += n;
+ pack_left -= n;
+ dst += n;
+ unpack_left -= n;
+ }
+ }
+
+ if (unpack_left > 0)
+ {
+ /* Pad with zeros to end of output buffer */
+ memset (dst, 0, unpack_left);
+ }
+
+ if (unpack_left)
+ {
+ IFDBG(2) g_debug ("Packbits decode - unpack left %d", unpack_left);
+ return_val -= unpack_left;
+ }
+ if (pack_left)
+ {
+ /* Some images seem to have a pad byte at the end of the packed data */
+ if (error_code || pack_left != 1)
+ {
+ IFDBG(2) g_debug ("Packbits decode - pack left %d", pack_left);
+ return_val = pack_left;
+ }
+ }
+
+ IFDBG(2)
+ if (error_code)
+ g_debug ("Error code %d", error_code);
+
+ return return_val;
+}
+
+gchar *
+encode_packbits (const gchar *src,
+ guint32 unpacked_len,
+ guint16 *packed_len)
+{
+ /*
+ * Encode a PackBits chunk.
+ */
+
+ GString *dst_str; /* destination string */
+ gint curr_char; /* current character */
+ gchar char_buff[128]; /* buffer of already read characters */
+ guchar run_len; /* number of characters in a run */
+ gint32 unpack_left = unpacked_len;
+
+ IFDBG(2) g_debug ("Encode packbits");
+
+ /* Initialise destination string */
+ dst_str = g_string_sized_new (unpacked_len);
+
+ /* prime the read loop */
+ curr_char = *src;
+ src++;
+ run_len = 0;
+
+ /* read input until there's nothing left */
+ while (unpack_left > 0)
+ {
+ char_buff[run_len] = (gchar) curr_char;
+ IFDBG(2) g_debug ("buff %x, run len %d, curr char %x",
+ char_buff[run_len], run_len, (gchar) curr_char);
+ run_len++;
+
+ if (run_len >= MIN_RUN)
+ {
+ gint i;
+
+ /* check for run */
+ for (i = 2; i <= MIN_RUN; ++i)
+ {
+ if (curr_char != char_buff[run_len - i])
+ {
+ /* no run */
+ i = 0;
+ break;
+ }
+ }
+
+ if (i != 0)
+ {
+ /* we have a run write out buffer before run*/
+ gint next_char;
+
+ if (run_len > MIN_RUN)
+ {
+ /* block size - 1 followed by contents */
+ g_string_append_c (dst_str, (run_len - MIN_RUN - 1));
+ g_string_append_len (dst_str, char_buff, (run_len - MIN_RUN));
+ IFDBG(2) g_debug ("(1) Number of chars: %d, run length tag: %d",
+ run_len - MIN_RUN, run_len - MIN_RUN - 1);
+ }
+
+ /* determine run length (MIN_RUN so far) */
+ run_len = MIN_RUN;
+
+ /* get next character */
+ next_char = *src;
+ src++;
+ unpack_left--;
+ while (next_char == curr_char)
+ {
+ run_len++;
+ if (run_len == 128)
+ {
+ /* run is at max length */
+ break;
+ }
+ next_char = *src;
+ src++;
+ unpack_left--;
+ }
+
+ /* write out encoded run length and run symbol */
+ g_string_append_c (dst_str, (gchar)(1 - (gint)(run_len)));
+ g_string_append_c (dst_str, curr_char);
+ IFDBG(2) g_debug ("(2) Number of chars: %d, run length tag: %d",
+ run_len, (1 - (gint)(run_len)));
+
+ if (unpack_left > 0)
+ {
+ /* make run breaker start of next buffer */
+ char_buff[0] = next_char;
+ run_len = 1;
+ }
+ else
+ {
+ /* file ends in a run */
+ run_len = 0;
+ }
+ }
+ }
+
+ if (run_len == 128)
+ {
+ /* write out buffer */
+ g_string_append_c (dst_str, 127);
+ g_string_append_len (dst_str, char_buff, 128);
+ IFDBG(2) g_debug ("(3) Number of chars: 128, run length tag: 127");
+
+ /* start a new buffer */
+ run_len = 0;
+ }
+
+ curr_char = *src;
+ src++;
+ unpack_left--;
+ }
+
+ /* write out last buffer */
+ if (run_len != 0)
+ {
+ if (run_len <= 128)
+ {
+ /* write out entire copy buffer */
+ g_string_append_c (dst_str, run_len - 1);
+ g_string_append_len (dst_str, char_buff, run_len);
+ IFDBG(2) g_debug ("(4) Number of chars: %d, run length tag: %d",
+ run_len, run_len - 1);
+ }
+ else
+ {
+ IFDBG(2) g_debug ("(5) Very bad - should not be here");
+ }
+ }
+
+ *packed_len = dst_str->len;
+ IFDBG(2) g_debug ("Packed len %d, unpacked %d", *packed_len, unpacked_len);
+
+ return g_string_free (dst_str, FALSE);
+}
+
+void
+psd_to_gimp_blend_mode (PSDlayer *psd_layer,
+ LayerModeInfo *mode_info)
+{
+ gint i;
+
+ mode_info->mode = GIMP_LAYER_MODE_NORMAL;
+ /* FIXME: use the image mode to select the correct color spaces. for now,
+ * we use rgb-perceptual blending/compositing unconditionally.
+ */
+ mode_info->blend_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL;
+ mode_info->composite_space = GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL;
+ if (psd_layer->clipping == 1)
+ mode_info->composite_mode = GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP;
+ else
+ mode_info->composite_mode = GIMP_LAYER_COMPOSITE_UNION;
+
+ for (i = 0; i < G_N_ELEMENTS (layer_mode_map); i++)
+ {
+ if (g_ascii_strncasecmp (psd_layer->blend_mode, layer_mode_map[i].psd_mode, 4) == 0)
+ {
+ if (! layer_mode_map[i].exact && CONVERSION_WARNINGS)
+ {
+ g_message ("GIMP uses a different equation than Photoshop for "
+ "blend mode: %s. Results will differ.",
+ layer_mode_map[i].name);
+ }
+
+ mode_info->mode = layer_mode_map[i].gimp_mode;
+
+ return;
+ }
+ }
+
+ if (CONVERSION_WARNINGS)
+ {
+ gchar *mode_name = g_strndup (psd_layer->blend_mode, 4);
+ g_message ("Unsupported blend mode: %s. Mode reverts to normal",
+ mode_name);
+ g_free (mode_name);
+ }
+}
+
+const gchar *
+gimp_to_psd_blend_mode (const LayerModeInfo *mode_info)
+{
+ gint i;
+
+ /* FIXME: select the image mode based on the layer mode color spaces. for
+ * now, we assume rgb-perceptual blending/compositing unconditionally.
+ */
+ if (mode_info->blend_space != GIMP_LAYER_COLOR_SPACE_AUTO &&
+ mode_info->blend_space != GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL)
+ {
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported blend color space: %s. "
+ "Blend color space reverts to rgb-perceptual",
+ get_enum_value_nick (GIMP_TYPE_LAYER_COLOR_SPACE,
+ mode_info->blend_space));
+ }
+
+ if (mode_info->composite_space != GIMP_LAYER_COLOR_SPACE_AUTO &&
+ mode_info->composite_space != GIMP_LAYER_COLOR_SPACE_RGB_PERCEPTUAL)
+ {
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported composite color space: %s. "
+ "Composite color space reverts to rgb-perceptual",
+ get_enum_value_nick (GIMP_TYPE_LAYER_COLOR_SPACE,
+ mode_info->composite_space));
+ }
+
+ if (mode_info->composite_mode != GIMP_LAYER_COMPOSITE_AUTO &&
+ mode_info->composite_mode != GIMP_LAYER_COMPOSITE_UNION &&
+ mode_info->composite_mode != GIMP_LAYER_COMPOSITE_CLIP_TO_BACKDROP)
+ {
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported composite mode: %s. "
+ "Composite mode reverts to union",
+ get_enum_value_nick (GIMP_TYPE_LAYER_COMPOSITE_MODE,
+ mode_info->composite_mode));
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (layer_mode_map); i++)
+ {
+ if (layer_mode_map[i].gimp_mode == mode_info->mode)
+ {
+ if (! layer_mode_map[i].exact && CONVERSION_WARNINGS)
+ {
+ g_message ("GIMP uses a different equation than Photoshop for "
+ "blend mode: %s. Results may differ.",
+ get_enum_value_nick (GIMP_TYPE_LAYER_MODE,
+ mode_info->mode));
+ }
+
+ return layer_mode_map[i].psd_mode;
+ }
+ }
+
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported blend mode: %s. Mode reverts to normal",
+ get_enum_value_nick (GIMP_TYPE_LAYER_MODE, mode_info->mode));
+
+ return "norm";
+}
+
+GimpColorTag
+psd_to_gimp_layer_color_tag (guint16 layer_color_tag)
+{
+ GimpColorTag colorTag;
+
+ switch (layer_color_tag)
+ {
+ case 1:
+ colorTag = GIMP_COLOR_TAG_RED;
+ break;
+
+ case 2:
+ colorTag = GIMP_COLOR_TAG_ORANGE;
+ break;
+
+ case 3:
+ colorTag = GIMP_COLOR_TAG_YELLOW;
+ break;
+
+ case 4:
+ colorTag = GIMP_COLOR_TAG_GREEN;
+ break;
+
+ case 5:
+ colorTag = GIMP_COLOR_TAG_BLUE;
+ break;
+
+ case 6:
+ colorTag = GIMP_COLOR_TAG_VIOLET;
+ break;
+
+ case 7:
+ colorTag = GIMP_COLOR_TAG_GRAY;
+ break;
+
+ default:
+ if (CONVERSION_WARNINGS)
+ g_message ("Unsupported Photoshop layer color tag: %i. GIMP layer color tag set to none.",
+ layer_color_tag);
+ colorTag = GIMP_COLOR_TAG_NONE;
+ }
+
+ return colorTag;
+}
+
+guint16
+gimp_to_psd_layer_color_tag (GimpColorTag layer_color_tag)
+{
+ guint16 color_tag;
+
+ switch (layer_color_tag)
+ {
+ case GIMP_COLOR_TAG_RED:
+ color_tag = 1;
+ break;
+
+ case GIMP_COLOR_TAG_ORANGE:
+ color_tag = 2;
+ break;
+
+ case GIMP_COLOR_TAG_YELLOW:
+ color_tag = 3;
+ break;
+
+ case GIMP_COLOR_TAG_GREEN:
+ color_tag = 4;
+ break;
+
+ case GIMP_COLOR_TAG_BLUE:
+ color_tag = 5;
+ break;
+
+ case GIMP_COLOR_TAG_VIOLET:
+ color_tag = 6;
+ break;
+
+ case GIMP_COLOR_TAG_GRAY:
+ color_tag = 7;
+ break;
+
+ default:
+ if (CONVERSION_WARNINGS)
+ g_message ("Photoshop doesn't support GIMP layer color tag: %i. Photoshop layer color tag set to none.",
+ layer_color_tag);
+ color_tag = 0;
+ }
+
+ return color_tag;
+}
+
+static const gchar *
+get_enum_value_nick (GType type,
+ gint value)
+{
+ const gchar *nick;
+
+ if (gimp_enum_get_value (type, value, NULL, &nick, NULL, NULL))
+ {
+ return nick;
+ }
+ else
+ {
+ static gchar err_name[32];
+
+ snprintf (err_name, sizeof (err_name), "UNKNOWN (%d)", value);
+
+ return err_name;
+ }
+}
diff --git a/plug-ins/file-psd/psd-util.h b/plug-ins/file-psd/psd-util.h
new file mode 100644
index 0000000..efa8aa7
--- /dev/null
+++ b/plug-ins/file-psd/psd-util.h
@@ -0,0 +1,87 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_UTIL_H__
+#define __PSD_UTIL_H__
+
+/*
+ * Set file read error
+ */
+void psd_set_error (gboolean file_eof,
+ gint err_no,
+ GError **error);
+
+/*
+ * Reads a pascal string from the file padded to a multiple of mod_len
+ * and returns a utf-8 string.
+ */
+gchar * fread_pascal_string (gint32 *bytes_read,
+ gint32 *bytes_written,
+ guint16 mod_len,
+ FILE *f,
+ GError **error);
+
+/*
+ * Converts utf-8 string to current locale and writes as pascal
+ * string with padding to a multiple of mod_len.
+ */
+gint32 fwrite_pascal_string (const gchar *src,
+ guint16 mod_len,
+ FILE *f,
+ GError **error);
+
+/*
+ * Reads a utf-16 string from the file padded to a multiple of mod_len
+ * and returns a utf-8 string.
+ */
+gchar * fread_unicode_string (gint32 *bytes_read,
+ gint32 *bytes_written,
+ guint16 mod_len,
+ FILE *f,
+ GError **error);
+
+/*
+ * Converts utf-8 string to utf-16 and writes 4 byte length
+ * then string padding to multiple of mod_len.
+ */
+gint32 fwrite_unicode_string (const gchar *src,
+ guint16 mod_len,
+ FILE *f,
+ GError **error);
+
+gint decode_packbits (const gchar *src,
+ gchar *dst,
+ guint16 packed_len,
+ guint32 unpacked_len);
+
+gchar * encode_packbits (const gchar *src,
+ guint32 unpacked_len,
+ guint16 *packed_len);
+
+void psd_to_gimp_blend_mode (PSDlayer *psd_layer,
+ LayerModeInfo *mode_info);
+
+const gchar * gimp_to_psd_blend_mode (const LayerModeInfo *mode_info);
+
+GimpColorTag psd_to_gimp_layer_color_tag (guint16 layer_color_tag);
+
+guint16 gimp_to_psd_layer_color_tag (GimpColorTag layer_color_tag);
+
+#endif /* __PSD_UTIL_H__ */
diff --git a/plug-ins/file-psd/psd.c b/plug-ins/file-psd/psd.c
new file mode 100644
index 0000000..083a5a9
--- /dev/null
+++ b/plug-ins/file-psd/psd.c
@@ -0,0 +1,385 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "psd.h"
+#include "psd-load.h"
+#include "psd-save.h"
+#include "psd-thumb-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+/* Local variables */
+
+GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ /* File Load */
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ /* Thumbnail Load */
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ /* File save */
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
+ { GIMP_PDB_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2)" },
+ { GIMP_PDB_INT32, "fill-order", "Fill Order: { MSB to LSB (0), LSB to MSB (1)" }
+ };
+
+ /* File load */
+ gimp_install_procedure (LOAD_PROC,
+ "Loads images from the Photoshop PSD file format",
+ "This plug-in loads images in Adobe "
+ "Photoshop (TM) native PSD format.",
+ "John Marshall",
+ "John Marshall",
+ "2007",
+ N_("Photoshop image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-psd");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "psd",
+ "",
+ "0,string,8BPS");
+
+ /* File load (merged) */
+ gimp_install_procedure (LOAD_MERGED_PROC,
+ "Loads merged images from the Photoshop PSD file format",
+ "This plug-in loads the merged image data in Adobe "
+ "Photoshop (TM) native PSD format.",
+ "Ell",
+ "Ell",
+ "2018",
+ N_("Photoshop image (merged)"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_priority (LOAD_MERGED_PROC, +1);
+ gimp_register_file_handler_mime (LOAD_MERGED_PROC, "image/x-psd");
+ gimp_register_magic_load_handler (LOAD_MERGED_PROC,
+ "psd",
+ "",
+ "0,string,8BPS");
+
+ /* Thumbnail load */
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads thumbnails from the Photoshop PSD file format",
+ "This plug-in loads thumbnail images from Adobe "
+ "Photoshop (TM) native PSD format files.",
+ "John Marshall",
+ "John Marshall",
+ "2007",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PROC,
+ "saves files in the Photoshop(tm) PSD file format",
+ "This filter saves files of Adobe Photoshop(tm) native PSD format. These files may be of any image type supported by GIMP, with or without layers, layer masks, aux channels and guides.",
+ "Monigotes",
+ "Monigotes",
+ "2000",
+ N_("Photoshop image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-psd");
+ gimp_register_save_handler (SAVE_PROC, "psd", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[4];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0 ||
+ strcmp (name, LOAD_MERGED_PROC) == 0)
+ {
+ gboolean resolution_loaded = FALSE;
+ gboolean profile_loaded = FALSE;
+ gboolean interactive;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ interactive = TRUE;
+ break;
+ default:
+ interactive = FALSE;
+ break;
+ }
+
+ image_ID = load_image (param[1].data.d_string,
+ strcmp (name, LOAD_MERGED_PROC) == 0,
+ &resolution_loaded,
+ &profile_loaded,
+ &error);
+
+ if (image_ID != -1)
+ {
+ GFile *file = g_file_new_for_path (param[1].data.d_string);
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/x-psd",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ if (profile_loaded)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image_ID, "image/x-psd",
+ metadata, flags,
+ interactive);
+
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ const gchar *filename = param[0].data.d_string;
+ gint width = 0;
+ gint height = 0;
+
+ image_ID = load_thumbnail_image (filename, &width, &height, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 4;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ gint32 drawable_id;
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ GimpExportReturn export = GIMP_EXPORT_IGNORE;
+
+ IFDBG(2) g_debug ("\n---------------- %s ----------------\n",
+ param[3].data.d_string);
+
+ image_ID = param[1].data.d_int32;
+ drawable_id = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_id, "PSD",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS |
+ GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ metadata = gimp_image_metadata_save_prepare (image_ID,
+ "image/x-psd",
+ &metadata_flags);
+
+ if (save_image (param[3].data.d_string, image_ID, &error))
+ {
+ if (metadata)
+ {
+ GFile *file;
+
+ gimp_metadata_set_bits_per_sample (metadata, 8);
+
+ file = g_file_new_for_path (param[3].data.d_string);
+ gimp_image_metadata_save_finish (image_ID,
+ "image/x-psd",
+ metadata, metadata_flags,
+ file, NULL);
+ g_object_unref (file);
+ }
+
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+ }
+ else
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+
+ /* Unknown procedure */
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/file-psd/psd.h b/plug-ins/file-psd/psd.h
new file mode 100644
index 0000000..febb003
--- /dev/null
+++ b/plug-ins/file-psd/psd.h
@@ -0,0 +1,688 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP PSD Plug-in
+ * Copyright 2007 by John Marshall
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PSD_H__
+#define __PSD_H__
+
+
+/* Set to the level of debugging output you want, 0 for none.
+ * Setting higher than 2 will result in a very large amount of debug
+ * output being produced. */
+#define PSD_DEBUG 3
+#define IFDBG(level) if (PSD_DEBUG >= level)
+
+/* Set to FALSE to suppress pop-up warnings about lossy file conversions */
+#define CONVERSION_WARNINGS FALSE
+
+#define LOAD_PROC "file-psd-load"
+#define LOAD_MERGED_PROC "file-psd-load-merged"
+#define LOAD_THUMB_PROC "file-psd-load-thumb"
+#define SAVE_PROC "file-psd-save"
+#define PLUG_IN_BINARY "file-psd"
+#define PLUG_IN_ROLE "gimp-file-psd"
+
+#define GIMP_PARASITE_COMMENT "gimp-comment"
+
+#define PSD_PARASITE_DUOTONE_DATA "psd-duotone-data"
+
+/* Copied from app/base/gimpimage-quick-mask.h - internal identifier for quick mask channel */
+#define GIMP_IMAGE_QUICK_MASK_NAME "Qmask"
+
+#define MAX_RAW_SIZE 0 /* FIXME all images are raw if 0 */
+
+/* PSD spec defines */
+#define MAX_CHANNELS 56 /* Photoshop CS to CS3 support 56 channels */
+
+/* PSD spec constants */
+
+/* Layer resource IDs */
+
+/* Adjustment layer IDs */
+#define PSD_LADJ_LEVEL "levl" /* Adjustment layer - levels (PS4) */
+#define PSD_LADJ_CURVE "curv" /* Adjustment layer - curves (PS4) */
+#define PSD_LADJ_BRIGHTNESS "brit" /* Adjustment layer - brightness/contrast (PS4) */
+#define PSD_LADJ_BALANCE "blnc" /* Adjustment layer - color balance (PS4) */
+#define PSD_LADJ_BLACK_WHITE "blwh" /* Adjustment layer - black & white (PS10) */
+#define PSD_LADJ_HUE "hue " /* Adjustment layer - old hue/saturation (PS4) */
+#define PSD_LADJ_HUE2 "hue2" /* Adjustment layer - hue/saturation (PS5) */
+#define PSD_LADJ_SELECTIVE "selc" /* Adjustment layer - selective color (PS4) */
+#define PSD_LADJ_MIXER "mixr" /* Adjustment layer - channel mixer (PS9) */
+#define PSD_LADJ_GRAD_MAP "grdm" /* Adjustment layer - gradient map (PS9) */
+#define PSD_LADJ_PHOTO_FILT "phfl" /* Adjustment layer - photo filter (PS9) */
+#define PSD_LADJ_EXPOSURE "expA" /* Adjustment layer - exposure (PS10) */
+#define PSD_LADJ_INVERT "nvrt" /* Adjustment layer - invert (PS4) */
+#define PSD_LADJ_THRESHOLD "thrs" /* Adjustment layer - threshold (PS4) */
+#define PSD_LADJ_POSTERIZE "post" /* Adjustment layer - posterize (PS4) */
+#define PSD_LADJ_VIBRANCE "vibA" /* Adjustment layer - vibrance (PS10) */
+#define PSD_LADJ_COLOR_LOOKUP "clrL" /* Adjustment layer - color lookup (PS13) */
+
+/* Fill Layer IDs */
+#define PSD_LFIL_SOLID "SoCo" /* Solid color sheet setting (PS6) */
+#define PSD_LFIL_PATTERN "PtFl" /* Pattern fill setting (PS6) */
+#define PSD_LFIL_GRADIENT "GdFl" /* Gradient fill setting (PS6) */
+
+/* Effects Layer IDs */
+#define PSD_LFX_FX "lrFX" /* Effects layer info (PS5) */
+#define PSD_LFX_FX2 "lfx2" /* Object based effects layer info (PS6) */
+
+/* Type Tool Layers */
+#define PSD_LTYP_TYPE "tySh" /* Type tool layer (PS5) */
+#define PSD_LTYP_TYPE2 "TySh" /* Type tool object setting (PS6) */
+
+/* Layer Properties */
+#define PSD_LPRP_UNICODE "luni" /* Unicode layer name (PS5) */
+#define PSD_LPRP_SOURCE "lnsr" /* Layer name source setting (PS6) */
+#define PSD_LPRP_ID "lyid" /* Layer ID (PS5) */
+#define PSD_LPRP_BLEND_CLIP "clbl" /* Blend clipping elements (PS6) */
+#define PSD_LPRP_BLEND_INT "infx" /* Blend interior elements (PS6) */
+#define PSD_LPRP_KNOCKOUT "knko" /* Knockout setting (PS6) */
+#define PSD_LPRP_PROTECT "lspf" /* Protected setting (PS6) */
+#define PSD_LPRP_COLOR "lclr" /* Sheet color setting (PS6) */
+#define PSD_LPRP_REF_POINT "fxrp" /* Reference point (PS6) */
+#define PSD_LPRP_VERSION "lyvr" /* Layer version (PS7) */
+
+/* Vector mask */
+#define PSD_LMSK_VMASK "vmsk" /* Vector mask setting (PS6) */
+
+/* Parasites */
+#define PSD_LPAR_ANNOTATE "Anno" /* Annotation (PS6) */
+
+/* Other */
+#define PSD_LOTH_SECTION "lsct" /* Section divider setting - Layer groups (PS6) */
+#define PSD_LOTH_SECTION2 "lsdk" /* Nested section divider setting - Layer groups (CS5) */
+#define PSD_LOTH_PATTERN "Patt" /* Patterns (PS6) */
+#define PSD_LOTH_PATTERN_2 "Pat2" /* Patterns 2nd key (PS6) */
+#define PSD_LOTH_PATTERN_3 "Pat3" /* Patterns 3rd key (PS6) */
+#define PSD_LOTH_GRADIENT "grdm" /* Gradient settings (PS6) */
+#define PSD_LOTH_RESTRICT "brst" /* Channel blending restriction setting (PS6) */
+#define PSD_LOTH_FOREIGN_FX "ffxi" /* Foreign effect ID (PS6) */
+#define PSD_LOTH_PATT_DATA "shpa" /* Pattern data (PS6) */
+#define PSD_LOTH_META_DATA "shmd" /* Meta data setting (PS6) */
+#define PSD_LOTH_LAYER_DATA "layr" /* Layer data (PS6) */
+#define PSD_LOTH_CONTENT_GEN "CgEd" /* Content generator extra data (PS12) */
+#define PSD_LOTH_TEXT_ENGINE "Txt2" /* Text engine data (PS10) */
+#define PSD_LOTH_PATH_NAME "pths" /* Unicode path name (PS13) */
+#define PSD_LOTH_ANIMATION_FX "anFX" /* Animation effects (PS13) */
+#define PSD_LOTH_FILTER_MASK "FMsk" /* Filter mask (PS10) */
+#define PSD_LOTH_VECTOR_STROKE "vscg" /* Vector stroke data (PS13) */
+#define PSD_LOTH_ALIGN_RENDER "sn2P" /* Aligned rendering flag (?) */
+#define PSD_LOTH_USER_MASK "LMsk" /* User mask (?) */
+
+/* Effects layer resource IDs */
+#define PSD_LFX_COMMON "cmnS" /* Effects layer - common state (PS5) */
+#define PSD_LFX_DROP_SDW "dsdw" /* Effects layer - drop shadow (PS5) */
+#define PSD_LFX_INNER_SDW "isdw" /* Effects layer - inner shadow (PS5) */
+#define PSD_LFX_OUTER_GLW "oglw" /* Effects layer - outer glow (PS5) */
+#define PSD_LFX_INNER_GLW "iglw" /* Effects layer - inner glow (PS5) */
+#define PSD_LFX_BEVEL "bevl" /* Effects layer - bevel (PS5) */
+
+/* Placed Layer */
+#define PSD_LPL_PLACE_LAYER "plLd" /* Placed layer (?) */
+#define PSD_LPL_PLACE_LAYER_NEW "SoLd" /* Placed layer (PS10) */
+
+/* Linked Layer */
+#define PSD_LLL_LINKED_LAYER "lnkD" /* Linked layer (?) */
+#define PSD_LLL_LINKED_LAYER_2 "lnk2" /* Linked layer 2nd key */
+#define PSD_LLL_LINKED_LAYER_3 "lnk3" /* Linked layer 3rd key */
+
+/* Merged Transparency */
+#define PSD_LMT_MERGE_TRANS "Mtrn" /* Merged transparency save flag (?) */
+#define PSD_LMT_MERGE_TRANS_16 "Mt16" /* Merged transparency save flag 2 */
+#define PSD_LMT_MERGE_TRANS_32 "Mt32" /* Merged transparency save flag 3 */
+
+/* Filter Effects */
+#define PSD_LFFX_FILTER_FX "FXid" /* Filter effects (?) */
+#define PSD_LFFX_FILTER_FX_2 "FEid" /* Filter effects 2 */
+
+/* PSD spec enums */
+
+/* Image color modes */
+typedef enum {
+ PSD_BITMAP = 0, /* Bitmap image */
+ PSD_GRAYSCALE = 1, /* Greyscale image */
+ PSD_INDEXED = 2, /* Indexed image */
+ PSD_RGB = 3, /* RGB image */
+ PSD_CMYK = 4, /* CMYK */
+ PSD_MULTICHANNEL = 7, /* Multichannel image*/
+ PSD_DUOTONE = 8, /* Duotone image*/
+ PSD_LAB = 9 /* L*a*b image */
+} PSDColorMode;
+
+/* Image color spaces */
+typedef enum {
+ PSD_CS_RGB = 0, /* RGB */
+ PSD_CS_HSB = 1, /* Hue, Saturation, Brightness */
+ PSD_CS_CMYK = 2, /* CMYK */
+ PSD_CS_PANTONE = 3, /* Pantone matching system (Lab)*/
+ PSD_CS_FOCOLTONE = 4, /* Focoltone color system (CMYK)*/
+ PSD_CS_TRUMATCH = 5, /* Trumatch color (CMYK)*/
+ PSD_CS_TOYO = 6, /* Toyo 88 colorfinder 1050 (Lab)*/
+ PSD_CS_LAB = 7, /* L*a*b*/
+ PSD_CS_GRAYSCALE = 8, /* Grey scale */
+ PSD_CS_HKS = 10, /* HKS colors (CMYK)*/
+ PSD_CS_DIC = 11, /* DIC color guide (Lab)*/
+ PSD_CS_ANPA = 3000, /* Anpa color (Lab)*/
+} PSDColorSpace;
+
+/* Image Resource IDs */
+typedef enum {
+ PSD_PS2_IMAGE_INFO = 1000, /* 0x03e8 - Obsolete - ps 2.0 image info */
+ PSD_MAC_PRINT_INFO = 1001, /* 0x03e9 - Optional - Mac print manager print info record */
+ PSD_PS2_COLOR_TAB = 1003, /* 0x03eb - Obsolete - ps 2.0 indexed color table */
+ PSD_RESN_INFO = 1005, /* 0x03ed - ResolutionInfo structure */
+ PSD_ALPHA_NAMES = 1006, /* 0x03ee - Alpha channel names */
+ PSD_DISPLAY_INFO = 1007, /* 0x03ef - Superceded by PSD_DISPLAY_INFO_NEW for ps CS3 and higher - DisplayInfo structure */
+ PSD_CAPTION = 1008, /* 0x03f0 - Optional - Caption string */
+ PSD_BORDER_INFO = 1009, /* 0x03f1 - Border info */
+ PSD_BACKGROUND_COL = 1010, /* 0x03f2 - Background color */
+ PSD_PRINT_FLAGS = 1011, /* 0x03f3 - Print flags */
+ PSD_GREY_HALFTONE = 1012, /* 0x03f4 - Greyscale and multichannel halftoning info */
+ PSD_COLOR_HALFTONE = 1013, /* 0x03f5 - Color halftoning info */
+ PSD_DUOTONE_HALFTONE = 1014, /* 0x03f6 - Duotone halftoning info */
+ PSD_GREY_XFER = 1015, /* 0x03f7 - Greyscale and multichannel transfer functions */
+ PSD_COLOR_XFER = 1016, /* 0x03f8 - Color transfer functions */
+ PSD_DUOTONE_XFER = 1017, /* 0x03f9 - Duotone transfer functions */
+ PSD_DUOTONE_INFO = 1018, /* 0x03fa - Duotone image information */
+ PSD_EFFECTIVE_BW = 1019, /* 0x03fb - Effective black & white values for dot range */
+ PSD_OBSOLETE_01 = 1020, /* 0x03fc - Obsolete */
+ PSD_EPS_OPT = 1021, /* 0x03fd - EPS options */
+ PSD_QUICK_MASK = 1022, /* 0x03fe - Quick mask info */
+ PSD_OBSOLETE_02 = 1023, /* 0x03ff - Obsolete */
+ PSD_LAYER_STATE = 1024, /* 0x0400 - Layer state info */
+ PSD_WORKING_PATH = 1025, /* 0x0401 - Working path (not saved) */
+ PSD_LAYER_GROUP = 1026, /* 0x0402 - Layers group info */
+ PSD_OBSOLETE_03 = 1027, /* 0x0403 - Obsolete */
+ PSD_IPTC_NAA_DATA = 1028, /* 0x0404 - IPTC-NAA record (IMV4.pdf) */
+ PSD_IMAGE_MODE_RAW = 1029, /* 0x0405 - Image mode for raw format files */
+ PSD_JPEG_QUAL = 1030, /* 0x0406 - JPEG quality */
+ PSD_GRID_GUIDE = 1032, /* 0x0408 - Grid & guide info */
+ PSD_THUMB_RES = 1033, /* 0x0409 - Thumbnail resource */
+ PSD_COPYRIGHT_FLG = 1034, /* 0x040a - Copyright flag */
+ PSD_URL = 1035, /* 0x040b - URL string */
+ PSD_THUMB_RES2 = 1036, /* 0x040c - Thumbnail resource */
+ PSD_GLOBAL_ANGLE = 1037, /* 0x040d - Superceded by PSD_NEW_COLOR_SAMPLER for ps CS3 and higher - Global angle */
+ PSD_COLOR_SAMPLER = 1038, /* 0x040e - Superceded by PSD_NEW_COLOR_SAMPLER for ps CS3 and higher - Color samplers resource */
+ PSD_ICC_PROFILE = 1039, /* 0x040f - ICC Profile */
+ PSD_WATERMARK = 1040, /* 0x0410 - Watermark */
+ PSD_ICC_UNTAGGED = 1041, /* 0x0411 - Do not use ICC profile flag */
+ PSD_EFFECTS_VISIBLE = 1042, /* 0x0412 - Show / hide all effects layers */
+ PSD_SPOT_HALFTONE = 1043, /* 0x0413 - Spot halftone */
+ PSD_DOC_IDS = 1044, /* 0x0414 - Document specific IDs */
+ PSD_ALPHA_NAMES_UNI = 1045, /* 0x0415 - Unicode alpha names */
+ PSD_IDX_COL_TAB_CNT = 1046, /* 0x0416 - Indexed color table count */
+ PSD_IDX_TRANSPARENT = 1047, /* 0x0417 - Index of transparent color (if any) */
+ PSD_GLOBAL_ALT = 1049, /* 0x0419 - Global altitude */
+ PSD_SLICES = 1050, /* 0x041a - Slices */
+ PSD_WORKFLOW_URL_UNI = 1051, /* 0x041b - Workflow URL - Unicode string */
+ PSD_JUMP_TO_XPEP = 1052, /* 0x041c - Jump to XPEP (?) */
+ PSD_ALPHA_ID = 1053, /* 0x041d - Alpha IDs */
+ PSD_URL_LIST_UNI = 1054, /* 0x041e - URL list - unicode */
+ PSD_VERSION_INFO = 1057, /* 0x0421 - Version info */
+ PSD_EXIF_DATA = 1058, /* 0x0422 - Exif data block 1 */
+ PSD_EXIF_DATA_3 = 1059, /* 0X0423 - Exif data block 3 (?) */
+ PSD_XMP_DATA = 1060, /* 0x0424 - XMP data block */
+ PSD_CAPTION_DIGEST = 1061, /* 0x0425 - Caption digest */
+ PSD_PRINT_SCALE = 1062, /* 0x0426 - Print scale */
+ PSD_PIXEL_AR = 1064, /* 0x0428 - Pixel aspect ratio */
+ PSD_LAYER_COMPS = 1065, /* 0x0429 - Layer comps */
+ PSD_ALT_DUOTONE_COLOR = 1066, /* 0x042A - Alternative Duotone colors */
+ PSD_ALT_SPOT_COLOR = 1067, /* 0x042B - Alternative Spot colors */
+ PSD_LAYER_SELECT_ID = 1069, /* 0x042D - Layer selection ID */
+ PSD_HDR_TONING_INFO = 1070, /* 0x042E - HDR toning information */
+ PSD_PRINT_INFO_SCALE = 1071, /* 0x042F - Print scale */
+ PSD_LAYER_GROUP_E_ID = 1072, /* 0x0430 - Layer group(s) enabled ID */
+ PSD_COLOR_SAMPLER_NEW = 1073, /* 0x0431 - Color sampler resource for ps CS3 and higher PSD files */
+ PSD_MEASURE_SCALE = 1074, /* 0x0432 - Measurement scale */
+ PSD_TIMELINE_INFO = 1075, /* 0x0433 - Timeline information */
+ PSD_SHEET_DISCLOSE = 1076, /* 0x0434 - Sheet discloser */
+ PSD_DISPLAY_INFO_NEW = 1077, /* 0x0435 - DisplayInfo structure for ps CS3 and higher PSD files */
+ PSD_ONION_SKINS = 1078, /* 0x0436 - Onion skins */
+ PSD_COUNT_INFO = 1080, /* 0x0438 - Count information*/
+ PSD_PRINT_INFO = 1082, /* 0x043A - Print information added in ps CS5*/
+ PSD_PRINT_STYLE = 1083, /* 0x043B - Print style */
+ PSD_MAC_NSPRINTINFO = 1084, /* 0x043C - Mac NSPrintInfo*/
+ PSD_WIN_DEVMODE = 1085, /* 0x043D - Windows DEVMODE */
+ PSD_AUTO_SAVE_PATH = 1086, /* 0x043E - Auto save file path */
+ PSD_AUTO_SAVE_FORMAT = 1087, /* 0x043F - Auto save format */
+ PSD_PATH_INFO_FIRST = 2000, /* 0x07d0 - First path info block */
+ PSD_PATH_INFO_LAST = 2998, /* 0x0bb6 - Last path info block */
+ PSD_CLIPPING_PATH = 2999, /* 0x0bb7 - Name of clipping path */
+ PSD_PLUGIN_R_FIRST = 4000, /* 0x0FA0 - First plugin resource */
+ PSD_PLUGIN_R_LAST = 4999, /* 0x1387 - Last plugin resource */
+ PSD_IMAGEREADY_VARS = 7000, /* 0x1B58 - Imageready variables */
+ PSD_IMAGEREADY_DATA = 7001, /* 0x1B59 - Imageready data sets */
+ PSD_LIGHTROOM_WORK = 8000, /* 0x1F40 - Lightroom workflow */
+ PSD_PRINT_FLAGS_2 = 10000 /* 0x2710 - Print flags */
+} PSDImageResID;
+
+/* Display resolution units */
+typedef enum {
+ PSD_RES_INCH = 1, /* Pixels / inch */
+ PSD_RES_CM = 2, /* Pixels / cm */
+} PSDDisplayResUnit;
+
+/* Width and height units */
+typedef enum {
+ PSD_UNIT_INCH = 1, /* inches */
+ PSD_UNIT_CM = 2, /* cm */
+ PSD_UNIT_POINT = 3, /* points (72 points = 1 inch) */
+ PSD_UNIT_PICA = 4, /* pica ( 6 pica = 1 inch) */
+ PSD_UNIT_COLUMN = 5, /* columns ( column defined in ps prefs, default = 2.5 inches) */
+} PSDUnit;
+
+/* Thumbnail image data encoding */
+typedef enum {
+ kRawRGB = 0, /* RAW data format (never used?) */
+ kJpegRGB = 1 /* JPEG compression */
+} PSDThumbFormat;
+
+/* Path record types */
+typedef enum {
+ PSD_PATH_CL_LEN = 0, /* Closed sub-path length record */
+ PSD_PATH_CL_LNK = 1, /* Closed sub-path Bezier knot, linked */
+ PSD_PATH_CL_UNLNK = 2, /* Closed sub-path Bezier knot, unlinked */
+ PSD_PATH_OP_LEN = 3, /* Open sub-path length record */
+ PSD_PATH_OP_LNK = 4, /* Open sub-path Bezier knot, linked */
+ PSD_PATH_OP_UNLNK = 5, /* Open sub-path Bezier knot, unlinked */
+ PSD_PATH_FILL_RULE = 6, /* Path fill rule record */
+ PSD_PATH_CLIPBOARD = 7, /* Path clipboard record */
+ PSD_PATH_FILL_INIT = 8 /* Path initial fill record */
+} PSDpathtype;
+
+/* Channel ID */
+typedef enum {
+ PSD_CHANNEL_EXTRA_MASK= -3, /* User supplied extra layer mask */
+ PSD_CHANNEL_MASK = -2, /* User supplied layer mask */
+ PSD_CHANNEL_ALPHA = -1, /* Transparency mask */
+ PSD_CHANNEL_RED = 0, /* Red channel data */
+ PSD_CHANNEL_GREEN = 1, /* Green channel data */
+ PSD_CHANNEL_BLUE = 2 /* Blue channel data */
+} PSDChannelID;
+
+/* Clipping */
+typedef enum {
+ PSD_CLIPPING_BASE = 0, /* Base clipping */
+ PSD_CLIPPING_NON_BASE = 1 /* Non-base clipping */
+} PSDClipping;
+
+/* Image compression mode */
+typedef enum {
+ PSD_COMP_RAW = 0, /* Raw data */
+ PSD_COMP_RLE, /* RLE compressed */
+ PSD_COMP_ZIP, /* ZIP without prediction */
+ PSD_COMP_ZIP_PRED /* ZIP with prediction */
+} PSDCompressMode;
+
+/* Vertical - horizontal selection */
+typedef enum {
+ PSD_VERTICAL = 0, /* Vertical */
+ PSD_HORIZONTAL = 1 /* Horizontal */
+} VHSelect;
+
+
+/* PSD spec data structures */
+
+/* PSD field types */
+typedef gint32 Fixed; /* Represents a fixed point implied decimal */
+
+
+/* Apple color space data structures */
+
+/* RGB Color Value
+ A color value expressed in the RGB color space is composed of red, green,
+ and blue component values. Each color component is expressed as a numeric
+ value within the range of 0 to 65535.
+*/
+typedef struct
+{
+ guint16 red;
+ guint16 green;
+ guint16 blue;
+} CMRGBColor;
+
+/* HSV Color Value
+ A color value expressed in the HSV color space is composed of hue,
+ saturation, and value component values. Each color component is
+ expressed as a numeric value within the range of 0 to 65535 inclusive.
+ The hue value represents a fraction of a circle in which red is
+ positioned at 0.
+*/
+
+typedef struct
+{
+ guint16 hue;
+ guint16 saturation;
+ guint16 value;
+} CMHSVColor;
+
+/* CMYK Color Value
+ A color value expressed in the CMYK color space is composed of cyan, magenta,
+ yellow, and black component values. Each color component is expressed as a
+ numeric value within the range of 0 to 65535 inclusive, with 0 representing
+ 100% ink (e.g. pure cyan = 0, 65535, 65535, 65535).
+*/
+
+typedef struct
+{
+ guint16 cyan;
+ guint16 magenta;
+ guint16 yellow;
+ guint16 black;
+} CMCMYKColor;
+
+/* L*a*b* Color Value
+ The first three values in the color data are, respectively, the colors
+ lightness, a chrominance, and b chrominance components. The lightness
+ component is a 16bit value ranging from 0 to 10000. The chrominance
+ components are each 16bit values ranging from 12800 to 12700. Gray
+ values are represented by chrominance components of 0 (e.g. pure white
+ is defined as 10000, 0, 0).
+*/
+typedef struct
+{
+ guint16 L;
+ gint16 a;
+ gint16 b;
+} CMLabColor;
+
+/* Gray Color Value
+ A color value expressed in the Gray color space is composed of a single component,
+ gray, represented as a numeric value within the range of 0 to 10000.
+*/
+typedef struct
+{
+ guint16 gray;
+} CMGrayColor ;
+
+/* The color union is defined by the CMColor type definition.
+ */
+typedef union
+{
+ CMRGBColor rgb;
+ CMHSVColor hsv;
+ CMLabColor Lab;
+ CMCMYKColor cmyk;
+ CMGrayColor gray;
+} CMColor;
+
+/* GIMP layer mode info */
+typedef struct
+{
+ GimpLayerMode mode;
+ GimpLayerColorSpace blend_space;
+ GimpLayerColorSpace composite_space;
+ GimpLayerCompositeMode composite_mode;
+} LayerModeInfo;
+
+/* Image resolution data */
+typedef struct {
+ Fixed hRes; /* Horizontal resolution pixels/inch */
+ gint16 hResUnit; /* Horizontal display resolution unit (1=pixels per inch, 2=pixels per cm) */
+ gint16 widthUnit; /* Display width unit (1=inches; 2=cm; 3=points; 4=picas; 5=columns) */
+ Fixed vRes; /* Vertical resolution pixels/inch */
+ gint16 vResUnit; /* Vertical display resolution unit */
+ gint16 heightUnit; /* Display height unit */
+} ResolutionInfo;
+
+/* Grid & guide header */
+typedef struct {
+ guint32 fVersion; /* Version - always 1 for PS */
+ guint32 fGridCycleV; /* Vertical grid size */
+ guint32 fGridCycleH; /* Horizontal grid size */
+ guint32 fGuideCount; /* Number of guides */
+} GuideHeader;
+
+/* Guide resource block */
+typedef struct {
+ guint32 fLocation; /* Guide position in Pixels * 100 */
+ gchar fDirection; /* Guide orientation */
+} GuideResource;
+
+/* Thumbnail data */
+typedef struct {
+ gint32 format; /* Thumbnail image data format (1 = JPEG) */
+ gint32 width; /* Thumbnail width in pixels */
+ gint32 height; /* Thumbnail height in pixels */
+ gint32 widthbytes; /* Padded row bytes ((width * bitspixel + 31) / 32 * 4) */
+ gint32 size; /* Total size (widthbytes * height * planes */
+ gint32 compressedsize; /* Size after compression for consistency */
+ gint16 bitspixel; /* Bits per pixel (always 24) */
+ gint16 planes; /* Number of planes (always 1) */
+} ThumbnailInfo;
+
+/* Channel display info data for Adobe Photoshop CS2 and lower */
+typedef struct {
+ gint16 colorSpace; /* Color space from PSDColorSpace */
+ guint16 color[4]; /* 4 * 16 bit color components */
+ gint16 opacity; /* Opacity 0 to 100 */
+ gchar kind; /* Selected = 0, Protected = 1 */
+ gchar padding; /* Padding */
+} DisplayInfo;
+
+/* Channel display info data for Adobe Photoshop CS3 and higher to support floating point colors */
+typedef struct {
+ gint16 colorSpace; /* Color space from PSDColorSpace */
+ guint16 color[4]; /* 4 * 16 bit color components */
+ gint16 opacity; /* Opacity 0 to 100 */
+ gchar mode; /* Alpha = 0, Inverted alpha = 1, Spot = 2 */
+} DisplayInfoNew;
+
+/* PSD Channel length info data structure */
+typedef struct
+{
+ gint16 channel_id; /* Channel ID */
+ guint32 data_len; /* Layer left */
+} ChannelLengthInfo;
+
+/* PSD Layer flags */
+typedef struct
+{
+ gboolean trans_prot; /* Transparency protected */
+ gboolean visible; /* Visible */
+ gboolean obsolete; /* Obsolete */
+ gboolean bit4; /* Bit 4 in use */
+ gboolean irrelevant; /* Pixel data irrelevant to image appearance */
+} LayerFlags;
+
+/* PSD Layer mask flags */
+typedef struct
+{
+ gboolean relative_pos; /* Mask position recorded relative to layer */
+ gboolean disabled; /* Mask disabled */
+ gboolean invert; /* Invert mask on blending (obsolete according to online specs) */
+ gboolean rendered; /* User mask actually came from rendering other data */
+ gboolean params_present; /* User and/or vector masks have parameters applied to them */
+} MaskFlags;
+
+/* PSD Slices */
+typedef struct
+{
+ gint32 id; /* ID */
+ gint32 groupid; /* Group ID */
+ gint32 origin; /* Origin */
+ gint32 associatedid; /* Associated Layer ID */
+ gchar *name; /* Name */
+ gint32 type; /* Type */
+ gint32 left; /* Position coordinates */
+ gint32 top;
+ gint32 right;
+ gint32 bottom;
+ gchar *url; /* URL */
+ gchar *target; /* Target */
+ gchar *message; /* Message */
+ gchar *alttag; /* Alt Tag */
+ gchar html; /* Boolean for if cell text is HTML */
+ gchar *celltext; /* Cell text */
+ gint32 horizontal; /* Horizontal alignment */
+ gint32 vertical; /* Vertical alignment */
+ gchar alpha; /* Alpha */
+ gchar red; /* Red */
+ gchar green; /* Green */
+ gchar blue; /* Blue */
+} PSDSlice;
+
+/* PSD Layer mask data (length 20) */
+typedef struct
+{
+ gint32 top; /* Layer top */
+ gint32 left; /* Layer left */
+ gint32 bottom; /* Layer bottom */
+ gint32 right; /* Layer right */
+ guchar def_color; /* Default background color */
+ guchar flags; /* Layer flags */
+ guchar mask_params; /* Mask parameters. Only present if bit 4 of flags is set. */
+ guchar extra_flags; /* Real layer flags */
+ guchar extra_def_color; /* Real user mask background */
+ MaskFlags mask_flags; /* Flags */
+} LayerMask;
+
+/* PSD Layer mask data (length 36) */
+typedef struct
+{
+ gint32 top; /* Layer top */
+ gint32 left; /* Layer left */
+ gint32 bottom; /* Layer bottom */
+ gint32 right; /* Layer right */
+} LayerMaskExtra;
+
+/* PSD text reading */
+typedef struct
+{
+ gdouble xx; /* Transform information */
+ gdouble xy;
+ gdouble yx;
+ gdouble yy;
+ gdouble tx;
+ gdouble ty;
+ gchar *info; /* Text information */
+} PSDText;
+
+/* PSD Layer data structure */
+typedef struct
+{
+ gboolean drop; /* Do not add layer to GIMP image */
+ gint32 top; /* Layer top */
+ gint32 left; /* Layer left */
+ gint32 bottom; /* Layer bottom */
+ gint32 right; /* Layer right */
+ guint16 num_channels; /* Number of channels */
+ ChannelLengthInfo *chn_info; /* Channel length info */
+ gchar mode_key[4]; /* Blend mode key */
+ gchar blend_mode[4]; /* Blend mode */
+ guchar opacity; /* Opacity - 0 = transparent ... 255 = opaque */
+ guchar clipping; /* Clipping */
+ guchar clipping_group_type; /* Used to track group needed for clipping (1 = group start, 2 = group end) */
+ guchar flags; /* Layer flags */
+ guchar filler; /* Filler */
+ guint32 extra_len; /* Extra data length */
+ gchar *name; /* Layer name */
+ guint32 mask_len; /* Layer mask data length */
+ LayerMask layer_mask; /* Layer mask data */
+ LayerMaskExtra layer_mask_extra; /* Layer mask extra data */
+ LayerFlags layer_flags; /* Layer flags */
+ PSDText text; /* PSD text */
+ guint32 id; /* Layer ID (Tattoo) */
+ guchar group_type; /* 0 -> not a group; 1 -> open folder; 2 -> closed folder; 3 -> end of group */
+ guint16 color_tag[4]; /* 4 * 16 bit color components */
+} PSDlayer;
+
+/* PSD Channel data structure */
+typedef struct
+{
+ gint16 id; /* Channel ID */
+ gchar *name; /* Channel name */
+ gchar *data; /* Channel image data */
+ guint32 rows; /* Channel rows */
+ guint32 columns; /* Channel columns */
+} PSDchannel;
+
+/* PSD Channel data structure */
+typedef struct
+{
+ GimpRGB gimp_color; /* Gimp RGB color */
+ gint16 opacity; /* Opacity */
+ guchar ps_mode; /* PS mode flag */
+ guchar ps_kind; /* PS type flag */
+ gint16 ps_cspace; /* PS color space */
+ CMColor ps_color; /* PS color */
+} PSDchanneldata;
+
+/* PSD Image Resource data structure */
+typedef struct
+{
+ gchar type[4]; /* Image resource type */
+ gint16 id; /* Image resource ID */
+ gchar name[256]; /* Image resource name (pascal string) */
+ guint32 data_start; /* Image resource data start */
+ guint32 data_len; /* Image resource data length */
+} PSDimageres;
+
+/* PSD Layer Resource data structure */
+typedef struct
+{
+ gchar sig[4]; /* Layer resource signature */
+ gchar key[4]; /* Layer resource key */
+ guint32 data_start; /* Layer resource data start */
+ guint32 data_len; /* Layer resource data length */
+} PSDlayerres;
+
+/* PSD File data structures */
+typedef struct
+{
+ gboolean merged_image_only; /* Whether to load only the merged image data */
+
+ guint16 channels; /* Number of channels: 1- 56 */
+ gboolean transparency; /* Image has merged transparency alpha channel */
+ guint32 rows; /* Number of rows: 1 - 30000 */
+ guint32 columns; /* Number of columns: 1 - 30000 */
+ guint16 bps; /* Bits per sample: 1, 8, 16, or 32 */
+ guint16 color_mode; /* Image color mode: {PSDColorMode} */
+ GimpImageBaseType base_type; /* Image base color mode: (GIMP) */
+ guint16 comp_mode; /* Merged image compression mode */
+ guchar *color_map; /* Color map data */
+ guint32 color_map_len; /* Color map data length */
+ guint32 color_map_entries; /* Color map number of entries */
+ guint32 image_res_start; /* Image resource block start address */
+ guint32 image_res_len; /* Image resource block length */
+ guint32 mask_layer_start; /* Mask & layer block start address */
+ guint32 mask_layer_len; /* Mask & layer block length */
+ gint16 num_layers; /* Number of layers */
+ guint32 layer_data_start; /* Layer pixel data start */
+ guint32 layer_data_len; /* Layer pixel data length */
+ guint32 merged_image_start; /* Merged image pixel data block start address */
+ guint32 merged_image_len; /* Merged image pixel data block length */
+ gboolean no_icc; /* Do not use ICC profile */
+ guint16 layer_state; /* Active layer number counting from bottom up */
+ GPtrArray *alpha_names; /* Alpha channel names */
+ PSDchanneldata **alpha_display_info; /* Alpha channel display info */
+ guint16 alpha_display_count; /* Number of alpha channel display info recs */
+ guint32 *alpha_id; /* Alpha channel ids (tattoos) */
+ guint16 alpha_id_count; /* Number of alpha channel id items */
+ guint16 quick_mask_id; /* Channel number containing quick mask */
+
+ GimpColorProfile *cmyk_profile;
+ gpointer cmyk_transform;
+ gpointer cmyk_transform_alpha;
+} PSDimage;
+
+/* Public functions */
+
+
+#endif /* __PSD_H__ */
diff --git a/plug-ins/file-raw/Makefile.am b/plug-ins/file-raw/Makefile.am
new file mode 100644
index 0000000..934ae61
--- /dev/null
+++ b/plug-ins/file-raw/Makefile.am
@@ -0,0 +1,90 @@
+## Process this file with automake to produce Makefile.in
+
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+
+filerawdatadir = $(gimpdatadir)/file-raw
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+# if HAVE_WINDRES
+# include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+# file_darktable_RC = file-darktable.rc.o
+# file_rawtherapee_RC = file-rawtherapee.rc.o
+# file_raw_placeholder_RC = file-raw-placeholder.rc.o
+# endif
+
+AM_LDFLAGS = $(mwindows)
+
+dt_libexecdir = $(gimpplugindir)/plug-ins/file-darktable
+rt_libexecdir = $(gimpplugindir)/plug-ins/file-rawtherapee
+rp_libexecdir = $(gimpplugindir)/plug-ins/file-raw-placeholder
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(includedir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS)
+
+dt_libexec_PROGRAMS = file-darktable
+rt_libexec_PROGRAMS = file-rawtherapee
+rp_libexec_PROGRAMS = file-raw-placeholder
+
+file_darktable_SOURCES = \
+ file-darktable.c \
+ file-raw-formats.h \
+ file-raw-utils.c \
+ file-raw-utils.h
+
+file_rawtherapee_SOURCES = \
+ file-rawtherapee.c \
+ file-raw-formats.h \
+ file-raw-utils.c \
+ file-raw-utils.h
+
+file_raw_placeholder_SOURCES = \
+ file-raw-formats.h \
+ file-raw-placeholder.c
+
+file_darktable_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_darktable_RC)
+
+file_rawtherapee_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_rawtherapee_RC)
+
+file_raw_placeholder_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_raw_placeholder_RC)
+
+filerawdata_DATA = \
+ file-darktable-export-on-exit.lua \
+ file-darktable-get-size.lua
+
+EXTRA_DIST = $(filerawdata_DATA)
diff --git a/plug-ins/file-raw/Makefile.in b/plug-ins/file-raw/Makefile.in
new file mode 100644
index 0000000..5162ad1
--- /dev/null
+++ b/plug-ins/file-raw/Makefile.in
@@ -0,0 +1,1220 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+dt_libexec_PROGRAMS = file-darktable$(EXEEXT)
+rt_libexec_PROGRAMS = file-rawtherapee$(EXEEXT)
+rp_libexec_PROGRAMS = file-raw-placeholder$(EXEEXT)
+subdir = plug-ins/file-raw
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(dt_libexecdir)" \
+ "$(DESTDIR)$(rp_libexecdir)" "$(DESTDIR)$(rt_libexecdir)" \
+ "$(DESTDIR)$(filerawdatadir)"
+PROGRAMS = $(dt_libexec_PROGRAMS) $(rp_libexec_PROGRAMS) \
+ $(rt_libexec_PROGRAMS)
+am_file_darktable_OBJECTS = file-darktable.$(OBJEXT) \
+ file-raw-utils.$(OBJEXT)
+file_darktable_OBJECTS = $(am_file_darktable_OBJECTS)
+am__DEPENDENCIES_1 =
+file_darktable_DEPENDENCIES = $(libgimp) $(libgimpbase) \
+ $(libgimpcolor) $(libgimpconfig) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_file_raw_placeholder_OBJECTS = file-raw-placeholder.$(OBJEXT)
+file_raw_placeholder_OBJECTS = $(am_file_raw_placeholder_OBJECTS)
+file_raw_placeholder_DEPENDENCIES = $(libgimp) $(libgimpbase) \
+ $(libgimpcolor) $(libgimpconfig) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_file_rawtherapee_OBJECTS = file-rawtherapee.$(OBJEXT) \
+ file-raw-utils.$(OBJEXT)
+file_rawtherapee_OBJECTS = $(am_file_rawtherapee_OBJECTS)
+file_rawtherapee_DEPENDENCIES = $(libgimp) $(libgimpbase) \
+ $(libgimpcolor) $(libgimpconfig) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file-darktable.Po \
+ ./$(DEPDIR)/file-raw-placeholder.Po \
+ ./$(DEPDIR)/file-raw-utils.Po ./$(DEPDIR)/file-rawtherapee.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_darktable_SOURCES) $(file_raw_placeholder_SOURCES) \
+ $(file_rawtherapee_SOURCES)
+DIST_SOURCES = $(file_darktable_SOURCES) \
+ $(file_raw_placeholder_SOURCES) $(file_rawtherapee_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+DATA = $(filerawdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+filerawdatadir = $(gimpdatadir)/file-raw
+@OS_WIN32_TRUE@mwindows = -mwindows
+
+# if HAVE_WINDRES
+# include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+# file_darktable_RC = file-darktable.rc.o
+# file_rawtherapee_RC = file-rawtherapee.rc.o
+# file_raw_placeholder_RC = file-raw-placeholder.rc.o
+# endif
+AM_LDFLAGS = $(mwindows)
+dt_libexecdir = $(gimpplugindir)/plug-ins/file-darktable
+rt_libexecdir = $(gimpplugindir)/plug-ins/file-rawtherapee
+rp_libexecdir = $(gimpplugindir)/plug-ins/file-raw-placeholder
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(includedir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS)
+
+file_darktable_SOURCES = \
+ file-darktable.c \
+ file-raw-formats.h \
+ file-raw-utils.c \
+ file-raw-utils.h
+
+file_rawtherapee_SOURCES = \
+ file-rawtherapee.c \
+ file-raw-formats.h \
+ file-raw-utils.c \
+ file-raw-utils.h
+
+file_raw_placeholder_SOURCES = \
+ file-raw-formats.h \
+ file-raw-placeholder.c
+
+file_darktable_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_darktable_RC)
+
+file_rawtherapee_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_rawtherapee_RC)
+
+file_raw_placeholder_LDADD = \
+ $(libgimp) \
+ $(libgimpbase) \
+ $(libgimpcolor) \
+ $(libgimpconfig) \
+ $(CAIRO_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_raw_placeholder_RC)
+
+filerawdata_DATA = \
+ file-darktable-export-on-exit.lua \
+ file-darktable-get-size.lua
+
+EXTRA_DIST = $(filerawdata_DATA)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-raw/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-raw/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-dt_libexecPROGRAMS: $(dt_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(dt_libexec_PROGRAMS)'; test -n "$(dt_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(dt_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(dt_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(dt_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(dt_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dt_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dt_libexec_PROGRAMS)'; test -n "$(dt_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(dt_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(dt_libexecdir)" && rm -f $$files
+
+clean-dt_libexecPROGRAMS:
+ @list='$(dt_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-rp_libexecPROGRAMS: $(rp_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(rp_libexec_PROGRAMS)'; test -n "$(rp_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(rp_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(rp_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(rp_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(rp_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-rp_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(rp_libexec_PROGRAMS)'; test -n "$(rp_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(rp_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(rp_libexecdir)" && rm -f $$files
+
+clean-rp_libexecPROGRAMS:
+ @list='$(rp_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-rt_libexecPROGRAMS: $(rt_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(rt_libexec_PROGRAMS)'; test -n "$(rt_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(rt_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(rt_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(rt_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(rt_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-rt_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(rt_libexec_PROGRAMS)'; test -n "$(rt_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(rt_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(rt_libexecdir)" && rm -f $$files
+
+clean-rt_libexecPROGRAMS:
+ @list='$(rt_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-darktable$(EXEEXT): $(file_darktable_OBJECTS) $(file_darktable_DEPENDENCIES) $(EXTRA_file_darktable_DEPENDENCIES)
+ @rm -f file-darktable$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_darktable_OBJECTS) $(file_darktable_LDADD) $(LIBS)
+
+file-raw-placeholder$(EXEEXT): $(file_raw_placeholder_OBJECTS) $(file_raw_placeholder_DEPENDENCIES) $(EXTRA_file_raw_placeholder_DEPENDENCIES)
+ @rm -f file-raw-placeholder$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_raw_placeholder_OBJECTS) $(file_raw_placeholder_LDADD) $(LIBS)
+
+file-rawtherapee$(EXEEXT): $(file_rawtherapee_OBJECTS) $(file_rawtherapee_DEPENDENCIES) $(EXTRA_file_rawtherapee_DEPENDENCIES)
+ @rm -f file-rawtherapee$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_rawtherapee_OBJECTS) $(file_rawtherapee_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-darktable.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-raw-placeholder.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-raw-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-rawtherapee.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-filerawdataDATA: $(filerawdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(filerawdata_DATA)'; test -n "$(filerawdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(filerawdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(filerawdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(filerawdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(filerawdatadir)" || exit $$?; \
+ done
+
+uninstall-filerawdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(filerawdata_DATA)'; test -n "$(filerawdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(filerawdatadir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(dt_libexecdir)" "$(DESTDIR)$(rp_libexecdir)" "$(DESTDIR)$(rt_libexecdir)" "$(DESTDIR)$(filerawdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-dt_libexecPROGRAMS clean-generic clean-libtool \
+ clean-rp_libexecPROGRAMS clean-rt_libexecPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file-darktable.Po
+ -rm -f ./$(DEPDIR)/file-raw-placeholder.Po
+ -rm -f ./$(DEPDIR)/file-raw-utils.Po
+ -rm -f ./$(DEPDIR)/file-rawtherapee.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-filerawdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-dt_libexecPROGRAMS install-rp_libexecPROGRAMS \
+ install-rt_libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file-darktable.Po
+ -rm -f ./$(DEPDIR)/file-raw-placeholder.Po
+ -rm -f ./$(DEPDIR)/file-raw-utils.Po
+ -rm -f ./$(DEPDIR)/file-rawtherapee.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dt_libexecPROGRAMS uninstall-filerawdataDATA \
+ uninstall-rp_libexecPROGRAMS uninstall-rt_libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-dt_libexecPROGRAMS clean-generic clean-libtool \
+ clean-rp_libexecPROGRAMS clean-rt_libexecPROGRAMS \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dt_libexecPROGRAMS \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-filerawdataDATA install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am \
+ install-rp_libexecPROGRAMS install-rt_libexecPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-dt_libexecPROGRAMS uninstall-filerawdataDATA \
+ uninstall-rp_libexecPROGRAMS uninstall-rt_libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-raw/file-darktable-export-on-exit.lua b/plug-ins/file-raw/file-darktable-export-on-exit.lua
new file mode 100644
index 0000000..79b27dc
--- /dev/null
+++ b/plug-ins/file-raw/file-darktable-export-on-exit.lua
@@ -0,0 +1,88 @@
+--[[
+ This file is part of GIMP,
+ copyright (c) 2015-2017 Tobias Ellinghaus
+
+ GIMP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ GIMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GIMP. If not, see <https://www.gnu.org/licenses/>.
+]]
+
+--[[
+EXPORT ON EXIT
+exports all (but at most 1) images from the database to prefs setting "lua/export_on_exit/export_filename"
+when darktable exits
+
+
+USAGE
+* require this file from your main lua config file
+* or: use --luacmd "dofile('/path/to/this/file.lua')"
+* and make sure to set the export filename
+
+]]
+
+local dt = require "darktable"
+
+local orig_register_event = dt.register_event
+
+function dt.register_event(name, event, func, label)
+ if dt.configuration.api_version_string >= "6.2.1" then
+ if label then
+ orig_register_event(name, event, func, label)
+ else
+ orig_register_event(name, event, func)
+ end
+ else
+ if label then
+ orig_register_event(event, func, label)
+ else
+ orig_register_event(event, func)
+ end
+ end
+end
+
+local min_api_version = "2.1.0"
+if dt.configuration.api_version_string < min_api_version then
+ dt.print("the exit export script requires at least darktable version 1.7.0")
+ dt.print_error("the exit export script requires at least darktable version 1.7.0")
+ return
+else
+ dt.print("closing darktable will export the image and make GIMP load it")
+end
+
+local CURR_API_STRING = dt.configuration.api_version_string
+
+local export_filename = dt.preferences.read("export_on_exit", "export_filename", "string")
+
+dt.register_event("fileraw", "exit", function()
+ -- safegurad against someone using this with their library containing 50k images
+ if #dt.database > 1 then
+ dt.print_error("too many images, only exporting the first")
+-- return
+ end
+
+ -- change the view first to force writing of the history stack
+ dt.gui.current_view(dt.gui.views.lighttable)
+ -- now export
+ local format = dt.new_format("exr")
+ format.max_width = 0
+ format.max_height = 0
+ -- let's have the export in a loop so we could easily support > 1 images
+ for _, image in ipairs(dt.database) do
+ dt.print_error("exporting `"..tostring(image).."' to `"..export_filename.."'")
+ format:write_image(image, export_filename)
+ break -- only export one image. see above for the reason
+ end
+end)
+
+--
+-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua
+-- kate: hl Lua;
diff --git a/plug-ins/file-raw/file-darktable-get-size.lua b/plug-ins/file-raw/file-darktable-get-size.lua
new file mode 100644
index 0000000..c82ed44
--- /dev/null
+++ b/plug-ins/file-raw/file-darktable-get-size.lua
@@ -0,0 +1,21 @@
+--[[
+ This file is part of GIMP,
+ copyright (c) 2016 Tobias Ellinghaus
+
+ GIMP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ GIMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GIMP. If not, see <https://www.gnu.org/licenses/>.
+]]
+
+local dt = require "darktable"
+
+print("[dt4gimp] " .. (dt.database[1].width) .. " " .. (dt.database[1].height))
diff --git a/plug-ins/file-raw/file-darktable.c b/plug-ins/file-raw/file-darktable.c
new file mode 100644
index 0000000..b5897c8
--- /dev/null
+++ b/plug-ins/file-raw/file-darktable.c
@@ -0,0 +1,550 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-darktable.c -- raw file format plug-in that uses darktable
+ * Copyright (C) 2012 Simon Budig <simon@gimp.org>
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "file-raw-formats.h"
+#include "file-raw-utils.h"
+
+
+#define LOAD_THUMB_PROC "file-darktable-load-thumb"
+#define REGISTRY_KEY_BASE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\darktable"
+
+
+static void init (void);
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error);
+
+static gint32 load_thumbnail_image (const gchar *filename,
+ gint thumb_size,
+ gint *width,
+ gint *height,
+ GError **error);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ init, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+init (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ /* check if darktable is installed
+ */
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("darktable", NULL,
+ "DARKTABLE_EXECUTABLE",
+ "org.darktable",
+ REGISTRY_KEY_BASE,
+ &search_path);
+ gchar *argv[] = { exec_path, "--version", NULL };
+ gchar *darktable_stdout = NULL;
+ gchar *darktable_stderr = NULL;
+ gboolean have_darktable = FALSE;
+ GError *error = NULL;
+ gint i;
+
+ /* allow the user to have some insight into why darktable may fail. */
+ gboolean debug_prints = g_getenv ("DARKTABLE_DEBUG") != NULL;
+
+ if (debug_prints)
+ g_printf ("[%s] trying to call '%s'\n", __FILE__, exec_path);
+
+ if (g_spawn_sync (NULL,
+ argv,
+ NULL,
+ (search_path ? G_SPAWN_SEARCH_PATH : 0),
+ NULL,
+ NULL,
+ &darktable_stdout,
+ &darktable_stderr,
+ NULL,
+ &error))
+ {
+ GRegex *regex;
+ GMatchInfo *matches;
+ gint major;
+ gint minor;
+
+ /* A default darktable would apparently output something like
+ * "this is darktable 2.2.5", but this version string is
+ * customizable. In the official Fedora package for instance, I
+ * encountered a "this is darktable darktable-2.2.5-4.fc27".
+ * Therefore make the version recognition a bit more flexible.
+ */
+ regex = g_regex_new ("this is darktable [^0-9]*([0-9]+)\\.([0-9]+)\\.([0-9]+)",
+ 0, 0, NULL);
+ if (g_regex_match (regex, darktable_stdout, 0, &matches))
+ {
+ gchar *match;
+
+ match = g_match_info_fetch (matches, 1);
+ major = g_ascii_strtoll (match, NULL, 10);
+ g_free (match);
+
+ match = g_match_info_fetch (matches, 2);
+ minor = g_ascii_strtoll (match, NULL, 10);
+ g_free (match);
+
+ if (((major == 1 && minor >= 7) || major >= 2))
+ {
+ if (g_strstr_len (darktable_stdout, -1,
+ "Lua support enabled"))
+ {
+ have_darktable = TRUE;
+ }
+ }
+ }
+
+ g_match_info_free (matches);
+ g_regex_unref (regex);
+ }
+ else if (debug_prints)
+ {
+ g_printf ("[%s] g_spawn_sync failed\n", __FILE__);
+ }
+
+ if (debug_prints)
+ {
+ if (error)
+ g_printf ("[%s] error: %s\n", __FILE__, error->message);
+
+ if (darktable_stdout && *darktable_stdout)
+ g_printf ("[%s] stdout:\n%s\n", __FILE__, darktable_stdout);
+
+ if (darktable_stderr && *darktable_stderr)
+ g_printf ("[%s] stderr:\n%s\n", __FILE__, darktable_stderr);
+
+ g_printf ("[%s] have_darktable: %d\n", __FILE__, have_darktable);
+ }
+
+ g_clear_error (&error);
+
+ g_free (darktable_stdout);
+ g_free (darktable_stderr);
+ g_free (exec_path);
+
+ if (! have_darktable)
+ return;
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Load thumbnail from a raw image via darktable",
+ "This plug-in loads a thumbnail from a raw image by calling darktable-cli.",
+ "Tobias Ellinghaus",
+ "Tobias Ellinghaus",
+ "2016",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc;
+ gchar *load_blurb;
+ gchar *load_help;
+
+ load_proc = g_strdup_printf (format->load_proc_format, "darktable");
+ load_blurb = g_strdup_printf (format->load_blurb_format, "darktable");
+ load_help = g_strdup_printf (format->load_help_format, "darktable");
+
+ gimp_install_procedure (load_proc,
+ load_blurb,
+ load_help,
+ "Tobias Ellinghaus",
+ "Tobias Ellinghaus",
+ "2016",
+ format->file_type,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (load_proc,
+ format->mime_type);
+ gimp_register_file_handler_raw (load_proc);
+ gimp_register_magic_load_handler (load_proc,
+ format->extensions,
+ "",
+ format->magic);
+
+ gimp_register_thumbnail_loader (load_proc, LOAD_THUMB_PROC);
+
+ g_free (load_proc);
+ g_free (load_blurb);
+ g_free (load_help);
+ }
+}
+
+static void
+query (void)
+{
+ /* query() is run only the first time for efficiency. Yet this plugin
+ * is dependent on the presence of darktable which may be installed
+ * or uninstalled between GIMP startups. Therefore we should move the
+ * usual gimp_install_procedure() to init() so that the check is done
+ * at every startup instead.
+ */
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint image_ID;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* check if the format passed is actually supported & load */
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc = NULL;
+
+ if (format->load_proc_format)
+ load_proc = g_strdup_printf (format->load_proc_format, "darktable");
+
+ if (load_proc && ! strcmp (name, load_proc))
+ {
+ image_ID = load_image (param[1].data.d_string, run_mode, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ break;
+ }
+ else if (! strcmp (name, LOAD_THUMB_PROC))
+ {
+ gint width = 0;
+ gint height = 0;
+
+ image_ID = load_thumbnail_image (param[0].data.d_string,
+ param[1].data.d_int32,
+ &width,
+ &height,
+ &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 6;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = GIMP_RGB_IMAGE;
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = 1; /* num_layers */
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (file_formats))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ gint32 image_ID = -1;
+ GFile *lua_file = gimp_data_directory_file ("file-raw",
+ "file-darktable-export-on-exit.lua",
+ NULL);
+ gchar *lua_script = g_file_get_path (lua_file);
+ gchar *lua_script_escaped = g_strescape (lua_script, "");
+ gchar *lua_quoted = g_shell_quote (lua_script_escaped);
+ gchar *lua_cmd = g_strdup_printf ("dofile(%s)", lua_quoted);
+ gchar *filename_out = gimp_temp_name ("exr");
+ gchar *export_filename = g_strdup_printf ("lua/export_on_exit/export_filename=%s",
+ filename_out);
+
+ gchar *darktable_stdout = NULL;
+ gchar *darktable_stderr = NULL;
+
+ /* allow the user to have some insight into why darktable may fail. */
+ gboolean debug_prints = g_getenv ("DARKTABLE_DEBUG") != NULL;
+
+ /* linear sRGB for now as GIMP uses that internally in many places anyway */
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("darktable", NULL,
+ "DARKTABLE_EXECUTABLE",
+ "org.darktable",
+ REGISTRY_KEY_BASE,
+ &search_path);
+ gchar *argv[] =
+ {
+ exec_path,
+ "--library", ":memory:",
+ "--luacmd", lua_cmd,
+ "--conf", "plugins/lighttable/export/icctype=3",
+ "--conf", export_filename,
+ (gchar *) filename,
+ NULL
+ };
+
+ g_object_unref (lua_file);
+ g_free (lua_script);
+ g_free (lua_script_escaped);
+ g_free (lua_quoted);
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if (debug_prints)
+ {
+ gchar **environ_ = g_get_environ ();
+ gint i;
+
+ g_printf ("[%s] trying to call\n", __FILE__);
+ for (gchar **iter = argv; *iter; iter++)
+ g_printf (" %s\n", *iter);
+ g_printf ("\n");
+
+ g_printf ("## Environment ##\n");
+ for (i = 0; environ_[i]; i++)
+ g_printf ("- %s\n", environ_[i]);
+ g_strfreev (environ_) ;
+ }
+
+ if (g_spawn_sync (NULL,
+ argv,
+ NULL,
+ /*G_SPAWN_STDOUT_TO_DEV_NULL |*/
+ /*G_SPAWN_STDERR_TO_DEV_NULL |*/
+ (search_path ? G_SPAWN_SEARCH_PATH : 0),
+ NULL,
+ NULL,
+ &darktable_stdout,
+ &darktable_stderr,
+ NULL,
+ error))
+ {
+ image_ID = gimp_file_load (run_mode, filename_out, filename_out);
+ if (image_ID != -1)
+ gimp_image_set_filename (image_ID, filename);
+ }
+
+ if (debug_prints)
+ {
+ if (darktable_stdout && *darktable_stdout)
+ g_printf ("\n## stdout ##\n%s\n", darktable_stdout);
+
+ if (darktable_stderr && *darktable_stderr)
+ g_printf ("\n## stderr ##\n%s\n", darktable_stderr);
+ }
+
+ g_free (darktable_stdout);
+ g_free (darktable_stderr);
+
+ g_unlink (filename_out);
+ g_free (lua_cmd);
+ g_free (filename_out);
+ g_free (export_filename);
+ g_free (exec_path);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+static gint32
+load_thumbnail_image (const gchar *filename,
+ gint thumb_size,
+ gint *width,
+ gint *height,
+ GError **error)
+{
+ gint32 image_ID = -1;
+ gchar *filename_out = gimp_temp_name ("jpg");
+ gchar *size = g_strdup_printf ("%d", thumb_size);
+ GFile *lua_file = gimp_data_directory_file ("file-raw",
+ "file-darktable-get-size.lua",
+ NULL);
+ gchar *lua_script = g_file_get_path (lua_file);
+ gchar *lua_script_escaped = g_strescape (lua_script, "");
+ gchar *lua_quoted = g_shell_quote (lua_script_escaped);
+ gchar *lua_cmd = g_strdup_printf ("dofile(%s)", lua_quoted);
+ gchar *darktable_stdout = NULL;
+
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("darktable", "-cli",
+ "DARKTABLE_EXECUTABLE",
+ "org.darktable",
+ REGISTRY_KEY_BASE,
+ &search_path);
+ gchar *argv[] =
+ {
+ exec_path,
+ (gchar *) filename, filename_out,
+ "--width", size,
+ "--height", size,
+ "--hq", "false",
+ "--core",
+ "--conf", "plugins/lighttable/export/icctype=3",
+ "--luacmd", lua_cmd,
+ NULL
+ };
+
+ g_object_unref (lua_file);
+ g_free (lua_script);
+ g_free (lua_script_escaped);
+ g_free (lua_quoted);
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ *width = *height = thumb_size;
+
+ if (g_spawn_sync (NULL,
+ argv,
+ NULL,
+ G_SPAWN_STDERR_TO_DEV_NULL |
+ (search_path ? G_SPAWN_SEARCH_PATH : 0),
+ NULL,
+ NULL,
+ &darktable_stdout,
+ NULL,
+ NULL,
+ error))
+ {
+ gimp_progress_update (0.5);
+
+ image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ filename_out,
+ filename_out);
+ if (image_ID != -1)
+ {
+ /* the size reported by raw files isn't precise,
+ * but it should be close enough to get an idea.
+ */
+ gchar *start_of_size = g_strstr_len (darktable_stdout,
+ -1,
+ "[dt4gimp]");
+ if (start_of_size)
+ sscanf (start_of_size, "[dt4gimp] %d %d", width, height);
+
+ /* is this needed for thumbnails? */
+ gimp_image_set_filename (image_ID, filename);
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ g_unlink (filename_out);
+ g_free (filename_out);
+ g_free (size);
+ g_free (lua_cmd);
+ g_free (darktable_stdout);
+ g_free (exec_path);
+
+ return image_ID;
+}
diff --git a/plug-ins/file-raw/file-raw-formats.h b/plug-ins/file-raw/file-raw-formats.h
new file mode 100644
index 0000000..672bb00
--- /dev/null
+++ b/plug-ins/file-raw/file-raw-formats.h
@@ -0,0 +1,320 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-darktable.c -- raw file format plug-in that uses darktable
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* These are the raw formats that file-darktable will register */
+
+typedef struct _FileFormat FileFormat;
+
+struct _FileFormat
+{
+ const gchar *file_type;
+ const gchar *mime_type;
+ const gchar *extensions;
+ const gchar *magic;
+
+ const gchar *load_proc_format;
+ const gchar *load_blurb_format;
+ const gchar *load_help_format;
+};
+
+/* some magic numbers taken from
+ * http://www.garykessler.net/library/file_sigs.html
+ *
+ * see also
+ * http://fileformats.archiveteam.org/wiki/Cameras_and_Digital_Image_Sensors
+ */
+static const FileFormat file_formats[] =
+{
+ {
+ N_("Raw Canon"),
+ "image/x-canon-cr2,image/x-canon-crw,image/x-canon-cr3",
+ "cr2,crw,cr3",
+ "0,string,II*\\0\\020\\0\\0\\0CR," /* cr2 */
+ "0,string,II\\024\\0\\0\\0HEAPCCDR," /* crw */
+ "4,string,ftypcrx ", /* cr3 */
+
+ "file-%s-canon-load",
+ "Load files in the Canon raw formats via %s",
+ "This plug-in loads files in Canon's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Nikon"),
+ "image/x-nikon-nef,image/x-nikon-nrw",
+ "nef,nrw",
+ NULL,
+
+ "file-%s-nikon-load",
+ "Load files in the Nikon raw formats via %s",
+ "This plug-in loads files in Nikon's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Hasselblad"),
+ "image/x-hasselblad-3fr,image/x-hasselblad-fff",
+ "3fr,fff",
+ NULL,
+
+ "file-%s-hasselblad-load",
+ "Load files in the Hasselblad raw formats via %s",
+ "This plug-in loads files in Hasselblad's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Sony"),
+ "image/x-sony-arw,image/x-sony-srf,image/x-sony-sr2",
+ "arw,srf,sr2",
+ NULL,
+
+ "file-%s-sony-load",
+ "Load files in the Sony raw formats via %s",
+ "This plug-in loads files in Sony's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Casio BAY"),
+ "image/x-casio-bay",
+ "bay",
+ NULL,
+
+ "file-%s-bay-load",
+ "Load files in the BAY raw format via %s",
+ "This plug-in loads files in Casio's raw BAY format by calling %s."
+ },
+
+ {
+ N_("Raw Phantom Software CINE"),
+ "", /* FIXME: find a mime type */
+ "cine,cin",
+ NULL,
+
+ "file-%s-cine-load",
+ "Load files in the CINE raw format via %s",
+ "This plug-in loads files in Phantom Software's raw CINE format by calling %s."
+ },
+
+ {
+ N_("Raw Sinar"),
+ "", /* FIXME: find a mime type */
+ "cs1,ia,sti",
+ NULL,
+
+ "file-%s-sinar-load",
+ "Load files in the Sinar raw formats via %s",
+ "This plug-in loads files in Sinar's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Kodak"),
+ "image/x-kodak-dc2,image/x-kodak-dcr,image/x-kodak-kdc,image/x-kodak-k25,image/x-kodak-kc2",
+ "dc2,dcr,kdc,k25,kc2",
+ NULL,
+
+ "file-%s-kodak-load",
+ "Load files in the Kodak raw formats via %s",
+ "This plug-in loads files in Kodak's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Adobe DNG Digital Negative"),
+ "image/x-adobe-dng",
+ "dng",
+ NULL,
+
+ "file-%s-dng-load",
+ "Load files in the DNG raw format via %s",
+ "This plug-in loads files in the Adobe Digital Negative DNG format by calling %s."
+ },
+
+ {
+ N_("Raw Epson ERF"),
+ "image/x-epson-erf",
+ "erf",
+ NULL,
+
+ "file-%s-erf-load",
+ "Load files in the ERF raw format via %s",
+ "This plug-in loads files in Epson's raw ERF format by calling %s."
+ },
+
+ {
+ N_("Raw Phase One"),
+ "image/x-phaseone-cap,image/x-phaseone-iiq",
+ "cap,iiq",
+ NULL,
+
+ "file-%s-phaseone-load",
+ "Load files in the Phase One raw formats via %s",
+ "This plug-in loads files in Phase One's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Minolta"),
+ "image/x-minolta-mdc,image/x-minolta-mrw",
+ "mdc,mrw",
+ NULL,
+
+ "file-%s-minolta-load",
+ "Load files in the Minolta raw formats via %s",
+ "This plug-in loads files in Minolta's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Mamiya MEF"),
+ "image/x-mamiya-mef",
+ "mef", NULL,
+
+ "file-%s-mef-load",
+ "Load files in the MEF raw format via %s",
+ "This plug-in loads files in Mamiya's raw MEF format by calling %s."
+ },
+
+ {
+ N_("Raw Leaf MOS"),
+ "image/x-leaf-mos",
+ "mos",
+ NULL,
+
+ "file-%s-mos-load",
+ "Load files in the MOS raw format via %s",
+ "This plug-in loads files in Leaf's raw MOS format by calling %s."
+ },
+
+ {
+ N_("Raw Olympus ORF"),
+ "image/x-olympus-orf",
+ "orf",
+ "0,string,IIRO,0,string,MMOR,0,string,IIRS",
+
+ "file-%s-orf-load",
+ "Load files in the ORF raw format via %s",
+ "This plug-in loads files in Olympus' raw ORF format by calling %s."
+ },
+
+ {
+ N_("Raw Pentax PEF"),
+ "image/x-pentax-pef,image/x-pentax-raw",
+ "pef,raw",
+ NULL,
+
+ "file-%s-pef-load",
+ "Load files in the PEF raw format via %s",
+ "This plug-in loads files in Pentax' raw PEF format by calling %s."
+ },
+
+ {
+ N_("Raw Logitech PXN"),
+ "image/x-pxn", /* FIXME: is that the correct mime type? */
+ "pxn",
+ NULL,
+
+ "file-%s-pxn-load",
+ "Load files in the PXN raw format via %s",
+ "This plug-in loads files in Logitech's raw PXN format by calling %s."
+ },
+
+ {
+ N_("Raw Apple QuickTake QTK"),
+ "", /* FIXME: find a mime type */
+ "qtk",
+ NULL,
+
+ "file-%s-qtk-load",
+ "Load files in the QTK raw format via %s",
+ "This plug-in loads files in Apple's QuickTake QTK raw format by calling %s."
+ },
+
+ {
+ N_("Raw Fujifilm RAF"),
+ "image/x-fuji-raf",
+ "raf",
+ "0,string,FUJIFILMCCD-RAW",
+
+ "file-%s-raf-load",
+ "Load files in the RAF raw format via %s",
+ "This plug-in loads files in Fujifilm's raw RAF format by calling %s."
+ },
+
+ {
+ N_("Raw Panasonic"),
+ "image/x-panasonic-raw,image/x-panasonic-rw2",
+ "raw,rw2",
+ "0,string,IIU\\0",
+
+ "file-%s-panasonic-load",
+ "Load files in the Panasonic raw formats via %s",
+ "This plug-in loads files in Panasonic's raw formats by calling %s."
+ },
+
+ {
+ N_("Raw Digital Foto Maker RDC"),
+ "", /* FIXME: find a mime type */
+ "rdc",
+ NULL,
+
+ "file-%s-rdc-load",
+ "Load files in the RDC raw format via %s",
+ "This plug-in loads files in Digital Foto Maker's raw RDC format by calling %s."
+ },
+
+ {
+ N_("Raw Leica RWL"),
+ "image/x-leica-rwl",
+ "rwl",
+ NULL,
+
+ "file-%s-rwl-load",
+ "Load files in the RWL raw format via %s",
+ "This plug-in loads files in Leica's raw RWL format by calling %s."
+ },
+
+ {
+ N_("Raw Samsung SRW"),
+ "image/x-samsung-srw",
+ "srw",
+ NULL,
+
+ "file-%s-srw-load",
+ "Load files in the SRW raw format via %s",
+ "This plug-in loads files in Samsung's raw SRW format by calling %s."
+ },
+
+ {
+ N_("Raw Sigma X3F"),
+ "image/x-sigma-x3f",
+ "x3f",
+ "0,string,FOVb",
+
+ "file-%s-x3f-load",
+ "Load files in the X3F raw format via %s",
+ "This plug-in loads files in Sigma's raw X3F format by calling %s."
+ },
+
+ {
+ N_("Raw Arriflex ARI"),
+ "",
+ "ari",
+ NULL,
+
+ "file-%s-ari-load",
+ "Load files in the ARI raw format via %s",
+ "This plug-in loads files in Arriflex' raw ARI format by calling %s."
+ }
+};
diff --git a/plug-ins/file-raw/file-raw-placeholder.c b/plug-ins/file-raw/file-raw-placeholder.c
new file mode 100644
index 0000000..b127da6
--- /dev/null
+++ b/plug-ins/file-raw/file-raw-placeholder.c
@@ -0,0 +1,163 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-raw-placeholder.c -- raw file format plug-in that does nothing
+ * except warning that there is no raw plug-in
+ * Copyright (C) 2017 Michael Natterer <mitch@gimp.org>
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "file-raw-formats.h"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc;
+ gchar *load_blurb;
+ gchar *load_help;
+
+ load_proc = g_strdup_printf (format->load_proc_format, "raw-placeholder");
+ load_blurb = g_strdup_printf (format->load_blurb_format, "raw-placeholder");
+ load_help = g_strdup_printf (format->load_help_format, "raw-placeholder");
+
+ gimp_install_procedure (load_proc,
+ load_blurb,
+ load_help,
+ "Tobias Ellinghaus",
+ "Tobias Ellinghaus",
+ "2016",
+ format->file_type,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (load_proc,
+ format->mime_type);
+ gimp_register_file_handler_raw (load_proc);
+ gimp_register_magic_load_handler (load_proc,
+ format->extensions,
+ "",
+ format->magic);
+
+ g_free (load_proc);
+ g_free (load_blurb);
+ g_free (load_help);
+ }
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* check if the format passed is actually supported & load */
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc = NULL;
+
+ if (format->load_proc_format)
+ load_proc = g_strdup_printf (format->load_proc_format, "raw-placeholder");
+
+ if (load_proc && ! strcmp (name, load_proc))
+ {
+ g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("There is no RAW loader installed to open '%s' files.\n"
+ "\n"
+ "GIMP currently supports these RAW loaders:\n"
+ "- darktable (http://www.darktable.org/), at least 1.7\n"
+ "- RawTherapee (http://rawtherapee.com/), at least 5.2\n"
+ "\n"
+ "Please install one of them in order to "
+ "load RAW files."),
+ gettext (format->file_type));
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (file_formats))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/file-raw/file-raw-utils.c b/plug-ins/file-raw/file-raw-utils.c
new file mode 100644
index 0000000..8847cd0
--- /dev/null
+++ b/plug-ins/file-raw/file-raw-utils.c
@@ -0,0 +1,153 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-raw-utils.h -- raw file format plug-in
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <CoreServices/CoreServices.h>
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+#include <windows.h>
+#endif
+
+#include <libgimp/gimp.h>
+
+#include "file-raw-utils.h"
+
+
+gchar *
+file_raw_get_executable_path (const gchar *main_executable,
+ const gchar *suffix,
+ const gchar *env_variable,
+ const gchar *mac_bundle_id,
+ const gchar *win32_registry_key_base,
+ gboolean *search_path)
+{
+ /*
+ * First check for the environment variable.
+ * Next do platform specific checks (bundle lookup on Mac, registry stuff
+ * on Windows).
+ * Last resort is hoping for the executable to be in PATH.
+ */
+
+ /*
+ * Look for env variable. That can be set directly or via an environ file.
+ * We assume that just appending the suffix to that value will work.
+ * That means that on Windows there should be no ".exe"!
+ */
+ const gchar *dt_env = env_variable ? g_getenv (env_variable) : NULL;
+
+ if (dt_env)
+ return g_strconcat (dt_env, suffix, NULL);
+
+#if defined (GDK_WINDOWING_QUARTZ)
+ if (mac_bundle_id)
+ {
+ CFStringRef bundle_id;
+
+ /* For macOS, attempt searching for an app bundle first. */
+ bundle_id = CFStringCreateWithCString (NULL, mac_bundle_id,
+ kCFStringEncodingUTF8);
+ if (bundle_id)
+ {
+ OSStatus status;
+ CFURLRef bundle_url = NULL;
+
+ status = LSFindApplicationForInfo (kLSUnknownCreator,
+ bundle_id, NULL, NULL,
+ &bundle_url);
+ if (status >= 0)
+ {
+ CFBundleRef bundle;
+ CFURLRef exec_url, absolute_url;
+ CFStringRef path;
+ gchar *ret;
+ CFIndex len;
+
+ bundle = CFBundleCreate (kCFAllocatorDefault, bundle_url);
+ CFRelease (bundle_url);
+
+ exec_url = CFBundleCopyExecutableURL (bundle);
+ absolute_url = CFURLCopyAbsoluteURL (exec_url);
+ path = CFURLCopyFileSystemPath (absolute_url, kCFURLPOSIXPathStyle);
+
+ /* This gets us the length in UTF16 characters, we multiply by 2
+ * to make sure we have a buffer big enough to fit the UTF8 string.
+ */
+ len = CFStringGetLength (path);
+ ret = g_malloc0 (len * 2 * sizeof (gchar));
+ if (! CFStringGetCString (path, ret, 2 * len * sizeof (gchar),
+ kCFStringEncodingUTF8))
+ ret = NULL;
+
+ CFRelease (path);
+ CFRelease (absolute_url);
+ CFRelease (exec_url);
+ CFRelease (bundle);
+
+ if (ret)
+ return ret;
+ }
+
+ CFRelease (bundle_id);
+ }
+ /* else, app bundle was not found, try path search as last resort. */
+ }
+
+#elif defined (GDK_WINDOWING_WIN32)
+ if (win32_registry_key_base)
+ {
+ /* Look for the application in the Windows registry. */
+ char *registry_key;
+ char path[MAX_PATH];
+ DWORD buffer_size = sizeof (path);
+ long status;
+
+ if (suffix)
+ registry_key = g_strconcat (win32_registry_key_base, suffix, ".exe", NULL);
+ else
+ registry_key = g_strconcat (win32_registry_key_base, ".exe", NULL);
+
+ /* Check HKCU first in case there is a user specific installation. */
+ status = RegGetValue (HKEY_CURRENT_USER, registry_key, "", RRF_RT_ANY,
+ NULL, (PVOID)&path, &buffer_size);
+
+ if (status != ERROR_SUCCESS)
+ status = RegGetValue (HKEY_LOCAL_MACHINE, registry_key, "", RRF_RT_ANY,
+ NULL, (PVOID)&path, &buffer_size);
+
+ g_free (registry_key);
+
+ if (status == ERROR_SUCCESS)
+ return g_strdup (path);
+ }
+#endif
+
+ /* Finally, the last resort. */
+ *search_path = TRUE;
+
+ if (suffix)
+ return g_strconcat (main_executable, suffix, NULL);
+
+ return g_strdup (main_executable);
+}
diff --git a/plug-ins/file-raw/file-raw-utils.h b/plug-ins/file-raw/file-raw-utils.h
new file mode 100644
index 0000000..0199f1f
--- /dev/null
+++ b/plug-ins/file-raw/file-raw-utils.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-raw-utils.h -- raw file format plug-in
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FILE_RAW_UTILS_H__
+#define __FILE_RAW_UTILS_H__
+
+
+gchar * file_raw_get_executable_path (const gchar *main_executable,
+ const gchar *suffix,
+ const gchar *env_variable,
+ const gchar *mac_bundle_id,
+ const gchar *win32_registry_key_base,
+ gboolean *search_path);
+
+
+#endif /* __FILE_RAW_UTILS_H__ */
diff --git a/plug-ins/file-raw/file-rawtherapee.c b/plug-ins/file-raw/file-rawtherapee.c
new file mode 100644
index 0000000..40ef04b
--- /dev/null
+++ b/plug-ins/file-raw/file-rawtherapee.c
@@ -0,0 +1,476 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-rawtherapee.c -- raw file format plug-in that uses RawTherapee
+ * Copyright (C) 2012 Simon Budig <simon@gimp.org>
+ * Copyright (C) 2016 Tobias Ellinghaus <me@houz.org>
+ * Copyright (C) 2017 Alberto Griggio <alberto.griggio@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "file-raw-formats.h"
+#include "file-raw-utils.h"
+
+
+#define LOAD_THUMB_PROC "file-rawtherapee-load-thumb"
+#define REGISTRY_KEY_BASE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\rawtherapee"
+
+
+static void init (void);
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint32 load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error);
+
+static gint32 load_thumbnail_image (const gchar *filename,
+ gint thumb_size,
+ GError **error);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ init, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+
+static void
+init (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load." },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ };
+
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" }
+ };
+
+ /* check if rawtherapee is installed
+ * TODO: allow setting the location of the executable in preferences
+ */
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("rawtherapee", NULL,
+ "RAWTHERAPEE_EXECUTABLE",
+ "com.rawtherapee.rawtherapee",
+ REGISTRY_KEY_BASE,
+ &search_path);
+#ifdef G_OS_WIN32
+ /* Issue #2716 - Prevent RT from opening a console window */
+ gchar *argv[] = { exec_path, "-v", "-w", NULL };
+#else
+ gchar *argv[] = { exec_path, "-v", NULL };
+#endif
+ gchar *rawtherapee_stdout = NULL;
+ gboolean have_rawtherapee = FALSE;
+ gint i;
+
+ if (g_spawn_sync (NULL,
+ argv,
+ NULL,
+ (search_path ? G_SPAWN_SEARCH_PATH : 0) |
+ G_SPAWN_STDERR_TO_DEV_NULL,
+ NULL,
+ NULL,
+ &rawtherapee_stdout,
+ NULL,
+ NULL,
+ NULL))
+ {
+ gint rtmajor = 0;
+ gint rtminor = 0;
+
+ if (sscanf (rawtherapee_stdout,
+ "RawTherapee, version %d.%d",
+ &rtmajor, &rtminor) == 2 &&
+ ((rtmajor == 5 && rtminor >= 2) || rtmajor >= 6))
+ {
+ have_rawtherapee = TRUE;
+ }
+
+ g_free (rawtherapee_stdout);
+ }
+
+ g_free (exec_path);
+
+ if (! have_rawtherapee)
+ return;
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Load thumbnail from a raw image via rawtherapee",
+ "This plug-in loads a thumbnail from a raw image by calling rawtherapee-cli.",
+ "Alberto Griggio",
+ "Alberto Griggio",
+ "2017",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc;
+ gchar *load_blurb;
+ gchar *load_help;
+
+ load_proc = g_strdup_printf (format->load_proc_format, "rawtherapee");
+ load_blurb = g_strdup_printf (format->load_blurb_format, "rawtherapee");
+ load_help = g_strdup_printf (format->load_help_format, "rawtherapee");
+
+ gimp_install_procedure (load_proc,
+ load_blurb,
+ load_help,
+ "Alberto Griggio",
+ "Alberto Griggio",
+ "2017",
+ format->file_type,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (load_proc,
+ format->mime_type);
+ gimp_register_file_handler_raw (load_proc);
+ gimp_register_magic_load_handler (load_proc,
+ format->extensions,
+ "",
+ format->magic);
+
+ gimp_register_thumbnail_loader (load_proc, LOAD_THUMB_PROC);
+
+ g_free (load_proc);
+ g_free (load_blurb);
+ g_free (load_help);
+ }
+}
+
+static void
+query (void)
+{
+ /* query() is run only the first time for efficiency. Yet this plugin
+ * is dependent on the presence of rawtherapee which may be installed
+ * or uninstalled between GIMP startups. Therefore we should move the
+ * usual gimp_install_procedure() to init() so that the check is done
+ * at every startup instead.
+ */
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ gint image_ID;
+ GError *error = NULL;
+ gint i;
+
+ INIT_I18N ();
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* check if the format passed is actually supported & load */
+ for (i = 0; i < G_N_ELEMENTS (file_formats); i++)
+ {
+ const FileFormat *format = &file_formats[i];
+ gchar *load_proc = NULL;
+
+ if (format->load_proc_format)
+ load_proc = g_strdup_printf (format->load_proc_format, "rawtherapee");
+
+ if (load_proc && ! strcmp (name, load_proc))
+ {
+ image_ID = load_image (param[1].data.d_string, run_mode, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ break;
+ }
+ else if (! strcmp (name, LOAD_THUMB_PROC))
+ {
+ image_ID = load_thumbnail_image (param[0].data.d_string,
+ param[1].data.d_int32,
+ &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 4;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = GIMP_RGB_IMAGE;
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = 1; /* num_layers */
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (file_formats))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gint32
+load_image (const gchar *filename,
+ GimpRunMode run_mode,
+ GError **error)
+{
+ gint32 image_ID = -1;
+ gchar *filename_out = gimp_temp_name ("tif");
+ gchar *rawtherapee_stdout = NULL;
+
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("rawtherapee", NULL,
+ "RAWTHERAPEE_EXECUTABLE",
+ "com.rawtherapee.rawtherapee",
+ REGISTRY_KEY_BASE,
+ &search_path);
+
+ /* linear sRGB for now as GIMP uses that internally in many places anyway */
+ gchar *argv[] =
+ {
+ exec_path,
+ "-gimp",
+ (gchar *) filename,
+ filename_out,
+ NULL
+ };
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if (g_spawn_sync (NULL,
+ argv,
+ NULL,
+ /*G_SPAWN_STDOUT_TO_DEV_NULL |*/
+ G_SPAWN_STDERR_TO_DEV_NULL |
+ (search_path ? G_SPAWN_SEARCH_PATH : 0),
+ NULL,
+ NULL,
+ &rawtherapee_stdout,
+ NULL,
+ NULL,
+ error))
+ {
+ image_ID = gimp_file_load (run_mode, filename_out, filename_out);
+ if (image_ID != -1)
+ gimp_image_set_filename (image_ID, filename);
+ }
+
+ /*if (rawtherapee_stdout) printf ("%s\n", rawtherapee_stdout);*/
+ g_free (rawtherapee_stdout);
+ g_free (exec_path);
+
+ g_unlink (filename_out);
+ g_free (filename_out);
+
+ gimp_progress_update (1.0);
+
+ return image_ID;
+}
+
+static gint32
+load_thumbnail_image (const gchar *filename,
+ gint thumb_size,
+ GError **error)
+{
+ gint32 image_ID = -1;
+ gchar *filename_out = gimp_temp_name ("jpg");
+ gchar *thumb_pp3 = gimp_temp_name ("pp3");
+ FILE *thumb_pp3_f = fopen (thumb_pp3, "w");
+ gchar *rawtherapee_stdout = NULL;
+ const char *pp3_content =
+ "[Version]\n"
+ "AppVersion=5.0\n"
+ "Version=326\n"
+ "\n"
+ "[Resize]\n"
+ "Enabled=true\n"
+ "AppliesTo=Cropped area\n"
+ "Method=Lanczos\n"
+ "Width=%d\n"
+ "Height=%d\n"
+ "\n"
+ "[Sharpening]\n"
+ "Enabled=false\n"
+ "\n"
+ "[SharpenEdge]\n"
+ "Enabled=false\n"
+ "\n"
+ "[SharpenMicro]\n"
+ "Enabled=false\n"
+ "\n"
+ "[Defringing]\n"
+ "Enabled=false\n"
+ "\n"
+ "[Directional Pyramid Equalizer]\n"
+ "Enabled=false\n"
+ "\n"
+ "[PostResizeSharpening]\n"
+ "Enabled=false\n"
+ "\n"
+ "[Directional Pyramid Denoising]\n"
+ "Enabled=false\n"
+ "\n"
+ "[Impulse Denoising]\n"
+ "Enabled=false\n"
+ "\n"
+ "[Wavelet]\n"
+ "Enabled=false\n"
+ "\n"
+ "[RAW Bayer]\n"
+ "Method=fast\n"
+ "\n"
+ "[RAW X-Trans]\n"
+ "Method=fast\n";
+
+
+ gboolean search_path = FALSE;
+ gchar *exec_path = file_raw_get_executable_path ("rawtherapee", "-cli",
+ "RAWTHERAPEE_EXECUTABLE",
+ "com.rawtherapee.rawtherapee",
+ REGISTRY_KEY_BASE,
+ &search_path);
+ gchar *argv[] =
+ {
+ exec_path,
+ "-o", filename_out,
+ "-d",
+ "-s",
+ "-j",
+ "-p", thumb_pp3,
+ "-f",
+ "-c", (char *) filename,
+ NULL
+ };
+
+ if (thumb_pp3_f)
+ {
+ if (fprintf (thumb_pp3_f, pp3_content, thumb_size, thumb_size) < 0)
+ {
+ fclose (thumb_pp3_f);
+ thumb_pp3_f = NULL;
+ }
+ }
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ if (thumb_pp3_f &&
+ g_spawn_sync (NULL,
+ argv,
+ NULL,
+ G_SPAWN_STDERR_TO_DEV_NULL |
+ (search_path ? G_SPAWN_SEARCH_PATH : 0),
+ NULL,
+ NULL,
+ &rawtherapee_stdout,
+ NULL,
+ NULL,
+ error))
+ {
+ gimp_progress_update (0.5);
+
+ image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ filename_out,
+ filename_out);
+ if (image_ID != -1)
+ {
+ /* is this needed for thumbnails? */
+ gimp_image_set_filename (image_ID, filename);
+ }
+ }
+
+ gimp_progress_update (1.0);
+
+ if (thumb_pp3_f)
+ fclose (thumb_pp3_f);
+
+ g_unlink (thumb_pp3);
+ g_free (filename_out);
+ g_free (thumb_pp3);
+ g_free (rawtherapee_stdout);
+ g_free (exec_path);
+
+ return image_ID;
+}
diff --git a/plug-ins/file-sgi/Makefile.am b/plug-ins/file-sgi/Makefile.am
new file mode 100644
index 0000000..2dd6a6f
--- /dev/null
+++ b/plug-ins/file-sgi/Makefile.am
@@ -0,0 +1,49 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_sgi_RC = file-sgi.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-sgi
+
+libexec_PROGRAMS = file-sgi
+
+file_sgi_SOURCES = \
+ sgi.c \
+ sgi-lib.c \
+ sgi-lib.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_sgi_RC)
diff --git a/plug-ins/file-sgi/Makefile.in b/plug-ins/file-sgi/Makefile.in
new file mode 100644
index 0000000..1430bcd
--- /dev/null
+++ b/plug-ins/file-sgi/Makefile.in
@@ -0,0 +1,1004 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-sgi$(EXEEXT)
+subdir = plug-ins/file-sgi
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_sgi_OBJECTS = sgi.$(OBJEXT) sgi-lib.$(OBJEXT)
+file_sgi_OBJECTS = $(am_file_sgi_OBJECTS)
+file_sgi_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+file_sgi_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimpmath) $(libgimp) $(libgimpcolor) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_sgi_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/sgi-lib.Po ./$(DEPDIR)/sgi.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_sgi_SOURCES)
+DIST_SOURCES = $(file_sgi_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-sgi
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_sgi_RC = file-sgi.rc.o
+AM_LDFLAGS = $(mwindows)
+file_sgi_SOURCES = \
+ sgi.c \
+ sgi-lib.c \
+ sgi-lib.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_sgi_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-sgi/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-sgi/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-sgi$(EXEEXT): $(file_sgi_OBJECTS) $(file_sgi_DEPENDENCIES) $(EXTRA_file_sgi_DEPENDENCIES)
+ @rm -f file-sgi$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_sgi_OBJECTS) $(file_sgi_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgi-lib.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sgi.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/sgi-lib.Po
+ -rm -f ./$(DEPDIR)/sgi.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/sgi-lib.Po
+ -rm -f ./$(DEPDIR)/sgi.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-sgi/sgi-lib.c b/plug-ins/file-sgi/sgi-lib.c
new file mode 100644
index 0000000..5f98b5e
--- /dev/null
+++ b/plug-ins/file-sgi/sgi-lib.c
@@ -0,0 +1,960 @@
+/*
+ * SGI image file format library routines.
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Contents:
+ *
+ * sgiClose() - Close an SGI image file.
+ * sgiGetRow() - Get a row of image data from a file.
+ * sgiOpen() - Open an SGI image file for reading or writing.
+ * sgiOpenFile() - Open an SGI image file for reading or writing.
+ * sgiPutRow() - Put a row of image data to a file.
+ * getlong() - Get a 32-bit big-endian integer.
+ * getshort() - Get a 16-bit big-endian integer.
+ * putlong() - Put a 32-bit big-endian integer.
+ * putshort() - Put a 16-bit big-endian integer.
+ * read_rle8() - Read 8-bit RLE data.
+ * read_rle16() - Read 16-bit RLE data.
+ * write_rle8() - Write 8-bit RLE data.
+ * write_rle16() - Write 16-bit RLE data.
+ *
+ * Revision History:
+ *
+ * $Log$
+ * Revision 1.9 2005/03/04 13:23:31 neo
+ * 2005-03-04 Sven Neumann <sven@gimp.org>
+ *
+ * * plug-ins/FractalExplorer
+ * * plug-ins/Lighting
+ * * plug-ins/bmp
+ * * plug-ins/dbbrowser
+ * * plug-ins/faxg3
+ * * plug-ins/fits
+ * * plug-ins/flame
+ * * plug-ins/gfig
+ * * plug-ins/gflare
+ * * plug-ins/gfli
+ * * plug-ins/gimpressionist
+ * * plug-ins/ifscompose
+ * * plug-ins/jpeg
+ * * plug-ins/maze
+ * * plug-ins/pagecurl
+ * * plug-ins/print
+ * * plug-ins/rcm
+ * * plug-ins/script-fu
+ * * plug-ins/sel2path
+ * * plug-ins/sgi
+ * * plug-ins/twain
+ * * plug-ins/winicon
+ * * plug-ins/xjt: ported to gstdio, removed unnecessary includes,
+ * minor fixes to filename handling here and there.
+ *
+ * Revision 1.8 2003/04/07 11:59:33 neo
+ * 2003-04-07 Sven Neumann <sven@gimp.org>
+ *
+ * * plug-ins/sgi/sgi.h
+ * * plug-ins/sgi/sgilib.c: applied a patch from marek@aki.cz that
+ * adds support for reading SGI files in little-endian format. Fixes
+ * bug #106610.
+ *
+ * Revision 1.7 1998/06/06 23:22:21 yosh
+ * * adding Lighting plugin
+ *
+ * * updated despeckle, png, sgi, and sharpen
+ *
+ * -Yosh
+ *
+ * Revision 1.5 1998/04/23 17:40:49 mike
+ * Updated to support 16-bit <unsigned> image data.
+ *
+ * Revision 1.4 1998/02/05 17:10:58 mike
+ * Added sgiOpenFile() function for opening an existing file pointer.
+ *
+ * Revision 1.3 1997/07/02 16:40:16 mike
+ * sgiOpen() wasn't opening files with "rb" or "wb+". This caused problems
+ * on PCs running Windows/DOS...
+ *
+ * Revision 1.2 1997/06/18 00:55:28 mike
+ * Updated to hold length table when writing.
+ * Updated to hold current length when doing ARLE.
+ * Wasn't writing length table on close.
+ * Wasn't saving new line into arle_row when necessary.
+ *
+ * Revision 1.1 1997/06/15 03:37:19 mike
+ * Initial revision
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "sgi-lib.h"
+
+
+/*
+ * Local functions...
+ */
+
+static int getlong(sgi_t*);
+static int getshort(sgi_t*);
+static int putlong(long, sgi_t*);
+static int putshort(unsigned short, sgi_t*);
+static int read_rle8(sgi_t*, unsigned short *, int);
+static int read_rle16(sgi_t*, unsigned short *, int);
+static int write_rle8(sgi_t*, unsigned short *, int);
+static int write_rle16(sgi_t*, unsigned short *, int);
+
+
+/*
+ * 'sgiClose()' - Close an SGI image file.
+ */
+
+int
+sgiClose(sgi_t *sgip) /* I - SGI image */
+{
+ int i; /* Return status */
+ long *offset; /* Looping var for offset table */
+
+
+ if (sgip == NULL)
+ return (-1);
+
+ if (sgip->mode == SGI_WRITE && sgip->comp != SGI_COMP_NONE)
+ {
+ /*
+ * Write the scanline offset table to the file...
+ */
+
+ fseek(sgip->file, 512, SEEK_SET);
+
+ for (i = sgip->ysize * sgip->zsize, offset = sgip->table[0];
+ i > 0;
+ i --, offset ++)
+ if (putlong(offset[0], sgip) < 0)
+ return (-1);
+
+ for (i = sgip->ysize * sgip->zsize, offset = sgip->length[0];
+ i > 0;
+ i --, offset ++)
+ if (putlong(offset[0], sgip) < 0)
+ return (-1);
+ };
+
+ if (sgip->table != NULL)
+ {
+ free(sgip->table[0]);
+ free(sgip->table);
+ };
+
+ if (sgip->length != NULL)
+ {
+ free(sgip->length[0]);
+ free(sgip->length);
+ };
+
+ if (sgip->comp == SGI_COMP_ARLE)
+ free(sgip->arle_row);
+
+ i = fclose(sgip->file);
+ free(sgip);
+
+ return (i);
+}
+
+
+/*
+ * 'sgiGetRow()' - Get a row of image data from a file.
+ */
+
+int
+sgiGetRow(sgi_t *sgip, /* I - SGI image */
+ unsigned short *row, /* O - Row to read */
+ int y, /* I - Line to read */
+ int z) /* I - Channel to read */
+{
+ int x; /* X coordinate */
+ long offset; /* File offset */
+
+
+ if (sgip == NULL ||
+ row == NULL ||
+ y < 0 || y >= sgip->ysize ||
+ z < 0 || z >= sgip->zsize)
+ return (-1);
+
+ switch (sgip->comp)
+ {
+ case SGI_COMP_NONE :
+ /*
+ * Seek to the image row - optimize buffering by only seeking if
+ * necessary...
+ */
+
+ offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ *row = getc(sgip->file);
+ }
+ else
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ *row = getshort(sgip);
+ };
+ break;
+
+ case SGI_COMP_RLE :
+ offset = sgip->table[z][y];
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ return (read_rle8(sgip, row, sgip->xsize));
+ else
+ return (read_rle16(sgip, row, sgip->xsize));
+ break;
+ };
+
+ return (0);
+}
+
+
+/*
+ * 'sgiOpen()' - Open an SGI image file for reading or writing.
+ */
+
+sgi_t *
+sgiOpen(const char *filename, /* I - File to open */
+ int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
+ int comp, /* I - Type of compression */
+ int bpp, /* I - Bytes per pixel */
+ int xsize, /* I - Width of image in pixels */
+ int ysize, /* I - Height of image in pixels */
+ int zsize) /* I - Number of channels */
+{
+ sgi_t *sgip; /* New SGI image file */
+ FILE *file; /* Image file pointer */
+
+
+ if (mode == SGI_READ)
+ file = g_fopen(filename, "rb");
+ else
+ file = g_fopen(filename, "w+b");
+
+ if (file == NULL)
+ return (NULL);
+
+ if ((sgip = sgiOpenFile(file, mode, comp, bpp, xsize, ysize, zsize)) == NULL)
+ fclose(file);
+
+ return (sgip);
+}
+
+
+/*
+ * 'sgiOpenFile()' - Open an SGI image file for reading or writing.
+ */
+
+sgi_t *
+sgiOpenFile(FILE *file, /* I - File to open */
+ int mode, /* I - Open mode (SGI_READ or SGI_WRITE) */
+ int comp, /* I - Type of compression */
+ int bpp, /* I - Bytes per pixel */
+ int xsize, /* I - Width of image in pixels */
+ int ysize, /* I - Height of image in pixels */
+ int zsize) /* I - Number of channels */
+{
+ int i, j; /* Looping var */
+ char name[80]; /* Name of file in image header */
+ short magic; /* Magic number */
+ sgi_t *sgip; /* New image pointer */
+
+
+ if ((sgip = calloc(sizeof(sgi_t), 1)) == NULL)
+ return (NULL);
+
+ sgip->file = file;
+ sgip->swapBytes = 0;
+
+ switch (mode)
+ {
+ case SGI_READ :
+ sgip->mode = SGI_READ;
+
+ magic = getshort(sgip);
+ if (magic != SGI_MAGIC)
+ {
+ /* try little endian format */
+ magic = ((magic >> 8) & 0x00ff) | ((magic << 8) & 0xff00);
+ if(magic != SGI_MAGIC) {
+ free(sgip);
+ return (NULL);
+ } else {
+ sgip->swapBytes = 1;
+ }
+ }
+
+ sgip->comp = getc(sgip->file);
+ sgip->bpp = getc(sgip->file);
+ getshort(sgip); /* Dimensions */
+ sgip->xsize = getshort(sgip);
+ sgip->ysize = getshort(sgip);
+ sgip->zsize = getshort(sgip);
+ getlong(sgip); /* Minimum pixel */
+ getlong(sgip); /* Maximum pixel */
+
+ if (sgip->comp)
+ {
+ /*
+ * This file is compressed; read the scanline tables...
+ */
+
+ fseek(sgip->file, 512, SEEK_SET);
+
+ sgip->table = calloc(sgip->zsize, sizeof(long *));
+ if (sgip->table == NULL)
+ {
+ free(sgip);
+ return (NULL);
+ }
+ sgip->table[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ if (sgip->table[0] == NULL)
+ {
+ free(sgip->table);
+ free(sgip);
+ return (NULL);
+ }
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->table[i] = sgip->table[0] + i * sgip->ysize;
+
+ for (i = 0; i < sgip->zsize; i ++)
+ for (j = 0; j < sgip->ysize; j ++)
+ sgip->table[i][j] = getlong(sgip);
+ };
+ break;
+
+ case SGI_WRITE :
+ if (xsize < 1 ||
+ ysize < 1 ||
+ zsize < 1 ||
+ bpp < 1 || bpp > 2 ||
+ comp < SGI_COMP_NONE || comp > SGI_COMP_ARLE)
+ {
+ free(sgip);
+ return (NULL);
+ };
+
+ sgip->mode = SGI_WRITE;
+
+ putshort(SGI_MAGIC, sgip);
+ putc((sgip->comp = comp) != 0, sgip->file);
+ putc(sgip->bpp = bpp, sgip->file);
+ putshort(3, sgip); /* Dimensions */
+ putshort(sgip->xsize = xsize, sgip);
+ putshort(sgip->ysize = ysize, sgip);
+ putshort(sgip->zsize = zsize, sgip);
+ if (bpp == 1)
+ {
+ putlong(0, sgip); /* Minimum pixel */
+ putlong(255, sgip); /* Maximum pixel */
+ }
+ else
+ {
+ putlong(-32768, sgip); /* Minimum pixel */
+ putlong(32767, sgip); /* Maximum pixel */
+ };
+ putlong(0, sgip); /* Reserved */
+
+ memset(name, 0, sizeof(name));
+ fwrite(name, sizeof(name), 1, sgip->file);
+
+ for (i = 0; i < 102; i ++)
+ putlong(0, sgip);
+
+ switch (comp)
+ {
+ case SGI_COMP_NONE : /* No compression */
+ /*
+ * This file is uncompressed. To avoid problems with sparse files,
+ * we need to write blank pixels for the entire image...
+ */
+
+ if (bpp == 1)
+ {
+ for (i = xsize * ysize * zsize; i > 0; i --)
+ putc(0, sgip->file);
+ }
+ else
+ {
+ for (i = xsize * ysize * zsize; i > 0; i --)
+ putshort(0, sgip);
+ };
+ break;
+
+ case SGI_COMP_ARLE : /* Aggressive RLE */
+ sgip->arle_row = (unsigned short *)calloc(xsize, sizeof(unsigned short));
+ if (sgip->arle_row == NULL)
+ {
+ free(sgip);
+ return (NULL);
+ }
+ sgip->arle_offset = 0;
+
+ case SGI_COMP_RLE : /* Run-Length Encoding */
+ /*
+ * This file is compressed; write the (blank) scanline tables...
+ */
+
+ for (i = 2 * ysize * zsize; i > 0; i --)
+ putlong(0, sgip);
+
+ sgip->firstrow = ftell(sgip->file);
+ sgip->nextrow = ftell(sgip->file);
+ sgip->table = calloc(sgip->zsize, sizeof(long *));
+ if (sgip->table == NULL)
+ {
+ free(sgip->arle_row);
+ free(sgip);
+ return (NULL);
+ }
+ sgip->table[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ if (sgip->table[0] == NULL)
+ {
+ free(sgip->table);
+ free(sgip->arle_row);
+ free(sgip);
+ return (NULL);
+ }
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->table[i] = sgip->table[0] + i * sgip->ysize;
+ sgip->length = calloc(sgip->zsize, sizeof(long *));
+ sgip->length[0] = calloc(sgip->ysize * sgip->zsize, sizeof(long));
+ for (i = 1; i < sgip->zsize; i ++)
+ sgip->length[i] = sgip->length[0] + i * sgip->ysize;
+ break;
+ };
+ break;
+
+ default :
+ free(sgip);
+ return (NULL);
+ };
+
+ return (sgip);
+}
+
+
+/*
+ * 'sgiPutRow()' - Put a row of image data to a file.
+ */
+
+int
+sgiPutRow(sgi_t *sgip, /* I - SGI image */
+ unsigned short *row, /* I - Row to write */
+ int y, /* I - Line to write */
+ int z) /* I - Channel to write */
+{
+ int x; /* X coordinate */
+ long offset; /* File offset */
+
+
+ if (sgip == NULL ||
+ row == NULL ||
+ y < 0 || y >= sgip->ysize ||
+ z < 0 || z >= sgip->zsize)
+ return (-1);
+
+ switch (sgip->comp)
+ {
+ case SGI_COMP_NONE :
+ /*
+ * Seek to the image row - optimize buffering by only seeking if
+ * necessary...
+ */
+
+ offset = 512 + (y + z * sgip->ysize) * sgip->xsize * sgip->bpp;
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ putc(*row, sgip->file);
+ }
+ else
+ {
+ for (x = sgip->xsize; x > 0; x --, row ++)
+ putshort(*row, sgip);
+ };
+ break;
+
+ case SGI_COMP_ARLE :
+ if (sgip->table[z][y] != 0)
+ return (-1);
+
+ /*
+ * First check the last row written...
+ */
+
+ if (sgip->arle_offset > 0)
+ {
+ for (x = 0; x < sgip->xsize; x ++)
+ if (row[x] != sgip->arle_row[x])
+ break;
+
+ if (x == sgip->xsize)
+ {
+ sgip->table[z][y] = sgip->arle_offset;
+ sgip->length[z][y] = sgip->arle_length;
+ return (0);
+ };
+ };
+
+ /*
+ * If that didn't match, search all the previous rows...
+ */
+
+ fseek(sgip->file, sgip->firstrow, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ {
+ do
+ {
+ sgip->arle_offset = ftell(sgip->file);
+ if ((sgip->arle_length = read_rle8(sgip, sgip->arle_row, sgip->xsize)) < 0)
+ {
+ x = 0;
+ break;
+ };
+
+ for (x = 0; x < sgip->xsize; x ++)
+ if (row[x] != sgip->arle_row[x])
+ break;
+ }
+ while (x < sgip->xsize);
+ }
+ else
+ {
+ do
+ {
+ sgip->arle_offset = ftell(sgip->file);
+ if ((sgip->arle_length = read_rle16(sgip, sgip->arle_row, sgip->xsize)) < 0)
+ {
+ x = 0;
+ break;
+ };
+
+ for (x = 0; x < sgip->xsize; x ++)
+ if (row[x] != sgip->arle_row[x])
+ break;
+ }
+ while (x < sgip->xsize);
+ };
+
+ if (x == sgip->xsize)
+ {
+ sgip->table[z][y] = sgip->arle_offset;
+ sgip->length[z][y] = sgip->arle_length;
+ return (0);
+ }
+ else
+ fseek(sgip->file, 0, SEEK_END); /* Clear EOF */
+
+ case SGI_COMP_RLE :
+ if (sgip->table[z][y] != 0)
+ return (-1);
+
+ offset = sgip->table[z][y] = sgip->nextrow;
+
+ if (offset != ftell(sgip->file))
+ fseek(sgip->file, offset, SEEK_SET);
+
+ if (sgip->bpp == 1)
+ x = write_rle8(sgip, row, sgip->xsize);
+ else
+ x = write_rle16(sgip, row, sgip->xsize);
+
+ if (sgip->comp == SGI_COMP_ARLE)
+ {
+ sgip->arle_offset = offset;
+ sgip->arle_length = x;
+ memcpy(sgip->arle_row, row, sgip->xsize * sizeof(short));
+ };
+
+ sgip->nextrow = ftell(sgip->file);
+ sgip->length[z][y] = x;
+
+ return (x);
+ };
+
+ return (0);
+}
+
+
+/*
+ * 'getlong()' - Get a 32-bit big-endian integer.
+ */
+
+static int
+getlong(sgi_t *sgip) /* I - SGI image to read from */
+{
+ unsigned char b[4];
+
+
+ fread(b, 4, 1, sgip->file);
+ if(sgip->swapBytes)
+ return ((b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]);
+ else
+ return ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+}
+
+
+/*
+ * 'getshort()' - Get a 16-bit big-endian integer.
+ */
+
+static int
+getshort(sgi_t *sgip) /* I - SGI image to read from */
+{
+ unsigned char b[2];
+
+
+ fread(b, 2, 1, sgip->file);
+ if(sgip->swapBytes)
+ return ((b[1] << 8) | b[0]);
+ else
+ return ((b[0] << 8) | b[1]);
+}
+
+
+/*
+ * 'putlong()' - Put a 32-bit big-endian integer.
+ */
+
+static int
+putlong(long n, /* I - Long to write */
+ sgi_t *sgip) /* I - File to write to */
+{
+ if (putc(n >> 24, sgip->file) == EOF)
+ return (EOF);
+ if (putc(n >> 16, sgip->file) == EOF)
+ return (EOF);
+ if (putc(n >> 8, sgip->file) == EOF)
+ return (EOF);
+ if (putc(n, sgip->file) == EOF)
+ return (EOF);
+ else
+ return (0);
+}
+
+
+/*
+ * 'putshort()' - Put a 16-bit big-endian integer.
+ */
+
+static int
+putshort(unsigned short n, /* I - Short to write */
+ sgi_t *sgip) /* I - File to write to */
+{
+ if (putc(n >> 8, sgip->file) == EOF)
+ return (EOF);
+ if (putc(n, sgip->file) == EOF)
+ return (EOF);
+ else
+ return (0);
+}
+
+
+/*
+ * 'read_rle8()' - Read 8-bit RLE data.
+ */
+
+static int
+read_rle8(sgi_t *sgip, /* I - SGI image to read from */
+ unsigned short *row, /* O - Data */
+ int xsize) /* I - Width of data in pixels */
+{
+ int i, /* Looping var */
+ ch, /* Current character */
+ count, /* RLE count */
+ length; /* Number of bytes read... */
+
+
+ length = 0;
+
+ while (xsize > 0)
+ {
+ if ((ch = getc(sgip->file)) == EOF)
+ return (-1);
+ length ++;
+
+ count = MIN (ch & 127, xsize);
+ if (count == 0)
+ break;
+
+ if (ch & 128)
+ {
+ for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+ *row = getc(sgip->file);
+ }
+ else
+ {
+ ch = getc(sgip->file);
+ length ++;
+ for (i = 0; i < count; i ++, row ++, xsize --)
+ *row = ch;
+ };
+ };
+
+ return (xsize > 0 ? -1 : length);
+}
+
+
+/*
+ * 'read_rle16()' - Read 16-bit RLE data.
+ */
+
+static int
+read_rle16(sgi_t *sgip, /* I - SGI image to read from */
+ unsigned short *row, /* O - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int i, /* Looping var */
+ ch, /* Current character */
+ count, /* RLE count */
+ length; /* Number of bytes read... */
+
+
+ length = 0;
+
+ while (xsize > 0)
+ {
+ if ((ch = getshort(sgip)) == EOF)
+ return (-1);
+ length ++;
+
+ count = MIN (ch & 127, xsize);
+ if (count == 0)
+ break;
+
+ if (ch & 128)
+ {
+ for (i = 0; i < count; i ++, row ++, xsize --, length ++)
+ *row = getshort(sgip);
+ }
+ else
+ {
+ ch = getshort(sgip);
+ length ++;
+ for (i = 0; i < count; i ++, row ++, xsize --)
+ *row = ch;
+ };
+ };
+
+ return (xsize > 0 ? -1 : length * 2);
+}
+
+
+/*
+ * 'write_rle8()' - Write 8-bit RLE data.
+ */
+
+static int
+write_rle8(sgi_t *sgip, /* I - SGI image to write to */
+ unsigned short *row, /* I - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int length, /* Length of output line */
+ count, /* Number of repeated/non-repeated pixels */
+ i, /* Looping var */
+ x; /* Looping var */
+ unsigned short *start, /* Start of sequence */
+ repeat; /* Repeated pixel */
+
+
+ for (x = xsize, length = 0; x > 0;)
+ {
+ start = row;
+ row += 2;
+ x -= 2;
+
+ while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
+ {
+ row ++;
+ x --;
+ };
+
+ row -= 2;
+ x += 2;
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putc(128 | i, sgip->file) == EOF)
+ return (-1);
+ length ++;
+
+ while (i > 0)
+ {
+ if (putc(*start, sgip->file) == EOF)
+ return (-1);
+ start ++;
+ i --;
+ length ++;
+ };
+ };
+
+ if (x <= 0)
+ break;
+
+ start = row;
+ repeat = row[0];
+
+ row ++;
+ x --;
+
+ while (x > 0 && *row == repeat)
+ {
+ row ++;
+ x --;
+ };
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putc(i, sgip->file) == EOF)
+ return (-1);
+ length ++;
+
+ if (putc(repeat, sgip->file) == EOF)
+ return (-1);
+ length ++;
+ };
+ };
+
+ length ++;
+
+ if (putc(0, sgip->file) == EOF)
+ return (-1);
+ else
+ return (length);
+}
+
+
+/*
+ * 'write_rle16()' - Write 16-bit RLE data.
+ */
+
+static int
+write_rle16(sgi_t *sgip, /* I - SGI image to write to */
+ unsigned short *row,/* I - Data */
+ int xsize)/* I - Width of data in pixels */
+{
+ int length, /* Length of output line */
+ count, /* Number of repeated/non-repeated pixels */
+ i, /* Looping var */
+ x; /* Looping var */
+ unsigned short *start, /* Start of sequence */
+ repeat; /* Repeated pixel */
+
+
+ for (x = xsize, length = 0; x > 0;)
+ {
+ start = row;
+ row += 2;
+ x -= 2;
+
+ while (x > 0 && (row[-2] != row[-1] || row[-1] != row[0]))
+ {
+ row ++;
+ x --;
+ };
+
+ row -= 2;
+ x += 2;
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putshort(128 | i, sgip) == EOF)
+ return (-1);
+ length ++;
+
+ while (i > 0)
+ {
+ if (putshort(*start, sgip) == EOF)
+ return (-1);
+ start ++;
+ i --;
+ length ++;
+ };
+ };
+
+ if (x <= 0)
+ break;
+
+ start = row;
+ repeat = row[0];
+
+ row ++;
+ x --;
+
+ while (x > 0 && *row == repeat)
+ {
+ row ++;
+ x --;
+ };
+
+ count = row - start;
+ while (count > 0)
+ {
+ i = count > 126 ? 126 : count;
+ count -= i;
+
+ if (putshort(i, sgip) == EOF)
+ return (-1);
+ length ++;
+
+ if (putshort(repeat, sgip) == EOF)
+ return (-1);
+ length ++;
+ };
+ };
+
+ length ++;
+
+ if (putshort(0, sgip) == EOF)
+ return (-1);
+ else
+ return (2 * length);
+}
diff --git a/plug-ins/file-sgi/sgi-lib.h b/plug-ins/file-sgi/sgi-lib.h
new file mode 100644
index 0000000..e2fc44d
--- /dev/null
+++ b/plug-ins/file-sgi/sgi-lib.h
@@ -0,0 +1,93 @@
+/*
+ * SGI image file format library definitions.
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SGI_LIB_H__
+#define __SGI_LIB_H__
+
+G_BEGIN_DECLS
+
+/*
+ * Constants...
+ */
+
+# define SGI_MAGIC 474 /* Magic number in image file */
+
+# define SGI_READ 0 /* Read from an SGI image file */
+# define SGI_WRITE 1 /* Write to an SGI image file */
+
+# define SGI_COMP_NONE 0 /* No compression */
+# define SGI_COMP_RLE 1 /* Run-length encoding */
+# define SGI_COMP_ARLE 2 /* Aggressive run-length encoding */
+
+
+/*
+ * Image structure...
+ */
+
+typedef struct
+{
+ FILE *file; /* Image file */
+ int mode, /* File open mode */
+ bpp, /* Bytes per pixel/channel */
+ comp, /* Compression */
+ swapBytes; /* SwapBytes flag */
+ unsigned short xsize, /* Width in pixels */
+ ysize, /* Height in pixels */
+ zsize; /* Number of channels */
+ long firstrow, /* File offset for first row */
+ nextrow, /* File offset for next row */
+ **table, /* Offset table for compression */
+ **length; /* Length table for compression */
+ unsigned short *arle_row; /* Advanced RLE compression buffer */
+ long arle_offset, /* Advanced RLE buffer offset */
+ arle_length; /* Advanced RLE buffer length */
+} sgi_t;
+
+
+/*
+ * Prototypes...
+ */
+
+extern int sgiClose (sgi_t *sgip);
+extern int sgiGetRow (sgi_t *sgip,
+ unsigned short *row,
+ int y,
+ int z);
+extern sgi_t *sgiOpen (const char *filename,
+ int mode,
+ int comp,
+ int bpp,
+ int xsize,
+ int ysize,
+ int zsize);
+extern sgi_t *sgiOpenFile (FILE *file,
+ int mode,
+ int comp,
+ int bpp,
+ int xsize,
+ int ysize,
+ int zsize);
+extern int sgiPutRow (sgi_t *sgip,
+ unsigned short *row,
+ int y,
+ int z);
+
+G_END_DECLS
+
+#endif /* !__SGI_LIB_H__ */
diff --git a/plug-ins/file-sgi/sgi.c b/plug-ins/file-sgi/sgi.c
new file mode 100644
index 0000000..03cd019
--- /dev/null
+++ b/plug-ins/file-sgi/sgi.c
@@ -0,0 +1,702 @@
+/*
+ * SGI image file plug-in for GIMP.
+ *
+ * Copyright 1997-1998 Michael Sweet (mike@easysw.com)
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Contents:
+ *
+ * main() - Main entry - just call gimp_main()...
+ * query() - Respond to a plug-in query...
+ * run() - Run the plug-in...
+ * load_image() - Load a PNG image into a new image window.
+ * save_image() - Export the specified image to a PNG file.
+ * save_ok_callback() - Destroy the export dialog and export the image.
+ * save_dialog() - Pop up the export dialog.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "sgi-lib.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*
+ * Constants...
+ */
+
+#define LOAD_PROC "file-sgi-load"
+#define SAVE_PROC "file-sgi-save"
+#define PLUG_IN_BINARY "file-sgi"
+#define PLUG_IN_ROLE "gimp-file-sgi"
+#define PLUG_IN_VERSION "1.1.1 - 17 May 1998"
+
+
+/*
+ * Local functions...
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint32 load_image (const gchar *filename,
+ GError **error);
+static gint save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error);
+
+static gboolean save_dialog (void);
+
+/*
+ * Globals...
+ */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gint compression = SGI_COMP_RLE;
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" },
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" },
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to export the image in" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in" },
+ { GIMP_PDB_INT32, "compression", "Compression level (0 = none, 1 = RLE, 2 = ARLE)" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads files in SGI image file format",
+ "This plug-in loads SGI image files.",
+ "Michael Sweet <mike@easysw.com>",
+ "Copyright 1997-1998 by Michael Sweet",
+ PLUG_IN_VERSION,
+ N_("Silicon Graphics IRIS image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args,
+ load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/x-sgi");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "sgi,rgb,rgba,bw,icon",
+ "",
+ "0,short,474");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in SGI image file format",
+ "This plug-in exports SGI image files.",
+ "Michael Sweet <mike@easysw.com>",
+ "Copyright 1997-1998 by Michael Sweet",
+ PLUG_IN_VERSION,
+ N_("Silicon Graphics IRIS image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args),
+ 0,
+ save_args,
+ NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/x-sgi");
+ gimp_register_save_handler (SAVE_PROC, "sgi,rgb,rgba,bw,icon", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ image_ID = load_image (param[1].data.d_string, &error);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* eventually export the image */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "SGI",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (SAVE_PROC, &compression);
+
+ /*
+ * Then acquire information with a dialog...
+ */
+ if (!save_dialog ())
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /*
+ * Make sure all the arguments are there!
+ */
+ if (nparams != 6)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ compression = param[5].data.d_int32;
+
+ if (compression < 0 || compression > 2)
+ status = GIMP_PDB_CALLING_ERROR;
+ };
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /*
+ * Possibly retrieve data...
+ */
+ gimp_get_data (SAVE_PROC, &compression);
+ break;
+
+ default:
+ break;
+ };
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (save_image (param[3].data.d_string, image_ID, drawable_ID,
+ &error))
+ {
+ gimp_set_data (SAVE_PROC, &compression, sizeof (compression));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/*
+ * 'load_image()' - Load a PNG image into a new image window.
+ */
+
+static gint32
+load_image (const gchar *filename,
+ GError **error)
+{
+ gint i, /* Looping var */
+ x, /* Current X coordinate */
+ y, /* Current Y coordinate */
+ image_type, /* Type of image */
+ layer_type, /* Type of drawable/layer */
+ tile_height, /* Height of tile in GIMP */
+ count, /* Count of rows to put in image */
+ bytes; /* Number of channels to use */
+ sgi_t *sgip; /* File pointer */
+ gint32 image, /* Image */
+ layer; /* Layer */
+ GeglBuffer *buffer; /* Buffer for layer */
+ guchar **pixels, /* Pixel rows */
+ *pptr; /* Current pixel */
+ gushort **rows; /* SGI image data */
+
+ /*
+ * Open the file for reading...
+ */
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ sgip = sgiOpen (filename, SGI_READ, 0, 0, 0, 0, 0);
+ if (sgip == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' for reading."),
+ gimp_filename_to_utf8 (filename));
+ free (sgip);
+ return -1;
+ };
+
+ /*
+ * Get the image dimensions and create the image...
+ */
+
+ /* Sanitize dimensions (note that they are unsigned short and can
+ * thus never be larger than GIMP_MAX_IMAGE_SIZE
+ */
+ if (sgip->xsize == 0 /*|| sgip->xsize > GIMP_MAX_IMAGE_SIZE*/)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid width: %hu"), sgip->xsize);
+ free (sgip);
+ return -1;
+ }
+
+ if (sgip->ysize == 0 /*|| sgip->ysize > GIMP_MAX_IMAGE_SIZE*/)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid height: %hu"), sgip->ysize);
+ free (sgip);
+ return -1;
+ }
+
+ if (sgip->zsize == 0 /*|| sgip->zsize > GIMP_MAX_IMAGE_SIZE*/)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Invalid number of channels: %hu"), sgip->zsize);
+ free (sgip);
+ return -1;
+ }
+
+ bytes = sgip->zsize;
+
+ switch (sgip->zsize)
+ {
+ case 1 : /* Grayscale */
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAY_IMAGE;
+ break;
+
+ case 2 : /* Grayscale + alpha */
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAYA_IMAGE;
+ break;
+
+ case 3 : /* RGB */
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+
+ case 4 : /* RGBA */
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGBA_IMAGE;
+ break;
+
+ default:
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGBA_IMAGE;
+ bytes = 4;
+ break;
+ }
+
+ image = gimp_image_new (sgip->xsize, sgip->ysize, image_type);
+ if (image == -1)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Could not allocate new image: %s",
+ gimp_get_pdb_error());
+ free (sgip);
+ return -1;
+ }
+
+ gimp_image_set_filename (image, filename);
+
+ /*
+ * Create the "background" layer to hold the image...
+ */
+
+ layer = gimp_layer_new (image, _("Background"), sgip->xsize, sgip->ysize,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image));
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ /*
+ * Get the drawable and set the pixel region for our load...
+ */
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ /*
+ * Temporary buffers...
+ */
+
+ tile_height = gimp_tile_height ();
+ pixels = g_new (guchar *, tile_height);
+ pixels[0] = g_new (guchar, ((gsize) tile_height) * sgip->xsize * bytes);
+
+ for (i = 1; i < tile_height; i ++)
+ pixels[i] = pixels[0] + sgip->xsize * bytes * i;
+
+ rows = g_new (unsigned short *, sgip->zsize);
+ rows[0] = g_new (unsigned short, ((gsize) sgip->xsize) * sgip->zsize);
+
+ for (i = 1; i < sgip->zsize; i ++)
+ rows[i] = rows[0] + i * sgip->xsize;
+
+ /*
+ * Load the image...
+ */
+
+ for (y = 0, count = 0;
+ y < sgip->ysize;
+ y ++, count ++)
+ {
+ if (count >= tile_height)
+ {
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - count,
+ sgip->xsize, count), 0,
+ NULL, pixels[0], GEGL_AUTO_ROWSTRIDE);
+
+ count = 0;
+
+ gimp_progress_update ((double) y / (double) sgip->ysize);
+ }
+
+ for (i = 0; i < sgip->zsize; i ++)
+ if (sgiGetRow (sgip, rows[i], sgip->ysize - 1 - y, i) < 0)
+ g_printerr ("sgiGetRow(sgip, rows[i], %d, %d) failed!\n",
+ sgip->ysize - 1 - y, i);
+
+ if (sgip->bpp == 1)
+ {
+ /*
+ * 8-bit (unsigned) pixels...
+ */
+
+ for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++)
+ for (i = 0; i < bytes; i ++, pptr ++)
+ *pptr = rows[i][x];
+ }
+ else
+ {
+ /*
+ * 16-bit (unsigned) pixels...
+ */
+
+ for (x = 0, pptr = pixels[count]; x < sgip->xsize; x ++)
+ for (i = 0; i < bytes; i ++, pptr ++)
+ *pptr = rows[i][x] >> 8;
+ }
+ }
+
+ /*
+ * Do the last n rows (count always > 0)
+ */
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, y - count,
+ sgip->xsize, count), 0,
+ NULL, pixels[0], GEGL_AUTO_ROWSTRIDE);
+
+ /*
+ * Done with the file...
+ */
+
+ sgiClose (sgip);
+
+ g_free (pixels[0]);
+ g_free (pixels);
+ g_free (rows[0]);
+ g_free (rows);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return image;
+}
+
+
+/*
+ * 'save_image()' - Save the specified image to a SGI file.
+ */
+
+static gint
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GError **error)
+{
+ gint i, j, /* Looping var */
+ x, /* Current X coordinate */
+ y, /* Current Y coordinate */
+ width, /* Drawable width */
+ height, /* Drawable height */
+ tile_height, /* Height of tile in GIMP */
+ count, /* Count of rows to put in image */
+ zsize; /* Number of channels in file */
+ sgi_t *sgip; /* File pointer */
+ GeglBuffer *buffer; /* Buffer for layer */
+ const Babl *format;
+ guchar **pixels, /* Pixel rows */
+ *pptr; /* Current pixel */
+ gushort **rows; /* SGI image data */
+
+ /*
+ * Get the drawable for the current image...
+ */
+
+ width = gimp_drawable_width (drawable_ID);
+ height = gimp_drawable_height (drawable_ID);
+
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ switch (gimp_drawable_type (drawable_ID))
+ {
+ case GIMP_GRAY_IMAGE:
+ zsize = 1;
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ zsize = 2;
+ format = babl_format ("Y'A u8");
+ break;
+
+ case GIMP_RGB_IMAGE:
+ case GIMP_INDEXED_IMAGE:
+ zsize = 3;
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ format = babl_format ("R'G'B'A u8");
+ zsize = 4;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ /*
+ * Open the file for writing...
+ */
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ sgip = sgiOpen (filename, SGI_WRITE, compression, 1,
+ width, height, zsize);
+ if (sgip == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Could not open '%s' for writing."),
+ gimp_filename_to_utf8 (filename));
+ return FALSE;
+ };
+
+ /*
+ * Allocate memory for "tile_height" rows...
+ */
+
+ tile_height = gimp_tile_height ();
+ pixels = g_new (guchar *, tile_height);
+ pixels[0] = g_new (guchar, ((gsize) tile_height) * width * zsize);
+
+ for (i = 1; i < tile_height; i ++)
+ pixels[i]= pixels[0] + width * zsize * i;
+
+ rows = g_new (gushort *, sgip->zsize);
+ rows[0] = g_new (gushort, ((gsize) sgip->xsize) * sgip->zsize);
+
+ for (i = 1; i < sgip->zsize; i ++)
+ rows[i] = rows[0] + i * sgip->xsize;
+
+ /*
+ * Save the image...
+ */
+
+ for (y = 0; y < height; y += count)
+ {
+ /*
+ * Grab more pixel data...
+ */
+
+ if ((y + tile_height) >= height)
+ count = height - y;
+ else
+ count = tile_height;
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, y, width, count), 1.0,
+ format, pixels[0],
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /*
+ * Convert to shorts and write each color plane separately...
+ */
+
+ for (i = 0, pptr = pixels[0]; i < count; i ++)
+ {
+ for (x = 0; x < width; x ++)
+ for (j = 0; j < zsize; j ++, pptr ++)
+ rows[j][x] = *pptr;
+
+ for (j = 0; j < zsize; j ++)
+ sgiPutRow (sgip, rows[j], height - 1 - y - i, j);
+ };
+
+ gimp_progress_update ((double) y / (double) height);
+ }
+
+ /*
+ * Done with the file...
+ */
+
+ sgiClose (sgip);
+
+ g_free (pixels[0]);
+ g_free (pixels);
+ g_free (rows[0]);
+ g_free (rows);
+
+ g_object_unref (buffer);
+
+ gimp_progress_update (1.0);
+
+ return TRUE;
+}
+
+static gboolean
+save_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *frame;
+ gboolean run;
+
+ dialog = gimp_export_dialog_new (_("SGI"), PLUG_IN_BINARY, SAVE_PROC);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Compression type"),
+ G_CALLBACK (gimp_radio_button_update),
+ &compression, compression,
+
+ _("_No compression"),
+ SGI_COMP_NONE, NULL,
+ _("_RLE compression"),
+ SGI_COMP_RLE, NULL,
+ _("_Aggressive RLE\n(not supported by SGI)"),
+ SGI_COMP_ARLE, NULL,
+
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/file-tiff/Makefile.am b/plug-ins/file-tiff/Makefile.am
new file mode 100644
index 0000000..8969040
--- /dev/null
+++ b/plug-ins/file-tiff/Makefile.am
@@ -0,0 +1,58 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_tiff_RC = file-tiff.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-tiff
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-tiff
+
+file_tiff_SOURCES = \
+ file-tiff.c \
+ file-tiff-io.c \
+ file-tiff-io.h \
+ file-tiff-load.c \
+ file-tiff-load.h \
+ file-tiff-save.c \
+ file-tiff-save.h
+
+file_tiff_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(TIFF_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tiff_RC)
diff --git a/plug-ins/file-tiff/Makefile.in b/plug-ins/file-tiff/Makefile.in
new file mode 100644
index 0000000..5ad29fd
--- /dev/null
+++ b/plug-ins/file-tiff/Makefile.in
@@ -0,0 +1,1022 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-tiff$(EXEEXT)
+subdir = plug-ins/file-tiff
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_tiff_OBJECTS = file-tiff.$(OBJEXT) file-tiff-io.$(OBJEXT) \
+ file-tiff-load.$(OBJEXT) file-tiff-save.$(OBJEXT)
+file_tiff_OBJECTS = $(am_file_tiff_OBJECTS)
+am__DEPENDENCIES_1 =
+file_tiff_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_tiff_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file-tiff-io.Po \
+ ./$(DEPDIR)/file-tiff-load.Po ./$(DEPDIR)/file-tiff-save.Po \
+ ./$(DEPDIR)/file-tiff.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_tiff_SOURCES)
+DIST_SOURCES = $(file_tiff_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-tiff
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_tiff_RC = file-tiff.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+file_tiff_SOURCES = \
+ file-tiff.c \
+ file-tiff-io.c \
+ file-tiff-io.h \
+ file-tiff-load.c \
+ file-tiff-load.h \
+ file-tiff-save.c \
+ file-tiff-save.h
+
+file_tiff_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(TIFF_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tiff_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-tiff/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-tiff/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-tiff$(EXEEXT): $(file_tiff_OBJECTS) $(file_tiff_DEPENDENCIES) $(EXTRA_file_tiff_DEPENDENCIES)
+ @rm -f file-tiff$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_tiff_OBJECTS) $(file_tiff_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-io.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file-tiff-io.Po
+ -rm -f ./$(DEPDIR)/file-tiff-load.Po
+ -rm -f ./$(DEPDIR)/file-tiff-save.Po
+ -rm -f ./$(DEPDIR)/file-tiff.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file-tiff-io.Po
+ -rm -f ./$(DEPDIR)/file-tiff-load.Po
+ -rm -f ./$(DEPDIR)/file-tiff-save.Po
+ -rm -f ./$(DEPDIR)/file-tiff.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-tiff/file-tiff-io.c b/plug-ins/file-tiff/file-tiff-io.c
new file mode 100644
index 0000000..09ef81f
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-io.c
@@ -0,0 +1,585 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+
+static gboolean tiff_file_size_error = FALSE;
+
+typedef struct
+{
+ GFile *file;
+ GObject *stream;
+ GInputStream *input;
+ GOutputStream *output;
+ gboolean can_seek;
+
+ gchar *buffer;
+ gsize allocated;
+ gsize used;
+ gsize position;
+} TiffIO;
+
+
+static TIFFExtendProc parent_extender;
+
+static void tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static void tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static tsize_t tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static tsize_t tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static toff_t tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence);
+static gint tiff_io_close (thandle_t handle);
+static toff_t tiff_io_get_file_size (thandle_t handle);
+static void register_geotags (TIFF *tif);
+
+static void
+register_geotags (TIFF *tif)
+{
+ static gboolean geotifftags_registered = FALSE;
+
+ if (geotifftags_registered)
+ return;
+
+ geotifftags_registered = TRUE;
+
+ TIFFMergeFieldInfo (tif, geotifftags_fieldinfo, (sizeof (geotifftags_fieldinfo) / sizeof (geotifftags_fieldinfo[0])));
+
+ if (parent_extender)
+ (*parent_extender) (tif);
+}
+
+static TiffIO tiff_io = { 0, };
+
+
+TIFF *
+tiff_open (GFile *file,
+ const gchar *mode,
+ GError **error)
+{
+ TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning);
+ TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error);
+
+ parent_extender = TIFFSetTagExtender (register_geotags);
+
+ tiff_io.file = file;
+
+ if (! strcmp (mode, "r"))
+ {
+ tiff_io.input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! tiff_io.input)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.input);
+ }
+#ifdef TIFF_VERSION_BIG
+ else if(! strcmp (mode, "w") || ! strcmp (mode, "w8"))
+#else
+ else if(! strcmp (mode, "w"))
+#endif
+ {
+ tiff_io.output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE,
+ G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! tiff_io.output)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.output);
+ }
+ else if(! strcmp (mode, "a"))
+ {
+ GIOStream *iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL,
+ error));
+ if (! iostream)
+ return NULL;
+
+ tiff_io.input = g_io_stream_get_input_stream (iostream);
+ tiff_io.output = g_io_stream_get_output_stream (iostream);
+ tiff_io.stream = G_OBJECT (iostream);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+#if 0
+#warning FIXME !can_seek code is broken
+ tiff_io.can_seek = g_seekable_can_seek (G_SEEKABLE (tiff_io.stream));
+#endif
+ tiff_io.can_seek = TRUE;
+
+ return TIFFClientOpen ("file-tiff", mode,
+ (thandle_t) &tiff_io,
+ tiff_io_read,
+ tiff_io_write,
+ tiff_io_seek,
+ tiff_io_close,
+ tiff_io_get_file_size,
+ NULL, NULL);
+}
+
+gboolean
+tiff_got_file_size_error (void)
+{
+ return tiff_file_size_error;
+}
+
+void
+tiff_reset_file_size_error (void)
+{
+ tiff_file_size_error = FALSE;
+}
+
+static void
+tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gint tag = 0;
+
+ /* Between libtiff 3.7.0beta2 and 4.0.0alpha. */
+ if (! strcmp (fmt, "%s: unknown field with tag %d (0x%x) encountered") ||
+ /* Before libtiff 3.7.0beta2. */
+ ! strcmp (fmt, "%.1000s: unknown field with tag %d (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ va_arg (ap_test, const char *); /* ignore first arg */
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ /* for older versions of libtiff? */
+ else if (! strcmp (fmt, "unknown field with tag %d (0x%x) ignored") ||
+ /* Since libtiff 4.0.0alpha. */
+ ! strcmp (fmt, "Unknown field with tag %d (0x%x) encountered") ||
+ /* Since libtiff 4.3.0rc1. */
+ ! strcmp (fmt, "Unknown field with tag %u (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ else if (! strcmp (fmt, "Incorrect value for \"%s\"; tag ignored"))
+ {
+ va_list ap_test;
+ const char *stag;
+
+ G_VA_COPY (ap_test, ap);
+
+ stag = va_arg (ap_test, const char *);
+
+ if (! strcmp (stag, "RichTIFFIPTC"))
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ /* This is an error in Adobe products. Just report to stderr. */
+ g_printerr ("[%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ va_end (ap_test);
+ }
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=131975
+ * Ignore the warnings about unregistered private tags (>= 32768).
+ */
+ if (tag >= 32768)
+ return;
+
+ /* Other unknown fields are only reported to stderr. */
+ if (tag > 0)
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("%s\n", msg);
+ g_free (msg);
+
+ return;
+ }
+ else if (! strcmp (module, "TIFFReadDirectory") &&
+ ! strcmp (fmt,
+ "Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel."
+ " Defining non-color channels as ExtraSamples."))
+ {
+ /* We will process this issue in our code. Just report to stderr. */
+ g_printerr ("%s: [%s] %s\n", G_STRFUNC, module, fmt);
+
+ return;
+ }
+ else if (! strcmp (module, "Fax4Decode") ||
+ g_str_has_prefix (module, "Fax3Decode"))
+ {
+ /* Certain corrupt TIFF Fax images can produce a large amount of
+ * warnings which can cause GIMP to run out of GDI resources on
+ * Windows and eventually crash.
+ * The real problem seems to be that the amount of error console
+ * messages does not have a limit.
+ * See e.g. the first page of m1-8110934bb3b18d0e87ccc1ddfc5f0107.tif
+ * from imagetestsuite. LibTiff does not return -1 from
+ * ReadScanline, presumably because for fax images it's not
+ * unreasonable to expect certain lines to fail.
+ * Let's just only report to stderr in this case. */
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("LibTiff warning: [%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
+}
+
+static void
+tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gchar *msg;
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=132297
+ * Ignore the errors related to random access and JPEG compression
+ */
+ if (! strcmp (fmt, "Compression algorithm does not support random access"))
+ return;
+
+ msg = g_strdup_vprintf (fmt, ap);
+
+#ifdef TIFF_VERSION_BIG
+ if (g_strcmp0 (fmt, "Maximum TIFF file size exceeded") == 0)
+ /* @module in my tests were "TIFFAppendToStrip" but I wonder if
+ * this same error could not happen with other "modules".
+ */
+ tiff_file_size_error = TRUE;
+ else
+ /* Easier for debugging to at least print messages on stderr. */
+ g_printerr ("LibTiff error: [%s] %s\n", module, msg);
+#endif
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "%s", msg);
+ g_free (msg);
+}
+
+static tsize_t
+tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize read = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_read = 0;
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) buffer, (gsize) size,
+ &bytes_read,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ read = bytes_read;
+ }
+ else
+ {
+ if (io->position + size > io->used)
+ {
+ gsize missing;
+ gsize bytes_read;
+
+ missing = io->position + size - io->used;
+
+ if (io->used + missing > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size = 1;
+ gsize needed;
+
+ needed = io->used + missing - io->allocated;
+ while (new_size < io->allocated + needed)
+ new_size *= 2;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) (io->buffer + io->used),
+ missing,
+ &bytes_read, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ io->used += bytes_read;
+ }
+
+ g_assert (io->position + size <= io->used);
+
+ memcpy (buffer, io->buffer + io->position, size);
+ io->position += size;
+
+ read = size;
+ }
+
+ return (tsize_t) read;
+}
+
+static tsize_t
+tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize written = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_written = 0;
+
+ if (! g_output_stream_write_all (io->output,
+ (void *) buffer, (gsize) size,
+ &bytes_written,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ written = bytes_written;
+ }
+ else
+ {
+ if (io->position + size > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size;
+
+ new_size = io->position + size;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ g_assert (io->position + size <= io->allocated);
+
+ memcpy (io->buffer + io->position, buffer, size);
+ io->position += size;
+
+ io->used = MAX (io->used, io->position);
+
+ written = size;
+ }
+
+ return (tsize_t) written;
+}
+
+static GSeekType
+lseek_to_seek_type (gint whence)
+{
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ return G_SEEK_SET;
+
+ case SEEK_CUR:
+ return G_SEEK_CUR;
+
+ case SEEK_END:
+ return G_SEEK_END;
+ }
+}
+
+static toff_t
+tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean sought = FALSE;
+ goffset position = -1;
+
+ if (io->can_seek)
+ {
+ sought = g_seekable_seek (G_SEEKABLE (io->stream),
+ (goffset) offset, lseek_to_seek_type (whence),
+ NULL, &error);
+ if (sought)
+ {
+ position = g_seekable_tell (G_SEEKABLE (io->stream));
+ }
+ else
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ if (offset <= io->used)
+ position = io->position = offset;
+ break;
+
+ case SEEK_CUR:
+ if (io->position + offset <= io->used)
+ position = io->position += offset;
+ break;
+
+ case G_SEEK_END:
+ if (io->used + offset <= io->used)
+ position = io->position = io->used + offset;
+ break;
+ }
+ }
+
+ return (toff_t) position;
+}
+
+static gint
+tiff_io_close (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean closed = FALSE;
+
+ if (io->input && ! io->output)
+ {
+ closed = g_input_stream_close (io->input, NULL, &error);
+ }
+ else
+ {
+ if (! io->can_seek && io->buffer && io->allocated)
+ {
+ if (! g_output_stream_write_all (io->output,
+ (void *) io->buffer,
+ io->allocated,
+ NULL, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (io->input)
+ {
+ closed = g_io_stream_close (G_IO_STREAM (io->stream), NULL, &error);
+ }
+ else
+ {
+ closed = g_output_stream_close (io->output, NULL, &error);
+ }
+ }
+
+ if (! closed)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (io->stream);
+ io->stream = NULL;
+ io->input = NULL;
+ io->output = NULL;
+
+ g_free (io->buffer);
+ io->buffer = NULL;
+
+ io->allocated = 0;
+ io->used = 0;
+ io->position = 0;
+
+ return closed ? 0 : -1;
+}
+
+static toff_t
+tiff_io_get_file_size (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ GFileInfo *info;
+ goffset size = 0;
+
+ info = g_file_query_info (io->file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+ if (! info)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ size = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+
+ return (toff_t) size;
+}
diff --git a/plug-ins/file-tiff/file-tiff-io.h b/plug-ins/file-tiff/file-tiff-io.h
new file mode 100644
index 0000000..9f17e48
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-io.h
@@ -0,0 +1,50 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_IO_H__
+#define __FILE_TIFF_IO_H__
+
+/* Adding support for GeoTIFF Tags */
+
+#define GEOTIFF_MODELPIXELSCALE 33550
+#define GEOTIFF_MODELTIEPOINT 33922
+#define GEOTIFF_MODELTRANSFORMATION 34264
+#define GEOTIFF_KEYDIRECTORY 34735
+#define GEOTIFF_DOUBLEPARAMS 34736
+#define GEOTIFF_ASCIIPARAMS 34737
+
+static const TIFFFieldInfo geotifftags_fieldinfo[] = {
+ { GEOTIFF_MODELPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoModelPixelScale" },
+ { GEOTIFF_MODELTIEPOINT, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" },
+ { GEOTIFF_MODELTRANSFORMATION, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoModelTransformation" },
+ { GEOTIFF_KEYDIRECTORY, -1, -1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" },
+ { GEOTIFF_DOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" },
+ { GEOTIFF_ASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoAsciiParams" }
+};
+
+TIFF * tiff_open (GFile *file,
+ const gchar *mode,
+ GError **error);
+
+gboolean tiff_got_file_size_error (void);
+void tiff_reset_file_size_error (void);
+
+#endif /* __FILE_TIFF_IO_H__ */
diff --git a/plug-ins/file-tiff/file-tiff-load.c b/plug-ins/file-tiff/file-tiff-load.c
new file mode 100644
index 0000000..72e9538
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-load.c
@@ -0,0 +1,2815 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_ROLE "gimp-file-tiff-load"
+
+
+typedef struct
+{
+ gint compression;
+ gint fillorder;
+ gboolean save_transp_pixels;
+} TiffSaveVals;
+
+typedef struct
+{
+ gint32 ID;
+ GeglBuffer *buffer;
+ const Babl *format;
+ guchar *pixels;
+ guchar *pixel;
+} ChannelData;
+
+typedef enum
+{
+ GIMP_TIFF_LOAD_ASSOCALPHA,
+ GIMP_TIFF_LOAD_UNASSALPHA,
+ GIMP_TIFF_LOAD_CHANNEL
+} DefaultExtra;
+
+typedef enum
+{
+ GIMP_TIFF_DEFAULT,
+ GIMP_TIFF_INDEXED,
+ GIMP_TIFF_GRAY,
+ GIMP_TIFF_GRAY_MINISWHITE,
+} TiffColorMode;
+
+/* Declare some local functions */
+
+static GimpColorProfile * load_profile (TIFF *tif);
+
+static void load_rgba (TIFF *tif,
+ ChannelData *channel);
+static void load_contiguous (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra);
+static void load_separate (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra);
+static void load_paths (TIFF *tif,
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y);
+
+static gboolean is_non_conformant_tiff (gushort photomet,
+ gushort spp);
+static gushort get_extra_channels_count (gushort photomet,
+ gushort spp,
+ gboolean alpha);
+
+static void fill_bit2byte (TiffColorMode tiff_mode);
+static void fill_2bit2byte (TiffColorMode tiff_mode);
+static void fill_4bit2byte (TiffColorMode tiff_mode);
+static void convert_bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+static void convert_2bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+static void convert_4bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+
+static void convert_miniswhite (guchar *buffer,
+ gint width,
+ gint height);
+static void convert_int2uint (guchar *buffer,
+ gint bps,
+ gint spp,
+ gint width,
+ gint height,
+ gint stride);
+
+static gboolean load_dialog (const gchar *help_id,
+ TiffSelectedPages *pages,
+ const gchar *extra_message,
+ DefaultExtra *default_extra);
+
+static void tiff_dialog_show_reduced (GtkWidget *toggle,
+ gpointer data);
+
+
+static TiffSaveVals tsvals =
+{
+ COMPRESSION_NONE, /* compression */
+ TRUE, /* alpha handling */
+};
+
+/* Grayscale conversion mappings */
+static const guchar _1_to_8_bitmap [2] =
+{
+ 0, 255
+};
+
+static const guchar _1_to_8_bitmap_rev [2] =
+{
+ 255, 0
+};
+
+static const guchar _2_to_8_bitmap [4] =
+{
+ 0, 85, 170, 255
+};
+
+static const guchar _2_to_8_bitmap_rev [4] =
+{
+ 255, 170, 85, 0
+};
+
+static const guchar _4_to_8_bitmap [16] =
+{
+ 0, 17, 34, 51, 68, 85, 102, 119,
+ 136, 153, 170, 187, 204, 221, 238, 255
+};
+
+static const guchar _4_to_8_bitmap_rev [16] =
+{
+ 255, 238, 221, 204, 187, 170, 153, 136,
+ 119, 102, 85, 68, 51, 34, 17, 0
+};
+
+static guchar bit2byte[256 * 8];
+static guchar _2bit2byte[256 * 4];
+static guchar _4bit2byte[256 * 2];
+
+
+/* returns a pointer into the TIFF */
+static const gchar *
+tiff_get_page_name (TIFF *tif)
+{
+ static gchar *name;
+
+ if (TIFFGetField (tif, TIFFTAG_PAGENAME, &name) &&
+ g_utf8_validate (name, -1, NULL))
+ {
+ return name;
+ }
+
+ return NULL;
+}
+
+/* is_non_conformant_tiff assumes TIFFTAG_EXTRASAMPLES was not set */
+static gboolean
+is_non_conformant_tiff (gushort photomet, gushort spp)
+{
+ switch (photomet)
+ {
+ case PHOTOMETRIC_RGB:
+ case PHOTOMETRIC_YCBCR:
+ case PHOTOMETRIC_CIELAB:
+ case PHOTOMETRIC_ICCLAB:
+ case PHOTOMETRIC_ITULAB:
+ case PHOTOMETRIC_LOGLUV:
+ return (spp > 3 || (spp == 2 && photomet != PHOTOMETRIC_RGB));
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ return (spp > 4);
+ break;
+ default:
+ return (spp > 1);
+ break;
+ }
+}
+
+/* get_extra_channels_count returns number of channels excluding
+ * alpha and color channels
+ */
+static gushort
+get_extra_channels_count (gushort photomet, gushort spp, gboolean alpha)
+{
+ switch (photomet)
+ {
+ case PHOTOMETRIC_RGB:
+ case PHOTOMETRIC_YCBCR:
+ case PHOTOMETRIC_CIELAB:
+ case PHOTOMETRIC_ICCLAB:
+ case PHOTOMETRIC_ITULAB:
+ case PHOTOMETRIC_LOGLUV:
+ if (spp >= 3)
+ return spp - 3 - (alpha? 1 : 0);
+ else
+ return spp - 1 - (alpha? 1 : 0);
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ return spp - 4 - (alpha? 1 : 0);
+ break;
+ default:
+ return spp - 1 - (alpha? 1 : 0);
+ break;
+ }
+}
+
+GimpPDBStatusType
+load_image (GFile *file,
+ GimpRunMode run_mode,
+ gint32 *image,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ TIFF *tif;
+ TiffSelectedPages pages;
+
+ GList *images_list = NULL;
+ DefaultExtra default_extra = GIMP_TIFF_LOAD_UNASSALPHA;
+ gint first_image_type = GIMP_RGB;
+ gint min_row = G_MAXINT;
+ gint min_col = G_MAXINT;
+ gint max_row = 0;
+ gint max_col = 0;
+ GimpColorProfile *first_profile = NULL;
+ const gchar *extra_message = NULL;
+ gint li;
+ gint selectable_pages;
+
+ *image = 0;
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ tif = tiff_open (file, "r", error);
+ if (! tif)
+ {
+ if (! (error && *error))
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Not a TIFF image or image is corrupt."));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+ gimp_get_data (LOAD_PROC "-target", &pages.target);
+
+ pages.keep_empty_space = TRUE;
+ gimp_get_data (LOAD_PROC "-keep-empty-space",
+ &pages.keep_empty_space);
+
+ pages.n_pages = pages.o_pages = TIFFNumberOfDirectories (tif);
+ if (pages.n_pages == 0)
+ {
+ /* See #5837.
+ * It seems we might be able to rescue some data even though the
+ * TIFF is possibly syntactically wrong.
+ */
+
+ /* libtiff says max number of directory is 65535. */
+ for (li = 0; li < 65536; li++)
+ {
+ if (TIFFSetDirectory (tif, li) == 0)
+ break;
+ }
+ pages.n_pages = li;
+ if (pages.n_pages == 0)
+ {
+ TIFFClose (tif);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("TIFF '%s' does not contain any directories"),
+ gimp_file_get_utf8_name (file));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ TIFFSetDirectory (tif, 0);
+ g_message (ngettext ("TIFF '%s' directory count by header failed "
+ "though there seems to be %d page."
+ " Attempting to load the file with this assumption.",
+ "TIFF '%s' directory count by header failed "
+ "though there seem to be %d pages."
+ " Attempting to load the file with this assumption.",
+ pages.n_pages),
+ gimp_file_get_utf8_name (file), pages.n_pages);
+ }
+
+ pages.pages = NULL;
+ pages.n_filtered_pages = pages.n_pages;
+ pages.n_reducedimage_pages = pages.n_pages;
+
+ pages.filtered_pages = g_new0 (gint, pages.n_pages);
+ for (li = 0; li < pages.n_pages; li++)
+ pages.filtered_pages[li] = li;
+
+ if (pages.n_pages == 1 || run_mode != GIMP_RUN_INTERACTIVE)
+ {
+ pages.pages = g_new0 (gint, pages.n_pages);
+ for (li = 0; li < pages.n_pages; li++)
+ pages.pages[li] = li;
+ pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+ }
+
+ /* Check all pages if any has an unspecified or unset channel. */
+ for (li = 0; li < pages.n_pages; li++)
+ {
+ gushort spp;
+ gushort photomet;
+ gushort extra;
+ gushort *extra_types;
+ gushort file_type = 0;
+ gboolean first_page_old_jpeg = FALSE;
+
+ if (TIFFSetDirectory (tif, li) == 0)
+ continue;
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTRLEW))
+ {
+ photomet = PHOTOMETRIC_MINISWHITE;
+ }
+ else
+ {
+ /* old AppleScan software misses out the photometric tag
+ * (and incidentally assumes min-is-white, but xv
+ * assumes min-is-black, so we follow xv's lead. It's
+ * not much hardship to invert the image later).
+ */
+ photomet = PHOTOMETRIC_MINISBLACK;
+ }
+ }
+ if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
+ extra = 0;
+
+ /* Try to detect if a TIFF page is a thumbnail.
+ * Easy case: if subfiletype is set to FILETYPE_REDUCEDIMAGE.
+ * If no subfiletype is defined we try to detect it ourselves.
+ * We will consider it a thumbnail if:
+ * - It's the second page
+ * - PhotometricInterpretation is YCbCr
+ * - Compression is old style jpeg
+ * - First page uses a different compression or PhotometricInterpretation
+ *
+ * We could also add a check for the presence of TIFFTAG_EXIFIFD since
+ * this should usually be a thumbnail part of EXIF metadata. Since that
+ * probably won't make a difference, I will leave that out for now.
+ */
+ if (li == 0)
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ compression == COMPRESSION_OJPEG &&
+ photomet == PHOTOMETRIC_YCBCR)
+ first_page_old_jpeg = TRUE;
+ }
+
+ if (TIFFGetField (tif, TIFFTAG_SUBFILETYPE, &file_type))
+ {
+ if (file_type == FILETYPE_REDUCEDIMAGE)
+ {
+ /* file_type is a mask but we will only filter out pages
+ * that only have FILETYPE_REDUCEDIMAGE set */
+ pages.filtered_pages[li] = TIFF_REDUCEDFILE;
+ pages.n_filtered_pages--;
+ g_debug ("Page %d is a FILETYPE_REDUCEDIMAGE thumbnail.\n", li);
+ }
+ }
+ else
+ {
+ if (li == 1 && photomet == PHOTOMETRIC_YCBCR &&
+ ! first_page_old_jpeg)
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ compression == COMPRESSION_OJPEG)
+ {
+ pages.filtered_pages[li] = TIFF_MISC_THUMBNAIL;
+ pages.n_filtered_pages--;
+ /* This is used to conditionally show reduced images
+ * if they're not a thumbnail
+ */
+ pages.n_reducedimage_pages--;
+ g_debug ("Page %d is most likely a thumbnail.\n", li);
+ }
+ }
+ }
+
+ /* TODO: current code always assumes that the alpha channel
+ * will be the first extra channel, though the TIFF spec does
+ * not mandate such assumption. A future improvement should be
+ * to actually loop through the extra channels and save the
+ * alpha channel index.
+ * Of course, this is an edge case, as most image would likely
+ * have only a single extra channel anyway. But still we could
+ * be more accurate.
+ */
+ if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
+ {
+ extra_message = _("Extra channels with unspecified data.");
+ break;
+ }
+ else if (extra == 0 && is_non_conformant_tiff (photomet, spp))
+ {
+ /* ExtraSamples field not set, yet we have more channels than
+ * the PhotometricInterpretation field suggests.
+ * This should not happen as the spec clearly says "This field
+ * must be present if there are extra samples". So the files
+ * can be considered non-conformant.
+ * Let's ask what to do with the channel.
+ */
+ extra_message = _("Non-conformant TIFF: extra channels without 'ExtraSamples' field.");
+ }
+ }
+ TIFFSetDirectory (tif, 0);
+
+ pages.show_reduced = FALSE;
+ if (pages.n_reducedimage_pages - pages.n_filtered_pages > 1)
+ pages.show_reduced = TRUE;
+
+ pages.tif = tif;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ (pages.n_pages > 1 || extra_message) &&
+ ! load_dialog (LOAD_PROC, &pages,
+ extra_message, &default_extra))
+ {
+ TIFFClose (tif);
+ g_clear_pointer (&pages.pages, g_free);
+
+ return GIMP_PDB_CANCEL;
+ }
+
+ selectable_pages = pages.n_filtered_pages;
+ if (pages.show_reduced)
+ selectable_pages = pages.n_reducedimage_pages;
+
+ /* Adjust pages to take filtered out pages into account. */
+ if (pages.o_pages > selectable_pages)
+ {
+ gint fi;
+ gint sel_index = 0;
+ gint sel_add = 0;
+
+ for (fi = 0; fi < pages.o_pages && sel_index < pages.n_pages; fi++)
+ {
+ if ((pages.show_reduced && pages.filtered_pages[fi] == TIFF_MISC_THUMBNAIL) ||
+ (! pages.show_reduced && pages.filtered_pages[fi] <= TIFF_MISC_THUMBNAIL))
+ {
+ sel_add++;
+ }
+ if (pages.pages[sel_index] + sel_add == fi)
+ {
+ pages.pages[sel_index] = fi;
+ sel_index++;
+ }
+ }
+ }
+
+ gimp_set_data (LOAD_PROC "-target",
+ &pages.target, sizeof (pages.target));
+ gimp_set_data (LOAD_PROC "-keep-empty-space",
+ &pages.keep_empty_space,
+ sizeof (pages.keep_empty_space));
+
+ /* We will loop through the all pages in case of multipage TIFF
+ * and load every page as a separate layer.
+ */
+ for (li = 0; li < pages.n_pages; li++)
+ {
+ gint ilayer;
+ gushort bps;
+ gushort spp;
+ gushort photomet;
+ gshort sampleformat;
+ GimpColorProfile *profile;
+ gboolean profile_linear = FALSE;
+ GimpPrecision image_precision;
+ const Babl *type;
+ const Babl *base_format = NULL;
+ const Babl *space = NULL;
+ guint16 orientation;
+ gint cols;
+ gint rows;
+ gboolean alpha;
+ gint image_type = GIMP_RGB;
+ gint layer;
+ gint layer_type = GIMP_RGB_IMAGE;
+ float layer_offset_x = 0.0;
+ float layer_offset_y = 0.0;
+ gint layer_offset_x_pixel = 0;
+ gint layer_offset_y_pixel = 0;
+ gushort extra;
+ gushort *extra_types;
+ ChannelData *channel = NULL;
+ uint16 planar = PLANARCONFIG_CONTIG;
+ TiffColorMode tiff_mode;
+ gboolean is_signed;
+ gint i;
+ gboolean worst_case = FALSE;
+ TiffSaveVals save_vals;
+ const gchar *name;
+
+ if (TIFFSetDirectory (tif, pages.pages[li]) == 0)
+ {
+ g_message (_("Couldn't read page %d of %d. Image might be corrupt.\n"),
+ li+1, pages.n_pages);
+ continue;
+ }
+ ilayer = pages.pages[li];
+
+ gimp_progress_update (0.0);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
+
+ profile = load_profile (tif);
+ if (! profile && first_profile)
+ {
+ profile = first_profile;
+ g_object_ref (profile);
+ }
+
+ if (profile)
+ {
+ profile_linear = gimp_color_profile_is_linear (profile);
+
+ if (! first_profile)
+ {
+ first_profile = profile;
+ g_object_ref (first_profile);
+
+ if (profile_linear && li > 0 && pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ g_message (_("This image has a linear color profile but "
+ "it was not set on the first layer. "
+ "The layers below layer # %d will be "
+ "interpreted as non linear."), li+1);
+ }
+ else if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES &&
+ ! gimp_color_profile_is_equal (first_profile, profile))
+ {
+ g_message (_("This image has multiple color profiles. "
+ "We will use the first one. If this leads "
+ "to incorrect results you should consider "
+ "loading each layer as a separate image."));
+ }
+
+ if (! *image)
+ *profile_loaded = TRUE;
+ }
+
+ if (bps > 64)
+ {
+ g_message (_("Suspicious bit depth: %d for page %d. Image may be corrupt."),
+ bps, li+1);
+ continue;
+ }
+
+ if (bps > 8 && bps != 8 && bps != 16 && bps != 32 && bps != 64)
+ worst_case = TRUE; /* Wrong sample width => RGBA */
+
+ switch (bps)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U8_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U8_GAMMA;
+
+ type = babl_type ("u8");
+ break;
+
+ case 16:
+ if (sampleformat == SAMPLEFORMAT_IEEEFP)
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_HALF_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_HALF_GAMMA;
+
+ type = babl_type ("half");
+ }
+ else
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U16_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U16_GAMMA;
+
+ type = babl_type ("u16");
+ }
+ break;
+
+ case 32:
+ if (sampleformat == SAMPLEFORMAT_IEEEFP)
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_FLOAT_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_FLOAT_GAMMA;
+
+ type = babl_type ("float");
+ }
+ else
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U32_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U32_GAMMA;
+
+ type = babl_type ("u32");
+ }
+ break;
+
+ case 64:
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_DOUBLE_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_DOUBLE_GAMMA;
+
+ type = babl_type ("double");
+ break;
+
+ default:
+ g_message (_("Unsupported bit depth: %d for page %d."),
+ bps, li+1);
+ continue;
+ }
+
+ g_printerr ("bps: %d\n", bps);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+
+ if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
+ extra = 0;
+
+ if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get image width from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get image length from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (cols > GIMP_MAX_IMAGE_SIZE || cols <= 0 ||
+ rows > GIMP_MAX_IMAGE_SIZE || rows <= 0)
+ {
+ g_message (_("Invalid image dimensions (%u x %u) for page %d. "
+ "Image may be corrupt."),
+ (guint32) cols, (guint32) rows, li+1);
+ continue;
+ }
+ else
+ {
+ g_printerr ("Image dimensions: %u x %u.\n",
+ (guint32) cols, (guint32) rows);
+ }
+
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTRLEW))
+ {
+ g_message (_("Could not get photometric from '%s'. "
+ "Image is CCITT compressed, assuming min-is-white"),
+ gimp_file_get_utf8_name (file));
+ photomet = PHOTOMETRIC_MINISWHITE;
+ }
+ else
+ {
+ g_message (_("Could not get photometric from '%s'. "
+ "Assuming min-is-black"),
+ gimp_file_get_utf8_name (file));
+
+ /* old AppleScan software misses out the photometric tag
+ * (and incidentally assumes min-is-white, but xv
+ * assumes min-is-black, so we follow xv's lead. It's
+ * not much hardship to invert the image later).
+ */
+ photomet = PHOTOMETRIC_MINISBLACK;
+ }
+ }
+
+ /* test if the extrasample represents an associated alpha channel... */
+ if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA))
+ {
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ extra--;
+ }
+ else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA))
+ {
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ extra--;
+ }
+ else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
+ {
+ if (run_mode != GIMP_RUN_INTERACTIVE)
+ /* In non-interactive mode, we assume unassociated alpha if unspecified.
+ * We don't output messages in interactive mode as the user
+ * has already the ability to choose through a dialog. */
+ g_message (_("Alpha channel type not defined for %s. "
+ "Assuming alpha is not premultiplied"),
+ gimp_file_get_utf8_name (file));
+
+ switch (default_extra)
+ {
+ case GIMP_TIFF_LOAD_ASSOCALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ break;
+ case GIMP_TIFF_LOAD_UNASSALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ break;
+ default: /* GIMP_TIFF_LOAD_CHANNEL */
+ alpha = FALSE;
+ break;
+ }
+ extra--;
+ }
+ else /* extra == 0 */
+ {
+ if (is_non_conformant_tiff (photomet, spp))
+ {
+ if (run_mode != GIMP_RUN_INTERACTIVE)
+ g_message (_("Image '%s' does not conform to the TIFF specification: "
+ "ExtraSamples field is not set while extra channels are present. "
+ "Assuming the first extra channel is non-premultiplied alpha."),
+ gimp_file_get_utf8_name (file));
+
+ switch (default_extra)
+ {
+ case GIMP_TIFF_LOAD_ASSOCALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ break;
+ case GIMP_TIFF_LOAD_UNASSALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ break;
+ default: /* GIMP_TIFF_LOAD_CHANNEL */
+ alpha = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ alpha = FALSE;
+ }
+ }
+
+ extra = get_extra_channels_count (photomet, spp, alpha);
+
+ tiff_mode = GIMP_TIFF_DEFAULT;
+ is_signed = sampleformat == SAMPLEFORMAT_INT;
+
+ switch (photomet)
+ {
+ case PHOTOMETRIC_PALETTE:
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_MINISWHITE:
+ /* Even for bps >= we may need to use tiff_mode, so always set it.
+ * Currently we use it to detect the need to convert 8 bps miniswhite. */
+ if (photomet == PHOTOMETRIC_PALETTE)
+ tiff_mode = GIMP_TIFF_INDEXED;
+ else if (photomet == PHOTOMETRIC_MINISBLACK)
+ tiff_mode = GIMP_TIFF_GRAY;
+ else if (photomet == PHOTOMETRIC_MINISWHITE)
+ tiff_mode = GIMP_TIFF_GRAY_MINISWHITE;
+
+ if (bps < 8)
+ {
+ /* FIXME: It should be a user choice whether this should be
+ * interpreted as indexed or grayscale. For now we will
+ * use indexed (see issue #6766). */
+ image_type = GIMP_INDEXED;
+ layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+
+ if ((bps == 1 || bps == 2 || bps == 4) && ! alpha && spp == 1)
+ {
+ if (bps == 1)
+ fill_bit2byte (tiff_mode);
+ else if (bps == 2)
+ fill_2bit2byte (tiff_mode);
+ else if (bps == 4)
+ fill_4bit2byte (tiff_mode);
+ }
+ }
+ else
+ {
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ image_type = GIMP_INDEXED;
+ layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+ }
+ else
+ {
+ image_type = GIMP_GRAY;
+ layer_type = alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
+ }
+ }
+
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ /* Do nothing here, handled later.
+ * Didn't want more indenting in the next part. */
+ }
+ else if (alpha)
+ {
+ if (tsvals.save_transp_pixels)
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("YA"),
+ type,
+ babl_component ("Y"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("YaA"),
+ type,
+ babl_component ("Ya"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'aA"),
+ type,
+ babl_component ("Y'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("Y"),
+ type,
+ babl_component ("Y"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_RGB:
+ image_type = GIMP_RGB;
+ layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+
+ if (alpha)
+ {
+ if (tsvals.save_transp_pixels)
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RGBA"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RGB"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_SEPARATED:
+ layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ /* It's possible that a CMYK image might not have an
+ * attached profile, so we'll check for it and set up
+ * space accordingly
+ */
+ if (profile && gimp_color_profile_is_cmyk (profile))
+ {
+ space = gimp_color_profile_get_space (profile,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ error);
+ g_clear_object (&profile);
+ }
+ else
+ {
+ space = NULL;
+ }
+
+ if (alpha)
+ base_format = babl_format_new (babl_model ("CMYKA"),
+ type,
+ babl_component ("Cyan"),
+ babl_component ("Magenta"),
+ babl_component ("Yellow"),
+ babl_component ("Key"),
+ babl_component ("A"),
+ NULL);
+ else
+ base_format = babl_format_new (babl_model ("CMYK"),
+ type,
+ babl_component ("Cyan"),
+ babl_component ("Magenta"),
+ babl_component ("Yellow"),
+ babl_component ("Key"),
+ NULL);
+
+ base_format =
+ babl_format_with_space (babl_format_get_encoding (base_format),
+ space);
+ break;
+
+ default:
+ g_printerr ("photomet: %d (%d)\n", photomet, PHOTOMETRIC_PALETTE);
+ worst_case = TRUE;
+ break;
+ }
+
+ /* attach a parasite containing the compression */
+ {
+ guint16 compression = COMPRESSION_NONE;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression))
+ {
+ switch (compression)
+ {
+ case COMPRESSION_NONE:
+ case COMPRESSION_LZW:
+ case COMPRESSION_PACKBITS:
+ case COMPRESSION_DEFLATE:
+ case COMPRESSION_ADOBE_DEFLATE:
+ case COMPRESSION_JPEG:
+ case COMPRESSION_CCITTFAX3:
+ case COMPRESSION_CCITTFAX4:
+ break;
+
+ case COMPRESSION_OJPEG:
+ worst_case = TRUE;
+ compression = COMPRESSION_JPEG;
+ break;
+
+ default:
+ g_message (_("Invalid or unknown compression %u. "
+ "Setting compression to none."),
+ compression);
+ compression = COMPRESSION_NONE;
+ break;
+ }
+ }
+
+ save_vals.compression = compression;
+ }
+
+ if (worst_case)
+ {
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGBA_IMAGE;
+
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
+ {
+ if (li == 0)
+ {
+ first_image_type = image_type;
+ }
+ else if (image_type != first_image_type)
+ {
+ continue;
+ }
+ }
+
+ if ((pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) || (! *image))
+ {
+ *image = gimp_image_new_with_precision (cols, rows, image_type,
+ image_precision);
+
+ if (*image < 1)
+ {
+ TIFFClose (tif);
+ g_message (_("Could not create a new image: %s"),
+ gimp_get_pdb_error ());
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_undo_disable (*image);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ gchar *fname = g_strdup_printf ("%s-%d", g_file_get_uri (file),
+ ilayer);
+
+ gimp_image_set_filename (*image, fname);
+ g_free (fname);
+
+ images_list = g_list_prepend (images_list,
+ GINT_TO_POINTER (*image));
+ }
+ else if (pages.o_pages != pages.n_pages)
+ {
+ gchar *fname = g_strdup_printf (_("%s-%d-of-%d-pages"),
+ g_file_get_uri (file),
+ pages.n_pages, pages.o_pages);
+
+ gimp_image_set_filename (*image, fname);
+ g_free (fname);
+ }
+ else
+ {
+ gimp_image_set_filename (*image, g_file_get_uri (file));
+ }
+ }
+
+ /* attach non-CMYK color profile */
+ if (profile)
+ {
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES || profile == first_profile)
+ gimp_image_set_color_profile (*image, profile);
+
+ g_object_unref (profile);
+ }
+
+ /* attach parasites */
+ {
+ GimpParasite *parasite;
+ const gchar *img_desc;
+
+ parasite = gimp_parasite_new ("tiff-save-options", 0,
+ sizeof (save_vals), &save_vals);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+
+ /* Attach a parasite containing the image description.
+ * Pretend to be a gimp comment so other plugins will use this
+ * description as an image comment where appropriate.
+ */
+ if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &img_desc) &&
+ g_utf8_validate (img_desc, -1, NULL))
+ {
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (img_desc) + 1, img_desc);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ /* Attach GeoTIFF Tags as Parasite, If available */
+ {
+ GimpParasite *parasite = NULL;
+ void *geotag_data = NULL;
+ uint32 count = 0;
+
+ if (TIFFGetField (tif, GEOTIFF_MODELPIXELSCALE, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelPixelScale",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_MODELTIEPOINT, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTiePoint",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_MODELTRANSFORMATION, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTransformation",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_KEYDIRECTORY, &count, &geotag_data) )
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_KeyDirectory",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_SHORT) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_DOUBLEPARAMS, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_DoubleParams",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_ASCIIPARAMS, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_Asciiparams",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_ASCII) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ /* any resolution info in the file? */
+ {
+ gfloat xres = 72.0;
+ gfloat yres = 72.0;
+ gushort read_unit;
+ GimpUnit unit = GIMP_UNIT_PIXEL; /* invalid unit */
+
+ if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres))
+ {
+ if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres))
+ {
+ if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT,
+ &read_unit))
+ {
+ switch (read_unit)
+ {
+ case RESUNIT_NONE:
+ /* ImageMagick writes files with this silly resunit */
+ break;
+
+ case RESUNIT_INCH:
+ unit = GIMP_UNIT_INCH;
+ break;
+
+ case RESUNIT_CENTIMETER:
+ xres *= 2.54;
+ yres *= 2.54;
+ unit = GIMP_UNIT_MM; /* this is our default metric unit */
+ break;
+
+ default:
+ g_message (_("Unknown resolution "
+ "unit type %d, assuming dpi"), read_unit);
+ break;
+ }
+ }
+ else
+ {
+ /* no res unit tag */
+
+ /* old AppleScan software produces these */
+ g_message (_("Warning: resolution specified without "
+ "unit type, assuming dpi"));
+ }
+ }
+ else
+ {
+ /* xres but no yres */
+
+ g_message (_("Warning: no y resolution info, assuming same as x"));
+ yres = xres;
+ }
+
+ /* now set the new image's resolution info */
+
+ /* If it is invalid, instead of forcing 72dpi, do not set
+ * the resolution at all. Gimp will then use the default
+ * set by the user
+ */
+ if (read_unit != RESUNIT_NONE)
+ {
+ if (! isfinite (xres) ||
+ xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
+ ! isfinite (yres) ||
+ yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
+ {
+ g_message (_("Invalid image resolution info, using default"));
+ /* We need valid xres and yres for computing
+ * layer_offset_x_pixel and layer_offset_y_pixel.
+ */
+ gimp_image_get_resolution (*image, &xres, &yres);
+ }
+ else
+ {
+ gimp_image_set_resolution (*image, xres, yres);
+ if (unit != GIMP_UNIT_PIXEL)
+ gimp_image_set_unit (*image, unit);
+
+ *resolution_loaded = TRUE;
+ }
+ }
+ }
+
+ /* no x res tag => we assume we have no resolution info, so we
+ * don't care. Older versions of this plugin used to write
+ * files with no resolution tags at all.
+ */
+
+ /* TODO: haven't caught the case where yres tag is present,
+ * but not xres. This is left as an exercise for the reader -
+ * they should feel free to shoot the author of the broken
+ * program that produced the damaged TIFF file in the first
+ * place.
+ */
+
+ /* handle layer offset */
+ if (! TIFFGetField (tif, TIFFTAG_XPOSITION, &layer_offset_x))
+ layer_offset_x = 0.0;
+
+ if (! TIFFGetField (tif, TIFFTAG_YPOSITION, &layer_offset_y))
+ layer_offset_y = 0.0;
+
+ /* round floating point position to integer position required
+ * by GIMP
+ */
+ layer_offset_x_pixel = ROUND (layer_offset_x * xres);
+ layer_offset_y_pixel = ROUND (layer_offset_y * yres);
+ }
+
+ /* Install colormap for INDEXED images only */
+ if (image_type == GIMP_INDEXED)
+ {
+ guchar cmap[768];
+
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ gushort *redmap;
+ gushort *greenmap;
+ gushort *bluemap;
+ gint i, j;
+
+ if (! TIFFGetField (tif, TIFFTAG_COLORMAP,
+ &redmap, &greenmap, &bluemap))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get colormaps from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = redmap[i] >> 8;
+ cmap[j++] = greenmap[i] >> 8;
+ cmap[j++] = bluemap[i] >> 8;
+ }
+
+ }
+ else if (photomet == PHOTOMETRIC_MINISBLACK)
+ {
+ gint i, j;
+
+ if (bps == 1)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _1_to_8_bitmap[i];
+ cmap[j++] = _1_to_8_bitmap[i];
+ cmap[j++] = _1_to_8_bitmap[i];
+ }
+ }
+ else if (bps == 2)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _2_to_8_bitmap[i];
+ cmap[j++] = _2_to_8_bitmap[i];
+ cmap[j++] = _2_to_8_bitmap[i];
+ }
+ }
+ else if (bps == 4)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _4_to_8_bitmap[i];
+ cmap[j++] = _4_to_8_bitmap[i];
+ cmap[j++] = _4_to_8_bitmap[i];
+ }
+ }
+ }
+ else if (photomet == PHOTOMETRIC_MINISWHITE)
+ {
+ gint i, j;
+
+ if (bps == 1)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ }
+ }
+ else if (bps == 2)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ }
+ }
+ else if (bps == 4)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ }
+ }
+ }
+
+ gimp_image_set_colormap (*image, cmap, (1 << bps));
+ }
+
+ if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ load_paths (tif, *image, cols, rows,
+ layer_offset_x_pixel, layer_offset_y_pixel);
+ else
+ load_paths (tif, *image, cols, rows, 0, 0);
+
+ if (extra > 99)
+ {
+ /* Validate number of channels to the same maximum as we use for
+ * Photoshop. A higher number most likely means a corrupt image
+ * and can cause GIMP to become unresponsive and/or stuck.
+ * See m2-d0f86ab189cbe900ec389ca6d7464713.tif from imagetestsuite
+ */
+ g_message (_("Suspicious number of extra channels: %d. Possibly corrupt image."), extra);
+ extra = 99;
+ }
+
+ /* Allocate ChannelData for all channels, even the background layer */
+ channel = g_new0 (ChannelData, extra + 1);
+
+ /* try and use layer name from tiff file */
+ name = tiff_get_page_name (tif);
+
+ if (name)
+ {
+ layer = gimp_layer_new (*image, name,
+ cols, rows,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image));
+ }
+ else
+ {
+ gchar *name;
+
+ if (ilayer == 0)
+ name = g_strdup (_("Background"));
+ else
+ name = g_strdup_printf (_("Page %d"), ilayer);
+
+ layer = gimp_layer_new (*image, name,
+ cols, rows,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image));
+ g_free (name);
+ }
+
+ if (! base_format && image_type == GIMP_INDEXED)
+ {
+ /* can't create the palette format here, need to get it from
+ * an existing layer
+ */
+ base_format = gimp_drawable_get_format (layer);
+ }
+ else if (! space)
+ {
+ base_format =
+ babl_format_with_space (babl_format_get_encoding (base_format),
+ gimp_drawable_get_format (layer));
+ }
+
+ channel[0].ID = layer;
+ channel[0].buffer = gimp_drawable_get_buffer (layer);
+ channel[0].format = base_format;
+
+ if (extra > 0 && ! worst_case)
+ {
+ /* Add extra channels as appropriate */
+ for (i = 1; i <= extra; i++)
+ {
+ GimpRGB color;
+
+ gimp_rgb_set (&color, 0.0, 0.0, 0.0);
+
+ channel[i].ID = gimp_channel_new (*image, _("TIFF Channel"),
+ cols, rows,
+ 100.0, &color);
+ gimp_image_insert_channel (*image, channel[i].ID, -1, 0);
+ channel[i].buffer = gimp_drawable_get_buffer (channel[i].ID);
+ channel[i].format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ }
+
+ TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar);
+
+ if (worst_case)
+ {
+ load_rgba (tif, channel);
+ }
+ else if (planar == PLANARCONFIG_CONTIG)
+ {
+ load_contiguous (tif, channel, type, bps, spp,
+ tiff_mode, is_signed, extra);
+ }
+ else
+ {
+ load_separate (tif, channel, type, bps, spp,
+ tiff_mode, is_signed, extra);
+ }
+
+ if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation))
+ {
+ gboolean flip_horizontal = FALSE;
+ gboolean flip_vertical = FALSE;
+
+ switch (orientation)
+ {
+ case ORIENTATION_TOPLEFT:
+ break;
+
+ case ORIENTATION_TOPRIGHT:
+ flip_horizontal = TRUE;
+ break;
+
+ case ORIENTATION_BOTRIGHT:
+ flip_horizontal = TRUE;
+ flip_vertical = TRUE;
+ break;
+
+ case ORIENTATION_BOTLEFT:
+ flip_vertical = TRUE;
+ break;
+
+ default:
+ g_warning ("Orientation %d not handled yet!", orientation);
+ break;
+ }
+
+ if (flip_horizontal)
+ gimp_item_transform_flip_simple (layer,
+ GIMP_ORIENTATION_HORIZONTAL,
+ TRUE /* auto_center */,
+ -1.0 /* axis */);
+
+ if (flip_vertical)
+ gimp_item_transform_flip_simple (layer,
+ GIMP_ORIENTATION_VERTICAL,
+ TRUE /* auto_center */,
+ -1.0 /* axis */);
+ }
+
+ for (i = 0; i <= extra; i++)
+ {
+ if (channel[i].buffer)
+ g_object_unref (channel[i].buffer);
+ }
+
+ g_free (channel);
+ channel = NULL;
+
+ if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ /* compute bounding box of all layers read so far */
+ if (min_col > layer_offset_x_pixel)
+ min_col = layer_offset_x_pixel;
+ if (min_row > layer_offset_y_pixel)
+ min_row = layer_offset_y_pixel;
+
+ if (max_col < layer_offset_x_pixel + cols)
+ max_col = layer_offset_x_pixel + cols;
+ if (max_row < layer_offset_y_pixel + rows)
+ max_row = layer_offset_y_pixel + rows;
+
+ /* position the layer */
+ if (layer_offset_x_pixel > 0 ||
+ layer_offset_y_pixel > 0)
+ {
+ gimp_layer_set_offsets (layer,
+ layer_offset_x_pixel,
+ layer_offset_y_pixel);
+ }
+ }
+
+ gimp_image_insert_layer (*image, layer, -1, -1);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ gimp_image_undo_enable (*image);
+ gimp_image_clean_all (*image);
+ }
+
+ gimp_progress_update (1.0);
+ }
+ g_clear_object (&first_profile);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ GList *list = images_list;
+
+ if (list)
+ {
+ *image = GPOINTER_TO_INT (list->data);
+
+ list = g_list_next (list);
+ }
+
+ for (; list; list = g_list_next (list))
+ {
+ gimp_display_new (GPOINTER_TO_INT (list->data));
+ }
+
+ g_list_free (images_list);
+ }
+ else
+ {
+ if (! (*image))
+ {
+ TIFFClose (tif);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("No data could be read from TIFF '%s'. The file is probably corrupted."),
+ gimp_file_get_utf8_name (file));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (pages.keep_empty_space)
+ {
+ /* unfortunately we have no idea about empty space
+ at the bottom/right of layers */
+ min_col = 0;
+ min_row = 0;
+ }
+
+ /* resize image to bounding box of all layers */
+ gimp_image_resize (*image,
+ max_col - min_col, max_row - min_row,
+ -min_col, -min_row);
+
+ gimp_image_undo_enable (*image);
+ }
+
+ g_free (pages.pages);
+ TIFFClose (tif);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static GimpColorProfile *
+load_profile (TIFF *tif)
+{
+ GimpColorProfile *profile = NULL;
+
+#ifdef TIFFTAG_ICCPROFILE
+ /* If TIFFTAG_ICCPROFILE is defined we are dealing with a
+ * libtiff version that can handle ICC profiles. Otherwise just
+ * return a NULL profile.
+ */
+ uint32 profile_size;
+ guchar *icc_profile;
+
+ /* set the ICC profile - if found in the TIFF */
+ if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile))
+ {
+ profile = gimp_color_profile_new_from_icc_profile (icc_profile,
+ profile_size,
+ NULL);
+ }
+#endif
+
+ return profile;
+}
+
+static void
+load_rgba (TIFF *tif,
+ ChannelData *channel)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 row;
+ guint32 *buffer;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ buffer = g_new (uint32, image_width * image_height);
+
+ if (! TIFFReadRGBAImage (tif, image_width, image_height, buffer, 0))
+ {
+ g_message (_("%s: Unsupported image format, no RGBA loader available"),
+ G_STRFUNC);
+ g_free (buffer);
+ return;
+ }
+
+ for (row = 0; row < image_height; row++)
+ {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ /* Make sure our channels are in the right order */
+ guint32 row_start = row * image_width;
+ guint32 row_end = row_start + image_width;
+ guint32 i;
+
+ for (i = row_start; i < row_end; i++)
+ buffer[i] = GUINT32_TO_LE (buffer[i]);
+#endif
+
+ gegl_buffer_set (channel[0].buffer,
+ GEGL_RECTANGLE (0, image_height - row - 1,
+ image_width, 1),
+ 0, channel[0].format,
+ ((guchar *) buffer) + row * image_width * 4,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if ((row % 32) == 0)
+ gimp_progress_update ((gdouble) row / (gdouble) image_height);
+ }
+
+ g_free (buffer);
+}
+
+static void
+load_paths (TIFF *tif,
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y)
+{
+ gsize n_bytes;
+ gchar *bytes;
+ gint path_index;
+ gsize pos;
+
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &n_bytes, &bytes))
+ return;
+
+ path_index = 0;
+ pos = 0;
+
+ while (pos < n_bytes)
+ {
+ guint16 id;
+ gsize len;
+ gchar *name;
+ guint32 *val32;
+ guint16 *val16;
+
+ if (n_bytes-pos < 7 ||
+ strncmp (bytes + pos, "8BIM", 4) != 0)
+ break;
+
+ pos += 4;
+
+ val16 = (guint16 *) (bytes + pos);
+ id = GUINT16_FROM_BE (*val16);
+ pos += 2;
+
+ /* g_printerr ("id: %x\n", id); */
+ len = (guchar) bytes[pos];
+
+ if (n_bytes - pos < len + 1)
+ break; /* block not big enough */
+
+ /* do we have the UTF-marker? is it valid UTF-8?
+ * if so, we assume an utf-8 encoded name, otherwise we
+ * assume iso8859-1
+ */
+ name = bytes + pos + 1;
+ if (len >= 3 &&
+ name[0] == '\xEF' && name[1] == '\xBB' && name[2] == '\xBF' &&
+ g_utf8_validate (name, len, NULL))
+ {
+ name = g_strndup (name + 3, len - 3);
+ }
+ else
+ {
+ name = g_convert (name, len, "utf-8", "iso8859-1", NULL, NULL, NULL);
+ }
+
+ if (! name)
+ name = g_strdup ("(imported path)");
+
+ pos += len + 1;
+
+ if (pos % 2) /* padding */
+ pos++;
+
+ if (n_bytes - pos < 4)
+ break; /* block not big enough */
+
+ val32 = (guint32 *) (bytes + pos);
+ len = GUINT32_FROM_BE (*val32);
+ pos += 4;
+
+ if (n_bytes - pos < len)
+ break; /* block not big enough */
+
+ if (id >= 2000 && id <= 2998)
+ {
+ /* path information */
+ guint16 type;
+ gint rec = pos;
+ gint32 vectors;
+ gdouble *points = NULL;
+ gint expected_points = 0;
+ gint pointcount = 0;
+ gboolean closed = FALSE;
+
+ vectors = gimp_vectors_new (image, name);
+ gimp_image_insert_vectors (image, vectors, -1, path_index);
+ path_index++;
+
+ while (rec < pos + len)
+ {
+ /* path records */
+ val16 = (guint16 *) (bytes + rec);
+ type = GUINT16_FROM_BE (*val16);
+
+ switch (type)
+ {
+ case 0: /* new closed subpath */
+ case 3: /* new open subpath */
+ val16 = (guint16 *) (bytes + rec + 2);
+ expected_points = GUINT16_FROM_BE (*val16);
+ pointcount = 0;
+ closed = (type == 0);
+
+ if (n_bytes - rec < (expected_points + 1) * 26)
+ {
+ g_printerr ("not enough point records\n");
+ rec = pos + len;
+ continue;
+ }
+
+ if (points)
+ g_free (points);
+ points = g_new (gdouble, expected_points * 6);
+ break;
+
+ case 1: /* closed subpath bezier knot, linked */
+ case 2: /* closed subpath bezier knot, unlinked */
+ case 4: /* open subpath bezier knot, linked */
+ case 5: /* open subpath bezier knot, unlinked */
+ /* since we already know if the subpath is open
+ * or closed and since we don't differentiate between
+ * linked and unlinked, just treat all the same... */
+
+ if (pointcount < expected_points)
+ {
+ gint j;
+
+ for (j = 0; j < 6; j++)
+ {
+ gdouble f;
+ guint32 coord;
+
+ const gint size = j % 2 ? width : height;
+ const gint offset = j % 2 ? offset_x : offset_y;
+
+ val32 = (guint32 *) (bytes + rec + 2 + j * 4);
+ coord = GUINT32_FROM_BE (*val32);
+
+ f = (double) ((gchar) ((coord >> 24) & 0xFF)) +
+ (double) (coord & 0x00FFFFFF) /
+ (double) 0xFFFFFF;
+
+ /* coords are stored with vertical component
+ * first, gimp expects the horizontal
+ * component first. Sigh.
+ */
+ points[pointcount * 6 + (j ^ 1)] = f * size + offset;
+ }
+
+ pointcount++;
+
+ if (pointcount == expected_points)
+ {
+ gimp_vectors_stroke_new_from_points (vectors,
+ GIMP_VECTORS_STROKE_TYPE_BEZIER,
+ pointcount * 6,
+ points,
+ closed);
+ }
+ }
+ else
+ {
+ g_printerr ("Oops - unexpected point record\n");
+ }
+
+ break;
+
+ case 6: /* path fill rule record */
+ case 7: /* clipboard record (?) */
+ case 8: /* initial fill rule record (?) */
+ /* we cannot use this information */
+
+ default:
+ break;
+ }
+
+ rec += 26;
+ }
+
+ if (points)
+ g_free (points);
+ }
+
+ pos += len;
+
+ if (pos % 2) /* padding */
+ pos++;
+
+ g_free (name);
+ }
+}
+
+
+static void
+load_contiguous (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 tile_width;
+ guint32 tile_height;
+ gint bytes_per_pixel;
+ const Babl *src_format;
+ guchar *buffer;
+ guchar *bw_buffer = NULL;
+ gdouble progress = 0.0;
+ gdouble one_row;
+ guint32 y;
+ gint i;
+ gboolean needs_upscale = FALSE;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ tile_width = image_width;
+
+ if (TIFFIsTiled (tif))
+ {
+ TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
+
+ buffer = g_malloc (TIFFTileSize (tif));
+ }
+ else
+ {
+ tile_width = image_width;
+ tile_height = 1;
+
+ buffer = g_malloc (TIFFScanlineSize (tif));
+ }
+
+ if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8)
+ {
+ needs_upscale = TRUE;
+ bw_buffer = g_malloc (tile_width * tile_height);
+ }
+
+ one_row = (gdouble) tile_height / (gdouble) image_height;
+
+ src_format = babl_format_n (type, spp);
+
+ /* consistency check */
+ bytes_per_pixel = 0;
+ for (i = 0; i <= extra; i++)
+ bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
+
+ g_printerr ("bytes_per_pixel: %d, format: %d\n",
+ bytes_per_pixel,
+ babl_format_get_bytes_per_pixel (src_format));
+
+ for (y = 0; y < image_height; y += tile_height)
+ {
+ guint32 x;
+
+ for (x = 0; x < image_width; x += tile_width)
+ {
+ GeglBuffer *src_buf;
+ guint32 rows;
+ guint32 cols;
+ gint offset;
+
+ gimp_progress_update (progress + one_row *
+ ((gdouble) x / (gdouble) image_width));
+
+ if (TIFFIsTiled (tif))
+ {
+ if (TIFFReadTile (tif, buffer, x, y, 0, 0) == -1)
+ {
+ g_message (_("Reading tile failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+ }
+ else if (TIFFReadScanline (tif, buffer, y, 0) == -1)
+ {
+ /* Error reading scanline, stop loading */
+ g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+
+ cols = MIN (image_width - x, tile_width);
+ rows = MIN (image_height - y, tile_height);
+
+ if (needs_upscale)
+ {
+ if (bps == 1)
+ convert_bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 2)
+ convert_2bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 4)
+ convert_4bit2byte (buffer, bw_buffer, cols, rows);
+ }
+ else if (is_signed)
+ {
+ convert_int2uint (buffer, bps, spp, cols, rows,
+ tile_width * bytes_per_pixel);
+ }
+
+ if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8)
+ {
+ convert_miniswhite (buffer, cols, rows);
+ }
+
+ src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer,
+ src_format,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ tile_width * bytes_per_pixel,
+ NULL, NULL);
+
+ offset = 0;
+
+ for (i = 0; i <= extra; i++)
+ {
+ GeglBufferIterator *iter;
+ gint src_bpp;
+ gint dest_bpp;
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
+
+ iter = gegl_buffer_iterator_new (src_buf,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ 0, NULL,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE, 2);
+ gegl_buffer_iterator_add (iter, channel[i].buffer,
+ GEGL_RECTANGLE (x, y, cols, rows),
+ 0, channel[i].format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *s = iter->items[0].data;
+ guchar *d = iter->items[1].data;
+ gint length = iter->length;
+
+ s += offset;
+
+ while (length--)
+ {
+ memcpy (d, s, dest_bpp);
+ d += dest_bpp;
+ s += src_bpp;
+ }
+ }
+
+ offset += dest_bpp;
+ }
+
+ g_object_unref (src_buf);
+ }
+
+ progress += one_row;
+ }
+
+ g_free (buffer);
+ g_free (bw_buffer);
+}
+
+
+static void
+load_separate (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 tile_width;
+ guint32 tile_height;
+ gint bytes_per_pixel;
+ const Babl *src_format;
+ guchar *buffer;
+ guchar *bw_buffer = NULL;
+ gdouble progress = 0.0;
+ gdouble one_row;
+ gint i, compindex;
+ gboolean needs_upscale = FALSE;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ tile_width = image_width;
+
+ if (TIFFIsTiled (tif))
+ {
+ TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
+
+ buffer = g_malloc (TIFFTileSize (tif));
+ }
+ else
+ {
+ tile_width = image_width;
+ tile_height = 1;
+
+ buffer = g_malloc (TIFFScanlineSize (tif));
+ }
+
+ if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8)
+ {
+ needs_upscale = TRUE;
+ bw_buffer = g_malloc (tile_width * tile_height);
+ }
+
+ one_row = (gdouble) tile_height / (gdouble) image_height;
+
+ src_format = babl_format_n (type, 1);
+
+ /* consistency check */
+ bytes_per_pixel = 0;
+ for (i = 0; i <= extra; i++)
+ bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
+
+ g_printerr ("bytes_per_pixel: %d, format: %d\n",
+ bytes_per_pixel,
+ babl_format_get_bytes_per_pixel (src_format));
+
+ compindex = 0;
+
+ for (i = 0; i <= extra; i++)
+ {
+ gint n_comps;
+ gint src_bpp;
+ gint dest_bpp;
+ gint offset;
+ gint j;
+
+ n_comps = babl_format_get_n_components (channel[i].format);
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
+
+ offset = 0;
+
+ for (j = 0; j < n_comps; j++)
+ {
+ guint32 y;
+
+ for (y = 0; y < image_height; y += tile_height)
+ {
+ guint32 x;
+
+ for (x = 0; x < image_width; x += tile_width)
+ {
+ GeglBuffer *src_buf;
+ GeglBufferIterator *iter;
+ guint32 rows;
+ guint32 cols;
+
+ gimp_progress_update (progress + one_row *
+ ((gdouble) x / (gdouble) image_width));
+
+ if (TIFFIsTiled (tif))
+ {
+ if (TIFFReadTile (tif, buffer, x, y, 0, compindex) == -1)
+ {
+ g_message (_("Reading tile failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+ }
+ else if (TIFFReadScanline (tif, buffer, y, compindex) == -1)
+ {
+ /* Error reading scanline, stop loading */
+ g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+
+ cols = MIN (image_width - x, tile_width);
+ rows = MIN (image_height - y, tile_height);
+
+ if (needs_upscale)
+ {
+ if (bps == 1)
+ convert_bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 2)
+ convert_2bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 4)
+ convert_4bit2byte (buffer, bw_buffer, cols, rows);
+ }
+ else if (is_signed)
+ {
+ convert_int2uint (buffer, bps, 1, cols, rows,
+ tile_width * bytes_per_pixel);
+ }
+
+ if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8)
+ {
+ convert_miniswhite (buffer, cols, rows);
+ }
+
+ src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer,
+ src_format,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ GEGL_AUTO_ROWSTRIDE,
+ NULL, NULL);
+
+ iter = gegl_buffer_iterator_new (src_buf,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ 0, NULL,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE, 2);
+ gegl_buffer_iterator_add (iter, channel[i].buffer,
+ GEGL_RECTANGLE (x, y, cols, rows),
+ 0, channel[i].format,
+ GEGL_ACCESS_READWRITE,
+ GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *s = iter->items[0].data;
+ guchar *d = iter->items[1].data;
+ gint length = iter->length;
+
+ d += offset;
+
+ while (length--)
+ {
+ memcpy (d, s, src_bpp);
+ d += dest_bpp;
+ s += src_bpp;
+ }
+ }
+
+ g_object_unref (src_buf);
+ }
+ }
+
+ offset += src_bpp;
+ compindex++;
+ }
+
+ progress += one_row;
+ }
+
+ g_free (buffer);
+ g_free (bw_buffer);
+}
+
+static void
+fill_bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled)
+ return;
+
+ dest = bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ {
+ *(dest++) = ((j & (1 << i)) != 0);
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_1_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_1_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (1 << i)) != 0);
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+
+ filled = TRUE;
+}
+
+static void
+fill_2bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled2 = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled2)
+ return;
+
+ dest = _2bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ *(dest++) = ((j & (3 << (2*i))) >> (2*i));
+ }
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_2_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_2_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (3 << (2*i))) >> (2*i));
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+ }
+
+ filled2 = TRUE;
+}
+
+static void
+fill_4bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled4 = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled4)
+ return;
+
+ dest = _4bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 1; i >= 0; i--)
+ {
+ *(dest++) = ((j & (15 << (4*i))) >> (4*i));
+ }
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_4_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_4_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 1; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (15 << (4*i))) >> (4*i));
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+ }
+
+ filled4 = TRUE;
+}
+
+static void
+convert_bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 8)
+ {
+ memcpy (dest, bit2byte + *src * 8, 8);
+ dest += 8;
+ x -= 8;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, bit2byte + *src * 8, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_2bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 4)
+ {
+ memcpy (dest, _2bit2byte + *src * 4, 4);
+ dest += 4;
+ x -= 4;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, _2bit2byte + *src * 4, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_4bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 2)
+ {
+ memcpy (dest, _4bit2byte + *src * 2, 2);
+ dest += 2;
+ x -= 2;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, _4bit2byte + *src * 2, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_miniswhite (guchar *buffer,
+ gint width,
+ gint height)
+{
+ gint y;
+ guchar *buf = buffer;
+
+ for (y = 0; y < height; y++)
+ {
+ gint x;
+
+ for (x = 0; x < width; x++)
+ {
+ *buf = ~*buf;
+ buf++;
+ }
+ }
+}
+
+static void
+convert_int2uint (guchar *buffer,
+ gint bps,
+ gint spp,
+ gint width,
+ gint height,
+ gint stride)
+{
+ gint bytes_per_pixel = bps / 8;
+ gint y;
+
+ for (y = 0; y < height; y++)
+ {
+ guchar *d = buffer + stride * y;
+ gint x;
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ d += bytes_per_pixel - 1;
+#endif
+
+ for (x = 0; x < width * spp; x++)
+ {
+ *d ^= 0x80;
+
+ d += bytes_per_pixel;
+ }
+ }
+}
+
+static gboolean
+load_dialog (const gchar *help_id,
+ TiffSelectedPages *pages,
+ const gchar *extra_message,
+ DefaultExtra *default_extra)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *show_reduced = NULL;
+ GtkWidget *crop_option = NULL;
+ GtkWidget *extra_radio = NULL;
+ gboolean run;
+
+ pages->selector = NULL;
+
+ dialog = gimp_dialog_new (_("Import from TIFF"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, help_id,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+
+ show_reduced = gtk_check_button_new_with_mnemonic (_("_Show reduced images"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_reduced),
+ pages->show_reduced);
+ gtk_box_pack_start (GTK_BOX (vbox), show_reduced, TRUE, TRUE, 0);
+
+ g_signal_connect (show_reduced, "toggled",
+ G_CALLBACK (tiff_dialog_show_reduced),
+ pages);
+
+ if (pages->n_pages > 1)
+ {
+ /* Page Selector */
+ pages->selector = gimp_page_selector_new ();
+ gtk_widget_set_size_request (pages->selector, 300, 200);
+ gtk_box_pack_start (GTK_BOX (vbox), pages->selector, TRUE, TRUE, 0);
+
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ pages->n_filtered_pages);
+ gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (pages->selector),
+ pages->target);
+
+ /* Load a set number of pages, based on whether "Show Reduced Images"
+ * is checked
+ */
+ tiff_dialog_show_reduced (show_reduced, pages);
+
+ g_signal_connect_swapped (pages->selector, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+
+ /* Option to shrink the loaded image to its bounding box
+ or keep as much empty space as possible.
+ Note that there seems to be no way to keep the empty
+ space on the right and bottom. */
+ crop_option = gtk_check_button_new_with_mnemonic (_("_Keep empty space around imported layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (crop_option),
+ pages->keep_empty_space);
+ gtk_box_pack_start (GTK_BOX (vbox), crop_option, TRUE, TRUE, 0);
+ }
+
+ if (extra_message)
+ {
+ GtkWidget *warning;
+
+ warning = g_object_new (GIMP_TYPE_HINT_BOX,
+ "icon-name", GIMP_ICON_DIALOG_WARNING,
+ "hint", extra_message,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), warning, TRUE, TRUE, 0);
+ gtk_widget_show (warning);
+
+ extra_radio = gimp_int_radio_group_new (TRUE, _("Process extra channel as:"),
+ (GCallback) gimp_radio_button_update,
+ default_extra, GIMP_TIFF_LOAD_UNASSALPHA,
+ _("_Non-premultiplied alpha"), GIMP_TIFF_LOAD_UNASSALPHA, NULL,
+ _("Pre_multiplied alpha"), GIMP_TIFF_LOAD_ASSOCALPHA, NULL,
+ _("Channe_l"), GIMP_TIFF_LOAD_CHANNEL, NULL,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), extra_radio, TRUE, TRUE, 0);
+ gtk_widget_show (extra_radio);
+ }
+
+ /* Setup done; display the dialog */
+ gtk_widget_show_all (dialog);
+
+ /* run the dialog */
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ if (pages->n_pages > 1)
+ {
+ pages->target =
+ gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (pages->selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ &pages->n_pages);
+
+ pages->keep_empty_space =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crop_option));
+
+ /* select all if none selected */
+ if (pages->n_pages == 0)
+ {
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (pages->selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ &pages->n_pages);
+ }
+ }
+ }
+
+ return run;
+}
+
+static void
+tiff_dialog_show_reduced (GtkWidget *toggle,
+ gpointer data)
+{
+ gint selectable_pages;
+ gint i, j;
+ TiffSelectedPages *pages = (TiffSelectedPages *) data;
+
+ pages->show_reduced = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle));
+
+ /* Clear current pages from selection */
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector), 0);
+ /* Jump back to start of the TIFF file */
+ TIFFSetDirectory (pages->tif, 0);
+
+ selectable_pages = pages->n_filtered_pages;
+ if (pages->show_reduced)
+ selectable_pages = pages->n_reducedimage_pages;
+
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ selectable_pages);
+
+ for (i = 0, j = 0; i < pages->n_pages && j < selectable_pages; i++)
+ {
+ if ((pages->show_reduced && pages->filtered_pages[i] != TIFF_MISC_THUMBNAIL) ||
+ (! pages->show_reduced && pages->filtered_pages[i] > TIFF_MISC_THUMBNAIL))
+ {
+ const gchar *name = tiff_get_page_name (pages->tif);
+
+ if (name)
+ gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (pages->selector),
+ j, name);
+ j++;
+ }
+
+ TIFFReadDirectory (pages->tif);
+ }
+}
diff --git a/plug-ins/file-tiff/file-tiff-load.h b/plug-ins/file-tiff/file-tiff-load.h
new file mode 100644
index 0000000..cc97dda
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-load.h
@@ -0,0 +1,57 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_LOAD_H__
+#define __FILE_TIFF_LOAD_H__
+
+#define LOAD_PROC "file-tiff-load"
+
+typedef enum
+{
+ TIFF_REDUCEDFILE = -2,
+ TIFF_MISC_THUMBNAIL = -1
+} TIFF_THUMBNAIL_TYPE;
+
+typedef struct
+{
+ TIFF *tif;
+ gint o_pages;
+ gint n_pages;
+ gint *pages;
+ gint *filtered_pages; /* thumbnail is marked as < 0 */
+ gint n_filtered_pages;
+ gint n_reducedimage_pages;
+ GtkWidget *selector;
+ GimpPageSelectorTarget target;
+ gboolean keep_empty_space;
+ gboolean show_reduced;
+} TiffSelectedPages;
+
+
+GimpPDBStatusType load_image (GFile *file,
+ GimpRunMode run_mode,
+ gint32 *image,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+
+#endif /* __FILE_TIFF_LOAD_H__ */
diff --git a/plug-ins/file-tiff/file-tiff-save.c b/plug-ins/file-tiff/file-tiff-save.c
new file mode 100644
index 0000000..0def177
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-save.c
@@ -0,0 +1,1387 @@
+/* tiff exporting for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_ROLE "gimp-file-tiff-save"
+
+
+static gboolean save_paths (TIFF *tif,
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y);
+
+static void comment_entry_callback (GtkWidget *widget,
+ gchar **comment);
+
+static void byte2bit (const guchar *byteline,
+ gint width,
+ guchar *bitline,
+ gboolean invert);
+
+
+static void
+double_to_psd_fixed (gdouble value,
+ gchar *target)
+{
+ gdouble in, frac;
+ gint i, f;
+
+ frac = modf (value, &in);
+ if (frac < 0)
+ {
+ in -= 1;
+ frac += 1;
+ }
+
+ i = (gint) CLAMP (in, -16, 15);
+ f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF);
+
+ target[0] = i & 0xFF;
+ target[1] = (f >> 16) & 0xFF;
+ target[2] = (f >> 8) & 0xFF;
+ target[3] = f & 0xFF;
+}
+
+static gboolean
+save_paths (TIFF *tif,
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y)
+{
+ gint id = 2000; /* Photoshop paths have IDs >= 2000 */
+ gint num_vectors, *vectors, v;
+ gint num_strokes, *strokes, s;
+ GString *ps_tag;
+
+ vectors = gimp_image_get_vectors (image, &num_vectors);
+
+ if (num_vectors <= 0)
+ return FALSE;
+
+ ps_tag = g_string_new ("");
+
+ /* Only up to 1000 paths supported */
+ for (v = 0; v < MIN (num_vectors, 1000); v++)
+ {
+ GString *data;
+ gchar *name, *nameend;
+ gsize len;
+ gint lenpos;
+ gchar pointrecord[26] = { 0, };
+ gchar *tmpname;
+ GError *err = NULL;
+
+ data = g_string_new ("8BIM");
+ g_string_append_c (data, id / 256);
+ g_string_append_c (data, id % 256);
+
+ /*
+ * - use iso8859-1 if possible
+ * - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark)
+ */
+ name = gimp_item_get_name (vectors[v]);
+ tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err);
+
+ if (tmpname && err == NULL)
+ {
+ g_string_append_c (data, MIN (len, 255));
+ g_string_append_len (data, tmpname, MIN (len, 255));
+ g_free (tmpname);
+ }
+ else
+ {
+ /* conversion failed, we fall back to UTF-8 */
+ len = g_utf8_strlen (name, 255 - 3); /* need three marker-bytes */
+
+ nameend = g_utf8_offset_to_pointer (name, len);
+ len = nameend - name; /* in bytes */
+ g_assert (len + 3 <= 255);
+
+ g_string_append_c (data, len + 3);
+ g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */
+ g_string_append_len (data, name, len);
+
+ if (tmpname)
+ g_free (tmpname);
+ }
+
+ if (data->len % 2) /* padding to even size */
+ g_string_append_c (data, 0);
+ g_free (name);
+
+ lenpos = data->len;
+ g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */
+ len = data->len; /* to calculate the data size later */
+
+ pointrecord[1] = 6; /* fill rule record */
+ g_string_append_len (data, pointrecord, 26);
+
+ strokes = gimp_vectors_get_strokes (vectors[v], &num_strokes);
+
+ for (s = 0; s < num_strokes; s++)
+ {
+ GimpVectorsStrokeType type;
+ gdouble *points;
+ gint num_points;
+ gboolean closed;
+ gint p = 0;
+
+ type = gimp_vectors_stroke_get_points (vectors[v], strokes[s],
+ &num_points, &points, &closed);
+
+ if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER ||
+ num_points > 65535 ||
+ num_points % 6)
+ {
+ g_printerr ("tiff-save: unsupported stroke type: "
+ "%d (%d points)\n", type, num_points);
+ continue;
+ }
+
+ memset (pointrecord, 0, 26);
+ pointrecord[1] = closed ? 0 : 3;
+ pointrecord[2] = (num_points / 6) / 256;
+ pointrecord[3] = (num_points / 6) % 256;
+ g_string_append_len (data, pointrecord, 26);
+
+ for (p = 0; p < num_points; p += 6)
+ {
+ pointrecord[1] = closed ? 2 : 5;
+
+ double_to_psd_fixed ((points[p+1] - offset_y) / height, pointrecord + 2);
+ double_to_psd_fixed ((points[p+0] - offset_x) / width, pointrecord + 6);
+ double_to_psd_fixed ((points[p+3] - offset_y) / height, pointrecord + 10);
+ double_to_psd_fixed ((points[p+2] - offset_x) / width, pointrecord + 14);
+ double_to_psd_fixed ((points[p+5] - offset_y) / height, pointrecord + 18);
+ double_to_psd_fixed ((points[p+4] - offset_x) / width, pointrecord + 22);
+
+ g_string_append_len (data, pointrecord, 26);
+ }
+ }
+
+ g_free (strokes);
+
+ /* fix up the length */
+
+ len = data->len - len;
+ data->str[lenpos + 0] = (len & 0xFF000000) >> 24;
+ data->str[lenpos + 1] = (len & 0x00FF0000) >> 16;
+ data->str[lenpos + 2] = (len & 0x0000FF00) >> 8;
+ data->str[lenpos + 3] = (len & 0x000000FF) >> 0;
+
+ g_string_append_len (ps_tag, data->str, data->len);
+ g_string_free (data, TRUE);
+ id ++;
+ }
+
+ TIFFSetField (tif, TIFFTAG_PHOTOSHOP, ps_tag->len, ps_tag->str);
+ g_string_free (ps_tag, TRUE);
+
+ g_free (vectors);
+
+ return TRUE;
+}
+
+/*
+ * pnmtotiff.c - converts a portable anymap to a Tagged Image File
+ *
+ * Derived by Jef Poskanzer from ras2tif.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+static gboolean
+save_layer (TIFF *tif,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 layer,
+ gint32 page,
+ gint32 num_pages,
+ gint32 orig_image, /* the export function might */
+ /* have created a duplicate */
+ gint origin_x,
+ gint origin_y,
+ gint *saved_bpp,
+ gboolean out_linear,
+ GError **error)
+{
+ gboolean status = FALSE;
+ gushort red[256];
+ gushort grn[256];
+ gushort blu[256];
+ gint cols, rows, row, i;
+ glong rowsperstrip;
+ gushort compression;
+ gushort extra_samples[1];
+ gboolean alpha;
+ gshort predictor;
+ gshort photometric;
+ const Babl *format;
+ const Babl *type;
+ gshort samplesperpixel;
+ gshort bitspersample;
+ gshort sampleformat;
+ gint bytesperrow;
+ guchar *src = NULL;
+ guchar *data = NULL;
+ guchar *cmap;
+ gint num_colors;
+ gint success;
+ GimpImageType drawable_type;
+ GeglBuffer *buffer = NULL;
+ gint tile_height;
+ gint y, yend;
+ gboolean is_bw = FALSE;
+ gboolean invert = TRUE;
+ const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 };
+ const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 };
+ gchar *layer_name = NULL;
+ const gdouble progress_base = (gdouble) page / (gdouble) num_pages;
+ const gdouble progress_fraction = 1.0 / (gdouble) num_pages;
+ gdouble xresolution;
+ gdouble yresolution;
+ gushort save_unit = RESUNIT_INCH;
+ gint offset_x, offset_y;
+
+ compression = tsvals->compression;
+
+ layer_name = gimp_item_get_name (layer);
+
+ /* Disabled because this isn't in older releases of libtiff, and it
+ * wasn't helping much anyway
+ */
+#if 0
+ if (TIFFFindCODEC((uint16) compression) == NULL)
+ compression = COMPRESSION_NONE; /* CODEC not available */
+#endif
+
+ predictor = 0;
+ tile_height = gimp_tile_height ();
+ rowsperstrip = tile_height;
+
+ drawable_type = gimp_drawable_type (layer);
+ buffer = gimp_drawable_get_buffer (layer);
+
+ format = gegl_buffer_get_format (buffer);
+ type = babl_format_get_type (format, 0);
+
+ switch (gimp_image_get_precision (image))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ /* Promote to 16-bit if storage and export TRC don't match. */
+ if ((gimp_image_get_precision (image) == GIMP_PRECISION_U8_LINEAR && out_linear) ||
+ (gimp_image_get_precision (image) != GIMP_PRECISION_U8_LINEAR && ! out_linear))
+ {
+ bitspersample = 8;
+ sampleformat = SAMPLEFORMAT_UINT;
+ }
+ else
+ {
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_UINT;
+ type = babl_type ("u16");
+ }
+ break;
+
+ case GIMP_PRECISION_U16_LINEAR:
+ case GIMP_PRECISION_U16_GAMMA:
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_UINT;
+ break;
+
+ case GIMP_PRECISION_U32_LINEAR:
+ case GIMP_PRECISION_U32_GAMMA:
+ bitspersample = 32;
+ sampleformat = SAMPLEFORMAT_UINT;
+ break;
+
+ case GIMP_PRECISION_HALF_LINEAR:
+ case GIMP_PRECISION_HALF_GAMMA:
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+
+ default:
+ case GIMP_PRECISION_FLOAT_LINEAR:
+ case GIMP_PRECISION_FLOAT_GAMMA:
+ bitspersample = 32;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+
+ case GIMP_PRECISION_DOUBLE_LINEAR:
+ case GIMP_PRECISION_DOUBLE_GAMMA:
+ bitspersample = 64;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+ }
+
+ *saved_bpp = bitspersample;
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ predictor = 2;
+ samplesperpixel = 3;
+ photometric = PHOTOMETRIC_RGB;
+ alpha = FALSE;
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RGB"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ samplesperpixel = 1;
+ photometric = PHOTOMETRIC_MINISBLACK;
+ alpha = FALSE;
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("Y"),
+ type,
+ babl_component ("Y"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ predictor = 2;
+ samplesperpixel = 4;
+ photometric = PHOTOMETRIC_RGB;
+ alpha = TRUE;
+ if (tsvals->save_transp_pixels)
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RGBA"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ samplesperpixel = 2;
+ photometric = PHOTOMETRIC_MINISBLACK;
+ alpha = TRUE;
+ if (tsvals->save_transp_pixels)
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("YA"),
+ type,
+ babl_component ("Y"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("YaA"),
+ type,
+ babl_component ("Ya"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'aA"),
+ type,
+ babl_component ("Y'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ cmap = gimp_image_get_colormap (image, &num_colors);
+
+ if (num_colors == 2 || num_colors == 1)
+ {
+ is_bw = (memcmp (cmap, bw_map, 3 * num_colors) == 0);
+ photometric = PHOTOMETRIC_MINISWHITE;
+
+ if (!is_bw)
+ {
+ is_bw = (memcmp (cmap, wb_map, 3 * num_colors) == 0);
+
+ if (is_bw)
+ invert = FALSE;
+ }
+ }
+
+ if (is_bw)
+ {
+ bitspersample = 1;
+ }
+ else
+ {
+ bitspersample = 8;
+ photometric = PHOTOMETRIC_PALETTE;
+
+ for (i = 0; i < num_colors; i++)
+ {
+ red[i] = cmap[i * 3 + 0] * 65535 / 255;
+ grn[i] = cmap[i * 3 + 1] * 65535 / 255;
+ blu[i] = cmap[i * 3 + 2] * 65535 / 255;
+ }
+ }
+
+ samplesperpixel = (drawable_type == GIMP_INDEXEDA_IMAGE) ? 2 : 1;
+ bytesperrow = cols;
+ alpha = (drawable_type == GIMP_INDEXEDA_IMAGE);
+ format = gimp_drawable_get_format (layer);
+
+ g_free (cmap);
+ break;
+
+ default:
+ goto out;
+ }
+
+ bytesperrow = cols * babl_format_get_bytes_per_pixel (format);
+
+ if (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4)
+ {
+ if (bitspersample != 1 || samplesperpixel != 1)
+ {
+ const gchar *msg = _("Only monochrome pictures can be compressed "
+ "with \"CCITT Group 4\" or \"CCITT Group 3\".");
+
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, msg);
+
+ goto out;
+ }
+ }
+
+ if (compression == COMPRESSION_JPEG)
+ {
+ if (gimp_image_base_type (image) == GIMP_INDEXED)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Indexed pictures cannot be compressed "
+ "with \"JPEG\"."));
+ goto out;
+ }
+ }
+
+
+ /* Set TIFF parameters. */
+ if (num_pages > 1)
+ {
+ TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+ TIFFSetField (tif, TIFFTAG_PAGENUMBER, page, num_pages);
+ }
+ TIFFSetField (tif, TIFFTAG_PAGENAME, layer_name);
+ TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
+ TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
+ TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
+ TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sampleformat);
+ TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField (tif, TIFFTAG_COMPRESSION, compression);
+
+ if ((compression == COMPRESSION_LZW ||
+ compression == COMPRESSION_ADOBE_DEFLATE) &&
+ (predictor != 0))
+ {
+ TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
+ }
+
+ if (alpha)
+ {
+ if (tsvals->save_transp_pixels ||
+ /* Associated alpha, hence premultiplied components is
+ * meaningless for palette images with transparency in TIFF
+ * format, since alpha is set per pixel, not per color (so a
+ * given color could be set to different alpha on different
+ * pixels, hence it cannot be premultiplied).
+ */
+ drawable_type == GIMP_INDEXEDA_IMAGE)
+ extra_samples [0] = EXTRASAMPLE_UNASSALPHA;
+ else
+ extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
+
+ TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
+ }
+
+ TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
+ TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
+ TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+ /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
+ TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+ /* resolution fields */
+ gimp_image_get_resolution (orig_image, &xresolution, &yresolution);
+
+ if (gimp_unit_is_metric (gimp_image_get_unit (orig_image)))
+ {
+ save_unit = RESUNIT_CENTIMETER;
+ xresolution /= 2.54;
+ yresolution /= 2.54;
+ }
+
+ if (xresolution > 1e-5 && yresolution > 1e-5)
+ {
+ TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
+ TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
+ TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
+ }
+
+ gimp_drawable_offsets (layer, &offset_x, &offset_y);
+
+ offset_x -= origin_x;
+ offset_y -= origin_y;
+
+ if (offset_x || offset_y)
+ {
+ TIFFSetField (tif, TIFFTAG_XPOSITION, offset_x / xresolution);
+ TIFFSetField (tif, TIFFTAG_YPOSITION, offset_y / yresolution);
+ }
+
+ if (! is_bw &&
+ (drawable_type == GIMP_INDEXED_IMAGE || drawable_type == GIMP_INDEXEDA_IMAGE))
+ TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu);
+
+ /* save path data. we need layer information for that,
+ * so we have to do this in here. :-( */
+ if (page == 0)
+ save_paths (tif, orig_image, cols, rows, offset_x, offset_y);
+
+ /* array to rearrange data */
+ src = g_new (guchar, bytesperrow * tile_height);
+ data = g_new (guchar, bytesperrow);
+
+ /* Now write the TIFF data. */
+ for (y = 0; y < rows; y = yend)
+ {
+ yend = y + tile_height;
+ yend = MIN (yend, rows);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, y, cols, yend - y), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (row = y; row < yend; row++)
+ {
+ guchar *t = src + bytesperrow * (row - y);
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ if (is_bw)
+ {
+ byte2bit (t, bytesperrow, data, invert);
+ success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
+ }
+ else
+ {
+ success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ case GIMP_GRAYA_IMAGE:
+ case GIMP_RGB_IMAGE:
+ case GIMP_RGBA_IMAGE:
+ success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (! success)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Failed a scanline write on row %d"), row);
+ goto out;
+ }
+ }
+
+ if ((row % 32) == 0)
+ gimp_progress_update (progress_base + progress_fraction
+ * (gdouble) row / (gdouble) rows);
+ }
+
+ /* Save GeoTIFF tags to file, if available */
+ if (tsvals->save_geotiff)
+ {
+ GimpParasite *parasite = NULL;
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelPixelScale");
+
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELPIXELSCALE,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelTiePoint");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELTIEPOINT,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelTransformation");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELTRANSFORMATION,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_KeyDirectory");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_KEYDIRECTORY,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_SHORT)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_DoubleParams");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_DOUBLEPARAMS,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_Asciiparams");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_ASCIIPARAMS,
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ TIFFWriteDirectory (tif);
+
+ gimp_progress_update (progress_base + progress_fraction);
+
+ status = TRUE;
+
+out:
+ if (buffer)
+ g_object_unref (buffer);
+
+ g_free (data);
+ g_free (src);
+ g_free (layer_name);
+
+ return status;
+}
+
+/* FIXME Most of the stuff in save_metadata except the
+ * thumbnail saving should probably be moved to
+ * gimpmetadata.c and gimpmetadata-save.c.
+ */
+static void
+save_metadata (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gint saved_bpp)
+{
+ gchar **exif_tags;
+
+ /* See bug 758909: clear TIFFTAG_MIN/MAXSAMPLEVALUE because
+ * exiv2 saves them with wrong type and the original values
+ * could be invalid, see also bug 761823.
+ * we also clear some other tags that were only meaningful
+ * for the original imported image.
+ */
+ static const gchar *exif_tags_to_remove[] =
+ {
+ "Exif.Image.0x0118", /* MinSampleValue */
+ "Exif.Image.0x0119", /* MaxSampleValue */
+ "Exif.Image.0x011d", /* PageName */
+ "Exif.Image.Compression",
+ "Exif.Image.FillOrder",
+ "Exif.Image.InterColorProfile",
+ "Exif.Image.NewSubfileType",
+ "Exif.Image.PageNumber",
+ "Exif.Image.PhotometricInterpretation",
+ "Exif.Image.PlanarConfiguration",
+ "Exif.Image.Predictor",
+ "Exif.Image.RowsPerStrip",
+ "Exif.Image.SampleFormat",
+ "Exif.Image.SamplesPerPixel",
+ "Exif.Image.StripByteCounts",
+ "Exif.Image.StripOffsets"
+ };
+ static const guint n_keys = G_N_ELEMENTS(exif_tags_to_remove);
+
+ for (int k = 0; k < n_keys; k++)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
+ exif_tags_to_remove[k]);
+ }
+
+ /* get rid of all the EXIF tags for anything but the first sub image. */
+ exif_tags = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA(metadata));
+ for (char **tag = exif_tags; *tag; tag++)
+ {
+ /* Keeping Exif.Image2, 3 can cause exiv2 to save faulty extra TIFF pages
+ * that are empty except for the Exif metadata. See issue #7195. */
+ if (g_str_has_prefix (*tag, "Exif.Image")
+ && (*tag)[strlen ("Exif.Image")] >= '0'
+ && (*tag)[strlen ("Exif.Image")] <= '9')
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ if (g_str_has_prefix (*tag, "Exif.SubImage")
+ && (*tag)[strlen ("Exif.SubImage")] >= '0'
+ && (*tag)[strlen ("Exif.SubImage")] <= '9')
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ if (g_str_has_prefix (*tag, "Exif.Thumbnail"))
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ }
+
+ gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
+
+ if (tsvals->save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (tsvals->save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (tsvals->save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ if (tsvals->save_thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (tsvals->save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ gimp_image_metadata_save_finish (image,
+ "image/tiff",
+ metadata, metadata_flags,
+ file, NULL);
+}
+
+gboolean
+save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image, /* the export function */
+ /* might have created */
+ /* a duplicate */
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error)
+{
+ TIFF *tif = NULL;
+ gboolean status = FALSE;
+ gboolean out_linear = FALSE;
+ gint32 num_layers, *layers, current_layer = 0;
+ gint origin_x = 0, origin_y = 0;
+ gint32 i;
+
+ layers = gimp_image_get_layers (image, &num_layers);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ /* Open file and write some global data */
+#ifdef TIFF_VERSION_BIG
+ tif = tiff_open (file, (tsvals->bigtiff ? "w8" : "w"), error);
+#else
+ tif = tiff_open (file, "w", error);
+#endif
+
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file), g_strerror (errno));
+ goto out;
+ }
+
+ /* The TIFF spec explicitly says ASCII for the image description. */
+ if (image_comment)
+ {
+ const gchar *c = image_comment;
+ gint len;
+
+ for (len = strlen (c); len; c++, len--)
+ {
+ if ((guchar) *c > 127)
+ {
+ g_message (_("The TIFF format only supports comments in\n"
+ "7bit ASCII encoding. No comment is saved."));
+ image_comment = NULL;
+
+ break;
+ }
+ }
+ }
+
+ /* do we have a comment? If so, create a new parasite to hold it,
+ * and attach it to the image. The attach function automatically
+ * detaches a previous incarnation of the parasite. */
+ if (image_comment && *image_comment)
+ {
+ GimpParasite *parasite;
+
+ TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, image_comment);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (image_comment) + 1, image_comment);
+ gimp_image_attach_parasite (orig_image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+#ifdef TIFFTAG_ICCPROFILE
+ if (tsvals->save_profile)
+ {
+ GimpColorProfile *profile;
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ profile = gimp_image_get_effective_color_profile (orig_image);
+
+ /* Curve of the exported data depends on the saved profile, i.e.
+ * any explicitly-set profile in priority, or the default one for
+ * the storage format as fallback.
+ */
+ out_linear = (gimp_color_profile_is_linear (profile));
+
+ /* Write the profile to the TIFF file. */
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ TIFFSetField (tif, TIFFTAG_ICCPROFILE, icc_length, icc_data);
+ g_object_unref (profile);
+ }
+#endif
+
+ /* calculate the top-left coordinates */
+ for (i = 0; i < num_layers; i++)
+ {
+ gint offset_x, offset_y;
+
+ gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
+
+ origin_x = MIN (origin_x, offset_x);
+ origin_y = MIN (origin_y, offset_y);
+ }
+
+ /* write last layer as first page. */
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers, orig_image,
+ origin_x, origin_y,
+ saved_bpp, out_linear, error))
+ {
+ goto out;
+ }
+ current_layer++;
+
+ /* close file so we can safely let exiv2 work on it to write metadata.
+ * this can be simplified once multi page TIFF is supported by exiv2
+ */
+ TIFFFlushData (tif);
+ TIFFClose (tif);
+ tif = NULL;
+ if (metadata)
+ save_metadata (file, tsvals, image, metadata, metadata_flags, *saved_bpp);
+
+ /* write the remaining layers */
+ if (num_layers > 1)
+ {
+ tif = tiff_open (file, "a", error);
+
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file),
+ g_strerror (errno));
+ goto out;
+ }
+
+ for (; current_layer < num_layers; current_layer++)
+ {
+ gint tmp_saved_bpp;
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers, orig_image,
+ origin_x, origin_y,
+ &tmp_saved_bpp, out_linear, error))
+ {
+ goto out;
+ }
+ if (tmp_saved_bpp != *saved_bpp)
+ {
+ /* this should never happen.
+ * if it does, decide if it's really an error.
+ */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Writing pages with different bit depth "
+ "is strange."));
+ goto out;
+ }
+ gimp_progress_update ((gdouble) (current_layer + 1) / num_layers);
+ }
+ }
+
+ status = TRUE;
+
+out:
+ /* close the file for good */
+ if (tif)
+ {
+ TIFFFlushData (tif);
+ TIFFClose (tif);
+ }
+
+ gimp_progress_update (1.0);
+
+ return status;
+}
+
+gboolean
+save_dialog (TiffSaveVals *tsvals,
+ gint32 image,
+ const gchar *help_id,
+ gboolean has_alpha,
+ gboolean is_monochrome,
+ gboolean is_indexed,
+ gchar **image_comment,
+ GError **error,
+ gboolean classic_tiff_failed)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *entry;
+ GtkWidget *toggle;
+ GtkWidget *label;
+ GtkWidget *cmp_g3;
+ GtkWidget *cmp_g4;
+ GtkWidget *cmp_jpeg;
+ GtkBuilder *builder;
+ gchar *ui_file;
+ gchar **parasites;
+ gboolean run;
+ gboolean has_geotiff = FALSE;
+ gint n_parasites;
+ gint i;
+
+ parasites = gimp_image_get_parasite_list (image, &n_parasites);
+ for (i = 0; i < n_parasites; i++)
+ {
+ if (g_str_has_prefix (parasites[i], "Gimp_GeoTIFF_"))
+ {
+ has_geotiff = TRUE;
+ break;
+ }
+ }
+ g_strfreev (parasites);
+
+ dialog = gimp_export_dialog_new (_("TIFF"), PLUG_IN_ROLE, help_id);
+
+ builder = gtk_builder_new ();
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-file-tiff.ui",
+ NULL);
+ if (! gtk_builder_add_from_file (builder, ui_file, error))
+ {
+ gchar *display_name = g_filename_display_name (ui_file);
+
+ g_printerr (_("Error loading UI file '%s': %s"),
+ display_name, error ? (*error)->message : _("Unknown error"));
+
+ g_free (display_name);
+ }
+
+ g_free (ui_file);
+
+ vbox = GTK_WIDGET (gtk_builder_get_object (builder, "tiff_export_vbox"));
+
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ vbox = GTK_WIDGET (gtk_builder_get_object (builder, "radio_button_box"));
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "bigtiff-warning"));
+#ifdef TIFF_VERSION_BIG
+ if (classic_tiff_failed)
+ {
+ gtk_label_set_markup (GTK_LABEL (label),
+ _("<i>Warning: maximum TIFF file size exceeded.\n Retry as BigTIFF or cancel.</i>"));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 60);
+ }
+#endif
+ if (! classic_tiff_failed)
+ {
+ gtk_widget_hide (label);
+ }
+
+ frame = gimp_int_radio_group_new (TRUE, _("Compression"),
+ G_CALLBACK (gimp_radio_button_update),
+ &tsvals->compression, tsvals->compression,
+
+ _("_None"), COMPRESSION_NONE, NULL,
+ _("_LZW"), COMPRESSION_LZW, NULL,
+ _("_Pack Bits"), COMPRESSION_PACKBITS, NULL,
+ _("_Deflate"), COMPRESSION_ADOBE_DEFLATE, NULL,
+ _("_JPEG"), COMPRESSION_JPEG, &cmp_jpeg,
+ _("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &cmp_g3,
+ _("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &cmp_g4,
+
+ NULL);
+
+ gtk_widget_set_sensitive (cmp_g3, is_monochrome);
+ gtk_widget_set_sensitive (cmp_g4, is_monochrome);
+ gtk_widget_set_sensitive (cmp_jpeg, ! is_indexed);
+
+ if (! is_monochrome)
+ {
+ if (tsvals->compression == COMPRESSION_CCITTFAX3 ||
+ tsvals->compression == COMPRESSION_CCITTFAX4)
+ {
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (cmp_g3),
+ COMPRESSION_NONE);
+ }
+ }
+
+ if (is_indexed && tsvals->compression == COMPRESSION_JPEG)
+ {
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (cmp_jpeg),
+ COMPRESSION_NONE);
+ }
+
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-alpha"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ has_alpha && (tsvals->save_transp_pixels || is_indexed));
+ gtk_widget_set_sensitive (toggle, has_alpha && ! is_indexed);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_transp_pixels);
+
+ entry = GTK_WIDGET (gtk_builder_get_object (builder, "commentfield"));
+ gtk_entry_set_text (GTK_ENTRY (entry), *image_comment ? *image_comment : "");
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (comment_entry_callback),
+ image_comment);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-exif"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_exif);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_exif);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-xmp"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_xmp);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_xmp);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-iptc"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_iptc);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_iptc);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-geotiff"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ has_geotiff && tsvals->save_geotiff);
+ gtk_widget_set_sensitive (toggle, has_geotiff);;
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_geotiff);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-thumbnail"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_thumbnail);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_thumbnail);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-color-profile"));
+#ifdef TIFFTAG_ICCPROFILE
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_profile);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_profile);
+#else
+ gtk_widget_hide (toggle);
+#endif
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "bigtiff"));
+#ifdef TIFF_VERSION_BIG
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->bigtiff);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->bigtiff);
+#else
+ gtk_widget_hide (toggle);
+#endif
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_layers);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_layers);
+
+ frame = GTK_WIDGET (gtk_builder_get_object (builder, "layers-frame"));
+ g_object_bind_property (toggle, "active",
+ frame, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "crop-layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->crop_layers);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->crop_layers);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+comment_entry_callback (GtkWidget *widget,
+ gchar **comment)
+{
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ g_free (*comment);
+ *comment = g_strdup (text);
+}
+
+/* Convert n bytes of 0/1 to a line of bits */
+static void
+byte2bit (const guchar *byteline,
+ gint width,
+ guchar *bitline,
+ gboolean invert)
+{
+ guchar bitval;
+ guchar rest[8];
+
+ while (width >= 8)
+ {
+ bitval = 0;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ if (*(byteline++)) bitval |= 0x01;
+ *(bitline++) = invert ? ~bitval : bitval;
+ width -= 8;
+ }
+
+ if (width > 0)
+ {
+ memset (rest, 0, 8);
+ memcpy (rest, byteline, width);
+ bitval = 0;
+ byteline = rest;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ *bitline = invert ? ~bitval & (0xff << (8 - width)) : bitval;
+ }
+}
diff --git a/plug-ins/file-tiff/file-tiff-save.h b/plug-ins/file-tiff/file-tiff-save.h
new file mode 100644
index 0000000..f28d347
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-save.h
@@ -0,0 +1,64 @@
+/* tiff saving for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_SAVE_H__
+#define __FILE_TIFF_SAVE_H__
+
+
+typedef struct
+{
+ gint compression;
+ gint fillorder;
+ gboolean save_transp_pixels;
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_geotiff;
+ gboolean save_thumbnail;
+ gboolean save_profile;
+ gboolean save_layers;
+ gboolean crop_layers;
+ gboolean bigtiff;
+} TiffSaveVals;
+
+
+gboolean save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image,
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error);
+
+gboolean save_dialog (TiffSaveVals *tsvals,
+ gint32 image,
+ const gchar *help_id,
+ gboolean has_alpha,
+ gboolean is_monochrome,
+ gboolean is_indexed,
+ gchar **image_comment,
+ GError **error,
+ gboolean classic_tiff_failed);
+
+
+#endif /* __FILE_TIFF_SAVE_H__ */
diff --git a/plug-ins/file-tiff/file-tiff.c b/plug-ins/file-tiff/file-tiff.c
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff.c
@@ -0,0 +1,587 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-load.h"
+#include "file-tiff-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-tiff-save"
+#define SAVE2_PROC "file-tiff-save2"
+#define SAVE_BIGTIFF_PROC "file-bigtiff-save"
+#define PLUG_IN_BINARY "file-tiff"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static GimpPDBStatusType tiff_save_rec (GimpRunMode run_mode,
+ gint32 orig_image,
+ gint32 orig_drawable,
+ GFile *file,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gboolean retried,
+ GError **error);
+
+static gboolean image_is_monochrome (gint32 image);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static TiffSaveVals tsvals =
+{
+ COMPRESSION_NONE, /* compression */
+ TRUE, /* alpha handling */
+ FALSE, /* save transp. pixels */
+ FALSE, /* save exif */
+ FALSE, /* save xmp */
+ FALSE, /* save iptc */
+ TRUE, /* save geotiff */
+ TRUE, /* save thumbnail */
+ TRUE, /* save profile */
+ TRUE, /* save layers */
+ TRUE, /* crop layers */
+ FALSE /* save BigTIFF */
+};
+
+static gchar *image_comment = NULL;
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+#define COMMON_SAVE_ARGS \
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },\
+ { GIMP_PDB_IMAGE, "image", "Input image" },\
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },\
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },\
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },\
+ { GIMP_PDB_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3), JPEG (4), CCITT G3 Fax (5), CCITT G4 Fax (6) }" }
+
+ static const GimpParamDef save_args_old[] =
+ {
+ COMMON_SAVE_ARGS
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" }
+ };
+
+ static const GimpParamDef save_bigtiff_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" },
+ { GIMP_PDB_INT32, "bigtiff", "Export in BigTIFF variant file format" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+#ifdef TIFF_VERSION_BIG
+ "Loads files of the TIFF and BigTIFF file formats",
+ "Loads files of the Tag Image File Format (TIFF) and "
+ "its 64-bits offsets variant (BigTIFF)",
+#else
+ "Loads files of the TIFF file format",
+ "Loads files of the Tag Image File Format (TIFF)",
+#endif
+ "Spencer Kimball, Peter Mattis & Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "1995-1996,1998-2003",
+ N_("TIFF image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/tiff");
+ gimp_register_file_handler_uri (LOAD_PROC);
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "tif,tiff",
+ "",
+ "0,string,II*\\0,0,string,MM\\0*");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in the TIFF file format",
+ "Exports files in the Tagged Image File Format. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args_old), 0,
+ save_args_old, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/tiff");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "tif,tiff", "");
+
+ gimp_install_procedure (SAVE2_PROC,
+ "Exports files in the TIFF file format",
+ "Exports files in the Tagged Image File Format. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+#ifdef TIFF_VERSION_BIG
+ gimp_install_procedure (SAVE_BIGTIFF_PROC,
+ "Exports files in the TIFF or BigTIFF file format",
+ "Exports files in the Tagged Image File Format or "
+ "its 64-bit offsets variant (BigTIFF) able to support "
+ "much bigger file sizes. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_bigtiff_args), 0,
+ save_bigtiff_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/tiff");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "tif,tiff", "");
+#endif
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ GFile *file = g_file_new_for_uri (param[1].data.d_string);
+ gint32 image = 0;
+ gboolean resolution_loaded = FALSE;
+ gboolean profile_loaded = FALSE;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ status = load_image (file, run_mode, &image,
+ &resolution_loaded,
+ &profile_loaded,
+ &error);
+
+ if (image > 0)
+ {
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image,
+ "image/tiff",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ if (profile_loaded)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image, "image/tiff",
+ metadata, flags,
+ run_mode == GIMP_RUN_INTERACTIVE);
+
+ g_object_unref (metadata);
+ }
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image;
+ }
+
+ g_object_unref (file);
+ }
+ else if ((strcmp (name, SAVE_PROC) == 0) ||
+ (strcmp (name, SAVE2_PROC) == 0) ||
+ (strcmp (name, SAVE_BIGTIFF_PROC) == 0))
+ {
+ /* Plug-in is either file_tiff_save or file_tiff_save2 */
+ GFile *file;
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ GimpParasite *parasite;
+ gint32 image = param[1].data.d_int32;
+ gint32 drawable = param[2].data.d_int32;
+ gint32 orig_image = image;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ break;
+ default:
+ break;
+ }
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (orig_image,
+ "image/tiff",
+ &metadata_flags);
+ tsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ tsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ tsvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ tsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+ tsvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ tsvals.save_geotiff = TRUE;
+ tsvals.bigtiff = FALSE;
+#ifdef TIFF_VERSION_BIG
+ if (nparams >= 8)
+ tsvals.bigtiff = param[7].data.d_int32;
+#endif
+
+ parasite = gimp_image_get_parasite (orig_image, "gimp-comment");
+ if (parasite)
+ {
+ image_comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &tsvals);
+
+ parasite = gimp_image_get_parasite (orig_image, "tiff-save-options");
+ if (parasite)
+ {
+ const TiffSaveVals *pvals = gimp_parasite_data (parasite);
+
+ if (pvals->compression == COMPRESSION_DEFLATE)
+ tsvals.compression = COMPRESSION_ADOBE_DEFLATE;
+ else
+ tsvals.compression = pvals->compression;
+
+ tsvals.save_transp_pixels = pvals->save_transp_pixels;
+ }
+ gimp_parasite_free (parasite);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams == 6 || nparams == 7 || nparams == 8)
+ {
+ switch (param[5].data.d_int32)
+ {
+ case 0: tsvals.compression = COMPRESSION_NONE; break;
+ case 1: tsvals.compression = COMPRESSION_LZW; break;
+ case 2: tsvals.compression = COMPRESSION_PACKBITS; break;
+ case 3: tsvals.compression = COMPRESSION_ADOBE_DEFLATE; break;
+ case 4: tsvals.compression = COMPRESSION_JPEG; break;
+ case 5: tsvals.compression = COMPRESSION_CCITTFAX3; break;
+ case 6: tsvals.compression = COMPRESSION_CCITTFAX4; break;
+ default: status = GIMP_PDB_CALLING_ERROR; break;
+ }
+
+ if (nparams >= 7)
+ tsvals.save_transp_pixels = param[6].data.d_int32;
+ else
+ tsvals.save_transp_pixels = TRUE;
+
+ if (nparams == 8)
+ tsvals.bigtiff = param[7].data.d_int32;
+ else
+ tsvals.bigtiff = FALSE;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &tsvals);
+
+ parasite = gimp_image_get_parasite (orig_image, "tiff-save-options");
+ if (parasite)
+ {
+ const TiffSaveVals *pvals = gimp_parasite_data (parasite);
+
+ tsvals.compression = pvals->compression;
+ tsvals.save_transp_pixels = pvals->save_transp_pixels;
+ }
+ gimp_parasite_free (parasite);
+ break;
+
+ default:
+ break;
+ }
+
+ file = g_file_new_for_uri (param[3].data.d_string);
+ status = tiff_save_rec (run_mode, orig_image, drawable,
+ file, metadata, metadata_flags, FALSE, &error);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+image_is_monochrome (gint32 image)
+{
+ guchar *colors;
+ gint num_colors;
+ gboolean monochrome = FALSE;
+
+ g_return_val_if_fail (image != -1, FALSE);
+
+ colors = gimp_image_get_colormap (image, &num_colors);
+
+ if (colors)
+ {
+ if (num_colors == 2 || num_colors == 1)
+ {
+ const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 };
+ const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 };
+
+ if (memcmp (colors, bw_map, 3 * num_colors) == 0 ||
+ memcmp (colors, wb_map, 3 * num_colors) == 0)
+ {
+ monochrome = TRUE;
+ }
+ }
+
+ g_free (colors);
+ }
+
+ return monochrome;
+}
+
+static GimpPDBStatusType
+tiff_save_rec (GimpRunMode run_mode,
+ gint32 orig_image,
+ gint32 orig_drawable,
+ GFile *file,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gboolean retried,
+ GError **error)
+{
+ gint32 image = orig_image;
+ gint32 drawable = orig_drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ gboolean bigtiff = FALSE;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! save_dialog (&tsvals, image,
+ SAVE_PROC,
+ gimp_drawable_has_alpha (drawable),
+ image_is_monochrome (image),
+ gimp_image_base_type (image) == GIMP_INDEXED,
+ &image_comment,
+ error,
+ retried))
+ {
+ return GIMP_PDB_CANCEL;
+ }
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ GimpExportCapabilities capabilities;
+
+ if (tsvals.compression == COMPRESSION_CCITTFAX3 ||
+ tsvals.compression == COMPRESSION_CCITTFAX4)
+ {
+ /* G3/G4 are fax compressions. They only support
+ * monochrome images without alpha support.
+ */
+ capabilities = GIMP_EXPORT_CAN_HANDLE_INDEXED;
+ }
+ else
+ {
+ capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+ }
+
+ if (tsvals.save_layers)
+ {
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
+
+ if (tsvals.crop_layers)
+ capabilities |= GIMP_EXPORT_NEEDS_CROP;
+ }
+
+ export = gimp_export_image (&image, &drawable, "TIFF", capabilities);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ return GIMP_PDB_CANCEL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint saved_bpp;
+
+ if (save_image (file, &tsvals, image, orig_image, image_comment,
+ &saved_bpp, metadata, metadata_flags, error))
+ {
+ /* Store mvals data */
+ gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ {
+ gimp_image_delete (image);
+ }
+
+#ifdef TIFF_VERSION_BIG
+ if (status == GIMP_PDB_EXECUTION_ERROR &&
+ run_mode == GIMP_RUN_INTERACTIVE &&
+ ! retried && ! bigtiff && tiff_got_file_size_error ())
+ {
+ /* Retrying but just once, when the save failed because we exceeded
+ * TIFF max size, to propose BigTIFF instead. */
+ tiff_reset_file_size_error ();
+ g_clear_error (error);
+
+ return tiff_save_rec (run_mode, orig_image, orig_drawable,
+ file, metadata, metadata_flags, TRUE, error);
+ }
+#endif
+
+ return status;
+}
+
diff --git a/plug-ins/file-webp/Makefile.am b/plug-ins/file-webp/Makefile.am
new file mode 100644
index 0000000..20bb79b
--- /dev/null
+++ b/plug-ins/file-webp/Makefile.am
@@ -0,0 +1,64 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_webp_RC = file-webp.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-webp
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ $(WEBP_CFLAGS) \
+ $(WEBPMUX_CFLAGS) \
+ $(WEBPDEMUX_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-webp
+
+file_webp_SOURCES = \
+ file-webp.c \
+ file-webp.h \
+ file-webp-dialog.c \
+ file-webp-dialog.h \
+ file-webp-load.c \
+ file-webp-load.h \
+ file-webp-save.c \
+ file-webp-save.h
+
+file_webp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(webp_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(WEBP_LIBS) \
+ $(WEBPMUX_LIBS) \
+ $(WEBPDEMUX_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_webp_RC)
diff --git a/plug-ins/file-webp/Makefile.in b/plug-ins/file-webp/Makefile.in
new file mode 100644
index 0000000..7885e3d
--- /dev/null
+++ b/plug-ins/file-webp/Makefile.in
@@ -0,0 +1,1029 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-webp$(EXEEXT)
+subdir = plug-ins/file-webp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_webp_OBJECTS = file-webp.$(OBJEXT) file-webp-dialog.$(OBJEXT) \
+ file-webp-load.$(OBJEXT) file-webp-save.$(OBJEXT)
+file_webp_OBJECTS = $(am_file_webp_OBJECTS)
+am__DEPENDENCIES_1 =
+file_webp_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_webp_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file-webp-dialog.Po \
+ ./$(DEPDIR)/file-webp-load.Po ./$(DEPDIR)/file-webp-save.Po \
+ ./$(DEPDIR)/file-webp.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_webp_SOURCES)
+DIST_SOURCES = $(file_webp_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-webp
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_webp_RC = file-webp.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ $(WEBP_CFLAGS) \
+ $(WEBPMUX_CFLAGS) \
+ $(WEBPDEMUX_CFLAGS) \
+ -I$(includedir)
+
+file_webp_SOURCES = \
+ file-webp.c \
+ file-webp.h \
+ file-webp-dialog.c \
+ file-webp-dialog.h \
+ file-webp-load.c \
+ file-webp-load.h \
+ file-webp-save.c \
+ file-webp-save.h
+
+file_webp_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(webp_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(WEBP_LIBS) \
+ $(WEBPMUX_LIBS) \
+ $(WEBPDEMUX_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_webp_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-webp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-webp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-webp$(EXEEXT): $(file_webp_OBJECTS) $(file_webp_DEPENDENCIES) $(EXTRA_file_webp_DEPENDENCIES)
+ @rm -f file-webp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_webp_OBJECTS) $(file_webp_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-webp-dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-webp-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-webp-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-webp.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file-webp-dialog.Po
+ -rm -f ./$(DEPDIR)/file-webp-load.Po
+ -rm -f ./$(DEPDIR)/file-webp-save.Po
+ -rm -f ./$(DEPDIR)/file-webp.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file-webp-dialog.Po
+ -rm -f ./$(DEPDIR)/file-webp-load.Po
+ -rm -f ./$(DEPDIR)/file-webp-save.Po
+ -rm -f ./$(DEPDIR)/file-webp.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-webp/file-webp-dialog.c b/plug-ins/file-webp/file-webp-dialog.c
new file mode 100644
index 0000000..782c072
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-dialog.c
@@ -0,0 +1,430 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <webp/encode.h>
+
+#include "file-webp.h"
+#include "file-webp-dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void save_dialog_toggle_scale (GtkWidget *widget,
+ gpointer data);
+
+static void save_dialog_toggle_minsize (GtkWidget *widget,
+ gpointer data);
+
+static void show_maxkeyframe_hints (GtkAdjustment *adj,
+ GtkLabel *label);
+
+
+static void
+save_dialog_toggle_scale (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_scale_entry_set_sensitive (GTK_OBJECT (data),
+ ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+save_dialog_toggle_minsize (GtkWidget *widget,
+ gpointer data)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (data),
+ ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+static void
+show_maxkeyframe_hints (GtkAdjustment *adj,
+ GtkLabel *label)
+{
+ gint kmax;
+
+ kmax = (gint) gtk_adjustment_get_value (adj);
+ if (kmax == 0)
+ {
+ gtk_label_set_text (label, _("(no keyframes)"));
+ }
+ else if (kmax == 1)
+ {
+ gtk_label_set_text (label, _("(all frames are keyframes)"));
+ }
+ else
+ {
+ gtk_label_set_text (label, "");
+ }
+}
+
+gboolean
+save_dialog (WebPSaveParams *params,
+ gint32 image_ID)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ GtkWidget *vbox2;
+ GtkWidget *label;
+ GtkWidget *toggle;
+ GtkWidget *toggle_minsize;
+ GtkWidget *toggle_iptc;
+ GtkWidget *combo;
+ GtkObject *quality_scale;
+ GtkObject *alpha_quality_scale;
+ gint32 nlayers;
+ gboolean animation_supported = FALSE;
+ gboolean run;
+ gchar *text;
+ gint row = 0;
+
+ g_free (gimp_image_get_layers (image_ID, &nlayers));
+ animation_supported = nlayers > 1;
+
+ /* Create the dialog */
+ dialog = gimp_export_dialog_new (_("WebP"), PLUG_IN_BINARY, SAVE_PROC);
+
+ /* Create the vbox */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* Create the table */
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Create the lossless checkbox */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Lossless"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ params->lossless);
+ gtk_table_attach (GTK_TABLE (table), toggle,
+ 0, 3, row, row + 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (toggle);
+ row++;
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->lossless);
+
+ /* Create the slider for image quality */
+ quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
+ 0, row++,
+ _("Image _quality:"),
+ 125,
+ 0,
+ params->quality,
+ 0.0, 100.0,
+ 1.0, 10.0,
+ 0, TRUE,
+ 0.0, 0.0,
+ _("Image quality"),
+ NULL);
+ gimp_scale_entry_set_sensitive (quality_scale, ! params->lossless);
+
+ g_signal_connect (quality_scale, "value-changed",
+ G_CALLBACK (gimp_float_adjustment_update),
+ &params->quality);
+
+ /* Create the slider for alpha channel quality */
+ alpha_quality_scale = gimp_scale_entry_new (GTK_TABLE (table),
+ 0, row++,
+ _("Alpha q_uality:"),
+ 125,
+ 0,
+ params->alpha_quality,
+ 0.0, 100.0,
+ 1.0, 10.0,
+ 0, TRUE,
+ 0.0, 0.0,
+ _("Alpha channel quality"),
+ NULL);
+ gimp_scale_entry_set_sensitive (alpha_quality_scale, ! params->lossless);
+
+ g_signal_connect (alpha_quality_scale, "value-changed",
+ G_CALLBACK (gimp_float_adjustment_update),
+ &params->alpha_quality);
+
+ /* Enable and disable the sliders when the lossless option is selected */
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (save_dialog_toggle_scale),
+ quality_scale);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (save_dialog_toggle_scale),
+ alpha_quality_scale);
+
+ /* Create the combobox containing the presets */
+ combo = gimp_int_combo_box_new ("Default", WEBP_PRESET_DEFAULT,
+ "Picture", WEBP_PRESET_PICTURE,
+ "Photo", WEBP_PRESET_PHOTO,
+ "Drawing", WEBP_PRESET_DRAWING,
+ "Icon", WEBP_PRESET_ICON,
+ "Text", WEBP_PRESET_TEXT,
+ NULL);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, row++,
+ _("Source _type:"), 0.0, 0.5,
+ combo, 2, FALSE);
+ gimp_help_set_help_data (label,
+ _("WebP encoder \"preset\""),
+ NULL);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ params->preset,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &params->preset);
+
+ if (animation_supported)
+ {
+ GtkWidget *animation_box;
+ GtkAdjustment *adj;
+ GtkWidget *delay;
+ GtkWidget *hbox;
+ GtkWidget *label_kf;
+ GtkAdjustment *adj_kf;
+ GtkWidget *kf_distance;
+ GtkWidget *hbox_kf;
+ PangoAttrList *attrs;
+ PangoAttribute *attr;
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ text = g_strdup_printf ("<b>%s</b>", _("_Advanced Options"));
+ expander = gtk_expander_new_with_mnemonic (text);
+ gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE);
+ g_free (text);
+
+
+ /* Create the top-level animation checkbox expander */
+ text = g_strdup_printf ("<b>%s</b>", _("As A_nimation"));
+ toggle = gtk_check_button_new_with_mnemonic (text);
+ g_free (text);
+
+ gtk_label_set_use_markup (GTK_LABEL (gtk_bin_get_child (GTK_BIN (toggle))),
+ TRUE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ params->animation);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, TRUE, TRUE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->animation);
+
+ frame = gimp_frame_new ("<expander>");
+ gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ g_object_bind_property (toggle, "active",
+ frame, "visible",
+ G_BINDING_SYNC_CREATE);
+
+ /* animation options box */
+ animation_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), animation_box);
+ gtk_widget_show (animation_box);
+
+ /* loop animation checkbox */
+ toggle = gtk_check_button_new_with_mnemonic (_("Loop _forever"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->loop);
+ gtk_box_pack_start (GTK_BOX (animation_box), toggle,
+ FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->loop);
+
+ /* create a hbox for 'max key-frame distance */
+ hbox_kf = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (animation_box), hbox_kf, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive (hbox_kf, TRUE);
+ gtk_widget_show (hbox_kf);
+
+ /* label for 'max key-frame distance' adjustment */
+ label_kf = gtk_label_new (_("Max distance between key-frames:"));
+ gtk_label_set_xalign (GTK_LABEL (label_kf), 0.2);
+ gtk_box_pack_start (GTK_BOX (hbox_kf), label_kf, FALSE, FALSE, 0);
+ gtk_widget_show (label_kf);
+
+ /* key-frame distance entry */
+ adj_kf = (GtkAdjustment *) gtk_adjustment_new (params->kf_distance,
+ 0.0, 10000.0,
+ 1.0, 10.0, 0.0);
+ kf_distance = gimp_spin_button_new (adj_kf, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (kf_distance), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox_kf), kf_distance, FALSE, FALSE, 0);
+ gtk_widget_show (kf_distance);
+
+ g_signal_connect (adj_kf, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &params->kf_distance);
+
+ /* Add some hinting text for special values of key-frame distance. */
+ label_kf = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (hbox_kf), label_kf, FALSE, FALSE, 0);
+ gtk_widget_show (label_kf);
+
+ attrs = pango_attr_list_new ();
+ attr = pango_attr_style_new (PANGO_STYLE_ITALIC);
+ pango_attr_list_insert (attrs, attr);
+ gtk_label_set_attributes (GTK_LABEL (label_kf), attrs);
+ pango_attr_list_unref (attrs);
+
+ g_signal_connect (adj_kf, "value-changed",
+ G_CALLBACK (show_maxkeyframe_hints),
+ label_kf);
+ show_maxkeyframe_hints (adj_kf, GTK_LABEL (label_kf));
+
+ /* minimize-size checkbox */
+ toggle_minsize = gtk_check_button_new_with_mnemonic (_("_Minimize output size (slower)"));
+
+ gtk_box_pack_start (GTK_BOX (animation_box), toggle_minsize,
+ FALSE, FALSE, 0);
+ gtk_widget_show (toggle_minsize);
+
+ g_signal_connect (toggle_minsize, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->minimize_size);
+
+
+ /* Enable and disable the kf-distance box when the 'minimize size' option is selected */
+ g_signal_connect (toggle_minsize, "toggled",
+ G_CALLBACK (save_dialog_toggle_minsize),
+ hbox_kf);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_minsize), params->minimize_size);
+
+ /* create a hbox for delay */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (animation_box), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* label for 'delay' adjustment */
+ label = gtk_label_new (_("Delay between frames where unspecified:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* default delay */
+ adj = (GtkAdjustment *) gtk_adjustment_new (params->delay,
+ 1, 10000, 1, 10, 0);
+ delay = gimp_spin_button_new (adj, 1, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (delay), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), delay, FALSE, FALSE, 0);
+ gtk_widget_show (delay);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &params->delay);
+
+ /* label for 'ms' adjustment */
+ label = gtk_label_new (_("milliseconds"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Create the force-delay checkbox */
+ toggle = gtk_check_button_new_with_mnemonic (_("Use _delay entered above for all frames"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ params->force_delay);
+ gtk_box_pack_start (GTK_BOX (animation_box), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->force_delay);
+ }
+
+ /* Save EXIF data */
+ toggle = gtk_check_button_new_with_mnemonic (_("_Save Exif data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->exif);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->exif);
+
+ /* IPTC metadata */
+ toggle_iptc = gtk_check_button_new_with_mnemonic (_("Save _IPTC"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle_iptc), params->iptc);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle_iptc, FALSE, FALSE, 0);
+ gtk_widget_show (toggle_iptc);
+
+ g_signal_connect (toggle_iptc, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->iptc);
+
+ gtk_widget_set_sensitive (toggle_iptc, params->xmp);
+
+ /* XMP metadata */
+ toggle = gtk_check_button_new_with_mnemonic (_("Save _XMP data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->xmp);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->xmp);
+
+ g_object_bind_property (toggle, "active",
+ toggle_iptc, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /* Color profile */
+ toggle = gtk_check_button_new_with_mnemonic (_("Save color _profile"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->profile);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->profile);
+
+ /* Save Thumbnail */
+ toggle = gtk_check_button_new_with_mnemonic (_("Save _thumbnail"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), params->thumbnail);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &params->thumbnail);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/file-webp/file-webp-dialog.h b/plug-ins/file-webp/file-webp-dialog.h
new file mode 100644
index 0000000..b5c85ad
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-dialog.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEBP_DIALOG_H__
+#define __WEBP_DIALOG_H__
+
+
+#include "file-webp-save.h"
+
+
+gboolean save_dialog (WebPSaveParams *params,
+ gint32 image_ID);
+
+
+#endif /* __WEBP_DIALOG_H__ */
diff --git a/plug-ins/file-webp/file-webp-load.c b/plug-ins/file-webp/file-webp-load.c
new file mode 100644
index 0000000..1f49784
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-load.c
@@ -0,0 +1,294 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <webp/decode.h>
+#include <webp/demux.h>
+#include <webp/mux.h>
+
+#include <gegl.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-webp-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void
+create_layer (gint32 image_ID,
+ uint8_t *layer_data,
+ gint32 position,
+ gchar *name,
+ gint width,
+ gint height)
+{
+ gint32 layer_ID;
+ GeglBuffer *buffer;
+ GeglRectangle extent;
+
+ layer_ID = gimp_layer_new (image_ID, name,
+ width, height,
+ GIMP_RGBA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, position);
+
+ /* Retrieve the buffer for the layer */
+ buffer = gimp_drawable_get_buffer (layer_ID);
+
+ /* Copy the image data to the region */
+ gegl_rectangle_set (&extent, 0, 0, width, height);
+ gegl_buffer_set (buffer, &extent, 0, NULL, layer_data,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* Flush the drawable and detach */
+ gegl_buffer_flush (buffer);
+ g_object_unref (buffer);
+}
+
+gint32
+load_image (const gchar *filename,
+ gboolean interactive,
+ GError **error)
+{
+ uint8_t *indata = NULL;
+ gsize indatalen;
+ gint width;
+ gint height;
+ gint32 image_ID;
+ WebPMux *mux;
+ WebPData wp_data;
+ GimpColorProfile *profile = NULL;
+ uint32_t flags;
+ gboolean animation = FALSE;
+ gboolean icc = FALSE;
+ gboolean exif = FALSE;
+ gboolean xmp = FALSE;
+
+ /* Attempt to read the file contents from disk */
+ if (! g_file_get_contents (filename,
+ (gchar **) &indata,
+ &indatalen,
+ error))
+ {
+ return -1;
+ }
+
+ /* Validate WebP data */
+ if (! WebPGetInfo (indata, indatalen, &width, &height))
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Invalid WebP file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ return -1;
+ }
+
+ wp_data.bytes = indata;
+ wp_data.size = indatalen;
+
+ mux = WebPMuxCreate (&wp_data, 1);
+ if (! mux)
+ return -1;
+
+ WebPMuxGetFeatures (mux, &flags);
+
+ if (flags & ANIMATION_FLAG)
+ animation = TRUE;
+
+ if (flags & ICCP_FLAG)
+ icc = TRUE;
+
+ if (flags & EXIF_FLAG)
+ exif = TRUE;
+
+ if (flags & XMP_FLAG)
+ xmp = TRUE;
+
+ /* TODO: decode the image in "chunks" or "tiles" */
+ /* TODO: check if an alpha channel is present */
+
+ /* Create the new image and associated layer */
+ image_ID = gimp_image_new (width, height, GIMP_RGB);
+
+ if (icc)
+ {
+ WebPData icc_profile;
+
+ WebPMuxGetChunk (mux, "ICCP", &icc_profile);
+ profile = gimp_color_profile_new_from_icc_profile (icc_profile.bytes,
+ icc_profile.size, NULL);
+ if (profile)
+ gimp_image_set_color_profile (image_ID, profile);
+ }
+
+ if (! animation)
+ {
+ uint8_t *outdata;
+
+ /* Attempt to decode the data as a WebP image */
+ outdata = WebPDecodeRGBA (indata, indatalen, &width, &height);
+
+ /* Check to ensure the image data was loaded correctly */
+ if (! outdata)
+ {
+ WebPMuxDelete (mux);
+ return -1;
+ }
+
+ create_layer (image_ID, outdata, 0, _("Background"),
+ width, height);
+
+ /* Free the image data */
+ free (outdata);
+ }
+ else
+ {
+ WebPAnimDecoder *dec = NULL;
+ WebPAnimInfo anim_info;
+ WebPAnimDecoderOptions dec_options;
+ gint frame_num = 1;
+ WebPDemuxer *demux = NULL;
+ WebPIterator iter = { 0, };
+
+ if (! WebPAnimDecoderOptionsInit (&dec_options))
+ {
+ error:
+ if (dec)
+ WebPAnimDecoderDelete (dec);
+
+ if (demux)
+ {
+ WebPDemuxReleaseIterator (&iter);
+ WebPDemuxDelete (demux);
+ }
+
+ WebPMuxDelete (mux);
+ return -1;
+ }
+
+ /* dec_options.color_mode is MODE_RGBA by default here */
+ dec = WebPAnimDecoderNew (&wp_data, &dec_options);
+ if (! dec)
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Failed to decode animated WebP file '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto error;
+ }
+
+ if (! WebPAnimDecoderGetInfo (dec, &anim_info))
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Failed to decode animated WebP information from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto error;
+ }
+
+ demux = WebPDemux (&wp_data);
+ if (! demux || ! WebPDemuxGetFrame (demux, 1, &iter))
+ goto error;
+
+ /* Attempt to decode the data as a WebP animation image */
+ while (WebPAnimDecoderHasMoreFrames (dec))
+ {
+ uint8_t *outdata;
+ int timestamp;
+ gchar *name;
+
+ if (! WebPAnimDecoderGetNext (dec, &outdata, &timestamp))
+ {
+ g_set_error (error, G_FILE_ERROR, 0,
+ _("Failed to decode animated WebP frame from '%s'"),
+ gimp_filename_to_utf8 (filename));
+ goto error;
+ }
+
+ name = g_strdup_printf (_("Frame %d (%dms)"), frame_num, iter.duration);
+ create_layer (image_ID, outdata, 0, name, width, height);
+ g_free (name);
+
+ frame_num++;
+ WebPDemuxNextFrame (&iter);
+ }
+
+ WebPAnimDecoderDelete (dec);
+ WebPDemuxReleaseIterator (&iter);
+ WebPDemuxDelete (demux);
+ }
+
+ /* Free the original compressed data */
+ g_free (indata);
+
+ if (exif || xmp)
+ {
+ GimpMetadata *metadata;
+ GFile *file;
+
+ if (exif)
+ {
+ WebPData exif;
+
+ WebPMuxGetChunk (mux, "EXIF", &exif);
+ }
+
+ if (xmp)
+ {
+ WebPData xmp;
+
+ WebPMuxGetChunk (mux, "XMP ", &xmp);
+ }
+
+ file = g_file_new_for_path (filename);
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/webp",
+ file, NULL);
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (profile)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image_ID, "image/webp",
+ metadata, flags,
+ interactive);
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+ }
+
+ WebPMuxDelete (mux);
+
+ gimp_image_set_filename (image_ID, filename);
+
+ if (profile)
+ g_object_unref (profile);
+
+ return image_ID;
+}
diff --git a/plug-ins/file-webp/file-webp-load.h b/plug-ins/file-webp/file-webp-load.h
new file mode 100644
index 0000000..9025f24
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-load.h
@@ -0,0 +1,31 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEBP_LOAD_H__
+#define __WEBP_LOAD_H__
+
+
+gint32 load_image (const gchar *filename,
+ gboolean interactive,
+ GError **error);
+
+
+#endif /* __WEBP_LOAD_H__ */
diff --git a/plug-ins/file-webp/file-webp-save.c b/plug-ins/file-webp/file-webp-save.c
new file mode 100644
index 0000000..51dffd9
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-save.c
@@ -0,0 +1,903 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <gegl.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <webp/encode.h>
+#include <webp/mux.h>
+
+#include "file-webp-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+int webp_anim_file_writer (FILE *outfile,
+ const uint8_t *data,
+ size_t data_size);
+int webp_file_writer (const uint8_t *data,
+ size_t data_size,
+ const WebPPicture *picture);
+int webp_file_progress (int percent,
+ const WebPPicture *picture);
+gchar * webp_error_string (WebPEncodingError error_code);
+
+gboolean save_layer (const gchar *filename,
+ gint32 nLayers,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ WebPSaveParams *params,
+ GError **error);
+
+gboolean save_animation (const gchar *filename,
+ gint32 nLayers,
+ gint32 *allLayers,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ WebPSaveParams *params,
+ GError **error);
+
+static void webp_decide_output (gint32 image_ID,
+ WebPSaveParams *params,
+ GimpColorProfile **profile,
+ gboolean *out_linear);
+
+int
+webp_anim_file_writer (FILE *outfile,
+ const uint8_t *data,
+ size_t data_size)
+{
+ int ok = 0;
+
+ if (data == NULL)
+ return 0;
+
+ ok = (fwrite (data, data_size, 1, outfile) == 1);
+
+ return ok;
+}
+
+int
+webp_file_writer (const uint8_t *data,
+ size_t data_size,
+ const WebPPicture *picture)
+{
+ FILE *outfile;
+
+ /* Obtain the FILE* and write the data to the file */
+ outfile = (FILE *) picture->custom_ptr;
+
+ return fwrite (data, sizeof (uint8_t), data_size, outfile) == data_size;
+}
+
+int
+webp_file_progress (int percent,
+ const WebPPicture *picture)
+{
+ return gimp_progress_update (percent / 100.0);
+}
+
+gchar *
+webp_error_string (WebPEncodingError error_code)
+{
+ switch (error_code)
+ {
+ case VP8_ENC_ERROR_OUT_OF_MEMORY:
+ return g_strdup (_("out of memory"));
+ case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY:
+ return g_strdup (_("not enough memory to flush bits"));
+ case VP8_ENC_ERROR_NULL_PARAMETER:
+ return g_strdup (_("NULL parameter"));
+ case VP8_ENC_ERROR_INVALID_CONFIGURATION:
+ return g_strdup (_("invalid configuration"));
+ case VP8_ENC_ERROR_BAD_DIMENSION:
+ /* TRANSLATORS: widthxheight with UTF-8 encoded multiply sign. */
+ return g_strdup_printf (_("bad image dimensions (maximum: %d\xc3\x97%d)"),
+ WEBP_MAX_DIMENSION, WEBP_MAX_DIMENSION);
+ case VP8_ENC_ERROR_PARTITION0_OVERFLOW:
+ return g_strdup (_("partition is bigger than 512K"));
+ case VP8_ENC_ERROR_PARTITION_OVERFLOW:
+ return g_strdup (_("partition is bigger than 16M"));
+ case VP8_ENC_ERROR_BAD_WRITE:
+ return g_strdup (_("unable to flush bytes"));
+ case VP8_ENC_ERROR_FILE_TOO_BIG:
+ return g_strdup (_("file is larger than 4GiB"));
+ case VP8_ENC_ERROR_USER_ABORT:
+ return g_strdup (_("user aborted encoding"));
+ case VP8_ENC_ERROR_LAST:
+ return g_strdup (_("list terminator"));
+ default:
+ return g_strdup (_("unknown error"));
+ }
+}
+
+gboolean
+save_layer (const gchar *filename,
+ gint32 nLayers,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ WebPSaveParams *params,
+ GError **error)
+{
+ gboolean status = FALSE;
+ FILE *outfile = NULL;
+ WebPConfig config = {0};
+ WebPPicture picture = {0};
+ guchar *buffer = NULL;
+ gint w, h;
+ gboolean has_alpha;
+ const Babl *format;
+ gint bpp;
+ GimpColorProfile *profile = NULL;
+ GeglBuffer *geglbuffer = NULL;
+ GeglRectangle extent;
+ gchar *indata;
+ gsize indatalen;
+ struct stat stsz;
+ int fd_outfile;
+ WebPData chunk;
+ gboolean out_linear = FALSE;
+ int res;
+
+ webp_decide_output (image_ID, params, &profile, &out_linear);
+
+ /* The do...while() loop is a neat little trick that makes it easier
+ * to jump to error handling code while still ensuring proper
+ * cleanup
+ */
+
+ do
+ {
+ /* Begin displaying export progress */
+ gimp_progress_init_printf (_("Saving '%s'"),
+ gimp_filename_to_utf8(filename));
+
+ /* Attempt to open the output file */
+ if ((outfile = g_fopen (filename, "w+b")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Unable to open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename),
+ g_strerror (errno));
+ break;
+ }
+
+ /* Obtain the drawable type */
+ has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (has_alpha)
+ {
+ if (out_linear)
+ format = babl_format ("RGBA u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+ }
+ else
+ {
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ /* Retrieve the buffer for the layer */
+ geglbuffer = gimp_drawable_get_buffer (drawable_ID);
+ extent = *gegl_buffer_get_extent (geglbuffer);
+ w = extent.width;
+ h = extent.height;
+
+ /* Initialize the WebP configuration with a preset and fill in the
+ * remaining values */
+ WebPConfigPreset (&config, params->preset, params->quality);
+
+ config.lossless = params->lossless;
+ config.method = 6; /* better quality */
+ config.alpha_quality = params->alpha_quality;
+
+ /* Prepare the WebP structure */
+ WebPPictureInit (&picture);
+ picture.use_argb = 1;
+ picture.width = w;
+ picture.height = h;
+ picture.writer = webp_file_writer;
+ picture.custom_ptr = outfile;
+ picture.progress_hook = webp_file_progress;
+
+ /* Attempt to allocate a buffer of the appropriate size */
+ buffer = g_try_malloc (w * h * bpp);
+ if (! buffer)
+ break;
+
+ /* Read the region into the buffer */
+ gegl_buffer_get (geglbuffer, &extent, 1.0, format, buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* Use the appropriate function to import the data from the buffer */
+ if (! has_alpha)
+ {
+ status = WebPPictureImportRGB (&picture, buffer, w * bpp);
+ }
+ else
+ {
+ status = WebPPictureImportRGBA (&picture, buffer, w * bpp);
+ }
+
+ g_free (buffer);
+ if (! status)
+ {
+ g_printerr ("%s: memory error in WebPPictureImportRGB(A)().",
+ G_STRFUNC);
+ break;
+ }
+
+ /* Perform the actual encode */
+ if (! WebPEncode (&config, &picture))
+ {
+ gchar *error_str = webp_error_string (picture.error_code);
+
+ g_printerr ("WebP error: '%s'", error_str);
+ g_set_error (error, G_FILE_ERROR,
+ picture.error_code,
+ _("WebP error: '%s'"),
+ error_str);
+ g_free (error_str);
+ status = FALSE;
+ break;
+ }
+
+ /* The cleanup stuff still needs to run but indicate that everything
+ * completed successfully
+ */
+ status = TRUE;
+
+ }
+ while (0);
+
+ /* Flush the drawable and detach */
+ if (geglbuffer)
+ {
+ g_object_unref (geglbuffer);
+ }
+
+ fflush (outfile);
+ fd_outfile = fileno (outfile);
+ fstat (fd_outfile, &stsz);
+ indatalen = stsz.st_size;
+ if (indatalen > 0)
+ {
+ indata = (gchar*) g_malloc (indatalen);
+ rewind (outfile);
+ res = fread (indata, 1, indatalen, outfile);
+ if (res > 0)
+ {
+ WebPMux *mux;
+ WebPData wp_data;
+
+ wp_data.bytes = (uint8_t*) indata;
+ wp_data.size = indatalen;
+ mux = WebPMuxCreate (&wp_data, 1);
+
+ if (mux)
+ {
+ /* Save ICC data */
+ if (profile)
+ {
+ const guint8 *icc_data;
+ gsize icc_data_size;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile,
+ &icc_data_size);
+ chunk.bytes = icc_data;
+ chunk.size = icc_data_size;
+ WebPMuxSetChunk(mux, "ICCP", &chunk, 1);
+
+ WebPMuxAssemble (mux, &wp_data);
+ rewind (outfile);
+ webp_anim_file_writer (outfile, wp_data.bytes, wp_data.size);
+ }
+
+ WebPMuxDelete (mux);
+ }
+ else
+ {
+ g_printerr ("ERROR: Cannot create mux. Can't save features update.\n");
+ }
+
+ WebPDataClear (&wp_data);
+ }
+ else
+ {
+ g_printerr ("ERROR: No data read for features. Can't save features update.\n");
+ }
+ }
+ else
+ {
+ g_printerr ("ERROR: No data for features. Can't save features update.\n");
+ }
+
+ /* Free any resources */
+ if (outfile)
+ fclose (outfile);
+
+ WebPPictureFree (&picture);
+ g_clear_object (&profile);
+
+ return status;
+}
+
+static gint
+parse_ms_tag (const gchar *str)
+{
+ gint sum = 0;
+ gint offset = 0;
+ gint length;
+
+ length = strlen (str);
+
+ find_another_bra:
+
+ while ((offset < length) && (str[offset] != '('))
+ offset++;
+
+ if (offset >= length)
+ return -1;
+
+ if (! g_ascii_isdigit (str[++offset]))
+ goto find_another_bra;
+
+ do
+ {
+ sum *= 10;
+ sum += str[offset] - '0';
+ offset++;
+ }
+ while ((offset < length) && (g_ascii_isdigit (str[offset])));
+
+ if (length - offset <= 2)
+ return -3;
+
+ if ((g_ascii_toupper (str[offset]) != 'M') ||
+ (g_ascii_toupper (str[offset + 1]) != 'S'))
+ return -4;
+
+ return sum;
+}
+
+static gint
+get_layer_delay (gint32 layer)
+{
+ gchar *layer_name;
+ gint delay_ms;
+
+ layer_name = gimp_item_get_name (layer);
+ delay_ms = parse_ms_tag (layer_name);
+ g_free (layer_name);
+
+ return delay_ms;
+}
+
+static gboolean
+parse_combine (const char* str)
+{
+ gint offset = 0;
+ gint length = strlen (str);
+
+ while ((offset + 9) <= length)
+ {
+ if (strncmp (&str[offset], "(combine)", 9) == 0)
+ return TRUE;
+
+ if (strncmp (&str[offset], "(replace)", 9) == 0)
+ return FALSE;
+
+ offset++;
+ }
+
+ return FALSE;
+}
+
+static gint
+get_layer_needs_combine (gint32 layer)
+{
+ gchar *layer_name;
+ gboolean needs_combine;
+
+ layer_name = gimp_item_get_name (layer);
+ needs_combine = parse_combine (layer_name);
+ g_free (layer_name);
+
+ return needs_combine;
+}
+
+static GeglBuffer*
+combine_buffers (GeglBuffer *layer_buffer,
+ GeglBuffer *prev_frame_buffer)
+{
+ GeglBuffer *buffer;
+ GeglNode *graph;
+ GeglNode *source;
+ GeglNode *backdrop;
+ GeglNode *over;
+ GeglNode *target;
+
+ graph = gegl_node_new ();
+ buffer = gegl_buffer_new (gegl_buffer_get_extent (prev_frame_buffer),
+ gegl_buffer_get_format (prev_frame_buffer));
+
+ source = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-source",
+ "buffer", layer_buffer,
+ NULL);
+ backdrop = gegl_node_new_child (graph,
+ "operation", "gegl:buffer-source",
+ "buffer", prev_frame_buffer,
+ NULL);
+
+ over = gegl_node_new_child (graph,
+ "operation", "gegl:over",
+ NULL);
+ target = gegl_node_new_child (graph,
+ "operation", "gegl:write-buffer",
+ "buffer", buffer,
+ NULL);
+ gegl_node_link_many (backdrop, over, target, NULL);
+ gegl_node_connect_to (source, "output",
+ over, "aux");
+ gegl_node_process (target);
+ g_object_unref (graph);
+
+ return buffer;
+}
+
+gboolean
+save_animation (const gchar *filename,
+ gint32 nLayers,
+ gint32 *allLayers,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ WebPSaveParams *params,
+ GError **error)
+{
+ gboolean status = TRUE;
+ FILE *outfile = NULL;
+ guchar *buffer = NULL;
+ gint buffer_size = 0;
+ gint w, h;
+ gint bpp;
+ gboolean has_alpha;
+ const Babl *format;
+ GimpColorProfile *profile = NULL;
+ WebPAnimEncoderOptions enc_options;
+ WebPData webp_data;
+ int frame_timestamp = 0;
+ WebPAnimEncoder *enc = NULL;
+ GeglBuffer *prev_frame = NULL;
+ gboolean out_linear = FALSE;
+
+ if (nLayers < 1)
+ return FALSE;
+
+ webp_decide_output (image_ID, params, &profile, &out_linear);
+
+ gimp_image_undo_freeze (image_ID);
+
+ WebPDataInit (&webp_data);
+
+ do
+ {
+ gint loop;
+ gint default_delay = params->delay;
+ gboolean force_delay = params->force_delay;
+
+ /* Begin displaying export progress */
+ gimp_progress_init_printf (_("Saving '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Attempt to open the output file */
+ if ((outfile = g_fopen (filename, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Unable to open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename),
+ g_strerror (errno));
+ status = FALSE;
+ break;
+ }
+
+ if (! WebPAnimEncoderOptionsInit (&enc_options))
+ {
+ g_printerr ("ERROR: version mismatch\n");
+ status = FALSE;
+ break;
+ }
+
+ enc_options.anim_params.loop_count = 0;
+ if (! params->loop)
+ enc_options.anim_params.loop_count = 1;
+
+ enc_options.allow_mixed = params->lossless ? 0 : 1;
+ enc_options.minimize_size = params->minimize_size ? 1 : 0;
+ if (! params->minimize_size)
+ {
+ enc_options.kmax = params->kf_distance;
+ /* explicitly force minimum key-frame distance too, for good measure */
+ enc_options.kmin = params->kf_distance - 1;
+ }
+
+ for (loop = 0; loop < nLayers; loop++)
+ {
+ GeglBuffer *geglbuffer;
+ GeglBuffer *current_frame;
+ GeglRectangle extent;
+ WebPConfig config;
+ WebPPicture picture;
+ WebPMemoryWriter mw = { 0 };
+ gint32 drawable = allLayers[nLayers - 1 - loop];
+ gint delay = get_layer_delay (drawable);
+ gboolean needs_combine = get_layer_needs_combine (drawable);
+
+ /* Obtain the drawable type */
+ has_alpha = gimp_drawable_has_alpha (drawable);
+
+ if (has_alpha)
+ {
+ if (out_linear)
+ format = babl_format ("RGBA u8");
+ else
+ format = babl_format ("R'G'B'A u8");
+ }
+ else
+ {
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ }
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ /* fix layers to avoid offset errors */
+ gimp_layer_resize_to_image_size (drawable);
+
+ /* Retrieve the buffer for the layer */
+ geglbuffer = gimp_drawable_get_buffer (drawable);
+ extent = *gegl_buffer_get_extent (geglbuffer);
+ w = extent.width;
+ h = extent.height;
+
+ if (loop == 0)
+ {
+ enc = WebPAnimEncoderNew (w, h, &enc_options);
+ if (! enc)
+ {
+ g_printerr ("ERROR: enc == null\n");
+ status = FALSE;
+ break;
+ }
+ }
+
+ /* Attempt to allocate a buffer of the appropriate size */
+ if (! buffer || buffer_size < w * h * bpp)
+ {
+ buffer = g_try_realloc (buffer, w * h * bpp);
+
+ if (! buffer)
+ {
+ g_printerr ("Buffer error: 'buffer null'\n");
+ status = FALSE;
+ break;
+ }
+ else
+ {
+ buffer_size = w * h * bpp;
+ }
+ }
+
+ WebPConfigPreset (&config, params->preset, params->quality);
+
+ config.lossless = params->lossless;
+ config.method = 6; /* better quality */
+ config.alpha_quality = params->alpha_quality;
+ config.exact = 1;
+
+ WebPMemoryWriterInit (&mw);
+
+ /* Prepare the WebP structure */
+ WebPPictureInit (&picture);
+ picture.use_argb = 1;
+ picture.argb_stride = w * bpp;
+ picture.width = w;
+ picture.height = h;
+ picture.custom_ptr = &mw;
+ picture.writer = WebPMemoryWrite;
+
+ if (loop == 0 || ! needs_combine)
+ {
+ g_clear_object (&prev_frame);
+ current_frame = geglbuffer;
+ }
+ else
+ {
+ current_frame = combine_buffers (geglbuffer, prev_frame);
+
+ /* release resources. */
+ g_object_unref (geglbuffer);
+ g_clear_object (&prev_frame);
+ }
+ prev_frame = current_frame;
+
+ /* Read the region into the buffer */
+ gegl_buffer_get (current_frame, &extent, 1.0, format, buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ /* Use the appropriate function to import the data from the buffer */
+ if (! has_alpha)
+ {
+ status = WebPPictureImportRGB (&picture, buffer, w * bpp);
+ }
+ else
+ {
+ status = WebPPictureImportRGBA (&picture, buffer, w * bpp);
+ }
+
+ if (! status)
+ {
+ g_printerr ("%s: memory error in WebPPictureImportRGB(A)().",
+ G_STRFUNC);
+ }
+ /* Perform the actual encode */
+ else if (! WebPAnimEncoderAdd (enc, &picture, frame_timestamp, &config))
+ {
+ gchar *error_str = webp_error_string (picture.error_code);
+ g_printerr ("ERROR[%d]: line %d: %s\n",
+ picture.error_code, __LINE__,
+ error_str);
+ g_free (error_str);
+ status = FALSE;
+ }
+
+ WebPMemoryWriterClear (&mw);
+ WebPPictureFree (&picture);
+
+ if (status == FALSE)
+ break;
+
+ gimp_progress_update ((loop + 1.0) / nLayers);
+ frame_timestamp += (delay <= 0 || force_delay) ? default_delay : delay;
+ }
+ g_free (buffer);
+
+ if (status == FALSE)
+ break;
+
+ WebPAnimEncoderAdd (enc, NULL, frame_timestamp, NULL);
+
+ if (! WebPAnimEncoderAssemble (enc, &webp_data))
+ {
+ g_printerr ("ERROR: %s\n",
+ WebPAnimEncoderGetError (enc));
+ status = FALSE;
+ break;
+ }
+
+ /* Create a mux object if profile is present */
+ if (profile)
+ {
+ WebPMux *mux;
+ WebPData chunk;
+ const guint8 *icc_data;
+ gsize icc_data_size;
+
+ mux = WebPMuxCreate (&webp_data, 1);
+ if (mux == NULL)
+ {
+ g_printerr ("ERROR: could not extract muxing object\n");
+ status = FALSE;
+ break;
+ }
+
+ /* Save ICC data */
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_data_size);
+ chunk.bytes = icc_data;
+ chunk.size = icc_data_size;
+ WebPMuxSetChunk (mux, "ICCP", &chunk, 1);
+
+ WebPDataClear (&webp_data);
+ if (WebPMuxAssemble (mux, &webp_data) != WEBP_MUX_OK)
+ {
+ g_printerr ("ERROR: could not assemble final bytestream\n");
+ status = FALSE;
+ break;
+ }
+ }
+
+ webp_anim_file_writer (outfile, webp_data.bytes, webp_data.size);
+ }
+ while (0);
+
+ /* Free any resources */
+ WebPDataClear (&webp_data);
+ WebPAnimEncoderDelete (enc);
+ g_clear_object (&profile);
+
+ if (prev_frame != NULL)
+ {
+ g_object_unref (prev_frame);
+ }
+
+ if (outfile)
+ fclose (outfile);
+
+ return status;
+}
+
+
+gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ WebPSaveParams *params,
+ GError **error)
+{
+ GFile *file;
+ gboolean status = FALSE;
+ gint32 *layers;
+ gint nlayers;
+
+ layers = gimp_image_get_layers (image_ID, &nlayers);
+
+ if (nlayers == 0)
+ {
+ g_free (layers);
+ return FALSE;
+ }
+
+ g_printerr ("Saving WebP file %s\n", filename);
+
+ if (params->animation)
+ {
+ status = save_animation (filename,
+ nlayers, layers, image_ID, drawable_ID, params,
+ error);
+ }
+ else
+ {
+ status = save_layer (filename,
+ nlayers, image_ID, drawable_ID, params, error);
+ }
+
+ g_free (layers);
+
+ if (metadata)
+ {
+ gimp_metadata_set_bits_per_sample (metadata, 8);
+
+ if (params->exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ /* WebP doesn't support iptc natively and
+ sets it via xmp */
+ if (params->xmp)
+ {
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ if (params->iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+ }
+ else
+ {
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+ }
+
+ if (params->profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ if (params->thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ file = g_file_new_for_path (filename);
+ gimp_image_metadata_save_finish (image_ID,
+ "image/webp",
+ metadata, metadata_flags,
+ file, NULL);
+ g_object_unref (file);
+ }
+
+ /* Return the status */
+ return status;
+}
+
+static void
+webp_decide_output (gint32 image_ID,
+ WebPSaveParams *params,
+ GimpColorProfile **profile,
+ gboolean *out_linear)
+{
+ g_return_if_fail (profile && *profile == NULL);
+
+ *out_linear = FALSE;
+ if (params->profile)
+ {
+ *profile = gimp_image_get_color_profile (image_ID);
+
+ /* If a profile is explicitly set, follow its TRC, whatever the
+ * storage format.
+ */
+ if (*profile && gimp_color_profile_is_linear (*profile))
+ *out_linear = TRUE;
+
+ /* When no profile was explicitly set, since WebP is apparently
+ * 8-bit max, we export it as sRGB to avoid shadow posterization
+ * (we don't care about storage TRC).
+ * We do an exception for 8-bit linear work image to avoid
+ * conversion loss while the precision is the same.
+ */
+ if (! *profile)
+ {
+ /* There is always an effective profile. */
+ *profile = gimp_image_get_effective_color_profile (image_ID);
+
+ if (gimp_color_profile_is_linear (*profile))
+ {
+ if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR)
+ {
+ /* If stored data was linear, let's convert the profile. */
+ GimpColorProfile *saved_profile;
+
+ saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (*profile);
+ g_clear_object (profile);
+ *profile = saved_profile;
+ }
+ else
+ {
+ /* Keep linear profile as-is for 8-bit linear image. */
+ *out_linear = TRUE;
+ }
+ }
+ }
+ }
+}
diff --git a/plug-ins/file-webp/file-webp-save.h b/plug-ins/file-webp/file-webp-save.h
new file mode 100644
index 0000000..ddb2afb
--- /dev/null
+++ b/plug-ins/file-webp/file-webp-save.h
@@ -0,0 +1,55 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __WEBP_SAVE_H__
+#define __WEBP_SAVE_H__
+
+
+typedef struct
+{
+ WebPPreset preset;
+ gboolean lossless;
+ gboolean animation;
+ gboolean loop;
+ gboolean minimize_size;
+ gint kf_distance;
+ gfloat quality;
+ gfloat alpha_quality;
+ gboolean exif;
+ gboolean iptc;
+ gboolean xmp;
+ gboolean profile;
+ gboolean thumbnail;
+ gint delay;
+ gboolean force_delay;
+} WebPSaveParams;
+
+
+gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ WebPSaveParams *params,
+ GError **error);
+
+
+#endif /* __WEBP_SAVE_H__ */
diff --git a/plug-ins/file-webp/file-webp.c b/plug-ins/file-webp/file-webp.c
new file mode 100644
index 0000000..219e6a6
--- /dev/null
+++ b/plug-ins/file-webp/file-webp.c
@@ -0,0 +1,384 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <webp/encode.h>
+
+#include "file-webp-dialog.h"
+#include "file-webp-load.h"
+#include "file-webp-save.h"
+#include "file-webp.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL,
+ NULL,
+ query,
+ run
+};
+
+
+MAIN()
+
+static void
+query (void)
+{
+ static const GimpParamDef load_arguments[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" }
+ };
+
+ static const GimpParamDef load_return_values[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ static const GimpParamDef save_arguments[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image to" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "preset", "preset (Default=0, Picture=1, Photo=2, Drawing=3, Icon=4, Text=5)" },
+ { GIMP_PDB_INT32, "lossless", "Use lossless encoding (0/1)" },
+ { GIMP_PDB_FLOAT, "quality", "Quality of the image (0 <= quality <= 100)" },
+ { GIMP_PDB_FLOAT, "alpha-quality", "Quality of the image's alpha channel (0 <= alpha-quality <= 100)" },
+ { GIMP_PDB_INT32, "animation", "Use layers for animation (0/1)" },
+ { GIMP_PDB_INT32, "anim-loop", "Loop animation infinitely (0/1)" },
+ { GIMP_PDB_INT32, "minimize-size", "Minimize animation size (0/1)" },
+ { GIMP_PDB_INT32, "kf-distance", "Maximum distance between key-frames (>=0)" },
+ { GIMP_PDB_INT32, "exif", "Toggle saving exif data (0/1)" },
+ { GIMP_PDB_INT32, "iptc", "Toggle saving iptc data (0/1)" },
+ { GIMP_PDB_INT32, "xmp", "Toggle saving xmp data (0/1)" },
+ { GIMP_PDB_INT32, "delay", "Delay to use when timestamps are not available or forced" },
+ { GIMP_PDB_INT32, "force-delay", "Force delay on all frames" }
+ };
+
+ static const GimpParamDef save_arguments2[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image to" },
+ { GIMP_PDB_STRING, "raw-filename", "The name entered" },
+ { GIMP_PDB_INT32, "preset", "preset (Default=0, Picture=1, Photo=2, Drawing=3, Icon=4, Text=5)" },
+ { GIMP_PDB_INT32, "lossless", "Use lossless encoding (0/1)" },
+ { GIMP_PDB_FLOAT, "quality", "Quality of the image (0 <= quality <= 100)" },
+ { GIMP_PDB_FLOAT, "alpha-quality", "Quality of the image's alpha channel (0 <= alpha-quality <= 100)" },
+ { GIMP_PDB_INT32, "animation", "Use layers for animation (0/1)" },
+ { GIMP_PDB_INT32, "anim-loop", "Loop animation infinitely (0/1)" },
+ { GIMP_PDB_INT32, "minimize-size", "Minimize animation size (0/1)" },
+ { GIMP_PDB_INT32, "kf-distance", "Maximum distance between key-frames (>=0)" },
+ { GIMP_PDB_INT32, "exif", "Toggle saving exif data (0/1)" },
+ { GIMP_PDB_INT32, "iptc", "Toggle saving iptc data (0/1)" },
+ { GIMP_PDB_INT32, "xmp", "Toggle saving xmp data (0/1)" },
+ { GIMP_PDB_INT32, "thumbnail", "Toggle saving thumbnail (0/1)" },
+ { GIMP_PDB_INT32, "delay", "Delay to use when timestamps are not available or forced" },
+ { GIMP_PDB_INT32, "force-delay", "Force delay on all frames" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "Loads images in the WebP file format",
+ "Loads images in the WebP file format",
+ "Nathan Osman, Ben Touchette",
+ "(C) 2015-2016 Nathan Osman, (C) 2016 Ben Touchette",
+ "2015,2016",
+ N_("WebP image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_arguments),
+ G_N_ELEMENTS (load_return_values),
+ load_arguments,
+ load_return_values);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/webp");
+ gimp_register_load_handler (LOAD_PROC, "webp", "");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "webp",
+ "",
+ "8,string,WEBP");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Saves files in the WebP image format",
+ "Saves files in the WebP image format",
+ "Nathan Osman, Ben Touchette",
+ "(C) 2015-2016 Nathan Osman, (C) 2016 Ben Touchette",
+ "2015,2016",
+ N_("WebP image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_arguments),
+ 0,
+ save_arguments,
+ NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/webp");
+ gimp_register_save_handler (SAVE_PROC, "webp", "");
+
+ gimp_install_procedure (SAVE_PROC2,
+ "Saves files in the WebP image format",
+ "Saves files in the WebP image format "
+ "with additional metadata control",
+ "Nathan Osman, Ben Touchette",
+ "(C) 2015-2016 Nathan Osman, (C) 2016 Ben Touchette",
+ "2015,2016",
+ N_("WebP image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_arguments2),
+ 0,
+ save_arguments2,
+ NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC2, "image/webp");
+ gimp_register_save_handler (SAVE_PROC2, "webp", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (! strcmp (name, LOAD_PROC))
+ {
+ image_ID = load_image (param[1].data.d_string, FALSE, &error);
+
+ if (image_ID != -1)
+ {
+ /* Return the new image that was loaded */
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (! strcmp (name, SAVE_PROC) ||
+ ! strcmp (name, SAVE_PROC2))
+ {
+ GimpMetadata *metadata = NULL;
+ GimpMetadataSaveFlags metadata_flags;
+ WebPSaveParams params;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE ||
+ run_mode == GIMP_RUN_WITH_LAST_VALS)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ /* Default settings */
+ params.preset = WEBP_PRESET_DEFAULT;
+ params.lossless = FALSE;
+ params.animation = FALSE;
+ params.loop = TRUE;
+ params.minimize_size = TRUE;
+ params.kf_distance = 50;
+ params.quality = 90.0f;
+ params.alpha_quality = 100.0f;
+ params.exif = FALSE;
+ params.iptc = FALSE;
+ params.xmp = FALSE;
+ params.delay = 200;
+ params.force_delay = FALSE;
+ params.thumbnail = FALSE;
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (image_ID,
+ "image/webp",
+ &metadata_flags);
+ params.exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ params.xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ params.iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ params.profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ if (! strcmp (name, SAVE_PROC2))
+ params.thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly override with session data */
+ if (! strcmp (name, SAVE_PROC))
+ gimp_get_data (SAVE_PROC, &params);
+ else if (! strcmp (name, SAVE_PROC2))
+ gimp_get_data (SAVE_PROC2, &params);
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly override with session data */
+ if (! strcmp (name, SAVE_PROC))
+ gimp_get_data (SAVE_PROC, &params);
+ else if (! strcmp (name, SAVE_PROC2))
+ gimp_get_data (SAVE_PROC2, &params);
+
+ if (! save_dialog (&params, image_ID))
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if ((! strcmp (name, SAVE_PROC) && nparams != 18) ||
+ (! strcmp (name, SAVE_PROC2) && nparams != 19))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ if (param[5].data.d_int32 < WEBP_PRESET_DEFAULT ||
+ param[5].data.d_int32 > WEBP_PRESET_TEXT)
+ params.preset = WEBP_PRESET_DEFAULT;
+ else
+ params.preset = param[5].data.d_int32;
+
+ params.lossless = param[6].data.d_int32;
+ params.quality = param[7].data.d_float;
+ params.alpha_quality = param[8].data.d_float;
+ params.animation = param[9].data.d_int32;
+ params.loop = param[10].data.d_int32;
+ params.minimize_size = param[11].data.d_int32;
+ params.kf_distance = param[12].data.d_int32;
+ params.exif = param[13].data.d_int32;
+ params.iptc = param[14].data.d_int32;
+ params.xmp = param[15].data.d_int32;
+ if (! strcmp (name, SAVE_PROC))
+ {
+ params.delay = param[16].data.d_int32;
+ params.force_delay = param[17].data.d_int32;
+ }
+ else
+ {
+ params.thumbnail = param[16].data.d_int32;
+ params.delay = param[17].data.d_int32;
+ params.force_delay = param[18].data.d_int32;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS && (run_mode == GIMP_RUN_INTERACTIVE ||
+ run_mode == GIMP_RUN_WITH_LAST_VALS))
+ {
+ GimpExportCapabilities capabilities =
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA;
+
+ if (params.animation)
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION;
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "WebP",
+ capabilities);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ {
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ status = GIMP_PDB_CANCEL;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string,
+ image_ID,
+ drawable_ID,
+ metadata, metadata_flags,
+ &params,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+
+ if (export == GIMP_EXPORT_EXPORT)
+ gimp_image_delete (image_ID);
+
+ if (metadata)
+ g_object_unref (metadata);
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* save parameters for later */
+ if (! strcmp (name, SAVE_PROC))
+ gimp_set_data (SAVE_PROC, &params, sizeof (params));
+ else if (! strcmp (name, SAVE_PROC2))
+ gimp_set_data (SAVE_PROC2, &params, sizeof (params));
+ }
+ }
+
+ /* If an error was supplied, include it in the return values */
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/file-webp/file-webp.h b/plug-ins/file-webp/file-webp.h
new file mode 100644
index 0000000..6d77e3b
--- /dev/null
+++ b/plug-ins/file-webp/file-webp.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * file-webp - WebP file format plug-in for the GIMP
+ * Copyright (C) 2015 Nathan Osman
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FILE_WEBP_H__
+#define __FILE_WEBP_H__
+
+
+#define LOAD_PROC "file-webp-load"
+#define SAVE_PROC "file-webp-save"
+#define SAVE_PROC2 "file-webp-save2"
+#define PLUG_IN_BINARY "file-webp"
+#define PLUG_IN_ROLE "gimp-file-webp"
+
+
+#endif /* __FILE_WEBP_H__ */
diff --git a/plug-ins/flame/Makefile.am b/plug-ins/flame/Makefile.am
new file mode 100644
index 0000000..4171d89
--- /dev/null
+++ b/plug-ins/flame/Makefile.am
@@ -0,0 +1,59 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+flame_RC = flame.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/flame
+
+libexec_PROGRAMS = flame
+
+flame_SOURCES = \
+ cmap.c \
+ cmap.h \
+ flame.c \
+ flame.h \
+ libifs.c \
+ libifs.h \
+ rect.c \
+ rect.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = README
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(flame_RC)
diff --git a/plug-ins/flame/Makefile.in b/plug-ins/flame/Makefile.in
new file mode 100644
index 0000000..04bece6
--- /dev/null
+++ b/plug-ins/flame/Makefile.in
@@ -0,0 +1,1021 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = flame$(EXEEXT)
+subdir = plug-ins/flame
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_flame_OBJECTS = cmap.$(OBJEXT) flame.$(OBJEXT) libifs.$(OBJEXT) \
+ rect.$(OBJEXT)
+flame_OBJECTS = $(am_flame_OBJECTS)
+flame_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+flame_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(flame_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cmap.Po ./$(DEPDIR)/flame.Po \
+ ./$(DEPDIR)/libifs.Po ./$(DEPDIR)/rect.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(flame_SOURCES)
+DIST_SOURCES = $(flame_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/flame
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@flame_RC = flame.rc.o
+AM_LDFLAGS = $(mwindows)
+flame_SOURCES = \
+ cmap.c \
+ cmap.h \
+ flame.c \
+ flame.h \
+ libifs.c \
+ libifs.h \
+ rect.c \
+ rect.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+EXTRA_DIST = README
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(flame_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/flame/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/flame/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+flame$(EXEEXT): $(flame_OBJECTS) $(flame_DEPENDENCIES) $(EXTRA_flame_DEPENDENCIES)
+ @rm -f flame$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(flame_OBJECTS) $(flame_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flame.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libifs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rect.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/cmap.Po
+ -rm -f ./$(DEPDIR)/flame.Po
+ -rm -f ./$(DEPDIR)/libifs.Po
+ -rm -f ./$(DEPDIR)/rect.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cmap.Po
+ -rm -f ./$(DEPDIR)/flame.Po
+ -rm -f ./$(DEPDIR)/libifs.Po
+ -rm -f ./$(DEPDIR)/rect.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/flame/README b/plug-ins/flame/README
new file mode 100644
index 0000000..5525468
--- /dev/null
+++ b/plug-ins/flame/README
@@ -0,0 +1,64 @@
+flame - cosmic recursive fractal flames
+Scott Draves <spot@cs.cmu.edu>
+
+get source code from
+http://www.cs.cmu.edu/~spot/gimp/flame.tar.gz
+
+images, documentation, and other interfaces (a batch animation
+renderer and a low-quality interactive editor) are available from
+http://www.cs.cmu.edu/~spot/flame.html
+
+-----------------------
+
+you are encouraged to exhibit the output of this software publicly as
+long as this software is credited as the source of the images.
+
+i also encourage you to let me know what you make with it, what you
+dis/like about it, and how it can be improved.
+
+-----------------------
+
+0.12 as of Thu Oct 9
+added variation_same. made preview have same aspect ratio as final
+image. included binary in tar file.
+
+0.11 as of Sun Sep 28
+patch from Owen Taylor <owt1@cornell.edu> gtk_signal_connect_object ->
+gtk_signal_connect. hacked mw routines so i can update my previews.
+removed much of mw code that i don't use.
+
+0.10 as of Fri Sep 26
+Added "eight directions" edit window. relayedout other widgets.
+
+0.9 as of Tue Sep 23
+reconfigured to use Makefile.am. cleaned warnings out of code.
+
+0.8 as of Thu Sep 18
+added Makefile.in.patch, made gimp integration easier. changed
+license. added some built-in cmaps to the menu.
+
+0.7 as of Sun Sep 14
+fixed image leak (thx Marcelo Malheiros). removed UI to black cmap
+(default is now gradient). added preview of the flame, disabled
+randomize mode in favor of a randomize button. added beginning of
+edit dialog, including multi-threaded computation of previews, but
+disabled for the release.
+
+0.6 as of Thu Sep 11
+added preview of cmap. added black cmap. added access to current
+gradient, but there is an image leak.
+
+0.5 as of Sat Sep 6
+cmaps now come from image menus. clarified license terms.
+
+0.4 as of Thu Sep 4
+added variation menu. removed text display, added load/store buttons.
+fixed alpha blend to be stable (alpha of 0 has no effect) and protect
+against overflow.
+
+0.3 as of Tue Sep 2
+added alpha channel
+
+0.2 as of Aug 24 1997
+real -> double, other header file reorganization. added new cmaps.
+added gimp interface.
diff --git a/plug-ins/flame/cmap.c b/plug-ins/flame/cmap.c
new file mode 100644
index 0000000..e8e1edb
--- /dev/null
+++ b/plug-ins/flame/cmap.c
@@ -0,0 +1,5776 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#include "config.h"
+
+#include <math.h>
+
+#include <glib.h>
+
+#include "cmap.h"
+
+
+static const guchar the_cmaps[][256][3] =
+{
+ /* south-sea-bather */
+ {
+ {185, 234, 235}, {193, 238, 235}, {197, 242, 235}, {201, 242, 235},
+ {201, 246, 235}, {205, 246, 235}, {205, 246, 235}, {205, 242, 235},
+ {209, 242, 235}, {210, 238, 235}, {209, 242, 225}, {214, 242, 235},
+ {221, 246, 254}, {213, 242, 244}, {242, 250, 244}, {226, 242, 235},
+ {222, 242, 235}, {214, 242, 235}, {214, 242, 244}, {209, 238, 244},
+ {209, 238, 244}, {205, 238, 244}, {205, 238, 235}, {201, 238, 235},
+ {201, 238, 235}, {201, 238, 244}, {201, 238, 244}, {201, 242, 244},
+ {205, 242, 244}, {209, 242, 244}, {210, 242, 244}, {209, 246, 244},
+ {205, 242, 244}, {197, 242, 244}, {189, 242, 244}, {189, 242, 244},
+ {185, 238, 244}, {181, 242, 244}, {189, 242, 244}, {193, 242, 244},
+ {197, 242, 254}, {197, 242, 254}, {189, 242, 244}, {181, 242, 244},
+ {177, 242, 244}, {181, 238, 244}, {189, 234, 235}, {189, 234, 235},
+ {193, 230, 235}, {193, 230, 235}, {189, 230, 225}, {181, 230, 225},
+ {165, 230, 225}, {165, 226, 225}, {161, 226, 235}, {157, 230, 234},
+ {153, 222, 244}, {165, 226, 244}, {165, 230, 244}, {165, 230, 244},
+ {169, 226, 244}, {173, 226, 235}, {177, 226, 235}, {177, 222, 235},
+ {177, 226, 235}, {177, 230, 244}, {177, 226, 244}, {177, 226, 244},
+ {177, 226, 244}, {173, 226, 244}, {169, 226, 244}, {161, 230, 254},
+ {157, 230, 254}, {165, 234, 244}, {173, 234, 244}, {177, 238, 234},
+ {185, 238, 235}, {193, 238, 235}, {197, 238, 235}, {197, 238, 235},
+ {201, 238, 235}, {201, 242, 244}, {197, 242, 244}, {197, 238, 244},
+ {197, 234, 244}, {197, 234, 244}, {197, 234, 244}, {193, 230, 235},
+ {193, 230, 235}, {197, 234, 225}, {197, 230, 226}, {194, 226, 207},
+ {206, 155, 132}, {178, 127, 113}, {166, 132, 85}, {145, 128, 85},
+ {154, 128, 85}, {162, 114, 85}, {162, 118, 75}, {182, 114, 75},
+ {186, 127, 103}, {210, 164, 132}, {198, 226, 198}, {201, 234, 225},
+ {206, 238, 216}, {222, 171, 131}, {206, 155, 122}, {190, 144, 122},
+ {202, 151, 122}, {219, 163, 132}, {230, 183, 150}, {250, 233, 206},
+ {222, 234, 235}, {209, 238, 235}, {193, 226, 235}, {189, 222, 235},
+ {181, 222, 235}, {173, 226, 244}, {169, 226, 244}, {169, 226, 244},
+ {173, 230, 244}, {173, 234, 235}, {173, 234, 234}, {173, 230, 235},
+ {173, 226, 235}, {173, 226, 234}, {177, 230, 226}, {181, 230, 226},
+ {189, 230, 235}, {193, 230, 244}, {197, 234, 244}, {201, 234, 244},
+ {201, 238, 244}, {201, 238, 244}, {201, 238, 244}, {201, 238, 244},
+ {201, 238, 244}, {201, 242, 244}, {201, 242, 244}, {201, 242, 244},
+ {201, 242, 244}, {201, 242, 244}, {197, 238, 244}, {193, 238, 254},
+ {177, 238, 254}, {173, 230, 244}, {177, 230, 244}, {177, 234, 244},
+ {181, 238, 244}, {189, 238, 244}, {193, 242, 244}, {201, 246, 244},
+ {205, 246, 244}, {205, 246, 244}, {205, 246, 244}, {205, 242, 244},
+ {205, 238, 244}, {205, 238, 254}, {201, 238, 254}, {197, 238, 254},
+ {193, 238, 244}, {193, 238, 244}, {193, 234, 244}, {193, 234, 235},
+ {193, 234, 235}, {193, 234, 235}, {189, 234, 244}, {185, 234, 244},
+ {181, 234, 244}, {181, 230, 244}, {181, 230, 244}, {181, 234, 244},
+ {189, 234, 244}, {193, 234, 244}, {197, 234, 235}, {197, 234, 235},
+ {201, 234, 235}, {201, 234, 244}, {205, 234, 244}, {205, 238, 244},
+ {205, 238, 244}, {205, 238, 244}, {205, 242, 244}, {201, 242, 235},
+ {201, 242, 235}, {197, 242, 235}, {193, 234, 244}, {185, 230, 244},
+ {181, 226, 244}, {181, 226, 244}, {181, 230, 244}, {181, 230, 244},
+ {185, 234, 235}, {189, 238, 235}, {189, 242, 235}, {193, 238, 235},
+ {197, 238, 235}, {197, 238, 235}, {197, 238, 225}, {193, 234, 225},
+ {189, 222, 216}, {170, 148, 113}, {117, 104, 66}, {72, 55, 37},
+ {11, 12, 9}, {36, 44, 37}, {76, 117, 103}, {158, 145, 113},
+ {177, 206, 197}, {189, 226, 216}, {189, 234, 226}, {193, 238, 235},
+ {189, 238, 244}, {189, 238, 244}, {189, 238, 244}, {185, 234, 244},
+ {185, 234, 244}, {185, 230, 244}, {189, 230, 235}, {189, 230, 235},
+ {189, 234, 244}, {193, 238, 244}, {197, 242, 254}, {201, 246, 254},
+ {201, 242, 254}, {197, 238, 254}, {193, 238, 244}, {189, 238, 235},
+ {185, 234, 235}, {177, 230, 234}, {181, 230, 235}, {181, 230, 235},
+ {185, 226, 235}, {181, 230, 235}, {189, 230, 235}, {193, 234, 235},
+ {193, 234, 244}, {189, 234, 244}, {185, 230, 244}, {181, 230, 244},
+ {177, 230, 244}, {177, 230, 235}, {169, 226, 235}, {169, 226, 235},
+ {161, 222, 225}, {137, 190, 197}, {158, 145, 122}, {149, 124, 103},
+ {133, 121, 103}, {141, 106, 75}, {141, 95, 66}, {133, 99, 66},
+ {121, 108, 66}, {121, 100, 56}, {117, 104, 65}, {93, 89, 56},
+ },
+ /* sky-flesh */
+ {
+ {166, 148, 122}, {219, 162, 132}, {238, 212, 178}, {250, 237, 206},
+ {238, 250, 235}, {226, 242, 235}, {218, 242, 225}, {206, 242, 225},
+ {206, 238, 225}, {206, 234, 207}, {182, 169, 141}, {178, 138, 103},
+ {178, 130, 103}, {170, 147, 113}, {177, 198, 179}, {181, 230, 225},
+ {185, 230, 235}, {189, 230, 244}, {197, 230, 244}, {201, 234, 244},
+ {205, 234, 244}, {205, 238, 244}, {201, 234, 235}, {197, 234, 225},
+ {193, 222, 207}, {170, 148, 122}, {145, 127, 94}, {117, 100, 66},
+ {60, 64, 47}, {20, 36, 28}, {8, 20, 9}, {20, 28, 18},
+ {56, 60, 37}, {113, 91, 56}, {145, 106, 75}, {166, 121, 85},
+ {190, 137, 103}, {230, 190, 169}, {218, 230, 226}, {214, 238, 235},
+ {209, 238, 244}, {205, 238, 244}, {205, 238, 244}, {201, 238, 244},
+ {197, 234, 244}, {197, 234, 244}, {197, 230, 244}, {197, 230, 244},
+ {197, 230, 244}, {193, 234, 244}, {185, 230, 244}, {177, 226, 235},
+ {157, 206, 206}, {120, 153, 141}, {96, 133, 132}, {108, 141, 141},
+ {141, 177, 178}, {173, 226, 225}, {177, 230, 235}, {185, 234, 235},
+ {185, 242, 244}, {197, 242, 244}, {197, 238, 244}, {197, 238, 244},
+ {197, 238, 244}, {193, 234, 244}, {189, 230, 235}, {185, 214, 197},
+ {154, 144, 122}, {150, 127, 94}, {146, 128, 85}, {133, 115, 75},
+ {133, 120, 75}, {125, 107, 75}, {125, 112, 85}, {133, 111, 75},
+ {141, 101, 66}, {129, 106, 75}, {137, 106, 66}, {133, 102, 66},
+ {141, 91, 66}, {141, 93, 56}, {137, 93, 47}, {117, 87, 37},
+ {109, 74, 37}, {56, 44, 28}, {12, 20, 9}, {4, 16, 0},
+ {12, 28, 9}, {52, 47, 28}, {80, 76, 47}, {113, 91, 56},
+ {121, 94, 56}, {125, 98, 56}, {113, 87, 37}, {85, 63, 28},
+ {40, 44, 28}, {12, 12, 9}, {0, 12, 0}, {4, 24, 9},
+ {28, 60, 37}, {56, 88, 75}, {121, 104, 84}, {145, 132, 103},
+ {170, 160, 132}, {193, 226, 207}, {197, 238, 225}, {205, 242, 235},
+ {205, 242, 244}, {205, 238, 244}, {201, 238, 244}, {201, 238, 244},
+ {201, 238, 244}, {197, 238, 244}, {193, 234, 244}, {189, 230, 235},
+ {189, 226, 225}, {186, 189, 151}, {186, 150, 103}, {166, 135, 94},
+ {166, 134, 94}, {162, 130, 94}, {162, 130, 94}, {166, 130, 85},
+ {162, 130, 85}, {158, 126, 75}, {150, 118, 75}, {158, 113, 75},
+ {150, 117, 66}, {154, 117, 66}, {149, 114, 75}, {158, 131, 85},
+ {166, 139, 85}, {174, 142, 85}, {182, 155, 113}, {198, 194, 169},
+ {205, 230, 216}, {205, 234, 235}, {197, 222, 244}, {193, 226, 244},
+ {185, 230, 244}, {181, 226, 244}, {177, 226, 226}, {145, 165, 160},
+ {158, 144, 103}, {158, 144, 94}, {166, 143, 94}, {186, 160, 122},
+ {197, 222, 207}, {202, 226, 226}, {201, 234, 235}, {197, 234, 235},
+ {193, 238, 244}, {189, 234, 244}, {173, 226, 244}, {173, 222, 235},
+ {137, 157, 160}, {137, 120, 113}, {129, 124, 103}, {141, 132, 94},
+ {158, 126, 94}, {170, 143, 113}, {178, 185, 160}, {185, 230, 225},
+ {189, 230, 235}, {193, 234, 235}, {193, 234, 235}, {193, 234, 225},
+ {182, 194, 170}, {162, 144, 113}, {146, 119, 85}, {113, 92, 66},
+ {68, 64, 37}, {24, 32, 9}, {0, 16, 9}, {0, 12, 9},
+ {12, 16, 18}, {56, 47, 28}, {113, 83, 46}, {133, 90, 56},
+ {150, 108, 56}, {154, 109, 66}, {158, 104, 66}, {153, 100, 56},
+ {165, 95, 47}, {158, 103, 47}, {158, 108, 56}, {158, 104, 66},
+ {157, 110, 75}, {158, 104, 75}, {162, 113, 75}, {162, 108, 75},
+ {158, 113, 66}, {170, 112, 66}, {174, 116, 75}, {182, 124, 75},
+ {174, 120, 75}, {182, 124, 85}, {190, 142, 103}, {214, 192, 169},
+ {210, 230, 225}, {214, 234, 235}, {214, 234, 235}, {210, 234, 235},
+ {209, 234, 235}, {205, 234, 235}, {205, 238, 235}, {202, 238, 235},
+ {197, 238, 235}, {197, 238, 235}, {197, 238, 235}, {197, 238, 235},
+ {201, 238, 235}, {202, 238, 235}, {205, 238, 235}, {205, 238, 235},
+ {210, 242, 235}, {214, 242, 235}, {214, 238, 225}, {210, 234, 226},
+ {198, 210, 188}, {178, 151, 122}, {158, 115, 103}, {125, 107, 85},
+ {92, 97, 94}, {48, 76, 66}, {12, 24, 28}, {8, 12, 18},
+ {4, 8, 9}, {16, 28, 18}, {56, 56, 37}, {100, 75, 56},
+ {133, 98, 66}, {146, 110, 85}, {154, 123, 94}, {158, 140, 103},
+ {178, 177, 141}, {185, 230, 216}, {193, 234, 235}, {197, 234, 235},
+ {206, 234, 225}, {206, 234, 207}, {214, 162, 132}, {190, 137, 94},
+ {178, 125, 94}, {170, 123, 94}, {170, 125, 94}, {166, 135, 113},
+ {178, 168, 141}, {185, 226, 207}, {181, 230, 235}, {185, 230, 244},
+ {185, 230, 244}, {181, 226, 235}, {190, 210, 198}, {170, 152, 113},
+ },
+ /* blue-bather */
+ {
+ {125, 100, 84}, {162, 99, 75}, {174, 99, 75}, {182, 115, 104},
+ {190, 124, 122}, {190, 137, 141}, {194, 153, 151}, {206, 161, 160},
+ {218, 173, 169}, {230, 169, 188}, {238, 172, 197}, {234, 180, 197},
+ {234, 176, 188}, {222, 180, 188}, {222, 173, 179}, {226, 161, 169},
+ {226, 149, 169}, {214, 141, 151}, {210, 141, 151}, {198, 145, 141},
+ {194, 137, 132}, {190, 132, 122}, {178, 124, 104}, {150, 104, 85},
+ {113, 92, 75}, {56, 64, 75}, {52, 68, 103}, {48, 60, 122},
+ {40, 60, 132}, {39, 56, 132}, {40, 56, 122}, {36, 52, 122},
+ {36, 52, 103}, {40, 36, 56}, {31, 24, 47}, {12, 24, 37},
+ {4, 20, 28}, {0, 16, 28}, {4, 16, 28}, {12, 24, 28},
+ {40, 31, 37}, {52, 40, 56}, {44, 48, 66}, {52, 56, 113},
+ {48, 68, 141}, {56, 81, 160}, {64, 85, 170}, {68, 97, 179},
+ {72, 97, 188}, {72, 97, 188}, {72, 101, 188}, {72, 105, 179},
+ {76, 101, 179}, {80, 101, 179}, {173, 124, 123}, {186, 136, 132},
+ {186, 141, 132}, {190, 141, 141}, {190, 141, 132}, {190, 145, 132},
+ {194, 141, 132}, {190, 137, 132}, {186, 137, 132}, {177, 128, 132},
+ {76, 105, 179}, {80, 105, 188}, {84, 109, 188}, {186, 153, 151},
+ {190, 157, 151}, {198, 161, 151}, {202, 165, 160}, {202, 161, 170},
+ {202, 165, 170}, {202, 169, 170}, {202, 169, 170}, {206, 173, 170},
+ {206, 173, 170}, {210, 173, 170}, {210, 169, 170}, {202, 165, 160},
+ {202, 165, 160}, {202, 161, 151}, {198, 153, 151}, {198, 145, 141},
+ {194, 145, 141}, {194, 145, 141}, {198, 145, 132}, {198, 136, 122},
+ {182, 120, 113}, {170, 103, 94}, {158, 88, 75}, {84, 60, 56},
+ {48, 52, 56}, {40, 40, 47}, {36, 48, 47}, {40, 48, 56},
+ {40, 52, 66}, {40, 52, 113}, {48, 56, 132}, {52, 68, 151},
+ {60, 81, 170}, {72, 93, 179}, {72, 97, 179}, {72, 97, 179},
+ {72, 97, 179}, {73, 93, 170}, {169, 111, 122}, {186, 128, 132},
+ {186, 132, 132}, {190, 128, 141}, {194, 116, 132}, {182, 115, 113},
+ {190, 111, 104}, {178, 99, 85}, {170, 84, 75}, {117, 84, 84},
+ {68, 60, 85}, {52, 68, 113}, {48, 64, 132}, {56, 73, 141},
+ {109, 88, 122}, {158, 116, 104}, {169, 124, 113}, {161, 128, 113},
+ {73, 101, 170}, {72, 101, 179}, {72, 101, 179}, {73, 97, 170},
+ {165, 120, 113}, {170, 120, 113}, {178, 124, 113}, {182, 120, 113},
+ {178, 119, 103}, {154, 107, 85}, {121, 96, 94}, {52, 64, 132},
+ {44, 64, 132}, {48, 68, 141}, {52, 73, 151}, {56, 85, 160},
+ {64, 89, 170}, {64, 93, 170}, {64, 93, 170}, {68, 93, 170},
+ {68, 97, 170}, {84, 97, 151}, {145, 104, 94}, {174, 80, 75},
+ {162, 72, 66}, {84, 60, 65}, {64, 52, 66}, {52, 48, 66},
+ {77, 73, 75}, {141, 100, 84}, {170, 116, 104}, {182, 120, 113},
+ {182, 120, 113}, {178, 124, 113}, {174, 124, 122}, {141, 104, 132},
+ {64, 77, 151}, {52, 68, 151}, {48, 64, 141}, {44, 56, 141},
+ {52, 68, 151}, {56, 81, 170}, {68, 89, 179}, {64, 97, 188},
+ {72, 97, 188}, {76, 101, 188}, {76, 105, 188}, {80, 109, 188},
+ {182, 137, 151}, {194, 149, 151}, {198, 153, 160}, {198, 153, 160},
+ {194, 153, 151}, {186, 149, 141}, {186, 141, 132}, {169, 128, 122},
+ {76, 101, 179}, {72, 101, 179}, {68, 101, 179}, {60, 93, 170},
+ {52, 81, 160}, {48, 73, 151}, {52, 73, 151}, {60, 77, 151},
+ {129, 100, 113}, {169, 120, 113}, {182, 132, 122}, {186, 132, 122},
+ {186, 132, 122}, {170, 124, 123}, {76, 97, 170}, {68, 93, 170},
+ {68, 93, 170}, {68, 85, 170}, {64, 85, 170}, {64, 81, 160},
+ {68, 85, 122}, {129, 92, 84}, {170, 95, 75}, {178, 91, 75},
+ {186, 111, 94}, {194, 119, 94}, {186, 119, 104}, {186, 132, 113},
+ {186, 136, 113}, {182, 140, 113}, {174, 128, 113}, {166, 120, 113},
+ {80, 84, 151}, {56, 73, 151}, {52, 68, 151}, {48, 68, 151},
+ {48, 68, 151}, {44, 64, 151}, {44, 64, 141}, {44, 68, 141},
+ {48, 73, 141}, {48, 73, 141}, {44, 73, 151}, {56, 85, 170},
+ {68, 97, 179}, {72, 97, 188}, {72, 101, 188}, {72, 105, 198},
+ {76, 101, 188}, {80, 109, 188}, {186, 141, 141}, {186, 141, 141},
+ {194, 149, 151}, {202, 157, 151}, {206, 157, 151}, {202, 161, 151},
+ {198, 153, 141}, {194, 149, 132}, {190, 148, 122}, {186, 136, 113},
+ {182, 123, 104}, {182, 115, 104}, {166, 103, 75}, {113, 92, 65},
+ {56, 60, 56}, {36, 52, 47}, {36, 52, 47}, {32, 44, 37},
+ {56, 39, 28}, {146, 68, 66}, {174, 80, 66}, {182, 87, 66},
+ {194, 107, 104}, {194, 111, 113}, {194, 115, 113}, {190, 116, 103},
+ },
+ /* no-name */
+ {
+ {16, 9, 9}, {57, 50, 24}, {85, 76, 49}, {100, 96, 80},
+ {121, 117, 88}, {138, 105, 74}, {141, 92, 73}, {144, 76, 66},
+ {139, 70, 53}, {127, 60, 43}, {99, 44, 25}, {75, 35, 29},
+ {61, 27, 18}, {41, 12, 9}, {19, 8, 8}, {13, 8, 8},
+ {14, 8, 8}, {31, 12, 10}, {45, 34, 14}, {71, 49, 21},
+ {89, 72, 38}, {103, 70, 29}, {107, 74, 31}, {107, 76, 31},
+ {95, 75, 35}, {82, 65, 33}, {68, 40, 18}, {52, 24, 12},
+ {41, 11, 7}, {16, 7, 7}, {11, 6, 6}, {8, 6, 6},
+ {7, 6, 6}, {8, 6, 6}, {8, 6, 6}, {9, 7, 7},
+ {11, 8, 8}, {13, 8, 12}, {15, 9, 13}, {19, 18, 20},
+ {41, 32, 16}, {59, 53, 27}, {87, 74, 37}, {115, 89, 34},
+ {129, 97, 34}, {137, 107, 44}, {136, 103, 35}, {136, 108, 36},
+ {141, 109, 38}, {137, 109, 44}, {139, 113, 45}, {140, 111, 47},
+ {140, 108, 50}, {155, 102, 67}, {148, 101, 76}, {151, 100, 78},
+ {153, 105, 84}, {155, 104, 89}, {162, 113, 99}, {170, 123, 116},
+ {172, 140, 123}, {168, 144, 123}, {168, 140, 121}, {169, 140, 125},
+ {164, 141, 121}, {165, 135, 116}, {156, 123, 99}, {153, 117, 94},
+ {141, 104, 88}, {132, 93, 72}, {99, 89, 54}, {90, 75, 43},
+ {46, 45, 33}, {17, 26, 41}, {16, 20, 38}, {18, 25, 32},
+ {47, 42, 23}, {65, 58, 32}, {92, 75, 43}, {116, 93, 36},
+ {122, 96, 37}, {135, 97, 41}, {138, 87, 56}, {147, 91, 69},
+ {141, 91, 68}, {140, 85, 58}, {140, 88, 69}, {143, 84, 66},
+ {135, 76, 53}, {134, 76, 53}, {130, 71, 46}, {127, 70, 44},
+ {126, 66, 43}, {128, 71, 43}, {127, 87, 31}, {126, 89, 30},
+ {131, 93, 34}, {129, 97, 33}, {128, 91, 34}, {122, 87, 34},
+ {112, 76, 30}, {96, 67, 28}, {79, 48, 24}, {56, 39, 21},
+ {36, 32, 19}, {19, 17, 22}, {15, 8, 14}, {12, 8, 12},
+ {10, 8, 8}, {10, 7, 7}, {9, 7, 8}, {8, 8, 8},
+ {9, 8, 8}, {10, 8, 8}, {11, 8, 8}, {13, 8, 8},
+ {16, 9, 8}, {36, 11, 8}, {49, 18, 14}, {69, 39, 21},
+ {96, 59, 24}, {122, 75, 23}, {139, 97, 32}, {148, 109, 39},
+ {148, 119, 52}, {148, 120, 58}, {148, 118, 60}, {148, 106, 81},
+ {150, 108, 81}, {156, 110, 86}, {162, 117, 98}, {165, 127, 114},
+ {162, 144, 123}, {145, 144, 133}, {140, 140, 125}, {139, 142, 126},
+ {141, 161, 177}, {169, 153, 146}, {166, 152, 140}, {167, 148, 135},
+ {171, 143, 125}, {167, 138, 120}, {167, 128, 110}, {163, 118, 96},
+ {154, 109, 85}, {146, 100, 76}, {136, 80, 58}, {117, 58, 52},
+ {84, 46, 39}, {62, 27, 19}, {47, 15, 8}, {25, 8, 8},
+ {14, 8, 8}, {14, 8, 8}, {32, 11, 7}, {48, 17, 11},
+ {70, 35, 17}, {88, 44, 27}, {118, 51, 33}, {119, 65, 38},
+ {119, 65, 38}, {122, 66, 43}, {116, 69, 36}, {101, 76, 38},
+ {90, 73, 39}, {68, 53, 25}, {43, 33, 21}, {17, 20, 31},
+ {15, 17, 34}, {16, 16, 29}, {14, 10, 21}, {15, 8, 17},
+ {38, 26, 16}, {67, 35, 14}, {91, 51, 19}, {106, 58, 20},
+ {121, 72, 23}, {124, 83, 26}, {118, 86, 39}, {105, 91, 37},
+ {90, 73, 40}, {63, 56, 33}, {26, 30, 32}, {16, 17, 32},
+ {14, 15, 27}, {12, 8, 17}, {14, 8, 14}, {17, 8, 8},
+ {40, 11, 7}, {53, 24, 15}, {75, 31, 18}, {103, 48, 18},
+ {122, 55, 33}, {120, 71, 30}, {130, 88, 30}, {141, 101, 40},
+ {139, 100, 51}, {139, 103, 70}, {120, 121, 97}, {118, 113, 124},
+ {137, 138, 123}, {137, 140, 123}, {130, 131, 117}, {123, 123, 98},
+ {128, 125, 108}, {125, 125, 93}, {151, 109, 83}, {169, 127, 69},
+ {166, 129, 66}, {167, 131, 62}, {167, 130, 62}, {160, 123, 54},
+ {155, 129, 57}, {152, 124, 55}, {153, 124, 53}, {154, 120, 46},
+ {154, 119, 48}, {146, 116, 53}, {135, 108, 46}, {128, 107, 56},
+ {104, 95, 65}, {92, 85, 52}, {93, 80, 46}, {81, 68, 35},
+ {59, 51, 27}, {41, 32, 16}, {20, 11, 9}, {13, 8, 8},
+ {12, 8, 8}, {12, 8, 8}, {13, 8, 8}, {13, 7, 7},
+ {16, 8, 8}, {36, 9, 7}, {49, 15, 7}, {50, 15, 10},
+ {54, 21, 14}, {63, 27, 16}, {67, 28, 16}, {70, 27, 20},
+ {74, 26, 16}, {80, 31, 17}, {86, 30, 16}, {84, 37, 20},
+ {98, 55, 20}, {101, 65, 25}, {111, 70, 23}, {121, 83, 29},
+ {122, 96, 37}, {130, 105, 44}, {126, 103, 44}, {108, 94, 64},
+ {109, 98, 68}, {106, 98, 69}, {106, 102, 74}, {116, 105, 71},
+ },
+ /* pillows */
+ {
+ {75, 58, 47}, {58, 43, 43}, {43, 34, 28}, {28, 28, 22},
+ {17, 17, 17}, {17, 17, 17}, {17, 17, 17}, {11, 17, 11},
+ {11, 11, 11}, {11, 11, 11}, {11, 11, 11}, {11, 11, 11},
+ {11, 11, 11}, {11, 17, 11}, {17, 17, 17}, {28, 17, 17},
+ {43, 22, 17}, {60, 28, 22}, {77, 45, 28}, {94, 54, 32},
+ {119, 68, 34}, {141, 90, 45}, {173, 101, 50}, {191, 106, 50},
+ {203, 117, 67}, {186, 117, 78}, {175, 112, 67}, {141, 96, 62},
+ {128, 79, 62}, {113, 73, 51}, {114, 66, 43}, {122, 77, 43},
+ {151, 73, 22}, {160, 73, 28}, {138, 73, 22}, {117, 65, 26},
+ {103, 60, 37}, {83, 49, 26}, {77, 49, 20}, {60, 28, 17},
+ {45, 17, 11}, {26, 17, 11}, {17, 17, 17}, {17, 17, 17},
+ {17, 17, 17}, {28, 22, 17}, {39, 22, 22}, {54, 34, 28},
+ {60, 34, 22}, {81, 34, 22}, {81, 34, 17}, {87, 39, 17},
+ {104, 45, 11}, {131, 50, 0}, {156, 61, 0}, {153, 67, 5},
+ {136, 67, 5}, {125, 50, 11}, {107, 51, 17}, {107, 51, 17},
+ {104, 50, 17}, {102, 51, 17}, {90, 62, 34}, {83, 54, 32},
+ {92, 64, 30}, {96, 68, 39}, {96, 62, 45}, {104, 73, 56},
+ {119, 79, 56}, {126, 81, 58}, {136, 96, 62}, {136, 96, 62},
+ {136, 90, 56}, {117, 83, 54}, {102, 73, 51}, {100, 60, 49},
+ {96, 60, 43}, {96, 62, 39}, {90, 62, 39}, {90, 62, 39},
+ {86, 64, 41}, {86, 64, 47}, {81, 58, 52}, {71, 54, 49},
+ {73, 62, 56}, {92, 75, 69}, {119, 79, 90}, {130, 102, 102},
+ {145, 162, 173}, {175, 192, 197}, {210, 216, 227}, {243, 243, 243},
+ {252, 246, 252}, {246, 246, 241}, {250, 250, 239}, {246, 246, 241},
+ {242, 197, 157}, {201, 121, 88}, {169, 112, 96}, {141, 102, 90},
+ {134, 84, 79}, {107, 68, 68}, {85, 51, 62}, {69, 49, 49},
+ {54, 39, 39}, {45, 34, 28}, {34, 28, 22}, {34, 22, 17},
+ {54, 22, 17}, {58, 20, 32}, {62, 35, 35}, {73, 51, 39},
+ {79, 56, 39}, {81, 58, 47}, {92, 64, 47}, {96, 68, 51},
+ {92, 75, 58}, {107, 85, 62}, {111, 83, 60}, {113, 85, 62},
+ {122, 66, 77}, {134, 83, 83}, {121, 76, 93}, {119, 73, 84},
+ {103, 70, 65}, {104, 73, 56}, {100, 66, 49}, {86, 64, 47},
+ {73, 62, 45}, {73, 51, 39}, {56, 39, 28}, {28, 28, 28},
+ {17, 22, 17}, {17, 17, 17}, {17, 17, 17}, {22, 28, 22},
+ {28, 39, 45}, {28, 53, 78}, {64, 58, 52}, {75, 51, 51},
+ {100, 60, 54}, {119, 68, 79}, {121, 76, 76}, {145, 62, 79},
+ {130, 85, 73}, {147, 84, 34}, {179, 56, 56}, {158, 73, 22},
+ {171, 73, 11}, {201, 5, 11}, {182, 49, 0}, {153, 61, 11},
+ {153, 78, 11}, {167, 78, 5}, {150, 56, 11}, {134, 50, 5},
+ {124, 22, 11}, {109, 17, 17}, {96, 34, 22}, {83, 45, 22},
+ {83, 39, 17}, {73, 34, 22}, {62, 28, 22}, {54, 28, 17},
+ {41, 35, 18}, {28, 22, 17}, {22, 17, 17}, {17, 17, 17},
+ {11, 17, 11}, {11, 17, 11}, {11, 17, 11}, {17, 17, 17},
+ {17, 17, 17}, {17, 17, 17}, {17, 17, 17}, {17, 17, 17},
+ {11, 11, 11}, {11, 11, 11}, {5, 5, 5}, {5, 5, 5},
+ {0, 5, 0}, {0, 11, 0}, {0, 11, 0}, {0, 11, 0},
+ {0, 11, 0}, {5, 11, 5}, {5, 11, 5}, {11, 17, 5},
+ {17, 11, 11}, {26, 11, 11}, {34, 17, 11}, {52, 17, 11},
+ {56, 28, 22}, {60, 34, 34}, {62, 37, 37}, {75, 49, 43},
+ {66, 54, 49}, {66, 54, 49}, {60, 54, 49}, {62, 51, 45},
+ {66, 45, 39}, {69, 43, 43}, {58, 32, 47}, {45, 34, 39},
+ {34, 28, 28}, {34, 34, 22}, {45, 39, 28}, {52, 47, 35},
+ {71, 54, 43}, {79, 51, 56}, {96, 68, 79}, {119, 79, 90},
+ {149, 115, 109}, {164, 169, 175}, {185, 191, 196}, {222, 228, 233},
+ {241, 241, 241}, {254, 214, 186}, {240, 206, 139}, {203, 128, 78},
+ {186, 112, 78}, {173, 90, 67}, {141, 84, 51}, {124, 79, 56},
+ {113, 79, 62}, {86, 81, 75}, {86, 86, 118}, {134, 134, 156},
+ {167, 139, 162}, {197, 140, 135}, {173, 127, 116}, {155, 133, 104},
+ {141, 102, 79}, {156, 90, 67}, {169, 106, 50}, {164, 101, 28},
+ {151, 73, 16}, {116, 45, 17}, {88, 34, 17}, {77, 28, 11},
+ {66, 17, 17}, {36, 5, 5}, {22, 11, 0}, {17, 20, 0},
+ {17, 11, 5}, {17, 11, 0}, {11, 11, 11}, {11, 11, 11},
+ {11, 11, 11}, {11, 11, 11}, {5, 11, 5}, {5, 17, 5},
+ {5, 17, 5}, {5, 11, 5}, {11, 11, 11}, {17, 11, 11},
+ },
+ /* mauve-splat */
+ {
+ {22, 22, 22}, {22, 22, 22}, {22, 17, 20}, {22, 11, 15},
+ {17, 5, 11}, {11, 5, 11}, {11, 5, 11}, {11, 5, 11},
+ {11, 5, 11}, {11, 5, 11}, {11, 5, 11}, {11, 5, 15},
+ {11, 11, 15}, {11, 11, 15}, {11, 11, 17}, {17, 11, 17},
+ {17, 17, 17}, {17, 17, 17}, {17, 17, 17}, {17, 11, 11},
+ {17, 11, 11}, {17, 11, 11}, {17, 5, 11}, {11, 5, 9},
+ {5, 5, 5}, {5, 5, 5}, {5, 5, 5}, {5, 5, 9},
+ {5, 5, 11}, {5, 5, 15}, {11, 5, 17}, {17, 5, 17},
+ {17, 5, 15}, {17, 5, 15}, {11, 5, 11}, {11, 5, 11},
+ {11, 5, 11}, {11, 5, 11}, {11, 5, 15}, {11, 11, 17},
+ {11, 11, 17}, {17, 11, 17}, {17, 11, 20}, {17, 5, 22},
+ {17, 11, 20}, {22, 11, 20}, {22, 17, 22}, {22, 22, 22},
+ {22, 28, 17}, {22, 28, 22}, {22, 22, 22}, {22, 22, 22},
+ {22, 22, 22}, {22, 22, 22}, {22, 22, 22}, {22, 17, 22},
+ {22, 17, 20}, {22, 17, 17}, {22, 17, 17}, {28, 17, 17},
+ {28, 17, 17}, {22, 17, 17}, {22, 11, 17}, {22, 11, 17},
+ {17, 17, 17}, {17, 17, 22}, {17, 11, 22}, {11, 11, 17},
+ {11, 11, 17}, {11, 11, 17}, {5, 11, 15}, {5, 5, 11},
+ {5, 0, 11}, {5, 0, 9}, {5, 5, 5}, {11, 5, 5},
+ {17, 5, 5}, {22, 11, 11}, {22, 17, 17}, {28, 22, 22},
+ {28, 22, 22}, {28, 22, 22}, {28, 22, 34}, {28, 22, 37},
+ {22, 17, 32}, {17, 11, 30}, {11, 11, 24}, {11, 11, 20},
+ {11, 11, 20}, {11, 5, 26}, {11, 5, 20}, {11, 5, 20},
+ {11, 5, 20}, {11, 5, 17}, {11, 5, 17}, {17, 5, 17},
+ {17, 5, 17}, {17, 5, 20}, {17, 5, 20}, {17, 5, 20},
+ {22, 11, 26}, {17, 11, 32}, {26, 20, 39}, {28, 28, 43},
+ {77, 66, 72}, {96, 122, 101}, {174, 172, 129}, {84, 139, 146},
+ {240, 195, 184}, {191, 112, 78}, {159, 90, 73}, {210, 73, 44},
+ {120, 94, 66}, {107, 90, 90}, {84, 88, 90}, {118, 90, 67},
+ {85, 96, 107}, {66, 100, 117}, {75, 75, 64}, {54, 80, 74},
+ {54, 66, 54}, {39, 45, 17}, {28, 28, 28}, {32, 32, 32},
+ {45, 39, 34}, {83, 37, 37}, {131, 28, 11}, {213, 22, 0},
+ {218, 16, 5}, {148, 27, 0}, {75, 16, 22}, {43, 22, 17},
+ {28, 22, 11}, {22, 22, 5}, {32, 17, 0}, {28, 37, 0},
+ {17, 17, 5}, {17, 22, 11}, {17, 17, 11}, {17, 11, 11},
+ {17, 11, 11}, {17, 11, 11}, {11, 11, 11}, {11, 5, 11},
+ {11, 5, 11}, {11, 5, 11}, {17, 5, 11}, {17, 5, 11},
+ {17, 5, 11}, {11, 0, 11}, {11, 0, 11}, {11, 0, 11},
+ {11, 0, 11}, {11, 5, 11}, {11, 5, 11}, {11, 5, 11},
+ {11, 5, 11}, {11, 5, 11}, {11, 11, 11}, {11, 11, 15},
+ {11, 5, 17}, {11, 5, 17}, {17, 5, 17}, {17, 5, 17},
+ {17, 11, 17}, {17, 11, 17}, {17, 11, 17}, {17, 11, 17},
+ {17, 11, 15}, {17, 11, 11}, {17, 11, 5}, {11, 11, 0},
+ {17, 5, 0}, {17, 5, 5}, {17, 5, 5}, {17, 5, 11},
+ {17, 5, 11}, {17, 5, 11}, {17, 5, 11}, {17, 0, 11},
+ {17, 0, 11}, {17, 0, 5}, {11, 5, 5}, {11, 5, 5},
+ {5, 5, 5}, {0, 0, 5}, {5, 5, 5}, {11, 5, 5},
+ {11, 5, 5}, {11, 11, 11}, {11, 11, 11}, {11, 11, 11},
+ {11, 5, 11}, {11, 5, 11}, {11, 5, 11}, {11, 5, 11},
+ {11, 5, 11}, {5, 5, 15}, {5, 0, 15}, {5, 0, 17},
+ {11, 0, 17}, {11, 0, 15}, {11, 0, 11}, {11, 0, 11},
+ {11, 0, 11}, {11, 0, 9}, {11, 0, 5}, {11, 0, 0},
+ {11, 0, 0}, {11, 0, 5}, {11, 0, 5}, {11, 0, 5},
+ {11, 5, 5}, {17, 5, 5}, {17, 5, 5}, {17, 11, 11},
+ {17, 11, 11}, {17, 11, 11}, {17, 11, 11}, {20, 11, 11},
+ {22, 17, 11}, {22, 17, 17}, {22, 17, 17}, {22, 17, 17},
+ {17, 11, 17}, {17, 11, 17}, {17, 11, 17}, {17, 11, 17},
+ {17, 11, 17}, {17, 11, 17}, {17, 11, 17}, {17, 11, 17},
+ {17, 11, 17}, {17, 11, 17}, {17, 11, 17}, {17, 11, 20},
+ {17, 11, 22}, {17, 17, 26}, {17, 22, 30}, {22, 22, 32},
+ {28, 17, 28}, {28, 17, 22}, {34, 17, 26}, {37, 26, 20},
+ {45, 28, 22}, {54, 49, 26}, {100, 51, 51}, {77, 56, 50},
+ {77, 58, 58}, {50, 78, 90}, {49, 54, 60}, {71, 50, 56},
+ {62, 58, 52}, {60, 66, 22}, {79, 68, 45}, {45, 34, 37},
+ },
+ /* facial-treescape 6 */
+ {
+ {39, 34, 26}, {52, 37, 56}, {37, 60, 58}, {52, 73, 52},
+ {73, 85, 51}, {110, 87, 59}, {164, 147, 127}, {133, 105, 88},
+ {166, 134, 117}, {122, 133, 142}, {137, 125, 91}, {150, 129, 56},
+ {152, 101, 0}, {135, 73, 39}, {62, 71, 17}, {39, 86, 17},
+ {34, 62, 22}, {37, 77, 43}, {39, 62, 34}, {39, 79, 28},
+ {45, 92, 51}, {54, 79, 49}, {52, 64, 58}, {49, 69, 49},
+ {39, 64, 45}, {47, 62, 30}, {39, 62, 17}, {39, 49, 11},
+ {45, 45, 17}, {39, 49, 22}, {34, 54, 28}, {32, 55, 35},
+ {39, 56, 39}, {45, 51, 34}, {45, 60, 34}, {34, 49, 22},
+ {28, 39, 17}, {20, 35, 15}, {22, 34, 11}, {17, 39, 11},
+ {22, 28, 11}, {17, 34, 11}, {17, 39, 17}, {11, 28, 11},
+ {11, 28, 5}, {11, 28, 11}, {17, 28, 11}, {22, 22, 11},
+ {22, 17, 5}, {22, 11, 0}, {22, 11, 0}, {22, 17, 0},
+ {22, 17, 0}, {35, 20, 9}, {60, 22, 17}, {82, 39, 16},
+ {81, 49, 20}, {132, 80, 41}, {135, 123, 90}, {178, 166, 112},
+ {197, 203, 169}, {205, 233, 222}, {178, 218, 212}, {107, 158, 175},
+ {50, 96, 132}, {49, 86, 103}, {37, 55, 55}, {34, 45, 39},
+ {34, 39, 34}, {28, 28, 34}, {34, 22, 22}, {28, 17, 17},
+ {22, 22, 17}, {22, 22, 17}, {22, 17, 17}, {17, 17, 11},
+ {15, 20, 9}, {17, 22, 5}, {22, 26, 0}, {22, 28, 0},
+ {34, 32, 0}, {28, 32, 5}, {34, 28, 5}, {34, 28, 0},
+ {28, 28, 0}, {34, 22, 11}, {34, 22, 11}, {28, 17, 17},
+ {28, 22, 11}, {28, 22, 11}, {22, 22, 11}, {22, 28, 11},
+ {22, 28, 17}, {22, 28, 17}, {17, 22, 22}, {17, 17, 17},
+ {22, 17, 11}, {22, 17, 11}, {22, 17, 11}, {22, 17, 11},
+ {22, 11, 11}, {17, 11, 11}, {17, 11, 11}, {17, 11, 11},
+ {15, 9, 9}, {22, 5, 5}, {22, 11, 5}, {22, 11, 5},
+ {22, 17, 5}, {22, 22, 5}, {17, 22, 5}, {17, 28, 11},
+ {17, 28, 11}, {17, 26, 11}, {11, 22, 11}, {11, 22, 11},
+ {11, 17, 5}, {11, 17, 5}, {5, 17, 5}, {11, 17, 0},
+ {11, 17, 5}, {17, 17, 5}, {17, 17, 11}, {17, 22, 17},
+ {22, 28, 22}, {28, 28, 37}, {22, 39, 64}, {51, 83, 73},
+ {85, 117, 74}, {122, 176, 142}, {193, 222, 227}, {220, 231, 237},
+ {215, 226, 232}, {250, 216, 154}, {244, 205, 109}, {195, 114, 69},
+ {149, 114, 58}, {93, 103, 53}, {62, 71, 34}, {51, 54, 22},
+ {39, 34, 17}, {32, 26, 20}, {17, 22, 11}, {11, 11, 11},
+ {11, 5, 5}, {11, 5, 5}, {11, 5, 5}, {11, 11, 5},
+ {11, 11, 0}, {11, 11, 5}, {17, 11, 5}, {17, 11, 5},
+ {17, 17, 5}, {17, 22, 5}, {11, 22, 0}, {11, 17, 0},
+ {11, 11, 0}, {11, 11, 0}, {11, 5, 0}, {17, 5, 0},
+ {20, 5, 0}, {17, 5, 0}, {17, 5, 0}, {17, 0, 0},
+ {17, 0, 0}, {11, 0, 0}, {11, 5, 0}, {11, 5, 0},
+ {11, 5, 0}, {0, 0, 0}, {11, 5, 0}, {5, 5, 0},
+ {5, 5, 0}, {5, 11, 0}, {5, 11, 5}, {11, 11, 5},
+ {17, 17, 5}, {17, 22, 0}, {22, 28, 0}, {22, 37, 5},
+ {26, 37, 9}, {28, 43, 15}, {26, 57, 29}, {43, 68, 43},
+ {28, 64, 43}, {28, 69, 34}, {32, 80, 35}, {41, 77, 47},
+ {56, 96, 68}, {73, 110, 76}, {45, 111, 79}, {82, 105, 94},
+ {62, 114, 116}, {67, 147, 147}, {78, 175, 197}, {180, 220, 220},
+ {216, 222, 222}, {250, 210, 131}, {180, 123, 95}, {121, 87, 59},
+ {73, 68, 39}, {58, 72, 44}, {51, 66, 34}, {37, 49, 32},
+ {34, 39, 22}, {28, 34, 17}, {28, 22, 11}, {22, 17, 11},
+ {17, 11, 5}, {17, 11, 5}, {17, 11, 0}, {11, 11, 0},
+ {11, 11, 0}, {11, 11, 0}, {17, 17, 0}, {22, 22, 5},
+ {26, 26, 9}, {28, 34, 11}, {28, 34, 11}, {26, 37, 15},
+ {28, 39, 17}, {28, 34, 22}, {28, 32, 22}, {32, 32, 20},
+ {34, 34, 17}, {34, 37, 17}, {39, 43, 11}, {37, 43, 15},
+ {34, 39, 17}, {34, 37, 11}, {28, 32, 11}, {22, 28, 11},
+ {28, 28, 11}, {26, 26, 9}, {34, 34, 11}, {45, 28, 11},
+ {49, 37, 15}, {54, 39, 22}, {69, 39, 28}, {56, 39, 28},
+ {41, 35, 26}, {34, 34, 28}, {28, 34, 28}, {26, 34, 22},
+ {28, 37, 22}, {34, 39, 22}, {34, 45, 22}, {34, 39, 22},
+ {34, 39, 17}, {34, 37, 17}, {45, 39, 11}, {45, 39, 20},
+ {45, 39, 22}, {51, 39, 17}, {49, 43, 20}, {51, 57, 36},
+ },
+ /* fasion-bug */
+ {
+ {24, 19, 26}, {45, 31, 41}, {67, 45, 51}, {122, 31, 44},
+ {144, 31, 39}, {145, 29, 37}, {146, 30, 35}, {150, 30, 36},
+ {144, 30, 36}, {133, 33, 35}, {69, 45, 44}, {47, 34, 40},
+ {35, 29, 36}, {31, 25, 30}, {27, 23, 27}, {26, 21, 26},
+ {27, 22, 28}, {30, 24, 33}, {32, 28, 34}, {37, 34, 41},
+ {48, 43, 47}, {59, 53, 56}, {83, 77, 73}, {95, 77, 69},
+ {123, 90, 77}, {130, 109, 88}, {124, 116, 98}, {148, 142, 112},
+ {176, 164, 120}, {201, 192, 153}, {214, 208, 194}, {228, 222, 206},
+ {230, 224, 205}, {230, 225, 201}, {230, 221, 202}, {229, 213, 190},
+ {219, 190, 158}, {176, 152, 111}, {141, 109, 83}, {152, 73, 69},
+ {156, 58, 53}, {146, 125, 94}, {182, 149, 123}, {218, 205, 144},
+ {229, 216, 191}, {230, 222, 205}, {230, 224, 218}, {230, 226, 219},
+ {228, 226, 220}, {230, 227, 221}, {232, 228, 217}, {236, 232, 204},
+ {233, 231, 191}, {230, 222, 199}, {229, 219, 200}, {217, 212, 198},
+ {221, 209, 181}, {199, 168, 145}, {156, 127, 125}, {92, 74, 81},
+ {69, 50, 51}, {55, 40, 50}, {41, 34, 40}, {29, 29, 34},
+ {23, 24, 32}, {21, 24, 29}, {22, 21, 28}, {24, 20, 27},
+ {30, 21, 29}, {37, 25, 29}, {45, 25, 32}, {56, 30, 34},
+ {108, 29, 38}, {142, 30, 39}, {149, 34, 37}, {150, 32, 36},
+ {148, 30, 36}, {146, 29, 36}, {142, 29, 35}, {136, 31, 36},
+ {72, 50, 53}, {63, 47, 50}, {59, 48, 48}, {57, 45, 49},
+ {63, 48, 51}, {78, 55, 58}, {92, 80, 76}, {126, 112, 95},
+ {167, 147, 117}, {206, 160, 142}, {204, 195, 168}, {214, 206, 190},
+ {172, 155, 142}, {146, 130, 123}, {115, 95, 88}, {90, 74, 81},
+ {61, 54, 56}, {55, 40, 52}, {49, 40, 43}, {46, 41, 45},
+ {44, 42, 46}, {46, 42, 46}, {46, 38, 40}, {42, 41, 37},
+ {37, 33, 33}, {34, 27, 27}, {31, 23, 28}, {29, 21, 23},
+ {25, 18, 23}, {24, 13, 21}, {22, 14, 21}, {20, 15, 21},
+ {19, 16, 21}, {19, 17, 21}, {20, 16, 23}, {22, 17, 24},
+ {25, 19, 25}, {28, 19, 27}, {31, 21, 29}, {44, 24, 36},
+ {52, 33, 41}, {66, 40, 44}, {122, 29, 33}, {142, 28, 35},
+ {143, 28, 35}, {140, 31, 34}, {119, 34, 33}, {68, 40, 40},
+ {43, 30, 33}, {31, 19, 27}, {24, 16, 20}, {18, 14, 17},
+ {16, 12, 16}, {13, 12, 16}, {12, 11, 17}, {13, 11, 16},
+ {15, 12, 16}, {18, 14, 18}, {22, 20, 24}, {27, 25, 29},
+ {37, 33, 35}, {52, 39, 43}, {69, 45, 48}, {127, 30, 36},
+ {142, 29, 36}, {148, 30, 36}, {152, 29, 38}, {156, 29, 39},
+ {156, 28, 35}, {154, 28, 36}, {148, 29, 32}, {145, 33, 33},
+ {138, 32, 34}, {74, 48, 52}, {59, 40, 50}, {48, 34, 44},
+ {44, 34, 40}, {38, 33, 36}, {31, 31, 36}, {33, 28, 34},
+ {41, 26, 31}, {46, 27, 37}, {58, 37, 41}, {116, 28, 35},
+ {139, 27, 33}, {144, 27, 35}, {149, 29, 39}, {153, 31, 38},
+ {156, 31, 39}, {157, 33, 39}, {154, 36, 40}, {151, 34, 42},
+ {145, 30, 37}, {136, 27, 35}, {95, 24, 29}, {49, 32, 25},
+ {32, 24, 25}, {25, 20, 23}, {20, 17, 24}, {18, 16, 23},
+ {17, 15, 21}, {18, 16, 22}, {16, 15, 24}, {20, 16, 27},
+ {18, 19, 35}, {32, 25, 35}, {42, 36, 38}, {46, 42, 41},
+ {52, 53, 45}, {67, 68, 46}, {98, 75, 65}, {101, 69, 63},
+ {146, 34, 48}, {150, 33, 43}, {157, 33, 41}, {160, 30, 39},
+ {160, 32, 39}, {162, 29, 37}, {161, 35, 40}, {171, 37, 38},
+ {157, 37, 40}, {151, 36, 40}, {144, 32, 39}, {138, 32, 38},
+ {79, 44, 48}, {61, 41, 46}, {55, 36, 39}, {47, 32, 42},
+ {35, 28, 35}, {27, 24, 29}, {27, 21, 27}, {22, 20, 26},
+ {19, 20, 24}, {16, 18, 24}, {15, 16, 22}, {15, 16, 23},
+ {15, 19, 22}, {16, 16, 22}, {17, 15, 22}, {14, 16, 21},
+ {12, 15, 20}, {15, 13, 20}, {18, 14, 21}, {19, 16, 21},
+ {22, 16, 21}, {25, 20, 21}, {24, 22, 25}, {25, 24, 29},
+ {29, 25, 31}, {32, 27, 32}, {31, 27, 33}, {31, 25, 29},
+ {27, 21, 26}, {28, 19, 26}, {26, 16, 24}, {25, 17, 23},
+ {23, 14, 23}, {19, 15, 22}, {16, 14, 19}, {14, 13, 18},
+ {13, 11, 18}, {15, 12, 18}, {16, 14, 17}, {15, 15, 19},
+ {16, 16, 20}, {16, 16, 21}, {18, 15, 23}, {20, 16, 23},
+ {23, 18, 26}, {24, 19, 33}, {29, 18, 30}, {35, 24, 32},
+ {43, 28, 34}, {55, 32, 37}, {69, 42, 43}, {131, 27, 32},
+ },
+ /* leafy-face */
+ {
+ {42, 37, 33}, {31, 23, 25}, {23, 19, 22}, {20, 20, 24},
+ {24, 23, 25}, {31, 25, 25}, {47, 37, 39}, {64, 40, 39},
+ {81, 51, 51}, {74, 59, 57}, {73, 69, 63}, {77, 81, 72},
+ {95, 98, 94}, {200, 168, 134}, {215, 188, 153}, {223, 209, 177},
+ {238, 225, 207}, {237, 227, 215}, {238, 227, 201}, {225, 195, 162},
+ {210, 182, 147}, {191, 163, 133}, {91, 98, 97}, {75, 74, 79},
+ {70, 70, 67}, {73, 69, 59}, {83, 76, 55}, {84, 74, 55},
+ {107, 79, 73}, {197, 132, 96}, {218, 159, 116}, {222, 174, 130},
+ {225, 182, 134}, {222, 182, 137}, {220, 173, 131}, {215, 164, 114},
+ {163, 109, 88}, {103, 83, 68}, {90, 83, 61}, {145, 104, 88},
+ {208, 163, 121}, {209, 174, 138}, {213, 176, 151}, {213, 178, 151},
+ {211, 183, 143}, {206, 174, 133}, {197, 154, 120}, {89, 91, 91},
+ {72, 77, 78}, {66, 71, 69}, {64, 66, 63}, {62, 56, 59},
+ {60, 59, 58}, {58, 58, 55}, {56, 54, 54}, {50, 51, 48},
+ {60, 47, 46}, {65, 50, 46}, {65, 50, 46}, {67, 52, 49},
+ {66, 59, 57}, {65, 67, 59}, {68, 73, 68}, {78, 84, 80},
+ {130, 120, 108}, {206, 162, 135}, {217, 172, 144}, {223, 184, 152},
+ {224, 188, 154}, {226, 188, 154}, {226, 187, 152}, {225, 190, 148},
+ {226, 195, 144}, {224, 195, 146}, {220, 190, 147}, {223, 190, 153},
+ {226, 193, 152}, {227, 192, 152}, {223, 187, 152}, {223, 187, 147},
+ {227, 184, 143}, {223, 184, 141}, {221, 174, 135}, {204, 162, 130},
+ {130, 108, 102}, {82, 82, 75}, {74, 77, 63}, {70, 76, 63},
+ {68, 71, 63}, {68, 71, 63}, {66, 72, 63}, {71, 76, 66},
+ {78, 89, 77}, {138, 122, 109}, {205, 166, 137}, {210, 179, 149},
+ {213, 182, 152}, {222, 189, 155}, {226, 199, 156}, {237, 210, 172},
+ {240, 227, 199}, {238, 229, 214}, {238, 231, 222}, {239, 232, 227},
+ {235, 230, 227}, {230, 228, 223}, {220, 217, 207}, {203, 193, 176},
+ {206, 166, 149}, {194, 148, 118}, {87, 98, 78}, {72, 89, 71},
+ {82, 86, 69}, {139, 116, 93}, {208, 167, 123}, {219, 170, 135},
+ {219, 179, 138}, {218, 182, 149}, {214, 176, 151}, {212, 174, 147},
+ {198, 160, 136}, {98, 97, 97}, {78, 76, 82}, {68, 71, 71},
+ {60, 66, 68}, {65, 67, 69}, {67, 72, 71}, {82, 87, 80},
+ {168, 130, 113}, {208, 164, 139}, {209, 167, 138}, {189, 141, 129},
+ {99, 86, 90}, {80, 75, 64}, {82, 71, 63}, {83, 77, 68},
+ {98, 94, 96}, {202, 155, 123}, {219, 170, 130}, {212, 164, 117},
+ {142, 106, 92}, {81, 81, 74}, {72, 73, 68}, {71, 73, 67},
+ {73, 79, 69}, {83, 99, 85}, {164, 149, 121}, {207, 166, 140},
+ {213, 177, 153}, {224, 193, 170}, {222, 210, 207}, {223, 222, 215},
+ {232, 231, 225}, {233, 227, 226}, {226, 226, 223}, {218, 226, 216},
+ {222, 222, 214}, {222, 213, 199}, {225, 191, 157}, {213, 184, 148},
+ {209, 176, 138}, {199, 165, 128}, {96, 96, 95}, {75, 77, 69},
+ {70, 67, 64}, {68, 61, 60}, {68, 62, 59}, {70, 63, 58},
+ {74, 72, 63}, {82, 88, 77}, {175, 142, 113}, {217, 171, 125},
+ {221, 170, 130}, {207, 159, 122}, {112, 86, 73}, {83, 64, 60},
+ {51, 41, 43}, {29, 27, 31}, {21, 18, 24}, {13, 11, 15},
+ {19, 16, 17}, {22, 17, 23}, {30, 24, 29}, {46, 37, 41},
+ {44, 41, 44}, {43, 46, 38}, {32, 32, 27}, {25, 26, 28},
+ {28, 22, 25}, {32, 30, 32}, {41, 41, 42}, {43, 41, 47},
+ {47, 50, 49}, {55, 55, 53}, {58, 63, 60}, {61, 68, 58},
+ {64, 70, 57}, {67, 74, 62}, {75, 76, 65}, {88, 89, 83},
+ {176, 145, 118}, {206, 166, 144}, {224, 190, 168}, {238, 223, 210},
+ {233, 230, 225}, {233, 229, 224}, {227, 218, 210}, {220, 188, 162},
+ {204, 167, 151}, {150, 142, 132}, {85, 97, 95}, {75, 78, 72},
+ {70, 70, 65}, {67, 64, 57}, {63, 61, 52}, {57, 55, 49},
+ {62, 56, 46}, {60, 59, 46}, {61, 59, 52}, {64, 61, 58},
+ {64, 68, 57}, {62, 70, 54}, {62, 67, 54}, {63, 67, 55},
+ {64, 64, 55}, {66, 65, 51}, {67, 65, 50}, {69, 65, 51},
+ {69, 65, 54}, {76, 66, 55}, {84, 65, 54}, {121, 72, 64},
+ {197, 132, 96}, {217, 166, 119}, {222, 178, 124}, {225, 188, 129},
+ {224, 188, 138}, {222, 184, 139}, {209, 178, 141}, {206, 171, 134},
+ {162, 144, 108}, {78, 90, 81}, {67, 76, 72}, {72, 72, 72},
+ {83, 70, 82}, {114, 112, 105}, {203, 167, 143}, {213, 184, 157},
+ {219, 210, 199}, {221, 220, 210}, {218, 213, 200}, {183, 173, 165},
+ {107, 106, 103}, {77, 83, 78}, {74, 83, 75}, {82, 95, 83},
+ },
+ /* mouldy-sun */
+ {
+ {17, 11, 11}, {11, 11, 11}, {11, 11, 5}, {11, 17, 0},
+ {11, 17, 0}, {17, 22, 0}, {22, 28, 0}, {34, 28, 11},
+ {51, 39, 22}, {69, 64, 24}, {84, 105, 50}, {129, 135, 73},
+ {189, 166, 78}, {195, 194, 78}, {207, 189, 83}, {208, 180, 95},
+ {214, 203, 90}, {214, 197, 90}, {207, 179, 105}, {191, 173, 92},
+ {191, 163, 90}, {180, 158, 62}, {177, 149, 45}, {180, 146, 39},
+ {174, 145, 55}, {159, 130, 65}, {126, 109, 60}, {144, 104, 34},
+ {148, 104, 45}, {153, 115, 48}, {146, 93, 31}, {154, 96, 17},
+ {163, 115, 25}, {180, 123, 16}, {183, 138, 28}, {173, 129, 45},
+ {210, 135, 61}, {225, 146, 67}, {197, 174, 123}, {148, 193, 193},
+ {191, 191, 180}, {204, 192, 170}, {213, 196, 139}, {203, 208, 135},
+ {165, 185, 156}, {113, 124, 90}, {67, 113, 50}, {77, 84, 26},
+ {60, 68, 20}, {66, 64, 20}, {83, 69, 20}, {120, 82, 20},
+ {159, 104, 11}, {172, 126, 39}, {186, 134, 45}, {208, 146, 39},
+ {242, 146, 44}, {206, 148, 39}, {164, 121, 45}, {145, 95, 45},
+ {128, 84, 34}, {89, 39, 22}, {58, 28, 17}, {43, 22, 11},
+ {28, 17, 11}, {22, 11, 11}, {22, 11, 11}, {22, 11, 11},
+ {17, 11, 11}, {11, 5, 26}, {11, 11, 11}, {17, 11, 17},
+ {17, 17, 11}, {22, 22, 5}, {17, 11, 0}, {22, 22, 0},
+ {17, 22, 0}, {17, 11, 0}, {28, 17, 0}, {37, 11, 0},
+ {49, 22, 5}, {62, 39, 0}, {71, 58, 9}, {119, 73, 0},
+ {164, 72, 28}, {186, 117, 22}, {203, 157, 33}, {242, 174, 50},
+ {248, 208, 89}, {244, 239, 154}, {254, 248, 169}, {237, 220, 135},
+ {240, 206, 88}, {231, 186, 72}, {203, 174, 50}, {178, 146, 39},
+ {139, 127, 16}, {116, 101, 11}, {94, 81, 5}, {51, 58, 0},
+ {22, 32, 0}, {17, 26, 0}, {11, 17, 0}, {5, 17, 0},
+ {5, 5, 5}, {0, 0, 0}, {5, 0, 0}, {11, 5, 0},
+ {11, 5, 5}, {11, 5, 5}, {17, 5, 5}, {17, 5, 5},
+ {22, 5, 5}, {22, 5, 5}, {17, 5, 5}, {17, 5, 5},
+ {17, 5, 5}, {11, 11, 0}, {11, 11, 0}, {11, 5, 5},
+ {11, 5, 5}, {17, 5, 0}, {17, 5, 0}, {17, 5, 0},
+ {17, 5, 0}, {22, 5, 5}, {17, 0, 0}, {17, 0, 0},
+ {17, 0, 0}, {17, 5, 5}, {17, 17, 11}, {11, 17, 9},
+ {11, 22, 0}, {11, 28, 0}, {17, 39, 17}, {45, 69, 0},
+ {62, 86, 25}, {84, 98, 11}, {107, 99, 17}, {119, 93, 17},
+ {122, 99, 20}, {134, 101, 28}, {141, 112, 22}, {150, 110, 33},
+ {130, 113, 39}, {128, 111, 75}, {133, 132, 87}, {165, 142, 120},
+ {197, 163, 135}, {220, 202, 146}, {225, 225, 180}, {229, 229, 190},
+ {250, 239, 199}, {244, 233, 188}, {242, 237, 163}, {218, 200, 127},
+ {225, 202, 118}, {224, 201, 105}, {227, 205, 91}, {225, 208, 78},
+ {237, 220, 44}, {237, 197, 50}, {248, 191, 67}, {225, 174, 89},
+ {181, 146, 85}, {144, 138, 76}, {124, 124, 51}, {108, 119, 43},
+ {96, 99, 42}, {73, 73, 28}, {71, 52, 20}, {79, 50, 22},
+ {79, 39, 22}, {93, 50, 56}, {154, 90, 84}, {158, 107, 67},
+ {162, 127, 66}, {197, 163, 50}, {210, 176, 47}, {203, 168, 33},
+ {180, 139, 0}, {152, 118, 5}, {125, 80, 0}, {96, 73, 11},
+ {69, 45, 11}, {54, 28, 22}, {39, 34, 22}, {34, 34, 22},
+ {28, 28, 17}, {28, 28, 11}, {39, 22, 5}, {34, 22, 0},
+ {28, 24, 0}, {22, 28, 5}, {28, 34, 20}, {51, 52, 30},
+ {66, 54, 24}, {94, 60, 46}, {126, 86, 47}, {132, 118, 82},
+ {158, 140, 90}, {208, 168, 101}, {214, 197, 140}, {233, 227, 188},
+ {252, 240, 201}, {252, 252, 212}, {252, 246, 201}, {250, 244, 176},
+ {254, 242, 152}, {254, 220, 95}, {248, 208, 84}, {231, 174, 44},
+ {203, 157, 28}, {194, 126, 0}, {191, 123, 5}, {190, 112, 5},
+ {175, 118, 16}, {138, 116, 0}, {107, 99, 0}, {99, 96, 14},
+ {91, 89, 20}, {54, 64, 9}, {39, 28, 17}, {28, 17, 17},
+ {17, 11, 11}, {11, 5, 5}, {11, 5, 5}, {11, 5, 5},
+ {11, 5, 5}, {11, 5, 5}, {5, 5, 5}, {5, 0, 0},
+ {5, 0, 0}, {11, 0, 0}, {11, 5, 0}, {11, 5, 5},
+ {17, 5, 5}, {17, 11, 17}, {17, 17, 17}, {28, 33, 33},
+ {57, 63, 46}, {77, 71, 34}, {91, 75, 43}, {124, 88, 33},
+ {155, 112, 11}, {186, 135, 22}, {203, 146, 39}, {231, 157, 39},
+ {225, 168, 50}, {237, 203, 90}, {239, 233, 148}, {252, 235, 189},
+ {248, 242, 208}, {229, 218, 224}, {244, 244, 210}, {237, 208, 169},
+ },
+ /* sunny-harvest */
+ {
+ {0, 0, 0}, {34, 4, 13}, {71, 19, 23}, {105, 20, 32},
+ {107, 51, 32}, {102, 30, 34}, {70, 23, 27}, {52, 20, 20},
+ {18, 6, 8}, {4, 0, 1}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {8, 5, 6}, {16, 12, 21}, {58, 29, 30}, {89, 49, 39},
+ {116, 94, 73}, {149, 126, 100}, {193, 171, 144}, {208, 199, 181},
+ {213, 202, 184}, {204, 203, 195}, {227, 209, 188}, {228, 210, 184},
+ {219, 200, 171}, {216, 190, 148}, {212, 182, 123}, {211, 171, 108},
+ {189, 137, 88}, {173, 125, 72}, {167, 123, 82}, {157, 127, 103},
+ {184, 159, 137}, {204, 180, 162}, {205, 185, 156}, {206, 182, 146},
+ {200, 168, 133}, {170, 131, 87}, {163, 112, 64}, {145, 103, 68},
+ {132, 98, 68}, {123, 102, 49}, {93, 65, 39}, {84, 57, 33},
+ {86, 64, 30}, {118, 81, 44}, {137, 96, 57}, {148, 118, 62},
+ {159, 116, 64}, {176, 136, 77}, {195, 143, 90}, {206, 164, 112},
+ {186, 160, 114}, {169, 130, 85}, {151, 114, 89}, {121, 104, 81},
+ {112, 93, 68}, {102, 77, 53}, {86, 52, 33}, {76, 49, 28},
+ {82, 63, 30}, {92, 79, 38}, {113, 102, 71}, {132, 128, 98},
+ {154, 155, 150}, {201, 187, 161}, {207, 191, 163}, {215, 191, 174},
+ {204, 181, 165}, {185, 169, 137}, {143, 133, 100}, {99, 88, 80},
+ {61, 41, 39}, {41, 16, 21}, {17, 3, 12}, {6, 0, 3},
+ {0, 0, 0}, {0, 0, 0}, {3, 1, 3}, {16, 8, 9},
+ {54, 24, 30}, {65, 63, 43}, {85, 87, 72}, {124, 122, 90},
+ {182, 154, 108}, {201, 174, 136}, {202, 181, 122}, {207, 183, 113},
+ {218, 169, 91}, {208, 146, 67}, {213, 132, 64}, {227, 133, 48},
+ {226, 130, 48}, {223, 124, 47}, {213, 122, 45}, {211, 112, 41},
+ {215, 101, 43}, {214, 103, 44}, {205, 112, 45}, {194, 106, 42},
+ {175, 100, 37}, {169, 89, 35}, {164, 74, 39}, {166, 83, 42},
+ {163, 74, 36}, {176, 92, 35}, {187, 102, 42}, {202, 116, 54},
+ {218, 131, 45}, {229, 156, 54}, {229, 164, 62}, {229, 179, 77},
+ {224, 183, 95}, {222, 193, 115}, {224, 189, 123}, {218, 195, 149},
+ {223, 200, 158}, {215, 190, 172}, {202, 179, 157}, {154, 144, 119},
+ {143, 122, 97}, {122, 94, 70}, {119, 85, 51}, {101, 45, 32},
+ {97, 47, 31}, {83, 35, 29}, {71, 29, 33}, {56, 21, 24},
+ {16, 8, 21}, {2, 0, 3}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {16, 10, 8}, {23, 16, 11},
+ {62, 40, 29}, {92, 66, 41}, {138, 99, 61}, {193, 146, 50},
+ {215, 170, 60}, {222, 164, 57}, {229, 168, 54}, {233, 168, 56},
+ {236, 175, 55}, {238, 180, 55}, {236, 177, 57}, {235, 179, 60},
+ {236, 183, 59}, {231, 190, 63}, {234, 184, 61}, {236, 173, 60},
+ {233, 169, 59}, {235, 170, 58}, {235, 166, 58}, {233, 160, 58},
+ {233, 151, 58}, {233, 150, 59}, {226, 137, 53}, {231, 125, 50},
+ {219, 101, 46}, {202, 98, 45}, {207, 84, 39}, {193, 77, 37},
+ {190, 74, 37}, {178, 62, 39}, {179, 52, 35}, {183, 57, 36},
+ {191, 62, 40}, {190, 80, 45}, {187, 97, 43}, {170, 114, 57},
+ {174, 124, 71}, {181, 151, 81}, {205, 170, 95}, {218, 189, 119},
+ {215, 190, 114}, {227, 191, 103}, {225, 190, 96}, {235, 192, 73},
+ {235, 186, 67}, {233, 174, 68}, {233, 152, 56}, {216, 126, 43},
+ {198, 94, 40}, {183, 72, 41}, {151, 71, 39}, {128, 43, 33},
+ {89, 14, 25}, {25, 15, 6}, {6, 0, 2}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {3, 0, 1}, {18, 4, 7}, {33, 13, 19}, {60, 28, 33},
+ {74, 44, 36}, {87, 53, 35}, {93, 62, 37}, {122, 84, 46},
+ {121, 88, 45}, {141, 90, 51}, {140, 67, 41}, {122, 81, 49},
+ {139, 60, 40}, {135, 61, 34}, {150, 57, 34}, {145, 38, 38},
+ {149, 64, 37}, {123, 56, 32}, {154, 40, 39}, {158, 46, 36},
+ {175, 45, 38}, {179, 56, 41}, {164, 72, 39}, {160, 85, 40},
+ {150, 81, 47}, {166, 93, 48}, {157, 99, 55}, {135, 106, 66},
+ {116, 109, 74}, {136, 114, 90}, {141, 108, 86}, {159, 115, 69},
+ {180, 133, 80}, {211, 175, 110}, {214, 190, 145}, {224, 203, 178},
+ {232, 226, 194}, {235, 218, 187}, {225, 213, 184}, {212, 196, 164},
+ {193, 177, 137}, {180, 135, 90}, {175, 113, 57}, {198, 110, 44},
+ {208, 113, 41}, {222, 136, 45}, {233, 152, 53}, {235, 157, 54},
+ {231, 160, 55}, {231, 160, 54}, {228, 151, 52}, {216, 130, 45},
+ {197, 106, 43}, {173, 84, 39}, {150, 67, 37}, {133, 55, 31},
+ },
+ /* peach-tree */
+ {
+ {170, 59, 28}, {170, 63, 28}, {129, 68, 37}, {68, 84, 37},
+ {77, 109, 37}, {81, 121, 47}, {81, 113, 47}, {93, 113, 56},
+ {101, 133, 75}, {117, 141, 85}, {137, 161, 94}, {141, 162, 84},
+ {194, 173, 65}, {218, 139, 47}, {222, 119, 37}, {214, 103, 47},
+ {214, 106, 37}, {194, 95, 28}, {145, 76, 37}, {76, 105, 37},
+ {52, 80, 18}, {32, 68, 9}, {32, 60, 18}, {32, 48, 18},
+ {32, 56, 18}, {44, 72, 18}, {64, 105, 37}, {97, 137, 56},
+ {141, 178, 94}, {170, 198, 122}, {238, 209, 122}, {250, 225, 84},
+ {230, 151, 46}, {230, 122, 37}, {230, 122, 37}, {219, 105, 37},
+ {202, 94, 37}, {182, 76, 28}, {133, 55, 18}, {84, 43, 9},
+ {36, 27, 9}, {28, 32, 0}, {28, 48, 9}, {32, 64, 9},
+ {64, 92, 28}, {84, 121, 56}, {105, 145, 75}, {145, 174, 103},
+ {150, 178, 113}, {149, 174, 113}, {141, 170, 113}, {113, 141, 85},
+ {81, 109, 66}, {64, 68, 47}, {36, 52, 28}, {16, 24, 18},
+ {12, 12, 9}, {4, 8, 0}, {0, 4, 0}, {0, 4, 0},
+ {0, 4, 0}, {0, 0, 0}, {4, 0, 0}, {12, 0, 0},
+ {16, 11, 0}, {36, 15, 0}, {68, 23, 18}, {129, 38, 18},
+ {170, 55, 18}, {194, 95, 28}, {178, 124, 47}, {154, 174, 103},
+ {166, 194, 122}, {170, 202, 122}, {178, 210, 141}, {190, 218, 160},
+ {190, 210, 151}, {202, 214, 151}, {226, 222, 132}, {182, 218, 141},
+ {182, 210, 141}, {186, 206, 141}, {190, 206, 151}, {198, 206, 169},
+ {210, 218, 179}, {234, 230, 197}, {234, 234, 197}, {250, 241, 188},
+ {246, 229, 188}, {234, 192, 141}, {210, 172, 150}, {190, 202, 150},
+ {178, 198, 151}, {182, 206, 141}, {182, 202, 141}, {182, 198, 132},
+ {170, 202, 132}, {166, 198, 132}, {158, 186, 122}, {141, 178, 113},
+ {113, 149, 75}, {81, 117, 56}, {60, 84, 37}, {48, 64, 28},
+ {24, 56, 9}, {20, 40, 9}, {20, 24, 9}, {12, 16, 9},
+ {7, 12, 9}, {4, 8, 9}, {8, 4, 9}, {20, 7, 9},
+ {28, 15, 9}, {36, 23, 9}, {80, 39, 18}, {129, 47, 18},
+ {178, 59, 18}, {198, 87, 37}, {219, 102, 37}, {222, 102, 37},
+ {222, 117, 28}, {226, 135, 28}, {230, 142, 28}, {242, 146, 37},
+ {250, 155, 28}, {246, 187, 28}, {234, 127, 47}, {250, 188, 103},
+ {250, 225, 112}, {250, 225, 84}, {254, 211, 46}, {254, 207, 46},
+ {254, 208, 74}, {218, 181, 94}, {149, 120, 103}, {89, 121, 66},
+ {64, 93, 47}, {32, 60, 18}, {20, 44, 9}, {4, 24, 0},
+ {0, 12, 0}, {4, 8, 0}, {0, 4, 0}, {0, 4, 0},
+ {0, 4, 0}, {4, 0, 0}, {4, 0, 0}, {4, 4, 0},
+ {4, 4, 0}, {4, 8, 0}, {4, 8, 0}, {4, 8, 0},
+ {0, 4, 0}, {0, 0, 0}, {0, 0, 9}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {4, 0, 0}, {4, 4, 0}, {12, 8, 0},
+ {16, 16, 0}, {32, 16, 0}, {56, 27, 9}, {89, 51, 9},
+ {125, 67, 9}, {166, 63, 18}, {178, 72, 18}, {198, 90, 28},
+ {190, 107, 37}, {157, 112, 56}, {153, 99, 47}, {89, 121, 56},
+ {85, 101, 56}, {113, 72, 28}, {182, 80, 37}, {206, 98, 37},
+ {210, 102, 37}, {214, 110, 28}, {214, 110, 28}, {218, 118, 37},
+ {222, 110, 28}, {226, 114, 37}, {222, 110, 47}, {238, 111, 65},
+ {198, 160, 84}, {169, 148, 112}, {161, 186, 122}, {170, 194, 132},
+ {190, 206, 151}, {206, 214, 160}, {226, 238, 188}, {246, 238, 197},
+ {254, 250, 254}, {250, 254, 206}, {242, 254, 206}, {230, 238, 197},
+ {198, 214, 160}, {170, 194, 132}, {145, 149, 113}, {84, 109, 66},
+ {56, 64, 37}, {28, 44, 18}, {24, 36, 9}, {20, 20, 9},
+ {32, 19, 9}, {48, 15, 9}, {101, 39, 18}, {137, 47, 18},
+ {166, 51, 19}, {145, 55, 18}, {101, 47, 18}, {68, 39, 18},
+ {32, 56, 9}, {28, 44, 9}, {20, 28, 9}, {16, 20, 9},
+ {8, 16, 0}, {8, 16, 0}, {4, 12, 0}, {4, 16, 0},
+ {8, 20, 9}, {16, 32, 9}, {20, 48, 18}, {40, 72, 18},
+ {68, 109, 37}, {97, 129, 56}, {137, 170, 103}, {149, 182, 113},
+ {145, 182, 113}, {129, 161, 94}, {105, 133, 75}, {68, 89, 56},
+ {60, 76, 37}, {48, 64, 37}, {60, 68, 28}, {113, 55, 18},
+ {166, 51, 19}, {190, 76, 28}, {202, 91, 28}, {210, 94, 28},
+ {210, 98, 28}, {198, 87, 18}, {158, 55, 9}, {105, 34, 0},
+ {44, 15, 0}, {20, 4, 0}, {12, 0, 0}, {8, 4, 0},
+ {4, 8, 0}, {8, 16, 0}, {16, 32, 9}, {28, 52, 9},
+ },
+ /* fire-dragon */
+ {
+ {88, 3, 9}, {64, 0, 9}, {44, 3, 9}, {24, 4, 9},
+ {15, 0, 9}, {16, 4, 9}, {28, 8, 9}, {40, 4, 9},
+ {48, 3, 0}, {52, 0, 0}, {52, 0, 0}, {48, 0, 0},
+ {40, 0, 0}, {28, 4, 0}, {16, 0, 0}, {8, 0, 9},
+ {8, 0, 9}, {15, 0, 9}, {28, 0, 0}, {40, 0, 0},
+ {44, 0, 0}, {48, 0, 0}, {56, 4, 0}, {56, 3, 0},
+ {52, 0, 0}, {48, 0, 0}, {40, 0, 0}, {28, 0, 0},
+ {16, 0, 0}, {4, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 4, 0},
+ {0, 4, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {4, 0, 0}, {8, 4, 0}, {12, 4, 0}, {24, 4, 0},
+ {32, 4, 0}, {40, 4, 9}, {48, 4, 9}, {52, 3, 9},
+ {68, 3, 9}, {81, 4, 0}, {101, 3, 0}, {117, 0, 0},
+ {137, 0, 9}, {158, 0, 9}, {182, 0, 9}, {202, 7, 0},
+ {210, 10, 0}, {202, 7, 0}, {190, 0, 9}, {174, 0, 9},
+ {145, 3, 9}, {113, 3, 0}, {89, 3, 0}, {68, 3, 9},
+ {48, 7, 9}, {36, 4, 9}, {20, 0, 0}, {8, 0, 0},
+ {4, 0, 0}, {4, 0, 0}, {4, 0, 0}, {8, 4, 0},
+ {24, 4, 0}, {40, 4, 0}, {60, 3, 0}, {84, 3, 0},
+ {109, 3, 0}, {125, 3, 0}, {145, 0, 0}, {158, 0, 0},
+ {162, 0, 0}, {162, 0, 0}, {162, 0, 0}, {162, 0, 0},
+ {162, 3, 0}, {153, 7, 9}, {157, 11, 0}, {153, 0, 0},
+ {145, 3, 0}, {129, 0, 0}, {109, 0, 0}, {89, 0, 0},
+ {73, 3, 0}, {52, 3, 0}, {36, 4, 0}, {16, 0, 0},
+ {8, 0, 0}, {4, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 9}, {4, 0, 9},
+ {0, 0, 9}, {0, 0, 9}, {0, 0, 9}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {4, 0, 0},
+ {16, 0, 0}, {40, 4, 0}, {60, 0, 0}, {73, 0, 0},
+ {97, 0, 0}, {113, 0, 0}, {133, 0, 0}, {157, 0, 0},
+ {166, 7, 0}, {182, 7, 0}, {174, 10, 0}, {174, 15, 9},
+ {166, 29, 0}, {157, 35, 9}, {121, 30, 9}, {93, 15, 9},
+ {93, 11, 9}, {92, 11, 0}, {89, 3, 9}, {88, 3, 9},
+ {80, 3, 9}, {76, 0, 0}, {68, 0, 0}, {64, 3, 0},
+ {68, 3, 0}, {76, 0, 0}, {89, 3, 0}, {105, 0, 0},
+ {121, 0, 0}, {145, 0, 0}, {158, 0, 0}, {162, 3, 0},
+ {153, 18, 9}, {169, 7, 0}, {166, 0, 0}, {174, 3, 0},
+ {174, 0, 0}, {178, 0, 0}, {178, 3, 0}, {182, 3, 0},
+ {190, 11, 9}, {206, 14, 9}, {234, 58, 9}, {246, 131, 28},
+ {242, 179, 28}, {250, 187, 37}, {250, 233, 84}, {246, 254, 102},
+ {250, 254, 140}, {254, 249, 140}, {250, 241, 102}, {242, 195, 46},
+ {246, 159, 46}, {186, 91, 37}, {137, 43, 18}, {101, 55, 18},
+ {76, 39, 18}, {56, 11, 9}, {44, 11, 9}, {28, 4, 0},
+ {16, 0, 0}, {4, 0, 0}, {4, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {4, 0, 0},
+ {8, 4, 0}, {16, 8, 0}, {24, 4, 0}, {36, 7, 0},
+ {40, 4, 0}, {32, 0, 0}, {28, 0, 0}, {20, 0, 0},
+ {12, 0, 0}, {8, 0, 0}, {4, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {4, 0, 0},
+ {8, 0, 0}, {24, 0, 0}, {44, 3, 0}, {60, 0, 0},
+ {77, 0, 0}, {101, 0, 0}, {133, 3, 0}, {162, 30, 0},
+ {182, 47, 0}, {194, 79, 18}, {197, 107, 18}, {206, 95, 18},
+ {198, 86, 18}, {198, 47, 0}, {190, 39, 9}, {194, 18, 9},
+ {182, 7, 0}, {182, 10, 0}, {186, 22, 9}, {190, 22, 9},
+ {206, 66, 9}, {230, 98, 9}, {246, 134, 9}, {246, 155, 28},
+ {238, 138, 9}, {238, 142, 37}, {226, 126, 18}, {190, 87, 9},
+ {141, 48, 0}, {125, 43, 9}, {101, 15, 9}, {101, 0, 9},
+ {97, 3, 0}, {92, 7, 0}, {89, 15, 9}, {77, 24, 9},
+ {85, 7, 0}, {64, 7, 0}, {60, 3, 0}, {52, 7, 0},
+ {48, 4, 0}, {44, 4, 0}, {44, 3, 0}, {48, 0, 0},
+ {44, 0, 0}, {40, 7, 0}, {36, 4, 0}, {28, 0, 0},
+ },
+ /* ice-dragon */
+ {
+ {96, 43, 37}, {113, 80, 75}, {157, 120, 103}, {174, 141, 122},
+ {178, 161, 151}, {194, 173, 160}, {186, 169, 170}, {165, 141, 150},
+ {153, 129, 141}, {141, 133, 131}, {137, 129, 131}, {137, 133, 141},
+ {141, 137, 141}, {157, 157, 160}, {161, 173, 188}, {165, 189, 216},
+ {181, 205, 235}, {193, 222, 235}, {189, 226, 244}, {193, 230, 244},
+ {197, 230, 254}, {197, 226, 254}, {189, 226, 244}, {185, 222, 244},
+ {173, 209, 244}, {157, 193, 235}, {133, 177, 226}, {120, 169, 235},
+ {124, 173, 225}, {133, 185, 225}, {144, 198, 235}, {149, 193, 244},
+ {157, 201, 244}, {161, 197, 235}, {169, 193, 226}, {161, 185, 197},
+ {177, 177, 179}, {185, 177, 188}, {193, 193, 207}, {226, 222, 216},
+ {246, 246, 235}, {254, 254, 254}, {254, 254, 254}, {254, 254, 254},
+ {250, 254, 254}, {237, 245, 254}, {209, 230, 244}, {193, 201, 216},
+ {181, 173, 188}, {173, 161, 179}, {161, 157, 160}, {145, 137, 141},
+ {149, 120, 122}, {133, 96, 94}, {121, 60, 56}, {97, 56, 46},
+ {80, 39, 37}, {60, 15, 18}, {44, 19, 9}, {48, 19, 18},
+ {56, 23, 18}, {60, 31, 37}, {64, 39, 56}, {76, 52, 65},
+ {92, 72, 103}, {97, 101, 122}, {117, 113, 132}, {125, 117, 132},
+ {133, 117, 122}, {137, 117, 113}, {125, 109, 94}, {117, 92, 84},
+ {117, 88, 84}, {133, 97, 94}, {141, 100, 94}, {133, 104, 113},
+ {129, 121, 132}, {128, 145, 178}, {116, 162, 216}, {121, 149, 197},
+ {116, 112, 151}, {108, 92, 122}, {76, 48, 85}, {56, 40, 66},
+ {60, 31, 37}, {52, 27, 28}, {44, 27, 28}, {48, 19, 28},
+ {48, 27, 28}, {56, 31, 47}, {68, 56, 75}, {96, 92, 113},
+ {113, 117, 151}, {108, 153, 197}, {108, 158, 225}, {100, 154, 235},
+ {100, 158, 235}, {108, 158, 235}, {141, 189, 244}, {157, 201, 244},
+ {169, 210, 244}, {173, 209, 244}, {165, 205, 244}, {148, 197, 254},
+ {140, 193, 254}, {116, 185, 254}, {104, 173, 244}, {100, 162, 244},
+ {96, 154, 244}, {100, 158, 244}, {104, 158, 244}, {104, 165, 244},
+ {108, 181, 244}, {141, 197, 244}, {169, 210, 244}, {185, 226, 244},
+ {205, 238, 254}, {218, 246, 254}, {230, 250, 254}, {234, 254, 254},
+ {234, 254, 244}, {213, 250, 244}, {189, 242, 244}, {177, 226, 254},
+ {173, 226, 244}, {160, 226, 254}, {173, 234, 254}, {189, 238, 254},
+ {221, 246, 254}, {250, 254, 254}, {254, 254, 254}, {254, 254, 254},
+ {250, 254, 254}, {234, 250, 254}, {222, 242, 254}, {201, 226, 244},
+ {181, 210, 235}, {161, 202, 235}, {149, 189, 235}, {120, 173, 235},
+ {108, 177, 235}, {108, 177, 244}, {112, 181, 244}, {140, 197, 244},
+ {157, 205, 244}, {177, 218, 244}, {193, 234, 235}, {214, 246, 244},
+ {222, 246, 254}, {226, 250, 254}, {222, 246, 244}, {209, 226, 235},
+ {193, 197, 216}, {173, 177, 207}, {161, 161, 179}, {145, 133, 150},
+ {125, 100, 122}, {100, 72, 84}, {88, 52, 66}, {80, 48, 56},
+ {88, 43, 47}, {68, 39, 47}, {72, 39, 56}, {80, 43, 56},
+ {80, 52, 65}, {88, 64, 75}, {113, 97, 103}, {125, 129, 151},
+ {116, 154, 207}, {104, 158, 235}, {100, 158, 235}, {100, 158, 244},
+ {100, 166, 254}, {104, 173, 254}, {100, 173, 244}, {104, 189, 254},
+ {132, 202, 254}, {161, 201, 244}, {185, 202, 244}, {201, 226, 254},
+ {221, 230, 254}, {233, 250, 254}, {250, 254, 254}, {254, 254, 254},
+ {250, 250, 254}, {230, 238, 235}, {206, 202, 207}, {178, 169, 169},
+ {149, 129, 132}, {117, 88, 103}, {93, 60, 65}, {105, 56, 56},
+ {113, 56, 56}, {133, 88, 75}, {141, 108, 94}, {149, 137, 132},
+ {170, 165, 169}, {182, 190, 198}, {214, 214, 226}, {217, 230, 244},
+ {217, 234, 244}, {209, 234, 254}, {197, 226, 235}, {173, 194, 226},
+ {149, 173, 198}, {157, 165, 179}, {149, 149, 160}, {149, 121, 122},
+ {129, 100, 103}, {113, 76, 84}, {96, 56, 65}, {92, 52, 56},
+ {88, 52, 56}, {84, 68, 56}, {97, 85, 94}, {112, 109, 132},
+ {128, 129, 150}, {133, 137, 179}, {137, 157, 188}, {161, 157, 169},
+ {161, 141, 150}, {166, 136, 132}, {153, 132, 122}, {157, 120, 113},
+ {169, 141, 131}, {198, 173, 160}, {202, 177, 170}, {218, 210, 207},
+ {246, 238, 244}, {250, 254, 254}, {254, 254, 254}, {254, 254, 254},
+ {250, 254, 254}, {238, 250, 244}, {222, 242, 244}, {218, 214, 226},
+ {190, 190, 198}, {181, 177, 178}, {185, 173, 169}, {194, 173, 169},
+ {181, 173, 179}, {161, 165, 179}, {157, 169, 179}, {141, 173, 207},
+ {133, 173, 216}, {136, 169, 226}, {137, 149, 188}, {125, 121, 132},
+ {96, 92, 103}, {84, 64, 75}, {80, 60, 66}, {88, 60, 66},
+ {104, 76, 85}, {129, 125, 122}, {161, 161, 169}, {201, 193, 197},
+ },
+ /* german-landscape */
+ {
+ {61, 62, 43}, {52, 48, 45}, {42, 44, 39}, {37, 39, 34},
+ {33, 39, 23}, {36, 44, 22}, {46, 54, 32}, {51, 55, 33},
+ {45, 49, 38}, {45, 49, 38}, {45, 45, 45}, {45, 45, 45},
+ {35, 35, 35}, {35, 39, 28}, {36, 44, 22}, {36, 44, 22},
+ {31, 39, 17}, {31, 39, 17}, {31, 39, 17}, {31, 39, 17},
+ {31, 39, 17}, {31, 39, 17}, {28, 34, 18}, {23, 29, 13},
+ {25, 29, 18}, {30, 34, 23}, {30, 34, 23}, {33, 39, 23},
+ {36, 44, 22}, {39, 48, 22}, {45, 43, 38}, {55, 55, 44},
+ {60, 54, 45}, {63, 61, 54}, {69, 68, 58}, {70, 66, 54},
+ {66, 64, 48}, {70, 69, 45}, {79, 75, 50}, {77, 85, 55},
+ {94, 101, 63}, {100, 123, 70}, {115, 123, 80}, {131, 132, 106},
+ {134, 139, 102}, {131, 130, 80}, {120, 105, 63}, {110, 98, 70},
+ {96, 76, 62}, {77, 68, 57}, {65, 79, 60}, {72, 79, 65},
+ {90, 95, 69}, {102, 99, 77}, {118, 130, 107}, {137, 163, 141},
+ {159, 182, 173}, {167, 178, 180}, {177, 170, 179}, {178, 172, 183},
+ {183, 179, 196}, {165, 177, 194}, {151, 168, 185}, {127, 154, 156},
+ {131, 133, 141}, {132, 125, 149}, {133, 139, 140}, {151, 129, 154},
+ {163, 157, 170}, {169, 168, 176}, {164, 174, 180}, {168, 179, 193},
+ {168, 187, 195}, {161, 179, 194}, {161, 195, 194}, {174, 198, 204},
+ {193, 227, 202}, {210, 230, 224}, {214, 223, 214}, {240, 234, 209},
+ {219, 215, 201}, {201, 197, 192}, {204, 180, 172}, {197, 156, 171},
+ {184, 157, 167}, {169, 156, 169}, {177, 129, 137}, {148, 105, 71},
+ {111, 83, 64}, {101, 79, 57}, {93, 80, 50}, {81, 73, 45},
+ {79, 79, 47}, {85, 89, 56}, {100, 94, 74}, {119, 120, 84},
+ {154, 109, 96}, {132, 132, 120}, {133, 188, 112}, {124, 180, 99},
+ {130, 156, 103}, {111, 139, 75}, {102, 109, 62}, {94, 94, 54},
+ {91, 79, 57}, {86, 72, 56}, {86, 70, 53}, {78, 68, 49},
+ {68, 57, 46}, {58, 58, 44}, {53, 59, 43}, {50, 54, 43},
+ {46, 50, 28}, {41, 49, 27}, {39, 48, 22}, {39, 48, 22},
+ {39, 48, 22}, {39, 48, 22}, {44, 53, 27}, {47, 57, 33},
+ {54, 63, 37}, {70, 71, 45}, {75, 79, 40}, {84, 78, 41},
+ {96, 93, 46}, {110, 106, 55}, {122, 115, 50}, {142, 158, 73},
+ {137, 185, 77}, {132, 178, 79}, {135, 150, 71}, {114, 109, 53},
+ {97, 92, 58}, {86, 97, 78}, {97, 96, 62}, {113, 121, 90},
+ {110, 129, 129}, {138, 144, 170}, {164, 173, 181}, {183, 184, 200},
+ {194, 193, 202}, {214, 210, 211}, {208, 220, 216}, {210, 220, 212},
+ {205, 202, 204}, {186, 186, 193}, {186, 173, 176}, {176, 155, 152},
+ {185, 123, 88}, {172, 130, 65}, {161, 126, 53}, {120, 106, 56},
+ {98, 83, 60}, {76, 70, 51}, {58, 58, 47}, {58, 58, 44},
+ {64, 67, 43}, {70, 78, 42}, {80, 97, 50}, {99, 111, 58},
+ {104, 140, 59}, {113, 144, 62}, {145, 139, 67}, {164, 133, 60},
+ {172, 133, 65}, {184, 149, 59}, {158, 135, 56}, {134, 109, 56},
+ {114, 100, 58}, {106, 103, 65}, {109, 108, 64}, {114, 120, 75},
+ {123, 134, 118}, {136, 147, 152}, {145, 168, 187}, {156, 179, 183},
+ {153, 174, 190}, {153, 172, 190}, {143, 161, 178}, {130, 139, 150},
+ {114, 116, 120}, {93, 89, 82}, {87, 81, 62}, {79, 79, 47},
+ {75, 85, 47}, {76, 87, 46}, {77, 85, 45}, {84, 88, 45},
+ {87, 97, 46}, {97, 97, 47}, {116, 108, 56}, {159, 130, 57},
+ {170, 120, 72}, {137, 103, 67}, {108, 85, 65}, {99, 75, 56},
+ {87, 78, 54}, {78, 68, 46}, {69, 68, 44}, {70, 64, 45},
+ {73, 67, 48}, {77, 75, 55}, {84, 88, 65}, {97, 102, 83},
+ {108, 119, 128}, {131, 146, 146}, {151, 162, 177}, {168, 172, 190},
+ {180, 182, 200}, {168, 187, 195}, {182, 190, 196}, {194, 197, 197},
+ {212, 210, 202}, {221, 217, 208}, {220, 217, 217}, {218, 217, 216},
+ {210, 205, 204}, {200, 182, 162}, {223, 194, 83}, {220, 174, 75},
+ {225, 173, 78}, {214, 151, 77}, {170, 115, 76}, {125, 88, 80},
+ {101, 83, 60}, {88, 83, 55}, {86, 91, 47}, {83, 91, 50},
+ {82, 92, 57}, {98, 99, 70}, {114, 116, 69}, {117, 137, 99},
+ {129, 148, 127}, {143, 157, 168}, {161, 171, 179}, {160, 168, 189},
+ {153, 157, 182}, {155, 139, 161}, {179, 116, 112}, {158, 104, 89},
+ {164, 97, 83}, {161, 97, 68}, {173, 102, 80}, {182, 119, 100},
+ {181, 144, 157}, {170, 170, 169}, {156, 158, 172}, {130, 141, 156},
+ {115, 140, 140}, {124, 139, 130}, {131, 155, 114}, {130, 160, 116},
+ {126, 149, 104}, {134, 148, 136}, {147, 157, 172}, {163, 178, 187},
+ },
+ /* no-name */
+ {
+ {218, 222, 188}, {238, 230, 197}, {238, 242, 206}, {238, 242, 207},
+ {230, 238, 225}, {234, 238, 216}, {242, 241, 235}, {242, 245, 216},
+ {230, 242, 206}, {222, 234, 197}, {214, 218, 198}, {206, 210, 188},
+ {194, 202, 169}, {190, 202, 160}, {206, 214, 170}, {222, 226, 197},
+ {234, 237, 206}, {234, 242, 197}, {222, 226, 197}, {194, 202, 170},
+ {174, 190, 170}, {165, 178, 150}, {162, 169, 141}, {150, 162, 132},
+ {141, 141, 122}, {133, 137, 113}, {133, 141, 113}, {133, 133, 103},
+ {121, 125, 103}, {121, 125, 103}, {113, 121, 94}, {101, 109, 85},
+ {77, 93, 66}, {52, 64, 37}, {48, 64, 37}, {48, 56, 28},
+ {44, 60, 28}, {44, 52, 28}, {36, 44, 18}, {36, 36, 0},
+ {36, 44, 9}, {40, 52, 9}, {56, 64, 18}, {48, 64, 28},
+ {64, 80, 47}, {85, 105, 66}, {101, 117, 85}, {108, 125, 94},
+ {116, 141, 113}, {116, 137, 132}, {116, 141, 132}, {120, 141, 132},
+ {121, 137, 122}, {121, 133, 113}, {117, 125, 103}, {113, 121, 94},
+ {108, 113, 94}, {109, 112, 94}, {108, 121, 94}, {104, 117, 94},
+ {105, 113, 85}, {109, 117, 85}, {109, 117, 85}, {105, 113, 85},
+ {101, 113, 85}, {105, 109, 85}, {100, 113, 94}, {104, 112, 103},
+ {104, 112, 103}, {104, 108, 103}, {108, 108, 94}, {113, 113, 94},
+ {121, 121, 103}, {129, 133, 113}, {125, 145, 113}, {133, 146, 122},
+ {133, 146, 122}, {125, 146, 132}, {121, 146, 132}, {120, 146, 141},
+ {120, 141, 141}, {116, 141, 132}, {116, 146, 132}, {117, 146, 122},
+ {108, 133, 103}, {100, 121, 94}, {89, 105, 75}, {170, 87, 0},
+ {56, 68, 28}, {48, 60, 28}, {44, 60, 28}, {52, 64, 37},
+ {80, 97, 75}, {93, 109, 94}, {100, 113, 94}, {96, 113, 94},
+ {96, 117, 94}, {104, 117, 94}, {108, 121, 94}, {108, 121, 94},
+ {113, 121, 103}, {112, 121, 103}, {108, 125, 103}, {104, 121, 103},
+ {108, 121, 103}, {108, 117, 103}, {113, 117, 94}, {113, 117, 94},
+ {117, 121, 94}, {117, 125, 94}, {129, 141, 94}, {141, 146, 94},
+ {141, 146, 103}, {154, 154, 113}, {154, 162, 122}, {154, 162, 122},
+ {154, 161, 122}, {146, 154, 113}, {137, 146, 103}, {129, 133, 94},
+ {125, 125, 85}, {105, 121, 75}, {97, 109, 66}, {68, 76, 37},
+ {56, 68, 28}, {64, 81, 37}, {69, 77, 37}, {113, 109, 66},
+ {121, 125, 75}, {129, 125, 85}, {121, 121, 85}, {109, 117, 85},
+ {105, 125, 85}, {108, 121, 94}, {101, 121, 84}, {104, 121, 94},
+ {100, 121, 103}, {100, 121, 103}, {104, 117, 94}, {100, 113, 94},
+ {100, 108, 94}, {101, 109, 85}, {77, 88, 66}, {52, 64, 37},
+ {44, 56, 28}, {32, 40, 28}, {28, 20, 9}, {24, 24, 0},
+ {4, 20, 0}, {8, 16, 0}, {12, 24, 0}, {28, 40, 9},
+ {36, 48, 18}, {60, 68, 37}, {93, 101, 85}, {117, 121, 85},
+ {133, 137, 94}, {137, 145, 113}, {150, 162, 113}, {154, 162, 122},
+ {158, 170, 122}, {158, 170, 122}, {170, 174, 113}, {166, 170, 122},
+ {162, 170, 122}, {161, 165, 122}, {150, 158, 122}, {133, 150, 122},
+ {129, 146, 122}, {120, 146, 122}, {117, 146, 122}, {117, 141, 122},
+ {112, 129, 103}, {125, 125, 103}, {137, 137, 103}, {146, 154, 113},
+ {158, 170, 122}, {170, 182, 132}, {178, 182, 141}, {182, 186, 151},
+ {194, 202, 151}, {178, 190, 141}, {178, 182, 141}, {170, 178, 141},
+ {162, 174, 132}, {154, 166, 122}, {146, 158, 122}, {133, 154, 122},
+ {125, 150, 122}, {125, 146, 122}, {125, 146, 132}, {125, 146, 132},
+ {129, 150, 132}, {133, 150, 132}, {133, 150, 132}, {129, 146, 132},
+ {133, 154, 132}, {133, 154, 132}, {141, 154, 132}, {146, 150, 132},
+ {158, 169, 132}, {174, 178, 132}, {170, 186, 132}, {178, 190, 141},
+ {182, 194, 160}, {198, 202, 160}, {198, 206, 170}, {214, 218, 179},
+ {218, 226, 197}, {210, 214, 188}, {190, 194, 160}, {170, 182, 141},
+ {158, 166, 122}, {146, 154, 113}, {137, 137, 113}, {129, 125, 103},
+ {125, 125, 103}, {129, 120, 103}, {125, 125, 94}, {121, 121, 94},
+ {121, 129, 94}, {129, 125, 103}, {129, 125, 103}, {129, 129, 103},
+ {129, 137, 113}, {133, 145, 122}, {137, 146, 122}, {150, 157, 122},
+ {150, 158, 122}, {146, 150, 132}, {150, 154, 141}, {146, 146, 132},
+ {137, 145, 132}, {129, 146, 132}, {125, 146, 141}, {120, 146, 141},
+ {116, 146, 141}, {120, 146, 141}, {120, 150, 151}, {129, 150, 141},
+ {129, 150, 141}, {129, 154, 141}, {133, 154, 141}, {137, 158, 141},
+ {137, 158, 141}, {141, 162, 151}, {149, 162, 151}, {141, 162, 151},
+ {150, 166, 141}, {154, 161, 141}, {158, 169, 132}, {158, 166, 122},
+ {162, 165, 113}, {158, 166, 113}, {154, 166, 113}, {149, 157, 113},
+ },
+ /* living-mud-bomb */
+ {
+ {193, 144, 35}, {153, 117, 33}, {133, 107, 35}, {102, 76, 20},
+ {96, 67, 20}, {79, 51, 16}, {70, 47, 16}, {66, 52, 16},
+ {64, 48, 16}, {64, 47, 16}, {61, 44, 16}, {58, 41, 16},
+ {58, 42, 16}, {64, 46, 16}, {72, 48, 16}, {85, 52, 18},
+ {86, 60, 18}, {88, 64, 22}, {85, 76, 24}, {75, 76, 24},
+ {89, 56, 15}, {84, 47, 16}, {84, 46, 16}, {84, 43, 16},
+ {84, 41, 16}, {84, 40, 16}, {84, 40, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {81, 39, 16}, {79, 39, 16},
+ {75, 39, 16}, {66, 39, 16}, {64, 40, 16}, {55, 40, 16},
+ {51, 40, 16}, {48, 40, 16}, {48, 40, 16}, {49, 42, 16},
+ {53, 43, 16}, {55, 41, 16}, {55, 41, 16}, {53, 41, 16},
+ {49, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 38, 16},
+ {48, 38, 16}, {48, 38, 16}, {48, 38, 16}, {48, 38, 16},
+ {45, 35, 16}, {48, 38, 16}, {48, 38, 16}, {49, 40, 16},
+ {53, 40, 16}, {58, 40, 16}, {64, 40, 16}, {75, 39, 16},
+ {81, 39, 16}, {83, 39, 16}, {84, 39, 16}, {81, 39, 16},
+ {79, 39, 16}, {75, 40, 16}, {68, 47, 16}, {64, 48, 16},
+ {64, 48, 16}, {64, 48, 16}, {58, 46, 16}, {56, 44, 16},
+ {56, 44, 16}, {53, 43, 16}, {49, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {48, 40, 16}, {49, 40, 16},
+ {55, 41, 16}, {63, 43, 16}, {68, 47, 16}, {79, 49, 16},
+ {93, 56, 15}, {96, 68, 20}, {120, 84, 20}, {151, 110, 35},
+ {186, 141, 35}, {236, 162, 35}, {246, 167, 44}, {238, 165, 35},
+ {226, 153, 33}, {185, 106, 22}, {128, 82, 15}, {96, 70, 20},
+ {84, 60, 22}, {72, 48, 16}, {62, 47, 16}, {55, 42, 16},
+ {49, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {49, 40, 16}, {55, 40, 16},
+ {58, 40, 16}, {64, 42, 16}, {68, 46, 16}, {70, 47, 16},
+ {77, 43, 16}, {81, 40, 16}, {84, 39, 16}, {85, 39, 16},
+ {91, 39, 15}, {96, 39, 16}, {96, 39, 16}, {94, 39, 16},
+ {91, 39, 16}, {85, 39, 16}, {85, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {84, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {84, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {85, 39, 16}, {87, 42, 16},
+ {91, 43, 16}, {96, 43, 16}, {100, 40, 16}, {106, 52, 22},
+ {124, 75, 17}, {164, 105, 21}, {217, 135, 30}, {239, 162, 49},
+ {248, 175, 48}, {237, 159, 49}, {202, 125, 32}, {163, 84, 15},
+ {130, 76, 20}, {106, 71, 22}, {98, 53, 15}, {91, 43, 16},
+ {84, 41, 16}, {77, 39, 16}, {68, 39, 16}, {64, 42, 16},
+ {64, 44, 16}, {68, 47, 16}, {75, 46, 16}, {84, 42, 16},
+ {91, 40, 16}, {98, 39, 16}, {100, 40, 16}, {101, 50, 15},
+ {117, 55, 15}, {120, 53, 15}, {120, 51, 15}, {120, 54, 15},
+ {106, 52, 15}, {102, 43, 15}, {100, 39, 16}, {92, 39, 16},
+ {87, 39, 16}, {85, 39, 16}, {84, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {84, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {84, 39, 16}, {84, 39, 16},
+ {84, 39, 16}, {84, 39, 16}, {84, 39, 16}, {85, 39, 16},
+ {92, 39, 16}, {100, 39, 16}, {100, 39, 16}, {117, 46, 15},
+ {127, 62, 15}, {175, 55, 15}, {192, 53, 15}, {237, 60, 15},
+ {247, 74, 20}, {230, 62, 17}, {184, 53, 15}, {182, 56, 15},
+ {134, 48, 15}, {128, 67, 20}, {128, 78, 28}, {137, 103, 28},
+ {188, 128, 30}, {229, 153, 35}, {245, 151, 35}, {248, 133, 41},
+ {248, 106, 31}, {202, 107, 22}, {137, 94, 35}, {108, 76, 15},
+ {93, 56, 15}, {84, 47, 16}, {84, 45, 16}, {84, 42, 16},
+ {81, 39, 16}, {81, 39, 16}, {73, 39, 16}, {64, 40, 16},
+ {58, 40, 16}, {55, 39, 16}, {49, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {48, 40, 16}, {48, 40, 16}, {48, 40, 16},
+ {48, 40, 16}, {49, 40, 16}, {53, 40, 16}, {57, 40, 16},
+ {64, 40, 16}, {66, 39, 16}, {70, 39, 16}, {77, 39, 16},
+ {81, 39, 16}, {84, 39, 16}, {84, 39, 16}, {81, 41, 16},
+ {77, 40, 16}, {68, 46, 16}, {64, 47, 16}, {64, 47, 16},
+ },
+ /* cars */
+ {
+ {43, 30, 36}, {38, 26, 33}, {29, 22, 30}, {32, 24, 32},
+ {31, 31, 35}, {40, 39, 36}, {51, 56, 42}, {69, 71, 43},
+ {92, 94, 54}, {116, 120, 64}, {183, 130, 77}, {221, 172, 85},
+ {218, 188, 136}, {187, 189, 148}, {190, 191, 173}, {176, 180, 172},
+ {159, 184, 170}, {142, 161, 152}, {126, 135, 130}, {104, 119, 98},
+ {98, 83, 78}, {81, 60, 64}, {87, 41, 42}, {90, 44, 36},
+ {78, 35, 32}, {59, 24, 34}, {57, 24, 32}, {54, 32, 38},
+ {51, 35, 37}, {54, 32, 39}, {64, 51, 38}, {74, 59, 34},
+ {82, 70, 38}, {91, 96, 51}, {132, 137, 68}, {207, 152, 66},
+ {229, 204, 97}, {217, 199, 117}, {213, 222, 147}, {218, 215, 178},
+ {193, 204, 181}, {177, 197, 185}, {171, 199, 191}, {165, 182, 185},
+ {155, 161, 156}, {116, 122, 129}, {91, 87, 99}, {72, 74, 85},
+ {63, 55, 63}, {44, 41, 50}, {34, 33, 45}, {33, 25, 34},
+ {27, 23, 28}, {33, 21, 25}, {39, 33, 24}, {47, 45, 38},
+ {52, 51, 45}, {67, 62, 56}, {92, 82, 69}, {120, 118, 89},
+ {152, 134, 85}, {164, 135, 91}, {152, 152, 93}, {155, 166, 95},
+ {146, 157, 118}, {138, 166, 102}, {130, 164, 111}, {133, 155, 93},
+ {113, 136, 88}, {102, 131, 85}, {96, 100, 60}, {90, 81, 57},
+ {69, 70, 37}, {59, 63, 38}, {58, 49, 37}, {49, 39, 39},
+ {43, 36, 32}, {33, 23, 30}, {27, 17, 30}, {27, 19, 31},
+ {26, 23, 29}, {24, 20, 27}, {26, 21, 29}, {33, 24, 32},
+ {45, 32, 37}, {51, 45, 46}, {70, 66, 62}, {108, 94, 78},
+ {174, 107, 74}, {205, 144, 56}, {210, 171, 78}, {205, 194, 148},
+ {211, 201, 178}, {205, 226, 197}, {195, 210, 202}, {183, 207, 199},
+ {178, 206, 195}, {162, 203, 184}, {156, 195, 177}, {158, 187, 169},
+ {152, 186, 156}, {152, 162, 151}, {143, 143, 137}, {147, 147, 111},
+ {125, 138, 102}, {125, 124, 97}, {122, 132, 68}, {109, 114, 62},
+ {92, 94, 49}, {82, 73, 41}, {72, 55, 49}, {69, 50, 39},
+ {67, 49, 47}, {69, 62, 60}, {70, 70, 80}, {84, 93, 100},
+ {96, 132, 114}, {106, 133, 125}, {113, 130, 123}, {117, 128, 114},
+ {136, 138, 123}, {143, 160, 126}, {151, 174, 128}, {157, 188, 118},
+ {165, 184, 135}, {173, 203, 155}, {186, 219, 163}, {198, 226, 180},
+ {195, 244, 191}, {203, 247, 199}, {207, 238, 196}, {205, 244, 226},
+ {221, 248, 232}, {236, 250, 236}, {222, 247, 232}, {207, 230, 212},
+ {190, 212, 200}, {165, 190, 191}, {136, 162, 173}, {107, 133, 128},
+ {96, 116, 109}, {92, 105, 77}, {76, 72, 69}, {89, 76, 60},
+ {100, 92, 76}, {120, 118, 98}, {150, 164, 137}, {167, 198, 158},
+ {188, 218, 157}, {209, 237, 162}, {215, 244, 184}, {222, 254, 212},
+ {236, 253, 238}, {246, 253, 239}, {252, 252, 241}, {251, 254, 243},
+ {248, 252, 241}, {243, 251, 238}, {242, 240, 224}, {239, 239, 213},
+ {211, 229, 209}, {224, 229, 197}, {215, 229, 179}, {203, 228, 197},
+ {190, 220, 180}, {182, 218, 163}, {159, 197, 139}, {133, 167, 129},
+ {120, 145, 128}, {106, 130, 110}, {79, 82, 94}, {64, 64, 70},
+ {52, 44, 57}, {42, 31, 43}, {39, 42, 46}, {58, 59, 61},
+ {76, 73, 84}, {108, 107, 114}, {140, 148, 145}, {169, 178, 165},
+ {195, 205, 186}, {213, 227, 209}, {218, 244, 225}, {241, 253, 215},
+ {241, 254, 212}, {239, 248, 180}, {233, 247, 175}, {223, 225, 171},
+ {183, 192, 147}, {152, 143, 122}, {123, 118, 103}, {105, 112, 95},
+ {90, 89, 94}, {76, 77, 89}, {79, 88, 98}, {96, 110, 112},
+ {116, 116, 116}, {134, 134, 134}, {144, 156, 164}, {138, 166, 175},
+ {153, 184, 183}, {166, 201, 161}, {174, 218, 170}, {179, 215, 175},
+ {182, 191, 169}, {171, 184, 169}, {149, 164, 168}, {136, 158, 163},
+ {116, 131, 127}, {92, 120, 99}, {78, 101, 65}, {77, 78, 61},
+ {66, 68, 48}, {66, 67, 44}, {75, 72, 52}, {92, 77, 66},
+ {153, 100, 68}, {178, 87, 59}, {168, 60, 50}, {152, 53, 43},
+ {92, 77, 50}, {79, 68, 52}, {68, 59, 66}, {63, 59, 72},
+ {57, 71, 73}, {79, 83, 91}, {111, 110, 95}, {134, 120, 91},
+ {182, 74, 55}, {189, 30, 39}, {189, 28, 37}, {167, 26, 34},
+ {159, 32, 36}, {97, 36, 37}, {63, 36, 40}, {48, 27, 36},
+ {40, 23, 24}, {32, 22, 16}, {24, 17, 23}, {22, 17, 25},
+ {23, 17, 25}, {24, 20, 27}, {24, 19, 24}, {26, 19, 26},
+ {30, 18, 26}, {33, 16, 29}, {36, 19, 27}, {33, 19, 27},
+ {35, 22, 27}, {45, 27, 21}, {50, 34, 37}, {49, 44, 43},
+ {64, 53, 45}, {80, 69, 52}, {92, 94, 68}, {127, 123, 85},
+ },
+ /* unhealthy-tan */
+ {
+ {218, 205, 192}, {223, 205, 183}, {219, 202, 177}, {205, 193, 171},
+ {206, 186, 157}, {189, 170, 147}, {173, 151, 130}, {163, 143, 129},
+ {160, 142, 119}, {156, 141, 120}, {159, 150, 133}, {168, 161, 143},
+ {183, 172, 164}, {211, 200, 191}, {216, 206, 199}, {218, 212, 211},
+ {220, 213, 206}, {222, 213, 203}, {223, 209, 199}, {212, 203, 192},
+ {205, 194, 176}, {198, 180, 151}, {187, 160, 127}, {173, 141, 106},
+ {153, 126, 101}, {130, 108, 88}, {104, 96, 79}, {88, 76, 64},
+ {72, 59, 51}, {67, 48, 48}, {63, 42, 44}, {58, 35, 38},
+ {60, 41, 45}, {64, 46, 45}, {76, 56, 50}, {80, 60, 53},
+ {92, 66, 57}, {96, 70, 58}, {99, 77, 64}, {105, 84, 67},
+ {114, 91, 71}, {118, 97, 79}, {121, 104, 84}, {123, 105, 82},
+ {114, 101, 82}, {113, 96, 72}, {113, 89, 64}, {113, 81, 60},
+ {108, 67, 51}, {102, 65, 52}, {88, 63, 53}, {79, 54, 49},
+ {74, 48, 48}, {65, 46, 47}, {69, 46, 44}, {68, 41, 43},
+ {70, 46, 46}, {73, 48, 49}, {77, 54, 52}, {77, 58, 54},
+ {90, 70, 59}, {93, 76, 64}, {97, 85, 67}, {101, 93, 81},
+ {115, 106, 87}, {125, 112, 97}, {141, 124, 97}, {147, 121, 104},
+ {146, 120, 104}, {138, 114, 91}, {134, 109, 85}, {122, 96, 75},
+ {111, 83, 66}, {106, 70, 62}, {102, 65, 53}, {92, 58, 51},
+ {83, 49, 49}, {83, 48, 46}, {83, 54, 49}, {94, 66, 56},
+ {99, 70, 54}, {103, 75, 58}, {108, 79, 63}, {113, 85, 64},
+ {119, 90, 72}, {123, 98, 76}, {128, 102, 78}, {130, 107, 78},
+ {131, 108, 79}, {132, 104, 79}, {131, 100, 79}, {135, 99, 73},
+ {135, 98, 71}, {131, 100, 70}, {124, 94, 70}, {118, 91, 71},
+ {114, 93, 70}, {105, 96, 69}, {105, 90, 67}, {105, 86, 64},
+ {104, 81, 63}, {103, 75, 62}, {94, 73, 56}, {88, 72, 58},
+ {85, 74, 59}, {92, 70, 60}, {87, 74, 60}, {96, 79, 62},
+ {101, 87, 70}, {112, 102, 77}, {124, 107, 84}, {133, 108, 87},
+ {143, 114, 92}, {148, 119, 99}, {149, 124, 104}, {149, 124, 106},
+ {150, 131, 104}, {165, 140, 109}, {175, 148, 113}, {189, 164, 131},
+ {199, 178, 144}, {202, 182, 160}, {209, 192, 178}, {214, 202, 191},
+ {220, 210, 194}, {219, 207, 192}, {222, 204, 181}, {208, 192, 165},
+ {196, 169, 138}, {184, 153, 118}, {176, 142, 104}, {165, 129, 92},
+ {155, 115, 75}, {142, 110, 77}, {136, 108, 82}, {136, 108, 84},
+ {134, 110, 84}, {134, 115, 87}, {136, 116, 90}, {146, 123, 95},
+ {148, 126, 98}, {149, 127, 102}, {149, 125, 101}, {149, 127, 97},
+ {150, 125, 99}, {148, 124, 97}, {140, 120, 92}, {136, 118, 86},
+ {131, 118, 86}, {129, 109, 85}, {128, 108, 85}, {125, 108, 89},
+ {126, 107, 97}, {131, 112, 95}, {140, 126, 101}, {153, 135, 112},
+ {171, 147, 118}, {195, 164, 136}, {204, 177, 144}, {199, 179, 149},
+ {201, 185, 160}, {199, 184, 162}, {182, 170, 153}, {172, 156, 140},
+ {158, 148, 125}, {144, 139, 121}, {132, 125, 112}, {129, 121, 100},
+ {124, 110, 92}, {112, 100, 84}, {104, 84, 68}, {100, 76, 67},
+ {98, 75, 63}, {94, 76, 60}, {94, 76, 60}, {94, 73, 60},
+ {102, 78, 61}, {111, 81, 65}, {113, 89, 65}, {123, 93, 71},
+ {128, 101, 78}, {132, 108, 80}, {133, 108, 84}, {130, 109, 80},
+ {123, 102, 78}, {117, 101, 75}, {112, 94, 71}, {106, 86, 67},
+ {106, 82, 65}, {109, 82, 60}, {111, 84, 57}, {113, 84, 57},
+ {118, 88, 57}, {121, 92, 64}, {125, 98, 69}, {129, 106, 74},
+ {132, 109, 79}, {143, 119, 82}, {162, 125, 87}, {168, 133, 95},
+ {162, 140, 115}, {158, 144, 132}, {156, 152, 147}, {155, 152, 150},
+ {161, 159, 156}, {183, 168, 159}, {188, 174, 151}, {190, 171, 145},
+ {194, 170, 143}, {193, 173, 146}, {193, 167, 145}, {188, 163, 138},
+ {180, 155, 124}, {162, 140, 118}, {148, 123, 100}, {137, 106, 85},
+ {130, 99, 76}, {123, 94, 68}, {124, 91, 62}, {121, 89, 64},
+ {123, 91, 64}, {126, 94, 69}, {131, 97, 75}, {128, 98, 75},
+ {123, 96, 72}, {117, 94, 66}, {117, 92, 65}, {111, 89, 63},
+ {102, 83, 63}, {90, 75, 60}, {84, 66, 59}, {79, 55, 55},
+ {72, 49, 52}, {71, 46, 48}, {72, 46, 48}, {75, 50, 48},
+ {79, 54, 52}, {92, 62, 53}, {99, 67, 54}, {109, 75, 59},
+ {122, 79, 55}, {126, 89, 63}, {132, 94, 67}, {136, 97, 67},
+ {138, 102, 70}, {135, 106, 80}, {139, 114, 91}, {152, 126, 96},
+ {163, 130, 101}, {159, 134, 104}, {162, 141, 109}, {168, 136, 101},
+ {152, 132, 103}, {151, 123, 95}, {142, 115, 83}, {138, 108, 78},
+ },
+ /* daffodil */
+ {
+ {17, 0, 0}, {54, 34, 22}, {92, 62, 28}, {128, 122, 46},
+ {214, 134, 0}, {248, 152, 0}, {242, 157, 5}, {225, 157, 5},
+ {172, 143, 56}, {133, 110, 56}, {90, 90, 51}, {65, 65, 46},
+ {66, 54, 43}, {54, 54, 47}, {37, 51, 45}, {34, 49, 34},
+ {34, 60, 28}, {39, 52, 36}, {34, 45, 28}, {51, 51, 30},
+ {49, 49, 32}, {54, 49, 32}, {49, 45, 26}, {45, 39, 17},
+ {34, 28, 11}, {22, 22, 11}, {17, 17, 11}, {17, 17, 5},
+ {17, 5, 5}, {11, 5, 11}, {5, 11, 11}, {5, 5, 5},
+ {5, 0, 0}, {5, 0, 0}, {11, 5, 5}, {11, 5, 5},
+ {11, 5, 5}, {11, 5, 5}, {11, 5, 0}, {11, 5, 5},
+ {11, 0, 0}, {11, 0, 0}, {17, 0, 0}, {17, 5, 0},
+ {28, 5, 5}, {47, 22, 11}, {75, 43, 20}, {114, 83, 46},
+ {186, 134, 50}, {239, 146, 11}, {254, 146, 0}, {237, 152, 5},
+ {210, 135, 28}, {114, 101, 43}, {68, 66, 22}, {54, 60, 32},
+ {45, 56, 34}, {51, 60, 28}, {51, 54, 28}, {47, 51, 24},
+ {54, 49, 15}, {54, 49, 11}, {51, 28, 5}, {43, 26, 0},
+ {28, 22, 0}, {22, 17, 0}, {28, 34, 0}, {30, 45, 15},
+ {43, 41, 15}, {45, 45, 22}, {49, 45, 22}, {51, 45, 22},
+ {47, 37, 22}, {45, 39, 22}, {32, 32, 20}, {28, 17, 17},
+ {22, 11, 11}, {17, 11, 11}, {17, 11, 11}, {17, 17, 17},
+ {17, 17, 17}, {17, 22, 17}, {17, 20, 15}, {17, 22, 5},
+ {17, 17, 0}, {17, 11, 0}, {17, 5, 5}, {22, 11, 5},
+ {24, 9, 9}, {28, 17, 11}, {34, 22, 11}, {41, 28, 17},
+ {45, 39, 22}, {51, 53, 29}, {68, 62, 34}, {101, 78, 56},
+ {155, 116, 65}, {220, 151, 73}, {242, 157, 39}, {254, 186, 39},
+ {248, 203, 33}, {248, 191, 27}, {248, 163, 11}, {254, 134, 0},
+ {254, 129, 0}, {212, 95, 0}, {127, 62, 28}, {77, 59, 25},
+ {60, 58, 32}, {60, 63, 35}, {68, 79, 45}, {85, 93, 59},
+ {96, 96, 56}, {103, 81, 92}, {82, 124, 147}, {60, 111, 139},
+ {45, 73, 131}, {49, 83, 83}, {51, 62, 68}, {58, 52, 52},
+ {49, 43, 43}, {35, 36, 36}, {34, 34, 22}, {22, 22, 20},
+ {22, 22, 26}, {22, 17, 26}, {22, 17, 17}, {22, 17, 11},
+ {22, 22, 17}, {22, 22, 17}, {26, 32, 20}, {28, 34, 22},
+ {34, 45, 26}, {49, 49, 37}, {56, 56, 49}, {77, 71, 66},
+ {109, 112, 92}, {120, 160, 159}, {180, 186, 180}, {218, 224, 190},
+ {239, 233, 205}, {207, 218, 178}, {158, 168, 123}, {127, 155, 96},
+ {107, 113, 90}, {66, 77, 66}, {54, 79, 54}, {49, 69, 49},
+ {56, 68, 36}, {39, 72, 48}, {49, 86, 63}, {90, 102, 79},
+ {139, 133, 94}, {179, 179, 122}, {237, 214, 157}, {246, 235, 189},
+ {248, 225, 163}, {248, 208, 129}, {248, 208, 73}, {254, 197, 44},
+ {254, 197, 44}, {248, 185, 56}, {208, 180, 73}, {189, 155, 59},
+ {152, 141, 79}, {127, 138, 73}, {138, 144, 67}, {135, 141, 79},
+ {150, 156, 94}, {177, 148, 103}, {214, 180, 118}, {246, 217, 161},
+ {244, 198, 159}, {246, 229, 178}, {254, 237, 180}, {254, 237, 180},
+ {246, 223, 172}, {171, 191, 142}, {147, 152, 118}, {147, 141, 107},
+ {135, 141, 107}, {149, 131, 104}, {158, 141, 108}, {152, 141, 118},
+ {156, 154, 128}, {188, 165, 154}, {231, 208, 169}, {248, 226, 186},
+ {248, 226, 197}, {208, 225, 197}, {175, 179, 152}, {151, 145, 117},
+ {119, 124, 96}, {86, 100, 66}, {62, 85, 51}, {45, 71, 22},
+ {49, 64, 26}, {60, 69, 25}, {101, 87, 50}, {145, 113, 56},
+ {214, 135, 28}, {248, 163, 16}, {248, 169, 22}, {254, 174, 16},
+ {254, 180, 11}, {254, 180, 16}, {242, 174, 11}, {254, 168, 5},
+ {254, 157, 0}, {254, 151, 0}, {254, 157, 0}, {254, 174, 0},
+ {254, 186, 0}, {254, 191, 16}, {242, 197, 28}, {196, 178, 60},
+ {152, 130, 68}, {113, 124, 59}, {100, 100, 56}, {68, 79, 59},
+ {53, 60, 39}, {49, 37, 32}, {39, 28, 22}, {34, 17, 17},
+ {28, 22, 17}, {34, 22, 11}, {34, 32, 11}, {49, 37, 15},
+ {56, 39, 28}, {60, 47, 32}, {77, 57, 31}, {90, 80, 48},
+ {107, 116, 65}, {164, 118, 84}, {188, 148, 109}, {197, 152, 107},
+ {231, 197, 129}, {197, 174, 95}, {158, 152, 95}, {133, 121, 65},
+ {111, 82, 71}, {90, 60, 54}, {92, 75, 47}, {110, 70, 42},
+ {132, 88, 50}, {186, 112, 50}, {220, 129, 28}, {224, 117, 22},
+ {180, 95, 28}, {102, 60, 34}, {60, 60, 20}, {43, 52, 15},
+ {26, 43, 5}, {17, 31, 0}, {11, 28, 5}, {28, 22, 17},
+ },
+ /* rose */
+ {
+ {113, 33, 37}, {84, 23, 37}, {55, 27, 28}, {36, 40, 18},
+ {32, 56, 9}, {60, 77, 18}, {56, 101, 27}, {60, 81, 28},
+ {52, 89, 18}, {48, 85, 28}, {40, 68, 28}, {40, 56, 28},
+ {40, 52, 28}, {76, 30, 28}, {105, 18, 18}, {121, 22, 19},
+ {125, 21, 28}, {105, 22, 28}, {84, 19, 28}, {56, 23, 28},
+ {36, 36, 28}, {32, 40, 28}, {28, 36, 18}, {28, 32, 18},
+ {27, 32, 9}, {48, 27, 9}, {77, 22, 9}, {101, 15, 9},
+ {137, 14, 9}, {150, 21, 19}, {162, 28, 38}, {178, 38, 57},
+ {166, 35, 86}, {166, 39, 86}, {178, 44, 86}, {178, 47, 95},
+ {174, 68, 113}, {170, 182, 170}, {177, 190, 188}, {181, 198, 179},
+ {166, 170, 160}, {182, 52, 105}, {198, 44, 96}, {166, 31, 86},
+ {154, 30, 67}, {146, 29, 47}, {129, 25, 28}, {101, 22, 18},
+ {68, 22, 18}, {52, 23, 18}, {36, 36, 18}, {28, 36, 18},
+ {32, 44, 18}, {36, 48, 18}, {40, 56, 28}, {52, 60, 28},
+ {52, 73, 28}, {64, 73, 28}, {101, 26, 28}, {125, 25, 28},
+ {141, 22, 28}, {129, 18, 28}, {109, 21, 28}, {76, 26, 28},
+ {48, 52, 37}, {60, 72, 28}, {73, 97, 47}, {77, 109, 85},
+ {92, 133, 122}, {100, 133, 132}, {96, 133, 131}, {85, 113, 85},
+ {93, 96, 47}, {93, 117, 47}, {113, 108, 37}, {125, 63, 47},
+ {141, 29, 38}, {146, 25, 28}, {146, 22, 28}, {146, 25, 38},
+ {146, 25, 57}, {150, 26, 66}, {158, 30, 76}, {158, 35, 86},
+ {162, 27, 86}, {166, 27, 86}, {158, 34, 76}, {153, 34, 57},
+ {141, 29, 47}, {121, 33, 37}, {76, 59, 37}, {48, 60, 37},
+ {28, 56, 28}, {28, 40, 18}, {20, 28, 18}, {20, 24, 18},
+ {23, 24, 18}, {24, 32, 28}, {28, 36, 37}, {40, 39, 37},
+ {64, 23, 28}, {101, 22, 28}, {141, 19, 38}, {146, 22, 47},
+ {150, 21, 47}, {158, 26, 57}, {150, 22, 47}, {150, 21, 57},
+ {146, 22, 47}, {141, 18, 38}, {137, 21, 28}, {137, 22, 28},
+ {133, 21, 28}, {133, 21, 18}, {113, 18, 9}, {133, 20, 18},
+ {141, 21, 19}, {146, 21, 28}, {150, 21, 28}, {154, 28, 28},
+ {158, 24, 28}, {150, 28, 28}, {154, 28, 38}, {166, 33, 48},
+ {166, 25, 48}, {154, 22, 47}, {158, 30, 57}, {162, 31, 67},
+ {162, 43, 76}, {157, 140, 56}, {129, 170, 103}, {166, 186, 169},
+ {181, 189, 198}, {173, 189, 207}, {169, 185, 188}, {154, 170, 170},
+ {182, 52, 133}, {178, 44, 115}, {178, 40, 105}, {182, 56, 114},
+ {150, 170, 160}, {165, 186, 188}, {165, 185, 188}, {112, 158, 160},
+ {104, 133, 113}, {76, 113, 75}, {85, 92, 47}, {133, 30, 38},
+ {150, 29, 28}, {158, 28, 28}, {158, 29, 28}, {170, 25, 28},
+ {166, 37, 38}, {157, 30, 48}, {150, 26, 57}, {146, 22, 66},
+ {150, 34, 67}, {150, 33, 57}, {150, 25, 47}, {146, 22, 47},
+ {146, 19, 38}, {137, 21, 28}, {109, 21, 28}, {72, 19, 28},
+ {48, 27, 28}, {43, 40, 37}, {36, 44, 28}, {36, 52, 28},
+ {48, 60, 37}, {52, 77, 37}, {52, 81, 47}, {60, 77, 56},
+ {60, 85, 56}, {77, 109, 66}, {77, 125, 65}, {77, 133, 56},
+ {93, 141, 46}, {101, 146, 66}, {93, 133, 74}, {81, 129, 83},
+ {105, 137, 75}, {109, 162, 94}, {113, 158, 84}, {121, 162, 85},
+ {142, 154, 66}, {113, 141, 75}, {109, 129, 56}, {101, 121, 47},
+ {109, 137, 47}, {97, 121, 37}, {81, 117, 37}, {73, 97, 28},
+ {81, 93, 18}, {109, 25, 28}, {129, 25, 28}, {141, 25, 28},
+ {141, 22, 28}, {141, 22, 28}, {141, 21, 28}, {137, 21, 37},
+ {93, 19, 28}, {68, 19, 28}, {51, 19, 28}, {24, 24, 37},
+ {23, 24, 28}, {24, 24, 18}, {24, 24, 18}, {27, 24, 18},
+ {28, 24, 18}, {47, 23, 28}, {63, 23, 28}, {88, 19, 28},
+ {121, 18, 19}, {133, 24, 19}, {146, 21, 28}, {146, 21, 28},
+ {146, 21, 28}, {146, 18, 28}, {146, 18, 28}, {146, 18, 28},
+ {146, 17, 28}, {146, 18, 28}, {146, 21, 28}, {146, 21, 19},
+ {146, 21, 19}, {146, 17, 19}, {141, 21, 19}, {141, 21, 19},
+ {141, 21, 19}, {133, 21, 19}, {129, 21, 19}, {109, 18, 18},
+ {80, 26, 18}, {56, 27, 18}, {32, 32, 18}, {28, 28, 18},
+ {32, 32, 18}, {56, 19, 18}, {80, 19, 18}, {113, 22, 28},
+ {146, 26, 57}, {154, 34, 67}, {161, 51, 85}, {107, 133, 132},
+ {150, 174, 170}, {165, 186, 179}, {154, 174, 170}, {124, 154, 122},
+ {170, 55, 85}, {174, 39, 67}, {182, 55, 66}, {170, 37, 47},
+ {158, 32, 38}, {145, 26, 38}, {125, 25, 28}, {68, 23, 28},
+ },
+ /* healthy-skin */
+ {
+ {250, 225, 235}, {194, 165, 188}, {157, 133, 141}, {137, 100, 94},
+ {96, 60, 47}, {72, 43, 28}, {60, 35, 28}, {52, 23, 18},
+ {35, 16, 9}, {35, 16, 9}, {35, 16, 9}, {32, 8, 0},
+ {31, 12, 9}, {24, 12, 9}, {35, 16, 18}, {40, 19, 28},
+ {56, 27, 28}, {80, 39, 47}, {113, 64, 65}, {141, 84, 75},
+ {166, 95, 85}, {182, 107, 94}, {186, 107, 94}, {190, 111, 94},
+ {186, 111, 94}, {182, 111, 85}, {154, 92, 66}, {141, 88, 47},
+ {105, 68, 46}, {81, 51, 18}, {56, 27, 18}, {52, 23, 9},
+ {35, 16, 18}, {27, 12, 18}, {24, 8, 18}, {0, 0, 0},
+ {0, 0, 0}, {24, 4, 18}, {35, 8, 18}, {44, 12, 18},
+ {44, 20, 18}, {44, 19, 18}, {40, 12, 18}, {52, 19, 18},
+ {52, 19, 28}, {40, 16, 28}, {35, 12, 28}, {39, 16, 28},
+ {52, 23, 28}, {56, 27, 28}, {68, 39, 28}, {76, 48, 47},
+ {101, 64, 65}, {133, 100, 84}, {158, 112, 94}, {186, 119, 94},
+ {198, 136, 113}, {206, 152, 132}, {210, 157, 141}, {226, 161, 160},
+ {238, 164, 160}, {238, 180, 169}, {246, 184, 169}, {250, 184, 169},
+ {246, 180, 159}, {242, 172, 150}, {250, 160, 141}, {242, 152, 122},
+ {242, 148, 122}, {234, 144, 113}, {234, 144, 122}, {222, 145, 132},
+ {214, 149, 132}, {214, 149, 122}, {219, 149, 122}, {210, 165, 132},
+ {219, 165, 141}, {234, 172, 150}, {238, 176, 150}, {238, 176, 150},
+ {246, 176, 140}, {250, 164, 141}, {242, 160, 131}, {234, 156, 113},
+ {234, 144, 103}, {219, 128, 94}, {214, 123, 85}, {210, 119, 94},
+ {214, 124, 103}, {226, 140, 113}, {238, 152, 131}, {238, 164, 141},
+ {238, 164, 150}, {234, 160, 150}, {238, 156, 141}, {242, 148, 141},
+ {219, 141, 132}, {210, 136, 122}, {198, 119, 104}, {194, 119, 103},
+ {182, 103, 94}, {174, 103, 85}, {170, 95, 75}, {166, 88, 85},
+ {150, 88, 85}, {149, 92, 75}, {146, 96, 85}, {141, 88, 75},
+ {121, 76, 65}, {96, 60, 56}, {84, 52, 47}, {80, 47, 37},
+ {84, 51, 28}, {92, 60, 37}, {113, 60, 47}, {137, 84, 56},
+ {166, 95, 75}, {182, 115, 85}, {186, 128, 104}, {206, 157, 132},
+ {222, 165, 151}, {238, 184, 169}, {246, 197, 206}, {246, 213, 244},
+ {234, 226, 254}, {242, 226, 244}, {214, 202, 207}, {219, 169, 160},
+ {214, 149, 132}, {219, 145, 122}, {210, 137, 113}, {214, 128, 113},
+ {210, 120, 104}, {202, 124, 103}, {206, 128, 113}, {198, 119, 104},
+ {194, 107, 94}, {178, 99, 85}, {158, 84, 75}, {154, 80, 75},
+ {137, 68, 66}, {125, 68, 65}, {125, 64, 56}, {137, 72, 56},
+ {146, 84, 56}, {158, 107, 75}, {166, 111, 85}, {182, 128, 103},
+ {202, 153, 141}, {222, 185, 169}, {246, 209, 188}, {246, 222, 244},
+ {250, 226, 244}, {246, 221, 244}, {230, 205, 225}, {206, 202, 198},
+ {226, 185, 179}, {238, 184, 169}, {234, 172, 150}, {210, 161, 122},
+ {210, 141, 113}, {190, 111, 94}, {170, 103, 85}, {146, 84, 75},
+ {117, 60, 56}, {96, 47, 47}, {80, 43, 37}, {72, 39, 37},
+ {76, 43, 47}, {96, 60, 47}, {113, 76, 56}, {141, 92, 66},
+ {162, 116, 94}, {182, 140, 113}, {190, 161, 150}, {198, 157, 179},
+ {206, 169, 179}, {210, 173, 170}, {206, 160, 151}, {194, 153, 151},
+ {194, 148, 151}, {166, 133, 122}, {149, 120, 113}, {129, 104, 103},
+ {125, 100, 103}, {101, 80, 84}, {84, 56, 47}, {80, 56, 37},
+ {88, 47, 37}, {101, 56, 47}, {121, 72, 56}, {154, 88, 66},
+ {186, 115, 94}, {202, 145, 132}, {218, 161, 160}, {230, 172, 169},
+ {226, 181, 188}, {205, 185, 207}, {202, 185, 198}, {178, 174, 170},
+ {161, 137, 141}, {133, 112, 113}, {129, 84, 84}, {100, 68, 56},
+ {84, 51, 47}, {72, 39, 37}, {72, 39, 28}, {80, 43, 28},
+ {92, 64, 47}, {117, 88, 75}, {133, 112, 94}, {170, 128, 103},
+ {186, 157, 141}, {186, 165, 151}, {186, 157, 160}, {169, 149, 160},
+ {170, 137, 132}, {170, 120, 104}, {178, 111, 94}, {186, 107, 94},
+ {194, 107, 85}, {198, 111, 85}, {186, 115, 85}, {178, 111, 85},
+ {178, 107, 85}, {178, 95, 56}, {149, 63, 37}, {101, 52, 47},
+ {84, 39, 37}, {72, 31, 28}, {72, 27, 28}, {60, 27, 28},
+ {48, 31, 28}, {56, 31, 28}, {68, 35, 28}, {68, 35, 28},
+ {80, 39, 28}, {89, 56, 37}, {113, 76, 56}, {137, 96, 75},
+ {157, 120, 94}, {178, 140, 103}, {202, 153, 132}, {198, 149, 141},
+ {198, 145, 132}, {198, 128, 104}, {194, 119, 94}, {174, 103, 85},
+ {146, 104, 85}, {145, 100, 85}, {145, 104, 85}, {153, 112, 103},
+ {170, 112, 103}, {194, 119, 94}, {214, 132, 94}, {230, 140, 103},
+ },
+ /* orange */
+ {
+ {123, 106, 57}, {97, 100, 60}, {91, 83, 58}, {70, 74, 54},
+ {70, 71, 53}, {59, 68, 51}, {59, 56, 32}, {54, 61, 44},
+ {57, 72, 57}, {67, 74, 62}, {81, 97, 76}, {95, 114, 91},
+ {109, 114, 82}, {115, 113, 84}, {112, 120, 98}, {128, 131, 98},
+ {130, 137, 106}, {134, 142, 106}, {150, 151, 107}, {149, 150, 114},
+ {149, 152, 123}, {148, 156, 135}, {170, 161, 127}, {188, 121, 129},
+ {228, 101, 100}, {234, 92, 73}, {224, 66, 57}, {195, 72, 57},
+ {121, 65, 36}, {86, 54, 32}, {75, 73, 38}, {29, 34, 39},
+ {54, 52, 52}, {53, 57, 62}, {60, 78, 68}, {63, 69, 77},
+ {64, 89, 73}, {84, 105, 77}, {84, 114, 95}, {98, 122, 108},
+ {120, 143, 125}, {132, 158, 138}, {138, 165, 137}, {139, 162, 137},
+ {154, 153, 159}, {178, 155, 163}, {176, 154, 155}, {183, 159, 157},
+ {187, 171, 130}, {169, 174, 143}, {165, 183, 130}, {170, 171, 138},
+ {175, 179, 129}, {159, 175, 124}, {155, 156, 122}, {143, 142, 110},
+ {131, 129, 116}, {125, 128, 116}, {127, 121, 131}, {119, 119, 134},
+ {115, 118, 132}, {121, 139, 133}, {123, 138, 124}, {124, 141, 130},
+ {136, 154, 141}, {140, 148, 158}, {131, 149, 158}, {91, 155, 182},
+ {100, 132, 171}, {83, 129, 184}, {82, 128, 193}, {85, 149, 207},
+ {93, 183, 236}, {113, 192, 242}, {137, 196, 237}, {127, 191, 237},
+ {119, 187, 234}, {114, 170, 214}, {109, 156, 197}, {144, 153, 173},
+ {153, 147, 160}, {169, 161, 168}, {175, 185, 189}, {158, 196, 225},
+ {143, 193, 219}, {161, 186, 214}, {179, 185, 193}, {199, 168, 158},
+ {225, 119, 116}, {235, 102, 114}, {230, 100, 108}, {217, 112, 120},
+ {176, 143, 147}, {141, 135, 141}, {113, 145, 147}, {95, 128, 153},
+ {103, 125, 146}, {112, 129, 127}, {122, 133, 106}, {135, 148, 97},
+ {144, 134, 85}, {163, 136, 81}, {160, 143, 93}, {198, 163, 73},
+ {220, 178, 61}, {213, 162, 67}, {201, 149, 50}, {161, 119, 68},
+ {186, 108, 93}, {166, 138, 99}, {152, 149, 140}, {156, 158, 164},
+ {142, 180, 213}, {138, 181, 225}, {117, 187, 236}, {90, 177, 233},
+ {83, 153, 201}, {111, 136, 164}, {136, 113, 115}, {187, 103, 92},
+ {236, 100, 67}, {228, 75, 48}, {247, 68, 36}, {248, 69, 32},
+ {241, 64, 42}, {233, 68, 41}, {218, 64, 51}, {136, 85, 64},
+ {96, 85, 68}, {100, 103, 79}, {91, 100, 77}, {106, 99, 68},
+ {121, 113, 71}, {136, 148, 79}, {147, 152, 81}, {158, 154, 91},
+ {191, 168, 101}, {200, 179, 97}, {190, 173, 101}, {185, 175, 112},
+ {177, 194, 115}, {172, 183, 132}, {175, 191, 149}, {149, 177, 132},
+ {136, 156, 131}, {125, 139, 122}, {107, 132, 119}, {97, 119, 102},
+ {89, 109, 92}, {84, 92, 72}, {75, 75, 66}, {79, 80, 73},
+ {76, 91, 73}, {77, 106, 93}, {76, 101, 107}, {92, 121, 107},
+ {98, 125, 119}, {112, 148, 137}, {139, 170, 156}, {150, 187, 213},
+ {144, 193, 230}, {145, 192, 228}, {146, 191, 222}, {167, 180, 181},
+ {158, 185, 154}, {157, 177, 121}, {155, 160, 89}, {158, 168, 78},
+ {172, 175, 93}, {186, 158, 73}, {186, 158, 73}, {181, 153, 84},
+ {151, 157, 88}, {133, 154, 91}, {130, 151, 99}, {132, 147, 98},
+ {126, 139, 105}, {123, 129, 103}, {106, 111, 96}, {84, 95, 105},
+ {74, 81, 89}, {71, 64, 92}, {77, 73, 81}, {90, 101, 83},
+ {96, 100, 90}, {105, 105, 97}, {115, 114, 97}, {117, 122, 95},
+ {134, 137, 83}, {136, 138, 55}, {150, 126, 52}, {110, 118, 59},
+ {85, 107, 55}, {89, 96, 60}, {76, 76, 53}, {68, 67, 37},
+ {72, 73, 59}, {90, 87, 73}, {115, 107, 86}, {144, 110, 76},
+ {150, 121, 87}, {152, 123, 95}, {139, 140, 106}, {132, 144, 123},
+ {141, 151, 150}, {162, 158, 160}, {169, 178, 175}, {185, 199, 218},
+ {191, 205, 221}, {185, 202, 221}, {185, 195, 212}, {189, 197, 144},
+ {194, 177, 136}, {213, 196, 90}, {220, 182, 64}, {208, 164, 76},
+ {238, 106, 91}, {236, 103, 85}, {240, 88, 83}, {239, 80, 72},
+ {239, 82, 57}, {236, 68, 52}, {246, 65, 43}, {245, 63, 26},
+ {244, 67, 16}, {244, 78, 17}, {244, 82, 23}, {243, 76, 23},
+ {247, 69, 28}, {243, 74, 28}, {244, 72, 44}, {238, 86, 55},
+ {243, 75, 60}, {225, 84, 71}, {159, 92, 97}, {124, 125, 103},
+ {113, 117, 105}, {92, 113, 100}, {95, 113, 101}, {106, 111, 113},
+ {107, 115, 109}, {110, 115, 99}, {117, 123, 90}, {119, 123, 75},
+ {134, 122, 81}, {130, 128, 71}, {132, 142, 75}, {155, 155, 94},
+ {163, 164, 121}, {160, 152, 148}, {130, 165, 160}, {107, 163, 204},
+ {94, 183, 235}, {96, 188, 238}, {106, 180, 234}, {126, 172, 209},
+ },
+ /* white-ivy */
+ {
+ {242, 242, 254}, {208, 231, 197}, {163, 197, 152}, {141, 175, 118},
+ {107, 152, 73}, {96, 147, 51}, {79, 124, 45}, {79, 107, 28},
+ {85, 107, 22}, {96, 124, 39}, {107, 147, 62}, {113, 164, 96},
+ {135, 169, 135}, {152, 203, 152}, {178, 212, 178}, {208, 231, 197},
+ {220, 242, 225}, {212, 246, 246}, {208, 237, 254}, {197, 242, 254},
+ {169, 233, 225}, {169, 220, 186}, {152, 208, 163}, {152, 203, 152},
+ {163, 186, 152}, {146, 163, 158}, {156, 156, 156}, {144, 158, 158},
+ {135, 152, 124}, {118, 152, 118}, {101, 130, 113}, {82, 82, 82},
+ {58, 32, 32}, {56, 28, 28}, {84, 84, 39}, {96, 113, 33},
+ {107, 130, 22}, {118, 152, 28}, {141, 175, 34}, {135, 175, 67},
+ {113, 164, 90}, {118, 152, 101}, {112, 141, 112}, {101, 137, 112},
+ {101, 135, 101}, {96, 130, 96}, {84, 118, 84}, {67, 113, 62},
+ {56, 107, 51}, {56, 107, 45}, {68, 102, 34}, {73, 124, 51},
+ {79, 118, 79}, {107, 141, 96}, {130, 164, 124}, {158, 180, 141},
+ {186, 186, 169}, {214, 220, 208}, {239, 239, 239}, {246, 246, 246},
+ {254, 254, 254}, {254, 254, 254}, {242, 254, 254}, {242, 248, 254},
+ {237, 242, 248}, {231, 242, 242}, {220, 237, 203}, {214, 208, 118},
+ {163, 197, 56}, {169, 163, 28}, {147, 169, 50}, {141, 158, 84},
+ {118, 164, 118}, {113, 169, 118}, {107, 158, 118}, {73, 141, 101},
+ {45, 128, 56}, {34, 85, 34}, {28, 73, 28}, {28, 77, 28},
+ {45, 90, 56}, {84, 118, 84}, {118, 152, 118}, {152, 180, 152},
+ {175, 203, 175}, {197, 231, 208}, {208, 231, 254}, {208, 231, 254},
+ {208, 220, 254}, {206, 214, 197}, {192, 169, 169}, {171, 180, 175},
+ {174, 208, 174}, {180, 209, 180}, {197, 231, 191}, {220, 237, 214},
+ {237, 242, 237}, {246, 246, 246}, {254, 254, 254}, {254, 254, 254},
+ {254, 254, 254}, {242, 254, 254}, {242, 254, 254}, {242, 248, 254},
+ {237, 237, 248}, {225, 225, 225}, {231, 163, 163}, {150, 112, 112},
+ {96, 84, 79}, {82, 82, 82}, {56, 96, 56}, {50, 92, 45},
+ {34, 85, 28}, {39, 90, 39}, {73, 115, 73}, {124, 135, 101},
+ {158, 175, 129}, {163, 186, 163}, {186, 209, 186}, {208, 214, 254},
+ {208, 225, 254}, {208, 208, 254}, {174, 208, 197}, {152, 192, 152},
+ {118, 152, 118}, {84, 124, 79}, {62, 101, 56}, {28, 73, 28},
+ {5, 51, 0}, {34, 0, 0}, {0, 52, 0}, {0, 51, 0},
+ {0, 52, 0}, {11, 62, 5}, {28, 79, 22}, {28, 73, 28},
+ {56, 83, 11}, {68, 68, 0}, {45, 51, 0}, {73, 96, 28},
+ {84, 101, 33}, {90, 102, 28}, {79, 130, 51}, {90, 143, 112},
+ {90, 175, 118}, {152, 203, 152}, {163, 209, 186}, {186, 220, 254},
+ {208, 225, 254}, {220, 220, 254}, {220, 220, 254}, {220, 225, 254},
+ {220, 231, 254}, {220, 231, 254}, {231, 231, 254}, {231, 231, 254},
+ {231, 231, 254}, {231, 242, 254}, {220, 254, 254}, {208, 254, 254},
+ {208, 254, 254}, {197, 254, 254}, {208, 254, 254}, {220, 254, 254},
+ {231, 248, 254}, {231, 237, 254}, {231, 231, 254}, {231, 231, 254},
+ {231, 231, 254}, {231, 237, 254}, {231, 242, 254}, {231, 242, 254},
+ {231, 242, 254}, {231, 242, 254}, {231, 237, 254}, {231, 231, 254},
+ {231, 231, 254}, {220, 237, 254}, {220, 237, 254}, {197, 254, 254},
+ {163, 254, 254}, {140, 191, 197}, {152, 175, 152}, {152, 158, 129},
+ {124, 130, 90}, {107, 67, 62}, {124, 34, 34}, {137, 45, 45},
+ {153, 51, 51}, {153, 51, 51}, {175, 95, 67}, {141, 141, 112},
+ {135, 152, 124}, {141, 163, 135}, {152, 186, 152}, {152, 203, 152},
+ {152, 203, 152}, {163, 208, 163}, {191, 225, 191}, {231, 231, 231},
+ {220, 231, 254}, {220, 225, 254}, {220, 225, 254}, {231, 231, 254},
+ {242, 242, 254}, {242, 242, 254}, {242, 248, 254}, {242, 254, 254},
+ {254, 254, 254}, {254, 254, 254}, {254, 254, 254}, {242, 248, 254},
+ {242, 242, 254}, {231, 242, 254}, {231, 242, 254}, {231, 242, 254},
+ {231, 242, 231}, {231, 225, 175}, {220, 214, 135}, {254, 169, 140},
+ {208, 197, 118}, {208, 191, 112}, {163, 191, 107}, {107, 152, 73},
+ {96, 147, 51}, {90, 141, 51}, {90, 135, 39}, {85, 136, 51},
+ {96, 141, 45}, {101, 147, 45}, {118, 141, 62}, {135, 129, 78},
+ {135, 158, 107}, {146, 192, 101}, {146, 197, 112}, {152, 197, 135},
+ {152, 203, 152}, {186, 214, 186}, {208, 242, 231}, {220, 237, 254},
+ {231, 242, 254}, {242, 242, 254}, {242, 242, 254}, {242, 242, 254},
+ {242, 242, 254}, {242, 248, 254}, {231, 254, 254}, {231, 254, 254},
+ {231, 242, 254}, {237, 237, 248}, {214, 237, 220}, {186, 220, 180},
+ {152, 203, 152}, {141, 192, 84}, {118, 164, 45}, {96, 124, 28},
+ },
+ /* summer-makeup */
+ {
+ {238, 193, 141}, {238, 192, 141}, {238, 192, 141}, {234, 193, 141},
+ {234, 193, 141}, {234, 193, 141}, {230, 193, 141}, {230, 189, 141},
+ {226, 189, 141}, {226, 193, 141}, {234, 193, 141}, {238, 197, 141},
+ {238, 201, 141}, {234, 197, 150}, {234, 193, 150}, {234, 197, 150},
+ {230, 193, 150}, {226, 189, 141}, {222, 185, 132}, {219, 177, 113},
+ {210, 144, 85}, {219, 106, 75}, {219, 95, 66}, {190, 63, 37},
+ {150, 64, 28}, {145, 47, 28}, {133, 51, 37}, {101, 43, 28},
+ {84, 51, 37}, {80, 52, 37}, {80, 56, 37}, {80, 60, 47},
+ {89, 72, 56}, {109, 84, 56}, {125, 104, 66}, {166, 107, 66},
+ {198, 136, 75}, {219, 161, 94}, {222, 180, 103}, {226, 193, 122},
+ {226, 189, 122}, {222, 180, 113}, {219, 169, 103}, {198, 140, 85},
+ {178, 115, 66}, {158, 99, 56}, {158, 88, 47}, {158, 88, 56},
+ {166, 107, 56}, {186, 119, 66}, {202, 140, 94}, {219, 173, 103},
+ {230, 180, 113}, {230, 180, 122}, {226, 180, 122}, {219, 165, 113},
+ {202, 140, 94}, {162, 103, 66}, {125, 80, 56}, {113, 72, 46},
+ {89, 60, 37}, {76, 52, 37}, {64, 48, 37}, {52, 39, 28},
+ {40, 35, 28}, {36, 36, 28}, {32, 36, 37}, {36, 36, 37},
+ {40, 35, 37}, {48, 40, 37}, {52, 39, 37}, {48, 35, 37},
+ {52, 40, 37}, {64, 48, 37}, {72, 52, 37}, {80, 60, 47},
+ {96, 68, 56}, {97, 68, 56}, {100, 68, 56}, {97, 64, 56},
+ {84, 56, 56}, {76, 48, 47}, {68, 47, 47}, {64, 48, 47},
+ {68, 48, 47}, {76, 52, 47}, {84, 60, 47}, {101, 68, 56},
+ {117, 84, 65}, {137, 88, 66}, {166, 103, 66}, {206, 136, 75},
+ {210, 157, 85}, {222, 164, 94}, {214, 156, 85}, {202, 140, 85},
+ {186, 123, 66}, {150, 103, 66}, {121, 88, 56}, {105, 76, 56},
+ {88, 60, 47}, {76, 48, 47}, {64, 48, 37}, {56, 44, 37},
+ {60, 48, 47}, {68, 48, 47}, {85, 60, 47}, {105, 80, 56},
+ {137, 88, 66}, {154, 107, 75}, {194, 140, 85}, {214, 169, 113},
+ {219, 177, 122}, {222, 180, 141}, {137, 145, 151}, {88, 104, 94},
+ {76, 68, 66}, {81, 68, 65}, {80, 64, 56}, {76, 60, 56},
+ {80, 56, 47}, {76, 52, 47}, {76, 52, 56}, {76, 52, 56},
+ {68, 52, 56}, {56, 44, 56}, {48, 39, 47}, {48, 35, 47},
+ {48, 35, 37}, {40, 35, 37}, {39, 32, 28}, {36, 32, 28},
+ {36, 32, 28}, {36, 32, 28}, {32, 32, 28}, {31, 28, 28},
+ {28, 32, 28}, {31, 32, 28}, {24, 32, 28}, {31, 28, 28},
+ {32, 28, 28}, {35, 28, 37}, {36, 32, 37}, {36, 32, 37},
+ {40, 31, 28}, {44, 35, 28}, {52, 36, 28}, {56, 39, 28},
+ {52, 35, 37}, {60, 39, 37}, {52, 40, 37}, {48, 40, 37},
+ {48, 48, 37}, {44, 40, 28}, {44, 36, 28}, {44, 36, 28},
+ {48, 35, 28}, {56, 39, 28}, {64, 39, 28}, {88, 51, 37},
+ {117, 72, 46}, {146, 80, 56}, {166, 111, 56}, {198, 136, 75},
+ {214, 164, 94}, {219, 177, 113}, {222, 185, 132}, {226, 184, 141},
+ {226, 189, 141}, {230, 193, 131}, {230, 197, 131}, {234, 192, 131},
+ {230, 184, 131}, {222, 185, 132}, {230, 184, 131}, {230, 184, 131},
+ {226, 189, 132}, {222, 189, 132}, {234, 188, 131}, {234, 184, 131},
+ {230, 184, 122}, {222, 180, 113}, {219, 177, 103}, {210, 148, 85},
+ {194, 132, 66}, {190, 106, 56}, {170, 95, 56}, {174, 91, 47},
+ {194, 87, 28}, {194, 72, 28}, {154, 76, 37}, {129, 64, 37},
+ {93, 51, 37}, {72, 43, 37}, {68, 43, 37}, {64, 48, 37},
+ {60, 52, 37}, {68, 52, 37}, {89, 64, 47}, {113, 84, 56},
+ {145, 100, 66}, {182, 127, 85}, {210, 165, 94}, {219, 181, 103},
+ {234, 193, 112}, {234, 193, 122}, {234, 188, 122}, {222, 176, 113},
+ {202, 148, 94}, {186, 119, 75}, {170, 103, 66}, {146, 92, 75},
+ {137, 92, 75}, {149, 104, 75}, {174, 111, 75}, {186, 127, 85},
+ {202, 144, 94}, {222, 169, 122}, {226, 180, 141}, {234, 193, 150},
+ {234, 201, 160}, {234, 205, 169}, {234, 201, 160}, {230, 197, 150},
+ {230, 189, 141}, {226, 180, 122}, {214, 161, 103}, {186, 135, 85},
+ {153, 104, 84}, {129, 108, 84}, {121, 92, 75}, {121, 80, 65},
+ {113, 76, 56}, {121, 72, 56}, {117, 72, 46}, {125, 68, 47},
+ {125, 68, 56}, {109, 64, 56}, {109, 60, 46}, {88, 55, 47},
+ {84, 43, 47}, {72, 43, 47}, {60, 44, 37}, {52, 44, 37},
+ {44, 40, 37}, {40, 35, 28}, {36, 36, 28}, {36, 36, 37},
+ {40, 35, 37}, {44, 44, 37}, {52, 48, 47}, {80, 60, 47},
+ {109, 68, 46}, {129, 84, 47}, {158, 99, 66}, {186, 119, 75},
+ },
+ /* glow-buzz */
+ {
+ {182, 91, 37}, {141, 84, 37}, {121, 68, 46}, {93, 60, 46},
+ {76, 48, 37}, {72, 43, 37}, {80, 48, 47}, {101, 60, 46},
+ {137, 68, 47}, {162, 76, 47}, {178, 95, 56}, {182, 102, 56},
+ {182, 99, 47}, {182, 87, 47}, {194, 84, 56}, {198, 84, 56},
+ {190, 87, 66}, {174, 87, 56}, {146, 76, 56}, {117, 60, 47},
+ {88, 51, 37}, {64, 39, 28}, {52, 27, 28}, {44, 24, 28},
+ {40, 24, 28}, {40, 23, 28}, {40, 23, 28}, {40, 23, 28},
+ {44, 27, 28}, {52, 31, 28}, {60, 39, 28}, {84, 47, 37},
+ {109, 51, 47}, {133, 59, 56}, {162, 64, 56}, {178, 68, 47},
+ {190, 68, 56}, {198, 72, 47}, {198, 72, 47}, {198, 72, 47},
+ {190, 68, 47}, {178, 68, 47}, {166, 68, 47}, {149, 71, 37},
+ {129, 64, 37}, {100, 60, 28}, {92, 47, 37}, {76, 43, 37},
+ {72, 48, 37}, {76, 52, 47}, {100, 56, 56}, {133, 76, 56},
+ {154, 80, 47}, {178, 87, 47}, {178, 80, 47}, {158, 80, 47},
+ {141, 68, 47}, {113, 60, 46}, {96, 56, 47}, {84, 47, 56},
+ {84, 48, 47}, {92, 55, 47}, {100, 51, 46}, {96, 51, 47},
+ {92, 55, 47}, {84, 56, 47}, {93, 60, 47}, {84, 52, 37},
+ {76, 52, 28}, {72, 40, 18}, {64, 35, 28}, {56, 31, 28},
+ {48, 32, 37}, {44, 35, 37}, {48, 31, 37}, {56, 35, 37},
+ {48, 36, 37}, {56, 35, 37}, {64, 35, 37}, {60, 39, 47},
+ {60, 35, 47}, {56, 35, 37}, {52, 35, 28}, {48, 31, 28},
+ {48, 31, 28}, {44, 31, 28}, {44, 31, 28}, {44, 28, 28},
+ {40, 27, 28}, {44, 28, 28}, {56, 31, 37}, {64, 43, 47},
+ {80, 43, 56}, {100, 60, 56}, {137, 76, 65}, {178, 102, 56},
+ {210, 123, 66}, {198, 144, 75}, {214, 148, 75}, {219, 157, 75},
+ {222, 160, 75}, {222, 168, 84}, {250, 179, 74}, {250, 191, 65},
+ {246, 183, 56}, {242, 183, 65}, {238, 168, 56}, {230, 152, 47},
+ {242, 159, 56}, {219, 145, 75}, {214, 128, 122}, {222, 176, 169},
+ {190, 169, 122}, {254, 212, 121}, {250, 225, 150}, {254, 212, 93},
+ {254, 221, 84}, {250, 213, 93}, {254, 217, 84}, {250, 200, 74},
+ {254, 212, 84}, {246, 208, 74}, {254, 208, 74}, {254, 191, 56},
+ {254, 178, 46}, {234, 167, 46}, {222, 149, 47}, {210, 145, 56},
+ {206, 127, 56}, {194, 106, 56}, {190, 106, 56}, {198, 91, 56},
+ {190, 91, 47}, {186, 106, 56}, {198, 119, 47}, {219, 132, 56},
+ {230, 119, 56}, {230, 110, 47}, {230, 123, 47}, {234, 148, 56},
+ {250, 166, 56}, {250, 175, 56}, {250, 179, 56}, {250, 179, 56},
+ {238, 171, 56}, {219, 153, 66}, {230, 152, 56}, {210, 140, 47},
+ {194, 106, 47}, {174, 83, 37}, {162, 72, 37}, {154, 72, 47},
+ {166, 76, 47}, {170, 84, 47}, {170, 91, 47}, {182, 95, 47},
+ {186, 84, 56}, {198, 87, 56}, {210, 102, 56}, {226, 110, 56},
+ {226, 98, 47}, {226, 102, 56}, {222, 87, 56}, {214, 83, 56},
+ {202, 84, 56}, {194, 84, 56}, {182, 80, 56}, {162, 72, 56},
+ {146, 76, 47}, {137, 64, 47}, {125, 64, 47}, {109, 56, 56},
+ {113, 56, 56}, {129, 64, 56}, {146, 76, 56}, {166, 72, 56},
+ {182, 76, 56}, {194, 72, 47}, {182, 80, 56}, {186, 72, 37},
+ {186, 59, 28}, {194, 72, 37}, {206, 72, 47}, {206, 83, 37},
+ {206, 80, 47}, {210, 87, 47}, {219, 83, 47}, {210, 80, 56},
+ {198, 76, 56}, {174, 72, 56}, {146, 60, 47}, {129, 60, 37},
+ {109, 51, 28}, {92, 47, 28}, {101, 51, 28}, {121, 51, 37},
+ {141, 55, 37}, {158, 60, 37}, {170, 56, 47}, {174, 64, 47},
+ {174, 68, 47}, {178, 64, 47}, {182, 63, 47}, {190, 80, 47},
+ {202, 80, 47}, {202, 87, 47}, {198, 83, 47}, {182, 83, 47},
+ {178, 80, 47}, {150, 72, 47}, {133, 64, 47}, {113, 60, 46},
+ {101, 51, 37}, {76, 39, 37}, {60, 31, 28}, {44, 27, 28},
+ {40, 23, 28}, {39, 19, 18}, {36, 16, 18}, {36, 16, 18},
+ {35, 20, 18}, {35, 20, 28}, {35, 24, 28}, {36, 24, 18},
+ {35, 20, 9}, {36, 20, 9}, {36, 20, 18}, {36, 24, 18},
+ {44, 31, 18}, {56, 35, 28}, {72, 43, 37}, {96, 43, 37},
+ {109, 51, 46}, {117, 60, 46}, {129, 72, 47}, {125, 68, 37},
+ {109, 60, 37}, {96, 47, 37}, {80, 39, 37}, {60, 35, 37},
+ {48, 27, 28}, {44, 27, 18}, {40, 23, 18}, {40, 23, 18},
+ {39, 24, 28}, {36, 24, 28}, {35, 20, 28}, {35, 16, 28},
+ {35, 20, 37}, {40, 28, 37}, {56, 40, 37}, {72, 43, 47},
+ {97, 52, 46}, {113, 51, 47}, {162, 60, 47}, {186, 68, 47},
+ },
+ /* deep-water */
+ {
+ {24, 20, 18}, {24, 24, 28}, {28, 28, 47}, {27, 24, 66},
+ {24, 28, 75}, {23, 28, 75}, {23, 28, 66}, {19, 28, 47},
+ {20, 24, 28}, {20, 24, 18}, {20, 24, 18}, {24, 28, 28},
+ {28, 32, 56}, {28, 36, 66}, {28, 36, 85}, {36, 52, 103},
+ {48, 64, 113}, {48, 56, 122}, {44, 64, 122}, {52, 64, 132},
+ {52, 64, 113}, {68, 60, 113}, {68, 64, 103}, {56, 77, 103},
+ {56, 68, 94}, {52, 60, 94}, {52, 52, 94}, {36, 52, 94},
+ {36, 52, 85}, {36, 40, 66}, {32, 32, 56}, {24, 24, 28},
+ {16, 16, 18}, {11, 8, 18}, {11, 8, 9}, {8, 4, 0},
+ {8, 4, 0}, {11, 4, 9}, {11, 8, 18}, {11, 8, 28},
+ {16, 16, 28}, {19, 28, 37}, {24, 32, 56}, {24, 40, 75},
+ {28, 48, 94}, {36, 64, 103}, {40, 64, 113}, {44, 68, 122},
+ {44, 68, 113}, {40, 64, 113}, {36, 60, 113}, {36, 52, 103},
+ {28, 48, 94}, {32, 40, 75}, {36, 36, 66}, {32, 36, 56},
+ {24, 32, 47}, {19, 32, 37}, {20, 32, 28}, {11, 24, 18},
+ {8, 20, 18}, {8, 16, 18}, {12, 16, 18}, {12, 16, 18},
+ {15, 20, 18}, {16, 20, 28}, {20, 28, 37}, {23, 32, 47},
+ {27, 32, 56}, {28, 28, 47}, {27, 28, 37}, {23, 20, 28},
+ {19, 16, 18}, {15, 16, 18}, {16, 16, 9}, {12, 12, 0},
+ {8, 12, 0}, {8, 8, 0}, {8, 8, 9}, {8, 8, 9},
+ {8, 8, 9}, {8, 12, 9}, {11, 12, 18}, {11, 12, 18},
+ {11, 12, 18}, {11, 12, 18}, {12, 16, 18}, {16, 24, 18},
+ {24, 32, 18}, {28, 44, 37}, {40, 52, 37}, {48, 56, 66},
+ {48, 56, 75}, {52, 68, 85}, {56, 77, 113}, {73, 97, 132},
+ {84, 109, 160}, {100, 113, 160}, {100, 117, 169}, {101, 117, 160},
+ {104, 129, 151}, {113, 129, 151}, {104, 125, 170}, {104, 125, 170},
+ {124, 137, 198}, {104, 116, 170}, {88, 100, 132}, {68, 77, 103},
+ {52, 64, 94}, {40, 56, 85}, {28, 44, 75}, {28, 36, 56},
+ {24, 32, 47}, {24, 28, 28}, {24, 24, 18}, {20, 24, 18},
+ {15, 20, 18}, {12, 16, 18}, {12, 16, 18}, {12, 16, 9},
+ {12, 12, 9}, {12, 12, 9}, {11, 12, 9}, {11, 16, 9},
+ {12, 16, 9}, {16, 16, 9}, {16, 20, 0}, {16, 24, 0},
+ {20, 20, 9}, {24, 20, 18}, {28, 24, 18}, {32, 32, 28},
+ {32, 36, 47}, {32, 40, 56}, {32, 36, 66}, {28, 36, 75},
+ {28, 36, 75}, {28, 36, 75}, {28, 36, 75}, {28, 36, 66},
+ {24, 36, 66}, {24, 36, 75}, {20, 36, 85}, {20, 36, 85},
+ {24, 36, 75}, {24, 32, 66}, {24, 32, 56}, {23, 24, 28},
+ {20, 20, 18}, {16, 16, 9}, {16, 16, 9}, {16, 20, 9},
+ {16, 20, 9}, {19, 20, 9}, {20, 24, 18}, {20, 24, 18},
+ {20, 24, 18}, {16, 16, 18}, {15, 16, 18}, {12, 12, 18},
+ {11, 12, 9}, {11, 12, 9}, {8, 12, 9}, {12, 8, 9},
+ {8, 8, 9}, {8, 8, 9}, {4, 4, 9}, {4, 4, 9},
+ {7, 0, 18}, {3, 4, 18}, {3, 8, 18}, {3, 4, 9},
+ {4, 4, 9}, {8, 8, 9}, {8, 8, 9}, {12, 12, 9},
+ {16, 12, 9}, {16, 12, 9}, {16, 16, 9}, {19, 20, 9},
+ {20, 28, 18}, {24, 32, 28}, {32, 40, 37}, {36, 44, 56},
+ {56, 68, 75}, {84, 101, 113}, {117, 125, 151}, {149, 165, 188},
+ {157, 177, 216}, {181, 189, 235}, {193, 206, 216}, {198, 202, 198},
+ {149, 177, 197}, {141, 161, 188}, {108, 146, 150}, {92, 108, 141},
+ {68, 73, 113}, {56, 64, 103}, {48, 56, 94}, {36, 52, 85},
+ {28, 40, 85}, {32, 40, 85}, {28, 36, 85}, {32, 44, 85},
+ {36, 56, 94}, {48, 68, 113}, {52, 73, 132}, {52, 73, 132},
+ {48, 73, 141}, {56, 72, 141}, {64, 81, 141}, {73, 93, 141},
+ {76, 92, 132}, {60, 77, 132}, {56, 64, 132}, {48, 48, 113},
+ {36, 40, 85}, {32, 36, 75}, {31, 32, 56}, {23, 28, 37},
+ {19, 20, 28}, {11, 20, 18}, {7, 16, 9}, {7, 16, 9},
+ {7, 12, 9}, {7, 12, 9}, {7, 8, 9}, {4, 8, 0},
+ {4, 4, 0}, {4, 4, 0}, {4, 4, 0}, {4, 4, 0},
+ {7, 8, 9}, {8, 12, 9}, {8, 16, 9}, {11, 16, 18},
+ {12, 20, 18}, {19, 24, 28}, {20, 32, 47}, {20, 36, 66},
+ {20, 36, 85}, {20, 36, 85}, {24, 32, 85}, {24, 32, 85},
+ {23, 28, 85}, {19, 28, 85}, {20, 32, 85}, {23, 32, 85},
+ {24, 32, 85}, {31, 32, 94}, {28, 36, 85}, {24, 36, 85},
+ {27, 28, 66}, {24, 28, 47}, {23, 24, 28}, {15, 16, 18},
+ },
+ /* afternoon-beach */
+ {
+ {182, 162, 170}, {190, 157, 132}, {178, 136, 113}, {174, 107, 75},
+ {162, 124, 85}, {190, 160, 94}, {219, 197, 103}, {230, 210, 113},
+ {254, 245, 131}, {254, 254, 159}, {254, 254, 178}, {250, 254, 206},
+ {254, 254, 216}, {254, 254, 197}, {250, 254, 169}, {234, 222, 141},
+ {219, 193, 160}, {202, 189, 170}, {190, 185, 169}, {181, 169, 179},
+ {181, 157, 179}, {169, 153, 188}, {173, 157, 198}, {161, 149, 188},
+ {165, 149, 179}, {162, 146, 170}, {145, 141, 151}, {108, 108, 141},
+ {112, 112, 103}, {129, 129, 103}, {149, 124, 103}, {173, 149, 94},
+ {206, 149, 94}, {234, 168, 103}, {250, 192, 112}, {250, 200, 112},
+ {242, 217, 122}, {242, 229, 122}, {254, 237, 112}, {246, 229, 112},
+ {246, 209, 103}, {254, 204, 93}, {250, 196, 93}, {238, 152, 94},
+ {194, 127, 75}, {186, 111, 66}, {190, 127, 75}, {222, 168, 94},
+ {254, 204, 121}, {254, 245, 169}, {250, 254, 206}, {250, 254, 225},
+ {222, 213, 225}, {210, 193, 207}, {238, 197, 169}, {250, 233, 150},
+ {254, 249, 159}, {254, 254, 169}, {254, 254, 197}, {250, 254, 216},
+ {254, 254, 197}, {254, 254, 169}, {254, 254, 159}, {254, 241, 140},
+ {254, 221, 121}, {250, 209, 112}, {246, 209, 112}, {254, 217, 112},
+ {254, 221, 121}, {250, 225, 140}, {238, 197, 169}, {226, 185, 207},
+ {206, 185, 207}, {205, 177, 207}, {201, 185, 216}, {189, 181, 226},
+ {189, 181, 216}, {177, 165, 216}, {173, 165, 207}, {185, 169, 188},
+ {202, 165, 188}, {219, 181, 160}, {230, 184, 141}, {234, 180, 131},
+ {230, 180, 122}, {210, 181, 141}, {210, 169, 151}, {202, 161, 160},
+ {174, 154, 170}, {177, 149, 179}, {170, 150, 170}, {169, 153, 179},
+ {166, 162, 170}, {161, 165, 151}, {174, 162, 151}, {178, 157, 141},
+ {170, 157, 141}, {158, 145, 113}, {141, 116, 94}, {125, 96, 75},
+ {97, 92, 47}, {77, 77, 47}, {73, 73, 47}, {64, 68, 47},
+ {68, 64, 47}, {64, 60, 47}, {60, 52, 47}, {56, 48, 47},
+ {48, 36, 47}, {48, 40, 37}, {52, 40, 37}, {52, 39, 37},
+ {52, 39, 37}, {64, 39, 37}, {64, 44, 37}, {72, 56, 37},
+ {64, 60, 37}, {68, 60, 37}, {84, 56, 56}, {117, 76, 65},
+ {146, 88, 75}, {145, 92, 75}, {149, 99, 75}, {146, 108, 85},
+ {145, 104, 103}, {162, 108, 104}, {158, 116, 94}, {169, 115, 104},
+ {157, 129, 94}, {162, 153, 103}, {182, 186, 113}, {222, 214, 122},
+ {246, 246, 131}, {250, 254, 169}, {254, 254, 197}, {254, 254, 216},
+ {254, 254, 225}, {234, 226, 254}, {209, 201, 226}, {206, 193, 207},
+ {210, 177, 198}, {181, 165, 179}, {162, 141, 160}, {145, 121, 122},
+ {145, 100, 113}, {121, 96, 94}, {104, 93, 94}, {85, 80, 75},
+ {89, 85, 75}, {97, 93, 84}, {100, 96, 94}, {101, 105, 84},
+ {121, 121, 103}, {137, 129, 113}, {150, 150, 103}, {182, 152, 103},
+ {206, 177, 113}, {230, 197, 94}, {238, 180, 84}, {238, 168, 93},
+ {226, 172, 84}, {189, 152, 84}, {182, 112, 66}, {166, 99, 66},
+ {133, 80, 56}, {88, 59, 47}, {68, 48, 47}, {64, 44, 47},
+ {52, 39, 37}, {48, 40, 28}, {48, 35, 28}, {52, 39, 28},
+ {48, 44, 37}, {56, 48, 37}, {56, 48, 47}, {68, 52, 56},
+ {76, 64, 56}, {85, 76, 65}, {113, 88, 75}, {117, 92, 84},
+ {125, 96, 84}, {125, 96, 84}, {125, 92, 84}, {133, 88, 84},
+ {117, 88, 75}, {92, 72, 56}, {81, 68, 56}, {72, 68, 56},
+ {68, 68, 56}, {76, 76, 66}, {81, 81, 75}, {85, 77, 75},
+ {85, 68, 65}, {72, 56, 66}, {68, 64, 56}, {64, 60, 66},
+ {64, 56, 56}, {60, 48, 56}, {60, 52, 56}, {60, 56, 56},
+ {60, 56, 47}, {68, 60, 47}, {68, 64, 56}, {73, 73, 56},
+ {81, 81, 66}, {85, 96, 75}, {97, 105, 75}, {105, 109, 56},
+ {125, 117, 66}, {125, 129, 66}, {146, 141, 85}, {170, 169, 85},
+ {198, 202, 113}, {234, 205, 112}, {250, 192, 112}, {238, 172, 103},
+ {234, 152, 94}, {190, 119, 66}, {174, 106, 56}, {166, 111, 66},
+ {141, 108, 75}, {125, 112, 85}, {108, 113, 103}, {113, 129, 103},
+ {133, 125, 122}, {182, 145, 160}, {202, 161, 179}, {210, 193, 179},
+ {254, 241, 197}, {254, 254, 216}, {254, 254, 216}, {254, 254, 197},
+ {254, 254, 169}, {254, 254, 159}, {254, 245, 150}, {254, 245, 140},
+ {250, 229, 131}, {226, 206, 122}, {182, 182, 113}, {162, 141, 94},
+ {149, 128, 65}, {145, 116, 75}, {137, 100, 84}, {125, 108, 85},
+ {129, 96, 84}, {125, 112, 75}, {117, 109, 84}, {105, 109, 75},
+ {97, 109, 75}, {101, 105, 75}, {121, 112, 66}, {141, 112, 75},
+ {149, 116, 85}, {162, 141, 103}, {194, 169, 122}, {218, 201, 132},
+ },
+ /* dim-beach */
+ {
+ {27, 28, 28}, {48, 36, 37}, {68, 48, 37}, {72, 60, 37},
+ {68, 68, 37}, {56, 60, 37}, {44, 44, 28}, {36, 36, 28},
+ {32, 32, 28}, {28, 32, 28}, {28, 28, 28}, {28, 28, 28},
+ {28, 32, 28}, {28, 36, 28}, {32, 40, 37}, {48, 60, 56},
+ {56, 73, 66}, {60, 81, 75}, {80, 93, 75}, {92, 137, 141},
+ {100, 170, 160}, {133, 185, 188}, {153, 194, 188}, {141, 182, 169},
+ {117, 162, 131}, {109, 145, 122}, {85, 113, 84}, {69, 85, 56},
+ {64, 68, 47}, {56, 56, 37}, {40, 40, 28}, {32, 36, 28},
+ {31, 32, 28}, {28, 32, 28}, {28, 32, 28}, {32, 32, 28},
+ {36, 36, 28}, {52, 44, 28}, {72, 56, 18}, {97, 60, 28},
+ {121, 84, 28}, {129, 112, 37}, {150, 107, 37}, {141, 100, 37},
+ {129, 84, 28}, {109, 72, 28}, {64, 73, 28}, {48, 44, 28},
+ {36, 40, 18}, {36, 40, 18}, {52, 56, 37}, {64, 64, 47},
+ {68, 76, 56}, {97, 88, 56}, {109, 100, 56}, {109, 100, 56},
+ {93, 97, 66}, {68, 76, 66}, {48, 76, 75}, {48, 56, 56},
+ {40, 36, 37}, {28, 32, 37}, {28, 32, 37}, {27, 32, 37},
+ {28, 32, 37}, {32, 32, 28}, {32, 32, 28}, {28, 32, 28},
+ {28, 32, 28}, {28, 28, 28}, {27, 28, 28}, {24, 28, 18},
+ {24, 28, 18}, {23, 28, 18}, {20, 28, 18}, {20, 28, 28},
+ {19, 28, 28}, {19, 28, 28}, {16, 24, 28}, {19, 28, 28},
+ {20, 28, 28}, {24, 32, 28}, {24, 36, 28}, {24, 40, 28},
+ {24, 36, 37}, {24, 32, 37}, {32, 36, 37}, {52, 60, 47},
+ {68, 77, 47}, {105, 88, 46}, {133, 116, 37}, {166, 140, 37},
+ {182, 153, 103}, {214, 206, 132}, {222, 218, 169}, {230, 234, 188},
+ {242, 242, 197}, {242, 241, 197}, {230, 226, 178}, {246, 196, 93},
+ {242, 184, 74}, {226, 156, 56}, {210, 164, 56}, {222, 160, 56},
+ {214, 156, 47}, {222, 163, 37}, {222, 152, 37}, {214, 157, 37},
+ {186, 123, 37}, {182, 114, 47}, {174, 111, 47}, {149, 99, 56},
+ {121, 92, 56}, {101, 113, 75}, {125, 170, 122}, {154, 174, 160},
+ {185, 202, 188}, {198, 218, 198}, {169, 198, 188}, {141, 178, 169},
+ {104, 154, 150}, {52, 109, 151}, {44, 89, 122}, {40, 56, 75},
+ {20, 48, 56}, {24, 36, 37}, {24, 32, 28}, {24, 32, 28},
+ {24, 32, 28}, {24, 32, 28}, {27, 32, 28}, {28, 32, 28},
+ {28, 36, 28}, {32, 40, 28}, {40, 56, 47}, {48, 60, 56},
+ {48, 64, 56}, {56, 64, 56}, {56, 64, 56}, {80, 68, 56},
+ {92, 60, 46}, {113, 96, 47}, {150, 111, 56}, {153, 128, 66},
+ {170, 166, 132}, {210, 202, 160}, {226, 226, 188}, {246, 242, 197},
+ {250, 241, 206}, {234, 230, 197}, {226, 222, 188}, {194, 189, 141},
+ {153, 145, 103}, {105, 101, 66}, {73, 77, 56}, {60, 64, 47},
+ {44, 40, 37}, {32, 32, 37}, {27, 28, 37}, {24, 24, 37},
+ {24, 28, 28}, {24, 28, 28}, {24, 28, 28}, {27, 32, 28},
+ {31, 32, 28}, {32, 36, 28}, {40, 48, 37}, {48, 64, 47},
+ {52, 73, 47}, {56, 72, 56}, {60, 68, 47}, {68, 76, 56},
+ {93, 85, 47}, {113, 96, 47}, {105, 80, 47}, {101, 72, 46},
+ {104, 72, 56}, {97, 76, 46}, {92, 68, 37}, {89, 76, 37},
+ {88, 72, 47}, {68, 73, 47}, {48, 64, 37}, {36, 40, 28},
+ {32, 36, 28}, {28, 36, 28}, {28, 32, 28}, {28, 32, 18},
+ {32, 28, 18}, {28, 28, 28}, {28, 28, 28}, {24, 28, 28},
+ {23, 28, 28}, {24, 28, 28}, {24, 28, 28}, {24, 28, 28},
+ {24, 32, 28}, {27, 36, 37}, {28, 36, 37}, {32, 60, 56},
+ {48, 64, 56}, {48, 68, 56}, {52, 73, 56}, {52, 72, 56},
+ {48, 64, 56}, {48, 60, 56}, {52, 56, 47}, {44, 40, 37},
+ {36, 36, 28}, {36, 32, 28}, {36, 36, 28}, {44, 52, 37},
+ {60, 60, 47}, {60, 64, 47}, {68, 73, 56}, {77, 84, 56},
+ {93, 97, 56}, {93, 101, 56}, {97, 101, 66}, {97, 97, 56},
+ {97, 101, 66}, {97, 109, 66}, {88, 137, 112}, {97, 154, 141},
+ {108, 158, 141}, {116, 166, 160}, {125, 182, 160}, {125, 178, 141},
+ {141, 182, 132}, {166, 157, 113}, {174, 136, 75}, {198, 140, 56},
+ {210, 149, 56}, {219, 172, 66}, {210, 206, 151}, {210, 222, 198},
+ {214, 230, 207}, {222, 230, 207}, {226, 226, 197}, {206, 206, 151},
+ {166, 162, 122}, {125, 125, 75}, {89, 101, 66}, {60, 73, 47},
+ {40, 44, 28}, {28, 32, 18}, {24, 32, 18}, {20, 32, 18},
+ {24, 32, 28}, {28, 36, 37}, {36, 40, 47}, {36, 52, 66},
+ {40, 80, 85}, {56, 85, 84}, {88, 142, 141}, {129, 182, 160},
+ },
+ /* cloudy-brick */
+ {
+ {202, 206, 188}, {178, 186, 160}, {153, 157, 141}, {129, 141, 122},
+ {112, 129, 113}, {96, 125, 122}, {104, 146, 141}, {112, 158, 169},
+ {137, 181, 188}, {157, 222, 225}, {153, 218, 235}, {132, 210, 235},
+ {100, 198, 216}, {84, 154, 188}, {60, 117, 122}, {48, 80, 94},
+ {40, 60, 75}, {28, 48, 56}, {40, 44, 37}, {24, 32, 28},
+ {48, 56, 37}, {80, 35, 28}, {76, 35, 37}, {68, 35, 37},
+ {68, 39, 28}, {68, 39, 37}, {68, 72, 56}, {56, 68, 47},
+ {68, 72, 56}, {48, 72, 47}, {60, 72, 66}, {77, 97, 85},
+ {84, 113, 103}, {100, 133, 132}, {133, 162, 160}, {173, 190, 188},
+ {189, 210, 198}, {193, 218, 207}, {193, 218, 216}, {193, 218, 216},
+ {173, 210, 225}, {132, 201, 225}, {104, 161, 188}, {84, 141, 169},
+ {80, 121, 141}, {76, 109, 122}, {93, 104, 84}, {105, 92, 66},
+ {129, 80, 56}, {145, 95, 28}, {153, 95, 28}, {174, 111, 66},
+ {226, 167, 84}, {234, 155, 65}, {194, 119, 37}, {178, 63, 9},
+ {170, 33, 9}, {129, 38, 18}, {125, 63, 37}, {153, 76, 28},
+ {170, 107, 56}, {246, 188, 74}, {246, 196, 103}, {250, 217, 159},
+ {226, 213, 188}, {230, 234, 216}, {230, 237, 225}, {230, 246, 235},
+ {218, 246, 244}, {218, 242, 235}, {209, 238, 225}, {210, 230, 225},
+ {206, 222, 226}, {197, 218, 216}, {189, 218, 207}, {185, 210, 207},
+ {153, 169, 188}, {105, 150, 160}, {80, 125, 141}, {68, 101, 122},
+ {56, 93, 113}, {52, 97, 122}, {68, 105, 113}, {92, 129, 132},
+ {129, 150, 141}, {185, 177, 141}, {242, 196, 122}, {202, 165, 132},
+ {182, 115, 75}, {170, 59, 28}, {153, 59, 18}, {125, 42, 28},
+ {113, 43, 28}, {92, 35, 37}, {80, 39, 37}, {64, 64, 66},
+ {52, 76, 85}, {56, 89, 84}, {56, 81, 85}, {64, 85, 94},
+ {73, 97, 94}, {85, 109, 94}, {100, 109, 94}, {121, 145, 113},
+ {129, 166, 141}, {146, 186, 169}, {177, 210, 197}, {189, 218, 207},
+ {197, 218, 226}, {197, 222, 226}, {206, 222, 226}, {217, 226, 225},
+ {222, 234, 225}, {218, 234, 226}, {214, 234, 216}, {201, 234, 216},
+ {201, 230, 216}, {197, 230, 225}, {197, 230, 225}, {193, 230, 225},
+ {193, 230, 226}, {193, 230, 226}, {189, 242, 235}, {201, 242, 244},
+ {213, 237, 244}, {205, 234, 235}, {201, 238, 235}, {201, 234, 235},
+ {193, 230, 235}, {193, 230, 226}, {193, 226, 226}, {189, 226, 225},
+ {181, 230, 225}, {161, 198, 207}, {108, 166, 178}, {92, 153, 160},
+ {73, 126, 150}, {88, 137, 150}, {89, 142, 151}, {125, 162, 151},
+ {149, 182, 170}, {181, 202, 188}, {189, 218, 207}, {194, 226, 216},
+ {201, 230, 225}, {205, 230, 225}, {213, 234, 225}, {214, 234, 225},
+ {214, 230, 216}, {206, 230, 207}, {194, 218, 198}, {166, 190, 170},
+ {145, 149, 132}, {121, 109, 84}, {117, 104, 75}, {141, 113, 103},
+ {166, 141, 132}, {182, 174, 160}, {206, 214, 179}, {218, 238, 207},
+ {246, 249, 225}, {250, 254, 235}, {254, 254, 244}, {254, 254, 254},
+ {254, 254, 254}, {250, 254, 254}, {254, 254, 254}, {254, 254, 244},
+ {250, 250, 244}, {246, 250, 244}, {242, 250, 244}, {230, 245, 235},
+ {226, 230, 225}, {214, 222, 216}, {226, 238, 216}, {210, 222, 216},
+ {206, 222, 216}, {201, 230, 226}, {205, 226, 216}, {206, 222, 207},
+ {198, 222, 207}, {202, 222, 207}, {201, 226, 216}, {197, 222, 216},
+ {198, 222, 207}, {198, 222, 198}, {202, 210, 179}, {174, 198, 169},
+ {153, 185, 188}, {157, 190, 179}, {181, 202, 188}, {198, 210, 198},
+ {202, 210, 198}, {193, 218, 207}, {198, 226, 207}, {206, 230, 207},
+ {206, 230, 207}, {202, 230, 216}, {202, 226, 216}, {197, 218, 216},
+ {193, 218, 226}, {185, 221, 235}, {185, 230, 244}, {181, 234, 244},
+ {173, 234, 244}, {173, 230, 244}, {116, 207, 235}, {76, 194, 216},
+ {72, 185, 206}, {60, 170, 207}, {76, 153, 169}, {68, 121, 122},
+ {76, 105, 94}, {60, 85, 75}, {73, 81, 75}, {77, 77, 56},
+ {76, 68, 66}, {73, 89, 85}, {97, 80, 65}, {80, 64, 47},
+ {96, 43, 37}, {125, 38, 28}, {125, 42, 28}, {100, 47, 47},
+ {92, 72, 75}, {68, 81, 85}, {68, 85, 94}, {84, 93, 103},
+ {72, 113, 122}, {100, 149, 141}, {133, 170, 169}, {185, 202, 207},
+ {206, 222, 216}, {226, 234, 225}, {238, 250, 244}, {246, 254, 244},
+ {250, 254, 244}, {250, 250, 244}, {250, 254, 244}, {250, 254, 244},
+ {254, 254, 244}, {250, 254, 235}, {250, 254, 235}, {250, 254, 225},
+ {254, 254, 216}, {250, 249, 216}, {210, 222, 207}, {218, 181, 169},
+ {214, 185, 150}, {182, 116, 103}, {129, 120, 56}, {129, 91, 56},
+ {125, 88, 65}, {105, 88, 84}, {129, 129, 113}, {149, 161, 160},
+ },
+ /* burning-wood */
+ {
+ {80, 35, 28}, {92, 39, 28}, {97, 39, 28}, {96, 39, 37},
+ {97, 39, 28}, {80, 43, 37}, {68, 43, 37}, {84, 39, 37},
+ {76, 43, 47}, {72, 48, 56}, {60, 43, 47}, {44, 44, 47},
+ {36, 32, 37}, {31, 28, 37}, {27, 28, 37}, {28, 32, 37},
+ {40, 28, 37}, {48, 32, 37}, {52, 31, 37}, {56, 27, 28},
+ {68, 35, 28}, {84, 35, 28}, {96, 30, 28}, {104, 34, 18},
+ {105, 30, 18}, {97, 34, 18}, {88, 34, 18}, {76, 35, 28},
+ {68, 39, 28}, {64, 43, 18}, {60, 48, 37}, {52, 52, 37},
+ {48, 44, 37}, {32, 40, 28}, {32, 32, 28}, {27, 32, 28},
+ {28, 28, 28}, {28, 28, 18}, {31, 28, 18}, {31, 28, 18},
+ {35, 28, 18}, {36, 32, 18}, {52, 35, 18}, {56, 31, 18},
+ {76, 31, 18}, {96, 30, 18}, {113, 30, 18}, {133, 26, 18},
+ {145, 26, 18}, {154, 22, 19}, {162, 22, 18}, {174, 25, 0},
+ {178, 25, 0}, {178, 46, 9}, {190, 87, 18}, {214, 105, 18},
+ {219, 97, 0}, {198, 87, 18}, {178, 72, 9}, {162, 37, 0},
+ {158, 22, 0}, {158, 22, 9}, {154, 22, 0}, {146, 22, 0},
+ {146, 26, 9}, {137, 26, 9}, {117, 30, 9}, {105, 30, 18},
+ {92, 23, 18}, {88, 23, 28}, {92, 39, 28}, {88, 39, 28},
+ {80, 52, 47}, {72, 56, 56}, {84, 68, 65}, {73, 73, 75},
+ {64, 60, 56}, {52, 56, 56}, {36, 52, 47}, {32, 36, 37},
+ {36, 32, 28}, {40, 31, 28}, {52, 31, 28}, {48, 31, 18},
+ {60, 27, 18}, {76, 31, 18}, {88, 31, 18}, {100, 30, 9},
+ {113, 34, 9}, {121, 30, 9}, {133, 30, 18}, {125, 39, 18},
+ {121, 31, 28}, {121, 38, 28}, {109, 35, 37}, {109, 34, 28},
+ {105, 31, 28}, {101, 34, 28}, {84, 35, 37}, {72, 31, 37},
+ {68, 27, 37}, {56, 27, 28}, {48, 23, 28}, {40, 24, 28},
+ {36, 27, 28}, {35, 28, 28}, {35, 28, 28}, {36, 32, 18},
+ {48, 31, 18}, {80, 43, 18}, {101, 64, 28}, {141, 76, 28},
+ {182, 95, 28}, {214, 123, 37}, {234, 163, 56}, {234, 176, 84},
+ {234, 176, 75}, {222, 157, 47}, {222, 131, 47}, {210, 110, 28},
+ {222, 79, 37}, {194, 38, 18}, {202, 14, 9}, {206, 26, 19},
+ {186, 30, 9}, {182, 30, 9}, {186, 72, 9}, {174, 83, 9},
+ {161, 79, 18}, {141, 59, 9}, {129, 64, 28}, {121, 76, 47},
+ {121, 63, 56}, {133, 46, 37}, {125, 47, 28}, {129, 43, 28},
+ {121, 47, 28}, {113, 47, 28}, {125, 47, 18}, {121, 47, 28},
+ {109, 47, 18}, {100, 43, 28}, {92, 39, 28}, {76, 31, 28},
+ {56, 31, 28}, {48, 31, 37}, {40, 27, 28}, {35, 28, 28},
+ {32, 28, 28}, {36, 31, 28}, {44, 36, 28}, {56, 35, 28},
+ {68, 35, 28}, {76, 35, 37}, {80, 47, 28}, {88, 39, 28},
+ {88, 43, 37}, {101, 43, 28}, {105, 39, 28}, {109, 43, 28},
+ {109, 60, 46}, {97, 72, 56}, {105, 88, 75}, {113, 104, 84},
+ {133, 100, 94}, {165, 91, 56}, {165, 87, 47}, {162, 71, 37},
+ {166, 59, 28}, {162, 63, 18}, {149, 64, 18}, {150, 59, 18},
+ {146, 60, 9}, {141, 33, 0}, {129, 59, 0}, {121, 59, 18},
+ {133, 64, 18}, {149, 64, 28}, {154, 80, 37}, {174, 83, 18},
+ {182, 91, 18}, {198, 96, 18}, {202, 110, 28}, {202, 106, 37},
+ {202, 106, 37}, {198, 95, 47}, {178, 86, 28}, {133, 68, 28},
+ {113, 59, 28}, {92, 43, 18}, {76, 35, 28}, {60, 35, 28},
+ {56, 31, 28}, {64, 31, 28}, {68, 27, 28}, {72, 31, 28},
+ {76, 31, 28}, {76, 35, 28}, {80, 39, 18}, {84, 31, 28},
+ {85, 35, 18}, {81, 31, 18}, {80, 31, 18}, {76, 27, 28},
+ {64, 27, 28}, {56, 27, 28}, {44, 27, 28}, {36, 28, 28},
+ {31, 28, 28}, {27, 24, 28}, {23, 24, 28}, {27, 24, 28},
+ {31, 28, 28}, {32, 28, 28}, {35, 28, 28}, {44, 31, 28},
+ {68, 31, 28}, {88, 43, 28}, {109, 60, 28}, {133, 64, 28},
+ {166, 91, 28}, {210, 140, 66}, {214, 210, 188}, {230, 180, 113},
+ {238, 176, 93}, {219, 173, 85}, {222, 164, 66}, {202, 131, 47},
+ {174, 103, 47}, {141, 100, 47}, {121, 88, 66}, {80, 60, 47},
+ {60, 43, 37}, {48, 27, 37}, {44, 27, 37}, {36, 28, 28},
+ {32, 28, 28}, {31, 28, 28}, {27, 28, 28}, {27, 28, 28},
+ {24, 28, 28}, {27, 28, 28}, {27, 28, 28}, {31, 28, 28},
+ {32, 28, 28}, {32, 36, 37}, {44, 40, 37}, {56, 31, 37},
+ {68, 35, 56}, {89, 60, 47}, {117, 64, 37}, {121, 56, 37},
+ {125, 56, 47}, {109, 47, 37}, {105, 47, 37}, {92, 27, 37},
+ },
+ /* aquatic-garden */
+ {
+ {146, 80, 9}, {133, 92, 28}, {133, 108, 56}, {153, 112, 75},
+ {141, 125, 84}, {133, 113, 65}, {105, 76, 56}, {80, 72, 37},
+ {60, 68, 37}, {52, 60, 47}, {40, 44, 47}, {36, 36, 37},
+ {32, 28, 37}, {32, 28, 28}, {32, 24, 28}, {27, 24, 37},
+ {27, 28, 37}, {28, 32, 37}, {40, 44, 47}, {60, 64, 56},
+ {77, 89, 66}, {92, 105, 66}, {117, 133, 85}, {125, 158, 103},
+ {166, 177, 103}, {149, 178, 122}, {157, 157, 113}, {125, 133, 103},
+ {109, 96, 94}, {81, 93, 75}, {64, 85, 75}, {52, 68, 56},
+ {40, 56, 47}, {40, 44, 37}, {36, 36, 28}, {32, 32, 28},
+ {28, 28, 28}, {24, 28, 28}, {23, 28, 28}, {28, 32, 28},
+ {36, 36, 28}, {40, 40, 28}, {56, 39, 28}, {72, 48, 28},
+ {84, 51, 28}, {80, 56, 28}, {84, 64, 28}, {72, 64, 28},
+ {68, 39, 28}, {56, 43, 28}, {36, 32, 18}, {28, 28, 18},
+ {24, 28, 28}, {23, 24, 28}, {20, 20, 28}, {23, 20, 28},
+ {19, 20, 28}, {19, 20, 28}, {24, 24, 28}, {31, 24, 28},
+ {31, 28, 28}, {35, 28, 28}, {48, 35, 28}, {64, 43, 28},
+ {72, 52, 37}, {76, 56, 37}, {72, 52, 37}, {56, 39, 28},
+ {48, 31, 28}, {36, 28, 28}, {32, 28, 28}, {28, 28, 28},
+ {28, 32, 28}, {28, 32, 28}, {28, 32, 28}, {24, 32, 28},
+ {24, 32, 28}, {24, 32, 28}, {24, 32, 28}, {24, 32, 28},
+ {24, 32, 28}, {28, 36, 28}, {36, 40, 28}, {44, 48, 37},
+ {64, 64, 47}, {81, 97, 66}, {96, 121, 103}, {88, 146, 150},
+ {96, 158, 169}, {100, 158, 179}, {100, 150, 160}, {100, 129, 141},
+ {85, 109, 113}, {68, 93, 85}, {44, 60, 66}, {36, 44, 47},
+ {24, 40, 37}, {24, 36, 37}, {23, 32, 28}, {20, 24, 18},
+ {24, 24, 18}, {23, 20, 9}, {24, 32, 18}, {36, 44, 18},
+ {44, 48, 37}, {52, 60, 47}, {56, 81, 75}, {64, 97, 85},
+ {68, 101, 94}, {85, 113, 85}, {96, 117, 94}, {84, 84, 94},
+ {68, 80, 85}, {64, 73, 75}, {56, 56, 56}, {52, 56, 47},
+ {40, 44, 37}, {36, 36, 37}, {35, 32, 28}, {28, 24, 18},
+ {32, 28, 28}, {32, 32, 18}, {52, 39, 18}, {60, 35, 18},
+ {84, 47, 37}, {81, 55, 37}, {80, 72, 56}, {64, 76, 66},
+ {52, 64, 75}, {48, 73, 85}, {52, 81, 94}, {60, 89, 122},
+ {64, 101, 122}, {88, 105, 122}, {89, 117, 113}, {93, 117, 113},
+ {100, 117, 113}, {100, 125, 103}, {104, 125, 103}, {109, 129, 85},
+ {96, 105, 75}, {93, 101, 66}, {89, 93, 66}, {105, 105, 75},
+ {117, 125, 56}, {121, 121, 75}, {117, 100, 56}, {109, 100, 47},
+ {89, 93, 56}, {84, 93, 66}, {73, 85, 66}, {56, 64, 47},
+ {44, 48, 47}, {40, 32, 37}, {44, 31, 37}, {40, 44, 37},
+ {56, 52, 28}, {68, 68, 37}, {64, 56, 37}, {68, 56, 37},
+ {64, 48, 47}, {48, 52, 47}, {40, 40, 47}, {32, 36, 37},
+ {32, 36, 28}, {32, 36, 28}, {36, 40, 37}, {44, 52, 47},
+ {52, 60, 66}, {64, 85, 85}, {84, 109, 103}, {100, 121, 122},
+ {104, 145, 151}, {116, 158, 141}, {112, 150, 151}, {108, 129, 132},
+ {113, 105, 103}, {89, 101, 75}, {69, 93, 56}, {68, 76, 37},
+ {68, 68, 28}, {52, 48, 28}, {40, 40, 28}, {36, 36, 28},
+ {32, 36, 28}, {32, 32, 28}, {32, 32, 28}, {32, 32, 28},
+ {32, 36, 28}, {32, 40, 37}, {28, 44, 47}, {36, 68, 66},
+ {48, 85, 94}, {44, 109, 160}, {88, 150, 169}, {113, 178, 159},
+ {116, 170, 169}, {133, 173, 179}, {92, 154, 197}, {36, 93, 170},
+ {40, 93, 160}, {36, 73, 103}, {48, 56, 75}, {44, 48, 56},
+ {36, 36, 37}, {32, 32, 28}, {27, 28, 18}, {24, 28, 18},
+ {20, 32, 18}, {24, 28, 18}, {19, 28, 18}, {19, 24, 28},
+ {20, 24, 28}, {20, 28, 28}, {23, 28, 28}, {20, 32, 28},
+ {20, 28, 28}, {24, 28, 28}, {28, 28, 28}, {28, 28, 28},
+ {28, 32, 28}, {32, 32, 28}, {36, 32, 37}, {40, 36, 47},
+ {52, 48, 66}, {60, 77, 94}, {92, 121, 132}, {125, 166, 160},
+ {145, 177, 179}, {161, 190, 198}, {169, 198, 188}, {174, 186, 170},
+ {166, 186, 170}, {145, 170, 132}, {141, 162, 85}, {129, 133, 85},
+ {137, 99, 47}, {137, 72, 9}, {125, 46, 0}, {109, 47, 18},
+ {85, 51, 28}, {105, 80, 28}, {93, 64, 47}, {104, 72, 75},
+ {125, 121, 94}, {129, 137, 113}, {108, 150, 122}, {96, 146, 131},
+ {88, 137, 122}, {68, 105, 141}, {52, 72, 94}, {28, 52, 56},
+ {23, 32, 47}, {31, 28, 47}, {32, 40, 47}, {40, 52, 56},
+ },
+ /* no-name */
+ {
+ {24, 24, 9}, {24, 28, 9}, {24, 28, 18}, {28, 28, 18},
+ {28, 28, 28}, {27, 28, 28}, {24, 28, 28}, {24, 28, 28},
+ {20, 28, 28}, {19, 28, 18}, {24, 28, 9}, {23, 24, 9},
+ {20, 24, 18}, {19, 20, 18}, {20, 20, 18}, {19, 20, 28},
+ {15, 24, 28}, {11, 20, 18}, {12, 16, 18}, {12, 12, 9},
+ {16, 20, 18}, {20, 24, 28}, {24, 24, 28}, {27, 32, 28},
+ {32, 36, 28}, {32, 44, 28}, {32, 52, 37}, {48, 60, 37},
+ {76, 68, 47}, {101, 80, 37}, {150, 96, 28}, {194, 102, 28},
+ {210, 106, 37}, {214, 110, 37}, {214, 127, 37}, {210, 127, 37},
+ {206, 132, 47}, {214, 156, 47}, {226, 168, 47}, {246, 191, 46},
+ {246, 208, 46}, {254, 220, 56}, {254, 207, 56}, {250, 191, 46},
+ {250, 187, 46}, {238, 179, 46}, {238, 159, 46}, {234, 148, 46},
+ {222, 136, 37}, {218, 148, 37}, {222, 179, 47}, {230, 200, 37},
+ {227, 230, 46}, {246, 225, 46}, {226, 218, 47}, {215, 192, 56},
+ {165, 149, 66}, {101, 109, 47}, {72, 72, 28}, {56, 60, 28},
+ {40, 44, 28}, {32, 32, 18}, {28, 28, 18}, {24, 32, 18},
+ {24, 32, 18}, {24, 32, 18}, {32, 32, 28}, {32, 36, 28},
+ {40, 44, 37}, {52, 48, 47}, {64, 48, 56}, {76, 64, 47},
+ {89, 64, 47}, {109, 80, 56}, {154, 96, 56}, {190, 107, 47},
+ {186, 106, 47}, {174, 123, 37}, {149, 111, 37}, {109, 84, 46},
+ {85, 80, 47}, {81, 76, 56}, {64, 77, 66}, {56, 64, 66},
+ {48, 52, 56}, {32, 36, 47}, {28, 36, 37}, {28, 32, 28},
+ {28, 32, 28}, {35, 32, 18}, {44, 40, 18}, {72, 48, 28},
+ {101, 55, 37}, {133, 72, 47}, {186, 102, 47}, {186, 91, 56},
+ {149, 84, 56}, {109, 80, 56}, {137, 99, 47}, {162, 108, 47},
+ {178, 136, 56}, {186, 148, 56}, {210, 189, 75}, {234, 209, 103},
+ {234, 205, 103}, {234, 197, 103}, {238, 180, 65}, {210, 148, 56},
+ {226, 115, 65}, {222, 115, 75}, {198, 110, 66}, {182, 110, 47},
+ {125, 80, 37}, {105, 59, 37}, {68, 52, 28}, {44, 40, 28},
+ {32, 32, 18}, {28, 28, 18}, {24, 28, 18}, {20, 24, 18},
+ {23, 24, 18}, {28, 32, 28}, {40, 40, 37}, {52, 48, 37},
+ {72, 60, 37}, {96, 72, 37}, {145, 99, 56}, {194, 102, 56},
+ {214, 123, 85}, {230, 180, 103}, {254, 245, 131}, {254, 254, 140},
+ {250, 254, 197}, {234, 234, 188}, {190, 173, 151}, {129, 146, 141},
+ {105, 137, 84}, {85, 101, 47}, {93, 77, 46}, {109, 60, 37},
+ {121, 76, 37}, {149, 108, 47}, {182, 119, 47}, {178, 140, 56},
+ {186, 144, 47}, {194, 136, 47}, {194, 123, 37}, {178, 115, 37},
+ {117, 88, 47}, {84, 64, 47}, {60, 56, 47}, {52, 60, 56},
+ {52, 64, 47}, {60, 68, 56}, {73, 81, 75}, {116, 141, 141},
+ {182, 186, 151}, {218, 222, 169}, {206, 206, 179}, {150, 166, 151},
+ {84, 109, 103}, {60, 73, 85}, {56, 73, 75}, {73, 77, 75},
+ {85, 93, 65}, {97, 113, 66}, {129, 141, 75}, {166, 157, 113},
+ {182, 169, 113}, {198, 156, 103}, {186, 132, 66}, {174, 119, 47},
+ {121, 84, 37}, {81, 60, 28}, {64, 56, 28}, {48, 52, 18},
+ {44, 44, 28}, {40, 40, 37}, {32, 36, 37}, {32, 36, 37},
+ {40, 40, 37}, {48, 48, 47}, {48, 73, 56}, {73, 117, 46},
+ {113, 125, 47}, {150, 170, 94}, {186, 181, 103}, {215, 206, 94},
+ {246, 233, 74}, {250, 249, 84}, {254, 233, 84}, {254, 241, 74},
+ {254, 229, 56}, {250, 229, 56}, {254, 225, 65}, {250, 233, 84},
+ {238, 241, 112}, {246, 217, 122}, {206, 181, 113}, {198, 123, 85},
+ {202, 119, 66}, {194, 127, 56}, {198, 131, 56}, {194, 136, 66},
+ {222, 185, 84}, {234, 230, 112}, {254, 249, 112}, {254, 254, 131},
+ {222, 226, 150}, {214, 193, 132}, {174, 157, 113}, {105, 113, 56},
+ {77, 84, 56}, {52, 68, 47}, {44, 52, 37}, {36, 40, 28},
+ {32, 36, 28}, {36, 40, 28}, {36, 36, 28}, {40, 32, 37},
+ {48, 39, 37}, {64, 39, 28}, {80, 52, 37}, {105, 55, 47},
+ {165, 84, 56}, {194, 98, 47}, {198, 106, 47}, {202, 131, 56},
+ {238, 172, 84}, {242, 196, 103}, {250, 213, 103}, {246, 221, 74},
+ {246, 200, 56}, {210, 180, 56}, {170, 149, 56}, {141, 111, 47},
+ {93, 92, 47}, {84, 72, 47}, {64, 68, 28}, {56, 64, 28},
+ {44, 52, 28}, {36, 44, 28}, {36, 36, 28}, {40, 40, 28},
+ {40, 40, 28}, {56, 43, 18}, {60, 43, 18}, {72, 52, 28},
+ {96, 64, 46}, {117, 84, 65}, {170, 123, 66}, {170, 132, 66},
+ {121, 72, 56}, {100, 55, 46}, {56, 48, 37}, {36, 36, 37},
+ },
+ /* fall-quilt */
+ {
+ {24, 40, 37}, {56, 52, 37}, {93, 68, 37}, {137, 68, 18},
+ {174, 76, 18}, {190, 102, 9}, {190, 94, 18}, {178, 75, 18},
+ {162, 64, 18}, {129, 60, 18}, {105, 76, 18}, {77, 68, 28},
+ {64, 60, 37}, {40, 48, 37}, {24, 40, 37}, {20, 32, 37},
+ {19, 32, 37}, {15, 32, 37}, {15, 32, 37}, {20, 36, 47},
+ {24, 40, 56}, {24, 40, 56}, {28, 40, 56}, {28, 44, 56},
+ {28, 44, 56}, {32, 44, 47}, {36, 48, 37}, {48, 52, 37},
+ {76, 68, 37}, {81, 73, 37}, {109, 80, 37}, {137, 80, 28},
+ {170, 87, 37}, {194, 98, 18}, {198, 102, 18}, {210, 110, 18},
+ {214, 118, 9}, {198, 119, 18}, {170, 128, 28}, {146, 99, 28},
+ {117, 80, 28}, {85, 72, 37}, {68, 52, 37}, {40, 40, 37},
+ {24, 36, 28}, {19, 32, 28}, {24, 24, 28}, {32, 28, 28},
+ {40, 31, 18}, {56, 35, 18}, {76, 52, 28}, {105, 84, 37},
+ {133, 96, 37}, {194, 144, 56}, {214, 197, 141}, {214, 202, 179},
+ {234, 230, 216}, {250, 233, 225}, {250, 233, 216}, {222, 193, 169},
+ {150, 145, 94}, {109, 96, 56}, {77, 68, 47}, {40, 48, 56},
+ {36, 44, 56}, {36, 40, 56}, {32, 36, 56}, {28, 40, 56},
+ {24, 36, 47}, {20, 32, 47}, {20, 32, 37}, {24, 36, 37},
+ {24, 36, 37}, {36, 36, 37}, {36, 36, 28}, {40, 32, 28},
+ {40, 31, 28}, {48, 31, 28}, {68, 47, 28}, {109, 39, 28},
+ {129, 46, 18}, {133, 47, 9}, {162, 59, 0}, {198, 114, 9},
+ {214, 145, 28}, {226, 171, 47}, {234, 183, 47}, {230, 192, 65},
+ {222, 165, 65}, {194, 144, 47}, {145, 112, 47}, {113, 88, 47},
+ {93, 72, 46}, {60, 60, 47}, {36, 48, 47}, {32, 44, 47},
+ {32, 36, 47}, {36, 36, 37}, {36, 40, 37}, {40, 40, 37},
+ {40, 44, 28}, {48, 52, 28}, {76, 68, 37}, {93, 76, 37},
+ {113, 88, 28}, {170, 123, 28}, {194, 122, 28}, {210, 132, 18},
+ {214, 141, 18}, {210, 127, 18}, {182, 119, 28}, {153, 76, 28},
+ {137, 64, 18}, {121, 55, 9}, {93, 51, 18}, {80, 64, 37},
+ {97, 76, 37}, {113, 84, 37}, {166, 115, 47}, {214, 173, 75},
+ {230, 197, 75}, {242, 200, 75}, {218, 173, 66}, {182, 132, 47},
+ {113, 88, 46}, {77, 72, 37}, {56, 48, 37}, {44, 40, 37},
+ {35, 24, 28}, {24, 12, 18}, {23, 12, 18}, {27, 20, 18},
+ {52, 31, 18}, {93, 51, 18}, {149, 92, 28}, {194, 140, 37},
+ {226, 180, 75}, {242, 213, 84}, {250, 213, 112}, {254, 212, 121},
+ {238, 222, 141}, {250, 233, 197}, {254, 241, 216}, {250, 237, 225},
+ {254, 233, 197}, {250, 229, 169}, {254, 229, 150}, {254, 229, 140},
+ {250, 221, 131}, {246, 217, 112}, {250, 209, 84}, {250, 208, 74},
+ {238, 192, 56}, {214, 131, 28}, {186, 72, 9}, {146, 38, 0},
+ {109, 30, 9}, {76, 15, 9}, {48, 16, 9}, {27, 8, 9},
+ {23, 24, 28}, {24, 32, 37}, {24, 36, 37}, {24, 40, 47},
+ {32, 44, 47}, {40, 44, 56}, {56, 52, 56}, {89, 64, 56},
+ {125, 72, 37}, {158, 60, 37}, {162, 63, 28}, {154, 55, 18},
+ {141, 38, 18}, {133, 26, 9}, {113, 22, 0}, {89, 11, 9},
+ {73, 11, 0}, {52, 11, 9}, {36, 20, 0}, {40, 16, 9},
+ {27, 20, 18}, {23, 20, 28}, {24, 24, 37}, {19, 28, 47},
+ {15, 28, 47}, {16, 32, 47}, {12, 28, 37}, {16, 28, 37},
+ {19, 28, 37}, {16, 28, 37}, {24, 36, 28}, {40, 35, 18},
+ {44, 35, 18}, {64, 35, 18}, {97, 38, 0}, {121, 51, 9},
+ {125, 42, 9}, {121, 38, 9}, {93, 31, 18}, {52, 40, 28},
+ {36, 40, 47}, {32, 44, 47}, {32, 48, 47}, {32, 48, 47},
+ {28, 44, 47}, {24, 36, 47}, {19, 28, 37}, {19, 20, 28},
+ {20, 12, 18}, {24, 4, 9}, {16, 8, 0}, {16, 12, 0},
+ {20, 12, 9}, {20, 20, 18}, {16, 28, 28}, {16, 28, 37},
+ {15, 28, 37}, {12, 28, 28}, {23, 28, 18}, {44, 27, 18},
+ {56, 19, 18}, {80, 19, 28}, {113, 30, 18}, {129, 51, 9},
+ {145, 79, 28}, {186, 127, 28}, {226, 163, 47}, {234, 192, 65},
+ {242, 200, 75}, {226, 188, 84}, {190, 144, 47}, {125, 96, 47},
+ {97, 80, 56}, {60, 60, 47}, {36, 48, 47}, {24, 40, 47},
+ {20, 32, 47}, {16, 28, 47}, {12, 24, 37}, {12, 20, 37},
+ {12, 20, 47}, {15, 24, 47}, {15, 28, 56}, {20, 28, 56},
+ {20, 36, 56}, {28, 44, 56}, {36, 48, 47}, {52, 48, 47},
+ {77, 73, 37}, {109, 88, 56}, {125, 96, 47}, {186, 127, 37},
+ {218, 163, 47}, {230, 166, 37}, {230, 144, 28}, {198, 120, 37},
+ },
+ /* night-blue-sky */
+ {
+ {3, 12, 66}, {4, 12, 66}, {7, 12, 66}, {7, 8, 56},
+ {4, 8, 47}, {3, 8, 37}, {0, 4, 18}, {0, 4, 9},
+ {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 4, 0},
+ {0, 4, 9}, {0, 4, 18}, {3, 4, 18}, {4, 4, 18},
+ {4, 4, 18}, {4, 4, 9}, {4, 4, 9}, {0, 4, 9},
+ {0, 0, 0}, {0, 0, 0}, {0, 4, 0}, {0, 4, 0},
+ {4, 4, 9}, {4, 4, 18}, {4, 4, 18}, {4, 8, 18},
+ {3, 8, 18}, {0, 8, 18}, {0, 4, 18}, {0, 4, 18},
+ {0, 4, 18}, {0, 4, 28}, {0, 8, 37}, {0, 12, 47},
+ {3, 12, 56}, {3, 16, 66}, {3, 16, 75}, {3, 12, 75},
+ {3, 12, 75}, {3, 16, 75}, {3, 16, 75}, {4, 16, 75},
+ {4, 12, 75}, {3, 12, 75}, {3, 12, 75}, {3, 12, 75},
+ {0, 12, 66}, {3, 8, 56}, {3, 8, 56}, {3, 8, 56},
+ {3, 4, 47}, {4, 4, 47}, {4, 4, 56}, {4, 8, 56},
+ {4, 8, 56}, {3, 8, 56}, {3, 8, 47}, {0, 8, 37},
+ {0, 8, 28}, {0, 4, 28}, {0, 4, 18}, {0, 4, 18},
+ {4, 4, 18}, {4, 4, 28}, {3, 4, 28}, {0, 4, 37},
+ {0, 4, 37}, {0, 4, 37}, {0, 4, 37}, {0, 4, 28},
+ {0, 0, 18}, {0, 0, 9}, {0, 0, 9}, {0, 4, 9},
+ {0, 4, 9}, {0, 4, 9}, {0, 4, 9}, {0, 4, 0},
+ {0, 4, 0}, {0, 4, 9}, {0, 4, 9}, {0, 8, 18},
+ {0, 8, 28}, {0, 4, 28}, {0, 4, 28}, {3, 4, 28},
+ {4, 4, 18}, {4, 4, 9}, {4, 4, 9}, {4, 4, 18},
+ {4, 4, 28}, {7, 8, 37}, {4, 12, 47}, {8, 12, 56},
+ {7, 12, 66}, {7, 16, 75}, {7, 16, 75}, {11, 16, 75},
+ {11, 16, 75}, {11, 16, 75}, {7, 16, 66}, {7, 16, 66},
+ {7, 16, 66}, {4, 16, 66}, {3, 16, 66}, {4, 12, 56},
+ {4, 8, 47}, {4, 4, 37}, {4, 4, 28}, {4, 0, 18},
+ {4, 0, 18}, {3, 0, 18}, {0, 0, 9}, {0, 0, 9},
+ {0, 4, 9}, {0, 4, 9}, {0, 4, 9}, {0, 0, 9},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 9},
+ {4, 4, 9}, {4, 4, 9}, {4, 4, 9}, {3, 4, 9},
+ {0, 4, 9}, {0, 4, 9}, {0, 0, 9}, {0, 0, 9},
+ {0, 0, 9}, {0, 4, 9}, {0, 4, 9}, {4, 4, 9},
+ {4, 4, 18}, {4, 8, 28}, {7, 8, 37}, {4, 8, 47},
+ {3, 8, 47}, {3, 8, 47}, {0, 4, 47}, {0, 4, 37},
+ {0, 4, 37}, {0, 4, 37}, {0, 4, 37}, {0, 4, 47},
+ {0, 4, 56}, {3, 8, 56}, {4, 12, 56}, {7, 20, 66},
+ {7, 24, 66}, {3, 20, 66}, {0, 16, 56}, {0, 8, 56},
+ {0, 4, 47}, {0, 4, 28}, {0, 4, 18}, {4, 4, 9},
+ {4, 4, 9}, {0, 4, 18}, {0, 4, 28}, {0, 8, 37},
+ {0, 8, 56}, {3, 12, 66}, {7, 12, 75}, {8, 16, 75},
+ {8, 16, 85}, {7, 20, 85}, {8, 20, 75}, {7, 16, 75},
+ {7, 12, 66}, {4, 12, 56}, {3, 8, 47}, {3, 8, 37},
+ {0, 8, 37}, {0, 4, 28}, {0, 4, 28}, {0, 4, 28},
+ {0, 4, 28}, {0, 4, 28}, {0, 4, 28}, {0, 8, 28},
+ {0, 8, 18}, {0, 8, 9}, {4, 8, 9}, {4, 4, 9},
+ {4, 4, 18}, {4, 8, 37}, {4, 12, 56}, {3, 12, 66},
+ {3, 16, 66}, {3, 16, 56}, {4, 16, 56}, {8, 12, 56},
+ {7, 8, 56}, {7, 8, 47}, {4, 4, 47}, {4, 4, 47},
+ {4, 4, 47}, {3, 8, 56}, {3, 12, 56}, {3, 12, 66},
+ {3, 12, 66}, {3, 12, 75}, {3, 12, 75}, {4, 12, 75},
+ {4, 12, 75}, {7, 12, 75}, {7, 12, 75}, {7, 12, 66},
+ {4, 8, 56}, {3, 4, 47}, {0, 0, 28}, {0, 0, 18},
+ {0, 0, 9}, {0, 0, 9}, {0, 0, 9}, {4, 0, 9},
+ {4, 0, 9}, {4, 0, 9}, {4, 0, 9}, {0, 0, 0},
+ {0, 0, 0}, {0, 4, 0}, {0, 4, 0}, {0, 4, 9},
+ {0, 4, 9}, {4, 4, 9}, {4, 4, 9}, {4, 4, 9},
+ {4, 4, 18}, {4, 8, 28}, {4, 8, 37}, {4, 8, 47},
+ {4, 8, 56}, {4, 12, 56}, {3, 12, 47}, {0, 16, 47},
+ {0, 8, 37}, {0, 8, 28}, {0, 4, 18}, {0, 4, 9},
+ {0, 0, 9}, {0, 0, 9}, {0, 4, 9}, {3, 4, 18},
+ {4, 8, 28}, {4, 12, 37}, {4, 16, 47}, {4, 16, 47},
+ {4, 12, 37}, {4, 8, 28}, {4, 4, 18}, {4, 4, 9},
+ },
+ /* shadow-iris */
+ {
+ {36, 20, 18}, {32, 20, 18}, {31, 20, 18}, {31, 20, 18},
+ {32, 24, 18}, {35, 28, 18}, {36, 32, 18}, {28, 24, 18},
+ {27, 20, 18}, {24, 16, 18}, {23, 12, 18}, {23, 12, 18},
+ {23, 16, 18}, {24, 20, 18}, {24, 20, 18}, {23, 20, 18},
+ {19, 20, 18}, {16, 20, 18}, {19, 16, 9}, {20, 12, 9},
+ {23, 12, 9}, {24, 16, 9}, {24, 20, 9}, {24, 20, 9},
+ {20, 20, 9}, {20, 16, 9}, {20, 8, 9}, {23, 12, 9},
+ {24, 12, 9}, {28, 16, 9}, {28, 20, 9}, {28, 20, 9},
+ {27, 20, 9}, {24, 20, 18}, {23, 16, 18}, {23, 16, 18},
+ {20, 16, 18}, {19, 12, 18}, {16, 12, 9}, {19, 16, 9},
+ {20, 16, 9}, {23, 16, 9}, {28, 16, 9}, {35, 16, 18},
+ {44, 20, 28}, {56, 23, 37}, {64, 19, 47}, {76, 23, 47},
+ {80, 27, 37}, {88, 35, 37}, {88, 35, 37}, {88, 31, 28},
+ {68, 31, 28}, {52, 23, 28}, {44, 20, 28}, {36, 20, 28},
+ {35, 24, 28}, {35, 24, 28}, {35, 24, 28}, {35, 20, 28},
+ {32, 20, 18}, {28, 20, 18}, {28, 20, 18}, {27, 16, 18},
+ {27, 16, 18}, {28, 16, 18}, {31, 16, 18}, {35, 16, 28},
+ {40, 19, 28}, {48, 23, 37}, {60, 23, 47}, {72, 31, 56},
+ {88, 35, 66}, {100, 39, 75}, {96, 44, 75}, {96, 48, 75},
+ {100, 40, 84}, {100, 40, 75}, {84, 27, 56}, {72, 23, 47},
+ {56, 23, 37}, {44, 23, 18}, {36, 20, 9}, {35, 16, 9},
+ {32, 16, 9}, {28, 16, 9}, {28, 20, 9}, {35, 20, 9},
+ {35, 24, 9}, {35, 24, 18}, {35, 20, 18}, {35, 20, 18},
+ {35, 24, 18}, {36, 24, 28}, {40, 23, 28}, {44, 23, 28},
+ {56, 27, 28}, {64, 27, 37}, {72, 27, 37}, {76, 35, 37},
+ {64, 31, 37}, {52, 23, 28}, {52, 23, 28}, {52, 23, 28},
+ {60, 27, 37}, {64, 23, 37}, {68, 27, 47}, {72, 27, 47},
+ {80, 35, 56}, {88, 40, 65}, {101, 44, 66}, {100, 43, 56},
+ {84, 35, 56}, {68, 39, 56}, {40, 40, 47}, {39, 28, 37},
+ {40, 23, 28}, {43, 20, 28}, {44, 23, 28}, {48, 23, 37},
+ {52, 24, 37}, {56, 27, 37}, {52, 27, 37}, {47, 27, 47},
+ {39, 24, 47}, {35, 24, 37}, {35, 20, 37}, {28, 24, 28},
+ {28, 24, 18}, {24, 24, 18}, {23, 20, 18}, {19, 16, 18},
+ {15, 16, 18}, {16, 16, 18}, {19, 16, 18}, {20, 16, 18},
+ {23, 20, 18}, {23, 20, 18}, {23, 16, 18}, {20, 12, 18},
+ {24, 12, 18}, {27, 12, 18}, {31, 16, 18}, {32, 16, 18},
+ {35, 16, 18}, {36, 16, 18}, {36, 20, 18}, {36, 20, 18},
+ {40, 20, 18}, {40, 19, 18}, {44, 20, 28}, {56, 23, 37},
+ {64, 23, 56}, {76, 31, 66}, {84, 68, 75}, {108, 60, 94},
+ {121, 64, 94}, {125, 52, 94}, {117, 56, 84}, {109, 39, 75},
+ {104, 40, 75}, {104, 43, 65}, {109, 35, 66}, {100, 35, 56},
+ {80, 31, 47}, {72, 19, 37}, {56, 19, 28}, {52, 19, 28},
+ {48, 20, 28}, {44, 19, 28}, {39, 16, 28}, {36, 16, 18},
+ {32, 16, 9}, {36, 20, 9}, {35, 20, 18}, {40, 24, 18},
+ {48, 23, 28}, {72, 31, 37}, {92, 31, 56}, {113, 44, 84},
+ {141, 60, 94}, {202, 181, 188}, {177, 116, 142}, {145, 76, 122},
+ {145, 76, 113}, {137, 80, 113}, {129, 56, 94}, {125, 56, 84},
+ {121, 47, 75}, {104, 35, 66}, {96, 35, 56}, {84, 31, 47},
+ {76, 35, 37}, {64, 23, 28}, {52, 23, 28}, {44, 20, 18},
+ {39, 16, 18}, {31, 16, 18}, {24, 16, 18}, {20, 16, 18},
+ {16, 12, 9}, {16, 12, 9}, {16, 12, 9}, {16, 16, 9},
+ {15, 16, 9}, {15, 12, 9}, {15, 12, 18}, {16, 12, 18},
+ {16, 12, 18}, {19, 12, 18}, {20, 12, 18}, {24, 12, 18},
+ {27, 12, 18}, {27, 16, 28}, {27, 16, 28}, {27, 16, 28},
+ {28, 16, 28}, {31, 16, 28}, {40, 19, 28}, {48, 23, 37},
+ {52, 23, 47}, {56, 27, 47}, {64, 35, 47}, {72, 31, 47},
+ {68, 35, 47}, {60, 36, 47}, {48, 31, 37}, {40, 24, 28},
+ {40, 24, 28}, {43, 24, 28}, {48, 28, 28}, {52, 32, 37},
+ {64, 31, 47}, {68, 27, 47}, {80, 31, 47}, {84, 31, 56},
+ {88, 35, 56}, {96, 35, 56}, {96, 31, 56}, {84, 31, 56},
+ {76, 27, 56}, {72, 27, 56}, {68, 27, 56}, {60, 27, 47},
+ {52, 23, 37}, {40, 20, 28}, {31, 20, 28}, {16, 20, 28},
+ {20, 12, 28}, {20, 12, 28}, {23, 12, 18}, {27, 16, 18},
+ {32, 20, 28}, {35, 24, 28}, {36, 28, 28}, {40, 28, 37},
+ },
+ /* solid-sky */
+ {
+ {145, 104, 84}, {178, 111, 75}, {202, 123, 75}, {198, 127, 75},
+ {129, 112, 85}, {84, 72, 37}, {40, 40, 28}, {20, 16, 18},
+ {11, 4, 28}, {12, 4, 28}, {23, 24, 28}, {0, 93, 141},
+ {0, 109, 160}, {4, 122, 170}, {7, 134, 179}, {19, 138, 188},
+ {24, 142, 179}, {31, 151, 179}, {44, 158, 179}, {52, 162, 179},
+ {129, 117, 122}, {190, 137, 103}, {214, 152, 66}, {206, 131, 37},
+ {202, 119, 28}, {198, 106, 9}, {194, 90, 9}, {166, 43, 9},
+ {133, 18, 0}, {72, 19, 9}, {60, 19, 18}, {36, 28, 9},
+ {36, 36, 18}, {52, 60, 37}, {0, 101, 150}, {0, 109, 150},
+ {0, 109, 150}, {0, 105, 150}, {0, 97, 141}, {20, 36, 18},
+ {16, 24, 9}, {8, 8, 9}, {12, 4, 9}, {12, 4, 9},
+ {11, 4, 9}, {8, 4, 9}, {4, 4, 9}, {7, 4, 9},
+ {8, 0, 9}, {12, 0, 9}, {15, 0, 9}, {16, 4, 18},
+ {19, 12, 28}, {0, 89, 141}, {0, 97, 160}, {0, 101, 160},
+ {0, 101, 150}, {60, 60, 37}, {68, 31, 18}, {105, 30, 9},
+ {170, 80, 9}, {198, 90, 0}, {178, 67, 0}, {133, 30, 0},
+ {72, 15, 9}, {36, 12, 18}, {23, 8, 9}, {16, 4, 9},
+ {19, 8, 9}, {27, 20, 18}, {52, 44, 37}, {4, 105, 150},
+ {60, 162, 188}, {173, 173, 160}, {198, 165, 151}, {194, 148, 113},
+ {218, 160, 84}, {222, 153, 84}, {214, 132, 47}, {210, 123, 37},
+ {202, 111, 28}, {218, 118, 28}, {202, 119, 47}, {149, 104, 85},
+ {88, 116, 113}, {4, 113, 160}, {0, 118, 160}, {0, 122, 160},
+ {0, 122, 160}, {0, 122, 160}, {0, 122, 170}, {4, 122, 170},
+ {0, 118, 170}, {0, 114, 170}, {0, 109, 160}, {0, 101, 150},
+ {44, 60, 28}, {28, 36, 9}, {20, 16, 9}, {16, 8, 9},
+ {15, 4, 9}, {12, 0, 18}, {11, 0, 18}, {8, 0, 18},
+ {8, 0, 18}, {12, 4, 9}, {15, 4, 9}, {20, 4, 9},
+ {28, 12, 18}, {36, 36, 18}, {52, 52, 47}, {0, 97, 150},
+ {0, 101, 150}, {73, 73, 66}, {64, 60, 37}, {48, 56, 28},
+ {64, 64, 47}, {0, 105, 150}, {0, 118, 160}, {4, 122, 170},
+ {8, 126, 170}, {4, 130, 170}, {4, 134, 170}, {0, 130, 170},
+ {0, 126, 170}, {0, 126, 170}, {0, 122, 170}, {0, 114, 170},
+ {0, 105, 160}, {0, 93, 150}, {16, 32, 28}, {16, 12, 18},
+ {19, 8, 9}, {20, 20, 18}, {28, 44, 28}, {0, 97, 141},
+ {4, 118, 160}, {23, 142, 179}, {44, 158, 188}, {60, 166, 197},
+ {60, 166, 197}, {68, 170, 197}, {72, 170, 197}, {72, 173, 197},
+ {96, 177, 188}, {149, 145, 103}, {214, 143, 75}, {210, 127, 47},
+ {154, 99, 37}, {68, 68, 28}, {44, 35, 18}, {24, 20, 18},
+ {15, 8, 18}, {7, 4, 18}, {4, 0, 18}, {3, 4, 18},
+ {7, 4, 18}, {11, 4, 18}, {19, 8, 18}, {32, 36, 28},
+ {0, 105, 150}, {24, 138, 179}, {72, 174, 188}, {149, 190, 197},
+ {206, 218, 207}, {210, 222, 207}, {218, 210, 207}, {218, 218, 198},
+ {218, 210, 198}, {194, 194, 170}, {194, 181, 160}, {141, 137, 103},
+ {113, 81, 75}, {72, 84, 37}, {52, 68, 28}, {52, 48, 28},
+ {68, 64, 28}, {81, 80, 37}, {141, 88, 47}, {194, 106, 37},
+ {206, 106, 28}, {206, 110, 18}, {206, 110, 9}, {194, 98, 18},
+ {174, 87, 37}, {96, 88, 94}, {4, 113, 150}, {12, 134, 170},
+ {19, 142, 179}, {31, 147, 179}, {39, 151, 188}, {48, 158, 188},
+ {48, 166, 188}, {48, 162, 188}, {43, 159, 197}, {39, 151, 198},
+ {27, 147, 188}, {23, 142, 179}, {20, 138, 179}, {19, 134, 179},
+ {8, 130, 179}, {4, 122, 160}, {4, 109, 160}, {0, 97, 150},
+ {44, 36, 37}, {31, 16, 18}, {23, 8, 18}, {23, 8, 18},
+ {27, 28, 28}, {0, 93, 141}, {0, 105, 150}, {0, 97, 141},
+ {48, 39, 37}, {28, 28, 18}, {32, 24, 9}, {44, 15, 9},
+ {60, 27, 18}, {93, 55, 37}, {154, 84, 37}, {186, 98, 28},
+ {186, 102, 9}, {170, 87, 18}, {73, 80, 28}, {40, 52, 18},
+ {40, 36, 18}, {48, 35, 18}, {64, 60, 18}, {149, 75, 0},
+ {194, 94, 9}, {198, 98, 9}, {214, 105, 0}, {210, 98, 9},
+ {210, 101, 18}, {198, 112, 37}, {190, 107, 56}, {108, 108, 94},
+ {4, 105, 150}, {0, 105, 160}, {0, 109, 160}, {0, 105, 160},
+ {0, 101, 160}, {0, 89, 141}, {11, 8, 37}, {11, 4, 28},
+ {11, 4, 18}, {12, 4, 18}, {20, 8, 18}, {36, 12, 18},
+ {64, 19, 9}, {77, 11, 0}, {109, 19, 0}, {169, 14, 9},
+ {149, 67, 18}, {85, 85, 66}, {24, 113, 150}, {35, 155, 188},
+ },
+ /* misty-field */
+ {
+ {84, 89, 103}, {96, 100, 94}, {101, 105, 85}, {93, 105, 84},
+ {89, 109, 85}, {100, 113, 94}, {109, 121, 103}, {104, 117, 113},
+ {116, 117, 122}, {121, 141, 141}, {133, 166, 179}, {153, 185, 197},
+ {161, 185, 207}, {157, 189, 207}, {161, 185, 207}, {157, 177, 207},
+ {149, 169, 198}, {133, 158, 188}, {116, 146, 179}, {108, 133, 179},
+ {108, 133, 179}, {116, 141, 179}, {112, 141, 170}, {129, 150, 170},
+ {133, 157, 188}, {141, 161, 188}, {137, 153, 169}, {125, 141, 141},
+ {121, 133, 113}, {109, 121, 94}, {105, 125, 85}, {101, 133, 84},
+ {101, 133, 85}, {109, 133, 85}, {97, 133, 85}, {93, 121, 75},
+ {97, 125, 85}, {101, 117, 85}, {105, 117, 75}, {89, 117, 75},
+ {84, 121, 75}, {93, 141, 75}, {109, 150, 75}, {117, 170, 103},
+ {133, 178, 122}, {145, 170, 151}, {169, 194, 188}, {169, 198, 207},
+ {173, 193, 207}, {169, 189, 207}, {169, 189, 216}, {165, 189, 216},
+ {165, 185, 216}, {161, 181, 216}, {157, 181, 226}, {157, 185, 226},
+ {157, 189, 225}, {177, 201, 226}, {205, 210, 235}, {214, 218, 244},
+ {214, 222, 244}, {209, 222, 244}, {205, 218, 244}, {193, 214, 235},
+ {177, 197, 216}, {177, 189, 207}, {169, 185, 207}, {169, 181, 207},
+ {165, 177, 216}, {161, 181, 216}, {165, 185, 216}, {165, 189, 216},
+ {169, 193, 216}, {177, 197, 216}, {193, 206, 226}, {206, 222, 226},
+ {218, 222, 235}, {222, 226, 244}, {226, 222, 235}, {222, 230, 244},
+ {222, 226, 254}, {222, 226, 244}, {218, 226, 244}, {218, 226, 244},
+ {222, 234, 244}, {214, 226, 244}, {205, 218, 235}, {185, 197, 216},
+ {165, 181, 207}, {141, 153, 169}, {121, 121, 141}, {105, 101, 113},
+ {84, 84, 94}, {77, 85, 85}, {73, 77, 75}, {73, 73, 75},
+ {73, 73, 75}, {73, 77, 75}, {73, 85, 85}, {77, 97, 85},
+ {84, 113, 94}, {104, 133, 132}, {112, 154, 132}, {129, 162, 160},
+ {141, 169, 188}, {145, 165, 198}, {141, 165, 207}, {137, 161, 207},
+ {124, 165, 207}, {64, 139, 207}, {64, 139, 207}, {64, 135, 207},
+ {100, 129, 179}, {96, 125, 160}, {108, 145, 132}, {112, 137, 103},
+ {97, 121, 94}, {89, 113, 85}, {89, 105, 85}, {77, 89, 85},
+ {77, 81, 84}, {77, 73, 75}, {77, 73, 75}, {77, 77, 75},
+ {81, 81, 84}, {77, 77, 75}, {73, 73, 66}, {73, 73, 66},
+ {73, 77, 75}, {76, 77, 94}, {68, 68, 103}, {72, 77, 103},
+ {92, 100, 132}, {124, 133, 141}, {145, 161, 179}, {165, 185, 207},
+ {173, 201, 225}, {201, 222, 235}, {213, 230, 244}, {222, 230, 235},
+ {222, 226, 235}, {222, 226, 235}, {209, 222, 235}, {205, 218, 235},
+ {185, 206, 226}, {173, 197, 216}, {169, 189, 216}, {165, 189, 207},
+ {165, 185, 198}, {153, 169, 179}, {141, 166, 141}, {137, 154, 132},
+ {125, 157, 132}, {121, 158, 103}, {101, 149, 75}, {85, 125, 65},
+ {81, 117, 75}, {72, 109, 94}, {77, 85, 85}, {80, 81, 94},
+ {81, 77, 84}, {77, 73, 75}, {73, 73, 75}, {68, 68, 66},
+ {68, 68, 66}, {73, 73, 66}, {73, 73, 75}, {72, 72, 75},
+ {81, 85, 75}, {81, 89, 75}, {81, 89, 75}, {85, 97, 75},
+ {89, 97, 94}, {89, 93, 94}, {85, 89, 85}, {81, 81, 75},
+ {85, 97, 75}, {85, 89, 75}, {85, 85, 75}, {81, 97, 75},
+ {81, 105, 66}, {85, 97, 66}, {85, 101, 75}, {81, 101, 75},
+ {76, 101, 75}, {73, 97, 75}, {77, 93, 75}, {73, 81, 75},
+ {68, 77, 75}, {73, 73, 75}, {73, 73, 75}, {73, 73, 75},
+ {73, 73, 75}, {73, 73, 66}, {81, 85, 75}, {81, 97, 66},
+ {81, 105, 56}, {60, 101, 47}, {73, 97, 56}, {68, 89, 66},
+ {73, 77, 66}, {73, 73, 66}, {68, 68, 75}, {52, 72, 103},
+ {44, 81, 141}, {60, 130, 207}, {60, 135, 207}, {64, 138, 198},
+ {96, 125, 160}, {88, 121, 122}, {84, 108, 113}, {92, 101, 103},
+ {101, 101, 113}, {112, 117, 132}, {104, 117, 141}, {104, 125, 151},
+ {116, 133, 170}, {128, 145, 188}, {128, 157, 198}, {129, 154, 207},
+ {129, 157, 197}, {137, 173, 198}, {145, 173, 198}, {141, 165, 188},
+ {133, 165, 188}, {125, 157, 188}, {120, 145, 179}, {112, 133, 170},
+ {96, 121, 160}, {100, 117, 151}, {72, 105, 122}, {68, 89, 113},
+ {80, 93, 103}, {76, 97, 132}, {108, 125, 151}, {137, 141, 160},
+ {145, 157, 188}, {161, 181, 207}, {169, 193, 216}, {181, 210, 226},
+ {193, 222, 235}, {197, 218, 235}, {181, 201, 226}, {165, 193, 216},
+ {161, 189, 216}, {157, 181, 216}, {157, 181, 216}, {153, 181, 216},
+ {149, 181, 207}, {157, 181, 207}, {153, 177, 207}, {149, 177, 207},
+ {149, 177, 207}, {141, 169, 207}, {137, 165, 198}, {128, 161, 198},
+ },
+ /* wooden-highlight */
+ {
+ {97, 76, 75}, {85, 68, 56}, {97, 72, 46}, {125, 84, 56},
+ {141, 100, 85}, {149, 107, 113}, {137, 116, 122}, {153, 112, 113},
+ {154, 133, 122}, {157, 124, 103}, {153, 124, 94}, {161, 119, 94},
+ {178, 119, 85}, {174, 132, 103}, {170, 137, 122}, {194, 153, 122},
+ {166, 124, 123}, {162, 125, 123}, {170, 120, 103}, {178, 131, 85},
+ {174, 119, 66}, {166, 111, 47}, {154, 95, 47}, {137, 80, 28},
+ {141, 72, 9}, {141, 68, 18}, {141, 64, 18}, {137, 59, 18},
+ {125, 55, 9}, {141, 51, 9}, {125, 51, 9}, {117, 51, 18},
+ {117, 55, 28}, {109, 55, 28}, {105, 55, 28}, {109, 51, 28},
+ {97, 55, 28}, {101, 51, 46}, {88, 64, 47}, {109, 68, 46},
+ {141, 96, 66}, {166, 120, 85}, {170, 119, 66}, {174, 111, 75},
+ {170, 115, 85}, {178, 123, 85}, {150, 108, 94}, {154, 107, 85},
+ {133, 92, 75}, {141, 88, 47}, {129, 76, 28}, {121, 64, 28},
+ {133, 72, 28}, {137, 80, 37}, {146, 88, 37}, {150, 96, 47},
+ {154, 99, 47}, {154, 99, 47}, {146, 92, 28}, {154, 76, 18},
+ {146, 64, 18}, {141, 64, 28}, {141, 76, 28}, {145, 80, 37},
+ {158, 95, 47}, {174, 106, 37}, {178, 110, 47}, {170, 102, 28},
+ {150, 84, 18}, {129, 72, 18}, {109, 68, 18}, {96, 47, 9},
+ {84, 35, 18}, {72, 35, 18}, {68, 35, 18}, {68, 39, 18},
+ {76, 43, 28}, {84, 43, 28}, {92, 51, 28}, {100, 55, 28},
+ {117, 72, 28}, {133, 88, 28}, {162, 106, 28}, {170, 106, 28},
+ {162, 91, 28}, {150, 80, 28}, {141, 64, 28}, {125, 51, 28},
+ {117, 47, 18}, {101, 42, 18}, {84, 39, 18}, {60, 31, 9},
+ {44, 23, 9}, {32, 16, 9}, {24, 16, 9}, {16, 16, 9},
+ {16, 20, 0}, {16, 16, 9}, {16, 16, 9}, {16, 20, 9},
+ {20, 20, 9}, {27, 20, 9}, {27, 20, 9}, {27, 24, 9},
+ {24, 20, 18}, {32, 16, 18}, {28, 16, 18}, {20, 16, 18},
+ {19, 12, 18}, {19, 12, 18}, {16, 12, 18}, {16, 16, 18},
+ {16, 16, 18}, {15, 16, 18}, {16, 16, 18}, {19, 16, 18},
+ {27, 24, 18}, {31, 20, 28}, {36, 24, 28}, {48, 24, 28},
+ {68, 35, 28}, {80, 43, 28}, {89, 51, 18}, {105, 51, 18},
+ {109, 51, 18}, {121, 51, 18}, {121, 42, 9}, {121, 51, 18},
+ {125, 46, 18}, {121, 55, 18}, {117, 64, 18}, {113, 68, 18},
+ {109, 68, 18}, {105, 64, 28}, {105, 59, 28}, {96, 51, 28},
+ {80, 43, 28}, {56, 27, 28}, {44, 19, 28}, {36, 24, 28},
+ {24, 20, 28}, {19, 16, 18}, {12, 12, 9}, {12, 12, 9},
+ {16, 16, 9}, {24, 20, 9}, {40, 23, 9}, {68, 35, 9},
+ {85, 43, 9}, {105, 42, 9}, {109, 47, 18}, {109, 47, 18},
+ {109, 46, 9}, {109, 46, 9}, {97, 38, 9}, {80, 43, 9},
+ {60, 35, 9}, {40, 24, 18}, {32, 24, 18}, {28, 24, 18},
+ {28, 24, 18}, {40, 28, 18}, {44, 23, 18}, {56, 31, 18},
+ {68, 39, 18}, {80, 43, 18}, {85, 52, 28}, {101, 72, 37},
+ {133, 88, 47}, {141, 100, 75}, {146, 112, 85}, {146, 116, 85},
+ {146, 107, 75}, {121, 72, 37}, {113, 60, 28}, {88, 47, 28},
+ {56, 35, 28}, {35, 24, 18}, {23, 16, 18}, {19, 16, 18},
+ {16, 16, 18}, {16, 12, 9}, {16, 12, 9}, {24, 16, 9},
+ {35, 20, 9}, {48, 27, 9}, {56, 31, 18}, {60, 31, 18},
+ {68, 35, 37}, {76, 39, 37}, {76, 39, 28}, {76, 35, 28},
+ {84, 35, 28}, {96, 38, 18}, {97, 39, 18}, {100, 38, 18},
+ {84, 39, 28}, {68, 35, 18}, {56, 27, 18}, {44, 23, 18},
+ {48, 23, 18}, {56, 27, 18}, {64, 35, 37}, {72, 64, 56},
+ {89, 72, 65}, {105, 93, 84}, {137, 100, 85}, {149, 108, 94},
+ {150, 104, 85}, {129, 92, 84}, {101, 84, 75}, {101, 72, 37},
+ {113, 68, 28}, {133, 68, 28}, {129, 64, 18}, {137, 68, 18},
+ {137, 72, 28}, {125, 64, 18}, {125, 60, 18}, {121, 60, 18},
+ {117, 55, 18}, {101, 47, 28}, {84, 47, 28}, {68, 39, 28},
+ {48, 31, 18}, {31, 24, 18}, {20, 16, 9}, {16, 12, 9},
+ {12, 12, 9}, {11, 12, 9}, {12, 12, 9}, {16, 12, 9},
+ {24, 12, 9}, {31, 12, 9}, {35, 16, 9}, {60, 23, 9},
+ {64, 27, 9}, {72, 39, 18}, {72, 47, 18}, {76, 47, 28},
+ {72, 52, 28}, {76, 47, 28}, {80, 43, 28}, {72, 35, 28},
+ {64, 31, 18}, {60, 31, 9}, {40, 27, 9}, {27, 20, 9},
+ {32, 20, 9}, {36, 28, 9}, {48, 27, 9}, {56, 39, 18},
+ {81, 47, 18}, {105, 59, 18}, {121, 64, 18}, {146, 76, 28},
+ },
+ /* jet-tundra */
+ {
+ {109, 92, 75}, {105, 85, 75}, {105, 85, 75}, {97, 89, 75},
+ {93, 85, 75}, {93, 81, 75}, {93, 80, 65}, {85, 73, 65},
+ {84, 68, 56}, {84, 68, 47}, {80, 64, 47}, {76, 64, 47},
+ {72, 60, 37}, {68, 64, 37}, {72, 64, 47}, {77, 73, 47},
+ {85, 76, 47}, {93, 76, 56}, {97, 80, 56}, {101, 80, 56},
+ {105, 80, 56}, {109, 84, 56}, {105, 88, 56}, {101, 84, 56},
+ {97, 80, 56}, {93, 76, 56}, {85, 68, 47}, {72, 60, 47},
+ {64, 56, 37}, {60, 56, 37}, {60, 56, 37}, {68, 60, 37},
+ {72, 56, 37}, {73, 64, 47}, {76, 68, 56}, {76, 68, 56},
+ {81, 64, 56}, {80, 68, 56}, {81, 73, 56}, {85, 80, 65},
+ {93, 85, 65}, {101, 93, 65}, {101, 93, 75}, {105, 97, 84},
+ {113, 96, 84}, {113, 100, 94}, {113, 104, 94}, {113, 104, 94},
+ {117, 108, 94}, {121, 108, 85}, {121, 104, 84}, {121, 104, 75},
+ {117, 100, 75}, {109, 97, 75}, {105, 93, 75}, {105, 92, 65},
+ {105, 92, 66}, {97, 89, 75}, {93, 89, 75}, {93, 85, 75},
+ {85, 77, 65}, {81, 73, 65}, {81, 72, 56}, {77, 72, 56},
+ {73, 73, 56}, {72, 68, 56}, {72, 68, 56}, {76, 68, 56},
+ {77, 68, 56}, {85, 76, 56}, {89, 80, 56}, {93, 81, 65},
+ {97, 89, 75}, {105, 92, 75}, {113, 92, 75}, {121, 92, 75},
+ {121, 92, 66}, {113, 92, 65}, {105, 88, 65}, {93, 85, 56},
+ {89, 84, 56}, {85, 77, 56}, {73, 73, 56}, {72, 68, 56},
+ {72, 64, 47}, {68, 60, 47}, {68, 60, 47}, {64, 60, 47},
+ {64, 60, 56}, {68, 60, 56}, {68, 60, 56}, {72, 64, 66},
+ {68, 68, 56}, {68, 64, 47}, {68, 64, 47}, {64, 56, 47},
+ {56, 52, 37}, {56, 52, 37}, {52, 52, 37}, {52, 52, 37},
+ {52, 48, 37}, {48, 48, 37}, {48, 48, 28}, {60, 52, 28},
+ {60, 56, 28}, {56, 52, 37}, {52, 48, 37}, {56, 48, 37},
+ {56, 48, 37}, {60, 48, 37}, {72, 60, 47}, {85, 72, 56},
+ {97, 85, 65}, {109, 96, 75}, {121, 104, 85}, {129, 116, 103},
+ {133, 125, 103}, {137, 125, 113}, {137, 124, 113}, {133, 125, 103},
+ {137, 116, 103}, {133, 112, 103}, {129, 112, 94}, {125, 116, 85},
+ {113, 108, 85}, {105, 101, 75}, {101, 92, 66}, {93, 88, 65},
+ {89, 85, 65}, {89, 85, 56}, {89, 85, 56}, {89, 85, 65},
+ {89, 89, 75}, {93, 89, 84}, {104, 104, 94}, {108, 96, 94},
+ {108, 96, 94}, {105, 97, 84}, {101, 93, 84}, {97, 80, 75},
+ {89, 76, 65}, {81, 73, 56}, {73, 68, 47}, {68, 64, 47},
+ {60, 56, 47}, {56, 56, 47}, {64, 64, 47}, {72, 68, 47},
+ {81, 73, 56}, {93, 81, 56}, {97, 88, 66}, {101, 88, 75},
+ {105, 89, 84}, {109, 97, 84}, {117, 100, 84}, {125, 100, 84},
+ {125, 100, 84}, {121, 100, 84}, {121, 104, 84}, {121, 108, 94},
+ {121, 108, 94}, {117, 108, 94}, {121, 108, 94}, {121, 117, 94},
+ {133, 121, 103}, {145, 116, 103}, {149, 124, 94}, {150, 120, 94},
+ {154, 120, 94}, {158, 128, 85}, {158, 132, 85}, {137, 120, 75},
+ {133, 112, 66}, {113, 96, 65}, {97, 88, 56}, {85, 76, 47},
+ {73, 68, 47}, {64, 56, 47}, {60, 52, 47}, {60, 48, 37},
+ {64, 52, 37}, {73, 60, 37}, {85, 72, 47}, {89, 76, 47},
+ {93, 80, 47}, {97, 81, 56}, {97, 85, 65}, {97, 88, 65},
+ {101, 92, 65}, {101, 97, 75}, {113, 104, 84}, {125, 108, 94},
+ {133, 116, 103}, {145, 120, 113}, {154, 137, 122}, {178, 149, 141},
+ {198, 165, 151}, {218, 193, 179}, {210, 185, 179}, {178, 153, 141},
+ {166, 145, 132}, {157, 141, 122}, {153, 133, 132}, {120, 112, 122},
+ {125, 108, 113}, {121, 100, 94}, {113, 97, 75}, {109, 93, 65},
+ {109, 92, 65}, {105, 93, 65}, {109, 93, 75}, {113, 104, 84},
+ {113, 100, 84}, {113, 104, 84}, {113, 104, 84}, {113, 104, 75},
+ {109, 96, 66}, {105, 92, 56}, {105, 92, 56}, {105, 88, 47},
+ {109, 93, 56}, {113, 96, 65}, {121, 100, 75}, {125, 100, 84},
+ {129, 108, 85}, {133, 112, 85}, {133, 112, 94}, {133, 116, 103},
+ {137, 120, 103}, {145, 124, 103}, {137, 120, 103}, {141, 116, 94},
+ {133, 116, 75}, {121, 100, 75}, {121, 96, 75}, {109, 92, 65},
+ {105, 88, 65}, {101, 84, 56}, {93, 77, 56}, {85, 72, 47},
+ {80, 68, 37}, {72, 64, 37}, {68, 64, 28}, {60, 56, 28},
+ {64, 60, 28}, {68, 60, 37}, {76, 64, 37}, {85, 73, 37},
+ {89, 84, 47}, {97, 88, 47}, {97, 88, 56}, {97, 85, 56},
+ {97, 80, 56}, {93, 76, 56}, {81, 72, 56}, {73, 68, 56},
+ },
+ /* pastel-lime */
+ {
+ {137, 149, 85}, {133, 146, 94}, {129, 141, 94}, {109, 133, 85},
+ {121, 133, 85}, {105, 121, 75}, {133, 129, 75}, {162, 128, 85},
+ {190, 107, 66}, {158, 111, 37}, {198, 115, 56}, {202, 152, 66},
+ {174, 140, 85}, {149, 149, 75}, {137, 146, 94}, {113, 133, 75},
+ {105, 121, 75}, {101, 121, 75}, {97, 121, 66}, {93, 105, 56},
+ {93, 85, 56}, {113, 76, 56}, {146, 88, 18}, {162, 59, 9},
+ {113, 68, 9}, {85, 80, 37}, {81, 68, 47}, {64, 64, 47},
+ {52, 73, 47}, {56, 73, 37}, {64, 77, 37}, {64, 77, 28},
+ {68, 73, 37}, {68, 77, 37}, {77, 89, 47}, {89, 109, 56},
+ {113, 137, 75}, {109, 133, 85}, {121, 150, 103}, {137, 162, 122},
+ {145, 166, 151}, {150, 182, 151}, {194, 198, 160}, {234, 205, 150},
+ {214, 185, 132}, {214, 181, 94}, {210, 157, 103}, {198, 144, 94},
+ {153, 116, 94}, {109, 113, 85}, {101, 109, 75}, {89, 100, 66},
+ {77, 93, 66}, {69, 89, 56}, {64, 77, 56}, {60, 77, 47},
+ {56, 81, 56}, {56, 76, 56}, {56, 73, 56}, {60, 73, 56},
+ {68, 73, 56}, {73, 81, 56}, {73, 89, 56}, {73, 89, 56},
+ {85, 113, 56}, {101, 121, 75}, {109, 129, 85}, {129, 150, 94},
+ {141, 166, 113}, {166, 173, 103}, {162, 178, 132}, {174, 182, 132},
+ {186, 190, 122}, {194, 194, 132}, {214, 202, 132}, {186, 194, 141},
+ {170, 186, 122}, {166, 173, 103}, {162, 169, 85}, {145, 166, 85},
+ {125, 133, 85}, {109, 117, 66}, {105, 109, 66}, {97, 97, 56},
+ {93, 76, 56}, {113, 88, 66}, {113, 109, 75}, {170, 91, 37},
+ {170, 119, 56}, {174, 115, 56}, {157, 145, 85}, {125, 137, 85},
+ {109, 113, 75}, {105, 109, 75}, {101, 101, 75}, {97, 105, 66},
+ {93, 109, 66}, {89, 113, 66}, {89, 109, 75}, {89, 109, 75},
+ {85, 109, 75}, {85, 105, 75}, {84, 101, 66}, {81, 101, 66},
+ {81, 101, 66}, {81, 101, 66}, {85, 105, 66}, {85, 105, 56},
+ {89, 100, 56}, {81, 97, 56}, {73, 97, 56}, {73, 93, 56},
+ {77, 81, 47}, {93, 76, 47}, {85, 72, 56}, {81, 73, 56},
+ {81, 80, 66}, {89, 73, 75}, {101, 85, 84}, {113, 96, 84},
+ {121, 121, 94}, {137, 137, 103}, {157, 149, 122}, {178, 177, 122},
+ {202, 185, 151}, {210, 206, 170}, {214, 193, 170}, {190, 190, 170},
+ {182, 190, 151}, {170, 186, 160}, {174, 190, 151}, {170, 194, 151},
+ {158, 186, 141}, {149, 178, 141}, {162, 194, 160}, {149, 170, 132},
+ {145, 153, 122}, {125, 133, 113}, {104, 117, 94}, {89, 105, 75},
+ {77, 93, 66}, {64, 76, 56}, {56, 64, 56}, {60, 60, 56},
+ {60, 60, 66}, {76, 64, 65}, {97, 76, 65}, {97, 97, 75},
+ {101, 109, 75}, {105, 121, 84}, {125, 141, 103}, {129, 158, 113},
+ {133, 158, 122}, {129, 154, 122}, {129, 158, 113}, {117, 150, 103},
+ {109, 117, 75}, {89, 97, 66}, {73, 77, 56}, {60, 60, 47},
+ {52, 48, 37}, {44, 39, 28}, {32, 40, 18}, {20, 20, 18},
+ {23, 20, 28}, {20, 24, 37}, {19, 28, 37}, {36, 32, 37},
+ {40, 48, 47}, {44, 56, 66}, {48, 44, 56}, {48, 48, 56},
+ {44, 56, 56}, {56, 64, 56}, {56, 60, 56}, {48, 56, 47},
+ {52, 52, 47}, {60, 48, 47}, {48, 48, 47}, {48, 48, 47},
+ {36, 56, 47}, {40, 48, 37}, {32, 52, 37}, {44, 60, 47},
+ {44, 52, 37}, {40, 56, 37}, {40, 64, 37}, {40, 68, 37},
+ {48, 56, 28}, {56, 68, 37}, {48, 52, 28}, {56, 64, 37},
+ {56, 52, 47}, {52, 64, 47}, {60, 68, 47}, {64, 64, 56},
+ {64, 73, 56}, {68, 73, 56}, {68, 68, 47}, {68, 73, 47},
+ {60, 77, 47}, {68, 76, 47}, {68, 81, 56}, {60, 81, 56},
+ {60, 81, 56}, {64, 81, 56}, {68, 81, 66}, {73, 77, 75},
+ {76, 88, 75}, {81, 93, 75}, {85, 97, 75}, {89, 105, 75},
+ {89, 109, 75}, {93, 117, 75}, {93, 113, 85}, {101, 117, 84},
+ {109, 129, 94}, {125, 141, 113}, {141, 161, 122}, {174, 157, 122},
+ {190, 145, 132}, {166, 170, 103}, {162, 182, 113}, {154, 178, 132},
+ {150, 174, 113}, {141, 170, 132}, {137, 170, 122}, {137, 162, 113},
+ {125, 145, 103}, {121, 133, 94}, {105, 117, 85}, {101, 109, 85},
+ {93, 109, 85}, {93, 104, 85}, {89, 109, 85}, {101, 117, 85},
+ {117, 137, 103}, {117, 150, 113}, {116, 133, 113}, {97, 109, 94},
+ {85, 105, 85}, {73, 97, 75}, {76, 84, 66}, {77, 84, 66},
+ {81, 80, 56}, {73, 81, 56}, {68, 85, 56}, {64, 85, 47},
+ {56, 85, 47}, {69, 81, 47}, {73, 81, 56}, {68, 81, 47},
+ {73, 85, 47}, {73, 89, 56}, {81, 89, 56}, {85, 97, 56},
+ },
+ /* hell */
+ {
+ {141, 31, 18}, {145, 38, 9}, {149, 34, 9}, {154, 34, 9},
+ {154, 34, 9}, {153, 34, 18}, {154, 34, 9}, {162, 37, 0},
+ {166, 37, 0}, {174, 46, 0}, {186, 54, 0}, {198, 67, 0},
+ {198, 62, 0}, {174, 58, 0}, {166, 38, 0}, {158, 26, 0},
+ {137, 22, 0}, {137, 11, 0}, {133, 11, 0}, {129, 7, 9},
+ {137, 22, 0}, {137, 26, 9}, {129, 26, 9}, {141, 30, 0},
+ {154, 42, 0}, {170, 50, 0}, {178, 63, 9}, {170, 75, 18},
+ {182, 75, 18}, {182, 75, 18}, {178, 71, 9}, {186, 75, 0},
+ {186, 75, 9}, {190, 67, 9}, {194, 67, 9}, {202, 63, 9},
+ {198, 67, 9}, {206, 79, 9}, {218, 94, 9}, {214, 102, 18},
+ {230, 98, 9}, {218, 103, 9}, {242, 139, 56}, {222, 98, 9},
+ {210, 86, 9}, {194, 75, 28}, {182, 63, 9}, {170, 42, 9},
+ {150, 38, 9}, {137, 34, 9}, {125, 26, 9}, {113, 15, 0},
+ {101, 7, 0}, {85, 7, 0}, {73, 11, 0}, {76, 3, 0},
+ {73, 11, 0}, {85, 11, 0}, {89, 15, 9}, {97, 26, 9},
+ {113, 39, 18}, {125, 47, 28}, {121, 67, 37}, {133, 63, 18},
+ {158, 59, 9}, {190, 87, 18}, {210, 98, 9}, {210, 107, 18},
+ {218, 103, 18}, {214, 98, 9}, {202, 90, 9}, {182, 74, 9},
+ {162, 59, 9}, {150, 42, 9}, {133, 34, 9}, {117, 26, 0},
+ {97, 19, 0}, {80, 15, 9}, {60, 11, 9}, {56, 15, 9},
+ {56, 15, 0}, {56, 7, 0}, {64, 7, 0}, {77, 15, 0},
+ {89, 19, 0}, {97, 22, 0}, {117, 34, 0}, {129, 34, 0},
+ {133, 34, 0}, {137, 34, 0}, {137, 34, 0}, {137, 38, 0},
+ {133, 34, 0}, {129, 34, 0}, {121, 27, 0}, {113, 22, 0},
+ {113, 22, 0}, {109, 22, 0}, {105, 26, 9}, {105, 23, 9},
+ {105, 19, 0}, {105, 11, 0}, {109, 11, 0}, {109, 22, 0},
+ {113, 26, 0}, {117, 31, 0}, {121, 34, 0}, {125, 30, 0},
+ {121, 30, 0}, {117, 30, 0}, {109, 26, 0}, {101, 22, 0},
+ {89, 19, 0}, {85, 19, 0}, {77, 23, 0}, {73, 19, 0},
+ {73, 19, 0}, {68, 15, 9}, {76, 19, 0}, {76, 15, 9},
+ {76, 19, 9}, {80, 27, 18}, {80, 31, 18}, {84, 23, 9},
+ {93, 23, 9}, {105, 26, 9}, {125, 26, 18}, {117, 26, 18},
+ {105, 27, 18}, {97, 31, 18}, {93, 27, 18}, {97, 23, 9},
+ {89, 26, 0}, {89, 23, 0}, {89, 27, 9}, {93, 26, 0},
+ {97, 26, 0}, {109, 27, 0}, {117, 34, 0}, {125, 34, 9},
+ {133, 34, 9}, {141, 34, 0}, {146, 34, 0}, {154, 33, 0},
+ {158, 29, 0}, {158, 33, 0}, {158, 38, 9}, {162, 34, 9},
+ {161, 42, 9}, {161, 42, 9}, {162, 38, 9}, {166, 37, 0},
+ {170, 37, 0}, {170, 50, 0}, {174, 63, 0}, {178, 63, 9},
+ {174, 59, 0}, {162, 46, 0}, {146, 42, 0}, {129, 43, 9},
+ {121, 38, 9}, {125, 38, 9}, {121, 38, 9}, {121, 38, 0},
+ {117, 42, 0}, {121, 46, 9}, {125, 42, 9}, {129, 44, 18},
+ {149, 56, 28}, {145, 75, 37}, {153, 68, 28}, {182, 80, 47},
+ {165, 95, 75}, {165, 99, 84}, {194, 124, 75}, {178, 120, 56},
+ {173, 87, 37}, {174, 83, 37}, {174, 79, 28}, {169, 55, 28},
+ {157, 46, 28}, {154, 55, 9}, {154, 55, 9}, {146, 43, 9},
+ {149, 51, 0}, {154, 46, 0}, {146, 42, 0}, {150, 42, 9},
+ {150, 38, 9}, {146, 34, 9}, {141, 39, 18}, {133, 43, 18},
+ {133, 39, 18}, {141, 42, 19}, {146, 51, 9}, {149, 59, 9},
+ {158, 67, 9}, {190, 86, 18}, {202, 95, 18}, {206, 79, 9},
+ {194, 72, 0}, {174, 63, 9}, {149, 51, 18}, {133, 46, 9},
+ {121, 55, 0}, {113, 39, 0}, {101, 31, 9}, {92, 30, 9},
+ {101, 30, 9}, {105, 26, 9}, {117, 39, 9}, {129, 38, 9},
+ {133, 34, 9}, {137, 34, 9}, {137, 38, 9}, {141, 38, 9},
+ {145, 38, 9}, {145, 38, 0}, {146, 38, 0}, {154, 34, 0},
+ {158, 34, 0}, {150, 33, 0}, {141, 27, 0}, {133, 22, 0},
+ {125, 22, 0}, {125, 22, 0}, {121, 18, 0}, {121, 18, 0},
+ {117, 22, 0}, {125, 27, 0}, {125, 34, 0}, {121, 38, 9},
+ {121, 38, 9}, {121, 38, 9}, {113, 38, 9}, {105, 26, 0},
+ {101, 19, 0}, {97, 15, 0}, {97, 15, 0}, {85, 15, 0},
+ {84, 15, 0}, {80, 15, 0}, {77, 15, 0}, {81, 19, 0},
+ {85, 19, 0}, {89, 22, 0}, {97, 22, 0}, {105, 22, 0},
+ {113, 15, 9}, {125, 26, 9}, {129, 30, 0}, {129, 30, 0},
+ {125, 22, 0}, {125, 18, 0}, {133, 18, 0}, {133, 22, 0},
+ },
+ /* indian-coast */
+ {
+ {68, 56, 47}, {80, 64, 47}, {89, 76, 47}, {105, 68, 46},
+ {104, 68, 46}, {80, 60, 37}, {60, 52, 37}, {52, 44, 37},
+ {44, 40, 37}, {36, 40, 37}, {32, 32, 37}, {40, 44, 28},
+ {36, 44, 28}, {36, 40, 28}, {28, 36, 28}, {20, 32, 28},
+ {19, 24, 28}, {20, 24, 28}, {28, 28, 28}, {35, 32, 37},
+ {27, 36, 37}, {12, 36, 47}, {20, 36, 28}, {32, 32, 28},
+ {32, 32, 28}, {36, 44, 28}, {36, 48, 28}, {40, 48, 28},
+ {52, 56, 28}, {56, 60, 28}, {60, 60, 28}, {64, 60, 37},
+ {81, 77, 47}, {93, 89, 47}, {109, 92, 37}, {113, 88, 47},
+ {129, 96, 56}, {125, 100, 56}, {125, 100, 56}, {129, 96, 56},
+ {121, 84, 56}, {117, 84, 56}, {109, 80, 47}, {109, 76, 46},
+ {97, 80, 47}, {85, 77, 56}, {84, 76, 56}, {81, 72, 66},
+ {77, 73, 66}, {89, 97, 85}, {137, 177, 188}, {169, 210, 216},
+ {173, 210, 216}, {173, 206, 216}, {177, 202, 198}, {182, 119, 75},
+ {166, 103, 56}, {162, 99, 56}, {178, 123, 75}, {181, 206, 207},
+ {181, 210, 216}, {181, 210, 216}, {177, 210, 216}, {177, 210, 216},
+ {177, 210, 207}, {165, 133, 103}, {149, 108, 56}, {141, 107, 56},
+ {145, 103, 56}, {145, 100, 56}, {158, 103, 75}, {169, 210, 197},
+ {169, 210, 216}, {169, 206, 207}, {141, 125, 113}, {133, 88, 75},
+ {121, 84, 65}, {125, 76, 56}, {117, 80, 56}, {117, 76, 47},
+ {121, 80, 46}, {121, 88, 47}, {125, 84, 56}, {133, 92, 56},
+ {141, 103, 56}, {141, 104, 75}, {149, 149, 122}, {177, 202, 216},
+ {177, 210, 216}, {181, 214, 216}, {185, 218, 216}, {185, 214, 216},
+ {181, 210, 226}, {181, 210, 226}, {177, 210, 226}, {173, 210, 216},
+ {161, 206, 197}, {121, 121, 85}, {101, 88, 56}, {84, 76, 47},
+ {68, 68, 37}, {52, 60, 37}, {52, 60, 28}, {40, 60, 47},
+ {56, 68, 28}, {77, 64, 37}, {109, 72, 46}, {133, 88, 56},
+ {145, 96, 66}, {170, 115, 85}, {173, 206, 207}, {177, 206, 216},
+ {177, 210, 226}, {177, 206, 225}, {177, 210, 216}, {177, 214, 207},
+ {137, 137, 113}, {129, 100, 75}, {113, 88, 56}, {93, 76, 37},
+ {85, 60, 9}, {48, 44, 18}, {44, 40, 28}, {36, 28, 28},
+ {52, 35, 28}, {52, 48, 47}, {64, 56, 56}, {76, 72, 56},
+ {93, 85, 56}, {117, 109, 84}, {157, 206, 207}, {165, 206, 225},
+ {169, 206, 225}, {169, 206, 225}, {169, 210, 216}, {165, 198, 207},
+ {121, 108, 94}, {117, 88, 65}, {109, 72, 56}, {109, 72, 56},
+ {85, 72, 56}, {80, 68, 56}, {80, 64, 56}, {76, 68, 56},
+ {81, 72, 56}, {85, 76, 56}, {105, 80, 56}, {121, 80, 56},
+ {141, 96, 66}, {145, 104, 75}, {157, 166, 141}, {181, 210, 207},
+ {189, 210, 216}, {185, 214, 216}, {185, 210, 207}, {186, 127, 85},
+ {154, 99, 66}, {145, 99, 56}, {145, 99, 56}, {146, 96, 47},
+ {158, 88, 47}, {157, 99, 56}, {186, 127, 75}, {177, 206, 198},
+ {169, 210, 216}, {153, 194, 207}, {108, 104, 94}, {85, 89, 75},
+ {73, 76, 56}, {64, 68, 47}, {56, 60, 47}, {60, 60, 56},
+ {68, 60, 56}, {72, 68, 56}, {81, 77, 56}, {89, 89, 47},
+ {93, 88, 56}, {105, 97, 84}, {165, 186, 197}, {189, 206, 216},
+ {197, 214, 216}, {206, 218, 216}, {246, 229, 197}, {246, 225, 188},
+ {206, 148, 94}, {158, 100, 75}, {133, 96, 65}, {105, 76, 56},
+ {72, 60, 56}, {32, 48, 56}, {32, 48, 56}, {32, 48, 56},
+ {24, 48, 66}, {52, 68, 66}, {77, 77, 84}, {113, 121, 113},
+ {165, 206, 207}, {181, 214, 216}, {193, 218, 216}, {193, 218, 216},
+ {202, 218, 216}, {202, 218, 216}, {202, 210, 198}, {186, 131, 75},
+ {170, 119, 56}, {174, 115, 66}, {194, 144, 85}, {185, 214, 207},
+ {181, 214, 216}, {181, 218, 226}, {181, 214, 226}, {189, 214, 226},
+ {193, 218, 226}, {197, 218, 226}, {206, 218, 226}, {206, 218, 226},
+ {201, 218, 226}, {197, 218, 226}, {198, 218, 207}, {226, 214, 179},
+ {190, 136, 75}, {174, 115, 66}, {178, 111, 75}, {194, 152, 103},
+ {185, 214, 207}, {181, 210, 216}, {177, 202, 216}, {149, 133, 113},
+ {137, 100, 66}, {141, 96, 56}, {133, 84, 47}, {125, 88, 37},
+ {129, 79, 37}, {125, 80, 47}, {109, 84, 37}, {109, 76, 46},
+ {93, 80, 47}, {68, 64, 47}, {60, 60, 37}, {60, 60, 37},
+ {68, 60, 47}, {72, 68, 56}, {73, 73, 66}, {81, 85, 85},
+ {113, 133, 122}, {165, 210, 216}, {173, 210, 225}, {173, 210, 226},
+ {173, 210, 216}, {169, 214, 216}, {165, 210, 207}, {129, 125, 85},
+ {125, 104, 56}, {109, 84, 56}, {89, 76, 56}, {81, 77, 56},
+ },
+ /* dentist-decor */
+ {
+ {218, 185, 169}, {214, 193, 151}, {210, 189, 141}, {202, 198, 132},
+ {178, 169, 113}, {166, 162, 103}, {149, 133, 94}, {133, 120, 103},
+ {121, 109, 94}, {109, 105, 84}, {105, 101, 84}, {105, 105, 85},
+ {109, 109, 85}, {109, 109, 85}, {109, 109, 85}, {117, 109, 94},
+ {129, 129, 103}, {137, 146, 94}, {146, 166, 94}, {146, 166, 85},
+ {137, 149, 85}, {133, 141, 85}, {141, 141, 85}, {137, 146, 85},
+ {141, 137, 94}, {137, 145, 103}, {146, 154, 113}, {158, 162, 122},
+ {157, 166, 113}, {154, 170, 113}, {150, 170, 122}, {166, 182, 132},
+ {194, 177, 141}, {210, 185, 141}, {218, 185, 151}, {230, 180, 141},
+ {234, 180, 150}, {234, 176, 141}, {230, 172, 131}, {214, 157, 113},
+ {198, 145, 113}, {182, 128, 103}, {153, 108, 94}, {141, 100, 84},
+ {121, 101, 84}, {117, 92, 84}, {109, 88, 94}, {96, 80, 94},
+ {121, 76, 94}, {117, 92, 94}, {117, 105, 94}, {117, 92, 94},
+ {129, 100, 94}, {133, 125, 103}, {153, 132, 103}, {174, 141, 122},
+ {190, 148, 113}, {194, 165, 113}, {219, 161, 122}, {230, 180, 141},
+ {230, 180, 141}, {238, 180, 141}, {234, 157, 151}, {230, 172, 141},
+ {234, 157, 160}, {234, 156, 150}, {230, 157, 169}, {222, 149, 151},
+ {219, 145, 141}, {206, 149, 122}, {198, 140, 113}, {190, 128, 113},
+ {186, 136, 103}, {194, 128, 104}, {194, 136, 103}, {202, 140, 103},
+ {202, 145, 113}, {202, 152, 113}, {210, 152, 113}, {222, 161, 113},
+ {222, 169, 122}, {219, 165, 122}, {219, 169, 132}, {214, 169, 132},
+ {210, 165, 132}, {214, 161, 132}, {219, 145, 141}, {219, 141, 132},
+ {222, 141, 141}, {219, 145, 141}, {214, 161, 141}, {206, 168, 132},
+ {202, 173, 132}, {174, 170, 122}, {149, 149, 103}, {137, 133, 94},
+ {113, 108, 85}, {101, 93, 84}, {81, 77, 75}, {77, 81, 75},
+ {85, 85, 84}, {109, 97, 84}, {137, 112, 75}, {149, 112, 94},
+ {165, 119, 94}, {186, 123, 94}, {186, 128, 104}, {194, 136, 113},
+ {194, 148, 113}, {206, 165, 132}, {222, 197, 160}, {250, 245, 216},
+ {222, 197, 160}, {214, 181, 160}, {214, 181, 179}, {250, 197, 197},
+ {238, 164, 169}, {234, 157, 160}, {234, 160, 150}, {234, 145, 151},
+ {219, 141, 141}, {206, 137, 141}, {186, 144, 132}, {178, 153, 132},
+ {166, 165, 122}, {162, 170, 113}, {146, 166, 103}, {137, 150, 103},
+ {149, 137, 94}, {174, 157, 103}, {190, 173, 103}, {202, 198, 113},
+ {206, 219, 132}, {206, 202, 113}, {190, 181, 122}, {174, 169, 113},
+ {141, 141, 94}, {105, 113, 85}, {77, 81, 75}, {60, 64, 66},
+ {48, 44, 47}, {44, 44, 37}, {36, 32, 37}, {24, 32, 37},
+ {36, 36, 47}, {52, 40, 56}, {80, 64, 65}, {84, 72, 75},
+ {105, 88, 84}, {121, 113, 103}, {145, 141, 113}, {162, 149, 122},
+ {186, 157, 141}, {202, 181, 151}, {210, 189, 151}, {210, 193, 160},
+ {222, 197, 160}, {218, 189, 150}, {206, 181, 141}, {210, 165, 132},
+ {210, 153, 122}, {210, 153, 122}, {219, 141, 132}, {219, 137, 132},
+ {206, 137, 122}, {206, 148, 122}, {206, 145, 122}, {219, 137, 132},
+ {219, 141, 132}, {222, 145, 141}, {222, 153, 151}, {222, 164, 150},
+ {219, 153, 151}, {219, 145, 151}, {206, 137, 151}, {169, 132, 141},
+ {165, 141, 132}, {145, 129, 113}, {145, 108, 103}, {133, 92, 103},
+ {137, 104, 103}, {125, 113, 103}, {125, 117, 103}, {145, 116, 103},
+ {161, 128, 103}, {178, 152, 94}, {178, 161, 103}, {178, 165, 94},
+ {166, 174, 94}, {154, 166, 94}, {174, 169, 94}, {154, 162, 94},
+ {153, 149, 85}, {157, 137, 94}, {186, 132, 94}, {186, 127, 94},
+ {190, 128, 94}, {194, 136, 104}, {190, 136, 104}, {194, 140, 94},
+ {202, 140, 94}, {202, 145, 103}, {202, 145, 113}, {206, 149, 122},
+ {206, 149, 132}, {210, 141, 141}, {214, 137, 141}, {206, 137, 141},
+ {206, 137, 132}, {198, 141, 132}, {198, 145, 122}, {194, 145, 122},
+ {178, 148, 122}, {174, 161, 122}, {166, 165, 103}, {162, 178, 94},
+ {158, 170, 103}, {162, 186, 113}, {178, 186, 94}, {190, 194, 103},
+ {190, 177, 122}, {198, 173, 132}, {186, 161, 151}, {206, 145, 151},
+ {214, 141, 141}, {219, 141, 132}, {206, 141, 122}, {219, 137, 113},
+ {202, 145, 122}, {194, 140, 122}, {182, 136, 113}, {182, 132, 103},
+ {149, 120, 94}, {121, 105, 84}, {93, 76, 65}, {85, 68, 66},
+ {72, 68, 75}, {48, 48, 66}, {48, 40, 47}, {44, 44, 47},
+ {44, 40, 47}, {40, 44, 47}, {32, 40, 47}, {28, 40, 56},
+ {32, 36, 56}, {44, 44, 56}, {68, 56, 66}, {85, 81, 65},
+ {105, 101, 75}, {117, 117, 85}, {137, 133, 85}, {133, 137, 94},
+ {145, 120, 85}, {133, 108, 75}, {97, 97, 66}, {76, 68, 75},
+ },
+ /* greenland */
+ {
+ {173, 210, 188}, {169, 185, 198}, {145, 169, 198}, {141, 161, 188},
+ {137, 157, 179}, {125, 153, 169}, {120, 158, 169}, {129, 158, 170},
+ {137, 174, 169}, {162, 198, 160}, {194, 234, 197}, {206, 242, 206},
+ {229, 242, 225}, {254, 238, 225}, {254, 233, 216}, {250, 237, 216},
+ {250, 229, 216}, {234, 209, 188}, {230, 201, 179}, {222, 189, 169},
+ {206, 168, 141}, {190, 173, 132}, {214, 189, 160}, {230, 197, 169},
+ {234, 201, 188}, {230, 210, 197}, {242, 225, 216}, {250, 242, 235},
+ {238, 250, 244}, {234, 242, 254}, {218, 234, 254}, {205, 222, 254},
+ {193, 210, 235}, {181, 201, 226}, {165, 181, 216}, {141, 165, 198},
+ {137, 165, 188}, {137, 169, 188}, {145, 178, 170}, {162, 210, 169},
+ {189, 226, 207}, {197, 230, 216}, {214, 238, 225}, {222, 238, 244},
+ {222, 238, 254}, {222, 238, 254}, {221, 238, 254}, {218, 238, 254},
+ {213, 242, 244}, {197, 238, 225}, {181, 230, 216}, {177, 197, 216},
+ {137, 173, 188}, {125, 158, 169}, {120, 154, 170}, {124, 157, 179},
+ {133, 165, 188}, {153, 177, 198}, {177, 193, 226}, {189, 205, 235},
+ {201, 218, 235}, {210, 238, 225}, {214, 242, 225}, {218, 242, 226},
+ {218, 242, 235}, {218, 246, 235}, {214, 242, 235}, {214, 238, 235},
+ {214, 238, 235}, {218, 238, 235}, {218, 238, 235}, {218, 238, 235},
+ {218, 238, 225}, {214, 242, 225}, {206, 238, 216}, {198, 230, 207},
+ {181, 218, 188}, {166, 186, 160}, {150, 150, 170}, {137, 141, 170},
+ {133, 141, 160}, {125, 137, 160}, {121, 145, 169}, {120, 146, 170},
+ {124, 150, 170}, {112, 153, 169}, {116, 146, 170}, {120, 154, 170},
+ {129, 154, 169}, {145, 165, 179}, {169, 202, 188}, {189, 222, 207},
+ {189, 226, 207}, {177, 206, 179}, {174, 186, 151}, {153, 166, 151},
+ {133, 146, 151}, {133, 146, 151}, {116, 141, 151}, {108, 133, 151},
+ {104, 129, 151}, {100, 129, 151}, {104, 129, 160}, {108, 133, 160},
+ {108, 137, 160}, {112, 137, 160}, {112, 137, 160}, {113, 137, 160},
+ {116, 137, 160}, {112, 141, 160}, {112, 141, 170}, {108, 141, 170},
+ {108, 137, 170}, {108, 137, 170}, {112, 133, 160}, {116, 129, 141},
+ {104, 121, 132}, {100, 117, 132}, {100, 129, 132}, {104, 133, 141},
+ {116, 133, 141}, {116, 133, 151}, {133, 141, 160}, {145, 145, 160},
+ {166, 153, 151}, {174, 161, 151}, {182, 166, 160}, {222, 189, 179},
+ {230, 201, 188}, {206, 206, 216}, {210, 222, 235}, {222, 230, 244},
+ {222, 234, 254}, {222, 234, 254}, {226, 234, 254}, {226, 234, 254},
+ {226, 242, 254}, {226, 246, 254}, {226, 246, 254}, {230, 250, 254},
+ {234, 250, 254}, {234, 250, 254}, {230, 246, 254}, {226, 246, 254},
+ {222, 242, 254}, {218, 242, 244}, {210, 238, 225}, {193, 230, 216},
+ {202, 214, 197}, {218, 193, 169}, {202, 181, 151}, {186, 169, 160},
+ {186, 181, 170}, {189, 218, 207}, {206, 238, 226}, {214, 238, 235},
+ {222, 238, 244}, {218, 234, 244}, {213, 222, 244}, {205, 218, 244},
+ {205, 218, 244}, {197, 210, 235}, {201, 206, 226}, {193, 206, 226},
+ {189, 206, 226}, {197, 214, 226}, {214, 238, 226}, {226, 242, 235},
+ {230, 246, 244}, {230, 246, 244}, {226, 246, 244}, {222, 238, 244},
+ {222, 234, 244}, {213, 218, 244}, {201, 209, 235}, {185, 201, 226},
+ {177, 193, 216}, {161, 161, 179}, {166, 153, 179}, {170, 154, 151},
+ {178, 161, 151}, {178, 169, 151}, {202, 173, 151}, {222, 193, 169},
+ {234, 205, 188}, {246, 218, 216}, {250, 230, 225}, {230, 242, 244},
+ {226, 242, 254}, {226, 242, 254}, {226, 242, 254}, {226, 242, 254},
+ {225, 238, 254}, {222, 238, 254}, {222, 238, 254}, {222, 238, 254},
+ {222, 234, 254}, {218, 234, 235}, {217, 238, 225}, {201, 234, 207},
+ {186, 222, 179}, {162, 194, 160}, {150, 182, 151}, {129, 166, 141},
+ {125, 158, 132}, {141, 146, 122}, {145, 141, 132}, {174, 153, 132},
+ {194, 161, 132}, {202, 160, 132}, {222, 189, 169}, {234, 205, 188},
+ {238, 221, 216}, {234, 234, 235}, {226, 238, 244}, {226, 238, 254},
+ {226, 238, 254}, {226, 238, 254}, {226, 238, 254}, {230, 242, 254},
+ {230, 250, 244}, {234, 254, 244}, {246, 254, 235}, {254, 245, 235},
+ {250, 242, 254}, {238, 250, 254}, {234, 246, 254}, {234, 246, 244},
+ {234, 246, 244}, {230, 242, 244}, {226, 238, 244}, {226, 242, 244},
+ {226, 242, 244}, {226, 246, 244}, {226, 246, 244}, {226, 250, 244},
+ {226, 246, 244}, {222, 242, 244}, {222, 238, 244}, {222, 234, 244},
+ {214, 226, 244}, {205, 218, 244}, {185, 201, 235}, {173, 193, 216},
+ {145, 165, 188}, {129, 153, 170}, {125, 149, 160}, {120, 146, 160},
+ {112, 146, 160}, {112, 146, 151}, {112, 142, 160}, {112, 141, 160},
+ {112, 141, 170}, {116, 141, 170}, {116, 141, 170}, {112, 141, 169},
+ },
+ /* purple-dress */
+ {
+ {198, 96, 142}, {206, 108, 142}, {198, 136, 122}, {178, 153, 132},
+ {182, 149, 141}, {174, 149, 132}, {158, 137, 113}, {153, 120, 103},
+ {153, 111, 103}, {153, 107, 94}, {146, 104, 85}, {137, 100, 84},
+ {121, 92, 84}, {109, 89, 84}, {109, 85, 84}, {109, 85, 84},
+ {117, 84, 84}, {137, 96, 94}, {149, 104, 94}, {170, 88, 113},
+ {178, 92, 123}, {178, 88, 132}, {186, 96, 142}, {194, 100, 142},
+ {194, 104, 132}, {182, 128, 113}, {173, 132, 113}, {162, 136, 113},
+ {154, 137, 113}, {153, 133, 113}, {157, 124, 113}, {154, 116, 113},
+ {169, 92, 123}, {177, 88, 132}, {190, 100, 142}, {206, 108, 151},
+ {219, 121, 170}, {238, 141, 188}, {246, 168, 207}, {246, 205, 188},
+ {219, 173, 151}, {246, 188, 178}, {249, 225, 225}, {190, 161, 151},
+ {174, 149, 132}, {165, 128, 113}, {165, 115, 94}, {166, 111, 94},
+ {174, 120, 103}, {170, 132, 113}, {166, 141, 122}, {165, 141, 122},
+ {161, 141, 132}, {157, 137, 132}, {157, 137, 141}, {169, 141, 141},
+ {174, 150, 151}, {178, 157, 141}, {182, 157, 141}, {178, 153, 141},
+ {186, 161, 151}, {210, 117, 161}, {218, 121, 161}, {219, 121, 161},
+ {218, 112, 161}, {222, 125, 160}, {219, 157, 141}, {219, 161, 141},
+ {202, 153, 122}, {170, 140, 122}, {161, 124, 122}, {157, 96, 132},
+ {149, 88, 132}, {153, 88, 132}, {157, 84, 122}, {161, 80, 123},
+ {169, 84, 142}, {173, 92, 142}, {177, 88, 142}, {169, 88, 132},
+ {157, 84, 122}, {149, 80, 113}, {141, 80, 103}, {137, 92, 94},
+ {145, 104, 94}, {146, 116, 94}, {145, 124, 103}, {150, 129, 103},
+ {154, 128, 103}, {161, 124, 94}, {158, 116, 94}, {158, 112, 94},
+ {153, 124, 113}, {150, 128, 113}, {150, 133, 113}, {145, 133, 113},
+ {145, 133, 122}, {153, 129, 132}, {153, 133, 132}, {154, 133, 132},
+ {154, 133, 122}, {153, 133, 122}, {153, 133, 113}, {161, 128, 113},
+ {165, 120, 113}, {186, 104, 123}, {190, 96, 123}, {198, 100, 132},
+ {202, 104, 132}, {198, 104, 132}, {182, 100, 132}, {170, 112, 123},
+ {157, 128, 122}, {162, 137, 122}, {162, 140, 122}, {161, 133, 113},
+ {170, 124, 113}, {186, 104, 132}, {198, 104, 132}, {202, 104, 142},
+ {206, 104, 142}, {206, 104, 142}, {202, 100, 142}, {202, 96, 142},
+ {198, 100, 142}, {198, 104, 142}, {202, 104, 142}, {198, 100, 151},
+ {198, 101, 151}, {198, 108, 151}, {190, 100, 151}, {198, 100, 151},
+ {194, 100, 142}, {190, 104, 142}, {182, 104, 132}, {165, 120, 113},
+ {158, 124, 113}, {158, 120, 113}, {153, 108, 113}, {153, 88, 122},
+ {165, 92, 122}, {166, 104, 104}, {157, 112, 104}, {157, 120, 113},
+ {154, 120, 113}, {149, 125, 113}, {149, 116, 113}, {141, 112, 103},
+ {133, 112, 103}, {133, 108, 103}, {133, 100, 94}, {117, 92, 84},
+ {109, 85, 84}, {97, 81, 84}, {97, 85, 84}, {117, 96, 84},
+ {145, 104, 94}, {157, 111, 103}, {174, 100, 123}, {186, 96, 132},
+ {190, 100, 132}, {190, 92, 132}, {182, 100, 132}, {157, 120, 122},
+ {141, 112, 113}, {133, 104, 103}, {117, 100, 103}, {121, 108, 113},
+ {137, 116, 113}, {145, 124, 113}, {149, 129, 122}, {153, 132, 122},
+ {157, 133, 122}, {161, 141, 122}, {165, 141, 122}, {165, 137, 132},
+ {166, 137, 132}, {186, 104, 142}, {194, 100, 142}, {198, 100, 142},
+ {198, 104, 142}, {194, 100, 142}, {190, 104, 132}, {178, 124, 113},
+ {162, 124, 104}, {145, 120, 113}, {129, 112, 103}, {108, 96, 94},
+ {100, 88, 94}, {109, 89, 84}, {121, 96, 84}, {133, 100, 94},
+ {141, 104, 94}, {137, 104, 94}, {121, 100, 94}, {108, 92, 94},
+ {100, 84, 94}, {97, 81, 84}, {89, 81, 75}, {89, 73, 75},
+ {89, 73, 75}, {93, 77, 84}, {104, 84, 93}, {133, 92, 94},
+ {161, 84, 122}, {186, 96, 142}, {198, 104, 151}, {210, 112, 161},
+ {222, 125, 170}, {238, 137, 188}, {238, 137, 188}, {234, 133, 189},
+ {222, 121, 170}, {210, 112, 151}, {202, 132, 122}, {190, 136, 113},
+ {202, 149, 113}, {186, 132, 104}, {182, 128, 103}, {182, 123, 104},
+ {166, 120, 113}, {169, 88, 123}, {165, 84, 123}, {161, 80, 122},
+ {153, 76, 122}, {153, 84, 122}, {137, 92, 113}, {141, 100, 103},
+ {141, 112, 113}, {149, 120, 122}, {145, 116, 122}, {145, 120, 122},
+ {149, 124, 122}, {157, 133, 122}, {166, 141, 122}, {170, 145, 122},
+ {170, 145, 122}, {170, 145, 132}, {162, 141, 132}, {166, 141, 132},
+ {170, 137, 132}, {194, 108, 151}, {198, 112, 161}, {198, 108, 161},
+ {202, 100, 161}, {206, 104, 151}, {206, 108, 151}, {210, 108, 151},
+ {206, 100, 151}, {202, 100, 151}, {202, 100, 151}, {202, 96, 142},
+ {198, 96, 142}, {194, 92, 132}, {178, 88, 123}, {166, 84, 123},
+ },
+ /* no-name */
+ {
+ {121, 92, 75}, {162, 132, 103}, {182, 152, 122}, {198, 169, 141},
+ {210, 189, 160}, {206, 210, 188}, {194, 198, 188}, {202, 185, 170},
+ {206, 181, 160}, {206, 177, 151}, {202, 177, 160}, {198, 177, 151},
+ {194, 173, 160}, {194, 173, 160}, {194, 173, 160}, {198, 173, 151},
+ {194, 165, 151}, {186, 157, 141}, {186, 153, 132}, {186, 148, 122},
+ {170, 128, 94}, {145, 84, 66}, {109, 64, 37}, {81, 68, 37},
+ {64, 60, 37}, {48, 56, 37}, {56, 52, 28}, {48, 60, 37},
+ {56, 60, 37}, {60, 60, 37}, {72, 68, 37}, {89, 51, 28},
+ {109, 59, 28}, {133, 72, 28}, {166, 115, 66}, {186, 148, 103},
+ {190, 157, 132}, {194, 165, 151}, {190, 181, 170}, {202, 206, 207},
+ {202, 214, 207}, {197, 214, 216}, {197, 214, 216}, {197, 214, 216},
+ {193, 214, 216}, {197, 214, 216}, {201, 214, 216}, {206, 214, 216},
+ {206, 210, 216}, {206, 214, 207}, {202, 214, 198}, {194, 206, 188},
+ {186, 182, 169}, {190, 173, 141}, {194, 173, 113}, {202, 173, 113},
+ {214, 181, 122}, {230, 193, 141}, {230, 201, 160}, {234, 201, 169},
+ {234, 205, 169}, {234, 205, 169}, {230, 201, 169}, {230, 197, 160},
+ {222, 189, 160}, {202, 169, 141}, {182, 153, 122}, {158, 141, 94},
+ {121, 76, 56}, {85, 52, 37}, {68, 35, 28}, {60, 40, 37},
+ {48, 39, 37}, {52, 44, 28}, {44, 44, 37}, {44, 44, 37},
+ {44, 52, 37}, {40, 44, 28}, {36, 44, 37}, {40, 48, 47},
+ {44, 44, 47}, {48, 44, 47}, {56, 48, 56}, {68, 48, 56},
+ {72, 52, 47}, {92, 68, 46}, {105, 93, 65}, {137, 141, 85},
+ {146, 154, 94}, {154, 153, 103}, {158, 157, 113}, {129, 141, 132},
+ {84, 97, 85}, {64, 81, 66}, {44, 60, 47}, {36, 44, 47},
+ {28, 32, 28}, {31, 24, 18}, {24, 28, 28}, {20, 28, 28},
+ {20, 24, 28}, {20, 24, 28}, {20, 24, 28}, {20, 32, 28},
+ {20, 32, 28}, {20, 32, 28}, {20, 36, 28}, {32, 36, 37},
+ {40, 44, 47}, {52, 52, 47}, {64, 52, 47}, {72, 68, 56},
+ {93, 105, 66}, {133, 146, 94}, {178, 169, 113}, {222, 193, 151},
+ {234, 205, 169}, {242, 217, 188}, {222, 226, 207}, {222, 226, 216},
+ {226, 222, 216}, {226, 230, 216}, {218, 226, 216}, {202, 218, 216},
+ {201, 214, 216}, {201, 210, 216}, {202, 218, 216}, {206, 218, 207},
+ {210, 218, 207}, {234, 209, 179}, {230, 205, 169}, {226, 193, 160},
+ {202, 173, 132}, {190, 165, 113}, {162, 149, 94}, {113, 97, 75},
+ {85, 80, 56}, {64, 68, 47}, {52, 56, 47}, {44, 48, 47},
+ {40, 52, 47}, {44, 52, 56}, {52, 60, 56}, {72, 72, 66},
+ {93, 80, 65}, {105, 121, 85}, {137, 150, 94}, {166, 165, 103},
+ {178, 153, 113}, {190, 169, 122}, {194, 169, 132}, {198, 169, 141},
+ {202, 185, 160}, {202, 214, 198}, {198, 214, 207}, {198, 210, 207},
+ {189, 210, 207}, {181, 198, 188}, {190, 177, 170}, {182, 161, 151},
+ {178, 145, 122}, {146, 92, 75}, {129, 68, 46}, {129, 68, 47},
+ {141, 112, 85}, {186, 153, 122}, {219, 193, 160}, {234, 209, 178},
+ {214, 214, 207}, {210, 218, 207}, {206, 218, 207}, {206, 214, 198},
+ {194, 177, 170}, {186, 153, 141}, {169, 140, 113}, {125, 104, 75},
+ {97, 80, 75}, {77, 77, 66}, {72, 76, 75}, {97, 109, 75},
+ {146, 146, 94}, {182, 157, 122}, {202, 173, 141}, {219, 185, 151},
+ {226, 189, 141}, {214, 164, 132}, {198, 161, 113}, {182, 140, 94},
+ {141, 92, 66}, {109, 59, 46}, {117, 72, 46}, {146, 88, 66},
+ {182, 131, 94}, {202, 157, 122}, {230, 193, 141}, {242, 201, 150},
+ {242, 213, 169}, {242, 213, 178}, {238, 213, 178}, {230, 201, 160},
+ {226, 193, 141}, {206, 168, 113}, {194, 161, 94}, {170, 153, 103},
+ {137, 145, 85}, {109, 104, 66}, {77, 77, 56}, {64, 64, 56},
+ {64, 64, 56}, {76, 72, 66}, {97, 88, 75}, {129, 125, 85},
+ {169, 149, 122}, {182, 145, 132}, {190, 145, 132}, {190, 157, 132},
+ {190, 157, 132}, {194, 161, 132}, {186, 157, 132}, {178, 145, 122},
+ {137, 104, 84}, {109, 64, 56}, {72, 64, 56}, {64, 60, 56},
+ {60, 64, 47}, {60, 68, 47}, {60, 64, 47}, {68, 64, 56},
+ {93, 81, 65}, {125, 133, 85}, {150, 145, 103}, {178, 153, 122},
+ {186, 165, 132}, {190, 165, 132}, {190, 161, 141}, {194, 165, 141},
+ {190, 165, 132}, {190, 165, 132}, {190, 164, 132}, {190, 161, 122},
+ {174, 152, 113}, {150, 145, 94}, {113, 84, 65}, {89, 72, 56},
+ {68, 64, 47}, {52, 48, 37}, {36, 28, 37}, {28, 28, 37},
+ {19, 32, 37}, {16, 24, 28}, {19, 20, 18}, {16, 20, 28},
+ {20, 24, 28}, {24, 28, 28}, {32, 40, 28}, {48, 44, 28},
+ },
+ /* spring-flora */
+ {
+ {51, 67, 36}, {53, 66, 34}, {51, 62, 29}, {40, 51, 23},
+ {40, 42, 26}, {37, 43, 25}, {36, 48, 25}, {32, 46, 23},
+ {34, 44, 22}, {21, 28, 17}, {18, 19, 14}, {16, 17, 11},
+ {15, 17, 11}, {13, 15, 10}, {13, 14, 12}, {13, 14, 12},
+ {15, 17, 13}, {17, 21, 14}, {26, 35, 21}, {34, 44, 28},
+ {42, 51, 39}, {49, 59, 39}, {50, 64, 54}, {55, 57, 63},
+ {57, 54, 66}, {55, 37, 59}, {50, 27, 51}, {44, 38, 28},
+ {26, 32, 18}, {17, 20, 14}, {15, 18, 12}, {14, 16, 10},
+ {14, 14, 9}, {18, 18, 13}, {19, 23, 16}, {31, 41, 36},
+ {44, 40, 58}, {52, 48, 72}, {66, 53, 79}, {65, 63, 76},
+ {63, 61, 68}, {54, 63, 42}, {53, 66, 34}, {46, 67, 34},
+ {46, 62, 32}, {35, 51, 24}, {26, 35, 19}, {18, 19, 14},
+ {12, 13, 9}, {9, 10, 7}, {8, 6, 5}, {7, 5, 4},
+ {9, 6, 6}, {11, 9, 9}, {14, 15, 11}, {21, 22, 15},
+ {44, 31, 50}, {48, 27, 54}, {55, 39, 59}, {53, 32, 59},
+ {48, 38, 62}, {44, 43, 55}, {50, 59, 42}, {53, 67, 39},
+ {54, 72, 45}, {58, 79, 44}, {63, 87, 44}, {61, 87, 42},
+ {58, 79, 42}, {56, 74, 36}, {50, 72, 34}, {45, 66, 34},
+ {36, 57, 31}, {32, 44, 26}, {23, 33, 20}, {16, 20, 15},
+ {15, 19, 12}, {17, 19, 13}, {17, 20, 15}, {23, 33, 22},
+ {30, 46, 29}, {36, 55, 32}, {41, 62, 33}, {46, 67, 34},
+ {48, 66, 36}, {50, 66, 37}, {50, 66, 37}, {53, 66, 37},
+ {51, 67, 39}, {53, 67, 38}, {55, 72, 42}, {57, 72, 42},
+ {52, 72, 45}, {52, 72, 45}, {49, 72, 45}, {49, 72, 42},
+ {50, 72, 37}, {50, 72, 37}, {47, 72, 42}, {51, 74, 39},
+ {57, 72, 34}, {55, 72, 39}, {57, 75, 39}, {61, 79, 39},
+ {70, 81, 37}, {76, 89, 51}, {82, 102, 67}, {86, 112, 73},
+ {100, 114, 76}, {89, 118, 60}, {102, 126, 88}, {114, 140, 97},
+ {135, 152, 98}, {112, 127, 93}, {118, 140, 93}, {191, 174, 163},
+ {143, 154, 113}, {99, 127, 100}, {93, 120, 59}, {79, 102, 60},
+ {82, 103, 67}, {83, 104, 71}, {86, 103, 72}, {86, 103, 64},
+ {87, 87, 52}, {73, 88, 50}, {76, 80, 52}, {62, 87, 57},
+ {70, 88, 50}, {71, 84, 56}, {70, 75, 50}, {67, 74, 44},
+ {57, 72, 42}, {52, 72, 42}, {47, 66, 40}, {41, 54, 36},
+ {34, 42, 25}, {21, 27, 16}, {16, 19, 12}, {12, 11, 10},
+ {10, 8, 7}, {7, 5, 5}, {6, 4, 4}, {5, 4, 3},
+ {5, 3, 4}, {5, 4, 3}, {5, 5, 5}, {6, 5, 5},
+ {6, 4, 5}, {7, 4, 5}, {7, 4, 5}, {7, 5, 5},
+ {8, 6, 6}, {10, 8, 7}, {13, 11, 10}, {14, 15, 11},
+ {19, 21, 15}, {39, 33, 23}, {48, 47, 30}, {60, 57, 53},
+ {79, 65, 97}, {74, 58, 76}, {106, 59, 79}, {96, 90, 85},
+ {84, 62, 85}, {88, 79, 94}, {80, 82, 72}, {70, 69, 59},
+ {57, 69, 54}, {60, 79, 47}, {58, 79, 47}, {58, 79, 46},
+ {54, 79, 44}, {53, 79, 44}, {53, 79, 44}, {53, 79, 47},
+ {55, 79, 50}, {60, 79, 50}, {62, 87, 49}, {60, 87, 52},
+ {61, 94, 52}, {67, 94, 52}, {76, 94, 56}, {86, 118, 56},
+ {80, 96, 53}, {83, 109, 44}, {83, 110, 55}, {76, 103, 55},
+ {73, 102, 54}, {68, 88, 50}, {63, 79, 44}, {57, 72, 39},
+ {55, 72, 34}, {53, 67, 34}, {50, 66, 37}, {44, 62, 36},
+ {44, 55, 38}, {41, 57, 35}, {40, 61, 36}, {41, 62, 38},
+ {44, 72, 42}, {55, 79, 41}, {61, 87, 47}, {67, 94, 47},
+ {77, 103, 53}, {89, 118, 57}, {86, 117, 57}, {89, 118, 76},
+ {114, 127, 85}, {106, 126, 97}, {93, 119, 76}, {82, 117, 72},
+ {73, 102, 59}, {60, 87, 52}, {49, 72, 45}, {39, 57, 34},
+ {32, 46, 27}, {23, 33, 18}, {17, 23, 13}, {17, 21, 13},
+ {17, 20, 14}, {22, 27, 17}, {33, 42, 25}, {36, 47, 28},
+ {44, 57, 31}, {49, 62, 33}, {49, 63, 35}, {45, 58, 33},
+ {37, 51, 27}, {32, 44, 23}, {23, 33, 17}, {16, 21, 13},
+ {12, 15, 8}, {11, 13, 7}, {11, 11, 7}, {10, 14, 8},
+ {12, 12, 9}, {13, 15, 8}, {15, 19, 11}, {19, 23, 15},
+ {29, 44, 22}, {35, 52, 29}, {39, 57, 30}, {46, 67, 28},
+ {50, 66, 28}, {47, 72, 29}, {47, 63, 30}, {39, 57, 27},
+ {32, 51, 25}, {28, 40, 25}, {21, 26, 17}, {19, 23, 15},
+ {28, 35, 21}, {35, 47, 26}, {46, 62, 33}, {53, 67, 39},
+ },
+ /* andi */
+ {
+ {53, 15, 5}, {77, 47, 22}, {92, 69, 39}, {112, 83, 55},
+ {126, 90, 59}, {138, 87, 63}, {140, 90, 66}, {140, 93, 64},
+ {137, 88, 50}, {122, 82, 32}, {100, 62, 19}, {72, 31, 12},
+ {52, 2, 1}, {20, 0, 0}, {2, 0, 0}, {2, 0, 0},
+ {24, 21, 12}, {72, 34, 30}, {82, 53, 47}, {97, 62, 41},
+ {108, 71, 42}, {122, 77, 55}, {132, 81, 65}, {136, 92, 71},
+ {137, 92, 71}, {137, 94, 71}, {136, 92, 71}, {135, 82, 69},
+ {132, 74, 61}, {115, 67, 53}, {106, 59, 54}, {85, 45, 42},
+ {48, 13, 21}, {16, 2, 1}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {2, 0, 0}, {46, 17, 5}, {73, 36, 18},
+ {84, 60, 38}, {116, 76, 52}, {137, 83, 59}, {148, 94, 71},
+ {157, 104, 76}, {162, 109, 79}, {160, 118, 82}, {156, 116, 79},
+ {153, 110, 79}, {148, 102, 73}, {148, 93, 72}, {143, 87, 65},
+ {145, 81, 58}, {144, 81, 55}, {146, 79, 49}, {140, 81, 48},
+ {143, 84, 48}, {149, 88, 63}, {151, 91, 65}, {148, 90, 67},
+ {145, 87, 68}, {144, 87, 68}, {138, 91, 68}, {137, 88, 62},
+ {118, 85, 56}, {111, 83, 43}, {100, 74, 36}, {100, 63, 33},
+ {100, 56, 24}, {94, 63, 20}, {96, 52, 20}, {87, 56, 18},
+ {88, 48, 14}, {72, 38, 7}, {68, 23, 6}, {69, 20, 11},
+ {83, 34, 12}, {97, 43, 28}, {100, 46, 40}, {114, 66, 50},
+ {122, 87, 64}, {129, 104, 76}, {142, 119, 101}, {162, 134, 122},
+ {170, 150, 140}, {168, 162, 155}, {184, 175, 167}, {178, 167, 159},
+ {174, 157, 148}, {164, 151, 133}, {163, 143, 115}, {149, 127, 91},
+ {140, 106, 63}, {117, 76, 51}, {89, 46, 30}, {57, 13, 4},
+ {23, 0, 0}, {2, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {19, 6, 1}, {56, 21, 8}, {86, 50, 35}, {108, 57, 48},
+ {126, 65, 49}, {130, 68, 57}, {128, 72, 48}, {133, 65, 46},
+ {128, 68, 40}, {135, 65, 29}, {120, 66, 24}, {130, 57, 20},
+ {97, 47, 11}, {73, 23, 7}, {63, 14, 2}, {54, 0, 0},
+ {54, 0, 0}, {65, 18, 11}, {83, 55, 30}, {89, 61, 37},
+ {105, 74, 43}, {116, 89, 52}, {130, 96, 61}, {129, 97, 66},
+ {132, 96, 71}, {141, 100, 78}, {142, 109, 76}, {145, 108, 76},
+ {149, 114, 80}, {151, 116, 80}, {151, 117, 84}, {154, 119, 87},
+ {170, 128, 103}, {177, 137, 108}, {173, 147, 120}, {172, 152, 124},
+ {176, 156, 141}, {187, 171, 142}, {182, 172, 146}, {174, 157, 140},
+ {172, 156, 121}, {161, 145, 106}, {155, 136, 98}, {153, 132, 93},
+ {148, 118, 87}, {150, 108, 77}, {149, 100, 74}, {150, 93, 72},
+ {145, 92, 61}, {143, 89, 62}, {140, 85, 57}, {140, 89, 52},
+ {138, 100, 54}, {134, 101, 59}, {135, 99, 59}, {141, 97, 64},
+ {142, 94, 66}, {138, 98, 59}, {132, 99, 54}, {130, 96, 49},
+ {120, 91, 38}, {105, 60, 17}, {94, 14, 3}, {61, 0, 0},
+ {59, 4, 2}, {68, 13, 17}, {88, 27, 10}, {96, 48, 19},
+ {111, 73, 37}, {125, 88, 42}, {130, 94, 50}, {140, 96, 58},
+ {148, 97, 69}, {150, 98, 73}, {153, 103, 80}, {158, 110, 79},
+ {155, 110, 84}, {158, 110, 80}, {157, 105, 79}, {155, 102, 75},
+ {152, 98, 73}, {154, 97, 72}, {163, 96, 70}, {164, 106, 80},
+ {165, 112, 81}, {164, 117, 80}, {163, 118, 81}, {162, 120, 81},
+ {158, 117, 80}, {152, 109, 77}, {140, 100, 73}, {126, 87, 68},
+ {118, 80, 61}, {105, 63, 45}, {91, 53, 25}, {88, 44, 12},
+ {86, 40, 15}, {104, 35, 17}, {117, 57, 30}, {117, 68, 42},
+ {131, 80, 42}, {127, 82, 47}, {124, 86, 46}, {126, 82, 39},
+ {120, 86, 40}, {109, 68, 30}, {104, 60, 25}, {95, 65, 24},
+ {101, 66, 21}, {103, 65, 36}, {107, 72, 39}, {116, 82, 42},
+ {124, 88, 46}, {124, 91, 52}, {121, 100, 56}, {126, 102, 62},
+ {131, 100, 70}, {136, 96, 72}, {140, 95, 72}, {142, 93, 72},
+ {142, 96, 72}, {143, 93, 68}, {140, 93, 70}, {140, 90, 70},
+ {142, 89, 70}, {140, 82, 64}, {137, 75, 60}, {132, 77, 55},
+ {125, 87, 60}, {120, 89, 62}, {128, 104, 70}, {136, 111, 76},
+ {147, 118, 89}, {156, 135, 117}, {159, 145, 124}, {174, 148, 130},
+ {179, 151, 125}, {174, 151, 119}, {173, 148, 113}, {178, 146, 100},
+ {170, 143, 112}, {161, 138, 109}, {161, 134, 92}, {160, 125, 81},
+ {161, 135, 91}, {170, 123, 85}, {164, 122, 88}, {153, 130, 80},
+ {152, 118, 84}, {148, 119, 80}, {139, 111, 77}, {141, 111, 73},
+ {137, 109, 79}, {135, 108, 93}, {143, 111, 94}, {148, 128, 100},
+ },
+ /* gig-o835 */
+ {
+ {56, 97, 85}, {76, 105, 84}, {77, 117, 84}, {76, 109, 94},
+ {84, 109, 94}, {88, 113, 94}, {92, 125, 94}, {101, 121, 103},
+ {105, 133, 113}, {113, 145, 122}, {113, 149, 122}, {101, 141, 113},
+ {92, 125, 103}, {84, 113, 94}, {81, 101, 75}, {56, 97, 66},
+ {52, 89, 66}, {36, 81, 66}, {28, 77, 66}, {44, 72, 47},
+ {20, 60, 47}, {20, 60, 47}, {16, 68, 66}, {16, 68, 66},
+ {4, 68, 75}, {4, 81, 84}, {12, 89, 103}, {16, 101, 122},
+ {28, 101, 122}, {28, 105, 122}, {28, 113, 122}, {36, 105, 122},
+ {36, 105, 122}, {48, 105, 122}, {52, 105, 113}, {48, 109, 103},
+ {44, 101, 93}, {40, 97, 84}, {48, 89, 75}, {40, 76, 56},
+ {24, 72, 56}, {28, 72, 56}, {24, 76, 56}, {40, 72, 66},
+ {48, 89, 75}, {56, 93, 84}, {68, 105, 103}, {68, 117, 122},
+ {68, 117, 122}, {72, 117, 122}, {72, 121, 113}, {76, 121, 113},
+ {84, 125, 103}, {89, 125, 103}, {92, 121, 113}, {93, 129, 103},
+ {104, 145, 122}, {100, 157, 141}, {121, 162, 150}, {125, 166, 169},
+ {161, 182, 169}, {173, 210, 197}, {161, 186, 160}, {137, 178, 179},
+ {121, 165, 160}, {108, 145, 141}, {92, 141, 131}, {80, 129, 131},
+ {80, 125, 122}, {76, 125, 122}, {76, 129, 112}, {72, 125, 103},
+ {72, 117, 103}, {72, 117, 103}, {68, 121, 113}, {60, 121, 112},
+ {52, 117, 122}, {48, 121, 131}, {40, 117, 141}, {40, 113, 141},
+ {48, 121, 150}, {56, 117, 160}, {64, 125, 150}, {76, 125, 132},
+ {89, 138, 132}, {100, 146, 141}, {112, 154, 150}, {117, 149, 151},
+ {116, 145, 150}, {112, 149, 150}, {100, 146, 160}, {80, 142, 160},
+ {60, 130, 150}, {48, 121, 141}, {36, 117, 131}, {36, 113, 131},
+ {56, 113, 131}, {68, 121, 131}, {76, 125, 132}, {84, 137, 131},
+ {96, 142, 141}, {100, 149, 150}, {100, 146, 160}, {104, 150, 160},
+ {100, 142, 160}, {100, 133, 150}, {92, 129, 132}, {88, 125, 122},
+ {89, 121, 103}, {80, 113, 103}, {72, 105, 94}, {72, 101, 85},
+ {64, 93, 75}, {36, 93, 84}, {28, 97, 93}, {12, 81, 84},
+ {12, 89, 103}, {4, 77, 103}, {16, 93, 103}, {19, 97, 103},
+ {32, 93, 94}, {48, 97, 94}, {64, 105, 94}, {76, 117, 94},
+ {93, 121, 103}, {97, 121, 103}, {92, 125, 113}, {84, 125, 113},
+ {92, 129, 113}, {84, 133, 113}, {88, 129, 122}, {88, 129, 122},
+ {92, 129, 122}, {96, 129, 122}, {108, 129, 122}, {112, 141, 132},
+ {109, 153, 131}, {108, 146, 141}, {113, 149, 141}, {125, 149, 132},
+ {113, 150, 132}, {108, 153, 141}, {108, 162, 150}, {113, 166, 150},
+ {112, 154, 141}, {116, 149, 141}, {117, 153, 141}, {120, 154, 151},
+ {125, 162, 151}, {133, 162, 151}, {141, 170, 151}, {145, 174, 160},
+ {141, 178, 169}, {137, 182, 179}, {133, 162, 160}, {125, 162, 141},
+ {121, 157, 132}, {117, 146, 122}, {109, 145, 122}, {96, 133, 113},
+ {88, 121, 113}, {84, 113, 103}, {76, 113, 103}, {68, 113, 103},
+ {60, 105, 103}, {52, 101, 94}, {48, 101, 103}, {28, 101, 112},
+ {32, 109, 131}, {28, 105, 132}, {32, 113, 132}, {39, 117, 131},
+ {56, 121, 132}, {68, 125, 131}, {68, 125, 131}, {76, 129, 131},
+ {73, 130, 132}, {72, 134, 141}, {81, 134, 151}, {80, 134, 160},
+ {84, 134, 150}, {84, 134, 141}, {88, 129, 132}, {100, 133, 132},
+ {100, 137, 132}, {100, 142, 132}, {100, 137, 141}, {100, 142, 141},
+ {97, 142, 132}, {92, 146, 121}, {88, 133, 122}, {88, 129, 122},
+ {88, 129, 122}, {88, 133, 122}, {96, 137, 122}, {96, 137, 122},
+ {108, 145, 132}, {112, 141, 132}, {108, 145, 141}, {116, 141, 141},
+ {108, 141, 150}, {96, 146, 160}, {72, 130, 160}, {60, 126, 150},
+ {44, 117, 141}, {36, 109, 131}, {40, 105, 122}, {44, 109, 122},
+ {60, 113, 122}, {72, 121, 122}, {85, 129, 122}, {96, 133, 131},
+ {100, 133, 132}, {104, 133, 132}, {104, 137, 141}, {108, 137, 141},
+ {104, 133, 141}, {100, 133, 132}, {92, 121, 122}, {88, 117, 122},
+ {76, 117, 113}, {80, 117, 103}, {76, 117, 103}, {72, 117, 103},
+ {68, 113, 103}, {64, 113, 113}, {56, 109, 113}, {56, 109, 113},
+ {48, 113, 113}, {52, 113, 112}, {60, 117, 122}, {73, 126, 132},
+ {80, 130, 160}, {88, 138, 170}, {80, 138, 170}, {68, 134, 160},
+ {56, 130, 160}, {40, 117, 160}, {28, 113, 141}, {20, 101, 141},
+ {24, 97, 132}, {16, 97, 122}, {20, 97, 112}, {36, 97, 94},
+ {48, 101, 93}, {60, 105, 94}, {72, 117, 94}, {77, 121, 103},
+ {84, 125, 122}, {84, 134, 132}, {88, 134, 150}, {88, 137, 160},
+ {84, 142, 160}, {76, 133, 141}, {80, 125, 132}, {80, 121, 122},
+ },
+ /* rie02 */
+ {
+ {48, 72, 37}, {60, 85, 28}, {48, 88, 28}, {60, 76, 37},
+ {56, 64, 37}, {48, 52, 28}, {44, 48, 28}, {36, 44, 28},
+ {24, 36, 18}, {20, 36, 18}, {24, 40, 18}, {28, 44, 18},
+ {36, 40, 18}, {36, 28, 18}, {20, 20, 18}, {12, 16, 9},
+ {16, 20, 9}, {20, 24, 9}, {28, 28, 9}, {40, 44, 18},
+ {56, 52, 28}, {68, 64, 37}, {88, 72, 47}, {85, 93, 47},
+ {85, 100, 56}, {88, 96, 75}, {93, 97, 75}, {92, 97, 66},
+ {85, 101, 56}, {88, 105, 66}, {89, 105, 75}, {84, 105, 66},
+ {89, 113, 66}, {113, 141, 85}, {133, 157, 122}, {141, 190, 150},
+ {145, 182, 169}, {129, 189, 188}, {169, 181, 179}, {177, 189, 169},
+ {210, 230, 160}, {222, 234, 179}, {246, 241, 159}, {214, 230, 141},
+ {190, 218, 160}, {198, 226, 122}, {234, 246, 121}, {218, 209, 112},
+ {157, 173, 93}, {125, 149, 94}, {121, 149, 84}, {101, 125, 66},
+ {93, 109, 66}, {89, 101, 47}, {89, 117, 47}, {101, 113, 47},
+ {113, 125, 56}, {125, 133, 75}, {153, 141, 84}, {153, 157, 103},
+ {162, 194, 103}, {182, 202, 103}, {182, 190, 84}, {194, 152, 112},
+ {137, 129, 94}, {113, 113, 75}, {89, 109, 66}, {72, 92, 56},
+ {60, 89, 56}, {56, 92, 56}, {60, 93, 47}, {64, 88, 47},
+ {56, 89, 56}, {52, 89, 56}, {52, 80, 56}, {56, 80, 56},
+ {48, 72, 47}, {44, 72, 47}, {36, 60, 47}, {32, 52, 56},
+ {36, 56, 66}, {36, 56, 56}, {36, 68, 56}, {48, 88, 56},
+ {68, 101, 65}, {97, 125, 85}, {113, 149, 94}, {145, 170, 122},
+ {149, 177, 112}, {125, 153, 103}, {97, 121, 84}, {85, 93, 66},
+ {60, 64, 66}, {36, 44, 47}, {20, 36, 28}, {12, 24, 18},
+ {8, 16, 0}, {12, 12, 0}, {20, 24, 18}, {32, 48, 28},
+ {44, 60, 37}, {68, 76, 47}, {72, 89, 47}, {64, 84, 47},
+ {81, 88, 47}, {72, 93, 28}, {60, 76, 47}, {56, 56, 47},
+ {44, 56, 37}, {32, 52, 28}, {24, 52, 28}, {20, 44, 18},
+ {28, 48, 9}, {24, 48, 18}, {28, 56, 28}, {24, 56, 37},
+ {24, 56, 37}, {28, 56, 37}, {36, 60, 47}, {48, 72, 37},
+ {52, 88, 37}, {60, 97, 37}, {60, 93, 47}, {60, 96, 46},
+ {48, 76, 47}, {40, 68, 37}, {32, 52, 28}, {28, 56, 28},
+ {16, 44, 18}, {16, 40, 18}, {16, 44, 9}, {28, 48, 9},
+ {44, 52, 28}, {36, 56, 18}, {40, 64, 18}, {36, 72, 37},
+ {44, 76, 37}, {44, 76, 37}, {40, 72, 37}, {44, 56, 28},
+ {36, 52, 28}, {28, 48, 28}, {32, 40, 28}, {32, 32, 18},
+ {24, 32, 28}, {20, 40, 28}, {28, 40, 37}, {28, 36, 28},
+ {24, 40, 37}, {24, 44, 37}, {24, 52, 47}, {28, 44, 47},
+ {28, 40, 37}, {28, 48, 37}, {20, 40, 37}, {12, 40, 28},
+ {20, 32, 18}, {16, 20, 18}, {12, 20, 18}, {12, 24, 9},
+ {12, 24, 9}, {16, 36, 18}, {20, 52, 28}, {24, 60, 37},
+ {32, 76, 47}, {44, 80, 56}, {44, 81, 47}, {48, 97, 47},
+ {60, 121, 37}, {64, 109, 37}, {72, 101, 47}, {60, 93, 56},
+ {56, 89, 75}, {60, 80, 94}, {76, 93, 103}, {88, 121, 94},
+ {101, 129, 75}, {97, 145, 75}, {93, 133, 56}, {113, 157, 65},
+ {93, 121, 66}, {85, 117, 47}, {77, 101, 47}, {68, 85, 37},
+ {60, 68, 37}, {60, 56, 37}, {56, 52, 37}, {60, 60, 47},
+ {80, 80, 56}, {88, 104, 75}, {117, 129, 94}, {149, 173, 113},
+ {166, 186, 141}, {166, 198, 122}, {173, 186, 132}, {162, 178, 122},
+ {133, 149, 94}, {117, 133, 75}, {93, 113, 75}, {84, 105, 75},
+ {60, 97, 75}, {64, 68, 66}, {44, 64, 75}, {36, 52, 66},
+ {28, 56, 66}, {28, 56, 66}, {32, 48, 56}, {36, 48, 47},
+ {52, 48, 28}, {48, 56, 37}, {48, 44, 28}, {44, 52, 28},
+ {56, 56, 28}, {60, 60, 28}, {64, 68, 37}, {80, 80, 56},
+ {105, 101, 66}, {129, 129, 85}, {133, 133, 113}, {129, 137, 122},
+ {117, 145, 103}, {121, 157, 84}, {113, 145, 66}, {97, 117, 66},
+ {76, 105, 56}, {72, 113, 56}, {68, 129, 56}, {60, 125, 55},
+ {52, 109, 65}, {52, 97, 65}, {64, 105, 75}, {89, 133, 94},
+ {125, 149, 113}, {162, 174, 132}, {153, 198, 151}, {170, 198, 141},
+ {169, 206, 141}, {174, 210, 132}, {181, 214, 131}, {169, 214, 140},
+ {174, 222, 141}, {182, 206, 151}, {190, 202, 150}, {198, 198, 150},
+ {218, 213, 141}, {214, 214, 160}, {189, 206, 141}, {190, 210, 103},
+ {174, 210, 75}, {117, 153, 56}, {105, 133, 56}, {105, 125, 56},
+ {109, 109, 56}, {93, 113, 66}, {105, 109, 75}, {117, 125, 113},
+ },
+ /* rie05 */
+ {
+ {73, 125, 84}, {77, 141, 83}, {89, 146, 83}, {104, 154, 94},
+ {109, 170, 103}, {146, 198, 113}, {162, 214, 132}, {182, 226, 160},
+ {198, 234, 160}, {206, 234, 169}, {218, 242, 178}, {222, 238, 169},
+ {218, 226, 150}, {222, 230, 141}, {218, 230, 131}, {210, 222, 131},
+ {206, 222, 122}, {190, 219, 113}, {203, 226, 122}, {206, 226, 103},
+ {218, 226, 94}, {211, 222, 94}, {198, 222, 75}, {170, 210, 94},
+ {170, 206, 103}, {150, 194, 113}, {137, 174, 113}, {121, 170, 103},
+ {129, 174, 113}, {129, 182, 113}, {146, 198, 132}, {162, 222, 160},
+ {182, 218, 169}, {210, 234, 197}, {230, 246, 206}, {230, 246, 216},
+ {230, 242, 207}, {222, 234, 197}, {214, 238, 188}, {214, 234, 169},
+ {214, 230, 150}, {210, 230, 131}, {198, 230, 122}, {186, 219, 122},
+ {170, 206, 122}, {154, 198, 113}, {137, 182, 85}, {125, 178, 75},
+ {121, 170, 84}, {137, 153, 94}, {141, 174, 94}, {162, 182, 113},
+ {193, 201, 141}, {205, 205, 178}, {222, 230, 197}, {234, 250, 206},
+ {246, 254, 197}, {254, 241, 169}, {246, 234, 159}, {242, 250, 112},
+ {230, 238, 94}, {222, 234, 112}, {198, 222, 113}, {190, 219, 103},
+ {198, 206, 103}, {186, 214, 103}, {174, 214, 113}, {166, 218, 113},
+ {182, 214, 113}, {186, 219, 122}, {198, 226, 132}, {202, 226, 150},
+ {222, 222, 160}, {238, 234, 159}, {246, 233, 169}, {226, 238, 178},
+ {210, 234, 169}, {198, 234, 160}, {198, 230, 150}, {202, 226, 132},
+ {207, 230, 122}, {198, 226, 122}, {186, 222, 113}, {178, 214, 103},
+ {166, 206, 103}, {166, 202, 94}, {150, 190, 84}, {141, 186, 75},
+ {153, 174, 66}, {165, 169, 66}, {150, 182, 85}, {158, 194, 94},
+ {162, 194, 103}, {162, 206, 103}, {158, 206, 103}, {166, 210, 113},
+ {162, 202, 113}, {162, 210, 122}, {166, 214, 113}, {158, 206, 94},
+ {142, 194, 85}, {129, 174, 85}, {121, 166, 75}, {121, 174, 75},
+ {121, 157, 75}, {129, 162, 75}, {154, 148, 37}, {137, 157, 75},
+ {109, 154, 75}, {121, 162, 103}, {137, 170, 132}, {166, 198, 151},
+ {178, 218, 169}, {182, 214, 179}, {185, 210, 179}, {178, 218, 169},
+ {154, 198, 141}, {125, 178, 122}, {125, 186, 113}, {146, 194, 103},
+ {150, 194, 103}, {150, 194, 103}, {154, 194, 94}, {146, 198, 85},
+ {142, 194, 85}, {142, 182, 85}, {141, 182, 84}, {146, 194, 75},
+ {150, 194, 75}, {154, 198, 75}, {158, 194, 56}, {170, 198, 56},
+ {162, 206, 66}, {190, 210, 75}, {230, 187, 28}, {254, 198, 9},
+ {242, 208, 37}, {242, 225, 75}, {246, 237, 112}, {222, 242, 159},
+ {226, 238, 178}, {242, 242, 207}, {246, 242, 216}, {246, 246, 225},
+ {242, 242, 225}, {242, 241, 216}, {226, 242, 188}, {206, 234, 179},
+ {194, 226, 150}, {178, 214, 132}, {162, 210, 122}, {129, 194, 103},
+ {117, 166, 85}, {109, 149, 65}, {101, 153, 75}, {89, 150, 74},
+ {97, 158, 75}, {101, 162, 84}, {113, 170, 85}, {137, 194, 94},
+ {154, 206, 103}, {170, 206, 94}, {189, 193, 84}, {238, 208, 65},
+ {254, 228, 84}, {254, 233, 102}, {254, 221, 93}, {230, 205, 94},
+ {182, 206, 113}, {153, 177, 122}, {113, 154, 94}, {100, 150, 94},
+ {84, 129, 93}, {73, 121, 75}, {65, 117, 65}, {61, 113, 56},
+ {60, 109, 56}, {65, 113, 47}, {61, 109, 56}, {65, 109, 56},
+ {65, 113, 56}, {69, 117, 65}, {77, 129, 65}, {93, 141, 65},
+ {93, 150, 74}, {113, 158, 85}, {133, 170, 85}, {146, 190, 103},
+ {150, 190, 122}, {157, 190, 132}, {158, 202, 132}, {162, 210, 132},
+ {174, 219, 122}, {186, 222, 122}, {198, 230, 150}, {206, 230, 169},
+ {214, 230, 179}, {222, 234, 197}, {230, 246, 216}, {238, 237, 225},
+ {238, 234, 235}, {238, 238, 216}, {226, 238, 188}, {214, 230, 160},
+ {194, 218, 141}, {190, 198, 131}, {174, 198, 122}, {174, 202, 122},
+ {174, 214, 122}, {182, 219, 122}, {194, 214, 132}, {198, 226, 160},
+ {202, 226, 160}, {202, 226, 160}, {190, 222, 179}, {190, 222, 179},
+ {194, 230, 179}, {202, 230, 178}, {214, 234, 178}, {222, 230, 179},
+ {218, 234, 178}, {210, 230, 169}, {206, 218, 169}, {182, 222, 160},
+ {154, 194, 141}, {129, 170, 113}, {101, 154, 93}, {81, 141, 83},
+ {81, 133, 83}, {77, 125, 74}, {73, 121, 74}, {69, 129, 65},
+ {73, 121, 74}, {77, 129, 74}, {109, 133, 75}, {97, 146, 75},
+ {108, 154, 94}, {125, 178, 103}, {150, 202, 113}, {166, 210, 122},
+ {186, 222, 141}, {194, 222, 151}, {194, 218, 151}, {182, 218, 151},
+ {162, 218, 131}, {170, 214, 122}, {174, 210, 122}, {182, 210, 122},
+ {186, 214, 122}, {190, 214, 122}, {194, 218, 132}, {198, 230, 141},
+ {202, 230, 150}, {207, 230, 141}, {202, 218, 131}, {190, 206, 122},
+ },
+ /* rie11 */
+ {
+ {174, 144, 103}, {129, 96, 85}, {117, 80, 66}, {105, 92, 56},
+ {113, 80, 47}, {101, 92, 66}, {109, 113, 85}, {125, 137, 103},
+ {161, 136, 122}, {198, 152, 122}, {206, 157, 132}, {174, 165, 132},
+ {145, 158, 103}, {113, 117, 94}, {88, 92, 66}, {68, 68, 56},
+ {60, 56, 47}, {52, 43, 37}, {48, 40, 37}, {44, 44, 28},
+ {44, 44, 28}, {44, 44, 37}, {48, 48, 47}, {52, 52, 47},
+ {52, 52, 47}, {52, 52, 47}, {44, 52, 56}, {52, 56, 56},
+ {56, 56, 56}, {56, 56, 56}, {56, 60, 56}, {60, 60, 56},
+ {64, 60, 56}, {68, 56, 56}, {64, 56, 47}, {60, 60, 47},
+ {56, 56, 37}, {52, 52, 37}, {48, 48, 37}, {48, 44, 37},
+ {48, 44, 37}, {48, 44, 47}, {44, 44, 56}, {40, 48, 56},
+ {44, 56, 56}, {48, 60, 66}, {52, 64, 75}, {56, 72, 75},
+ {81, 85, 85}, {105, 97, 84}, {121, 121, 103}, {153, 125, 103},
+ {174, 140, 122}, {194, 148, 132}, {190, 152, 132}, {161, 141, 113},
+ {133, 121, 94}, {113, 96, 66}, {92, 80, 56}, {68, 60, 47},
+ {60, 52, 37}, {52, 43, 28}, {48, 35, 28}, {48, 40, 28},
+ {52, 52, 37}, {52, 56, 47}, {56, 60, 47}, {60, 56, 47},
+ {56, 52, 47}, {48, 52, 47}, {40, 44, 37}, {32, 36, 28},
+ {32, 36, 28}, {28, 32, 28}, {36, 36, 28}, {48, 48, 37},
+ {68, 60, 47}, {85, 72, 56}, {113, 92, 75}, {153, 129, 103},
+ {178, 153, 122}, {198, 165, 151}, {198, 165, 151}, {198, 165, 141},
+ {158, 154, 113}, {133, 133, 94}, {101, 97, 66}, {76, 60, 47},
+ {73, 56, 37}, {64, 48, 47}, {64, 48, 47}, {60, 56, 56},
+ {68, 68, 66}, {72, 89, 66}, {97, 105, 85}, {121, 129, 103},
+ {145, 145, 122}, {194, 165, 132}, {210, 165, 141}, {210, 173, 141},
+ {218, 181, 151}, {218, 180, 160}, {218, 177, 160}, {198, 169, 141},
+ {157, 145, 113}, {125, 125, 103}, {109, 109, 75}, {77, 77, 66},
+ {72, 76, 66}, {73, 73, 75}, {68, 60, 66}, {60, 56, 56},
+ {48, 48, 56}, {48, 48, 47}, {44, 44, 47}, {32, 36, 37},
+ {24, 28, 28}, {20, 20, 28}, {15, 20, 28}, {12, 20, 18},
+ {12, 16, 18}, {12, 16, 9}, {12, 20, 9}, {16, 16, 9},
+ {12, 20, 9}, {16, 20, 9}, {20, 20, 9}, {20, 20, 9},
+ {23, 20, 9}, {24, 20, 9}, {27, 16, 9}, {28, 24, 9},
+ {24, 20, 9}, {23, 20, 18}, {19, 20, 18}, {24, 20, 18},
+ {31, 20, 18}, {35, 24, 28}, {28, 24, 18}, {32, 24, 28},
+ {31, 24, 28}, {28, 28, 28}, {28, 32, 37}, {24, 36, 47},
+ {28, 32, 47}, {28, 32, 37}, {23, 24, 28}, {23, 20, 28},
+ {24, 28, 28}, {28, 36, 37}, {40, 40, 37}, {44, 44, 47},
+ {44, 40, 56}, {40, 40, 56}, {31, 28, 37}, {20, 20, 28},
+ {19, 16, 28}, {16, 16, 28}, {16, 16, 18}, {16, 20, 18},
+ {16, 24, 18}, {16, 24, 18}, {20, 24, 18}, {23, 20, 18},
+ {20, 24, 18}, {24, 24, 18}, {28, 28, 28}, {40, 44, 28},
+ {48, 60, 37}, {56, 60, 47}, {56, 72, 56}, {64, 72, 56},
+ {68, 64, 56}, {68, 64, 66}, {64, 64, 56}, {68, 64, 66},
+ {68, 73, 66}, {76, 76, 66}, {68, 89, 85}, {84, 96, 113},
+ {113, 117, 103}, {145, 137, 122}, {149, 141, 132}, {137, 141, 122},
+ {105, 121, 103}, {81, 85, 84}, {60, 68, 66}, {48, 60, 56},
+ {52, 56, 56}, {56, 56, 47}, {52, 52, 47}, {56, 52, 47},
+ {60, 43, 47}, {56, 48, 47}, {52, 44, 47}, {48, 31, 37},
+ {40, 28, 28}, {40, 32, 28}, {36, 31, 28}, {44, 32, 18},
+ {36, 36, 28}, {36, 36, 28}, {36, 36, 37}, {40, 31, 37},
+ {36, 36, 37}, {40, 36, 37}, {48, 39, 37}, {52, 40, 37},
+ {64, 44, 37}, {64, 44, 37}, {60, 48, 37}, {56, 48, 47},
+ {52, 48, 56}, {56, 52, 56}, {60, 52, 56}, {56, 52, 47},
+ {56, 48, 37}, {60, 56, 37}, {56, 48, 37}, {52, 48, 37},
+ {48, 48, 37}, {44, 44, 37}, {44, 48, 28}, {32, 44, 18},
+ {24, 32, 18}, {23, 24, 18}, {23, 16, 18}, {20, 16, 18},
+ {15, 20, 28}, {12, 24, 28}, {15, 28, 28}, {24, 24, 28},
+ {36, 28, 28}, {44, 32, 28}, {48, 40, 37}, {52, 48, 47},
+ {52, 52, 47}, {52, 56, 47}, {56, 56, 47}, {60, 60, 47},
+ {68, 56, 47}, {76, 56, 47}, {85, 64, 56}, {121, 76, 56},
+ {153, 103, 75}, {174, 136, 94}, {210, 164, 132}, {222, 189, 169},
+ {230, 197, 169}, {234, 192, 178}, {222, 189, 169}, {206, 169, 151},
+ {174, 149, 132}, {125, 112, 94}, {64, 64, 47}, {15, 16, 9},
+ },
+ /* etretat.ppm */
+ {
+ {37, 54, 37}, {56, 74, 51}, {72, 89, 61}, {92, 102, 76},
+ {110, 104, 91}, {122, 114, 96}, {130, 115, 99}, {147, 129, 105},
+ {156, 123, 113}, {158, 122, 110}, {154, 117, 112}, {128, 105, 86},
+ {113, 95, 78}, {111, 88, 68}, {109, 75, 53}, {119, 73, 54},
+ {113, 87, 64}, {109, 90, 77}, {112, 99, 83}, {119, 107, 90},
+ {119, 106, 100}, {111, 114, 107}, {108, 108, 119}, {111, 132, 117},
+ {122, 140, 117}, {131, 147, 122}, {125, 148, 116}, {128, 146, 117},
+ {129, 134, 106}, {119, 120, 100}, {122, 118, 102}, {126, 111, 93},
+ {128, 105, 83}, {127, 100, 79}, {116, 96, 68}, {115, 95, 71},
+ {105, 93, 75}, {96, 95, 76}, {94, 88, 75}, {91, 90, 84},
+ {89, 104, 99}, {84, 113, 105}, {86, 116, 115}, {94, 113, 111},
+ {99, 115, 105}, {90, 106, 97}, {75, 101, 90}, {66, 98, 88},
+ {64, 83, 77}, {54, 80, 74}, {48, 74, 60}, {63, 77, 61},
+ {53, 84, 60}, {58, 84, 68}, {74, 78, 77}, {87, 88, 83},
+ {102, 100, 95}, {120, 112, 109}, {142, 128, 126}, {157, 150, 131},
+ {173, 160, 142}, {172, 164, 151}, {171, 163, 140}, {152, 166, 129},
+ {146, 152, 117}, {124, 122, 102}, {119, 101, 83}, {107, 87, 68},
+ {98, 77, 59}, {83, 73, 56}, {71, 70, 54}, {64, 69, 53},
+ {62, 65, 49}, {51, 58, 44}, {46, 53, 42}, {42, 55, 36},
+ {41, 51, 36}, {33, 46, 29}, {32, 35, 25}, {32, 33, 25},
+ {32, 33, 26}, {34, 33, 25}, {49, 41, 28}, {53, 42, 32},
+ {46, 46, 32}, {35, 39, 27}, {35, 36, 26}, {33, 35, 26},
+ {33, 45, 33}, {37, 50, 46}, {43, 49, 49}, {39, 53, 48},
+ {46, 60, 57}, {64, 73, 61}, {76, 83, 70}, {98, 92, 88},
+ {120, 108, 103}, {130, 119, 129}, {133, 122, 130}, {140, 134, 141},
+ {140, 136, 140}, {149, 148, 129}, {152, 158, 138}, {163, 155, 142},
+ {166, 158, 139}, {164, 150, 138}, {160, 129, 117}, {164, 127, 110},
+ {160, 117, 85}, {129, 101, 81}, {95, 89, 60}, {75, 83, 56},
+ {57, 74, 54}, {50, 72, 46}, {50, 66, 45}, {56, 66, 52},
+ {59, 61, 52}, {54, 61, 52}, {47, 62, 45}, {48, 57, 47},
+ {45, 58, 46}, {45, 51, 40}, {50, 49, 35}, {50, 48, 35},
+ {55, 49, 35}, {58, 51, 45}, {69, 65, 55}, {76, 67, 59},
+ {85, 76, 68}, {90, 80, 66}, {96, 75, 58}, {95, 73, 58},
+ {84, 72, 61}, {83, 72, 62}, {76, 71, 65}, {72, 68, 63},
+ {67, 65, 58}, {60, 61, 53}, {53, 66, 55}, {52, 70, 63},
+ {47, 70, 64}, {48, 65, 61}, {45, 66, 47}, {41, 58, 40},
+ {38, 58, 38}, {31, 46, 31}, {31, 34, 25}, {33, 33, 24},
+ {33, 33, 24}, {36, 36, 26}, {45, 45, 33}, {47, 49, 35},
+ {49, 48, 38}, {58, 57, 46}, {71, 66, 49}, {74, 71, 57},
+ {75, 74, 63}, {70, 78, 64}, {63, 77, 60}, {59, 67, 51},
+ {54, 57, 38}, {52, 52, 40}, {59, 57, 50}, {69, 64, 57},
+ {80, 83, 68}, {98, 92, 81}, {116, 107, 100}, {135, 122, 110},
+ {149, 139, 125}, {168, 152, 137}, {173, 164, 146}, {178, 166, 145},
+ {187, 161, 142}, {175, 160, 139}, {167, 156, 132}, {157, 146, 127},
+ {154, 127, 107}, {137, 103, 78}, {118, 89, 65}, {97, 74, 55},
+ {94, 64, 46}, {81, 67, 51}, {77, 68, 53}, {77, 75, 64},
+ {75, 79, 74}, {69, 86, 85}, {81, 84, 88}, {82, 92, 86},
+ {87, 93, 88}, {91, 98, 98}, {95, 112, 99}, {100, 109, 98},
+ {92, 102, 92}, {86, 97, 88}, {95, 99, 92}, {108, 109, 98},
+ {119, 115, 101}, {120, 135, 102}, {129, 145, 106}, {123, 142, 117},
+ {121, 136, 119}, {108, 117, 110}, {101, 118, 101}, {92, 104, 89},
+ {84, 95, 87}, {83, 88, 74}, {85, 82, 74}, {82, 80, 72},
+ {85, 79, 66}, {89, 82, 61}, {95, 83, 56}, {98, 81, 61},
+ {101, 80, 68}, {92, 81, 69}, {90, 84, 74}, {94, 86, 82},
+ {98, 92, 86}, {96, 99, 90}, {113, 110, 94}, {119, 105, 92},
+ {120, 104, 88}, {103, 94, 86}, {90, 91, 78}, {76, 90, 75},
+ {78, 83, 79}, {70, 84, 73}, {66, 92, 77}, {76, 94, 87},
+ {94, 99, 100}, {103, 108, 119}, {105, 132, 130}, {129, 156, 139},
+ {158, 162, 146}, {178, 172, 157}, {178, 176, 173}, {166, 166, 167},
+ {125, 150, 143}, {97, 132, 135}, {105, 129, 130}, {104, 126, 123},
+ {100, 128, 120}, {95, 119, 120}, {95, 117, 107}, {103, 127, 111},
+ {110, 137, 122}, {130, 141, 133}, {142, 138, 120}, {137, 157, 120},
+ {139, 133, 110}, {131, 123, 109}, {127, 112, 99}, {120, 105, 95},
+ {116, 103, 90}, {103, 92, 79}, {105, 92, 76}, {114, 99, 83},
+ },
+ /* the-hollow-needle-at-etretat.ppm */
+ {
+ {106, 110, 103}, {105, 117, 116}, {105, 119, 120}, {117, 133, 130},
+ {123, 144, 135}, {124, 144, 138}, {125, 149, 140}, {123, 146, 140},
+ {122, 149, 150}, {127, 156, 144}, {128, 155, 142}, {137, 151, 143},
+ {140, 148, 142}, {143, 151, 132}, {150, 154, 130}, {151, 152, 135},
+ {147, 158, 149}, {143, 154, 144}, {139, 151, 144}, {137, 150, 147},
+ {143, 152, 149}, {137, 149, 146}, {137, 147, 142}, {134, 146, 140},
+ {131, 141, 135}, {125, 140, 138}, {123, 143, 138}, {123, 144, 140},
+ {123, 144, 140}, {128, 145, 142}, {127, 150, 141}, {120, 153, 141},
+ {125, 157, 144}, {131, 159, 151}, {134, 161, 147}, {143, 160, 151},
+ {142, 166, 163}, {136, 172, 169}, {139, 163, 165}, {130, 157, 142},
+ {130, 151, 141}, {131, 146, 141}, {134, 148, 150}, {130, 152, 143},
+ {130, 156, 138}, {132, 155, 137}, {137, 158, 132}, {134, 153, 129},
+ {134, 147, 129}, {135, 143, 126}, {133, 142, 127}, {138, 135, 118},
+ {132, 125, 106}, {125, 113, 95}, {118, 101, 87}, {103, 92, 72},
+ {78, 69, 55}, {64, 59, 54}, {59, 56, 53}, {60, 58, 54},
+ {69, 64, 58}, {88, 86, 83}, {95, 101, 97}, {93, 108, 101},
+ {91, 108, 101}, {91, 109, 107}, {91, 110, 109}, {89, 114, 113},
+ {93, 112, 109}, {93, 108, 105}, {101, 112, 108}, {107, 116, 118},
+ {115, 128, 125}, {120, 147, 132}, {125, 154, 134}, {131, 150, 131},
+ {138, 147, 135}, {138, 144, 132}, {136, 139, 128}, {138, 138, 127},
+ {138, 138, 115}, {137, 134, 102}, {132, 131, 102}, {137, 124, 97},
+ {127, 120, 88}, {120, 108, 84}, {114, 110, 84}, {112, 104, 84},
+ {113, 99, 83}, {116, 105, 87}, {118, 111, 89}, {121, 117, 102},
+ {126, 127, 112}, {131, 131, 121}, {136, 137, 128}, {136, 136, 130},
+ {135, 139, 131}, {132, 140, 127}, {131, 132, 126}, {128, 121, 119},
+ {122, 110, 104}, {106, 92, 92}, {91, 75, 69}, {70, 59, 53},
+ {60, 55, 52}, {59, 54, 51}, {59, 55, 48}, {57, 54, 50},
+ {59, 53, 52}, {58, 51, 51}, {52, 51, 51}, {52, 49, 50},
+ {55, 52, 51}, {51, 53, 50}, {55, 52, 52}, {58, 53, 54},
+ {55, 55, 57}, {58, 61, 59}, {58, 79, 76}, {85, 101, 93},
+ {95, 101, 97}, {101, 102, 97}, {103, 104, 99}, {96, 106, 104},
+ {108, 116, 106}, {116, 120, 110}, {125, 129, 121}, {129, 141, 131},
+ {129, 141, 135}, {130, 139, 132}, {126, 128, 128}, {114, 120, 117},
+ {107, 106, 100}, {100, 94, 85}, {88, 80, 69}, {68, 59, 55},
+ {60, 53, 52}, {55, 51, 48}, {53, 49, 48}, {49, 52, 48},
+ {47, 50, 45}, {47, 46, 47}, {49, 44, 43}, {51, 43, 42},
+ {52, 49, 45}, {56, 52, 52}, {58, 57, 56}, {63, 81, 78},
+ {86, 105, 100}, {102, 121, 118}, {99, 124, 126}, {105, 124, 123},
+ {117, 127, 119}, {129, 135, 121}, {133, 141, 123}, {136, 139, 115},
+ {133, 130, 109}, {127, 125, 102}, {124, 118, 97}, {116, 114, 98},
+ {120, 113, 99}, {121, 113, 96}, {125, 109, 96}, {120, 110, 94},
+ {119, 106, 92}, {112, 106, 92}, {106, 94, 90}, {92, 84, 78},
+ {69, 60, 61}, {61, 55, 53}, {58, 54, 53}, {57, 54, 53},
+ {52, 54, 54}, {52, 53, 51}, {53, 55, 50}, {60, 57, 52},
+ {71, 66, 59}, {91, 97, 89}, {96, 106, 102}, {106, 121, 116},
+ {127, 138, 129}, {128, 141, 135}, {123, 139, 137}, {116, 129, 123},
+ {103, 125, 111}, {104, 121, 111}, {111, 115, 106}, {114, 114, 105},
+ {121, 111, 103}, {120, 115, 110}, {122, 120, 112}, {127, 124, 112},
+ {131, 118, 107}, {130, 116, 105}, {128, 124, 102}, {130, 124, 103},
+ {139, 121, 98}, {143, 116, 99}, {147, 128, 100}, {154, 127, 99},
+ {147, 135, 109}, {157, 128, 102}, {158, 130, 115}, {162, 148, 115},
+ {150, 137, 119}, {144, 145, 121}, {143, 148, 132}, {141, 142, 126},
+ {135, 140, 128}, {134, 141, 133}, {134, 144, 132}, {132, 140, 130},
+ {131, 142, 130}, {132, 141, 128}, {134, 141, 126}, {133, 141, 123},
+ {136, 135, 113}, {134, 129, 107}, {127, 122, 100}, {121, 119, 94},
+ {107, 116, 90}, {106, 111, 91}, {105, 103, 93}, {107, 103, 92},
+ {108, 111, 94}, {112, 119, 107}, {114, 128, 121}, {119, 139, 131},
+ {122, 145, 135}, {126, 148, 136}, {128, 147, 135}, {138, 146, 137},
+ {139, 145, 132}, {142, 142, 125}, {143, 133, 118}, {144, 132, 111},
+ {142, 136, 110}, {140, 136, 110}, {135, 130, 115}, {132, 123, 106},
+ {130, 118, 92}, {132, 115, 92}, {142, 106, 91}, {123, 109, 89},
+ {107, 101, 84}, {95, 91, 86}, {72, 64, 59}, {64, 58, 53},
+ {59, 54, 54}, {59, 56, 51}, {63, 57, 55}, {71, 69, 68},
+ {97, 100, 88}, {112, 114, 103}, {123, 135, 123}, {132, 145, 128},
+ },
+ /* rouen-cathedral-sunset.ppm */
+ {
+ {137, 129, 126}, {130, 125, 111}, {120, 112, 98}, {113, 109, 87},
+ {100, 101, 83}, {91, 93, 81}, {78, 84, 72}, {82, 89, 66},
+ {86, 88, 71}, {95, 95, 69}, {100, 92, 61}, {114, 94, 53},
+ {125, 91, 54}, {127, 96, 57}, {124, 99, 57}, {115, 101, 64},
+ {101, 98, 68}, {96, 101, 80}, {100, 106, 87}, {104, 104, 90},
+ {109, 106, 87}, {115, 111, 90}, {126, 115, 100}, {133, 126, 107},
+ {143, 135, 116}, {144, 133, 122}, {149, 131, 136}, {156, 134, 129},
+ {145, 134, 125}, {141, 128, 118}, {133, 129, 109}, {131, 125, 111},
+ {129, 125, 110}, {126, 124, 99}, {126, 130, 107}, {128, 129, 111},
+ {133, 129, 113}, {136, 136, 123}, {148, 145, 137}, {156, 150, 144},
+ {159, 153, 146}, {159, 150, 152}, {160, 152, 145}, {156, 147, 147},
+ {148, 143, 149}, {141, 141, 144}, {138, 133, 130}, {135, 127, 125},
+ {134, 131, 124}, {140, 131, 120}, {140, 129, 126}, {138, 137, 132},
+ {138, 138, 149}, {135, 149, 160}, {134, 149, 164}, {134, 153, 171},
+ {138, 149, 164}, {128, 140, 150}, {137, 142, 147}, {146, 144, 145},
+ {158, 141, 140}, {163, 146, 139}, {176, 154, 136}, {179, 159, 144},
+ {187, 167, 151}, {198, 171, 154}, {190, 158, 132}, {166, 145, 107},
+ {145, 133, 91}, {146, 114, 79}, {138, 108, 71}, {119, 101, 76},
+ {107, 102, 78}, {98, 98, 78}, {94, 92, 78}, {93, 90, 71},
+ {91, 88, 60}, {90, 82, 53}, {88, 77, 52}, {73, 74, 52},
+ {84, 82, 57}, {93, 89, 57}, {105, 94, 71}, {94, 91, 69},
+ {97, 94, 72}, {108, 98, 64}, {115, 99, 71}, {110, 101, 74},
+ {115, 107, 73}, {128, 112, 75}, {142, 113, 73}, {148, 117, 75},
+ {151, 128, 86}, {156, 137, 106}, {162, 141, 119}, {163, 151, 126},
+ {167, 156, 133}, {163, 155, 136}, {152, 147, 144}, {140, 147, 140},
+ {133, 139, 127}, {126, 130, 125}, {124, 125, 113}, {115, 119, 108},
+ {112, 120, 111}, {102, 118, 115}, {104, 116, 116}, {97, 117, 107},
+ {107, 113, 107}, {111, 108, 103}, {105, 109, 99}, {104, 111, 97},
+ {106, 106, 91}, {105, 101, 88}, {108, 102, 85}, {107, 102, 78},
+ {112, 105, 78}, {129, 112, 90}, {138, 127, 92}, {144, 135, 111},
+ {138, 142, 129}, {149, 149, 147}, {155, 152, 161}, {153, 158, 170},
+ {155, 159, 171}, {163, 158, 161}, {167, 155, 153}, {173, 159, 145},
+ {169, 155, 135}, {169, 141, 105}, {162, 125, 95}, {150, 117, 93},
+ {138, 116, 91}, {134, 109, 85}, {119, 109, 80}, {119, 108, 82},
+ {122, 108, 77}, {124, 105, 74}, {125, 111, 79}, {120, 109, 77},
+ {118, 111, 81}, {116, 115, 87}, {111, 113, 89}, {114, 114, 91},
+ {116, 121, 96}, {134, 128, 108}, {153, 138, 122}, {169, 146, 132},
+ {181, 154, 130}, {168, 153, 130}, {152, 142, 119}, {143, 135, 108},
+ {133, 126, 95}, {125, 123, 93}, {119, 112, 86}, {116, 106, 83},
+ {121, 111, 84}, {128, 107, 87}, {129, 110, 95}, {138, 120, 91},
+ {140, 124, 91}, {144, 126, 93}, {141, 131, 91}, {128, 122, 90},
+ {119, 119, 90}, {116, 112, 102}, {109, 114, 95}, {109, 112, 110},
+ {109, 120, 119}, {121, 129, 122}, {132, 131, 122}, {144, 145, 135},
+ {158, 148, 141}, {175, 158, 144}, {180, 158, 149}, {187, 171, 158},
+ {188, 176, 158}, {184, 167, 145}, {175, 166, 137}, {162, 151, 133},
+ {153, 137, 111}, {144, 123, 100}, {128, 119, 90}, {120, 113, 85},
+ {113, 110, 80}, {115, 104, 72}, {116, 100, 68}, {124, 101, 64},
+ {134, 100, 67}, {135, 109, 63}, {139, 112, 67}, {135, 120, 75},
+ {133, 120, 93}, {133, 127, 105}, {132, 128, 123}, {132, 136, 146},
+ {134, 143, 152}, {140, 153, 164}, {141, 154, 168}, {142, 155, 165},
+ {157, 156, 151}, {159, 159, 138}, {173, 162, 141}, {177, 164, 143},
+ {177, 163, 150}, {181, 166, 158}, {181, 169, 162}, {175, 170, 163},
+ {179, 164, 162}, {175, 162, 155}, {161, 155, 148}, {158, 157, 147},
+ {150, 155, 147}, {147, 153, 144}, {136, 138, 124}, {128, 126, 114},
+ {112, 112, 107}, {100, 106, 99}, {96, 104, 99}, {93, 103, 96},
+ {103, 102, 89}, {111, 104, 84}, {112, 102, 80}, {118, 102, 74},
+ {119, 98, 70}, {118, 95, 67}, {116, 94, 61}, {112, 90, 58},
+ {104, 90, 60}, {103, 91, 56}, {98, 86, 57}, {92, 84, 51},
+ {89, 84, 60}, {106, 82, 55}, {111, 90, 56}, {113, 85, 56},
+ {103, 92, 65}, {109, 103, 77}, {104, 99, 86}, {106, 96, 89},
+ {101, 101, 95}, {103, 105, 98}, {109, 110, 103}, {102, 114, 94},
+ {112, 114, 107}, {126, 117, 99}, {136, 130, 104}, {150, 140, 107},
+ {157, 138, 106}, {157, 142, 104}, {154, 139, 109}, {155, 146, 128},
+ {154, 142, 136}, {155, 145, 145}, {148, 151, 156}, {135, 158, 170},
+ },
+ /* the-houses-of-parliament.ppm */
+ {
+ {105, 95, 133}, {97, 86, 120}, {81, 81, 103}, {78, 71, 88},
+ {74, 66, 79}, {75, 64, 73}, {76, 68, 75}, {79, 65, 74},
+ {81, 71, 74}, {83, 74, 81}, {88, 74, 87}, {91, 81, 96},
+ {98, 92, 112}, {101, 95, 126}, {107, 95, 136}, {110, 95, 141},
+ {109, 90, 136}, {104, 88, 136}, {105, 89, 130}, {103, 87, 118},
+ {102, 88, 117}, {105, 87, 110}, {101, 84, 111}, {105, 90, 112},
+ {103, 99, 115}, {105, 101, 116}, {107, 98, 117}, {104, 90, 120},
+ {97, 87, 122}, {98, 90, 114}, {94, 89, 109}, {90, 91, 103},
+ {88, 82, 96}, {82, 76, 86}, {78, 74, 77}, {70, 72, 69},
+ {66, 72, 63}, {57, 67, 60}, {60, 67, 57}, {68, 73, 65},
+ {77, 83, 71}, {85, 94, 92}, {94, 102, 97}, {97, 101, 106},
+ {101, 101, 100}, {99, 99, 106}, {100, 98, 109}, {103, 94, 112},
+ {103, 90, 112}, {101, 87, 113}, {104, 89, 111}, {100, 96, 107},
+ {95, 96, 107}, {95, 88, 106}, {97, 88, 108}, {99, 93, 112},
+ {98, 90, 105}, {99, 89, 96}, {97, 90, 96}, {96, 87, 99},
+ {94, 88, 105}, {97, 85, 111}, {101, 86, 113}, {105, 88, 117},
+ {109, 93, 128}, {116, 98, 135}, {115, 98, 135}, {113, 103, 131},
+ {112, 105, 138}, {108, 99, 133}, {107, 95, 124}, {109, 94, 123},
+ {106, 91, 122}, {106, 88, 116}, {107, 87, 116}, {104, 84, 117},
+ {105, 84, 116}, {109, 87, 112}, {110, 91, 114}, {107, 96, 117},
+ {111, 92, 117}, {107, 89, 116}, {105, 86, 118}, {105, 80, 127},
+ {99, 75, 134}, {98, 73, 129}, {100, 80, 128}, {98, 79, 126},
+ {101, 74, 127}, {98, 78, 121}, {97, 76, 113}, {96, 75, 103},
+ {92, 76, 99}, {88, 79, 90}, {87, 75, 90}, {90, 74, 94},
+ {87, 75, 92}, {88, 76, 92}, {91, 71, 96}, {99, 77, 99},
+ {96, 80, 102}, {95, 77, 105}, {91, 71, 112}, {94, 69, 117},
+ {98, 78, 117}, {103, 87, 122}, {114, 90, 141}, {109, 96, 134},
+ {113, 101, 136}, {126, 107, 140}, {119, 100, 132}, {114, 96, 113},
+ {108, 88, 109}, {99, 87, 106}, {92, 83, 102}, {87, 79, 96},
+ {88, 76, 95}, {85, 74, 97}, {87, 69, 102}, {88, 71, 101},
+ {88, 73, 97}, {90, 79, 103}, {93, 81, 107}, {93, 82, 118},
+ {99, 81, 123}, {96, 81, 125}, {93, 84, 125}, {94, 82, 129},
+ {100, 90, 128}, {101, 97, 124}, {100, 99, 116}, {107, 103, 112},
+ {117, 106, 104}, {114, 106, 108}, {117, 106, 115}, {108, 99, 108},
+ {104, 99, 107}, {103, 107, 98}, {103, 102, 98}, {95, 96, 97},
+ {95, 94, 91}, {87, 87, 83}, {87, 79, 87}, {81, 73, 84},
+ {77, 63, 79}, {75, 60, 74}, {73, 61, 75}, {72, 61, 78},
+ {75, 63, 78}, {73, 62, 78}, {78, 63, 84}, {77, 58, 97},
+ {85, 66, 100}, {87, 65, 102}, {82, 72, 104}, {81, 66, 105},
+ {80, 60, 96}, {73, 63, 82}, {66, 63, 76}, {57, 55, 54},
+ {21, 24, 21}, {23, 29, 20}, {48, 55, 48}, {55, 64, 60},
+ {63, 67, 69}, {70, 66, 72}, {81, 74, 83}, {89, 79, 89},
+ {96, 86, 96}, {105, 86, 105}, {117, 96, 112}, {118, 114, 124},
+ {126, 123, 126}, {135, 132, 151}, {125, 115, 137}, {119, 118, 134},
+ {109, 109, 133}, {109, 101, 128}, {112, 107, 128}, {109, 104, 122},
+ {101, 103, 124}, {106, 99, 117}, {112, 100, 119}, {109, 98, 112},
+ {106, 101, 114}, {105, 102, 114}, {100, 99, 111}, {96, 94, 103},
+ {94, 89, 94}, {94, 88, 88}, {88, 92, 82}, {82, 92, 73},
+ {79, 89, 73}, {72, 84, 72}, {70, 83, 68}, {83, 75, 77},
+ {89, 76, 87}, {91, 80, 103}, {101, 84, 114}, {108, 86, 126},
+ {110, 87, 139}, {115, 90, 142}, {124, 85, 142}, {107, 82, 129},
+ {109, 92, 129}, {100, 82, 119}, {97, 82, 112}, {97, 83, 102},
+ {89, 84, 97}, {89, 83, 96}, {92, 86, 94}, {96, 87, 95},
+ {99, 86, 92}, {95, 84, 96}, {94, 85, 93}, {91, 84, 85},
+ {86, 83, 84}, {78, 85, 89}, {73, 84, 86}, {69, 76, 83},
+ {75, 71, 83}, {77, 68, 83}, {77, 69, 81}, {79, 70, 84},
+ {80, 69, 82}, {77, 68, 83}, {78, 69, 85}, {83, 72, 86},
+ {82, 75, 87}, {84, 74, 84}, {83, 72, 81}, {77, 66, 76},
+ {73, 60, 73}, {62, 52, 70}, {60, 52, 63}, {67, 65, 63},
+ {80, 68, 65}, {84, 78, 71}, {87, 78, 86}, {88, 82, 93},
+ {92, 84, 96}, {97, 85, 102}, {105, 82, 108}, {103, 84, 105},
+ {98, 82, 102}, {94, 77, 103}, {88, 74, 101}, {88, 73, 102},
+ {89, 74, 104}, {88, 75, 115}, {90, 81, 118}, {95, 83, 122},
+ {97, 82, 118}, {100, 83, 113}, {97, 86, 109}, {91, 89, 101},
+ },
+ /* starry-night.ppm */
+ {
+ {23, 26, 18}, {27, 29, 21}, {30, 29, 21}, {29, 30, 22},
+ {32, 35, 26}, {40, 42, 32}, {43, 47, 35}, {45, 54, 38},
+ {52, 52, 36}, {50, 54, 38}, {51, 56, 39}, {50, 56, 42},
+ {50, 60, 42}, {52, 63, 47}, {60, 68, 51}, {69, 73, 55},
+ {67, 76, 55}, {68, 78, 53}, {69, 82, 55}, {70, 87, 59},
+ {77, 101, 65}, {87, 101, 66}, {92, 108, 67}, {93, 105, 65},
+ {93, 107, 65}, {90, 110, 66}, {93, 110, 65}, {95, 111, 69},
+ {102, 114, 73}, {104, 110, 69}, {109, 110, 61}, {98, 105, 65},
+ {94, 108, 61}, {82, 98, 56}, {78, 86, 53}, {81, 79, 57},
+ {81, 82, 62}, {88, 91, 60}, {99, 93, 62}, {95, 106, 64},
+ {91, 110, 65}, {84, 117, 77}, {89, 124, 81}, {104, 128, 92},
+ {114, 130, 84}, {120, 132, 84}, {138, 148, 85}, {148, 150, 76},
+ {134, 147, 76}, {131, 138, 78}, {110, 108, 66}, {96, 89, 57},
+ {81, 72, 49}, {66, 65, 45}, {63, 60, 40}, {60, 55, 44},
+ {58, 56, 43}, {56, 56, 42}, {53, 57, 42}, {58, 59, 42},
+ {57, 61, 41}, {57, 65, 42}, {57, 65, 45}, {59, 61, 49},
+ {63, 68, 55}, {71, 77, 53}, {76, 86, 54}, {90, 95, 55},
+ {101, 108, 65}, {114, 125, 74}, {133, 146, 76}, {142, 156, 82},
+ {161, 168, 92}, {177, 178, 94}, {177, 173, 93}, {167, 158, 86},
+ {152, 147, 79}, {133, 115, 72}, {98, 100, 67}, {80, 86, 54},
+ {73, 80, 53}, {61, 71, 47}, {44, 58, 38}, {37, 46, 32},
+ {35, 38, 27}, {29, 35, 23}, {28, 36, 25}, {28, 38, 24},
+ {33, 46, 30}, {37, 47, 30}, {44, 48, 34}, {47, 49, 36},
+ {46, 51, 36}, {41, 51, 31}, {35, 50, 34}, {35, 48, 31},
+ {42, 51, 35}, {46, 53, 44}, {53, 62, 43}, {60, 70, 49},
+ {73, 77, 59}, {80, 88, 64}, {83, 98, 84}, {86, 101, 99},
+ {100, 114, 92}, {101, 108, 80}, {93, 106, 76}, {81, 103, 75},
+ {87, 101, 75}, {85, 113, 85}, {78, 110, 80}, {83, 109, 74},
+ {86, 107, 71}, {86, 113, 65}, {89, 104, 67}, {79, 101, 66},
+ {77, 98, 63}, {70, 86, 63}, {68, 76, 67}, {69, 82, 61},
+ {73, 82, 59}, {72, 84, 61}, {70, 90, 69}, {80, 98, 83},
+ {91, 106, 77}, {102, 110, 80}, {102, 120, 77}, {98, 126, 70},
+ {99, 122, 78}, {94, 114, 72}, {84, 101, 67}, {75, 84, 58},
+ {76, 78, 53}, {69, 77, 48}, {64, 65, 44}, {58, 64, 43},
+ {51, 55, 41}, {46, 51, 38}, {40, 45, 35}, {30, 34, 29},
+ {29, 29, 22}, {26, 27, 22}, {26, 28, 24}, {25, 27, 22},
+ {28, 28, 22}, {31, 30, 25}, {37, 34, 28}, {41, 37, 29},
+ {42, 39, 34}, {47, 44, 36}, {42, 47, 33}, {38, 44, 30},
+ {33, 36, 27}, {31, 31, 25}, {29, 27, 21}, {27, 24, 21},
+ {24, 26, 19}, {23, 26, 18}, {20, 18, 14}, {24, 25, 18},
+ {30, 32, 23}, {37, 40, 30}, {42, 45, 37}, {51, 55, 41},
+ {60, 63, 47}, {70, 75, 52}, {84, 78, 49}, {100, 92, 59},
+ {112, 106, 63}, {118, 115, 66}, {121, 131, 78}, {122, 138, 89},
+ {125, 149, 98}, {139, 157, 105}, {124, 152, 98}, {113, 139, 83},
+ {103, 125, 72}, {105, 112, 69}, {93, 103, 63}, {80, 88, 54},
+ {73, 78, 53}, {61, 76, 49}, {57, 66, 47}, {54, 68, 47},
+ {50, 64, 40}, {53, 60, 42}, {56, 53, 44}, {58, 54, 39},
+ {58, 56, 39}, {53, 57, 37}, {53, 56, 38}, {56, 54, 36},
+ {52, 49, 33}, {42, 44, 30}, {35, 38, 26}, {29, 35, 24},
+ {29, 35, 26}, {38, 42, 31}, {44, 44, 32}, {53, 51, 31},
+ {62, 62, 41}, {78, 77, 54}, {91, 97, 67}, {109, 122, 77},
+ {136, 138, 83}, {149, 149, 83}, {163, 154, 83}, {167, 153, 89},
+ {156, 160, 90}, {145, 153, 84}, {129, 142, 79}, {109, 130, 80},
+ {101, 123, 85}, {98, 123, 94}, {91, 102, 87}, {90, 98, 79},
+ {94, 92, 81}, {83, 92, 66}, {78, 84, 59}, {74, 77, 55},
+ {64, 65, 48}, {58, 56, 43}, {50, 48, 36}, {45, 42, 31},
+ {37, 34, 25}, {34, 30, 21}, {34, 28, 24}, {38, 39, 28},
+ {47, 48, 35}, {52, 52, 41}, {56, 59, 43}, {59, 68, 57},
+ {57, 78, 58}, {68, 89, 57}, {69, 88, 56}, {67, 80, 54},
+ {61, 77, 51}, {56, 74, 47}, {56, 69, 44}, {53, 62, 42},
+ {51, 56, 42}, {53, 53, 45}, {51, 52, 44}, {51, 54, 48},
+ {54, 59, 52}, {64, 61, 53}, {69, 66, 63}, {63, 69, 56},
+ {75, 76, 52}, {80, 79, 52}, {78, 82, 51}, {84, 91, 59},
+ {92, 100, 65}, {97, 104, 63}, {98, 99, 61}, {92, 93, 57},
+ },
+ /* water-lilies-sunset.ppm */
+ {
+ {71, 67, 46}, {71, 67, 50}, {67, 72, 57}, {72, 84, 60},
+ {81, 87, 72}, {92, 93, 74}, {84, 86, 74}, {88, 100, 84},
+ {83, 84, 73}, {77, 74, 68}, {68, 62, 56}, {60, 59, 48},
+ {60, 55, 47}, {57, 49, 38}, {53, 45, 38}, {52, 44, 36},
+ {52, 46, 36}, {57, 49, 37}, {58, 49, 38}, {60, 52, 39},
+ {63, 57, 43}, {70, 60, 45}, {80, 65, 48}, {85, 71, 52},
+ {89, 76, 54}, {90, 83, 53}, {96, 92, 57}, {102, 95, 62},
+ {106, 98, 61}, {112, 100, 62}, {115, 107, 65}, {119, 106, 67},
+ {129, 111, 70}, {132, 119, 74}, {130, 120, 71}, {126, 118, 69},
+ {112, 111, 84}, {101, 100, 80}, {87, 93, 92}, {90, 101, 87},
+ {85, 94, 100}, {107, 108, 90}, {130, 118, 110}, {135, 118, 104},
+ {153, 156, 127}, {183, 183, 126}, {189, 184, 126}, {177, 165, 100},
+ {173, 153, 91}, {162, 148, 88}, {145, 135, 77}, {134, 124, 72},
+ {116, 107, 66}, {103, 91, 59}, {89, 73, 62}, {78, 65, 49},
+ {69, 58, 43}, {67, 51, 41}, {63, 50, 42}, {66, 51, 48},
+ {69, 57, 49}, {76, 58, 53}, {81, 65, 54}, {86, 73, 55},
+ {86, 81, 53}, {95, 87, 55}, {100, 87, 58}, {101, 88, 65},
+ {112, 87, 80}, {117, 98, 73}, {114, 106, 68}, {120, 110, 68},
+ {122, 109, 67}, {122, 107, 66}, {125, 104, 66}, {116, 94, 63},
+ {114, 90, 60}, {105, 84, 56}, {93, 76, 53}, {93, 75, 51},
+ {91, 75, 52}, {84, 76, 49}, {83, 73, 48}, {82, 74, 48},
+ {77, 76, 51}, {72, 74, 53}, {68, 68, 54}, {69, 66, 54},
+ {66, 66, 52}, {65, 64, 48}, {65, 62, 49}, {64, 58, 49},
+ {63, 58, 46}, {62, 60, 46}, {61, 60, 46}, {58, 57, 44},
+ {56, 55, 45}, {56, 52, 44}, {55, 51, 45}, {49, 50, 41},
+ {51, 47, 38}, {54, 49, 41}, {50, 46, 38}, {45, 49, 39},
+ {44, 47, 36}, {50, 47, 34}, {48, 44, 33}, {51, 44, 34},
+ {51, 49, 37}, {55, 51, 37}, {56, 49, 38}, {54, 52, 42},
+ {55, 50, 40}, {57, 51, 39}, {58, 53, 39}, {60, 53, 38},
+ {59, 54, 40}, {61, 55, 41}, {61, 58, 43}, {64, 60, 43},
+ {72, 64, 48}, {83, 65, 47}, {84, 72, 48}, {93, 75, 51},
+ {102, 87, 55}, {106, 97, 61}, {113, 104, 64}, {122, 109, 67},
+ {133, 113, 69}, {136, 116, 69}, {142, 123, 72}, {142, 123, 74},
+ {144, 118, 73}, {130, 100, 83}, {134, 88, 71}, {112, 86, 58},
+ {100, 68, 52}, {80, 54, 44}, {72, 52, 42}, {66, 49, 38},
+ {63, 55, 41}, {63, 57, 42}, {63, 60, 44}, {70, 64, 47},
+ {72, 68, 48}, {78, 71, 47}, {76, 77, 51}, {78, 82, 52},
+ {82, 81, 52}, {79, 74, 48}, {78, 70, 47}, {75, 67, 45},
+ {67, 64, 43}, {62, 59, 42}, {59, 55, 42}, {57, 53, 40},
+ {56, 53, 44}, {60, 55, 50}, {67, 61, 49}, {77, 68, 51},
+ {84, 74, 52}, {89, 82, 53}, {98, 89, 56}, {101, 88, 58},
+ {102, 90, 59}, {101, 95, 69}, {95, 95, 61}, {96, 94, 59},
+ {83, 88, 66}, {84, 79, 68}, {76, 73, 67}, {73, 68, 54},
+ {68, 62, 51}, {66, 61, 48}, {61, 61, 47}, {60, 57, 47},
+ {57, 55, 44}, {57, 54, 40}, {57, 56, 40}, {57, 54, 39},
+ {56, 55, 38}, {52, 51, 37}, {48, 46, 38}, {44, 41, 36},
+ {42, 42, 33}, {44, 43, 33}, {48, 40, 34}, {52, 44, 36},
+ {57, 47, 42}, {57, 49, 42}, {62, 55, 41}, {64, 55, 44},
+ {67, 56, 43}, {68, 58, 44}, {69, 61, 41}, {71, 61, 41},
+ {78, 63, 48}, {78, 72, 52}, {86, 75, 58}, {95, 85, 61},
+ {110, 99, 68}, {119, 111, 69}, {134, 124, 74}, {149, 132, 78},
+ {152, 141, 84}, {161, 143, 85}, {143, 122, 76}, {129, 116, 70},
+ {114, 90, 60}, {98, 82, 55}, {89, 66, 48}, {79, 56, 45},
+ {74, 53, 44}, {71, 54, 46}, {66, 54, 42}, {64, 56, 40},
+ {62, 56, 40}, {64, 52, 39}, {63, 48, 39}, {61, 48, 43},
+ {58, 50, 44}, {57, 51, 44}, {61, 53, 46}, {67, 53, 49},
+ {68, 57, 48}, {71, 60, 52}, {70, 63, 49}, {69, 61, 48},
+ {70, 62, 49}, {70, 65, 48}, {68, 68, 47}, {65, 67, 48},
+ {62, 64, 46}, {63, 61, 43}, {64, 60, 42}, {68, 59, 43},
+ {72, 61, 44}, {79, 56, 45}, {85, 65, 47}, {85, 71, 52},
+ {82, 77, 59}, {79, 77, 63}, {72, 71, 65}, {70, 69, 56},
+ {63, 69, 53}, {61, 66, 52}, {58, 63, 47}, {64, 62, 45},
+ {67, 59, 48}, {71, 63, 51}, {74, 64, 54}, {74, 72, 52},
+ {84, 77, 54}, {92, 84, 61}, {105, 96, 62}, {117, 102, 75},
+ },
+ /* gogh.chambre-arles.ppm*/
+ {
+ {24, 17, 8}, {25, 34, 4}, {53, 56, 72}, {75, 94, 108},
+ {86, 110, 145}, {99, 141, 175}, {125, 154, 163}, {166, 146, 109},
+ {182, 142, 103}, {191, 159, 113}, {206, 191, 120}, {238, 216, 142},
+ {252, 233, 156}, {242, 229, 171}, {220, 226, 215}, {209, 216, 221},
+ {207, 213, 224}, {199, 215, 224}, {189, 208, 223}, {171, 194, 208},
+ {176, 188, 187}, {183, 159, 117}, {172, 130, 92}, {163, 113, 62},
+ {159, 109, 53}, {176, 110, 9}, {188, 116, 6}, {196, 118, 6},
+ {202, 123, 10}, {209, 126, 15}, {200, 127, 38}, {193, 145, 91},
+ {240, 193, 119}, {251, 235, 153}, {252, 240, 170}, {246, 231, 174},
+ {229, 224, 220}, {232, 232, 224}, {224, 230, 230}, {206, 219, 225},
+ {194, 207, 215}, {177, 193, 190}, {205, 177, 130}, {196, 149, 101},
+ {191, 134, 97}, {193, 146, 106}, {196, 168, 125}, {176, 190, 191},
+ {170, 197, 217}, {187, 204, 220}, {188, 206, 216}, {178, 198, 204},
+ {171, 191, 183}, {173, 147, 108}, {162, 121, 84}, {154, 108, 70},
+ {140, 98, 58}, {116, 85, 48}, {116, 84, 56}, {131, 93, 52},
+ {148, 97, 49}, {171, 95, 9}, {183, 104, 7}, {188, 110, 6},
+ {190, 110, 4}, {193, 109, 2}, {198, 109, 1}, {193, 107, 1},
+ {194, 106, 7}, {196, 109, 4}, {196, 110, 6}, {199, 108, 5},
+ {208, 107, 2}, {208, 114, 3}, {209, 118, 3}, {207, 116, 4},
+ {207, 118, 1}, {205, 116, 2}, {205, 115, 2}, {203, 113, 2},
+ {200, 113, 3}, {199, 113, 4}, {200, 113, 8}, {198, 110, 6},
+ {195, 112, 4}, {195, 111, 8}, {197, 113, 9}, {193, 112, 8},
+ {193, 108, 14}, {169, 119, 67}, {172, 143, 109}, {171, 184, 178},
+ {169, 189, 198}, {168, 187, 198}, {168, 185, 180}, {169, 148, 113},
+ {169, 132, 96}, {167, 122, 89}, {169, 124, 91}, {169, 131, 94},
+ {166, 155, 102}, {148, 172, 162}, {129, 172, 187}, {136, 170, 194},
+ {153, 157, 161}, {165, 131, 95}, {160, 121, 84}, {161, 118, 85},
+ {163, 122, 91}, {171, 133, 94}, {184, 152, 116}, {179, 181, 168},
+ {177, 188, 203}, {169, 193, 202}, {163, 191, 202}, {149, 178, 194},
+ {133, 162, 176}, {136, 138, 111}, {132, 144, 61}, {137, 134, 53},
+ {194, 125, 15}, {190, 117, 10}, {191, 118, 4}, {193, 122, 4},
+ {200, 125, 8}, {207, 130, 2}, {210, 134, 2}, {216, 134, 8},
+ {219, 135, 6}, {219, 145, 9}, {206, 205, 99}, {232, 220, 127},
+ {222, 216, 127}, {201, 205, 98}, {196, 170, 103}, {175, 139, 86},
+ {151, 118, 83}, {115, 102, 102}, {110, 133, 148}, {120, 156, 176},
+ {134, 169, 197}, {136, 177, 199}, {161, 174, 178}, {182, 144, 107},
+ {181, 124, 68}, {201, 121, 16}, {206, 122, 10}, {208, 119, 4},
+ {214, 118, 1}, {220, 132, 10}, {212, 144, 55}, {234, 182, 112},
+ {228, 188, 116}, {194, 165, 109}, {185, 142, 96}, {187, 133, 83},
+ {177, 126, 79}, {178, 127, 86}, {187, 139, 104}, {198, 164, 121},
+ {209, 185, 155}, {216, 212, 192}, {221, 224, 217}, {227, 230, 223},
+ {230, 233, 226}, {222, 228, 229}, {207, 218, 218}, {189, 198, 201},
+ {197, 169, 130}, {181, 136, 99}, {169, 120, 86}, {155, 117, 75},
+ {153, 114, 75}, {151, 110, 72}, {144, 106, 72}, {144, 104, 68},
+ {148, 105, 65}, {147, 103, 64}, {147, 100, 66}, {145, 106, 67},
+ {141, 112, 72}, {137, 119, 73}, {134, 114, 77}, {139, 118, 78},
+ {150, 115, 79}, {164, 119, 78}, {185, 138, 95}, {195, 171, 118},
+ {243, 205, 123}, {251, 237, 150}, {253, 240, 166}, {252, 235, 164},
+ {224, 211, 162}, {191, 180, 120}, {162, 128, 86}, {132, 94, 56},
+ {84, 77, 37}, {55, 30, 16}, {64, 34, 7}, {112, 61, 7},
+ {150, 78, 12}, {177, 94, 6}, {186, 107, 5}, {192, 115, 5},
+ {200, 115, 7}, {207, 117, 5}, {205, 124, 12}, {209, 130, 13},
+ {196, 163, 88}, {221, 200, 109}, {247, 221, 145}, {239, 223, 166},
+ {217, 211, 177}, {176, 195, 194}, {129, 161, 184}, {94, 125, 148},
+ {73, 92, 121}, {85, 85, 107}, {99, 94, 60}, {97, 90, 58},
+ {106, 72, 24}, {133, 77, 14}, {143, 80, 7}, {128, 63, 11},
+ {96, 59, 24}, {100, 56, 31}, {125, 80, 40}, {142, 96, 59},
+ {158, 112, 62}, {174, 133, 82}, {185, 158, 112}, {201, 192, 157},
+ {196, 206, 208}, {197, 216, 223}, {202, 212, 223}, {198, 212, 215},
+ {190, 198, 209}, {190, 188, 167}, {184, 158, 112}, {169, 129, 94},
+ {155, 114, 77}, {146, 112, 78}, {142, 112, 78}, {142, 107, 75},
+ {142, 107, 69}, {138, 104, 65}, {144, 98, 61}, {153, 107, 52},
+ {166, 97, 11}, {174, 101, 5}, {186, 109, 5}, {190, 106, 5},
+ {190, 102, 4}, {197, 41, 1}, {116, 33, 7}, {58, 32, 5},
+ },
+ /* gogh.entrance.ppm*/
+ {
+ {2, 2, 1}, {6, 5, 8}, {5, 8, 7}, {8, 15, 8},
+ {9, 21, 16}, {11, 26, 20}, {27, 49, 30}, {35, 65, 35},
+ {54, 77, 47}, {69, 106, 52}, {94, 125, 64}, {124, 137, 81},
+ {148, 157, 87}, {169, 182, 104}, {190, 193, 116}, {207, 207, 129},
+ {219, 220, 140}, {219, 208, 142}, {217, 220, 140}, {215, 214, 126},
+ {207, 199, 112}, {188, 169, 87}, {179, 142, 58}, {182, 132, 44},
+ {185, 143, 29}, {198, 203, 65}, {220, 214, 99}, {231, 222, 117},
+ {238, 228, 127}, {234, 229, 136}, {251, 245, 156}, {235, 235, 144},
+ {240, 235, 151}, {244, 234, 151}, {241, 233, 149}, {239, 236, 136},
+ {241, 230, 124}, {233, 232, 116}, {223, 216, 105}, {213, 204, 98},
+ {179, 179, 91}, {136, 145, 60}, {93, 138, 53}, {74, 111, 58},
+ {62, 113, 53}, {59, 102, 62}, {52, 94, 67}, {43, 94, 70},
+ {45, 87, 61}, {39, 83, 56}, {38, 87, 54}, {62, 100, 47},
+ {66, 108, 47}, {89, 112, 52}, {117, 127, 44}, {139, 139, 44},
+ {139, 139, 50}, {131, 129, 42}, {102, 121, 60}, {80, 112, 58},
+ {68, 94, 42}, {47, 88, 28}, {35, 68, 39}, {42, 56, 54},
+ {45, 56, 61}, {41, 64, 72}, {37, 98, 69}, {40, 111, 80},
+ {53, 139, 100}, {77, 133, 110}, {84, 136, 112}, {81, 122, 117},
+ {77, 95, 121}, {71, 97, 103}, {59, 97, 100}, {54, 90, 90},
+ {44, 80, 92}, {44, 71, 79}, {42, 80, 76}, {63, 93, 67},
+ {88, 113, 66}, {129, 128, 58}, {155, 157, 69}, {177, 179, 99},
+ {194, 197, 109}, {206, 201, 124}, {221, 216, 120}, {233, 236, 121},
+ {240, 235, 118}, {231, 221, 104}, {221, 208, 98}, {200, 199, 94},
+ {182, 172, 58}, {171, 136, 47}, {142, 133, 57}, {124, 108, 46},
+ {81, 97, 48}, {77, 94, 46}, {89, 92, 52}, {125, 84, 42},
+ {156, 99, 32}, {134, 106, 41}, {82, 99, 42}, {76, 92, 40},
+ {65, 85, 37}, {36, 75, 45}, {23, 75, 49}, {33, 79, 47},
+ {29, 90, 53}, {41, 104, 75}, {61, 112, 79}, {74, 134, 72},
+ {109, 151, 93}, {145, 173, 124}, {174, 196, 161}, {186, 206, 175},
+ {189, 207, 161}, {175, 188, 141}, {138, 168, 118}, {109, 132, 109},
+ {73, 101, 101}, {55, 95, 82}, {46, 73, 57}, {20, 55, 43},
+ {17, 36, 39}, {12, 37, 36}, {13, 28, 28}, {20, 25, 20},
+ {21, 18, 23}, {11, 15, 28}, {30, 30, 17}, {22, 29, 26},
+ {21, 28, 22}, {19, 42, 22}, {30, 37, 23}, {31, 38, 30},
+ {25, 38, 31}, {24, 27, 34}, {25, 30, 48}, {31, 46, 62},
+ {31, 55, 54}, {35, 63, 60}, {29, 76, 77}, {31, 78, 79},
+ {36, 91, 82}, {45, 90, 99}, {49, 97, 111}, {64, 108, 108},
+ {74, 122, 123}, {99, 143, 139}, {168, 173, 141}, {190, 209, 149},
+ {204, 215, 159}, {209, 226, 160}, {209, 214, 153}, {197, 207, 132},
+ {185, 180, 115}, {169, 158, 102}, {144, 149, 73}, {102, 133, 62},
+ {73, 107, 58}, {54, 74, 55}, {42, 64, 52}, {31, 52, 44},
+ {34, 45, 38}, {39, 47, 30}, {49, 51, 29}, {55, 52, 34},
+ {44, 53, 45}, {55, 76, 60}, {81, 95, 78}, {105, 124, 100},
+ {143, 165, 98}, {182, 200, 132}, {202, 214, 149}, {215, 220, 168},
+ {230, 236, 165}, {231, 235, 181}, {216, 229, 170}, {207, 213, 174},
+ {194, 210, 185}, {180, 219, 201}, {140, 198, 201}, {142, 188, 193},
+ {159, 184, 158}, {110, 160, 143}, {82, 144, 137}, {89, 145, 113},
+ {103, 137, 95}, {131, 146, 98}, {146, 151, 109}, {156, 157, 110},
+ {196, 190, 134}, {207, 201, 144}, {208, 209, 154}, {208, 211, 148},
+ {195, 201, 146}, {131, 157, 129}, {97, 132, 126}, {77, 126, 117},
+ {49, 120, 108}, {58, 113, 88}, {65, 92, 76}, {73, 95, 66},
+ {89, 94, 62}, {123, 96, 47}, {160, 100, 40}, {165, 107, 42},
+ {143, 113, 58}, {107, 105, 61}, {88, 112, 70}, {83, 97, 66},
+ {74, 81, 60}, {57, 84, 64}, {49, 90, 67}, {54, 100, 76},
+ {60, 96, 84}, {67, 115, 90}, {98, 124, 91}, {105, 118, 89},
+ {132, 135, 72}, {163, 134, 84}, {181, 167, 99}, {198, 191, 105},
+ {210, 208, 116}, {212, 212, 112}, {219, 215, 108}, {219, 219, 115},
+ {219, 221, 131}, {227, 218, 137}, {231, 228, 139}, {228, 228, 135},
+ {226, 224, 134}, {233, 228, 134}, {236, 233, 129}, {237, 233, 133},
+ {243, 233, 138}, {243, 230, 134}, {241, 232, 129}, {237, 230, 123},
+ {226, 217, 108}, {213, 211, 99}, {193, 191, 89}, {163, 175, 74},
+ {149, 155, 48}, {144, 152, 62}, {167, 155, 61}, {182, 173, 83},
+ {186, 180, 104}, {190, 176, 100}, {178, 171, 89}, {168, 141, 62},
+ {144, 137, 64}, {107, 111, 51}, {79, 86, 46}, {35, 31, 17},
+ },
+ /* gogh.the-night-cafe.ppm*/
+ {
+ {56, 36, 25}, {72, 40, 16}, {76, 46, 26}, {96, 55, 31},
+ {121, 71, 41}, {145, 88, 46}, {161, 90, 60}, {168, 109, 63},
+ {168, 119, 83}, {169, 122, 86}, {162, 123, 79}, {139, 115, 75},
+ {102, 115, 73}, {99, 123, 72}, {105, 132, 75}, {108, 137, 75},
+ {133, 147, 94}, {146, 139, 137}, {179, 173, 130}, {198, 185, 140},
+ {202, 187, 145}, {208, 180, 162}, {215, 187, 141}, {233, 188, 126},
+ {234, 192, 124}, {212, 183, 114}, {214, 176, 101}, {222, 181, 93},
+ {214, 174, 83}, {210, 165, 80}, {207, 161, 83}, {199, 155, 77},
+ {197, 154, 74}, {194, 149, 67}, {190, 144, 62}, {187, 136, 62},
+ {188, 133, 66}, {191, 122, 70}, {192, 129, 67}, {190, 137, 63},
+ {194, 137, 62}, {196, 143, 64}, {195, 149, 70}, {200, 155, 70},
+ {201, 162, 73}, {205, 165, 77}, {206, 162, 85}, {202, 160, 91},
+ {193, 150, 96}, {183, 156, 98}, {182, 153, 106}, {179, 160, 112},
+ {163, 157, 107}, {165, 147, 91}, {163, 143, 88}, {172, 137, 83},
+ {180, 134, 79}, {181, 136, 77}, {179, 132, 67}, {175, 132, 72},
+ {178, 131, 73}, {178, 124, 67}, {180, 134, 64}, {181, 131, 60},
+ {188, 126, 59}, {181, 116, 63}, {168, 117, 60}, {173, 101, 61},
+ {161, 88, 58}, {158, 80, 58}, {144, 83, 53}, {128, 81, 62},
+ {112, 94, 57}, {98, 104, 62}, {90, 102, 66}, {89, 102, 71},
+ {99, 93, 83}, {112, 100, 85}, {127, 103, 76}, {144, 107, 69},
+ {152, 110, 69}, {150, 112, 63}, {155, 113, 63}, {162, 112, 60},
+ {157, 110, 57}, {154, 109, 51}, {154, 101, 55}, {142, 91, 55},
+ {118, 79, 54}, {107, 74, 48}, {105, 74, 45}, {103, 67, 36},
+ {108, 52, 39}, {116, 56, 41}, {122, 53, 37}, {135, 60, 44},
+ {149, 67, 41}, {155, 67, 45}, {163, 68, 48}, {170, 74, 50},
+ {175, 81, 55}, {176, 98, 43}, {173, 109, 37}, {167, 112, 51},
+ {176, 116, 48}, {170, 117, 57}, {170, 119, 61}, {170, 116, 55},
+ {168, 123, 59}, {173, 129, 61}, {174, 124, 65}, {170, 120, 64},
+ {156, 126, 59}, {157, 110, 61}, {158, 93, 69}, {140, 100, 55},
+ {132, 91, 47}, {110, 76, 48}, {93, 79, 54}, {75, 63, 46},
+ {74, 52, 43}, {70, 45, 43}, {65, 48, 41}, {72, 55, 41},
+ {80, 57, 37}, {88, 61, 37}, {97, 66, 44}, {104, 66, 46},
+ {113, 69, 45}, {125, 70, 43}, {137, 83, 48}, {149, 85, 42},
+ {158, 82, 45}, {154, 90, 46}, {155, 79, 49}, {157, 81, 50},
+ {161, 80, 48}, {165, 76, 55}, {165, 75, 56}, {160, 70, 49},
+ {159, 70, 45}, {156, 74, 48}, {158, 72, 45}, {157, 68, 47},
+ {163, 67, 46}, {161, 64, 45}, {157, 66, 47}, {162, 63, 44},
+ {162, 58, 43}, {155, 59, 44}, {154, 67, 47}, {154, 76, 52},
+ {147, 80, 55}, {134, 71, 58}, {123, 73, 49}, {115, 78, 57},
+ {113, 78, 59}, {116, 89, 60}, {119, 91, 69}, {119, 92, 65},
+ {123, 92, 58}, {129, 97, 52}, {146, 103, 57}, {152, 109, 61},
+ {151, 113, 61}, {163, 114, 63}, {163, 124, 60}, {164, 122, 52},
+ {172, 133, 52}, {166, 111, 58}, {170, 101, 53}, {168, 95, 51},
+ {163, 91, 51}, {160, 91, 51}, {160, 92, 52}, {165, 97, 52},
+ {165, 96, 45}, {171, 93, 49}, {176, 92, 50}, {180, 105, 51},
+ {186, 112, 59}, {199, 121, 54}, {201, 141, 67}, {200, 146, 71},
+ {205, 156, 72}, {207, 162, 76}, {208, 165, 77}, {212, 169, 86},
+ {215, 170, 89}, {221, 174, 95}, {218, 172, 100}, {215, 172, 99},
+ {209, 166, 89}, {206, 165, 83}, {206, 165, 82}, {196, 164, 86},
+ {195, 162, 91}, {190, 160, 90}, {189, 154, 92}, {177, 149, 84},
+ {180, 142, 81}, {172, 133, 70}, {152, 129, 77}, {109, 130, 76},
+ {100, 127, 70}, {97, 123, 70}, {98, 118, 68}, {94, 116, 69},
+ {90, 113, 68}, {87, 97, 55}, {88, 80, 63}, {92, 80, 57},
+ {86, 75, 61}, {99, 73, 62}, {119, 83, 63}, {149, 98, 67},
+ {160, 113, 66}, {171, 128, 69}, {176, 142, 76}, {190, 156, 75},
+ {204, 170, 86}, {214, 175, 100}, {225, 179, 110}, {238, 201, 109},
+ {239, 196, 98}, {225, 180, 89}, {224, 178, 82}, {218, 159, 77},
+ {212, 158, 81}, {202, 155, 82}, {200, 151, 79}, {194, 149, 75},
+ {188, 141, 69}, {188, 144, 69}, {177, 140, 68}, {188, 140, 66},
+ {178, 147, 70}, {185, 141, 69}, {186, 136, 77}, {196, 138, 82},
+ {200, 143, 106}, {211, 164, 103}, {215, 172, 106}, {208, 180, 132},
+ {202, 174, 141}, {180, 169, 126}, {181, 163, 117}, {166, 141, 101},
+ {175, 131, 101}, {149, 106, 85}, {123, 92, 70}, {99, 74, 55},
+ {76, 55, 43}, {71, 47, 35}, {68, 34, 33}, {64, 45, 33},
+ },
+ /* gogh.vegetable-montmartre.ppm*/
+ {
+ {40, 43, 29}, {44, 40, 28}, {66, 55, 30}, {91, 61, 30},
+ {112, 65, 40}, {137, 82, 49}, {164, 102, 53}, {196, 118, 67},
+ {220, 129, 69}, {227, 128, 77}, {232, 141, 72}, {221, 151, 83},
+ {236, 161, 80}, {225, 161, 82}, {225, 163, 80}, {221, 168, 96},
+ {221, 153, 88}, {233, 171, 96}, {210, 174, 105}, {227, 161, 98},
+ {235, 172, 109}, {238, 179, 116}, {232, 176, 103}, {231, 186, 102},
+ {227, 184, 110}, {210, 171, 118}, {190, 161, 102}, {178, 160, 101},
+ {159, 156, 109}, {143, 154, 124}, {134, 149, 126}, {132, 146, 120},
+ {127, 144, 118}, {131, 144, 118}, {144, 152, 114}, {144, 150, 104},
+ {163, 143, 102}, {185, 147, 93}, {193, 154, 98}, {199, 155, 92},
+ {198, 155, 90}, {193, 146, 87}, {192, 142, 90}, {185, 142, 87},
+ {182, 141, 83}, {169, 139, 80}, {145, 132, 92}, {125, 148, 90},
+ {127, 138, 106}, {123, 137, 121}, {120, 136, 121}, {116, 132, 118},
+ {112, 134, 121}, {106, 134, 117}, {99, 122, 113}, {96, 119, 109},
+ {91, 107, 99}, {87, 103, 102}, {89, 103, 86}, {95, 100, 77},
+ {97, 96, 72}, {95, 90, 64}, {98, 89, 79}, {100, 95, 69},
+ {117, 98, 64}, {109, 97, 72}, {104, 100, 72}, {115, 96, 71},
+ {120, 99, 74}, {133, 110, 70}, {156, 123, 77}, {165, 139, 76},
+ {177, 146, 79}, {194, 150, 88}, {210, 148, 87}, {213, 156, 85},
+ {212, 158, 97}, {211, 160, 98}, {211, 161, 100}, {218, 168, 111},
+ {222, 182, 114}, {236, 194, 126}, {232, 198, 125}, {224, 187, 122},
+ {211, 183, 126}, {228, 198, 141}, {182, 189, 131}, {151, 169, 141},
+ {144, 161, 134}, {134, 155, 137}, {129, 152, 136}, {127, 149, 136},
+ {128, 148, 135}, {124, 145, 132}, {123, 145, 131}, {116, 142, 132},
+ {111, 136, 131}, {108, 130, 128}, {107, 126, 122}, {99, 122, 111},
+ {102, 105, 88}, {107, 112, 82}, {111, 107, 74}, {115, 106, 67},
+ {123, 102, 60}, {151, 111, 62}, {162, 99, 57}, {161, 103, 76},
+ {161, 115, 73}, {154, 121, 70}, {148, 125, 66}, {139, 123, 78},
+ {143, 122, 88}, {148, 124, 84}, {135, 113, 77}, {127, 117, 76},
+ {114, 111, 73}, {114, 111, 73}, {117, 106, 91}, {114, 110, 74},
+ {111, 114, 83}, {104, 111, 97}, {99, 122, 112}, {98, 124, 119},
+ {100, 127, 118}, {105, 128, 119}, {106, 129, 121}, {112, 132, 121},
+ {114, 136, 127}, {111, 136, 130}, {112, 137, 131}, {117, 141, 128},
+ {121, 147, 130}, {125, 145, 126}, {125, 141, 122}, {124, 141, 120},
+ {129, 141, 111}, {154, 137, 98}, {166, 147, 94}, {187, 148, 90},
+ {202, 147, 85}, {211, 141, 84}, {218, 142, 83}, {219, 149, 89},
+ {227, 156, 96}, {222, 155, 94}, {210, 153, 97}, {204, 152, 96},
+ {190, 148, 93}, {172, 149, 91}, {156, 147, 93}, {143, 149, 103},
+ {137, 146, 106}, {128, 143, 114}, {125, 141, 115}, {125, 138, 112},
+ {124, 131, 98}, {122, 123, 91}, {119, 120, 88}, {105, 115, 79},
+ {103, 102, 69}, {95, 94, 61}, {84, 87, 62}, {82, 78, 54},
+ {75, 75, 46}, {71, 69, 54}, {83, 76, 40}, {99, 68, 31},
+ {101, 66, 33}, {95, 48, 18}, {107, 55, 26}, {105, 60, 35},
+ {103, 57, 37}, {111, 56, 27}, {120, 40, 23}, {117, 60, 29},
+ {118, 80, 46}, {141, 99, 57}, {149, 104, 54}, {150, 98, 54},
+ {126, 88, 37}, {128, 68, 33}, {121, 73, 34}, {117, 65, 22},
+ {133, 74, 34}, {158, 66, 35}, {168, 90, 53}, {169, 97, 49},
+ {195, 102, 56}, {200, 110, 61}, {203, 118, 65}, {209, 124, 65},
+ {196, 126, 55}, {181, 128, 57}, {174, 125, 72}, {171, 121, 70},
+ {165, 114, 68}, {168, 100, 58}, {175, 105, 52}, {182, 110, 62},
+ {178, 120, 72}, {188, 131, 73}, {197, 139, 79}, {197, 143, 79},
+ {196, 155, 89}, {195, 163, 97}, {193, 159, 110}, {193, 173, 124},
+ {185, 173, 134}, {154, 164, 134}, {150, 164, 131}, {148, 159, 133},
+ {140, 157, 132}, {135, 149, 128}, {130, 152, 128}, {124, 146, 126},
+ {121, 144, 124}, {118, 142, 125}, {116, 135, 118}, {119, 130, 102},
+ {124, 131, 98}, {125, 128, 95}, {121, 120, 89}, {114, 109, 76},
+ {114, 104, 70}, {101, 95, 59}, {103, 88, 59}, {85, 84, 48},
+ {92, 75, 43}, {97, 79, 55}, {85, 85, 40}, {97, 74, 35},
+ {103, 72, 40}, {98, 76, 42}, {103, 78, 45}, {101, 83, 47},
+ {105, 88, 60}, {115, 95, 59}, {114, 99, 55}, {138, 112, 55},
+ {151, 122, 65}, {171, 130, 69}, {175, 125, 74}, {195, 135, 72},
+ {211, 143, 73}, {219, 137, 76}, {224, 140, 74}, {215, 144, 71},
+ {198, 140, 76}, {186, 137, 70}, {173, 126, 68}, {167, 109, 50},
+ {147, 102, 55}, {110, 85, 39}, {87, 62, 37}, {52, 51, 30},
+ },
+ /* matisse.bonheur-vivre.ppm*/
+ {
+ {3, 3, 3}, {39, 31, 46}, {91, 59, 63}, {169, 125, 81},
+ {206, 159, 105}, {208, 186, 115}, {204, 196, 101}, {223, 190, 85},
+ {245, 207, 76}, {247, 210, 66}, {248, 210, 60}, {245, 197, 62},
+ {222, 186, 74}, {207, 176, 85}, {191, 155, 108}, {161, 126, 102},
+ {105, 111, 111}, {80, 98, 102}, {43, 82, 60}, {36, 61, 45},
+ {23, 54, 40}, {18, 51, 42}, {32, 60, 38}, {53, 74, 51},
+ {73, 98, 67}, {118, 135, 93}, {175, 161, 120}, {219, 165, 132},
+ {226, 171, 140}, {226, 195, 154}, {228, 206, 139}, {241, 213, 115},
+ {239, 208, 112}, {234, 199, 103}, {223, 189, 95}, {227, 161, 77},
+ {220, 151, 71}, {205, 158, 48}, {202, 142, 41}, {184, 121, 37},
+ {177, 116, 25}, {160, 107, 36}, {100, 116, 44}, {81, 66, 62},
+ {66, 52, 60}, {75, 84, 83}, {79, 90, 108}, {94, 104, 112},
+ {122, 120, 122}, {175, 163, 125}, {212, 176, 145}, {221, 190, 167},
+ {225, 201, 166}, {232, 199, 158}, {230, 198, 147}, {228, 199, 107},
+ {228, 208, 101}, {226, 206, 68}, {230, 203, 59}, {223, 197, 46},
+ {243, 199, 23}, {232, 203, 40}, {217, 191, 46}, {218, 173, 35},
+ {201, 178, 35}, {226, 186, 25}, {210, 168, 29}, {204, 176, 59},
+ {217, 150, 36}, {188, 119, 21}, {216, 163, 19}, {220, 145, 9},
+ {225, 159, 1}, {223, 157, 1}, {219, 135, 0}, {204, 134, 21},
+ {204, 113, 2}, {193, 104, 2}, {195, 112, 0}, {186, 115, 18},
+ {173, 117, 13}, {183, 104, 19}, {183, 118, 32}, {164, 155, 43},
+ {142, 155, 66}, {97, 138, 69}, {47, 88, 61}, {48, 79, 51},
+ {35, 74, 49}, {39, 72, 52}, {42, 77, 57}, {67, 86, 90},
+ {90, 95, 101}, {142, 133, 104}, {168, 142, 94}, {187, 136, 104},
+ {201, 135, 106}, {207, 138, 123}, {209, 140, 121}, {217, 151, 122},
+ {217, 151, 127}, {222, 150, 126}, {219, 152, 134}, {222, 167, 136},
+ {227, 170, 144}, {220, 174, 146}, {220, 183, 156}, {219, 176, 157},
+ {192, 169, 155}, {138, 168, 128}, {104, 149, 108}, {65, 119, 86},
+ {38, 87, 71}, {29, 70, 58}, {25, 59, 54}, {25, 69, 56},
+ {29, 66, 56}, {43, 88, 64}, {73, 117, 88}, {118, 135, 93},
+ {153, 142, 78}, {193, 146, 72}, {198, 145, 61}, {199, 162, 70},
+ {196, 157, 53}, {219, 150, 47}, {222, 163, 48}, {217, 193, 59},
+ {222, 201, 62}, {222, 189, 82}, {207, 180, 93}, {211, 185, 98},
+ {205, 179, 100}, {190, 161, 97}, {169, 154, 85}, {117, 158, 94},
+ {82, 131, 102}, {75, 121, 93}, {78, 119, 94}, {88, 121, 90},
+ {131, 102, 87}, {162, 105, 38}, {160, 101, 33}, {163, 104, 28},
+ {166, 88, 30}, {176, 78, 3}, {169, 71, 3}, {166, 43, 1},
+ {159, 15, 8}, {154, 8, 8}, {165, 6, 3}, {160, 3, 0},
+ {149, 1, 0}, {148, 9, 3}, {50, 11, 10}, {48, 29, 12},
+ {46, 24, 27}, {48, 39, 33}, {82, 70, 32}, {155, 103, 30},
+ {172, 119, 46}, {184, 130, 54}, {210, 156, 69}, {227, 161, 91},
+ {220, 169, 110}, {224, 155, 107}, {210, 131, 104}, {184, 106, 94},
+ {170, 101, 86}, {166, 91, 81}, {156, 96, 85}, {88, 111, 82},
+ {76, 123, 90}, {62, 119, 86}, {54, 116, 91}, {66, 123, 102},
+ {86, 142, 116}, {126, 158, 123}, {163, 165, 118}, {209, 168, 119},
+ {222, 178, 144}, {225, 200, 163}, {226, 212, 162}, {221, 201, 167},
+ {225, 209, 170}, {215, 213, 188}, {234, 206, 190}, {224, 210, 171},
+ {227, 201, 174}, {223, 203, 178}, {225, 181, 170}, {218, 163, 147},
+ {196, 142, 130}, {181, 132, 97}, {180, 111, 63}, {198, 93, 35},
+ {200, 101, 3}, {213, 103, 26}, {208, 94, 4}, {209, 84, 8},
+ {210, 67, 1}, {195, 66, 1}, {194, 74, 2}, {181, 70, 0},
+ {188, 65, 1}, {189, 53, 4}, {184, 50, 1}, {179, 53, 3},
+ {170, 41, 4}, {174, 13, 6}, {176, 13, 3}, {178, 48, 5},
+ {166, 64, 52}, {197, 90, 56}, {188, 99, 80}, {209, 104, 96},
+ {222, 122, 92}, {206, 131, 112}, {206, 151, 120}, {199, 161, 131},
+ {173, 180, 138}, {136, 179, 145}, {98, 156, 130}, {83, 147, 117},
+ {70, 134, 107}, {85, 132, 106}, {110, 144, 103}, {162, 140, 88},
+ {178, 118, 84}, {192, 122, 93}, {204, 122, 113}, {203, 126, 119},
+ {196, 137, 118}, {191, 131, 113}, {182, 130, 107}, {161, 153, 120},
+ {104, 145, 109}, {78, 128, 103}, {53, 112, 87}, {35, 102, 80},
+ {40, 96, 71}, {52, 103, 75}, {97, 137, 79}, {138, 159, 67},
+ {187, 173, 64}, {192, 177, 59}, {210, 190, 61}, {219, 183, 91},
+ {224, 196, 104}, {221, 205, 139}, {225, 202, 155}, {209, 201, 152},
+ {156, 176, 144}, {115, 120, 128}, {69, 87, 96}, {27, 58, 48},
+ },
+ /* matisse.flowers.ppm*/
+ {
+ {13, 10, 17}, {66, 30, 14}, {140, 53, 34}, {165, 44, 33},
+ {205, 94, 45}, {177, 121, 75}, {182, 159, 89}, {185, 175, 117},
+ {209, 186, 130}, {203, 189, 140}, {208, 192, 141}, {214, 198, 142},
+ {220, 201, 150}, {221, 204, 151}, {222, 202, 151}, {220, 198, 141},
+ {216, 197, 135}, {220, 203, 142}, {216, 203, 156}, {224, 207, 168},
+ {227, 206, 168}, {227, 212, 168}, {230, 217, 173}, {232, 217, 181},
+ {235, 218, 193}, {241, 213, 200}, {235, 218, 199}, {233, 218, 198},
+ {231, 217, 185}, {231, 219, 176}, {228, 213, 170}, {221, 208, 170},
+ {211, 204, 178}, {206, 203, 188}, {209, 205, 191}, {214, 205, 197},
+ {211, 207, 198}, {200, 201, 195}, {199, 199, 187}, {198, 197, 178},
+ {203, 198, 167}, {206, 197, 164}, {213, 202, 163}, {216, 202, 164},
+ {218, 196, 160}, {224, 191, 153}, {225, 189, 141}, {215, 194, 130},
+ {217, 191, 115}, {220, 197, 104}, {221, 188, 104}, {217, 163, 87},
+ {215, 161, 51}, {211, 172, 50}, {213, 174, 59}, {206, 166, 88},
+ {214, 164, 87}, {208, 153, 87}, {197, 121, 93}, {209, 82, 39},
+ {193, 21, 8}, {168, 38, 20}, {170, 75, 61}, {151, 127, 95},
+ {154, 165, 148}, {180, 188, 174}, {184, 187, 172}, {181, 187, 175},
+ {178, 184, 177}, {157, 178, 184}, {152, 175, 182}, {133, 172, 173},
+ {130, 171, 159}, {139, 171, 165}, {156, 177, 183}, {175, 187, 185},
+ {180, 189, 190}, {182, 189, 183}, {189, 190, 174}, {190, 192, 168},
+ {202, 194, 160}, {206, 197, 153}, {212, 201, 158}, {218, 202, 169},
+ {225, 209, 179}, {226, 216, 183}, {237, 222, 193}, {236, 222, 200},
+ {235, 223, 207}, {235, 221, 210}, {236, 222, 213}, {233, 223, 214},
+ {234, 219, 215}, {234, 221, 214}, {233, 220, 212}, {234, 221, 214},
+ {237, 222, 217}, {239, 226, 216}, {239, 233, 216}, {239, 230, 221},
+ {239, 229, 219}, {237, 228, 214}, {233, 222, 211}, {231, 221, 208},
+ {230, 223, 204}, {232, 216, 201}, {222, 215, 187}, {207, 205, 185},
+ {199, 200, 185}, {195, 197, 184}, {195, 196, 182}, {194, 195, 178},
+ {186, 190, 176}, {180, 184, 177}, {158, 174, 178}, {143, 172, 156},
+ {116, 159, 149}, {72, 103, 96}, {55, 76, 65}, {21, 42, 44},
+ {41, 38, 37}, {70, 59, 35}, {149, 106, 97}, {175, 131, 93},
+ {213, 180, 99}, {219, 188, 113}, {221, 195, 117}, {227, 204, 124},
+ {226, 206, 143}, {233, 198, 151}, {229, 211, 163}, {230, 215, 178},
+ {233, 216, 200}, {220, 218, 206}, {211, 211, 204}, {195, 198, 199},
+ {185, 197, 189}, {187, 189, 187}, {188, 182, 188}, {181, 177, 182},
+ {175, 178, 176}, {161, 175, 162}, {163, 141, 151}, {176, 112, 118},
+ {141, 99, 109}, {82, 80, 53}, {53, 51, 40}, {36, 40, 51},
+ {60, 73, 63}, {78, 111, 97}, {131, 161, 125}, {154, 178, 154},
+ {156, 181, 179}, {157, 182, 187}, {164, 192, 197}, {180, 188, 197},
+ {192, 194, 200}, {200, 200, 198}, {209, 208, 199}, {215, 214, 202},
+ {213, 218, 201}, {212, 215, 205}, {205, 207, 204}, {198, 198, 204},
+ {192, 196, 199}, {180, 193, 193}, {176, 186, 192}, {174, 184, 189},
+ {158, 181, 187}, {151, 175, 178}, {135, 169, 158}, {116, 156, 147},
+ {57, 83, 86}, {56, 31, 32}, {70, 28, 19}, {121, 56, 46},
+ {155, 75, 65}, {178, 106, 114}, {193, 133, 101}, {182, 171, 116},
+ {165, 167, 140}, {188, 139, 142}, {184, 123, 127}, {186, 114, 117},
+ {197, 146, 111}, {208, 168, 141}, {221, 184, 171}, {208, 198, 181},
+ {213, 206, 191}, {223, 209, 195}, {232, 216, 201}, {232, 216, 207},
+ {231, 217, 208}, {232, 220, 213}, {226, 220, 215}, {219, 217, 209},
+ {217, 218, 209}, {216, 209, 208}, {202, 202, 202}, {192, 197, 195},
+ {193, 195, 186}, {192, 193, 185}, {193, 195, 182}, {194, 198, 191},
+ {205, 206, 200}, {217, 210, 195}, {231, 215, 202}, {231, 218, 204},
+ {235, 220, 205}, {236, 220, 204}, {234, 221, 205}, {232, 221, 205},
+ {231, 217, 207}, {231, 214, 204}, {225, 207, 196}, {214, 200, 192},
+ {205, 191, 187}, {204, 169, 169}, {206, 173, 167}, {200, 193, 175},
+ {195, 196, 180}, {194, 198, 186}, {198, 200, 196}, {215, 201, 202},
+ {229, 214, 207}, {231, 217, 208}, {233, 219, 209}, {234, 223, 211},
+ {238, 227, 211}, {240, 229, 207}, {240, 227, 208}, {239, 226, 209},
+ {237, 224, 208}, {236, 220, 205}, {235, 215, 204}, {227, 211, 197},
+ {214, 208, 193}, {197, 197, 184}, {188, 189, 172}, {169, 180, 160},
+ {149, 169, 137}, {115, 136, 85}, {73, 66, 39}, {51, 31, 30},
+ {35, 32, 12}, {33, 28, 14}, {38, 41, 33}, {63, 55, 40},
+ {77, 100, 78}, {129, 155, 131}, {123, 162, 151}, {129, 165, 159},
+ {135, 167, 158}, {138, 150, 139}, {124, 121, 135}, {56, 54, 65},
+ },
+ /* matisse.lecon-musique.ppm*/
+ {
+ {17, 24, 13}, {25, 50, 37}, {51, 60, 57}, {56, 70, 71},
+ {50, 108, 84}, {60, 120, 101}, {78, 109, 104}, {91, 110, 128},
+ {119, 123, 103}, {131, 141, 108}, {139, 147, 107}, {140, 144, 102},
+ {129, 130, 88}, {129, 120, 90}, {142, 119, 69}, {162, 122, 56},
+ {171, 127, 64}, {179, 127, 69}, {148, 107, 66}, {133, 103, 70},
+ {107, 101, 68}, {92, 116, 55}, {83, 102, 62}, {78, 127, 54},
+ {69, 132, 63}, {77, 137, 76}, {85, 144, 72}, {89, 154, 81},
+ {103, 161, 72}, {98, 163, 76}, {101, 156, 80}, {98, 164, 114},
+ {107, 177, 79}, {106, 162, 118}, {120, 164, 114}, {128, 150, 134},
+ {129, 160, 141}, {138, 163, 154}, {140, 170, 157}, {142, 170, 160},
+ {129, 167, 170}, {152, 180, 168}, {160, 179, 158}, {173, 190, 157},
+ {172, 193, 172}, {173, 201, 181}, {183, 205, 191}, {180, 198, 192},
+ {187, 202, 204}, {191, 202, 196}, {206, 209, 188}, {211, 213, 196},
+ {209, 214, 190}, {211, 208, 189}, {200, 201, 185}, {197, 199, 182},
+ {204, 198, 162}, {196, 190, 159}, {190, 192, 136}, {179, 190, 122},
+ {181, 178, 114}, {184, 163, 122}, {175, 156, 120}, {172, 156, 120},
+ {171, 156, 126}, {147, 146, 127}, {132, 151, 135}, {141, 158, 139},
+ {153, 154, 141}, {154, 149, 131}, {148, 158, 114}, {150, 150, 113},
+ {136, 142, 113}, {128, 122, 96}, {122, 119, 98}, {114, 121, 95},
+ {113, 117, 90}, {106, 107, 82}, {112, 94, 85}, {130, 95, 87},
+ {150, 92, 81}, {167, 91, 92}, {180, 78, 72}, {181, 74, 75},
+ {185, 69, 75}, {182, 67, 62}, {194, 85, 50}, {196, 122, 30},
+ {221, 159, 7}, {226, 161, 6}, {220, 163, 6}, {234, 187, 37},
+ {190, 132, 59}, {207, 156, 59}, {186, 128, 40}, {187, 130, 19},
+ {188, 129, 36}, {186, 115, 28}, {162, 108, 32}, {154, 110, 45},
+ {115, 91, 48}, {98, 100, 65}, {93, 97, 62}, {90, 93, 64},
+ {70, 81, 63}, {71, 74, 57}, {75, 78, 50}, {96, 74, 40},
+ {107, 80, 43}, {150, 81, 55}, {178, 92, 50}, {189, 88, 80},
+ {186, 105, 102}, {175, 138, 121}, {195, 151, 124}, {205, 153, 135},
+ {187, 179, 141}, {191, 210, 162}, {209, 223, 182}, {228, 234, 208},
+ {228, 225, 208}, {219, 221, 207}, {210, 212, 197}, {206, 206, 195},
+ {188, 198, 189}, {188, 194, 178}, {183, 187, 160}, {183, 191, 155},
+ {186, 186, 144}, {185, 176, 141}, {191, 175, 136}, {191, 163, 124},
+ {168, 170, 111}, {155, 184, 107}, {145, 177, 97}, {120, 168, 75},
+ {113, 149, 78}, {117, 161, 80}, {121, 171, 82}, {134, 178, 96},
+ {152, 190, 111}, {145, 194, 131}, {150, 180, 133}, {164, 179, 132},
+ {167, 177, 128}, {178, 176, 139}, {169, 166, 132}, {162, 157, 129},
+ {156, 146, 119}, {154, 146, 118}, {154, 131, 117}, {150, 109, 103},
+ {171, 93, 90}, {185, 102, 92}, {197, 102, 96}, {193, 104, 95},
+ {183, 84, 82}, {173, 71, 64}, {173, 65, 63}, {160, 58, 43},
+ {157, 48, 38}, {120, 4, 5}, {38, 30, 9}, {32, 26, 12},
+ {31, 25, 14}, {47, 48, 28}, {63, 52, 26}, {94, 63, 20},
+ {99, 66, 22}, {115, 88, 28}, {121, 98, 33}, {158, 111, 48},
+ {176, 129, 61}, {185, 121, 30}, {192, 148, 29}, {173, 124, 34},
+ {192, 107, 2}, {192, 98, 3}, {185, 91, 2}, {161, 89, 9},
+ {158, 89, 22}, {154, 96, 32}, {122, 86, 46}, {113, 85, 37},
+ {107, 94, 41}, {88, 83, 60}, {75, 80, 50}, {68, 80, 54},
+ {65, 78, 58}, {63, 71, 53}, {59, 66, 40}, {58, 51, 40},
+ {60, 68, 37}, {49, 55, 37}, {63, 65, 37}, {62, 64, 42},
+ {66, 73, 52}, {70, 80, 58}, {80, 90, 74}, {80, 95, 84},
+ {85, 93, 71}, {94, 101, 70}, {98, 98, 67}, {96, 100, 77},
+ {103, 111, 82}, {104, 115, 95}, {111, 112, 91}, {104, 109, 87},
+ {93, 113, 83}, {76, 110, 84}, {67, 93, 94}, {69, 85, 89},
+ {75, 90, 74}, {78, 81, 64}, {72, 75, 57}, {70, 73, 56},
+ {68, 71, 50}, {92, 72, 33}, {108, 59, 21}, {138, 77, 13},
+ {161, 80, 5}, {160, 65, 0}, {154, 54, 0}, {138, 34, 2},
+ {55, 42, 5}, {20, 14, 6}, {14, 9, 4}, {24, 25, 12},
+ {33, 39, 30}, {45, 45, 35}, {45, 48, 40}, {51, 62, 49},
+ {60, 74, 55}, {63, 77, 63}, {59, 103, 64}, {75, 128, 70},
+ {89, 149, 79}, {96, 157, 105}, {101, 175, 137}, {123, 187, 147},
+ {124, 194, 146}, {137, 189, 144}, {110, 177, 136}, {115, 181, 127},
+ {112, 176, 129}, {119, 162, 149}, {91, 170, 136}, {84, 152, 119},
+ {67, 152, 116}, {52, 140, 116}, {49, 141, 121}, {46, 134, 117},
+ {28, 119, 107}, {42, 88, 69}, {47, 75, 61}, {33, 50, 45},
+ },
+ /* modigliani.nude-caryatid.ppm*/
+ {
+ {31, 24, 17}, {57, 47, 38}, {89, 77, 61}, {119, 88, 86},
+ {151, 108, 104}, {173, 128, 129}, {184, 141, 152}, {196, 148, 160},
+ {197, 156, 158}, {201, 164, 169}, {199, 171, 174}, {200, 176, 179},
+ {203, 178, 181}, {203, 179, 182}, {203, 178, 185}, {201, 178, 183},
+ {202, 177, 186}, {198, 178, 184}, {195, 179, 178}, {203, 173, 173},
+ {202, 169, 169}, {204, 167, 163}, {199, 155, 155}, {200, 158, 151},
+ {203, 157, 147}, {199, 157, 148}, {196, 152, 139}, {202, 144, 123},
+ {200, 142, 123}, {190, 130, 119}, {175, 111, 102}, {175, 92, 77},
+ {168, 91, 81}, {158, 87, 83}, {139, 73, 72}, {133, 71, 62},
+ {114, 57, 55}, {86, 51, 50}, {50, 34, 32}, {40, 32, 30},
+ {33, 31, 33}, {33, 31, 34}, {40, 35, 34}, {57, 53, 43},
+ {82, 70, 66}, {108, 84, 89}, {130, 107, 112}, {170, 134, 135},
+ {184, 142, 145}, {191, 151, 145}, {189, 151, 150}, {181, 147, 146},
+ {172, 155, 132}, {169, 153, 130}, {168, 152, 129}, {168, 151, 129},
+ {167, 149, 127}, {149, 128, 113}, {142, 120, 97}, {140, 108, 87},
+ {127, 105, 84}, {127, 85, 80}, {118, 77, 78}, {115, 76, 80},
+ {112, 80, 74}, {96, 77, 60}, {72, 57, 48}, {44, 38, 36},
+ {38, 29, 34}, {35, 29, 31}, {37, 31, 27}, {41, 33, 31},
+ {48, 40, 38}, {86, 56, 51}, {116, 79, 74}, {125, 95, 94},
+ {153, 110, 106}, {176, 122, 127}, {185, 135, 135}, {194, 143, 142},
+ {195, 145, 143}, {190, 145, 134}, {179, 129, 122}, {164, 119, 119},
+ {156, 111, 108}, {140, 106, 101}, {128, 103, 98}, {124, 106, 103},
+ {125, 97, 95}, {145, 92, 89}, {156, 93, 87}, {157, 92, 91},
+ {164, 100, 96}, {165, 109, 111}, {182, 123, 125}, {181, 124, 126},
+ {180, 134, 130}, {169, 149, 128}, {169, 151, 129}, {171, 155, 130},
+ {193, 151, 139}, {201, 154, 150}, {204, 156, 152}, {212, 163, 163},
+ {210, 166, 162}, {216, 175, 169}, {225, 188, 179}, {218, 185, 188},
+ {217, 192, 196}, {224, 195, 196}, {224, 195, 200}, {225, 205, 209},
+ {229, 209, 212}, {226, 208, 211}, {218, 203, 212}, {217, 194, 200},
+ {215, 196, 199}, {214, 191, 194}, {218, 185, 183}, {215, 183, 181},
+ {214, 175, 173}, {206, 167, 164}, {197, 159, 160}, {193, 154, 153},
+ {177, 150, 148}, {170, 154, 132}, {168, 149, 127}, {160, 139, 111},
+ {152, 126, 103}, {152, 128, 104}, {161, 142, 118}, {168, 150, 129},
+ {174, 156, 134}, {190, 157, 148}, {196, 159, 161}, {196, 164, 172},
+ {196, 174, 181}, {198, 176, 183}, {202, 180, 182}, {206, 180, 180},
+ {210, 178, 180}, {214, 187, 183}, {209, 188, 189}, {208, 184, 193},
+ {211, 189, 191}, {215, 194, 193}, {212, 196, 197}, {207, 191, 199},
+ {194, 189, 194}, {177, 176, 180}, {173, 159, 165}, {173, 149, 155},
+ {170, 149, 154}, {167, 136, 146}, {161, 125, 128}, {146, 109, 113},
+ {132, 98, 92}, {128, 80, 81}, {133, 77, 80}, {148, 92, 92},
+ {156, 102, 99}, {165, 117, 113}, {174, 131, 128}, {187, 143, 140},
+ {181, 142, 139}, {168, 122, 122}, {144, 108, 104}, {121, 92, 94},
+ {107, 83, 81}, {74, 63, 54}, {45, 43, 41}, {41, 32, 31},
+ {37, 28, 26}, {33, 25, 25}, {35, 25, 24}, {38, 28, 27},
+ {58, 40, 24}, {76, 54, 40}, {109, 68, 56}, {104, 65, 44},
+ {67, 53, 40}, {42, 34, 32}, {36, 27, 28}, {32, 23, 27},
+ {29, 20, 23}, {30, 20, 21}, {31, 21, 20}, {29, 20, 19},
+ {21, 15, 15}, {15, 11, 12}, {15, 10, 11}, {18, 7, 9},
+ {18, 12, 12}, {21, 12, 14}, {21, 12, 15}, {21, 15, 17},
+ {25, 20, 21}, {29, 23, 24}, {30, 24, 27}, {31, 24, 27},
+ {34, 25, 26}, {40, 35, 33}, {51, 49, 46}, {68, 72, 61},
+ {105, 99, 94}, {123, 109, 116}, {144, 118, 116}, {157, 149, 136},
+ {166, 150, 125}, {154, 134, 102}, {151, 127, 103}, {147, 123, 99},
+ {143, 118, 96}, {153, 104, 100}, {160, 107, 104}, {166, 115, 103},
+ {181, 121, 112}, {187, 119, 112}, {177, 123, 111}, {155, 112, 108},
+ {142, 119, 97}, {138, 122, 97}, {126, 106, 85}, {118, 99, 81},
+ {103, 80, 60}, {80, 60, 48}, {53, 45, 37}, {42, 34, 32},
+ {41, 33, 31}, {43, 38, 35}, {66, 57, 53}, {90, 71, 73},
+ {116, 80, 81}, {135, 101, 104}, {152, 117, 118}, {168, 139, 142},
+ {164, 153, 155}, {153, 157, 168}, {147, 158, 172}, {170, 152, 159},
+ {187, 158, 166}, {186, 163, 166}, {188, 164, 163}, {192, 168, 167},
+ {201, 167, 164}, {204, 168, 168}, {202, 166, 167}, {198, 163, 166},
+ {190, 161, 162}, {185, 154, 157}, {187, 149, 152}, {182, 147, 147},
+ {166, 135, 138}, {139, 114, 121}, {104, 92, 88}, {69, 62, 55},
+ },
+ /* braque.instruments.ppm*/
+ {
+ {13, 9, 4}, {22, 18, 10}, {35, 31, 20}, {48, 42, 14},
+ {56, 45, 7}, {68, 57, 11}, {83, 66, 15}, {101, 82, 18},
+ {117, 89, 15}, {116, 87, 19}, {103, 83, 16}, {88, 94, 25},
+ {72, 92, 33}, {64, 83, 25}, {58, 79, 20}, {57, 68, 18},
+ {54, 66, 15}, {62, 69, 15}, {65, 73, 13}, {70, 68, 11},
+ {78, 62, 12}, {87, 61, 11}, {103, 68, 11}, {111, 70, 20},
+ {128, 81, 12}, {132, 87, 23}, {139, 85, 22}, {134, 107, 24},
+ {138, 115, 30}, {146, 118, 44}, {157, 130, 56}, {165, 139, 63},
+ {161, 145, 82}, {170, 164, 114}, {186, 177, 126}, {195, 185, 128},
+ {210, 200, 137}, {220, 213, 139}, {234, 213, 133}, {240, 213, 133},
+ {239, 220, 145}, {249, 227, 147}, {249, 227, 153}, {250, 231, 160},
+ {244, 227, 162}, {247, 232, 165}, {249, 242, 188}, {245, 242, 171},
+ {255, 252, 190}, {251, 241, 168}, {247, 241, 181}, {241, 231, 170},
+ {237, 229, 166}, {222, 217, 161}, {212, 205, 159}, {208, 202, 146},
+ {205, 192, 123}, {196, 174, 99}, {178, 150, 78}, {168, 140, 66},
+ {150, 126, 52}, {131, 117, 43}, {127, 95, 33}, {120, 96, 34},
+ {106, 95, 41}, {95, 90, 45}, {93, 87, 50}, {93, 91, 54},
+ {97, 86, 62}, {118, 109, 80}, {146, 132, 93}, {169, 161, 125},
+ {189, 183, 135}, {207, 197, 145}, {213, 206, 148}, {221, 206, 139},
+ {223, 203, 137}, {223, 202, 137}, {225, 204, 134}, {226, 207, 130},
+ {234, 207, 127}, {222, 203, 126}, {216, 188, 114}, {210, 179, 98},
+ {223, 172, 46}, {217, 171, 46}, {211, 166, 46}, {208, 159, 29},
+ {202, 145, 22}, {199, 153, 33}, {196, 161, 62}, {203, 179, 91},
+ {211, 192, 115}, {213, 197, 127}, {220, 204, 130}, {221, 208, 129},
+ {221, 201, 130}, {212, 196, 124}, {200, 179, 113}, {194, 174, 96},
+ {175, 154, 83}, {166, 146, 72}, {167, 147, 76}, {180, 156, 90},
+ {203, 177, 110}, {211, 192, 115}, {213, 198, 116}, {218, 199, 122},
+ {209, 191, 107}, {193, 167, 77}, {206, 159, 42}, {194, 146, 32},
+ {193, 141, 20}, {186, 135, 10}, {179, 141, 16}, {179, 143, 8},
+ {153, 120, 7}, {147, 110, 20}, {128, 110, 25}, {118, 103, 37},
+ {115, 107, 38}, {110, 122, 46}, {117, 120, 64}, {132, 129, 74},
+ {140, 133, 89}, {145, 133, 85}, {135, 128, 80}, {136, 112, 67},
+ {139, 116, 53}, {138, 118, 31}, {136, 109, 27}, {133, 101, 14},
+ {135, 92, 4}, {130, 80, 2}, {123, 74, 6}, {111, 73, 1},
+ {93, 67, 4}, {80, 63, 8}, {79, 61, 14}, {82, 70, 22},
+ {89, 93, 19}, {106, 100, 28}, {119, 103, 39}, {139, 125, 42},
+ {159, 138, 59}, {176, 148, 84}, {188, 162, 110}, {207, 184, 130},
+ {215, 196, 131}, {219, 193, 137}, {208, 187, 126}, {201, 174, 109},
+ {177, 158, 83}, {179, 154, 61}, {173, 137, 39}, {177, 139, 26},
+ {177, 145, 20}, {150, 115, 11}, {139, 108, 7}, {130, 101, 9},
+ {114, 76, 5}, {103, 67, 8}, {100, 55, 5}, {82, 59, 8},
+ {82, 58, 17}, {71, 64, 22}, {66, 74, 18}, {60, 73, 22},
+ {60, 73, 28}, {68, 85, 27}, {79, 88, 33}, {87, 97, 37},
+ {90, 107, 37}, {96, 97, 58}, {120, 113, 63}, {137, 121, 57},
+ {159, 131, 57}, {171, 144, 64}, {165, 147, 63}, {161, 136, 48},
+ {162, 125, 34}, {154, 122, 42}, {145, 113, 29}, {127, 102, 11},
+ {110, 93, 3}, {87, 87, 1}, {83, 59, 0}, {79, 56, 5},
+ {81, 53, 6}, {71, 45, 6}, {61, 36, 2}, {60, 34, 1},
+ {54, 31, 5}, {51, 24, 3}, {46, 19, 0}, {39, 15, 4},
+ {34, 16, 2}, {33, 15, 4}, {36, 13, 5}, {39, 16, 3},
+ {37, 29, 3}, {38, 27, 8}, {38, 27, 8}, {44, 20, 9},
+ {56, 28, 6}, {57, 26, 2}, {61, 25, 2}, {65, 29, 4},
+ {58, 30, 9}, {65, 38, 10}, {66, 45, 13}, {73, 47, 10},
+ {70, 54, 8}, {70, 55, 6}, {56, 59, 6}, {48, 61, 14},
+ {48, 55, 7}, {43, 55, 5}, {48, 55, 14}, {45, 68, 8},
+ {48, 85, 20}, {56, 86, 24}, {60, 81, 22}, {57, 81, 25},
+ {55, 82, 40}, {71, 74, 51}, {89, 75, 46}, {90, 82, 53},
+ {102, 82, 64}, {120, 106, 81}, {130, 123, 78}, {142, 137, 81},
+ {163, 153, 92}, {178, 167, 112}, {186, 175, 120}, {180, 168, 120},
+ {164, 154, 93}, {147, 144, 74}, {134, 132, 71}, {130, 115, 66},
+ {120, 108, 60}, {109, 98, 44}, {110, 87, 29}, {128, 87, 22},
+ {122, 82, 10}, {110, 77, 10}, {100, 65, 5}, {91, 55, 2},
+ {81, 54, 3}, {76, 54, 7}, {64, 50, 9}, {44, 52, 13},
+ {35, 42, 8}, {35, 41, 8}, {29, 26, 9}, {24, 12, 1},
+ },
+ /* calcoast09.ppm*/
+ {
+ {32, 32, 44}, {52, 56, 66}, {68, 71, 89}, {76, 94, 103},
+ {82, 116, 122}, {83, 114, 143}, {90, 123, 154}, {94, 127, 158},
+ {96, 130, 161}, {99, 134, 164}, {99, 135, 167}, {101, 136, 168},
+ {103, 138, 170}, {103, 139, 171}, {103, 139, 173}, {103, 139, 174},
+ {103, 139, 175}, {104, 140, 174}, {106, 142, 176}, {108, 143, 175},
+ {108, 143, 176}, {109, 144, 176}, {108, 143, 174}, {106, 141, 172},
+ {103, 139, 173}, {102, 138, 172}, {100, 136, 170}, {98, 134, 170},
+ {98, 133, 168}, {97, 131, 166}, {95, 129, 164}, {95, 128, 159},
+ {95, 128, 156}, {94, 126, 153}, {92, 127, 145}, {82, 118, 140},
+ {76, 115, 122}, {69, 100, 107}, {70, 89, 94}, {61, 84, 91},
+ {64, 76, 88}, {71, 75, 78}, {70, 72, 68}, {65, 73, 73},
+ {66, 65, 73}, {56, 62, 74}, {55, 59, 71}, {54, 59, 69},
+ {50, 55, 64}, {41, 45, 58}, {42, 41, 53}, {44, 44, 53},
+ {46, 44, 58}, {48, 50, 55}, {58, 56, 56}, {57, 64, 65},
+ {72, 68, 63}, {81, 77, 64}, {96, 82, 62}, {103, 93, 73},
+ {100, 92, 78}, {92, 89, 71}, {88, 88, 71}, {89, 84, 73},
+ {87, 87, 77}, {94, 94, 84}, {99, 101, 89}, {99, 107, 99},
+ {96, 110, 119}, {85, 113, 143}, {85, 115, 149}, {91, 124, 155},
+ {96, 129, 160}, {100, 135, 165}, {106, 139, 170}, {108, 143, 172},
+ {119, 146, 156}, {127, 149, 160}, {148, 165, 157}, {165, 188, 190},
+ {179, 191, 172}, {184, 189, 154}, {190, 194, 149}, {174, 155, 104},
+ {147, 131, 96}, {144, 138, 108}, {137, 135, 107}, {138, 130, 112},
+ {145, 142, 115}, {132, 137, 131}, {114, 128, 136}, {101, 130, 142},
+ {102, 130, 157}, {96, 129, 162}, {96, 130, 165}, {98, 131, 166},
+ {99, 133, 168}, {99, 134, 169}, {100, 134, 169}, {100, 136, 169},
+ {100, 134, 169}, {100, 134, 169}, {99, 134, 166}, {96, 129, 164},
+ {94, 127, 160}, {92, 124, 157}, {90, 123, 154}, {84, 114, 147},
+ {81, 109, 139}, {75, 101, 129}, {62, 91, 119}, {59, 89, 105},
+ {55, 88, 99}, {55, 87, 98}, {57, 82, 112}, {58, 81, 115},
+ {63, 87, 120}, {67, 93, 126}, {74, 100, 133}, {77, 106, 138},
+ {81, 110, 143}, {80, 110, 144}, {78, 107, 141}, {75, 101, 134},
+ {68, 94, 127}, {62, 88, 121}, {64, 90, 120}, {73, 98, 128},
+ {75, 104, 136}, {79, 110, 140}, {81, 111, 144}, {82, 112, 146},
+ {82, 111, 142}, {77, 105, 135}, {79, 108, 117}, {81, 98, 115},
+ {87, 96, 103}, {91, 95, 97}, {94, 91, 91}, {101, 102, 94},
+ {117, 116, 100}, {125, 128, 107}, {118, 134, 131}, {122, 145, 148},
+ {130, 166, 167}, {140, 176, 186}, {156, 193, 200}, {163, 204, 212},
+ {167, 213, 212}, {169, 207, 215}, {168, 207, 211}, {157, 199, 205},
+ {137, 175, 189}, {130, 166, 174}, {118, 152, 164}, {118, 153, 157},
+ {118, 158, 160}, {123, 164, 166}, {136, 177, 177}, {146, 191, 197},
+ {162, 197, 198}, {166, 201, 203}, {161, 200, 203}, {153, 189, 200},
+ {137, 175, 185}, {121, 163, 167}, {108, 147, 151}, {106, 141, 153},
+ {98, 131, 161}, {96, 131, 161}, {100, 136, 155}, {111, 132, 145},
+ {112, 134, 144}, {105, 134, 138}, {101, 134, 138}, {105, 130, 141},
+ {109, 117, 128}, {110, 115, 107}, {104, 107, 95}, {113, 111, 88},
+ {119, 112, 92}, {126, 124, 103}, {121, 116, 103}, {119, 117, 90},
+ {117, 112, 85}, {112, 102, 82}, {95, 95, 81}, {86, 86, 80},
+ {80, 84, 83}, {81, 92, 101}, {85, 112, 122}, {94, 127, 132},
+ {106, 140, 143}, {116, 162, 160}, {142, 183, 185}, {184, 201, 205},
+ {189, 224, 229}, {214, 248, 247}, {224, 251, 250}, {201, 232, 240},
+ {187, 218, 222}, {170, 194, 203}, {138, 175, 176}, {115, 153, 162},
+ {101, 137, 151}, {83, 126, 132}, {67, 106, 111}, {70, 87, 93},
+ {73, 80, 82}, {73, 76, 78}, {71, 81, 80}, {71, 78, 81},
+ {66, 75, 88}, {61, 81, 97}, {70, 91, 117}, {73, 98, 127},
+ {77, 106, 138}, {82, 110, 143}, {83, 115, 148}, {85, 118, 151},
+ {90, 123, 156}, {90, 124, 157}, {92, 125, 158}, {93, 126, 159},
+ {93, 126, 159}, {94, 127, 160}, {94, 127, 162}, {94, 127, 162},
+ {94, 128, 163}, {95, 128, 163}, {95, 129, 164}, {95, 128, 163},
+ {95, 128, 161}, {93, 126, 159}, {93, 126, 157}, {94, 124, 153},
+ {91, 122, 140}, {81, 116, 124}, {78, 105, 114}, {81, 91, 96},
+ {84, 84, 80}, {77, 78, 71}, {64, 62, 54}, {51, 43, 35},
+ {37, 29, 35}, {26, 23, 30}, {0, 0, 0}, {5, 2, 12},
+ {22, 27, 46}, {35, 32, 48}, {43, 40, 50}, {42, 49, 58},
+ {29, 56, 73}, {35, 62, 80}, {38, 47, 63}, {29, 33, 53},
+ },
+ /* dodge102.ppm*/
+ {
+ {47, 42, 46}, {88, 93, 90}, {136, 152, 150}, {140, 218, 225},
+ {130, 242, 251}, {123, 245, 243}, {122, 188, 198}, {99, 137, 153},
+ {74, 113, 109}, {74, 89, 82}, {58, 73, 61}, {49, 64, 54},
+ {58, 41, 32}, {64, 30, 24}, {63, 28, 23}, {65, 27, 22},
+ {76, 30, 21}, {75, 34, 17}, {74, 32, 14}, {79, 31, 14},
+ {80, 32, 15}, {85, 31, 18}, {82, 34, 23}, {89, 42, 25},
+ {85, 45, 27}, {78, 49, 29}, {77, 40, 29}, {77, 36, 27},
+ {89, 39, 35}, {112, 56, 39}, {116, 69, 43}, {118, 89, 51},
+ {124, 92, 37}, {160, 122, 46}, {171, 135, 56}, {168, 156, 97},
+ {153, 159, 102}, {144, 150, 88}, {137, 115, 85}, {118, 108, 89},
+ {108, 95, 75}, {95, 87, 63}, {95, 78, 55}, {88, 65, 56},
+ {92, 57, 49}, {94, 57, 48}, {94, 53, 47}, {98, 51, 43},
+ {105, 67, 50}, {109, 74, 60}, {116, 91, 81}, {140, 119, 89},
+ {179, 161, 111}, {235, 221, 170}, {245, 246, 140}, {237, 221, 102},
+ {227, 193, 91}, {215, 181, 106}, {197, 157, 88}, {184, 153, 62},
+ {161, 124, 46}, {149, 99, 41}, {143, 84, 37}, {130, 77, 42},
+ {117, 73, 48}, {118, 67, 53}, {124, 81, 60}, {139, 107, 90},
+ {156, 161, 147}, {192, 207, 161}, {221, 235, 186}, {216, 244, 206},
+ {178, 223, 200}, {179, 187, 163}, {154, 134, 116}, {146, 106, 79},
+ {129, 105, 52}, {114, 103, 49}, {119, 122, 74}, {135, 141, 112},
+ {147, 164, 149}, {165, 213, 168}, {199, 243, 222}, {187, 251, 248},
+ {190, 248, 233}, {169, 215, 199}, {158, 176, 150}, {136, 139, 120},
+ {119, 121, 104}, {121, 120, 93}, {146, 152, 109}, {180, 159, 99},
+ {181, 191, 105}, {181, 205, 157}, {175, 231, 216}, {168, 243, 243},
+ {138, 241, 247}, {127, 247, 247}, {138, 247, 240}, {141, 188, 174},
+ {112, 136, 129}, {91, 103, 81}, {69, 86, 67}, {69, 59, 41},
+ {62, 59, 21}, {59, 36, 17}, {61, 25, 14}, {61, 24, 16},
+ {55, 25, 15}, {44, 26, 23}, {47, 31, 23}, {54, 33, 22},
+ {60, 34, 30}, {53, 42, 49}, {51, 60, 66}, {46, 72, 60},
+ {53, 84, 86}, {54, 100, 125}, {84, 149, 155}, {92, 243, 249},
+ {108, 244, 249}, {130, 219, 231}, {125, 155, 166}, {71, 105, 120},
+ {47, 89, 118}, {26, 50, 68}, {24, 25, 32}, {13, 12, 21},
+ {12, 9, 8}, {5, 4, 4}, {5, 2, 2}, {4, 0, 1},
+ {5, 1, 0}, {12, 4, 2}, {16, 7, 4}, {21, 7, 1},
+ {22, 5, 2}, {18, 5, 5}, {16, 6, 6}, {9, 5, 4},
+ {5, 2, 1}, {1, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
+ {0, 0, 0}, {1, 0, 0}, {3, 0, 1}, {6, 0, 3},
+ {16, 5, 5}, {23, 9, 4}, {22, 15, 9}, {18, 14, 14},
+ {20, 20, 26}, {26, 26, 23}, {43, 36, 29}, {52, 42, 42},
+ {59, 67, 62}, {75, 80, 80}, {77, 116, 99}, {97, 141, 153},
+ {121, 219, 225}, {121, 246, 252}, {136, 238, 245}, {143, 176, 174},
+ {121, 125, 121}, {107, 98, 94}, {76, 93, 72}, {77, 86, 51},
+ {83, 57, 49}, {78, 51, 44}, {70, 40, 41}, {67, 31, 31},
+ {63, 28, 24}, {62, 27, 20}, {58, 29, 10}, {55, 31, 12},
+ {49, 28, 13}, {42, 23, 7}, {37, 19, 7}, {38, 17, 6},
+ {43, 13, 5}, {47, 14, 5}, {59, 15, 6}, {61, 17, 6},
+ {63, 21, 5}, {67, 23, 8}, {69, 27, 6}, {66, 28, 6},
+ {66, 30, 13}, {70, 34, 18}, {70, 40, 28}, {78, 42, 35},
+ {88, 53, 39}, {93, 69, 56}, {113, 83, 83}, {138, 119, 116},
+ {196, 181, 138}, {222, 227, 216}, {247, 245, 223}, {250, 249, 228},
+ {247, 251, 242}, {230, 247, 235}, {199, 236, 231}, {199, 236, 228},
+ {193, 231, 227}, {165, 173, 178}, {153, 134, 140}, {128, 119, 110},
+ {112, 95, 87}, {108, 78, 57}, {99, 79, 48}, {94, 71, 37},
+ {71, 52, 28}, {65, 47, 26}, {69, 31, 26}, {61, 26, 22},
+ {56, 24, 19}, {56, 21, 15}, {55, 21, 13}, {47, 16, 11},
+ {45, 11, 3}, {41, 10, 4}, {39, 10, 6}, {37, 11, 5},
+ {33, 14, 3}, {30, 9, 2}, {30, 5, 0}, {28, 9, 3},
+ {27, 11, 2}, {27, 11, 3}, {26, 14, 7}, {24, 12, 5},
+ {22, 15, 5}, {27, 12, 4}, {31, 16, 6}, {36, 17, 7},
+ {41, 21, 11}, {48, 20, 12}, {57, 18, 12}, {61, 22, 11},
+ {65, 23, 13}, {68, 23, 14}, {68, 22, 9}, {63, 19, 7},
+ {57, 17, 7}, {46, 17, 7}, {37, 18, 5}, {34, 17, 6},
+ {30, 18, 14}, {30, 26, 11}, {25, 32, 17}, {37, 31, 31},
+ },
+ /* ernst.anti-pope.ppm*/
+ {
+ {60, 41, 44}, {63, 44, 53}, {61, 45, 54}, {62, 43, 55},
+ {59, 42, 56}, {61, 39, 55}, {61, 43, 57}, {65, 44, 53},
+ {67, 46, 53}, {74, 44, 54}, {76, 44, 57}, {80, 48, 63},
+ {87, 49, 64}, {89, 43, 57}, {93, 46, 53}, {96, 44, 53},
+ {106, 49, 53}, {104, 56, 56}, {108, 68, 64}, {111, 72, 67},
+ {119, 70, 68}, {129, 76, 71}, {133, 79, 68}, {133, 75, 74},
+ {139, 65, 65}, {134, 69, 76}, {143, 77, 76}, {170, 77, 76},
+ {148, 92, 81}, {136, 92, 73}, {135, 90, 87}, {149, 96, 75},
+ {174, 114, 73}, {203, 130, 81}, {202, 137, 86}, {201, 164, 136},
+ {201, 164, 138}, {176, 147, 135}, {203, 131, 101}, {167, 145, 117},
+ {186, 121, 108}, {174, 107, 89}, {146, 117, 90}, {133, 112, 98},
+ {118, 112, 123}, {100, 95, 100}, {125, 102, 98}, {113, 100, 93},
+ {102, 84, 81}, {100, 74, 86}, {99, 77, 97}, {98, 63, 76},
+ {94, 53, 70}, {86, 52, 65}, {81, 49, 63}, {75, 52, 61},
+ {71, 52, 64}, {70, 49, 64}, {70, 50, 62}, {69, 52, 63},
+ {67, 50, 61}, {64, 50, 63}, {66, 51, 59}, {63, 51, 62},
+ {63, 54, 61}, {62, 50, 61}, {62, 48, 61}, {62, 48, 61},
+ {57, 45, 59}, {57, 43, 56}, {57, 46, 56}, {55, 45, 56},
+ {53, 43, 56}, {53, 43, 57}, {54, 40, 53}, {53, 41, 55},
+ {55, 43, 57}, {57, 42, 59}, {57, 47, 59}, {59, 49, 60},
+ {53, 48, 68}, {58, 50, 66}, {51, 49, 70}, {49, 49, 64},
+ {52, 51, 70}, {54, 51, 69}, {57, 53, 67}, {61, 52, 73},
+ {65, 53, 75}, {64, 52, 70}, {62, 51, 67}, {61, 50, 68},
+ {60, 48, 70}, {58, 49, 70}, {58, 49, 67}, {60, 48, 62},
+ {61, 50, 64}, {61, 50, 63}, {62, 50, 62}, {63, 48, 61},
+ {65, 47, 61}, {67, 45, 59}, {65, 46, 58}, {64, 46, 58},
+ {63, 47, 58}, {63, 45, 57}, {59, 45, 58}, {58, 44, 57},
+ {61, 43, 57}, {61, 44, 57}, {61, 43, 59}, {62, 42, 60},
+ {64, 44, 60}, {66, 45, 61}, {67, 47, 62}, {66, 48, 67},
+ {66, 50, 69}, {72, 53, 68}, {74, 56, 70}, {78, 56, 71},
+ {73, 68, 75}, {80, 55, 76}, {85, 58, 78}, {79, 60, 84},
+ {86, 65, 82}, {96, 68, 76}, {98, 72, 79}, {108, 85, 78},
+ {123, 88, 70}, {113, 83, 78}, {122, 85, 81}, {132, 104, 92},
+ {127, 111, 90}, {136, 93, 101}, {142, 83, 87}, {137, 82, 75},
+ {121, 80, 77}, {110, 67, 85}, {102, 67, 77}, {94, 66, 72},
+ {83, 68, 66}, {80, 71, 76}, {81, 73, 85}, {87, 84, 89},
+ {85, 69, 84}, {90, 84, 96}, {82, 84, 88}, {80, 90, 95},
+ {85, 84, 110}, {93, 72, 86}, {82, 72, 81}, {77, 70, 82},
+ {74, 63, 77}, {72, 56, 74}, {71, 55, 68}, {67, 51, 65},
+ {65, 51, 67}, {64, 50, 65}, {67, 50, 66}, {72, 51, 68},
+ {74, 50, 66}, {80, 53, 63}, {86, 55, 68}, {89, 63, 68},
+ {91, 66, 68}, {97, 65, 65}, {103, 66, 58}, {99, 64, 64},
+ {102, 61, 71}, {111, 58, 72}, {104, 63, 63}, {118, 53, 59},
+ {127, 52, 59}, {141, 47, 52}, {161, 39, 53}, {164, 37, 51},
+ {143, 41, 51}, {136, 46, 61}, {118, 47, 50}, {111, 47, 59},
+ {109, 45, 59}, {106, 46, 61}, {107, 42, 63}, {109, 41, 58},
+ {106, 42, 53}, {95, 44, 59}, {88, 45, 57}, {78, 43, 61},
+ {70, 47, 63}, {67, 46, 61}, {66, 45, 62}, {64, 46, 62},
+ {62, 45, 60}, {62, 46, 58}, {60, 45, 58}, {58, 45, 59},
+ {58, 47, 58}, {62, 48, 62}, {64, 50, 63}, {66, 52, 65},
+ {69, 55, 68}, {71, 63, 68}, {68, 65, 76}, {66, 66, 76},
+ {62, 61, 78}, {62, 61, 79}, {65, 55, 80}, {66, 54, 76},
+ {65, 53, 77}, {60, 52, 80}, {57, 54, 73}, {58, 58, 70},
+ {57, 59, 74}, {62, 61, 80}, {59, 67, 86}, {63, 68, 86},
+ {63, 68, 88}, {65, 70, 90}, {67, 65, 93}, {62, 70, 95},
+ {58, 68, 99}, {62, 75, 92}, {59, 77, 97}, {61, 76, 109},
+ {61, 77, 100}, {54, 70, 105}, {56, 70, 105}, {53, 67, 102},
+ {52, 65, 99}, {55, 65, 102}, {54, 68, 103}, {61, 73, 92},
+ {64, 68, 94}, {68, 76, 95}, {74, 79, 89}, {70, 77, 83},
+ {66, 72, 87}, {68, 62, 92}, {70, 56, 77}, {72, 55, 76},
+ {68, 56, 75}, {72, 53, 72}, {74, 54, 66}, {75, 56, 64},
+ {77, 53, 61}, {81, 50, 58}, {87, 54, 52}, {96, 51, 58},
+ {92, 44, 61}, {85, 45, 62}, {75, 45, 58}, {71, 41, 54},
+ {70, 41, 56}, {70, 41, 56}, {69, 41, 55}, {66, 41, 53},
+ },
+ /* ernst.ubu-imperator.ppm*/
+ {
+ {36, 57, 12}, {87, 102, 130}, {126, 148, 170}, {137, 166, 178},
+ {146, 174, 185}, {160, 179, 186}, {163, 183, 190}, {161, 180, 186},
+ {154, 172, 181}, {147, 165, 174}, {138, 144, 155}, {144, 103, 23},
+ {127, 77, 3}, {93, 20, 6}, {78, 10, 6}, {65, 4, 2},
+ {53, 0, 8}, {30, 4, 0}, {9, 3, 2}, {1, 1, 0},
+ {0, 0, 1}, {3, 1, 3}, {5, 0, 9}, {21, 15, 4},
+ {32, 17, 0}, {39, 3, 2}, {48, 5, 0}, {54, 4, 0},
+ {56, 3, 3}, {65, 3, 0}, {66, 2, 2}, {70, 3, 3},
+ {76, 7, 1}, {80, 4, 1}, {79, 3, 0}, {80, 4, 0},
+ {81, 9, 0}, {81, 15, 0}, {87, 26, 1}, {103, 68, 8},
+ {108, 151, 11}, {93, 159, 31}, {123, 149, 161}, {139, 163, 175},
+ {149, 171, 181}, {158, 176, 184}, {162, 177, 186}, {166, 178, 187},
+ {164, 175, 185}, {161, 173, 181}, {162, 170, 173}, {172, 123, 74},
+ {178, 113, 3}, {187, 117, 1}, {190, 122, 0}, {184, 122, 2},
+ {165, 123, 8}, {108, 143, 36}, {117, 147, 164}, {129, 161, 176},
+ {129, 166, 182}, {103, 148, 174}, {92, 105, 135}, {72, 69, 15},
+ {76, 24, 3}, {72, 17, 3}, {73, 16, 1}, {75, 15, 2},
+ {77, 14, 3}, {78, 11, 4}, {78, 9, 3}, {77, 9, 1},
+ {74, 9, 1}, {72, 10, 2}, {71, 10, 4}, {66, 9, 4},
+ {66, 6, 1}, {64, 6, 2}, {66, 10, 1}, {68, 12, 2},
+ {69, 8, 1}, {68, 6, 1}, {69, 6, 0}, {71, 7, 0},
+ {71, 8, 1}, {73, 10, 2}, {75, 12, 1}, {75, 14, 1},
+ {81, 13, 2}, {84, 15, 1}, {90, 17, 2}, {99, 23, 2},
+ {139, 79, 0}, {151, 87, 6}, {134, 63, 9}, {102, 21, 4},
+ {93, 15, 5}, {92, 12, 6}, {101, 10, 2}, {125, 27, 3},
+ {163, 66, 2}, {179, 76, 2}, {190, 94, 5}, {206, 116, 4},
+ {211, 121, 1}, {213, 130, 2}, {214, 136, 1}, {209, 137, 1},
+ {198, 131, 2}, {181, 112, 1}, {167, 100, 0}, {150, 90, 1},
+ {126, 87, 7}, {58, 84, 7}, {34, 64, 2}, {62, 90, 10},
+ {131, 87, 4}, {157, 83, 1}, {166, 87, 2}, {168, 88, 2},
+ {169, 88, 1}, {175, 87, 1}, {180, 90, 4}, {177, 98, 4},
+ {181, 102, 1}, {177, 105, 1}, {179, 109, 3}, {188, 112, 1},
+ {195, 107, 1}, {197, 109, 0}, {200, 116, 2}, {192, 121, 4},
+ {191, 121, 2}, {179, 113, 3}, {173, 105, 2}, {158, 106, 9},
+ {107, 121, 142}, {114, 139, 162}, {109, 136, 162}, {88, 101, 138},
+ {92, 44, 33}, {86, 16, 5}, {84, 10, 2}, {82, 9, 3},
+ {84, 7, 1}, {89, 11, 0}, {91, 14, 1}, {99, 17, 1},
+ {110, 20, 0}, {151, 57, 4}, {165, 68, 2}, {170, 70, 6},
+ {170, 69, 2}, {168, 73, 2}, {171, 78, 0}, {173, 88, 0},
+ {175, 95, 2}, {182, 105, 0}, {189, 114, 0}, {197, 123, 2},
+ {206, 127, 2}, {209, 130, 2}, {212, 129, 3}, {208, 124, 1},
+ {206, 122, 1}, {198, 124, 4}, {191, 119, 2}, {186, 104, 5},
+ {177, 89, 8}, {169, 74, 5}, {164, 73, 2}, {157, 57, 1},
+ {124, 25, 6}, {102, 16, 2}, {95, 16, 3}, {91, 14, 2},
+ {87, 16, 2}, {88, 15, 4}, {95, 22, 2}, {131, 52, 2},
+ {159, 67, 1}, {162, 76, 0}, {162, 80, 2}, {168, 88, 6},
+ {173, 96, 6}, {175, 96, 3}, {172, 97, 2}, {173, 98, 3},
+ {174, 102, 2}, {167, 102, 4}, {162, 113, 19}, {145, 151, 153},
+ {162, 171, 176}, {163, 172, 179}, {169, 178, 183}, {168, 177, 186},
+ {168, 184, 193}, {169, 185, 190}, {176, 186, 188}, {182, 191, 183},
+ {180, 189, 188}, {173, 190, 196}, {177, 191, 200}, {169, 191, 198},
+ {159, 190, 201}, {148, 176, 187}, {138, 166, 178}, {133, 158, 172},
+ {109, 126, 146}, {74, 88, 37}, {58, 27, 5}, {71, 19, 2},
+ {78, 19, 3}, {81, 15, 5}, {92, 33, 34}, {87, 99, 124},
+ {102, 126, 155}, {94, 105, 113}, {130, 95, 4}, {146, 99, 3},
+ {152, 96, 1}, {159, 92, 2}, {166, 103, 2}, {177, 113, 0},
+ {175, 117, 2}, {171, 121, 16}, {152, 133, 70}, {146, 163, 173},
+ {150, 168, 177}, {154, 172, 181}, {153, 176, 184}, {151, 173, 186},
+ {154, 171, 185}, {149, 169, 184}, {145, 169, 177}, {138, 154, 157},
+ {172, 124, 23}, {195, 124, 6}, {197, 126, 7}, {200, 127, 7},
+ {212, 130, 11}, {175, 176, 172}, {175, 183, 184}, {173, 182, 181},
+ {160, 170, 179}, {139, 161, 174}, {125, 138, 160}, {91, 98, 105},
+ {43, 58, 6}, {29, 18, 5}, {15, 12, 4}, {3, 6, 4},
+ {1, 3, 4}, {4, 3, 2}, {11, 11, 1}, {26, 25, 3},
+ },
+ /* fighting-forms.ppm*/
+ {
+ {2, 3, 0}, {11, 15, 2}, {22, 23, 8}, {40, 36, 23},
+ {82, 53, 40}, {133, 63, 36}, {149, 96, 18}, {169, 100, 12},
+ {182, 122, 14}, {214, 140, 47}, {235, 178, 73}, {230, 176, 63},
+ {220, 159, 44}, {174, 151, 29}, {141, 139, 43}, {129, 99, 40},
+ {56, 61, 33}, {35, 36, 18}, {25, 24, 6}, {17, 18, 3},
+ {18, 17, 0}, {24, 19, 0}, {27, 25, 3}, {35, 29, 12},
+ {100, 42, 16}, {140, 45, 14}, {144, 52, 14}, {120, 55, 39},
+ {75, 59, 56}, {51, 40, 38}, {34, 28, 15}, {24, 19, 5},
+ {13, 13, 3}, {12, 13, 1}, {7, 8, 3}, {11, 13, 2},
+ {9, 11, 0}, {12, 11, 2}, {13, 15, 2}, {22, 23, 7},
+ {30, 31, 12}, {40, 40, 28}, {52, 63, 69}, {63, 96, 65},
+ {66, 103, 73}, {100, 114, 124}, {105, 155, 133}, {138, 146, 105},
+ {152, 125, 65}, {159, 122, 55}, {158, 94, 30}, {156, 60, 7},
+ {149, 46, 14}, {135, 43, 40}, {124, 57, 81}, {97, 58, 132},
+ {80, 70, 149}, {66, 83, 139}, {85, 94, 140}, {97, 116, 153},
+ {96, 119, 151}, {96, 102, 126}, {97, 87, 79}, {137, 77, 67},
+ {164, 55, 24}, {174, 60, 16}, {175, 42, 4}, {175, 37, 7},
+ {164, 40, 12}, {142, 47, 14}, {111, 47, 30}, {65, 46, 47},
+ {37, 34, 22}, {32, 34, 12}, {31, 32, 16}, {43, 33, 25},
+ {88, 42, 37}, {140, 30, 9}, {164, 24, 1}, {169, 28, 2},
+ {174, 34, 2}, {168, 38, 1}, {160, 41, 4}, {131, 44, 7},
+ {71, 53, 21}, {34, 30, 10}, {27, 22, 5}, {18, 15, 1},
+ {13, 16, 1}, {18, 19, 3}, {30, 27, 11}, {37, 33, 32},
+ {46, 32, 70}, {59, 38, 117}, {66, 58, 127}, {67, 60, 133},
+ {63, 63, 123}, {53, 51, 71}, {56, 50, 67}, {55, 47, 64},
+ {45, 46, 37}, {45, 40, 29}, {48, 43, 39}, {61, 50, 62},
+ {75, 90, 70}, {81, 95, 120}, {89, 100, 136}, {99, 97, 124},
+ {130, 93, 79}, {158, 58, 28}, {174, 63, 9}, {184, 51, 4},
+ {186, 46, 8}, {190, 37, 18}, {187, 40, 21}, {180, 48, 10},
+ {174, 50, 3}, {166, 69, 2}, {181, 83, 21}, {205, 97, 2},
+ {203, 115, 5}, {212, 138, 3}, {218, 141, 4}, {237, 144, 0},
+ {233, 139, 5}, {225, 115, 4}, {207, 98, 4}, {204, 58, 27},
+ {199, 52, 18}, {199, 49, 16}, {193, 39, 9}, {194, 35, 6},
+ {189, 36, 5}, {186, 35, 2}, {186, 33, 6}, {182, 37, 5},
+ {185, 39, 2}, {186, 31, 2}, {185, 30, 3}, {186, 31, 3},
+ {187, 30, 3}, {186, 30, 2}, {183, 32, 2}, {179, 27, 3},
+ {180, 31, 3}, {184, 31, 5}, {184, 29, 6}, {181, 30, 3},
+ {181, 28, 0}, {181, 30, 2}, {179, 29, 2}, {174, 29, 0},
+ {176, 33, 3}, {178, 30, 3}, {175, 25, 1}, {174, 24, 1},
+ {173, 25, 0}, {166, 24, 1}, {153, 26, 7}, {116, 30, 5},
+ {43, 25, 4}, {28, 18, 2}, {26, 15, 1}, {27, 15, 1},
+ {32, 23, 8}, {43, 28, 14}, {111, 34, 2}, {144, 39, 4},
+ {160, 30, 2}, {163, 35, 4}, {155, 41, 12}, {129, 53, 66},
+ {132, 71, 99}, {99, 109, 132}, {95, 141, 145}, {107, 135, 143},
+ {101, 125, 156}, {100, 117, 143}, {87, 111, 128}, {66, 114, 73},
+ {72, 108, 48}, {97, 111, 29}, {108, 120, 58}, {138, 150, 119},
+ {183, 165, 155}, {195, 175, 164}, {163, 166, 182}, {125, 170, 197},
+ {131, 154, 188}, {120, 117, 172}, {86, 106, 175}, {68, 81, 143},
+ {67, 61, 123}, {85, 56, 66}, {112, 37, 48}, {154, 39, 15},
+ {180, 48, 24}, {187, 60, 69}, {188, 118, 123}, {193, 146, 160},
+ {206, 170, 188}, {162, 157, 190}, {144, 136, 147}, {139, 102, 158},
+ {173, 54, 109}, {167, 45, 49}, {177, 43, 13}, {170, 41, 14},
+ {153, 40, 9}, {126, 36, 5}, {55, 29, 11}, {29, 17, 2},
+ {21, 11, 1}, {18, 10, 0}, {25, 16, 1}, {28, 19, 8},
+ {36, 28, 10}, {91, 44, 9}, {143, 38, 7}, {146, 47, 6},
+ {142, 52, 5}, {130, 73, 10}, {78, 62, 22}, {40, 37, 23},
+ {44, 26, 16}, {90, 27, 4}, {136, 30, 7}, {158, 30, 2},
+ {169, 33, 1}, {175, 32, 7}, {166, 37, 17}, {149, 51, 36},
+ {129, 48, 74}, {99, 54, 126}, {76, 51, 129}, {73, 58, 137},
+ {84, 74, 148}, {102, 79, 152}, {119, 80, 142}, {174, 57, 99},
+ {178, 50, 50}, {175, 49, 49}, {186, 58, 48}, {186, 57, 99},
+ {176, 59, 115}, {124, 107, 116}, {116, 143, 147}, {128, 161, 139},
+ {190, 169, 127}, {235, 175, 114}, {233, 175, 81}, {148, 146, 56},
+ {151, 99, 30}, {120, 86, 12}, {120, 78, 22}, {56, 50, 9},
+ },
+ /* fog25.ppm*/
+ {
+ {11, 14, 2}, {30, 28, 10}, {35, 35, 14}, {52, 43, 23},
+ {52, 61, 22}, {61, 63, 29}, {67, 62, 30}, {83, 70, 39},
+ {113, 96, 63}, {149, 134, 131}, {163, 157, 162}, {175, 169, 173},
+ {178, 172, 176}, {173, 165, 170}, {160, 149, 152}, {162, 119, 78},
+ {159, 108, 65}, {135, 95, 47}, {112, 83, 32}, {88, 81, 23},
+ {75, 72, 18}, {72, 59, 18}, {73, 63, 24}, {70, 48, 22},
+ {72, 57, 30}, {66, 52, 20}, {64, 60, 21}, {66, 71, 23},
+ {72, 62, 27}, {66, 73, 21}, {75, 66, 26}, {80, 64, 23},
+ {103, 73, 33}, {130, 88, 40}, {163, 99, 47}, {172, 100, 47},
+ {174, 106, 51}, {185, 120, 60}, {194, 128, 73}, {206, 141, 84},
+ {201, 145, 94}, {179, 165, 164}, {191, 180, 184}, {205, 196, 192},
+ {214, 212, 217}, {231, 230, 236}, {238, 242, 245}, {239, 242, 247},
+ {233, 236, 241}, {227, 226, 231}, {213, 208, 214}, {197, 192, 198},
+ {178, 172, 177}, {150, 144, 146}, {130, 103, 68}, {86, 76, 53},
+ {74, 61, 44}, {68, 58, 40}, {61, 53, 28}, {58, 51, 19},
+ {60, 53, 11}, {62, 45, 5}, {45, 43, 6}, {34, 39, 4},
+ {31, 41, 3}, {36, 36, 9}, {32, 35, 9}, {30, 36, 6},
+ {17, 16, 10}, {21, 18, 2}, {31, 13, 5}, {28, 20, 7},
+ {28, 24, 4}, {33, 24, 6}, {35, 41, 17}, {45, 42, 12},
+ {51, 36, 17}, {53, 37, 22}, {62, 38, 16}, {78, 39, 12},
+ {81, 46, 17}, {83, 52, 12}, {89, 65, 13}, {103, 76, 18},
+ {119, 80, 26}, {137, 85, 29}, {160, 92, 41}, {162, 103, 54},
+ {171, 109, 59}, {184, 120, 59}, {196, 124, 60}, {194, 131, 73},
+ {199, 146, 97}, {181, 170, 172}, {196, 186, 191}, {203, 198, 202},
+ {215, 216, 218}, {230, 229, 235}, {231, 231, 239}, {231, 230, 238},
+ {225, 225, 229}, {211, 208, 207}, {195, 189, 193}, {173, 168, 171},
+ {145, 140, 142}, {117, 103, 54}, {98, 77, 33}, {86, 61, 34},
+ {78, 63, 30}, {76, 63, 31}, {75, 64, 34}, {70, 58, 42},
+ {76, 55, 36}, {72, 58, 30}, {67, 57, 17}, {63, 55, 9},
+ {53, 62, 2}, {50, 42, 4}, {47, 40, 7}, {46, 40, 6},
+ {51, 39, 8}, {52, 39, 10}, {55, 41, 12}, {58, 45, 19},
+ {57, 48, 20}, {63, 47, 18}, {70, 48, 15}, {71, 48, 16},
+ {87, 53, 19}, {100, 72, 29}, {110, 90, 39}, {134, 119, 63},
+ {148, 138, 137}, {167, 164, 163}, {182, 177, 177}, {186, 179, 187},
+ {184, 177, 185}, {178, 171, 178}, {168, 155, 158}, {187, 143, 107},
+ {192, 130, 79}, {179, 119, 63}, {165, 111, 56}, {159, 109, 55},
+ {160, 111, 61}, {160, 114, 72}, {153, 141, 142}, {167, 159, 164},
+ {172, 167, 171}, {171, 166, 170}, {157, 151, 153}, {151, 135, 129},
+ {143, 105, 64}, {124, 86, 40}, {112, 73, 31}, {88, 62, 31},
+ {91, 64, 24}, {90, 64, 19}, {89, 63, 18}, {92, 69, 20},
+ {102, 78, 19}, {112, 78, 29}, {123, 83, 37}, {142, 97, 52},
+ {143, 118, 69}, {155, 146, 149}, {178, 172, 174}, {194, 190, 190},
+ {203, 199, 202}, {217, 216, 222}, {232, 235, 239}, {241, 245, 248},
+ {245, 249, 252}, {247, 251, 254}, {249, 253, 255}, {251, 253, 255},
+ {253, 254, 255}, {253, 254, 255}, {253, 254, 255}, {251, 255, 255},
+ {249, 254, 255}, {249, 253, 254}, {249, 253, 254}, {247, 253, 255},
+ {245, 250, 253}, {244, 249, 253}, {244, 249, 252}, {240, 245, 248},
+ {231, 232, 237}, {219, 216, 218}, {203, 196, 202}, {189, 183, 187},
+ {169, 160, 164}, {180, 133, 98}, {169, 113, 66}, {163, 109, 56},
+ {167, 105, 58}, {167, 114, 65}, {162, 115, 75}, {155, 144, 148},
+ {173, 164, 171}, {184, 177, 185}, {187, 182, 189}, {188, 182, 187},
+ {179, 173, 174}, {166, 156, 155}, {198, 138, 89}, {181, 123, 67},
+ {163, 104, 55}, {152, 95, 50}, {133, 91, 37}, {124, 81, 28},
+ {117, 81, 28}, {108, 77, 27}, {111, 79, 32}, {118, 83, 39},
+ {133, 92, 51}, {146, 97, 56}, {153, 103, 61}, {152, 104, 57},
+ {148, 104, 57}, {142, 101, 48}, {134, 95, 43}, {123, 84, 31},
+ {109, 70, 28}, {99, 66, 22}, {95, 64, 28}, {97, 65, 36},
+ {102, 78, 52}, {120, 88, 58}, {143, 110, 81}, {152, 144, 147},
+ {174, 165, 170}, {193, 182, 188}, {205, 195, 203}, {220, 217, 224},
+ {232, 235, 242}, {243, 246, 251}, {245, 250, 253}, {248, 253, 254},
+ {249, 253, 254}, {253, 255, 254}, {255, 255, 255}, {255, 255, 255},
+ {254, 255, 255}, {253, 254, 255}, {253, 254, 255}, {250, 251, 253},
+ {244, 248, 250}, {234, 236, 243}, {222, 219, 229}, {207, 197, 205},
+ {188, 181, 189}, {170, 160, 167}, {142, 133, 134}, {76, 67, 44},
+ },
+ /* geyser27.ppm*/
+ {
+ {0, 0, 0}, {26, 5, 5}, {77, 17, 6}, {98, 45, 29},
+ {124, 78, 66}, {114, 129, 146}, {115, 168, 193}, {132, 179, 210},
+ {152, 186, 220}, {153, 176, 202}, {142, 171, 181}, {157, 169, 176},
+ {165, 162, 158}, {154, 131, 103}, {149, 104, 73}, {152, 98, 60},
+ {140, 85, 46}, {138, 67, 23}, {163, 57, 6}, {173, 55, 8},
+ {179, 56, 3}, {190, 61, 8}, {208, 86, 7}, {210, 110, 25},
+ {221, 127, 29}, {222, 150, 44}, {226, 161, 53}, {222, 159, 52},
+ {222, 152, 60}, {213, 148, 66}, {193, 134, 70}, {164, 146, 113},
+ {138, 141, 159}, {133, 144, 165}, {154, 139, 122}, {148, 108, 84},
+ {158, 115, 69}, {189, 121, 57}, {198, 124, 48}, {212, 136, 45},
+ {224, 146, 39}, {223, 143, 35}, {222, 137, 31}, {222, 136, 24},
+ {221, 137, 18}, {205, 101, 19}, {198, 76, 4}, {176, 60, 14},
+ {148, 86, 43}, {154, 110, 61}, {141, 115, 89}, {125, 168, 156},
+ {126, 173, 199}, {109, 156, 212}, {105, 144, 195}, {96, 120, 161},
+ {71, 74, 121}, {37, 58, 125}, {26, 49, 119}, {16, 42, 117},
+ {17, 41, 115}, {18, 38, 106}, {35, 42, 95}, {53, 36, 45},
+ {59, 35, 35}, {84, 25, 14}, {98, 35, 15}, {116, 48, 18},
+ {121, 56, 23}, {135, 72, 28}, {142, 82, 36}, {163, 103, 51},
+ {178, 108, 56}, {196, 123, 61}, {214, 145, 68}, {202, 165, 99},
+ {182, 166, 149}, {177, 174, 165}, {175, 176, 165}, {157, 168, 171},
+ {135, 153, 167}, {120, 125, 129}, {126, 87, 69}, {125, 82, 64},
+ {126, 81, 63}, {127, 75, 46}, {137, 83, 37}, {142, 82, 36},
+ {142, 80, 35}, {134, 70, 20}, {116, 51, 9}, {107, 38, 11},
+ {97, 29, 7}, {86, 25, 13}, {87, 26, 8}, {93, 28, 8},
+ {103, 31, 6}, {110, 40, 6}, {111, 43, 7}, {141, 28, 8},
+ {143, 31, 7}, {145, 34, 10}, {148, 36, 6}, {145, 39, 3},
+ {128, 54, 22}, {115, 51, 25}, {99, 50, 25}, {99, 47, 28},
+ {102, 44, 27}, {95, 42, 26}, {53, 35, 43}, {23, 38, 100},
+ {14, 34, 103}, {18, 28, 89}, {4, 0, 2}, {1, 0, 0},
+ {24, 3, 2}, {75, 15, 6}, {85, 15, 2}, {98, 23, 4},
+ {101, 24, 2}, {105, 32, 2}, {106, 34, 2}, {104, 37, 12},
+ {100, 33, 14}, {91, 30, 11}, {88, 29, 11}, {93, 38, 18},
+ {96, 39, 24}, {98, 47, 30}, {69, 63, 76}, {36, 54, 121},
+ {49, 63, 118}, {100, 68, 69}, {114, 71, 55}, {131, 86, 57},
+ {159, 109, 61}, {186, 112, 45}, {200, 123, 46}, {217, 143, 50},
+ {221, 149, 55}, {221, 147, 50}, {211, 139, 54}, {188, 119, 49},
+ {175, 103, 38}, {152, 85, 32}, {165, 61, 23}, {160, 43, 7},
+ {163, 41, 6}, {153, 31, 3}, {156, 35, 7}, {162, 41, 7},
+ {164, 43, 5}, {170, 53, 9}, {182, 67, 12}, {195, 110, 40},
+ {213, 134, 48}, {224, 152, 58}, {223, 161, 67}, {222, 154, 64},
+ {195, 134, 61}, {181, 119, 60}, {156, 111, 61}, {145, 104, 74},
+ {126, 81, 65}, {91, 91, 112}, {114, 123, 157}, {118, 163, 183},
+ {127, 182, 196}, {128, 186, 211}, {132, 187, 192}, {154, 193, 164},
+ {167, 204, 164}, {170, 202, 163}, {180, 203, 188}, {163, 198, 225},
+ {139, 193, 222}, {136, 192, 219}, {119, 177, 225}, {115, 173, 225},
+ {112, 166, 225}, {109, 154, 202}, {113, 118, 144}, {119, 77, 72},
+ {112, 67, 52}, {102, 53, 32}, {102, 53, 33}, {111, 67, 54},
+ {90, 85, 91}, {44, 63, 127}, {32, 52, 121}, {26, 46, 119},
+ {18, 38, 109}, {16, 31, 95}, {2, 4, 21}, {0, 0, 0},
+ {0, 0, 0}, {6, 1, 0}, {33, 11, 21}, {46, 37, 52},
+ {55, 66, 120}, {98, 107, 144}, {133, 149, 176}, {137, 158, 190},
+ {121, 132, 164}, {76, 86, 128}, {40, 58, 125}, {30, 50, 118},
+ {27, 47, 121}, {39, 50, 109}, {107, 61, 46}, {104, 57, 36},
+ {110, 60, 40}, {130, 82, 55}, {153, 108, 60}, {155, 114, 66},
+ {186, 132, 68}, {216, 177, 98}, {221, 182, 114}, {220, 213, 126},
+ {223, 205, 147}, {194, 197, 178}, {174, 198, 177}, {183, 184, 159},
+ {192, 145, 102}, {178, 111, 64}, {170, 81, 28}, {173, 58, 7},
+ {173, 52, 7}, {173, 50, 5}, {167, 48, 2}, {160, 47, 3},
+ {158, 48, 2}, {163, 51, 6}, {174, 58, 8}, {189, 97, 22},
+ {209, 124, 41}, {219, 144, 52}, {219, 148, 62}, {200, 138, 62},
+ {187, 134, 72}, {171, 156, 133}, {167, 172, 160}, {138, 150, 173},
+ {126, 139, 157}, {142, 106, 81}, {117, 71, 55}, {108, 50, 28},
+ {98, 42, 18}, {89, 28, 10}, {81, 23, 6}, {78, 14, 2},
+ {78, 14, 1}, {75, 7, 1}, {9, 0, 1}, {0, 0, 0},
+ },
+ /* gris.josette.ppm*/
+ {
+ {36, 41, 40}, {71, 77, 82}, {90, 114, 119}, {120, 152, 164},
+ {158, 192, 200}, {171, 198, 207}, {176, 199, 207}, {178, 198, 207},
+ {176, 199, 208}, {179, 200, 207}, {181, 202, 207}, {184, 204, 213},
+ {186, 209, 219}, {184, 209, 220}, {183, 211, 217}, {181, 207, 209},
+ {179, 205, 210}, {187, 207, 213}, {188, 211, 216}, {188, 211, 217},
+ {188, 211, 217}, {190, 209, 221}, {194, 213, 219}, {250, 249, 229},
+ {255, 250, 227}, {255, 247, 228}, {227, 228, 218}, {193, 205, 212},
+ {188, 204, 204}, {180, 195, 198}, {162, 178, 167}, {117, 146, 147},
+ {90, 124, 134}, {81, 117, 123}, {76, 105, 109}, {71, 102, 97},
+ {63, 89, 80}, {40, 59, 60}, {33, 47, 50}, {29, 43, 46},
+ {26, 36, 38}, {18, 27, 26}, {14, 23, 22}, {19, 28, 27},
+ {30, 36, 35}, {29, 36, 39}, {32, 41, 46}, {32, 46, 49},
+ {42, 48, 47}, {42, 53, 46}, {73, 82, 57}, {72, 84, 72},
+ {73, 91, 84}, {76, 95, 96}, {79, 98, 94}, {82, 102, 99},
+ {84, 104, 100}, {93, 106, 100}, {90, 111, 114}, {88, 115, 124},
+ {94, 122, 133}, {139, 153, 152}, {175, 190, 183}, {177, 193, 190},
+ {157, 174, 157}, {94, 123, 129}, {82, 108, 109}, {66, 93, 89},
+ {37, 52, 58}, {26, 38, 39}, {11, 18, 22}, {8, 11, 18},
+ {7, 9, 16}, {9, 14, 18}, {13, 20, 23}, {26, 36, 38},
+ {36, 50, 55}, {61, 80, 82}, {70, 96, 93}, {76, 100, 101},
+ {76, 103, 111}, {79, 110, 113}, {82, 110, 120}, {85, 112, 125},
+ {86, 115, 129}, {87, 118, 133}, {90, 124, 143}, {139, 166, 175},
+ {171, 187, 203}, {180, 195, 202}, {183, 198, 201}, {180, 198, 199},
+ {174, 194, 195}, {157, 160, 165}, {97, 119, 125}, {86, 106, 106},
+ {69, 83, 82}, {37, 52, 53}, {28, 39, 36}, {16, 25, 24},
+ {10, 18, 21}, {10, 16, 22}, {12, 20, 23}, {25, 39, 42},
+ {40, 59, 65}, {69, 97, 94}, {90, 117, 122}, {125, 150, 154},
+ {173, 193, 185}, {185, 200, 195}, {184, 200, 200}, {174, 196, 201},
+ {145, 179, 183}, {91, 133, 141}, {86, 120, 132}, {86, 116, 127},
+ {86, 116, 127}, {90, 116, 127}, {99, 128, 135}, {160, 176, 163},
+ {193, 195, 186}, {190, 205, 197}, {208, 214, 203}, {255, 250, 223},
+ {254, 252, 232}, {253, 252, 234}, {253, 250, 231}, {246, 245, 225},
+ {191, 210, 215}, {186, 204, 213}, {182, 203, 208}, {178, 201, 209},
+ {174, 201, 209}, {165, 197, 207}, {142, 175, 185}, {87, 123, 144},
+ {85, 119, 138}, {87, 120, 141}, {108, 137, 143}, {145, 181, 181},
+ {175, 196, 201}, {184, 197, 205}, {189, 207, 207}, {192, 206, 207},
+ {192, 209, 208}, {214, 225, 211}, {249, 253, 231}, {253, 253, 243},
+ {253, 253, 244}, {253, 252, 243}, {247, 249, 225}, {192, 209, 203},
+ {163, 178, 172}, {118, 136, 143}, {90, 119, 126}, {85, 113, 120},
+ {84, 112, 115}, {86, 110, 111}, {88, 112, 116}, {88, 117, 123},
+ {92, 122, 132}, {118, 148, 148}, {161, 181, 176}, {176, 195, 200},
+ {178, 197, 204}, {178, 198, 205}, {172, 200, 204}, {169, 192, 200},
+ {125, 156, 166}, {91, 123, 135}, {77, 112, 116}, {68, 97, 97},
+ {53, 71, 81}, {32, 49, 59}, {30, 46, 57}, {40, 60, 67},
+ {65, 88, 80}, {72, 92, 91}, {74, 95, 98}, {75, 100, 97},
+ {77, 102, 99}, {79, 104, 101}, {78, 103, 100}, {78, 101, 95},
+ {78, 101, 95}, {78, 102, 95}, {80, 103, 98}, {81, 102, 101},
+ {81, 101, 102}, {82, 102, 103}, {80, 104, 105}, {76, 106, 107},
+ {77, 106, 108}, {79, 109, 108}, {80, 110, 112}, {79, 111, 114},
+ {81, 111, 114}, {82, 113, 115}, {85, 115, 116}, {85, 114, 118},
+ {85, 113, 123}, {85, 115, 127}, {84, 116, 124}, {83, 117, 127},
+ {86, 119, 125}, {97, 126, 133}, {149, 174, 171}, {173, 194, 197},
+ {177, 197, 199}, {179, 200, 200}, {173, 193, 201}, {144, 173, 179},
+ {91, 124, 141}, {84, 118, 130}, {76, 112, 121}, {66, 106, 112},
+ {71, 104, 106}, {73, 104, 103}, {72, 101, 97}, {68, 91, 83},
+ {46, 61, 68}, {39, 50, 52}, {36, 46, 39}, {34, 38, 28},
+ {13, 19, 19}, {9, 18, 17}, {9, 20, 23}, {24, 34, 37},
+ {34, 43, 42}, {42, 52, 54}, {70, 81, 71}, {73, 88, 81},
+ {74, 96, 87}, {72, 95, 86}, {70, 90, 82}, {70, 84, 79},
+ {44, 62, 67}, {29, 48, 54}, {30, 48, 52}, {32, 51, 58},
+ {48, 63, 70}, {69, 91, 89}, {86, 108, 111}, {95, 124, 130},
+ {181, 190, 150}, {224, 207, 189}, {250, 246, 219}, {255, 251, 224},
+ {252, 250, 229}, {238, 243, 231}, {189, 212, 214}, {181, 203, 201},
+ {174, 193, 194}, {129, 156, 154}, {92, 121, 120}, {69, 90, 80},
+ },
+ /* gris.landscape-ceret.ppm*/
+ {
+ {16, 15, 9}, {38, 38, 25}, {74, 64, 42}, {102, 59, 39},
+ {124, 79, 40}, {136, 91, 44}, {160, 94, 56}, {173, 80, 72},
+ {182, 80, 70}, {210, 77, 71}, {209, 82, 67}, {202, 48, 38},
+ {227, 88, 54}, {211, 102, 54}, {226, 127, 50}, {234, 143, 56},
+ {238, 156, 65}, {228, 167, 71}, {189, 166, 79}, {133, 176, 107},
+ {119, 153, 122}, {111, 145, 124}, {96, 138, 126}, {103, 124, 100},
+ {99, 100, 94}, {78, 96, 107}, {69, 100, 132}, {64, 121, 173},
+ {98, 165, 196}, {107, 165, 182}, {122, 172, 179}, {171, 201, 211},
+ {189, 209, 209}, {201, 210, 207}, {210, 219, 205}, {196, 208, 204},
+ {163, 189, 199}, {132, 166, 174}, {118, 138, 153}, {134, 115, 126},
+ {168, 130, 107}, {180, 120, 108}, {181, 101, 112}, {209, 90, 97},
+ {215, 92, 95}, {220, 95, 96}, {217, 98, 100}, {226, 98, 95},
+ {228, 105, 92}, {232, 105, 88}, {229, 97, 83}, {224, 83, 73},
+ {227, 88, 85}, {219, 100, 76}, {227, 105, 83}, {229, 106, 90},
+ {224, 107, 98}, {226, 116, 103}, {226, 121, 114}, {215, 152, 155},
+ {218, 168, 168}, {217, 172, 167}, {222, 177, 172}, {239, 183, 170},
+ {239, 185, 167}, {226, 202, 144}, {252, 231, 99}, {252, 223, 89},
+ {248, 218, 82}, {249, 200, 79}, {228, 191, 83}, {180, 160, 91},
+ {136, 184, 126}, {112, 162, 130}, {113, 156, 132}, {111, 156, 135},
+ {124, 161, 154}, {128, 163, 167}, {126, 154, 174}, {120, 141, 162},
+ {98, 123, 138}, {101, 123, 131}, {100, 132, 109}, {100, 120, 96},
+ {96, 110, 85}, {88, 94, 70}, {112, 87, 42}, {120, 85, 44},
+ {128, 86, 56}, {139, 86, 54}, {159, 115, 62}, {177, 122, 52},
+ {198, 127, 62}, {221, 146, 50}, {221, 160, 47}, {214, 166, 37},
+ {234, 179, 32}, {248, 194, 35}, {253, 197, 34}, {252, 198, 35},
+ {249, 196, 31}, {249, 195, 43}, {249, 165, 48}, {242, 150, 38},
+ {241, 151, 32}, {242, 149, 36}, {241, 150, 35}, {237, 142, 33},
+ {225, 139, 28}, {229, 140, 32}, {230, 133, 43}, {220, 126, 44},
+ {223, 122, 32}, {221, 127, 29}, {214, 135, 33}, {207, 133, 39},
+ {210, 144, 43}, {206, 140, 60}, {193, 141, 67}, {164, 143, 82},
+ {129, 131, 69}, {109, 115, 58}, {90, 116, 69}, {85, 107, 68},
+ {76, 79, 81}, {89, 93, 72}, {78, 85, 68}, {66, 71, 62},
+ {56, 62, 58}, {48, 51, 51}, {36, 35, 24}, {25, 15, 16},
+ {10, 13, 9}, {8, 7, 5}, {4, 2, 3}, {14, 13, 11},
+ {16, 14, 15}, {19, 18, 15}, {20, 18, 19}, {28, 35, 24},
+ {33, 34, 32}, {29, 38, 31}, {45, 56, 40}, {52, 57, 47},
+ {58, 54, 43}, {57, 59, 46}, {60, 53, 49}, {62, 60, 58},
+ {69, 68, 63}, {70, 80, 60}, {83, 81, 70}, {97, 87, 81},
+ {112, 94, 86}, {116, 104, 83}, {129, 111, 73}, {130, 124, 55},
+ {125, 120, 57}, {134, 112, 46}, {163, 121, 48}, {198, 142, 59},
+ {205, 153, 76}, {218, 163, 137}, {212, 164, 160}, {208, 155, 162},
+ {199, 126, 129}, {194, 113, 123}, {189, 121, 124}, {202, 126, 104},
+ {196, 116, 69}, {198, 112, 35}, {199, 108, 31}, {207, 127, 39},
+ {211, 131, 40}, {228, 140, 40}, {244, 160, 44}, {248, 194, 44},
+ {253, 199, 40}, {253, 200, 35}, {253, 201, 37}, {254, 204, 41},
+ {251, 205, 41}, {252, 206, 42}, {254, 211, 39}, {254, 206, 44},
+ {253, 202, 44}, {249, 204, 53}, {246, 196, 59}, {241, 192, 54},
+ {236, 172, 64}, {195, 164, 84}, {132, 187, 124}, {127, 189, 119},
+ {115, 171, 106}, {111, 161, 95}, {103, 126, 95}, {97, 115, 89},
+ {94, 112, 86}, {90, 108, 78}, {106, 107, 67}, {115, 94, 66},
+ {104, 87, 59}, {100, 75, 56}, {92, 69, 49}, {85, 77, 61},
+ {93, 85, 62}, {98, 89, 50}, {96, 71, 46}, {93, 74, 46},
+ {87, 73, 43}, {79, 82, 63}, {69, 89, 69}, {69, 95, 62},
+ {52, 86, 60}, {60, 84, 63}, {64, 86, 47}, {65, 71, 57},
+ {69, 70, 50}, {76, 69, 49}, {74, 80, 52}, {72, 95, 61},
+ {78, 105, 71}, {95, 123, 75}, {108, 138, 71}, {119, 138, 81},
+ {152, 142, 139}, {201, 151, 162}, {215, 173, 175}, {218, 200, 175},
+ {227, 228, 211}, {254, 251, 249}, {252, 252, 250}, {250, 252, 252},
+ {228, 234, 230}, {219, 220, 216}, {237, 189, 172}, {253, 228, 97},
+ {251, 218, 72}, {254, 213, 63}, {251, 211, 67}, {249, 214, 62},
+ {251, 212, 56}, {252, 213, 52}, {253, 210, 46}, {254, 201, 44},
+ {246, 191, 47}, {244, 164, 74}, {222, 170, 100}, {231, 157, 139},
+ {214, 165, 166}, {213, 156, 162}, {198, 135, 148}, {178, 118, 131},
+ {161, 106, 115}, {123, 76, 72}, {75, 68, 56}, {42, 42, 38},
+ },
+ /* kandinsky.comp-9.ppm*/
+ {
+ {81, 31, 21}, {121, 26, 10}, {159, 21, 3}, {194, 57, 2},
+ {216, 102, 0}, {215, 111, 0}, {215, 122, 1}, {220, 132, 2},
+ {227, 148, 3}, {233, 167, 22}, {251, 191, 41}, {252, 194, 60},
+ {253, 200, 72}, {225, 192, 103}, {216, 204, 135}, {224, 205, 153},
+ {232, 201, 156}, {251, 210, 92}, {254, 207, 68}, {254, 191, 42},
+ {248, 182, 7}, {231, 146, 2}, {225, 127, 2}, {221, 107, 0},
+ {197, 62, 6}, {169, 26, 4}, {157, 35, 12}, {154, 76, 92},
+ {162, 80, 100}, {173, 137, 99}, {141, 186, 121}, {146, 183, 127},
+ {179, 183, 142}, {192, 170, 137}, {240, 177, 82}, {254, 198, 69},
+ {254, 207, 68}, {254, 203, 65}, {252, 194, 62}, {251, 190, 56},
+ {249, 181, 57}, {243, 169, 37}, {221, 129, 5}, {210, 110, 0},
+ {195, 60, 4}, {167, 19, 1}, {129, 20, 3}, {96, 28, 20},
+ {84, 30, 23}, {88, 33, 27}, {97, 29, 42}, {84, 35, 46},
+ {92, 136, 115}, {90, 161, 127}, {125, 186, 117}, {143, 187, 118},
+ {203, 172, 77}, {200, 138, 40}, {196, 115, 9}, {170, 51, 13},
+ {123, 29, 7}, {106, 35, 13}, {92, 32, 18}, {92, 29, 17},
+ {119, 28, 5}, {125, 30, 7}, {182, 57, 14}, {210, 109, 3},
+ {218, 123, 10}, {237, 165, 27}, {235, 187, 61}, {215, 194, 103},
+ {173, 197, 132}, {146, 197, 126}, {138, 195, 127}, {134, 194, 124},
+ {137, 192, 130}, {149, 193, 134}, {178, 188, 138}, {200, 205, 141},
+ {212, 205, 172}, {216, 213, 187}, {212, 217, 186}, {180, 205, 174},
+ {157, 195, 166}, {112, 187, 178}, {103, 185, 183}, {95, 177, 181},
+ {68, 160, 185}, {69, 166, 201}, {91, 173, 167}, {112, 172, 141},
+ {129, 190, 123}, {131, 188, 123}, {128, 186, 116}, {121, 182, 107},
+ {91, 149, 116}, {102, 34, 47}, {107, 34, 15}, {123, 28, 10},
+ {170, 49, 11}, {214, 97, 1}, {217, 111, 0}, {222, 124, 3},
+ {227, 141, 2}, {234, 157, 22}, {240, 176, 44}, {249, 194, 54},
+ {250, 194, 60}, {252, 202, 64}, {254, 200, 63}, {253, 196, 67},
+ {246, 192, 66}, {219, 182, 92}, {162, 192, 118}, {141, 188, 124},
+ {133, 187, 123}, {136, 186, 121}, {137, 189, 128}, {132, 187, 132},
+ {108, 176, 182}, {98, 183, 188}, {96, 185, 193}, {81, 171, 198},
+ {67, 175, 209}, {68, 174, 204}, {81, 180, 196}, {94, 181, 171},
+ {130, 192, 129}, {136, 186, 120}, {134, 118, 91}, {127, 53, 73},
+ {94, 31, 50}, {85, 26, 44}, {81, 25, 40}, {81, 25, 36},
+ {74, 29, 33}, {73, 29, 30}, {29, 41, 29}, {28, 38, 27},
+ {11, 17, 13}, {5, 16, 11}, {10, 12, 8}, {12, 12, 10},
+ {26, 18, 7}, {71, 26, 33}, {85, 30, 43}, {96, 81, 53},
+ {86, 157, 121}, {127, 184, 120}, {145, 173, 143}, {163, 176, 159},
+ {197, 184, 169}, {215, 198, 190}, {209, 202, 182}, {179, 201, 158},
+ {155, 200, 143}, {139, 195, 127}, {134, 184, 113}, {148, 113, 77},
+ {158, 36, 13}, {165, 15, 3}, {174, 4, 5}, {178, 5, 1},
+ {181, 14, 1}, {162, 18, 3}, {131, 19, 0}, {119, 22, 5},
+ {122, 28, 14}, {144, 95, 64}, {161, 147, 76}, {136, 184, 115},
+ {138, 187, 118}, {151, 186, 112}, {213, 190, 86}, {235, 190, 72},
+ {252, 203, 78}, {239, 213, 117}, {231, 214, 169}, {234, 218, 179},
+ {228, 220, 181}, {210, 214, 170}, {160, 205, 149}, {148, 202, 142},
+ {147, 200, 138}, {161, 198, 139}, {181, 200, 134}, {198, 195, 118},
+ {234, 187, 70}, {241, 174, 51}, {230, 129, 18}, {227, 125, 3},
+ {224, 121, 3}, {228, 119, 2}, {222, 123, 4}, {224, 125, 4},
+ {223, 130, 1}, {229, 145, 1}, {232, 166, 29}, {219, 181, 69},
+ {183, 183, 110}, {153, 193, 124}, {153, 201, 135}, {158, 203, 146},
+ {188, 210, 164}, {206, 218, 197}, {202, 219, 203}, {200, 214, 201},
+ {149, 193, 172}, {112, 192, 183}, {125, 194, 172}, {144, 192, 132},
+ {166, 192, 126}, {213, 152, 98}, {213, 131, 14}, {218, 121, 7},
+ {221, 113, 5}, {220, 111, 2}, {223, 117, 2}, {223, 117, 4},
+ {222, 118, 1}, {218, 119, 1}, {218, 122, 0}, {217, 125, 1},
+ {217, 134, 9}, {231, 160, 32}, {241, 171, 85}, {206, 186, 135},
+ {230, 211, 173}, {227, 216, 188}, {227, 210, 192}, {230, 220, 195},
+ {226, 221, 194}, {225, 219, 188}, {221, 218, 192}, {223, 227, 196},
+ {219, 222, 207}, {207, 221, 204}, {216, 223, 196}, {215, 218, 189},
+ {220, 218, 173}, {204, 191, 145}, {201, 135, 144}, {218, 142, 83},
+ {196, 127, 22}, {202, 87, 2}, {163, 21, 0}, {124, 19, 1},
+ {101, 32, 6}, {84, 47, 14}, {111, 120, 67}, {85, 155, 123},
+ {89, 165, 129}, {83, 157, 124}, {70, 122, 117}, {78, 32, 40},
+ },
+ /* kandinsky.yellow-red-blue.ppm*/
+ {
+ {8, 1, 9}, {18, 7, 12}, {41, 2, 17}, {56, 14, 26},
+ {97, 18, 40}, {101, 48, 45}, {110, 76, 36}, {142, 112, 37},
+ {125, 122, 42}, {118, 90, 40}, {110, 72, 34}, {104, 17, 29},
+ {111, 23, 27}, {141, 41, 42}, {168, 88, 88}, {178, 116, 101},
+ {156, 118, 128}, {138, 130, 114}, {139, 126, 99}, {132, 121, 90},
+ {129, 120, 101}, {129, 118, 116}, {110, 100, 123}, {92, 69, 125},
+ {85, 65, 116}, {98, 71, 105}, {130, 98, 74}, {137, 130, 54},
+ {140, 128, 55}, {145, 118, 45}, {149, 123, 38}, {144, 128, 50},
+ {158, 141, 56}, {172, 151, 55}, {159, 145, 47}, {171, 154, 58},
+ {184, 153, 66}, {185, 154, 70}, {183, 155, 68}, {177, 163, 70},
+ {174, 160, 66}, {168, 157, 71}, {172, 159, 65}, {168, 156, 68},
+ {163, 155, 67}, {160, 147, 69}, {164, 151, 85}, {164, 153, 89},
+ {162, 153, 91}, {156, 155, 98}, {155, 142, 108}, {158, 149, 97},
+ {152, 149, 96}, {151, 147, 113}, {143, 137, 124}, {139, 128, 135},
+ {128, 118, 135}, {119, 126, 142}, {115, 128, 147}, {116, 128, 154},
+ {117, 109, 167}, {105, 110, 165}, {103, 139, 206}, {116, 143, 241},
+ {118, 132, 242}, {128, 138, 243}, {135, 150, 243}, {148, 161, 244},
+ {140, 151, 239}, {141, 160, 242}, {145, 155, 240}, {158, 145, 230},
+ {161, 146, 223}, {144, 152, 223}, {158, 136, 227}, {170, 135, 216},
+ {169, 146, 220}, {188, 165, 214}, {210, 179, 238}, {219, 192, 244},
+ {233, 209, 245}, {236, 211, 243}, {218, 190, 245}, {191, 170, 246},
+ {178, 158, 247}, {157, 144, 237}, {147, 128, 230}, {143, 125, 227},
+ {103, 124, 233}, {100, 115, 200}, {102, 87, 192}, {75, 74, 148},
+ {27, 54, 120}, {30, 45, 100}, {30, 39, 66}, {28, 37, 51},
+ {33, 31, 33}, {32, 33, 37}, {39, 45, 51}, {40, 67, 94},
+ {59, 52, 113}, {71, 66, 120}, {78, 66, 158}, {88, 66, 180},
+ {107, 75, 215}, {120, 89, 216}, {125, 84, 226}, {132, 101, 228},
+ {141, 107, 236}, {144, 113, 236}, {139, 108, 233}, {125, 104, 207},
+ {110, 95, 198}, {133, 110, 177}, {155, 133, 166}, {168, 154, 171},
+ {163, 149, 162}, {145, 134, 166}, {129, 132, 161}, {109, 121, 132},
+ {96, 114, 122}, {86, 98, 124}, {67, 76, 117}, {56, 63, 104},
+ {60, 58, 69}, {66, 35, 69}, {95, 25, 56}, {113, 29, 62},
+ {144, 42, 79}, {157, 65, 100}, {129, 75, 130}, {98, 81, 181},
+ {97, 85, 199}, {96, 61, 204}, {94, 59, 202}, {89, 57, 197},
+ {85, 53, 196}, {92, 63, 190}, {94, 61, 197}, {90, 59, 188},
+ {74, 45, 173}, {70, 43, 176}, {69, 40, 160}, {63, 36, 144},
+ {31, 33, 99}, {20, 19, 67}, {19, 16, 48}, {17, 14, 39},
+ {19, 20, 27}, {13, 9, 7}, {5, 2, 9}, {5, 0, 7},
+ {4, 0, 6}, {1, 1, 3}, {1, 0, 2}, {3, 2, 2},
+ {3, 4, 4}, {3, 2, 6}, {3, 1, 10}, {8, 6, 14},
+ {22, 19, 32}, {34, 29, 31}, {34, 30, 30}, {42, 29, 38},
+ {44, 26, 48}, {52, 37, 48}, {60, 56, 66}, {66, 66, 74},
+ {67, 75, 95}, {128, 118, 104}, {155, 142, 109}, {168, 155, 126},
+ {178, 163, 154}, {177, 167, 165}, {183, 170, 173}, {193, 175, 195},
+ {201, 183, 220}, {214, 186, 227}, {215, 178, 232}, {219, 180, 240},
+ {208, 180, 244}, {198, 175, 242}, {178, 171, 231}, {177, 160, 220},
+ {182, 156, 210}, {184, 156, 207}, {186, 155, 207}, {181, 158, 187},
+ {180, 159, 185}, {151, 141, 201}, {143, 130, 204}, {154, 126, 218},
+ {147, 120, 232}, {148, 115, 236}, {158, 120, 237}, {158, 137, 235},
+ {156, 139, 240}, {149, 152, 246}, {148, 157, 250}, {150, 160, 250},
+ {163, 158, 254}, {182, 174, 250}, {181, 177, 239}, {175, 177, 235},
+ {176, 167, 212}, {163, 149, 196}, {147, 149, 183}, {136, 140, 187},
+ {120, 119, 174}, {126, 123, 191}, {145, 138, 187}, {139, 147, 196},
+ {149, 129, 196}, {157, 132, 188}, {164, 93, 177}, {183, 94, 182},
+ {185, 110, 174}, {183, 133, 144}, {158, 138, 149}, {166, 141, 158},
+ {159, 147, 145}, {158, 150, 130}, {160, 155, 125}, {161, 156, 101},
+ {168, 157, 80}, {175, 160, 75}, {187, 159, 70}, {192, 162, 64},
+ {228, 195, 48}, {198, 165, 66}, {200, 174, 84}, {202, 169, 86},
+ {220, 171, 90}, {216, 186, 60}, {211, 181, 92}, {211, 177, 96},
+ {221, 172, 100}, {207, 178, 98}, {219, 186, 97}, {228, 203, 101},
+ {225, 197, 113}, {223, 192, 148}, {213, 188, 149}, {209, 179, 151},
+ {194, 163, 141}, {182, 166, 137}, {162, 152, 107}, {161, 150, 87},
+ {159, 145, 65}, {158, 130, 56}, {138, 129, 75}, {125, 95, 54},
+ {104, 70, 31}, {56, 19, 44}, {27, 9, 40}, {13, 2, 11},
+ },
+ /* klee.insula-dulcamara.ppm*/
+ {
+ {23, 10, 4}, {63, 38, 27}, {144, 110, 85}, {148, 131, 73},
+ {144, 136, 86}, {163, 131, 90}, {176, 146, 96}, {192, 163, 102},
+ {193, 157, 114}, {193, 161, 130}, {193, 168, 147}, {181, 169, 156},
+ {168, 169, 150}, {146, 160, 158}, {130, 147, 155}, {120, 159, 162},
+ {121, 161, 146}, {112, 150, 152}, {87, 121, 142}, {92, 116, 138},
+ {119, 138, 142}, {131, 142, 131}, {154, 158, 110}, {156, 154, 101},
+ {171, 151, 99}, {170, 147, 84}, {166, 131, 76}, {152, 128, 75},
+ {149, 122, 63}, {109, 83, 48}, {26, 8, 7}, {24, 6, 4},
+ {19, 7, 6}, {17, 3, 2}, {9, 2, 2}, {4, 11, 6},
+ {26, 20, 6}, {87, 117, 85}, {117, 151, 117}, {145, 138, 113},
+ {143, 132, 87}, {129, 107, 66}, {28, 10, 7}, {22, 7, 8},
+ {30, 15, 12}, {92, 111, 109}, {84, 117, 137}, {84, 111, 146},
+ {110, 139, 153}, {126, 157, 141}, {153, 160, 132}, {151, 165, 120},
+ {164, 171, 122}, {171, 167, 111}, {170, 169, 104}, {177, 149, 104},
+ {186, 143, 93}, {176, 148, 77}, {160, 151, 65}, {175, 150, 75},
+ {169, 141, 61}, {163, 108, 61}, {132, 86, 35}, {32, 9, 1},
+ {34, 20, 5}, {128, 109, 56}, {159, 114, 78}, {177, 117, 83},
+ {186, 129, 83}, {184, 131, 89}, {193, 134, 99}, {187, 149, 91},
+ {192, 143, 100}, {199, 154, 108}, {201, 156, 119}, {201, 169, 121},
+ {202, 174, 128}, {201, 178, 133}, {213, 183, 133}, {206, 179, 137},
+ {210, 179, 137}, {219, 180, 138}, {219, 182, 133}, {216, 185, 134},
+ {216, 185, 137}, {221, 184, 144}, {221, 193, 147}, {221, 192, 158},
+ {219, 187, 159}, {221, 190, 165}, {221, 196, 167}, {220, 197, 172},
+ {220, 200, 165}, {206, 196, 165}, {196, 191, 167}, {195, 184, 166},
+ {192, 178, 167}, {166, 178, 173}, {160, 179, 167}, {158, 168, 161},
+ {149, 165, 164}, {148, 164, 167}, {144, 170, 170}, {142, 165, 178},
+ {121, 153, 171}, {113, 138, 160}, {94, 119, 147}, {89, 110, 140},
+ {52, 49, 54}, {23, 10, 7}, {14, 5, 0}, {6, 1, 0},
+ {9, 2, 1}, {19, 8, 7}, {26, 8, 6}, {54, 34, 14},
+ {150, 122, 40}, {147, 148, 57}, {143, 149, 61}, {155, 145, 69},
+ {176, 143, 69}, {194, 151, 66}, {199, 146, 64}, {197, 145, 60},
+ {181, 145, 67}, {178, 139, 72}, {186, 142, 76}, {183, 147, 72},
+ {185, 154, 80}, {190, 156, 84}, {191, 168, 99}, {175, 179, 104},
+ {181, 168, 120}, {195, 169, 133}, {183, 174, 137}, {197, 165, 130},
+ {206, 166, 126}, {211, 176, 133}, {209, 188, 136}, {215, 181, 133},
+ {202, 184, 122}, {205, 197, 119}, {195, 192, 115}, {203, 190, 107},
+ {210, 172, 110}, {212, 189, 110}, {207, 192, 106}, {191, 190, 101},
+ {182, 186, 92}, {174, 183, 78}, {168, 179, 79}, {158, 175, 75},
+ {157, 166, 69}, {145, 164, 64}, {129, 163, 91}, {94, 146, 106},
+ {67, 58, 29}, {24, 11, 6}, {26, 9, 6}, {95, 79, 42},
+ {121, 129, 96}, {146, 161, 92}, {166, 185, 88}, {172, 177, 82},
+ {185, 184, 76}, {184, 187, 85}, {179, 177, 86}, {189, 175, 97},
+ {203, 177, 100}, {199, 168, 110}, {211, 164, 112}, {208, 162, 123},
+ {202, 166, 121}, {210, 164, 134}, {207, 167, 147}, {207, 169, 135},
+ {207, 169, 130}, {214, 174, 125}, {210, 173, 122}, {205, 172, 125},
+ {202, 178, 124}, {187, 178, 120}, {184, 182, 117}, {203, 187, 95},
+ {209, 186, 94}, {217, 193, 101}, {213, 175, 93}, {208, 171, 87},
+ {208, 165, 80}, {205, 158, 79}, {193, 160, 79}, {192, 162, 80},
+ {203, 180, 85}, {199, 187, 82}, {217, 198, 90}, {220, 201, 98},
+ {226, 204, 105}, {221, 190, 103}, {222, 200, 120}, {222, 191, 127},
+ {234, 200, 134}, {222, 186, 122}, {221, 186, 133}, {219, 186, 135},
+ {215, 185, 130}, {206, 189, 130}, {204, 185, 139}, {206, 183, 146},
+ {200, 174, 144}, {202, 177, 146}, {193, 182, 150}, {187, 184, 154},
+ {181, 174, 153}, {194, 184, 149}, {210, 178, 152}, {209, 181, 157},
+ {216, 188, 146}, {214, 191, 146}, {216, 196, 147}, {223, 197, 151},
+ {226, 198, 154}, {223, 207, 156}, {215, 203, 166}, {221, 204, 164},
+ {222, 195, 177}, {237, 204, 177}, {223, 200, 171}, {230, 221, 187},
+ {226, 219, 191}, {217, 208, 195}, {255, 250, 232}, {210, 201, 181},
+ {217, 209, 167}, {230, 208, 161}, {227, 208, 158}, {224, 207, 153},
+ {226, 202, 156}, {227, 196, 161}, {223, 191, 165}, {224, 190, 159},
+ {219, 196, 162}, {215, 198, 163}, {212, 192, 167}, {211, 191, 160},
+ {206, 196, 161}, {206, 195, 165}, {207, 186, 165}, {200, 190, 166},
+ {199, 194, 160}, {198, 197, 143}, {179, 200, 141}, {171, 171, 130},
+ {155, 147, 120}, {147, 135, 111}, {134, 114, 74}, {34, 16, 7},
+ },
+ /* nile.ppm*/
+ {
+ {6, 39, 66}, {27, 62, 99}, {97, 68, 75}, {156, 67, 38},
+ {216, 117, 3}, {235, 142, 3}, {254, 172, 3}, {250, 212, 163},
+ {252, 231, 218}, {238, 226, 247}, {239, 229, 249}, {237, 221, 250},
+ {220, 207, 244}, {203, 200, 243}, {198, 194, 235}, {198, 173, 212},
+ {205, 162, 167}, {203, 138, 42}, {233, 145, 2}, {242, 148, 1},
+ {250, 157, 6}, {215, 175, 140}, {203, 175, 195}, {190, 164, 195},
+ {190, 148, 137}, {216, 135, 22}, {215, 125, 10}, {171, 102, 23},
+ {129, 100, 47}, {121, 95, 144}, {128, 142, 174}, {173, 161, 221},
+ {183, 170, 228}, {186, 173, 229}, {188, 175, 237}, {188, 180, 239},
+ {191, 178, 242}, {191, 176, 241}, {195, 171, 230}, {193, 166, 240},
+ {196, 167, 231}, {195, 168, 229}, {185, 164, 220}, {177, 160, 210},
+ {137, 139, 141}, {134, 82, 60}, {141, 38, 18}, {116, 24, 1},
+ {112, 16, 1}, {115, 28, 6}, {120, 61, 52}, {103, 73, 159},
+ {64, 36, 166}, {29, 40, 175}, {24, 42, 167}, {8, 47, 170},
+ {2, 38, 172}, {7, 34, 178}, {9, 48, 185}, {19, 54, 186},
+ {23, 67, 194}, {40, 59, 185}, {52, 61, 183}, {65, 68, 191},
+ {68, 70, 191}, {77, 86, 199}, {84, 89, 201}, {105, 106, 203},
+ {114, 114, 223}, {178, 149, 221}, {193, 157, 211}, {211, 172, 164},
+ {248, 158, 11}, {254, 165, 3}, {252, 172, 8}, {234, 191, 176},
+ {219, 196, 207}, {212, 193, 216}, {207, 192, 219}, {205, 177, 227},
+ {203, 179, 227}, {197, 183, 238}, {196, 189, 244}, {194, 184, 244},
+ {200, 187, 245}, {205, 199, 251}, {213, 215, 251}, {217, 214, 254},
+ {236, 233, 250}, {247, 232, 253}, {251, 238, 253}, {251, 242, 251},
+ {252, 243, 251}, {251, 240, 252}, {243, 231, 251}, {233, 219, 231},
+ {235, 212, 214}, {213, 182, 218}, {204, 164, 169}, {161, 110, 98},
+ {115, 59, 43}, {119, 40, 3}, {124, 31, 5}, {132, 39, 8},
+ {174, 73, 7}, {223, 128, 5}, {243, 157, 10}, {232, 179, 167},
+ {224, 196, 210}, {239, 205, 211}, {241, 221, 218}, {234, 223, 238},
+ {236, 225, 248}, {230, 217, 247}, {229, 206, 234}, {219, 205, 219},
+ {214, 196, 210}, {215, 184, 163}, {246, 158, 7}, {249, 165, 3},
+ {245, 160, 4}, {241, 146, 3}, {233, 137, 2}, {220, 122, 7},
+ {161, 88, 51}, {153, 127, 130}, {172, 145, 194}, {191, 160, 214},
+ {192, 164, 225}, {192, 162, 230}, {185, 159, 232}, {177, 156, 226},
+ {144, 147, 223}, {98, 131, 220}, {81, 124, 214}, {64, 115, 203},
+ {61, 117, 181}, {62, 111, 175}, {69, 106, 162}, {66, 106, 148},
+ {49, 100, 154}, {25, 87, 150}, {30, 80, 145}, {13, 66, 130},
+ {17, 70, 121}, {32, 80, 103}, {68, 82, 99}, {88, 34, 11},
+ {105, 17, 6}, {106, 19, 3}, {119, 56, 31}, {114, 76, 83},
+ {118, 101, 167}, {161, 150, 217}, {176, 172, 233}, {179, 177, 233},
+ {176, 183, 242}, {171, 177, 238}, {168, 181, 241}, {177, 185, 242},
+ {178, 188, 241}, {178, 190, 247}, {189, 196, 252}, {194, 208, 250},
+ {203, 203, 243}, {197, 198, 244}, {196, 195, 243}, {192, 196, 244},
+ {196, 193, 238}, {192, 187, 239}, {183, 191, 240}, {182, 190, 244},
+ {188, 191, 224}, {185, 179, 224}, {185, 171, 210}, {171, 144, 177},
+ {175, 129, 92}, {225, 138, 1}, {234, 140, 1}, {240, 146, 4},
+ {245, 151, 6}, {219, 168, 138}, {193, 157, 206}, {173, 157, 219},
+ {108, 123, 203}, {83, 122, 207}, {70, 127, 189}, {64, 116, 180},
+ {61, 113, 168}, {56, 97, 158}, {30, 89, 142}, {35, 90, 128},
+ {30, 89, 125}, {22, 77, 120}, {18, 68, 110}, {20, 55, 88},
+ {93, 8, 6}, {100, 9, 3}, {118, 14, 1}, {136, 41, 6},
+ {217, 113, 3}, {227, 137, 1}, {223, 132, 3}, {165, 99, 20},
+ {144, 55, 30}, {127, 32, 6}, {116, 26, 6}, {112, 46, 44},
+ {111, 71, 98}, {92, 93, 152}, {107, 110, 176}, {102, 115, 200},
+ {110, 118, 213}, {146, 142, 222}, {173, 163, 224}, {182, 175, 229},
+ {184, 179, 233}, {184, 177, 236}, {182, 172, 239}, {178, 170, 239},
+ {174, 173, 237}, {170, 160, 225}, {108, 120, 218}, {98, 125, 210},
+ {84, 119, 218}, {81, 121, 217}, {71, 123, 218}, {72, 115, 212},
+ {79, 114, 208}, {87, 116, 206}, {87, 106, 203}, {85, 100, 202},
+ {74, 92, 198}, {57, 90, 209}, {55, 95, 206}, {61, 88, 195},
+ {61, 79, 186}, {51, 83, 194}, {33, 80, 195}, {32, 87, 198},
+ {32, 90, 201}, {51, 104, 211}, {58, 102, 206}, {61, 106, 181},
+ {62, 110, 178}, {72, 115, 165}, {73, 110, 160}, {75, 95, 155},
+ {75, 96, 145}, {68, 91, 149}, {39, 96, 148}, {31, 81, 143},
+ {29, 82, 137}, {23, 79, 121}, {25, 64, 120}, {17, 60, 100},
+ },
+ /* picasso.jfille-chevre.ppm*/
+ {
+ {24, 0, 0}, {84, 2, 2}, {184, 46, 0}, {195, 61, 5},
+ {198, 61, 2}, {203, 76, 3}, {207, 80, 3}, {211, 85, 3},
+ {209, 84, 2}, {208, 79, 3}, {204, 73, 0}, {203, 61, 1},
+ {201, 59, 4}, {199, 62, 3}, {202, 76, 3}, {205, 77, 7},
+ {207, 85, 7}, {203, 93, 14}, {194, 90, 28}, {203, 102, 46},
+ {204, 100, 40}, {183, 97, 38}, {185, 96, 28}, {151, 102, 51},
+ {196, 102, 41}, {206, 119, 51}, {225, 142, 78}, {222, 148, 77},
+ {228, 148, 76}, {220, 140, 77}, {199, 136, 70}, {208, 114, 43},
+ {209, 108, 37}, {199, 124, 31}, {206, 127, 29}, {207, 130, 30},
+ {205, 135, 30}, {208, 137, 32}, {214, 146, 36}, {218, 154, 55},
+ {222, 162, 83}, {225, 184, 99}, {228, 197, 128}, {232, 202, 150},
+ {227, 202, 163}, {237, 208, 172}, {239, 211, 172}, {241, 214, 173},
+ {246, 218, 175}, {248, 219, 178}, {249, 220, 178}, {247, 223, 185},
+ {252, 226, 180}, {252, 224, 187}, {250, 226, 191}, {249, 225, 189},
+ {247, 224, 191}, {249, 226, 195}, {248, 225, 192}, {247, 227, 192},
+ {247, 224, 193}, {245, 222, 193}, {244, 220, 186}, {243, 221, 184},
+ {245, 220, 182}, {245, 216, 184}, {244, 215, 183}, {242, 216, 180},
+ {239, 215, 179}, {238, 213, 175}, {237, 214, 172}, {238, 211, 162},
+ {236, 206, 152}, {236, 204, 140}, {232, 199, 128}, {229, 179, 113},
+ {229, 167, 97}, {225, 163, 93}, {223, 155, 82}, {210, 147, 85},
+ {196, 126, 70}, {208, 104, 48}, {215, 101, 49}, {211, 111, 32},
+ {205, 125, 18}, {214, 133, 14}, {214, 142, 15}, {215, 153, 44},
+ {219, 168, 76}, {228, 184, 90}, {231, 188, 99}, {233, 194, 118},
+ {241, 203, 124}, {235, 202, 135}, {236, 201, 133}, {237, 186, 120},
+ {237, 176, 107}, {231, 171, 102}, {231, 168, 94}, {227, 167, 101},
+ {225, 168, 119}, {212, 164, 105}, {232, 178, 119}, {244, 194, 131},
+ {241, 206, 140}, {238, 208, 150}, {238, 210, 161}, {233, 208, 170},
+ {233, 211, 169}, {237, 215, 173}, {241, 217, 177}, {243, 217, 181},
+ {246, 218, 179}, {247, 221, 173}, {251, 223, 174}, {247, 220, 172},
+ {247, 220, 169}, {243, 216, 161}, {245, 209, 144}, {242, 201, 137},
+ {241, 201, 116}, {232, 187, 99}, {227, 160, 80}, {225, 145, 73},
+ {217, 122, 51}, {213, 122, 33}, {204, 129, 21}, {200, 127, 20},
+ {197, 121, 11}, {196, 117, 7}, {193, 109, 0}, {195, 120, 8},
+ {197, 132, 18}, {209, 137, 27}, {218, 145, 31}, {220, 163, 56},
+ {230, 186, 95}, {232, 193, 105}, {233, 200, 129}, {237, 207, 141},
+ {245, 211, 150}, {242, 215, 158}, {242, 213, 164}, {236, 212, 171},
+ {235, 211, 172}, {234, 212, 176}, {236, 214, 177}, {238, 213, 183},
+ {238, 214, 179}, {227, 207, 181}, {240, 215, 182}, {242, 219, 182},
+ {241, 221, 182}, {243, 221, 183}, {244, 220, 183}, {242, 218, 183},
+ {240, 216, 190}, {242, 218, 191}, {246, 219, 195}, {245, 224, 198},
+ {243, 226, 199}, {246, 228, 205}, {246, 226, 202}, {250, 230, 205},
+ {250, 229, 206}, {248, 231, 205}, {250, 228, 197}, {248, 230, 194},
+ {248, 228, 196}, {246, 227, 195}, {244, 224, 196}, {239, 223, 197},
+ {236, 214, 193}, {237, 218, 188}, {240, 217, 186}, {239, 215, 181},
+ {241, 212, 173}, {238, 211, 166}, {237, 212, 155}, {233, 206, 145},
+ {235, 201, 134}, {220, 179, 120}, {212, 170, 121}, {206, 175, 108},
+ {211, 162, 117}, {210, 156, 93}, {223, 154, 89}, {224, 158, 81},
+ {228, 163, 81}, {234, 167, 90}, {237, 176, 104}, {240, 193, 127},
+ {237, 204, 141}, {237, 206, 152}, {237, 208, 159}, {238, 212, 165},
+ {241, 218, 170}, {240, 215, 171}, {244, 216, 170}, {246, 215, 160},
+ {242, 213, 169}, {245, 210, 160}, {249, 215, 154}, {247, 212, 146},
+ {251, 213, 137}, {245, 211, 137}, {239, 207, 146}, {235, 203, 151},
+ {224, 201, 154}, {236, 202, 151}, {237, 203, 154}, {233, 206, 158},
+ {239, 210, 166}, {238, 211, 169}, {240, 211, 167}, {242, 212, 168},
+ {239, 212, 164}, {241, 211, 157}, {236, 208, 150}, {236, 205, 142},
+ {235, 202, 139}, {231, 192, 122}, {227, 189, 102}, {226, 168, 88},
+ {224, 159, 72}, {211, 144, 40}, {201, 136, 33}, {204, 132, 21},
+ {205, 133, 16}, {208, 132, 14}, {205, 131, 10}, {204, 130, 9},
+ {202, 120, 8}, {204, 124, 2}, {195, 119, 6}, {193, 117, 12},
+ {205, 109, 27}, {213, 98, 30}, {203, 89, 27}, {207, 92, 29},
+ {212, 95, 23}, {203, 90, 24}, {201, 89, 25}, {203, 90, 23},
+ {202, 90, 20}, {204, 92, 16}, {203, 85, 11}, {189, 82, 5},
+ {196, 80, 15}, {193, 78, 12}, {189, 68, 5}, {194, 78, 4},
+ {183, 70, 2}, {182, 65, 9}, {147, 49, 11}, {42, 1, 3},
+ },
+ /* pollock.lavender-mist.ppm*/
+ {
+ {3, 5, 2}, {37, 33, 23}, {65, 68, 36}, {96, 95, 63},
+ {128, 112, 83}, {152, 127, 86}, {168, 141, 100}, {181, 155, 104},
+ {183, 157, 111}, {179, 157, 119}, {175, 157, 121}, {175, 159, 122},
+ {172, 157, 121}, {168, 154, 114}, {161, 149, 111}, {154, 140, 106},
+ {150, 139, 107}, {149, 135, 103}, {144, 132, 102}, {140, 130, 104},
+ {137, 128, 103}, {140, 124, 102}, {136, 126, 102}, {133, 124, 97},
+ {135, 123, 94}, {137, 123, 96}, {140, 123, 94}, {142, 126, 98},
+ {146, 125, 99}, {150, 136, 101}, {157, 145, 102}, {164, 146, 107},
+ {172, 150, 121}, {172, 156, 126}, {180, 156, 123}, {196, 164, 119},
+ {207, 171, 120}, {208, 174, 128}, {212, 181, 136}, {210, 187, 137},
+ {209, 181, 131}, {206, 168, 129}, {203, 169, 127}, {196, 171, 121},
+ {184, 166, 122}, {177, 156, 117}, {164, 146, 105}, {156, 131, 93},
+ {136, 121, 89}, {121, 112, 82}, {105, 104, 75}, {102, 102, 77},
+ {94, 90, 74}, {71, 76, 62}, {73, 77, 57}, {94, 90, 65},
+ {108, 96, 66}, {111, 98, 71}, {110, 99, 81}, {115, 111, 86},
+ {121, 114, 95}, {123, 114, 94}, {129, 117, 94}, {130, 120, 90},
+ {130, 121, 90}, {130, 119, 86}, {130, 117, 81}, {125, 103, 68},
+ {107, 88, 56}, {96, 84, 53}, {74, 62, 40}, {55, 48, 36},
+ {41, 31, 29}, {40, 34, 24}, {37, 38, 25}, {42, 39, 28},
+ {56, 49, 28}, {59, 57, 36}, {69, 73, 51}, {94, 80, 63},
+ {99, 95, 79}, {106, 99, 80}, {118, 111, 85}, {126, 113, 92},
+ {133, 123, 95}, {132, 128, 95}, {137, 132, 95}, {140, 133, 103},
+ {141, 132, 104}, {142, 135, 106}, {144, 135, 109}, {148, 139, 112},
+ {148, 142, 113}, {151, 146, 118}, {151, 144, 121}, {156, 145, 124},
+ {152, 151, 130}, {147, 156, 130}, {150, 159, 134}, {158, 157, 132},
+ {162, 158, 136}, {164, 160, 137}, {173, 165, 143}, {179, 171, 154},
+ {189, 179, 155}, {194, 187, 161}, {192, 186, 164}, {188, 186, 158},
+ {188, 180, 149}, {185, 177, 146}, {179, 167, 139}, {170, 164, 132},
+ {167, 157, 129}, {162, 155, 126}, {155, 149, 120}, {158, 142, 119},
+ {156, 142, 115}, {157, 147, 114}, {159, 148, 115}, {164, 152, 115},
+ {167, 153, 123}, {169, 155, 129}, {173, 160, 131}, {176, 163, 130},
+ {185, 172, 135}, {191, 181, 142}, {197, 185, 149}, {198, 188, 159},
+ {202, 187, 160}, {207, 190, 156}, {212, 196, 152}, {212, 194, 155},
+ {207, 193, 164}, {209, 196, 165}, {213, 197, 165}, {218, 194, 160},
+ {215, 189, 152}, {207, 185, 152}, {200, 179, 150}, {197, 177, 145},
+ {188, 170, 137}, {174, 160, 131}, {169, 157, 127}, {166, 162, 119},
+ {155, 153, 116}, {147, 145, 108}, {145, 138, 114}, {139, 134, 114},
+ {138, 132, 115}, {129, 133, 113}, {129, 128, 109}, {127, 130, 102},
+ {119, 118, 93}, {119, 112, 87}, {114, 101, 77}, {114, 102, 72},
+ {117, 106, 74}, {117, 111, 77}, {123, 116, 87}, {120, 115, 91},
+ {126, 121, 92}, {134, 128, 92}, {145, 128, 90}, {149, 138, 97},
+ {154, 140, 104}, {162, 138, 103}, {163, 137, 102}, {167, 143, 108},
+ {167, 144, 111}, {172, 149, 118}, {175, 157, 121}, {177, 163, 122},
+ {180, 166, 119}, {186, 167, 128}, {191, 174, 130}, {200, 173, 131},
+ {201, 173, 132}, {192, 176, 136}, {188, 174, 141}, {189, 177, 143},
+ {191, 172, 136}, {185, 166, 128}, {181, 161, 126}, {187, 157, 122},
+ {194, 149, 113}, {182, 140, 94}, {166, 118, 76}, {131, 100, 61},
+ {101, 81, 56}, {74, 70, 52}, {71, 56, 38}, {66, 55, 35},
+ {58, 49, 32}, {69, 63, 49}, {77, 73, 56}, {94, 84, 71},
+ {98, 97, 80}, {103, 103, 88}, {101, 117, 98}, {109, 143, 125},
+ {138, 142, 122}, {138, 142, 121}, {145, 143, 123}, {155, 152, 128},
+ {161, 158, 133}, {172, 159, 137}, {184, 169, 143}, {197, 178, 149},
+ {201, 187, 146}, {206, 188, 152}, {215, 189, 147}, {213, 188, 135},
+ {222, 181, 123}, {213, 181, 124}, {203, 163, 112}, {192, 153, 110},
+ {185, 145, 99}, {177, 142, 106}, {168, 142, 105}, {162, 142, 105},
+ {154, 139, 106}, {151, 135, 109}, {149, 136, 110}, {150, 137, 114},
+ {151, 143, 120}, {158, 146, 122}, {167, 155, 130}, {181, 168, 134},
+ {194, 182, 143}, {208, 186, 143}, {210, 200, 145}, {224, 198, 145},
+ {225, 196, 150}, {229, 205, 161}, {243, 223, 174}, {245, 222, 183},
+ {232, 211, 177}, {225, 208, 171}, {224, 219, 189}, {224, 217, 200},
+ {222, 205, 186}, {207, 203, 169}, {203, 196, 156}, {196, 186, 157},
+ {189, 178, 149}, {175, 164, 133}, {163, 157, 129}, {151, 151, 120},
+ {141, 141, 112}, {132, 129, 104}, {121, 128, 106}, {100, 110, 94},
+ {84, 88, 75}, {73, 69, 60}, {50, 56, 42}, {18, 19, 13},
+ },
+ /* yngpaint.ppm*/
+ {
+ {20, 15, 10}, {52, 47, 45}, {20, 105, 104}, {63, 130, 123},
+ {91, 144, 114}, {104, 140, 123}, {126, 141, 130}, {137, 151, 131},
+ {160, 157, 128}, {170, 165, 126}, {182, 173, 128}, {188, 186, 129},
+ {190, 192, 144}, {200, 204, 157}, {204, 205, 163}, {211, 209, 166},
+ {212, 213, 167}, {216, 218, 171}, {219, 220, 174}, {221, 223, 173},
+ {222, 224, 176}, {222, 224, 177}, {224, 224, 174}, {222, 222, 171},
+ {221, 221, 167}, {220, 220, 164}, {219, 220, 160}, {217, 219, 154},
+ {215, 218, 153}, {212, 211, 148}, {202, 203, 153}, {194, 194, 152},
+ {185, 182, 141}, {171, 167, 129}, {165, 167, 121}, {166, 153, 113},
+ {162, 134, 98}, {158, 140, 102}, {171, 158, 105}, {172, 163, 116},
+ {165, 163, 117}, {175, 171, 107}, {179, 169, 111}, {187, 166, 130},
+ {192, 187, 131}, {197, 193, 140}, {202, 201, 155}, {205, 207, 158},
+ {210, 214, 155}, {211, 223, 162}, {220, 222, 163}, {226, 224, 166},
+ {225, 222, 171}, {224, 224, 172}, {224, 224, 174}, {224, 226, 176},
+ {225, 226, 182}, {225, 231, 185}, {225, 227, 177}, {227, 227, 175},
+ {228, 227, 180}, {229, 231, 184}, {228, 234, 188}, {226, 232, 188},
+ {228, 234, 190}, {229, 231, 192}, {228, 233, 191}, {227, 232, 192},
+ {229, 234, 194}, {228, 233, 196}, {229, 233, 200}, {231, 235, 202},
+ {233, 242, 214}, {244, 247, 219}, {236, 241, 210}, {238, 235, 204},
+ {234, 238, 201}, {229, 236, 201}, {229, 234, 202}, {231, 236, 199},
+ {234, 234, 196}, {233, 231, 191}, {233, 231, 190}, {229, 230, 188},
+ {229, 229, 188}, {220, 224, 189}, {218, 222, 189}, {219, 223, 190},
+ {220, 224, 188}, {220, 224, 187}, {222, 230, 183}, {224, 225, 181},
+ {220, 226, 180}, {220, 226, 178}, {219, 226, 178}, {217, 226, 174},
+ {218, 226, 172}, {220, 222, 172}, {221, 221, 171}, {223, 221, 169},
+ {224, 221, 168}, {224, 219, 169}, {222, 218, 170}, {217, 219, 169},
+ {216, 219, 165}, {216, 216, 162}, {213, 213, 161}, {205, 208, 157},
+ {201, 199, 146}, {184, 184, 129}, {165, 164, 109}, {152, 146, 97},
+ {143, 145, 89}, {137, 143, 95}, {145, 149, 111}, {126, 130, 101},
+ {107, 137, 86}, {89, 115, 101}, {116, 115, 83}, {117, 115, 97},
+ {122, 131, 115}, {115, 122, 105}, {108, 124, 125}, {133, 143, 104},
+ {139, 144, 116}, {142, 154, 120}, {166, 172, 118}, {182, 187, 121},
+ {195, 198, 119}, {206, 208, 146}, {212, 210, 145}, {212, 219, 154},
+ {214, 218, 157}, {215, 217, 157}, {216, 217, 157}, {216, 217, 159},
+ {215, 213, 159}, {212, 212, 162}, {205, 206, 162}, {203, 199, 162},
+ {200, 200, 157}, {184, 185, 144}, {173, 166, 139}, {141, 155, 133},
+ {114, 148, 132}, {99, 147, 140}, {90, 142, 146}, {110, 149, 142},
+ {125, 157, 145}, {129, 146, 147}, {164, 165, 137}, {183, 186, 140},
+ {200, 202, 161}, {203, 203, 170}, {204, 210, 173}, {205, 208, 177},
+ {211, 210, 180}, {212, 215, 184}, {212, 214, 190}, {217, 217, 189},
+ {219, 218, 187}, {217, 221, 189}, {221, 220, 190}, {221, 221, 195},
+ {219, 222, 191}, {221, 225, 192}, {218, 221, 195}, {218, 222, 198},
+ {218, 222, 197}, {215, 226, 194}, {218, 224, 190}, {218, 223, 183},
+ {218, 224, 180}, {218, 224, 177}, {217, 224, 180}, {216, 221, 181},
+ {216, 220, 183}, {213, 218, 183}, {213, 216, 185}, {213, 216, 184},
+ {214, 213, 183}, {213, 210, 180}, {209, 213, 175}, {213, 215, 168},
+ {216, 215, 168}, {216, 216, 166}, {215, 212, 165}, {214, 212, 163},
+ {214, 214, 160}, {217, 217, 163}, {217, 217, 167}, {216, 218, 170},
+ {218, 220, 173}, {221, 220, 174}, {222, 221, 175}, {221, 222, 179},
+ {217, 224, 180}, {217, 221, 187}, {214, 220, 186}, {216, 219, 188},
+ {216, 219, 188}, {218, 218, 190}, {220, 218, 192}, {214, 218, 190},
+ {216, 219, 188}, {215, 218, 187}, {217, 216, 185}, {216, 215, 184},
+ {216, 215, 184}, {217, 214, 184}, {216, 216, 184}, {217, 217, 183},
+ {220, 221, 182}, {219, 219, 184}, {220, 220, 184}, {217, 222, 183},
+ {217, 223, 179}, {215, 221, 176}, {213, 219, 175}, {215, 221, 173},
+ {215, 222, 171}, {213, 222, 169}, {215, 222, 173}, {216, 222, 173},
+ {219, 221, 174}, {216, 222, 174}, {215, 221, 175}, {215, 222, 178},
+ {215, 220, 180}, {219, 219, 181}, {217, 216, 183}, {215, 214, 183},
+ {211, 214, 181}, {206, 213, 173}, {201, 207, 161}, {202, 205, 148},
+ {187, 186, 124}, {181, 172, 107}, {166, 158, 86}, {166, 138, 81},
+ {162, 142, 87}, {150, 140, 75}, {136, 133, 68}, {132, 123, 66},
+ {122, 117, 45}, {119, 92, 9}, {133, 113, 56}, {102, 96, 52},
+ {99, 55, 50}, {116, 108, 66}, {74, 61, 7}, {75, 50, 24},
+ {55, 41, 51}, {46, 43, 20}, {84, 9, 8}, {32, 3, 5},
+ },
+};
+
+int
+get_cmap (int n,
+ clrmap c,
+ int cmap_len)
+{
+ int i, j;
+
+ if (n == cmap_random)
+ n = g_random_int_range (0, vlen (the_cmaps));
+ if (n < 0)
+ n = 0;
+ for (i = 0; i < cmap_len; i++)
+ {
+ int ii = (i * 256) / cmap_len;
+ for (j = 0; j < 3; j++)
+ c[i][j] = the_cmaps[n][ii][j] / 255.0;
+ }
+ return n;
+}
+
+/* rgb 0 - 1,
+ h 0 - 6, s 0 - 1, v 0 - 1 */
+void
+rgb2hsv (gdouble *rgb,
+ gdouble *hsv)
+{
+ double rd, gd, bd, h, s, v, max, min, del, rc, gc, bc;
+
+ rd = rgb[0];
+ gd = rgb[1];
+ bd = rgb[2];
+
+ /* compute maximum of rd,gd,bd */
+ if (rd >= gd)
+ max = MAX (rd, bd);
+ else
+ max = MAX (gd, bd);
+
+ /* compute minimum of rd,gd,bd */
+ if (rd <= gd)
+ min = MIN (rd, bd);
+ else
+ min = MIN (gd, bd);
+
+ del = max - min;
+ v = max;
+ if (max != 0.0)
+ s = del / max;
+ else
+ s = 0.0;
+
+ h = 0;
+ if (s != 0.0)
+ {
+ rc = (max - rd) / del;
+ gc = (max - gd) / del;
+ bc = (max - bd) / del;
+
+ if (rd == max)
+ h = bc - gc;
+ else if (gd == max)
+ h = 2 + rc - bc;
+ else if (bd == max)
+ h = 4 + gc - rc;
+ if (h < 0)
+ h += 6;
+ }
+ hsv[0] = h;
+ hsv[1] = s;
+ hsv[2] = v;
+}
+
+
+/* h 0 - 6, s 0 - 1, v 0 - 1
+ rgb 0 - 1 */
+void
+hsv2rgb (double *hsv,
+ double *rgb)
+{
+ double h = hsv[0], s = hsv[1], v = hsv[2];
+ int j;
+ double rd, gd, bd;
+ double f, p, q, t;
+
+ while (h >= 6.0)
+ h = h - 6.0;
+ while (h < 0.0)
+ h = h + 6.0;
+ j = (int) floor (h);
+ f = h - j;
+ p = v * (1 - s);
+ q = v * (1 - (s * f));
+ t = v * (1 - (s * (1 - f)));
+
+ switch (j)
+ {
+ case 0:
+ rd = v; gd = t; bd = p; break;
+ case 1:
+ rd = q; gd = v; bd = p; break;
+ case 2:
+ rd = p; gd = v; bd = t; break;
+ case 3:
+ rd = p; gd = q; bd = v; break;
+ case 4:
+ rd = t; gd = p; bd = v; break;
+ case 5:
+ rd = v; gd = p; bd = q; break;
+ default:
+ rd = v; gd = t; bd = p; break;
+ }
+
+ rgb[0] = rd;
+ rgb[1] = gd;
+ rgb[2] = bd;
+}
diff --git a/plug-ins/flame/cmap.h b/plug-ins/flame/cmap.h
new file mode 100644
index 0000000..78669c1
--- /dev/null
+++ b/plug-ins/flame/cmap.h
@@ -0,0 +1,35 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef cmap_included
+#define cmap_included
+
+#define cmap_random (-1)
+
+
+
+#define vlen(x) (sizeof(x)/sizeof(*x))
+
+typedef double clrmap[256][3];
+
+extern void rgb2hsv(double *rgb, double *hsv);
+extern void hsv2rgb(double *hsv, double *rgb);
+extern int get_cmap(int n, clrmap c, int cmap_len);
+
+#endif
diff --git a/plug-ins/flame/flame.c b/plug-ins/flame/flame.c
new file mode 100644
index 0000000..f2e3898
--- /dev/null
+++ b/plug-ins/flame/flame.c
@@ -0,0 +1,1294 @@
+/* flame - cosmic recursive fractal flames
+ * Copyright (C) 1997 Scott Draves <spot@cs.cmu.edu>
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "flame.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_PROC "plug-in-flame"
+#define PLUG_IN_BINARY "flame"
+#define PLUG_IN_ROLE "gimp-flame"
+
+#define VARIATION_SAME (-2)
+
+#define BUFFER_SIZE 10000
+
+#define SCALE_WIDTH 150
+#define PREVIEW_SIZE 150
+#define EDIT_PREVIEW_SIZE 85
+#define NMUTANTS 9
+
+#define BLACK_DRAWABLE (-2)
+#define GRADIENT_DRAWABLE (-3)
+#define TABLE_DRAWABLE (-4)
+
+
+struct
+{
+ gint randomize; /* superseded */
+ gint variation;
+ gint32 cmap_drawable;
+ control_point cp;
+} config;
+
+
+/* Declare local functions. */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static void flame (gint32 drawable_id);
+
+static gboolean flame_dialog (void);
+static void set_flame_preview (void);
+static void load_callback (GtkWidget *widget,
+ gpointer data);
+static void save_callback (GtkWidget *widget,
+ gpointer data);
+static void set_edit_preview (void);
+static void combo_callback (GtkWidget *widget,
+ gpointer data);
+static void init_mutants (void);
+
+
+static gchar buffer[BUFFER_SIZE];
+static GtkWidget *cmap_preview;
+static GtkWidget *flame_preview;
+static gint preview_width, preview_height;
+static GtkWidget *dialog;
+static GtkWidget *load_button = NULL;
+static GtkWidget *save_button = NULL;
+static GtkWidget *file_dialog = NULL;
+static gint load_save;
+
+static GtkWidget *edit_dialog = NULL;
+
+static control_point edit_cp;
+static control_point mutants[NMUTANTS];
+static GtkWidget *edit_previews[NMUTANTS];
+static gdouble pick_speed = 0.2;
+
+static frame_spec f = { 0.0, &config.cp, 1, 0.0 };
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create cosmic recursive fractal flames"),
+ "Create cosmic recursive fractal flames",
+ "Scott Draves",
+ "Scott Draves",
+ "1997",
+ N_("_Flame..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Fractals");
+}
+
+static void
+maybe_init_cp (void)
+{
+ if (0 == config.cp.spatial_oversample)
+ {
+ config.randomize = 0;
+ config.variation = VARIATION_SAME;
+ config.cmap_drawable = GRADIENT_DRAWABLE;
+
+ random_control_point (&config.cp, variation_random);
+
+ config.cp.center[0] = 0.0;
+ config.cp.center[1] = 0.0;
+ config.cp.pixels_per_unit = 100;
+ config.cp.spatial_oversample = 2;
+ config.cp.gamma = 2.0;
+ config.cp.contrast = 1.0;
+ config.cp.brightness = 1.0;
+ config.cp.spatial_filter_radius = 0.75;
+ config.cp.sample_density = 5.0;
+ config.cp.zoom = 0.0;
+ config.cp.nbatches = 1;
+ config.cp.white_level = 200;
+ config.cp.cmap_index = 72;
+ /* cheating */
+ config.cp.width = 256;
+ config.cp.height = 256;
+ }
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 drawable_id;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gimp_get_data (PLUG_IN_PROC, &config);
+ maybe_init_cp ();
+
+ config.cp.width = gimp_drawable_width (drawable_id);
+ config.cp.height = gimp_drawable_height (drawable_id);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! flame_dialog ())
+ {
+ status = GIMP_PDB_CANCEL;
+ }
+ }
+ else
+ {
+ /* reusing a drawable_ID from the last run is a bad idea
+ since the drawable might have vanished (bug #37761) */
+ if (config.cmap_drawable >= 0)
+ config.cmap_drawable = GRADIENT_DRAWABLE;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ gimp_progress_init (_("Drawing flame"));
+
+ flame (drawable_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ gimp_set_data (PLUG_IN_PROC, &config, sizeof (config));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static void
+drawable_to_cmap (control_point *cp)
+{
+ gint i, j;
+
+ if (TABLE_DRAWABLE >= config.cmap_drawable)
+ {
+ i = TABLE_DRAWABLE - config.cmap_drawable;
+ get_cmap (i, cp->cmap, 256);
+ }
+ else if (BLACK_DRAWABLE == config.cmap_drawable)
+ {
+ for (i = 0; i < 256; i++)
+ for (j = 0; j < 3; j++)
+ cp->cmap[i][j] = 0.0;
+ }
+ else if (GRADIENT_DRAWABLE == config.cmap_drawable)
+ {
+ gchar *name = gimp_context_get_gradient ();
+ gint num;
+ gdouble *g;
+
+ /* FIXME: "reverse" hardcoded to FALSE. */
+ gimp_gradient_get_uniform_samples (name, 256, FALSE,
+ &num, &g);
+
+ g_free (name);
+
+ for (i = 0; i < 256; i++)
+ for (j = 0; j < 3; j++)
+ cp->cmap[i][j] = g[i*4 + j];
+ g_free (g);
+ }
+ else
+ {
+ GeglBuffer *buffer = gimp_drawable_get_buffer (config.cmap_drawable);
+ gint width = gegl_buffer_get_width (buffer);
+ gint height = gegl_buffer_get_height (buffer);
+ guchar p[3];
+
+ for (i = 0; i < 256; i++)
+ {
+ gegl_buffer_sample (buffer,
+ i % width,
+ (i / width) % height,
+ NULL, p, babl_format ("R'G'B'"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ for (j = 0; j < 3; j++)
+ cp->cmap[i][j] = p[j] / 255.0;
+ }
+
+ g_object_unref (buffer);
+ }
+}
+
+static void
+flame (gint32 drawable_id)
+{
+ const Babl *format;
+ gint width, height;
+ guchar *tmp;
+ gint bytes;
+
+ width = gimp_drawable_width (drawable_id);
+ height = gimp_drawable_height (drawable_id);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bytes = babl_format_get_bytes_per_pixel (format);
+
+ tmp = g_new (guchar, width * height * 4);
+
+ /* render */
+ config.cp.width = width;
+ config.cp.height = height;
+ if (config.randomize)
+ random_control_point (&config.cp, config.variation);
+ drawable_to_cmap (&config.cp);
+ render_rectangle (&f, tmp, width, field_both, 4,
+ gimp_progress_update);
+ gimp_progress_update (1.0);
+
+ /* update destination */
+ if (bytes == 4)
+ {
+ GeglBuffer *buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ gegl_buffer_set (buffer, GEGL_RECTANGLE (0, 0, width, height), 0,
+ format, tmp,
+ GEGL_AUTO_ROWSTRIDE);
+
+ g_object_unref (buffer);
+ }
+ else if (bytes == 3)
+ {
+ GeglBuffer *src_buffer = gimp_drawable_get_buffer (drawable_id);
+ GeglBuffer *dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ gint i, j;
+ guchar *sl;
+
+ sl = g_new (guchar, 3 * width);
+
+ for (i = 0; i < height; i++)
+ {
+ guchar *rr = tmp + 4 * i * width;
+ guchar *sld = sl;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, i, width, 1), 1.0,
+ format, sl,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (j = 0; j < width; j++)
+ {
+ gint k, alpha = rr[3];
+
+ for (k = 0; k < 3; k++)
+ {
+ gint t = (rr[k] + ((sld[k] * (256-alpha)) >> 8));
+
+ if (t > 255) t = 255;
+ sld[k] = t;
+ }
+ rr += 4;
+ sld += 3;
+ }
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (0, i, width, 1), 0,
+ format, sl,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (sl);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+ }
+
+ g_free (tmp);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, 0, 0, width, height);
+}
+
+static void
+file_response_callback (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+
+ if (load_save)
+ {
+ FILE *f;
+ gint i, c;
+ gchar *ss;
+
+ if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ {
+ g_message (_("'%s' is not a regular file"),
+ gimp_filename_to_utf8 (filename));
+ g_free (filename);
+ return;
+ }
+
+ f = g_fopen (filename, "rb");
+
+ if (f == NULL)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ g_free (filename);
+ return;
+ }
+
+ i = 0;
+ ss = buffer;
+ do
+ {
+ c = getc (f);
+ if (EOF == c)
+ break;
+ ss[i++] = c;
+ }
+ while (i < BUFFER_SIZE && ';' != c);
+ parse_control_point (&ss, &config.cp);
+ fclose (f);
+ /* i want to update the existing dialogue, but it's
+ too painful */
+ gimp_set_data (PLUG_IN_PROC, &config, sizeof (config));
+ /* gtk_widget_destroy(dialog); */
+ set_flame_preview ();
+ set_edit_preview ();
+ }
+ else
+ {
+ FILE *f = g_fopen (filename, "wb");
+
+ if (NULL == f)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ g_free (filename);
+ return;
+ }
+
+ print_control_point (f, &config.cp, 0);
+ fclose (f);
+ }
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+
+ if (! gtk_widget_get_sensitive (load_button))
+ gtk_widget_set_sensitive (load_button, TRUE);
+
+ if (! gtk_widget_get_sensitive (save_button))
+ gtk_widget_set_sensitive (save_button, TRUE);
+}
+
+static void
+make_file_dialog (const gchar *title,
+ GtkWidget *parent)
+{
+ file_dialog = gtk_file_chooser_dialog_new (title, GTK_WINDOW (parent),
+ load_save ?
+ GTK_FILE_CHOOSER_ACTION_OPEN :
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ load_save ?
+ _("_Open") : _("_Save"),
+ GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (file_dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (file_dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ if (! load_save)
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_dialog),
+ TRUE);
+
+ g_object_add_weak_pointer (G_OBJECT (file_dialog), (gpointer) &file_dialog);
+
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (file_dialog), TRUE);
+
+ g_signal_connect (file_dialog, "delete-event",
+ G_CALLBACK (gtk_true),
+ NULL);
+ g_signal_connect (file_dialog, "response",
+ G_CALLBACK (file_response_callback),
+ NULL);
+}
+
+static void
+randomize_callback (GtkWidget *widget,
+ gpointer data)
+{
+ random_control_point (&edit_cp, config.variation);
+ init_mutants ();
+ set_edit_preview ();
+}
+
+static void
+edit_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ gtk_widget_hide (widget);
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ config.cp = edit_cp;
+ set_flame_preview ();
+ }
+}
+
+static void
+init_mutants (void)
+{
+ gint i;
+
+ for (i = 0; i < NMUTANTS; i++)
+ {
+ mutants[i] = edit_cp;
+ random_control_point (mutants + i, config.variation);
+ if (VARIATION_SAME == config.variation)
+ copy_variation (mutants + i, &edit_cp);
+ }
+}
+
+static void
+set_edit_preview (void)
+{
+ gint i, j;
+ guchar *b;
+ control_point pcp;
+ gint nbytes = EDIT_PREVIEW_SIZE * EDIT_PREVIEW_SIZE * 3;
+
+ static frame_spec pf = { 0.0, 0, 1, 0.0 };
+
+ if (NULL == edit_previews[0])
+ return;
+
+ b = g_new (guchar, nbytes);
+ maybe_init_cp ();
+ drawable_to_cmap (&edit_cp);
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ {
+ gint mut = i*3 + j;
+
+ pf.cps = &pcp;
+ if (1 == i && 1 == j)
+ {
+ pcp = edit_cp;
+ }
+ else
+ {
+ control_point ends[2];
+ ends[0] = edit_cp;
+ ends[1] = mutants[mut];
+ ends[0].time = 0.0;
+ ends[1].time = 1.0;
+ interpolate (ends, 2, pick_speed, &pcp);
+ }
+ pcp.pixels_per_unit =
+ (pcp.pixels_per_unit * EDIT_PREVIEW_SIZE) / pcp.width;
+ pcp.width = EDIT_PREVIEW_SIZE;
+ pcp.height = EDIT_PREVIEW_SIZE;
+
+ pcp.sample_density = 1;
+ pcp.spatial_oversample = 1;
+ pcp.spatial_filter_radius = 0.5;
+
+ drawable_to_cmap (&pcp);
+
+ render_rectangle (&pf, b, EDIT_PREVIEW_SIZE, field_both, 3, NULL);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (edit_previews[mut]),
+ 0, 0, EDIT_PREVIEW_SIZE, EDIT_PREVIEW_SIZE,
+ GIMP_RGB_IMAGE,
+ b,
+ EDIT_PREVIEW_SIZE * 3);
+ }
+ g_free (b);
+}
+
+static void
+edit_preview_size_allocate (GtkWidget *widget)
+{
+ set_edit_preview ();
+}
+
+static void
+preview_clicked (GtkWidget *widget,
+ gpointer data)
+{
+ gint mut = GPOINTER_TO_INT (data);
+
+ if (mut == 4)
+ {
+ control_point t = edit_cp;
+ init_mutants ();
+ edit_cp = t;
+ }
+ else
+ {
+ control_point ends[2];
+ ends[0] = edit_cp;
+ ends[1] = mutants[mut];
+ ends[0].time = 0.0;
+ ends[1].time = 1.0;
+ interpolate (ends, 2, pick_speed, &edit_cp);
+ }
+ set_edit_preview ();
+}
+
+static void
+edit_callback (GtkWidget *widget,
+ GtkWidget *parent)
+{
+ edit_cp = config.cp;
+
+ if (edit_dialog == NULL)
+ {
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *combo;
+ GtkWidget *label;
+ GtkObject *adj;
+ gint i, j;
+
+ edit_dialog = gimp_dialog_new (_("Edit Flame"), PLUG_IN_ROLE,
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (edit_dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (edit_dialog, "response",
+ G_CALLBACK (edit_response),
+ edit_dialog);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (edit_dialog))),
+ main_vbox, FALSE, FALSE, 0);
+
+ frame = gimp_frame_new (_("Directions"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ {
+ gint mut = i * 3 + j;
+
+ edit_previews[mut] = gimp_preview_area_new ();
+ gtk_widget_set_size_request (edit_previews[mut],
+ EDIT_PREVIEW_SIZE,
+ EDIT_PREVIEW_SIZE);
+ button = gtk_button_new ();
+ gtk_container_add (GTK_CONTAINER(button), edit_previews[mut]);
+ gtk_table_attach (GTK_TABLE (table), button, i, i+1, j, j+1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_widget_show (edit_previews[mut]);
+
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (preview_clicked),
+ GINT_TO_POINTER (mut));
+ }
+
+ g_signal_connect (edit_previews[0], "size-allocate",
+ G_CALLBACK (edit_preview_size_allocate),
+ NULL);
+
+ frame = gimp_frame_new (_("Controls"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show(table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Speed:"), SCALE_WIDTH, 0,
+ pick_speed,
+ 0.05, 0.5, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pick_speed);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_edit_preview),
+ NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic( _("_Randomize"));
+ gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))),
+ 2, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (randomize_callback),
+ NULL);
+
+ combo = gimp_int_combo_box_new (_("Same"), VARIATION_SAME,
+ _("Random"), variation_random,
+ _("Linear"), 0,
+ _("Sinusoidal"), 1,
+ _("Spherical"), 2,
+ _("Swirl"), 3,
+ _("Horseshoe"), 4,
+ _("Polar"), 5,
+ _("Bent"), 6,
+ _("Handkerchief"), 7,
+ _("Heart"), 8,
+ _("Disc"), 9,
+ _("Spiral"), 10,
+ _("Hyperbolic"), 11,
+ _("Diamond"), 12,
+ _("Ex"), 13,
+ _("Julia"), 14,
+ _("Waves"), 15,
+ _("Fisheye"), 16,
+ _("Popcorn"), 17,
+ _("Exponential"), 18,
+ _("Power"), 19,
+ _("Cosine"), 20,
+ _("Rings"), 21,
+ _("Fan"), 22,
+ _("Eyefish"), 23,
+ _("Bubble"), 24,
+ _("Cylinder"), 25,
+ _("Noise"), 26,
+ _("Blur"), 27,
+ _("Gaussian"), 28,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ VARIATION_SAME);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (combo_callback),
+ &config.variation);
+
+ gtk_box_pack_end (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ label = gtk_label_new_with_mnemonic (_("_Variation:"));
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+ gtk_widget_show (label);
+
+ gtk_widget_show (main_vbox);
+
+ init_mutants ();
+ }
+
+ set_edit_preview ();
+
+ gtk_window_present (GTK_WINDOW (edit_dialog));
+}
+
+static void
+load_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (! file_dialog)
+ {
+ load_save = 1;
+ make_file_dialog (_("Load Flame"), gtk_widget_get_toplevel (widget));
+
+ gtk_widget_set_sensitive (save_button, FALSE);
+ }
+
+ gtk_window_present (GTK_WINDOW (file_dialog));
+}
+
+static void
+save_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (! file_dialog)
+ {
+ load_save = 0;
+ make_file_dialog (_("Save Flame"), gtk_widget_get_toplevel (widget));
+
+ gtk_widget_set_sensitive (load_button, FALSE);
+ }
+
+ gtk_window_present (GTK_WINDOW (file_dialog));
+}
+
+static void
+combo_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
+
+ if (VARIATION_SAME != config.variation)
+ random_control_point (&edit_cp, config.variation);
+
+ init_mutants ();
+ set_edit_preview ();
+}
+
+static void
+set_flame_preview (void)
+{
+ guchar *b;
+ control_point pcp;
+
+ static frame_spec pf = {0.0, 0, 1, 0.0};
+
+ if (NULL == flame_preview)
+ return;
+
+ b = g_new (guchar, preview_width * preview_height * 3);
+
+ maybe_init_cp ();
+ drawable_to_cmap (&config.cp);
+
+ pf.cps = &pcp;
+ pcp = config.cp;
+ pcp.pixels_per_unit =
+ (pcp.pixels_per_unit * preview_width) / pcp.width;
+ pcp.width = preview_width;
+ pcp.height = preview_height;
+ pcp.sample_density = 1;
+ pcp.spatial_oversample = 1;
+ pcp.spatial_filter_radius = 0.1;
+ render_rectangle (&pf, b, preview_width, field_both, 3, NULL);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (flame_preview),
+ 0, 0, preview_width, PREVIEW_SIZE,
+ GIMP_RGB_IMAGE,
+ b,
+ preview_width * 3);
+ g_free (b);
+}
+
+static void
+flame_preview_size_allocate (GtkWidget *preview)
+{
+ set_flame_preview ();
+}
+
+static void
+set_cmap_preview (void)
+{
+ gint i, x, y;
+ guchar b[96];
+ guchar *cmap_buffer, *ptr;
+
+ if (NULL == cmap_preview)
+ return;
+
+ drawable_to_cmap (&config.cp);
+
+ cmap_buffer = g_new (guchar, 32*32*3);
+ ptr = cmap_buffer;
+ for (y = 0; y < 32; y += 4)
+ {
+ for (x = 0; x < 32; x++)
+ {
+ gint j;
+
+ i = x + (y/4) * 32;
+ for (j = 0; j < 3; j++)
+ b[x*3+j] = config.cp.cmap[i][j]*255.0;
+ }
+
+ memcpy (ptr, b, 32*3);
+ ptr += 32*3;
+ memcpy (ptr, b, 32*3);
+ ptr += 32*3;
+ memcpy (ptr, b, 32*3);
+ ptr += 32*3;
+ memcpy (ptr, b, 32*3);
+ ptr += 32*3;
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (cmap_preview),
+ 0, 0, 32, 32,
+ GIMP_RGB_IMAGE,
+ cmap_buffer,
+ 32 * 3);
+ g_free (cmap_buffer);
+}
+
+static void
+cmap_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &config.cmap_drawable);
+
+ set_cmap_preview ();
+ set_flame_preview ();
+ /* set_edit_preview(); */
+}
+
+static gboolean
+cmap_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ return ! gimp_drawable_is_indexed (drawable_id);
+}
+
+
+static gboolean
+flame_dialog (void)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *notebook;
+ GtkWidget *label;
+ GtkWidget *frame;
+ GtkWidget *abox;
+ GtkWidget *button;
+ GtkWidget *table;
+ GtkWidget *box;
+ GtkObject *adj;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("Flame"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), box, FALSE, FALSE, 0);
+ gtk_widget_show (box);
+
+ abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (box), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ flame_preview = gimp_preview_area_new ();
+ {
+ gdouble aspect = config.cp.width / (double) config.cp.height;
+
+ if (aspect > 1.0)
+ {
+ preview_width = PREVIEW_SIZE;
+ preview_height = PREVIEW_SIZE / aspect;
+ }
+ else
+ {
+ preview_width = PREVIEW_SIZE * aspect;
+ preview_height = PREVIEW_SIZE;
+ }
+ }
+ gtk_widget_set_size_request (flame_preview, preview_width, preview_height);
+ gtk_container_add (GTK_CONTAINER (frame), flame_preview);
+ gtk_widget_show (flame_preview);
+ g_signal_connect (flame_preview, "size-allocate",
+ G_CALLBACK (flame_preview_size_allocate), NULL);
+
+ {
+ GtkWidget *vbox;
+ GtkWidget *vbbox;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ vbbox= gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+ gtk_box_set_homogeneous (GTK_BOX (vbbox), FALSE);
+ gtk_box_set_spacing (GTK_BOX (vbbox), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Edit"));
+ gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (edit_callback),
+ dialog);
+
+ load_button = button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (load_callback),
+ NULL);
+
+ save_button = button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_box_pack_start (GTK_BOX (vbbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (save_callback),
+ NULL);
+ }
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), notebook, FALSE, FALSE, 0);
+ gtk_widget_show (notebook);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 12);
+ label = gtk_label_new_with_mnemonic(_("_Rendering"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), box, label);
+ gtk_widget_show (box);
+
+ table = gtk_table_new (6, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 12);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Brightness:"), SCALE_WIDTH, 5,
+ config.cp.brightness,
+ 0, 5, 0.1, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.brightness);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Co_ntrast:"), SCALE_WIDTH, 5,
+ config.cp.contrast,
+ 0, 5, 0.1, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.contrast);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Gamma:"), SCALE_WIDTH, 5,
+ config.cp.gamma,
+ 1, 5, 0.1, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.gamma);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Sample _density:"), SCALE_WIDTH, 5,
+ config.cp.sample_density,
+ 0.1, 20, 1, 5, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.sample_density);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Spa_tial oversample:"), SCALE_WIDTH, 5,
+ config.cp.spatial_oversample,
+ 1, 4, 0.01, 0.1, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &config.cp.spatial_oversample);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
+ _("Spatial _filter radius:"), SCALE_WIDTH, 5,
+ config.cp.spatial_filter_radius,
+ 0, 4, 0.2, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.spatial_filter_radius);
+
+ {
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("Color_map:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_drawable_combo_box_new (cmap_constrain, NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+#if 0
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, BLACK_DRAWABLE,
+ GIMP_INT_STORE_LABEL, _("Black"),
+ -1);
+#endif
+
+ {
+ static const gchar *names[] =
+ {
+ "sunny harvest",
+ "rose",
+ "calcoast09",
+ "klee insula-dulcamara",
+ "ernst anti-pope",
+ "gris josette"
+ };
+ static const gint good[] = { 10, 20, 68, 79, 70, 75 };
+
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (good); i++)
+ {
+ gint value = TABLE_DRAWABLE - good[i];
+
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, value,
+ GIMP_INT_STORE_LABEL, names[i],
+ -1);
+ }
+ }
+
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, GRADIENT_DRAWABLE,
+ GIMP_INT_STORE_LABEL, _("Custom gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ config.cmap_drawable,
+ G_CALLBACK (cmap_callback),
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ cmap_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (cmap_preview, 32, 32);
+
+ gtk_box_pack_end (GTK_BOX (hbox), cmap_preview, FALSE, FALSE, 0);
+ gtk_widget_show (cmap_preview);
+
+ set_cmap_preview ();
+ }
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+
+ label = gtk_label_new_with_mnemonic(_("C_amera"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), table, label);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Zoom:"), SCALE_WIDTH, 0,
+ config.cp.zoom,
+ -4, 4, 0.5, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.zoom);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("_X:"), SCALE_WIDTH, 0,
+ config.cp.center[0],
+ -2, 2, 0.5, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.center[0]);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("_Y:"), SCALE_WIDTH, 0,
+ config.cp.center[1],
+ -2, 2, 0.5, 1, 2,
+ TRUE, 0, 0,
+ NULL, NULL);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &config.cp.center[1]);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (set_flame_preview),
+ NULL);
+
+ set_flame_preview ();
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
diff --git a/plug-ins/flame/flame.h b/plug-ins/flame/flame.h
new file mode 100644
index 0000000..3cdead8
--- /dev/null
+++ b/plug-ins/flame/flame.h
@@ -0,0 +1,21 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "libifs.h"
+#include "rect.h"
+#include "cmap.h"
diff --git a/plug-ins/flame/libifs.c b/plug-ins/flame/libifs.c
new file mode 100644
index 0000000..461ac5c
--- /dev/null
+++ b/plug-ins/flame/libifs.c
@@ -0,0 +1,1487 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h> /* strcmp */
+
+#include "libgimp/gimp.h"
+
+#include "libifs.h"
+
+#define CHOOSE_XFORM_GRAIN 100
+
+static int flam3_random_bit (void);
+static double flam3_random01 (void);
+
+/*
+ * run the function system described by CP forward N generations.
+ * store the n resulting 3 vectors in POINTS. the initial point is passed
+ * in POINTS[0]. ignore the first FUSE iterations.
+ */
+
+void
+iterate (control_point *cp,
+ int n,
+ int fuse,
+ point *points)
+{
+ int i, j, count_large = 0, count_nan = 0;
+ int xform_distrib[CHOOSE_XFORM_GRAIN];
+ double p[3], t, r, dr;
+ p[0] = points[0][0];
+ p[1] = points[0][1];
+ p[2] = points[0][2];
+
+ /*
+ * first, set up xform, which is an array that converts a uniform random
+ * variable into one with the distribution dictated by the density
+ * fields
+ */
+ dr = 0.0;
+ for (i = 0; i < NXFORMS; i++)
+ dr += cp->xform[i].density;
+ dr = dr / CHOOSE_XFORM_GRAIN;
+
+ j = 0;
+ t = cp->xform[0].density;
+ r = 0.0;
+ for (i = 0; i < CHOOSE_XFORM_GRAIN; i++)
+ {
+ while (r >= t)
+ {
+ j++;
+ t += cp->xform[j].density;
+ }
+ xform_distrib[i] = j;
+ r += dr;
+ }
+
+ for (i = -fuse; i < n; i++)
+ {
+ /* FIXME: the following is supported only by gcc and c99 */
+ int fn = xform_distrib[g_random_int_range (0, CHOOSE_XFORM_GRAIN)];
+ double tx, ty, v;
+
+ if (p[0] > 100.0 || p[0] < -100.0 ||
+ p[1] > 100.0 || p[1] < -100.0)
+ count_large++;
+ if (p[0] != p[0])
+ count_nan++;
+
+#define coef cp->xform[fn].c
+#define vari cp->xform[fn].var
+
+ /* first compute the color coord */
+ p[2] = (p[2] + cp->xform[fn].color) / 2.0;
+
+ /* then apply the affine part of the function */
+ tx = coef[0][0] * p[0] + coef[1][0] * p[1] + coef[2][0];
+ ty = coef[0][1] * p[0] + coef[1][1] * p[1] + coef[2][1];
+
+ p[0] = p[1] = 0.0;
+ /* then add in proportional amounts of each of the variations */
+ v = vari[0];
+ if (v > 0.0)
+ {
+ /* linear */
+ double nx, ny;
+ nx = tx;
+ ny = ty;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[1];
+ if (v > 0.0)
+ {
+ /* sinusoidal */
+ double nx, ny;
+ nx = sin (tx);
+ ny = sin (ty);
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[2];
+ if (v > 0.0)
+ {
+ /* spherical */
+ double nx, ny;
+ double r2 = tx * tx + ty * ty + 1e-6;
+ nx = tx / r2;
+ ny = ty / r2;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[3];
+ if (v > 0.0)
+ {
+ /* swirl */
+ double r2 = tx * tx + ty * ty; /* /k here is fun */
+ double c1 = sin (r2);
+ double c2 = cos (r2);
+ double nx = c1 * tx - c2 * ty;
+ double ny = c2 * tx + c1 * ty;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[4];
+ if (v > 0.0)
+ {
+ /* horseshoe */
+ double a, c1, c2, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ a = atan2(tx, ty); /* times k here is fun */
+ else
+ a = 0.0;
+ c1 = sin (a);
+ c2 = cos (a);
+ nx = c1 * tx - c2 * ty;
+ ny = c2 * tx + c1 * ty;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[5];
+ if (v > 0.0)
+ {
+ /* polar */
+ double nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ nx = atan2 (tx, ty) / G_PI;
+ else
+ nx = 0.0;
+
+ ny = sqrt (tx * tx + ty * ty) - 1.0;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[6];
+ if (v > 0.0)
+ {
+ /* bent */
+ double nx, ny;
+ nx = tx;
+ ny = ty;
+ if (nx < 0.0) nx = nx * 2.0;
+ if (ny < 0.0) ny = ny / 2.0;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[7];
+ if (v > 0.0)
+ {
+ /* folded handkerchief */
+ double theta, r2, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2( tx, ty );
+ else
+ theta = 0.0;
+ r2 = sqrt (tx * tx + ty * ty);
+ nx = sin (theta + r2) * r2;
+ ny = cos (theta - r2) * r2;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[8];
+ if (v > 0.0)
+ {
+ /* heart */
+ double theta, r2, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2( tx, ty );
+ else
+ theta = 0.0;
+ r2 = sqrt (tx * tx + ty * ty);
+ theta *= r2;
+ nx = sin (theta) * r2;
+ ny = cos (theta) * -r2;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[9];
+ if (v > 0.0)
+ {
+ /* disc */
+ double theta, r2, nx, ny;
+ if ( tx < -EPS || tx > EPS ||
+ ty < - EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ nx = tx * G_PI;
+ ny = ty * G_PI;
+ r2 = sqrt (nx * nx * ny * ny);
+ p[0] += v * sin(r2) * theta / G_PI;
+ p[1] += v * cos(r2) * theta / G_PI;
+ }
+
+ v = vari[10];
+ if (v > 0.0)
+ {
+ /* spiral */
+ double theta, r2;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2( tx, ty );
+ else
+ theta = 0.0;
+ r2 = sqrt (tx * tx + ty * ty) + 1e-6;
+ p[0] += v * (cos (theta) + sin (r2)) / r2;
+ p[1] += v * (cos (theta) + cos (r2)) / r2;
+ }
+
+ v = vari[11];
+ if (v > 0.0)
+ {
+ /* hyperbolic */
+ double theta, r2;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ r2 = sqrt (tx * tx + ty * ty) + 1e-6;
+ p[0] += v * sin (theta) / r2;
+ p[1] += v * cos (theta) * r2;
+ }
+
+ v = vari[12];
+ if (v > 0.0 )
+ {
+ double theta, r2;
+ /* diamond */
+ if ( tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ r2 = sqrt( tx * tx + ty * ty );
+ p[0] += v * sin (theta) * cos (r2);
+ p[1] += v * cos (theta) * sin (r2);
+ }
+
+ v = vari[13];
+ if (v > 0.0)
+ {
+ /* ex */
+ double theta, r2, n0, n1, m0, m1;
+ if ( tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ r2 = sqrt( tx * tx + ty * ty );
+ n0 = sin(theta + r2);
+ n1 = cos(theta - r2);
+ m0 = n0 * n0 * n0 * r2;
+ m1 = n1 * n1 * n1 * r2;
+ p[0] += v * (m0 + m1);
+ p[1] += v * (m0 - m1);
+ }
+
+ v = vari[14];
+ if ( v > 0.0)
+ {
+ double theta, r2, nx, ny;
+ /* julia */
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ if (flam3_random_bit ())
+ theta += G_PI;
+ r2 = pow (tx * tx + ty * ty, 0.25);
+ nx = r2 * cos (theta);
+ ny = r2 * sin (theta);
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[15];
+ if (v > 0.0)
+ {
+ /* waves */
+ double dx, dy, nx, ny;
+ dx = coef[2][0];
+ dy = coef[2][1];
+ nx = tx + coef[1][0] * sin (ty / ((dx * dx) + EPS));
+ ny = ty + coef[1][1] * sin (tx / ((dy * dy) + EPS));
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[16];
+ if (v > 0.0)
+ {
+ /* fisheye */
+ double theta, r2, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ r2 = sqrt (tx * tx + ty * ty);
+ r2 = 2 * r2 / (r2 + 1);
+ nx = r2 * cos (theta);
+ ny = r2 * sin (theta);
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[17];
+ if (v > 0.0)
+ {
+ /* popcorn */
+ double dx, dy, nx, ny;
+ dx = tan (3 * ty);
+ dy = tan (3 * tx);
+ nx = tx + coef[2][0] * sin (dx);
+ ny = ty + coef[2][1] * sin (dy);
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[18];
+ if (v > 0.0)
+ {
+ /* exponential */
+ double dx, dy, nx, ny;
+ dx = exp (tx - 1.0);
+ dy = G_PI * ty;
+ nx = cos (dy) * dx;
+ ny = sin (dy) * dx;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[19];
+ if (v > 0.0)
+ {
+ /* power */
+ double theta, r2, tsin, tcos, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ tsin = sin (theta);
+ tcos = cos (theta);
+ r2 = sqrt (tx * tx + ty * ty);
+ r2 = pow (r2, tsin);
+ nx = r2 * tcos;
+ ny = r2 * tsin;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[20];
+ if (v > 0.0)
+ {
+ /* cosine */
+ double nx, ny;
+ nx = cos (tx * G_PI) * cosh (ty);
+ ny = -sin (tx * G_PI) * sinh (ty);
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[21];
+ if (v > 0.0)
+ {
+ /* rings */
+ double theta, r2, dx, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0;
+ dx = coef[2][0];
+ dx = dx * dx + EPS;
+ r2 = sqrt (tx * tx + ty * ty);
+ r2 = fmod (r2 + dx, 2 * dx) - dx + r2 * (1 - dx);
+ nx = cos (theta) * r2;
+ ny = sin (theta) * r2;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[22];
+ if (v > 0.0)
+ {
+ /* fan */
+ double theta, r2, dx, dy, dx2, nx, ny;
+ if (tx < -EPS || tx > EPS ||
+ ty < -EPS || ty > EPS)
+ theta = atan2 (tx, ty);
+ else
+ theta = 0.0;
+ dx = coef[2][0];
+ dy = coef[2][1];
+ dx = G_PI * (dx * dx + EPS);
+ dx2 = dx / 2;
+ r2 = sqrt (tx * tx + ty * ty );
+ theta += (fmod (theta + dy, dx) > dx2) ? -dx2: dx2;
+ nx = cos (theta) * r2;
+ ny = sin (theta) * r2;
+ p[0] += v * nx;
+ p[1] += v * ny;
+ }
+
+ v = vari[23];
+ if (v > 0.0)
+ {
+ /* eyefish */
+ double r2;
+ r2 = 2.0 * v / (sqrt(tx * tx + ty * ty) + 1.0);
+ p[0] += r2 * tx;
+ p[1] += r2 * ty;
+ }
+
+ v = vari[24];
+ if (v > 0.0)
+ {
+ /* bubble */
+ double r2;
+ r2 = v / ((tx * tx + ty * ty) / 4 + 1);
+ p[0] += r2 * tx;
+ p[1] += r2 * ty;
+ }
+
+ v = vari[25];
+ if (v > 0.0)
+ {
+ /* cylinder */
+ double nx;
+ nx = sin (tx);
+ p[0] += v * nx;
+ p[1] += v * ty;
+ }
+
+ v = vari[26];
+ if (v > 0.0)
+ {
+ /* noise */
+ double rx, sinr, cosr, nois;
+ rx = flam3_random01 () * 2 * G_PI;
+ sinr = sin (rx);
+ cosr = cos (rx);
+ nois = flam3_random01 ();
+ p[0] += v * nois * tx * cosr;
+ p[1] += v * nois * ty * sinr;
+ }
+
+ v = vari[27];
+ if (v > 0.0)
+ {
+ /* blur */
+ double rx, sinr, cosr, nois;
+ rx = flam3_random01 () * 2 * G_PI;
+ sinr = sin (rx);
+ cosr = cos (rx);
+ nois = flam3_random01 ();
+ p[0] += v * nois * cosr;
+ p[1] += v * nois * sinr;
+ }
+
+ v = vari[28];
+ if (v > 0.0)
+ {
+ /* gaussian */
+ double ang, sina, cosa, r2;
+ ang = flam3_random01 () * 2 * G_PI;
+ sina = sin (ang);
+ cosa = cos (ang);
+ r2 = v * (flam3_random01 () + flam3_random01 () + flam3_random01 () +
+ flam3_random01 () - 2.0);
+ p[0] += r2 * cosa;
+ p[1] += r2 * sina;
+ }
+
+ /* if fuse over, store it */
+ if (i >= 0)
+ {
+ points[i][0] = p[0];
+ points[i][1] = p[1];
+ points[i][2] = p[2];
+ }
+ }
+}
+
+/* args must be non-overlapping */
+void
+mult_matrix (double s1[2][2],
+ double s2[2][2],
+ double d[2][2])
+{
+ d[0][0] = s1[0][0] * s2[0][0] + s1[1][0] * s2[0][1];
+ d[1][0] = s1[0][0] * s2[1][0] + s1[1][0] * s2[1][1];
+ d[0][1] = s1[0][1] * s2[0][0] + s1[1][1] * s2[0][1];
+ d[1][1] = s1[0][1] * s2[1][0] + s1[1][1] * s2[1][1];
+}
+
+static
+double det_matrix (double s[2][2])
+{
+ return s[0][0] * s[1][1] - s[0][1] * s[1][0];
+}
+
+static void
+interpolate_angle (double t,
+ double s,
+ double *v1,
+ double *v2,
+ double *v3,
+ int tie,
+ int cross)
+{
+ double x = *v1;
+ double y = *v2;
+ double d;
+ static double lastx, lasty;
+
+ /* take the shorter way around the circle... */
+ if (x > y)
+ {
+ d = x - y;
+ if (d > G_PI + EPS ||
+ (d > G_PI - EPS && tie))
+ y += 2 * G_PI;
+ }
+ else
+ {
+ d = y - x;
+ if (d > G_PI + EPS ||
+ (d > G_PI - EPS && tie))
+ x += 2 * G_PI;
+ }
+ /* unless we are supposed to avoid crossing */
+ if (cross)
+ {
+ if (lastx > x)
+ {
+ if (lasty < y)
+ y -= 2 * G_PI;
+ }
+ else
+ {
+ if (lasty > y)
+ y += 2 * G_PI;
+ }
+ }
+ else
+ {
+ lastx = x;
+ lasty = y;
+ }
+
+ *v3 = s * x + t * y;
+}
+
+static void
+interpolate_complex (double t,
+ double s,
+ double *r1,
+ double *r2,
+ double *r3,
+ int flip,
+ int tie,
+ int cross)
+{
+ double c1[2], c2[2], c3[2];
+ double a1, a2, a3, d1, d2, d3;
+
+ c1[0] = r1[0];
+ c1[1] = r1[1];
+ c2[0] = r2[0];
+ c2[1] = r2[1];
+ if (flip)
+ {
+ double t = c1[0];
+ c1[0] = c1[1];
+ c1[1] = t;
+ t = c2[0];
+ c2[0] = c2[1];
+ c2[1] = t;
+ }
+
+ /* convert to log space */
+ a1 = atan2 (c1[1], c1[0]);
+ a2 = atan2 (c2[1], c2[0]);
+ d1 = 0.5 * log (c1[0] * c1[0] + c1[1] * c1[1]);
+ d2 = 0.5 * log (c2[0] * c2[0] + c2[1] * c2[1]);
+
+ /* interpolate linearly */
+ interpolate_angle (t, s, &a1, &a2, &a3, tie, cross);
+ d3 = s * d1 + t * d2;
+
+ /* convert back */
+ d3 = exp (d3);
+ c3[0] = cos (a3) * d3;
+ c3[1] = sin (a3) * d3;
+
+ if (flip)
+ {
+ r3[1] = c3[0];
+ r3[0] = c3[1];
+ }
+ else
+ {
+ r3[0] = c3[0];
+ r3[1] = c3[1];
+ }
+}
+
+static void
+interpolate_matrix (double t,
+ double m1[3][2],
+ double m2[3][2],
+ double m3[3][2])
+{
+ double s = 1.0 - t;
+
+ interpolate_complex (t, s, &m1[0][0], &m2[0][0], &m3[0][0], 0, 0, 0);
+ interpolate_complex (t, s, &m1[1][0], &m2[1][0], &m3[1][0], 1, 1, 0);
+
+ /* handle the translation part of the xform linearly */
+ m3[2][0] = s * m1[2][0] + t * m2[2][0];
+ m3[2][1] = s * m1[2][1] + t * m2[2][1];
+}
+
+#define INTERP(x) result->x = c0 * cps[i1].x + c1 * cps[i2].x
+
+/*
+ * create a control point that interpolates between the control points
+ * passed in CPS. for now just do linear. in the future, add control
+ * point types and other things to the cps. CPS must be sorted by time.
+ */
+void
+interpolate (control_point cps[],
+ int ncps,
+ double time,
+ control_point *result)
+{
+ int i, j, i1, i2;
+ double c0, c1, t;
+
+ g_return_if_fail (ncps > 0);
+
+ if (ncps == 1)
+ {
+ *result = cps[0];
+ return;
+ }
+ if (cps[0].time >= time)
+ {
+ i1 = 0;
+ i2 = 1;
+ }
+ else if (cps[ncps - 1].time <= time)
+ {
+ i1 = ncps - 2;
+ i2 = ncps - 1;
+ }
+ else
+ {
+ i1 = 0;
+ while (i1 < ncps && cps[i1].time < time)
+ i1++;
+ i1--;
+ i2 = i1 + 1;
+
+ if (i2 == ncps ||
+ (time - cps[i1].time > -1e-7 &&
+ time - cps[i1].time < 1e-7))
+ {
+ *result = cps[i1];
+ return;
+ }
+ }
+
+ c0 = (cps[i2].time - time) / (cps[i2].time - cps[i1].time);
+ c1 = 1.0 - c0;
+
+ result->time = time;
+
+ if (cps[i1].cmap_inter)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ double spread = 0.15;
+ double d0, d1, e0, e1, c = 2 * G_PI * i / 256.0;
+ c = cos(c * cps[i1].cmap_inter) + 4.0 * c1 - 2.0;
+ if (c > spread) c = spread;
+ if (c < -spread) c = -spread;
+ d1 = (c + spread) * 0.5 / spread;
+ d0 = 1.0 - d1;
+ e0 = (d0 < 0.5) ? (d0 * 2) : (d1 * 2);
+ e1 = 1.0 - e0;
+ for (j = 0; j < 3; j++)
+ {
+ result->cmap[i][j] = (d0 * cps[i1].cmap[i][j] +
+ d1 * cps[i2].cmap[i][j]);
+#define bright_peak 2.0
+ result->cmap[i][j] = (e1 * result->cmap[i][j] +
+ e0 * 1.0);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < 256; i++)
+ {
+ double t[3], s[3];
+ rgb2hsv (cps[i1].cmap[i], s);
+ rgb2hsv (cps[i2].cmap[i], t);
+ for (j = 0; j < 3; j++)
+ t[j] = c0 * s[j] + c1 * t[j];
+ hsv2rgb (t, result->cmap[i]);
+ }
+ }
+
+ result->cmap_index = -1;
+ INTERP(brightness);
+ INTERP(contrast);
+ INTERP(gamma);
+ INTERP(width);
+ INTERP(height);
+ INTERP(spatial_oversample);
+ INTERP(center[0]);
+ INTERP(center[1]);
+ INTERP(pixels_per_unit);
+ INTERP(spatial_filter_radius);
+ INTERP(sample_density);
+ INTERP(zoom);
+ INTERP(nbatches);
+ INTERP(white_level);
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 2; j++)
+ {
+ INTERP(pulse[i][j]);
+ INTERP(wiggle[i][j]);
+ }
+
+ for (i = 0; i < NXFORMS; i++)
+ {
+ double r;
+ double rh_time;
+
+ INTERP(xform[i].density);
+ if (result->xform[i].density > 0)
+ result->xform[i].density = 1.0;
+ INTERP(xform[i].color);
+ for (j = 0; j < NVARS; j++)
+ INTERP(xform[i].var[j]);
+ t = 0.0;
+ for (j = 0; j < NVARS; j++)
+ t += result->xform[i].var[j];
+ t = 1.0 / t;
+ for (j = 0; j < NVARS; j++)
+ result->xform[i].var[j] *= t;
+
+ interpolate_matrix(c1, cps[i1].xform[i].c, cps[i2].xform[i].c,
+ result->xform[i].c);
+
+ rh_time = time * 2 * G_PI / (60.0 * 30.0);
+
+ /* apply pulse factor. */
+ r = 1.0;
+ for (j = 0; j < 2; j++)
+ r += result->pulse[j][0] * sin(result->pulse[j][1] * rh_time);
+ for (j = 0; j < 3; j++)
+ {
+ result->xform[i].c[j][0] *= r;
+ result->xform[i].c[j][1] *= r;
+ }
+
+ /* apply wiggle factor */
+ for (j = 0; j < 2; j++)
+ {
+ double tt = result->wiggle[j][1] * rh_time;
+ double m = result->wiggle[j][0];
+ result->xform[i].c[0][0] += m * cos(tt);
+ result->xform[i].c[1][0] += m * -sin(tt);
+ result->xform[i].c[0][1] += m * sin(tt);
+ result->xform[i].c[1][1] += m * cos(tt);
+ }
+ } /* for i */
+}
+
+
+
+/*
+ * split a string passed in ss into tokens on whitespace.
+ * # comments to end of line. ; terminates the record
+ */
+void
+tokenize (char **ss,
+ char *argv[],
+ int *argc)
+{
+ char *s = *ss;
+ int i = 0, state = 0;
+ gint len = 0;
+
+ len = strlen (s);
+ while (*s != ';' && len > 0)
+ {
+ char c = *s;
+ switch (state)
+ {
+ case 0:
+ if (c == '#')
+ state = 2;
+ else if (!g_ascii_isspace (c))
+ {
+ argv[i] = s;
+ i++;
+ state = 1;
+ }
+ break;
+ case 1:
+ if (g_ascii_isspace (c))
+ {
+ *s = 0;
+ state = 0;
+ }
+ break;
+ case 2:
+ if (c == '\n')
+ state = 0;
+ break;
+ }
+ s++;
+ len--;
+ }
+ *s = 0;
+ *ss = s + 1;
+ *argc = i;
+}
+
+static int
+compare_xforms (const void *va,
+ const void *vb)
+{
+ double aa[2][2];
+ double bb[2][2];
+ double ad, bd;
+ const xform *a = va;
+ const xform *b = vb;
+
+ aa[0][0] = a->c[0][0];
+ aa[0][1] = a->c[0][1];
+ aa[1][0] = a->c[1][0];
+ aa[1][1] = a->c[1][1];
+ bb[0][0] = b->c[0][0];
+ bb[0][1] = b->c[0][1];
+ bb[1][0] = b->c[1][0];
+ bb[1][1] = b->c[1][1];
+ ad = det_matrix (aa);
+ bd = det_matrix (bb);
+ if (ad < bd)
+ return -1;
+ if (ad > bd)
+ return 1;
+ return 0;
+}
+
+#define MAXARGS 1000
+#define streql(x,y) (!strcmp(x,y))
+
+/*
+ * given a pointer to a string SS, fill fields of a control point CP.
+ */
+
+void
+parse_control_point (char **ss,
+ control_point *cp)
+{
+ char *argv[MAXARGS];
+ int argc, i, j;
+ gint64 xf_index = 0;
+ gint parse_errors = 0;
+ double *slot = NULL, t;
+
+ for (i = 0; i < NXFORMS; i++)
+ {
+ cp->xform[i].density = 0.0;
+ cp->xform[i].color = (i == 0);
+ cp->xform[i].var[0] = 1.0;
+ for (j = 1; j < NVARS; j++)
+ cp->xform[i].var[j] = 0.0;
+ cp->xform[i].c[0][0] = 1.0;
+ cp->xform[i].c[0][1] = 0.0;
+ cp->xform[i].c[1][0] = 0.0;
+ cp->xform[i].c[1][1] = 1.0;
+ cp->xform[i].c[2][0] = 0.0;
+ cp->xform[i].c[2][1] = 0.0;
+ }
+ for (j = 0; j < 2; j++)
+ {
+ cp->pulse[j][0] = 0.0;
+ cp->pulse[j][1] = 60.0;
+ cp->wiggle[j][0] = 0.0;
+ cp->wiggle[j][1] = 60.0;
+ }
+
+ tokenize (ss, argv, &argc);
+
+ i = 0;
+ while (i < argc)
+ {
+ gint itoken;
+
+ itoken = i;
+ if (i < argc)
+ {
+ /* First value belonging to token. */
+ i++;
+ }
+ else
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+
+ if (streql ("xform", argv[itoken]))
+ {
+ if (! g_ascii_string_to_signed (argv[i++], 10, 0, NXFORMS-1, &xf_index, NULL))
+ {
+ g_printerr ("Invalid xform index '%s'\n", argv[i-1]);
+ parse_errors++;
+ xf_index = 0;
+ }
+ }
+ else if (streql ("density", argv[itoken]))
+ {
+ cp->xform[xf_index].density = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("color", argv[itoken]))
+ {
+ cp->xform[xf_index].color = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("coefs", argv[itoken]))
+ {
+ /* We need 6 coef values and we know are at the first */
+ if (i + 5 >= argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ slot = cp->xform[xf_index].c[0];
+ for (j = 0; j < 6; j++)
+ {
+ *slot++ = g_strtod (argv[i++], NULL);
+ }
+ cp->xform[xf_index].density = 1.0;
+ }
+ else if (streql ("var", argv[itoken]))
+ {
+ /* We need NVARS var values and we know are at the first */
+ if (i + NVARS > argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ slot = cp->xform[xf_index].var;
+ for (j = 0; j < NVARS; j++)
+ {
+ *slot++ = g_strtod (argv[i++], NULL);
+ }
+ }
+ else if (streql ("time", argv[itoken]))
+ {
+ cp->time = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("brightness", argv[itoken]))
+ {
+ cp->brightness = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("contrast", argv[itoken]))
+ {
+ cp->contrast = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("gamma", argv[itoken]))
+ {
+ cp->gamma = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("zoom", argv[itoken]))
+ {
+ cp->zoom = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("image_size", argv[itoken]))
+ {
+ gint64 w, h;
+
+ /* We need 2 values and we know are at the first */
+ if (i + 1 >= argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ if (! g_ascii_string_to_signed (argv[i++], 10, 1, GIMP_MAX_IMAGE_SIZE, &w, NULL))
+ {
+ g_printerr ("Ignoring invalid image width '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else if (! g_ascii_string_to_signed (argv[i++], 10, 1, GIMP_MAX_IMAGE_SIZE, &h, NULL))
+ {
+ g_printerr ("Ignoring invalid image_size heigth '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->width = w;
+ cp->height = h;
+ }
+ }
+ else if (streql ("center", argv[itoken]))
+ {
+ /* We need 2 values and we know are at the first */
+ if (i + 1 >= argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ cp->center[0] = g_strtod (argv[i++], NULL);
+ cp->center[1] = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("pixels_per_unit", argv[itoken]))
+ {
+ cp->pixels_per_unit = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("pulse", argv[itoken]))
+ {
+ /* We need 4 values and we know are at the first */
+ if (i + 3 >= argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ slot = &cp->pulse[0][0];
+ for (j = 0; j < 4; j++)
+ {
+ *slot++ = g_strtod (argv[i++], NULL);
+ }
+ }
+ else if (streql ("wiggle", argv[itoken]))
+ {
+ /* We need 4 values and we know are at the first */
+ if (i + 3 >= argc)
+ {
+ g_printerr ("Not enough parameters. File may be corrupt!\n");
+ parse_errors++;
+ break;
+ }
+ slot = &cp->wiggle[0][0];
+ for (j = 0; j < 4; j++)
+ {
+ *slot++ = g_strtod (argv[i++], NULL);
+ }
+ }
+ else if (streql ("spatial_oversample", argv[itoken]))
+ {
+ gint64 oversample;
+
+ /* Values in the gui seem to be between 1 and 4 */
+ if (! g_ascii_string_to_signed (argv[i++], 10, 1, 4, &oversample, NULL))
+ {
+ g_printerr ("Ignoring invalid spatial oversample value '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->spatial_oversample = oversample;
+ }
+ }
+ else if (streql ("spatial_filter_radius", argv[itoken]))
+ {
+ cp->spatial_filter_radius = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("sample_density", argv[itoken]))
+ {
+ cp->sample_density = g_strtod (argv[i++], NULL);
+ }
+ else if (streql ("nbatches", argv[itoken]))
+ {
+ gint64 nbatches;
+
+ /* Not sure what the maximum should be. It always seems to be set to 1. */
+ if (! g_ascii_string_to_signed (argv[i++], 10, 0, 2, &nbatches, NULL))
+ {
+ g_printerr ("Ignoring invalid nbatches value '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->nbatches = nbatches;
+ }
+ }
+ else if (streql ("white_level", argv[itoken]))
+ {
+ gint64 wl;
+
+ if (! g_ascii_string_to_signed (argv[i++], 10, 0, 255, &wl, NULL))
+ {
+ g_printerr ("Ignoring invalid white level value '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->white_level = wl;
+ }
+ }
+ else if (streql ("cmap", argv[itoken]))
+ {
+ gint64 cmi;
+
+ /* -1 = random */
+ if (! g_ascii_string_to_signed (argv[i++], 10, -1, 255, &cmi, NULL))
+ {
+ g_printerr ("Ignoring invalid color map value '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->cmap_index = cmi;
+ }
+ }
+ else if (streql ("cmap_inter", argv[itoken]))
+ {
+ gint64 cmi;
+
+ /* 0 or 1 */
+ if (! g_ascii_string_to_signed (argv[i++], 10, 0, 1, &cmi, NULL))
+ {
+ g_printerr ("Ignoring invalid color interpolate value '%s'\n", argv[i-1]);
+ parse_errors++;
+ }
+ else
+ {
+ cp->cmap_inter = cmi;
+ }
+ }
+ else
+ {
+ g_printerr ("Invalid token '%s'. File may be corrupt!\n", argv[itoken]);
+ parse_errors++;
+ }
+ }
+
+ if (parse_errors > 0)
+ g_warning ("Input file contains %d errors. File may be corrupt!", parse_errors);
+
+ for (i = 0; i < NXFORMS; i++)
+ {
+ t = 0.0;
+ for (j = 0; j < NVARS; j++)
+ t += cp->xform[i].var[j];
+ t = 1.0 / t;
+ for (j = 0; j < NVARS; j++)
+ cp->xform[i].var[j] *= t;
+ }
+ qsort ((char *) cp->xform, NXFORMS, sizeof(xform), compare_xforms);
+}
+
+void
+print_control_point (FILE *f,
+ control_point *cp,
+ int quote)
+{
+ int i, j;
+ char *q = quote ? "# " : "";
+ fprintf (f, "%stime %g\n", q, cp->time);
+ if (cp->cmap_index != -1)
+ fprintf (f, "%scmap %d\n", q, cp->cmap_index);
+ fprintf (f, "%simage_size %d %d center %g %g pixels_per_unit %g\n",
+ q, cp->width, cp->height, cp->center[0], cp->center[1],
+ cp->pixels_per_unit);
+ fprintf (f, "%sspatial_oversample %d spatial_filter_radius %g",
+ q, cp->spatial_oversample, cp->spatial_filter_radius);
+ fprintf (f, " sample_density %g\n", cp->sample_density);
+ fprintf (f, "%snbatches %d white_level %d\n",
+ q, cp->nbatches, cp->white_level);
+ fprintf (f, "%sbrightness %g gamma %g cmap_inter %d\n",
+ q, cp->brightness, cp->gamma, cp->cmap_inter);
+
+ for (i = 0; i < NXFORMS; i++)
+ if (cp->xform[i].density > 0.0)
+ {
+ fprintf (f, "%sxform %d density %g color %g\n",
+ q, i, cp->xform[i].density, cp->xform[i].color);
+ fprintf (f, "%svar", q);
+ for (j = 0; j < NVARS; j++)
+ fprintf (f, " %g", cp->xform[i].var[j]);
+ fprintf (f, "\n%scoefs", q);
+ for (j = 0; j < 3; j++)
+ fprintf (f, " %g %g", cp->xform[i].c[j][0], cp->xform[i].c[j][1]);
+ fprintf (f, "\n");
+ }
+ fprintf (f, "%s;\n", q);
+}
+
+/* returns a uniform variable from 0 to 1 */
+double
+random_uniform01 (void)
+{
+ return g_random_double ();
+}
+
+double random_uniform11 (void)
+{
+ return g_random_double_range (-1, 1);
+}
+
+/* returns a mean 0 variance 1 random variable
+ see numerical recipes p 217 */
+double random_gaussian(void)
+{
+ static int iset = 0;
+ static double gset;
+ double fac, r, v1, v2;
+
+ if (iset == 0)
+ {
+ do
+ {
+ v1 = random_uniform11 ();
+ v2 = random_uniform11 ();
+ r = v1 * v1 + v2 * v2;
+ }
+ while (r >= 1.0 || r == 0.0);
+ fac = sqrt (-2.0 * log (r) / r);
+ gset = v1 * fac;
+ iset = 1;
+ return v2 * fac;
+ }
+ iset = 0;
+ return gset;
+}
+
+void
+copy_variation (control_point *cp0,
+ control_point *cp1)
+{
+ int i, j;
+ for (i = 0; i < NXFORMS; i++)
+ {
+ for (j = 0; j < NVARS; j++)
+ cp0->xform[i].var[j] = cp1->xform[i].var[j];
+ }
+}
+
+#define random_distrib(v) ((v)[g_random_int_range (0, vlen(v))])
+
+void
+random_control_point (control_point *cp,
+ int ivar)
+{
+ int i, nxforms, var;
+ static int xform_distrib[] =
+ {
+ 2, 2, 2,
+ 3, 3, 3,
+ 4, 4,
+ 5
+ };
+ static int var_distrib[] =
+ {
+ -1, -1, -1,
+ 0, 0, 0, 0,
+ 1, 1, 1,
+ 2, 2, 2,
+ 3, 3,
+ 4, 4,
+ 5
+ };
+
+ static int mixed_var_distrib[] =
+ {
+ 0, 0, 0,
+ 1, 1, 1,
+ 2, 2, 2,
+ 3, 3,
+ 4, 4,
+ 5, 5
+ };
+
+ get_cmap (cmap_random, cp->cmap, 256);
+ cp->time = 0.0;
+ nxforms = random_distrib (xform_distrib);
+ var = (0 > ivar) ?
+ random_distrib(var_distrib) :
+ ivar;
+ for (i = 0; i < nxforms; i++)
+ {
+ int j, k;
+ cp->xform[i].density = 1.0 / nxforms;
+ cp->xform[i].color = i == 0;
+ for (j = 0; j < 3; j++)
+ for (k = 0; k < 2; k++)
+ cp->xform[i].c[j][k] = random_uniform11();
+ for (j = 0; j < NVARS; j++)
+ cp->xform[i].var[j] = 0.0;
+ if (var >= 0)
+ cp->xform[i].var[var] = 1.0;
+ else
+ cp->xform[i].var[random_distrib(mixed_var_distrib)] = 1.0;
+
+ }
+ for (; i < NXFORMS; i++)
+ cp->xform[i].density = 0.0;
+}
+
+/*
+ * find a 2d bounding box that does not enclose eps of the fractal density
+ * in each compass direction. works by binary search.
+ * this is stupid, it should just use the find nth smallest algorithm.
+ */
+void
+estimate_bounding_box (control_point *cp,
+ double eps,
+ double *bmin,
+ double *bmax)
+{
+ int i, j, batch = (eps == 0.0) ? 10000 : 10.0/eps;
+ int low_target = batch * eps;
+ int high_target = batch - low_target;
+ point min, max, delta;
+ point *points = g_malloc0 (sizeof (point) * batch);
+
+ iterate (cp, batch, 20, points);
+
+ min[0] = min[1] = 1e10;
+ max[0] = max[1] = -1e10;
+
+ for (i = 0; i < batch; i++)
+ {
+ if (points[i][0] < min[0]) min[0] = points[i][0];
+ if (points[i][1] < min[1]) min[1] = points[i][1];
+ if (points[i][0] > max[0]) max[0] = points[i][0];
+ if (points[i][1] > max[1]) max[1] = points[i][1];
+ }
+
+ if (low_target == 0)
+ {
+ bmin[0] = min[0];
+ bmin[1] = min[1];
+ bmax[0] = max[0];
+ bmax[1] = max[1];
+ return;
+ }
+
+ delta[0] = (max[0] - min[0]) * 0.25;
+ delta[1] = (max[1] - min[1]) * 0.25;
+
+ bmax[0] = bmin[0] = min[0] + 2.0 * delta[0];
+ bmax[1] = bmin[1] = min[1] + 2.0 * delta[1];
+
+ for (i = 0; i < 14; i++)
+ {
+ int n, s, e, w;
+ n = s = e = w = 0;
+ for (j = 0; j < batch; j++)
+ {
+ if (points[j][0] < bmin[0]) n++;
+ if (points[j][0] > bmax[0]) s++;
+ if (points[j][1] < bmin[1]) w++;
+ if (points[j][1] > bmax[1]) e++;
+ }
+ bmin[0] += (n < low_target) ? delta[0] : -delta[0];
+ bmax[0] += (s < high_target) ? delta[0] : -delta[0];
+ bmin[1] += (w < low_target) ? delta[1] : -delta[1];
+ bmax[1] += (e < high_target) ? delta[1] : -delta[1];
+ delta[0] = delta[0] / 2.0;
+ delta[1] = delta[1] / 2.0;
+ }
+ g_free (points);
+}
+
+/* this has serious flaws in it */
+
+double
+standard_metric (control_point *cp1,
+ control_point *cp2)
+{
+ int i, j, k;
+ double t;
+ double dist = 0.0;
+
+ for (i = 0; i < NXFORMS; i++)
+ {
+ double var_dist = 0.0;
+ double coef_dist = 0.0;
+ for (j = 0; j < NVARS; j++)
+ {
+ t = cp1->xform[i].var[j] - cp2->xform[i].var[j];
+ var_dist += t * t;
+ }
+ for (j = 0; j < 3; j++)
+ for (k = 0; k < 2; k++)
+ {
+ t = cp1->xform[i].c[j][k] - cp2->xform[i].c[j][k];
+ coef_dist += t *t;
+ }
+ /* weight them equally for now. */
+ dist += var_dist + coef_dist;
+ }
+ return dist;
+}
+
+static int
+flam3_random_bit (void)
+{
+ static int n = 0;
+ static int l;
+ if (n == 0)
+ {
+ l = g_random_int ();
+ n = 20;
+ }
+ else
+ {
+ l = l >> 1;
+ n--;
+ }
+ return l & 1;
+}
+
+static double
+flam3_random01 (void)
+{
+ return (g_random_int () & 0xfffffff) / (double) 0xfffffff;
+}
diff --git a/plug-ins/flame/libifs.h b/plug-ins/flame/libifs.h
new file mode 100644
index 0000000..3cccf13
--- /dev/null
+++ b/plug-ins/flame/libifs.h
@@ -0,0 +1,87 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef libifs_included
+#define libifs_included
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "cmap.h"
+
+#define EPS (1e-10)
+
+#define variation_random (-1)
+
+#define NVARS 29
+#define NXFORMS 6
+
+typedef double point[3];
+
+typedef struct {
+ double var[NVARS]; /* normalized interp coefs between variations */
+ double c[3][2]; /* the coefs to the affine part of the function */
+ double density; /* prob is this function is chosen. 0 - 1 */
+ double color; /* color coord for this function. 0 - 1 */
+} xform;
+
+typedef struct {
+ xform xform[NXFORMS];
+ clrmap cmap;
+ double time;
+ int cmap_index;
+ double brightness; /* 1.0 = normal */
+ double contrast; /* 1.0 = normal */
+ double gamma;
+ int width, height; /* of the final image */
+ int spatial_oversample;
+ double center[2]; /* camera center */
+ double zoom; /* effects ppu and sample density */
+ double pixels_per_unit; /* and scale */
+ double spatial_filter_radius; /* variance of gaussian */
+ double sample_density; /* samples per pixel (not bucket) */
+ /* in order to motion blur more accurately we compute the logs of the
+ sample density many times and average the results. we interpolate
+ only this many times. */
+ int nbatches;
+ /* this much color resolution. but making it too high induces clipping */
+ int white_level;
+ int cmap_inter; /* if this is true, then color map interpolates one entry
+ at a time with a bright edge */
+ double pulse[2][2]; /* [i][0]=magnitude [i][1]=frequency */
+ double wiggle[2][2]; /* frequency is /minute, assuming 30 frames/s */
+} control_point;
+
+
+
+extern void iterate(control_point *cp, int n, int fuse, point points[]);
+extern void interpolate(control_point cps[], int ncps, double time, control_point *result);
+extern void tokenize(char **ss, char *argv[], int *argc);
+extern void print_control_point(FILE *f, control_point *cp, int quote);
+extern void random_control_point(control_point *cp, int ivar);
+extern void parse_control_point(char **ss, control_point *cp);
+extern void estimate_bounding_box(control_point *cp, double eps, double *bmin, double *bmax);
+extern double standard_metric(control_point *cp1, control_point *cp2);
+extern double random_uniform01(void);
+extern double random_uniform11(void);
+extern double random_gaussian(void);
+extern void mult_matrix(double s1[2][2], double s2[2][2], double d[2][2]);
+void copy_variation(control_point *cp0, control_point *cp1);
+#endif
diff --git a/plug-ins/flame/rect.c b/plug-ins/flame/rect.c
new file mode 100644
index 0000000..0ff0f5f
--- /dev/null
+++ b/plug-ins/flame/rect.c
@@ -0,0 +1,398 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "rect.h"
+
+#include <string.h>
+
+#include "libgimp/gimp.h"
+
+/* for batch
+ * interpolate
+ * compute colormap
+ * for subbatch
+ * compute samples
+ * buckets += cmap[samples]
+ * accum += time_filter[batch] * log(buckets)
+ * image = filter(accum)
+ */
+
+
+typedef short bucket[4];
+
+/* if you use longs instead of shorts, you
+ get higher quality, and spend more memory */
+
+#if 1
+typedef short accum_t;
+#define MAXBUCKET (1<<14)
+#define SUB_BATCH_SIZE 10000
+#else
+typedef long accum_t;
+#define MAXBUCKET (1<<30)
+#define SUB_BATCH_SIZE 10000
+#endif
+
+typedef accum_t abucket[4];
+
+
+
+/* allow this many iterations for settling into attractor */
+#define FUSE 15
+
+/* clamp spatial filter to zero at this std dev (2.5 ~= 0.0125) */
+#define FILTER_CUTOFF 2.5
+
+/* should be MAXBUCKET / (OVERSAMPLE^2) */
+#define PREFILTER_WHITE (MAXBUCKET>>4)
+
+
+#define bump_no_overflow(dest, delta, type) { \
+ type tt_ = dest + delta; \
+ if (tt_ > dest) dest = tt_; \
+}
+
+/* sum of entries of vector to 1 */
+static void
+normalize_vector(double *v,
+ int n)
+{
+ double t = 0.0;
+ int i;
+ for (i = 0; i < n; i++)
+ t += v[i];
+ t = 1.0 / t;
+ for (i = 0; i < n; i++)
+ v[i] *= t;
+}
+
+void
+render_rectangle (frame_spec *spec,
+ unsigned char *out,
+ int out_width,
+ int field,
+ int nchan,
+ int progress(double))
+{
+ int i, j, k, nsamples, nbuckets, batch_size, batch_num, sub_batch;
+ bucket *buckets;
+ abucket *accumulate;
+ point *points;
+ double *filter, *temporal_filter, *temporal_deltas;
+ double bounds[4], size[2], ppux, ppuy;
+ int image_width, image_height; /* size of the image to produce */
+ int width, height; /* size of histogram */
+ int filter_width;
+ int oversample = spec->cps[0].spatial_oversample;
+ int nbatches = spec->cps[0].nbatches;
+ bucket cmap[CMAP_SIZE];
+ int gutter_width;
+ int sbc;
+
+ image_width = spec->cps[0].width;
+ if (field)
+ {
+ image_height = spec->cps[0].height / 2;
+ if (field == field_odd)
+ out += nchan * out_width;
+ out_width *= 2;
+ }
+ else
+ image_height = spec->cps[0].height;
+
+ if (1)
+ {
+ filter_width = (2.0 * FILTER_CUTOFF * oversample *
+ spec->cps[0].spatial_filter_radius);
+ /* make sure it has same parity as oversample */
+ if ((filter_width ^ oversample) & 1)
+ filter_width++;
+
+ filter = g_malloc (sizeof (double) * filter_width * filter_width);
+ /* fill in the coefs */
+ for (i = 0; i < filter_width; i++)
+ for (j = 0; j < filter_width; j++)
+ {
+ double ii = ((2.0 * i + 1.0) / filter_width - 1.0) * FILTER_CUTOFF;
+ double jj = ((2.0 * j + 1.0) / filter_width - 1.0) * FILTER_CUTOFF;
+ if (field)
+ jj *= 2.0;
+ filter[i + j * filter_width] = exp(-2.0 * (ii * ii + jj * jj));
+ }
+ normalize_vector(filter, filter_width * filter_width);
+ }
+ temporal_filter = g_malloc (sizeof (double) * nbatches);
+ temporal_deltas = g_malloc (sizeof (double) * nbatches);
+ if (nbatches > 1)
+ {
+ double t;
+ /* fill in the coefs */
+ for (i = 0; i < nbatches; i++)
+ {
+ t = temporal_deltas[i] = (2.0 * ((double) i / (nbatches - 1)) - 1.0)
+ * spec->temporal_filter_radius;
+ temporal_filter[i] = exp(-2.0 * t * t);
+ }
+ normalize_vector(temporal_filter, nbatches);
+ }
+ else
+ {
+ temporal_filter[0] = 1.0;
+ temporal_deltas[0] = 0.0;
+ }
+
+ /* the number of additional rows of buckets we put at the edge so
+ that the filter doesn't go off the edge */
+ gutter_width = (filter_width - oversample) / 2;
+ height = oversample * image_height + 2 * gutter_width;
+ width = oversample * image_width + 2 * gutter_width;
+
+ nbuckets = width * height;
+ if (1)
+ {
+ static char *last_block = NULL;
+ static int last_block_size = 0;
+ int memory_rqd = (sizeof (bucket) * nbuckets +
+ sizeof (abucket) * nbuckets +
+ sizeof (point) * SUB_BATCH_SIZE);
+ if (memory_rqd > last_block_size)
+ {
+ if (last_block != NULL)
+ free (last_block);
+ last_block = g_try_malloc (memory_rqd);
+ if (last_block == NULL)
+ {
+ g_printerr ("render_rectangle: cannot malloc %d bytes.\n",
+ memory_rqd);
+ exit (1);
+ }
+ last_block_size = memory_rqd;
+ }
+ buckets = (bucket *) last_block;
+ accumulate = (abucket *) (last_block + sizeof (bucket) * nbuckets);
+ points = (point *) (last_block + (sizeof (bucket) + sizeof (abucket)) * nbuckets);
+ }
+
+ memset ((char *) accumulate, 0, sizeof (abucket) * nbuckets);
+ for (batch_num = 0; batch_num < nbatches; batch_num++)
+ {
+ double batch_time;
+ double sample_density;
+ control_point cp;
+ memset ((char *) buckets, 0, sizeof (bucket) * nbuckets);
+ batch_time = spec->time + temporal_deltas[batch_num];
+
+ /* interpolate and get a control point */
+ interpolate (spec->cps, spec->ncps, batch_time, &cp);
+
+ /* compute the colormap entries. the input colormap is 256 long with
+ entries from 0 to 1.0 */
+ for (j = 0; j < CMAP_SIZE; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+#if 1
+ cmap[j][k] = (int) (cp.cmap[(j * 256) / CMAP_SIZE][k] *
+ cp.white_level);
+#else
+ /* monochrome if you don't have any cmaps */
+ cmap[j][k] = cp.white_level;
+#endif
+ }
+ cmap[j][3] = cp.white_level;
+ }
+ /* compute camera */
+ if (1)
+ {
+ double t0, t1, shift = 0.0, corner0, corner1;
+ double scale;
+
+ scale = pow (2.0, cp.zoom);
+ sample_density = cp.sample_density * scale * scale;
+
+ ppux = cp.pixels_per_unit * scale;
+ ppuy = field ? (ppux / 2.0) : ppux;
+ switch (field)
+ {
+ case field_both:
+ shift = 0.0;
+ break;
+ case field_even:
+ shift = -0.5;
+ break;
+ case field_odd:
+ shift = 0.5;
+ break;
+ }
+ shift = shift / ppux;
+ t0 = (double) gutter_width / (oversample * ppux);
+ t1 = (double) gutter_width / (oversample * ppuy);
+ corner0 = cp.center[0] - image_width / ppux / 2.0;
+ corner1 = cp.center[1] - image_height / ppuy / 2.0;
+ bounds[0] = corner0 - t0;
+ bounds[1] = corner1 - t1 + shift;
+ bounds[2] = corner0 + image_width / ppux + t0;
+ bounds[3] = corner1 + image_height / ppuy + t1 + shift;
+ size[0] = 1.0 / (bounds[2] - bounds[0]);
+ size[1] = 1.0 / (bounds[3] - bounds[1]);
+ }
+ nsamples = (int) (sample_density * nbuckets /
+ (oversample * oversample));
+ batch_size = nsamples / cp.nbatches;
+
+ sbc = 0;
+ for (sub_batch = 0;
+ sub_batch < batch_size;
+ sub_batch += SUB_BATCH_SIZE)
+ {
+ if (progress && (sbc++ % 32) == 0)
+ (*progress)(0.5 * sub_batch / (double) batch_size);
+ /* generate a sub_batch_size worth of samples */
+ points[0][0] = random_uniform11 ();
+ points[0][1] = random_uniform11 ();
+ points[0][2] = random_uniform01 ();
+ iterate (&cp, SUB_BATCH_SIZE, FUSE, points);
+
+ /* merge them into buckets, looking up colors */
+ for (j = 0; j < SUB_BATCH_SIZE; j++)
+ {
+ int k, color_index;
+ double *p = points[j];
+ bucket *b;
+
+ /* Note that we must test if p[0] and p[1] is "within"
+ * the valid bounds rather than "not outside", because
+ * p[0] and p[1] might be NaN.
+ */
+ if (p[0] >= bounds[0] &&
+ p[1] >= bounds[1] &&
+ p[0] <= bounds[2] &&
+ p[1] <= bounds[3])
+ {
+ color_index = (int) (p[2] * CMAP_SIZE);
+
+ if (color_index < 0)
+ color_index = 0;
+ else if (color_index > CMAP_SIZE - 1)
+ color_index = CMAP_SIZE - 1;
+
+ b = buckets +
+ (int) (width * (p[0] - bounds[0]) * size[0]) +
+ width * (int) (height * (p[1] - bounds[1]) * size[1]);
+
+ for (k = 0; k < 4; k++)
+ bump_no_overflow(b[0][k], cmap[color_index][k], short);
+ }
+ }
+ }
+
+ if (1)
+ {
+ double k1 = (cp.contrast * cp.brightness *
+ PREFILTER_WHITE * 268.0 *
+ temporal_filter[batch_num]) / 256;
+ double area = image_width * image_height / (ppux * ppuy);
+ double k2 = (oversample * oversample * nbatches) /
+ (cp.contrast * area * cp.white_level * sample_density);
+
+ /* log intensity in hsv space */
+ for (j = 0; j < height; j++)
+ for (i = 0; i < width; i++)
+ {
+ abucket *a = accumulate + i + j * width;
+ bucket *b = buckets + i + j * width;
+ double c[4], ls;
+ c[0] = (double) b[0][0];
+ c[1] = (double) b[0][1];
+ c[2] = (double) b[0][2];
+ c[3] = (double) b[0][3];
+ if (0.0 == c[3])
+ continue;
+
+ ls = (k1 * log(1.0 + c[3] * k2))/c[3];
+ c[0] *= ls;
+ c[1] *= ls;
+ c[2] *= ls;
+ c[3] *= ls;
+
+ bump_no_overflow(a[0][0], c[0] + 0.5, accum_t);
+ bump_no_overflow(a[0][1], c[1] + 0.5, accum_t);
+ bump_no_overflow(a[0][2], c[2] + 0.5, accum_t);
+ bump_no_overflow(a[0][3], c[3] + 0.5, accum_t);
+ }
+ }
+ }
+ /*
+ * filter the accumulation buffer down into the image
+ */
+ if (1)
+ {
+ int x, y;
+ double t[4];
+ double g = 1.0 / spec->cps[0].gamma;
+ y = 0;
+ for (j = 0; j < image_height; j++)
+ {
+ if (progress && (j % 32) == 0)
+ (*progress)(0.5 + 0.5 * j / (double)image_height);
+ x = 0;
+ for (i = 0; i < image_width; i++)
+ {
+ int ii, jj, a;
+ unsigned char *p;
+ t[0] = t[1] = t[2] = t[3] = 0.0;
+ for (ii = 0; ii < filter_width; ii++)
+ for (jj = 0; jj < filter_width; jj++)
+ {
+ double k = filter[ii + jj * filter_width];
+ abucket *a = accumulate + x + ii + (y + jj) * width;
+
+ t[0] += k * a[0][0];
+ t[1] += k * a[0][1];
+ t[2] += k * a[0][2];
+ t[3] += k * a[0][3];
+ }
+ /* FIXME: we should probably use glib facilities to make
+ * this code readable
+ */
+ p = out + nchan * (i + j * out_width);
+ a = 256.0 * pow((double) t[0] / PREFILTER_WHITE, g) + 0.5;
+ if (a < 0) a = 0; else if (a > 255) a = 255;
+ p[0] = a;
+ a = 256.0 * pow((double) t[1] / PREFILTER_WHITE, g) + 0.5;
+ if (a < 0) a = 0; else if (a > 255) a = 255;
+ p[1] = a;
+ a = 256.0 * pow((double) t[2] / PREFILTER_WHITE, g) + 0.5;
+ if (a < 0) a = 0; else if (a > 255) a = 255;
+ p[2] = a;
+ if (nchan > 3)
+ {
+ a = 256.0 * pow((double) t[3] / PREFILTER_WHITE, g) + 0.5;
+ if (a < 0) a = 0; else if (a > 255) a = 255;
+ p[3] = a;
+ }
+ x += oversample;
+ }
+ y += oversample;
+ }
+ }
+
+ free (filter);
+ free (temporal_filter);
+ free (temporal_deltas);
+}
diff --git a/plug-ins/flame/rect.h b/plug-ins/flame/rect.h
new file mode 100644
index 0000000..c5eb17f
--- /dev/null
+++ b/plug-ins/flame/rect.h
@@ -0,0 +1,41 @@
+/*
+ flame - cosmic recursive fractal flames
+ Copyright (C) 1992 Scott Draves <spot@cs.cmu.edu>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include "libifs.h"
+
+
+/* size of the cmap actually used. may be smaller than input cmap size */
+#define CMAP_SIZE 256
+
+typedef struct {
+ double temporal_filter_radius;
+ control_point *cps;
+ int ncps;
+ double time;
+} frame_spec;
+
+
+#define field_both 0
+#define field_even 1
+#define field_odd 2
+
+
+extern void render_rectangle(frame_spec *spec, unsigned char *out,
+ int out_width, int field, int nchan,
+ int progress(double));
+
diff --git a/plug-ins/fractal-explorer/Makefile.am b/plug-ins/fractal-explorer/Makefile.am
new file mode 100644
index 0000000..2905f1f
--- /dev/null
+++ b/plug-ins/fractal-explorer/Makefile.am
@@ -0,0 +1,55 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+fractal_explorer_RC = fractal-explorer.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+SUBDIRS = examples
+
+libexecdir = $(gimpplugindir)/plug-ins/fractal-explorer
+
+libexec_PROGRAMS = fractal-explorer
+
+fractal_explorer_SOURCES = \
+ fractal-explorer-dialogs.h \
+ fractal-explorer-dialogs.c \
+ fractal-explorer.c \
+ fractal-explorer.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(fractal_explorer_RC)
diff --git a/plug-ins/fractal-explorer/Makefile.in b/plug-ins/fractal-explorer/Makefile.in
new file mode 100644
index 0000000..9fbc51a
--- /dev/null
+++ b/plug-ins/fractal-explorer/Makefile.in
@@ -0,0 +1,1127 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = fractal-explorer$(EXEEXT)
+subdir = plug-ins/fractal-explorer
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_fractal_explorer_OBJECTS = fractal-explorer-dialogs.$(OBJEXT) \
+ fractal-explorer.$(OBJEXT)
+fractal_explorer_OBJECTS = $(am_fractal_explorer_OBJECTS)
+fractal_explorer_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+fractal_explorer_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(fractal_explorer_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/fractal-explorer-dialogs.Po \
+ ./$(DEPDIR)/fractal-explorer.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(fractal_explorer_SOURCES)
+DIST_SOURCES = $(fractal_explorer_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/fractal-explorer
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@fractal_explorer_RC = fractal-explorer.rc.o
+AM_LDFLAGS = $(mwindows)
+SUBDIRS = examples
+fractal_explorer_SOURCES = \
+ fractal-explorer-dialogs.h \
+ fractal-explorer-dialogs.c \
+ fractal-explorer.c \
+ fractal-explorer.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(fractal_explorer_RC)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/fractal-explorer/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/fractal-explorer/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+fractal-explorer$(EXEEXT): $(fractal_explorer_OBJECTS) $(fractal_explorer_DEPENDENCIES) $(EXTRA_fractal_explorer_DEPENDENCIES)
+ @rm -f fractal-explorer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(fractal_explorer_OBJECTS) $(fractal_explorer_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fractal-explorer-dialogs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fractal-explorer.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/fractal-explorer-dialogs.Po
+ -rm -f ./$(DEPDIR)/fractal-explorer.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/fractal-explorer-dialogs.Po
+ -rm -f ./$(DEPDIR)/fractal-explorer.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/fractal-explorer/examples/Asteroid_Field b/plug-ins/fractal-explorer/examples/Asteroid_Field
new file mode 100644
index 0000000..2215754
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Asteroid_Field
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 5
+xmin: -0.664136886596680
+xmax: -0.663678705692291
+ymin: -0.378479450941086
+ymax: -0.378030359745026
+iter: 214.723999023437500
+cx: 0.184000000357628
+cy: -0.200000002980232
+redstretch: 89.130996704101562
+greenstretch: 70.365997314453125
+bluestretch: 58.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 2
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Bar_Code_Label b/plug-ins/fractal-explorer/examples/Bar_Code_Label
new file mode 100644
index 0000000..3d021cb
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Bar_Code_Label
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.443357914686203
+xmax: -0.443357914686203
+ymin: 0.085399866104126
+ymax: 0.085484646260738
+iter: 50.000000000000000
+cx: -0.847000002861023
+cy: -0.184000000357628
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Beauty_of_Nature b/plug-ins/fractal-explorer/examples/Beauty_of_Nature
new file mode 100644
index 0000000..992710e
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Beauty_of_Nature
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.125070333480835
+xmax: -0.120749041438103
+ymin: -0.537724196910858
+ymax: -0.532733201980591
+iter: 49.080001831054688
+cx: 0.453999996185303
+cy: 0.000000000000000
+redstretch: 123.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 78.000000000000000
+redmode: 1
+greenmode: 2
+bluemode: 0
+redinvert: 0
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Blue_Curtain b/plug-ins/fractal-explorer/examples/Blue_Curtain
new file mode 100644
index 0000000..4f15a6f
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Blue_Curtain
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: 0.257157027721405
+xmax: 0.257280886173248
+ymin: 0.153874531388283
+ymax: 0.154003381729126
+iter: 49.080001831054688
+cx: -0.750000000000000
+cy: -0.356000006198883
+redstretch: 123.978996276855469
+greenstretch: 112.000000000000000
+bluestretch: 60.000000000000000
+redmode: 1
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Car_Track b/plug-ins/fractal-explorer/examples/Car_Track
new file mode 100644
index 0000000..10fdfaf
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Car_Track
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.104991987347603
+xmax: -0.104991905391216
+ymin: 0.250050097703934
+ymax: 0.250050216913223
+iter: 202.453994750976562
+cx: 0.356000006198883
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 2
+greenmode: 0
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Energetic_Diamond b/plug-ins/fractal-explorer/examples/Energetic_Diamond
new file mode 100644
index 0000000..8e1d854
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Energetic_Diamond
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 0
+xmin: -1.255496978759766
+xmax: -1.249950170516968
+ymin: -0.347052335739136
+ymax: -0.338468551635742
+iter: 50.000000000000000
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 113.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 1
+bluemode: 2
+redinvert: 1
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Explosive b/plug-ins/fractal-explorer/examples/Explosive
new file mode 100644
index 0000000..085bf4c
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Explosive
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: 0.353848785161972
+xmax: 0.356627285480499
+ymin: 0.458385974168777
+ymax: 0.460633248090744
+iter: 147.238998413085938
+cx: 0.000000000000000
+cy: 0.000000000000000
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 1
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 1
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Flower b/plug-ins/fractal-explorer/examples/Flower
new file mode 100644
index 0000000..6cdeb70
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Flower
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.125281319022179
+xmax: -0.116631627082825
+ymin: 0.375891804695129
+ymax: 0.384632647037506
+iter: 67.485000610351562
+cx: 0.405000001192093
+cy: -0.200000002980232
+redstretch: 124.000000000000000
+greenstretch: 112.000000000000000
+bluestretch: 60.000000000000000
+redmode: 0
+greenmode: 2
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Fragments b/plug-ins/fractal-explorer/examples/Fragments
new file mode 100644
index 0000000..5973759
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Fragments
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 2
+xmin: -0.508517742156982
+xmax: -0.475813776254654
+ymin: -0.025438375771046
+ymax: 0.025984741747379
+iter: 177.914001464843750
+cx: -1.018000006675720
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 1
+redinvert: 1
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Hemp b/plug-ins/fractal-explorer/examples/Hemp
new file mode 100644
index 0000000..c537101
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Hemp
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.968750000000000
+xmax: 0.437500000000000
+ymin: -0.703125000000000
+ymax: 0.843750000000000
+iter: 50.000000000000000
+cx: -0.012000000104308
+cy: 0.000000000000000
+redstretch: 87.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 0.000000000000000
+redmode: 0
+greenmode: 2
+bluemode: 1
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/High_Voltage b/plug-ins/fractal-explorer/examples/High_Voltage
new file mode 100644
index 0000000..5cb4a64
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/High_Voltage
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 1
+xmin: -0.476808547973633
+xmax: -0.223514556884766
+ymin: -0.017941474914551
+ymax: 0.114898681640625
+iter: 50.000000000000000
+cx: -1.337000012397766
+cy: 0.086000002920628
+redstretch: 127.330001831054688
+greenstretch: 90.000000000000000
+bluestretch: 128.000000000000000
+redmode: 2
+greenmode: 2
+bluemode: 2
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Hoops b/plug-ins/fractal-explorer/examples/Hoops
new file mode 100644
index 0000000..e2c9ca2
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Hoops
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.345402956008911
+xmax: -0.252956151962280
+ymin: -0.274888515472412
+ymax: -0.204696178436279
+iter: 30.674999237060547
+cx: 0.356000006198883
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 67.000000000000000
+bluestretch: 98.000000000000000
+redmode: 1
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Ice_Crystal b/plug-ins/fractal-explorer/examples/Ice_Crystal
new file mode 100644
index 0000000..e80c360
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Ice_Crystal
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 1
+xmin: 0.068130433559418
+xmax: 0.116958513855934
+ymin: 0.512302279472351
+ymax: 0.534968316555023
+iter: 202.453994750976562
+cx: 0.356000006198883
+cy: 0.428999990224838
+redstretch: 128.000000000000000
+greenstretch: 113.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Leaves b/plug-ins/fractal-explorer/examples/Leaves
new file mode 100644
index 0000000..d1227c8
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Leaves
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.350769042968750
+xmax: -0.080402374267578
+ymin: -0.264384269714355
+ymax: -0.041095733642578
+iter: 50.000000000000000
+cx: -0.184000000357628
+cy: 0.000000000000000
+redstretch: 29.000000000000000
+greenstretch: 80.000000000000000
+bluestretch: 7.000000000000000
+redmode: 0
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Lightning b/plug-ins/fractal-explorer/examples/Lightning
new file mode 100644
index 0000000..5901c7f
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Lightning
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 0
+xmin: -1.285429239273071
+xmax: -1.283814907073975
+ymin: 0.428520709276199
+ymax: 0.432528734207153
+iter: 50.000000000000000
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 89.130996704101562
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 2
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Makefile.am b/plug-ins/fractal-explorer/examples/Makefile.am
new file mode 100644
index 0000000..c4e18be
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Makefile.am
@@ -0,0 +1,40 @@
+## Process this file with automake to produce Makefile.in
+
+fractalexplorerdatadir = $(gimpdatadir)/fractalexplorer
+
+fractalexplorerdata_DATA = \
+ Asteroid_Field \
+ Bar_Code_Label \
+ Beauty_of_Nature \
+ Blue_Curtain \
+ Car_Track \
+ Energetic_Diamond \
+ Explosive \
+ Flower \
+ Fragments \
+ Hemp \
+ High_Voltage \
+ Hoops \
+ Ice_Crystal \
+ Leaves \
+ Lightning \
+ Mandelbrot \
+ Marble \
+ Marble2 \
+ Medusa \
+ Nautilus \
+ Nebula \
+ Plant \
+ Rose \
+ Saturn \
+ Snow_Crystal \
+ Soma \
+ Spark \
+ Suns \
+ Tentacles \
+ The_Green_Place \
+ Wave \
+ Wood \
+ Zooming_Circle
+
+EXTRA_DIST = $(fractalexplorerdata_DATA)
diff --git a/plug-ins/fractal-explorer/examples/Makefile.in b/plug-ins/fractal-explorer/examples/Makefile.in
new file mode 100644
index 0000000..c4c3703
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Makefile.in
@@ -0,0 +1,838 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/fractal-explorer/examples
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(fractalexplorerdatadir)"
+DATA = $(fractalexplorerdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+fractalexplorerdatadir = $(gimpdatadir)/fractalexplorer
+fractalexplorerdata_DATA = \
+ Asteroid_Field \
+ Bar_Code_Label \
+ Beauty_of_Nature \
+ Blue_Curtain \
+ Car_Track \
+ Energetic_Diamond \
+ Explosive \
+ Flower \
+ Fragments \
+ Hemp \
+ High_Voltage \
+ Hoops \
+ Ice_Crystal \
+ Leaves \
+ Lightning \
+ Mandelbrot \
+ Marble \
+ Marble2 \
+ Medusa \
+ Nautilus \
+ Nebula \
+ Plant \
+ Rose \
+ Saturn \
+ Snow_Crystal \
+ Soma \
+ Spark \
+ Suns \
+ Tentacles \
+ The_Green_Place \
+ Wave \
+ Wood \
+ Zooming_Circle
+
+EXTRA_DIST = $(fractalexplorerdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/fractal-explorer/examples/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/fractal-explorer/examples/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-fractalexplorerdataDATA: $(fractalexplorerdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(fractalexplorerdata_DATA)'; test -n "$(fractalexplorerdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(fractalexplorerdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(fractalexplorerdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(fractalexplorerdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(fractalexplorerdatadir)" || exit $$?; \
+ done
+
+uninstall-fractalexplorerdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(fractalexplorerdata_DATA)'; test -n "$(fractalexplorerdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(fractalexplorerdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(fractalexplorerdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-fractalexplorerdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-fractalexplorerdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am \
+ install-fractalexplorerdataDATA install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-fractalexplorerdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/fractal-explorer/examples/Mandelbrot b/plug-ins/fractal-explorer/examples/Mandelbrot
new file mode 100644
index 0000000..09c05d6
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Mandelbrot
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 0
+xmin: -0.078803382813931
+xmax: -0.077717751264572
+ymin: -0.880311131477356
+ymax: -0.879459798336029
+iter: 503.066986083984375
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 120.627998352050781
+bluestretch: 128.000000000000000
+redmode: 1
+greenmode: 2
+bluemode: 0
+redinvert: 1
+greeninvert: 0
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Marble b/plug-ins/fractal-explorer/examples/Marble
new file mode 100644
index 0000000..dac8229
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Marble
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.235851287841797
+xmax: -0.060367584228516
+ymin: 0.091303825378418
+ymax: 0.220178604125977
+iter: 55.215000152587891
+cx: 0.356000006198883
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 127.330001831054688
+redmode: 1
+greenmode: 2
+bluemode: 2
+redinvert: 0
+greeninvert: 1
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Marble2 b/plug-ins/fractal-explorer/examples/Marble2
new file mode 100644
index 0000000..c2e2d8b
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Marble2
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.146484375000000
+xmax: 0.064453125000000
+ymin: 0.154907226562500
+ymax: 0.358154296875000
+iter: 50.000000000000000
+cx: 0.356000006198883
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 1
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Medusa b/plug-ins/fractal-explorer/examples/Medusa
new file mode 100644
index 0000000..84ff7b9
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Medusa
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 0
+xmin: -1.408687233924866
+xmax: -1.408686757087708
+ymin: -0.135643482208252
+ymax: -0.135642752051353
+iter: 104.293998718261719
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 113.000000000000000
+bluestretch: 128.000000000000000
+redmode: 1
+greenmode: 0
+bluemode: 1
+redinvert: 1
+greeninvert: 1
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Nautilus b/plug-ins/fractal-explorer/examples/Nautilus
new file mode 100644
index 0000000..98c4b2c
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Nautilus
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.078369140625000
+xmax: 0.158203125000000
+ymin: -0.485778808593750
+ymax: -0.197387695312500
+iter: 134.968994140625000
+cx: 0.109999999403954
+cy: 0.037000000476837
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 1
+greenmode: 2
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Nebula b/plug-ins/fractal-explorer/examples/Nebula
new file mode 100644
index 0000000..f541cb1
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Nebula
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.935546875000000
+xmax: 0.226562500000000
+ymin: 0.034790039062500
+ymax: 0.995361328125000
+iter: 67.485000610351562
+cx: 0.428999990224838
+cy: 0.232999995350838
+redstretch: 128.000000000000000
+greenstretch: 120.627998352050781
+bluestretch: 93.000000000000000
+redmode: 2
+greenmode: 2
+bluemode: 1
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Plant b/plug-ins/fractal-explorer/examples/Plant
new file mode 100644
index 0000000..644f8e3
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Plant
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.251464843750000
+xmax: -0.097656250000000
+ymin: -0.626953125000000
+ymax: -0.503356933593750
+iter: 55.215000152587891
+cx: 0.503000020980835
+cy: -0.012000000104308
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 2
+greenmode: 1
+bluemode: 0
+redinvert: 1
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Rose b/plug-ins/fractal-explorer/examples/Rose
new file mode 100644
index 0000000..e8bf1ee
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Rose
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 7
+xmin: 0.011789147742093
+xmax: 0.012022980488837
+ymin: 0.040065050125122
+ymax: 0.040298193693161
+iter: 92.025001525878906
+cx: -1.092000007629395
+cy: 0.232999995350838
+redstretch: 124.000000000000000
+greenstretch: 112.000000000000000
+bluestretch: 60.000000000000000
+redmode: 1
+greenmode: 0
+bluemode: 0
+redinvert: 1
+greeninvert: 1
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Saturn b/plug-ins/fractal-explorer/examples/Saturn
new file mode 100644
index 0000000..ff6fed9
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Saturn
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -0.720550119876862
+xmax: -0.736553728580475
+ymin: 0.128735020756721
+ymax: 0.068184189498425
+iter: 49.080001831054688
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 119.958000183105469
+greenstretch: 85.779998779296875
+bluestretch: 6.031000137329102
+redmode: 0
+greenmode: 0
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Snow_Crystal b/plug-ins/fractal-explorer/examples/Snow_Crystal
new file mode 100644
index 0000000..0e002ad
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Snow_Crystal
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 0
+xmin: -0.562806546688080
+xmax: -0.561547160148621
+ymin: -0.643333077430725
+ymax: -0.642277657985687
+iter: 122.698997497558594
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 113.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 0
+bluemode: 2
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Soma b/plug-ins/fractal-explorer/examples/Soma
new file mode 100644
index 0000000..eae469c
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Soma
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.038584709167480
+xmax: 0.094075202941895
+ymin: -0.689266920089722
+ymax: -0.584711074829102
+iter: 98.160003662109375
+cx: 0.405000001192093
+cy: 0.037000000476837
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 0
+greenmode: 1
+bluemode: 2
+redinvert: 0
+greeninvert: 1
+blueinvert: 1
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Spark b/plug-ins/fractal-explorer/examples/Spark
new file mode 100644
index 0000000..7fd72ea
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Spark
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 7
+xmin: -0.014160156250000
+xmax: 1.014160156250000
+ymin: -0.261108398437500
+ymax: 0.252685546875000
+iter: 42.944999694824219
+cx: -1.386999964714050
+cy: -0.257999986410141
+redstretch: 124.000000000000000
+greenstretch: 112.000000000000000
+bluestretch: 60.000000000000000
+redmode: 0
+greenmode: 2
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 1
+colormode: 1
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Suns b/plug-ins/fractal-explorer/examples/Suns
new file mode 100644
index 0000000..0ee2c9b
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Suns
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 1
+xmin: -0.573436737060547
+xmax: -0.399868011474609
+ymin: -0.177640914916992
+ymax: -0.024647712707520
+iter: 607.361999511718750
+cx: -0.723999977111816
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 2
+greenmode: 0
+bluemode: 1
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Tentacles b/plug-ins/fractal-explorer/examples/Tentacles
new file mode 100644
index 0000000..de9c9c6
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Tentacles
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 6
+xmin: -0.430790066719055
+xmax: -0.421469658613205
+ymin: 0.017559077590704
+ymax: 0.025114785879850
+iter: 67.485000610351562
+cx: -0.086000002920628
+cy: -0.012000000104308
+redstretch: 128.000000000000000
+greenstretch: 37.000000000000000
+bluestretch: 93.000000000000000
+redmode: 2
+greenmode: 2
+bluemode: 1
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/The_Green_Place b/plug-ins/fractal-explorer/examples/The_Green_Place
new file mode 100644
index 0000000..b25527e
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/The_Green_Place
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 2
+xmin: 0.064453125000000
+xmax: 0.950683593750000
+ymin: -0.389648437500000
+ymax: 0.476440429687500
+iter: 50.000000000000000
+cx: -1.067000031471252
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 110.000000000000000
+bluestretch: 2.000000000000000
+redmode: 0
+greenmode: 1
+bluemode: 1
+redinvert: 1
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Wave b/plug-ins/fractal-explorer/examples/Wave
new file mode 100644
index 0000000..d405d27
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Wave
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 2
+xmin: -0.937500000000000
+xmax: 1.125000000000000
+ymin: -0.609375000000000
+ymax: 0.609375000000000
+iter: 50.000000000000000
+cx: -1.042999982833862
+cy: -0.037000000476837
+redstretch: 128.000000000000000
+greenstretch: 22.000000000000000
+bluestretch: 101.000000000000000
+redmode: 1
+greenmode: 1
+bluemode: 0
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Wood b/plug-ins/fractal-explorer/examples/Wood
new file mode 100644
index 0000000..e1f64c4
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Wood
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 4
+xmin: -1.287102937698364
+xmax: -1.287102580070496
+ymin: 0.099825143814087
+ymax: 0.099862203001976
+iter: 50.000000000000000
+cx: -0.750000000000000
+cy: -0.200000002980232
+redstretch: 128.000000000000000
+greenstretch: 109.905998229980469
+bluestretch: 2.009999990463257
+redmode: 1
+greenmode: 0
+bluemode: 0
+redinvert: 1
+greeninvert: 1
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/examples/Zooming_Circle b/plug-ins/fractal-explorer/examples/Zooming_Circle
new file mode 100644
index 0000000..aa28d90
--- /dev/null
+++ b/plug-ins/fractal-explorer/examples/Zooming_Circle
@@ -0,0 +1,26 @@
+Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>
+#**********************************************************************
+# This is a data file for the Fractal Explorer plug-in for GIMP *
+# Get the plug-in at http://www.mygale.org/~cotting *
+#**********************************************************************
+fractaltype: 3
+xmin: 0.187500000000000
+xmax: 0.984375000000000
+ymin: 0.843750000000000
+ymax: 1.734375000000000
+iter: 50.000000000000000
+cx: 0.773000001907349
+cy: -0.723999977111816
+redstretch: 128.000000000000000
+greenstretch: 128.000000000000000
+bluestretch: 128.000000000000000
+redmode: 2
+greenmode: 0
+bluemode: 1
+redinvert: 0
+greeninvert: 0
+blueinvert: 0
+colormode: 0
+#**********************************************************************
+<EOF>
+#**********************************************************************
diff --git a/plug-ins/fractal-explorer/fractal-explorer-dialogs.c b/plug-ins/fractal-explorer/fractal-explorer-dialogs.c
new file mode 100644
index 0000000..8f53860
--- /dev/null
+++ b/plug-ins/fractal-explorer/fractal-explorer-dialogs.c
@@ -0,0 +1,1889 @@
+/**********************************************************************
+ GIMP - The GNU Image Manipulation Program
+ Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *********************************************************************/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "fractal-explorer.h"
+#include "fractal-explorer-dialogs.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define ZOOM_UNDO_SIZE 100
+
+
+static gint n_gradient_samples = 0;
+static gdouble *gradient_samples = NULL;
+static gchar *gradient_name = NULL;
+static gboolean ready_now = FALSE;
+static gchar *tpath = NULL;
+static DialogElements *elements = NULL;
+static GtkWidget *cmap_preview;
+static GtkWidget *maindlg;
+
+static explorer_vals_t zooms[ZOOM_UNDO_SIZE];
+static gint zoomindex = 0;
+static gint zoommax = 0;
+
+static gint oldxpos = -1;
+static gint oldypos = -1;
+static gdouble x_press = -1.0;
+static gdouble y_press = -1.0;
+
+static explorer_vals_t standardvals =
+{
+ 0,
+ -2.0,
+ 2.0,
+ -1.5,
+ 1.5,
+ 50.0,
+ -0.75,
+ -0.2,
+ 0,
+ 1.0,
+ 1.0,
+ 1.0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 256,
+ 0
+};
+
+/**********************************************************************
+ FORWARD DECLARATIONS
+ *********************************************************************/
+
+static void load_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data);
+static void save_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data);
+static void create_load_file_chooser (GtkWidget *widget,
+ GtkWidget *dialog);
+static void create_save_file_chooser (GtkWidget *widget,
+ GtkWidget *dialog);
+
+static void cmap_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+
+/**********************************************************************
+ CALLBACKS
+ *********************************************************************/
+
+static void
+dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ wint.run = TRUE;
+ gtk_widget_destroy (widget);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static void
+dialog_reset_callback (GtkWidget *widget,
+ gpointer data)
+{
+ wvals.xmin = standardvals.xmin;
+ wvals.xmax = standardvals.xmax;
+ wvals.ymin = standardvals.ymin;
+ wvals.ymax = standardvals.ymax;
+ wvals.iter = standardvals.iter;
+ wvals.cx = standardvals.cx;
+ wvals.cy = standardvals.cy;
+
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+dialog_redraw_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint alwaysprev = wvals.alwayspreview;
+
+ wvals.alwayspreview = TRUE;
+ set_cmap_preview ();
+ dialog_update_preview ();
+ wvals.alwayspreview = alwaysprev;
+}
+
+static void
+dialog_undo_zoom_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (zoomindex > 0)
+ {
+ zooms[zoomindex] = wvals;
+ zoomindex--;
+ wvals = zooms[zoomindex];
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+ }
+}
+
+static void
+dialog_redo_zoom_callback (GtkWidget *widget,
+ gpointer data)
+{
+ if (zoomindex < zoommax)
+ {
+ zoomindex++;
+ wvals = zooms[zoomindex];
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+ }
+}
+
+static void
+dialog_step_in_callback (GtkWidget *widget,
+ gpointer data)
+{
+ double xdifferenz;
+ double ydifferenz;
+
+ if (zoomindex < ZOOM_UNDO_SIZE - 1)
+ {
+ zooms[zoomindex]=wvals;
+ zoomindex++;
+ }
+ zoommax = zoomindex;
+
+ xdifferenz = wvals.xmax - wvals.xmin;
+ ydifferenz = wvals.ymax - wvals.ymin;
+ wvals.xmin += 1.0 / 6.0 * xdifferenz;
+ wvals.ymin += 1.0 / 6.0 * ydifferenz;
+ wvals.xmax -= 1.0 / 6.0 * xdifferenz;
+ wvals.ymax -= 1.0 / 6.0 * ydifferenz;
+ zooms[zoomindex] = wvals;
+
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+dialog_step_out_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gdouble xdifferenz;
+ gdouble ydifferenz;
+
+ if (zoomindex < ZOOM_UNDO_SIZE - 1)
+ {
+ zooms[zoomindex]=wvals;
+ zoomindex++;
+ }
+ zoommax = zoomindex;
+
+ xdifferenz = wvals.xmax - wvals.xmin;
+ ydifferenz = wvals.ymax - wvals.ymin;
+ wvals.xmin -= 1.0 / 4.0 * xdifferenz;
+ wvals.ymin -= 1.0 / 4.0 * ydifferenz;
+ wvals.xmax += 1.0 / 4.0 * xdifferenz;
+ wvals.ymax += 1.0 / 4.0 * ydifferenz;
+ zooms[zoomindex] = wvals;
+
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+explorer_toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+explorer_radio_update (GtkWidget *widget,
+ gpointer data)
+{
+ gboolean c_sensitive;
+
+ gimp_radio_button_update (widget, data);
+
+ switch (wvals.fractaltype)
+ {
+ case TYPE_MANDELBROT:
+ case TYPE_SIERPINSKI:
+ c_sensitive = FALSE;
+ break;
+
+ default:
+ c_sensitive = TRUE;
+ break;
+ }
+
+ gimp_scale_entry_set_sensitive (elements->cx, c_sensitive);
+ gimp_scale_entry_set_sensitive (elements->cy, c_sensitive);
+
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+explorer_double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_double_adjustment_update (adjustment, data);
+
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+explorer_number_of_colors_callback (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_int_adjustment_update (adjustment, data);
+
+ g_free (gradient_samples);
+
+ if (! gradient_name)
+ gradient_name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (gradient_name,
+ wvals.ncolors,
+ wvals.gradinvert,
+ &n_gradient_samples,
+ &gradient_samples);
+
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+explorer_gradient_select_callback (GimpGradientSelectButton *gradient_button,
+ const gchar *name,
+ gint width,
+ const gdouble *gradient_data,
+ gboolean dialog_closing,
+ gpointer data)
+{
+ g_free (gradient_name);
+ g_free (gradient_samples);
+
+ gradient_name = g_strdup (name);
+
+ gimp_gradient_get_uniform_samples (gradient_name,
+ wvals.ncolors,
+ wvals.gradinvert,
+ &n_gradient_samples,
+ &gradient_samples);
+
+ if (wvals.colormode == 1)
+ {
+ set_cmap_preview ();
+ dialog_update_preview ();
+ }
+}
+
+static void
+preview_draw_crosshair (gint px,
+ gint py)
+{
+ gint x, y;
+ guchar *p_ul;
+
+ p_ul = wint.wimage + 3 * (preview_width * py + 0);
+
+ for (x = 0; x < preview_width; x++)
+ {
+ p_ul[0] ^= 254;
+ p_ul[1] ^= 254;
+ p_ul[2] ^= 254;
+ p_ul += 3;
+ }
+
+ p_ul = wint.wimage + 3 * (preview_width * 0 + px);
+
+ for (y = 0; y < preview_height; y++)
+ {
+ p_ul[0] ^= 254;
+ p_ul[1] ^= 254;
+ p_ul[2] ^= 254;
+ p_ul += 3 * preview_width;
+ }
+}
+
+static void
+preview_redraw (void)
+{
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (wint.preview),
+ 0, 0, preview_width, preview_height,
+ GIMP_RGB_IMAGE,
+ wint.wimage, preview_width * 3);
+
+ gtk_widget_queue_draw (wint.preview);
+}
+
+static gboolean
+preview_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ x_press = event->x;
+ y_press = event->y;
+ xbild = preview_width;
+ ybild = preview_height;
+ xdiff = (xmax - xmin) / xbild;
+ ydiff = (ymax - ymin) / ybild;
+
+ preview_draw_crosshair (x_press, y_press);
+ preview_redraw ();
+ }
+ return TRUE;
+}
+
+static gboolean
+preview_motion_notify_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (oldypos != -1)
+ {
+ preview_draw_crosshair (oldxpos, oldypos);
+ }
+
+ oldxpos = event->x;
+ oldypos = event->y;
+
+ if ((oldxpos >= 0.0) &&
+ (oldypos >= 0.0) &&
+ (oldxpos < preview_width) &&
+ (oldypos < preview_height))
+ {
+ preview_draw_crosshair (oldxpos, oldypos);
+ }
+ else
+ {
+ oldypos = -1;
+ oldxpos = -1;
+ }
+
+ preview_redraw ();
+
+ return TRUE;
+}
+
+static gboolean
+preview_leave_notify_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (oldypos != -1)
+ {
+ preview_draw_crosshair (oldxpos, oldypos);
+ }
+ oldxpos = -1;
+ oldypos = -1;
+
+ preview_redraw ();
+
+ gdk_window_set_cursor (gtk_widget_get_window (maindlg), NULL);
+
+ return TRUE;
+}
+
+static gboolean
+preview_enter_notify_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ static GdkCursor *cursor = NULL;
+
+ if (! cursor)
+ {
+ GdkDisplay *display = gtk_widget_get_display (maindlg);
+
+ cursor = gdk_cursor_new_for_display (display, GDK_TCROSS);
+
+ }
+
+ gdk_window_set_cursor (gtk_widget_get_window (maindlg), cursor);
+
+ return TRUE;
+}
+
+static gboolean
+preview_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ gdouble l_xmin;
+ gdouble l_xmax;
+ gdouble l_ymin;
+ gdouble l_ymax;
+
+ if (event->button == 1)
+ {
+ gdouble x_release, y_release;
+
+ x_release = event->x;
+ y_release = event->y;
+
+ if ((x_press >= 0.0) && (y_press >= 0.0) &&
+ (x_release >= 0.0) && (y_release >= 0.0) &&
+ (x_press < preview_width) && (y_press < preview_height) &&
+ (x_release < preview_width) && (y_release < preview_height))
+ {
+ l_xmin = (wvals.xmin +
+ (wvals.xmax - wvals.xmin) * (x_press / preview_width));
+ l_xmax = (wvals.xmin +
+ (wvals.xmax - wvals.xmin) * (x_release / preview_width));
+ l_ymin = (wvals.ymin +
+ (wvals.ymax - wvals.ymin) * (y_press / preview_height));
+ l_ymax = (wvals.ymin +
+ (wvals.ymax - wvals.ymin) * (y_release / preview_height));
+
+ if (zoomindex < ZOOM_UNDO_SIZE - 1)
+ {
+ zooms[zoomindex] = wvals;
+ zoomindex++;
+ }
+ zoommax = zoomindex;
+ wvals.xmin = l_xmin;
+ wvals.xmax = l_xmax;
+ wvals.ymin = l_ymin;
+ wvals.ymax = l_ymax;
+ dialog_change_scale ();
+ dialog_update_preview ();
+ oldypos = oldxpos = -1;
+ }
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************
+ FUNCTION: explorer_dialog
+ *********************************************************************/
+
+gint
+explorer_dialog (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *top_hbox;
+ GtkWidget *left_vbox;
+ GtkWidget *abox;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GtkWidget *toggle_vbox;
+ GtkWidget *toggle_vbox2;
+ GtkWidget *toggle_vbox3;
+ GtkWidget *notebook;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *gradient;
+ gchar *path;
+ gchar *gradient_name;
+ GSList *group = NULL;
+ gint i;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ path = gimp_gimprc_query ("fractalexplorer-path");
+
+ if (path)
+ {
+ fractalexplorer_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+ g_free (path);
+ }
+ else
+ {
+ gchar *gimprc = gimp_personal_rc_file ("gimprc");
+ gchar *full_path = gimp_config_build_data_path ("fractalexplorer");
+ gchar *esc_path = g_strescape (full_path, NULL);
+ g_free (full_path);
+
+ g_message (_("No %s in gimprc:\n"
+ "You need to add an entry like\n"
+ "(%s \"%s\")\n"
+ "to your %s file."),
+ "fractalexplorer-path",
+ "fractalexplorer-path",
+ esc_path, gimp_filename_to_utf8 (gimprc));
+
+ g_free (gimprc);
+ g_free (esc_path);
+ }
+
+ wint.wimage = g_new (guchar, preview_width * preview_height * 3);
+ elements = g_new (DialogElements, 1);
+
+ dialog = maindlg =
+ gimp_dialog_new (_("Fractal Explorer"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (dialog_response),
+ NULL);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ top_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (top_hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ top_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (top_hbox);
+
+ left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (top_hbox), left_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (left_vbox);
+
+ /* Preview */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (left_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ wint.preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (wint.preview, preview_width, preview_height);
+ gtk_container_add (GTK_CONTAINER (frame), wint.preview);
+
+ g_signal_connect (wint.preview, "button-press-event",
+ G_CALLBACK (preview_button_press_event),
+ NULL);
+ g_signal_connect (wint.preview, "button-release-event",
+ G_CALLBACK (preview_button_release_event),
+ NULL);
+ g_signal_connect (wint.preview, "motion-notify-event",
+ G_CALLBACK (preview_motion_notify_event),
+ NULL);
+ g_signal_connect (wint.preview, "leave-notify-event",
+ G_CALLBACK (preview_leave_notify_event),
+ NULL);
+ g_signal_connect (wint.preview, "enter-notify-event",
+ G_CALLBACK (preview_enter_notify_event),
+ NULL);
+
+ gtk_widget_set_events (wint.preview, (GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_ENTER_NOTIFY_MASK));
+ gtk_widget_show (wint.preview);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Re_altime preview"));
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_toggle_update),
+ &wvals.alwayspreview);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ wvals.alwayspreview);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle, _("If enabled the preview will "
+ "be redrawn automatically"), NULL);
+
+ button = gtk_button_new_with_mnemonic (_("R_edraw preview"));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_redraw_callback),
+ dialog);
+ gtk_widget_show (button);
+
+ /* Zoom Options */
+ frame = gimp_frame_new (_("Zoom"));
+ gtk_box_pack_start (GTK_BOX (left_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_mnemonic (_("Zoom _In"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_step_in_callback),
+ dialog);
+
+ button = gtk_button_new_with_mnemonic (_("Zoom _Out"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_step_out_callback),
+ dialog);
+
+ bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Undo"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button, _("Undo last zoom change"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_undo_zoom_callback),
+ dialog);
+
+ button = gtk_button_new_with_mnemonic (_("_Redo"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button, _("Redo last zoom change"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_redo_zoom_callback),
+ dialog);
+
+ /* Create notebook */
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (top_hbox), notebook, FALSE, FALSE, 0);
+ gtk_widget_show (notebook);
+
+ /* "Parameters" page */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("_Parameters")));
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Fractal Parameters"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (8, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 6, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ elements->xmin =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Left:"), SCALE_WIDTH, 10,
+ wvals.xmin, -3, 3, 0.001, 0.01, 5,
+ TRUE, 0, 0, NULL, NULL);
+ g_signal_connect (elements->xmin, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.xmin);
+
+ elements->xmax =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Right:"), SCALE_WIDTH, 10,
+ wvals.xmax, -3, 3, 0.001, 0.01, 5,
+ TRUE, 0, 0, NULL, NULL);
+ g_signal_connect (elements->xmax, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.xmax);
+
+ elements->ymin =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Top:"), SCALE_WIDTH, 10,
+ wvals.ymin, -3, 3, 0.001, 0.01, 5,
+ TRUE, 0, 0, NULL, NULL);
+ g_signal_connect (elements->ymin, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.ymin);
+
+ elements->ymax =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Bottom:"), SCALE_WIDTH, 10,
+ wvals.ymax, -3, 3, 0.001, 0.01, 5,
+ TRUE, 0, 0, NULL, NULL);
+ g_signal_connect (elements->ymax, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.ymax);
+
+ elements->iter =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Iterations:"), SCALE_WIDTH, 10,
+ wvals.iter, 1, 1000, 1, 10, 0,
+ TRUE, 0, 0,
+ _("The higher the number of iterations, "
+ "the more details will be calculated"), NULL);
+ g_signal_connect (elements->iter, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.iter);
+
+ elements->cx =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 5,
+ _("CX:"), SCALE_WIDTH, 10,
+ wvals.cx, -2.5, 2.5, 0.001, 0.01, 5,
+ TRUE, 0, 0,
+ _("Changes aspect of fractal"), NULL);
+ g_signal_connect (elements->cx, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.cx);
+
+ elements->cy =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 6,
+ _("CY:"), SCALE_WIDTH, 10,
+ wvals.cy, -2.5, 2.5, 0.001, 0.01, 5,
+ TRUE, 0, 0,
+ _("Changes aspect of fractal"), NULL);
+ g_signal_connect (elements->cy, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.cy);
+
+ bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (bbox), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), bbox, 0, 3, 7, 8);
+ gtk_widget_show (bbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (create_load_file_chooser),
+ dialog);
+ gtk_widget_show (button);
+ gimp_help_set_help_data (button, _("Load a fractal from file"), NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_Reset"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (dialog_reset_callback),
+ dialog);
+ gtk_widget_show (button);
+ gimp_help_set_help_data (button, _("Reset parameters to default values"),
+ NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_box_pack_start (GTK_BOX (bbox), button, TRUE, TRUE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (create_save_file_chooser),
+ dialog);
+ gtk_widget_show (button);
+ gimp_help_set_help_data (button, _("Save active fractal to file"), NULL);
+
+ /* Fractal type toggle box */
+ frame = gimp_frame_new (_("Fractal Type"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ toggle_vbox =
+ gimp_int_radio_group_new (FALSE, NULL,
+ G_CALLBACK (explorer_radio_update),
+ &wvals.fractaltype, wvals.fractaltype,
+
+ _("Mandelbrot"), TYPE_MANDELBROT,
+ &(elements->type[TYPE_MANDELBROT]),
+ _("Julia"), TYPE_JULIA,
+ &(elements->type[TYPE_JULIA]),
+ _("Barnsley 1"), TYPE_BARNSLEY_1,
+ &(elements->type[TYPE_BARNSLEY_1]),
+ _("Barnsley 2"), TYPE_BARNSLEY_2,
+ &(elements->type[TYPE_BARNSLEY_2]),
+ _("Barnsley 3"), TYPE_BARNSLEY_3,
+ &(elements->type[TYPE_BARNSLEY_3]),
+ _("Spider"), TYPE_SPIDER,
+ &(elements->type[TYPE_SPIDER]),
+ _("Man'o'war"), TYPE_MAN_O_WAR,
+ &(elements->type[TYPE_MAN_O_WAR]),
+ _("Lambda"), TYPE_LAMBDA,
+ &(elements->type[TYPE_LAMBDA]),
+ _("Sierpinski"), TYPE_SIERPINSKI,
+ &(elements->type[TYPE_SIERPINSKI]),
+
+ NULL);
+
+ toggle_vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ for (i = TYPE_BARNSLEY_2; i <= TYPE_SPIDER; i++)
+ {
+ g_object_ref (elements->type[i]);
+
+ gtk_widget_hide (elements->type[i]);
+ gtk_container_remove (GTK_CONTAINER (toggle_vbox), elements->type[i]);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox2), elements->type[i],
+ FALSE, FALSE, 0);
+ gtk_widget_show (elements->type[i]);
+
+ g_object_unref (elements->type[i]);
+ }
+
+ toggle_vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ for (i = TYPE_MAN_O_WAR; i <= TYPE_SIERPINSKI; i++)
+ {
+ g_object_ref (elements->type[i]);
+
+ gtk_widget_hide (elements->type[i]);
+ gtk_container_remove (GTK_CONTAINER (toggle_vbox), elements->type[i]);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox3), elements->type[i],
+ FALSE, FALSE, 0);
+ gtk_widget_show (elements->type[i]);
+
+ g_object_unref (elements->type[i]);
+ }
+
+ gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (toggle_vbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (toggle_vbox2);
+
+ gtk_box_pack_start (GTK_BOX (hbox), toggle_vbox3, FALSE, FALSE, 0);
+ gtk_widget_show (toggle_vbox3);
+
+ /* Color page */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("Co_lors")));
+ gtk_widget_show (vbox);
+
+ /* Number of Colors frame */
+ frame = gimp_frame_new (_("Number of Colors"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ elements->ncol =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Number of colors:"), SCALE_WIDTH, 0,
+ wvals.ncolors, 2, MAXNCOLORS, 1, 10, 0,
+ TRUE, 0, 0,
+ _("Change the number of colors in the mapping"),
+ NULL);
+ g_signal_connect (elements->ncol, "value-changed",
+ G_CALLBACK (explorer_number_of_colors_callback),
+ &wvals.ncolors);
+
+ elements->useloglog = toggle =
+ gtk_check_button_new_with_label (_("Use loglog smoothing"));
+ gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 3, 1, 2);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_toggle_update),
+ &wvals.useloglog);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.useloglog);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle, _("Use log log smoothing to eliminate "
+ "\"banding\" in the result"), NULL);
+
+ /* Color Density frame */
+ frame = gimp_frame_new (_("Color Density"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ elements->red =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Red:"), SCALE_WIDTH, 0,
+ wvals.redstretch, 0, 1, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Change the intensity of the red channel"), NULL);
+ g_signal_connect (elements->red, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.redstretch);
+
+ elements->green =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Green:"), SCALE_WIDTH, 0,
+ wvals.greenstretch, 0, 1, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Change the intensity of the green channel"), NULL);
+ g_signal_connect (elements->green, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.greenstretch);
+
+ elements->blue =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Blue:"), SCALE_WIDTH, 0,
+ wvals.bluestretch, 0, 1, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Change the intensity of the blue channel"), NULL);
+ g_signal_connect (elements->blue, "value-changed",
+ G_CALLBACK (explorer_double_adjustment_update),
+ &wvals.bluestretch);
+
+ /* Color Function frame */
+ frame = gimp_frame_new (_("Color Function"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ /* Redmode radio frame */
+ frame = gimp_int_radio_group_new (TRUE, _("Red"),
+ G_CALLBACK (explorer_radio_update),
+ &wvals.redmode, wvals.redmode,
+
+ _("Sine"), SINUS,
+ &elements->redmode[SINUS],
+ _("Cosine"), COSINUS,
+ &elements->redmode[COSINUS],
+ C_("color-function", "None"), NONE,
+ &elements->redmode[NONE],
+
+ NULL);
+ gimp_help_set_help_data (elements->redmode[SINUS],
+ _("Use sine-function for this color component"),
+ NULL);
+ gimp_help_set_help_data (elements->redmode[COSINUS],
+ _("Use cosine-function for this color "
+ "component"), NULL);
+ gimp_help_set_help_data (elements->redmode[NONE],
+ _("Use linear mapping instead of any "
+ "trigonometrical function for this color "
+ "channel"), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
+
+ elements->redinvert = toggle =
+ gtk_check_button_new_with_label (_("Inversion"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.redinvert);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_toggle_update),
+ &wvals.redinvert);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle,
+ _("If you enable this option higher color values "
+ "will be swapped with lower ones and vice "
+ "versa"), NULL);
+
+ /* Greenmode radio frame */
+ frame = gimp_int_radio_group_new (TRUE, _("Green"),
+ G_CALLBACK (explorer_radio_update),
+ &wvals.greenmode, wvals.greenmode,
+
+ _("Sine"), SINUS,
+ &elements->greenmode[SINUS],
+ _("Cosine"), COSINUS,
+ &elements->greenmode[COSINUS],
+ C_("color-function", "None"), NONE,
+ &elements->greenmode[NONE],
+
+ NULL);
+ gimp_help_set_help_data (elements->greenmode[SINUS],
+ _("Use sine-function for this color component"),
+ NULL);
+ gimp_help_set_help_data (elements->greenmode[COSINUS],
+ _("Use cosine-function for this color "
+ "component"), NULL);
+ gimp_help_set_help_data (elements->greenmode[NONE],
+ _("Use linear mapping instead of any "
+ "trigonometrical function for this color "
+ "channel"), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
+
+ elements->greeninvert = toggle =
+ gtk_check_button_new_with_label (_("Inversion"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), wvals.greeninvert);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_toggle_update),
+ &wvals.greeninvert);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle,
+ _("If you enable this option higher color values "
+ "will be swapped with lower ones and vice "
+ "versa"), NULL);
+
+ /* Bluemode radio frame */
+ frame = gimp_int_radio_group_new (TRUE, _("Blue"),
+ G_CALLBACK (explorer_radio_update),
+ &wvals.bluemode, wvals.bluemode,
+
+ _("Sine"), SINUS,
+ &elements->bluemode[SINUS],
+ _("Cosine"), COSINUS,
+ &elements->bluemode[COSINUS],
+ C_("color-function", "None"), NONE,
+ &elements->bluemode[NONE],
+
+ NULL);
+ gimp_help_set_help_data (elements->bluemode[SINUS],
+ _("Use sine-function for this color component"),
+ NULL);
+ gimp_help_set_help_data (elements->bluemode[COSINUS],
+ _("Use cosine-function for this color "
+ "component"), NULL);
+ gimp_help_set_help_data (elements->bluemode[NONE],
+ _("Use linear mapping instead of any "
+ "trigonometrical function for this color "
+ "channel"), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ toggle_vbox = gtk_bin_get_child (GTK_BIN (frame));
+
+ elements->blueinvert = toggle =
+ gtk_check_button_new_with_label (_("Inversion"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON( toggle), wvals.blueinvert);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_toggle_update),
+ &wvals.blueinvert);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle,
+ _("If you enable this option higher color values "
+ "will be swapped with lower ones and vice "
+ "versa"), NULL);
+
+ /* Colormode toggle box */
+ frame = gimp_frame_new (_("Color Mode"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
+ gtk_widget_show (toggle_vbox);
+
+ toggle = elements->colormode[0] =
+ gtk_radio_button_new_with_label (group, _("As specified above"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (0));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_radio_update),
+ &wvals.colormode);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ wvals.colormode == 0);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle,
+ _("Create a color-map with the options you "
+ "specified above (color density/function). The "
+ "result is visible in the preview image"), NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = elements->colormode[1] =
+ gtk_radio_button_new_with_label (group,
+ _("Apply active gradient to final image"));
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (1));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (explorer_radio_update),
+ &wvals.colormode);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ wvals.colormode == 1);
+ gtk_widget_show (toggle);
+ gimp_help_set_help_data (toggle,
+ _("Create a color-map using a gradient from "
+ "the gradient editor"), NULL);
+
+ gradient_name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (gradient_name,
+ wvals.ncolors,
+ wvals.gradinvert,
+ &n_gradient_samples,
+ &gradient_samples);
+
+ gradient = gimp_gradient_select_button_new (_("FractalExplorer Gradient"),
+ gradient_name);
+ g_signal_connect (gradient, "gradient-set",
+ G_CALLBACK (explorer_gradient_select_callback), NULL);
+ g_free (gradient_name);
+ gtk_box_pack_start (GTK_BOX (hbox), gradient, FALSE, FALSE, 0);
+ gtk_widget_show (gradient);
+
+ abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ {
+ gint xsize, ysize;
+
+ for (ysize = 1; ysize * ysize * ysize < 8192; ysize++) /**/;
+ xsize = wvals.ncolors / ysize;
+ while (xsize * ysize < 8192) xsize++;
+
+ gtk_widget_set_size_request (abox, xsize, ysize * 4);
+ }
+ gtk_box_pack_start (GTK_BOX (toggle_vbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ cmap_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (cmap_preview, 32, 32);
+ gtk_container_add (GTK_CONTAINER (abox), cmap_preview);
+ g_signal_connect (cmap_preview, "size-allocate",
+ G_CALLBACK (cmap_preview_size_allocate), NULL);
+ gtk_widget_show (cmap_preview);
+
+ frame = add_objects_list ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), frame,
+ gtk_label_new_with_mnemonic (_("_Fractals")));
+ gtk_widget_show (frame);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0);
+
+ gtk_widget_show (dialog);
+ ready_now = TRUE;
+
+ set_cmap_preview ();
+ dialog_update_preview ();
+
+ gtk_main ();
+
+ g_free (wint.wimage);
+
+ return wint.run;
+}
+
+
+/**********************************************************************
+ FUNCTION: dialog_update_preview
+ *********************************************************************/
+
+void
+dialog_update_preview (void)
+{
+ gint ycoord;
+ guchar *p_ul;
+
+ if (NULL == wint.preview)
+ return;
+
+ if (ready_now && wvals.alwayspreview)
+ {
+ xmin = wvals.xmin;
+ xmax = wvals.xmax;
+ ymin = wvals.ymin;
+ ymax = wvals.ymax;
+ xbild = preview_width;
+ ybild = preview_height;
+ xdiff = (xmax - xmin) / xbild;
+ ydiff = (ymax - ymin) / ybild;
+
+ p_ul = wint.wimage;
+
+ for (ycoord = 0; ycoord < preview_height; ycoord++)
+ {
+ explorer_render_row (NULL,
+ p_ul,
+ ycoord,
+ preview_width,
+ 3);
+ p_ul += preview_width * 3;
+ }
+
+ preview_redraw ();
+ }
+}
+
+/**********************************************************************
+ FUNCTION: cmap_preview_size_allocate()
+ *********************************************************************/
+
+static void
+cmap_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ gint i;
+ gint x;
+ gint y;
+ gint j;
+ guchar *b;
+ GimpPreviewArea *preview = GIMP_PREVIEW_AREA (widget);
+
+ b = g_new (guchar, allocation->width * allocation->height * 3);
+
+ for (y = 0; y < allocation->height; y++)
+ {
+ for (x = 0; x < allocation->width; x++)
+ {
+ i = x + (y / 4) * allocation->width;
+ if (i > wvals.ncolors)
+ {
+ for (j = 0; j < 3; j++)
+ b[(y*allocation->width + x) * 3 + j] = 0;
+ }
+ else
+ {
+ b[(y*allocation->width + x) * 3] = colormap[i].r;
+ b[(y*allocation->width + x) * 3 + 1] = colormap[i].g;
+ b[(y*allocation->width + x) * 3 + 2] = colormap[i].b;
+ }
+ }
+ }
+ gimp_preview_area_draw (preview,
+ 0, 0, allocation->width, allocation->height,
+ GIMP_RGB_IMAGE, b, allocation->width*3);
+ gtk_widget_queue_draw (cmap_preview);
+
+ g_free (b);
+
+}
+
+/**********************************************************************
+ FUNCTION: set_cmap_preview()
+ *********************************************************************/
+
+void
+set_cmap_preview (void)
+{
+ gint xsize, ysize;
+
+ if (NULL == cmap_preview)
+ return;
+
+ make_color_map ();
+
+ for (ysize = 1; ysize * ysize * ysize < wvals.ncolors; ysize++)
+ /**/;
+ xsize = wvals.ncolors / ysize;
+ while (xsize * ysize < wvals.ncolors)
+ xsize++;
+
+ gtk_widget_set_size_request (cmap_preview, xsize, ysize * 4);
+}
+
+/**********************************************************************
+ FUNCTION: make_color_map()
+ *********************************************************************/
+
+void
+make_color_map (void)
+{
+ gint i;
+ gint r;
+ gint gr;
+ gint bl;
+ gdouble redstretch;
+ gdouble greenstretch;
+ gdouble bluestretch;
+ gdouble pi = atan (1) * 4;
+
+ /* get gradient samples if they don't exist -- fixes gradient color
+ * mode for noninteractive use (bug #103470).
+ */
+ if (gradient_samples == NULL)
+ {
+ gchar *gradient_name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (gradient_name,
+ wvals.ncolors,
+ wvals.gradinvert,
+ &n_gradient_samples,
+ &gradient_samples);
+
+ g_free (gradient_name);
+ }
+
+ redstretch = wvals.redstretch * 127.5;
+ greenstretch = wvals.greenstretch * 127.5;
+ bluestretch = wvals.bluestretch * 127.5;
+
+ for (i = 0; i < wvals.ncolors; i++)
+ if (wvals.colormode == 1)
+ {
+ colormap[i].r = (guchar)(gradient_samples[i * 4] * 255.9);
+ colormap[i].g = (guchar)(gradient_samples[i * 4 + 1] * 255.9);
+ colormap[i].b = (guchar)(gradient_samples[i * 4 + 2] * 255.9);
+ }
+ else
+ {
+ double x = (i*2.0) / wvals.ncolors;
+ r = gr = bl = 0;
+
+ switch (wvals.redmode)
+ {
+ case SINUS:
+ r = (int) redstretch *(1.0 + sin((x - 1) * pi));
+ break;
+ case COSINUS:
+ r = (int) redstretch *(1.0 + cos((x - 1) * pi));
+ break;
+ case NONE:
+ r = (int)(redstretch *(x));
+ break;
+ default:
+ break;
+ }
+
+ switch (wvals.greenmode)
+ {
+ case SINUS:
+ gr = (int) greenstretch *(1.0 + sin((x - 1) * pi));
+ break;
+ case COSINUS:
+ gr = (int) greenstretch *(1.0 + cos((x - 1) * pi));
+ break;
+ case NONE:
+ gr = (int)(greenstretch *(x));
+ break;
+ default:
+ break;
+ }
+
+ switch (wvals.bluemode)
+ {
+ case SINUS:
+ bl = (int) bluestretch * (1.0 + sin ((x - 1) * pi));
+ break;
+ case COSINUS:
+ bl = (int) bluestretch * (1.0 + cos ((x - 1) * pi));
+ break;
+ case NONE:
+ bl = (int) (bluestretch * x);
+ break;
+ default:
+ break;
+ }
+
+ r = MIN (r, 255);
+ gr = MIN (gr, 255);
+ bl = MIN (bl, 255);
+
+ if (wvals.redinvert)
+ r = 255 - r;
+
+ if (wvals.greeninvert)
+ gr = 255 - gr;
+
+ if (wvals.blueinvert)
+ bl = 255 - bl;
+
+ colormap[i].r = r;
+ colormap[i].g = gr;
+ colormap[i].b = bl;
+ }
+}
+
+/**********************************************************************
+ FUNCTION: dialog_change_scale
+ *********************************************************************/
+
+void
+dialog_change_scale (void)
+{
+ ready_now = FALSE;
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->xmin), wvals.xmin);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->xmax), wvals.xmax);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->ymin), wvals.ymin);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->ymax), wvals.ymax);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->iter), wvals.iter);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->cx), wvals.cx);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->cy), wvals.cy);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->red), wvals.redstretch);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->green),wvals.greenstretch);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (elements->blue), wvals.bluestretch);
+
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (elements->type[wvals.fractaltype]), TRUE);
+
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (elements->redmode[wvals.redmode]), TRUE);
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (elements->greenmode[wvals.greenmode]), TRUE);
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (elements->bluemode[wvals.bluemode]), TRUE);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->redinvert),
+ wvals.redinvert);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->greeninvert),
+ wvals.greeninvert);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (elements->blueinvert),
+ wvals.blueinvert);
+
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (elements->colormode[wvals.colormode]), TRUE);
+
+ ready_now = TRUE;
+}
+
+
+/**********************************************************************
+ FUNCTION: save_options
+ *********************************************************************/
+
+static void
+save_options (FILE * fp)
+{
+ gchar buf[64];
+
+ /* Save options */
+
+ fprintf (fp, "fractaltype: %i\n", wvals.fractaltype);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.xmin);
+ fprintf (fp, "xmin: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.xmax);
+ fprintf (fp, "xmax: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.ymin);
+ fprintf (fp, "ymin: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.ymax);
+ fprintf (fp, "ymax: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.iter);
+ fprintf (fp, "iter: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.cx);
+ fprintf (fp, "cx: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.cy);
+ fprintf (fp, "cy: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.redstretch * 128.0);
+ fprintf (fp, "redstretch: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.greenstretch * 128.0);
+ fprintf (fp, "greenstretch: %s\n", buf);
+
+ g_ascii_dtostr (buf, sizeof (buf), wvals.bluestretch * 128.0);
+ fprintf (fp, "bluestretch: %s\n", buf);
+
+ fprintf (fp, "redmode: %i\n", wvals.redmode);
+ fprintf (fp, "greenmode: %i\n", wvals.greenmode);
+ fprintf (fp, "bluemode: %i\n", wvals.bluemode);
+ fprintf (fp, "redinvert: %i\n", wvals.redinvert);
+ fprintf (fp, "greeninvert: %i\n", wvals.greeninvert);
+ fprintf (fp, "blueinvert: %i\n", wvals.blueinvert);
+ fprintf (fp, "colormode: %i\n", wvals.colormode);
+ fputs ("#**********************************************************************\n", fp);
+ fprintf(fp, "<EOF>\n");
+ fputs ("#**********************************************************************\n", fp);
+}
+
+static void
+save_callback (void)
+{
+ FILE *fp;
+ const gchar *savename = filename;
+
+ fp = g_fopen (savename, "wt+");
+
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (savename), g_strerror (errno));
+ return;
+ }
+
+ /* Write header out */
+ fputs (FRACTAL_HEADER, fp);
+ fputs ("#**********************************************************************\n", fp);
+ fputs ("# This is a data file for the Fractal Explorer plug-in for GIMP *\n", fp);
+ fputs ("#**********************************************************************\n", fp);
+
+ save_options (fp);
+
+ if (ferror (fp))
+ g_message (_("Could not write '%s': %s"),
+ gimp_filename_to_utf8 (savename), g_strerror (ferror (fp)));
+
+ fclose (fp);
+}
+
+static void
+save_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ filename = gtk_file_chooser_get_filename (chooser);
+
+ save_callback ();
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static void
+file_chooser_set_default_folder (GtkFileChooser *chooser)
+{
+ GList *path_list;
+ gchar *dir;
+
+ if (! fractalexplorer_path)
+ return;
+
+ path_list = gimp_path_parse (fractalexplorer_path, 256, FALSE, NULL);
+
+ dir = gimp_path_get_user_writable_dir (path_list);
+
+ if (! dir)
+ dir = g_strdup (gimp_directory ());
+
+ gtk_file_chooser_set_current_folder (chooser, dir);
+
+ g_free (dir);
+ gimp_path_free (path_list);
+}
+
+static void
+load_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ filename = gtk_file_chooser_get_filename (chooser);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ {
+ explorer_load ();
+ }
+
+ gtk_widget_show (maindlg);
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static void
+create_load_file_chooser (GtkWidget *widget,
+ GtkWidget *dialog)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ window =
+ gtk_file_chooser_dialog_new (_("Load Fractal Parameters"),
+ GTK_WINDOW (dialog),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ file_chooser_set_default_folder (GTK_FILE_CHOOSER (window));
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window);
+ g_signal_connect (window, "response",
+ G_CALLBACK (load_file_chooser_response),
+ window);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+static void
+create_save_file_chooser (GtkWidget *widget,
+ GtkWidget *dialog)
+{
+ static GtkWidget *window = NULL;
+
+ if (! window)
+ {
+ window =
+ gtk_file_chooser_dialog_new (_("Save Fractal Parameters"),
+ GTK_WINDOW (dialog),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (window),
+ TRUE);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window);
+ g_signal_connect (window, "response",
+ G_CALLBACK (save_file_chooser_response),
+ window);
+ }
+
+ if (tpath)
+ {
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (window), tpath);
+ }
+ else
+ {
+ file_chooser_set_default_folder (GTK_FILE_CHOOSER (window));
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+gchar*
+get_line (gchar *buf,
+ gint s,
+ FILE *from,
+ gint init)
+{
+ gint slen;
+ gchar *ret;
+
+ if (init)
+ line_no = 1;
+ else
+ line_no++;
+
+ do
+ {
+ ret = fgets (buf, s, from);
+ }
+ while (!ferror (from) && buf[0] == '#');
+
+ slen = strlen (buf);
+
+ /* The last newline is a pain */
+ if (slen > 0)
+ buf[slen - 1] = '\0';
+
+ if (ferror (from))
+ {
+ g_warning ("Error reading file");
+ return NULL;
+ }
+
+ return ret;
+}
+
+gint
+load_options (fractalexplorerOBJ *xxx,
+ FILE *fp)
+{
+ gchar load_buf[MAX_LOAD_LINE];
+ gchar str_buf[MAX_LOAD_LINE];
+ gchar opt_buf[MAX_LOAD_LINE];
+
+ /* default values */
+ xxx->opts = standardvals;
+ xxx->opts.gradinvert = FALSE;
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+
+ while (!feof (fp) && strcmp (load_buf, "<EOF>"))
+ {
+ /* Get option name */
+ sscanf (load_buf, "%255s %255s", str_buf, opt_buf);
+
+ if (!strcmp (str_buf, "fractaltype:"))
+ {
+ gint sp = 0;
+
+ sp = atoi (opt_buf);
+ if (sp < 0)
+ return -1;
+ xxx->opts.fractaltype = sp;
+ }
+ else if (!strcmp (str_buf, "xmin:"))
+ {
+ xxx->opts.xmin = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp (str_buf, "xmax:"))
+ {
+ xxx->opts.xmax = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp(str_buf, "ymin:"))
+ {
+ xxx->opts.ymin = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp (str_buf, "ymax:"))
+ {
+ xxx->opts.ymax = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp(str_buf, "redstretch:"))
+ {
+ gdouble sp = g_ascii_strtod (opt_buf, NULL);
+ xxx->opts.redstretch = sp / 128.0;
+ }
+ else if (!strcmp(str_buf, "greenstretch:"))
+ {
+ gdouble sp = g_ascii_strtod (opt_buf, NULL);
+ xxx->opts.greenstretch = sp / 128.0;
+ }
+ else if (!strcmp (str_buf, "bluestretch:"))
+ {
+ gdouble sp = g_ascii_strtod (opt_buf, NULL);
+ xxx->opts.bluestretch = sp / 128.0;
+ }
+ else if (!strcmp (str_buf, "iter:"))
+ {
+ xxx->opts.iter = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp(str_buf, "cx:"))
+ {
+ xxx->opts.cx = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp (str_buf, "cy:"))
+ {
+ xxx->opts.cy = g_ascii_strtod (opt_buf, NULL);
+ }
+ else if (!strcmp(str_buf, "redmode:"))
+ {
+ xxx->opts.redmode = atoi (opt_buf);
+ }
+ else if (!strcmp(str_buf, "greenmode:"))
+ {
+ xxx->opts.greenmode = atoi (opt_buf);
+ }
+ else if (!strcmp(str_buf, "bluemode:"))
+ {
+ xxx->opts.bluemode = atoi (opt_buf);
+ }
+ else if (!strcmp (str_buf, "redinvert:"))
+ {
+ xxx->opts.redinvert = atoi (opt_buf);
+ }
+ else if (!strcmp (str_buf, "greeninvert:"))
+ {
+ xxx->opts.greeninvert = atoi (opt_buf);
+ }
+ else if (!strcmp(str_buf, "blueinvert:"))
+ {
+ xxx->opts.blueinvert = atoi (opt_buf);
+ }
+ else if (!strcmp (str_buf, "colormode:"))
+ {
+ xxx->opts.colormode = atoi (opt_buf);
+ }
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+ }
+
+ return 0;
+}
+
+void
+explorer_load (void)
+{
+ FILE *fp;
+ gchar load_buf[MAX_LOAD_LINE];
+
+ g_assert (filename != NULL);
+
+ fp = g_fopen (filename, "rt");
+
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return;
+ }
+ get_line (load_buf, MAX_LOAD_LINE, fp, 1);
+
+ if (strncmp (FRACTAL_HEADER, load_buf, strlen (load_buf)))
+ {
+ g_message (_("'%s' is not a FractalExplorer file"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return;
+ }
+ if (load_options (current_obj,fp))
+ {
+ g_message (_("'%s' is corrupt. Line %d Option section incorrect"),
+ gimp_filename_to_utf8 (filename), line_no);
+ fclose (fp);
+ return;
+ }
+
+ wvals = current_obj->opts;
+
+ fclose (fp);
+}
diff --git a/plug-ins/fractal-explorer/fractal-explorer-dialogs.h b/plug-ins/fractal-explorer/fractal-explorer-dialogs.h
new file mode 100644
index 0000000..3c759be
--- /dev/null
+++ b/plug-ins/fractal-explorer/fractal-explorer-dialogs.h
@@ -0,0 +1,19 @@
+#ifndef __FRACTALEXPLORER_DIALOGS_H__
+#define __FRACTALEXPLORER_DIALOGS_H__
+
+gint explorer_dialog (void);
+void dialog_update_preview (void);
+
+void set_cmap_preview (void);
+void make_color_map (void);
+
+void dialog_change_scale (void);
+gchar * get_line (gchar *buf,
+ gint s,
+ FILE *from,
+ gint init);
+gint load_options (fractalexplorerOBJ *xxx,
+ FILE *fp);
+void explorer_load (void);
+
+#endif
diff --git a/plug-ins/fractal-explorer/fractal-explorer.c b/plug-ins/fractal-explorer/fractal-explorer.c
new file mode 100644
index 0000000..3322464
--- /dev/null
+++ b/plug-ins/fractal-explorer/fractal-explorer.c
@@ -0,0 +1,1201 @@
+/**********************************************************************
+ Fractal Explorer Plug-in (Version 2.00 Beta 2)
+ Daniel Cotting (cotting@multimania.com)
+ **********************************************************************
+ **********************************************************************
+ Official homepages: http://www.multimania.com/cotting
+ http://cotting.citeweb.net
+ *********************************************************************/
+
+/**********************************************************************
+ GIMP - The GNU Image Manipulation Program
+ Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *********************************************************************/
+
+/**********************************************************************
+ Some code has been 'stolen' from:
+ - Peter Kirchgessner (Pkirchg@aol.com)
+ - Scott Draves (spot@cs.cmu.edu)
+ - Andy Thomas (alt@picnic.demon.co.uk)
+ .
+ .
+ .
+ **********************************************************************
+ "If you steal from one author it's plagiarism; if you steal from
+ many it's research." --Wilson Mizner
+ *********************************************************************/
+
+
+/**********************************************************************
+ Include necessary files
+ *********************************************************************/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include <libgimpbase/gimpwin32-io.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "fractal-explorer.h"
+#include "fractal-explorer-dialogs.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/**********************************************************************
+ Global variables
+ *********************************************************************/
+
+gdouble xmin = -2;
+gdouble xmax = 1;
+gdouble ymin = -1.5;
+gdouble ymax = 1.5;
+gdouble xbild;
+gdouble ybild;
+gdouble xdiff;
+gdouble ydiff;
+gint sel_x;
+gint sel_y;
+gint preview_width;
+gint preview_height;
+gdouble *gg;
+gint line_no;
+gchar *filename;
+clrmap colormap;
+vlumap valuemap;
+gchar *fractalexplorer_path = NULL;
+
+static gfloat cx = -0.75;
+static gfloat cy = -0.2;
+gint32 drawable_id;
+static GList *fractalexplorer_list = NULL;
+
+explorer_interface_t wint =
+{
+ NULL, /* preview */
+ NULL, /* wimage */
+ FALSE /* run */
+}; /* wint */
+
+explorer_vals_t wvals =
+{
+ 0,
+ -2.0,
+ 2.0,
+ -1.5,
+ 1.5,
+ 50.0,
+ -0.75,
+ -0.2,
+ 0,
+ 1.0,
+ 1.0,
+ 1.0,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 1,
+ 256,
+ 0,
+ 0
+}; /* wvals */
+
+fractalexplorerOBJ *current_obj = NULL;
+static GtkWidget *delete_dialog = NULL;
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void explorer (gint32 drawable_id);
+
+/**********************************************************************
+ Declare local functions
+ *********************************************************************/
+
+/* Functions for dialog widgets */
+
+static void delete_dialog_callback (GtkWidget *widget,
+ gboolean value,
+ gpointer data);
+static gboolean delete_fractal_callback (GtkWidget *widget,
+ gpointer data);
+static gint fractalexplorer_list_pos (fractalexplorerOBJ *feOBJ);
+static gint fractalexplorer_list_insert (fractalexplorerOBJ *feOBJ);
+static fractalexplorerOBJ *fractalexplorer_new (void);
+static void fill_list_store (GtkListStore *list_store);
+static void activate_fractal (fractalexplorerOBJ *sel_obj);
+static void activate_fractal_callback (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer data);
+static gboolean apply_fractal_callback (GtkWidget *widget,
+ gpointer data);
+
+static void fractalexplorer_free (fractalexplorerOBJ *feOBJ);
+static void fractalexplorer_free_everything (fractalexplorerOBJ *feOBJ);
+static void fractalexplorer_list_free_all (void);
+static fractalexplorerOBJ * fractalexplorer_load (const gchar *filename,
+ const gchar *name);
+
+static void fractalexplorer_list_load_all (const gchar *path);
+static void fractalexplorer_rescan_list (GtkWidget *widget,
+ gpointer data);
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/**********************************************************************
+ MAIN()
+ *********************************************************************/
+
+MAIN()
+
+/**********************************************************************
+ FUNCTION: query
+ *********************************************************************/
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT8, "fractaltype", "0: Mandelbrot; "
+ "1: Julia; "
+ "2: Barnsley 1; "
+ "3: Barnsley 2; "
+ "4: Barnsley 3; "
+ "5: Spider; "
+ "6: ManOWar; "
+ "7: Lambda; "
+ "8: Sierpinski" },
+ { GIMP_PDB_FLOAT, "xmin", "xmin fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "xmax", "xmax fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "ymin", "ymin fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "ymax", "ymax fractal image delimiter" },
+ { GIMP_PDB_FLOAT, "iter", "Iteration value" },
+ { GIMP_PDB_FLOAT, "cx", "cx value ( only Julia)" },
+ { GIMP_PDB_FLOAT, "cy", "cy value ( only Julia)" },
+ { GIMP_PDB_INT8, "colormode", "0: Apply colormap as specified by the parameters below; "
+ "1: Apply active gradient to final image" },
+ { GIMP_PDB_FLOAT, "redstretch", "Red stretching factor" },
+ { GIMP_PDB_FLOAT, "greenstretch", "Green stretching factor" },
+ { GIMP_PDB_FLOAT, "bluestretch", "Blue stretching factor" },
+ { GIMP_PDB_INT8, "redmode", "Red application mode (0:SIN;1:COS;2:NONE)" },
+ { GIMP_PDB_INT8, "greenmode", "Green application mode (0:SIN;1:COS;2:NONE)" },
+ { GIMP_PDB_INT8, "bluemode", "Blue application mode (0:SIN;1:COS;2:NONE)" },
+ { GIMP_PDB_INT8, "redinvert", "Red inversion mode (1: enabled; 0: disabled)" },
+ { GIMP_PDB_INT8, "greeninvert", "Green inversion mode (1: enabled; 0: disabled)" },
+ { GIMP_PDB_INT8, "blueinvert", "Green inversion mode (1: enabled; 0: disabled)" },
+ { GIMP_PDB_INT32, "ncolors", "Number of Colors for mapping (2<=ncolors<=8192)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Render fractal art"),
+ "No help yet.",
+ "Daniel Cotting (cotting@multimania.com, www.multimania.com/cotting)",
+ "Daniel Cotting (cotting@multimania.com, www.multimania.com/cotting)",
+ "December, 1998",
+ N_("_Fractal Explorer..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render/Fractals");
+}
+
+/**********************************************************************
+ FUNCTION: run
+ *********************************************************************/
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint pwidth;
+ gint pheight;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint sel_width;
+ gint sel_height;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &sel_x, &sel_y,
+ &sel_width, &sel_height))
+ return;
+
+ /* Calculate preview size */
+ if (sel_width > sel_height)
+ {
+ pwidth = MIN (sel_width, PREVIEW_SIZE);
+ pheight = sel_height * pwidth / sel_width;
+ }
+ else
+ {
+ pheight = MIN (sel_height, PREVIEW_SIZE);
+ pwidth = sel_width * pheight / sel_height;
+ }
+
+ preview_width = MAX (pwidth, 2);
+ preview_height = MAX (pheight, 2);
+
+ /* See how we will run */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data ("plug_in_fractalexplorer", &wvals);
+
+ /* Get information from the dialog */
+ if (!explorer_dialog ())
+ return;
+
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are present */
+ if (nparams != 22)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ wvals.fractaltype = param[3].data.d_int8;
+ wvals.xmin = param[4].data.d_float;
+ wvals.xmax = param[5].data.d_float;
+ wvals.ymin = param[6].data.d_float;
+ wvals.ymax = param[7].data.d_float;
+ wvals.iter = param[8].data.d_float;
+ wvals.cx = param[9].data.d_float;
+ wvals.cy = param[10].data.d_float;
+ wvals.colormode = param[11].data.d_int8;
+ wvals.redstretch = param[12].data.d_float;
+ wvals.greenstretch = param[13].data.d_float;
+ wvals.bluestretch = param[14].data.d_float;
+ wvals.redmode = param[15].data.d_int8;
+ wvals.greenmode = param[16].data.d_int8;
+ wvals.bluemode = param[17].data.d_int8;
+ wvals.redinvert = param[18].data.d_int8;
+ wvals.greeninvert = param[19].data.d_int8;
+ wvals.blueinvert = param[20].data.d_int8;
+ wvals.ncolors = CLAMP (param[21].data.d_int32, 2, MAXNCOLORS);
+ }
+ make_color_map();
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data ("plug_in_fractalexplorer", &wvals);
+ make_color_map ();
+ break;
+
+ default:
+ break;
+ }
+
+ xmin = wvals.xmin;
+ xmax = wvals.xmax;
+ ymin = wvals.ymin;
+ ymax = wvals.ymax;
+ cx = wvals.cx;
+ cy = wvals.cy;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimp_progress_init (_("Rendering fractal"));
+
+ explorer (drawable_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data ("plug_in_fractalexplorer",
+ &wvals, sizeof (explorer_vals_t));
+ }
+
+ values[0].data.d_status = status;
+}
+
+/**********************************************************************
+ FUNCTION: explorer
+ *********************************************************************/
+
+static void
+explorer (gint32 drawable_id)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ const Babl *format;
+ gint width;
+ gint height;
+ gint bpp;
+ gint row;
+ gint x;
+ gint y;
+ gint w;
+ gint h;
+ guchar *src_row;
+ guchar *dest_row;
+
+ /* Get the input area. This is the bounding box of the selection in
+ * the image (or the entire image if there is no selection). Only
+ * operating on the input area is simply an optimization. It doesn't
+ * need to be done for correct operation. (It simply makes it go
+ * faster, since fewer pixels need to be operated on).
+ */
+ if (! gimp_drawable_mask_intersect (drawable_id, &x, &y, &w, &h))
+ return;
+
+ /* Get the size of the input image. (This will/must be the same
+ * as the size of the output image.
+ */
+ width = gimp_drawable_width (drawable_id);
+ height = gimp_drawable_height (drawable_id);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ /* allocate row buffers */
+ src_row = g_new (guchar, bpp * w);
+ dest_row = g_new (guchar, bpp * w);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ xbild = width;
+ ybild = height;
+ xdiff = (xmax - xmin) / xbild;
+ ydiff = (ymax - ymin) / ybild;
+
+ for (row = y; row < y + h; row++)
+ {
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x, row, w, 1), 1.0,
+ format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ explorer_render_row (src_row,
+ dest_row,
+ row,
+ w,
+ bpp);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, row, w, 1), 0,
+ format, dest_row,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if ((row % 10) == 0)
+ gimp_progress_update ((double) row / (double) h);
+ }
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ g_free (src_row);
+ g_free (dest_row);
+
+ gimp_progress_update (1.0);
+
+ /* update the processed region */
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x, y, w, h);
+}
+
+/**********************************************************************
+ FUNCTION: explorer_render_row
+ *********************************************************************/
+
+void
+explorer_render_row (const guchar *src_row,
+ guchar *dest_row,
+ gint row,
+ gint row_width,
+ gint bpp)
+{
+ gint col;
+ gdouble a;
+ gdouble b;
+ gdouble x;
+ gdouble y;
+ gdouble oldx;
+ gdouble oldy;
+ gdouble tempsqrx;
+ gdouble tempsqry;
+ gdouble tmpx = 0;
+ gdouble tmpy = 0;
+ gdouble foldxinitx;
+ gdouble foldxinity;
+ gdouble foldyinitx;
+ gdouble foldyinity;
+ gdouble xx = 0;
+ gdouble adjust;
+ gdouble cx;
+ gdouble cy;
+ gint counter;
+ gint color;
+ gint iteration;
+ gint useloglog;
+ gdouble log2;
+
+ cx = wvals.cx;
+ cy = wvals.cy;
+ useloglog = wvals.useloglog;
+ iteration = wvals.iter;
+ log2 = log (2.0);
+
+ for (col = 0; col < row_width; col++)
+ {
+ a = xmin + (double) col * xdiff;
+ b = ymin + (double) row * ydiff;
+ if (wvals.fractaltype != 0)
+ {
+ tmpx = x = a;
+ tmpy = y = b;
+ }
+ else
+ {
+ x = 0;
+ y = 0;
+ }
+
+ for (counter = 0; counter < iteration; counter++)
+ {
+ oldx=x;
+ oldy=y;
+
+ switch (wvals.fractaltype)
+ {
+ case TYPE_MANDELBROT:
+ xx = x * x - y * y + a;
+ y = 2.0 * x * y + b;
+ break;
+
+ case TYPE_JULIA:
+ xx = x * x - y * y + cx;
+ y = 2.0 * x * y + cy;
+ break;
+
+ case TYPE_BARNSLEY_1:
+ foldxinitx = oldx * cx;
+ foldyinity = oldy * cy;
+ foldxinity = oldx * cy;
+ foldyinitx = oldy * cx;
+ /* orbit calculation */
+ if (oldx >= 0)
+ {
+ xx = (foldxinitx - cx - foldyinity);
+ y = (foldyinitx - cy + foldxinity);
+ }
+ else
+ {
+ xx = (foldxinitx + cx - foldyinity);
+ y = (foldyinitx + cy + foldxinity);
+ }
+ break;
+
+ case TYPE_BARNSLEY_2:
+ foldxinitx = oldx * cx;
+ foldyinity = oldy * cy;
+ foldxinity = oldx * cy;
+ foldyinitx = oldy * cx;
+ /* orbit calculation */
+ if (foldxinity + foldyinitx >= 0)
+ {
+ xx = foldxinitx - cx - foldyinity;
+ y = foldyinitx - cy + foldxinity;
+ }
+ else
+ {
+ xx = foldxinitx + cx - foldyinity;
+ y = foldyinitx + cy + foldxinity;
+ }
+ break;
+
+ case TYPE_BARNSLEY_3:
+ foldxinitx = oldx * oldx;
+ foldyinity = oldy * oldy;
+ foldxinity = oldx * oldy;
+ /* orbit calculation */
+ if (oldx > 0)
+ {
+ xx = foldxinitx - foldyinity - 1.0;
+ y = foldxinity * 2;
+ }
+ else
+ {
+ xx = foldxinitx - foldyinity -1.0 + cx * oldx;
+ y = foldxinity * 2;
+ y += cy * oldx;
+ }
+ break;
+
+ case TYPE_SPIDER:
+ /* { c=z=pixel: z=z*z+c; c=c/2+z, |z|<=4 } */
+ xx = x*x - y*y + tmpx + cx;
+ y = 2 * oldx * oldy + tmpy +cy;
+ tmpx = tmpx/2 + xx;
+ tmpy = tmpy/2 + y;
+ break;
+
+ case TYPE_MAN_O_WAR:
+ xx = x*x - y*y + tmpx + cx;
+ y = 2.0 * x * y + tmpy + cy;
+ tmpx = oldx;
+ tmpy = oldy;
+ break;
+
+ case TYPE_LAMBDA:
+ tempsqrx = x * x;
+ tempsqry = y * y;
+ tempsqrx = oldx - tempsqrx + tempsqry;
+ tempsqry = -(oldy * oldx);
+ tempsqry += tempsqry + oldy;
+ xx = cx * tempsqrx - cy * tempsqry;
+ y = cx * tempsqry + cy * tempsqrx;
+ break;
+
+ case TYPE_SIERPINSKI:
+ xx = oldx + oldx;
+ y = oldy + oldy;
+ if (oldy > .5)
+ y = y - 1;
+ else if (oldx > .5)
+ xx = xx - 1;
+ break;
+
+ default:
+ break;
+ }
+
+ x = xx;
+
+ if (((x * x) + (y * y)) >= 4.0)
+ break;
+ }
+
+ if (useloglog)
+ {
+ gdouble modulus_square = (x * x) + (y * y);
+
+ if (modulus_square > (G_E * G_E))
+ adjust = log (log (modulus_square) / 2.0) / log2;
+ else
+ adjust = 0.0;
+ }
+ else
+ {
+ adjust = 0.0;
+ }
+
+ color = (int) (((counter - adjust) * (wvals.ncolors - 1)) / iteration);
+ if (bpp >= 3)
+ {
+ dest_row[col * bpp + 0] = colormap[color].r;
+ dest_row[col * bpp + 1] = colormap[color].g;
+ dest_row[col * bpp + 2] = colormap[color].b;
+ }
+ else
+ dest_row[col * bpp + 0] = valuemap[color];
+
+ if (! ( bpp % 2))
+ dest_row [col * bpp + bpp - 1] = 255;
+
+ }
+}
+
+static void
+delete_dialog_callback (GtkWidget *widget,
+ gboolean delete,
+ gpointer data)
+{
+ GtkWidget *view = (GtkWidget *) data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean valid;
+ fractalexplorerOBJ *sel_obj;
+
+ if (delete)
+ {
+ /* Must update which object we are editing */
+ /* Get the list and which item is selected */
+ /* Only allow single selections */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_selection_get_selected (selection, &model, &iter);
+
+ gtk_tree_model_get (model, &iter, 1, &sel_obj, -1);
+
+ /* Delete the current item + associated file */
+ valid = gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
+
+ /* Try to select first item if last one was deleted */
+ if (!valid)
+ valid = gtk_tree_model_get_iter_first (model, &iter);
+
+ /* Shadow copy for ordering info */
+ fractalexplorer_list = g_list_remove (fractalexplorer_list, sel_obj);
+ /*
+ if(sel_obj == current_obj)
+ {
+ clear_undo();
+ }
+ */
+ /* Free current obj */
+ fractalexplorer_free_everything (sel_obj);
+
+ /* Check whether there are items left */
+ if (valid)
+ {
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ gtk_tree_model_get (model, &iter, 1, &current_obj, -1);
+ }
+ }
+
+ delete_dialog = NULL;
+}
+
+static gboolean
+delete_fractal_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gchar *str;
+ GtkWidget *view = (GtkWidget *) data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ fractalexplorerOBJ *sel_obj;
+
+ if (delete_dialog)
+ return FALSE;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter, 1, &sel_obj, -1);
+
+ str = g_strdup_printf (_("Are you sure you want to delete "
+ "\"%s\" from the list and from disk?"),
+ sel_obj->draw_name);
+
+ delete_dialog = gimp_query_boolean_box (_("Delete Fractal"),
+ gtk_widget_get_toplevel (view),
+ gimp_standard_help_func, NULL,
+ GIMP_ICON_DIALOG_QUESTION,
+ str,
+ _("_Delete"), _("_Cancel"),
+ G_OBJECT (widget), "destroy",
+ delete_dialog_callback,
+ data);
+ g_free (str);
+
+ gtk_widget_show (delete_dialog);
+ }
+
+ return FALSE;
+}
+
+static gint
+fractalexplorer_list_pos (fractalexplorerOBJ *fractalexplorer)
+{
+ fractalexplorerOBJ *g;
+ gint n;
+ GList *tmp;
+
+ n = 0;
+
+ for (tmp = fractalexplorer_list; tmp; tmp = g_list_next (tmp))
+ {
+ g = tmp->data;
+
+ if (strcmp (fractalexplorer->draw_name, g->draw_name) <= 0)
+ break;
+
+ n++;
+ }
+ return n;
+}
+
+static gint
+fractalexplorer_list_insert (fractalexplorerOBJ *fractalexplorer)
+{
+ gint n = fractalexplorer_list_pos (fractalexplorer);
+
+ /*
+ * Insert fractalexplorers in alphabetical order
+ */
+
+ fractalexplorer_list = g_list_insert (fractalexplorer_list,
+ fractalexplorer, n);
+
+ return n;
+}
+
+static fractalexplorerOBJ *
+fractalexplorer_new (void)
+{
+ return g_new0 (fractalexplorerOBJ, 1);
+}
+
+static void
+fill_list_store (GtkListStore *list_store)
+{
+ GList *tmp;
+ GtkTreeIter iter;
+
+
+ for (tmp = fractalexplorer_list; tmp; tmp = tmp->next)
+ {
+ fractalexplorerOBJ *g;
+ g = tmp->data;
+
+ gtk_list_store_append (list_store, &iter);
+ gtk_list_store_set (list_store, &iter, 0, g->draw_name, 1, g, -1);
+ }
+}
+
+static void
+activate_fractal (fractalexplorerOBJ *sel_obj)
+{
+ current_obj = sel_obj;
+ wvals = current_obj->opts;
+ dialog_change_scale ();
+ set_cmap_preview ();
+ dialog_update_preview ();
+}
+
+static void
+activate_fractal_callback (GtkTreeView *view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ fractalexplorerOBJ *sel_obj;
+
+ model = gtk_tree_view_get_model (view);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ {
+ gtk_tree_model_get (model, &iter, 1, &sel_obj, -1);
+ activate_fractal (sel_obj);
+ }
+
+}
+
+static gboolean
+apply_fractal_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *view = (GtkWidget *) data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ fractalexplorerOBJ *sel_obj;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter, 1, &sel_obj, -1);
+ activate_fractal (sel_obj);
+ }
+
+ return FALSE;
+}
+
+static void
+fractalexplorer_free (fractalexplorerOBJ *fractalexplorer)
+{
+ g_assert (fractalexplorer != NULL);
+
+ g_free (fractalexplorer->name);
+ g_free (fractalexplorer->filename);
+ g_free (fractalexplorer->draw_name);
+ g_free (fractalexplorer);
+}
+
+static void
+fractalexplorer_free_everything (fractalexplorerOBJ *fractalexplorer)
+{
+ g_assert (fractalexplorer != NULL);
+
+ if (fractalexplorer->filename)
+ g_remove (fractalexplorer->filename);
+
+ fractalexplorer_free (fractalexplorer);
+}
+
+static void
+fractalexplorer_list_free_all (void)
+{
+ g_list_free_full (fractalexplorer_list, (GDestroyNotify) fractalexplorer_free);
+ fractalexplorer_list = NULL;
+}
+
+static fractalexplorerOBJ *
+fractalexplorer_load (const gchar *filename,
+ const gchar *name)
+{
+ fractalexplorerOBJ * fractalexplorer;
+ FILE * fp;
+ gchar load_buf[MAX_LOAD_LINE];
+
+ g_assert (filename != NULL);
+
+ fp = g_fopen (filename, "rt");
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return NULL;
+ }
+
+ fractalexplorer = fractalexplorer_new ();
+
+ fractalexplorer->name = g_strdup (name);
+ fractalexplorer->draw_name = g_strdup (name);
+ fractalexplorer->filename = g_strdup (filename);
+
+ /* HEADER
+ * draw_name
+ * version
+ * obj_list
+ */
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 1);
+
+ if (strncmp (fractalexplorer_HEADER, load_buf, strlen (load_buf)))
+ {
+ g_message (_("File '%s' is not a FractalExplorer file"),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ fractalexplorer_free (fractalexplorer);
+
+ return NULL;
+ }
+
+ if (load_options (fractalexplorer, fp))
+ {
+ g_message (_("File '%s' is corrupt.\nLine %d Option section incorrect"),
+ gimp_filename_to_utf8 (filename), line_no);
+ fclose (fp);
+ fractalexplorer_free (fractalexplorer);
+
+ return NULL;
+ }
+
+ fclose (fp);
+
+ fractalexplorer->obj_status = fractalexplorer_OK;
+
+ return fractalexplorer;
+}
+
+static void
+fractalexplorer_list_load_all (const gchar *explorer_path)
+{
+ GList *path;
+ GList *list;
+
+ /* Make sure to clear any existing fractalexplorers */
+ current_obj = NULL;
+ fractalexplorer_list_free_all ();
+
+ path = gimp_config_path_expand_to_files (explorer_path, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children (list->data,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFileType file_type = g_file_info_get_file_type (info);
+
+ if (file_type == G_FILE_TYPE_REGULAR &&
+ ! g_file_info_get_is_hidden (info))
+ {
+ fractalexplorerOBJ *fractalexplorer;
+ GFile *child;
+ gchar *filename;
+ gchar *basename;
+
+ child = g_file_enumerator_get_child (enumerator, info);
+
+ filename = g_file_get_path (child);
+ basename = g_file_get_basename (child);
+
+ fractalexplorer = fractalexplorer_load (filename,
+ basename);
+
+ g_free (filename);
+ g_free (basename);
+
+ if (fractalexplorer)
+ fractalexplorer_list_insert (fractalexplorer);
+
+ g_object_unref (child);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ if (!fractalexplorer_list)
+ {
+ fractalexplorerOBJ *fractalexplorer;
+
+ /* lets have at least one! */
+ fractalexplorer = fractalexplorer_new ();
+ fractalexplorer->draw_name = g_strdup (_("My first fractal"));
+ fractalexplorer_list_insert (fractalexplorer);
+ }
+ current_obj = fractalexplorer_list->data; /* set to first entry */
+}
+
+
+GtkWidget *
+add_objects_list (void)
+{
+ GtkWidget *table;
+ GtkWidget *scrolled_win;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *renderer;
+ GtkWidget *view;
+ GtkTreeSelection *selection;
+ GtkListStore *list_store;
+ GtkWidget *button;
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_widget_show (table);
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_IN);
+ gtk_table_attach (GTK_TABLE (table), scrolled_win, 0, 3, 0, 1,
+ GTK_FILL|GTK_EXPAND , GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_widget_show (scrolled_win);
+
+ view = gtk_tree_view_new ();
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), col);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (col, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (col, renderer, "text", 0);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (view, "row-activated",
+ G_CALLBACK (activate_fractal_callback),
+ NULL);
+ gtk_container_add (GTK_CONTAINER (scrolled_win), view);
+ gtk_widget_show (view);
+
+ fractalexplorer_list_load_all (fractalexplorer_path);
+ list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+ fill_list_store (list_store);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (list_store));
+ g_object_unref (list_store); /* destroy model automatically with view */
+
+ /* Put buttons in */
+ button = gtk_button_new_with_mnemonic (_("_Refresh"));
+ gtk_table_attach (GTK_TABLE (table), button, 0, 1, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Select folder and rescan collection"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (fractalexplorer_rescan_list),
+ view);
+
+ button = gtk_button_new_with_mnemonic (_("_Apply"));
+ gtk_table_attach (GTK_TABLE (table), button, 1, 2, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Apply currently selected fractal"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (apply_fractal_callback),
+ view);
+
+ button = gtk_button_new_with_mnemonic (_("_Delete"));
+ gtk_table_attach (GTK_TABLE (table), button, 2, 3, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button,
+ _("Delete currently selected fractal"), NULL);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (delete_fractal_callback),
+ view);
+
+ return table;
+}
+
+static void
+fractalexplorer_rescan_list (GtkWidget *widget,
+ gpointer data)
+{
+ static GtkWidget *dlg = NULL;
+ GtkWidget *view = data;
+ GtkWidget *patheditor;
+
+ if (dlg)
+ {
+ gtk_window_present (GTK_WINDOW (dlg));
+ return;
+ }
+
+ dlg = gimp_dialog_new (_("Rescan for Fractals"), PLUG_IN_ROLE,
+ gtk_widget_get_toplevel (view),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dlg);
+
+ patheditor = gimp_path_editor_new (_("Add FractalExplorer Path"),
+ fractalexplorer_path);
+ gtk_container_set_border_width (GTK_CONTAINER (patheditor), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ patheditor, TRUE, TRUE, 0);
+ gtk_widget_show (patheditor);
+
+ if (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK)
+ {
+ g_free (fractalexplorer_path);
+ fractalexplorer_path =
+ gimp_path_editor_get_path (GIMP_PATH_EDITOR (patheditor));
+
+ if (fractalexplorer_path)
+ {
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ fractalexplorer_list_load_all (fractalexplorer_path);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+ gtk_list_store_clear (GTK_LIST_STORE (model));
+ fill_list_store (GTK_LIST_STORE (model));
+
+ /* select active fractal, otherwise first fractal */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gtk_tree_selection_select_iter (selection, &iter);
+ path = gtk_tree_model_get_path (model, &iter);
+ current_obj = fractalexplorer_list->data;
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, NULL,
+ FALSE, 0.0, 0.0);
+ gtk_tree_path_free (path);
+ }
+ }
+ }
+
+ gtk_widget_destroy (dlg);
+}
diff --git a/plug-ins/fractal-explorer/fractal-explorer.h b/plug-ins/fractal-explorer/fractal-explorer.h
new file mode 100644
index 0000000..2792e47
--- /dev/null
+++ b/plug-ins/fractal-explorer/fractal-explorer.h
@@ -0,0 +1,216 @@
+#ifndef __FRACTALEXPLORER_H__
+#define __FRACTALEXPLORER_H__
+
+
+/**********************************************************************
+ Magic numbers
+ *********************************************************************/
+
+#define PREVIEW_SIZE 256
+#define SCALE_WIDTH 200
+#define ENTRY_WIDTH 60
+#define MAX_LOAD_LINE 256
+#define GR_WIDTH 325
+
+#define MAXNCOLORS 8192
+#define MAXSTRLEN 256
+
+#define PLUG_IN_PROC "plug-in-fractalexplorer"
+#define PLUG_IN_BINARY "fractal-explorer"
+#define PLUG_IN_ROLE "gimp-fractal-explorer"
+
+#define FRACTAL_HEADER "Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>\n"
+#define fractalexplorer_HEADER "Fractal Explorer Plug-In Version 2 - (c) 1997 <cotting@mygale.org>\n"
+
+enum
+{
+ SINUS,
+ COSINUS,
+ NONE
+};
+
+enum
+{
+ TYPE_MANDELBROT,
+ TYPE_JULIA,
+ TYPE_BARNSLEY_1,
+ TYPE_BARNSLEY_2,
+ TYPE_BARNSLEY_3,
+ TYPE_SPIDER,
+ TYPE_MAN_O_WAR,
+ TYPE_LAMBDA,
+ TYPE_SIERPINSKI,
+ NUM_TYPES
+};
+
+/**********************************************************************
+ Types
+ *********************************************************************/
+
+typedef struct
+{
+ gint fractaltype;
+ gdouble xmin;
+ gdouble xmax;
+ gdouble ymin;
+ gdouble ymax;
+ gdouble iter;
+ gdouble cx;
+ gdouble cy;
+ gint colormode;
+ gdouble redstretch;
+ gdouble greenstretch;
+ gdouble bluestretch;
+ gint redmode;
+ gint greenmode;
+ gint bluemode;
+ gboolean redinvert;
+ gboolean greeninvert;
+ gboolean blueinvert;
+ gboolean alwayspreview;
+ gint ncolors;
+ gboolean gradinvert;
+ gboolean useloglog;
+} explorer_vals_t;
+
+typedef struct
+{
+ GtkWidget *preview;
+ guchar *wimage;
+ gint run;
+} explorer_interface_t;
+
+/* typedef gint colorvalue[3]; */
+typedef struct
+ {
+ guchar r, g, b;
+ } gucharRGB;
+
+typedef gucharRGB clrmap[MAXNCOLORS];
+
+typedef guchar vlumap[MAXNCOLORS];
+
+typedef struct
+{
+ GtkWidget *text;
+ GtkAdjustment *data;
+} scaledata;
+
+typedef struct _DialogElements DialogElements;
+
+struct _DialogElements
+{
+ GtkWidget *type[NUM_TYPES];
+ GtkObject *xmin;
+ GtkObject *xmax;
+ GtkObject *ymin;
+ GtkObject *ymax;
+ GtkObject *iter;
+ GtkObject *cx;
+ GtkObject *cy;
+
+ GtkObject *ncol;
+ GtkWidget *useloglog;
+
+ GtkObject *red;
+ GtkObject *green;
+ GtkObject *blue;
+
+ GtkWidget *redmode[3];
+ GtkWidget *redinvert;
+
+ GtkWidget *greenmode[3];
+ GtkWidget *greeninvert;
+
+ GtkWidget *bluemode[3];
+ GtkWidget *blueinvert;
+
+ GtkWidget *colormode[2];
+};
+
+
+typedef struct DFigObj
+{
+ gchar *name; /* Trailing name of file */
+ gchar *filename; /* Filename itself */
+ gchar *draw_name; /* Name of the drawing */
+ explorer_vals_t opts; /* Options enforced when fig saved */
+ GtkWidget *list_item;
+ GtkWidget *label_widget;
+ GtkWidget *pixmap_widget;
+ gint obj_status;
+} fractalexplorerOBJ;
+
+
+typedef struct GigObj
+{
+ gchar *name; /* Trailing name of file */
+ gchar *filename; /* Filename itself */
+ gchar *draw_name; /* Name of the drawing */
+ gint typus;
+ GtkWidget *list_item;
+ GtkWidget *label_widget;
+ GtkWidget *pixmap_widget;
+ gint obj_status;
+} gradientOBJ;
+
+typedef struct _fractalexplorerListOptions
+{
+ GtkWidget *query_box;
+ GtkWidget *name_entry;
+ GtkWidget *list_entry;
+ fractalexplorerOBJ *obj;
+ gint created;
+} fractalexplorerListOptions;
+
+/* States of the object */
+#define fractalexplorer_OK 0x0
+#define fractalexplorer_MODIFIED 0x1
+
+#define gradient_GRADIENTEDITOR 0x2
+
+extern fractalexplorerOBJ *current_obj;
+
+GtkWidget * add_objects_list (void);
+
+/**********************************************************************
+ Global variables
+ *********************************************************************/
+
+extern gdouble xmin;
+extern gdouble xmax;
+extern gdouble ymin;
+extern gdouble ymax;
+extern gdouble xbild;
+extern gdouble ybild;
+extern gdouble xdiff;
+extern gdouble ydiff;
+extern gint sel_x1,
+ sel_y1,
+ sel_x2,
+ sel_y2;
+extern gint preview_width,
+ preview_height;
+extern gdouble *gg;
+extern int line_no;
+extern gchar *filename;
+extern clrmap colormap;
+extern gchar *fractalexplorer_path;
+
+
+extern explorer_interface_t wint;
+
+extern explorer_vals_t wvals;
+extern GimpDrawable *drawable;
+
+
+/**********************************************************************
+ Global functions
+ *********************************************************************/
+
+void explorer_render_row (const guchar *src_row,
+ guchar *dest_row,
+ gint row,
+ gint row_width,
+ gint bpp);
+#endif
diff --git a/plug-ins/gfig/Makefile.am b/plug-ins/gfig/Makefile.am
new file mode 100644
index 0000000..283ad92
--- /dev/null
+++ b/plug-ins/gfig/Makefile.am
@@ -0,0 +1,85 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+gfig_RC = gfig.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+SUBDIRS = gfig-examples images
+
+libexecdir = $(gimpplugindir)/plug-ins/gfig
+
+libexec_PROGRAMS = gfig
+
+gfig_SOURCES = \
+ gfig.c \
+ gfig.h \
+ gfig-arc.c \
+ gfig-arc.h \
+ gfig-bezier.c \
+ gfig-bezier.h \
+ gfig-circle.c \
+ gfig-circle.h \
+ gfig-dialog.c \
+ gfig-dialog.h \
+ gfig-dobject.c \
+ gfig-dobject.h \
+ gfig-ellipse.c \
+ gfig-ellipse.h \
+ gfig-grid.c \
+ gfig-grid.h \
+ gfig-line.c \
+ gfig-line.h \
+ gfig-poly.c \
+ gfig-poly.h \
+ gfig-preview.c \
+ gfig-preview.h \
+ gfig-rectangle.c \
+ gfig-rectangle.h \
+ gfig-spiral.c \
+ gfig-spiral.h \
+ gfig-star.c \
+ gfig-star.h \
+ gfig-stock.c \
+ gfig-stock.h \
+ gfig-style.c \
+ gfig-style.h \
+ gfig-types.h
+
+EXTRA_DIST = README
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gfig_RC)
diff --git a/plug-ins/gfig/Makefile.in b/plug-ins/gfig/Makefile.in
new file mode 100644
index 0000000..bd5d3d2
--- /dev/null
+++ b/plug-ins/gfig/Makefile.in
@@ -0,0 +1,1209 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = gfig$(EXEEXT)
+subdir = plug-ins/gfig
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_gfig_OBJECTS = gfig.$(OBJEXT) gfig-arc.$(OBJEXT) \
+ gfig-bezier.$(OBJEXT) gfig-circle.$(OBJEXT) \
+ gfig-dialog.$(OBJEXT) gfig-dobject.$(OBJEXT) \
+ gfig-ellipse.$(OBJEXT) gfig-grid.$(OBJEXT) gfig-line.$(OBJEXT) \
+ gfig-poly.$(OBJEXT) gfig-preview.$(OBJEXT) \
+ gfig-rectangle.$(OBJEXT) gfig-spiral.$(OBJEXT) \
+ gfig-star.$(OBJEXT) gfig-stock.$(OBJEXT) gfig-style.$(OBJEXT)
+gfig_OBJECTS = $(am_gfig_OBJECTS)
+gfig_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+gfig_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(gfig_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gfig-arc.Po \
+ ./$(DEPDIR)/gfig-bezier.Po ./$(DEPDIR)/gfig-circle.Po \
+ ./$(DEPDIR)/gfig-dialog.Po ./$(DEPDIR)/gfig-dobject.Po \
+ ./$(DEPDIR)/gfig-ellipse.Po ./$(DEPDIR)/gfig-grid.Po \
+ ./$(DEPDIR)/gfig-line.Po ./$(DEPDIR)/gfig-poly.Po \
+ ./$(DEPDIR)/gfig-preview.Po ./$(DEPDIR)/gfig-rectangle.Po \
+ ./$(DEPDIR)/gfig-spiral.Po ./$(DEPDIR)/gfig-star.Po \
+ ./$(DEPDIR)/gfig-stock.Po ./$(DEPDIR)/gfig-style.Po \
+ ./$(DEPDIR)/gfig.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(gfig_SOURCES)
+DIST_SOURCES = $(gfig_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/gfig
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@gfig_RC = gfig.rc.o
+AM_LDFLAGS = $(mwindows)
+SUBDIRS = gfig-examples images
+gfig_SOURCES = \
+ gfig.c \
+ gfig.h \
+ gfig-arc.c \
+ gfig-arc.h \
+ gfig-bezier.c \
+ gfig-bezier.h \
+ gfig-circle.c \
+ gfig-circle.h \
+ gfig-dialog.c \
+ gfig-dialog.h \
+ gfig-dobject.c \
+ gfig-dobject.h \
+ gfig-ellipse.c \
+ gfig-ellipse.h \
+ gfig-grid.c \
+ gfig-grid.h \
+ gfig-line.c \
+ gfig-line.h \
+ gfig-poly.c \
+ gfig-poly.h \
+ gfig-preview.c \
+ gfig-preview.h \
+ gfig-rectangle.c \
+ gfig-rectangle.h \
+ gfig-spiral.c \
+ gfig-spiral.h \
+ gfig-star.c \
+ gfig-star.h \
+ gfig-stock.c \
+ gfig-stock.h \
+ gfig-style.c \
+ gfig-style.h \
+ gfig-types.h
+
+EXTRA_DIST = README
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gfig_RC)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gfig/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gfig/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+gfig$(EXEEXT): $(gfig_OBJECTS) $(gfig_DEPENDENCIES) $(EXTRA_gfig_DEPENDENCIES)
+ @rm -f gfig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gfig_OBJECTS) $(gfig_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-arc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-bezier.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-circle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-dobject.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-ellipse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-grid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-line.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-poly.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-rectangle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-spiral.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-star.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-stock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig-style.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gfig.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/gfig-arc.Po
+ -rm -f ./$(DEPDIR)/gfig-bezier.Po
+ -rm -f ./$(DEPDIR)/gfig-circle.Po
+ -rm -f ./$(DEPDIR)/gfig-dialog.Po
+ -rm -f ./$(DEPDIR)/gfig-dobject.Po
+ -rm -f ./$(DEPDIR)/gfig-ellipse.Po
+ -rm -f ./$(DEPDIR)/gfig-grid.Po
+ -rm -f ./$(DEPDIR)/gfig-line.Po
+ -rm -f ./$(DEPDIR)/gfig-poly.Po
+ -rm -f ./$(DEPDIR)/gfig-preview.Po
+ -rm -f ./$(DEPDIR)/gfig-rectangle.Po
+ -rm -f ./$(DEPDIR)/gfig-spiral.Po
+ -rm -f ./$(DEPDIR)/gfig-star.Po
+ -rm -f ./$(DEPDIR)/gfig-stock.Po
+ -rm -f ./$(DEPDIR)/gfig-style.Po
+ -rm -f ./$(DEPDIR)/gfig.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/gfig-arc.Po
+ -rm -f ./$(DEPDIR)/gfig-bezier.Po
+ -rm -f ./$(DEPDIR)/gfig-circle.Po
+ -rm -f ./$(DEPDIR)/gfig-dialog.Po
+ -rm -f ./$(DEPDIR)/gfig-dobject.Po
+ -rm -f ./$(DEPDIR)/gfig-ellipse.Po
+ -rm -f ./$(DEPDIR)/gfig-grid.Po
+ -rm -f ./$(DEPDIR)/gfig-line.Po
+ -rm -f ./$(DEPDIR)/gfig-poly.Po
+ -rm -f ./$(DEPDIR)/gfig-preview.Po
+ -rm -f ./$(DEPDIR)/gfig-rectangle.Po
+ -rm -f ./$(DEPDIR)/gfig-spiral.Po
+ -rm -f ./$(DEPDIR)/gfig-star.Po
+ -rm -f ./$(DEPDIR)/gfig-stock.Po
+ -rm -f ./$(DEPDIR)/gfig-style.Po
+ -rm -f ./$(DEPDIR)/gfig.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gfig/README b/plug-ins/gfig/README
new file mode 100644
index 0000000..36076e8
--- /dev/null
+++ b/plug-ins/gfig/README
@@ -0,0 +1,313 @@
+Objects
++++++++
+
+The plug-in allows you to draw the following shapes:-
+
+Lines
+Circles
+Ellipses
+n sided regular polygons (n >=3)
+Curves (arcs)
+Spirals
+N pointed stars
+Simple Beizer curves
+
+An object is constructed as a number of control points and these are used
+to select the object on the drawing area once it has been created.
+
+The drawing area preview is currently set to
+
+ MIN(650 x 650,img_width x img_height).
+
+You can change this altering the PREVIEW_SIZE item in the source code.
+
+Operations
+++++++++++
+
+The following operations can be performed on objects:-
+
+The objects can either be moved independently around the drawing area or
+all together.
+
+Each control point can be moved.
+
+Objects can be copied.
+
+Objects can be deleted.
+
+N sided Polygons can be split into N line segments. This also works for N
+pointed stars.
+
+The drawing area can be scaled.
+
+Control points can be hidden.
+
+To aid alignment of objects a "grid" can be placed on the drawing area.
+This can be either a normal rectangular grid or a polar type grid centered
+in the drawing area.
+The spacings of the grid can be changed as well as the colors used to draw the
+grid lines. When "Snap to grid" is checked then all operations will be
+constrained to fall on a grid intersection. (Fun when using the polar grid).
+Additionally the third mouse button will constrain the point movement to be
+on a horiz/vert (for rect grid) or radial/radius (for polar) only when
+"Snap to grid" is set.
+
+The image that was selected when Gfig was started can also be shown (scaled) in
+the drawing area. (Options tag).
+
+Painting tab
+++++++++++++
+
+There are three possible mode when painting.
+
+1) Brush
+2) Selection
+3) Selection+fill
+
+Brush
+~~~~~
+When the paint button is pressed each object will be rendered/drawn onto
+the image using the currently selected ink color. The objects can be drawn
+onto the original layer or onto a new layer (either a single layer or
+multiple layers - latter is good for animations). When new layers are
+created the background can either be:-
+
+Transparent.
+Background (the current BG color).
+White.
+Copy.
+
+The last option means that the layer is duplicated from the previous layer
+before the draw operation is performed (re good for animations).
+
+Lines can be drawn in reverse order. This means that reg polygons/curves/lines
+that are normally constructed of lines can be drawn starting at either "end".
+This is only noticeable when the current brush has fading turned on.
+The "Approx Circles/Ellipses" toggle allows the same effects when drawing these
+types of objects.
+
+Note that in the current version any selections present in the image are first
+de-selected when Gfig starts up. This is because selections are used to
+draw circles and ellipses (unless the Approx. Circles & Ellipses toggle is set).
+
+Selection
+~~~~~~~~~
+With this method closed selections are made of the objects. See selection
+tab for more details.
+
+Selections can only be made on the current layer.
+
+Selection+Fill
+~~~~~~~~~~~~~~
+This method first selects the objects and then fills the selection area. The
+selection tab gives more details on how the selections are filled.
+
+
+Brush Tab
++++++++++
+
+This tab now contains a preview of the selected brush in black ink. If the
+brush is too large to fit in the preview use the mouse button to "move" it
+around (as with the brush dialog in the main GIMP - until that brush
+patch went in!).
+
+Four types of brush can be used:-
+
+Normal brush
+~~~~~~~~~~~~
+Simply paints with the current brush - honors fading if any
+The Fade option allows the "brush fading" to be selected. I am sure that some
+nifty anims can be created with this option.
+
+Pencil brush
+~~~~~~~~~~~~
+Same as the GIMPS pencil.
+
+Airbrush
+~~~~~~~~
+As GIMPS airbrush. The "pressure" can be specified and this is reflected in the
+preview window.
+
+Pattern
+~~~~~~~
+The lines are drawn with the currently selected pattern. The preview will
+show this pattern.
+
+Note a patch to enable a couple of "hidden" paintbrush features was recently
+posted to the GIMP developers list. The patch only allowed these features
+to be called via the UI and not the PDB so they cannot be used by Gfig. If they were exported via the PDB then they would have probably broken a number of
+scripts and Gfig itself (number of args changes on the PDB call).
+
+Select tab
+++++++++++
+
+This is enabled when using either Selection or Selection+fill types when
+painting objects.
+
+The anti aliasing and feather toggles apply to the selection made when
+"painting" the objects. The feather radius can be changed.
+
+The default selection mode is to add to the current selection so each object
+add to the overall selection. The other selection modes are also possible
+but you must bear in mind the following:-
+
+Subtraction/Intersection will not produce any selection if no selection is
+elect the area in the target window (AFTER starting Gfig up) and then paint
+using this method.
+
+Replace will leave only the last object selection.
+
+When the paint type is selection+fill then the objects will be selected
+and then filled. The opacity of the fill can be chosen. By default a fill
+is performed after each section. If filling onto a single layer
+then the fill will accumulate on the first objects painted. This will be
+noticeable when opacity != 100. To get around this then use replace mode
+OR use a new layer for each selection/fill. (Paint tab). Additionally you can
+set the fill to be performed after ALL objects have been selected (useful
+for intersection type selections).
+
+Selection of non-closed objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For objects that are not closed the first & last points will be joined
+together to form the closed selection. For arcs you can either fill
+as segments or sectors. Note straight lines will not cause any selection to be
+made. This can be a little confusing when filling since the whole canvas will
+be filled if any straight lines are present on it.
+
+
+Scaling
++++++++
+
+When Gfig first comes up the selected group of objects are automatically scaled
+upwards to the image size. (If the source image is < 256*256 then no automatic
+scaling is performed - change PREVIEW_SIZE in source to alter this values).
+By toggling the scale button the objects can then be scaled in the drawing
+area. This scaling will be reflected when the objects are drawn.
+To reposition the objects on the drawing area use SHIFT + MOVE_OBJ operation.
+
+
+Other ops
++++++++++
+
+Double click on reg polygon object selection to get dialog with slider to
+selected the number of sides to draw.
+
+Double click on star object selection to get dialog with slider to
+selected the number of points the star will have.
+
+Double click on spiral object selection to get dialog with slider to
+selected the number of turns the spiral will have and it direction.
+
+Double click on bezier object selection to get dialog which allows you to
+control how the curve is displayed and if it is closed.
+
+SHIFT + MOVE_PNT will break a reg polygon or star into a number of
+connected lines.
+
+When drawing lines if the SHIFT modifier is used then connected lines
+can be drawn.
+
+SHIFT + MOVE_OBJ will move all objects. Note if "snap to grid" is selected then
+the new point will be constrained to fall on a grid intersection.
+
+Mouse button 3 + "snap to grid" will constrain the point to fall on one of the
+two gridlines it was originally on.
+
+The ">" and "<" buttons allow you to selective move through each of the
+objects in the collection. The "==" returns the display to normal. If the
+"paint" button is pressed then only the single object displayed will be
+drawn into the target window. Also when in this mode pressing the shift button
+will temporarily show all the objects in the collection.
+
+The the options tab there is now a toggle that allows you to turn the
+position display on and off.
+
+Gfig objects
+++++++++++++
+
+The upper right part of gfig contains a scroll region when different
+collection of objects can be found. When button 1 is pressed on an entry
+the small preview window will be updated with the objects contained in
+the selected figure. These objects can then be loaded into the main window
+by using the "Edit" button or the "Merge" button. In the former case
+the objects area will first be cleared before the new objects
+are added. In the latter case the objects will be merged into the drawing area.
+
+When a collection of objects is altered to small image of a floppy disc
+will be placed next to the entry signifying that this collection needs to be
+saved. If a red cross appears here then the file associated with the
+entry is read only and cannot be saved to (it can be copied however).
+
+Button 3 brings up a popup menu that allows collections to be saved away to
+different files. (Accel. keys also exist for some functions).
+
+The buttons on the right of the list area allow the following operations:-
+
+Rescan:-
+This popus up a window which allows you to add directories to the internal
+gfig-path. A (re)scan of these directories can the performed. Note any
+modification to the gfig-path will NOT be mirrored in your ~/.gimp/gimprc file.
+
+Load:-
+Allows you to load a single collection of objects.
+
+New:-
+Create a new collection. Note that until this is saved away to a file then no
+filename will be associated with it. (See status area).
+
+Delete:-
+Popups a dialog asking if you wish to delete the entry selected in the list
+box as well as on disc.
+
+Additionally button 3 over the list area allows the following:-
+
+Save as...:-
+Save collection to new filename. The collections filename is also
+changed to this new name. The original name is NOT deleted from the disc
+but it is effectively removed from the list selection area (actually it
+is overwritten internally).
+
+Save:-
+Save the currently selected entry to disc.
+
+Copy:-
+Make a copy of the collection.
+
+Edit:-
+Same as the "Edit button".
+
+Double click on entry with Button 1 popups up a dialog that allows you to
+modify the nme of an entry (similar to other list areas in GIMP).
+
+Bezier
+~~~~~~
+
+OK my maths is a little rusty. The bezier stuff is only very simple. However...
+I have tried to abstract as much as possible the bezier calculations from
+the drawing of the control points / painting on the screen and in the target
+window.
+
+The bezier function is defined as:-
+
+void
+DrawBezier (gdouble (*points)[2], gint np, gdouble mid, gint depth)
+{
+...
+}
+
+Points an array of pairs of doubles that define the control points
+np is the number of points. Mid is always starts as 0.5 - and depth
+defines how deep the recursion should go when calculating the line points.
+
+The function should call fp_pnt_add() when a point on the curve has been
+calculated.
+
+If anyone would like to produce a better/faster function then you
+should be able to replace the DrawBezier function. (Only the first two args
+are really needed).
+
+The algorithm the current code is based on was obtained by looking
+at the many many examples out on the web.
+(Bezier drawing seems to be topic of the year for computer degrees
+and people learning java!!!)
+
diff --git a/plug-ins/gfig/gfig-arc.c b/plug-ins/gfig/gfig-arc.c
new file mode 100644
index 0000000..5e8be02
--- /dev/null
+++ b/plug-ins/gfig/gfig-arc.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-arc.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gdouble dist (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+
+static void mid_point (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble *mx,
+ gdouble *my);
+
+static gdouble line_grad (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+
+static gdouble line_cons (gdouble x,
+ gdouble y,
+ gdouble lgrad);
+
+static void line_definition (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble *lgrad,
+ gdouble *lconst);
+
+static void arc_details (GdkPoint *vert_a,
+ GdkPoint *vert_b,
+ GdkPoint *vert_c,
+ GdkPoint *center_pnt,
+ gdouble *radius);
+
+static gdouble arc_angle (GdkPoint *pnt,
+ GdkPoint *center);
+
+static void arc_drawing_details (GfigObject *obj,
+ gdouble *minang,
+ GdkPoint *center_pnt,
+ gdouble *arcang,
+ gdouble *radius,
+ gboolean draw_cnts,
+ gboolean do_scale);
+
+static void d_draw_arc (GfigObject *obj,
+ cairo_t *cr);
+
+static void d_paint_arc (GfigObject *obj);
+
+static GfigObject *d_copy_arc (GfigObject *obj);
+
+static void d_update_arc_line (GdkPoint *pnt);
+static void d_update_arc (GdkPoint *pnt);
+static void d_arc_line_start (GdkPoint *pnt,
+ gboolean shift_down);
+static void d_arc_line_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+/* Distance between two points. */
+static gdouble
+dist (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+
+ double s1 = x1 - x2;
+ double s2 = y1 - y2;
+
+ return sqrt (s1 * s1 + s2 * s2);
+}
+
+/* Mid point of line returned */
+static void
+mid_point (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble *mx,
+ gdouble *my)
+{
+ *mx = (x1 + x2) / 2.0;
+ *my = (y1 + y2) / 2.0;
+}
+
+/* Careful about infinite grads */
+static gdouble
+line_grad (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ double dx, dy;
+
+ dx = x1 - x2;
+ dy = y1 - y2;
+
+ return (dx == 0.0) ? 0.0 : dy / dx;
+}
+
+/* Constant of line that goes through x, y with grad lgrad */
+static gdouble
+line_cons (gdouble x,
+ gdouble y,
+ gdouble lgrad)
+{
+ return y - lgrad * x;
+}
+
+/* Get grad & const for perpend. line to given points */
+static void
+line_definition (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2,
+ gdouble *lgrad,
+ gdouble *lconst)
+{
+ double grad1;
+ double midx, midy;
+
+ grad1 = line_grad (x1, y1, x2, y2);
+
+ if (grad1 == 0.0)
+ {
+#ifdef DEBUG
+ printf ("Infinite grad....\n");
+#endif /* DEBUG */
+ return;
+ }
+
+ mid_point (x1, y1, x2, y2, &midx, &midy);
+
+ /* Invert grad for perpen gradient */
+
+ *lgrad = -1.0 / grad1;
+
+ *lconst = line_cons (midx, midy,*lgrad);
+}
+
+/* Arch details
+ * Given three points get arc radius and the co-ords
+ * of center point.
+ */
+
+static void
+arc_details (GdkPoint *vert_a,
+ GdkPoint *vert_b,
+ GdkPoint *vert_c,
+ GdkPoint *center_pnt,
+ gdouble *radius)
+{
+ /* Only vertices are in whole numbers - everything else is in doubles */
+ double ax, ay;
+ double bx, by;
+ double cx, cy;
+
+ double len_a, len_b, len_c;
+ double sum_sides2;
+ double area;
+ double circumcircle_R;
+ double line1_grad = 0, line1_const = 0;
+ double line2_grad = 0, line2_const = 0;
+ double inter_x = 0.0, inter_y = 0.0;
+ int got_x = 0, got_y = 0;
+
+ ax = (double) (vert_a->x);
+ ay = (double) (vert_a->y);
+ bx = (double) (vert_b->x);
+ by = (double) (vert_b->y);
+ cx = (double) (vert_c->x);
+ cy = (double) (vert_c->y);
+
+ len_a = dist (ax, ay, bx, by);
+ len_b = dist (bx, by, cx, cy);
+ len_c = dist (cx, cy, ax, ay);
+
+ sum_sides2 = (fabs (len_a) + fabs (len_b) + fabs (len_c))/2;
+
+ /* Area */
+ area = sqrt (sum_sides2 * (sum_sides2 - len_a) *
+ (sum_sides2 - len_b) *
+ (sum_sides2 - len_c));
+
+ /* Circumcircle */
+ circumcircle_R = len_a * len_b * len_c / (4 * area);
+ *radius = circumcircle_R;
+
+ /* Deal with exceptions - I hate exceptions */
+
+ if (ax == bx || ax == cx || cx == bx)
+ {
+ /* vert line -> mid point gives inter_x */
+ if (ax == bx && bx == cx)
+ {
+ /* Straight line */
+ double miny = ay;
+ double maxy = ay;
+
+ if (by > maxy)
+ maxy = by;
+
+ if (by < miny)
+ miny = by;
+
+ if (cy > maxy)
+ maxy = cy;
+
+ if (cy < miny)
+ miny = cy;
+
+ inter_y = (maxy - miny) / 2 + miny;
+ }
+ else if (ax == bx)
+ {
+ inter_y = (ay - by) / 2 + by;
+ }
+ else if (bx == cx)
+ {
+ inter_y = (by - cy) / 2 + cy;
+ }
+ else
+ {
+ inter_y = (cy - ay) / 2 + ay;
+ }
+ got_y = 1;
+ }
+
+ if (ay == by || by == cy || ay == cy)
+ {
+ /* Horz line -> midpoint gives inter_y */
+ if (ax == bx && bx == cx)
+ {
+ /* Straight line */
+ double minx = ax;
+ double maxx = ax;
+
+ if (bx > maxx)
+ maxx = bx;
+
+ if (bx < minx)
+ minx = bx;
+
+ if (cx > maxx)
+ maxx = cx;
+
+ if (cx < minx)
+ minx = cx;
+
+ inter_x = (maxx - minx) / 2 + minx;
+ }
+ else if (ay == by)
+ {
+ inter_x = (ax - bx) / 2 + bx;
+ }
+ else if (by == cy)
+ {
+ inter_x = (bx - cx) / 2 + cx;
+ }
+ else
+ {
+ inter_x = (cx - ax) / 2 + ax;
+ }
+ got_x = 1;
+ }
+
+ if (!got_x || !got_y)
+ {
+ /* At least two of the lines are not parallel to the axis */
+ /*first line */
+ if (ax != bx && ay != by)
+ line_definition (ax, ay, bx, by, &line1_grad, &line1_const);
+ else
+ line_definition (ax, ay, cx, cy, &line1_grad, &line1_const);
+ /* second line */
+ if (bx != cx && by != cy)
+ line_definition (bx, by, cx, cy, &line2_grad, &line2_const);
+ else
+ line_definition (ax, ay, cx, cy, &line2_grad, &line2_const);
+ }
+
+ /* Intersection point */
+
+ if (!got_x)
+ inter_x = (line2_const - line1_const) / (line1_grad - line2_grad);
+ if (!got_y)
+ inter_y = line1_grad * inter_x + line1_const;
+
+ center_pnt->x = (gint) inter_x;
+ center_pnt->y = (gint) inter_y;
+}
+
+static gdouble
+arc_angle (GdkPoint *pnt,
+ GdkPoint *center)
+{
+ /* Get angle (in degrees) of point given origin of center */
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble offset_angle;
+
+ shift_x = pnt->x - center->x;
+ shift_y = -pnt->y + center->y;
+ offset_angle = atan2 (shift_y, shift_x);
+
+ if (offset_angle < 0)
+ offset_angle += 2.0 * G_PI;
+
+ return offset_angle * 360 / (2.0 * G_PI);
+}
+
+static void
+arc_drawing_details (GfigObject *obj,
+ gdouble *minang,
+ GdkPoint *center_pnt,
+ gdouble *arcang,
+ gdouble *radius,
+ gboolean draw_cnts,
+ gboolean do_scale)
+{
+ DobjPoints *pnt1 = NULL;
+ DobjPoints *pnt2 = NULL;
+ DobjPoints *pnt3 = NULL;
+ DobjPoints dpnts[3];
+ gdouble ang1, ang2, ang3;
+ gdouble maxang;
+
+ pnt1 = obj->points;
+
+ if (!pnt1)
+ return; /* Not fully drawn */
+
+ pnt2 = pnt1->next;
+
+ if (!pnt2)
+ return; /* Not fully drawn */
+
+ pnt3 = pnt2->next;
+
+ if (!pnt3)
+ return; /* Still not fully drawn */
+
+ if (do_scale)
+ {
+ /* Adjust pnts for scaling */
+ /* Warning struct copies here! and casting to double <-> int */
+ /* Too complex fix me - to much hacking */
+ gdouble xy[2];
+ int j;
+
+ dpnts[0] = *pnt1;
+ dpnts[1] = *pnt2;
+ dpnts[2] = *pnt3;
+
+ pnt1 = &dpnts[0];
+ pnt2 = &dpnts[1];
+ pnt3 = &dpnts[2];
+
+ for (j = 0 ; j < 3; j++)
+ {
+ xy[0] = dpnts[j].pnt.x;
+ xy[1] = dpnts[j].pnt.y;
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&xy[0], 1);
+ else
+ scale_to_xy (&xy[0], 1);
+ dpnts[j].pnt.x = xy[0];
+ dpnts[j].pnt.y = xy[1];
+ }
+ }
+
+ arc_details (&pnt1->pnt, &pnt2->pnt, &pnt3->pnt, center_pnt, radius);
+
+ ang1 = arc_angle (&pnt1->pnt, center_pnt);
+ ang2 = arc_angle (&pnt2->pnt, center_pnt);
+ ang3 = arc_angle (&pnt3->pnt, center_pnt);
+
+ /* Find min/max angle */
+
+ maxang = ang1;
+
+ if (ang3 > maxang)
+ maxang = ang3;
+
+ *minang = ang1;
+
+ if (ang3 < *minang)
+ *minang = ang3;
+
+ if (ang2 > *minang && ang2 < maxang)
+ *arcang = maxang - *minang;
+ else
+ *arcang = maxang - *minang - 360;
+}
+
+static void
+d_draw_arc (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *pnt1, *pnt2, *pnt3;
+ GdkPoint center_pnt;
+ gdouble radius, minang, arcang;
+
+ g_assert (obj != NULL);
+
+ if (!obj)
+ return;
+
+ pnt1 = obj->points;
+ pnt2 = pnt1 ? pnt1->next : NULL;
+ pnt3 = pnt2 ? pnt2->next : NULL;
+
+ if (! pnt3)
+ return;
+
+ draw_sqr (&pnt1->pnt, obj == gfig_context->selected_obj, cr);
+ draw_sqr (&pnt2->pnt, obj == gfig_context->selected_obj, cr);
+ draw_sqr (&pnt3->pnt, obj == gfig_context->selected_obj, cr);
+
+ arc_drawing_details (obj, &minang, &center_pnt, &arcang, &radius,
+ TRUE, FALSE);
+ gfig_draw_arc (center_pnt.x, center_pnt.y, radius, radius, -minang, -(minang + arcang), cr);
+}
+
+static void
+d_paint_arc (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ gdouble *line_pnts;
+ gint seg_count = 0;
+ gint i = 0;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gint loop;
+ GdkPoint last_pnt = { 0, 0 };
+ gboolean first = TRUE;
+ GdkPoint center_pnt;
+ gdouble minang, arcang;
+
+ g_assert (obj != NULL);
+
+ if (!obj)
+ return;
+
+ /* No cnt pnts & must scale */
+ arc_drawing_details (obj, &minang, &center_pnt, &arcang, &radius,
+ FALSE, TRUE);
+
+ seg_count = 360; /* Should make a smoth-ish curve */
+
+ /* +3 because we MIGHT do pie selection */
+ line_pnts = g_new0 (gdouble, 2 * seg_count + 3);
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI / 360.0;
+
+ if (arcang < 0.0)
+ {
+ /* Swap - since we always draw anti-clock wise */
+ minang += arcang;
+ arcang = -arcang;
+ }
+
+ minang = minang * (2.0 * G_PI / 360.0); /* min ang is in degrees - need in rads */
+
+ for (loop = 0 ; loop < abs ((gint)arcang) ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + minang;
+
+ lx = radius * cos (ang_loop);
+ ly = -radius * sin (ang_loop); /* y grows down screen and angs measured from x clockwise */
+
+ calc_pnt.x = RINT (lx + center_pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt.y);
+
+ /* Miss out duped pnts */
+ if (!first)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ line_pnts[i++] = calc_pnt.x;
+ line_pnts[i++] = calc_pnt.y;
+ last_pnt = calc_pnt;
+
+ if (first)
+ {
+ first = FALSE;
+ }
+ }
+
+ /* One go */
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ gfig_paint (selvals.brshtype,
+ gfig_context->drawable_id,
+ i, line_pnts);
+ }
+
+ g_free (line_pnts);
+}
+
+static GfigObject *
+d_copy_arc (GfigObject *obj)
+{
+ GfigObject *nc;
+
+ g_assert (obj->type == ARC);
+
+ nc = d_new_object (ARC, obj->points->pnt.x, obj->points->pnt.y);
+ nc->points->next = d_copy_dobjpoints (obj->points->next);
+
+ return nc;
+}
+
+void
+d_arc_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[ARC];
+
+ class->type = ARC;
+ class->name = "ARC";
+ class->drawfunc = d_draw_arc;
+ class->paintfunc = d_paint_arc;
+ class->copyfunc = d_copy_arc;
+ class->update = d_update_arc;
+}
+
+/* Update end point of line */
+static void
+d_update_arc_line (GdkPoint *pnt)
+{
+ DobjPoints *spnt, *epnt;
+ /* Get last but one segment and undraw it -
+ * Then draw new segment in.
+ * always dealing with the static object.
+ */
+
+ /* Get start of segments */
+ spnt = obj_creating->points;
+
+ if (!spnt)
+ return; /* No points */
+
+ if ((epnt = spnt->next))
+ {
+ g_free (epnt);
+ }
+
+ epnt = new_dobjpoint (pnt->x, pnt->y);
+ spnt->next = epnt;
+}
+
+static void
+d_update_arc (GdkPoint *pnt)
+{
+ DobjPoints *pnt1 = NULL;
+ DobjPoints *pnt2 = NULL;
+ DobjPoints *pnt3 = NULL;
+
+ /* First two points as line only become arch when third
+ * point is placed on canvas.
+ */
+
+ pnt1 = obj_creating->points;
+
+ if (!pnt1 ||
+ !(pnt2 = pnt1->next) ||
+ !(pnt3 = pnt2->next))
+ {
+ d_update_arc_line (pnt);
+ return; /* Not fully drawn */
+ }
+
+ /* Update a real curve */
+ /* Nothing to be done ... */
+}
+
+static void
+d_arc_line_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (!obj_creating || !shift_down)
+ {
+ /* Must delete obj_creating if we have one */
+ obj_creating = d_new_object (LINE, pnt->x, pnt->y);
+ }
+ else
+ {
+ /* Contniuation */
+ d_update_arc_line (pnt);
+ }
+}
+
+void
+d_arc_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* Draw lines to start with -- then convert to an arc */
+ d_arc_line_start (pnt, TRUE); /* TRUE means multiple pointed line */
+}
+
+static void
+d_arc_line_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (shift_down)
+ {
+ if (tmp_line)
+ {
+ GdkPoint tmp_pnt = *pnt;
+
+ if (need_to_scale)
+ {
+ tmp_pnt.x = pnt->x * scale_x_factor;
+ tmp_pnt.y = pnt->y * scale_y_factor;
+ }
+
+ d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1);
+ free_one_obj (obj_creating);
+ /* Must free obj_creating */
+ }
+ else
+ {
+ tmp_line = obj_creating;
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ obj_creating = d_new_object (LINE, pnt->x, pnt->y);
+ }
+ else
+ {
+ if (tmp_line)
+ {
+ GdkPoint tmp_pnt = *pnt;
+
+ if (need_to_scale)
+ {
+ tmp_pnt.x = pnt->x * scale_x_factor;
+ tmp_pnt.y = pnt->y * scale_y_factor;
+ }
+
+ d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1);
+ free_one_obj (obj_creating);
+ /* Must free obj_creating */
+ }
+ else
+ {
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+ obj_creating = NULL;
+ tmp_line = NULL;
+ }
+ /*gtk_widget_queue_draw (gfig_context->preview);*/
+}
+
+void
+d_arc_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* Under control point */
+ if (!tmp_line ||
+ !tmp_line->points ||
+ !tmp_line->points->next)
+ {
+ /* No arc created - yet. Must have three points */
+ d_arc_line_end (pnt, TRUE);
+ }
+ else
+ {
+ /* Complete arc */
+ /* Convert to an arc ... */
+ tmp_line->type = ARC;
+ tmp_line->class = &dobj_class[ARC];
+ d_arc_line_end (pnt, FALSE);
+ if (need_to_scale)
+ {
+ selvals.scaletoimage = 0;
+ }
+ gtk_widget_queue_draw (gfig_context->preview);
+ if (need_to_scale)
+ {
+ selvals.scaletoimage = 1;
+ }
+ }
+}
+
diff --git a/plug-ins/gfig/gfig-arc.h b/plug-ins/gfig/gfig-arc.h
new file mode 100644
index 0000000..70e599e
--- /dev/null
+++ b/plug-ins/gfig/gfig-arc.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_ARC_H__
+#define __GFIG_ARC_H__
+
+void d_arc_object_class_init (void);
+
+void d_arc_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_arc_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+
+#endif /* __GFIG_ARC_H__ */
diff --git a/plug-ins/gfig/gfig-bezier.c b/plug-ins/gfig/gfig-bezier.c
new file mode 100644
index 0000000..9156d7c
--- /dev/null
+++ b/plug-ins/gfig/gfig-bezier.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-line.h"
+#include "gfig-dobject.h"
+#include "gfig-dialog.h"
+#include "gfig-bezier.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define FP_PNT_MAX 10
+
+typedef gdouble (*fp_pnt)[2];
+
+static gboolean bezier_closed = FALSE;
+static gboolean bezier_line_frame = FALSE;
+static int fp_pnt_cnt = 0;
+static int fp_pnt_chunk = 0;
+static gdouble *fp_pnt_pnts = NULL;
+
+GfigObject *tmp_bezier; /* Needed when drawing bezier curves */
+
+static void fp_pnt_start (void);
+static void fp_pnt_add (gdouble p1,
+ gdouble p2,
+ gdouble p3,
+ gdouble p4);
+static gdouble *d_bz_get_array (gint *sz);
+static void d_bz_line (cairo_t *cr);
+static void DrawBezier (fp_pnt points,
+ gint np,
+ gdouble mid,
+ gint depth);
+static void d_paint_bezier (GfigObject *obj);
+static GfigObject *d_copy_bezier (GfigObject *obj);
+static void d_update_bezier (GdkPoint *pnt);
+
+static void
+fp_pnt_start (void)
+{
+ fp_pnt_cnt = 0;
+}
+
+/* Add a line segment to collection array */
+static void
+fp_pnt_add (gdouble p1,
+ gdouble p2,
+ gdouble p3,
+ gdouble p4)
+{
+ if (!fp_pnt_pnts)
+ {
+ fp_pnt_pnts = g_new0 (gdouble, FP_PNT_MAX);
+ fp_pnt_chunk = 1;
+ }
+
+ if (((fp_pnt_cnt + 4) / FP_PNT_MAX) >= fp_pnt_chunk)
+ {
+ /* more space pls */
+ fp_pnt_chunk++;
+ fp_pnt_pnts = g_renew (gdouble, fp_pnt_pnts, fp_pnt_chunk * FP_PNT_MAX);
+ }
+
+ fp_pnt_pnts[fp_pnt_cnt++] = p1;
+ fp_pnt_pnts[fp_pnt_cnt++] = p2;
+ fp_pnt_pnts[fp_pnt_cnt++] = p3;
+ fp_pnt_pnts[fp_pnt_cnt++] = p4;
+}
+
+static gdouble *
+d_bz_get_array (gint *sz)
+{
+ *sz = fp_pnt_cnt;
+ return fp_pnt_pnts;
+}
+
+static void
+d_bz_line (cairo_t *cr)
+{
+ gint i, x0, y0, x1, y1;
+
+ g_assert ((fp_pnt_cnt % 4) == 0);
+
+ for (i = 0 ; i < fp_pnt_cnt; i += 4)
+ {
+ x0 = fp_pnt_pnts[i];
+ y0 = fp_pnt_pnts[i + 1];
+ x1 = fp_pnt_pnts[i + 2];
+ y1 = fp_pnt_pnts[i + 3];
+
+ gfig_draw_line (x0, y0, x1, y1, cr);
+ }
+}
+
+/* Return points to plot */
+/* Terminate by point with DBL_MAX, DBL_MAX */
+static void
+DrawBezier (fp_pnt points,
+ gint np,
+ gdouble mid,
+ gint depth)
+{
+ gint i, j, x0 = 0, y0 = 0, x1, y1;
+ fp_pnt left;
+ fp_pnt right;
+
+ if (depth == 0) /* draw polyline */
+ {
+ for (i = 0; i < np; i++)
+ {
+ x1 = (int) points[i][0];
+ y1 = (int) points[i][1];
+ if (i > 0 && (x1 != x0 || y1 != y0))
+ {
+ /* Add pnts up */
+ fp_pnt_add ((gdouble) x0, (gdouble) y0,
+ (gdouble) x1, (gdouble) y1);
+ }
+ x0 = x1;
+ y0 = y1;
+ }
+ }
+ else /* subdivide control points at mid */
+ {
+ left = (fp_pnt)g_new (gdouble, np * 2);
+ right = (fp_pnt)g_new (gdouble, np * 2);
+ for (i = 0; i < np; i++)
+ {
+ right[i][0] = points[i][0];
+ right[i][1] = points[i][1];
+ }
+ left[0][0] = right[0][0];
+ left[0][1] = right[0][1];
+ for (j = np - 1; j >= 1; j--)
+ {
+ for (i = 0; i < j; i++)
+ {
+ right[i][0] = (1 - mid) * right[i][0] + mid * right[i + 1][0];
+ right[i][1] = (1 - mid) * right[i][1] + mid * right[i + 1][1];
+ }
+ left[np - j][0] = right[0][0];
+ left[np - j][1] = right[0][1];
+ }
+ if (depth > 0)
+ {
+ DrawBezier (left, np, mid, depth - 1);
+ DrawBezier (right, np, mid, depth - 1);
+ g_free (left);
+ g_free (right);
+ }
+ }
+}
+
+void
+d_draw_bezier (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *spnt;
+ gint seg_count = 0;
+ gint i = 0;
+ gdouble (*line_pnts)[2];
+
+ spnt = obj->points;
+
+ /* First count the number of points */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ seg_count++;
+
+ if (!seg_count)
+ return; /* no-line */
+
+ line_pnts = (fp_pnt) g_new0 (gdouble, 2 * seg_count + 1);
+
+ /* Go around all the points drawing a line from one to the next */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ if (! spnt->next && obj == obj_creating)
+ draw_circle (&spnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&spnt->pnt, obj == gfig_context->selected_obj, cr);
+ line_pnts[i][0] = spnt->pnt.x;
+ line_pnts[i][1] = spnt->pnt.y;
+ i++;
+ }
+
+ /* Generate an array of doubles which are the control points */
+
+ if (bezier_line_frame && tmp_bezier)
+ {
+ fp_pnt_start ();
+ DrawBezier (line_pnts, seg_count, 0.5, 0);
+ d_bz_line (cr);
+ }
+
+ fp_pnt_start ();
+ DrawBezier (line_pnts, seg_count, 0.5, 3);
+ d_bz_line (cr);
+
+ g_free (line_pnts);
+}
+
+static void
+d_paint_bezier (GfigObject *obj)
+{
+ gdouble *line_pnts;
+ gdouble (*bz_line_pnts)[2];
+ DobjPoints *spnt;
+ gint seg_count = 0;
+ gint i = 0;
+
+ /* First count the number of points */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ seg_count++;
+
+ if (!seg_count)
+ return; /* no-line */
+
+ bz_line_pnts = (fp_pnt) g_new0 (gdouble, 2 * seg_count + 1);
+
+ /* Go around all the points drawing a line from one to the next */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ bz_line_pnts[i][0] = spnt->pnt.x;
+ bz_line_pnts[i][1] = spnt->pnt.y;
+ i++;
+ }
+
+ fp_pnt_start ();
+ DrawBezier (bz_line_pnts, seg_count, 0.5, 5);
+ line_pnts = d_bz_get_array (&i);
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&line_pnts[0], i / 2);
+ else
+ scale_to_xy (&line_pnts[0], i / 2);
+
+ /* One go */
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ gfig_paint (selvals.brshtype,
+ gfig_context->drawable_id,
+ i, line_pnts);
+ }
+
+ g_free (bz_line_pnts);
+ /* Don't free line_pnts - may need again */
+}
+
+static GfigObject *
+d_copy_bezier (GfigObject *obj)
+{
+ GfigObject *np;
+
+ g_assert (obj->type == BEZIER);
+
+ np = d_new_object (BEZIER, obj->points->pnt.x, obj->points->pnt.y);
+ np->points->next = d_copy_dobjpoints (obj->points->next);
+ np->type_data = obj->type_data;
+
+ return np;
+}
+
+void
+d_bezier_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[BEZIER];
+
+ class->type = BEZIER;
+ class->name = "BEZIER";
+ class->drawfunc = d_draw_bezier;
+ class->paintfunc = d_paint_bezier;
+ class->copyfunc = d_copy_bezier;
+ class->update = d_update_bezier;
+}
+
+static void
+d_update_bezier (GdkPoint *pnt)
+{
+ DobjPoints *s_pnt, *l_pnt;
+
+ g_assert (tmp_bezier != NULL);
+
+ s_pnt = tmp_bezier->points;
+
+ if (!s_pnt)
+ return; /* No points */
+
+ if ((l_pnt = s_pnt->next))
+ {
+ while (l_pnt->next)
+ {
+ l_pnt = l_pnt->next;
+ }
+
+ l_pnt->pnt = *pnt;
+ }
+ else
+ {
+ /* Radius is a few pixels away */
+ /* First edge point */
+ d_pnt_add_line (tmp_bezier, pnt->x, pnt->y,-1);
+ }
+}
+
+void
+d_bezier_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (!tmp_bezier)
+ {
+ /* New curve */
+ tmp_bezier = obj_creating = d_new_object (BEZIER, pnt->x, pnt->y);
+ }
+}
+
+void
+d_bezier_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ DobjPoints *l_pnt;
+
+ if (!tmp_bezier)
+ {
+ tmp_bezier = obj_creating;
+ }
+
+ l_pnt = tmp_bezier->points->next;
+
+ if (!l_pnt)
+ return;
+
+ if (shift_down)
+ {
+ while (l_pnt->next)
+ {
+ l_pnt = l_pnt->next;
+ }
+
+ if (l_pnt)
+ {
+ if (bezier_closed)
+ {
+ /* if closed then add first point */
+ d_pnt_add_line (tmp_bezier,
+ tmp_bezier->points->pnt.x,
+ tmp_bezier->points->pnt.y, -1);
+ }
+
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ /* small mem leak if !l_pnt ? */
+ tmp_bezier = NULL;
+ obj_creating = NULL;
+ }
+ else
+ {
+ d_pnt_add_line (tmp_bezier, pnt->x, pnt->y,-1);
+ }
+}
+
+void
+tool_options_bezier (GtkWidget *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *toggle;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, NULL);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_check_button_new_with_label (_("Closed"));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &bezier_closed);
+ gimp_help_set_help_data (toggle,
+ _("Close curve on completion"), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bezier_closed);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_label (_("Show Line Frame"));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &bezier_line_frame);
+ gimp_help_set_help_data (toggle,
+ _("Draws lines between the control points. "
+ "Only during curve creation"), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), bezier_line_frame);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+}
+
diff --git a/plug-ins/gfig/gfig-bezier.h b/plug-ins/gfig/gfig-bezier.h
new file mode 100644
index 0000000..e030d0c
--- /dev/null
+++ b/plug-ins/gfig/gfig-bezier.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_BEZIER_H__
+#define __GFIG_BEZIER_H__
+
+extern GfigObject *tmp_bezier;
+
+void d_draw_bezier (GfigObject *obj,
+ cairo_t *cr);
+
+void d_bezier_object_class_init (void);
+
+void d_bezier_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_bezier_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+void tool_options_bezier (GtkWidget *notebook);
+
+#endif /* __GFIG_BEZIER_H__ */
diff --git a/plug-ins/gfig/gfig-circle.c b/plug-ins/gfig/gfig-circle.c
new file mode 100644
index 0000000..0839abf
--- /dev/null
+++ b/plug-ins/gfig/gfig-circle.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-poly.h"
+#include "gfig-circle.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gint calc_radius (GdkPoint *center,
+ GdkPoint *edge);
+static void d_draw_circle (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_circle (GfigObject *obj);
+static GfigObject *d_copy_circle (GfigObject *obj);
+
+static void d_update_circle (GdkPoint *pnt);
+
+static gint
+calc_radius (GdkPoint *center, GdkPoint *edge)
+{
+ gint dx = center->x - edge->x;
+ gint dy = center->y - edge->y;
+
+ return (gint) sqrt (dx * dx + dy * dy);
+}
+
+static void
+d_draw_circle (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+ gint radius;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ edge_pnt = center_pnt->next;
+
+ if (!edge_pnt)
+ return;
+
+ radius = calc_radius (&center_pnt->pnt, &edge_pnt->pnt);
+
+ if (obj_creating == obj)
+ draw_circle (&edge_pnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&edge_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ gfig_draw_arc (center_pnt->pnt.x, center_pnt->pnt.y,
+ radius, radius, 0, 360, cr);
+}
+
+static void
+d_paint_circle (GfigObject *obj)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+ gint radius;
+ gdouble dpnts[4];
+
+ g_assert (obj != NULL);
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ edge_pnt = center_pnt->next;
+
+ if (!edge_pnt)
+ {
+ g_error ("Internal error - circle no edge pnt");
+ }
+
+ radius = calc_radius (&center_pnt->pnt, &edge_pnt->pnt);
+
+ dpnts[0] = (gdouble) center_pnt->pnt.x - radius;
+ dpnts[1] = (gdouble) center_pnt->pnt.y - radius;
+ dpnts[3] = dpnts[2] = (gdouble) radius * 2;
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&dpnts[0], 2);
+ else
+ scale_to_xy (&dpnts[0], 2);
+
+ if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
+ {
+ gimp_context_push ();
+ gimp_context_set_antialias (selopt.antia);
+ gimp_context_set_feather (selopt.feather);
+ gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
+ gimp_image_select_ellipse (gfig_context->image_id,
+ selopt.type,
+ dpnts[0], dpnts[1],
+ dpnts[2], dpnts[3]);
+ gimp_context_pop ();
+
+ paint_layer_fill (center_pnt->pnt.x - radius,
+ center_pnt->pnt.y - radius,
+ center_pnt->pnt.x + radius,
+ center_pnt->pnt.y + radius);
+ gimp_selection_none (gfig_context->image_id);
+ }
+
+ /* Drawing a circle may be harder than stroking a circular selection,
+ * but we have to do it or we will not be able to draw outside of the
+ * layer. */
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ const gdouble r = dpnts[2] / 2;
+ const gdouble cx = dpnts[0] + r, cy = dpnts[1] + r;
+ gdouble line_pnts[362];
+ gdouble angle = 0;
+ gint i = 0;
+
+ while (i < 361)
+ {
+ static const gdouble step = 2 * G_PI / 180;
+
+ line_pnts[i++] = cx + r * cos (angle);
+ line_pnts[i++] = cy + r * sin (angle);
+ angle += step;
+ }
+
+ gfig_paint (selvals.brshtype, gfig_context->drawable_id, i, line_pnts);
+ }
+}
+
+static GfigObject *
+d_copy_circle (GfigObject * obj)
+{
+ GfigObject *nc;
+
+ g_assert (obj->type == CIRCLE);
+
+ nc = d_new_object (CIRCLE, obj->points->pnt.x, obj->points->pnt.y);
+ nc->points->next = d_copy_dobjpoints (obj->points->next);
+
+ return nc;
+}
+
+void
+d_circle_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[CIRCLE];
+
+ class->type = CIRCLE;
+ class->name = "CIRCLE";
+ class->drawfunc = d_draw_circle;
+ class->paintfunc = d_paint_circle;
+ class->copyfunc = d_copy_circle;
+ class->update = d_update_circle;
+}
+
+static void
+d_update_circle (GdkPoint *pnt)
+{
+ DobjPoints *center_pnt, *edge_pnt;
+
+ center_pnt = obj_creating->points;
+
+ if (!center_pnt)
+ return; /* No points */
+
+ if ((edge_pnt = center_pnt->next))
+ {
+ edge_pnt->pnt = *pnt;
+ }
+ else
+ {
+ edge_pnt = new_dobjpoint (pnt->x, pnt->y);
+ center_pnt->next = edge_pnt;
+ }
+}
+
+void
+d_circle_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (CIRCLE, pnt->x, pnt->y);
+}
+
+void
+d_circle_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* Under control point */
+ if (!obj_creating->points->next)
+ {
+ /* No circle created */
+ free_one_obj (obj_creating);
+ }
+ else
+ {
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ obj_creating = NULL;
+}
+
diff --git a/plug-ins/gfig/gfig-circle.h b/plug-ins/gfig/gfig-circle.h
new file mode 100644
index 0000000..910e3a3
--- /dev/null
+++ b/plug-ins/gfig/gfig-circle.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_CIRCLE_H__
+#define __GFIG_CIRCLE_H__
+
+void d_circle_object_class_init (void);
+
+void d_circle_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_circle_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_CIRCLE_H__ */
diff --git a/plug-ins/gfig/gfig-dialog.c b/plug-ins/gfig/gfig-dialog.c
new file mode 100644
index 0000000..9f72b97
--- /dev/null
+++ b/plug-ins/gfig/gfig-dialog.c
@@ -0,0 +1,2193 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#include <libgimpbase/gimpwin32-io.h>
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "gfig.h"
+#include "gfig-style.h"
+#include "gfig-dialog.h"
+#include "gfig-arc.h"
+#include "gfig-bezier.h"
+#include "gfig-circle.h"
+#include "gfig-dobject.h"
+#include "gfig-ellipse.h"
+#include "gfig-grid.h"
+#include "gfig-line.h"
+#include "gfig-poly.h"
+#include "gfig-preview.h"
+#include "gfig-rectangle.h"
+#include "gfig-spiral.h"
+#include "gfig-star.h"
+#include "gfig-stock.h"
+
+#define SEL_BUTTON_WIDTH 100
+#define SEL_BUTTON_HEIGHT 20
+
+#define GRID_TYPE_MENU 1
+#define GRID_RENDER_MENU 2
+
+#define PAINT_BGS_MENU 2
+#define PAINT_TYPE_MENU 3
+
+#define OBJ_SELECT_GT 1
+#define OBJ_SELECT_LT 2
+#define OBJ_SELECT_EQ 4
+
+#define UPDATE_DELAY 300 /* From GtkRange in GTK+ 2.22 */
+
+/* Globals */
+gint undo_level; /* Last slot filled in -1 = no undo */
+GList *undo_table[MAX_UNDO];
+
+/* Values when first invoked */
+SelectItVals selvals =
+{
+ {
+ MIN_GRID + (MAX_GRID - MIN_GRID)/2, /* Gridspacing */
+ RECT_GRID, /* Default to rectangle type */
+ FALSE, /* drawgrid */
+ FALSE, /* snap2grid */
+ FALSE, /* lockongrid */
+ TRUE, /* show control points */
+ 0.0, /* grid_radius_min */
+ 10.0, /* grid_radius_interval */
+ 0.0, /* grid_rotation */
+ 5.0, /* grid_granularity */
+ 120 /* grid_sectors_desired */
+ },
+ FALSE, /* show image */
+ MIN_UNDO + (MAX_UNDO - MIN_UNDO)/2, /* Max level of undos */
+ TRUE, /* Show pos updates */
+ 0.0, /* Brush fade */
+ 0.0, /* Brush gradient */
+ 20.0, /* Air brush pressure */
+ ORIGINAL_LAYER, /* Draw all objects on one layer */
+ LAYER_TRANS_BG, /* New layers background */
+ PAINT_BRUSH_TYPE, /* Default to use brushes */
+ FALSE, /* reverse lines */
+ TRUE, /* Scale to image when painting */
+ 1.0, /* Scale to image fp */
+ BRUSH_BRUSH_TYPE, /* Default to use a brush */
+ LINE /* Initial object type */
+};
+
+selection_option selopt =
+{
+ ADD, /* type */
+ FALSE, /* Antia */
+ FALSE, /* Feather */
+ 10.0, /* feather radius */
+ ARC_SEGMENT, /* Arc as a segment */
+ FILL_PATTERN, /* Fill as pattern */
+ 100.0, /* Max opacity */
+};
+
+/* Should be kept in sync with GfigOpts */
+typedef struct
+{
+ GtkAdjustment *gridspacing;
+ GtkAdjustment *grid_sectors_desired;
+ GtkAdjustment *grid_radius_interval;
+ GtkWidget *gridtypemenu;
+ GtkWidget *drawgrid;
+ GtkWidget *snap2grid;
+ GtkWidget *lockongrid;
+ GtkWidget *showcontrol;
+} GfigOptWidgets;
+
+static GfigOptWidgets gfig_opt_widget = { NULL, NULL, NULL, NULL, NULL, NULL };
+static gchar *gfig_path = NULL;
+static GtkWidget *page_menu_bg;
+static GtkWidget *tool_options_notebook;
+static GtkWidget *fill_type_notebook;
+static guint paint_timeout = 0;
+
+static GtkActionGroup *gfig_actions = NULL;
+
+
+static void gfig_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void gfig_load_action_callback (GtkAction *action,
+ gpointer data);
+static void gfig_save_action_callback (GtkAction *action,
+ gpointer data);
+static void gfig_list_load_all (const gchar *path);
+static void gfig_list_free_all (void);
+static void create_notebook_pages (GtkWidget *notebook);
+static void select_filltype_callback (GtkWidget *widget);
+static void gfig_grid_action_callback (GtkAction *action,
+ gpointer data);
+static void gfig_prefs_action_callback (GtkAction *action,
+ gpointer data);
+static void toggle_show_image (void);
+static void gridtype_combo_callback (GtkWidget *widget,
+ gpointer data);
+
+static void load_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data);
+static void save_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ GFigObj *obj);
+static void paint_combo_callback (GtkWidget *widget,
+ gpointer data);
+
+static void select_button_clicked (gint type);
+static void select_button_clicked_lt (void);
+static void select_button_clicked_gt (void);
+static void select_button_clicked_eq (void);
+static void raise_selected_obj_to_top (GtkWidget *widget,
+ gpointer data);
+static void lower_selected_obj_to_bottom (GtkWidget *widget,
+ gpointer data);
+static void raise_selected_obj (GtkWidget *widget,
+ gpointer data);
+static void lower_selected_obj (GtkWidget *widget,
+ gpointer data);
+
+static void toggle_obj_type (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer data);
+
+static GtkUIManager *create_ui_manager (GtkWidget *window);
+
+
+gboolean
+gfig_dialog (void)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GFigObj *gfig;
+ GimpParasite *parasite;
+ gint newlayer;
+ GtkWidget *menubar;
+ GtkWidget *toolbar;
+ GtkWidget *combo;
+ GtkWidget *frame;
+ gint img_width;
+ gint img_height;
+ GimpImageType img_type;
+ GtkWidget *toggle;
+ GtkWidget *right_vbox;
+ GtkWidget *hbox;
+ GtkUIManager *ui_manager;
+ GtkWidget *empty_label;
+ gchar *path;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ img_width = gimp_drawable_width (gfig_context->drawable_id);
+ img_height = gimp_drawable_height (gfig_context->drawable_id);
+ img_type = gimp_drawable_type_with_alpha (gfig_context->drawable_id);
+
+ /*
+ * See if there is a "gfig" parasite. If so, this is a gfig layer,
+ * and we start by clearing it to transparent.
+ * If not, we create a new transparent layer.
+ */
+ gfig_list = NULL;
+ undo_level = -1;
+ parasite = gimp_item_get_parasite (gfig_context->drawable_id, "gfig");
+ gfig_context->enable_repaint = FALSE;
+
+ /* debug */
+ gfig_context->debug_styles = FALSE;
+
+ /* initial default style */
+ gfig_read_gimp_style (&gfig_context->default_style, "Base");
+ gfig_context->default_style.paint_type = selvals.painttype;
+
+ if (parasite)
+ {
+ gimp_drawable_fill (gfig_context->drawable_id, GIMP_FILL_TRANSPARENT);
+ gfig_context->using_new_layer = FALSE;
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ newlayer = gimp_layer_new (gfig_context->image_id, "GFig",
+ img_width, img_height,
+ img_type,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (gfig_context->image_id));
+ gimp_drawable_fill (newlayer, GIMP_FILL_TRANSPARENT);
+ gimp_image_insert_layer (gfig_context->image_id, newlayer, -1, -1);
+ gfig_context->drawable_id = newlayer;
+ gfig_context->using_new_layer = TRUE;
+ }
+
+ gfig_stock_init ();
+
+ path = gimp_gimprc_query ("gfig-path");
+
+ if (path)
+ {
+ gfig_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+ g_free (path);
+ }
+ else
+ {
+ gchar *gimprc = gimp_personal_rc_file ("gimprc");
+ gchar *full_path = gimp_config_build_data_path ("gfig");
+ gchar *esc_path = g_strescape (full_path, NULL);
+ g_free (full_path);
+
+ g_message (_("No %s in gimprc:\n"
+ "You need to add an entry like\n"
+ "(%s \"%s\")\n"
+ "to your %s file."),
+ "gfig-path", "gfig-path", esc_path,
+ gimp_filename_to_utf8 (gimprc));
+
+ g_free (gimprc);
+ g_free (esc_path);
+ }
+
+ /* Start building the dialog up */
+ top_level_dlg = gimp_dialog_new (_("Gfig"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Close"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (top_level_dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (top_level_dlg, "response",
+ G_CALLBACK (gfig_response),
+ top_level_dlg);
+
+ /* build the menu */
+ ui_manager = create_ui_manager (top_level_dlg);
+ menubar = gtk_ui_manager_get_widget (ui_manager, "/ui/gfig-menubar");
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))),
+ menubar, FALSE, FALSE, 0);
+ gtk_widget_show (menubar);
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/ui/gfig-toolbar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))),
+ toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ gfig_dialog_action_set_sensitive ("undo", undo_level >= 0);
+
+ /* Main box */
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+ gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (top_level_dlg))),
+ main_hbox, TRUE, TRUE, 0);
+
+ /* Preview itself */
+ gtk_box_pack_start (GTK_BOX (main_hbox), make_preview (), FALSE, FALSE, 0);
+
+ gtk_widget_show (gfig_context->preview);
+
+ right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (right_vbox);
+
+ /* Tool options notebook */
+ frame = gimp_frame_new ( _("Tool Options"));
+ gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ tool_options_notebook = gtk_notebook_new ();
+ gtk_container_add (GTK_CONTAINER (frame), tool_options_notebook);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (tool_options_notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (tool_options_notebook), FALSE);
+ gtk_widget_show (tool_options_notebook);
+ create_notebook_pages (tool_options_notebook);
+
+ /* Stroke frame on right side */
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gfig_context->paint_type_toggle =
+ toggle = gtk_check_button_new_with_mnemonic (_("_Stroke"));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selvals.painttype);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
+ gtk_widget_show (toggle);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ gtk_widget_set_sensitive (vbox, selvals.painttype);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (set_paint_type_callback),
+ vbox);
+
+ /* foreground color button in Stroke frame*/
+ gfig_context->fg_color = g_new0 (GimpRGB, 1);
+ gfig_context->fg_color_button = gimp_color_button_new ("Foreground",
+ SEL_BUTTON_WIDTH,
+ SEL_BUTTON_HEIGHT,
+ gfig_context->fg_color,
+ GIMP_COLOR_AREA_SMALL_CHECKS);
+ g_signal_connect (gfig_context->fg_color_button, "color-changed",
+ G_CALLBACK (set_foreground_callback),
+ gfig_context->fg_color);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->fg_color_button),
+ &gfig_context->default_style.foreground);
+ gtk_box_pack_start (GTK_BOX (vbox), gfig_context->fg_color_button,
+ FALSE, FALSE, 0);
+ gtk_widget_show (gfig_context->fg_color_button);
+
+ /* brush selector in Stroke frame */
+ gfig_context->brush_select
+ = gimp_brush_select_button_new ("Brush",
+ gfig_context->default_style.brush_name,
+ -1.0, -1, -1);
+ g_signal_connect (gfig_context->brush_select, "brush-set",
+ G_CALLBACK (gfig_brush_changed_callback), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), gfig_context->brush_select,
+ FALSE, FALSE, 0);
+ gtk_widget_show (gfig_context->brush_select);
+
+ /* Fill frame on right side */
+ frame = gimp_frame_new (_("Fill"));
+ gtk_box_pack_start (GTK_BOX (right_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* fill style combo box in Style frame */
+ gfig_context->fillstyle_combo = combo
+ = gimp_int_combo_box_new (_("No fill"), FILL_NONE,
+ _("Color fill"), FILL_COLOR,
+ _("Pattern fill"), FILL_PATTERN,
+ _("Shape gradient"), FILL_GRADIENT,
+ _("Vertical gradient"), FILL_VERTICAL,
+ _("Horizontal gradient"), FILL_HORIZONTAL,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), 0);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (select_filltype_callback),
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ fill_type_notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), fill_type_notebook, FALSE, FALSE, 0);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (fill_type_notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (fill_type_notebook), FALSE);
+ gtk_widget_show (fill_type_notebook);
+
+ /* An empty page for "No fill" */
+ empty_label = gtk_label_new ("");
+ gtk_widget_show (empty_label);
+ gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook),
+ empty_label, NULL);
+
+ /* A page for the fill color button */
+ gfig_context->bg_color = g_new0 (GimpRGB, 1);
+ gfig_context->bg_color_button = gimp_color_button_new ("Background",
+ SEL_BUTTON_WIDTH, SEL_BUTTON_HEIGHT,
+ gfig_context->bg_color,
+ GIMP_COLOR_AREA_SMALL_CHECKS);
+ g_signal_connect (gfig_context->bg_color_button, "color-changed",
+ G_CALLBACK (set_background_callback),
+ gfig_context->bg_color);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->bg_color_button),
+ &gfig_context->default_style.background);
+ gtk_widget_show (gfig_context->bg_color_button);
+ gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook),
+ gfig_context->bg_color_button, NULL);
+
+ /* A page for the pattern selector */
+ gfig_context->pattern_select
+ = gimp_pattern_select_button_new ("Pattern", gfig_context->default_style.pattern);
+ g_signal_connect (gfig_context->pattern_select, "pattern-set",
+ G_CALLBACK (gfig_pattern_changed_callback), NULL);
+ gtk_widget_show (gfig_context->pattern_select);
+ gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook),
+ gfig_context->pattern_select, NULL);
+
+ /* A page for the gradient selector */
+ gfig_context->gradient_select
+ = gimp_gradient_select_button_new ("Gradient", gfig_context->default_style.gradient);
+ g_signal_connect (gfig_context->gradient_select, "gradient-set",
+ G_CALLBACK (gfig_gradient_changed_callback), NULL);
+ gtk_widget_show (gfig_context->gradient_select);
+ gtk_notebook_append_page (GTK_NOTEBOOK (fill_type_notebook),
+ gfig_context->gradient_select, NULL);
+
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (right_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* "show image" checkbutton at bottom of style frame */
+ toggle = gtk_check_button_new_with_label (_("Show image"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gfig_context->show_background);
+ gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &gfig_context->show_background);
+ g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (gtk_widget_queue_draw),
+ gfig_context->preview);
+ gtk_widget_show (toggle);
+
+ /* "snap to grid" checkbutton at bottom of style frame */
+ toggle = gtk_check_button_new_with_label (C_("checkbutton", "Snap to grid"));
+ gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selvals.opts.snap2grid);
+ gtk_widget_show (toggle);
+ gfig_opt_widget.snap2grid = toggle;
+
+ /* "show grid" checkbutton at bottom of style frame */
+ toggle = gtk_check_button_new_with_label (_("Show grid"));
+ gtk_box_pack_end (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selvals.opts.drawgrid);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (draw_grid_clear),
+ NULL);
+ gtk_widget_show (toggle);
+ gfig_opt_widget.drawgrid = toggle;
+
+ /* Load saved objects */
+ gfig_list_load_all (gfig_path);
+
+ /* Setup initial brush settings */
+ gfig_context->bdesc.name = gimp_context_get_brush ();
+ mygimp_brush_info (&gfig_context->bdesc.width, &gfig_context->bdesc.height);
+
+ gtk_widget_show (main_hbox);
+
+ gtk_widget_show (top_level_dlg);
+
+ gfig = gfig_load_from_parasite ();
+ if (gfig)
+ {
+ gfig_list_insert (gfig);
+ new_obj_2edit (gfig);
+ gfig_style_set_context_from_style (&gfig_context->default_style);
+ gfig_style_apply (&gfig_context->default_style);
+ }
+
+ gfig_context->enable_repaint = TRUE;
+ gfig_paint_callback ();
+
+ gtk_main ();
+
+ /* FIXME */
+ return TRUE;
+}
+
+static void
+gfig_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ GFigObj *gfig;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ /* if we created a new layer, delete it */
+ if (gfig_context->using_new_layer)
+ {
+ gimp_image_remove_layer (gfig_context->image_id,
+ gfig_context->drawable_id);
+ }
+ else /* revert back to the original figure */
+ {
+ free_all_objs (gfig_context->current_obj->obj_list);
+ gfig_context->current_obj->obj_list = NULL;
+ gfig = gfig_load_from_parasite ();
+ if (gfig)
+ {
+ gfig_list_insert (gfig);
+ new_obj_2edit (gfig);
+ }
+ gfig_context->enable_repaint = TRUE;
+ gfig_paint_callback ();
+ }
+ break;
+
+ case GTK_RESPONSE_OK: /* Close button */
+ gfig_save_as_parasite ();
+ break;
+
+ default:
+ break;
+ }
+
+ gtk_widget_destroy (widget);
+ gtk_main_quit ();
+}
+
+void
+gfig_dialog_action_set_sensitive (const gchar *name,
+ gboolean sensitive)
+{
+ g_return_if_fail (name != NULL);
+
+ if (gfig_actions)
+ {
+ GtkAction *action = gtk_action_group_get_action (gfig_actions, name);
+
+ if (! action)
+ {
+ g_warning ("%s: Unable to set sensitivity of action "
+ "which doesn't exist: %s",
+ G_STRFUNC, name);
+ return;
+ }
+
+ g_object_set (action, "sensitive", sensitive ? TRUE : FALSE, NULL);
+ }
+}
+
+static gchar *
+gfig_get_user_writable_dir (void)
+{
+ if (gfig_path)
+ {
+ GList *list;
+ gchar *dir;
+
+ list = gimp_path_parse (gfig_path, 256, FALSE, NULL);
+ dir = gimp_path_get_user_writable_dir (list);
+ gimp_path_free (list);
+
+ return dir;
+ }
+
+ return g_strdup (gimp_directory ());
+}
+
+static void
+gfig_load_action_callback (GtkAction *action,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (! dialog)
+ {
+ gchar *dir;
+
+ dialog =
+ gtk_file_chooser_dialog_new (_("Load Gfig Object Collection"),
+ GTK_WINDOW (data),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (load_file_chooser_response),
+ NULL);
+
+ dir = gfig_get_user_writable_dir ();
+ if (dir)
+ {
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
+ dir);
+ g_free (dir);
+ }
+
+ gtk_widget_show (dialog);
+ }
+ else
+ {
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+}
+
+static void
+gfig_save_action_callback (GtkAction *action,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (!dialog)
+ {
+ gchar *dir;
+
+ dialog =
+ gtk_file_chooser_dialog_new (_("Save Gfig Drawing"),
+ GTK_WINDOW (data),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
+
+ /* FIXME: GFigObj should be a GObject and g_signal_connect_object()
+ * should be used here.
+ */
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_file_chooser_response),
+ gfig_context->current_obj);
+
+ dir = gfig_get_user_writable_dir ();
+ if (dir)
+ {
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), dir);
+ g_free (dir);
+ }
+
+ gtk_widget_show (dialog);
+ }
+ else
+ {
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+}
+
+static void
+gfig_close_action_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
+}
+
+static void
+gfig_undo_action_callback (GtkAction *action,
+ gpointer data)
+{
+ if (undo_level >= 0)
+ {
+ /* Free current objects an reinstate previous */
+ free_all_objs (gfig_context->current_obj->obj_list);
+ gfig_context->current_obj->obj_list = NULL;
+ tmp_bezier = tmp_line = obj_creating = NULL;
+ gfig_context->current_obj->obj_list = undo_table[undo_level];
+ undo_level--;
+ /* Update the screen */
+ gtk_widget_queue_draw (gfig_context->preview);
+ /* And preview */
+ gfig_context->current_obj->obj_status |= GFIG_MODIFIED;
+ if (gfig_context->current_obj->obj_list)
+ gfig_context->selected_obj = gfig_context->current_obj->obj_list->data;
+ else
+ gfig_context->selected_obj = NULL;
+ }
+
+ gfig_dialog_action_set_sensitive ("undo", undo_level >= 0);
+ gfig_paint_callback ();
+}
+
+static void
+gfig_clear_action_callback (GtkWidget *widget,
+ gpointer data)
+{
+ /* Make sure we can get back - if we have some objects to get back to */
+ if (!gfig_context->current_obj->obj_list)
+ return;
+
+ setup_undo ();
+ /* Free all objects */
+ free_all_objs (gfig_context->current_obj->obj_list);
+ gfig_context->current_obj->obj_list = NULL;
+ gfig_context->selected_obj = NULL;
+ obj_creating = NULL;
+ tmp_line = NULL;
+ tmp_bezier = NULL;
+ gtk_widget_queue_draw (gfig_context->preview);
+ gfig_paint_callback ();
+}
+
+void
+draw_item (cairo_t *cr, gboolean fill)
+{
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
+
+ if (fill)
+ {
+ cairo_fill_preserve (cr);
+ }
+ else
+ {
+ cairo_set_line_width (cr, 3.0);
+ cairo_stroke_preserve (cr);
+ }
+
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+ cairo_set_line_width (cr, 1.0);
+ cairo_stroke (cr);
+}
+
+/* Given a point x, y draw a circle */
+void
+draw_circle (GdkPoint *p,
+ gboolean selected,
+ cairo_t *cr)
+{
+ if (!selvals.opts.showcontrol)
+ return;
+
+ cairo_arc (cr,
+ gfig_scale_x (p->x) + .5,
+ gfig_scale_y (p->y) + .5,
+ SQ_SIZE / 2,
+ 0, 2 * G_PI);
+
+ draw_item (cr, selected);
+}
+
+/* Given a point x, y draw a square around it */
+void
+draw_sqr (GdkPoint *p,
+ gboolean selected,
+ cairo_t *cr)
+{
+ if (!selvals.opts.showcontrol)
+ return;
+
+ cairo_rectangle (cr,
+ gfig_scale_x (p->x) + .5 - SQ_SIZE / 2,
+ gfig_scale_y (p->y) + .5 - SQ_SIZE / 2,
+ SQ_SIZE,
+ SQ_SIZE);
+
+ draw_item (cr, selected);
+}
+
+static void
+gfig_list_load_all (const gchar *path)
+{
+ /* Make sure to clear any existing gfigs */
+ gfig_context->current_obj = NULL;
+ gfig_list_free_all ();
+
+ if (! gfig_list)
+ {
+ GFigObj *gfig;
+
+ /* lets have at least one! */
+ gfig = gfig_new ();
+ gfig->draw_name = g_strdup (_("First Gfig"));
+ gfig_list_insert (gfig);
+ }
+
+ gfig_context->current_obj = gfig_list->data; /* set to first entry */
+}
+
+static void
+gfig_list_free_all (void)
+{
+ g_list_free_full (gfig_list, (GDestroyNotify) gfig_free);
+ gfig_list = NULL;
+}
+
+static GtkUIManager *
+create_ui_manager (GtkWidget *window)
+{
+ static GtkActionEntry actions[] =
+ {
+ { "gfig-menubar", NULL, "GFig Menu" },
+
+ { "gfig-file-menu", NULL, "_File" },
+
+ { "open", GIMP_ICON_DOCUMENT_OPEN,
+ N_("_Open..."), "<control>O", NULL,
+ G_CALLBACK (gfig_load_action_callback) },
+
+ { "save", GIMP_ICON_DOCUMENT_SAVE,
+ N_("_Save..."), "<control>S", NULL,
+ G_CALLBACK (gfig_save_action_callback) },
+
+ { "close", GIMP_ICON_CLOSE,
+ N_("_Close"), "<control>C", NULL,
+ G_CALLBACK (gfig_close_action_callback) },
+
+ { "gfig-edit-menu", NULL, "_Edit" },
+
+ { "undo", GIMP_ICON_EDIT_UNDO,
+ N_("_Undo"), "<control>Z", NULL,
+ G_CALLBACK (gfig_undo_action_callback) },
+
+ { "clear", GIMP_ICON_EDIT_CLEAR,
+ N_("_Clear"), NULL, NULL,
+ G_CALLBACK (gfig_clear_action_callback) },
+
+ { "grid", GIMP_ICON_GRID,
+ N_("_Grid"), "<control>G", NULL,
+ G_CALLBACK (gfig_grid_action_callback) },
+
+ { "prefs", GIMP_ICON_PREFERENCES_SYSTEM,
+ N_("_Preferences..."), "<control>P", NULL,
+ G_CALLBACK (gfig_prefs_action_callback) },
+
+ { "raise", GIMP_ICON_GO_UP,
+ N_("_Raise"), "<control>U", N_("Raise selected object"),
+ G_CALLBACK (raise_selected_obj) },
+
+ { "lower", GIMP_ICON_GO_DOWN,
+ N_("_Lower"), "<control>D", N_("Lower selected object"),
+ G_CALLBACK (lower_selected_obj) },
+
+ { "top", GIMP_ICON_GO_TOP,
+ N_("Raise to _top"), "<control>T", N_("Raise selected object to top"),
+ G_CALLBACK (raise_selected_obj_to_top) },
+
+ { "bottom", GIMP_ICON_GO_BOTTOM,
+ N_("Lower to _bottom"), "<control>B", N_("Lower selected object to bottom"),
+ G_CALLBACK (lower_selected_obj_to_bottom) },
+
+ { "show_previous", GIMP_ICON_GO_PREVIOUS,
+ N_("_Previous"), "<control>H", N_("Show previous object"),
+ G_CALLBACK (select_button_clicked_lt) },
+
+ { "show_next", GIMP_ICON_GO_NEXT,
+ N_("_Next"), "<control>L", N_("Show next object"),
+ G_CALLBACK (select_button_clicked_gt) },
+
+ { "show_all", GFIG_STOCK_SHOW_ALL,
+ N_("Show _all"), "<control>A", N_("Show all objects"),
+ G_CALLBACK (select_button_clicked_eq) }
+ };
+ static GtkRadioActionEntry radio_actions[] =
+ {
+ { "line", GFIG_STOCK_LINE,
+ NULL, "L", N_("Create line"), LINE },
+
+ { "rectangle", GFIG_STOCK_RECTANGLE,
+ NULL, "R", N_("Create rectangle"), RECTANGLE },
+
+ { "circle", GFIG_STOCK_CIRCLE,
+ NULL, "C", N_("Create circle"), CIRCLE },
+
+ { "ellipse", GFIG_STOCK_ELLIPSE,
+ NULL, "E", N_("Create ellipse"), ELLIPSE },
+
+ { "arc", GFIG_STOCK_CURVE,
+ NULL, "A", N_("Create arc"), ARC },
+
+ { "polygon", GFIG_STOCK_POLYGON,
+ NULL, "P", N_("Create reg polygon"), POLY },
+
+ { "star", GFIG_STOCK_STAR,
+ NULL, "S", N_("Create star"), STAR },
+
+ { "spiral", GFIG_STOCK_SPIRAL,
+ NULL, "I", N_("Create spiral"), SPIRAL },
+
+ { "bezier", GFIG_STOCK_BEZIER,
+ NULL, "B", N_("Create bezier curve. "
+ "Shift + Button ends object creation."), BEZIER },
+
+ { "move_obj", GFIG_STOCK_MOVE_OBJECT,
+ NULL, "M", N_("Move an object"), MOVE_OBJ },
+
+ { "move_point", GFIG_STOCK_MOVE_POINT,
+ NULL, "V", N_("Move a single point"), MOVE_POINT },
+
+ { "copy", GFIG_STOCK_COPY_OBJECT,
+ NULL, "Y", N_("Copy an object"), COPY_OBJ },
+
+ { "delete", GFIG_STOCK_DELETE_OBJECT,
+ NULL, "D", N_("Delete an object"), DEL_OBJ },
+
+ { "select", GFIG_STOCK_SELECT_OBJECT,
+ NULL, "A", N_("Select an object"), SELECT_OBJ }
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+
+ gfig_actions = gtk_action_group_new ("Actions");
+
+ gtk_action_group_set_translation_domain (gfig_actions, NULL);
+
+ gtk_action_group_add_actions (gfig_actions,
+ actions,
+ G_N_ELEMENTS (actions),
+ window);
+ gtk_action_group_add_radio_actions (gfig_actions,
+ radio_actions,
+ G_N_ELEMENTS (radio_actions),
+ LINE,
+ G_CALLBACK (toggle_obj_type),
+ window);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, gfig_actions, -1);
+ g_object_unref (gfig_actions);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <menubar name=\"gfig-menubar\">"
+ " <menu name=\"File\" action=\"gfig-file-menu\">"
+ " <menuitem action=\"open\" />"
+ " <menuitem action=\"save\" />"
+ " <menuitem action=\"close\" />"
+ " </menu>"
+ " <menu name=\"Edit\" action=\"gfig-edit-menu\">"
+ " <menuitem action=\"undo\" />"
+ " <menuitem action=\"clear\" />"
+ " <menuitem action=\"grid\" />"
+ " <menuitem action=\"prefs\" />"
+ " </menu>"
+ " </menubar>"
+ "</ui>",
+ -1, NULL);
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <toolbar name=\"gfig-toolbar\">"
+ " <toolitem action=\"line\" />"
+ " <toolitem action=\"rectangle\" />"
+ " <toolitem action=\"circle\" />"
+ " <toolitem action=\"ellipse\" />"
+ " <toolitem action=\"arc\" />"
+ " <toolitem action=\"polygon\" />"
+ " <toolitem action=\"star\" />"
+ " <toolitem action=\"spiral\" />"
+ " <toolitem action=\"bezier\" />"
+ " <toolitem action=\"move_obj\" />"
+ " <toolitem action=\"move_point\" />"
+ " <toolitem action=\"copy\" />"
+ " <toolitem action=\"delete\" />"
+ " <toolitem action=\"select\" />"
+ " <separator />"
+ " <toolitem action=\"raise\" />"
+ " <toolitem action=\"lower\" />"
+ " <toolitem action=\"top\" />"
+ " <toolitem action=\"bottom\" />"
+ " <separator />"
+ " <toolitem action=\"show_previous\" />"
+ " <toolitem action=\"show_next\" />"
+ " <toolitem action=\"show_all\" />"
+ " </toolbar>"
+ "</ui>",
+ -1, NULL);
+
+ return ui_manager;
+}
+
+static void
+tool_option_no_option (GtkWidget *notebook)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new (_("This tool has no options"));
+ gtk_widget_show (label);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), label, NULL);
+}
+
+static void
+create_notebook_pages (GtkWidget *notebook)
+{
+ tool_option_no_option (notebook); /* Line */
+ tool_option_no_option (notebook); /* Rectangle */
+ tool_option_no_option (notebook); /* Circle */
+ tool_option_no_option (notebook); /* Ellipse */
+ tool_option_no_option (notebook); /* Arc */
+ tool_options_poly (notebook); /* Polygon */
+ tool_options_star (notebook); /* Star */
+ tool_options_spiral (notebook); /* Spiral */
+ tool_options_bezier (notebook); /* Bezier */
+ tool_option_no_option (notebook); /* Dummy */
+ tool_option_no_option (notebook); /* Move Object */
+ tool_option_no_option (notebook); /* Move Point */
+ tool_option_no_option (notebook); /* Copy Object */
+ tool_option_no_option (notebook); /* Delete Object */
+}
+
+static void
+raise_selected_obj_to_top (GtkWidget *widget,
+ gpointer data)
+{
+ if (!gfig_context->selected_obj)
+ return;
+
+ if (g_list_find (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj))
+ {
+ gfig_context->current_obj->obj_list =
+ g_list_remove (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_append (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ }
+ else
+ {
+ g_message ("Trying to raise object that does not exist.");
+ return;
+ }
+
+ gfig_paint_callback ();
+}
+
+static void
+lower_selected_obj_to_bottom (GtkWidget *widget,
+ gpointer data)
+{
+ if (!gfig_context->selected_obj)
+ return;
+
+ if (g_list_find (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj))
+ {
+ gfig_context->current_obj->obj_list =
+ g_list_remove (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_prepend (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ }
+ else
+ {
+ g_message ("Trying to lower object that does not exist.");
+ return;
+ }
+
+ gfig_paint_callback ();
+}
+
+static void
+raise_selected_obj (GtkWidget *widget,
+ gpointer data)
+{
+ if (!gfig_context->selected_obj)
+ return;
+
+ if (g_list_find (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj))
+ {
+ int position;
+
+ position = g_list_index (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_remove (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_insert (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj,
+ position + 1);
+ }
+ else
+ {
+ g_message ("Trying to raise object that does not exist.");
+ return;
+ }
+
+ gfig_paint_callback ();
+}
+
+
+static void
+lower_selected_obj (GtkWidget *widget,
+ gpointer data)
+{
+ if (!gfig_context->selected_obj)
+ return;
+
+ if (g_list_find (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj))
+ {
+ int position;
+
+ position = g_list_index (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_remove (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj);
+ gfig_context->current_obj->obj_list =
+ g_list_insert (gfig_context->current_obj->obj_list,
+ gfig_context->selected_obj,
+ MAX (0, position - 1));
+ }
+ else
+ {
+ g_message ("Trying to lower object that does not exist.");
+ return;
+ }
+
+ gfig_paint_callback ();
+}
+
+
+static void
+select_filltype_callback (GtkWidget *widget)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (fill_type_notebook),
+ MIN (value, FILL_GRADIENT));
+
+ gfig_context_get_current_style ()->fill_type = (FillType) value;
+
+ gfig_paint_callback ();
+}
+
+static gboolean
+gfig_paint_timeout (gpointer data)
+{
+ gfig_paint_callback ();
+
+ paint_timeout = 0;
+
+ return FALSE;
+}
+
+static void
+gfig_paint_delayed (void)
+{
+ if (paint_timeout)
+ g_source_remove (paint_timeout);
+
+ paint_timeout =
+ g_timeout_add (UPDATE_DELAY, gfig_paint_timeout, NULL);
+}
+
+static void
+gfig_prefs_action_callback (GtkAction *widget,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (!dialog)
+ {
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *toggle;
+ GtkObject *size_data;
+ GtkWidget *scale;
+ GtkAdjustment *scale_data;
+
+ dialog = gimp_dialog_new (_("Options"), "gimp-gfig-options",
+ GTK_WIDGET (data), 0, NULL, NULL,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+
+ g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* Put buttons in */
+ toggle = gtk_check_button_new_with_label (_("Show position"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ selvals.showpos);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selvals.showpos);
+ g_signal_connect_after (toggle, "toggled",
+ G_CALLBACK (gfig_pos_enable),
+ NULL);
+ gtk_widget_show (toggle);
+
+ toggle = gtk_check_button_new_with_label (_("Show control points"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ selvals.opts.showcontrol);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selvals.opts.showcontrol);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_show_image),
+ NULL);
+ gtk_widget_show (toggle);
+
+ gfig_opt_widget.showcontrol = toggle;
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.showcontrol),
+ (gpointer) &gfig_opt_widget.showcontrol);
+
+ toggle = gtk_check_button_new_with_label (_("Antialiasing"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), toggle, FALSE, FALSE, 6);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), selopt.antia);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selopt.antia);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gfig_paint_callback),
+ NULL);
+ gtk_widget_show (toggle);
+
+ table = gtk_table_new (4, 4, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 6);
+ gtk_widget_show (table);
+
+ size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Max undo:"), 100, 50,
+ selvals.maxundo,
+ MIN_UNDO, MAX_UNDO, 1, 2, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &selvals.maxundo);
+
+ page_menu_bg = gimp_int_combo_box_new (_("Transparent"), LAYER_TRANS_BG,
+ _("Background"), LAYER_BG_BG,
+ _("Foreground"), LAYER_FG_BG,
+ _("White"), LAYER_WHITE_BG,
+ _("Copy"), LAYER_COPY_BG,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (page_menu_bg), selvals.onlayerbg);
+
+ g_signal_connect (page_menu_bg, "changed",
+ G_CALLBACK (paint_combo_callback),
+ GINT_TO_POINTER (PAINT_BGS_MENU));
+
+ gimp_help_set_help_data (page_menu_bg,
+ _("Layer background type. Copy causes the "
+ "previous layer to be copied before the "
+ "draw is performed."),
+ NULL);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Background:"), 0.0, 0.5,
+ page_menu_bg, 2, FALSE);
+
+ toggle = gtk_check_button_new_with_label (_("Feather"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 2, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &selopt.feather);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gfig_paint_callback),
+ NULL);
+ gtk_widget_show (toggle);
+
+ scale_data = (GtkAdjustment *)
+ gtk_adjustment_new (selopt.feather_radius, 0.0, 100.0, 1.0, 1.0, 0.0);
+ scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, scale_data);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &selopt.feather_radius);
+ g_signal_connect (scale_data, "value-changed",
+ G_CALLBACK (gfig_paint_delayed),
+ NULL);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Radius:"), 0.0, 1.0, scale, 1, FALSE);
+
+ gtk_widget_show (dialog);
+ }
+ else
+ {
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+}
+
+static void
+gfig_grid_action_callback (GtkAction *action,
+ gpointer data)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (!dialog)
+ {
+ GtkWidget *main_vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkObject *size_data;
+ GtkObject *sectors_data;
+ GtkObject *radius_data;
+
+ dialog = gimp_dialog_new (_("Grid"), "gimp-gfig-grid",
+ GTK_WIDGET (data), 0, NULL, NULL,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+
+ g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Grid spacing:"), 100, 50,
+ selvals.opts.gridspacing,
+ MIN_GRID, MAX_GRID, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &selvals.opts.gridspacing);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (draw_grid_clear),
+ NULL);
+
+ gfig_opt_widget.gridspacing = GTK_ADJUSTMENT (size_data);
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing),
+ (gpointer) &gfig_opt_widget.gridspacing);
+
+ sectors_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Polar grid sectors desired:"), 1, 5,
+ selvals.opts.grid_sectors_desired,
+ 5, 360, 5, 1, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (sectors_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &selvals.opts.grid_sectors_desired);
+ g_signal_connect (sectors_data, "value-changed",
+ G_CALLBACK (draw_grid_clear),
+ NULL);
+
+ gfig_opt_widget.grid_sectors_desired = GTK_ADJUSTMENT (sectors_data);
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_sectors_desired),
+ (gpointer) &gfig_opt_widget.grid_sectors_desired);
+
+
+ gfig_opt_widget.gridspacing = GTK_ADJUSTMENT (size_data);
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridspacing),
+ (gpointer) &gfig_opt_widget.gridspacing);
+
+ radius_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Polar grid radius interval:"), 1, 5,
+ selvals.opts.grid_radius_interval,
+ 5, 50, 5, 1, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (radius_data, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &selvals.opts.grid_radius_interval);
+ g_signal_connect (radius_data, "value-changed",
+ G_CALLBACK (draw_grid_clear),
+ NULL);
+
+ gfig_opt_widget.grid_radius_interval = GTK_ADJUSTMENT (radius_data);
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.grid_radius_interval),
+ (gpointer) &gfig_opt_widget.grid_radius_interval);
+
+ combo = gimp_int_combo_box_new (_("Rectangle"), RECT_GRID,
+ _("Polar"), POLAR_GRID,
+ _("Isometric"), ISO_GRID,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), selvals.opts.gridtype);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gridtype_combo_callback),
+ GINT_TO_POINTER (GRID_TYPE_MENU));
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Grid type:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ gfig_opt_widget.gridtypemenu = combo;
+ g_object_add_weak_pointer (G_OBJECT (gfig_opt_widget.gridtypemenu),
+ (gpointer) &gfig_opt_widget.gridtypemenu);
+
+ combo = gimp_int_combo_box_new (_("Normal"), GFIG_NORMAL_GC,
+ _("Black"), GFIG_BLACK_GC,
+ _("White"), GFIG_WHITE_GC,
+ _("Grey"), GFIG_GREY_GC,
+ _("Darker"), GFIG_DARKER_GC,
+ _("Lighter"), GFIG_LIGHTER_GC,
+ _("Very dark"), GFIG_VERY_DARK_GC,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), grid_gc_type);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gridtype_combo_callback),
+ GINT_TO_POINTER (GRID_RENDER_MENU));
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Grid color:"), 0.0, 0.5,
+ combo, 2, FALSE);
+
+ gtk_widget_show (dialog);
+ }
+ else
+ {
+ gtk_window_present (GTK_WINDOW (dialog));
+ }
+}
+
+void
+options_update (GFigObj *old_obj)
+{
+ /* Save old vals */
+ if (selvals.opts.gridspacing != old_obj->opts.gridspacing)
+ {
+ old_obj->opts.gridspacing = selvals.opts.gridspacing;
+ }
+ if (selvals.opts.grid_sectors_desired != old_obj->opts.grid_sectors_desired)
+ {
+ old_obj->opts.grid_sectors_desired = selvals.opts.grid_sectors_desired;
+ }
+ if (selvals.opts.grid_radius_interval != old_obj->opts.grid_radius_interval)
+ {
+ old_obj->opts.grid_radius_interval = selvals.opts.grid_radius_interval;
+ }
+ if (selvals.opts.gridtype != old_obj->opts.gridtype)
+ {
+ old_obj->opts.gridtype = selvals.opts.gridtype;
+ }
+ if (selvals.opts.drawgrid != old_obj->opts.drawgrid)
+ {
+ old_obj->opts.drawgrid = selvals.opts.drawgrid;
+ }
+ if (selvals.opts.snap2grid != old_obj->opts.snap2grid)
+ {
+ old_obj->opts.snap2grid = selvals.opts.snap2grid;
+ }
+ if (selvals.opts.lockongrid != old_obj->opts.lockongrid)
+ {
+ old_obj->opts.lockongrid = selvals.opts.lockongrid;
+ }
+ if (selvals.opts.showcontrol != old_obj->opts.showcontrol)
+ {
+ old_obj->opts.showcontrol = selvals.opts.showcontrol;
+ }
+
+ /* New vals */
+ if (selvals.opts.gridspacing != gfig_context->current_obj->opts.gridspacing)
+ {
+ if (gfig_opt_widget.gridspacing)
+ gtk_adjustment_set_value (gfig_opt_widget.gridspacing,
+ gfig_context->current_obj->opts.gridspacing);
+ }
+ if (selvals.opts.grid_sectors_desired != gfig_context->current_obj->opts.grid_sectors_desired)
+ {
+ if (gfig_opt_widget.grid_sectors_desired)
+ gtk_adjustment_set_value (gfig_opt_widget.grid_sectors_desired,
+ gfig_context->current_obj->opts.grid_sectors_desired);
+ }
+ if (selvals.opts.grid_radius_interval != gfig_context->current_obj->opts.grid_radius_interval)
+ {
+ if (gfig_opt_widget.grid_radius_interval)
+ gtk_adjustment_set_value (gfig_opt_widget.grid_radius_interval,
+ gfig_context->current_obj->opts.grid_radius_interval);
+ }
+ if (selvals.opts.gridtype != gfig_context->current_obj->opts.gridtype)
+ {
+ if (gfig_opt_widget.gridtypemenu)
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gfig_opt_widget.gridtypemenu),
+ gfig_context->current_obj->opts.gridtype);
+ }
+ if (selvals.opts.drawgrid != gfig_context->current_obj->opts.drawgrid)
+ {
+ if (gfig_opt_widget.drawgrid)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.drawgrid),
+ gfig_context->current_obj->opts.drawgrid);
+ }
+ if (selvals.opts.snap2grid != gfig_context->current_obj->opts.snap2grid)
+ {
+ if (gfig_opt_widget.snap2grid)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.snap2grid),
+ gfig_context->current_obj->opts.snap2grid);
+ }
+ if (selvals.opts.lockongrid != gfig_context->current_obj->opts.lockongrid)
+ {
+ if (gfig_opt_widget.lockongrid)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.lockongrid),
+ gfig_context->current_obj->opts.lockongrid);
+ }
+ if (selvals.opts.showcontrol != gfig_context->current_obj->opts.showcontrol)
+ {
+ if (gfig_opt_widget.showcontrol)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_opt_widget.showcontrol),
+ gfig_context->current_obj->opts.showcontrol);
+ }
+}
+
+static void
+save_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ GFigObj *obj)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (chooser);
+
+ obj->filename = filename;
+
+ gfig_context->current_obj = obj;
+ gfig_save_callbk ();
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static GfigObject *
+gfig_select_obj_by_number (gint count)
+{
+ GList *objs;
+ GfigObject *object = NULL;
+ gint k;
+
+ gfig_context->selected_obj = NULL;
+
+ for (objs = gfig_context->current_obj->obj_list, k = 0;
+ objs;
+ objs = g_list_next (objs), k++)
+ {
+ if (k == obj_show_single)
+ {
+ object = objs->data;
+ gfig_context->selected_obj = object;
+ gfig_style_set_context_from_style (&object->style);
+ break;
+ }
+ }
+
+ return object;
+}
+
+static void
+select_button_clicked (gint type)
+{
+ gint count = 0;
+
+ if (gfig_context->current_obj)
+ {
+ count = g_list_length (gfig_context->current_obj->obj_list);
+ }
+
+ switch (type)
+ {
+ case OBJ_SELECT_LT:
+ obj_show_single--;
+ if (obj_show_single < 0)
+ obj_show_single = count - 1;
+ break;
+
+ case OBJ_SELECT_GT:
+ obj_show_single++;
+ if (obj_show_single >= count)
+ obj_show_single = 0;
+ break;
+
+ case OBJ_SELECT_EQ:
+ obj_show_single = -1; /* Reset to show all */
+ break;
+
+ default:
+ break;
+ }
+
+ if (obj_show_single >= 0)
+ gfig_select_obj_by_number (obj_show_single);
+
+ draw_grid_clear ();
+ gfig_paint_callback ();
+}
+
+static void
+select_button_clicked_lt (void)
+{
+ select_button_clicked (OBJ_SELECT_LT);
+}
+
+static void
+select_button_clicked_gt (void)
+{
+ select_button_clicked (OBJ_SELECT_GT);
+}
+
+static void
+select_button_clicked_eq (void)
+{
+ select_button_clicked (OBJ_SELECT_EQ);
+}
+
+/* Special case for now - options on poly/star/spiral button */
+
+GtkWidget *
+num_sides_widget (const gchar *d_title,
+ gint *num_sides,
+ gint *which_way,
+ gint adj_min,
+ gint adj_max)
+{
+ GtkWidget *table;
+ GtkObject *size_data;
+
+ table = gtk_table_new (which_way ? 2 : 1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_widget_show (table);
+
+ size_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Sides:"), 0, 0,
+ *num_sides, adj_min, adj_max, 1, 10, 0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (size_data, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ num_sides);
+
+ if (which_way)
+ {
+ GtkWidget *combo = gimp_int_combo_box_new (_("Right"), 0,
+ _("Left"), 1,
+ NULL);
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), *which_way);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ which_way);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Orientation:"), 0.0, 0.5,
+ combo, 1, FALSE);
+ }
+ return table;
+}
+
+void
+gfig_paint (BrushType brush_type,
+ gint32 drawable_ID,
+ gint seg_count,
+ gdouble line_pnts[])
+{
+ switch (brush_type)
+ {
+ case BRUSH_BRUSH_TYPE:
+ gimp_paintbrush (drawable_ID,
+ selvals.brushfade,
+ seg_count, line_pnts,
+ GIMP_PAINT_CONSTANT,
+ selvals.brushgradient);
+ break;
+
+ case BRUSH_PENCIL_TYPE:
+ gimp_pencil (drawable_ID,
+ seg_count, line_pnts);
+ break;
+
+ case BRUSH_AIRBRUSH_TYPE:
+ gimp_airbrush (drawable_ID,
+ selvals.airbrushpressure,
+ seg_count, line_pnts);
+ break;
+
+ case BRUSH_PATTERN_TYPE:
+ gimp_clone (drawable_ID,
+ drawable_ID,
+ GIMP_CLONE_PATTERN,
+ 0.0, 0.0,
+ seg_count, line_pnts);
+ break;
+ }
+}
+
+
+static void
+paint_combo_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint mtype = GPOINTER_TO_INT (data);
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ switch (mtype)
+ {
+ case PAINT_BGS_MENU:
+ selvals.onlayerbg = (LayersBGType) value;
+ break;
+
+ default:
+ g_return_if_reached ();
+ break;
+ }
+
+ gfig_paint_callback ();
+}
+
+
+static void
+gridtype_combo_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint mtype = GPOINTER_TO_INT (data);
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &value);
+
+ switch (mtype)
+ {
+ case GRID_TYPE_MENU:
+ selvals.opts.gridtype = value;
+ break;
+
+ case GRID_RENDER_MENU:
+ grid_gc_type = value;
+ break;
+
+ default:
+ g_return_if_reached ();
+ break;
+ }
+
+ draw_grid_clear ();
+}
+
+/*
+ * The edit gfig name attributes dialog
+ * Modified from Gimp source - layer edit.
+ */
+
+typedef struct _GfigListOptions
+{
+ GtkWidget *query_box;
+ GtkWidget *name_entry;
+ GtkWidget *list_entry;
+ GFigObj *obj;
+ gboolean created;
+} GfigListOptions;
+
+static void
+load_file_chooser_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+ GFigObj *gfig;
+ GFigObj *current_saved;
+
+ filename = gtk_file_chooser_get_filename (chooser);
+
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ {
+ /* Hack - current object MUST be NULL to prevent setup_undo ()
+ * from kicking in.
+ */
+ current_saved = gfig_context->current_obj;
+ gfig_context->current_obj = NULL;
+ gfig = gfig_load (filename, filename);
+ gfig_context->current_obj = current_saved;
+
+ if (gfig)
+ {
+ /* Read only ?*/
+ if (access (filename, W_OK))
+ gfig->obj_status |= GFIG_READONLY;
+
+ gfig_list_insert (gfig);
+ new_obj_2edit (gfig);
+ }
+ }
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+ gfig_paint_callback ();
+}
+
+void
+paint_layer_fill (gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+{
+ GimpFillType fill_type = GIMP_FILL_FOREGROUND;
+ Style *current_style;
+
+ current_style = gfig_context_get_current_style ();
+
+ gimp_context_push ();
+
+ gimp_context_set_paint_mode (GIMP_LAYER_MODE_NORMAL_LEGACY);
+ gimp_context_set_opacity (100.0);
+ gimp_context_set_gradient_repeat_mode (GIMP_REPEAT_NONE);
+ gimp_context_set_gradient_reverse (FALSE);
+
+ switch (current_style->fill_type)
+ {
+ case FILL_NONE:
+ return;
+
+ case FILL_COLOR:
+ fill_type = GIMP_FILL_BACKGROUND;
+ break;
+
+ case FILL_PATTERN:
+ fill_type = GIMP_FILL_PATTERN;
+ break;
+
+ case FILL_GRADIENT:
+ gimp_drawable_edit_gradient_fill (gfig_context->drawable_id,
+ GIMP_GRADIENT_SHAPEBURST_DIMPLED,
+ 0.0, /* offset */
+ FALSE, /* supersampling */
+ 0, /* max_depth */
+ 0.0, /* threshold */
+ FALSE, /* dither */
+ 0.0, 0.0, /* (x1, y1) - ignored */
+ 0.0, 0.0); /* (x2, y2) - ignored */
+ return;
+ case FILL_VERTICAL:
+ gimp_drawable_edit_gradient_fill (gfig_context->drawable_id,
+ GIMP_GRADIENT_LINEAR,
+ 0.0,
+ FALSE,
+ 0,
+ 0.0,
+ FALSE,
+ x1, y1,
+ x1, y2);
+ return;
+ case FILL_HORIZONTAL:
+ gimp_drawable_edit_gradient_fill (gfig_context->drawable_id,
+ GIMP_GRADIENT_LINEAR,
+ 0.0,
+ FALSE,
+ 0,
+ 0.0,
+ FALSE,
+ x1, y1,
+ x2, y1);
+ return;
+ }
+
+ gimp_context_set_opacity (current_style->fill_opacity);
+
+ gimp_drawable_edit_fill (gfig_context->drawable_id,
+ fill_type);
+
+ gimp_context_pop ();
+}
+
+void
+gfig_paint_callback (void)
+{
+ GList *objs;
+ gint ccount = 0;
+ GfigObject *object;
+
+ if (!gfig_context->enable_repaint || !gfig_context->current_obj)
+ return;
+
+ objs = gfig_context->current_obj->obj_list;
+
+ gimp_drawable_fill (gfig_context->drawable_id, GIMP_FILL_TRANSPARENT);
+
+ while (objs)
+ {
+ if (ccount == obj_show_single || obj_show_single == -1)
+ {
+ FillType saved_filltype;
+
+ object = objs->data;
+
+ gfig_style_apply (&object->style);
+
+ saved_filltype = gfig_context_get_current_style ()->fill_type;
+ gfig_context_get_current_style ()->fill_type = object->style.fill_type;
+ object->class->paintfunc (object);
+ gfig_context_get_current_style ()->fill_type = saved_filltype;
+ }
+
+ objs = g_list_next (objs);
+
+ ccount++;
+ }
+
+ gimp_displays_flush ();
+
+ if (back_pixbuf)
+ {
+ g_object_unref (back_pixbuf);
+ back_pixbuf = NULL;
+ }
+
+ gtk_widget_queue_draw (gfig_context->preview);
+}
+
+/* Draw the grid on the screen
+ */
+
+void
+draw_grid_clear (void)
+{
+ /* wipe slate and start again */
+ gtk_widget_queue_draw (gfig_context->preview);
+}
+
+static void
+toggle_show_image (void)
+{
+ /* wipe slate and start again */
+ draw_grid_clear ();
+}
+
+static void
+toggle_obj_type (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer data)
+{
+ static GdkCursor *p_cursors[DEL_OBJ + 1];
+ GdkCursorType ctype = GDK_LAST_CURSOR;
+ DobjType new_type;
+
+ new_type = gtk_radio_action_get_current_value (action);
+ if (selvals.otype != new_type)
+ {
+ /* Mem leak */
+ obj_creating = NULL;
+ tmp_line = NULL;
+ tmp_bezier = NULL;
+
+ if (new_type < MOVE_OBJ) /* Eeeeek */
+ {
+ obj_show_single = -1; /* Cancel select preview */
+ }
+ /* Update draw areas */
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+
+ selvals.otype = new_type;
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (tool_options_notebook),
+ new_type - 1);
+
+ switch (selvals.otype)
+ {
+ case LINE:
+ case RECTANGLE:
+ case CIRCLE:
+ case ELLIPSE:
+ case ARC:
+ case POLY:
+ case STAR:
+ case SPIRAL:
+ case BEZIER:
+ default:
+ ctype = GDK_CROSSHAIR;
+ break;
+ case MOVE_OBJ:
+ case MOVE_POINT:
+ case COPY_OBJ:
+ case MOVE_COPY_OBJ:
+ ctype = GDK_DIAMOND_CROSS;
+ break;
+ case DEL_OBJ:
+ ctype = GDK_PIRATE;
+ break;
+ }
+
+ if (!p_cursors[selvals.otype])
+ {
+ GdkDisplay *display = gtk_widget_get_display (gfig_context->preview);
+
+ p_cursors[selvals.otype] = gdk_cursor_new_for_display (display, ctype);
+ }
+
+ gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview),
+ p_cursors[selvals.otype]);
+}
+
+/* This could belong in a separate file ... but makes it easier to lump into
+ * one when compiling the plugin.
+ */
+
+/* Given a number of float co-ords adjust for scaling back to org size */
+/* Size is number of PAIRS of points */
+/* FP + int variants */
+
+static void
+scale_to_orginal_x (gdouble *list)
+{
+ *list *= scale_x_factor;
+}
+
+gint
+gfig_scale_x (gint x)
+{
+ if (!selvals.scaletoimage)
+ return (gint) (x * (1 / scale_x_factor));
+ else
+ return x;
+}
+
+static void
+scale_to_orginal_y (gdouble *list)
+{
+ *list *= scale_y_factor;
+}
+
+gint
+gfig_scale_y (gint y)
+{
+ if (!selvals.scaletoimage)
+ return (gint) (y * (1 / scale_y_factor));
+ else
+ return y;
+}
+
+/* Pairs x followed by y */
+void
+scale_to_original_xy (gdouble *list,
+ gint size)
+{
+ gint i;
+
+ for (i = 0; i < size * 2; i += 2)
+ {
+ scale_to_orginal_x (&list[i]);
+ scale_to_orginal_y (&list[i + 1]);
+ }
+}
+
+/* Pairs x followed by y */
+void
+scale_to_xy (gdouble *list,
+ gint size)
+{
+ gint i;
+
+ for (i = 0; i < size * 2; i += 2)
+ {
+ list[i] *= (org_scale_x_factor / scale_x_factor);
+ list[i + 1] *= (org_scale_y_factor / scale_y_factor);
+ }
+}
+
+void
+gfig_draw_arc (gint x, gint y, gint width, gint height, gint angle1,
+ gint angle2, cairo_t *cr)
+{
+ cairo_save (cr);
+
+ cairo_translate (cr, gfig_scale_x (x), gfig_scale_y (y));
+ cairo_scale (cr, gfig_scale_x (width), gfig_scale_y (height));
+
+ if (angle1 < angle2)
+ cairo_arc (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180);
+ else
+ cairo_arc_negative (cr, 0., 0., 1., angle1 * G_PI / 180, angle2 * G_PI / 180);
+
+ cairo_restore (cr);
+
+ draw_item (cr, FALSE);
+}
+
+void
+gfig_draw_line (gint x0, gint y0, gint x1, gint y1, cairo_t *cr)
+{
+ cairo_move_to (cr, gfig_scale_x (x0) + .5, gfig_scale_y (y0) + .5);
+ cairo_line_to (cr, gfig_scale_x (x1) + .5, gfig_scale_y (y1) + .5);
+
+ draw_item (cr, FALSE);
+}
diff --git a/plug-ins/gfig/gfig-dialog.h b/plug-ins/gfig/gfig-dialog.h
new file mode 100644
index 0000000..1b4eced
--- /dev/null
+++ b/plug-ins/gfig/gfig-dialog.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_DIALOG_H__
+#define __GFIG_DIALOG_H__
+
+extern gint undo_level; /* Last slot filled in -1 = no undo */
+extern GList *undo_table[MAX_UNDO];
+
+gboolean gfig_dialog (void);
+void gfig_dialog_action_set_sensitive (const gchar *name,
+ gboolean sensitive);
+
+void options_update (GFigObj *old_obj);
+void tool_option_page_update (GtkWidget *button,
+ GtkWidget *notebook);
+
+#endif /* __GFIG_DIALOG_H__ */
diff --git a/plug-ins/gfig/gfig-dobject.c b/plug-ins/gfig/gfig-dobject.c
new file mode 100644
index 0000000..1340cec
--- /dev/null
+++ b/plug-ins/gfig/gfig-dobject.c
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dialog.h"
+#include "gfig-style.h"
+#include "gfig-arc.h"
+#include "gfig-bezier.h"
+#include "gfig-circle.h"
+#include "gfig-dobject.h"
+#include "gfig-ellipse.h"
+#include "gfig-line.h"
+#include "gfig-poly.h"
+#include "gfig-rectangle.h"
+#include "gfig-spiral.h"
+#include "gfig-star.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static GfigObject *operation_obj = NULL;
+static GdkPoint *move_all_pnt; /* Point moving all from */
+
+
+static void draw_one_obj (GfigObject *obj,
+ cairo_t *cr);
+static void do_move_obj (GfigObject *obj,
+ GdkPoint *to_pnt);
+static void do_move_all_obj (GdkPoint *to_pnt);
+static void do_move_obj_pnt (GfigObject *obj,
+ GdkPoint *to_pnt);
+static void remove_obj_from_list (GFigObj *obj,
+ GfigObject *del_obj);
+static gint scan_obj_points (DobjPoints *opnt,
+ GdkPoint *pnt);
+
+void
+d_save_object (GfigObject *obj,
+ GString *string)
+{
+ do_save_obj (obj, string);
+
+ switch (obj->type)
+ {
+ case BEZIER:
+ case POLY:
+ case SPIRAL:
+ case STAR:
+ g_string_append_printf (string, "<EXTRA>\n");
+ g_string_append_printf (string, "%d\n</EXTRA>\n", obj->type_data);
+ break;
+ default:
+ break;
+ }
+}
+
+static DobjType
+gfig_read_object_type (gchar *desc)
+{
+ gchar *ptr = desc;
+ DobjType type;
+
+ if (*ptr != '<')
+ return OBJ_TYPE_NONE;
+
+ ptr++;
+
+ for (type = LINE; type < NUM_OBJ_TYPES; type++)
+ {
+ if (ptr == strstr (ptr, dobj_class[type].name))
+ return type;
+ }
+
+ return OBJ_TYPE_NONE;
+}
+
+GfigObject *
+d_load_object (gchar *desc,
+ FILE *fp)
+{
+ GfigObject *new_obj = NULL;
+ gint xpnt;
+ gint ypnt;
+ gchar buf[MAX_LOAD_LINE];
+ DobjType type;
+
+ type = gfig_read_object_type (desc);
+ if (type == OBJ_TYPE_NONE)
+ {
+ g_message ("Error loading object: type not recognized.");
+ return NULL;
+ }
+
+ while (get_line (buf, MAX_LOAD_LINE, fp, 0))
+ {
+ if (sscanf (buf, "%d %d", &xpnt, &ypnt) != 2)
+ {
+ /* Read <EXTRA> block if there is one */
+ if (!strcmp ("<EXTRA>", buf))
+ {
+ if ( !new_obj)
+ {
+ g_message ("Error while loading object (no points)");
+ return NULL;
+ }
+
+ get_line (buf, MAX_LOAD_LINE, fp, 0);
+
+ if (sscanf (buf, "%d", &new_obj->type_data) != 1)
+ {
+ g_message ("Error while loading object (no type data)");
+ g_free (new_obj);
+ return NULL;
+ }
+
+ get_line (buf, MAX_LOAD_LINE, fp, 0);
+ if (strcmp ("</EXTRA>", buf))
+ {
+ g_message ("Syntax error while loading object");
+ g_free (new_obj);
+ return NULL;
+ }
+ /* Go around and read the last line */
+ continue;
+ }
+ else
+ return new_obj;
+ }
+
+ if (!new_obj)
+ new_obj = d_new_object (type, xpnt, ypnt);
+ else
+ d_pnt_add_line (new_obj, xpnt, ypnt, -1);
+ }
+
+ return new_obj;
+}
+
+GfigObject *
+d_new_object (DobjType type,
+ gint x,
+ gint y)
+{
+ GfigObject *nobj = g_new0 (GfigObject, 1);
+
+ nobj->type = type;
+ nobj->class = &dobj_class[type];
+ nobj->points = new_dobjpoint (x, y);
+
+ nobj->type_data = 0;
+
+ if (type == BEZIER)
+ {
+ nobj->type_data = 4;
+ }
+ else if (type == POLY)
+ {
+ nobj->type_data = 3; /* default to 3 sides */
+ }
+ else if (type == SPIRAL)
+ {
+ nobj->type_data = 4; /* default to 4 turns */
+ }
+ else if (type == STAR)
+ {
+ nobj->type_data = 3; /* default to 3 sides 6 points */
+ }
+
+ return nobj;
+}
+
+void
+gfig_init_object_classes (void)
+{
+ d_arc_object_class_init ();
+ d_line_object_class_init ();
+ d_rectangle_object_class_init ();
+ d_circle_object_class_init ();
+ d_ellipse_object_class_init ();
+ d_poly_object_class_init ();
+ d_spiral_object_class_init ();
+ d_star_object_class_init ();
+ d_bezier_object_class_init ();
+}
+
+/* Delete a list of points */
+void
+d_delete_dobjpoints (DobjPoints * pnts)
+{
+ DobjPoints *next;
+ DobjPoints *pnt2del = pnts;
+
+ while (pnt2del)
+ {
+ next = pnt2del->next;
+ g_free (pnt2del);
+ pnt2del = next;
+ }
+}
+
+DobjPoints *
+new_dobjpoint (gint x, gint y)
+{
+ DobjPoints *npnt = g_new0 (DobjPoints, 1);
+
+ npnt->pnt.x = x;
+ npnt->pnt.y = y;
+
+ return npnt;
+}
+
+DobjPoints *
+d_copy_dobjpoints (DobjPoints *pnts)
+{
+ DobjPoints *ret = NULL;
+ DobjPoints *head = NULL;
+ DobjPoints *newpnt;
+ DobjPoints *pnt2copy;
+
+ for (pnt2copy = pnts; pnt2copy; pnt2copy = pnt2copy->next)
+ {
+ newpnt = new_dobjpoint (pnt2copy->pnt.x, pnt2copy->pnt.y);
+
+ if (!ret)
+ {
+ head = ret = newpnt;
+ }
+ else
+ {
+ head->next = newpnt;
+ head = newpnt;
+ }
+ }
+
+ return ret;
+}
+
+static DobjPoints *
+get_diffs (GfigObject *obj,
+ gint *xdiff,
+ gint *ydiff,
+ GdkPoint *to_pnt)
+{
+ DobjPoints *spnt;
+
+ g_assert (obj != NULL);
+
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ if (spnt->found_me)
+ {
+ *xdiff = spnt->pnt.x - to_pnt->x;
+ *ydiff = spnt->pnt.y - to_pnt->y;
+ return spnt;
+ }
+ }
+ return NULL;
+}
+
+static gboolean
+inside_sqr (GdkPoint *cpnt,
+ GdkPoint *testpnt)
+{
+ /* Return TRUE if testpnt is near cpnt */
+ gint x = cpnt->x;
+ gint y = cpnt->y;
+ gint tx = testpnt->x;
+ gint ty = testpnt->y;
+
+ return (abs (x - tx) <= SQ_SIZE && abs (y - ty) < SQ_SIZE);
+}
+
+static gboolean
+scan_obj_points (DobjPoints *opnt,
+ GdkPoint *pnt)
+{
+ while (opnt)
+ {
+ if (inside_sqr (&opnt->pnt, pnt))
+ {
+ opnt->found_me = TRUE;
+ return TRUE;
+ }
+ opnt->found_me = FALSE;
+ opnt = opnt->next;
+ }
+ return FALSE;
+}
+
+static GfigObject *
+get_nearest_objs (GFigObj *obj,
+ GdkPoint *pnt)
+{
+ /* Nearest object to given point or NULL */
+ GList *all;
+ GfigObject *test_obj;
+ gint count = 0;
+
+ if (!obj)
+ return NULL;
+
+ for (all = obj->obj_list; all; all = g_list_next (all))
+ {
+ test_obj = all->data;
+
+ if (count == obj_show_single || obj_show_single == -1)
+ if (scan_obj_points (test_obj->points, pnt))
+ {
+ return test_obj;
+ }
+ count++;
+ }
+ return NULL;
+}
+
+void
+object_operation_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ GfigObject *new_obj;
+
+ /* Find point in given object list */
+ operation_obj = get_nearest_objs (gfig_context->current_obj, pnt);
+
+ /* Special case if shift down && move obj then moving all objs */
+
+ if (shift_down && selvals.otype == MOVE_OBJ)
+ {
+ move_all_pnt = g_new (GdkPoint, 1);
+ *move_all_pnt = *pnt; /* Structure copy */
+ setup_undo ();
+ return;
+ }
+
+ if (!operation_obj)
+ return;/* None to work on */
+
+ gfig_context->selected_obj = operation_obj;
+
+ setup_undo ();
+
+ switch (selvals.otype)
+ {
+ case MOVE_OBJ:
+ if (operation_obj->type == BEZIER)
+ {
+ tmp_bezier = operation_obj;
+ }
+ break;
+
+ case MOVE_POINT:
+ if (operation_obj->type == BEZIER)
+ {
+ tmp_bezier = operation_obj;
+ }
+ /* If shift is down the break into sep lines */
+ if ((operation_obj->type == POLY
+ || operation_obj->type == STAR)
+ && shift_down)
+ {
+ switch (operation_obj->type)
+ {
+ case POLY:
+ d_poly2lines (operation_obj);
+ break;
+ case STAR:
+ d_star2lines (operation_obj);
+ break;
+ default:
+ break;
+ }
+ /* Re calc which object point we are looking at */
+ scan_obj_points (operation_obj->points, pnt);
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ break;
+
+ case COPY_OBJ:
+ /* Copy the "operation object" */
+ /* Then bung us into "copy/move" mode */
+
+ new_obj = (GfigObject*) operation_obj->class->copyfunc (operation_obj);
+ if (new_obj)
+ {
+ gfig_style_copy (&new_obj->style, &operation_obj->style, "Object");
+ scan_obj_points (new_obj->points, pnt);
+ add_to_all_obj (gfig_context->current_obj, new_obj);
+ operation_obj = new_obj;
+ selvals.otype = MOVE_COPY_OBJ;
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ break;
+
+ case DEL_OBJ:
+ remove_obj_from_list (gfig_context->current_obj, operation_obj);
+ break;
+
+ case SELECT_OBJ:
+ /* don't need to do anything */
+ break;
+
+ case MOVE_COPY_OBJ: /* Never when button down */
+ default:
+ g_warning ("Internal error selvals.otype object operation start");
+ break;
+ }
+
+}
+
+void
+object_operation_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (selvals.otype != DEL_OBJ && operation_obj &&
+ operation_obj->type == BEZIER)
+ {
+ tmp_bezier = NULL; /* use as switch */
+ }
+
+ if (operation_obj && selvals.otype != DEL_OBJ)
+ gfig_style_set_context_from_style (&operation_obj->style);
+
+ operation_obj = NULL;
+
+ if (move_all_pnt)
+ {
+ g_free (move_all_pnt);
+ move_all_pnt = NULL;
+ }
+
+ /* Special case - if copying mode MUST be copy when button up received */
+ if (selvals.otype == MOVE_COPY_OBJ)
+ selvals.otype = COPY_OBJ;
+}
+
+/* Move object around */
+void
+object_operation (GdkPoint *to_pnt,
+ gboolean shift_down)
+{
+ /* Must do different things depending on object type */
+ /* but must have object to operate on! */
+
+ /* Special case - if shift own and move_obj then move ALL objects */
+ if (move_all_pnt && shift_down && selvals.otype == MOVE_OBJ)
+ {
+ do_move_all_obj (to_pnt);
+ return;
+ }
+
+ if (!operation_obj)
+ return;
+
+ switch (selvals.otype)
+ {
+ case MOVE_OBJ:
+ case MOVE_COPY_OBJ:
+ switch (operation_obj->type)
+ {
+ case LINE:
+ case RECTANGLE:
+ case CIRCLE:
+ case ELLIPSE:
+ case POLY:
+ case ARC:
+ case STAR:
+ case SPIRAL:
+ case BEZIER:
+ do_move_obj (operation_obj, to_pnt);
+ break;
+ default:
+ /* Internal error */
+ g_warning ("Internal error in operation_obj->type");
+ break;
+ }
+ break;
+ case MOVE_POINT:
+ switch (operation_obj->type)
+ {
+ case LINE:
+ case RECTANGLE:
+ case CIRCLE:
+ case ELLIPSE:
+ case POLY:
+ case ARC:
+ case STAR:
+ case SPIRAL:
+ case BEZIER:
+ do_move_obj_pnt (operation_obj, to_pnt);
+ break;
+ default:
+ /* Internal error */
+ g_warning ("Internal error in operation_obj->type");
+ break;
+ }
+ break;
+ case DEL_OBJ:
+ case SELECT_OBJ:
+ break;
+ case COPY_OBJ: /* Should have been changed to MOVE_COPY_OBJ */
+ default:
+ g_warning ("Internal error selvals.otype");
+ break;
+ }
+}
+
+static void
+update_pnts (GfigObject *obj,
+ gint xdiff,
+ gint ydiff)
+{
+ DobjPoints *spnt;
+
+ g_assert (obj != NULL);
+
+ /* Update all pnts */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ spnt->pnt.x -= xdiff;
+ spnt->pnt.y -= ydiff;
+ }
+}
+
+static void
+remove_obj_from_list (GFigObj *obj,
+ GfigObject *del_obj)
+{
+ /* Nearest object to given point or NULL */
+
+ g_assert (del_obj != NULL);
+
+ if (g_list_find (obj->obj_list, del_obj))
+ {
+ obj->obj_list = g_list_remove (obj->obj_list, del_obj);
+
+ free_one_obj (del_obj);
+
+ if (obj->obj_list)
+ gfig_context->selected_obj = obj->obj_list->data;
+ else
+ gfig_context->selected_obj = NULL;
+
+ if (obj_show_single != -1)
+ {
+ /* We've just deleted the only visible one */
+ draw_grid_clear ();
+ obj_show_single = -1; /* Show entry again */
+ }
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+ else
+ g_warning (_("Hey, where has the object gone?"));
+}
+
+static void
+do_move_all_obj (GdkPoint *to_pnt)
+{
+ /* Move all objects in one go */
+ /* Undraw/then draw in new pos */
+ gint xdiff = move_all_pnt->x - to_pnt->x;
+ gint ydiff = move_all_pnt->y - to_pnt->y;
+
+ if (xdiff || ydiff)
+ {
+ GList *all;
+
+ for (all = gfig_context->current_obj->obj_list; all; all = all->next)
+ {
+ GfigObject *obj = all->data;
+
+ update_pnts (obj, xdiff, ydiff);
+ }
+
+ *move_all_pnt = *to_pnt;
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+}
+
+void
+do_save_obj (GfigObject *obj,
+ GString *string)
+{
+ DobjPoints *spnt;
+
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ g_string_append_printf (string, "%d %d\n", spnt->pnt.x, spnt->pnt.y);
+ }
+}
+
+static void
+do_move_obj (GfigObject *obj,
+ GdkPoint *to_pnt)
+{
+ /* Move the whole line - undraw the line to start with */
+ /* Then draw in new pos */
+ gint xdiff = 0;
+ gint ydiff = 0;
+
+ get_diffs (obj, &xdiff, &ydiff, to_pnt);
+
+ if (xdiff || ydiff)
+ {
+ update_pnts (obj, xdiff, ydiff);
+
+ gtk_widget_queue_draw (gfig_context->preview);
+ }
+}
+
+static void
+do_move_obj_pnt (GfigObject *obj,
+ GdkPoint *to_pnt)
+{
+ /* Move the whole line - undraw the line to start with */
+ /* Then draw in new pos */
+ DobjPoints *spnt;
+ gint xdiff = 0;
+ gint ydiff = 0;
+
+ spnt = get_diffs (obj, &xdiff, &ydiff, to_pnt);
+
+ if ((!xdiff && !ydiff) || !spnt)
+ return;
+
+ spnt->pnt.x = spnt->pnt.x - xdiff;
+ spnt->pnt.y = spnt->pnt.y - ydiff;
+
+ /* Draw in new pos */
+ gtk_widget_queue_draw (gfig_context->preview);
+}
+
+/* copy objs */
+GList *
+copy_all_objs (GList *objs)
+{
+ GList *new_all_objs = NULL;
+
+ while (objs)
+ {
+ GfigObject *object = objs->data;
+ GfigObject *new_object = (GfigObject *) object->class->copyfunc (object);
+ gfig_style_copy (&new_object->style, &object->style, "Object");
+
+ new_all_objs = g_list_prepend (new_all_objs, new_object);
+
+ objs = objs->next;
+ }
+
+ new_all_objs = g_list_reverse (new_all_objs);
+
+ return new_all_objs;
+}
+
+/* Screen refresh */
+static void
+draw_one_obj (GfigObject *obj,
+ cairo_t *cr)
+{
+ obj->class->drawfunc (obj, cr);
+}
+
+void
+draw_objects (GList *objs,
+ gboolean show_single,
+ cairo_t *cr)
+{
+ /* Show_single - only one object to draw Unless shift
+ * is down in which case show all.
+ */
+
+ gint count = 0;
+
+ while (objs)
+ {
+ if (!show_single || count == obj_show_single || obj_show_single == -1)
+ draw_one_obj (objs->data, cr);
+
+ objs = g_list_next (objs);
+ count++;
+ }
+}
+
+void
+prepend_to_all_obj (GFigObj *fobj,
+ GList *nobj)
+{
+ setup_undo (); /* Remember ME */
+
+ fobj->obj_list = g_list_concat (fobj->obj_list, nobj);
+}
+
+static void
+scale_obj_points (DobjPoints *opnt,
+ gdouble scale_x,
+ gdouble scale_y)
+{
+ while (opnt)
+ {
+ opnt->pnt.x = (gint) (opnt->pnt.x * scale_x);
+ opnt->pnt.y = (gint) (opnt->pnt.y * scale_y);
+ opnt = opnt->next;
+ }
+}
+
+void
+add_to_all_obj (GFigObj *fobj,
+ GfigObject *obj)
+{
+ GList *nobj = NULL;
+
+ nobj = g_list_append (nobj, obj);
+
+ if (need_to_scale)
+ scale_obj_points (obj->points, scale_x_factor, scale_y_factor);
+
+ prepend_to_all_obj (fobj, nobj);
+
+ /* initialize style when we add the object */
+ gfig_context->selected_obj = obj;
+}
+
+/* First button press -- start drawing object */
+/*
+ * object_start() creates a new object of the type specified in the
+ * button panel. It is activated by a button press, and causes
+ * a small square to be drawn at the initial point. The style of
+ * the new object is set to values taken from the style control
+ * widgets.
+ */
+void
+object_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* start for the current object */
+ if (!selvals.scaletoimage)
+ {
+ need_to_scale = 1;
+ selvals.scaletoimage = 1;
+ }
+ else
+ {
+ need_to_scale = 0;
+ }
+
+ switch (selvals.otype)
+ {
+ case LINE:
+ /* Shift means we are still drawing */
+ d_line_start (pnt, shift_down);
+ break;
+ case RECTANGLE:
+ d_rectangle_start (pnt, shift_down);
+ break;
+ case CIRCLE:
+ d_circle_start (pnt, shift_down);
+ break;
+ case ELLIPSE:
+ d_ellipse_start (pnt, shift_down);
+ break;
+ case POLY:
+ d_poly_start (pnt, shift_down);
+ break;
+ case ARC:
+ d_arc_start (pnt, shift_down);
+ break;
+ case STAR:
+ d_star_start (pnt, shift_down);
+ break;
+ case SPIRAL:
+ d_spiral_start (pnt, shift_down);
+ break;
+ case BEZIER:
+ d_bezier_start (pnt, shift_down);
+ break;
+ default:
+ /* Internal error */
+ break;
+ }
+
+ if (obj_creating)
+ {
+ if (gfig_context->debug_styles)
+ g_printerr ("Creating object, setting style from context\n");
+ gfig_style_set_style_from_context (&obj_creating->style);
+ }
+
+}
+
+void
+object_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* end for the current object */
+ /* Add onto global object list */
+
+ /* If shift is down may carry on drawing */
+ switch (selvals.otype)
+ {
+ case LINE:
+ d_line_end (pnt, shift_down);
+ break;
+ case RECTANGLE:
+ d_rectangle_end (pnt, shift_down);
+ break;
+ case CIRCLE:
+ d_circle_end (pnt, shift_down);
+ break;
+ case ELLIPSE:
+ d_ellipse_end (pnt, shift_down);
+ break;
+ case POLY:
+ d_poly_end (pnt, shift_down);
+ break;
+ case STAR:
+ d_star_end (pnt, shift_down);
+ break;
+ case ARC:
+ d_arc_end (pnt, shift_down);
+ break;
+ case SPIRAL:
+ d_spiral_end (pnt, shift_down);
+ break;
+ case BEZIER:
+ d_bezier_end (pnt, shift_down);
+ break;
+ default:
+ /* Internal error */
+ break;
+ }
+
+ if (need_to_scale)
+ {
+ need_to_scale = 0;
+ selvals.scaletoimage = 0;
+ }
+}
+
+/* Stuff for the generation/deletion of objects. */
+
+/* Objects are easy one they are created - you just go down the object
+ * list calling the draw function for each object but... when they
+ * are been created we have to be a little more careful. When
+ * the first point is placed on the canvas we create the object,
+ * the mouse position then defines the next point that can move around.
+ * careful how we draw this position.
+ */
+
+void
+free_one_obj (GfigObject *obj)
+{
+ d_delete_dobjpoints (obj->points);
+ g_free (obj);
+}
+
+void
+free_all_objs (GList *objs)
+{
+ g_list_free_full (objs, (GDestroyNotify) free_one_obj);
+}
+
+gchar *
+get_line (gchar *buf,
+ gint s,
+ FILE *from,
+ gint init)
+{
+ gint slen;
+ char *ret;
+
+ if (init)
+ line_no = 1;
+ else
+ line_no++;
+
+ do
+ {
+ ret = fgets (buf, s, from);
+ }
+ while (!ferror (from) && buf[0] == '#');
+
+ slen = strlen (buf);
+
+ /* The last newline is a pain */
+ if (slen > 0)
+ buf[slen - 1] = '\0';
+
+ /* Check and remove an '\r' too from Windows */
+ if ((slen > 1) && (buf[slen - 2] == '\r'))
+ buf[slen - 2] = '\0';
+
+ if (ferror (from))
+ {
+ g_warning (_("Error reading file"));
+ return NULL;
+ }
+
+#ifdef DEBUG
+ printf ("Processing line '%s'\n", buf);
+#endif /* DEBUG */
+
+ return ret;
+}
+
+void
+clear_undo (void)
+{
+ int lv;
+
+ for (lv = undo_level; lv >= 0; lv--)
+ {
+ free_all_objs (undo_table[lv]);
+ undo_table[lv] = NULL;
+ }
+
+ undo_level = -1;
+
+ gfig_dialog_action_set_sensitive ("undo", FALSE);
+}
+
+void
+setup_undo (void)
+{
+ /* Copy object list to undo buffer */
+#if DEBUG
+ printf ("setup undo level [%d]\n", undo_level);
+#endif /*DEBUG*/
+
+ if (!gfig_context->current_obj)
+ {
+ /* If no current_obj must be loading -> no undo */
+ return;
+ }
+
+ if (undo_level >= selvals.maxundo - 1)
+ {
+ int loop;
+ /* the little one in the bed said "roll over".. */
+ if (undo_table[0])
+ free_one_obj (undo_table[0]->data);
+ for (loop = 0; loop < undo_level; loop++)
+ {
+ undo_table[loop] = undo_table[loop + 1];
+ }
+ }
+ else
+ {
+ undo_level++;
+ }
+ undo_table[undo_level] =
+ copy_all_objs (gfig_context->current_obj->obj_list);
+
+ gfig_dialog_action_set_sensitive ("undo", TRUE);
+
+ gfig_context->current_obj->obj_status |= GFIG_MODIFIED;
+}
+
+void
+new_obj_2edit (GFigObj *obj)
+{
+ GFigObj *old_current = gfig_context->current_obj;
+
+ /* Clear undo levels */
+ /* redraw the preview */
+ /* Set up options as define in the selected object */
+
+ clear_undo ();
+
+ /* Point at this one */
+ gfig_context->current_obj = obj;
+
+ /* Show all objects to start with */
+ obj_show_single = -1;
+
+ /* Change options */
+ options_update (old_current);
+
+ /* redraw with new */
+ gtk_widget_queue_draw (gfig_context->preview);
+
+ if (obj->obj_status & GFIG_READONLY)
+ {
+ g_message (_("Editing read-only object - "
+ "you will not be able to save it"));
+
+ gfig_dialog_action_set_sensitive ("save", FALSE);
+ }
+ else
+ {
+ gfig_dialog_action_set_sensitive ("save", TRUE);
+ }
+}
+
+/* Add a point to a line (given x, y)
+ * pos = 0 = head
+ * pos = -1 = tail
+ * 0 < pos = nth position
+ */
+
+void
+d_pnt_add_line (GfigObject *obj,
+ gint x,
+ gint y,
+ gint pos)
+{
+ DobjPoints *npnts = new_dobjpoint (x, y);
+
+ g_assert (obj != NULL);
+
+ if (!pos)
+ {
+ /* Add to head */
+ npnts->next = obj->points;
+ obj->points = npnts;
+ }
+ else
+ {
+ DobjPoints *pnt = obj->points;
+
+ /* Go down chain until the end if pos */
+ while (pos < 0 || pos-- > 0)
+ {
+ if (!(pnt->next) || !pos)
+ {
+ npnts->next = pnt->next;
+ pnt->next = npnts;
+ break;
+ }
+ else
+ {
+ pnt = pnt->next;
+ }
+ }
+ }
+}
diff --git a/plug-ins/gfig/gfig-dobject.h b/plug-ins/gfig/gfig-dobject.h
new file mode 100644
index 0000000..19be4dd
--- /dev/null
+++ b/plug-ins/gfig/gfig-dobject.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GFIG_DOBJECT_H__
+#define __GFIG_DOBJECT_H__
+
+#include "gfig-types.h"
+#include "gfig-style.h"
+
+typedef void (*DobjDrawFunc) (GfigObject *, cairo_t *);
+typedef void (*DobjFunc) (GfigObject *);
+typedef GfigObject *(*DobjGenFunc) (GfigObject *);
+
+typedef struct DobjPoints
+{
+ struct DobjPoints *next;
+ GdkPoint pnt;
+ gboolean found_me;
+} DobjPoints;
+
+typedef struct
+{
+ DobjType type; /* the object type for this class */
+ const gchar *name;
+
+ /* virtuals */
+ DobjDrawFunc drawfunc; /* How do I draw myself */
+ DobjFunc paintfunc; /* Draw me on canvas */
+ DobjGenFunc copyfunc; /* copy */
+ void (*update) (GdkPoint *pnt);
+} GfigObjectClass;
+
+extern GfigObjectClass dobj_class[10];
+
+/* The object itself */
+struct _GfigObject
+{
+ DobjType type; /* What is the type? */
+ GfigObjectClass *class; /* What class does it belong to? */
+ gint type_data; /* Extra data needed by the object */
+ DobjPoints *points; /* List of points */
+ Style style; /* this object's individual style settings */
+ gint style_no; /* style index of this specific object */
+};
+
+/* States of the object */
+#define GFIG_OK 0x0
+#define GFIG_MODIFIED 0x1
+#define GFIG_READONLY 0x2
+
+extern GfigObject *obj_creating;
+extern GfigObject *tmp_line;
+
+
+DobjPoints *new_dobjpoint (gint x,
+ gint y);
+void do_save_obj (GfigObject *obj,
+ GString *to);
+
+DobjPoints *d_copy_dobjpoints (DobjPoints *pnts);
+void free_one_obj (GfigObject *obj);
+void d_delete_dobjpoints (DobjPoints *pnts);
+void object_update (GdkPoint *pnt);
+GList *copy_all_objs (GList *objs);
+void draw_objects (GList *objs,
+ gboolean show_single,
+ cairo_t *cr);
+
+GfigObject *d_load_object (gchar *desc,
+ FILE *fp);
+
+GfigObject *d_new_object (DobjType type,
+ gint x,
+ gint y);
+
+void d_save_object (GfigObject *obj,
+ GString *string);
+
+void free_all_objs (GList *objs);
+
+void clear_undo (void);
+
+void new_obj_2edit (GFigObj *obj);
+
+void gfig_init_object_classes (void);
+
+void d_pnt_add_line (GfigObject *obj,
+ gint x,
+ gint y,
+ gint pos);
+
+#endif /* __GFIG_DOBJECT_H__ */
+
+
diff --git a/plug-ins/gfig/gfig-ellipse.c b/plug-ins/gfig/gfig-ellipse.c
new file mode 100644
index 0000000..e6fd430
--- /dev/null
+++ b/plug-ins/gfig/gfig-ellipse.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-ellipse.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void d_draw_ellipse (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_ellipse (GfigObject *obj);
+static GfigObject *d_copy_ellipse (GfigObject *obj);
+
+static void d_update_ellipse (GdkPoint *pnt);
+
+static void
+d_draw_ellipse (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+ gint bound_wx;
+ gint bound_wy;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ edge_pnt = center_pnt->next;
+
+ if (!edge_pnt)
+ return;
+
+ if (obj == obj_creating)
+ draw_circle (&edge_pnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&edge_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x);
+ bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y);
+
+ gfig_draw_arc (center_pnt->pnt.x, center_pnt->pnt.y, bound_wx, bound_wy, 0, 360, cr);
+}
+
+static void
+d_paint_ellipse (GfigObject *obj)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+ gint bound_wx;
+ gint bound_wy;
+ gint top_x;
+ gint top_y;
+ gdouble dpnts[4];
+
+ g_assert (obj != NULL);
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ edge_pnt = center_pnt->next;
+
+ if (!edge_pnt)
+ {
+ g_error ("Internal error - ellipse no edge pnt");
+ }
+
+ bound_wx = abs (center_pnt->pnt.x - edge_pnt->pnt.x)*2;
+ bound_wy = abs (center_pnt->pnt.y - edge_pnt->pnt.y)*2;
+
+ if (edge_pnt->pnt.x > center_pnt->pnt.x)
+ top_x = 2*center_pnt->pnt.x - edge_pnt->pnt.x;
+ else
+ top_x = edge_pnt->pnt.x;
+
+ if (edge_pnt->pnt.y > center_pnt->pnt.y)
+ top_y = 2*center_pnt->pnt.y - edge_pnt->pnt.y;
+ else
+ top_y = edge_pnt->pnt.y;
+
+ dpnts[0] = (gdouble)top_x;
+ dpnts[1] = (gdouble)top_y;
+ dpnts[2] = (gdouble)bound_wx;
+ dpnts[3] = (gdouble)bound_wy;
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&dpnts[0], 2);
+ else
+ scale_to_xy (&dpnts[0], 2);
+
+ if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
+ {
+ gimp_context_push ();
+ gimp_context_set_antialias (selopt.antia);
+ gimp_context_set_feather (selopt.feather);
+ gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
+ gimp_image_select_ellipse (gfig_context->image_id,
+ selopt.type,
+ dpnts[0], dpnts[1],
+ dpnts[2], dpnts[3]);
+ gimp_context_pop ();
+
+ paint_layer_fill (top_x, top_y, top_x + bound_wx, top_y + bound_wy);
+ gimp_selection_none (gfig_context->image_id);
+ }
+
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ const gdouble rx = dpnts[2] / 2, ry = dpnts[3] / 2;
+ const gdouble cx = dpnts[0] + rx, cy = dpnts[1] + ry;
+ gdouble line_pnts[362];
+ gdouble angle = 0;
+ gint i = 0;
+
+ while (i < 361)
+ {
+ static const gdouble step = 2 * G_PI / 180;
+
+ line_pnts[i++] = cx + rx * cos (angle);
+ line_pnts[i++] = cy + ry * sin (angle);
+ angle += step;
+ }
+
+ gfig_paint (selvals.brshtype, gfig_context->drawable_id, i, line_pnts);
+ }
+}
+
+static GfigObject *
+d_copy_ellipse (GfigObject * obj)
+{
+ GfigObject *nc;
+
+ g_assert (obj->type == ELLIPSE);
+
+ nc = d_new_object (ELLIPSE, obj->points->pnt.x, obj->points->pnt.y);
+ nc->points->next = d_copy_dobjpoints (obj->points->next);
+
+ return nc;
+}
+
+void
+d_ellipse_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[ELLIPSE];
+
+ class->type = ELLIPSE;
+ class->name = "ELLIPSE";
+ class->drawfunc = d_draw_ellipse;
+ class->paintfunc = d_paint_ellipse;
+ class->copyfunc = d_copy_ellipse;
+ class->update = d_update_ellipse;
+}
+
+static void
+d_update_ellipse (GdkPoint *pnt)
+{
+ DobjPoints *center_pnt, *edge_pnt;
+
+ center_pnt = obj_creating->points;
+
+ if (!center_pnt)
+ return; /* No points */
+
+ if ((edge_pnt = center_pnt->next))
+ {
+ edge_pnt->pnt = *pnt;
+ }
+ else
+ {
+ edge_pnt = new_dobjpoint (pnt->x, pnt->y);
+ center_pnt->next = edge_pnt;
+ }
+}
+
+void
+d_ellipse_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (ELLIPSE, pnt->x, pnt->y);
+}
+
+void
+d_ellipse_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* Under control point */
+ if (!obj_creating->points->next)
+ {
+ /* No circle created */
+ free_one_obj (obj_creating);
+ }
+ else
+ {
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ obj_creating = NULL;
+}
+
diff --git a/plug-ins/gfig/gfig-ellipse.h b/plug-ins/gfig/gfig-ellipse.h
new file mode 100644
index 0000000..e770311
--- /dev/null
+++ b/plug-ins/gfig/gfig-ellipse.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_ELLIPSE_H__
+#define __GFIG_ELLIPSE_H__
+
+void d_ellipse_object_class_init (void);
+
+void d_ellipse_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_ellipse_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_ELLIPSE_H__ */
diff --git a/plug-ins/gfig/gfig-examples/A_star b/plug-ins/gfig/gfig-examples/A_star
new file mode 100644
index 0000000..6d5e981
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/A_star
@@ -0,0 +1,84 @@
+GFIG Version 0.1
+Name: A\040star
+Version: 0.000000
+ObjCount: 18
+<OPTIONS>
+GridSpacing: 30
+GridType: POLAR_GRID
+DrawGrid: TRUE
+Snap2Grid: TRUE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<LINE>
+127 67
+44 44
+</LINE>
+<LINE>
+44 44
+68 128
+</LINE>
+<LINE>
+68 128
+44 212
+</LINE>
+<LINE>
+44 212
+128 188
+</LINE>
+<LINE>
+128 188
+212 212
+</LINE>
+<LINE>
+212 212
+188 128
+</LINE>
+<LINE>
+188 128
+212 44
+</LINE>
+<LINE>
+212 44
+128 68
+</LINE>
+<CIRCLE>
+128 128
+139 186
+</CIRCLE>
+<CIRCLE>
+128 128
+151 245
+</CIRCLE>
+<LINE>
+128 8
+86 86
+</LINE>
+<LINE>
+86 86
+8 128
+</LINE>
+<LINE>
+8 128
+86 170
+</LINE>
+<LINE>
+86 170
+128 248
+</LINE>
+<LINE>
+128 248
+170 170
+</LINE>
+<LINE>
+170 170
+248 128
+</LINE>
+<LINE>
+248 128
+170 86
+</LINE>
+<LINE>
+170 86
+128 8
+</LINE>
diff --git a/plug-ins/gfig/gfig-examples/Makefile.am b/plug-ins/gfig/gfig-examples/Makefile.am
new file mode 100644
index 0000000..f41deea
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/Makefile.am
@@ -0,0 +1,17 @@
+## Process this file with automake to produce Makefile.in
+
+gfigdatadir = $(gimpdatadir)/gfig
+
+gfigdata_DATA = \
+ A_star \
+ curves \
+ polys \
+ ring \
+ ring+star \
+ simily \
+ spirals_and_stars \
+ sprial \
+ star2 \
+ stars
+
+EXTRA_DIST = $(gfigdata_DATA)
diff --git a/plug-ins/gfig/gfig-examples/Makefile.in b/plug-ins/gfig/gfig-examples/Makefile.in
new file mode 100644
index 0000000..f343359
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/Makefile.in
@@ -0,0 +1,815 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gfig/gfig-examples
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(gfigdatadir)"
+DATA = $(gfigdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+gfigdatadir = $(gimpdatadir)/gfig
+gfigdata_DATA = \
+ A_star \
+ curves \
+ polys \
+ ring \
+ ring+star \
+ simily \
+ spirals_and_stars \
+ sprial \
+ star2 \
+ stars
+
+EXTRA_DIST = $(gfigdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gfig/gfig-examples/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gfig/gfig-examples/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-gfigdataDATA: $(gfigdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(gfigdata_DATA)'; test -n "$(gfigdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(gfigdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(gfigdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(gfigdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(gfigdatadir)" || exit $$?; \
+ done
+
+uninstall-gfigdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(gfigdata_DATA)'; test -n "$(gfigdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(gfigdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(gfigdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-gfigdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-gfigdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am \
+ install-gfigdataDATA install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-gfigdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gfig/gfig-examples/curves b/plug-ins/gfig/gfig-examples/curves
new file mode 100644
index 0000000..3c48165
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/curves
@@ -0,0 +1,57 @@
+GFIG Version 0.1
+Name: curves
+Version: 0.000000
+ObjCount: 9
+<OPTIONS>
+GridSpacing: 13
+GridType: POLAR_GRID
+DrawGrid: TRUE
+Snap2Grid: TRUE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<ARC>
+53 39
+109 13
+170 19
+</ARC>
+<ARC>
+177 37
+217 75
+231 115
+</ARC>
+<ARC>
+219 128
+199 184
+160 212
+</ARC>
+<ARC>
+146 203
+105 202
+71 180
+</ARC>
+<ARC>
+75 164
+64 132
+70 99
+</ARC>
+<ARC>
+95 89
+128 77
+158 87
+</ARC>
+<ARC>
+158 104
+166 128
+155 156
+</ARC>
+<ARC>
+143 148
+121 152
+105 139
+</ARC>
+<ARC>
+116 130
+125 116
+139 123
+</ARC>
diff --git a/plug-ins/gfig/gfig-examples/polys b/plug-ins/gfig/gfig-examples/polys
new file mode 100644
index 0000000..3bdd0ab
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/polys
@@ -0,0 +1,40 @@
+GFIG Version 0.1
+Name: polys
+Version: 0.000000
+ObjCount: 4
+<OPTIONS>
+GridSpacing: 10
+GridType: RECT_GRID
+DrawGrid: TRUE
+Snap2Grid: TRUE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<POLY>
+60 40
+60 100
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+170 60
+200 110
+<EXTRA>
+4
+</EXTRA>
+</POLY>
+<POLY>
+50 170
+70 210
+<EXTRA>
+5
+</EXTRA>
+</POLY>
+<POLY>
+170 170
+110 160
+<EXTRA>
+6
+</EXTRA>
+</POLY>
diff --git a/plug-ins/gfig/gfig-examples/ring b/plug-ins/gfig/gfig-examples/ring
new file mode 100644
index 0000000..8c0bffe
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/ring
@@ -0,0 +1,44 @@
+GFIG Version 0.1
+Name: a\040ring
+Version: 0.000000
+ObjCount: 8
+<OPTIONS>
+GridSpacing: 50
+GridType: RECT_GRID
+DrawGrid: TRUE
+Snap2Grid: TRUE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<CIRCLE>
+163 163
+147 174
+</CIRCLE>
+<CIRCLE>
+178 128
+174 147
+</CIRCLE>
+<CIRCLE>
+163 93
+174 109
+</CIRCLE>
+<CIRCLE>
+128 78
+109 82
+</CIRCLE>
+<CIRCLE>
+93 93
+82 109
+</CIRCLE>
+<CIRCLE>
+78 128
+82 147
+</CIRCLE>
+<CIRCLE>
+93 163
+109 174
+</CIRCLE>
+<CIRCLE>
+128 178
+147 174
+</CIRCLE>
diff --git a/plug-ins/gfig/gfig-examples/ring+star b/plug-ins/gfig/gfig-examples/ring+star
new file mode 100644
index 0000000..8b48129
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/ring+star
@@ -0,0 +1,123 @@
+GFIG Version 0.1
+Name: a\040ring\040merged
+Version: 0.000000
+ObjCount: 27
+<OPTIONS>
+GridSpacing: 47
+GridType: RECT_GRID
+DrawGrid: TRUE
+Snap2Grid: TRUE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<CIRCLE>
+163 163
+147 174
+</CIRCLE>
+<CIRCLE>
+178 128
+174 147
+</CIRCLE>
+<CIRCLE>
+163 93
+174 109
+</CIRCLE>
+<CIRCLE>
+128 78
+109 82
+</CIRCLE>
+<CIRCLE>
+93 93
+82 109
+</CIRCLE>
+<CIRCLE>
+78 128
+82 147
+</CIRCLE>
+<CIRCLE>
+93 163
+109 174
+</CIRCLE>
+<CIRCLE>
+128 178
+147 174
+</CIRCLE>
+<LINE>
+127 67
+44 44
+</LINE>
+<LINE>
+44 44
+68 128
+</LINE>
+<LINE>
+68 128
+44 212
+</LINE>
+<LINE>
+44 212
+128 188
+</LINE>
+<LINE>
+128 188
+212 212
+</LINE>
+<LINE>
+212 212
+188 128
+</LINE>
+<LINE>
+188 128
+212 44
+</LINE>
+<LINE>
+212 44
+128 68
+</LINE>
+<CIRCLE>
+128 128
+139 186
+</CIRCLE>
+<CIRCLE>
+128 128
+151 245
+</CIRCLE>
+<LINE>
+128 8
+86 86
+</LINE>
+<LINE>
+86 86
+8 128
+</LINE>
+<LINE>
+8 128
+86 170
+</LINE>
+<LINE>
+86 170
+128 248
+</LINE>
+<LINE>
+128 248
+170 170
+</LINE>
+<LINE>
+170 170
+248 128
+</LINE>
+<LINE>
+248 128
+170 86
+</LINE>
+<LINE>
+170 86
+128 8
+</LINE>
+<POLY>
+338 132
+256 256
+<EXTRA>
+3
+</EXTRA>
+</POLY>
diff --git a/plug-ins/gfig/gfig-examples/simily b/plug-ins/gfig/gfig-examples/simily
new file mode 100644
index 0000000..d2e9d44
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/simily
@@ -0,0 +1,47 @@
+GFIG Version 0.1
+Name: Simly\040face
+Version: 0.000000
+ObjCount: 5
+<OPTIONS>
+GridSpacing: 30
+GridType: RECT_GRID
+DrawGrid: FALSE
+Snap2Grid: FALSE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<CIRCLE>
+128 128
+142 235
+</CIRCLE>
+<CIRCLE>
+72 90
+72 108
+</CIRCLE>
+<CIRCLE>
+180 90
+180 108
+</CIRCLE>
+<LINE>
+66 163
+71 171
+78 178
+85 185
+92 190
+101 194
+110 197
+119 199
+128 200
+137 199
+146 197
+155 194
+163 190
+171 185
+178 178
+185 171
+190 164
+</LINE>
+<CIRCLE>
+128 145
+128 164
+</CIRCLE>
diff --git a/plug-ins/gfig/gfig-examples/spirals_and_stars b/plug-ins/gfig/gfig-examples/spirals_and_stars
new file mode 100644
index 0000000..fae6052
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/spirals_and_stars
@@ -0,0 +1,55 @@
+GFIG Version 0.1
+Name: First\040gfig
+Version: 0.000000
+ObjCount: 6
+<OPTIONS>
+GridSpacing: 30
+GridType: RECT_GRID
+DrawGrid: FALSE
+Snap2Grid: FALSE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<SPIRAL>
+71 81
+96 103
+<EXTRA>
+4
+</EXTRA>
+</SPIRAL>
+<SPIRAL>
+152 79
+179 91
+<EXTRA>
+-4
+</EXTRA>
+</SPIRAL>
+<BEZIER>
+116 94
+105 147
+73 161
+<EXTRA>
+4
+</EXTRA>
+</BEZIER>
+<STAR>
+206 106
+228 123
+214 112
+<EXTRA>
+3
+</EXTRA>
+</STAR>
+<STAR>
+26 115
+10 133
+20 121
+<EXTRA>
+3
+</EXTRA>
+</STAR>
+<ARC>
+38 169
+92 218
+182 182
+</ARC>
diff --git a/plug-ins/gfig/gfig-examples/sprial b/plug-ins/gfig/gfig-examples/sprial
new file mode 100644
index 0000000..0223c45
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/sprial
@@ -0,0 +1,38 @@
+GFIG Version 0.1
+Name: square\040sprial
+Version: 0.000000
+ObjCount: 2
+<OPTIONS>
+GridSpacing: 30
+GridType: RECT_GRID
+DrawGrid: FALSE
+Snap2Grid: FALSE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<LINE>
+30 20
+230 20
+230 230
+50 230
+50 40
+210 40
+210 210
+70 210
+70 60
+190 60
+190 190
+90 190
+90 80
+170 80
+170 170
+110 170
+110 100
+150 100
+150 150
+130 150
+</LINE>
+<LINE>
+130 150
+130 120
+</LINE>
diff --git a/plug-ins/gfig/gfig-examples/star2 b/plug-ins/gfig/gfig-examples/star2
new file mode 100644
index 0000000..568859d
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/star2
@@ -0,0 +1,61 @@
+GFIG Version 0.1
+Name: Another\040star
+Version: 0.000000
+ObjCount: 7
+<OPTIONS>
+GridSpacing: 30
+GridType: RECT_GRID
+DrawGrid: FALSE
+Snap2Grid: FALSE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<POLY>
+128 128
+164 190
+<EXTRA>
+6
+</EXTRA>
+</POLY>
+<POLY>
+128 43
+163 63
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+203 170
+238 190
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+54 170
+89 190
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+202 85
+237 65
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+54 85
+89 65
+<EXTRA>
+3
+</EXTRA>
+</POLY>
+<POLY>
+128 213
+163 193
+<EXTRA>
+3
+</EXTRA>
+</POLY>
diff --git a/plug-ins/gfig/gfig-examples/stars b/plug-ins/gfig/gfig-examples/stars
new file mode 100644
index 0000000..29a784f
--- /dev/null
+++ b/plug-ins/gfig/gfig-examples/stars
@@ -0,0 +1,44 @@
+GFIG Version 0.1
+Name: First\040gfig
+Version: 0.000000
+ObjCount: 4
+<OPTIONS>
+GridSpacing: 30
+GridType: RECT_GRID
+DrawGrid: FALSE
+Snap2Grid: FALSE
+LockOnGrid: FALSE
+ShowControl: TRUE
+</OPTIONS>
+<STAR>
+80 70
+80 130
+80 90
+<EXTRA>
+6
+</EXTRA>
+</STAR>
+<STAR>
+200 70
+200 130
+200 90
+<EXTRA>
+6
+</EXTRA>
+</STAR>
+<STAR>
+200 190
+200 250
+200 210
+<EXTRA>
+6
+</EXTRA>
+</STAR>
+<STAR>
+80 190
+80 250
+80 210
+<EXTRA>
+6
+</EXTRA>
+</STAR>
diff --git a/plug-ins/gfig/gfig-grid.c b/plug-ins/gfig/gfig-grid.c
new file mode 100644
index 0000000..ac81859
--- /dev/null
+++ b/plug-ins/gfig/gfig-grid.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-grid.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* For the isometric grid */
+#define SQRT3 1.73205080756887729353 /* Square root of 3 */
+#define SIN_1o6PI_RAD 0.5 /* Sine 1/6 Pi Radians */
+#define COS_1o6PI_RAD SQRT3 / 2 /* Cosine 1/6 Pi Radians */
+#define TAN_1o6PI_RAD 1 / SQRT3 /* Tangent 1/6 Pi Radians == SIN / COS */
+#define RECIP_TAN_1o6PI_RAD SQRT3 /* Reciprocal of Tangent 1/6 Pi Radians */
+
+gint grid_gc_type = GFIG_NORMAL_GC;
+
+static void draw_grid_polar (cairo_t *drawgc);
+static void draw_grid_sq (cairo_t *drawgc);
+static void draw_grid_iso (cairo_t *drawgc);
+
+static cairo_t * gfig_get_grid_gc (cairo_t *cr,
+ GtkWidget *widget,
+ gint gctype);
+
+static void find_grid_pos_polar (GdkPoint *p,
+ GdkPoint *gp);
+
+
+/********** PrimeFactors for Shaneyfelt-style Polar Grid **********
+ * Quickly factor any number up to 17160
+ * Correctly factors numbers up to 131 * 131 - 1
+ */
+typedef struct
+{
+ gint product;
+ gint remaining;
+ gint current;
+ gint next;
+ gint index;
+} PrimeFactors;
+
+static gchar primes[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
+ 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127 };
+
+#define PRIMES_MAX_INDEX 30
+
+
+static gint
+prime_factors_get (PrimeFactors *this)
+{
+ this->current = this->next;
+ while (this->index <= PRIMES_MAX_INDEX)
+ {
+ if (this->remaining % primes[this->index] == 0) /* divisible */
+ {
+ this->remaining /= primes[this->index];
+ this->next = primes[this->index];
+ return this->current;
+ }
+ this->index++;
+ }
+ this->next = this->remaining;
+ this->remaining = 1;
+
+ return this->current;
+}
+
+static gint
+prime_factors_lookahead (PrimeFactors *this)
+{
+ return this->next;
+}
+
+static void
+prime_factors_reset (PrimeFactors *this)
+{
+ this->remaining = this->product;
+ this->index = 0;
+ prime_factors_get (this);
+}
+
+static PrimeFactors *
+prime_factors_new (gint n)
+{
+ PrimeFactors *this = g_new (PrimeFactors, 1);
+
+ this->product = n;
+ prime_factors_reset (this);
+
+ return this;
+}
+
+static void
+prime_factors_delete (PrimeFactors* this)
+{
+ g_free (this);
+}
+
+/********** ********** **********/
+
+static gdouble
+sector_size_at_radius (gdouble inner_radius)
+{
+ PrimeFactors *factors = prime_factors_new (selvals.opts.grid_sectors_desired);
+ gint current_sectors = 1;
+ gdouble sector_size = 2 * G_PI / current_sectors;
+
+ while ((current_sectors < selvals.opts.grid_sectors_desired)
+ && (inner_radius*sector_size
+ > (prime_factors_lookahead (factors) *
+ selvals.opts.grid_granularity)))
+ {
+ current_sectors *= prime_factors_get (factors);
+ sector_size = 2 * G_PI / current_sectors;
+ }
+
+ prime_factors_delete(factors);
+
+ return sector_size;
+}
+
+static void
+find_grid_pos_polar (GdkPoint *p,
+ GdkPoint *gp)
+{
+ gdouble cx = preview_width / 2.0;
+ gdouble cy = preview_height / 2.0;
+ gdouble px = p->x - cx;
+ gdouble py = p->y - cy;
+ gdouble x = 0;
+ gdouble y = 0;
+ gdouble r = sqrt (SQR (px) + SQR (py));
+
+ if (r >= selvals.opts.grid_radius_min * 0.5)
+ {
+ gdouble t;
+ gdouble sectorSize;
+
+ r = selvals.opts.grid_radius_interval
+ * (gint) (0.5 + ((r - selvals.opts.grid_radius_min) /
+ selvals.opts.grid_radius_interval))
+ + selvals.opts.grid_radius_min;
+
+ t = atan2 (py, px) + 2 * G_PI;
+ sectorSize = sector_size_at_radius (r);
+ t = selvals.opts.grid_rotation
+ + (gint) (0.5 + ((t - selvals.opts.grid_rotation) / sectorSize))
+ * sectorSize;
+ x = r * cos (t);
+ y = r * sin (t);
+ }
+
+ gp->x = x + cx;
+ gp->y = y + cy;
+}
+
+/* find_grid_pos - Given an x, y point return the grid position of it */
+/* return the new position in the passed point */
+
+void
+gfig_grid_colors (GtkWidget *widget)
+{
+}
+
+void
+find_grid_pos (GdkPoint *p,
+ GdkPoint *gp,
+ guint is_butt3)
+{
+ gint16 x = p->x;
+ gint16 y = p->y;
+ static GdkPoint cons_pnt;
+
+ if (selvals.opts.gridtype == RECT_GRID)
+ {
+ if (p->x % selvals.opts.gridspacing > selvals.opts.gridspacing/2)
+ x += selvals.opts.gridspacing;
+
+ if (p->y % selvals.opts.gridspacing > selvals.opts.gridspacing/2)
+ y += selvals.opts.gridspacing;
+
+ gp->x = (x/selvals.opts.gridspacing)*selvals.opts.gridspacing;
+ gp->y = (y/selvals.opts.gridspacing)*selvals.opts.gridspacing;
+
+ if (is_butt3)
+ {
+ if (abs (gp->x - cons_pnt.x) < abs (gp->y - cons_pnt.y))
+ gp->x = cons_pnt.x;
+ else
+ gp->y = cons_pnt.y;
+ }
+ else
+ {
+ /* Store the point since might be used later */
+ cons_pnt = *gp; /* Structure copy */
+ }
+ }
+ else if (selvals.opts.gridtype == POLAR_GRID)
+ {
+ find_grid_pos_polar (p,gp);
+ }
+ else if (selvals.opts.gridtype == ISO_GRID)
+ {
+ /*
+ * This really needs a picture to show the math...
+ *
+ * Consider an isometric grid with one of the sets of lines
+ * parallel to the y axis (vertical alignment). Further define
+ * that the origin of a Cartesian grid is at a isometric vertex.
+ * For simplicity consider the first quadrant only.
+ *
+ * - Let one line segment between vertices be r
+ * - Define the value of r as the grid spacing
+ * - Assign an integer n identifier to each vertical grid line
+ * along the x axis. with n=0 being the y axis. n can be any
+ * integer
+ * - Let m to be any integer
+
+ * - Let h be the spacing between vertical grid lines measured
+ * along the x axis. It follows from the isometric grid that
+ * h has a value of r * COS(1/6 Pi Rad)
+ *
+ * Consider a Vertex V at the Cartesian location [Xv, Yv]
+ *
+ * It follows that vertices belong to the set...
+ * V[Xv, Yv] = [ [ n * h ] ,
+ * [ m * r + ( 0.5 * r (n % 2) ) ] ]
+ * for all integers n and m
+ *
+ * Who cares? Me. It's useful in solving this problem:
+ * Consider an arbitrary point P[Xp,Yp], find the closest vertex
+ * in the set V.
+ *
+ * Restated this problem is "find values for m and n that are
+ * drive V closest to P"
+ *
+ * A Solution method (there may be a better one?):
+ *
+ * Step 1) bound n to the two closest values for Xp
+ * n_lo = (int) (Xp / h)
+ * n_hi = n_lo + 1
+ *
+ * Step 2) Consider the two closes vertices for each n_lo and
+ * n_hi. The further of the vertices in each pair can
+ * readily be discarded.
+ *
+ * m_lo_n_lo = (int) ( (Yp / r) - 0.5 (n_lo % 2) )
+ * m_hi_n_lo = m_lo_n_lo + 1
+ *
+ * m_lo_n_hi = (int) ( (Yp / r) - 0.5 (n_hi % 2) )
+ * m_hi_n_hi = m_hi_n_hi
+ *
+ * Step 3) compute the distance from P to V1 and V2. Snap to the
+ * closer point.
+ */
+
+ gint n_lo;
+ gint n_hi;
+ gint m_lo_n_lo;
+ gint m_hi_n_lo;
+ gint m_lo_n_hi;
+ gint m_hi_n_hi;
+ gint m_n_lo;
+ gint m_n_hi;
+ gdouble r;
+ gdouble h;
+ gint x1;
+ gint x2;
+ gint y1;
+ gint y2;
+
+ r = selvals.opts.gridspacing;
+ h = COS_1o6PI_RAD * r;
+
+ n_lo = (gint) x / h;
+ n_hi = n_lo + 1;
+
+ /* evaluate m candidates for n_lo */
+ m_lo_n_lo = (gint) ((y / r) - 0.5 * (n_lo % 2));
+ m_hi_n_lo = m_lo_n_lo + 1;
+
+ /* figure out which is the better candidate */
+ if (fabs ((m_lo_n_lo * r + (0.5 * r * (n_lo % 2))) - y) <
+ fabs ((m_hi_n_lo * r + (0.5 * r * (n_lo % 2))) - y))
+ {
+ m_n_lo = m_lo_n_lo;
+ }
+ else
+ {
+ m_n_lo = m_hi_n_lo;
+ }
+
+ /* evaluate m candidates for n_hi */
+ m_lo_n_hi = (gint) ( (y / r) - 0.5 * (n_hi % 2) );
+ m_hi_n_hi = m_lo_n_hi + 1;
+
+ /* figure out which is the better candidate */
+ if (fabs((m_lo_n_hi * r + (0.5 * r * (n_hi % 2))) - y) <
+ fabs((m_hi_n_hi * r + (0.5 * r * (n_hi % 2))) - y))
+ {
+ m_n_hi = m_lo_n_hi;
+ }
+ else
+ {
+ m_n_hi = m_hi_n_hi;
+ }
+
+ /* Now, which is closer to [x,y]? we can use a somewhat
+ * abbreviated form of the distance formula since we only care
+ * about relative values.
+ */
+
+ x1 = (gint) (n_lo * h);
+ y1 = (gint) (m_n_lo * r + (0.5 * r * (n_lo % 2)));
+ x2 = (gint) (n_hi * h);
+ y2 = (gint) (m_n_hi * r + (0.5 * r * (n_hi % 2)));
+
+ if (((x - x1) * (x - x1) + (y - y1) * (y - y1)) <
+ ((x - x2) * (x - x2) + (y - y2) * (y - y2)))
+ {
+ gp->x = x1;
+ gp->y = y1;
+ }
+ else
+ {
+ gp->x = x2;
+ gp->y = y2;
+ }
+ }
+}
+
+static void
+draw_grid_polar (cairo_t *cr)
+{
+ gdouble inner_radius;
+ gdouble outer_radius;
+ gdouble max_radius = sqrt (SQR (preview_width) + SQR (preview_height));
+ gint current_sectors = 1;
+ PrimeFactors *factors = prime_factors_new (selvals.opts.grid_sectors_desired);
+ for (inner_radius = 0, outer_radius = selvals.opts.grid_radius_min;
+ outer_radius <= max_radius;
+ inner_radius = outer_radius, outer_radius += selvals.opts.grid_radius_interval)
+ {
+ gdouble t;
+ gdouble sector_size = 2 * G_PI / current_sectors;
+ cairo_arc (cr,
+ 0.5 + preview_width / 2.0,
+ 0.5 + preview_height / 2.0,
+ outer_radius, 0, 2 * G_PI);
+ cairo_stroke (cr);
+
+ while ((current_sectors < selvals.opts.grid_sectors_desired)
+ && (inner_radius * sector_size
+ > prime_factors_lookahead (factors) * selvals.opts.grid_granularity ))
+ {
+ current_sectors *= prime_factors_get (factors);
+ sector_size = 2 * G_PI / current_sectors;
+ }
+
+ for (t = 0 ; t < 2 * G_PI ; t += sector_size)
+ {
+ gdouble normal_x = cos (selvals.opts.grid_rotation+t);
+ gdouble normal_y = sin (selvals.opts.grid_rotation+t);
+ cairo_move_to (cr,
+ 0.5 + (preview_width / 2.0 + inner_radius * normal_x),
+ 0.5 + (preview_height / 2.0 - inner_radius * normal_y));
+ cairo_line_to (cr,
+ 0.5 + (preview_width / 2.0 + outer_radius * normal_x),
+ 0.5 + (preview_height / 2.0 - outer_radius * normal_y));
+ cairo_stroke (cr);
+ }
+ }
+
+ prime_factors_delete (factors);
+}
+
+
+static void
+draw_grid_sq (cairo_t *cr)
+{
+ gint step;
+ gint loop;
+
+ /* Draw the horizontal lines */
+ step = selvals.opts.gridspacing;
+
+ for (loop = 0 ; loop < preview_height ; loop += step)
+ {
+ cairo_move_to (cr, 0 + .5, loop + .5);
+ cairo_line_to (cr, preview_width + .5, loop + .5);
+ }
+
+ /* Draw the vertical lines */
+
+ for (loop = 0 ; loop < preview_width ; loop += step)
+ {
+ cairo_move_to (cr, loop + .5, 0 + .5);
+ cairo_line_to (cr, loop + .5, preview_height + .5);
+ }
+ cairo_stroke (cr);
+}
+
+static void
+draw_grid_iso (cairo_t *cr)
+{
+ /* vstep is an int since it's defined from grid size */
+ gint vstep;
+ gdouble loop;
+ gdouble hstep;
+
+ gdouble diagonal_start;
+ gdouble diagonal_end;
+ gdouble diagonal_width;
+ gdouble diagonal_height;
+
+ vstep = selvals.opts.gridspacing;
+ hstep = selvals.opts.gridspacing * COS_1o6PI_RAD;
+
+ /* Draw the vertical lines - These are easy */
+ for (loop = 0 ; loop < preview_width ; loop += hstep)
+ {
+ cairo_move_to (cr, loop, 0);
+ cairo_line_to (cr, loop, preview_height);
+ }
+ cairo_stroke (cr);
+
+ /* draw diag lines at a Theta of +/- 1/6 Pi Rad */
+
+ diagonal_start = -(((int)preview_width * TAN_1o6PI_RAD) - (((int)(preview_width * TAN_1o6PI_RAD)) % vstep));
+
+ diagonal_end = preview_height + (preview_width * TAN_1o6PI_RAD);
+ diagonal_end -= ((int)diagonal_end) % vstep;
+
+ diagonal_width = preview_width;
+ diagonal_height = preview_width * TAN_1o6PI_RAD;
+
+ /* Draw diag lines */
+ for (loop = diagonal_start ; loop < diagonal_end ; loop += vstep)
+ {
+ cairo_move_to (cr, 0, loop);
+ cairo_line_to (cr, diagonal_width, loop + diagonal_height);
+
+ cairo_move_to (cr, 0, loop);
+ cairo_line_to (cr, diagonal_width, loop - diagonal_height);
+ }
+ cairo_stroke (cr);
+}
+
+static cairo_t *
+gfig_get_grid_gc (cairo_t *cr, GtkWidget *w, gint gctype)
+{
+ switch (gctype)
+ {
+ default:
+ case GFIG_NORMAL_GC:
+ cairo_set_source_rgb (cr, .92, .92, .92);
+ break;
+ case GFIG_BLACK_GC:
+ cairo_set_source_rgb (cr, 0., 0., 0.);
+ break;
+ case GFIG_WHITE_GC:
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ break;
+ case GFIG_GREY_GC:
+ cairo_set_source_rgb (cr, .5, .5, .5);
+ break;
+ case GFIG_DARKER_GC:
+ cairo_set_source_rgb (cr, .25, .25, .25);
+ break;
+ case GFIG_LIGHTER_GC:
+ cairo_set_source_rgb (cr, .75, .75, .75);
+ break;
+ case GFIG_VERY_DARK_GC:
+ cairo_set_source_rgb (cr, .125, .125, .125);
+ break;
+ }
+
+ return cr;
+}
+
+void
+draw_grid (cairo_t *cr)
+{
+ /* Get the size of the preview and calc where the lines go */
+ /* Draw in prelight to start with... */
+ /* Always start in the upper left corner for rect.
+ */
+
+ if ((preview_width < selvals.opts.gridspacing &&
+ preview_height < selvals.opts.gridspacing))
+ {
+ /* Don't draw if they don't fit */
+ return;
+ }
+
+ if (selvals.opts.drawgrid)
+ gfig_get_grid_gc (cr, gfig_context->preview, grid_gc_type);
+ else
+ return;
+
+ cairo_set_line_width (cr, 1.);
+ if (selvals.opts.gridtype == RECT_GRID)
+ draw_grid_sq (cr);
+ else if (selvals.opts.gridtype == POLAR_GRID)
+ draw_grid_polar (cr);
+ else if (selvals.opts.gridtype == ISO_GRID)
+ draw_grid_iso (cr);
+}
+
+
diff --git a/plug-ins/gfig/gfig-grid.h b/plug-ins/gfig/gfig-grid.h
new file mode 100644
index 0000000..20e2e90
--- /dev/null
+++ b/plug-ins/gfig/gfig-grid.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_GRID_H__
+#define __GFIG_GRID_H__
+
+#define GFIG_NORMAL_GC -1
+#define GFIG_BLACK_GC -2
+#define GFIG_WHITE_GC -3
+#define GFIG_GREY_GC -4
+#define GFIG_DARKER_GC -5
+#define GFIG_LIGHTER_GC -6
+#define GFIG_VERY_DARK_GC -7
+
+#define MIN_GRID 10
+#define MAX_GRID 50
+
+extern gint grid_gc_type;
+
+void gfig_grid_colors (GtkWidget *widget);
+void find_grid_pos (GdkPoint *p,
+ GdkPoint *gp,
+ guint state);
+void draw_grid (cairo_t *cr);
+
+#endif /* __GFIG_GRID_H__ */
diff --git a/plug-ins/gfig/gfig-line.c b/plug-ins/gfig/gfig-line.c
new file mode 100644
index 0000000..f842d7b
--- /dev/null
+++ b/plug-ins/gfig/gfig-line.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-line.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static GfigObject *d_copy_line (GfigObject *obj);
+static void d_draw_line (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_line (GfigObject *obj);
+
+static void d_update_line (GdkPoint *pnt);
+
+static GfigObject *
+d_copy_line (GfigObject *obj)
+{
+ GfigObject *nl;
+
+ g_assert (obj->type == LINE);
+
+ nl = d_new_object (LINE, obj->points->pnt.x, obj->points->pnt.y);
+ nl->points->next = d_copy_dobjpoints (obj->points->next);
+
+ return nl;
+}
+
+static void
+d_draw_line (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *spnt;
+ DobjPoints *epnt;
+
+ spnt = obj->points;
+
+ if (!spnt)
+ return; /* End-of-line */
+
+ epnt = spnt->next;
+
+ while (spnt && epnt)
+ {
+ draw_sqr (&spnt->pnt, obj == gfig_context->selected_obj, cr);
+ /* Go around all the points drawing a line from one to the next */
+ gfig_draw_line (spnt->pnt.x, spnt->pnt.y, epnt->pnt.x, epnt->pnt.y, cr);
+ spnt = epnt;
+ epnt = epnt->next;
+ }
+ if (obj_creating == obj)
+ draw_circle (&spnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&spnt->pnt, obj == gfig_context->selected_obj, cr);
+}
+
+static void
+d_paint_line (GfigObject *obj)
+{
+ DobjPoints *spnt;
+ gdouble *line_pnts;
+ gint seg_count = 0;
+ gint i = 0;
+
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ seg_count++;
+
+ if (!seg_count)
+ return; /* no-line */
+
+ line_pnts = g_new0 (gdouble, 2 * seg_count + 1);
+
+ /* Go around all the points drawing a line from one to the next */
+ for (spnt = obj->points; spnt; spnt = spnt->next)
+ {
+ line_pnts[i++] = spnt->pnt.x;
+ line_pnts[i++] = spnt->pnt.y;
+ }
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&line_pnts[0], i/2);
+ else
+ scale_to_xy (&line_pnts[0], i/2);
+
+ /* One go */
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ gfig_paint (selvals.brshtype,
+ gfig_context->drawable_id,
+ seg_count * 2, line_pnts);
+ }
+
+ g_free (line_pnts);
+}
+
+void
+d_line_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[LINE];
+
+ class->type = LINE;
+ class->name = "LINE";
+ class->drawfunc = d_draw_line;
+ class->paintfunc = d_paint_line;
+ class->copyfunc = d_copy_line;
+ class->update = d_update_line;
+}
+
+static void
+d_update_line (GdkPoint *pnt)
+{
+ DobjPoints *spnt, *epnt;
+
+ /* Get start of segments */
+ spnt = obj_creating->points;
+
+ if (!spnt)
+ return; /* No points */
+
+ if ((epnt = spnt->next))
+ {
+ g_free (epnt);
+ }
+
+ epnt = new_dobjpoint (pnt->x, pnt->y);
+ spnt->next = epnt;
+}
+
+void
+d_line_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (!obj_creating || !shift_down)
+ {
+ /* Must delete obj_creating if we have one */
+ obj_creating = d_new_object (LINE, pnt->x, pnt->y);
+ }
+ else
+ {
+ /* Contniuation */
+ d_update_line (pnt);
+ }
+}
+
+void
+d_line_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ if (shift_down)
+ {
+ if (tmp_line)
+ {
+ GdkPoint tmp_pnt = *pnt;
+
+ if (need_to_scale)
+ {
+ tmp_pnt.x = pnt->x * scale_x_factor;
+ tmp_pnt.y = pnt->y * scale_y_factor;
+ }
+
+ d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1);
+ free_one_obj (obj_creating);
+ /* Must free obj_creating */
+ }
+ else
+ {
+ tmp_line = obj_creating;
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ obj_creating = d_new_object (LINE, pnt->x, pnt->y);
+ }
+ else
+ {
+ if (tmp_line)
+ {
+ GdkPoint tmp_pnt = *pnt;
+
+ if (need_to_scale)
+ {
+ tmp_pnt.x = pnt->x * scale_x_factor;
+ tmp_pnt.y = pnt->y * scale_y_factor;
+ }
+
+ d_pnt_add_line (tmp_line, tmp_pnt.x, tmp_pnt.y, -1);
+ free_one_obj (obj_creating);
+ /* Must free obj_creating */
+ }
+ else
+ {
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+ obj_creating = NULL;
+ tmp_line = NULL;
+ }
+}
diff --git a/plug-ins/gfig/gfig-line.h b/plug-ins/gfig/gfig-line.h
new file mode 100644
index 0000000..f15c9d6
--- /dev/null
+++ b/plug-ins/gfig/gfig-line.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_LINE_H__
+#define __GFIG_LINE_H__
+
+void d_line_object_class_init (void);
+
+void d_line_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_line_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_LINE_H__ */
diff --git a/plug-ins/gfig/gfig-poly.c b/plug-ins/gfig/gfig-poly.c
new file mode 100644
index 0000000..2fe9e5d
--- /dev/null
+++ b/plug-ins/gfig/gfig-poly.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-line.h"
+#include "gfig-dialog.h"
+#include "gfig-poly.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gint poly_num_sides = 3; /* Default to three sided object */
+
+static void d_draw_poly (GfigObject *obj,
+ cairo_t *cr);
+static GfigObject *d_copy_poly (GfigObject *obj);
+
+static void d_update_poly (GdkPoint *pnt);
+
+void
+tool_options_poly (GtkWidget *notebook)
+{
+ GtkWidget *sides;
+
+ sides = num_sides_widget (_("Regular Polygon Number of Sides"),
+ &poly_num_sides, NULL, 3, 200);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sides, NULL);
+}
+
+static void
+d_draw_poly (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint start_pnt = { 0, 0 };
+ GdkPoint first_pnt = { 0, 0 };
+ gboolean do_line = FALSE;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ /* First point is the center */
+ /* Just draw a control point around it */
+
+ draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ /* Next point defines the radius */
+ radius_pnt = center_pnt->next; /* this defines the vertices */
+
+ if (!radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in polygon - no vertice point \n");
+#endif /* DEBUG */
+ return;
+ }
+
+ /* Other control point */
+ if (obj == obj_creating)
+ draw_circle (&radius_pnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ /* Have center and radius - draw polygon */
+
+ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2 * G_PI / (gdouble) obj->type_data;
+ offset_angle = atan2 (shift_y, shift_x);
+
+ for (loop = 0 ; loop < obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ lx = radius * cos (ang_loop);
+ ly = radius * sin (ang_loop);
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ if (do_line)
+ {
+
+ /* Miss out points that come to the same location */
+ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y)
+ continue;
+
+ gfig_draw_line (calc_pnt.x, calc_pnt.y, start_pnt.x, start_pnt.y, cr);
+ }
+ else
+ {
+ do_line = TRUE;
+ first_pnt = calc_pnt;
+ }
+ start_pnt = calc_pnt;
+ }
+
+ gfig_draw_line (first_pnt.x, first_pnt.y, start_pnt.x, start_pnt.y, cr);
+}
+
+void
+d_paint_poly (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ gdouble *line_pnts;
+ gint seg_count;
+ gint i = 0;
+ DobjPoints *center_pnt;
+ DobjPoints *radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint first_pnt = { 0, 0 };
+ GdkPoint last_pnt = { 0, 0 };
+ gboolean first = TRUE;
+ gdouble *min_max;
+
+ g_assert (obj != NULL);
+
+ /* count - add one to close polygon */
+ seg_count = obj->type_data + 1;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt || !seg_count || !center_pnt->next)
+ return; /* no-line */
+
+ line_pnts = g_new0 (gdouble, 2 * seg_count + 1);
+ min_max = g_new (gdouble, 4);
+
+ /* Go around all the points drawing a line from one to the next */
+
+ radius_pnt = center_pnt->next; /* this defines the vetices */
+
+ /* Have center and radius - get lines */
+ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI/(gdouble) obj->type_data;
+ offset_angle = atan2 (shift_y, shift_x);
+
+ for (loop = 0 ; loop < obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ lx = radius * cos (ang_loop);
+ ly = radius * sin (ang_loop);
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ /* Miss out duped pnts */
+ if (!first)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ line_pnts[i++] = calc_pnt.x;
+ line_pnts[i++] = calc_pnt.y;
+ last_pnt = calc_pnt;
+
+ if (first)
+ {
+ first_pnt = calc_pnt;
+ first = FALSE;
+ min_max[0] = min_max[2] = calc_pnt.x;
+ min_max[1] = min_max[3] = calc_pnt.y;
+ }
+ else
+ {
+ min_max[0] = MIN (min_max[0], calc_pnt.x);
+ min_max[1] = MIN (min_max[1], calc_pnt.y);
+ min_max[2] = MAX (min_max[2], calc_pnt.x);
+ min_max[3] = MAX (min_max[3], calc_pnt.y);
+ }
+ }
+
+ line_pnts[i++] = first_pnt.x;
+ line_pnts[i++] = first_pnt.y;
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ {/* FIXME scale xmax and al. */
+ scale_to_original_xy (&line_pnts[0], i/2);
+ scale_to_original_xy (min_max, 2);
+ }
+ else
+ {
+ scale_to_xy (&line_pnts[0], i/2);
+ scale_to_xy (min_max, 2);
+ }
+
+
+ if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
+ {
+ gimp_context_push ();
+ gimp_context_set_antialias (selopt.antia);
+ gimp_context_set_feather (selopt.feather);
+ gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
+ gimp_image_select_polygon (gfig_context->image_id,
+ selopt.type,
+ i, line_pnts);
+ gimp_context_pop ();
+
+ paint_layer_fill (min_max[0], min_max[1], min_max[2], min_max[3]);
+ gimp_selection_none (gfig_context->image_id);
+ }
+
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ gfig_paint (selvals.brshtype, gfig_context->drawable_id, i, line_pnts);
+
+ g_free (line_pnts);
+ g_free (min_max);
+}
+
+void
+d_poly2lines (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ DobjPoints *center_pnt;
+ DobjPoints *radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint first_pnt = { 0, 0 };
+ GdkPoint last_pnt = { 0, 0 };
+ gboolean first = TRUE;
+
+ g_assert (obj != NULL);
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* no-line */
+
+ /* NULL out these points free later */
+ obj->points = NULL;
+
+ /* Go around all the points creating line points */
+
+ radius_pnt = center_pnt->next; /* this defines the vertices */
+
+ /* Have center and radius - get lines */
+ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI / (gdouble) obj->type_data;
+ offset_angle = atan2 (shift_y, shift_x);
+
+ for (loop = 0 ; loop < obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ lx = radius * cos (ang_loop);
+ ly = radius * sin (ang_loop);
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ if (!first)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0);
+
+ last_pnt = calc_pnt;
+
+ if (first)
+ {
+ first_pnt = calc_pnt;
+ first = FALSE;
+ }
+ }
+
+ d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0);
+ /* Free old pnts */
+ d_delete_dobjpoints (center_pnt);
+
+ /* hey we're a line now */
+ obj->type = LINE;
+ obj->class = &dobj_class[LINE];
+}
+
+void
+d_star2lines (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ DobjPoints *center_pnt;
+ DobjPoints *outer_radius_pnt;
+ DobjPoints *inner_radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble outer_radius;
+ gdouble inner_radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint first_pnt = { 0, 0 };
+ GdkPoint last_pnt = { 0, 0 };
+ gboolean first = TRUE;
+
+ g_assert (obj != NULL);
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* no-line */
+
+ /* NULL out these points free later */
+ obj->points = NULL;
+
+ /* Go around all the points creating line points */
+ /* Next point defines the radius */
+ outer_radius_pnt = center_pnt->next; /* this defines the vetices */
+
+ if (!outer_radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in star - no outer vertice point \n");
+#endif /* DEBUG */
+ return;
+ }
+
+ inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */
+
+ if (!inner_radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in star - no inner vertice point \n");
+#endif /* DEBUG */
+ return;
+ }
+
+ shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI / (2.0 * (gdouble) obj->type_data);
+ offset_angle = atan2 (shift_y, shift_x);
+
+ shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ inner_radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
+
+ for (loop = 0 ; loop < 2 * obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ if (loop % 2)
+ {
+ lx = inner_radius * cos (ang_loop);
+ ly = inner_radius * sin (ang_loop);
+ }
+ else
+ {
+ lx = outer_radius * cos (ang_loop);
+ ly = outer_radius * sin (ang_loop);
+ }
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ if (!first)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ d_pnt_add_line (obj, calc_pnt.x, calc_pnt.y, 0);
+
+ last_pnt = calc_pnt;
+
+ if (first)
+ {
+ first_pnt = calc_pnt;
+ first = FALSE;
+ }
+ }
+
+ d_pnt_add_line (obj, first_pnt.x, first_pnt.y, 0);
+ /* Free old pnts */
+ d_delete_dobjpoints (center_pnt);
+
+ /* hey we're a line now */
+ obj->type = LINE;
+ obj->class = &dobj_class[LINE];
+}
+
+static GfigObject *
+d_copy_poly (GfigObject *obj)
+{
+ GfigObject *np;
+
+ g_assert (obj->type == POLY);
+
+ np = d_new_object (POLY, obj->points->pnt.x, obj->points->pnt.y);
+ np->points->next = d_copy_dobjpoints (obj->points->next);
+ np->type_data = obj->type_data;
+
+ return np;
+}
+
+void
+d_poly_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[POLY];
+
+ class->type = POLY;
+ class->name = "POLY";
+ class->drawfunc = d_draw_poly;
+ class->paintfunc = d_paint_poly;
+ class->copyfunc = d_copy_poly;
+ class->update = d_update_poly;
+}
+
+static void
+d_update_poly (GdkPoint *pnt)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+
+ center_pnt = obj_creating->points;
+
+ if (!center_pnt)
+ return; /* No points */
+
+ if ((edge_pnt = center_pnt->next))
+ {
+ edge_pnt->pnt = *pnt;
+ }
+ else
+ {
+ d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1);
+ }
+}
+
+void
+d_poly_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (POLY, pnt->x, pnt->y);
+ obj_creating->type_data = poly_num_sides;
+}
+
+void
+d_poly_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ obj_creating = NULL;
+}
diff --git a/plug-ins/gfig/gfig-poly.h b/plug-ins/gfig/gfig-poly.h
new file mode 100644
index 0000000..9cadcce
--- /dev/null
+++ b/plug-ins/gfig/gfig-poly.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_POLY_H__
+#define __GFIG_POLY_H__
+
+void tool_options_poly (GtkWidget *notebook);
+
+void d_paint_poly (GfigObject *obj);
+
+void d_poly2lines (GfigObject *obj);
+void d_star2lines (GfigObject *obj);
+
+void d_poly_object_class_init (void);
+
+void d_poly_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_poly_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_POLY_H__ */
diff --git a/plug-ins/gfig/gfig-preview.c b/plug-ins/gfig/gfig-preview.c
new file mode 100644
index 0000000..c9f0009
--- /dev/null
+++ b/plug-ins/gfig/gfig-preview.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-grid.h"
+#include "gfig-dobject.h"
+#include "gfig-preview.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define PREVIEW_MASK (GDK_EXPOSURE_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON_MOTION_MASK | \
+ GDK_KEY_PRESS_MASK | \
+ GDK_KEY_RELEASE_MASK)
+
+static gint x_pos_val;
+static gint y_pos_val;
+static gint pos_tag = -1;
+GtkWidget *status_label_dname;
+GtkWidget *status_label_fname;
+static GtkWidget *pos_label; /* XY pos marker */
+
+
+static void gfig_preview_realize (GtkWidget *widget);
+static gboolean gfig_preview_events (GtkWidget *widget,
+ GdkEvent *event);
+static gboolean gfig_preview_expose (GtkWidget *widget,
+ GdkEvent *event);
+
+static gint gfig_invscale_x (gint x);
+static gint gfig_invscale_y (gint y);
+static GtkWidget *gfig_pos_labels (void);
+static GtkWidget *make_pos_info (void);
+
+static void gfig_pos_update (gint x,
+ gint y);
+static void gfig_pos_update_labels (gpointer data);
+
+GtkWidget *
+make_preview (void)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *ruler;
+
+ gfig_context->preview = gtk_drawing_area_new ();
+ gtk_widget_set_events (GTK_WIDGET (gfig_context->preview), PREVIEW_MASK);
+
+ g_signal_connect (gfig_context->preview , "realize",
+ G_CALLBACK (gfig_preview_realize),
+ NULL);
+
+ g_signal_connect (gfig_context->preview , "event",
+ G_CALLBACK (gfig_preview_events),
+ NULL);
+
+ g_signal_connect_after (gfig_context->preview , "expose-event",
+ G_CALLBACK (gfig_preview_expose),
+ NULL);
+
+ gtk_widget_set_size_request (gfig_context->preview,
+ preview_width, preview_height);
+
+ frame = gtk_frame_new (NULL);
+
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_attach (GTK_TABLE (table), gfig_context->preview, 1, 2, 1, 2,
+ GTK_FILL , GTK_FILL , 0, 0);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
+ gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_width, PREVIEW_SIZE);
+ g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
+ G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
+ ruler);
+ gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ruler);
+
+ ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
+ gimp_ruler_set_range (GIMP_RULER (ruler), 0, preview_height, PREVIEW_SIZE);
+ g_signal_connect_swapped (gfig_context->preview, "motion-notify-event",
+ G_CALLBACK (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (ruler))->motion_notify_event),
+ ruler);
+ gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ruler);
+
+ gtk_widget_show (frame);
+ gtk_widget_show (table);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ frame = make_pos_info ();
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+
+ gtk_widget_show (vbox);
+ gtk_widget_show (hbox);
+
+ return vbox;
+}
+
+static void
+gfig_preview_realize (GtkWidget *widget)
+{
+ GdkDisplay *display = gtk_widget_get_display (widget);
+
+ gdk_window_set_cursor (gtk_widget_get_window (gfig_context->preview),
+ gdk_cursor_new_for_display (display, GDK_CROSSHAIR));
+ gfig_grid_colors (widget);
+}
+
+static void
+draw_background (cairo_t *cr)
+{
+ if (! back_pixbuf)
+ back_pixbuf = gimp_image_get_thumbnail (gfig_context->image_id,
+ preview_width, preview_height,
+ GIMP_PIXBUF_LARGE_CHECKS);
+
+ if (back_pixbuf)
+ {
+ gdk_cairo_set_source_pixbuf (cr, back_pixbuf, 0, 0);
+ cairo_paint (cr);
+ }
+}
+
+static gboolean
+gfig_preview_expose (GtkWidget *widget,
+ GdkEvent *event)
+{
+ cairo_t *cr = gdk_cairo_create (event->expose.window);
+
+ if (gfig_context->show_background)
+ draw_background (cr);
+
+ draw_grid (cr);
+ draw_objects (gfig_context->current_obj->obj_list, TRUE, cr);
+
+ if (obj_creating)
+ {
+ GList *single = g_list_prepend (NULL, obj_creating);
+ draw_objects (single, TRUE, cr);
+ g_list_free (single);
+ }
+
+ cairo_destroy (cr);
+ return FALSE;
+}
+
+static gboolean
+gfig_preview_events (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ GdkEventMotion *mevent;
+ GdkPoint point;
+ static gint tmp_show_single = 0;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ bevent = (GdkEventButton *) event;
+ point.x = bevent->x;
+ point.y = bevent->y;
+
+ g_assert (need_to_scale == 0); /* If not out of step some how */
+
+ /* Start drawing of object */
+ if (selvals.otype >= MOVE_OBJ)
+ {
+ if (!selvals.scaletoimage)
+ {
+ point.x = gfig_invscale_x (point.x);
+ point.y = gfig_invscale_y (point.y);
+ }
+ object_operation_start (&point, bevent->state & GDK_SHIFT_MASK);
+
+ /* If constraining save start pnt */
+ if (selvals.opts.snap2grid)
+ {
+ /* Save point to constrained point ... if button 3 down */
+ if (bevent->button == 3)
+ {
+ find_grid_pos (&point, &point, FALSE);
+ }
+ }
+ }
+ else
+ {
+ if (selvals.opts.snap2grid)
+ find_grid_pos (&point, &point, FALSE);
+ object_start (&point, bevent->state & GDK_SHIFT_MASK);
+
+ gtk_widget_queue_draw (widget);
+ }
+
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ bevent = (GdkEventButton *) event;
+ point.x = bevent->x;
+ point.y = bevent->y;
+
+ if (selvals.opts.snap2grid)
+ find_grid_pos (&point, &point, bevent->button == 3);
+
+ /* Still got shift down ?*/
+ if (selvals.otype >= MOVE_OBJ)
+ {
+ if (!selvals.scaletoimage)
+ {
+ point.x = gfig_invscale_x (point.x);
+ point.y = gfig_invscale_y (point.y);
+ }
+ object_operation_end (&point, bevent->state & GDK_SHIFT_MASK);
+ }
+ else
+ {
+ if (obj_creating)
+ {
+ object_end (&point, bevent->state & GDK_SHIFT_MASK);
+ }
+ else
+ break;
+ }
+
+ gfig_paint_callback ();
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ mevent = (GdkEventMotion *) event;
+ point.x = mevent->x;
+ point.y = mevent->y;
+
+ if (selvals.opts.snap2grid)
+ find_grid_pos (&point, &point, mevent->state & GDK_BUTTON3_MASK);
+
+ if (selvals.otype >= MOVE_OBJ)
+ {
+ /* Moving objects around */
+ if (!selvals.scaletoimage)
+ {
+ point.x = gfig_invscale_x (point.x);
+ point.y = gfig_invscale_y (point.y);
+ }
+ object_operation (&point, mevent->state & GDK_SHIFT_MASK);
+ gfig_pos_update (point.x, point.y);
+ return FALSE;
+ }
+
+ if (obj_creating)
+ {
+ obj_creating->class->update (&point);
+ gtk_widget_queue_draw (widget);
+ }
+ gfig_pos_update (point.x, point.y);
+ break;
+
+ case GDK_KEY_PRESS:
+ if ((tmp_show_single = obj_show_single) != -1)
+ {
+ obj_show_single = -1;
+ draw_grid_clear ();
+ }
+ break;
+
+ case GDK_KEY_RELEASE:
+ if (tmp_show_single != -1)
+ {
+ obj_show_single = tmp_show_single;
+ draw_grid_clear ();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static GtkWidget *
+make_pos_info (void)
+{
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ frame = gimp_frame_new (_("Object Details"));
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ /* Add labels */
+ label = gfig_pos_labels ();
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gfig_pos_enable (NULL, NULL);
+
+#if 0
+ label = gfig_obj_size_label ();
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+#endif /* 0 */
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (frame);
+
+ return frame;
+}
+
+static gint
+gfig_invscale_x (gint x)
+{
+ if (!selvals.scaletoimage)
+ return (gint) (x * scale_x_factor);
+ else
+ return x;
+}
+
+static gint
+gfig_invscale_y (gint y)
+{
+ if (!selvals.scaletoimage)
+ return (gint) (y * scale_y_factor);
+ else
+ return y;
+}
+
+static GtkWidget *
+gfig_pos_labels (void)
+{
+ GtkWidget *label;
+ GtkWidget *hbox;
+ gchar buf[256];
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_widget_show (hbox);
+
+ /* Position labels */
+ label = gtk_label_new (_("XY position:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ pos_label = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), pos_label, FALSE, FALSE, 0);
+ gtk_widget_show (pos_label);
+
+ g_snprintf (buf, sizeof (buf), "%d, %d", 0, 0);
+ gtk_label_set_text (GTK_LABEL (pos_label), buf);
+
+ return hbox;
+}
+
+void
+gfig_pos_enable (GtkWidget *widget,
+ gpointer data)
+{
+ gboolean enable = selvals.showpos;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (pos_label), enable);
+}
+
+static void
+gfig_pos_update_labels (gpointer data)
+{
+ static gchar buf[256];
+
+ pos_tag = -1;
+
+ g_snprintf (buf, sizeof (buf), "%d, %d", x_pos_val, y_pos_val);
+ gtk_label_set_text (GTK_LABEL (pos_label), buf);
+}
+
+static void
+gfig_pos_update (gint x,
+ gint y)
+{
+ if ((x_pos_val !=x || y_pos_val != y) && pos_tag == -1 && selvals.showpos)
+ {
+ x_pos_val = x;
+ y_pos_val = y;
+ gfig_pos_update_labels (NULL);
+ }
+}
diff --git a/plug-ins/gfig/gfig-preview.h b/plug-ins/gfig/gfig-preview.h
new file mode 100644
index 0000000..49804e8
--- /dev/null
+++ b/plug-ins/gfig/gfig-preview.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_PREVIEW_H__
+#define __GFIG_PREVIEW_H__
+
+#define PREVIEW_SIZE 400
+
+GtkWidget *make_preview (void);
+
+void gfig_pos_enable (GtkWidget *widget,
+ gpointer data);
+
+#endif /* __GFIG_PREVIEW_H__ */
diff --git a/plug-ins/gfig/gfig-rectangle.c b/plug-ins/gfig/gfig-rectangle.c
new file mode 100644
index 0000000..d3688f1
--- /dev/null
+++ b/plug-ins/gfig/gfig-rectangle.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-rectangle.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void d_draw_rectangle (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_rectangle (GfigObject *obj);
+static GfigObject *d_copy_rectangle (GfigObject *obj);
+
+static void d_update_rectangle (GdkPoint *pnt);
+
+static void
+d_draw_rectangle (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *first_pnt;
+ DobjPoints *second_pnt;
+ gint xmin, ymin;
+ gint xmax, ymax;
+
+ first_pnt = obj->points;
+
+ if (!first_pnt)
+ return; /* End-of-line */
+
+ draw_sqr (&first_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ second_pnt = first_pnt->next;
+
+ if (!second_pnt)
+ return;
+
+ if (obj == obj_creating)
+ draw_circle (&second_pnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&second_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ xmin = MIN (gfig_scale_x (first_pnt->pnt.x),
+ gfig_scale_x (second_pnt->pnt.x));
+ ymin = MIN (gfig_scale_y (first_pnt->pnt.y),
+ gfig_scale_y (second_pnt->pnt.y));
+ xmax = MAX (gfig_scale_x (first_pnt->pnt.x),
+ gfig_scale_x (second_pnt->pnt.x));
+ ymax = MAX (gfig_scale_y (first_pnt->pnt.y),
+ gfig_scale_y (second_pnt->pnt.y));
+
+ cairo_rectangle (cr, xmin + .5, ymin + .5, xmax - xmin, ymax - ymin);
+ draw_item (cr, FALSE);
+}
+
+static void
+d_paint_rectangle (GfigObject *obj)
+{
+ DobjPoints *first_pnt;
+ DobjPoints *second_pnt;
+ gdouble dpnts[4];
+
+ g_assert (obj != NULL);
+
+ /* Drawing rectangles is hard .
+ * 1) select rectangle
+ * 2) stroke it
+ */
+ first_pnt = obj->points;
+
+ if (!first_pnt)
+ return; /* End-of-line */
+
+ second_pnt = first_pnt->next;
+
+ if (!second_pnt)
+ {
+ g_error ("Internal error - rectangle no second pnt");
+ }
+
+ dpnts[0] = (gdouble) MIN (first_pnt->pnt.x, second_pnt->pnt.x);
+ dpnts[1] = (gdouble) MIN (first_pnt->pnt.y, second_pnt->pnt.y);
+ dpnts[2] = (gdouble) MAX (first_pnt->pnt.x, second_pnt->pnt.x);
+ dpnts[3] = (gdouble) MAX (first_pnt->pnt.y, second_pnt->pnt.y);
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&dpnts[0], 2);
+ else
+ scale_to_xy (&dpnts[0], 2);
+
+ if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
+ {
+ gimp_context_push ();
+ gimp_context_set_feather (selopt.feather);
+ gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
+ gimp_image_select_rectangle (gfig_context->image_id,
+ selopt.type,
+ dpnts[0], dpnts[1],
+ dpnts[2] - dpnts[0],
+ dpnts[3] - dpnts[1]);
+ gimp_context_pop ();
+
+ paint_layer_fill (dpnts[0], dpnts[1], dpnts[2], dpnts[3]);
+ gimp_selection_none (gfig_context->image_id);
+ }
+
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ gdouble line_pnts[] = { dpnts[0], dpnts[1], dpnts[2], dpnts[1],
+ dpnts[2], dpnts[3], dpnts[0], dpnts[3],
+ dpnts[0], dpnts[1] };
+
+ gfig_paint (selvals.brshtype, gfig_context->drawable_id, 10, line_pnts);
+ }
+}
+
+static GfigObject *
+d_copy_rectangle (GfigObject * obj)
+{
+ GfigObject *new_rectangle;
+
+ g_assert (obj->type == RECTANGLE);
+
+ new_rectangle = d_new_object (RECTANGLE,
+ obj->points->pnt.x, obj->points->pnt.y);
+ new_rectangle->points->next = d_copy_dobjpoints (obj->points->next);
+
+ return new_rectangle;
+}
+
+void
+d_rectangle_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[RECTANGLE];
+
+ class->type = RECTANGLE;
+ class->name = "RECTANGLE";
+ class->drawfunc = d_draw_rectangle;
+ class->paintfunc = d_paint_rectangle;
+ class->copyfunc = d_copy_rectangle;
+ class->update = d_update_rectangle;
+}
+
+static void
+d_update_rectangle (GdkPoint *pnt)
+{
+ DobjPoints *first_pnt;
+ DobjPoints *second_pnt;
+
+ first_pnt = obj_creating->points;
+
+ if (!first_pnt)
+ return; /* No points */
+
+ if ((second_pnt = first_pnt->next))
+ {
+ second_pnt->pnt.x = pnt->x;
+ second_pnt->pnt.y = pnt->y;
+ }
+ else
+ {
+ second_pnt = new_dobjpoint (pnt->x, pnt->y);
+ first_pnt->next = second_pnt;
+ }
+}
+
+void
+d_rectangle_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (RECTANGLE, pnt->x, pnt->y);
+}
+
+void
+d_rectangle_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ /* Under control point */
+ if (!obj_creating->points->next)
+ {
+ /* No circle created */
+ free_one_obj (obj_creating);
+ }
+ else
+ {
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ }
+
+ obj_creating = NULL;
+}
+
diff --git a/plug-ins/gfig/gfig-rectangle.h b/plug-ins/gfig/gfig-rectangle.h
new file mode 100644
index 0000000..72ebe74
--- /dev/null
+++ b/plug-ins/gfig/gfig-rectangle.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_RECTANGLE_H__
+#define __GFIG_RECTANGLE_H__
+
+void d_rectangle_object_class_init (void);
+
+void d_rectangle_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_rectangle_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_RECTANGLE_H__ */
diff --git a/plug-ins/gfig/gfig-spiral.c b/plug-ins/gfig/gfig-spiral.c
new file mode 100644
index 0000000..fbb56cf
--- /dev/null
+++ b/plug-ins/gfig/gfig-spiral.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-line.h"
+#include "gfig-spiral.h"
+#include "gfig-dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void d_draw_spiral (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_spiral (GfigObject *obj);
+static GfigObject *d_copy_spiral (GfigObject *obj);
+
+static void d_update_spiral (GdkPoint *pnt);
+
+static gint spiral_num_turns = 4; /* Default to 4 turns */
+static gint spiral_toggle = 0; /* 0 = clockwise -1 = anti-clockwise */
+
+void
+tool_options_spiral (GtkWidget *notebook)
+{
+ GtkWidget *sides;
+
+ sides = num_sides_widget (_("Spiral Number of Turns"),
+ &spiral_num_turns, &spiral_toggle, 1, 20);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sides, NULL);
+}
+
+static void
+d_draw_spiral (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gdouble offset_angle;
+ gdouble sp_cons;
+ gint loop;
+ GdkPoint start_pnt = { 0, 0 };
+ gboolean do_line = FALSE;
+ gint clock_wise = 1;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ /* First point is the center */
+ /* Just draw a control point around it */
+
+ draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ /* Next point defines the radius */
+ radius_pnt = center_pnt->next; /* this defines the vetices */
+
+ if (!radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in spiral - no vertice point \n");
+#endif /* DEBUG */
+ return;
+ }
+
+ /* Other control point */
+ if (obj_creating == obj)
+ draw_circle (&radius_pnt->pnt, TRUE, cr);
+ else
+ draw_sqr (&radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ /* Have center and radius - draw spiral */
+
+ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
+
+ offset_angle = atan2 (shift_y, shift_x);
+
+ clock_wise = obj->type_data / abs (obj->type_data);
+
+ if (offset_angle < 0)
+ offset_angle += 2.0 * G_PI;
+
+ sp_cons = radius/(obj->type_data * 2 * G_PI + offset_angle);
+ /* Lines */
+ ang_grid = 2.0 * G_PI / 180.0;
+
+
+ for (loop = 0 ; loop <= abs (obj->type_data * 180) +
+ clock_wise * (gint)RINT (offset_angle/ang_grid) ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid;
+
+ lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise;
+ ly = sp_cons * ang_loop * sin (ang_loop);
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ if (do_line)
+ {
+ /* Miss out points that come to the same location */
+ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y)
+ continue;
+
+ gfig_draw_line (calc_pnt.x, calc_pnt.y, start_pnt.x, start_pnt.y, cr);
+ }
+ else
+ {
+ do_line = TRUE;
+ }
+ start_pnt = calc_pnt;
+ }
+}
+
+static void
+d_paint_spiral (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ gdouble *line_pnts;
+ gint seg_count = 0;
+ gint i = 0;
+ DobjPoints *center_pnt;
+ DobjPoints *radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble radius;
+ gdouble offset_angle;
+ gdouble sp_cons;
+ gint loop;
+ GdkPoint last_pnt = { 0, 0 };
+ gint clock_wise = 1;
+
+ g_assert (obj != NULL);
+
+ center_pnt = obj->points;
+
+ if (!center_pnt || !center_pnt->next)
+ return; /* no-line */
+
+ /* Go around all the points drawing a line from one to the next */
+
+ radius_pnt = center_pnt->next; /* this defines the vetices */
+
+ /* Have center and radius - get lines */
+ shift_x = radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ radius = sqrt ((shift_x * shift_x) + (shift_y * shift_y));
+
+ clock_wise = obj->type_data / abs (obj->type_data);
+
+ offset_angle = atan2 (shift_y, shift_x);
+
+ if (offset_angle < 0)
+ offset_angle += 2.0 * G_PI;
+
+ sp_cons = radius/(obj->type_data * 2.0 * G_PI + offset_angle);
+ /* Lines */
+ ang_grid = 2.0 * G_PI / 180.0;
+
+ /* count - */
+ seg_count = abs (obj->type_data * 180) + clock_wise * (gint)RINT (offset_angle/ang_grid);
+
+ line_pnts = g_new0 (gdouble, 2 * seg_count + 3);
+
+ for (loop = 0 ; loop <= seg_count; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid;
+
+ lx = sp_cons * ang_loop * cos (ang_loop)*clock_wise;
+ ly = sp_cons * ang_loop * sin (ang_loop);
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ /* Miss out duped pnts */
+ if (!loop)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ line_pnts[i++] = calc_pnt.x;
+ line_pnts[i++] = calc_pnt.y;
+ last_pnt = calc_pnt;
+ }
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ scale_to_original_xy (&line_pnts[0], i / 2);
+ else
+ scale_to_xy (&line_pnts[0], i / 2);
+
+ /* One go */
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ {
+ gfig_paint (selvals.brshtype,
+ gfig_context->drawable_id,
+ i, line_pnts);
+ }
+
+ g_free (line_pnts);
+}
+
+static GfigObject *
+d_copy_spiral (GfigObject *obj)
+{
+ GfigObject *np;
+
+ g_assert (obj->type == SPIRAL);
+
+ np = d_new_object (SPIRAL, obj->points->pnt.x, obj->points->pnt.y);
+ np->points->next = d_copy_dobjpoints (obj->points->next);
+ np->type_data = obj->type_data;
+
+ return np;
+}
+
+void
+d_spiral_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[SPIRAL];
+
+ class->type = SPIRAL;
+ class->name = "SPIRAL";
+ class->drawfunc = d_draw_spiral;
+ class->paintfunc = d_paint_spiral;
+ class->copyfunc = d_copy_spiral;
+ class->update = d_update_spiral;
+}
+
+static void
+d_update_spiral (GdkPoint *pnt)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *edge_pnt;
+
+ center_pnt = obj_creating->points;
+
+ if (!center_pnt)
+ return; /* No points */
+
+ if ((edge_pnt = center_pnt->next))
+ {
+ edge_pnt->pnt = *pnt;
+ }
+ else
+ {
+ /* Radius is a few pixels away */
+ /* First edge point */
+ d_pnt_add_line (obj_creating, pnt->x, pnt->y, -1);
+ }
+}
+
+void
+d_spiral_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (SPIRAL, pnt->x, pnt->y);
+ obj_creating->type_data = spiral_num_turns * ((spiral_toggle == 0) ? 1 : -1);
+}
+
+void
+d_spiral_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ obj_creating = NULL;
+}
diff --git a/plug-ins/gfig/gfig-spiral.h b/plug-ins/gfig/gfig-spiral.h
new file mode 100644
index 0000000..37a1007
--- /dev/null
+++ b/plug-ins/gfig/gfig-spiral.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_SPIRAL_H__
+#define __GFIG_SPIRAL_H__
+
+void tool_options_spiral (GtkWidget *notebook);
+
+void d_spiral_object_class_init (void);
+
+void d_spiral_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_spiral_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+#endif /* __GFIG_SPIRAL_H__ */
diff --git a/plug-ins/gfig/gfig-star.c b/plug-ins/gfig/gfig-star.c
new file mode 100644
index 0000000..6ef6848
--- /dev/null
+++ b/plug-ins/gfig/gfig-star.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-line.h"
+#include "gfig-dobject.h"
+#include "gfig-star.h"
+#include "gfig-dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gint star_num_sides = 3; /* Default to three sided object */
+
+static void d_draw_star (GfigObject *obj,
+ cairo_t *cr);
+static void d_paint_star (GfigObject *obj);
+static GfigObject *d_copy_star (GfigObject *obj);
+
+static void d_update_star (GdkPoint *pnt);
+
+void
+tool_options_star (GtkWidget *notebook)
+{
+ GtkWidget *sides;
+
+ sides = num_sides_widget (_("Star Number of Points"),
+ &star_num_sides, NULL, 3, 200);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), sides, NULL);
+}
+
+static void
+d_draw_star (GfigObject *obj,
+ cairo_t *cr)
+{
+ DobjPoints *center_pnt;
+ DobjPoints *outer_radius_pnt;
+ DobjPoints *inner_radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble outer_radius;
+ gdouble inner_radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint start_pnt = { 0, 0 };
+ GdkPoint first_pnt = { 0, 0 };
+ gboolean do_line = FALSE;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt)
+ return; /* End-of-line */
+
+ /* First point is the center */
+ /* Just draw a control point around it */
+
+ draw_sqr (&center_pnt->pnt, obj == gfig_context->selected_obj, cr);
+
+ /* Next point defines the radius */
+ outer_radius_pnt = center_pnt->next; /* this defines the vertices */
+
+ if (!outer_radius_pnt)
+ {
+ return;
+ }
+
+ inner_radius_pnt = outer_radius_pnt->next; /* this defines the vertices */
+
+ if (!inner_radius_pnt)
+ {
+ return;
+ }
+
+ /* Other control points */
+ if (obj == obj_creating)
+ {
+ draw_circle (&outer_radius_pnt->pnt, TRUE, cr);
+ draw_circle (&inner_radius_pnt->pnt, TRUE, cr);
+ }
+ else
+ {
+ draw_sqr (&outer_radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
+ draw_sqr (&inner_radius_pnt->pnt, obj == gfig_context->selected_obj, cr);
+ }
+
+ /* Have center and radius - draw star */
+
+ shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI / (2.0 * (gdouble) obj->type_data);
+ offset_angle = atan2 (shift_y, shift_x);
+
+ shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ inner_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ for (loop = 0 ; loop < 2 * obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ if (loop % 2)
+ {
+ lx = inner_radius * cos (ang_loop);
+ ly = inner_radius * sin (ang_loop);
+ }
+ else
+ {
+ lx = outer_radius * cos (ang_loop);
+ ly = outer_radius * sin (ang_loop);
+ }
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ if (do_line)
+ {
+
+ /* Miss out points that come to the same location */
+ if (calc_pnt.x == start_pnt.x && calc_pnt.y == start_pnt.y)
+ continue;
+
+ gfig_draw_line (calc_pnt.x, calc_pnt.y, start_pnt.x, start_pnt.y, cr);
+ }
+ else
+ {
+ do_line = TRUE;
+ first_pnt = calc_pnt;
+ }
+ start_pnt = calc_pnt;
+ }
+
+ gfig_draw_line (first_pnt.x, first_pnt.y, start_pnt.x, start_pnt.y, cr);
+}
+
+static void
+d_paint_star (GfigObject *obj)
+{
+ /* first point center */
+ /* Next point is radius */
+ gdouble *line_pnts;
+ gint seg_count = 0;
+ gint i = 0;
+ DobjPoints *center_pnt;
+ DobjPoints *outer_radius_pnt;
+ DobjPoints *inner_radius_pnt;
+ gint16 shift_x;
+ gint16 shift_y;
+ gdouble ang_grid;
+ gdouble ang_loop;
+ gdouble outer_radius;
+ gdouble inner_radius;
+ gdouble offset_angle;
+ gint loop;
+ GdkPoint first_pnt = { 0, 0 };
+ GdkPoint last_pnt = { 0, 0 };
+ gboolean first = TRUE;
+ gdouble *min_max;
+
+ g_assert (obj != NULL);
+
+ /* count - add one to close polygon */
+ seg_count = 2 * obj->type_data + 1;
+
+ center_pnt = obj->points;
+
+ if (!center_pnt || !seg_count)
+ return; /* no-line */
+
+ line_pnts = g_new0 (gdouble, 2 * seg_count + 1);
+ min_max = g_new (gdouble, 4);
+
+ /* Go around all the points drawing a line from one to the next */
+ /* Next point defines the radius */
+ outer_radius_pnt = center_pnt->next; /* this defines the vetices */
+
+ if (!outer_radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in star - no outer vertice point \n");
+#endif /* DEBUG */
+ g_free (line_pnts);
+ g_free (min_max);
+ return;
+ }
+
+ inner_radius_pnt = outer_radius_pnt->next; /* this defines the vetices */
+
+ if (!inner_radius_pnt)
+ {
+#ifdef DEBUG
+ g_warning ("Internal error in star - no inner vertice point \n");
+#endif /* DEBUG */
+ g_free (line_pnts);
+ g_free (min_max);
+ return;
+ }
+
+ shift_x = outer_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = outer_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ outer_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ /* Lines */
+ ang_grid = 2.0 * G_PI / (2.0 * (gdouble) obj->type_data);
+ offset_angle = atan2 (shift_y, shift_x);
+
+ shift_x = inner_radius_pnt->pnt.x - center_pnt->pnt.x;
+ shift_y = inner_radius_pnt->pnt.y - center_pnt->pnt.y;
+
+ inner_radius = sqrt ((shift_x*shift_x) + (shift_y*shift_y));
+
+ for (loop = 0 ; loop < 2 * obj->type_data ; loop++)
+ {
+ gdouble lx, ly;
+ GdkPoint calc_pnt;
+
+ ang_loop = (gdouble)loop * ang_grid + offset_angle;
+
+ if (loop % 2)
+ {
+ lx = inner_radius * cos (ang_loop);
+ ly = inner_radius * sin (ang_loop);
+ }
+ else
+ {
+ lx = outer_radius * cos (ang_loop);
+ ly = outer_radius * sin (ang_loop);
+ }
+
+ calc_pnt.x = RINT (lx + center_pnt->pnt.x);
+ calc_pnt.y = RINT (ly + center_pnt->pnt.y);
+
+ /* Miss out duped pnts */
+ if (!first)
+ {
+ if (calc_pnt.x == last_pnt.x && calc_pnt.y == last_pnt.y)
+ {
+ continue;
+ }
+ }
+
+ line_pnts[i++] = calc_pnt.x;
+ line_pnts[i++] = calc_pnt.y;
+ last_pnt = calc_pnt;
+
+ if (first)
+ {
+ first_pnt = calc_pnt;
+ first = FALSE;
+ min_max[0] = min_max[2] = calc_pnt.x;
+ min_max[1] = min_max[3] = calc_pnt.y;
+ }
+ else
+ {
+ min_max[0] = MIN (min_max[0], calc_pnt.x);
+ min_max[1] = MIN (min_max[1], calc_pnt.y);
+ min_max[2] = MAX (min_max[2], calc_pnt.x);
+ min_max[3] = MAX (min_max[3], calc_pnt.y);
+ }
+ }
+
+ line_pnts[i++] = first_pnt.x;
+ line_pnts[i++] = first_pnt.y;
+
+ /* Scale before drawing */
+ if (selvals.scaletoimage)
+ {
+ scale_to_original_xy (&line_pnts[0], i / 2);
+ scale_to_original_xy (min_max, 2);
+ }
+ else
+ {
+ scale_to_xy (&line_pnts[0], i / 2);
+ scale_to_xy (min_max, 2);
+ }
+
+ if (gfig_context_get_current_style ()->fill_type != FILL_NONE)
+ {
+ gimp_context_push ();
+ gimp_context_set_antialias (selopt.antia);
+ gimp_context_set_feather (selopt.feather);
+ gimp_context_set_feather_radius (selopt.feather_radius, selopt.feather_radius);
+ gimp_image_select_polygon (gfig_context->image_id,
+ selopt.type,
+ i, line_pnts);
+ gimp_context_pop ();
+
+ paint_layer_fill (min_max[0], min_max[1], min_max[2], min_max[3]);
+ gimp_selection_none (gfig_context->image_id);
+ }
+
+ if (obj->style.paint_type == PAINT_BRUSH_TYPE)
+ gfig_paint (selvals.brshtype, gfig_context->drawable_id, i, line_pnts);
+
+ g_free (line_pnts);
+ g_free (min_max);
+}
+
+static GfigObject *
+d_copy_star (GfigObject *obj)
+{
+ GfigObject *np;
+
+ g_assert (obj->type == STAR);
+
+ np = d_new_object (STAR, obj->points->pnt.x, obj->points->pnt.y);
+ np->points->next = d_copy_dobjpoints (obj->points->next);
+ np->type_data = obj->type_data;
+
+ return np;
+}
+
+void
+d_star_object_class_init (void)
+{
+ GfigObjectClass *class = &dobj_class[STAR];
+
+ class->type = STAR;
+ class->name = "STAR";
+ class->drawfunc = d_draw_star;
+ class->paintfunc = d_paint_star;
+ class->copyfunc = d_copy_star;
+ class->update = d_update_star;
+}
+
+static void
+d_update_star (GdkPoint *pnt)
+{
+ DobjPoints *center_pnt, *inner_pnt, *outer_pnt;
+
+ center_pnt = obj_creating->points;
+
+ if (!center_pnt)
+ return; /* No points */
+
+ if ((outer_pnt = center_pnt->next))
+ {
+ inner_pnt = outer_pnt->next;
+ outer_pnt->pnt = *pnt;
+ inner_pnt->pnt.x = pnt->x + (2 * (center_pnt->pnt.x - pnt->x)) / 3;
+ inner_pnt->pnt.y = pnt->y + (2 * (center_pnt->pnt.y - pnt->y)) / 3;
+ }
+ else
+ {
+ /* Radius is a few pixels away */
+ /* First edge point */
+ d_pnt_add_line (obj_creating, pnt->x, pnt->y,-1);
+ /* Inner radius */
+ d_pnt_add_line (obj_creating,
+ pnt->x + (2 * (center_pnt->pnt.x - pnt->x)) / 3,
+ pnt->y + (2 * (center_pnt->pnt.y - pnt->y)) / 3,
+ -1);
+ }
+}
+
+void
+d_star_start (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ obj_creating = d_new_object (STAR, pnt->x, pnt->y);
+ obj_creating->type_data = star_num_sides;
+}
+
+void
+d_star_end (GdkPoint *pnt,
+ gboolean shift_down)
+{
+ add_to_all_obj (gfig_context->current_obj, obj_creating);
+ obj_creating = NULL;
+}
+
diff --git a/plug-ins/gfig/gfig-star.h b/plug-ins/gfig/gfig-star.h
new file mode 100644
index 0000000..193e03a
--- /dev/null
+++ b/plug-ins/gfig/gfig-star.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_STAR_H__
+#define __GFIG_STAR_H__
+
+void tool_options_star (GtkWidget *notebook);
+
+void d_star_object_class_init (void);
+
+void d_star_start (GdkPoint *pnt,
+ gboolean shift_down);
+void d_star_end (GdkPoint *pnt,
+ gboolean shift_down);
+
+
+#endif /* __GFIG_STAR_H__ */
diff --git a/plug-ins/gfig/gfig-stock.c b/plug-ins/gfig/gfig-stock.c
new file mode 100644
index 0000000..19e791d
--- /dev/null
+++ b/plug-ins/gfig/gfig-stock.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas <alt@picnic.demon.co.uk>
+ * 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gfig-stock.h"
+
+#include "images/gfig-stock-pixbufs.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static GtkIconFactory *gfig_icon_factory = NULL;
+
+static GtkStockItem gfig_stock_items[] =
+{
+ { GFIG_STOCK_BEZIER, N_("Create bezier curve"), 0, 0, NULL },
+ { GFIG_STOCK_CIRCLE, N_("Create circle"), 0, 0, NULL },
+ { GFIG_STOCK_COPY_OBJECT, N_("Copy an object"), 0, 0, NULL },
+ { GFIG_STOCK_CURVE, N_("Create arc"), 0, 0, NULL },
+ { GFIG_STOCK_DELETE_OBJECT, N_("Delete an object"), 0, 0, NULL },
+ { GFIG_STOCK_ELLIPSE, N_("Create ellipse"), 0, 0, NULL },
+ { GFIG_STOCK_LINE, N_("Create line"), 0, 0, NULL },
+ { GFIG_STOCK_MOVE_OBJECT, N_("Move an object"), 0, 0, NULL },
+ { GFIG_STOCK_MOVE_POINT, N_("Move a single point"), 0, 0, NULL },
+ { GFIG_STOCK_POLYGON, N_("Create reg polygon"), 0, 0, NULL },
+ { GFIG_STOCK_RECTANGLE, N_("Create rectangle"), 0, 0, NULL },
+ { GFIG_STOCK_SELECT_OBJECT, N_("Select an object"), 0, 0, NULL },
+ { GFIG_STOCK_SHOW_ALL, N_("Show all objects"), 0, 0, NULL },
+ { GFIG_STOCK_SPIRAL, N_("Create spiral"), 0, 0, NULL },
+ { GFIG_STOCK_STAR, N_("Create star"), 0, 0, NULL }
+};
+
+static void
+add_stock_icon (const gchar *stock_id,
+ GtkIconSize size,
+ const guint8 *inline_data)
+{
+ GtkIconSource *source;
+ GtkIconSet *set;
+ GdkPixbuf *pixbuf;
+
+ source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_size (source, size);
+ gtk_icon_source_set_size_wildcarded (source, TRUE);
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
+
+ gtk_icon_source_set_pixbuf (source, pixbuf);
+ g_object_unref (pixbuf);
+
+ set = gtk_icon_set_new ();
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (gfig_icon_factory, stock_id, set);
+
+ gtk_icon_set_unref (set);
+}
+
+void
+gfig_stock_init (void)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ gfig_icon_factory = gtk_icon_factory_new ();
+
+ add_stock_icon (GFIG_STOCK_BEZIER, GTK_ICON_SIZE_BUTTON, stock_bezier);
+ add_stock_icon (GFIG_STOCK_CIRCLE, GTK_ICON_SIZE_BUTTON, stock_circle);
+ add_stock_icon (GFIG_STOCK_COPY_OBJECT, GTK_ICON_SIZE_BUTTON, stock_copy_object);
+ add_stock_icon (GFIG_STOCK_CURVE, GTK_ICON_SIZE_BUTTON, stock_curve);
+ add_stock_icon (GFIG_STOCK_DELETE_OBJECT, GTK_ICON_SIZE_BUTTON, stock_delete_object);
+ add_stock_icon (GFIG_STOCK_ELLIPSE, GTK_ICON_SIZE_BUTTON, stock_ellipse);
+ add_stock_icon (GFIG_STOCK_LINE, GTK_ICON_SIZE_BUTTON, stock_line);
+ add_stock_icon (GFIG_STOCK_MOVE_OBJECT, GTK_ICON_SIZE_BUTTON, stock_move_object);
+ add_stock_icon (GFIG_STOCK_MOVE_POINT, GTK_ICON_SIZE_BUTTON, stock_move_point);
+ add_stock_icon (GFIG_STOCK_POLYGON, GTK_ICON_SIZE_BUTTON, stock_polygon);
+ add_stock_icon (GFIG_STOCK_RECTANGLE, GTK_ICON_SIZE_BUTTON, stock_rectangle);
+ add_stock_icon (GFIG_STOCK_SELECT_OBJECT, GTK_ICON_SIZE_BUTTON, stock_select_object);
+ add_stock_icon (GFIG_STOCK_SHOW_ALL, GTK_ICON_SIZE_BUTTON, stock_show_all);
+ add_stock_icon (GFIG_STOCK_SPIRAL, GTK_ICON_SIZE_BUTTON, stock_spiral);
+ add_stock_icon (GFIG_STOCK_STAR, GTK_ICON_SIZE_BUTTON, stock_star);
+
+ gtk_icon_factory_add_default (gfig_icon_factory);
+
+ gtk_stock_add_static (gfig_stock_items, G_N_ELEMENTS (gfig_stock_items));
+
+ initialized = TRUE;
+}
diff --git a/plug-ins/gfig/gfig-stock.h b/plug-ins/gfig/gfig-stock.h
new file mode 100644
index 0000000..ee459c8
--- /dev/null
+++ b/plug-ins/gfig/gfig-stock.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas <alt@picnic.demon.co.uk>
+ * 2003 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GFIG_STOCK_H__
+#define __GFIG_STOCK_H__
+
+#define GFIG_STOCK_BEZIER "gfig-bezier"
+#define GFIG_STOCK_CIRCLE "gfig-circle"
+#define GFIG_STOCK_COPY_OBJECT "gfig-copy-object"
+#define GFIG_STOCK_CURVE "gfig-curve"
+#define GFIG_STOCK_DELETE_OBJECT "gfig-delete"
+#define GFIG_STOCK_ELLIPSE "gfig-ellipse"
+#define GFIG_STOCK_LINE "gfig-line"
+#define GFIG_STOCK_MOVE_OBJECT "gfig-move-object"
+#define GFIG_STOCK_MOVE_POINT "gfig-move-point"
+#define GFIG_STOCK_POLYGON "gfig-polygon"
+#define GFIG_STOCK_RECTANGLE "gfig-rectangle"
+#define GFIG_STOCK_SELECT_OBJECT "gfig-select-object"
+#define GFIG_STOCK_SHOW_ALL "gfig-show-all"
+#define GFIG_STOCK_SPIRAL "gfig-spiral"
+#define GFIG_STOCK_STAR "gfig-star"
+
+void gfig_stock_init (void);
+
+#endif /* __GFIG_STOCK_H__ */
diff --git a/plug-ins/gfig/gfig-style.c b/plug-ins/gfig/gfig-style.c
new file mode 100644
index 0000000..69776e9
--- /dev/null
+++ b/plug-ins/gfig/gfig-style.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "gfig.h"
+#include "gfig-dobject.h"
+#include "gfig-style.h"
+
+
+static void gfig_read_parameter_string (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gchar **style_entry);
+
+static void gfig_read_parameter_int (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gint *style_entry);
+
+static void gfig_read_parameter_double (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gdouble *style_entry);
+
+static void gfig_read_parameter_gimp_rgb (gchar **text,
+ gint nitems,
+ const gchar *name,
+ GimpRGB *style_entry);
+
+static void
+gfig_read_parameter_string (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gchar **style_entry)
+{
+ gint n = 0;
+ gchar *ptr;
+ gchar *tmpstr;
+
+ *style_entry = NULL;
+
+ while (n < nitems)
+ {
+ ptr = strchr (text[n], ':');
+ if (ptr)
+ {
+ tmpstr = g_strndup (text[n], ptr - text[n]);
+ ptr++;
+ if (!strcmp (tmpstr, name))
+ {
+ *style_entry = g_strdup (g_strchug (ptr));
+ g_free (tmpstr);
+ return;
+ }
+ g_free (tmpstr);
+ }
+ ++n;
+ }
+
+ g_message ("Parameter '%s' not found", name);
+}
+
+
+static void
+gfig_read_parameter_int (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gint *style_entry)
+{
+ gint n = 0;
+ gchar *ptr;
+ gchar *tmpstr;
+
+ *style_entry = 0;
+
+ while (n < nitems)
+ {
+ ptr = strchr (text[n], ':');
+ if (ptr)
+ {
+ tmpstr = g_strndup (text[n], ptr - text[n]);
+ ptr++;
+ if (!strcmp (tmpstr, name))
+ {
+ *style_entry = atoi (g_strchug (ptr));
+ g_free (tmpstr);
+ return;
+ }
+ g_free (tmpstr);
+ }
+ ++n;
+ }
+}
+
+static void
+gfig_read_parameter_double (gchar **text,
+ gint nitems,
+ const gchar *name,
+ gdouble *style_entry)
+{
+ gint n = 0;
+ gchar *ptr;
+ gchar *endptr;
+ gchar *tmpstr;
+
+ *style_entry = 0.;
+
+ while (n < nitems)
+ {
+ ptr = strchr (text[n], ':');
+ if (ptr)
+ {
+ tmpstr = g_strndup (text[n], ptr - text[n]);
+ ptr++;
+ if (!strcmp (tmpstr, name))
+ {
+ *style_entry = g_ascii_strtod (g_strchug (ptr), &endptr);
+ g_free (tmpstr);
+ return;
+ }
+ g_free (tmpstr);
+ }
+ ++n;
+ }
+}
+
+static void
+gfig_read_parameter_gimp_rgb (gchar **text,
+ gint nitems,
+ const gchar *name,
+ GimpRGB *style_entry)
+{
+ gint n = 0;
+ gchar *ptr;
+ gchar *tmpstr;
+ gchar *endptr;
+ gchar fmt_str[32];
+ gchar colorstr_r[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar colorstr_g[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar colorstr_b[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar colorstr_a[G_ASCII_DTOSTR_BUF_SIZE];
+
+ style_entry->r = style_entry->g = style_entry->b = style_entry->a = 0.;
+
+ snprintf (fmt_str, sizeof (fmt_str),
+ "%%%" G_GSIZE_FORMAT "s"
+ " %%%" G_GSIZE_FORMAT "s"
+ " %%%" G_GSIZE_FORMAT "s"
+ " %%%" G_GSIZE_FORMAT "s",
+ sizeof (colorstr_r) - 1, sizeof (colorstr_g) - 1,
+ sizeof (colorstr_b) - 1, sizeof (colorstr_a) - 1);
+
+ while (n < nitems)
+ {
+ ptr = strchr (text[n], ':');
+ if (ptr)
+ {
+ tmpstr = g_strndup (text[n], ptr - text[n]);
+ ptr++;
+ if (!strcmp (tmpstr, name))
+ {
+ sscanf (ptr, fmt_str,
+ colorstr_r, colorstr_g, colorstr_b, colorstr_a);
+ style_entry->r = g_ascii_strtod (colorstr_r, &endptr);
+ style_entry->g = g_ascii_strtod (colorstr_g, &endptr);
+ style_entry->b = g_ascii_strtod (colorstr_b, &endptr);
+ style_entry->a = g_ascii_strtod (colorstr_a, &endptr);
+ g_free (tmpstr);
+ return;
+ }
+ g_free (tmpstr);
+ }
+ ++n;
+ }
+}
+
+#define MAX_STYLE_TEXT_ENTRIES 100
+
+gboolean
+gfig_load_style (Style *style,
+ FILE *fp)
+{
+ gulong offset;
+ gchar load_buf2[MAX_LOAD_LINE];
+ gchar *style_text[MAX_STYLE_TEXT_ENTRIES];
+ gint nitems = 0;
+ gint value;
+ gint k;
+ gchar name[100];
+
+ offset = ftell (fp);
+
+ get_line (load_buf2, MAX_LOAD_LINE, fp, 0);
+ /* nuke final > and preserve spaces in name */
+ if (1 != sscanf (load_buf2, "<Style %99[^>]>", name))
+ {
+ /* no style data, copy default style and fail silently */
+ gfig_style_copy (style, &gfig_context->default_style, "default style");
+ fseek (fp, offset, SEEK_SET);
+ return TRUE;
+ }
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Loading style '%s' -- ", name);
+
+ style->name = g_strdup (name);
+
+ while (TRUE)
+ {
+ get_line (load_buf2, MAX_LOAD_LINE, fp, 0);
+ if (!strcmp (load_buf2, "</Style>") || feof (fp))
+ break;
+
+ style_text[nitems] = g_strdup (load_buf2);
+ nitems++;
+
+ if (nitems >= MAX_STYLE_TEXT_ENTRIES)
+ break;
+ }
+
+ if (feof (fp) || (nitems >= MAX_STYLE_TEXT_ENTRIES))
+ {
+ g_message ("Error reading style data");
+ return TRUE;
+ }
+
+ gfig_read_parameter_string (style_text, nitems, "BrushName",
+ &style->brush_name);
+
+ if (style->brush_name == NULL)
+ g_message ("Error loading style: got NULL for brush name.");
+
+ gfig_read_parameter_string (style_text, nitems, "Pattern", &style->pattern);
+ gfig_read_parameter_string (style_text, nitems, "Gradient", &style->gradient);
+
+ gfig_read_parameter_gimp_rgb (style_text, nitems, "Foreground",
+ &style->foreground);
+ gfig_read_parameter_gimp_rgb (style_text, nitems, "Background",
+ &style->background);
+
+ gfig_read_parameter_int (style_text, nitems, "FillType", &value);
+ style->fill_type = value;
+
+ gfig_read_parameter_int (style_text, nitems, "PaintType", &value);
+ style->paint_type = value;
+
+ gfig_read_parameter_double (style_text, nitems, "FillOpacity",
+ &style->fill_opacity);
+
+ for (k = 0; k < nitems; k++)
+ {
+ g_free (style_text[k]);
+ }
+
+ if (gfig_context->debug_styles)
+ g_printerr ("done\n");
+
+ return FALSE;
+}
+
+
+gboolean
+gfig_skip_style (Style *style,
+ FILE *fp)
+{
+ gulong offset;
+ gchar load_buf2[MAX_LOAD_LINE];
+
+ offset = ftell (fp);
+
+ get_line (load_buf2, MAX_LOAD_LINE, fp, 0);
+ if (strncmp (load_buf2, "<Style ", 7))
+ {
+ /* no style data */
+ fseek (fp, offset, SEEK_SET);
+ return TRUE;
+ }
+
+ while (TRUE)
+ {
+ get_line (load_buf2, MAX_LOAD_LINE, fp, 0);
+ if (!strcmp (load_buf2, "</Style>") || feof (fp))
+ break;
+ }
+
+ if (feof (fp))
+ {
+ g_message ("Error trying to skip style data");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * FIXME: need to make this load a list of styles if there are more than one.
+ */
+gboolean
+gfig_load_styles (GFigObj *gfig,
+ FILE *fp)
+{
+ if (gfig_context->debug_styles)
+ g_printerr ("Loading global styles -- ");
+
+ /* currently we only have the default style */
+ gfig_load_style (&gfig_context->default_style, fp);
+
+ if (gfig_context->debug_styles)
+ g_printerr ("done\n");
+
+ return FALSE;
+}
+
+void
+gfig_save_style (Style *style,
+ GString *string)
+{
+ gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_r[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_g[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_b[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_a[G_ASCII_DTOSTR_BUF_SIZE];
+ gint blen = G_ASCII_DTOSTR_BUF_SIZE;
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Saving style %s, brush name '%s'\n", style->name, style->brush_name);
+
+ g_string_append_printf (string, "<Style %s>\n", style->name);
+ g_string_append_printf (string, "BrushName: %s\n", style->brush_name);
+ if (!style->brush_name)
+ g_message ("Error saving style %s: saving NULL for brush name", style->name);
+
+ g_string_append_printf (string, "PaintType: %d\n", style->paint_type);
+
+ g_string_append_printf (string, "FillType: %d\n", style->fill_type);
+
+ g_string_append_printf (string, "FillOpacity: %s\n",
+ g_ascii_dtostr (buffer, blen, style->fill_opacity));
+
+ g_string_append_printf (string, "Pattern: %s\n", style->pattern);
+
+ g_string_append_printf (string, "Gradient: %s\n", style->gradient);
+
+ g_string_append_printf (string, "Foreground: %s %s %s %s\n",
+ g_ascii_dtostr (buffer_r, blen, style->foreground.r),
+ g_ascii_dtostr (buffer_g, blen, style->foreground.g),
+ g_ascii_dtostr (buffer_b, blen, style->foreground.b),
+ g_ascii_dtostr (buffer_a, blen, style->foreground.a));
+
+ g_string_append_printf (string, "Background: %s %s %s %s\n",
+ g_ascii_dtostr (buffer_r, blen, style->background.r),
+ g_ascii_dtostr (buffer_g, blen, style->background.g),
+ g_ascii_dtostr (buffer_b, blen, style->background.b),
+ g_ascii_dtostr (buffer_a, blen, style->background.a));
+
+ g_string_append_printf (string, "</Style>\n");
+}
+
+void
+gfig_style_save_as_attributes (Style *style,
+ GString *string)
+{
+ gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_r[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_g[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_b[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer_a[G_ASCII_DTOSTR_BUF_SIZE];
+ gint blen = G_ASCII_DTOSTR_BUF_SIZE;
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Saving style %s as attributes\n", style->name);
+ g_string_append_printf (string, "BrushName=\"%s\" ", style->brush_name);
+
+ g_string_append_printf (string, "Foreground=\"%s %s %s %s\" ",
+ g_ascii_dtostr (buffer_r, blen, style->foreground.r),
+ g_ascii_dtostr (buffer_g, blen, style->foreground.g),
+ g_ascii_dtostr (buffer_b, blen, style->foreground.b),
+ g_ascii_dtostr (buffer_a, blen, style->foreground.a));
+
+ g_string_append_printf (string, "Background=\"%s %s %s %s\" ",
+ g_ascii_dtostr (buffer_r, blen, style->background.r),
+ g_ascii_dtostr (buffer_g, blen, style->background.g),
+ g_ascii_dtostr (buffer_b, blen, style->background.b),
+ g_ascii_dtostr (buffer_a, blen, style->background.a));
+
+ g_string_append_printf (string, "FillType=%d ", style->fill_type);
+
+ g_string_append_printf (string, "PaintType=%d ", style->paint_type);
+
+ g_string_append_printf (string, "FillOpacity=%s ",
+ g_ascii_dtostr (buffer, blen, style->fill_opacity));
+
+
+}
+
+void
+gfig_save_styles (GString *string)
+{
+ if (gfig_context->debug_styles)
+ g_printerr ("Saving global styles.\n");
+
+ gfig_save_style (&gfig_context->default_style, string);
+}
+
+/*
+ * set_foreground_callback() is the callback for the Foreground color select
+ * widget. It reads the color from the widget, and applies this color to the
+ * current style. It then produces a repaint (which will be suppressed if
+ * gfig_context->enable_repaint is FALSE).
+ */
+void
+set_foreground_callback (GimpColorButton *button,
+ gpointer data)
+{
+ GimpRGB color2;
+ Style *current_style;
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Setting foreground color from color selector\n");
+
+ current_style = gfig_context_get_current_style ();
+
+ gimp_color_button_get_color (button, &color2);
+
+ gimp_rgba_set (&current_style->foreground,
+ color2.r, color2.g, color2.b, color2.a);
+
+ gfig_paint_callback ();
+}
+
+void
+set_background_callback (GimpColorButton *button,
+ gpointer data)
+{
+ GimpRGB color2;
+ Style *current_style;
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Setting background color from color selector\n");
+
+ current_style = gfig_context_get_current_style ();
+
+ gimp_color_button_get_color (button, &color2);
+ gimp_rgba_set (&current_style->background,
+ color2.r, color2.g, color2.b, color2.a);
+
+ gfig_paint_callback ();
+}
+
+void
+set_paint_type_callback (GtkToggleButton *toggle,
+ gpointer data)
+{
+ gboolean paint_type;
+ Style *current_style;
+
+ current_style = gfig_context_get_current_style ();
+ paint_type = gtk_toggle_button_get_active (toggle);
+ current_style->paint_type = paint_type;
+ gfig_paint_callback ();
+
+ gtk_widget_set_sensitive (GTK_WIDGET (data), paint_type);
+}
+
+/*
+ * gfig_brush_changed_callback() is the callback for the brush
+ * selector widget. It reads the brush name from the widget, and
+ * applies this to the current style, as well as the gfig_context->bdesc
+ * values. It then produces a repaint (which will be suppressed if
+ * gfig_context->enable_repaint is FALSE).
+ */
+void
+gfig_brush_changed_callback (GimpBrushSelectButton *button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ Style *current_style;
+
+ current_style = gfig_context_get_current_style ();
+ current_style->brush_name = g_strdup (brush_name);
+
+ /* this will soon be unneeded. How soon? */
+ gfig_context->bdesc.name = g_strdup (brush_name);
+ gfig_context->bdesc.width = width;
+ gfig_context->bdesc.height = height;
+ gimp_context_set_brush (brush_name);
+ gimp_context_set_brush_default_size ();
+
+ gfig_paint_callback ();
+}
+
+void
+gfig_pattern_changed_callback (GimpPatternSelectButton *button,
+ const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bpp,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ Style *current_style;
+
+ current_style = gfig_context_get_current_style ();
+ current_style->pattern = g_strdup (pattern_name);
+
+ gfig_paint_callback ();
+}
+
+void
+gfig_gradient_changed_callback (GimpGradientSelectButton *button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ gpointer user_data)
+{
+ Style *current_style;
+
+ current_style = gfig_context_get_current_style ();
+ current_style->gradient = g_strdup (gradient_name);
+
+ gfig_paint_callback ();
+}
+
+void
+gfig_rgba_copy (GimpRGB *color1,
+ GimpRGB *color2)
+{
+ color1->r = color2->r;
+ color1->g = color2->g;
+ color1->b = color2->b;
+ color1->a = color2->a;
+}
+
+void
+gfig_style_copy (Style *style1,
+ Style *style0,
+ const gchar *name)
+{
+ if (name)
+ style1->name = g_strdup (name);
+ else
+ g_message ("Error: name is NULL in gfig_style_copy.");
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Copying style %s as style %s\n", style0->name, name);
+
+ gfig_rgba_copy (&style1->foreground, &style0->foreground);
+ gfig_rgba_copy (&style1->background, &style0->background);
+
+ if (!style0->brush_name)
+ g_message ("Error copying style %s: brush name is NULL.", style0->name);
+
+ style1->brush_name = g_strdup (style0->brush_name);
+ style1->gradient = g_strdup (style0->gradient);
+ style1->pattern = g_strdup (style0->pattern);
+ style1->fill_type = style0->fill_type;
+ style1->fill_opacity = style0->fill_opacity;
+ style1->paint_type = style0->paint_type;
+}
+
+/*
+ * gfig_style_apply() applies the settings from the specified style to
+ * the GIMP core. It does not change any widgets, and does not cause
+ * a repaint.
+ */
+void
+gfig_style_apply (Style *style)
+{
+ if (gfig_context->debug_styles)
+ g_printerr ("Applying style '%s' -- ", style->name);
+
+ gimp_context_set_foreground (&style->foreground);
+
+ gimp_context_set_background (&style->background);
+
+ if (! gimp_context_set_brush (style->brush_name))
+ g_message ("Style apply: Failed to set brush to '%s' in style '%s'",
+ style->brush_name, style->name);
+
+ gimp_context_set_brush_default_size ();
+
+ gimp_context_set_pattern (style->pattern);
+
+ gimp_context_set_gradient (style->gradient);
+
+ if (gfig_context->debug_styles)
+ g_printerr ("done.\n");
+}
+
+/*
+ * gfig_read_gimp_style() reads the style settings from the Gimp core,
+ * and applies them to the specified style, giving that style the
+ * specified name. This is mainly useful as a way of initializing
+ * a style. The function does not cause a repaint.
+ */
+void
+gfig_read_gimp_style (Style *style,
+ const gchar *name)
+{
+ gint dummy;
+
+ if (!name)
+ g_message ("Error: name is NULL in gfig_read_gimp_style.");
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Reading Gimp settings as style %s\n", name);
+ style->name = g_strdup (name);
+
+ gimp_context_get_foreground (&style->foreground);
+ gimp_context_get_background (&style->background);
+
+ style->brush_name = gimp_context_get_brush ();
+ gimp_brush_get_info (style->brush_name,
+ &style->brush_width, &style->brush_height,
+ &dummy, &dummy);
+ gimp_brush_get_spacing (style->brush_name, &style->brush_spacing);
+
+ style->gradient = gimp_context_get_gradient ();
+ style->pattern = gimp_context_get_pattern ();
+
+ style->fill_opacity = 100.;
+
+ gfig_context->bdesc.name = style->brush_name;
+ gfig_context->bdesc.width = style->brush_width;
+ gfig_context->bdesc.height = style->brush_height;
+}
+
+/*
+ * gfig_style_set_content_from_style() sets all of the style control widgets
+ * to values from the specified style. This in turn sets the Gimp core's
+ * values to the same things. Repainting is suppressed while this happens,
+ * so calling this function will not produce a repaint.
+ *
+ */
+void
+gfig_style_set_context_from_style (Style *style)
+{
+ gboolean enable_repaint;
+
+ if (gfig_context->debug_styles)
+ g_printerr ("Setting context from style '%s' -- ", style->name);
+
+ enable_repaint = gfig_context->enable_repaint;
+ gfig_context->enable_repaint = FALSE;
+
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->fg_color_button),
+ &style->foreground);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (gfig_context->bg_color_button),
+ &style->background);
+ if (! gimp_context_set_brush (style->brush_name))
+ g_message ("Style from context: Failed to set brush to '%s'",
+ style->brush_name);
+
+ gimp_context_set_brush_default_size ();
+
+ gimp_brush_select_button_set_brush (GIMP_BRUSH_SELECT_BUTTON (gfig_context->brush_select),
+ style->brush_name, -1.0, -1, -1); /* FIXME */
+
+ gimp_pattern_select_button_set_pattern (GIMP_PATTERN_SELECT_BUTTON (gfig_context->pattern_select),
+ style->pattern);
+
+ gimp_gradient_select_button_set_gradient (GIMP_GRADIENT_SELECT_BUTTON (gfig_context->gradient_select),
+ style->gradient);
+
+ gfig_context->bdesc.name = style->brush_name;
+ if (gfig_context->debug_styles)
+ g_printerr ("done.\n");
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gfig_context->fillstyle_combo),
+ (gint) style->fill_type);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gfig_context->paint_type_toggle),
+ style->paint_type);
+ gfig_context->enable_repaint = enable_repaint;
+}
+
+/*
+ * gfig_style_set_style_from_context() sets the values in the specified
+ * style to those that appear in the style control widgets f
+ */
+void
+gfig_style_set_style_from_context (Style *style)
+{
+ Style *current_style;
+ GimpRGB color;
+ gint value;
+
+ style->name = "object";
+ current_style = gfig_context_get_current_style ();
+
+ gimp_color_button_get_color (GIMP_COLOR_BUTTON (gfig_context->fg_color_button),
+ &color);
+ if (gfig_context->debug_styles)
+ g_printerr ("Setting foreground color to %lg %lg %lg\n",
+ color.r, color.g, color.b);
+
+ gfig_rgba_copy (&style->foreground, &color);
+ gimp_color_button_get_color (GIMP_COLOR_BUTTON (gfig_context->bg_color_button),
+ &color);
+ gfig_rgba_copy (&style->background, &color);
+
+ style->brush_name = current_style->brush_name;
+
+ if (!style->pattern || strcmp (style->pattern, current_style->pattern))
+ {
+ style->pattern = g_strdup (current_style->pattern); /* why strduping? */
+ }
+
+ style->gradient = current_style->gradient;
+
+ if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (gfig_context->fillstyle_combo), &value))
+ style->fill_type = value;
+
+ /* FIXME when there is an opacity control widget to read */
+ style->fill_opacity = 100.;
+
+ style->paint_type = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gfig_context->paint_type_toggle));
+}
+
+void
+mygimp_brush_info (gint *width,
+ gint *height)
+{
+ gchar *name = gimp_context_get_brush ();
+ gint dummy;
+
+ if (name && gimp_brush_get_info (name, width, height, &dummy, &dummy))
+ {
+ *width = MAX (*width, 32);
+ *height = MAX (*height, 32);
+ }
+ else
+ {
+ g_message ("Failed to get brush info");
+ *width = *height = 48;
+ }
+
+ g_free (name);
+}
+
+Style *
+gfig_context_get_current_style (void)
+{
+ if (gfig_context->selected_obj)
+ return &gfig_context->selected_obj->style;
+ else
+ return &gfig_context->default_style;
+}
diff --git a/plug-ins/gfig/gfig-style.h b/plug-ins/gfig/gfig-style.h
new file mode 100644
index 0000000..3d18c05
--- /dev/null
+++ b/plug-ins/gfig/gfig-style.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_STYLE_H__
+#define __GFIG_STYLE_H__
+
+#include <libgimp/gimpui.h>
+
+struct _Style
+{
+ gchar *name;
+ gchar *brush_name;
+ gint brush_width;
+ gint brush_height;
+ gint brush_spacing;
+ BrushType brush_type;
+ gdouble brushfade;
+ gdouble brushgradient;
+ gdouble airbrushpressure;
+ FillType fill_type;
+ gdouble fill_opacity;
+ gchar *pattern;
+ gchar *gradient;
+ PaintType paint_type;
+ GimpRGB foreground;
+ GimpRGB background;
+ gboolean reverselines;
+ gint ref_count;
+};
+
+gboolean gfig_load_style (Style *style,
+ FILE *fp);
+
+gboolean gfig_skip_style (Style *style,
+ FILE *fp);
+
+gboolean gfig_load_styles (GFigObj *gfig,
+ FILE *fp);
+
+void gfig_save_style (Style *style,
+ GString *string);
+
+void gfig_style_save_as_attributes (Style *style,
+ GString *string);
+
+void gfig_save_styles (GString *string);
+
+void set_foreground_callback (GimpColorButton *button,
+ gpointer data);
+
+void set_background_callback (GimpColorButton *button,
+ gpointer data);
+
+void set_paint_type_callback (GtkToggleButton *toggle,
+ gpointer data);
+
+void gfig_brush_changed_callback (GimpBrushSelectButton *button,
+ const gchar *brush_name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+void gfig_pattern_changed_callback (GimpPatternSelectButton *button,
+ const gchar *pattern_name,
+ gint width,
+ gint height,
+ gint bpp,
+ const guchar *mask_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+void gfig_gradient_changed_callback (GimpGradientSelectButton *button,
+ const gchar *gradient_name,
+ gint width,
+ const gdouble *grad_data,
+ gboolean dialog_closing,
+ gpointer user_data);
+
+void gfig_rgba_copy (GimpRGB *color1,
+ GimpRGB *color2);
+
+void gfig_style_copy (Style *style1,
+ Style *style0,
+ const gchar *name);
+
+void gfig_style_apply (Style *style);
+
+void gfig_read_gimp_style (Style *style,
+ const gchar *name);
+
+void gfig_style_set_context_from_style (Style *style);
+
+void gfig_style_set_style_from_context (Style *style);
+
+void mygimp_brush_info (gint *width,
+ gint *height);
+
+Style *gfig_context_get_current_style (void);
+
+#endif /* __GFIG_STYLE_H__ */
diff --git a/plug-ins/gfig/gfig-types.h b/plug-ins/gfig/gfig-types.h
new file mode 100644
index 0000000..5cb3168
--- /dev/null
+++ b/plug-ins/gfig/gfig-types.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_TYPES_H__
+#define __GFIG_TYPES_H__
+
+typedef enum
+{
+ RECT_GRID = 0,
+ POLAR_GRID,
+ ISO_GRID
+} GridType;
+
+typedef enum
+{
+ ADD = 0,
+ SUBTRACT,
+ REPLACE,
+ INTERSECT
+} SelectionType;
+
+typedef enum
+{
+ ARC_SEGMENT = 0,
+ ARC_SECTOR
+} ArcType;
+
+typedef enum
+{
+ FILL_NONE = 0,
+ FILL_COLOR,
+ FILL_PATTERN,
+ FILL_GRADIENT,
+ FILL_VERTICAL,
+ FILL_HORIZONTAL
+} FillType;
+
+typedef enum
+{
+ ORIGINAL_LAYER = 0,
+ SINGLE_LAYER,
+ MULTI_LAYER
+} DrawonLayers;
+
+typedef enum
+{
+ LAYER_TRANS_BG = 0,
+ LAYER_BG_BG,
+ LAYER_FG_BG,
+ LAYER_WHITE_BG,
+ LAYER_COPY_BG
+} LayersBGType;
+
+typedef enum
+{
+ PAINT_NONE = 0,
+ PAINT_BRUSH_TYPE = 1
+} PaintType;
+
+typedef enum
+{
+ BRUSH_BRUSH_TYPE = 0,
+ BRUSH_PENCIL_TYPE,
+ BRUSH_AIRBRUSH_TYPE,
+ BRUSH_PATTERN_TYPE
+} BrushType;
+
+typedef enum
+{
+ OBJ_TYPE_NONE = 0,
+ LINE,
+ RECTANGLE,
+ CIRCLE,
+ ELLIPSE,
+ ARC,
+ POLY,
+ STAR,
+ SPIRAL,
+ BEZIER,
+ NUM_OBJ_TYPES,
+ MOVE_OBJ,
+ MOVE_POINT,
+ COPY_OBJ,
+ MOVE_COPY_OBJ,
+ DEL_OBJ,
+ SELECT_OBJ,
+ NULL_OPER
+} DobjType;
+
+typedef struct _GFigObj GFigObj;
+typedef struct _GfigObject GfigObject;
+typedef struct _Style Style;
+
+#endif /* __GFIG_TYPES_H__ */
diff --git a/plug-ins/gfig/gfig.c b/plug-ins/gfig/gfig.c
new file mode 100644
index 0000000..118930f
--- /dev/null
+++ b/plug-ins/gfig/gfig.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gfig.h"
+#include "gfig-style.h"
+#include "gfig-dialog.h"
+#include "gfig-arc.h"
+#include "gfig-bezier.h"
+#include "gfig-circle.h"
+#include "gfig-dobject.h"
+#include "gfig-ellipse.h"
+#include "gfig-grid.h"
+#include "gfig-line.h"
+#include "gfig-poly.h"
+#include "gfig-preview.h"
+#include "gfig-spiral.h"
+#include "gfig-star.h"
+#include "gfig-stock.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define GFIG_HEADER "GFIG Version 0.2\n"
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+gint line_no;
+
+gint obj_show_single = -1; /* -1 all >= 0 object number */
+
+/* Structures etc for the objects */
+/* Points used to draw the object */
+
+GfigObject *obj_creating; /* Object we are creating */
+GfigObject *tmp_line; /* Needed when drawing lines */
+
+gboolean need_to_scale;
+
+static gint load_options (GFigObj *gfig,
+ FILE *fp);
+/* globals */
+
+GfigObjectClass dobj_class[10];
+GFigContext *gfig_context;
+GtkWidget *top_level_dlg;
+GList *gfig_list;
+gdouble org_scale_x_factor, org_scale_y_factor;
+
+
+/* Stuff for the preview bit */
+static gint sel_x, sel_y;
+static gint sel_width, sel_height;
+gint preview_width, preview_height;
+gdouble scale_x_factor, scale_y_factor;
+GdkPixbuf *back_pixbuf = NULL;
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "dummy", "dummy" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create geometric shapes"),
+ "Draw Vector Graphics and paint them onto your images. "
+ "Gfig allows you to draw many types of objects "
+ "including Lines, Circles, Ellipses, Curves, Polygons, "
+ "pointed stars, Bezier curves, and Spirals. "
+ "Objects can be painted using Brushes or other tools"
+ "or filled using colors or patterns. "
+ "Gfig objects can also be used to create selections. ",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1997",
+ N_("_Gfig..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Render");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 drawable_id;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint pwidth, pheight;
+
+ INIT_I18N ();
+
+ gfig_context = g_new0 (GFigContext, 1);
+ gfig_context->show_background = TRUE;
+ gfig_context->selected_obj = NULL;
+
+ drawable_id = param[2].data.d_drawable;
+
+ run_mode = param[0].data.d_int32;
+
+ gfig_context->image_id = param[1].data.d_image;
+ gfig_context->drawable_id = drawable_id;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ gimp_image_undo_group_start (gfig_context->image_id);
+
+ gimp_context_push ();
+
+ /* TMP Hack - clear any selections */
+ if (! gimp_selection_is_empty (gfig_context->image_id))
+ gimp_selection_none (gfig_context->image_id);
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &sel_x, &sel_y,
+ &sel_width, &sel_height))
+ {
+ gimp_context_pop ();
+
+ gimp_image_undo_group_end (gfig_context->image_id);
+ return;
+ }
+
+ /* Calculate preview size */
+
+ if (sel_width > sel_height)
+ {
+ pwidth = MIN (sel_width, PREVIEW_SIZE);
+ pheight = sel_height * pwidth / sel_width;
+ }
+ else
+ {
+ pheight = MIN (sel_height, PREVIEW_SIZE);
+ pwidth = sel_width * pheight / sel_height;
+ }
+
+
+ preview_width = MAX (pwidth, 2); /* Min size is 2 */
+ preview_height = MAX (pheight, 2);
+
+ org_scale_x_factor = scale_x_factor =
+ (gdouble) sel_width / (gdouble) preview_width;
+ org_scale_y_factor = scale_y_factor =
+ (gdouble) sel_height / (gdouble) preview_height;
+
+ /* initialize */
+ gfig_init_object_classes ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /*gimp_get_data (PLUG_IN_PROC, &selvals);*/
+ if (! gfig_dialog ())
+ {
+ gimp_image_undo_group_end (gfig_context->image_id);
+
+ return;
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ gimp_context_pop ();
+
+ gimp_image_undo_group_end (gfig_context->image_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+ else
+#if 0
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &selvals, sizeof (SelectItVals));
+ else
+#endif /* 0 */
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/*
+ Translate SPACE to "\\040", etc.
+ Taken from gflare plugin
+ */
+void
+gfig_name_encode (gchar *dest,
+ gchar *src)
+{
+ gint cnt = MAX_LOAD_LINE - 1;
+
+ while (*src && cnt--)
+ {
+ if (g_ascii_iscntrl (*src) || g_ascii_isspace (*src) || *src == '\\')
+ {
+ sprintf (dest, "\\%03o", *src++);
+ dest += 4;
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+}
+
+/*
+ Translate "\\040" to SPACE, etc.
+ */
+void
+gfig_name_decode (gchar *dest,
+ const gchar *src)
+{
+ gint cnt = MAX_LOAD_LINE - 1;
+ guint tmp;
+
+ while (*src && cnt--)
+ {
+ if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
+ {
+ sscanf (src+1, "%3o", &tmp);
+ *dest++ = tmp;
+ src += 4;
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+}
+
+
+/*
+ * Load all gfig, which are founded in gfig-path-list, into gfig_list.
+ * gfig-path-list must be initialized first. (plug_in_parse_gfig_path ())
+ * based on code from Gflare.
+ */
+
+gint
+gfig_list_pos (GFigObj *gfig)
+{
+ GFigObj *g;
+ gint n;
+ GList *tmp;
+
+ n = 0;
+
+ for (tmp = gfig_list; tmp; tmp = g_list_next (tmp))
+ {
+ g = tmp->data;
+
+ if (strcmp (gfig->draw_name, g->draw_name) <= 0)
+ break;
+
+ n++;
+ }
+ return n;
+}
+
+/*
+ * Insert gfigs in alphabetical order
+ */
+
+gint
+gfig_list_insert (GFigObj *gfig)
+{
+ gint n;
+
+ n = gfig_list_pos (gfig);
+
+ gfig_list = g_list_insert (gfig_list, gfig, n);
+
+ return n;
+}
+
+void
+gfig_free (GFigObj *gfig)
+{
+ g_assert (gfig != NULL);
+
+ free_all_objs (gfig->obj_list);
+
+ g_free (gfig->name);
+ g_free (gfig->filename);
+ g_free (gfig->draw_name);
+
+ g_free (gfig);
+}
+
+GFigObj *
+gfig_new (void)
+{
+ return g_new0 (GFigObj, 1);
+}
+
+static void
+gfig_load_objs (GFigObj *gfig,
+ gint load_count,
+ FILE *fp)
+{
+ GfigObject *obj;
+ gchar load_buf[MAX_LOAD_LINE];
+ glong offset;
+ glong offset2;
+ Style style;
+
+ while (load_count-- > 0)
+ {
+ obj = NULL;
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+
+ /* kludge */
+ offset = ftell (fp);
+ gfig_skip_style (&style, fp);
+
+ obj = d_load_object (load_buf, fp);
+
+ if (obj)
+ {
+ add_to_all_obj (gfig, obj);
+ offset2 = ftell (fp);
+ fseek (fp, offset, SEEK_SET);
+ gfig_load_style (&obj->style, fp);
+ fseek (fp, offset2, SEEK_SET);
+ }
+ else
+ {
+ g_message ("Failed to load object, load count = %d", load_count);
+ }
+ }
+}
+
+GFigObj *
+gfig_load (const gchar *filename,
+ const gchar *name)
+{
+ GFigObj *gfig;
+ FILE *fp;
+ gchar load_buf[MAX_LOAD_LINE];
+ gchar str_buf[MAX_LOAD_LINE];
+ gint chk_count;
+ gint load_count = 0;
+ gdouble version;
+ gchar magic1[20];
+ gchar magic2[20];
+
+ g_assert (filename != NULL);
+
+#ifdef DEBUG
+ printf ("Loading %s (%s)\n", filename, name);
+#endif /* DEBUG */
+
+ fp = g_fopen (filename, "rb");
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return NULL;
+ }
+
+ gfig = gfig_new ();
+
+ gfig->name = g_strdup (name);
+ gfig->filename = g_strdup (filename);
+
+
+ /* HEADER
+ * draw_name
+ * version
+ * obj_list
+ */
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 1);
+
+ sscanf (load_buf, "%10s %10s %lf", magic1, magic2, &version);
+
+ if (strcmp (magic1, "GFIG") || strcmp (magic2, "Version"))
+ {
+ g_message ("File '%s' is not a gfig file",
+ gimp_filename_to_utf8 (gfig->filename));
+ gfig_free (gfig);
+ fclose (fp);
+ return NULL;
+ }
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+ sscanf (load_buf, "Name: %100s", str_buf);
+ gfig_name_decode (load_buf, str_buf);
+ gfig->draw_name = g_strdup (load_buf);
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+ if (strncmp (load_buf, "Version: ", 9) == 0)
+ gfig->version = g_ascii_strtod (load_buf + 9, NULL);
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+ sscanf (load_buf, "ObjCount: %d", &load_count);
+
+ if (load_options (gfig, fp))
+ {
+ g_message ("File '%s' corrupt file - Line %d Option section incorrect",
+ gimp_filename_to_utf8 (filename), line_no);
+ gfig_free (gfig);
+ fclose (fp);
+ return NULL;
+ }
+
+ if (gfig_load_styles (gfig, fp))
+ {
+ g_message ("File '%s' corrupt file - Line %d Option section incorrect",
+ gimp_filename_to_utf8 (filename), line_no);
+ gfig_free (gfig);
+ fclose (fp);
+ return NULL;
+ }
+
+
+
+ gfig_load_objs (gfig, load_count, fp);
+
+ /* Check count ? */
+
+ chk_count = g_list_length (gfig->obj_list);
+
+ if (chk_count != load_count)
+ {
+ g_message ("File '%s' corrupt file - Line %d Object count to small",
+ gimp_filename_to_utf8 (filename), line_no);
+ gfig_free (gfig);
+ fclose (fp);
+ return NULL;
+ }
+
+ fclose (fp);
+
+ if (!gfig_context->current_obj)
+ gfig_context->current_obj = gfig;
+
+ gfig->obj_status = GFIG_OK;
+
+ return gfig;
+}
+
+void
+save_options (GString *string)
+{
+ /* Save options */
+ g_string_append_printf (string, "<OPTIONS>\n");
+ g_string_append_printf (string, "GridSpacing: %d\n",
+ selvals.opts.gridspacing);
+ if (selvals.opts.gridtype == RECT_GRID)
+ {
+ g_string_append_printf (string, "GridType: RECT_GRID\n");
+ }
+ else if (selvals.opts.gridtype == POLAR_GRID)
+ {
+ g_string_append_printf (string, "GridType: POLAR_GRID\n");
+ }
+ else if (selvals.opts.gridtype == ISO_GRID)
+ {
+ g_string_append_printf (string, "GridType: ISO_GRID\n");
+ }
+ else
+ {
+ /* default to RECT_GRID */
+ g_string_append_printf (string, "GridType: RECT_GRID\n");
+ }
+
+ g_string_append_printf (string, "DrawGrid: %s\n",
+ (selvals.opts.drawgrid) ? "TRUE" : "FALSE");
+ g_string_append_printf (string, "Snap2Grid: %s\n",
+ (selvals.opts.snap2grid) ? "TRUE" : "FALSE");
+ g_string_append_printf (string, "LockOnGrid: %s\n",
+ (selvals.opts.lockongrid) ? "TRUE" : "FALSE");
+ g_string_append_printf (string, "ShowControl: %s\n",
+ (selvals.opts.showcontrol) ? "TRUE" : "FALSE");
+ g_string_append_printf (string, "</OPTIONS>\n");
+}
+
+static void
+gfig_save_obj_start (GfigObject *obj,
+ GString *string)
+{
+ g_string_append_printf (string, "<%s ", obj->class->name);
+ gfig_style_save_as_attributes (&obj->style, string);
+ g_string_append_printf (string, ">\n");
+}
+
+static void
+gfig_save_obj_end (GfigObject *obj,
+ GString *string)
+{
+ g_string_append_printf (string, "</%s>\n",obj->class->name);
+}
+
+static gboolean
+load_bool (gchar *opt_buf,
+ gint *toset)
+{
+ if (!strcmp (opt_buf, "TRUE"))
+ *toset = 1;
+ else if (!strcmp (opt_buf, "FALSE"))
+ *toset = 0;
+ else
+ return TRUE;
+
+ return FALSE;
+}
+
+static gint
+load_options (GFigObj *gfig,
+ FILE *fp)
+{
+ gchar load_buf[MAX_LOAD_LINE];
+ gchar str_buf[MAX_LOAD_LINE];
+ gchar opt_buf[MAX_LOAD_LINE];
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+
+#ifdef DEBUG
+ printf ("load '%s'\n", load_buf);
+#endif /* DEBUG */
+
+ if (strcmp (load_buf, "<OPTIONS>"))
+ return (-1);
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+
+#ifdef DEBUG
+ printf ("opt line '%s'\n", load_buf);
+#endif /* DEBUG */
+
+ while (strcmp (load_buf, "</OPTIONS>"))
+ {
+ /* Get option name */
+#ifdef DEBUG
+ printf ("num = %d\n", sscanf (load_buf, "%255s %255s", str_buf, opt_buf));
+
+ printf ("option %s val %s\n", str_buf, opt_buf);
+#else
+ sscanf (load_buf, "%255s %255s", str_buf, opt_buf);
+#endif /* DEBUG */
+
+ if (!strcmp (str_buf, "GridSpacing:"))
+ {
+ /* Value is decimal */
+ int sp = 0;
+ sp = atoi (opt_buf);
+ if (sp <= 0)
+ return (-1);
+ gfig->opts.gridspacing = sp;
+ }
+ else if (!strcmp (str_buf, "DrawGrid:"))
+ {
+ /* Value is bool */
+ if (load_bool (opt_buf, &gfig->opts.drawgrid))
+ return (-1);
+ }
+ else if (!strcmp (str_buf, "Snap2Grid:"))
+ {
+ /* Value is bool */
+ if (load_bool (opt_buf, &gfig->opts.snap2grid))
+ return (-1);
+ }
+ else if (!strcmp (str_buf, "LockOnGrid:"))
+ {
+ /* Value is bool */
+ if (load_bool (opt_buf, &gfig->opts.lockongrid))
+ return (-1);
+ }
+ else if (!strcmp (str_buf, "ShowControl:"))
+ {
+ /* Value is bool */
+ if (load_bool (opt_buf, &gfig->opts.showcontrol))
+ return (-1);
+ }
+ else if (!strcmp (str_buf, "GridType:"))
+ {
+ /* Value is string */
+ if (!strcmp (opt_buf, "RECT_GRID"))
+ gfig->opts.gridtype = RECT_GRID;
+ else if (!strcmp (opt_buf, "POLAR_GRID"))
+ gfig->opts.gridtype = POLAR_GRID;
+ else if (!strcmp (opt_buf, "ISO_GRID"))
+ gfig->opts.gridtype = ISO_GRID;
+ else
+ return (-1);
+ }
+
+ get_line (load_buf, MAX_LOAD_LINE, fp, 0);
+
+#ifdef DEBUG
+ printf ("opt line '%s'\n", load_buf);
+#endif /* DEBUG */
+ }
+ return (0);
+}
+
+GString *
+gfig_save_as_string (void)
+{
+ GList *objs;
+ gint count;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar conv_buf[MAX_LOAD_LINE * 3 + 1];
+ GString *string;
+
+ string = g_string_new (GFIG_HEADER);
+
+ gfig_name_encode (conv_buf, gfig_context->current_obj->draw_name);
+ g_string_append_printf (string, "Name: %s\n", conv_buf);
+ g_string_append_printf (string, "Version: %s\n",
+ g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
+ gfig_context->current_obj->version));
+ objs = gfig_context->current_obj->obj_list;
+
+ count = g_list_length (objs);
+
+ g_string_append_printf (string, "ObjCount: %d\n", count);
+
+ save_options (string);
+
+ gfig_save_styles (string);
+
+ for (objs = gfig_context->current_obj->obj_list;
+ objs;
+ objs = g_list_next (objs))
+ {
+ GfigObject *object = objs->data;
+
+ gfig_save_obj_start (object, string);
+
+ gfig_save_style (&object->style, string);
+
+ if (object->points)
+ d_save_object (object, string);
+
+ gfig_save_obj_end (object, string);
+ }
+
+ return string;
+}
+
+
+gboolean
+gfig_save_as_parasite (void)
+{
+ GimpParasite *parasite;
+ GString *string;
+
+ string = gfig_save_as_string ();
+
+ parasite = gimp_parasite_new ("gfig",
+ GIMP_PARASITE_PERSISTENT |
+ GIMP_PARASITE_UNDOABLE,
+ string->len, string->str);
+
+ g_string_free (string, TRUE);
+
+ if (!gimp_item_attach_parasite (gfig_context->drawable_id, parasite))
+ {
+ g_message (_("Error trying to save figure as a parasite: "
+ "can't attach parasite to drawable."));
+ gimp_parasite_free (parasite);
+ return FALSE;
+ }
+
+ gimp_parasite_free (parasite);
+ return TRUE;
+}
+
+GFigObj *
+gfig_load_from_parasite (void)
+{
+ FILE *fp;
+ gchar *fname;
+ GimpParasite *parasite;
+ GFigObj *gfig;
+
+ parasite = gimp_item_get_parasite (gfig_context->drawable_id, "gfig");
+ if (! parasite)
+ return NULL;
+
+ fname = gimp_temp_name ("gfigtmp");
+
+ fp = g_fopen (fname, "wb");
+ if (!fp)
+ {
+ g_message (_("Error trying to open temporary file '%s' "
+ "for parasite loading: %s"),
+ gimp_filename_to_utf8 (fname), g_strerror (errno));
+ return NULL;
+ }
+
+ fwrite (gimp_parasite_data (parasite),
+ sizeof (guchar),
+ gimp_parasite_data_size (parasite),
+ fp);
+ fclose (fp);
+
+ gimp_parasite_free (parasite);
+
+ gfig = gfig_load (fname, "(none)");
+
+ g_unlink (fname);
+
+ g_free (fname);
+
+ return gfig;
+}
+
+void
+gfig_save_callbk (void)
+{
+ FILE *fp;
+ gchar *savename;
+ GString *string;
+
+ savename = gfig_context->current_obj->filename;
+
+ fp = g_fopen (savename, "w+b");
+
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (savename), g_strerror (errno));
+ return;
+ }
+
+ string = gfig_save_as_string ();
+
+ fwrite (string->str, string->len, 1, fp);
+
+ if (ferror (fp))
+ g_message ("Failed to write file.");
+ else
+ gfig_context->current_obj->obj_status &= ~(GFIG_MODIFIED | GFIG_READONLY);
+
+ fclose (fp);
+}
diff --git a/plug-ins/gfig/gfig.h b/plug-ins/gfig/gfig.h
new file mode 100644
index 0000000..a2ed727
--- /dev/null
+++ b/plug-ins/gfig/gfig.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Generates images containing vector type drawings.
+ *
+ * Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __GFIG_H__
+#define __GFIG_H__
+
+#include "gfig-types.h"
+#include "gfig-style.h"
+
+#define MAX_UNDO 10
+#define MIN_UNDO 1
+
+typedef struct
+{
+ gint gridspacing;
+ GridType gridtype;
+ gboolean drawgrid;
+ gboolean snap2grid;
+ gboolean lockongrid;
+ gboolean showcontrol;
+ gdouble grid_radius_min;
+ gdouble grid_radius_interval;
+ gdouble grid_rotation;
+ gdouble grid_granularity;
+ gint grid_sectors_desired;
+} GfigOpts;
+
+typedef struct
+{
+ SelectionType type; /* ADD etc .. */
+ gint antia; /* Boolean for Antia */
+ gint feather; /* Feather it ? */
+ gdouble feather_radius; /* Radius to feather */
+ ArcType as_pie; /* Arc type selection segment/sector */
+ FillType fill_type; /* Fill type for selection */
+ gdouble fill_opacity; /* You can guess this one */
+} selection_option;
+
+void object_start (GdkPoint *pnt, gint);
+void object_operation (GdkPoint *pnt, gint);
+void object_operation_start (GdkPoint *pnt, gint shift_down);
+void object_operation_end (GdkPoint *pnt, gint);
+void object_end (GdkPoint *pnt, gint shift_down);
+
+#define MAX_LOAD_LINE 256
+#define SQ_SIZE 8
+
+#define PLUG_IN_PROC "plug-in-gfig"
+#define PLUG_IN_BINARY "gfig"
+#define PLUG_IN_ROLE "gimp-gfig"
+
+extern gint line_no;
+extern gint preview_width, preview_height;
+extern gint need_to_scale;
+extern gdouble scale_x_factor, scale_y_factor;
+extern GdkPixbuf *back_pixbuf;
+
+extern GtkWidget *pic_preview;
+extern gint obj_show_single;
+
+typedef struct
+{
+ GfigOpts opts;
+ gboolean showimage;
+ gint maxundo;
+ gboolean showpos;
+ gdouble brushfade;
+ gdouble brushgradient;
+ gdouble airbrushpressure;
+ DrawonLayers onlayers;
+ LayersBGType onlayerbg;
+ PaintType painttype;
+ gboolean reverselines;
+ gboolean scaletoimage;
+ gdouble scaletoimagefp;
+ BrushType brshtype;
+ DobjType otype;
+} SelectItVals;
+
+struct _GFigObj
+{
+ gchar *name; /* Trailing name of file */
+ gchar *filename; /* Filename itself */
+ gchar *draw_name; /* Name of the drawing */
+ gfloat version; /* Version number of data file */
+ GfigOpts opts; /* Options enforced when fig saved */
+ GList *obj_list; /* Objects that make up this list */
+ gint obj_status; /* See above for possible values */
+ GtkWidget *list_item;
+ GtkWidget *label_widget;
+ GtkWidget *pixmap_widget;
+};
+
+/* this is temp, should be able to get rid of */
+typedef struct BrushDesc
+{
+ gchar *name;
+ gdouble opacity;
+ gint spacing;
+ GimpLayerMode paint_mode;
+ gint width;
+ gint height;
+ guchar *pv_buf; /* Buffer where brush placed */
+ gint16 x_off;
+ gint16 y_off;
+ const gchar *popup;
+} BrushDesc;
+
+typedef struct
+{
+ gboolean debug_styles;
+ gboolean show_background; /* show thumbnail of image behind figure */
+ gint32 image_id; /* Gimp image id */
+ gint32 drawable_id; /* Gimp drawable to paint on */
+ GFigObj *current_obj;
+ GfigObject *selected_obj;
+ GtkWidget *preview;
+ Style default_style;
+ BrushDesc bdesc;
+ GtkWidget *fg_color_button;
+ GtkWidget *bg_color_button;
+ GtkWidget *brush_select;
+ GtkWidget *pattern_select;
+ GtkWidget *gradient_select;
+ GtkWidget *fillstyle_combo;
+ GtkWidget *paint_type_toggle;
+ GimpRGB *fg_color;
+ GimpRGB *bg_color;
+ gboolean enable_repaint;
+ gboolean using_new_layer;
+} GFigContext;
+
+extern GFigContext *gfig_context;
+
+extern selection_option selopt;
+extern SelectItVals selvals;
+
+void add_to_all_obj (GFigObj *fobj,
+ GfigObject *obj);
+
+gchar *get_line (gchar *buf,
+ gint s,
+ FILE *from,
+ gint init);
+
+gint gfig_scale_x (gint x);
+gint gfig_scale_y (gint y);
+void scale_to_xy (gdouble *list,
+ gint size);
+void scale_to_original_xy (gdouble *list,
+ gint size);
+
+void gfig_paint (BrushType brush_type,
+ gint32 drawable_ID,
+ gint seg_count,
+ gdouble line_pnts[]);
+
+void draw_item (cairo_t *cr,
+ gboolean fill);
+void draw_circle (GdkPoint *p,
+ gboolean selected,
+ cairo_t *cr);
+void draw_sqr (GdkPoint *p,
+ gboolean selected,
+ cairo_t *cr);
+
+void list_button_update (GFigObj *obj);
+
+GtkWidget *num_sides_widget (const gchar *d_title,
+ gint *num_sides,
+ gint *which_way,
+ gint adj_min,
+ gint adj_max);
+
+void setup_undo (void);
+void draw_grid_clear (void);
+void prepend_to_all_obj (GFigObj *fobj,
+ GList *nobj);
+
+void gfig_draw_arc (gint x,
+ gint y,
+ gint width,
+ gint height,
+ gint angle1,
+ gint angle2,
+ cairo_t *cr);
+
+void gfig_draw_line (gint x0,
+ gint y0,
+ gint x1,
+ gint y1,
+ cairo_t *cr);
+
+void gfig_paint_callback (void);
+GFigObj *gfig_load (const gchar *filename,
+ const gchar *name);
+void gfig_name_encode (gchar *dest,
+ gchar *src);
+void gfig_name_decode (gchar *dest,
+ const gchar *src);
+
+gint gfig_list_pos (GFigObj *gfig);
+gint gfig_list_insert (GFigObj *gfig);
+void gfig_free (GFigObj *gfig);
+
+void save_options (GString *string);
+
+GString *gfig_save_as_string (void);
+gboolean gfig_save_as_parasite (void);
+GFigObj *gfig_load_from_parasite (void);
+GFigObj *gfig_new (void);
+void gfig_save_callbk (void);
+void paint_layer_fill (gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2);
+
+
+
+extern GtkWidget *top_level_dlg;
+extern GList *gfig_list;
+extern gdouble org_scale_x_factor, org_scale_y_factor;
+
+#endif /* __GFIG_H__ */
diff --git a/plug-ins/gfig/images/Makefile.am b/plug-ins/gfig/images/Makefile.am
new file mode 100644
index 0000000..093fe5c
--- /dev/null
+++ b/plug-ins/gfig/images/Makefile.am
@@ -0,0 +1,35 @@
+## Process this file with automake to produce Makefile.in
+
+STOCK_IMAGES = \
+ stock-bezier.png \
+ stock-circle.png \
+ stock-copy-object.png \
+ stock-curve.png \
+ stock-delete-object.png \
+ stock-ellipse.png \
+ stock-line.png \
+ stock-move-object.png \
+ stock-move-point.png \
+ stock-polygon.png \
+ stock-rectangle.png \
+ stock-select-object.png \
+ stock-show-all.png \
+ stock-spiral.png \
+ stock-star.png \
+ stock-logo.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+
+noinst_DATA = gfig-stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/gfig-stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
diff --git a/plug-ins/gfig/images/Makefile.in b/plug-ins/gfig/images/Makefile.in
new file mode 100644
index 0000000..af6efd6
--- /dev/null
+++ b/plug-ins/gfig/images/Makefile.in
@@ -0,0 +1,781 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gfig/images
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+STOCK_IMAGES = \
+ stock-bezier.png \
+ stock-circle.png \
+ stock-copy-object.png \
+ stock-curve.png \
+ stock-delete-object.png \
+ stock-ellipse.png \
+ stock-line.png \
+ stock-move-object.png \
+ stock-move-point.png \
+ stock-polygon.png \
+ stock-rectangle.png \
+ stock-select-object.png \
+ stock-show-all.png \
+ stock-spiral.png \
+ stock-star.png \
+ stock-logo.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+noinst_DATA = gfig-stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gfig/images/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gfig/images/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/gfig-stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gfig/images/stock-bezier.png b/plug-ins/gfig/images/stock-bezier.png
new file mode 100644
index 0000000..6d4955d
--- /dev/null
+++ b/plug-ins/gfig/images/stock-bezier.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-circle.png b/plug-ins/gfig/images/stock-circle.png
new file mode 100644
index 0000000..f926cce
--- /dev/null
+++ b/plug-ins/gfig/images/stock-circle.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-copy-object.png b/plug-ins/gfig/images/stock-copy-object.png
new file mode 100644
index 0000000..0608b2d
--- /dev/null
+++ b/plug-ins/gfig/images/stock-copy-object.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-curve.png b/plug-ins/gfig/images/stock-curve.png
new file mode 100644
index 0000000..36d58a0
--- /dev/null
+++ b/plug-ins/gfig/images/stock-curve.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-delete-object.png b/plug-ins/gfig/images/stock-delete-object.png
new file mode 100644
index 0000000..cc5cc78
--- /dev/null
+++ b/plug-ins/gfig/images/stock-delete-object.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-ellipse.png b/plug-ins/gfig/images/stock-ellipse.png
new file mode 100644
index 0000000..e5cac4d
--- /dev/null
+++ b/plug-ins/gfig/images/stock-ellipse.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-line.png b/plug-ins/gfig/images/stock-line.png
new file mode 100644
index 0000000..071e61e
--- /dev/null
+++ b/plug-ins/gfig/images/stock-line.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-logo.png b/plug-ins/gfig/images/stock-logo.png
new file mode 100644
index 0000000..d47a675
--- /dev/null
+++ b/plug-ins/gfig/images/stock-logo.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-move-object.png b/plug-ins/gfig/images/stock-move-object.png
new file mode 100644
index 0000000..f2e502b
--- /dev/null
+++ b/plug-ins/gfig/images/stock-move-object.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-move-point.png b/plug-ins/gfig/images/stock-move-point.png
new file mode 100644
index 0000000..a4d9cd9
--- /dev/null
+++ b/plug-ins/gfig/images/stock-move-point.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-polygon.png b/plug-ins/gfig/images/stock-polygon.png
new file mode 100644
index 0000000..cd78f4a
--- /dev/null
+++ b/plug-ins/gfig/images/stock-polygon.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-rectangle.png b/plug-ins/gfig/images/stock-rectangle.png
new file mode 100644
index 0000000..8800ac8
--- /dev/null
+++ b/plug-ins/gfig/images/stock-rectangle.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-select-object.png b/plug-ins/gfig/images/stock-select-object.png
new file mode 100644
index 0000000..3ac980b
--- /dev/null
+++ b/plug-ins/gfig/images/stock-select-object.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-show-all.png b/plug-ins/gfig/images/stock-show-all.png
new file mode 100644
index 0000000..0a24db1
--- /dev/null
+++ b/plug-ins/gfig/images/stock-show-all.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-spiral.png b/plug-ins/gfig/images/stock-spiral.png
new file mode 100644
index 0000000..4e04807
--- /dev/null
+++ b/plug-ins/gfig/images/stock-spiral.png
Binary files differ
diff --git a/plug-ins/gfig/images/stock-star.png b/plug-ins/gfig/images/stock-star.png
new file mode 100644
index 0000000..dfeff33
--- /dev/null
+++ b/plug-ins/gfig/images/stock-star.png
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/Makefile.am b/plug-ins/gimpressionist/Brushes/Makefile.am
new file mode 100644
index 0000000..fa8860d
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/Makefile.am
@@ -0,0 +1,66 @@
+## Process this file with automake to produce Makefile.in
+
+brushdatadir = $(gimpdatadir)/gimpressionist/Brushes
+
+brushdata_DATA = \
+ arrow01.pgm \
+ ball.ppm \
+ blob.ppm \
+ box.ppm \
+ chalk01.pgm \
+ cone.ppm \
+ crayon01.pgm \
+ crayon02.pgm \
+ crayon03.pgm \
+ crayon04.pgm \
+ crayon05.pgm \
+ crayon06.pgm \
+ crayon07.pgm \
+ crayon08.pgm \
+ defaultbrush.pgm \
+ dribble.pgm \
+ fabric.pgm \
+ fabric01.pgm \
+ fabric02.pgm \
+ fabric03.pgm \
+ flower01.pgm \
+ flower02.pgm \
+ flower03.pgm \
+ flower04.pgm \
+ grad01.pgm \
+ grad02.pgm \
+ grad03.pgm \
+ heart.ppm \
+ leaf01.pgm \
+ paintbrush01.pgm \
+ paintbrush02.pgm \
+ paintbrush03.pgm \
+ paintbrush04.pgm \
+ paper01.pgm \
+ paper02.pgm \
+ paper03.pgm \
+ paper04.pgm \
+ pentagram.pgm \
+ scribble.pgm \
+ shape01.pgm \
+ shape02.pgm \
+ shape03.pgm \
+ shape04.pgm \
+ snow1.pgm \
+ sphere.ppm \
+ splat1.pgm \
+ splat2.pgm \
+ splat3.pgm \
+ spunge01.pgm \
+ spunge02.pgm \
+ spunge03.pgm \
+ spunge04.pgm \
+ spunge05.pgm \
+ strange01.pgm \
+ thegimp.pgm \
+ torus.ppm \
+ wavy.pgm \
+ weave.pgm \
+ worm.pgm
+
+EXTRA_DIST = $(brushdata_DATA)
diff --git a/plug-ins/gimpressionist/Brushes/Makefile.in b/plug-ins/gimpressionist/Brushes/Makefile.in
new file mode 100644
index 0000000..2c35feb
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/Makefile.in
@@ -0,0 +1,864 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gimpressionist/Brushes
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(brushdatadir)"
+DATA = $(brushdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+brushdatadir = $(gimpdatadir)/gimpressionist/Brushes
+brushdata_DATA = \
+ arrow01.pgm \
+ ball.ppm \
+ blob.ppm \
+ box.ppm \
+ chalk01.pgm \
+ cone.ppm \
+ crayon01.pgm \
+ crayon02.pgm \
+ crayon03.pgm \
+ crayon04.pgm \
+ crayon05.pgm \
+ crayon06.pgm \
+ crayon07.pgm \
+ crayon08.pgm \
+ defaultbrush.pgm \
+ dribble.pgm \
+ fabric.pgm \
+ fabric01.pgm \
+ fabric02.pgm \
+ fabric03.pgm \
+ flower01.pgm \
+ flower02.pgm \
+ flower03.pgm \
+ flower04.pgm \
+ grad01.pgm \
+ grad02.pgm \
+ grad03.pgm \
+ heart.ppm \
+ leaf01.pgm \
+ paintbrush01.pgm \
+ paintbrush02.pgm \
+ paintbrush03.pgm \
+ paintbrush04.pgm \
+ paper01.pgm \
+ paper02.pgm \
+ paper03.pgm \
+ paper04.pgm \
+ pentagram.pgm \
+ scribble.pgm \
+ shape01.pgm \
+ shape02.pgm \
+ shape03.pgm \
+ shape04.pgm \
+ snow1.pgm \
+ sphere.ppm \
+ splat1.pgm \
+ splat2.pgm \
+ splat3.pgm \
+ spunge01.pgm \
+ spunge02.pgm \
+ spunge03.pgm \
+ spunge04.pgm \
+ spunge05.pgm \
+ strange01.pgm \
+ thegimp.pgm \
+ torus.ppm \
+ wavy.pgm \
+ weave.pgm \
+ worm.pgm
+
+EXTRA_DIST = $(brushdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gimpressionist/Brushes/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gimpressionist/Brushes/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-brushdataDATA: $(brushdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(brushdata_DATA)'; test -n "$(brushdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(brushdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(brushdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(brushdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(brushdatadir)" || exit $$?; \
+ done
+
+uninstall-brushdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(brushdata_DATA)'; test -n "$(brushdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(brushdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(brushdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-brushdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-brushdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-brushdataDATA install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-brushdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gimpressionist/Brushes/arrow01.pgm b/plug-ins/gimpressionist/Brushes/arrow01.pgm
new file mode 100644
index 0000000..1c72f0d
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/arrow01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/ball.ppm b/plug-ins/gimpressionist/Brushes/ball.ppm
new file mode 100644
index 0000000..15d5fa2
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/ball.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/blob.ppm b/plug-ins/gimpressionist/Brushes/blob.ppm
new file mode 100644
index 0000000..59a546b
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/blob.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/box.ppm b/plug-ins/gimpressionist/Brushes/box.ppm
new file mode 100644
index 0000000..b5c0a55
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/box.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/chalk01.pgm b/plug-ins/gimpressionist/Brushes/chalk01.pgm
new file mode 100644
index 0000000..659639b
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/chalk01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/cone.ppm b/plug-ins/gimpressionist/Brushes/cone.ppm
new file mode 100644
index 0000000..d354433
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/cone.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon01.pgm b/plug-ins/gimpressionist/Brushes/crayon01.pgm
new file mode 100644
index 0000000..59851db
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon02.pgm b/plug-ins/gimpressionist/Brushes/crayon02.pgm
new file mode 100644
index 0000000..1bfe3c4
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon03.pgm b/plug-ins/gimpressionist/Brushes/crayon03.pgm
new file mode 100644
index 0000000..74a9a1a
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon04.pgm b/plug-ins/gimpressionist/Brushes/crayon04.pgm
new file mode 100644
index 0000000..5f8eaf1
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon05.pgm b/plug-ins/gimpressionist/Brushes/crayon05.pgm
new file mode 100644
index 0000000..ba7186f
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon05.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon06.pgm b/plug-ins/gimpressionist/Brushes/crayon06.pgm
new file mode 100644
index 0000000..adb13c4
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon06.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon07.pgm b/plug-ins/gimpressionist/Brushes/crayon07.pgm
new file mode 100644
index 0000000..ee78838
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon07.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/crayon08.pgm b/plug-ins/gimpressionist/Brushes/crayon08.pgm
new file mode 100644
index 0000000..6dd791b
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/crayon08.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/defaultbrush.pgm b/plug-ins/gimpressionist/Brushes/defaultbrush.pgm
new file mode 100644
index 0000000..e0f52e6
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/defaultbrush.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/dribble.pgm b/plug-ins/gimpressionist/Brushes/dribble.pgm
new file mode 100644
index 0000000..de264ed
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/dribble.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/fabric.pgm b/plug-ins/gimpressionist/Brushes/fabric.pgm
new file mode 100644
index 0000000..ab63505
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/fabric.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/fabric01.pgm b/plug-ins/gimpressionist/Brushes/fabric01.pgm
new file mode 100644
index 0000000..ab63505
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/fabric01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/fabric02.pgm b/plug-ins/gimpressionist/Brushes/fabric02.pgm
new file mode 100644
index 0000000..7b9b794
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/fabric02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/fabric03.pgm b/plug-ins/gimpressionist/Brushes/fabric03.pgm
new file mode 100644
index 0000000..c923c7f
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/fabric03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/flower01.pgm b/plug-ins/gimpressionist/Brushes/flower01.pgm
new file mode 100644
index 0000000..02dae4c
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/flower01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/flower02.pgm b/plug-ins/gimpressionist/Brushes/flower02.pgm
new file mode 100644
index 0000000..effe674
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/flower02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/flower03.pgm b/plug-ins/gimpressionist/Brushes/flower03.pgm
new file mode 100644
index 0000000..bb7aab7
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/flower03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/flower04.pgm b/plug-ins/gimpressionist/Brushes/flower04.pgm
new file mode 100644
index 0000000..e515e61
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/flower04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/grad01.pgm b/plug-ins/gimpressionist/Brushes/grad01.pgm
new file mode 100644
index 0000000..dc7302d
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/grad01.pgm
@@ -0,0 +1,205 @@
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+200 200
+255
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooommmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiihhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa________________________________________________________________________________________________________________________________________________________________________________________________________^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999988888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666665555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555544444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++********************************************************************************************************************************************************************************************************))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plug-ins/gimpressionist/Brushes/grad02.pgm b/plug-ins/gimpressionist/Brushes/grad02.pgm
new file mode 100644
index 0000000..80600ed
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/grad02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/grad03.pgm b/plug-ins/gimpressionist/Brushes/grad03.pgm
new file mode 100644
index 0000000..365827a
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/grad03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/heart.ppm b/plug-ins/gimpressionist/Brushes/heart.ppm
new file mode 100644
index 0000000..674fdf0
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/heart.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/leaf01.pgm b/plug-ins/gimpressionist/Brushes/leaf01.pgm
new file mode 100644
index 0000000..8f191d7
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/leaf01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paintbrush01.pgm b/plug-ins/gimpressionist/Brushes/paintbrush01.pgm
new file mode 100644
index 0000000..ce2de78
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paintbrush01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paintbrush02.pgm b/plug-ins/gimpressionist/Brushes/paintbrush02.pgm
new file mode 100644
index 0000000..261b238
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paintbrush02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paintbrush03.pgm b/plug-ins/gimpressionist/Brushes/paintbrush03.pgm
new file mode 100644
index 0000000..0339bd8
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paintbrush03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paintbrush04.pgm b/plug-ins/gimpressionist/Brushes/paintbrush04.pgm
new file mode 100644
index 0000000..06aa796
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paintbrush04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paper01.pgm b/plug-ins/gimpressionist/Brushes/paper01.pgm
new file mode 100644
index 0000000..079ae32
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paper01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paper02.pgm b/plug-ins/gimpressionist/Brushes/paper02.pgm
new file mode 100644
index 0000000..bc1afbb
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paper02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paper03.pgm b/plug-ins/gimpressionist/Brushes/paper03.pgm
new file mode 100644
index 0000000..c1caa44
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paper03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/paper04.pgm b/plug-ins/gimpressionist/Brushes/paper04.pgm
new file mode 100644
index 0000000..4e11e06
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/paper04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/pentagram.pgm b/plug-ins/gimpressionist/Brushes/pentagram.pgm
new file mode 100644
index 0000000..ebc8135
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/pentagram.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/scribble.pgm b/plug-ins/gimpressionist/Brushes/scribble.pgm
new file mode 100644
index 0000000..944e1f1
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/scribble.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/shape01.pgm b/plug-ins/gimpressionist/Brushes/shape01.pgm
new file mode 100644
index 0000000..61bc319
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/shape01.pgm
@@ -0,0 +1,5 @@
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+185 185
+255
+ \ No newline at end of file
diff --git a/plug-ins/gimpressionist/Brushes/shape02.pgm b/plug-ins/gimpressionist/Brushes/shape02.pgm
new file mode 100644
index 0000000..e6cd78d
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/shape02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/shape03.pgm b/plug-ins/gimpressionist/Brushes/shape03.pgm
new file mode 100644
index 0000000..888db5e
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/shape03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/shape04.pgm b/plug-ins/gimpressionist/Brushes/shape04.pgm
new file mode 100644
index 0000000..87eebdd
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/shape04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/snow1.pgm b/plug-ins/gimpressionist/Brushes/snow1.pgm
new file mode 100644
index 0000000..057f515
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/snow1.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/sphere.ppm b/plug-ins/gimpressionist/Brushes/sphere.ppm
new file mode 100644
index 0000000..7a4d1fc
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/sphere.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/splat1.pgm b/plug-ins/gimpressionist/Brushes/splat1.pgm
new file mode 100644
index 0000000..6f3f842
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/splat1.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/splat2.pgm b/plug-ins/gimpressionist/Brushes/splat2.pgm
new file mode 100644
index 0000000..dbbb197
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/splat2.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/splat3.pgm b/plug-ins/gimpressionist/Brushes/splat3.pgm
new file mode 100644
index 0000000..5fd45ae
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/splat3.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/spunge01.pgm b/plug-ins/gimpressionist/Brushes/spunge01.pgm
new file mode 100644
index 0000000..5638e59
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/spunge01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/spunge02.pgm b/plug-ins/gimpressionist/Brushes/spunge02.pgm
new file mode 100644
index 0000000..8bb8908
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/spunge02.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/spunge03.pgm b/plug-ins/gimpressionist/Brushes/spunge03.pgm
new file mode 100644
index 0000000..f8ae3ed
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/spunge03.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/spunge04.pgm b/plug-ins/gimpressionist/Brushes/spunge04.pgm
new file mode 100644
index 0000000..5597100
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/spunge04.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/spunge05.pgm b/plug-ins/gimpressionist/Brushes/spunge05.pgm
new file mode 100644
index 0000000..1dbbd00
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/spunge05.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/strange01.pgm b/plug-ins/gimpressionist/Brushes/strange01.pgm
new file mode 100644
index 0000000..eefbaff
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/strange01.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/thegimp.pgm b/plug-ins/gimpressionist/Brushes/thegimp.pgm
new file mode 100644
index 0000000..2c23b64
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/thegimp.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/torus.ppm b/plug-ins/gimpressionist/Brushes/torus.ppm
new file mode 100644
index 0000000..442c9ec
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/torus.ppm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/wavy.pgm b/plug-ins/gimpressionist/Brushes/wavy.pgm
new file mode 100644
index 0000000..1fe9559
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/wavy.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/weave.pgm b/plug-ins/gimpressionist/Brushes/weave.pgm
new file mode 100644
index 0000000..dd9a3ca
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/weave.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Brushes/worm.pgm b/plug-ins/gimpressionist/Brushes/worm.pgm
new file mode 100644
index 0000000..00a3f68
--- /dev/null
+++ b/plug-ins/gimpressionist/Brushes/worm.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Makefile.am b/plug-ins/gimpressionist/Makefile.am
new file mode 100644
index 0000000..9262dcb
--- /dev/null
+++ b/plug-ins/gimpressionist/Makefile.am
@@ -0,0 +1,87 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+SUBDIRS = Brushes Paper Presets
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+gimpressionist_RC = gimpressionist.rc.o
+endif
+
+AM_CPPFLAGS = \
+ -DDEFAULTPATH=\""~/$(gimpdir)/gimpressionist:$(gimpdatadir)/gimpressionist"\" \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+AM_LDFLAGS = $(mwindows)
+
+
+libexecdir = $(gimpplugindir)/plug-ins/gimpressionist
+
+libexec_PROGRAMS = gimpressionist
+
+gimpressionist_SOURCES = \
+ brush.c \
+ brush.h \
+ color.c \
+ color.h \
+ general.c \
+ general.h \
+ gimp.c \
+ gimpressionist.c \
+ gimpressionist.h \
+ globals.c \
+ infile.h \
+ orientation.h \
+ orientation.c \
+ orientmap.h \
+ orientmap.c \
+ paper.c \
+ paper.h \
+ placement.c \
+ placement.h \
+ plasma.c \
+ ppmtool.c \
+ ppmtool.h \
+ presets.c \
+ presets.h \
+ preview.c \
+ preview.h \
+ random.h \
+ repaint.c \
+ size.h \
+ size.c \
+ sizemap.c \
+ utils.c
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gimpressionist_RC)
+
+EXTRA_DIST = README
diff --git a/plug-ins/gimpressionist/Makefile.in b/plug-ins/gimpressionist/Makefile.in
new file mode 100644
index 0000000..73ddb16
--- /dev/null
+++ b/plug-ins/gimpressionist/Makefile.in
@@ -0,0 +1,1217 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = gimpressionist$(EXEEXT)
+subdir = plug-ins/gimpressionist
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_gimpressionist_OBJECTS = brush.$(OBJEXT) color.$(OBJEXT) \
+ general.$(OBJEXT) gimp.$(OBJEXT) gimpressionist.$(OBJEXT) \
+ globals.$(OBJEXT) orientation.$(OBJEXT) orientmap.$(OBJEXT) \
+ paper.$(OBJEXT) placement.$(OBJEXT) plasma.$(OBJEXT) \
+ ppmtool.$(OBJEXT) presets.$(OBJEXT) preview.$(OBJEXT) \
+ repaint.$(OBJEXT) size.$(OBJEXT) sizemap.$(OBJEXT) \
+ utils.$(OBJEXT)
+gimpressionist_OBJECTS = $(am_gimpressionist_OBJECTS)
+gimpressionist_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+gimpressionist_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(gimpressionist_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/brush.Po ./$(DEPDIR)/color.Po \
+ ./$(DEPDIR)/general.Po ./$(DEPDIR)/gimp.Po \
+ ./$(DEPDIR)/gimpressionist.Po ./$(DEPDIR)/globals.Po \
+ ./$(DEPDIR)/orientation.Po ./$(DEPDIR)/orientmap.Po \
+ ./$(DEPDIR)/paper.Po ./$(DEPDIR)/placement.Po \
+ ./$(DEPDIR)/plasma.Po ./$(DEPDIR)/ppmtool.Po \
+ ./$(DEPDIR)/presets.Po ./$(DEPDIR)/preview.Po \
+ ./$(DEPDIR)/repaint.Po ./$(DEPDIR)/size.Po \
+ ./$(DEPDIR)/sizemap.Po ./$(DEPDIR)/utils.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(gimpressionist_SOURCES)
+DIST_SOURCES = $(gimpressionist_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/gimpressionist
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+SUBDIRS = Brushes Paper Presets
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@gimpressionist_RC = gimpressionist.rc.o
+AM_CPPFLAGS = \
+ -DDEFAULTPATH=\""~/$(gimpdir)/gimpressionist:$(gimpdatadir)/gimpressionist"\" \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+AM_LDFLAGS = $(mwindows)
+gimpressionist_SOURCES = \
+ brush.c \
+ brush.h \
+ color.c \
+ color.h \
+ general.c \
+ general.h \
+ gimp.c \
+ gimpressionist.c \
+ gimpressionist.h \
+ globals.c \
+ infile.h \
+ orientation.h \
+ orientation.c \
+ orientmap.h \
+ orientmap.c \
+ paper.c \
+ paper.h \
+ placement.c \
+ placement.h \
+ plasma.c \
+ ppmtool.c \
+ ppmtool.h \
+ presets.c \
+ presets.h \
+ preview.c \
+ preview.h \
+ random.h \
+ repaint.c \
+ size.h \
+ size.c \
+ sizemap.c \
+ utils.c
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gimpressionist_RC)
+
+EXTRA_DIST = README
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gimpressionist/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gimpressionist/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+gimpressionist$(EXEEXT): $(gimpressionist_OBJECTS) $(gimpressionist_DEPENDENCIES) $(EXTRA_gimpressionist_DEPENDENCIES)
+ @rm -f gimpressionist$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gimpressionist_OBJECTS) $(gimpressionist_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/brush.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/general.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpressionist.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globals.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/orientation.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/orientmap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/paper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/placement.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plasma.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ppmtool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/presets.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/repaint.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/size.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sizemap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/brush.Po
+ -rm -f ./$(DEPDIR)/color.Po
+ -rm -f ./$(DEPDIR)/general.Po
+ -rm -f ./$(DEPDIR)/gimp.Po
+ -rm -f ./$(DEPDIR)/gimpressionist.Po
+ -rm -f ./$(DEPDIR)/globals.Po
+ -rm -f ./$(DEPDIR)/orientation.Po
+ -rm -f ./$(DEPDIR)/orientmap.Po
+ -rm -f ./$(DEPDIR)/paper.Po
+ -rm -f ./$(DEPDIR)/placement.Po
+ -rm -f ./$(DEPDIR)/plasma.Po
+ -rm -f ./$(DEPDIR)/ppmtool.Po
+ -rm -f ./$(DEPDIR)/presets.Po
+ -rm -f ./$(DEPDIR)/preview.Po
+ -rm -f ./$(DEPDIR)/repaint.Po
+ -rm -f ./$(DEPDIR)/size.Po
+ -rm -f ./$(DEPDIR)/sizemap.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/brush.Po
+ -rm -f ./$(DEPDIR)/color.Po
+ -rm -f ./$(DEPDIR)/general.Po
+ -rm -f ./$(DEPDIR)/gimp.Po
+ -rm -f ./$(DEPDIR)/gimpressionist.Po
+ -rm -f ./$(DEPDIR)/globals.Po
+ -rm -f ./$(DEPDIR)/orientation.Po
+ -rm -f ./$(DEPDIR)/orientmap.Po
+ -rm -f ./$(DEPDIR)/paper.Po
+ -rm -f ./$(DEPDIR)/placement.Po
+ -rm -f ./$(DEPDIR)/plasma.Po
+ -rm -f ./$(DEPDIR)/ppmtool.Po
+ -rm -f ./$(DEPDIR)/presets.Po
+ -rm -f ./$(DEPDIR)/preview.Po
+ -rm -f ./$(DEPDIR)/repaint.Po
+ -rm -f ./$(DEPDIR)/size.Po
+ -rm -f ./$(DEPDIR)/sizemap.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gimpressionist/Paper/Makefile.am b/plug-ins/gimpressionist/Paper/Makefile.am
new file mode 100644
index 0000000..6094996
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/Makefile.am
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+paperdatadir = $(gimpdatadir)/gimpressionist/Paper
+
+paperdata_DATA = \
+ bricks.pgm \
+ bricks2.pgm \
+ burlap.pgm \
+ canvas2.pgm \
+ defaultpaper.pgm \
+ marble.pgm \
+ marble2.pgm \
+ stone.pgm \
+ struc.pgm
+
+EXTRA_DIST = $(paperdata_DATA)
diff --git a/plug-ins/gimpressionist/Paper/Makefile.in b/plug-ins/gimpressionist/Paper/Makefile.in
new file mode 100644
index 0000000..9bc30d1
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/Makefile.in
@@ -0,0 +1,814 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gimpressionist/Paper
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(paperdatadir)"
+DATA = $(paperdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+paperdatadir = $(gimpdatadir)/gimpressionist/Paper
+paperdata_DATA = \
+ bricks.pgm \
+ bricks2.pgm \
+ burlap.pgm \
+ canvas2.pgm \
+ defaultpaper.pgm \
+ marble.pgm \
+ marble2.pgm \
+ stone.pgm \
+ struc.pgm
+
+EXTRA_DIST = $(paperdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gimpressionist/Paper/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gimpressionist/Paper/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-paperdataDATA: $(paperdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(paperdata_DATA)'; test -n "$(paperdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(paperdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(paperdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(paperdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(paperdatadir)" || exit $$?; \
+ done
+
+uninstall-paperdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(paperdata_DATA)'; test -n "$(paperdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(paperdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(paperdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-paperdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-paperdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-paperdataDATA install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags-am uninstall uninstall-am \
+ uninstall-paperdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gimpressionist/Paper/bricks.pgm b/plug-ins/gimpressionist/Paper/bricks.pgm
new file mode 100644
index 0000000..c721f76
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/bricks.pgm
@@ -0,0 +1,43 @@
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+96 95
+255
+ú©ױ㪰ԭ˭ٳsű~˳{ĖӚth~wuj}uͰ˪yd|qor}fbaqȁSi}|]`glnlUi#9ŕnpvƥu£1rX}NÜsGX`-wzvrq_xUkpzuyG k~Z{lwrOȮsO_ro^drm ʇȯ~zlzdr×"|~}uA|}&pc`YwyKcy {stO~z}qx~[tI g{'zWq}Vwvjn]}W4}Wvzongofvpy_vžtm}fN+:}gx~mttlpS}Sh}|o}nxq`l^|Wnz&3|sxdgx|w|y -bkl\wT{vrPgv k|uy|zmw"zl~fw}K^_T  `vylomjo}}tZp}~x~~HFT=QIt_qPԧ˳yj}}iyY}~oJdvsJqha^obl$kֿ~}}~ q{vG`S~xljsxb/U|]-
+ uțl{ynw[cnpqmh{x~j}u_\UY]j--
+aڷx}vo yqgq`{}su}spSrnoFsxUTl `á|}y*
+rw|{tfx\vnigzpy/ iϢ|g}ɐ
+zvrb{wsj{tm{|F_׳Ƣwvshz~ihzszzyn#jÝwt~ls_Ay`SQ{Xiiddvza fοw+ rnxoX]r^knzm}u97
+aւF{nvjD \20Bo}nh_o81Lo}ymzkYO].Ej`<
+M)2-#31$FI8%$/ )%!<&X'rclgzOO(5;lp5bq}tbon=Ydi
+
+#           ypPdObQ"<GV$CSS1aba')(hepU^KRh L/5/       >2[fg}D$e[," ! (   z|vA)%
+
+- ) 7,) 2?&21("Xtc^T[>b`8 
+ "  :\u_uq; \'$%!$;2 -k+#!3~NMhjTUkF8Nfpyxvd^o~#!YZy`Xp]hz{qim{xmP|ƙ1L|nQ="%Cfwiz^ƺǬõͳ˿wxx2?tÜ}jZxw=k|YZHXy͹vjug~qlnNUmdqlwgzU~$ipʩ!FcxvOovqzod_Udkqptawd
+sܷĽЭǫɺ²\ywmlIgyT1ɍƻǼҪƣ ¸loygizrQx|+O#x԰·ѭɣ˴ŭٹẽ `œzĜ{x[h˔us ;Ǽʺǻƽ|. ~sxzw|y! # շxuZf_x}s|qosȨm~`i_M˥{l~z{qvopxuz}{ǝw_}c~S
+BtipΪqqzsqv^U)rh}k}' wߧ{h}[o~Zu 8ҵgSy+ Quo~Fyioz~{yCpwpj{td}yu!ӭ}upqwcat঳gp{{yCϮ{d~onjyy
+tX^vtAFE ¬rzwu}{xsӣqjxkg[~pR~[ܠnlw~vqmzShzxyqqr 전[krvqx~}iwxybp ·xyXunrh~r{\iKdIR]ʗensk`xc|W|qx|hJD*h}c}}lrZ\oxoLkvph\
+ tx{i{~y~uO!ťkm^iTq[u^D{UYjQ`~~Kvçys"ibwaQYyk|jprx0W: }} (ye|V}zȹeFwR$BSpfY3@Bu!': {{Sgjcf2  :|rr<T1glJY}'3 =R[EOerSL<CY^dxCq ~JasbgLjZʒ](+~p_6%#/ TQ'Znj=<^s-.Hryss|J3%iuaE 
+ 
+
+ 
+ 
+ }/ LbMV=C;VJBY7*03GWdZpRI7>#
+ *]q9
+ '       GL$-J{A}sRx@E~')IPcbg{fN6Q^ 
+ bX 
+   žȶ%   "! 6 ?Ew¾L   6ΰȹ˯p   jļō<.Qͺuxlmq~eRWnОљ׿˰̽˭ȍʦ¶ʥotèŠ`RXkX. QѺt9mɺ̲üì˭¢Űxp[wxs''19|B`>\CgvtC%P䜜ò~~n~kmyO}r|fQpYe{_YaO ;ϻĵq_xrt`k|rwvtsvjtqgp(Ļm[lzunypf}}hPrp{}ah&
+ȯϑX{W3Tssfzs^_}^tc VΓxxj^ik|wrvfwJ}|f /đnQx}gS}tfjjE~gJl
+
+e~ʩ}~xrm~^yUhyWyzy_exp{uu}{Ct
+rv}w^iya~Yxwum[~pΣdtvfǫ{uiv}pzyY0vtZ{Rzoj~quf}pqfysdSyZdTkdwNr|}u{[]t{|dr2Y}U ~ycyr|\of]ddmpVdxciœ~ee\ygXor~¨xtvǠxyǕ}r{fppX|}qkxUt~tc3m©xooapcp]hztwéwY uryvYzTmz|o )vvw{}m~|hzn|\{˥~űw`}rcu|jx|akj`y{v{|s})
+ x~agMka]azzdjrpu/l´xkty`qCqwt~otZvwmeba{l (
+ԙҮ|v|~|~sbebirVsuzeXZ_vi#|y¡~RayxnxlwwszinPnunxq~|ψMf{tmqsekqycZ]wtwllbr~znv[5fڹ-H&$BYf^igmwlnuwqSrbcgtX}sgymkoa_~¢thnkrW)*hBjG"7((Y]x|xwjt}s}|Zw]DU{nV9d ũΩҶvXbc@&,#GLg<"#7Abmtecgf|ksl}X}ij'!<pJ|$ P}d[h!1)DD5LZf_T(F#2Xbxfqulo~tVj|C=5uT~nu {w2 !^{m''09_xa+.#B"_emt\@<**,"+!\{llLǮqu? %+NbrevwR! #%*+ *#)
+2" )   
+ '.6bbiv|amXqpM
+u_M #%
+   #
+('&SWb#  &    ϡ  &   ! %
+$4PKhc}k~I   
+ #$)Z'o#S8oDA$rAt 
+', 7ʿ͹ĞV=EwҶԤƺ§س¼òIJյ÷عʶ־ϾϿ֯ۗѭɵ \ No newline at end of file
diff --git a/plug-ins/gimpressionist/Paper/bricks2.pgm b/plug-ins/gimpressionist/Paper/bricks2.pgm
new file mode 100644
index 0000000..a7d9c2e
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/bricks2.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Paper/burlap.pgm b/plug-ins/gimpressionist/Paper/burlap.pgm
new file mode 100644
index 0000000..6eb247b
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/burlap.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Paper/canvas2.pgm b/plug-ins/gimpressionist/Paper/canvas2.pgm
new file mode 100644
index 0000000..0ff93e7
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/canvas2.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Paper/defaultpaper.pgm b/plug-ins/gimpressionist/Paper/defaultpaper.pgm
new file mode 100644
index 0000000..da32d96
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/defaultpaper.pgm
@@ -0,0 +1,5 @@
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+100 100
+255
+ovnw}v}}yˬo}rromy{{mgnr|cutzvzswtzswy{e|}}hzyppo^|uunt}{yp}yw}\|}{wsgi}{d}ham{wwswz}{u|zwwrk{vrrv|uvvnum{uwspwϴ{yw|ztuw_k|tthrpyzu}}kh|oyvyniv}}evif|vtwsevl]ttpgz}yyvlnosnuu{ol|wu|_]nt}y|hz{t}lvupo|sz{|vyv^|l{oywnpvz|twwzv}}|yp}\z|r}y|l|urp}{w{vyzsyvr{}s|vtuz}z}|p|fd|uSsrvru}yyvwl|uw{{zotr}zgrpr{{csdv}\{{mt|y}rz\`ttyo{nw|ezs^{e}hr|ww~sbmlyznXzvwgvtd|oy^}uzssZmwwotswo{t{p^u}llw}spfu}z}e]oz|uyh|{a{}}y{y}uyhmm^zèuu||uzzwrtywrzwru[u^Ɍ}ywy|um}vpvc|ufe}vy|m}m}fy{m{ymt|c}tzon}}}sy}kc|y||{zs}Suzz]uy}}ptM|wlel|}w}ç˜]}tu{yorrūvorcrw{iyt}{{wmc|olioy{ptyo|aahk}{soyvuocmh}}yt{r|nrou|iuyĬvz||yt|yv}rywuto|}}vm}yz|}i}t|fu}{{}y}zszvylwprzw{}h|wvcmwovuts{ivvzo}mwezvnuvw{wy}n|{{h|vrhv{}}}}zmyws|{nas}z}tr}sty{{||}}{ww|u}yvwyezs|vvsotr{eo}|l}v{{|svu}tv`ozwhy{wmnvyy|pynz}ǚs}fty|rmustwz{i}s{}Ϣwlyyˬo}rromyovnw}v}}r|cutzv{{mgny{e|zswtzswpo^|uu}}hzyp\|}{wnt}{yp}yw}swz}{u|sgi}{d}ham{wwspwϴ{yzwwrk{vrrv|uvvnum{uwzu}w|ztuw_k|tthrpyevif|}kh|oyvyniv}}pgz}yvtwsevl]ttuu{oyvlnosny|hz{t}lvl|wu|_]nt}{|vyv^|l{oupo|sztwwzv}}|ywnpvz|y|l|urp}{yp}\z|r}|vtuz}w{vyzsyvr{}srvru}yz}|p|fd|uSs}zgrpr{yvwl|uw{{zotry}oz_`tt{csdv}\{{mt||wx~uiyznXzyo{nw|ezs^{e}hru{~Zmwvwgvtd|oy^}u}lv}|uqfu}zwotswo{t{p^}y{y{}mm^}e]oz|uyh|{a{}r|[u^zèuu||uzzwrtyw|feɌ}ywy|um}vpvcctzo}vy|m}m}fy{m{ymt|y|{}n}}}sy}kc|y}zs}Suzz]u}ptM}|wlel|}wrç˜]}tu{yort}{{ūvorcrw{iyy{ptwmc|olioyo|aahk}}}yt{r{soyvuocmhiu|nrou|}rywyĬvz||yt|yv}yz|}uto|}}vm}zszvyi}t|fu}{{}y|wvcmlwprzw{}h{ivvzwovutsuvw{wo}mwezvnvrhv{}y}n|{{h|s}}}zmyw}tr}sty|{nas}zww|u}yv{{||}}{otr{eo}|lwyezs|vvsv`ozwhy{w}v{{|svu}tpynz}ǚs}mnvyy|wz{i}s{}Ϣwlyfty|rmust \ No newline at end of file
diff --git a/plug-ins/gimpressionist/Paper/marble.pgm b/plug-ins/gimpressionist/Paper/marble.pgm
new file mode 100644
index 0000000..589d8d3
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/marble.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Paper/marble2.pgm b/plug-ins/gimpressionist/Paper/marble2.pgm
new file mode 100644
index 0000000..0090344
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/marble2.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Paper/stone.pgm b/plug-ins/gimpressionist/Paper/stone.pgm
new file mode 100644
index 0000000..a1c0e34
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/stone.pgm
@@ -0,0 +1,6 @@
+P5
+# CREATOR: The GIMP's PNM Filter Version 1.0
+130 130
+255
+ՙ%+/Gu¹±ů|I cyxsuweekzuyzǽöο}LXȴIJˤ`cNDs~`n¼¢ǺǬŴѸ4sǺúвȦn1ToǺVJ̔˲˼Ÿ³ĹtG&*N̿ļ¶jz^пo\Ϸ¿İ{̯¶ʱƨ7#º¶W$mDzɱvۼzǤȺȿzZ.r̽ƺ0X}s0%`ļȻ{³öuxwѺ̽ıwna5+7 '_ýVmsM<:dԳñx}sϦοgi|wlZS:"AvЯ˦8ƴu&?wžѷŸɩ_>]Ƽ¬j~oRɿƷʳɬz]Xn½Z</)-J]c͵pZdtlzN<;>jʼ˰MJM¾yfu|lMϿѽźǺŶRdxɬU1.( ;n]6Ox]eX 4UϮγɵʸ˗_EȵŽtdaVŲŸæھgJWq2&* !>YyF]a  HfpOҾƱuθ|ºVƹ²ŵǵX2Ll˸vMMCD8>gD=_U:)(,?9nȻȻĹpovrog{~s̽ýѿҫX/+KMDs٧ĺץ}VKNYYTVSTXXGC8oͿv}x^nnui^j}~ξ˽صœQFGcqd82ͻz[ZI+cwrǶ˿Œ·uUL<Iqtǽļֵ}Fxx[,ϿȾ~pXSΣƯʼcηKCZOn~c>m̾Ʈǽʳ[4kUtQĽ¼͹ϾsxuYƵ[s̰{-Gſ¬s<FA5bɱݷƼxmK:m½¾ynJDRթ˦ͼĸVvnuyȳ_1Կ̧NIkذĩw{|oojiϺ̭};óȾ¿iQ~ٷӌlldPI^~^uͺ{_֭tȿÿϼo˖ɠg@*5!9.!. co؝ƹӇͼϥ~{M=YPw{ez·Byƿڝطxfžƿŭ~|ƺܿý~_\KP[ɟĹʌUiʰкħйpiýԷ`4˽ǼͩluL6-CTdxƥ|ŽɼذoEkþ̴z#Z~īYϼs_yӾǼιC9>QQj˽r÷оiVk}vZLp{IHLlĞ}T9/0E{raGILXbo˻ϲԶX%v93HH=QspPPMS)EPaȱmRQ`ūm_ǹŻybHjУu{uFV溷׿̬E1.[ĺ=2b՗ѵű|-EzZDSǷƾ¤~RUPDU{ʳH3xͭ\Xz縪gǩ½q\dĮs֚G;ikSg= UlsʻpsԮJPіB{qBUqƿĶb*Dǯݾ}Z[͸(6xVmڮǹ®xAY͹ýǼź¯ȶ֫{gemťµgjwԯZkׯxnFKкſѵmfеltzzܶԻ~MlɿգgɻƭvkLb˿ƿǧӰʨdzzM\Ѻɿɹo(]Wͽ¿Ϳ֕_ެGVνXoGcƾZ<Ɲ}ʡ^ЗOw݁OtحĬÿ¾֘ȭhP< ;ܶ{3~ΞٺV{ʾ̾ôĿڱ}~soU=@DIE@Pܝĵssóɢ}rQi˾˺}\R05hk˕UZWjRLÿ˾UYOWnĿǹusen}o20KWGRo\UACOϿºΤuzn҂iTK|־rNbW<3aZdqKm?9pgkíȣl'q~pǹhXa՞g|rahsZ;TonԠE&AXZxΔF>PIDDnˢåֿ~]kdĿenln}mVOHAʱǏ]RZak[I622.6?bxgYc}vȫʲ̾zɥ~uxwk17,(8OS\n{ͼpйұnwűץamż~yöĹǔĢ~•{MDX{fJB'20oˡۻM^ϼʭoyŔcJu·rsѱ˱o{s}pm~ڎABϚ\%gɓuqq}ǣžٰСhxIyʖD5uګe½ܡyú}Էm<.'&0BBX}z~~uaCⱗ]ŹٷɶHv¼KJOJQKSSU`\^>BQt̹rbXTdڳsS]եǼÄfs])(,hE@mGoѵòƾƿkI?Uyʞ;<p̝Ŀİܫ3dű¼÷ĽV0YktǐħƮʷƿ;ͺɝŠiPuʿƿҿH.tqͺǓi{Zpķqλɢvz>.2K*Qvҽκъ # [wotcusq|²ªϿn{lļvhqytynL0sFSSW>Ȟi0Lapxi\QC447>>JNTtu~ʮµwıvնo\֦HXep¾JHJ+mѼƄ]zU $6/57AOENSguðƼ\Ɯoϴĸu]ȼѿ]3UoS:HmyW7rϯܸl[jj]=L=8H7IB24:,0;Q:;f}p{hʟԞÿ{xɷKk^@=ӯݝۿfQM<-(Iun{iȵӭqzŹΒlۼ[Upඊtǭ^3 D_n~vo̺p^ʥQ6<ђYҮ͋G69W`QyDzɉ˴eg:߇tTGexٞtڿ˟|^kij`oͷӺŇyuUNKNZqм֕yxuPr@ۦĿԦleú=û˽̿k`wslYXXs¢tI<zPpq͸Ⱦx~xԵʻwfʀhhҪry栈l仧äī»ԭz*xƏ>;٤˱}ݮɬĸױplʊD\3p¾ӧĹпʾƺǻuj݋ȕEt`,hաtƷùľͳŲÄq2_~t侤ɽɱkNMsλlƿǵӲsɚrzzOH2ҜO+ʼžʎzҰؘtȾӽinj=nȺԣG*!ZfKoŶǾ¶VJsֽʘɾõġ˄dzɻ+ !Q¾ؼ%%8sqöôî@vȲ|_24K¿÷ݨbhF:̵üc;C|λĈu̽žq|ۡ\ŭ°̼pŻڽĿǾƿ?Okܐ<Y̓]̺ѴWSıڎżʽŝfOI洙}fRݪRյȿogT¾üw+]p؏9nhioxUi|Ͷʾ-$!mǾ˸6%Zhnɦ˲2yĿӮM25;eþwzϿǶҿ1;mʿǽ½ QoiZJ]VP]r|fXuY1"DGξyR@>V[ùüʶ_BUcscģxQ9hry{vP_i_]apvzvywiZRcsx|wb"/>nݾѵķ̺ľÞwA;e½ǺZ)Elͮ҆5(WǽԸ״ǽƳɿ{n&5`βyRtxŗֶˤq7)2gezN@b̞wƼªҗEEzs(z˟ҼձcH*00:^jxk}[JuĬ/',7uv3kYRpʶɼ`jז+$!)3@^ŻKIľ|ĸ]EG@PQpz}e1"+<8ZYXQZ´˜aCXσzM:<HM]fYutOFqɾǷǷhlǴop~}}wiUB^VvzͿvWhr|zȘ~hutlɸέ}pxἸö[/+5x|ƺΖyjltnx^û̼`"Gs¼˩C!ĿΘnZs±īکz].)bǴyl]ǹYWšuhUbo~]OĺǬY!E˹]eϛt31}psůĹzwt]f|N+`ͮпR-Ž˚_A?ٯҴJ%bðƳYs~ѿŸ[=;$;\{@AźpFkŕF7[ӿXS`Ƚɫy^hSǙ]O*9OF\|VBteþƽ§k9]Ӵ~ϯĝdFjɧk}`,WZ|Ŵwo~l;?DIILtȷծT<ȿ|˻ҷӮG*yϠml;ZtWYG0.10nn|xuz1JaԽ|2jØϭ۰M@cw:7IPeԺľ3skZ82RT<v|e8}uvĪkMóha]Ra­F}i{* F΋z8G͍eRȽŒȽӝDTxż»qqL[goйyxoKl_E$#'DL^SUZϮqNRļȱfuq͉ǚ{ĻbEmrqLcq}~rEPF45hT,:Ҥk+ 4}l=EG//6$-""SB̋·º‹yɺòϨfPJƙvk/Ḇ~+#0^vwJ%
+ $@Q`HAF@qnA«{aDNmĶŻȹžE"FmvphS:GY\\fԳӔoaӹǼȾήp˞vL~X>oλQE[axz||հM-ٽ»ʈ{nĿʡt}prI=uʹúնzzz|SAhޟt¿oxyĿѹ|T8jte]WM]zlR`bp{¾̸ղɣَa^~mf>)5_VryXJ/A@N~Ļ¹ʽ>ar߿ҩѷw_swyr9 5b¹лsXVLET8Lyö¾Ǡ¼ǽԹѰmsn̿Z*E[¹»ºʾ~kaVŚkAYsï~Ǽǹ)VƛȾѺʹΗXB59UaµĿɴqxute{ѥlK<DuǻǾumsm<v}r~ūڥD 0LfnȠu}p*hŶƼĿζS3)-?0Hrлΐ{afPkǼŒgtշnN'C'J̤_ؔkf;:1kƮȵuY'ülʺGL@'#F|gÿĻʽf*!ĘğxѪXhci()zƵø|}ӿ_LNPB»{q`T;Bmaɱ|vzn~lryvO/2_ðwxûٰ[}wyOirƵxg\oOƸeV*4fr|khִ{`GQslWC94rѴrzrYԶ|ڶ{UxŸ}];(5ɾ{C&5oqgf崦{CWG/)DlƿfR%.JԟV~ȐQԱ7ͯǮxbIXr|÷ǝvn}|#2ʸqYGD?wʧS'(|ױv=RԞŶҾŽйsQgȸXRCLFmɼ\;VeͷèwRW˷ȳżйʹ`.eФ¹W7$ @Uc~ƾi/Vx潠{Ҋmްʻàuɸ}4)ҿ›gG1E^wż`xbmtm~ʔUYlڲf|צgoȷþĸº9$ֲ],LMiżǾЪXVazzY TжaqՕRLǫĿʨƱ{YȔDץ_bqоɹȎ+oƅg^dbdm~ȾC<rzeŗʿ̤;įøđ\̩¸u=UٕteV]ojc~lK]ĠuqܿſıȴǾżŷDzbԢ{qBdlig]cwng=.ӡpw?9ܯ~±oŵǼ|fWilQddm<FgĿh{r||iYdgjotqqȼ~RbžC6ݫhN_ʶpƸżu^1,9IvP5&@m|f.Lȿ[YtvXVOcagpsmuy|ΉIaPephƉG~L8BѿǴ\qμgh\7TȳenE@) 4m˹^UƮjRXupks|vnttux˻ügik@$2U_vȸʼxY,Ji˻{: %,Hh¢l-*r=0Ty[5i\x~ujwvmwow¼kĻT/0lȗp{]"NjV[qkT'"AKox—;ԫnKPxsRET^sWNUyv{u|oiyĽ|ffx͙{mX@8T=19`|X(">Uǫ½ûʪyhyUJT^e{ȪĒ[5~~hxVZlpD=>IKJ:ŴTvmWJ& (3T3$Kpv~}˴ʽͳϽƜq\Wiڴ}2<tyq|~x9c||bWBD;sƝw5y{þǢ]WV:8:(0)$.A_y¦ɫ˛uiuʸhKspux~bO}ýRѦʯõzcYìخaXžɿҴnjrԥ|Glq{wkhAr{ݧ̪ŷ̳³ė?lџl{TCaw[`yuWg¶ԈƿѰ˽m3Ȧã|uˈ8+d{FU_tm[_u´­êƼɳøf%2įɨ2"Ts'Tnz|ywxiZwxk|u{kWF?mFƷô \ No newline at end of file
diff --git a/plug-ins/gimpressionist/Paper/struc.pgm b/plug-ins/gimpressionist/Paper/struc.pgm
new file mode 100644
index 0000000..3d6fe74
--- /dev/null
+++ b/plug-ins/gimpressionist/Paper/struc.pgm
Binary files differ
diff --git a/plug-ins/gimpressionist/Presets/ApplyCanvas b/plug-ins/gimpressionist/Presets/ApplyCanvas
new file mode 100644
index 0000000..839bc3e
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/ApplyCanvas
@@ -0,0 +1,43 @@
+Preset
+desc=Simulates the Apply Canvas plugin, by just adding a paper relief to the image.
+orientnum=1
+orientfirst=0.000000
+orientlast=0.000000
+orienttype=0
+sizenum=1
+sizefirst=0.000000
+sizelast=0.000000
+sizetype=0
+brushrelief=12.000000
+brushdensity=1.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=1
+generaldarkedge=0.000000
+generalpaintedges=0
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+generalshadowdepth=10
+generalshadowblur=4
+devthresh=0.100000
+paperrelief=25.0
+paperscale=100.0
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/defaultbrush.pgm
+selectedpaper=Paper/struc.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,50.000000,1.000000
+sizestrexp=1.000000
+sizevoronoi=0
+colortype=0
+colornoise=0.000000
diff --git a/plug-ins/gimpressionist/Presets/Ballpark b/plug-ins/gimpressionist/Presets/Ballpark
new file mode 100644
index 0000000..e6205a8
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Ballpark
@@ -0,0 +1,40 @@
+Preset
+desc=Huge spheres! Tileable. Might be suitable as a background somewhere.
+orientnum=1
+orientfirst=0.000000
+orientlast=60.000000
+orienttype=0
+sizenum=6
+sizefirst=28.670000
+sizelast=50.439999
+sizetype=2
+brushrelief=100.000000
+brushdensity=7.070000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.020000
+generalpaintedges=1
+generaltileable=1
+generaldropshadow=1
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/sphere.ppm
+selectedpaper=Paper/defaultpaper.pgm
+color=ffffff
+placetype=0
+placecenter=1
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Canvas b/plug-ins/gimpressionist/Presets/Canvas
new file mode 100644
index 0000000..f0a0e21
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Canvas
@@ -0,0 +1,28 @@
+Preset
+orientnum=1
+orientfirst=0.000000
+orientlast=360.000000
+orienttype=2
+brushrelief=12.000000
+brushscale=15.930000
+brushdensity=7.940000
+brushgamma=1.310000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/weave.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Crosshatch b/plug-ins/gimpressionist/Presets/Crosshatch
new file mode 100644
index 0000000..1d64a80
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Crosshatch
@@ -0,0 +1,28 @@
+Preset
+orientnum=4
+orientfirst=44.599998
+orientlast=360.000000
+orienttype=2
+brushrelief=0.000000
+brushscale=10.630000
+brushdensity=21.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.000000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/chalk01.pgm
+selectedpaper=Paper/canvas2.pgm
+color=010101
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Cubism b/plug-ins/gimpressionist/Presets/Cubism
new file mode 100644
index 0000000..3644e80
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Cubism
@@ -0,0 +1,40 @@
+Preset
+desc=Simulates a cubistic painting.
+orientnum=30
+orientfirst=0.000000
+orientlast=360.000000
+orienttype=2
+sizenum=1
+sizefirst=17.080000
+sizelast=17.080000
+sizetype=0
+brushrelief=0.000000
+brushdensity=18.600000
+brushgamma=0.500000
+brushaspect=0.000000
+generalbgtype=1
+generaldarkedge=0.000000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/grad01.pgm
+selectedpaper=Paper/canvas2.pgm
+color=646566
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Dotify b/plug-ins/gimpressionist/Presets/Dotify
new file mode 100644
index 0000000..38e9749
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Dotify
@@ -0,0 +1,40 @@
+Preset
+desc=Turns the image into a layer of small pebbles.
+orientnum=1
+orientfirst=47.790001
+orientlast=0.000000
+orienttype=0
+sizenum=1
+sizefirst=5.000000
+sizelast=5.000000
+sizetype=0
+brushrelief=0.000000
+brushdensity=20.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.300000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/shape02.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Embroidery b/plug-ins/gimpressionist/Presets/Embroidery
new file mode 100644
index 0000000..e854fb3
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Embroidery
@@ -0,0 +1,28 @@
+Preset
+orientnum=4
+orientfirst=44.599998
+orientlast=360.000000
+orienttype=2
+brushrelief=0.000000
+brushscale=10.750000
+brushdensity=11.040000
+brushgamma=0.500000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.000000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/fabric02.pgm
+selectedpaper=Paper/canvas2.pgm
+color=010101
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Feathers b/plug-ins/gimpressionist/Presets/Feathers
new file mode 100644
index 0000000..31f3369
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Feathers
@@ -0,0 +1,28 @@
+Preset
+orientnum=10
+orientfirst=38.230000
+orientlast=360.000000
+orienttype=6
+brushrelief=12.000000
+brushscale=20.000000
+brushdensity=50.000000
+brushgamma=2.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.300000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/dribble.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Felt-marker b/plug-ins/gimpressionist/Presets/Felt-marker
new file mode 100644
index 0000000..fc3e09f
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Felt-marker
@@ -0,0 +1,28 @@
+Preset
+orientnum=14
+orientfirst=66.900002
+orientlast=360.000000
+orienttype=6
+brushrelief=0.000000
+brushscale=13.730000
+brushdensity=20.000000
+brushgamma=1.000000
+brushaspect=-0.450000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/shape04.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Flowerbed b/plug-ins/gimpressionist/Presets/Flowerbed
new file mode 100644
index 0000000..32baef1
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Flowerbed
@@ -0,0 +1,40 @@
+Preset
+desc=Flower power, man!
+orientnum=7
+orientfirst=11.489998
+orientlast=45.599998
+orienttype=2
+sizenum=1
+sizefirst=15.930000
+sizelast=15.930000
+sizetype=0
+brushrelief=12.390000
+brushdensity=21.709999
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=1
+generaldarkedge=0.150000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/flower01.pgm
+selectedpaper=Paper/canvas2.pgm
+color=646566
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Furry b/plug-ins/gimpressionist/Presets/Furry
new file mode 100644
index 0000000..0bbdce0
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Furry
@@ -0,0 +1,40 @@
+Preset
+desc=Woof!
+orientnum=30
+orientfirst=89.199997
+orientlast=203.889999
+orienttype=4
+sizenum=1
+sizefirst=14.200000
+sizelast=14.200000
+sizetype=0
+brushrelief=0.000000
+brushdensity=28.320000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=103.169998
+paperinvert=1
+paperoverlay=0
+selectedbrush=Brushes/chalk01.pgm
+selectedpaper=Paper/marble.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Line-art b/plug-ins/gimpressionist/Presets/Line-art
new file mode 100644
index 0000000..274ca86
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Line-art
@@ -0,0 +1,28 @@
+Preset
+orientnum=15
+orientfirst=44.599998
+orientlast=180.220001
+orienttype=6
+brushrelief=12.000000
+brushscale=14.200000
+brushdensity=20.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/chalk01.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Line-art-2 b/plug-ins/gimpressionist/Presets/Line-art-2
new file mode 100644
index 0000000..5ec301b
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Line-art-2
@@ -0,0 +1,28 @@
+Preset
+orientnum=15
+orientfirst=31.860001
+orientlast=180.220001
+orienttype=6
+brushrelief=41.590000
+brushscale=15.350000
+brushdensity=26.580000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/chalk01.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Maggot-invasion b/plug-ins/gimpressionist/Presets/Maggot-invasion
new file mode 100644
index 0000000..93b4477
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Maggot-invasion
@@ -0,0 +1,40 @@
+Preset
+desc=Sends a horde of maggots to gnaw at your image.
+orientnum=30
+orientfirst=0.000000
+orientlast=360.000000
+orienttype=6
+sizenum=1
+sizefirst=13.150000
+sizelast=13.150000
+sizetype=0
+brushrelief=16.000000
+brushdensity=20.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=0
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/worm.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=1
+numorientvector=1
+orientvector=0,0.633333,0.466667,282.000000,-0.978148,0.207912,1.000000,-1603454457
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Makefile.am b/plug-ins/gimpressionist/Presets/Makefile.am
new file mode 100644
index 0000000..8cfdc88
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Makefile.am
@@ -0,0 +1,32 @@
+## Process this file with automake to produce Makefile.in
+
+presetsdatadir = $(gimpdatadir)/gimpressionist/Presets
+
+presetsdata_DATA = \
+ ApplyCanvas \
+ Ballpark \
+ Canvas \
+ Crosshatch \
+ Cubism \
+ Dotify \
+ Embroidery \
+ Feathers \
+ Felt-marker \
+ Flowerbed \
+ Furry \
+ Line-art \
+ Line-art-2 \
+ Maggot-invasion \
+ MarbleMadness \
+ Mossy \
+ Painted_Rock \
+ Parquette \
+ Patchwork \
+ Ringworks \
+ Sample \
+ Smash \
+ Straws \
+ Weave \
+ Wormcan
+
+EXTRA_DIST = $(presetsdata_DATA)
diff --git a/plug-ins/gimpressionist/Presets/Makefile.in b/plug-ins/gimpressionist/Presets/Makefile.in
new file mode 100644
index 0000000..61790ca
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Makefile.in
@@ -0,0 +1,830 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gimpressionist/Presets
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(presetsdatadir)"
+DATA = $(presetsdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+presetsdatadir = $(gimpdatadir)/gimpressionist/Presets
+presetsdata_DATA = \
+ ApplyCanvas \
+ Ballpark \
+ Canvas \
+ Crosshatch \
+ Cubism \
+ Dotify \
+ Embroidery \
+ Feathers \
+ Felt-marker \
+ Flowerbed \
+ Furry \
+ Line-art \
+ Line-art-2 \
+ Maggot-invasion \
+ MarbleMadness \
+ Mossy \
+ Painted_Rock \
+ Parquette \
+ Patchwork \
+ Ringworks \
+ Sample \
+ Smash \
+ Straws \
+ Weave \
+ Wormcan
+
+EXTRA_DIST = $(presetsdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gimpressionist/Presets/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gimpressionist/Presets/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-presetsdataDATA: $(presetsdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(presetsdata_DATA)'; test -n "$(presetsdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(presetsdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(presetsdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(presetsdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(presetsdatadir)" || exit $$?; \
+ done
+
+uninstall-presetsdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(presetsdata_DATA)'; test -n "$(presetsdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(presetsdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(presetsdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-presetsdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-presetsdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-presetsdataDATA install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags-am uninstall uninstall-am \
+ uninstall-presetsdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gimpressionist/Presets/MarbleMadness b/plug-ins/gimpressionist/Presets/MarbleMadness
new file mode 100644
index 0000000..4db9fba
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/MarbleMadness
@@ -0,0 +1,39 @@
+Preset
+desc=Quite close to the original Cryptiq-plugins effect.
+orientnum=1
+orientfirst=0.000000
+orientlast=0.000000
+orienttype=0
+sizenum=10
+sizefirst=6.000000
+sizelast=25.000000
+sizetype=6
+brushrelief=100.000000
+brushdensity=50.000000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=3.500000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/ball.ppm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Mossy b/plug-ins/gimpressionist/Presets/Mossy
new file mode 100644
index 0000000..679ef76
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Mossy
@@ -0,0 +1,40 @@
+Preset
+desc=Very rough moss-like texture. Works quite well for tileable backgrounds and other textures.
+orientnum=21
+orientfirst=31.860001
+orientlast=180.220001
+orienttype=6
+sizenum=1
+sizefirst=30.570000
+sizelast=30.570000
+sizetype=0
+brushrelief=41.590000
+brushdensity=26.580000
+brushgamma=0.500000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/strange01.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Painted_Rock b/plug-ins/gimpressionist/Presets/Painted_Rock
new file mode 100644
index 0000000..622ba4b
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Painted_Rock
@@ -0,0 +1,40 @@
+Preset
+desc=Rough paint-on-rock sort of texture. Good for tileable backgrounds.
+orientnum=30
+orientfirst=0.000000
+orientlast=360.000000
+orienttype=2
+sizenum=1
+sizefirst=35.369999
+sizelast=35.369999
+sizetype=0
+brushrelief=100.000000
+brushdensity=20.000000
+brushgamma=2.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/splat3.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Parquette b/plug-ins/gimpressionist/Presets/Parquette
new file mode 100644
index 0000000..9ea0b14
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Parquette
@@ -0,0 +1,30 @@
+Preset
+orientnum=4
+orientfirst=31.860001
+orientlast=360.000000
+orienttype=7
+brushrelief=12.000000
+brushscale=20.000000
+brushdensity=20.000000
+brushgamma=1.000000
+brushaspect=0.630000
+generalbgtype=0
+generaldarkedge=0.400000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/crayon04.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=3
+orientvector=0,0.240000,0.333333,0.000000,0.000000,1.000000,1.000000,3
+orientvector=1,0.780000,0.293333,0.000000,0.000000,1.000000,1.000000,3
+orientvector=2,0.533333,0.813333,0.000000,0.000000,1.000000,1.000000,3
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=1
diff --git a/plug-ins/gimpressionist/Presets/Patchwork b/plug-ins/gimpressionist/Presets/Patchwork
new file mode 100644
index 0000000..68fbc25
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Patchwork
@@ -0,0 +1,28 @@
+Preset
+orientnum=8
+orientfirst=22.299999
+orientlast=360.000000
+orienttype=2
+brushrelief=15.040000
+brushscale=13.630000
+brushdensity=15.500000
+brushgamma=0.860000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=0
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/spunge02.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Ringworks b/plug-ins/gimpressionist/Presets/Ringworks
new file mode 100644
index 0000000..a0de14c
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Ringworks
@@ -0,0 +1,28 @@
+Preset
+orientnum=1
+orientfirst=0.000000
+orientlast=0.000000
+orienttype=2
+brushrelief=0.000000
+brushscale=11.330000
+brushdensity=12.390000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.000000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/shape03.pgm
+selectedpaper=Paper/canvas2.pgm
+color=646566
+placetype=1
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Sample b/plug-ins/gimpressionist/Presets/Sample
new file mode 100644
index 0000000..9607029
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Sample
@@ -0,0 +1,40 @@
+Preset
+desc=Plain oil-painting. Good for big pictures, as much of the details are lost.
+orientnum=30
+orientfirst=70.089996
+orientlast=360.000000
+orienttype=5
+sizenum=1
+sizefirst=15.350000
+sizelast=15.350000
+sizetype=0
+brushrelief=12.000000
+brushdensity=40.000000
+brushgamma=1.310000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.050000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/defaultbrush.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Smash b/plug-ins/gimpressionist/Presets/Smash
new file mode 100644
index 0000000..92f29f0
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Smash
@@ -0,0 +1,40 @@
+Preset
+desc=Looks like a mixture between a spiderweb and a smashed window.
+orientnum=17
+orientfirst=15.930000
+orientlast=360.000000
+orienttype=3
+sizenum=1
+sizefirst=25.780001
+sizelast=25.780001
+sizetype=0
+brushrelief=0.000000
+brushdensity=20.950001
+brushgamma=0.500000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.100000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/strange01.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Straws b/plug-ins/gimpressionist/Presets/Straws
new file mode 100644
index 0000000..82b703a
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Straws
@@ -0,0 +1,41 @@
+Preset
+desc=The image looks like it is made up of small pieces of straw or chips of wood. Tileable.
+orientnum=30
+orientfirst=41.419998
+orientlast=360.000000
+orienttype=6
+sizenum=1
+sizefirst=29.730000
+sizelast=29.730000
+sizetype=0
+brushrelief=0.000000
+brushdensity=32.650002
+brushgamma=1.000000
+brushaspect=-0.400000
+generalbgtype=0
+generaldarkedge=0.350000
+generalpaintedges=1
+generaltileable=1
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/defaultbrush.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=000000
+placetype=0
+placecenter=0
+numorientvector=2
+orientvector=0,0.500000,0.500000,90.000000,1.000000,0.000000,1.000000,3
+orientvector=1,0.500000,0.500000,270.000000,-1.000000,0.000000,1.000000,3
+orientangoff=0.000000
+orientstrexp=1.400000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Weave b/plug-ins/gimpressionist/Presets/Weave
new file mode 100644
index 0000000..63a3edb
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Weave
@@ -0,0 +1,40 @@
+Preset
+desc=Makes the image look like some sort of woven quilt or rug, or something in that direction.
+orientnum=4
+orientfirst=9.560000
+orientlast=360.000000
+orienttype=2
+sizenum=1
+sizefirst=20.530001
+sizelast=20.530001
+sizetype=0
+brushrelief=12.000000
+brushdensity=13.080000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=1
+generaldarkedge=0.050000
+generalpaintedges=1
+generaltileable=0
+generaldropshadow=0
+generalshadowdarkness=20.000000
+devthresh=0.100000
+paperrelief=30.969999
+paperscale=100.570000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/fabric01.pgm
+selectedpaper=Paper/canvas2.pgm
+color=646566
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
+numsizevector=1
+sizevector=0,0.500000,0.500000,0.000000,0.000000,0
+sizeangoff=0.000000
+sizestrexp=0.000000
+sizevoronoi=0
diff --git a/plug-ins/gimpressionist/Presets/Wormcan b/plug-ins/gimpressionist/Presets/Wormcan
new file mode 100644
index 0000000..9880db7
--- /dev/null
+++ b/plug-ins/gimpressionist/Presets/Wormcan
@@ -0,0 +1,28 @@
+Preset
+orientnum=16
+orientfirst=0.000000
+orientlast=360.000000
+orienttype=2
+brushrelief=30.969999
+brushscale=20.000000
+brushdensity=25.160000
+brushgamma=1.000000
+brushaspect=0.000000
+generalbgtype=0
+generaldarkedge=0.050000
+generalpaintedges=1
+generaltileable=0
+paperrelief=0.000000
+paperscale=30.000000
+paperinvert=0
+paperoverlay=0
+selectedbrush=Brushes/wavy.pgm
+selectedpaper=Paper/defaultpaper.pgm
+color=010101
+placetype=0
+placecenter=0
+numorientvector=1
+orientvector=0,0.500000,0.500000,0.000000,0.000000,1.000000,1.000000,0
+orientangoff=0.000000
+orientstrexp=1.000000
+orientvoronoi=0
diff --git a/plug-ins/gimpressionist/README b/plug-ins/gimpressionist/README
new file mode 100644
index 0000000..42d90a4
--- /dev/null
+++ b/plug-ins/gimpressionist/README
@@ -0,0 +1,102 @@
+ This is the README for The GIMPressionist
+ (c) 1998, 1999 Vidar Madsen - vidar@prosalg.no
+
+ The GIMPressionist is free software, and may be distributed
+ freely, provided this file is included!
+
+ This program comes with no warranty, whatsoever. The author
+ can not be held liable for any damage caused by proper or
+ improper use of this program.
+
+
+--- Introduction
+
+The GIMPressionist is a plug-in for the GNU Image Manipulation Program,
+a.k.a. GIMP. It can be used to create natural looking painting effects
+and similar. It can also be run in a primitive "standalone" mode.
+
+The current version is still a bit shaky, and is to be considered beta
+software. I'll do some serious testing, hopefully across a few more
+platforms, as soon as I can.
+
+The official Web-page is http://www.prosalg.no/~vidar/gimpressionist/ and
+will always contain a link to the latest released version.
+
+
+--- Compiling / Installing
+
+To compile (hopefully):
+ make
+
+If you want to compile for GTK-1.1 and GIMP-1.1 instead, you will have
+to change the two variable at the top of Makefile. Possibly you would
+want to edit DEFAULTPATH as well, but that's not critical.
+
+To install plug-in and accompanying files:
+ make install
+
+The plug-in installs itself under $HOME/.gimp/plug-ins/ and copies a
+couple of files into the directory $HOME/.gimp/gimpressionist/. The
+subdirectories 'Brushes', 'Paper' and 'Presets' will be created during
+"make install", and a few files will be added; As a minimum,
+'defaultbrush.pgm' and 'defaultpaper.pgm' under their respective
+directories. These are vanilla .PGM files, which could be edited (and
+created) with any decent program - like GIMP! :-)
+
+
+--- Standalone
+
+As of version 0.99 and later, the GIMPressionist can also be run
+separately from GIMP. This is primarily intended for debugging
+purposes, though, as it still needs GIMP libraries to compile. In your
+shell, type:
+ gimpressionist somefile.ppm
+
+The image specified must be a valid PPM file. No other formats are
+supported, and probably never will.
+
+If you click "OK", the PPM will be replaced with the "repainted" version.
+If you "Cancel", nothing will be done to the file.
+
+
+--- File Formats
+
+The "normal" brushes are simply grayscale PGM files, which can be created
+with almost any program, including xv and of course GIMP. A number of
+brushes is included, so I guess a more in-depth explanation is not
+necessary. And, for those who might want to try it, GIMPressionist can
+also read GBR files, which is GIMP's native brush format. (You still
+need to copy them to the GIMPressionist's brush-directory, though. This
+will probably be fixed sometimes.)
+
+Version 0.99.4 introduced the concept of using "colored" brushes (although
+"pre-rendered" usually is more correct). The brushes' file format is a
+plain PPM file, but the layout is somewhat special (and kinda hard to
+explain);
+
+- The Red channel contains a shading map of the brush in question. Often
+ it resembles the overall image more or less completely.
+
+- The Green channel contains a high-light map. This is a pre-defined
+ image of the shiny parts of the brush. (Ordinary grayscale brushes
+ have this calculated automatically with a rough emboss-ish algorithm.)
+
+- The Blue channel functions as a brush mask. When applying a brush
+ stroke to the image, all areas colored blue in the file are first
+ "blacked out" of the image.
+
+
+--- Feedback
+
+If you have any comments, criticism, ideas for improvement or new
+features, or if you run into problems of any sort, let me know, so that I
+can (try to) fix it for the next release! (And, if you find platform-
+dependent bugs, please try to fix them and send me a patch!)
+
+I'm also welcoming contributions in the form of brushes or textures! For
+this program to be valuable, it needs them... Bad! All contributions will
+be properly credited, of course.
+
+Good luck and happy painting!
+
+Vidar Madsen <vidar@prosalg.no>
diff --git a/plug-ins/gimpressionist/brush.c b/plug-ins/gimpressionist/brush.c
new file mode 100644
index 0000000..a5d1e59
--- /dev/null
+++ b/plug-ins/gimpressionist/brush.c
@@ -0,0 +1,660 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include <libgimpmath/gimpmath.h>
+
+#include <gtk/gtklist.h>
+#include <gtk/gtkpreview.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "brush.h"
+#include "presets.h"
+
+#include <libgimp/stdplugins-intl.h>
+
+
+static void update_brush_preview (const char *fn);
+
+
+static GtkWidget *brush_preview = NULL;
+static GtkListStore *brush_list_store = NULL;
+
+static GtkWidget *brush_list = NULL;
+static GtkAdjustment *brush_relief_adjust = NULL;
+static GtkAdjustment *brush_aspect_adjust = NULL;
+static GtkAdjustment *brush_gamma_adjust = NULL;
+static gboolean brush_dont_update = FALSE;
+
+static gchar *last_selected_brush = NULL;
+
+static gint brush_from_file = 2;
+
+static ppm_t brushppm = {0, 0, NULL};
+
+void
+brush_restore (void)
+{
+ reselect (brush_list, pcvals.selected_brush);
+ gtk_adjustment_set_value (brush_gamma_adjust, pcvals.brushgamma);
+ gtk_adjustment_set_value (brush_relief_adjust, pcvals.brush_relief);
+ gtk_adjustment_set_value (brush_aspect_adjust, pcvals.brush_aspect);
+}
+
+void
+brush_store (void)
+{
+ pcvals.brushgamma = gtk_adjustment_get_value (brush_gamma_adjust);
+}
+
+void
+brush_free (void)
+{
+ g_free (last_selected_brush);
+}
+
+void brush_get_selected (ppm_t *p)
+{
+ if (brush_from_file)
+ brush_reload (pcvals.selected_brush, p);
+ else
+ ppm_copy (&brushppm, p);
+}
+
+
+static gboolean
+file_is_color (const char *fn)
+{
+ return fn && strstr (fn, ".ppm");
+}
+
+void
+set_colorbrushes (const gchar *fn)
+{
+ pcvals.color_brushes = file_is_color (fn);
+}
+
+static const Babl *
+get_u8_format (gint32 drawable_id)
+{
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("R'G'B'A u8");
+ else
+ return babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("Y'A u8");
+ else
+ return babl_format ("Y' u8");
+ }
+}
+
+static void
+brushdmenuselect (GtkWidget *widget,
+ gpointer data)
+{
+ GeglBuffer *src_buffer;
+ const Babl *format;
+ guchar *src_row;
+ guchar *src;
+ gint bpp;
+ gint x, y;
+ ppm_t *p;
+ gint x1, y1, w, h;
+ gint row;
+ gint32 drawable_id;
+ gint rowstride;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &drawable_id);
+
+ if (drawable_id == -1)
+ return;
+
+ if (brush_from_file == 2)
+ return; /* Not finished GUI-building yet */
+
+ if (brush_from_file)
+ {
+#if 0
+ unselectall (brush_list);
+#endif
+ preset_save_button_set_sensitive (FALSE);
+ }
+
+ gtk_adjustment_set_value (brush_gamma_adjust, 1.0);
+ gtk_adjustment_set_value (brush_aspect_adjust, 0.0);
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &x1, &y1, &w, &h))
+ return;
+
+ format = get_u8_format (drawable_id);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ ppm_kill (&brushppm);
+ ppm_new (&brushppm, w, h);
+ p = &brushppm;
+
+ rowstride = p->width * 3;
+
+ src_row = g_new (guchar, w * bpp);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ if (bpp == 3)
+ { /* RGB */
+ gint bpr = w * 3;
+ gint y2 = y1 + h;
+
+ for (row = 0, y = y1; y < y2; row++, y++)
+ {
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y, w, 1), 1.0,
+ format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ memcpy (p->col + row*rowstride, src_row, bpr);
+ }
+ }
+ else
+ { /* RGBA (bpp > 3) GrayA (bpp == 2) or Gray */
+ gboolean is_gray = ((bpp > 3) ? TRUE : FALSE);
+ gint y2 = y1 + h;
+
+ for (row = 0, y = y1; y < y2; row++, y++)
+ {
+ guchar *tmprow = p->col + row * rowstride;
+ guchar *tmprow_ptr;
+ gint x2 = x1 + w;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (x1, y, w, 1), 1.0,
+ format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = src_row;
+ tmprow_ptr = tmprow;
+ /* Possible micro-optimization here:
+ * src_end = src + src_rgn.bpp * w);
+ * for ( ; src < src_end ; src += src_rgn.bpp)
+ */
+ for (x = x1; x < x2; x++)
+ {
+ *(tmprow_ptr++) = src[0];
+ *(tmprow_ptr++) = src[is_gray ? 1 : 0];
+ *(tmprow_ptr++) = src[is_gray ? 2 : 0];
+ src += bpp;
+ }
+ }
+ }
+
+ g_object_unref (src_buffer);
+
+ g_free (src_row);
+
+ if (bpp >= 3)
+ pcvals.color_brushes = 1;
+ else
+ pcvals.color_brushes = 0;
+
+ brush_from_file = 0;
+ update_brush_preview (NULL);
+}
+
+#if 0
+void
+dummybrushdmenuselect (GtkWidget *w, gpointer data)
+{
+ ppm_kill (&brushppm);
+ ppm_new (&brushppm, 10,10);
+ brush_from_file = 0;
+ update_brush_preview (NULL);
+}
+#endif
+
+static void
+brushlistrefresh (void)
+{
+ gtk_list_store_clear (brush_list_store);
+ readdirintolist ("Brushes", brush_list, NULL);
+}
+
+static void
+savebrush_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ ppm_save (&brushppm, name);
+ brushlistrefresh ();
+
+ g_free (name);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+savebrush (GtkWidget *wg,
+ gpointer data)
+{
+ GtkWidget *dialog = NULL;
+ GList *thispath = parsepath ();
+ gchar *path;
+
+ if (! PPM_IS_INITED (&brushppm))
+ {
+ g_message ( _("Can only save drawables!"));
+ return;
+ }
+
+ dialog =
+ gtk_file_chooser_dialog_new (_("Save Brush"),
+ GTK_WINDOW (gtk_widget_get_toplevel (wg)),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ path = g_build_filename ((gchar *)thispath->data, "Brushes", NULL);
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), path);
+
+ g_free (path);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (savebrush_response),
+ NULL);
+
+ gtk_widget_show (dialog);
+}
+
+static gboolean
+validdrawable (gint32 imageid,
+ gint32 drawableid,
+ gpointer data)
+{
+ return (gimp_drawable_is_rgb (drawableid) ||
+ gimp_drawable_is_gray (drawableid));
+}
+
+/*
+ * This function caches the last result. Call it with fn as NULL, to
+ * free the arguments.
+ * */
+void
+brush_reload (const gchar *fn,
+ ppm_t *p)
+{
+ static char lastfn[256] = "";
+ static ppm_t cache = {0, 0, NULL};
+
+ if (fn == NULL)
+ {
+ ppm_kill (&cache);
+ lastfn[0] = '\0';
+ return;
+ }
+
+ if (strcmp (fn, lastfn))
+ {
+ g_strlcpy (lastfn, fn, sizeof (lastfn));
+ ppm_kill (&cache);
+ ppm_load (fn, &cache);
+ }
+ ppm_copy (&cache, p);
+ set_colorbrushes (fn);
+}
+
+static void
+padbrush (ppm_t *p,
+ gint width,
+ gint height)
+{
+ guchar black[3] = {0, 0, 0};
+
+ int left = (width - p->width) / 2;
+ int right = (width - p->width) - left;
+ int top = (height - p->height) / 2;
+ int bottom = (height - p->height) - top;
+
+ ppm_pad (p, left, right, top, bottom, black);
+}
+
+static void
+update_brush_preview (const gchar *fn)
+{
+ gint i, j;
+ guchar *preview_image;
+
+ if (fn)
+ brush_from_file = 1;
+
+ preview_image = g_new0 (guchar, 100*100);
+
+ if (!fn && brush_from_file)
+ {
+ /* preview_image is already initialized to our liking. */
+ }
+ else
+ {
+ double sc;
+ ppm_t p = {0, 0, NULL};
+ guchar gammatable[256];
+ int newheight;
+
+ if (brush_from_file)
+ brush_reload (fn, &p);
+ else if (PPM_IS_INITED (&brushppm))
+ ppm_copy (&brushppm, &p);
+
+ set_colorbrushes (fn);
+
+ sc = gtk_adjustment_get_value (brush_gamma_adjust);
+ if (sc != 1.0)
+ for (i = 0; i < 256; i++)
+ gammatable[i] = pow (i / 255.0, sc) * 255;
+ else
+ for (i = 0; i < 256; i++)
+ gammatable[i] = i;
+
+ newheight = p.height *
+ pow (10, gtk_adjustment_get_value (brush_aspect_adjust));
+
+ sc = p.width > newheight ? p.width : newheight;
+ sc = 100.0 / sc;
+ resize_fast (&p, p.width*sc,newheight*sc);
+ padbrush (&p, 100, 100);
+ for (i = 0; i < 100; i++)
+ {
+ int k = i * p.width * 3;
+ if (i < p.height)
+ for (j = 0; j < p.width; j++)
+ preview_image[i*100+j] = gammatable[p.col[k + j * 3]];
+ }
+ ppm_kill (&p);
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (brush_preview),
+ 0, 0, 100, 100,
+ GIMP_GRAY_IMAGE,
+ preview_image,
+ 100);
+
+ g_free (preview_image);
+}
+
+
+/*
+ * "force" implies here to change the brush even if it was the same.
+ * It is used for the initialization of the preview.
+ * */
+static void
+brush_select (GtkTreeSelection *selection, gboolean force)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *fname = NULL;
+ gchar *brush = NULL;
+
+ if (brush_dont_update)
+ goto cleanup;
+
+ if (brush_from_file == 0)
+ {
+ update_brush_preview (NULL);
+ goto cleanup;
+ }
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter, 0, &brush, -1);
+
+ /* Check if the same brush was selected twice, and if so
+ * break. Otherwise, the brush gamma and stuff would have been
+ * reset.
+ * */
+ if (last_selected_brush == NULL)
+ {
+ last_selected_brush = g_strdup (brush);
+ }
+ else
+ {
+ if (!strcmp (last_selected_brush, brush))
+ {
+ if (!force)
+ {
+ goto cleanup;
+ }
+ }
+ else
+ {
+ g_free (last_selected_brush);
+ last_selected_brush = g_strdup (brush);
+ }
+ }
+
+ brush_dont_update = TRUE;
+ gtk_adjustment_set_value (brush_gamma_adjust, 1.0);
+ gtk_adjustment_set_value (brush_aspect_adjust, 0.0);
+ brush_dont_update = FALSE;
+
+ if (brush)
+ {
+ fname = g_build_filename ("Brushes", brush, NULL);
+
+ g_strlcpy (pcvals.selected_brush,
+ fname, sizeof (pcvals.selected_brush));
+
+ update_brush_preview (fname);
+
+ }
+ }
+cleanup:
+ g_free (fname);
+ g_free (brush);
+}
+
+static void
+brush_select_file (GtkTreeSelection *selection, gpointer data)
+{
+ brush_from_file = 1;
+ preset_save_button_set_sensitive (TRUE);
+ brush_select (selection, FALSE);
+}
+
+static void
+brush_preview_size_allocate (GtkWidget *preview)
+{
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (brush_list));
+ brush_select (selection, TRUE);
+}
+
+static void
+brush_asepct_adjust_cb (GtkWidget *w, gpointer data)
+{
+ gimp_double_adjustment_update (GTK_ADJUSTMENT (w), data);
+ update_brush_preview (pcvals.selected_brush);
+}
+
+void
+create_brushpage (GtkNotebook *notebook)
+{
+ GtkWidget *box1, *box2, *box3, *thispage;
+ GtkWidget *view;
+ GtkWidget *tmpw, *table;
+ GtkWidget *frame;
+ GtkWidget *combo;
+ GtkWidget *label;
+ GtkSizeGroup *group;
+ GtkTreeSelection *selection;
+
+ label = gtk_label_new_with_mnemonic (_("_Brush"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box1, TRUE,TRUE,0);
+ gtk_widget_show (box1);
+
+ view = create_one_column_list (box1, brush_select_file);
+ brush_list = view;
+ brush_list_store =
+ GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ brush_preview = tmpw = gimp_preview_area_new ();
+ gtk_widget_set_size_request (brush_preview, 100, 100);
+ gtk_container_add (GTK_CONTAINER (frame), tmpw);
+ gtk_widget_show (tmpw);
+ g_signal_connect (brush_preview, "size-allocate",
+ G_CALLBACK (brush_preview_size_allocate), NULL);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_box_pack_end (GTK_BOX (box2), box3, FALSE, FALSE,0);
+ gtk_widget_show (box3);
+
+ tmpw = gtk_label_new (_("Gamma:"));
+ gtk_label_set_xalign (GTK_LABEL (tmpw), 0.0);
+ gtk_box_pack_start (GTK_BOX (box3), tmpw, FALSE, FALSE,0);
+ gtk_widget_show (tmpw);
+
+ brush_gamma_adjust = GTK_ADJUSTMENT (gtk_adjustment_new (pcvals.brushgamma,
+ 0.5, 3.0, 0.1, 0.1, 1.0));
+ tmpw = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, brush_gamma_adjust);
+ gtk_widget_set_size_request (GTK_WIDGET (tmpw), 100, 30);
+ gtk_scale_set_draw_value (GTK_SCALE (tmpw), FALSE);
+ gtk_scale_set_digits (GTK_SCALE (tmpw), 2);
+ gtk_box_pack_start (GTK_BOX (box3), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect_swapped (brush_gamma_adjust, "value-changed",
+ G_CALLBACK (update_brush_preview),
+ pcvals.selected_brush);
+
+ gimp_help_set_help_data
+ (tmpw, _("Changes the gamma (brightness) of the selected brush"), NULL);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (thispage), box3, FALSE, FALSE,0);
+ gtk_widget_show (box3);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ tmpw = gtk_label_new (_("Select:"));
+ gtk_label_set_xalign (GTK_LABEL (tmpw), 0.0);
+ gtk_box_pack_start (GTK_BOX (box3), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+
+ gtk_size_group_add_widget (group, tmpw);
+ g_object_unref (group);
+
+ combo = gimp_drawable_combo_box_new (validdrawable, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), -1,
+ G_CALLBACK (brushdmenuselect),
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (box3), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ tmpw = gtk_button_new_with_mnemonic (_("Save _as"));
+ gtk_box_pack_start (GTK_BOX (box3),tmpw, FALSE, FALSE, 0);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (savebrush), NULL);
+ gtk_widget_show (tmpw);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ brush_aspect_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Aspect ratio:"),
+ 150, -1, pcvals.brush_aspect,
+ -1.0, 1.0, 0.1, 0.1, 2,
+ TRUE, 0, 0,
+ _("Specifies the aspect ratio of the brush"),
+ NULL);
+ gtk_size_group_add_widget (group,
+ GIMP_SCALE_ENTRY_LABEL (brush_aspect_adjust));
+ g_signal_connect (brush_aspect_adjust, "value-changed",
+ G_CALLBACK (brush_asepct_adjust_cb), &pcvals.brush_aspect);
+
+ brush_relief_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Relief:"),
+ 150, -1, pcvals.brush_relief,
+ 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Specifies the amount of embossing to apply to the image (in percent)"),
+ NULL);
+ gtk_size_group_add_widget (group,
+ GIMP_SCALE_ENTRY_LABEL (brush_relief_adjust));
+ g_signal_connect (brush_relief_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.brush_relief);
+
+ brush_select (selection, FALSE);
+ readdirintolist ("Brushes", view, pcvals.selected_brush);
+
+ /*
+ * This is so the "changed signal won't get sent to the brushes' list
+ * and reset the gamma and stuff.
+ * */
+ gtk_widget_grab_focus (brush_list);
+
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
+
diff --git a/plug-ins/gimpressionist/brush.h b/plug-ins/gimpressionist/brush.h
new file mode 100644
index 0000000..65296f8
--- /dev/null
+++ b/plug-ins/gimpressionist/brush.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __BRUSH_H
+#define __BRUSH_H
+
+#include "ppmtool.h"
+
+void brush_store(void);
+void brush_restore(void);
+void brush_free(void);
+void create_brushpage(GtkNotebook *);
+void brush_get_selected (ppm_t *p);
+
+#endif /* #ifndef __BRUSH_H */
diff --git a/plug-ins/gimpressionist/color.c b/plug-ins/gimpressionist/color.c
new file mode 100644
index 0000000..9dc18d7
--- /dev/null
+++ b/plug-ins/gimpressionist/color.c
@@ -0,0 +1,106 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "color.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define NUMCOLORRADIO 2
+
+static GtkWidget *colorradio[NUMCOLORRADIO];
+static GtkObject *colornoiseadjust = NULL;
+
+void
+color_restore (void)
+{
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (colorradio[pcvals.color_type]), TRUE);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (colornoiseadjust),
+ pcvals.color_noise);
+}
+
+int
+color_type_input (int in)
+{
+ return CLAMP_UP_TO (in, NUMCOLORRADIO);
+}
+
+void
+create_colorpage (GtkNotebook *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *label, *table;
+ GtkWidget *frame;
+
+ label = gtk_label_new_with_mnemonic (_("Co_lor"));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Color"),
+ G_CALLBACK (gimp_radio_button_update),
+ &pcvals.color_type, 0,
+
+ _("A_verage under brush"),
+ COLOR_TYPE_AVERAGE, &colorradio[COLOR_TYPE_AVERAGE],
+ _("C_enter of brush"),
+ COLOR_TYPE_CENTER, &colorradio[COLOR_TYPE_CENTER],
+
+ NULL);
+
+ gimp_help_set_help_data
+ (colorradio[COLOR_TYPE_AVERAGE],
+ _("Color is computed from the average of all pixels under the brush"),
+ NULL);
+ gimp_help_set_help_data
+ (colorradio[COLOR_TYPE_CENTER],
+ _("Samples the color from the pixel in the center of the brush"), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ colornoiseadjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Color _noise:"),
+ 100, -1, pcvals.color_noise,
+ 0.0, 100.0, 1.0, 5.0, 0,
+ TRUE, 0, 0,
+ _("Adds random noise to the color"),
+ NULL);
+ g_signal_connect (colornoiseadjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.color_noise);
+
+ color_restore ();
+
+ gtk_notebook_append_page_menu (notebook, vbox, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/color.h b/plug-ins/gimpressionist/color.h
new file mode 100644
index 0000000..51ebdfa
--- /dev/null
+++ b/plug-ins/gimpressionist/color.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __COLOR_H
+#define __COLOR_H
+
+enum COLOR_TYPE_ENUM
+{
+ COLOR_TYPE_AVERAGE = 0,
+ COLOR_TYPE_CENTER = 1,
+};
+
+void create_colorpage (GtkNotebook *);
+void color_restore (void);
+int color_type_input (int in);
+
+#endif /* #ifndef __COLOR_H */
+
diff --git a/plug-ins/gimpressionist/general.c b/plug-ins/gimpressionist/general.c
new file mode 100644
index 0000000..5f58056
--- /dev/null
+++ b/plug-ins/gimpressionist/general.c
@@ -0,0 +1,283 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "infile.h"
+#include "general.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define COLORBUTTONWIDTH 30
+#define COLORBUTTONHEIGHT 20
+
+
+#define NUMGENERALBGRADIO 4
+
+static GtkWidget *general_bg_radio[NUMGENERALBGRADIO];
+static GtkWidget *general_paint_edges = NULL;
+static GtkObject *general_dark_edge_adjust = NULL;
+static GtkWidget *general_tileable;
+static GtkWidget *general_drop_shadow = NULL;
+static GtkWidget *general_color_button;
+static GtkObject *general_shadow_adjust = NULL;
+static GtkObject *general_shadow_depth = NULL;
+static GtkObject *general_shadow_blur = NULL;
+static GtkObject *dev_thresh_adjust = NULL;
+
+static int
+normalize_bg (int n)
+{
+ return (!img_has_alpha && (n == 3)) ? 1 : n;
+}
+
+static void
+general_bg_callback (GtkWidget *wg, void *d)
+{
+ pcvals.general_background_type = normalize_bg (GPOINTER_TO_INT (d));
+}
+
+void
+general_store (void)
+{
+ pcvals.general_paint_edges = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_paint_edges));
+ pcvals.general_dark_edge = gtk_adjustment_get_value (GTK_ADJUSTMENT (general_dark_edge_adjust));
+ pcvals.general_tileable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_tileable));
+ pcvals.general_drop_shadow = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (general_drop_shadow));
+ pcvals.general_shadow_darkness = gtk_adjustment_get_value (GTK_ADJUSTMENT (general_shadow_adjust));
+ pcvals.general_shadow_depth = gtk_adjustment_get_value (GTK_ADJUSTMENT (general_shadow_depth));
+ pcvals.general_shadow_blur = gtk_adjustment_get_value (GTK_ADJUSTMENT (general_shadow_blur));
+ pcvals.devthresh = gtk_adjustment_get_value (GTK_ADJUSTMENT (dev_thresh_adjust));
+}
+
+int
+general_bg_type_input (int in)
+{
+ return CLAMP_UP_TO (in, NUMGENERALBGRADIO);
+}
+
+void
+general_restore (void)
+{
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (general_bg_radio[normalize_bg (pcvals.general_background_type)]),
+ TRUE);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (general_paint_edges),
+ pcvals.general_paint_edges);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (general_dark_edge_adjust),
+ pcvals.general_dark_edge);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (general_shadow_adjust),
+ pcvals.general_shadow_darkness);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (general_drop_shadow),
+ pcvals.general_drop_shadow);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (general_shadow_depth),
+ pcvals.general_shadow_depth);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (general_shadow_blur),
+ pcvals.general_shadow_blur);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (general_tileable),
+ pcvals.general_tileable);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (general_color_button),
+ &pcvals.color);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (dev_thresh_adjust),
+ pcvals.devthresh);
+}
+
+static void
+select_color (GtkWidget *widget, gpointer data)
+{
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (general_bg_radio[BG_TYPE_SOLID]),
+ TRUE);
+}
+
+static GtkWidget *
+create_general_button (GtkWidget *box,
+ int idx,
+ const gchar *label,
+ const gchar *help_string,
+ GSList **radio_group)
+{
+ return create_radio_button (box, idx, general_bg_callback, label,
+ help_string, radio_group, general_bg_radio);
+}
+
+void
+create_generalpage (GtkNotebook *notebook)
+{
+ GtkWidget *box1, *box2, *box3, *box4, *thispage;
+ GtkWidget *label, *tmpw, *frame, *table;
+ GSList * radio_group = NULL;
+
+ label = gtk_label_new_with_mnemonic (_("_General"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ frame = gimp_frame_new (_("Background"));
+ gtk_box_pack_start (GTK_BOX (thispage), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), box3);
+ gtk_widget_show (box3);
+
+ create_general_button (box3,
+ BG_TYPE_KEEP_ORIGINAL,
+ _("Keep original"),
+ _("Preserve the original image as a background"),
+ &radio_group);
+
+ create_general_button (box3,
+ BG_TYPE_FROM_PAPER,
+ _("From paper"),
+ _("Copy the texture of the selected paper as a background"),
+ &radio_group);
+
+ box4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (box3), box4, FALSE, FALSE, 0);
+ gtk_widget_show (box4);
+
+ create_general_button (box4,
+ BG_TYPE_SOLID,
+ _("Solid"),
+ _("Solid colored background"),
+ &radio_group);
+
+ general_color_button = gimp_color_button_new (_("Color"),
+ COLORBUTTONWIDTH,
+ COLORBUTTONHEIGHT,
+ &pcvals.color,
+ GIMP_COLOR_AREA_FLAT);
+ g_signal_connect (general_color_button, "clicked",
+ G_CALLBACK (select_color), NULL);
+ g_signal_connect (general_color_button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &pcvals.color);
+ gtk_box_pack_start (GTK_BOX (box4), general_color_button, FALSE, FALSE, 0);
+ gtk_widget_show (general_color_button);
+
+ tmpw = create_general_button (box3,
+ BG_TYPE_TRANSPARENT,
+ _("Transparent"),
+ _("Use a transparent background; Only the strokes painted will be visible"),
+ &radio_group);
+
+ if (!img_has_alpha)
+ gtk_widget_set_sensitive (tmpw, FALSE);
+
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (general_bg_radio[pcvals.general_background_type]), TRUE);
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box1, FALSE, FALSE, 0);
+ gtk_widget_show (box1);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ tmpw = gtk_check_button_new_with_label ( _("Paint edges"));
+ general_paint_edges = tmpw;
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ gimp_help_set_help_data (tmpw,
+ _("Selects if to place strokes all the way out to the edges of the image"),
+ NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.general_paint_edges);
+
+ general_tileable = tmpw = gtk_check_button_new_with_label ( _("Tileable"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ gimp_help_set_help_data (tmpw,
+ _("Selects if the resulting image should be seamlessly tileable"),
+ NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.general_tileable);
+
+ tmpw = gtk_check_button_new_with_label ( _("Drop shadow"));
+ general_drop_shadow = tmpw;
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ gimp_help_set_help_data (tmpw,
+ _("Adds a shadow effect to each brush stroke"),
+ NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.general_drop_shadow);
+
+ table = gtk_table_new (5, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (box1), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ general_dark_edge_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Edge darken:"),
+ 150, 6, pcvals.general_dark_edge,
+ 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("How much to \"darken\" the edges of each brush stroke"),
+ NULL);
+
+ general_shadow_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Shadow darken:"),
+ 150, 6, pcvals.general_shadow_darkness,
+ 0.0, 99.0, 0.1, 1, 2,
+ TRUE, 0, 0,
+ _("How much to \"darken\" the drop shadow"),
+ NULL);
+
+ general_shadow_depth =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Shadow depth:"),
+ 150, 6, pcvals.general_shadow_depth,
+ 0, 99, 1, 5, 0,
+ TRUE, 0, 0,
+ _("The depth of the drop shadow, i.e. how far apart from the object it should be"),
+ NULL);
+
+ general_shadow_blur =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 3,
+ _("Shadow blur:"),
+ 150, 6, pcvals.general_shadow_blur,
+ 0, 99, 1, 5, 0,
+ TRUE, 0, 0,
+ _("How much to blur the drop shadow"),
+ NULL);
+
+ dev_thresh_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 4,
+ _("Deviation threshold:"),
+ 150, 6, pcvals.devthresh,
+ 0.0, 1.0, 0.01, 0.01, 2,
+ TRUE, 0, 0,
+ _("A bailout-value for adaptive selections"),
+ NULL);
+
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/general.h b/plug-ins/gimpressionist/general.h
new file mode 100644
index 0000000..a706c7e
--- /dev/null
+++ b/plug-ins/gimpressionist/general.h
@@ -0,0 +1,26 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GENERAL_H
+#define __GENERAL_H
+
+void general_restore(void);
+void general_store(void);
+void create_generalpage(GtkNotebook *);
+int general_bg_type_input (int in);
+
+#endif /* #ifndef __GENERAL_H */
diff --git a/plug-ins/gimpressionist/gimp.c b/plug-ins/gimpressionist/gimp.c
new file mode 100644
index 0000000..fea302d
--- /dev/null
+++ b/plug-ins/gimpressionist/gimp.c
@@ -0,0 +1,535 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "ppmtool.h"
+#include "infile.h"
+#include "gimpressionist.h"
+#include "preview.h"
+#include "brush.h"
+#include "presets.h"
+#include "random.h"
+#include "orientmap.h"
+#include "size.h"
+
+
+#include "libgimp/stdplugins-intl.h"
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static void gimpressionist_main (void);
+
+
+const GimpPlugInInfo PLUG_IN_INFO = {
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+}; /* PLUG_IN_INFO */
+
+static gint32 drawable_id;
+static ppm_t infile = {0, 0, NULL};
+static ppm_t inalpha = {0, 0, NULL};
+
+
+void
+infile_copy_to_ppm (ppm_t * p)
+{
+ if (!PPM_IS_INITED (&infile))
+ grabarea ();
+
+ ppm_copy (&infile, p);
+}
+
+void
+infile_copy_alpha_to_ppm (ppm_t * p)
+{
+ ppm_copy (&inalpha, p);
+}
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "preset", "Preset Name" },
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Performs various artistic operations"),
+ "Performs various artistic operations on an image",
+ "Vidar Madsen <vidar@prosalg.no>",
+ "Vidar Madsen",
+ PLUG_IN_VERSION,
+ N_("_GIMPressionist..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Artistic");
+}
+
+static void
+gimpressionist_get_data (void)
+{
+ restore_default_values ();
+ gimp_get_data (PLUG_IN_PROC, &pcvals);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status;
+ gboolean with_specified_preset;
+ gchar *preset_name = NULL;
+
+ status = GIMP_PDB_SUCCESS;
+ run_mode = param[0].data.d_int32;
+ with_specified_preset = FALSE;
+
+ if (nparams > 3)
+ {
+ preset_name = param[3].data.d_string;
+ if (strcmp (preset_name, ""))
+ {
+ with_specified_preset = TRUE;
+ }
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ /* Get the active drawable info */
+
+ drawable_id = param[2].data.d_drawable;
+ img_has_alpha = gimp_drawable_has_alpha (drawable_id);
+
+ random_generator = g_rand_new ();
+
+ /*
+ * Check precondition before we open a dialog: Is there a selection
+ * that intersects, OR is there no selection (use entire drawable.)
+ */
+ {
+ gint x1, y1, width, height; /* Not used. */
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("The selection does not intersect "
+ "the active layer or mask.");
+
+ return;
+ }
+ }
+
+
+ switch (run_mode)
+ {
+ /*
+ * Note: there's a limitation here. Running this plug-in before the
+ * interactive plug-in was run will cause it to crash, because the
+ * data is uninitialized.
+ * */
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_NONINTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimpressionist_get_data ();
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (!create_gimpressionist ())
+ return;
+ }
+ break;
+ default:
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+ }
+ if ((status == GIMP_PDB_SUCCESS) &&
+ (gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id)))
+ {
+
+ if (with_specified_preset)
+ {
+ /* If select_preset fails - set to an error */
+ if (select_preset (preset_name))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ /* It seems that the value of the run variable is stored in
+ * the preset. I don't know if it's a bug or a feature, but
+ * I just work here and am anxious to get a working version.
+ * So I'm setting it to the correct value here.
+ *
+ * It also seems that defaultpcvals have this erroneous
+ * value as well, so it gets set to FALSE as well. Thus it
+ * is always set to TRUE upon a non-interactive run.
+ * -- Shlomi Fish
+ * */
+ if (run_mode == GIMP_RUN_NONINTERACTIVE)
+ {
+ pcvals.run = TRUE;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gimpressionist_main ();
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC,
+ &pcvals,
+ sizeof (gimpressionist_vals_t));
+ }
+ }
+ else if (status == GIMP_PDB_SUCCESS)
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ /* Resources Cleanup */
+ g_rand_free (random_generator);
+ free_parsepath_cache ();
+ brush_reload (NULL, NULL);
+ preview_free_resources ();
+ brush_free ();
+ preset_free ();
+ orientation_map_free_resources ();
+ size_map_free_resources ();
+
+ values[0].data.d_status = status;
+}
+
+static const Babl *
+get_u8_format (gint32 drawable_id)
+{
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("R'G'B'A u8");
+ else
+ return babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (gimp_drawable_has_alpha (drawable_id))
+ return babl_format ("Y'A u8");
+ else
+ return babl_format ("Y' u8");
+ }
+}
+
+void
+grabarea (void)
+{
+ GeglBuffer *src_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint bpp;
+ ppm_t *p;
+ gint x1, y1;
+ gint x, y;
+ gint width, height;
+ gint row, col;
+ gint rowstride;
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ ppm_new (&infile, width, height);
+ p = &infile;
+
+ format = get_u8_format (drawable_id);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ if (gimp_drawable_has_alpha (drawable_id))
+ ppm_new (&inalpha, width, height);
+
+ rowstride = p->width * 3;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_id);
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[0].roi;
+ const guchar *src = iter->items[0].data;
+
+ switch (bpp)
+ {
+ case 1:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ const guchar *s = src;
+ guchar *tmprow = p->col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+
+ tmprow[k + 0] = s[0];
+ tmprow[k + 1] = s[0];
+ tmprow[k + 2] = s[0];
+
+ s++;
+ }
+
+ src += bpp * roi.width;
+ }
+ break;
+
+ case 2:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ const guchar *s = src;
+ guchar *tmprow = p->col + row * rowstride;
+ guchar *tmparow = inalpha.col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+
+ tmprow[k + 0] = s[0];
+ tmprow[k + 1] = s[0];
+ tmprow[k + 2] = s[0];
+ tmparow[k] = 255 - s[1];
+
+ s += 2;
+ }
+
+ src += bpp * roi.width;
+ }
+ break;
+
+ case 3:
+ col = roi.x - x1;
+
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ memcpy (p->col + row * rowstride + col * 3, src, roi.width * 3);
+
+ src += bpp * roi.width;
+ }
+ break;
+
+ case 4:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ const guchar *s = src;
+ guchar *tmprow = p->col + row * rowstride;
+ guchar *tmparow = inalpha.col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+
+ tmprow[k + 0] = s[0];
+ tmprow[k + 1] = s[1];
+ tmprow[k + 2] = s[2];
+ tmparow[k] = 255 - s[3];
+
+ s += 4;
+ }
+
+ src += bpp * roi.width;
+ }
+ break;
+ }
+ }
+
+ g_object_unref (src_buffer);
+}
+
+static void
+gimpressionist_main (void)
+{
+ GeglBuffer *dest_buffer;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint bpp;
+ ppm_t *p;
+ gint x1, y1;
+ gint x, y;
+ gint width, height;
+ gint row, col;
+ gint rowstride;
+ glong done = 0;
+ glong total;
+
+ if (! gimp_drawable_mask_intersect (drawable_id,
+ &x1, &y1, &width, &height))
+ return;
+
+ total = width * height;
+
+ format = get_u8_format (drawable_id);
+ bpp = babl_format_get_bytes_per_pixel (format);
+
+ gimp_progress_init (_("Painting"));
+
+ if (! PPM_IS_INITED (&infile))
+ grabarea ();
+
+ repaint (&infile, (img_has_alpha) ? &inalpha : NULL);
+
+ p = &infile;
+
+ rowstride = p->width * 3;
+
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+
+ iter = gegl_buffer_iterator_new (dest_buffer,
+ GEGL_RECTANGLE (x1, y1, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ GeglRectangle roi = iter->items[0].roi;
+ guchar *dest = iter->items[0].data;
+
+ switch (bpp)
+ {
+ case 1:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ guchar *d = dest;
+ const guchar *tmprow = p->col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+
+ *d++ = GIMP_RGB_LUMINANCE (tmprow[k + 0],
+ tmprow[k + 1],
+ tmprow[k + 2]);
+ }
+
+ dest += bpp * roi.width;
+ }
+ break;
+
+ case 2:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ guchar *d = dest;
+ const guchar *tmprow = p->col + row * rowstride;
+ const guchar *tmparow = inalpha.col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+ gint value = GIMP_RGB_LUMINANCE (tmprow[k + 0],
+ tmprow[k + 1],
+ tmprow[k + 2]);
+
+ d[0] = value;
+ d[1] = 255 - tmparow[k];
+
+ d += 2;
+ }
+
+ dest += bpp * roi.width;
+ }
+ break;
+
+ case 3:
+ col = roi.x - x1;
+
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ memcpy (dest, p->col + row * rowstride + col * 3, roi.width * 3);
+
+ dest += bpp * roi.width;
+ }
+ break;
+
+ case 4:
+ for (y = 0, row = roi.y - y1; y < roi.height; y++, row++)
+ {
+ guchar *d = dest;
+ const guchar *tmprow = p->col + row * rowstride;
+ const guchar *tmparow = inalpha.col + row * rowstride;
+
+ for (x = 0, col = roi.x - x1; x < roi.width; x++, col++)
+ {
+ gint k = col * 3;
+
+ d[0] = tmprow[k + 0];
+ d[1] = tmprow[k + 1];
+ d[2] = tmprow[k + 2];
+ d[3] = 255 - tmparow[k];
+
+ d += 4;
+ }
+
+ dest += bpp * roi.width;
+ }
+ break;
+ }
+
+ done += roi.width * roi.height;
+
+ gimp_progress_update (0.8 + 0.2 * done / total);
+ }
+
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, x1, y1, width, height);
+}
diff --git a/plug-ins/gimpressionist/gimpressionist.c b/plug-ins/gimpressionist/gimpressionist.c
new file mode 100644
index 0000000..905149b
--- /dev/null
+++ b/plug-ins/gimpressionist/gimpressionist.c
@@ -0,0 +1,207 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+/*
+ * The Page Specific Imports
+ * */
+#include "brush.h"
+#include "color.h"
+#include "general.h"
+#include "orientation.h"
+#include "orientmap.h"
+#include "placement.h"
+#include "preview.h"
+#include "size.h"
+#include "paper.h"
+#include "presets.h"
+
+#include "ppmtool.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static GtkWidget *dialog = NULL;
+
+void
+store_values (void)
+{
+ paper_store ();
+ brush_store ();
+ general_store ();
+}
+
+void
+restore_values (void)
+{
+ brush_restore ();
+ paper_restore ();
+ orientation_restore ();
+ size_restore ();
+ place_restore ();
+ general_restore ();
+ color_restore ();
+
+ update_orientmap_dialog ();
+}
+
+GtkWidget *
+create_one_column_list (GtkWidget *parent,
+ void (*changed_cb) (GtkTreeSelection *selection,
+ gpointer data))
+{
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *swin, *view;
+
+ swin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (parent), swin, FALSE, FALSE, 0);
+ gtk_widget_show (swin);
+ gtk_widget_set_size_request (swin, 150,-1);
+
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+ view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+ g_object_unref (store);
+ gtk_widget_show (view);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Preset", renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+ gtk_container_add (GTK_CONTAINER (swin), view);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (selection, "changed", G_CALLBACK (changed_cb), NULL);
+
+ return view;
+}
+
+static void
+dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ store_values ();
+ pcvals.run = TRUE;
+ gtk_widget_destroy (widget);
+ break;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+static GtkWidget *
+create_dialog (void)
+{
+ GtkWidget *notebook;
+ GtkWidget *hbox;
+ GtkWidget *preview_box;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("GIMPressionist"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (dialog_response),
+ NULL);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ preview_box = create_preview ();
+ gtk_box_pack_start (GTK_BOX (hbox), preview_box, FALSE, FALSE, 0);
+ gtk_widget_show (preview_box);
+
+ notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 5);
+ gtk_widget_show (notebook);
+
+ create_presetpage (GTK_NOTEBOOK (notebook));
+ create_paperpage (GTK_NOTEBOOK (notebook));
+ create_brushpage (GTK_NOTEBOOK (notebook));
+ create_orientationpage (GTK_NOTEBOOK (notebook));
+ create_sizepage (GTK_NOTEBOOK (notebook));
+ create_placementpage (GTK_NOTEBOOK (notebook));
+ create_colorpage (GTK_NOTEBOOK (notebook));
+ create_generalpage (GTK_NOTEBOOK (notebook));
+
+ updatepreview (NULL, NULL);
+
+ /*
+ * This is to make sure the values from the pcvals will be reflected
+ * in the GUI here. Otherwise they will be set to the defaults.
+ * */
+ restore_values ();
+
+ gtk_widget_show (dialog);
+
+ return dialog;
+}
+
+gint
+create_gimpressionist (void)
+{
+ pcvals.run = FALSE;
+
+ create_dialog ();
+
+ gtk_main ();
+
+ return pcvals.run;
+}
diff --git a/plug-ins/gimpressionist/gimpressionist.h b/plug-ins/gimpressionist/gimpressionist.h
new file mode 100644
index 0000000..ba944e0
--- /dev/null
+++ b/plug-ins/gimpressionist/gimpressionist.h
@@ -0,0 +1,181 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMPRESSIONIST_H
+#define __GIMPRESSIONIST_H
+
+/* Includes necessary for the correct processing of this file. */
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "ppmtool.h"
+/* Defines */
+
+#define PLUG_IN_PROC "plug-in-gimpressionist"
+#define PLUG_IN_VERSION "v1.0, November 2003"
+#define PLUG_IN_BINARY "gimpressionist"
+#define PLUG_IN_ROLE "gimp-gimpressionist"
+
+#define PREVIEWSIZE 150
+#define MAXORIENTVECT 50
+#define MAXSIZEVECT 50
+
+/* Type declaration and definitions */
+
+typedef struct vector
+{
+ double x, y;
+ double dir;
+ double dx, dy;
+ double str;
+ int type;
+} vector_t;
+
+typedef struct smvector
+{
+ double x, y;
+ double siz;
+ double str;
+} smvector_t;
+
+typedef struct
+{
+ int orient_num;
+ double orient_first;
+ double orient_last;
+ int orient_type;
+ double brush_relief;
+ double brush_scale;
+ double brush_density;
+ double brushgamma;
+ int general_background_type;
+ double general_dark_edge;
+ double paper_relief;
+ double paper_scale;
+ int paper_invert;
+ int run;
+ char selected_brush[200];
+ char selected_paper[200];
+ GimpRGB color;
+ int general_paint_edges;
+ int place_type;
+ vector_t orient_vectors[MAXORIENTVECT];
+ int num_orient_vectors;
+ int placement_center;
+ double brush_aspect;
+ double orient_angle_offset;
+ double orient_strength_exponent;
+ int general_tileable;
+ int paper_overlay;
+ int orient_voronoi;
+ int color_brushes;
+ int general_drop_shadow;
+ double general_shadow_darkness;
+ int size_num;
+ double size_first;
+ double size_last;
+ int size_type;
+ double devthresh;
+
+ smvector_t size_vectors[MAXSIZEVECT];
+ int num_size_vectors;
+ double size_strength_exponent;
+ int size_voronoi;
+
+ int general_shadow_depth;
+ int general_shadow_blur;
+
+ int color_type;
+ double color_noise;
+} gimpressionist_vals_t;
+
+/* Enumerations */
+
+enum GENERAL_BG_TYPE_ENUM
+{
+ BG_TYPE_SOLID = 0,
+ BG_TYPE_KEEP_ORIGINAL = 1,
+ BG_TYPE_FROM_PAPER = 2,
+ BG_TYPE_TRANSPARENT = 3,
+};
+
+enum PRESETS_LIST_COLUMN_ENUM
+{
+ PRESETS_LIST_COLUMN_FILENAME = 0,
+ PRESETS_LIST_COLUMN_OBJECT_NAME = 1,
+};
+
+/* Globals */
+
+extern gimpressionist_vals_t pcvals;
+
+
+/* Prototypes */
+
+GList *parsepath (void);
+void free_parsepath_cache (void);
+
+void grabarea (void);
+void store_values (void);
+void restore_values (void);
+gchar *findfile (const gchar *);
+
+void unselectall (GtkWidget *list);
+void reselect (GtkWidget *list, char *fname);
+void readdirintolist (const char *subdir, GtkWidget *view, char *selected);
+void readdirintolist_extended (const char *subdir,
+ GtkWidget *view, char *selected,
+ gboolean with_filename_column,
+ gchar *(*get_object_name_cb) (const gchar *dir,
+ gchar *filename,
+ void *context),
+ void * context);
+
+GtkWidget *create_one_column_list (GtkWidget *parent,
+ void (*changed_cb)
+ (GtkTreeSelection *selection,
+ gpointer data));
+
+void brush_reload (const gchar *fn, struct ppm *p);
+
+double get_direction (double x, double y, int from);
+
+void create_sizemap_dialog (GtkWidget *parent);
+double getsiz_proto (double x, double y, int n, smvector_t *vec,
+ double smstrexp, int voronoi);
+
+
+void set_colorbrushes (const gchar *fn);
+int create_gimpressionist (void);
+
+double dist (double x, double y, double dx, double dy);
+
+void restore_default_values (void);
+
+GtkWidget *create_radio_button (GtkWidget *box, int orient_type,
+ void (*callback)(GtkWidget *wg, void *d),
+ const gchar *label,
+ const gchar *help_string,
+ GSList **radio_group,
+ GtkWidget **buttons_array
+ );
+
+#define CLAMP_UP_TO(x, max) (CLAMP((x),(0),(max-1)))
+
+#endif /* #ifndef __GIMPRESSIONIST_H */
+
+
diff --git a/plug-ins/gimpressionist/globals.c b/plug-ins/gimpressionist/globals.c
new file mode 100644
index 0000000..8b4bf5e
--- /dev/null
+++ b/plug-ins/gimpressionist/globals.c
@@ -0,0 +1,86 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+
+gboolean img_has_alpha = FALSE;
+GRand *random_generator;
+gimpressionist_vals_t pcvals;
+
+/*
+ * The default values for the application, to be initialized at startup.
+ * */
+static const gimpressionist_vals_t defaultpcvals = {
+ 4,
+ 0.0,
+ 60.0,
+ 0,
+ 12.0,
+ 20.0,
+ 20.0,
+ 1.0,
+ 1,
+ 0.1,
+ 0.0,
+ 30.0,
+ 0,
+ 0,
+ "defaultbrush.pgm",
+ "defaultpaper.pgm",
+ {0,0,0,1.0},
+ 1,
+ 0,
+ { { 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 0 } },
+ 1,
+ 0,
+ 0.0,
+ 0.0,
+ 1.0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 20.0,
+ 1,
+ 10.0,
+ 20.0,
+ 0,
+ 0.1,
+
+ { { 0.5, 0.5, 50.0, 1.0 } },
+ 1,
+ 1.0,
+ 0,
+
+ 10,
+ 4,
+
+ 0, 0.0
+};
+
+void
+restore_default_values (void)
+{
+ pcvals = defaultpcvals;
+}
diff --git a/plug-ins/gimpressionist/infile.h b/plug-ins/gimpressionist/infile.h
new file mode 100644
index 0000000..b263428
--- /dev/null
+++ b/plug-ins/gimpressionist/infile.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __INFILE_H
+#define __INFILE_H
+
+#include "ppmtool.h"
+
+/* Globals */
+
+extern gboolean img_has_alpha;
+
+/* Prototypes */
+
+void infile_copy_to_ppm(ppm_t * p);
+void infile_copy_alpha_to_ppm(ppm_t * p);
+
+#endif
diff --git a/plug-ins/gimpressionist/orientation.c b/plug-ins/gimpressionist/orientation.c
new file mode 100644
index 0000000..1a5183d
--- /dev/null
+++ b/plug-ins/gimpressionist/orientation.c
@@ -0,0 +1,206 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "orientation.h"
+#include "orientmap.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static GtkWidget *orient_radio[NUMORIENTRADIO];
+static GtkObject *orient_num_adjust = NULL;
+static GtkObject *orient_first_adjust = NULL;
+static GtkObject *orient_last_adjust = NULL;
+
+
+static void
+orientation_store (GtkWidget *wg, void *d)
+{
+ pcvals.orient_type = GPOINTER_TO_INT (d);
+}
+
+int orientation_type_input (int in)
+{
+ return CLAMP_UP_TO (in, NUMORIENTRADIO);
+}
+
+void orientation_restore (void)
+{
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (orient_radio[pcvals.orient_type]),
+ TRUE);
+ gtk_adjustment_set_value (
+ GTK_ADJUSTMENT (orient_num_adjust),
+ pcvals.orient_num);
+ gtk_adjustment_set_value (
+ GTK_ADJUSTMENT (orient_first_adjust),
+ pcvals.orient_first);
+ gtk_adjustment_set_value (
+ GTK_ADJUSTMENT (orient_last_adjust),
+ pcvals.orient_last);
+}
+
+static void
+create_orientmap_dialog_helper (GtkWidget *widget)
+{
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (orient_radio[7]), TRUE);
+
+ create_orientmap_dialog (widget);
+}
+
+
+static void
+create_orientradio_button (GtkWidget *box,
+ int orient_type,
+ const gchar *label,
+ const gchar *help_string,
+ GSList **radio_group)
+{
+ create_radio_button (box, orient_type, orientation_store, label,
+ help_string, radio_group, orient_radio);
+}
+
+void
+create_orientationpage (GtkNotebook *notebook)
+{
+ GtkWidget *box2, *box3, *box4, *thispage;
+ GtkWidget *label, *tmpw, *table;
+ GSList *radio_group = NULL;
+
+ label = gtk_label_new_with_mnemonic (_("Or_ientation"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ orient_num_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Directions:"),
+ 150, -1, pcvals.orient_num,
+ 1.0, 30.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ _("The number of directions (i.e. brushes) to use"),
+ NULL);
+ g_signal_connect (orient_num_adjust, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &pcvals.orient_num);
+
+ orient_first_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Start angle:"),
+ 150, -1, pcvals.orient_first,
+ 0.0, 360.0, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("The starting angle of the first brush to create"),
+ NULL);
+ g_signal_connect (orient_first_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.orient_first);
+
+ orient_last_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Angle span:"),
+ 150, -1, pcvals.orient_last,
+ 0.0, 360.0, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("The angle span of the first brush to create"),
+ NULL);
+ g_signal_connect (orient_last_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.orient_last);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box2), box3, FALSE, FALSE, 0);
+ gtk_widget_show (box3);
+
+ tmpw = gtk_label_new (_("Orientation:"));
+ gtk_box_pack_start (GTK_BOX (box3), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box2), box3, FALSE, FALSE, 0);
+ gtk_widget_show (box3);
+
+ create_orientradio_button (box3, ORIENTATION_VALUE, _("Value"),
+ _("Let the value (brightness) of the region determine the direction of the stroke"),
+ &radio_group);
+
+ create_orientradio_button (box3, ORIENTATION_RADIUS, _("Radius"),
+ _("The distance from the center of the image determines the direction of the stroke"),
+ &radio_group);
+
+ create_orientradio_button (box3, ORIENTATION_RANDOM, _("Random"),
+ _("Selects a random direction of each stroke"),
+ &radio_group);
+
+ create_orientradio_button (box3, ORIENTATION_RADIAL, _("Radial"),
+ _("Let the direction from the center determine the direction of the stroke"),
+ &radio_group);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box2), box3, FALSE, FALSE, 0);
+ gtk_widget_show (box3);
+
+ create_orientradio_button (box3, ORIENTATION_FLOWING, _("Flowing"),
+ _("The strokes follow a \"flowing\" pattern"),
+ &radio_group);
+
+ create_orientradio_button (box3, ORIENTATION_HUE, _("Hue"),
+ _("The hue of the region determines the direction of the stroke"),
+ &radio_group);
+
+ create_orientradio_button (box3, ORIENTATION_ADAPTIVE, _("Adaptive"),
+ _("The direction that matches the original image the closest is selected"),
+ &radio_group);
+
+ box4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (box3), box4, FALSE, FALSE, 0);
+ gtk_widget_show (box4);
+
+ create_orientradio_button (box4, ORIENTATION_MANUAL, _("Manual"),
+ _("Manually specify the stroke orientation"),
+ &radio_group);
+
+ orientation_restore ();
+
+ tmpw = gtk_button_new_with_mnemonic (_("_Edit"));
+ gtk_box_pack_start (GTK_BOX (box4), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (create_orientmap_dialog_helper), NULL);
+ gimp_help_set_help_data
+ (tmpw, _("Opens up the Orientation Map Editor"), NULL);
+
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/orientation.h b/plug-ins/gimpressionist/orientation.h
new file mode 100644
index 0000000..6355f59
--- /dev/null
+++ b/plug-ins/gimpressionist/orientation.h
@@ -0,0 +1,39 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ORIENTATION_H
+#define __ORIENTATION_H
+
+#define NUMORIENTRADIO 8
+
+enum ORIENTATION_ENUM
+{
+ ORIENTATION_VALUE = 0,
+ ORIENTATION_RADIUS = 1,
+ ORIENTATION_RANDOM = 2,
+ ORIENTATION_RADIAL = 3,
+ ORIENTATION_FLOWING = 4,
+ ORIENTATION_HUE = 5,
+ ORIENTATION_ADAPTIVE = 6,
+ ORIENTATION_MANUAL = 7,
+};
+
+void create_orientationpage (GtkNotebook *);
+void orientation_restore (void);
+int orientation_type_input (int in);
+
+#endif /* #ifndef __ORIENTATION_H */
diff --git a/plug-ins/gimpressionist/orientmap.c b/plug-ins/gimpressionist/orientmap.c
new file mode 100644
index 0000000..1b8b765
--- /dev/null
+++ b/plug-ins/gimpressionist/orientmap.c
@@ -0,0 +1,717 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "infile.h"
+
+#include "preview.h"
+
+#include "orientmap.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define NUMVECTYPES 4
+
+static GtkWidget *orient_map_window;
+
+static GtkWidget *vector_preview;
+static GtkWidget *orient_map_preview_prev;
+static GtkWidget *prev_button;
+static GtkWidget *next_button;
+static GtkWidget *add_button;
+static GtkWidget *kill_button;
+static GtkAdjustment *vector_preview_brightness_adjust = NULL;
+
+static GtkAdjustment *angle_adjust = NULL;
+static GtkAdjustment *strength_adjust = NULL;
+static GtkAdjustment *orient_map_str_exp_adjust = NULL;
+static GtkAdjustment *angle_offset_adjust = NULL;
+static GtkWidget *vector_types[NUMVECTYPES];
+static GtkWidget *orient_voronoi = NULL;
+
+#define OMWIDTH 150
+#define OMHEIGHT 150
+
+static vector_t vector[MAXORIENTVECT];
+static gint num_vectors = 0;
+static gint vector_type;
+
+static ppm_t update_om_preview_nbuffer = {0, 0, NULL};
+
+static gint selectedvector = 0;
+static ppm_t update_vector_preview_backup = {0, 0, NULL};
+static ppm_t update_vector_preview_buffer = {0, 0, NULL};
+
+static gboolean adjignore = FALSE;
+
+double get_direction (double x, double y, int from)
+{
+ gint i;
+ gint n;
+ gint voronoi;
+ gdouble sum, dx, dy, dst;
+ vector_t *vec;
+ gdouble angoff, strexp;
+ gint first = 0, last;
+
+ if (from == 0)
+ {
+ n = num_vectors;
+ vec = vector;
+ angoff = gtk_adjustment_get_value (angle_offset_adjust);
+ strexp = gtk_adjustment_get_value (orient_map_str_exp_adjust);
+ voronoi = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (orient_voronoi));
+ }
+ else
+ {
+ n = pcvals.num_orient_vectors;
+ vec = pcvals.orient_vectors;
+ angoff = pcvals.orient_angle_offset;
+ strexp = pcvals.orient_strength_exponent;
+ voronoi = pcvals.orient_voronoi;
+ }
+
+ if (voronoi)
+ {
+ gdouble bestdist = -1.0;
+
+ for (i = 0; i < n; i++)
+ {
+ dst = dist (x,y,vec[i].x,vec[i].y);
+
+ if ((bestdist < 0.0) || (dst < bestdist))
+ {
+ bestdist = dst;
+ first = i;
+ }
+ }
+ last = first+1;
+ }
+ else
+ {
+ first = 0;
+ last = n;
+ }
+
+ dx = dy = 0.0;
+ sum = 0.0;
+ for (i = first; i < last; i++)
+ {
+ gdouble s = vec[i].str;
+ gdouble tx = 0.0, ty = 0.0;
+
+ if (vec[i].type == 0)
+ {
+ tx = vec[i].dx;
+ ty = vec[i].dy;
+ }
+ else if (vec[i].type == 1)
+ {
+ gdouble a = atan2 (vec[i].dy, vec[i].dx);
+
+ a -= atan2 (y-vec[i].y, x-vec[i].x);
+ tx = sin (a + G_PI_2);
+ ty = cos (a + G_PI_2);
+ }
+ else if (vec[i].type == 2)
+ {
+ gdouble a = atan2 (vec[i].dy, vec[i].dx);
+
+ a += atan2 (y-vec[i].y, x-vec[i].x);
+ tx = sin (a + G_PI_2);
+ ty = cos (a + G_PI_2);
+ }
+ else if (vec[i].type == 3)
+ {
+ gdouble a = atan2 (vec[i].dy, vec[i].dx);
+
+ a -= atan2 (y-vec[i].y, x-vec[i].x)*2;
+ tx = sin (a + G_PI_2);
+ ty = cos (a + G_PI_2);
+ }
+
+ dst = dist (x,y,vec[i].x,vec[i].y);
+ dst = pow (dst, strexp);
+
+ if (dst < 0.0001)
+ dst = 0.0001;
+ s = s / dst;
+
+ dx += tx * s;
+ dy += ty * s;
+ sum += s;
+ }
+ dx = dx / sum;
+ dy = dy / sum;
+
+ return 90 - (gimp_rad_to_deg (atan2 (dy, dx)) + angoff);
+}
+
+static void
+update_orient_map_preview_prev (void)
+{
+ int x, y;
+ guchar black[3] = {0, 0, 0};
+ guchar gray[3] = {120, 120, 120};
+ guchar white[3] = {255, 255, 255};
+
+ if (!PPM_IS_INITED (&update_om_preview_nbuffer))
+ ppm_new (&update_om_preview_nbuffer,OMWIDTH,OMHEIGHT);
+
+ fill (&update_om_preview_nbuffer, black);
+
+ for (y = 6; y < OMHEIGHT-4; y += 10)
+ for (x = 6; x < OMWIDTH-4; x += 10)
+ {
+ double dir =
+ gimp_deg_to_rad (get_direction (x / (double)OMWIDTH,
+ y / (double)OMHEIGHT,0));
+ double xo = sin (dir) * 4.0;
+ double yo = cos (dir) * 4.0;
+ ppm_drawline (&update_om_preview_nbuffer,
+ x - xo, y - yo, x + xo, y + yo,
+ gray);
+ ppm_put_rgb (&update_om_preview_nbuffer,
+ x - xo, y - yo,
+ white);
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (orient_map_preview_prev),
+ 0, 0, OMWIDTH, OMHEIGHT,
+ GIMP_RGB_IMAGE,
+ (guchar *)update_om_preview_nbuffer.col,
+ OMWIDTH * 3);
+
+ gtk_widget_queue_draw (orient_map_preview_prev);
+
+ gtk_widget_set_sensitive (prev_button, (num_vectors > 1));
+ gtk_widget_set_sensitive (next_button, (num_vectors > 1));
+ gtk_widget_set_sensitive (add_button, (num_vectors < MAXORIENTVECT));
+ gtk_widget_set_sensitive (kill_button, (num_vectors > 1));
+}
+
+static void
+update_vector_prev (void)
+{
+ static gint ok = 0;
+ gint i, x, y;
+ gdouble dir, xo, yo;
+ gdouble val;
+ static gdouble last_val = 0.0;
+ guchar gray[3] = {120, 120, 120};
+ guchar red[3] = {255, 0, 0};
+ guchar white[3] = {255, 255, 255};
+
+ if (vector_preview_brightness_adjust)
+ val = 1.0 - gtk_adjustment_get_value (vector_preview_brightness_adjust) / 100.0;
+ else
+ val = 0.5;
+
+ if (!ok || (val != last_val))
+ {
+ infile_copy_to_ppm (&update_vector_preview_backup);
+ ppm_apply_brightness (&update_vector_preview_backup, val, 1,1,1);
+
+ if ((update_vector_preview_backup.width != OMWIDTH) ||
+ (update_vector_preview_backup.height != OMHEIGHT))
+ resize_fast (&update_vector_preview_backup, OMWIDTH, OMHEIGHT);
+ ok = 1;
+ }
+ ppm_copy (&update_vector_preview_backup, &update_vector_preview_buffer);
+
+ for (i = 0; i < num_vectors; i++)
+ {
+ gdouble s;
+
+ x = vector[i].x * OMWIDTH;
+ y = vector[i].y * OMHEIGHT;
+ dir = gimp_deg_to_rad (vector[i].dir);
+ s = gimp_deg_to_rad (vector[i].str);
+ xo = sin (dir) * (6.0+100*s);
+ yo = cos (dir) * (6.0+100*s);
+
+ if (i == selectedvector)
+ {
+ ppm_drawline (&update_vector_preview_buffer,
+ x - xo, y - yo, x + xo, y + yo, red);
+ }
+ else
+ {
+ ppm_drawline (&update_vector_preview_buffer,
+ x - xo, y - yo, x + xo, y + yo, gray);
+ }
+ ppm_put_rgb (&update_vector_preview_buffer, x - xo, y - yo, white);
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (vector_preview),
+ 0, 0, OMWIDTH, OMHEIGHT,
+ GIMP_RGB_IMAGE,
+ (guchar *)update_vector_preview_buffer.col,
+ OMWIDTH * 3);
+}
+
+void
+orientation_map_free_resources (void)
+{
+ ppm_kill (&update_om_preview_nbuffer);
+ ppm_kill (&update_vector_preview_backup);
+ ppm_kill (&update_vector_preview_buffer);
+}
+
+static void
+update_slides (void)
+{
+ gint type;
+
+ adjignore = TRUE;
+ gtk_adjustment_set_value (angle_adjust, vector[selectedvector].dir);
+ gtk_adjustment_set_value (strength_adjust, vector[selectedvector].str);
+ type = vector[selectedvector].type;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vector_types[type]), TRUE);
+ adjignore = FALSE;
+}
+
+static void
+prev_click_callback (GtkWidget *w, gpointer data)
+{
+ selectedvector--;
+ if (selectedvector < 0)
+ selectedvector = num_vectors-1;
+ update_slides ();
+ update_vector_prev ();
+}
+
+static void
+next_click_callback (GtkWidget *w, gpointer data)
+{
+ selectedvector++;
+ if (selectedvector == num_vectors) selectedvector = 0;
+ update_slides ();
+ update_vector_prev ();
+}
+
+static void
+add_new_vector (gdouble x, gdouble y)
+{
+ vector[num_vectors].x = x;
+ vector[num_vectors].y = y;
+ vector[num_vectors].dir = 0.0;
+ vector[num_vectors].dx = sin (gimp_deg_to_rad (0.0));
+ vector[num_vectors].dy = cos (gimp_deg_to_rad (0.0));
+ vector[num_vectors].str = 1.0;
+ vector[num_vectors].type = 0;
+ selectedvector = num_vectors;
+ num_vectors++;
+}
+
+static void
+add_click_callback (GtkWidget *w, gpointer data)
+{
+ add_new_vector (0.5, 0.5);
+ update_slides ();
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+delete_click_callback (GtkWidget *w, gpointer data)
+{
+ int i;
+
+ for (i = selectedvector; i < num_vectors-1; i++)
+ vector[i] = vector[i + 1];
+
+ num_vectors--;
+
+ if (selectedvector >= num_vectors) selectedvector = 0;
+ update_slides ();
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+map_click_callback (GtkWidget *w, GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ vector[selectedvector].x = event->x / (double)OMWIDTH;
+ vector[selectedvector].y = event->y / (double)OMHEIGHT;
+ }
+ else if (event->button == 2)
+ {
+ if (num_vectors + 1 == MAXORIENTVECT)
+ return;
+ add_new_vector (event->x / (double)OMWIDTH,
+ event->y / (double)OMHEIGHT);
+ update_slides ();
+
+ }
+ else if (event->button == 3)
+ {
+ gdouble d;
+
+ d = atan2 (OMWIDTH * vector[selectedvector].x - event->x,
+ OMHEIGHT * vector[selectedvector].y - event->y);
+ vector[selectedvector].dir = gimp_rad_to_deg (d);
+ vector[selectedvector].dx = sin (d);
+ vector[selectedvector].dy = cos (d);
+ update_slides ();
+ }
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+angle_adjust_move_callback (GtkWidget *w, gpointer data)
+{
+ if (adjignore)
+ return;
+ vector[selectedvector].dir = gtk_adjustment_get_value (angle_adjust);
+ vector[selectedvector].dx =
+ sin (gimp_deg_to_rad (vector[selectedvector].dir));
+ vector[selectedvector].dy =
+ cos (gimp_deg_to_rad (vector[selectedvector].dir));
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+strength_adjust_move_callback (GtkWidget *w, gpointer data)
+{
+ if (adjignore)
+ return;
+ vector[selectedvector].str = gtk_adjustment_get_value (strength_adjust);
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+strength_exponent_adjust_move_callback (GtkWidget *w, gpointer data)
+{
+ if (adjignore)
+ return;
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+angle_offset_adjust_move_callback (GtkWidget *w, gpointer data)
+{
+ if (adjignore)
+ return;
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+vector_type_click_callback (GtkWidget *w, gpointer data)
+{
+ if (adjignore)
+ return;
+
+ gimp_radio_button_update (w, data);
+ vector[selectedvector].type = vector_type;
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+static void
+orient_map_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_APPLY:
+ case GTK_RESPONSE_OK:
+ {
+ gint i;
+
+ for (i = 0; i < num_vectors; i++)
+ pcvals.orient_vectors[i] = vector[i];
+
+ pcvals.num_orient_vectors = num_vectors;
+ pcvals.orient_strength_exponent = gtk_adjustment_get_value (orient_map_str_exp_adjust);
+ pcvals.orient_angle_offset = gtk_adjustment_get_value (angle_offset_adjust);
+ pcvals.orient_voronoi = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (orient_voronoi));
+ }
+ };
+
+ if (response_id != GTK_RESPONSE_APPLY)
+ gtk_widget_hide (widget);
+}
+
+static void
+init_vectors (void)
+{
+ if (pcvals.num_orient_vectors)
+ {
+ gint i;
+
+ num_vectors = pcvals.num_orient_vectors;
+ for (i = 0; i < num_vectors; i++)
+ vector[i] = pcvals.orient_vectors[i];
+ }
+ else
+ {/* Shouldn't happen */
+ num_vectors = 0;
+ add_new_vector (0.5, 0.5);
+ }
+ if (selectedvector >= num_vectors)
+ selectedvector = num_vectors-1;
+}
+
+void
+update_orientmap_dialog (void)
+{
+ if (!orient_map_window) return;
+
+ init_vectors ();
+
+ gtk_adjustment_set_value (orient_map_str_exp_adjust,
+ pcvals.orient_strength_exponent);
+ gtk_adjustment_set_value (angle_offset_adjust,
+ pcvals.orient_angle_offset);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (orient_voronoi),
+ pcvals.orient_voronoi);
+
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
+
+void
+create_orientmap_dialog (GtkWidget *parent)
+{
+ GtkWidget *tmpw, *tmpw2;
+ GtkWidget *table1, *table2;
+ GtkWidget *frame;
+ GtkWidget *ebox, *hbox, *vbox;
+
+ init_vectors ();
+
+ if (orient_map_window)
+ {
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+ gtk_widget_show (orient_map_window);
+ return;
+ }
+
+ orient_map_window =
+ gimp_dialog_new (_("Orientation Map Editor"), PLUG_IN_ROLE,
+ gtk_widget_get_toplevel (parent), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Apply"), GTK_RESPONSE_APPLY,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (orient_map_window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_APPLY,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (orient_map_window, "response",
+ G_CALLBACK (orient_map_response),
+ orient_map_window);
+ g_signal_connect (orient_map_window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &orient_map_window);
+
+ table1 = gtk_table_new (2, 5, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (orient_map_window))),
+ table1, TRUE, TRUE, 0);
+ gtk_widget_show (table1);
+
+ frame = gtk_frame_new (_("Vectors"));
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 2);
+ gtk_table_attach (GTK_TABLE (table1), frame, 0, 1, 0, 1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ ebox = gtk_event_box_new ();
+ gimp_help_set_help_data (ebox,
+ _("The vector-field. "
+ "Left-click to move selected vector, "
+ "Right-click to point it towards mouse, "
+ "Middle-click to add a new vector."), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), ebox, FALSE, FALSE, 0);
+
+ tmpw = vector_preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tmpw, OMWIDTH, OMHEIGHT);
+ gtk_container_add (GTK_CONTAINER (ebox), tmpw);
+ gtk_widget_show (tmpw);
+ gtk_widget_add_events (ebox, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect (ebox, "button-press-event",
+ G_CALLBACK (map_click_callback), NULL);
+ gtk_widget_show (ebox);
+
+ vector_preview_brightness_adjust = (GtkAdjustment *)
+ gtk_adjustment_new (50.0, 0.0, 100.0, 1.0, 1.0, 1.0);
+ tmpw = gtk_scale_new (GTK_ORIENTATION_VERTICAL,
+ vector_preview_brightness_adjust);
+ gtk_scale_set_draw_value (GTK_SCALE (tmpw), FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, FALSE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (vector_preview_brightness_adjust, "value-changed",
+ G_CALLBACK (update_vector_prev), NULL);
+ gimp_help_set_help_data (tmpw, _("Adjust the preview's brightness"), NULL);
+
+ tmpw2 = tmpw = gtk_frame_new (_("Preview"));
+ gtk_container_set_border_width (GTK_CONTAINER (tmpw), 2);
+ gtk_table_attach (GTK_TABLE (table1), tmpw, 1,2, 0,1,
+ GTK_EXPAND, GTK_EXPAND, 0, 0);
+ gtk_widget_show (tmpw);
+
+ tmpw = orient_map_preview_prev = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tmpw, OMWIDTH, OMHEIGHT);
+ gtk_container_add (GTK_CONTAINER (tmpw2), tmpw);
+ gtk_widget_show (tmpw);
+
+ hbox = tmpw = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (tmpw), 2);
+ gtk_table_attach_defaults (GTK_TABLE (table1), tmpw, 0,1, 1,2);
+ gtk_widget_show (tmpw);
+
+ prev_button = tmpw = gtk_button_new_with_mnemonic ("_<<");
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, TRUE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (prev_click_callback), NULL);
+ gimp_help_set_help_data (tmpw, _("Select previous vector"), NULL);
+
+ next_button = tmpw = gtk_button_new_with_mnemonic ("_>>");
+ gtk_box_pack_start (GTK_BOX (hbox),tmpw,FALSE,TRUE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (next_click_callback), NULL);
+ gimp_help_set_help_data (tmpw, _("Select next vector"), NULL);
+
+ add_button = tmpw = gtk_button_new_with_mnemonic ( _("A_dd"));
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, TRUE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (add_click_callback), NULL);
+ gimp_help_set_help_data (tmpw, _("Add new vector"), NULL);
+
+ kill_button = tmpw = gtk_button_new_with_mnemonic ( _("_Kill"));
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, TRUE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (delete_click_callback), NULL);
+ gimp_help_set_help_data (tmpw, _("Delete selected vector"), NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_spacing (GTK_BOX (hbox), 12);
+ gtk_table_attach_defaults (GTK_TABLE (table1), hbox, 0, 2, 2, 3);
+ gtk_widget_show (hbox);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Type"),
+ G_CALLBACK (vector_type_click_callback),
+ &vector_type, 0,
+
+ _("_Normal"), 0, &vector_types[0],
+ _("Vorte_x"), 1, &vector_types[1],
+ _("Vortex_2"), 2, &vector_types[2],
+ _("Vortex_3"), 3, &vector_types[3],
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ orient_voronoi = tmpw = gtk_check_button_new_with_mnemonic ( _("_Voronoi"));
+ gtk_box_pack_start (GTK_BOX (vbox), tmpw, TRUE, TRUE, 0);
+ gtk_widget_show (tmpw);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.orient_voronoi);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (angle_offset_adjust_move_callback), NULL);
+ gimp_help_set_help_data (tmpw,
+ _("Voronoi-mode makes only the vector closest to the given point have any influence"),
+ NULL);
+
+ table2 = gtk_table_new (4, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table2), 4);
+ gtk_box_pack_start (GTK_BOX (hbox), table2, TRUE, TRUE, 0);
+ gtk_widget_show (table2);
+
+ angle_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 0,
+ _("A_ngle:"),
+ 150, 6, 0.0,
+ 0.0, 360.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Change the angle of the selected vector"),
+ NULL);
+ g_signal_connect (angle_adjust, "value-changed",
+ G_CALLBACK (angle_adjust_move_callback), NULL);
+
+ angle_offset_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 1,
+ _("Ang_le offset:"),
+ 150, 6, 0.0,
+ 0.0, 360.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Offset all vectors with a given angle"),
+ NULL);
+ g_signal_connect (angle_offset_adjust, "value-changed",
+ G_CALLBACK (angle_offset_adjust_move_callback), NULL);
+
+ strength_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 2,
+ _("_Strength:"),
+ 150, 6, 1.0,
+ 0.1, 5.0, 0.1, 1.0, 1,
+ TRUE, 0, 0,
+ _("Change the strength of the selected vector"),
+ NULL);
+ g_signal_connect (strength_adjust, "value-changed",
+ G_CALLBACK (strength_adjust_move_callback), NULL);
+
+ orient_map_str_exp_adjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 3,
+ _("S_trength exp.:"),
+ 150, 6, 1.0,
+ 0.1, 10.9, 0.1, 1.0, 1,
+ TRUE, 0, 0,
+ _("Change the exponent of the strength"),
+ NULL);
+ g_signal_connect (orient_map_str_exp_adjust, "value-changed",
+ G_CALLBACK (strength_exponent_adjust_move_callback), NULL);
+
+ gtk_widget_show (orient_map_window);
+
+ update_vector_prev ();
+ update_orient_map_preview_prev ();
+}
diff --git a/plug-ins/gimpressionist/orientmap.h b/plug-ins/gimpressionist/orientmap.h
new file mode 100644
index 0000000..480908d
--- /dev/null
+++ b/plug-ins/gimpressionist/orientmap.h
@@ -0,0 +1,25 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ORIENTMAP_H
+#define __ORIENTMAP_H
+
+void create_orientmap_dialog (GtkWidget *parent);
+void update_orientmap_dialog (void);
+void orientation_map_free_resources(void);
+
+#endif
diff --git a/plug-ins/gimpressionist/paper.c b/plug-ins/gimpressionist/paper.c
new file mode 100644
index 0000000..562b0e4
--- /dev/null
+++ b/plug-ins/gimpressionist/paper.c
@@ -0,0 +1,221 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "paper.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static GtkWidget *paper_preview = NULL;
+static GtkWidget *paper_invert = NULL;
+static GtkWidget *paper_list = NULL;
+static GtkObject *paper_relief_adjust = NULL;
+static GtkObject *paper_scale_adjust = NULL;
+static GtkWidget *paper_overlay = NULL;
+
+static void paper_update_preview (void)
+{
+ gint i, j;
+ guchar *buf, *paper_preview_buffer;
+ gdouble sc;
+ ppm_t p = {0, 0, NULL};
+
+ ppm_load (pcvals.selected_paper, &p);
+ sc = p.width > p.height ? p.width : p.height;
+ sc = 100.0 / sc;
+ resize (&p, p.width*sc,p.height*sc);
+
+ paper_preview_buffer = g_new0 (guchar, 100*100);
+
+ for (i = 0, buf = paper_preview_buffer; i < 100; i++, buf += 100)
+ {
+ gint k = i * p.width * 3;
+
+ if (i < p.height)
+ {
+ for (j = 0; j < p.width; j++)
+ buf[j] = p.col[k + j * 3];
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (paper_invert)))
+ for (j = 0; j < p.width; j++)
+ buf[j] = 255 - buf[j];
+ }
+ }
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (paper_preview),
+ 0, 0, 100, 100,
+ GIMP_GRAY_IMAGE,
+ paper_preview_buffer,
+ 100);
+
+ ppm_kill (&p);
+ g_free (paper_preview_buffer);
+
+ gtk_widget_queue_draw (paper_preview);
+}
+
+static void
+paper_select (GtkTreeSelection *selection,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gchar *paper;
+
+ gtk_tree_model_get (model, &iter, 0, &paper, -1);
+
+ if (paper)
+ {
+ gchar *fname = g_build_filename ("Paper", paper, NULL);
+
+ g_strlcpy (pcvals.selected_paper,
+ fname, sizeof (pcvals.selected_paper));
+
+ paper_update_preview ();
+
+ g_free (fname);
+ g_free (paper);
+ }
+ }
+}
+
+void
+paper_restore (void)
+{
+ reselect (paper_list, pcvals.selected_paper);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (paper_relief_adjust), pcvals.paper_relief);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (paper_scale_adjust), pcvals.paper_scale);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (paper_invert), pcvals.paper_invert);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (paper_overlay), pcvals.paper_overlay);
+}
+
+void
+paper_store (void)
+{
+ pcvals.paper_invert = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (paper_invert));
+ pcvals.paper_overlay = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (paper_overlay));
+}
+
+void
+create_paperpage (GtkNotebook *notebook)
+{
+ GtkWidget *box1, *thispage, *box2;
+ GtkWidget *label, *tmpw, *table;
+ GtkWidget *view;
+ GtkWidget *frame;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkListStore *paper_store_list;
+
+ label = gtk_label_new_with_mnemonic (_("P_aper"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box1, TRUE, TRUE, 0);
+ gtk_widget_show (box1);
+
+ paper_list = view = create_one_column_list (box1, paper_select);
+ paper_store_list =
+ GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box2), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ paper_preview = tmpw = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tmpw, 100, 100);
+ gtk_container_add (GTK_CONTAINER (frame), tmpw);
+ gtk_widget_show (tmpw);
+
+ paper_invert = tmpw = gtk_check_button_new_with_mnemonic ( _("_Invert"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw), FALSE);
+ gtk_widget_show (tmpw);
+ g_signal_connect_swapped (tmpw, "clicked",
+ G_CALLBACK (paper_select), selection);
+ gimp_help_set_help_data (tmpw, _("Inverts the Papers texture"), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.paper_invert);
+
+ paper_overlay = tmpw = gtk_check_button_new_with_mnemonic ( _("O_verlay"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw), FALSE);
+ gtk_widget_show (tmpw);
+ gimp_help_set_help_data
+ (tmpw, _("Applies the paper as it is (without embossing it)"), NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.paper_overlay);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ paper_scale_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Scale:"),
+ 150, -1, pcvals.paper_scale,
+ 3.0, 150.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Specifies the scale of the texture (in percent of original file)"),
+ NULL);
+ g_signal_connect (paper_scale_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.paper_scale);
+
+ paper_relief_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Relief:"),
+ 150, -1, pcvals.paper_relief,
+ 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Specifies the amount of embossing to apply to the image (in percent)"),
+ NULL);
+ g_signal_connect (paper_relief_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.paper_relief);
+
+
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (paper_store_list), &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ paper_select (selection, NULL);
+ readdirintolist ("Paper", view, pcvals.selected_paper);
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/paper.h b/plug-ins/gimpressionist/paper.h
new file mode 100644
index 0000000..ee7b45c
--- /dev/null
+++ b/plug-ins/gimpressionist/paper.h
@@ -0,0 +1,25 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PAPER_H
+#define __PAPER_H
+
+void create_paperpage(GtkNotebook *);
+void paper_store(void);
+void paper_restore(void);
+
+#endif
diff --git a/plug-ins/gimpressionist/placement.c b/plug-ins/gimpressionist/placement.c
new file mode 100644
index 0000000..1c4ce59
--- /dev/null
+++ b/plug-ins/gimpressionist/placement.c
@@ -0,0 +1,130 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "placement.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define NUM_PLACE_RADIO 2
+
+static GtkWidget *placement_radio[NUM_PLACE_RADIO];
+static GtkWidget *placement_center = NULL;
+static GtkObject *brush_density_adjust = NULL;
+
+void
+place_restore (void)
+{
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (placement_radio[pcvals.place_type]), TRUE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (placement_center),
+ pcvals.placement_center);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (brush_density_adjust),
+ pcvals.brush_density);
+}
+
+int
+place_type_input (int in)
+{
+ return CLAMP_UP_TO (in, NUM_PLACE_RADIO);
+}
+
+void
+place_store (void)
+{
+ pcvals.placement_center = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (placement_center));
+}
+
+void
+create_placementpage (GtkNotebook *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *label, *tmpw, *table, *frame;
+
+ label = gtk_label_new_with_mnemonic (_("Pl_acement"));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+
+ frame = gimp_int_radio_group_new (TRUE, _("Placement"),
+ G_CALLBACK (gimp_radio_button_update),
+ &pcvals.place_type, 0,
+
+ _("Randomly"),
+ PLACEMENT_TYPE_RANDOM,
+ &placement_radio[PLACEMENT_TYPE_RANDOM],
+
+ _("Evenly distributed"),
+ PLACEMENT_TYPE_EVEN_DIST,
+ &placement_radio[PLACEMENT_TYPE_EVEN_DIST],
+
+ NULL);
+
+ gimp_help_set_help_data
+ (placement_radio[PLACEMENT_TYPE_RANDOM],
+ _("Place strokes randomly around the image"),
+ NULL);
+ gimp_help_set_help_data
+ (placement_radio[PLACEMENT_TYPE_EVEN_DIST],
+ _("The strokes are evenly distributed across the image"),
+ NULL);
+
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_toggle_button_set_active
+ (GTK_TOGGLE_BUTTON (placement_radio[pcvals.place_type]), TRUE);
+
+ placement_center = gtk_check_button_new_with_mnemonic ( _("Centered"));
+ tmpw = placement_center;
+
+ gtk_box_pack_start (GTK_BOX (vbox), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ gimp_help_set_help_data
+ (tmpw, _("Focus the brush strokes around the center of the image"), NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw),
+ pcvals.placement_center);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ brush_density_adjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Stroke _density:"),
+ 100, -1, pcvals.brush_density,
+ 1.0, 50.0, 1.0, 5.0, 0,
+ TRUE, 0, 0,
+ _("The relative density of the brush strokes"),
+ NULL);
+ g_signal_connect (brush_density_adjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.brush_density);
+
+ gtk_notebook_append_page_menu (notebook, vbox, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/placement.h b/plug-ins/gimpressionist/placement.h
new file mode 100644
index 0000000..f308731
--- /dev/null
+++ b/plug-ins/gimpressionist/placement.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PLACEMENT_H
+#define __PLACEMENT_H
+
+enum PLACEMENT_TYPE_ENUM
+{
+ PLACEMENT_TYPE_RANDOM = 0,
+ PLACEMENT_TYPE_EVEN_DIST = 1,
+};
+
+void place_store (void);
+void place_restore (void);
+void create_placementpage (GtkNotebook *);
+int place_type_input (int in);
+
+#endif
diff --git a/plug-ins/gimpressionist/plasma.c b/plug-ins/gimpressionist/plasma.c
new file mode 100644
index 0000000..82f9212
--- /dev/null
+++ b/plug-ins/gimpressionist/plasma.c
@@ -0,0 +1,122 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimpmath/gimpmath.h>
+#include <libgimp/gimp.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "random.h"
+
+static int
+pfix (int n)
+{
+ if (n < 1) return 1;
+ if (n > 255) return 255;
+ return n;
+}
+
+#define PIXEL(y,x,z) p->col[(y) * rowstride + (x) * 3 + z]
+
+static void
+mkplasma_sub (ppm_t *p, int x1, int x2, int y1, int y2, float turb)
+{
+ int rowstride = p->width * 3;
+ int r=0;
+ int xr, yr, nx, ny;
+
+ xr = abs (x1 - x2);
+ yr = abs (y1 - y2);
+
+ if ((xr == 0) && (yr == 0))
+ return;
+
+ nx = (x1 + x2)/2;
+ ny = (y1 + y2)/2;
+ if (!PIXEL (y1,nx,r))
+ PIXEL (y1, nx, r) = pfix ((PIXEL (y1, x1, r) + PIXEL (y1, x2, r)) / 2.0 +
+ turb * g_rand_double_range (random_generator,
+ -xr / 2.0,
+ xr / 2.0));
+ if (!PIXEL (y2, nx, r))
+ PIXEL (y2, nx, r) = pfix ((PIXEL (y2, x1, r) + PIXEL (y2, x2, r)) / 2.0 +
+ turb * g_rand_double_range (random_generator,
+ -xr / 2.0,
+ xr / 2.0));
+ if (!PIXEL (ny, x1, r))
+ PIXEL (ny, x1, r) = pfix ((PIXEL (y1, x1, r)+PIXEL (y2, x1, r)) / 2.0 +
+ turb * g_rand_double_range (random_generator,
+ -yr / 2.0,
+ yr / 2.0));
+ if (!PIXEL (ny, x2, r))
+ PIXEL (ny, x2, r) = pfix ((PIXEL (y1, x2, r) + PIXEL (y2, x2, r)) / 2.0 +
+ turb * g_rand_double_range (random_generator,
+ -yr / 2.0,
+ yr / 2.0));
+ if (!PIXEL (ny, nx, r))
+ PIXEL (ny, nx, r) =
+ pfix ((PIXEL (y1, x1, r) + PIXEL (y1, x2, r) +
+ PIXEL (y2, x1, r) + PIXEL( y2, x2, r)) / 4.0 +
+ turb * g_rand_double_range (random_generator,
+ -(xr + yr)/4.0,
+ (xr + yr)/4.0));
+
+ if (xr>1)
+ {
+ mkplasma_sub (p, x1, nx, y1, ny, turb);
+ mkplasma_sub (p, nx, x2, y1, ny, turb);
+ }
+ if (yr>1)
+ {
+ mkplasma_sub (p, x1, nx, ny, y2, turb);
+ mkplasma_sub (p, nx, x2, ny, y2, turb);
+ }
+}
+
+static void
+mkplasma_red (ppm_t *p, float turb)
+{
+ int x = 0, y = 0;
+ int rowstride = p->width * 3;
+
+ for (x = 0; x < p->width; x++)
+ for (y = 0; y < p->height; y++)
+ PIXEL (y, x, 0) = 0;
+ x--; y--;
+ PIXEL (0, 0, 0) = g_rand_int_range (random_generator, 1, 256);
+ PIXEL (y, 0, 0) = g_rand_int_range (random_generator, 1, 256);
+ PIXEL (0, x, 0) = g_rand_int_range (random_generator, 1, 256);
+ PIXEL (y, x, 0) = g_rand_int_range (random_generator, 1, 256);
+ mkplasma_sub (p, 0, x, 0, y, turb);
+}
+
+void
+mkgrayplasma (ppm_t *p, float turb)
+{
+ int y, l;
+
+ mkplasma_red (p, turb);
+ l = p->width * 3 * p->height;
+ for (y = 0; y < l; y += 3)
+ p->col[y+1] = p->col[y+2] = p->col[y];
+}
diff --git a/plug-ins/gimpressionist/ppmtool.c b/plug-ins/gimpressionist/ppmtool.c
new file mode 100644
index 0000000..307779e
--- /dev/null
+++ b/plug-ins/gimpressionist/ppmtool.c
@@ -0,0 +1,904 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimpmath/gimpmath.h>
+
+#include "ppmtool.h"
+#include "gimpressionist.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static int
+readline (FILE *f, char *buffer, int len)
+{
+ do
+ {
+ if (!fgets (buffer, len, f))
+ return -1;
+ }
+ while (buffer[0] == '#');
+
+ g_strchomp (buffer);
+ return 0;
+}
+
+void
+ppm_kill (ppm_t *p)
+{
+ g_free (p->col);
+ p->col = NULL;
+ p->height = p->width = 0;
+}
+
+void ppm_new (ppm_t *p, int xs, int ys)
+{
+ int x;
+ guchar bgcol[3] = {0,0,0};
+
+ if (xs < 1)
+ xs = 1;
+ if (ys < 1)
+ ys = 1;
+
+ p->width = xs;
+ p->height = ys;
+ p->col = g_malloc (xs * 3 * ys);
+ for (x = 0; x < xs * 3 * ys; x += 3)
+ {
+ p->col[x+0] = bgcol[0];
+ p->col[x+1] = bgcol[1];
+ p->col[x+2] = bgcol[2];
+ }
+}
+
+void
+get_rgb (ppm_t *s, float xo, float yo, guchar *d)
+{
+ float ix, iy;
+ int x1, x2, y1, y2;
+ float x1y1, x2y1, x1y2, x2y2;
+ float r, g, b;
+ int bail = 0;
+ int rowstride = s->width * 3;
+
+ if (xo < 0.0)
+ bail=1;
+ else if (xo >= s->width-1)
+ {
+ xo = s->width-1;
+#if 0
+ bail=1;
+#endif
+ }
+
+ if (yo < 0.0)
+ bail=1;
+ else if (yo >= s->height-1)
+ {
+ yo= s->height-1;
+#if 0
+ bail=1;
+#endif
+ }
+
+ if (bail)
+ {
+ d[0] = d[1] = d[2] = 0;
+ return;
+ }
+
+ ix = (int)xo;
+ iy = (int)yo;
+
+#if 0
+ x1 = wrap(ix, s->width);
+ x2 = wrap(ix+1, s->width);
+ y1 = wrap(iy, s->height);
+ y2 = wrap(iy+1, s->height);
+#endif
+ x1 = ix; x2 = ix + 1;
+ y1 = iy; y2 = iy + 1;
+
+#if 0
+ printf("x1=%d y1=%d x2=%d y2=%d\n",x1,y1,x2,y2);
+#endif
+
+ x1y1 = (1.0 - xo + ix) * (1.0 - yo + iy);
+ x2y1 = (xo - ix) * (1.0 - yo + iy);
+ x1y2 = (1.0 - xo + ix) * (yo - iy);
+ x2y2 = (xo - ix) * (yo - iy);
+
+ r = s->col[y1 * rowstride + x1 * 3 + 0] * x1y1;
+ g = s->col[y1 * rowstride + x1 * 3 + 1] * x1y1;
+ b = s->col[y1 * rowstride + x1 * 3 + 2] * x1y1;
+
+ if (x2y1 > 0.0)
+ r += s->col[y1 * rowstride + x2 * 3 + 0] * x2y1;
+ if (x2y1 > 0.0)
+ g += s->col[y1 * rowstride + x2 * 3 + 1] * x2y1;
+ if (x2y1 > 0.0)
+ b += s->col[y1 * rowstride + x2 * 3 + 2] * x2y1;
+
+ if (x1y2 > 0.0)
+ r += s->col[y2 * rowstride + x1 * 3 + 0] * x1y2;
+ if (x1y2 > 0.0)
+ g += s->col[y2 * rowstride + x1 * 3 + 1] * x1y2;
+ if (x1y2 > 0.0)
+ b += s->col[y2 * rowstride + x1 * 3 + 2] * x1y2;
+
+ if (x2y2 > 0.0)
+ r += s->col[y2 * rowstride + x2 * 3 + 0] * x2y2;
+ if (x2y2 > 0.0)
+ g += s->col[y2 * rowstride + x2 * 3 + 1] * x2y2;
+ if (x2y2 > 0.0)
+ b += s->col[y2 * rowstride + x2 * 3 + 2] * x2y2;
+
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+}
+
+
+void
+resize (ppm_t *p, int nx, int ny)
+{
+ int x, y;
+ float xs = p->width / (float)nx;
+ float ys = p->height / (float)ny;
+ ppm_t tmp = {0, 0, NULL};
+
+ ppm_new (&tmp, nx, ny);
+ for (y = 0; y < ny; y++)
+ {
+ guchar *row = tmp.col + y * tmp.width * 3;
+
+ for (x = 0; x < nx; x++)
+ {
+ get_rgb (p, x * xs, y * ys, &row[x * 3]);
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+void
+rescale (ppm_t *p, double sc)
+{
+ resize (p, p->width * sc, p->height * sc);
+}
+
+void resize_fast (ppm_t *p, int nx, int ny)
+{
+ int x, y;
+ float xs = p->width / (float)nx;
+ float ys = p->height / (float)ny;
+ ppm_t tmp = {0, 0, NULL};
+
+ ppm_new (&tmp, nx, ny);
+ for (y = 0; y < ny; y++)
+ {
+ for (x = 0; x < nx; x++)
+ {
+ gint rx = x * xs, ry = y * ys;
+
+ memcpy (&tmp.col[y * tmp.width * 3 + x * 3],
+ &p->col[ry * p->width * 3 + rx * 3],
+ 3);
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+
+struct _BrushHeader
+{
+ unsigned int header_size; /* header_size = sz_BrushHeader + brush name */
+ unsigned int version; /* brush file version # */
+ unsigned int width; /* width of brush */
+ unsigned int height; /* height of brush */
+ unsigned int bytes; /* depth of brush in bytes--always 1 */
+ unsigned int magic_number;/* GIMP brush magic number */
+ unsigned int spacing; /* brush spacing */
+};
+
+static void
+msb2lsb (unsigned int *i)
+{
+ guchar *p = (guchar *)i, c;
+
+ c = p[1]; p[1] = p[2]; p[2] = c;
+ c = p[0]; p[0] = p[3]; p[3] = c;
+}
+
+static FILE *
+fopen_from_search_path (const gchar * fn, const char * mode)
+{
+ FILE * f;
+ gchar * full_filename;
+
+ f = g_fopen (fn, mode);
+ if (!f)
+ {
+ full_filename = findfile (fn);
+ f = g_fopen (full_filename, mode);
+ g_free (full_filename);
+ }
+ return f;
+}
+
+static void
+load_gimp_brush (const gchar *fn, ppm_t *p)
+{
+ FILE *f;
+ struct _BrushHeader hdr;
+ gchar *ptr;
+ gint x, y;
+
+ f = fopen_from_search_path (fn, "rb");
+ ppm_kill (p);
+
+ if (!f)
+ {
+ g_printerr ("load_gimp_brush: Unable to open file \"%s\"!\n",
+ gimp_filename_to_utf8 (fn));
+ ppm_new (p, 10,10);
+ return;
+ }
+
+ fread (&hdr, 1, sizeof (struct _BrushHeader), f);
+
+ for (x = 0; x < 7; x++)
+ msb2lsb (&((unsigned int *)&hdr)[x]);
+
+ ppm_new (p, hdr.width, hdr.height);
+
+ ptr = g_malloc (hdr.width);
+ fseek (f, hdr.header_size, SEEK_SET);
+ for (y = 0; y < p->height; y++)
+ {
+ fread (ptr, p->width, 1, f);
+ for (x = 0; x < p->width; x++)
+ {
+ int k = y * p->width * 3 + x * 3;
+ p->col[k+0] = p->col[k+1] = p->col[k+2] = ptr[x];
+ }
+ }
+ fclose (f);
+ g_free (ptr);
+}
+
+void
+ppm_load (const char *fn, ppm_t *p)
+{
+ char line[200];
+ int y, pgm = 0;
+ FILE *f;
+
+ if (!strcmp (&fn[strlen (fn)-4], ".gbr"))
+ {
+ load_gimp_brush(fn, p);
+ return;
+ }
+
+ f = fopen_from_search_path (fn, "rb");
+
+ ppm_kill (p);
+
+ if (!f)
+ {
+ g_printerr ("ppm_load: Unable to open file \"%s\"!\n",
+ gimp_filename_to_utf8 (fn));
+ ppm_new (p, 10,10);
+ return;
+ }
+
+ readline (f, line, 200);
+ if (strcmp (line, "P6"))
+ {
+ if (strcmp (line, "P5"))
+ {
+ fclose (f);
+ g_printerr ("ppm_load: File \"%s\" not PPM/PGM? (line=\"%s\")%c\n",
+ gimp_filename_to_utf8 (fn), line, 7);
+ ppm_new (p, 10,10);
+ return;
+ }
+ pgm = 1;
+ }
+ readline (f, line, 200);
+ p->width = atoi (line);
+ p->height = atoi (strchr (line, ' ')+1);
+ readline (f, line, 200);
+ if (strcmp (line, "255"))
+ {
+ fclose (f);
+ g_printerr ("ppm_load: File \"%s\" not valid PPM/PGM? (line=\"%s\")%c\n",
+ gimp_filename_to_utf8 (fn), line, 7);
+ ppm_new (p, 10,10);
+ return;
+ }
+ p->col = g_malloc (p->height * p->width * 3);
+
+ if (!pgm)
+ {
+ fread (p->col, p->height * 3 * p->width, 1, f);
+ }
+ else
+ {
+ guchar *tmpcol = g_malloc (p->width * p->height);
+
+ fread (tmpcol, p->height * p->width, 1, f);
+ for (y = 0; y < p->width * p->height * 3; y++) {
+ p->col[y] = tmpcol[y / 3];
+ }
+
+ g_free (tmpcol);
+ }
+ fclose (f);
+}
+
+void
+fill (ppm_t *p, guchar *c)
+{
+ int x, y;
+
+ if ((c[0] == c[1]) && (c[0] == c[2]))
+ {
+ guchar col = c[0];
+ for (y = 0; y < p->height; y++)
+ {
+ memset(p->col + y*p->width*3, col, p->width*3);
+ }
+ }
+ else
+ {
+ for (y = 0; y < p->height; y++)
+ {
+ guchar *row = p->col + y * p->width * 3;
+
+ for (x = 0; x < p->width; x++)
+ {
+ int k = x * 3;
+
+ row[k+0] = c[0];
+ row[k+1] = c[1];
+ row[k+2] = c[2];
+ }
+ }
+ }
+}
+
+void
+ppm_copy (ppm_t *s, ppm_t *p)
+{
+ ppm_kill (p);
+ p->width = s->width;
+ p->height = s->height;
+ p->col = g_memdup (s->col, p->width * 3 * p->height);
+}
+
+void
+free_rotate (ppm_t *p, double amount)
+{
+ int x, y;
+ double nx, ny;
+ ppm_t tmp = {0, 0, NULL};
+ double f = amount * G_PI * 2 / 360.0;
+ int rowstride = p->width * 3;
+
+ ppm_new (&tmp, p->width, p->height);
+ for (y = 0; y < p->height; y++)
+ {
+ for (x = 0; x < p->width; x++)
+ {
+ double r, d;
+
+ nx = fabs (x - p->width / 2.0);
+ ny = fabs (y - p->height / 2.0);
+ r = sqrt (nx * nx + ny * ny);
+
+ d = atan2 ((y - p->height / 2.0), (x - p->width / 2.0));
+
+ nx = (p->width / 2.0 + cos (d - f) * r);
+ ny = (p->height / 2.0 + sin (d - f) * r);
+ get_rgb (p, nx, ny, tmp.col + y * rowstride + x * 3);
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+void
+crop (ppm_t *p, int lx, int ly, int hx, int hy)
+{
+ ppm_t tmp = {0,0,NULL};
+ int x, y;
+ int srowstride = p->width * 3;
+ int drowstride;
+
+ ppm_new (&tmp, hx - lx, hy - ly);
+ drowstride = tmp.width * 3;
+ for (y = ly; y < hy; y++)
+ for (x = lx; x < hx; x++)
+ memcpy (&tmp.col[(y - ly) * drowstride + (x - lx) * 3],
+ &p->col[y * srowstride + x * 3],
+ 3);
+ ppm_kill (p);
+ p->col = tmp.col;
+ p->width = tmp.width;
+ p->height = tmp.height;
+}
+
+void
+autocrop (ppm_t *p, int room)
+{
+ int lx = 0, hx = p->width, ly = 0, hy = p->height;
+ int x, y, n = 0;
+ guchar tc[3];
+ ppm_t tmp = {0,0,NULL};
+ int rowstride = p->width * 3;
+ int drowstride;
+
+ /* upper */
+ memcpy (&tc, p->col, 3);
+ for (y = 0; y < p->height; y++)
+ {
+ n = 0;
+ for (x = 0; x < p->width; x++)
+ {
+ if (memcmp (&tc, &p->col[y*rowstride+x*3], 3))
+ {
+ n++;
+ break;
+ }
+ }
+ if (n)
+ break;
+ }
+ if (n)
+ ly = y;
+#if 0
+ printf("ly = %d\n", ly);
+#endif
+
+ /* lower */
+ memcpy (&tc, &p->col[(p->height - 1) * rowstride], 3);
+ for (y = p->height-1; y >= 0; y--)
+ {
+ n = 0;
+ for (x = 0; x < p->width; x++)
+ {
+ if (memcmp (&tc, &p->col[y*rowstride+x*3], 3))
+ {
+ n++;
+ break;
+ }
+ }
+ if (n)
+ break;
+ }
+ if (n)
+ hy = y+1;
+ if (hy >= p->height)
+ hy = p->height - 1;
+#if 0
+ printf("hy = %d\n", hy);
+#endif
+
+ /* left */
+ memcpy (&tc, &p->col[ly * rowstride], 3);
+ for (x = 0; x < p->width; x++)
+ {
+ n = 0;
+ for (y = ly; y <= hy && y < p->height; y++)
+ {
+ if (memcmp (&tc, &p->col[y * rowstride + x * 3], 3))
+ {
+ n++;
+ break;
+ }
+ }
+ if (n)
+ break;
+ }
+ if (n)
+ lx = x;
+#if 0
+ printf("lx = %d\n", lx);
+#endif
+
+ /* right */
+ memcpy
+ (&tc, &p->col[ly * rowstride + (p->width - 1) * 3], 3);
+ for (x = p->width-1; x >= 0; x--)
+ {
+ n = 0;
+ for (y = ly; y <= hy; y++)
+ {
+ if (memcmp (&tc, &p->col[y * rowstride + x * 3], 3))
+ {
+ n++;
+ break;
+ }
+ }
+ if (n)
+ break;
+ }
+ if (n)
+ hx = x + 1;
+#if 0
+ printf("hx = %d\n", hx);
+#endif
+
+ lx -= room; if (lx < 0) lx = 0;
+ ly -= room; if (ly < 0) ly = 0;
+ hx += room; if (hx >= p->width) hx = p->width - 1;
+ hy += room; if (hy >= p->height) hy = p->height - 1;
+
+ ppm_new (&tmp, hx - lx, hy - ly);
+ drowstride = tmp.width * 3;
+ for (y = ly; y < hy; y++)
+ for (x = lx; x < hx; x++)
+ memcpy (&tmp.col[(y - ly) * drowstride + (x - lx) * 3],
+ &p->col[y * rowstride + x * 3],
+ 3);
+ ppm_kill (p);
+ p->col = tmp.col;
+ p->width = tmp.width;
+ p->height = tmp.height;
+}
+
+void
+ppm_pad (ppm_t *p, int left,int right, int top, int bottom, guchar *bg)
+{
+ int x, y;
+ ppm_t tmp = {0, 0, NULL};
+
+ ppm_new (&tmp, p->width + left + right, p->height + top + bottom);
+ for (y = 0; y < tmp.height; y++)
+ {
+ guchar *row, *srcrow;
+
+ row = tmp.col + y * tmp.width * 3;
+ if ((y < top) || (y >= tmp.height-bottom))
+ {
+ for (x = 0; x < tmp.width; x++)
+ {
+ int k = x * 3;
+
+ row[k+0] = bg[0];
+ row[k+1] = bg[1];
+ row[k+2] = bg[2];
+ }
+ continue;
+ }
+ srcrow = p->col + (y-top) * p->width * 3;
+ for (x = 0; x < left; x++)
+ {
+ int k = x * 3;
+
+ row[k+0] = bg[0];
+ row[k+1] = bg[1];
+ row[k+2] = bg[2];
+ }
+ for (; x < tmp.width-right; x++)
+ {
+ int k = y * tmp.width * 3 + x * 3;
+
+ tmp.col[k+0] = srcrow[(x - left) * 3 + 0];
+ tmp.col[k+1] = srcrow[(x - left) * 3 + 1];
+ tmp.col[k+2] = srcrow[(x - left) * 3 + 2];
+ }
+ for (; x < tmp.width; x++)
+ {
+ int k = x * 3;
+
+ row[k+0] = bg[0];
+ row[k+1] = bg[1];
+ row[k+2] = bg[2];
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+void
+ppm_save (ppm_t *p, const char *fn)
+{
+ FILE *f = g_fopen (fn, "wb");
+
+ if (!f)
+ {
+ /*
+ * gimp_filename_to_utf8 () and g_strerror () return temporary strings
+ * that need not and should not be freed. So this call is OK.
+ * */
+ g_message (_("Failed to save PPM file '%s': %s"),
+ gimp_filename_to_utf8 (fn), g_strerror (errno));
+ return;
+ }
+
+ fprintf (f, "P6\n%d %d\n255\n", p->width, p->height);
+ fwrite (p->col, p->width * 3 * p->height, 1, f);
+ fclose (f);
+}
+
+void
+edgepad (ppm_t *p, int left,int right, int top, int bottom)
+{
+ int x, y;
+ ppm_t tmp = {0, 0, NULL};
+ guchar testcol[3] = {0, 255, 0};
+ int srowstride, drowstride;
+
+ ppm_new (&tmp, p->width+left+right, p->height+top+bottom);
+ fill (&tmp, testcol);
+
+ srowstride = p->width * 3;
+ drowstride = tmp.width * 3;
+
+ for (y = 0; y < top; y++)
+ {
+ memcpy (&tmp.col[y * drowstride + left * 3], p->col, srowstride);
+ }
+ for (; y-top < p->height; y++)
+ {
+ memcpy (&tmp.col[y * drowstride + left * 3],
+ p->col + (y - top) * srowstride,
+ srowstride);
+ }
+ for (; y < tmp.height ; y++)
+ {
+ memcpy (&tmp.col[y * drowstride + left * 3],
+ p->col + (p->height - 1) * srowstride,
+ srowstride);
+ }
+ for (y = 0; y < tmp.height; y++)
+ {
+ guchar *col, *tmprow;
+
+ tmprow = tmp.col + y*drowstride;
+ col = tmp.col + y*drowstride + left*3;
+
+ for (x = 0; x < left; x++)
+ {
+ memcpy (&tmprow[x * 3], col, 3);
+ }
+ col = tmp.col + y * drowstride + (tmp.width-right - 1) * 3;
+ for (x = 0; x < right; x++)
+ {
+ memcpy (&tmprow[(x + tmp.width - right - 1) * 3], col, 3);
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+void
+ppm_apply_gamma (ppm_t *p, float e, int r, int g, int b)
+{
+ int x, l = p->width * 3 * p->height;
+ guchar xlat[256], *pix;
+
+ if (e > 0.0)
+ for (x = 0; x < 256; x++)
+ {
+ xlat[x] = pow ((x / 255.0), (1.0 / e)) * 255.0;
+ }
+ else if (e < 0.0)
+ for (x = 0; x < 256; x++)
+ {
+ xlat[255-x] = pow ((x / 255.0), (-1.0 / e)) * 255.0;
+ }
+ else
+ for (x = 0; x < 256; x++)
+ {
+ xlat[x] = 0;
+ }
+
+ pix = p->col;
+ if (r)
+ for (x = 0; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+ if (g)
+ for (x = 1; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+ if (b)
+ for (x = 2; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+}
+
+void
+ppm_apply_brightness (ppm_t *p, float e, int r, int g, int b)
+{
+ int x, l = p->width * 3 * p->height;
+ guchar xlat[256], *pix;
+ for (x = 0; x < 256; x++)
+ xlat[x] = x * e;
+
+ pix = p->col;
+ if (r)
+ for (x = 0; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+ if (g)
+ for (x = 1; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+ if (b)
+ for (x = 2; x < l; x += 3)
+ pix[x] = xlat[pix[x]];
+}
+
+void
+blur (ppm_t *p, int xrad, int yrad)
+{
+ int x, y, k;
+ int tx, ty;
+ ppm_t tmp = {0,0,NULL};
+ int r, g, b, n;
+ int rowstride = p->width * 3;
+
+ ppm_new (&tmp, p->width, p->height);
+ for (y = 0; y < p->height; y++)
+ {
+ for (x = 0; x < p->width; x++)
+ {
+ r = g = b = n = 0;
+
+ for (ty = y-yrad; ty <= y+yrad; ty++)
+ {
+ for (tx = x-xrad; tx <= x+xrad; tx++)
+ {
+ if (ty<0) continue;
+ if (ty>=p->height) continue;
+ if (tx<0) continue;
+ if (tx>=p->width) continue;
+ k = ty * rowstride + tx * 3;
+ r += p->col[k+0];
+ g += p->col[k+1];
+ b += p->col[k+2];
+ n++;
+ }
+ }
+ k = y * rowstride + x * 3;
+ tmp.col[k+0] = r / n;
+ tmp.col[k+1] = g / n;
+ tmp.col[k+2] = b / n;
+ }
+ }
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+}
+
+void
+ppm_put_rgb_fast (ppm_t *s, float xo, float yo, guchar *d)
+{
+ guchar *tp;
+ tp = s->col + s->width * 3 * (int)(yo + 0.5) + 3 * (int)(xo + 0.5);
+ tp[0] = d[0];
+ tp[1] = d[1];
+ tp[2] = d[2];
+}
+
+void
+ppm_put_rgb (ppm_t *s, float xo, float yo, guchar *d)
+{
+ int x, y;
+ float aa, ab, ba, bb;
+ int k, rowstride = s->width * 3;
+
+ x = xo;
+ y = yo;
+
+ if ((x < 0) || (y < 0) || (x >= s->width-1) || (y >= s->height-1))
+ return;
+
+ xo -= x;
+ yo -= y;
+
+ aa = (1.0 - xo) * (1.0 - yo);
+ ab = xo * (1.0 - yo);
+ ba = (1.0 - xo) * yo;
+ bb = xo * yo;
+
+ k = y * rowstride + x * 3;
+ s->col[k+0] *= (1.0 - aa);
+ s->col[k+1] *= (1.0 - aa);
+ s->col[k+2] *= (1.0 - aa);
+
+ s->col[k+3] *= (1.0 - ab);
+ s->col[k+4] *= (1.0 - ab);
+ s->col[k+5] *= (1.0 - ab);
+
+ s->col[k+rowstride+0] *= (1.0 - ba);
+ s->col[k+rowstride+1] *= (1.0 - ba);
+ s->col[k+rowstride+2] *= (1.0 - ba);
+
+ s->col[k+rowstride+3] *= (1.0 - bb);
+ s->col[k+rowstride+4] *= (1.0 - bb);
+ s->col[k+rowstride+5] *= (1.0 - bb);
+
+ s->col[k+0] += aa * d[0];
+ s->col[k+1] += aa * d[1];
+ s->col[k+2] += aa * d[2];
+ s->col[k+3] += ab * d[0];
+ s->col[k+4] += ab * d[1];
+ s->col[k+5] += ab * d[2];
+ s->col[k+rowstride+0] += ba * d[0];
+ s->col[k+rowstride+1] += ba * d[1];
+ s->col[k+rowstride+2] += ba * d[2];
+ s->col[k+rowstride+3] += bb * d[0];
+ s->col[k+rowstride+4] += bb * d[1];
+ s->col[k+rowstride+5] += bb * d[2];
+}
+
+void
+ppm_drawline (ppm_t *p, float fx, float fy, float tx, float ty, guchar *col)
+{
+ float i;
+ float d, x, y;
+
+ if (fabs (fx - tx) > fabs ( fy - ty))
+ {
+ if (fx > tx)
+ {
+ i = tx; tx = fx; fx = i; i = ty; ty = fy; fy = i;
+ }
+ d = (ty - fy) / (tx - fx);
+ y = fy;
+ for (x = fx; x <= tx; x += 1.0)
+ {
+ ppm_put_rgb (p, x, y, col);
+ y += d;
+ }
+ }
+ else
+ {
+ if (fy > ty)
+ {
+ i = tx; tx = fx; fx = i; i = ty; ty = fy; fy = i;
+ }
+ d = (tx - fx) / (ty - fy);
+ x = fx;
+ for (y = fy; y <= ty; y += 1.0)
+ {
+ ppm_put_rgb (p, x, y, col);
+ x += d;
+ }
+ }
+}
diff --git a/plug-ins/gimpressionist/ppmtool.h b/plug-ins/gimpressionist/ppmtool.h
new file mode 100644
index 0000000..cf464e4
--- /dev/null
+++ b/plug-ins/gimpressionist/ppmtool.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PPM_TOOL_H
+#define __PPM_TOOL_H
+
+#include <glib.h>
+
+typedef struct ppm {
+ int width;
+ int height;
+ unsigned char *col;
+} ppm_t;
+
+void fatal(char *s);
+void ppm_kill(ppm_t *p);
+void ppm_new(ppm_t *p, int xs, int ys);
+void get_rgb(ppm_t *s, float xo, float yo, unsigned char *d);
+void resize(ppm_t *p, int nx, int ny);
+void rescale(ppm_t *p, double scale);
+void resize_fast(ppm_t *p, int nx, int ny);
+void ppm_load(const char *fn, ppm_t *p);
+void ppm_save(ppm_t *p, const char *fn);
+void ppm_copy(ppm_t *s, ppm_t *p);
+void fill(ppm_t *p, guchar *c);
+void free_rotate(ppm_t *p, double amount);
+void ppm_pad(ppm_t *p, int left,int right, int top, int bottom, guchar *);
+void edgepad(ppm_t *p, int left,int right, int top, int bottom);
+void autocrop(ppm_t *p, int room);
+void crop(ppm_t *p, int lx, int ly, int hx, int hy);
+void ppm_apply_gamma(ppm_t *p, float e, int r, int g, int b);
+void ppm_apply_brightness(ppm_t *p, float e, int r, int g, int b);
+void ppm_put_rgb_fast(ppm_t *s, float xo, float yo, guchar *d);
+void ppm_put_rgb(ppm_t *s, float xo, float yo, guchar *d);
+void ppm_drawline(ppm_t *p, float fx, float fy, float tx, float ty, guchar *col);
+
+void repaint(ppm_t *p, ppm_t *a);
+
+void blur(ppm_t *p, int xrad, int yrad);
+
+void mkgrayplasma(ppm_t *p, float turb);
+
+#define PPM_IS_INITED(ppm_ptr) ((ppm_ptr)->col != NULL)
+
+#endif /* #ifndef __PPM_TOOL_H */
+
diff --git a/plug-ins/gimpressionist/presets.c b/plug-ins/gimpressionist/presets.c
new file mode 100644
index 0000000..3db9715
--- /dev/null
+++ b/plug-ins/gimpressionist/presets.c
@@ -0,0 +1,1098 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "presets.h"
+
+#include "color.h"
+#include "general.h"
+#include "orientation.h"
+#include "placement.h"
+#include "size.h"
+
+
+#include "libgimp/stdplugins-intl.h"
+
+#ifdef G_OS_WIN32
+#include "libgimpbase/gimpwin32-io.h"
+#endif
+
+#define PRESETMAGIC "Preset"
+
+static GtkWidget *presetnameentry = NULL;
+static GtkWidget *presetlist = NULL;
+static GtkWidget *presetdesclabel = NULL;
+static GtkWidget *presetsavebutton = NULL;
+static GtkWidget *delete_button = NULL;
+static GtkListStore *store = NULL;
+static gchar *selected_preset_orig_name = NULL;
+static gchar *selected_preset_filename = NULL;
+
+static gboolean
+can_delete_preset (const gchar *abs)
+{
+ gchar *user_data_dir;
+ gboolean ret;
+
+ user_data_dir = g_strconcat (gimp_directory (),
+ G_DIR_SEPARATOR_S,
+ NULL);
+
+
+ ret = (!strncmp (abs, user_data_dir, strlen (user_data_dir)));
+
+ g_free (user_data_dir);
+
+ return ret;
+}
+
+void
+preset_save_button_set_sensitive (gboolean s)
+{
+ if (GTK_IS_WIDGET (presetsavebutton))
+ gtk_widget_set_sensitive (GTK_WIDGET (presetsavebutton), s);
+}
+
+void
+preset_free (void)
+{
+ g_free (selected_preset_orig_name);
+ g_free (selected_preset_filename);
+}
+
+static void set_preset_description_text (const gchar *text)
+{
+ gtk_label_set_text (GTK_LABEL (presetdesclabel), text);
+}
+
+static char presetdesc[4096] = "";
+
+static const char *factory_defaults = "<Factory defaults>";
+
+static gchar *
+get_early_line_from_preset (gchar *full_path, const gchar *prefix)
+{
+ FILE *f;
+ gchar line[4096];
+ gint prefix_len, line_idx;
+
+ prefix_len = strlen (prefix);
+ f = g_fopen (full_path, "rt");
+ if (f)
+ {
+ /* Skip the preset magic. */
+ fgets (line, 10, f);
+ if (!strncmp (line, PRESETMAGIC, 4))
+ {
+ for (line_idx = 0; line_idx < 5; line_idx++)
+ {
+ if (!fgets (line, sizeof (line), f))
+ break;
+ g_strchomp (line);
+ if (!strncmp (line, prefix, prefix_len))
+ {
+ fclose(f);
+ return g_strdup (line+prefix_len);
+ }
+ }
+ }
+ fclose (f);
+ }
+ return NULL;
+}
+
+static gchar *
+get_object_name (const gchar *dir,
+ gchar *filename,
+ void *context)
+{
+ gchar *ret = NULL, *unprocessed_line = NULL;
+ gchar *full_path = NULL;
+
+ /* First try to extract the object's name (= user-friendly description)
+ * from the preset file
+ * */
+
+ full_path = g_build_filename (dir, filename, NULL);
+
+ unprocessed_line = get_early_line_from_preset (full_path, "name=");
+ if (unprocessed_line)
+ {
+ ret = g_strcompress (unprocessed_line);
+ g_free (unprocessed_line);
+ }
+ else
+ {
+ /* The object name defaults to a filename-derived description */
+ ret = g_filename_display_basename (full_path);
+ }
+
+ g_free (full_path);
+
+ return ret;
+}
+
+
+static void
+preset_read_dir_into_list (void)
+{
+ readdirintolist_extended ("Presets", presetlist, NULL, TRUE,
+ get_object_name, NULL);
+}
+
+static gchar *
+preset_create_filename (const gchar *basename,
+ const gchar *dest_dir)
+{
+ gchar *fullpath;
+ gchar *safe_name;
+ gint i;
+ gint unum = 1;
+
+ g_return_val_if_fail (basename != NULL, NULL);
+ g_return_val_if_fail (dest_dir != NULL, NULL);
+ g_return_val_if_fail (g_path_is_absolute (dest_dir), NULL);
+
+ safe_name = g_filename_from_utf8 (basename, -1, NULL, NULL, NULL);
+
+ if (safe_name[0] == '.')
+ safe_name[0] = '-';
+
+ for (i = 0; safe_name[i]; i++)
+ if (safe_name[i] == G_DIR_SEPARATOR || g_ascii_isspace (safe_name[i]))
+ safe_name[i] = '-';
+
+ fullpath = g_build_filename (dest_dir, safe_name, NULL);
+
+ while (g_file_test (fullpath, G_FILE_TEST_EXISTS))
+ {
+ gchar *filename;
+
+ g_free (fullpath);
+
+ filename = g_strdup_printf ("%s-%d", safe_name, unum++);
+
+ fullpath = g_build_filename (dest_dir, filename, NULL);
+
+ g_free (filename);
+ }
+
+ g_free (safe_name);
+
+ return fullpath;
+}
+
+
+static void
+add_factory_defaults (void)
+{
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+ /* Set the filename. */
+ gtk_list_store_set (store, &iter, PRESETS_LIST_COLUMN_FILENAME,
+ factory_defaults, -1);
+ /* Set the object name. */
+ gtk_list_store_set (store, &iter, PRESETS_LIST_COLUMN_OBJECT_NAME,
+ factory_defaults, -1);
+
+}
+
+static void
+preset_refresh_presets (void)
+{
+ gtk_list_store_clear (store);
+ add_factory_defaults ();
+ preset_read_dir_into_list ();
+}
+
+static int
+load_old_preset (const gchar *fname)
+{
+ FILE *f;
+ int len;
+
+ f = g_fopen (fname, "rb");
+ if (!f)
+ {
+ g_printerr ("Error opening file \"%s\" for reading!\n",
+ gimp_filename_to_utf8 (fname));
+ return -1;
+ }
+ len = fread (&pcvals, 1, sizeof (pcvals), f);
+ fclose (f);
+
+ return (len != sizeof (pcvals)) ? -1 : 0;
+}
+
+static unsigned int
+hexval (char c)
+{
+ c = g_ascii_tolower (c);
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ return 0;
+}
+
+static char *
+parse_rgb_string (const gchar *s)
+{
+ static char col[3];
+ col[0] = (hexval (s[0]) << 4) | hexval (s[1]);
+ col[1] = (hexval (s[2]) << 4) | hexval (s[3]);
+ col[2] = (hexval (s[4]) << 4) | hexval (s[5]);
+ return col;
+}
+
+static void
+set_orient_vector (const gchar *str)
+{
+ const gchar *tmps = str;
+ int n;
+
+ n = atoi (tmps);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].x = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].y = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].dir = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].dx = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].dy = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].str = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.orient_vectors[n].type = atoi (++tmps);
+
+}
+
+static void set_size_vector (const gchar *str)
+{
+ const gchar *tmps = str;
+ int n;
+
+ n = atoi (tmps);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.size_vectors[n].x = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.size_vectors[n].y = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.size_vectors[n].siz = g_ascii_strtod (++tmps, NULL);
+
+ if (!(tmps = strchr (tmps, ',')))
+ return;
+ pcvals.size_vectors[n].str = g_ascii_strtod (++tmps, NULL);
+
+}
+
+static void
+parse_desc (const gchar *str, gchar *d, gssize d_len)
+{
+ gchar *dest = g_strcompress (str);
+
+ g_strlcpy (d, dest, d_len);
+
+ g_free (dest);
+}
+
+static void
+set_values (const gchar *key, const gchar *val)
+{
+ if (!strcmp (key, "desc"))
+ parse_desc (val, presetdesc, sizeof (presetdesc));
+ else if (!strcmp (key, "orientnum"))
+ pcvals.orient_num = atoi (val);
+ else if (!strcmp (key, "orientfirst"))
+ pcvals.orient_first = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "orientlast"))
+ pcvals.orient_last = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "orienttype"))
+ pcvals.orient_type = orientation_type_input (atoi (val));
+
+ else if (!strcmp (key, "sizenum"))
+ pcvals.size_num = atoi (val);
+ else if (!strcmp (key, "sizefirst"))
+ pcvals.size_first = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "sizelast"))
+ pcvals.size_last = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "sizetype"))
+ pcvals.size_type = size_type_input (atoi (val));
+
+ else if (!strcmp (key, "brushrelief"))
+ pcvals.brush_relief = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "brushscale"))
+ {
+ /* For compatibility */
+ pcvals.size_num = 1;
+ pcvals.size_first = pcvals.size_last = g_ascii_strtod (val, NULL);
+ }
+ else if (!strcmp (key, "brushdensity"))
+ pcvals.brush_density = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "brushgamma"))
+ pcvals.brushgamma = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "brushaspect"))
+ pcvals.brush_aspect = g_ascii_strtod (val, NULL);
+
+ else if (!strcmp (key, "generalbgtype"))
+ pcvals.general_background_type = general_bg_type_input (atoi (val));
+ else if (!strcmp (key, "generaldarkedge"))
+ pcvals.general_dark_edge = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "generalpaintedges"))
+ pcvals.general_paint_edges = atoi (val);
+ else if (!strcmp (key, "generaltileable"))
+ pcvals.general_tileable = atoi (val);
+ else if (!strcmp (key, "generaldropshadow"))
+ pcvals.general_drop_shadow = atoi (val);
+ else if (!strcmp (key, "generalshadowdarkness"))
+ pcvals.general_shadow_darkness = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "generalshadowdepth"))
+ pcvals.general_shadow_depth = atoi (val);
+ else if (!strcmp (key, "generalshadowblur"))
+ pcvals.general_shadow_blur = atoi (val);
+ else if (!strcmp (key, "devthresh"))
+ pcvals.devthresh = g_ascii_strtod (val, NULL);
+
+ else if (!strcmp (key, "paperrelief"))
+ pcvals.paper_relief = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "paperscale"))
+ pcvals.paper_scale = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "paperinvert"))
+ pcvals.paper_invert = atoi (val);
+ else if (!strcmp (key, "paperoverlay"))
+ pcvals.paper_overlay = atoi (val);
+
+ else if (!strcmp (key, "placetype"))
+ pcvals.place_type = place_type_input (atoi (val));
+ else if (!strcmp (key, "placecenter"))
+ pcvals.placement_center = atoi (val);
+
+ else if (!strcmp (key, "selectedbrush"))
+ g_strlcpy (pcvals.selected_brush, val, sizeof (pcvals.selected_brush));
+ else if (!strcmp (key, "selectedpaper"))
+ g_strlcpy (pcvals.selected_paper, val, sizeof (pcvals.selected_paper));
+
+ else if (!strcmp (key, "color"))
+ {
+ char *c = parse_rgb_string (val);
+ gimp_rgba_set_uchar (&pcvals.color, c[0], c[1], c[2], 255);
+ }
+
+ else if (!strcmp (key, "numorientvector"))
+ pcvals.num_orient_vectors = atoi (val);
+ else if (!strcmp (key, "orientvector"))
+ set_orient_vector (val);
+ else if (!strcmp (key, "orientangoff"))
+ pcvals.orient_angle_offset = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "orientstrexp"))
+ pcvals.orient_strength_exponent = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "orientvoronoi"))
+ pcvals.orient_voronoi = atoi (val);
+
+ else if (!strcmp (key, "numsizevector"))
+ pcvals.num_size_vectors = atoi (val);
+ else if (!strcmp (key, "sizevector"))
+ set_size_vector (val);
+ else if (!strcmp (key, "sizestrexp"))
+ pcvals.size_strength_exponent = g_ascii_strtod (val, NULL);
+ else if (!strcmp (key, "sizevoronoi"))
+ pcvals.size_voronoi = atoi (val);
+
+ else if (!strcmp (key, "colortype"))
+ pcvals.color_type = color_type_input (atoi (val));
+ else if (!strcmp (key, "colornoise"))
+ pcvals.color_noise = g_ascii_strtod (val, NULL);
+}
+
+static int
+load_preset (const gchar *fn)
+{
+ char line[1024] = "";
+ FILE *f;
+
+ f = g_fopen (fn, "rt");
+ if (!f)
+ {
+ g_printerr ("Error opening file \"%s\" for reading!\n",
+ gimp_filename_to_utf8 (fn));
+ return -1;
+ }
+ fgets (line, 10, f);
+ if (strncmp (line, PRESETMAGIC, 4))
+ {
+ fclose (f);
+ return load_old_preset (fn);
+ }
+
+ restore_default_values ();
+
+ while (!feof (f))
+ {
+ char *tmps;
+
+ if (!fgets (line, 1024, f))
+ break;
+ g_strchomp (line);
+ tmps = strchr (line, '=');
+ if (!tmps)
+ continue;
+ *tmps = '\0';
+ tmps++;
+ set_values (line, tmps);
+ }
+ fclose (f);
+ return 0;
+}
+
+int
+select_preset (const gchar *preset)
+{
+ int ret = SELECT_PRESET_OK;
+ /* I'm copying this behavior as is. As it seems applying the
+ * factory_defaults preset does nothing, which I'm not sure
+ * if that was what the author intended.
+ * -- Shlomi Fish
+ */
+ if (strcmp (preset, factory_defaults))
+ {
+ gchar *rel = g_build_filename ("Presets", preset, NULL);
+ gchar *abs = findfile (rel);
+
+ g_free (rel);
+
+ if (abs)
+ {
+ if (load_preset (abs))
+ ret = SELECT_PRESET_LOAD_FAILED;
+
+ g_free (abs);
+ }
+ else
+ {
+ ret = SELECT_PRESET_FILE_NOT_FOUND;
+ }
+ }
+ if (ret == SELECT_PRESET_OK)
+ {
+ /* This is so the colorbrushes param (that is not stored in the
+ * preset will be set correctly upon the preset loading.
+ * */
+ set_colorbrushes (pcvals.selected_brush);
+ }
+
+ return ret;
+}
+
+static void
+apply_preset (GtkWidget *w, GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gchar *preset;
+
+ gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
+ &preset, -1);
+
+ select_preset (preset);
+
+ restore_values ();
+
+ /* g_free (preset); */
+ g_free (selected_preset_filename);
+ selected_preset_filename = preset;
+ }
+}
+
+static void
+delete_preset (GtkWidget *w, GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gchar *preset;
+
+ gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
+ &preset, -1);
+
+ if (preset)
+ {
+ gchar *rel = g_build_filename ("Presets", preset, NULL);
+ gchar *abs = findfile (rel);
+
+ g_free (rel);
+
+ if (abs)
+ {
+ /* Don't delete global presets - bug # 147483 */
+ if (can_delete_preset (abs))
+ g_unlink (abs);
+
+ g_free (abs);
+ }
+
+ preset_refresh_presets ();
+
+ g_free (preset);
+ }
+ }
+}
+
+static void save_preset (void);
+
+static void
+preset_desc_callback (GtkTextBuffer *buffer, gpointer data)
+{
+ char *str;
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ g_strlcpy (presetdesc, str, sizeof (presetdesc));
+ g_free (str);
+}
+
+static void
+save_preset_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ gtk_widget_destroy (widget);
+
+ if (response_id == GTK_RESPONSE_OK)
+ save_preset ();
+}
+
+static void
+create_save_preset (GtkWidget *parent)
+{
+ static GtkWidget *window = NULL;
+ GtkWidget *box, *label;
+ GtkWidget *swin, *text;
+ GtkTextBuffer *buffer;
+
+ if (window)
+ {
+ gtk_window_present (GTK_WINDOW (window));
+ return;
+ }
+
+ window = gimp_dialog_new (_("Save Current"), PLUG_IN_ROLE,
+ gtk_widget_get_toplevel (parent), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (window, "response",
+ G_CALLBACK (save_preset_response),
+ NULL);
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window);
+
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
+ box, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+
+ label = gtk_label_new (_("Description:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ swin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (box), swin, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_show (swin);
+
+ buffer = gtk_text_buffer_new (NULL);
+ g_signal_connect (buffer, "changed",
+ G_CALLBACK (preset_desc_callback), NULL);
+ gtk_text_buffer_set_text (buffer, presetdesc, -1);
+
+ text = gtk_text_view_new_with_buffer (buffer);
+ gtk_widget_set_size_request (text, -1, 192);
+ gtk_container_add (GTK_CONTAINER (swin), text);
+ gtk_widget_show (text);
+
+ gtk_widget_show (window);
+}
+
+static void
+save_preset (void)
+{
+ const gchar *preset_name;
+
+ gchar *fname, *presets_dir_path;
+ FILE *f;
+ GList *thispath;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar vbuf[6][G_ASCII_DTOSTR_BUF_SIZE];
+ guchar color[3];
+ gint i;
+ gchar *desc_escaped, *preset_name_escaped;
+
+ preset_name = gtk_entry_get_text (GTK_ENTRY (presetnameentry));
+ thispath = parsepath ();
+ store_values ();
+
+ if (!thispath)
+ {
+ g_printerr ("Internal error: (save_preset) thispath == NULL\n");
+ return;
+ }
+
+ /* Create the ~/.gimp-$VER/gimpressionist/Presets directory if
+ * it doesn't already exists.
+ *
+ */
+ presets_dir_path = g_build_filename ((const gchar *) thispath->data,
+ "Presets",
+ NULL);
+
+ if (! g_file_test (presets_dir_path, G_FILE_TEST_IS_DIR))
+ {
+ if (g_mkdir (presets_dir_path,
+ S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH) == -1)
+ {
+ g_printerr ("Error creating folder \"%s\"!\n",
+ gimp_filename_to_utf8 (presets_dir_path));
+ g_free (presets_dir_path);
+ return;
+ }
+ }
+
+ /* Check if the user-friendly name has changed. If so, then save it
+ * under a new file. If not - use the same file name.
+ */
+ if (selected_preset_orig_name &&
+ strcmp (preset_name, selected_preset_orig_name) == 0)
+ {
+ fname = g_build_filename (presets_dir_path,
+ selected_preset_filename,
+ NULL);
+ }
+ else
+ {
+ fname = preset_create_filename (preset_name, presets_dir_path);
+ }
+
+ g_free (presets_dir_path);
+
+ if (!fname)
+ {
+ g_printerr ("Error building a filename for preset \"%s\"!\n",
+ preset_name);
+ return;
+ }
+
+ f = g_fopen (fname, "wt");
+ if (!f)
+ {
+ g_printerr ("Error opening file \"%s\" for writing!\n",
+ gimp_filename_to_utf8 (fname));
+ g_free (fname);
+ return;
+ }
+
+ fprintf (f, "%s\n", PRESETMAGIC);
+ desc_escaped = g_strescape (presetdesc, NULL);
+ fprintf (f, "desc=%s\n", desc_escaped);
+ g_free (desc_escaped);
+ preset_name_escaped = g_strescape (preset_name, NULL);
+ fprintf (f, "name=%s\n", preset_name_escaped);
+ g_free (preset_name_escaped);
+ fprintf (f, "orientnum=%d\n", pcvals.orient_num);
+ fprintf (f, "orientfirst=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_first));
+ fprintf (f, "orientlast=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_last));
+ fprintf (f, "orienttype=%d\n", pcvals.orient_type);
+
+ fprintf (f, "sizenum=%d\n", pcvals.size_num);
+ fprintf (f, "sizefirst=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_first));
+ fprintf (f, "sizelast=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_last));
+ fprintf (f, "sizetype=%d\n", pcvals.size_type);
+
+ fprintf (f, "brushrelief=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_relief));
+ fprintf (f, "brushdensity=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_density));
+ fprintf (f, "brushgamma=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brushgamma));
+ fprintf (f, "brushaspect=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_aspect));
+
+ fprintf (f, "generalbgtype=%d\n", pcvals.general_background_type);
+ fprintf (f, "generaldarkedge=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.general_dark_edge));
+ fprintf (f, "generalpaintedges=%d\n", pcvals.general_paint_edges);
+ fprintf (f, "generaltileable=%d\n", pcvals.general_tileable);
+ fprintf (f, "generaldropshadow=%d\n", pcvals.general_drop_shadow);
+ fprintf (f, "generalshadowdarkness=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.general_shadow_darkness));
+ fprintf (f, "generalshadowdepth=%d\n", pcvals.general_shadow_depth);
+ fprintf (f, "generalshadowblur=%d\n", pcvals.general_shadow_blur);
+ fprintf (f, "devthresh=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.devthresh));
+
+ fprintf (f, "paperrelief=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.paper_relief));
+ fprintf (f, "paperscale=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.paper_scale));
+ fprintf (f, "paperinvert=%d\n", pcvals.paper_invert);
+ fprintf (f, "paperoverlay=%d\n", pcvals.paper_overlay);
+
+ fprintf (f, "selectedbrush=%s\n", pcvals.selected_brush);
+ fprintf (f, "selectedpaper=%s\n", pcvals.selected_paper);
+
+ gimp_rgb_get_uchar (&pcvals.color, &color[0], &color[1], &color[2]);
+ fprintf (f, "color=%02x%02x%02x\n", color[0], color[1], color[2]);
+
+ fprintf (f, "placetype=%d\n", pcvals.place_type);
+ fprintf (f, "placecenter=%d\n", pcvals.placement_center);
+
+ fprintf (f, "numorientvector=%d\n", pcvals.num_orient_vectors);
+ for (i = 0; i < pcvals.num_orient_vectors; i++)
+ {
+ g_ascii_dtostr (vbuf[0], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].x);
+ g_ascii_dtostr (vbuf[1], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].y);
+ g_ascii_dtostr (vbuf[2], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dir);
+ g_ascii_dtostr (vbuf[3], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dx);
+ g_ascii_dtostr (vbuf[4], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dy);
+ g_ascii_dtostr (vbuf[5], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].str);
+
+ fprintf (f, "orientvector=%d,%s,%s,%s,%s,%s,%s,%d\n", i,
+ vbuf[0], vbuf[1], vbuf[2], vbuf[3], vbuf[4], vbuf[5],
+ pcvals.orient_vectors[i].type);
+ }
+
+ fprintf (f, "orientangoff=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+ pcvals.orient_angle_offset));
+ fprintf (f, "orientstrexp=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+ pcvals.orient_strength_exponent));
+ fprintf (f, "orientvoronoi=%d\n", pcvals.orient_voronoi);
+
+ fprintf (f, "numsizevector=%d\n", pcvals.num_size_vectors);
+ for (i = 0; i < pcvals.num_size_vectors; i++)
+ {
+ g_ascii_dtostr (vbuf[0], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].x);
+ g_ascii_dtostr (vbuf[1], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].y);
+ g_ascii_dtostr (vbuf[2], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].siz);
+ g_ascii_dtostr (vbuf[3], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].str);
+ fprintf (f, "sizevector=%d,%s,%s,%s,%s\n", i,
+ vbuf[0], vbuf[1], vbuf[2], vbuf[3]);
+ }
+ fprintf (f, "sizestrexp=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_strength_exponent));
+ fprintf (f, "sizevoronoi=%d\n", pcvals.size_voronoi);
+
+ fprintf (f, "colortype=%d\n", pcvals.color_type);
+ fprintf (f, "colornoise=%s\n",
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.color_noise));
+
+ fclose (f);
+ preset_refresh_presets ();
+ reselect (presetlist, fname);
+
+ g_free (fname);
+}
+
+static void
+read_description (const char *fn)
+{
+ char *rel_fname;
+ char *fname;
+ gchar *unprocessed_line;
+
+ rel_fname = g_build_filename ("Presets", fn, NULL);
+ fname = findfile (rel_fname);
+ g_free (rel_fname);
+
+ if (!fname)
+ {
+ if (!strcmp (fn, factory_defaults))
+ {
+ gtk_widget_set_sensitive (delete_button, FALSE);
+ set_preset_description_text (_("Gimpressionist Defaults"));
+ }
+ else
+ {
+ set_preset_description_text ("");
+ }
+ return;
+ }
+
+ /* Don't delete global presets - bug # 147483 */
+ gtk_widget_set_sensitive (delete_button, can_delete_preset (fname));
+
+ unprocessed_line = get_early_line_from_preset (fname, "desc=");
+ g_free (fname);
+
+ if (unprocessed_line)
+ {
+ char tmplabel[4096];
+ parse_desc (unprocessed_line, tmplabel, sizeof (tmplabel));
+ g_free (unprocessed_line);
+ set_preset_description_text (tmplabel);
+ }
+ else
+ {
+ set_preset_description_text ("");
+ }
+}
+
+static void
+presets_list_select_preset (GtkTreeSelection *selection,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gchar *preset_name;
+ gchar *preset_filename;
+
+ gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_OBJECT_NAME,
+ &preset_name, -1);
+ gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
+ &preset_filename, -1);
+
+ /* TODO : Maybe make the factory defaults behavior in regards
+ * to the preset's object name and filename more robust?
+ *
+ */
+ if (strcmp (preset_filename, factory_defaults))
+ {
+ gtk_entry_set_text (GTK_ENTRY (presetnameentry), preset_name);
+ g_free (selected_preset_orig_name);
+ g_free (selected_preset_filename);
+ selected_preset_orig_name = g_strdup (preset_name);
+ selected_preset_filename = g_strdup (selected_preset_filename);
+ }
+
+ read_description (preset_filename);
+
+ g_free (preset_name);
+ g_free (preset_filename);
+ }
+}
+
+static GtkWidget *
+create_presets_list (GtkWidget *parent)
+{
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *swin, *view;
+
+ swin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (parent), swin, FALSE, FALSE, 0);
+ gtk_widget_show (swin);
+ gtk_widget_set_size_request (swin, 200, -1);
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
+ g_object_unref (store);
+ gtk_widget_show (view);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ column =
+ gtk_tree_view_column_new_with_attributes ("Preset", renderer,
+ "text",
+ PRESETS_LIST_COLUMN_OBJECT_NAME,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+
+ gtk_container_add (GTK_CONTAINER (swin), view);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (presets_list_select_preset),
+ NULL);
+
+ return view;
+}
+
+void
+create_presetpage (GtkNotebook *notebook)
+{
+ GtkWidget *vbox, *hbox, *box1, *box2, *thispage;
+ GtkWidget *view;
+ GtkWidget *tmpw;
+ GtkWidget *label;
+ GtkTreeSelection *selection;
+
+ label = gtk_label_new_with_mnemonic (_("_Presets"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box1, FALSE, FALSE, 0);
+ gtk_widget_show (box1);
+
+ presetnameentry = tmpw = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (box1), tmpw, FALSE, FALSE, 0);
+ gtk_widget_set_size_request (tmpw, 200, -1);
+ gtk_widget_show (tmpw);
+
+ presetsavebutton = tmpw = gtk_button_new_with_label ( _("Save Current..."));
+ gtk_button_set_image (GTK_BUTTON (presetsavebutton),
+ gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_SAVE,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_box_pack_start (GTK_BOX (box1), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (create_save_preset), NULL);
+ gimp_help_set_help_data
+ (tmpw, _("Save the current settings to the specified file"), NULL);
+
+ box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box1, TRUE, TRUE, 0);
+ gtk_widget_show (box1);
+
+ presetlist = view = create_presets_list (box1);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ add_factory_defaults ();
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (box1), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), box2, FALSE, FALSE, 0);
+ gtk_widget_show (box2);
+
+ tmpw = gtk_button_new_with_mnemonic (_("_Apply"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (apply_preset), selection);
+ gimp_help_set_help_data
+ (tmpw, _("Reads the selected Preset into memory"), NULL);
+
+ tmpw = delete_button = gtk_button_new_with_mnemonic (_("_Delete"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (delete_preset), selection);
+ gimp_help_set_help_data (tmpw, _("Deletes the selected Preset"), NULL);
+
+ tmpw = gtk_button_new_with_mnemonic (_("_Refresh"));
+ gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked", G_CALLBACK (preset_refresh_presets), NULL);
+ gimp_help_set_help_data (tmpw, _("Reread the folder of Presets"), NULL);
+
+ presetdesclabel = tmpw = gtk_label_new (NULL);
+ gimp_label_set_attributes (GTK_LABEL (tmpw),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_label_set_line_wrap (GTK_LABEL (tmpw), TRUE);
+ /*
+ * Make sure the label's width is reasonable and it won't stretch
+ * the dialog more than its width.
+ * */
+ gtk_widget_set_size_request (tmpw, 240, -1);
+
+ gtk_label_set_xalign (GTK_LABEL (tmpw), 0.0);
+ gtk_label_set_yalign (GTK_LABEL (tmpw), 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), tmpw, TRUE, TRUE, 0);
+ gtk_widget_show (tmpw);
+
+ preset_read_dir_into_list ();
+
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/presets.h b/plug-ins/gimpressionist/presets.h
new file mode 100644
index 0000000..ba67bdc
--- /dev/null
+++ b/plug-ins/gimpressionist/presets.h
@@ -0,0 +1,34 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PRESETS_H
+#define __PRESETS_H
+
+enum SELECT_PRESET_RETURN_VALUES
+{
+ SELECT_PRESET_OK = 0,
+ SELECT_PRESET_FILE_NOT_FOUND = -1,
+ SELECT_PRESET_LOAD_FAILED = -2,
+};
+
+void create_presetpage (GtkNotebook *);
+int select_preset (const gchar *preset);
+void preset_free (void);
+void preset_save_button_set_sensitive (gboolean s);
+
+#endif
+
diff --git a/plug-ins/gimpressionist/preview.c b/plug-ins/gimpressionist/preview.c
new file mode 100644
index 0000000..3619782
--- /dev/null
+++ b/plug-ins/gimpressionist/preview.c
@@ -0,0 +1,191 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "infile.h"
+#include "preview.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static GtkWidget *preview = NULL;
+static GtkWidget *previewbutton = NULL;
+
+void
+preview_set_button_label (const gchar *text)
+{
+ g_object_set (previewbutton,
+ "label", text,
+ "use-underline", TRUE,
+ NULL);
+}
+
+static void
+drawalpha (ppm_t *p, ppm_t *a)
+{
+ int x, y, g;
+ double v;
+ int gridsize = 16;
+ int rowstride = p->width * 3;
+
+ for (y = 0; y < p->height; y++)
+ {
+ for (x = 0; x < p->width; x++)
+ {
+ int k = y * rowstride + x * 3;
+
+ if (!a->col[k])
+ continue;
+
+ v = 1.0 - a->col[k] / 255.0;
+ g = ((x / gridsize + y / gridsize) % 2) * 60 + 100;
+ p->col[k+0] *= v;
+ p->col[k+1] *= v;
+ p->col[k+2] *= v;
+ v = 1.0 - v;
+ p->col[k+0] += g*v;
+ p->col[k+1] += g*v;
+ p->col[k+2] += g*v;
+ }
+ }
+}
+
+static ppm_t preview_ppm = {0, 0, NULL};
+static ppm_t alpha_ppm = {0, 0, NULL};
+static ppm_t backup_ppm = {0, 0, NULL};
+static ppm_t alpha_backup_ppm = {0, 0, NULL};
+
+void
+preview_free_resources (void)
+{
+ ppm_kill (&preview_ppm);
+ ppm_kill (&alpha_ppm);
+ ppm_kill (&backup_ppm);
+ ppm_kill (&alpha_backup_ppm);
+}
+
+void
+updatepreview (GtkWidget *wg, gpointer d)
+{
+ if (!PPM_IS_INITED (&backup_ppm))
+ {
+ infile_copy_to_ppm (&backup_ppm);
+ if ((backup_ppm.width != PREVIEWSIZE) ||
+ (backup_ppm.height != PREVIEWSIZE))
+ resize_fast (&backup_ppm, PREVIEWSIZE, PREVIEWSIZE);
+ if (img_has_alpha)
+ {
+ infile_copy_alpha_to_ppm (&alpha_backup_ppm);
+ if ((alpha_backup_ppm.width != PREVIEWSIZE) ||
+ (alpha_backup_ppm.height != PREVIEWSIZE))
+ resize_fast (&alpha_backup_ppm, PREVIEWSIZE, PREVIEWSIZE);
+ }
+ }
+
+ if (!PPM_IS_INITED (&preview_ppm))
+ {
+ ppm_copy (&backup_ppm, &preview_ppm);
+
+ if (img_has_alpha)
+ ppm_copy (&alpha_backup_ppm, &alpha_ppm);
+ }
+
+ if (d)
+ {
+ store_values ();
+
+ if (GPOINTER_TO_INT (d) != 2)
+ repaint (&preview_ppm, &alpha_ppm);
+ }
+
+ if (img_has_alpha)
+ drawalpha (&preview_ppm, &alpha_ppm);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, PREVIEWSIZE, PREVIEWSIZE,
+ GIMP_RGB_IMAGE,
+ preview_ppm.col,
+ PREVIEWSIZE * 3);
+
+ ppm_kill (&preview_ppm);
+ if (img_has_alpha)
+ ppm_kill (&alpha_ppm);
+}
+
+static void
+preview_size_allocate (GtkWidget *preview)
+{
+ updatepreview (preview, GINT_TO_POINTER (0));
+}
+
+GtkWidget *
+create_preview (void)
+{
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 5);
+ gtk_widget_show (frame);
+
+ preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview, PREVIEWSIZE, PREVIEWSIZE);
+
+ gtk_container_add (GTK_CONTAINER (frame), preview);
+ gtk_widget_show (preview);
+ /* This is so the preview will be displayed when the dialog is invoked. */
+ g_signal_connect (preview, "size-allocate",
+ G_CALLBACK (preview_size_allocate), NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ previewbutton = button = gtk_button_new_with_mnemonic (_("_Update"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (updatepreview), (gpointer) 1);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+ gimp_help_set_help_data (button,
+ _("Refresh the Preview window"), NULL);
+
+ button = gtk_button_new_with_mnemonic (_("_Reset"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (updatepreview), (gpointer) 2);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+ gimp_help_set_help_data (button,
+ _("Revert to the original image"), NULL);
+
+ return vbox;
+}
diff --git a/plug-ins/gimpressionist/preview.h b/plug-ins/gimpressionist/preview.h
new file mode 100644
index 0000000..1e3d456
--- /dev/null
+++ b/plug-ins/gimpressionist/preview.h
@@ -0,0 +1,26 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PREVIEW_H
+#define __PREVIEW_H
+
+GtkWidget* create_preview (void);
+void updatepreview (GtkWidget *wg, gpointer d);
+void preview_free_resources(void);
+void preview_set_button_label(const gchar * text);
+
+#endif /* #ifndef __PREVIEW_H */
diff --git a/plug-ins/gimpressionist/random.h b/plug-ins/gimpressionist/random.h
new file mode 100644
index 0000000..f52cb13
--- /dev/null
+++ b/plug-ins/gimpressionist/random.h
@@ -0,0 +1,24 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RAND_H
+#define __RAND_H
+
+extern GRand *random_generator;
+
+#endif
+
diff --git a/plug-ins/gimpressionist/repaint.c b/plug-ins/gimpressionist/repaint.c
new file mode 100644
index 0000000..9948068
--- /dev/null
+++ b/plug-ins/gimpressionist/repaint.c
@@ -0,0 +1,1201 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "gimpressionist.h"
+#include "brush.h"
+#include "orientation.h"
+#include "placement.h"
+#include "ppmtool.h"
+#include "preview.h"
+#include "random.h"
+#include "size.h"
+#include "infile.h"
+
+#include <libgimp/stdplugins-intl.h>
+
+static gimpressionist_vals_t runningvals;
+
+static double
+get_siz_from_pcvals (double x, double y)
+{
+ return getsiz_proto (x, y, pcvals.num_size_vectors, pcvals.size_vectors,
+ pcvals.size_strength_exponent, pcvals.size_voronoi);
+}
+
+static int
+get_pixel_value (double dir)
+{
+ while (dir < 0.0)
+ dir += 360.0;
+ while (dir >= 360.0)
+ dir -= 360.0;
+ return dir * 255.0 / 360.0;
+}
+
+static void
+prepare_brush (ppm_t *p)
+{
+ int x, y;
+ int rowstride = p->width * 3;
+
+ for (y = 0; y< p->height; y++)
+ {
+ for (x = 0; x < p->width; x++)
+ {
+ p->col[y * rowstride + x * 3 + 1] = 0;
+ }
+ }
+
+ for (y = 1; y< p->height; y++)
+ {
+ for (x = 1; x < p->width; x++)
+ {
+ int v = p->col[y * rowstride + x * 3] -
+ p->col[(y - 1) * rowstride + (x - 1) * 3];
+ if (v < 0)
+ v = 0;
+ p->col[y * rowstride + x * 3 + 1] = v;
+ }
+ }
+}
+
+static double
+sum_brush (ppm_t *p)
+{
+ double sum = 0;
+ int i;
+
+ for (i = 0; i < p->width*3*p->height; i += 3)
+ sum += p->col[i];
+ return sum;
+}
+
+/* TODO : Use r = rgb[0]; g = rgb[1] ; b = rgb[2]; instead of
+ * the direct references here.
+ * */
+static int
+get_hue (guchar *rgb)
+{
+ double h, v, temp, diff;
+ /* TODO : There seems to be some typos in the comments here.
+ * Ask vidar what he meant.
+ * */
+ if ((rgb[0] == rgb[1]) && (rgb[0] == rgb[2])) /* Gray */
+ return 0;
+ v = (rgb[0] > rgb[1] ? rgb[0] : rgb[1]); /* v = st<F8>rste verdi */
+ if (rgb[2] > v)
+ v = rgb[2];
+ temp = (rgb[0] > rgb[1] ? rgb[1] : rgb[0] ); /* temp = minste */
+ if (rgb[2] < temp)
+ temp = rgb[2];
+ diff = v - temp;
+
+ if (v == rgb[0])
+ h = ((double)rgb[1] - rgb[2]) / diff;
+ else if(v == rgb[1])
+ h = ((double)rgb[2] - rgb[0]) / diff + 2;
+ else /* v == rgb[2] */
+ h = ((double)rgb[0] - rgb[1]) / diff + 4;
+ if(h < 0) h += 6;
+ return h * 255.0 / 6.0;
+}
+
+static int
+choose_best_brush (ppm_t *p, ppm_t *a, int tx, int ty,
+ ppm_t *brushes, int num_brushes,
+ double *brushes_sum, int start, int step)
+{
+ double dev, thissum;
+ double bestdev = 0.0;
+ double r, g, b;
+ int best = -1;
+ int x, y, h;
+ long i;
+ GList *brlist = NULL;
+
+ for (i = start; i < num_brushes; i += step)
+ {
+ ppm_t *brush = &brushes[i];
+#if 0
+ thissum = 0.0;
+#endif
+ thissum = brushes_sum[i];
+
+/* TODO: Pointer-arithmeticize this code */
+ r = g = b = 0.0;
+ for (y = 0; y < brush->height; y++)
+ {
+ guchar *row = p->col + (ty + y) * p->width * 3;
+
+ for (x = 0; x < brush->width; x++)
+ {
+ int k = (tx + x) * 3;
+ double v;
+
+ if ((h = brush->col[(y * brush->width * 3) + x * 3]))
+ {
+#if 0
+ thissum += h;
+#endif
+ v = h / 255.0;
+ r += row[k+0] * v;
+ g += row[k+1] * v;
+ b += row[k+2] * v;
+ }
+ }
+ }
+ r = r * 255.0 / thissum;
+ g = g * 255.0 / thissum;
+ b = b * 255.0 / thissum;
+
+ dev = 0.0;
+ for (y = 0; y < brush->height; y++)
+ {
+ guchar *row = p->col + (ty + y) * p->width * 3;
+
+ for (x = 0; x < brush->width; x++)
+ {
+ int k = (tx + x) * 3;
+ double v;
+
+ if ((h = brush->col[(y * brush->width * 3) + x * 3]))
+ {
+ v = h / 255.0;
+ dev += abs (row[k+0] - r) * v;
+ dev += abs (row[k+1] - g) * v;
+ dev += abs (row[k+2] - b) * v;
+ if (img_has_alpha)
+ dev += a->col[(ty + y) * a->width * 3 + (tx + x) * 3] * v;
+ }
+ }
+ }
+ dev /= thissum;
+
+ if ((best == -1) || (dev < bestdev))
+ {
+ if (brlist)
+ g_list_free (brlist);
+ brlist = NULL;
+ }
+
+ if (dev <= bestdev || best < 0)
+ {
+ best = i;
+ bestdev = dev;
+ brlist = g_list_append (brlist, (void *)i);
+ }
+ if (dev < runningvals.devthresh)
+ break;
+ }
+
+ if (!brlist)
+ {
+ g_printerr("What!? No brushes?!\n");
+ return 0;
+ }
+
+ i = g_rand_int_range (random_generator, 0, g_list_length (brlist));
+ best = (long)((g_list_nth (brlist,i))->data);
+ g_list_free (brlist);
+
+ return best;
+}
+
+static void
+apply_brush (ppm_t *brush,
+ ppm_t *shadow,
+ ppm_t *p, ppm_t *a,
+ int tx, int ty, int r, int g, int b)
+{
+ ppm_t tmp;
+ ppm_t atmp;
+ double v, h;
+ int x, y;
+ double edgedarken = 1.0 - runningvals.general_dark_edge;
+ double relief = runningvals.brush_relief / 100.0;
+ int shadowdepth = pcvals.general_shadow_depth;
+ int shadowblur = pcvals.general_shadow_blur;
+
+ atmp.col = 0;
+ atmp.width = 0;
+
+ tmp = *p;
+ if (img_has_alpha)
+ atmp = *a;
+
+ if (shadow)
+ {
+ int sx = tx + shadowdepth - shadowblur * 2;
+ int sy = ty + shadowdepth - shadowblur * 2;
+
+ for (y = 0; y < shadow->height; y++)
+ {
+ guchar *row, *arow = NULL;
+
+ if ((sy + y) < 0)
+ continue;
+ if ((sy + y) >= tmp.height)
+ break;
+ row = tmp.col + (sy + y) * tmp.width * 3;
+
+ if (img_has_alpha)
+ arow = atmp.col + (sy + y) * atmp.width * 3;
+
+ for (x = 0; x < shadow->width; x++)
+ {
+ int k = (sx + x) * 3;
+
+ if ((sx + x) < 0)
+ continue;
+ if ((sx + x) >= tmp.width)
+ break;
+
+ h = shadow->col[y * shadow->width * 3 + x * 3 + 2];
+
+ if (!h)
+ continue;
+ v = 1.0 - (h / 255.0 * runningvals.general_shadow_darkness / 100.0);
+
+ row[k+0] *= v;
+ row[k+1] *= v;
+ row[k+2] *= v;
+ if (img_has_alpha)
+ arow[k] *= v;
+ }
+ }
+ }
+
+ for (y = 0; y < brush->height; y++)
+ {
+ guchar *row = tmp.col + (ty + y) * tmp.width * 3;
+ guchar *arow = NULL;
+
+ if (img_has_alpha)
+ arow = atmp.col + (ty + y) * atmp.width * 3;
+
+ for (x = 0; x < brush->width; x++)
+ {
+ int k = (tx + x) * 3;
+ h = brush->col[y * brush->width * 3 + x * 3];
+
+ if (!h) continue;
+
+ if (runningvals.color_brushes)
+ {
+ v = 1.0 - brush->col[y * brush->width * 3 + x * 3 + 2] / 255.0;
+ row[k+0] *= v;
+ row[k+1] *= v;
+ row[k+2] *= v;
+ if (img_has_alpha)
+ arow[(tx + x) * 3] *= v;
+ }
+ v = (1.0 - h / 255.0) * edgedarken;
+ row[k+0] *= v;
+ row[k+1] *= v;
+ row[k+2] *= v;
+ if(img_has_alpha) arow[k] *= v;
+
+ v = h / 255.0;
+ row[k+0] += r * v;
+ row[k+1] += g * v;
+ row[k+2] += b * v;
+ }
+ }
+
+ if (relief > 0.001)
+ {
+ for (y = 1; y < brush->height; y++)
+ {
+ guchar *row = tmp.col + (ty + y) * tmp.width * 3;
+
+ for (x = 1; x < brush->width; x++)
+ {
+ int k = (tx + x) * 3;
+ h = brush->col[y * brush->width * 3 + x * 3 + 1] * relief;
+ if (h < 0.001)
+ continue;
+ if (h > 255) h = 255;
+ row[k+0] = (row[k+0] * (255-h) + 255 * h) / 255;
+ row[k+1] = (row[k+1] * (255-h) + 255 * h) / 255;
+ row[k+2] = (row[k+2] * (255-h) + 255 * h) / 255;
+ }
+ }
+ }
+}
+
+void
+repaint (ppm_t *p, ppm_t *a)
+{
+ int x, y;
+ int tx = 0, ty = 0;
+ ppm_t tmp = {0, 0, NULL};
+ ppm_t atmp = {0, 0, NULL};
+ int r, g, b, h, i, j, on, sn;
+ int num_brushes, maxbrushwidth, maxbrushheight;
+ guchar back[3] = {0, 0, 0};
+ ppm_t *brushes, *shadows;
+ ppm_t *brush, *shadow = NULL;
+ double *brushes_sum;
+ int cx, cy, maxdist;
+ double scale, relief, startangle, anglespan, density, bgamma;
+ int max_progress;
+ ppm_t paper_ppm = {0, 0, NULL};
+ ppm_t dirmap = {0, 0, NULL};
+ ppm_t sizmap = {0, 0, NULL};
+ int *xpos = NULL, *ypos = NULL;
+ int progstep;
+ static int running = 0;
+
+ int dropshadow = pcvals.general_drop_shadow;
+ int shadowblur = pcvals.general_shadow_blur;
+
+ if (running)
+ return;
+ running++;
+
+ runningvals = pcvals;
+
+ /* Shouldn't be necessary, but... */
+ if (img_has_alpha)
+ if ((p->width != a->width) || (p->height != a->height))
+ {
+ g_printerr ("Huh? Image size != alpha size?\n");
+ return;
+ }
+
+ num_brushes = runningvals.orient_num * runningvals.size_num;
+ startangle = runningvals.orient_first;
+ anglespan = runningvals.orient_last;
+
+ density = runningvals.brush_density;
+
+ if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
+ density /= 3.0;
+
+ bgamma = runningvals.brushgamma;
+
+ brushes = g_malloc (num_brushes * sizeof (ppm_t));
+ brushes_sum = g_malloc (num_brushes * sizeof (double));
+
+ if (dropshadow)
+ shadows = g_malloc (num_brushes * sizeof (ppm_t));
+ else
+ shadows = NULL;
+
+ brushes[0].col = NULL;
+ brush_get_selected (&brushes[0]);
+
+ resize (&brushes[0],
+ brushes[0].width,
+ brushes[0].height * pow (10, runningvals.brush_aspect));
+ scale = runningvals.size_last / MAX (brushes[0].width, brushes[0].height);
+
+ if (bgamma != 1.0)
+ ppm_apply_gamma (&brushes[0], 1.0 / bgamma, 1,1,1);
+
+ resize (&brushes[0], brushes[0].width * scale, brushes[0].height * scale);
+ i = 1 + sqrt (brushes[0].width * brushes[0].width +
+ brushes[0].height * brushes[0].height);
+ ppm_pad (&brushes[0], i-brushes[0].width, i-brushes[0].width,
+ i - brushes[0].height, i - brushes[0].height, back);
+
+ for (i = 1; i < num_brushes; i++)
+ {
+ brushes[i].col = NULL;
+ ppm_copy (&brushes[0], &brushes[i]);
+ }
+
+ for (i = 0; i < runningvals.size_num; i++)
+ {
+ double sv;
+ if (runningvals.size_num > 1)
+ sv = i / (runningvals.size_num - 1.0);
+ else sv = 1.0;
+ for (j = 0; j < runningvals.orient_num; j++)
+ {
+ h = j + i * runningvals.orient_num;
+ free_rotate (&brushes[h],
+ startangle + j * anglespan / runningvals.orient_num);
+ rescale (&brushes[h],
+ ( sv * runningvals.size_first +
+ (1.0-sv) * runningvals.size_last ) / runningvals.size_last);
+ autocrop (&brushes[h],1);
+ }
+ }
+
+ /* Brush-debugging */
+#if 0
+ for (i = 0; i < num_brushes; i++)
+ {
+ char tmp[1000];
+ g_snprintf (tmp, sizeof (tmp), "/tmp/_brush%03d.ppm", i);
+ ppm_save (&brushes[i], tmp);
+ }
+#endif
+
+ for (i = 0; i < num_brushes; i++)
+ {
+ if (!runningvals.color_brushes)
+ prepare_brush (&brushes[i]);
+ brushes_sum[i] = sum_brush (&brushes[i]);
+ }
+
+ brush = &brushes[0];
+
+ maxbrushwidth = maxbrushheight = 0;
+ for (i = 0; i < num_brushes; i++)
+ {
+ if (brushes[i].width > maxbrushwidth)
+ maxbrushwidth = brushes[i].width;
+ if (brushes[i].height > maxbrushheight)
+ maxbrushheight = brushes[i].height;
+ }
+
+ for (i = 0; i < num_brushes; i++)
+ {
+ int xp, yp;
+ guchar blk[3] = {0, 0, 0};
+
+ xp = maxbrushwidth - brushes[i].width;
+ yp = maxbrushheight - brushes[i].height;
+ if (xp || yp)
+ ppm_pad (&brushes[i], xp / 2, xp - xp / 2, yp / 2, yp - yp / 2, blk);
+ }
+
+ if (dropshadow)
+ {
+ for (i = 0; i < num_brushes; i++)
+ {
+ shadows[i].col = NULL;
+ ppm_copy (&brushes[i], &shadows[i]);
+ ppm_apply_gamma (&shadows[i], 0, 1,1,0);
+ ppm_pad (&shadows[i], shadowblur*2, shadowblur*2,
+ shadowblur*2, shadowblur*2, back);
+ for (j = 0; j < shadowblur; j++)
+ blur (&shadows[i], 2, 2);
+#if 0
+ autocrop (&shadows[i],1);
+#endif
+ }
+#if 0
+ maxbrushwidth += shadowdepth*3;
+ maxbrushheight += shadowdepth*3;
+#endif
+ }
+
+ /* For extra annoying debugging :-) */
+#if 0
+ ppm_save (brushes, "/tmp/__brush.ppm");
+ if (shadows) ppm_save (shadows, "/tmp/__shadow.ppm");
+ system ("xv /tmp/__brush.ppm & xv /tmp/__shadow.ppm & ");
+#endif
+
+ if (runningvals.general_paint_edges)
+ {
+ edgepad (p, maxbrushwidth, maxbrushwidth,
+ maxbrushheight, maxbrushheight);
+ if (img_has_alpha)
+ edgepad (a, maxbrushwidth, maxbrushwidth,
+ maxbrushheight, maxbrushheight);
+ }
+
+ if (img_has_alpha)
+ {
+ /* Initially fully transparent */
+ if (runningvals.general_background_type == BG_TYPE_TRANSPARENT)
+ {
+ guchar tmpcol[3] = {255, 255, 255};
+
+ ppm_new (&atmp, a->width, a->height);
+ fill (&atmp, tmpcol);
+ }
+ else
+ {
+ ppm_copy (a, &atmp);
+ }
+ }
+
+ if (runningvals.general_background_type == BG_TYPE_SOLID)
+ {
+ guchar tmpcol[3];
+
+ ppm_new (&tmp, p->width, p->height);
+ gimp_rgb_get_uchar (&runningvals.color,
+ &tmpcol[0], &tmpcol[1], &tmpcol[2]);
+ fill (&tmp, tmpcol);
+ }
+ else if (runningvals.general_background_type == BG_TYPE_KEEP_ORIGINAL)
+ {
+ ppm_copy (p, &tmp);
+ }
+ else
+ {
+ int dx, dy;
+
+ ppm_new (&tmp, p->width, p->height);
+ ppm_load (runningvals.selected_paper, &paper_ppm);
+
+ if (runningvals.paper_scale != 100.0)
+ {
+ scale = runningvals.paper_scale / 100.0;
+ resize (&paper_ppm, paper_ppm.width * scale, paper_ppm.height * scale);
+ }
+
+ if (runningvals.paper_invert)
+ ppm_apply_gamma (&paper_ppm, -1.0, 1, 1, 1);
+
+ dx = runningvals.general_paint_edges ? paper_ppm.width - maxbrushwidth : 0;
+ dy = runningvals.general_paint_edges ? paper_ppm.height - maxbrushheight : 0;
+
+ for (y = 0; y < tmp.height; y++)
+ {
+ int lx;
+ int ry = (y + dy) % paper_ppm.height;
+
+ for (x = 0; x < tmp.width; x+=lx)
+ {
+ int rx = (x + dx) % paper_ppm.width;
+
+ lx = MIN (tmp.width - x, paper_ppm.width - rx);
+
+ memcpy (&tmp.col[y * tmp.width * 3 + x * 3],
+ &paper_ppm.col[ry * paper_ppm.width * 3 + rx * 3],
+ 3 * lx);
+ }
+ }
+ }
+
+ cx = p->width / 2;
+ cy = p->height / 2;
+ maxdist = sqrt (cx * cx + cy * cy);
+
+ switch (runningvals.orient_type)
+ {
+ case ORIENTATION_VALUE:
+ ppm_new (&dirmap, p->width, p->height);
+ for (y = 0; y < dirmap.height; y++)
+ {
+ guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
+ guchar *srcrow = &p->col[y * p->width * 3];
+ for (x = 0; x < dirmap.width; x++)
+ {
+ dstrow[x * 3] =
+ (srcrow[x * 3] + srcrow[x * 3 + 1] + srcrow[x * 3 + 2]) / 3;
+ }
+ }
+ break;
+
+ case ORIENTATION_RADIUS:
+ ppm_new (&dirmap, p->width, p->height);
+ for (y = 0; y < dirmap.height; y++)
+ {
+ guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
+ double ysqr = (cy - y) * (cy - y);
+
+ for (x = 0; x < dirmap.width; x++)
+ {
+ dstrow[x*3] = sqrt ((cx - x) * (cx - x) + ysqr) * 255 / maxdist;
+ }
+ }
+ break;
+
+ case ORIENTATION_RADIAL:
+ ppm_new (&dirmap, p->width, p->height);
+ for (y = 0; y < dirmap.height; y++)
+ {
+ guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
+
+ for (x = 0; x < dirmap.width; x++)
+ {
+ dstrow[x * 3] = (G_PI + atan2 (cy - y, cx - x)) *
+ 255.0 / (G_PI * 2);
+ }
+ }
+ break;
+
+ case ORIENTATION_FLOWING:
+ ppm_new (&dirmap, p->width / 6 + 5, p->height / 6 + 5);
+ mkgrayplasma (&dirmap, 15);
+ blur (&dirmap, 2, 2);
+ blur (&dirmap, 2, 2);
+ resize (&dirmap, p->width, p->height);
+ blur (&dirmap, 2, 2);
+ if (runningvals.general_paint_edges)
+ edgepad (&dirmap, maxbrushwidth, maxbrushheight,
+ maxbrushwidth, maxbrushheight);
+ break;
+
+ case ORIENTATION_HUE:
+ ppm_new (&dirmap, p->width, p->height);
+ for (y = 0; y < dirmap.height; y++)
+ {
+ guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
+ guchar *srcrow = &p->col[y * p->width * 3];
+
+ for (x = 0; x < dirmap.width; x++)
+ {
+ dstrow[x * 3] = get_hue (&srcrow[x * 3]);
+ }
+ }
+ break;
+
+ case ORIENTATION_ADAPTIVE:
+ {
+ guchar tmpcol[3] = {0, 0, 0};
+
+ ppm_new (&dirmap, p->width, p->height);
+ fill (&dirmap, tmpcol);
+ }
+ break;
+
+ case ORIENTATION_MANUAL:
+ ppm_new (&dirmap, p->width-maxbrushwidth*2, p->height-maxbrushheight*2);
+ for (y = 0; y < dirmap.height; y++)
+ {
+ guchar *dstrow = &dirmap.col[y * dirmap.width * 3];
+ double tmpy = y / (double)dirmap.height;
+ for (x = 0; x < dirmap.width; x++)
+ {
+ dstrow[x * 3] = get_pixel_value(90 -
+ get_direction(x /
+ (double)dirmap.width,
+ tmpy, 1));
+ }
+ }
+ edgepad (&dirmap,
+ maxbrushwidth, maxbrushwidth,
+ maxbrushheight, maxbrushheight);
+ break;
+ }
+
+ if (runningvals.size_type == SIZE_TYPE_VALUE)
+ {
+ ppm_new (&sizmap, p->width, p->height);
+ for (y = 0; y < sizmap.height; y++)
+ {
+ guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
+ guchar *srcrow = &p->col[y * p->width * 3];
+
+ for (x = 0; x < sizmap.width; x++)
+ {
+ dstrow[x * 3] =
+ (srcrow[x * 3] + srcrow[x * 3 + 1] + srcrow[x * 3 + 2]) / 3;
+ }
+ }
+ }
+ else if (runningvals.size_type == SIZE_TYPE_RADIUS)
+ {
+ ppm_new (&sizmap, p->width, p->height);
+ for (y = 0; y < sizmap.height; y++)
+ {
+ guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
+ double ysqr = (cy - y) * (cy - y);
+
+ for (x = 0; x < sizmap.width; x++)
+ {
+ dstrow[x * 3] =
+ sqrt ((cx - x) * (cx - x) + ysqr) * 255 / maxdist;
+ }
+ }
+ }
+ else if (runningvals.size_type == SIZE_TYPE_RADIAL)
+ {
+ ppm_new (&sizmap, p->width, p->height);
+ for (y = 0; y < sizmap.height; y++)
+ {
+ guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
+
+ for (x = 0; x < sizmap.width; x++)
+ {
+ dstrow[x * 3] = (G_PI + atan2 (cy - y, cx - x)) *
+ 255.0 / (G_PI * 2);
+ }
+ }
+ }
+ else if (runningvals.size_type == SIZE_TYPE_FLOWING)
+ {
+ ppm_new (&sizmap, p->width / 6 + 5, p->height / 6 + 5);
+ mkgrayplasma (&sizmap, 15);
+ blur (&sizmap, 2, 2);
+ blur (&sizmap, 2, 2);
+ resize (&sizmap, p->width, p->height);
+ blur (&sizmap, 2, 2);
+ if (runningvals.general_paint_edges)
+ edgepad (&sizmap,
+ maxbrushwidth, maxbrushheight,
+ maxbrushwidth, maxbrushheight);
+ }
+ else if (runningvals.size_type == SIZE_TYPE_HUE)
+ {
+ ppm_new (&sizmap, p->width, p->height);
+ for (y = 0; y < sizmap.height; y++)
+ {
+ guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
+ guchar *srcrow = &p->col[y * p->width * 3];
+
+ for (x = 0; x < sizmap.width; x++)
+ {
+ dstrow[ x * 3] = get_hue (&srcrow[x * 3]);
+ }
+ }
+ }
+ else if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
+ {
+ guchar tmpcol[3] = {0, 0, 0};
+
+ ppm_new (&sizmap, p->width, p->height);
+ fill (&sizmap, tmpcol);
+ }
+ else if (runningvals.size_type == SIZE_TYPE_MANUAL)
+ {
+ ppm_new (&sizmap,
+ p->width-maxbrushwidth * 2,
+ p->height-maxbrushheight * 2);
+
+ for (y = 0; y < sizmap.height; y++)
+ {
+ guchar *dstrow = &sizmap.col[y * sizmap.width * 3];
+ double tmpy = y / (double)sizmap.height;
+
+ for (x = 0; x < sizmap.width; x++)
+ {
+ dstrow[x * 3] = 255 * (1.0 - get_siz_from_pcvals (x / (double)sizmap.width, tmpy));
+ }
+ }
+ edgepad (&sizmap,
+ maxbrushwidth, maxbrushwidth,
+ maxbrushheight, maxbrushheight);
+ }
+#if 0
+ ppm_save(&sizmap, "/tmp/_sizmap.ppm");
+#endif
+ if (runningvals.place_type == PLACEMENT_TYPE_RANDOM)
+ {
+ i = tmp.width * tmp.height / (maxbrushwidth * maxbrushheight);
+ i *= density;
+ }
+ else if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
+ {
+ i = (int)(tmp.width * density / maxbrushwidth) *
+ (int)(tmp.height * density / maxbrushheight);
+#if 0
+ g_printerr("i=%d\n", i);
+#endif
+ }
+
+ if (i < 1)
+ i = 1;
+
+ max_progress = i;
+ progstep = max_progress / 30;
+ if (progstep < 10)
+ progstep = 10;
+
+ if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
+ {
+ int j;
+
+ xpos = g_new (int, i);
+ ypos = g_new (int, i);
+ for (j = 0; j < i; j++)
+ {
+ int factor = (int)(tmp.width * density / maxbrushwidth + 0.5);
+
+ if (factor < 1)
+ factor = 1;
+ xpos[j] = maxbrushwidth/2 + (j % factor) * maxbrushwidth / density;
+ ypos[j] = maxbrushheight/2 + (j / factor) * maxbrushheight / density;
+ }
+ for (j = 0; j < i; j++)
+ {
+ int a, b;
+
+ a = g_rand_int_range (random_generator, 0, i);
+ b = xpos[j]; xpos[j] = xpos[a]; xpos[a] = b;
+ b = ypos[j]; ypos[j] = ypos[a]; ypos[a] = b;
+ }
+ }
+
+ for (; i; i--)
+ {
+ int n;
+ double thissum;
+
+ if (i % progstep == 0)
+ {
+ if(runningvals.run)
+ {
+ gimp_progress_update (0.8 - 0.8 * ((double)i / max_progress));
+ }
+ else
+ {
+ char tmps[40];
+
+ g_snprintf (tmps, sizeof (tmps),
+ "%.1f %%", 100 * (1.0 - ((double)i / max_progress)));
+ preview_set_button_label (tmps);
+
+ while(gtk_events_pending())
+ gtk_main_iteration();
+ }
+ }
+
+ if (runningvals.place_type == PLACEMENT_TYPE_RANDOM)
+ {
+ tx = g_rand_int_range (random_generator, maxbrushwidth / 2,
+ tmp.width - maxbrushwidth / 2);
+ ty = g_rand_int_range (random_generator, maxbrushheight / 2,
+ tmp.height - maxbrushheight / 2);
+ }
+ else if (runningvals.place_type == PLACEMENT_TYPE_EVEN_DIST)
+ {
+ tx = xpos[i - 1];
+ ty = ypos[i - 1];
+ }
+ if (runningvals.placement_center)
+ {
+ double z = g_rand_double_range (random_generator, 0, 0.75);
+ tx = tx * (1.0 - z) + tmp.width / 2 * z;
+ ty = ty * (1.0 - z) + tmp.height / 2 * z;
+ }
+
+ if ((tx < maxbrushwidth / 2) ||
+ (ty < maxbrushwidth / 2) ||
+ (tx + maxbrushwidth / 2 >= p->width) ||
+ (ty + maxbrushheight / 2 >= p->height))
+ {
+#if 0
+ g_printerr("Internal Error; invalid coords: (%d,%d) i=%d\n", tx, ty, i);
+#endif
+ continue;
+ }
+
+ if (img_has_alpha)
+ {
+ if (a->col[ty * a->width * 3 + tx * 3] > 128)
+ continue;
+ }
+
+ sn = on = 0;
+
+ switch (runningvals.orient_type)
+ {
+ case ORIENTATION_RANDOM:
+ on = g_rand_int_range (random_generator, 0, runningvals.orient_num);
+ break;
+
+ case ORIENTATION_VALUE:
+ case ORIENTATION_RADIUS:
+ case ORIENTATION_RADIAL:
+ case ORIENTATION_FLOWING:
+ case ORIENTATION_HUE:
+ case ORIENTATION_MANUAL:
+ on = runningvals.orient_num *
+ dirmap.col[ty * dirmap.width * 3 + tx * 3] / 256;
+ break;
+
+ case ORIENTATION_ADAPTIVE:
+ break; /* Handled below */
+
+ default:
+ g_printerr ("Internal error; Unknown orientationtype\n");
+ on = 0;
+ break;
+ }
+
+ switch (runningvals.size_type)
+ {
+ case SIZE_TYPE_RANDOM:
+ sn = g_rand_int_range (random_generator, 0, runningvals.size_num);
+ break;
+
+ case SIZE_TYPE_VALUE:
+ case SIZE_TYPE_RADIUS:
+ case SIZE_TYPE_RADIAL:
+ case SIZE_TYPE_FLOWING:
+ case SIZE_TYPE_HUE:
+ case SIZE_TYPE_MANUAL:
+ sn = runningvals.size_num * sizmap.col[ty*sizmap.width*3+tx*3] / 256;
+ break;
+
+ case SIZE_TYPE_ADAPTIVE:
+ break; /* Handled below */
+
+ default:
+ g_printerr ("Internal error; Unknown size_type\n");
+ sn = 0;
+ break;
+ }
+
+ /* Handle Adaptive selections */
+ if (runningvals.orient_type == ORIENTATION_ADAPTIVE)
+ {
+ if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
+ n = choose_best_brush (p, a, tx-maxbrushwidth/2,
+ ty-maxbrushheight/2, brushes,
+ num_brushes, brushes_sum, 0, 1);
+ else
+ {
+ int st = sn * runningvals.orient_num;
+ n = choose_best_brush (p, a, tx-maxbrushwidth/2,
+ ty-maxbrushheight/2, brushes,
+ st+runningvals.orient_num, brushes_sum,
+ st, 1);
+ }
+ }
+ else
+ {
+ if (runningvals.size_type == SIZE_TYPE_ADAPTIVE)
+ n = choose_best_brush (p, a, tx-maxbrushwidth/2,
+ ty-maxbrushheight/2, brushes,
+ num_brushes, brushes_sum,
+ on, runningvals.orient_num);
+ else
+ n = sn * runningvals.orient_num + on;
+ }
+ /* Should never happen, but hey... */
+ if (n < 0)
+ n = 0;
+ else if (n >= num_brushes)
+ n = num_brushes - 1;
+
+ tx -= maxbrushwidth/2;
+ ty -= maxbrushheight/2;
+
+ brush = &brushes[n];
+ if (dropshadow)
+ shadow = &shadows[n];
+ thissum = brushes_sum[n];
+
+ /* Calculate color - avg. of in-brush pixels */
+ if (runningvals.color_type == 0)
+ {
+ r = g = b = 0;
+ for (y = 0; y < brush->height; y++)
+ {
+ guchar *row = &p->col[(ty + y) * p->width * 3];
+
+ for (x = 0; x < brush->width; x++)
+ {
+ int k = (tx + x) * 3;
+ double v;
+
+ if ((h = brush->col[y * brush->width * 3 + x * 3]))
+ {
+ v = h / 255.0;
+ r += row[k+0] * v;
+ g += row[k+1] * v;
+ b += row[k+2] * v;
+ }
+ }
+ }
+ r = r * 255.0 / thissum;
+ g = g * 255.0 / thissum;
+ b = b * 255.0 / thissum;
+ }
+ else if (runningvals.color_type == 1)
+ {
+ guchar *pixel;
+
+ y = ty + (brush->height / 2);
+ x = tx + (brush->width / 2);
+ pixel = &p->col[y*p->width * 3 + x * 3];
+ r = pixel[0];
+ g = pixel[1];
+ b = pixel[2];
+ }
+ else
+ {
+ /* No such color_type! */
+ r = g = b = 0;
+ }
+ if (runningvals.color_noise > 0.0)
+ {
+ double v = runningvals.color_noise;
+#define BOUNDS(a) (((a) < 0) ? (0) : ((a) > 255) ? 255 : (a))
+#define MYASSIGN(a) \
+ { \
+ a = a + g_rand_double_range (random_generator, -v/2.0, v/2.0); \
+ a = BOUNDS(a) ; \
+ }
+ MYASSIGN (r);
+ MYASSIGN (g);
+ MYASSIGN (b);
+#undef BOUNDS
+#undef MYASSIGN
+ }
+
+ apply_brush (brush, shadow, &tmp, &atmp, tx,ty, r,g,b);
+
+ if (runningvals.general_tileable && runningvals.general_paint_edges)
+ {
+ int orig_width = tmp.width - 2 * maxbrushwidth;
+ int orig_height = tmp.height - 2 * maxbrushheight;
+ int dox = 0, doy = 0;
+
+ if (tx < maxbrushwidth)
+ {
+ apply_brush (brush, shadow, &tmp, &atmp, tx+orig_width,ty, r,g,b);
+ dox = -1;
+ }
+ else if (tx > orig_width)
+ {
+ apply_brush (brush, shadow, &tmp, &atmp, tx-orig_width,ty, r,g,b);
+ dox = 1;
+ }
+ if (ty < maxbrushheight)
+ {
+ apply_brush (brush, shadow, &tmp, &atmp, tx,ty+orig_height, r,g,b);
+ doy = 1;
+ }
+ else if (ty > orig_height)
+ {
+ apply_brush (brush, shadow, &tmp, &atmp, tx,ty-orig_height, r,g,b);
+ doy = -1;
+ }
+ if (doy)
+ {
+ if (dox < 0)
+ apply_brush (brush, shadow, &tmp, &atmp,
+ tx+orig_width, ty + doy * orig_height, r, g, b);
+ if (dox > 0)
+ apply_brush (brush, shadow, &tmp, &atmp,
+ tx-orig_width, ty + doy * orig_height, r, g, b);
+ }
+ }
+ }
+ for (i = 0; i < num_brushes; i++)
+ {
+ ppm_kill (&brushes[i]);
+ }
+ g_free (brushes);
+ g_free (shadows);
+ g_free (brushes_sum);
+
+ g_free (xpos);
+ g_free (ypos);
+
+ if (runningvals.general_paint_edges)
+ {
+ crop (&tmp,
+ maxbrushwidth, maxbrushheight,
+ tmp.width - maxbrushwidth, tmp.height - maxbrushheight);
+ if (img_has_alpha)
+ crop (&atmp,
+ maxbrushwidth, maxbrushheight,
+ atmp.width - maxbrushwidth, atmp.height - maxbrushheight);
+ }
+
+ ppm_kill (p);
+ p->width = tmp.width;
+ p->height = tmp.height;
+ p->col = tmp.col;
+
+ if (img_has_alpha)
+ {
+ ppm_kill (a);
+ a->width = atmp.width;
+ a->height = atmp.height;
+ a->col = atmp.col;
+ }
+
+ relief = runningvals.paper_relief / 100.0;
+ if (relief > 0.001)
+ {
+ scale = runningvals.paper_scale / 100.0;
+
+ if (PPM_IS_INITED (&paper_ppm))
+ {
+ tmp = paper_ppm;
+ paper_ppm.col = NULL;
+ }
+ else
+ {
+ tmp.col = NULL;
+ ppm_load (runningvals.selected_paper, &tmp);
+ resize (&tmp, tmp.width * scale, tmp.height * scale);
+ if (runningvals.paper_invert)
+ ppm_apply_gamma (&tmp, -1.0, 1,1,1);
+ }
+ for (x = 0; x < p->width; x++)
+ {
+ double h, v;
+ int px = x % tmp.width, py;
+
+ for (y = 0; y < p->height; y++)
+ {
+ int k = y * p->width * 3 + x * 3;
+
+ py = y % tmp.height;
+ if (runningvals.paper_overlay)
+ h = (tmp.col[py * tmp.width * 3 + px * 3]-128) * relief;
+ else
+ h = (tmp.col[py * tmp.width * 3 + px * 3] -
+ (int)tmp.col[((py + 1) % tmp.height) * tmp.width * 3 +
+ ((px + 1) % tmp.width) * 3]) /
+ -2.0 * relief;
+ if (h <= 0.0)
+ {
+ v = 1.0 + h/128.0;
+ if (v < 0.0)
+ v = 0.0;
+ else if (v > 1.0)
+ v = 1.0;
+ p->col[k+0] *= v;
+ p->col[k+1] *= v;
+ p->col[k+2] *= v;
+ }
+ else
+ {
+ v = h/128.0;
+ if (v < 0.0)
+ v = 0.0;
+ else if (v > 1.0)
+ v = 1.0;
+ p->col[k+0] = p->col[k+0] * (1.0-v) + 255 * v;
+ p->col[k+1] = p->col[k+1] * (1.0-v) + 255 * v;
+ p->col[k+2] = p->col[k+2] * (1.0-v) + 255 * v;
+ }
+ }
+ }
+ ppm_kill (&tmp);
+ }
+
+ ppm_kill (&paper_ppm);
+ ppm_kill (&dirmap);
+ ppm_kill (&sizmap);
+
+ if (runningvals.run)
+ {
+ gimp_progress_update (0.8);
+ }
+ else
+ {
+ preview_set_button_label (_("_Update"));
+ }
+
+ running = 0;
+}
diff --git a/plug-ins/gimpressionist/size.c b/plug-ins/gimpressionist/size.c
new file mode 100644
index 0000000..007e09e
--- /dev/null
+++ b/plug-ins/gimpressionist/size.c
@@ -0,0 +1,210 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "size.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define NUMSIZERADIO 8
+
+static GtkObject *sizenumadjust = NULL;
+static GtkObject *sizefirstadjust = NULL;
+static GtkObject *sizelastadjust = NULL;
+static GtkWidget *sizeradio[NUMSIZERADIO];
+
+static void
+size_store (GtkWidget *wg, void *d)
+{
+ pcvals.size_type = GPOINTER_TO_INT (d);
+}
+
+int size_type_input (int in)
+{
+ return CLAMP_UP_TO (in, NUMSIZERADIO);
+}
+
+static void
+size_type_restore (void)
+{
+ gtk_toggle_button_set_active (
+ GTK_TOGGLE_BUTTON (sizeradio[pcvals.size_type]),
+ TRUE);
+}
+
+void
+size_restore (void)
+{
+ size_type_restore ();
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (sizenumadjust),
+ pcvals.size_num);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (sizefirstadjust),
+ pcvals.size_first);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (sizelastadjust),
+ pcvals.size_last);
+}
+
+static void
+create_sizemap_dialog_helper (GtkWidget *widget)
+{
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sizeradio[7]), TRUE);
+ create_sizemap_dialog (widget);
+}
+
+static void
+create_size_radio_button (GtkWidget *box,
+ int orient_type,
+ const gchar *label,
+ const gchar *help_string,
+ GSList **radio_group)
+{
+ create_radio_button (box, orient_type, size_store, label,
+ help_string, radio_group, sizeradio);
+}
+
+void
+create_sizepage (GtkNotebook *notebook)
+{
+ GtkWidget *box2, *box3, *box4, *thispage;
+ GtkWidget *label, *tmpw, *table;
+ GSList *radio_group = NULL;
+
+ label = gtk_label_new_with_mnemonic (_("_Size"));
+
+ thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
+ gtk_widget_show (thispage);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ sizenumadjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Size variants:"),
+ 150, -1, pcvals.size_num,
+ 1.0, 30.0, 1.0, 1.0, 0,
+ TRUE, 0, 0,
+ _("The number of sizes of brushes to use"),
+ NULL);
+ g_signal_connect (sizenumadjust, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &pcvals.size_num);
+
+ sizefirstadjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Minimum size:"),
+ 150, -1, pcvals.size_first,
+ 0.0, 360.0, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("The smallest brush to create"),
+ NULL);
+ g_signal_connect (sizefirstadjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.size_first);
+
+ sizelastadjust =
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Maximum size:"),
+ 150, -1, pcvals.size_last,
+ 0.0, 360.0, 1.0, 10.0, 0,
+ TRUE, 0, 0,
+ _("The largest brush to create"),
+ NULL);
+ g_signal_connect (sizelastadjust, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pcvals.size_last);
+
+ box2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (thispage), box2,FALSE,FALSE,0);
+ gtk_widget_show (box2);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box2), box3, FALSE, FALSE, 0);
+ gtk_widget_show (box3);
+
+ tmpw = gtk_label_new (_("Size depends on:"));
+ gtk_box_pack_start (GTK_BOX (box3), tmpw,FALSE,FALSE,0);
+ gtk_widget_show (tmpw);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX( box2), box3, FALSE, FALSE, 0);
+ gtk_widget_show (box3);
+
+ create_size_radio_button (box3, SIZE_TYPE_VALUE, _("Value"),
+ _("Let the value (brightness) of the region determine the size of the stroke"),
+ &radio_group);
+
+ create_size_radio_button (box3, SIZE_TYPE_RADIUS, _("Radius"),
+ _("The distance from the center of the image determines the size of the stroke"),
+ &radio_group);
+
+ create_size_radio_button (box3, SIZE_TYPE_RANDOM, _("Random"),
+ _("Selects a random size for each stroke"),
+ &radio_group);
+
+ create_size_radio_button (box3, SIZE_TYPE_RADIAL, _("Radial"),
+ _("Let the direction from the center determine the size of the stroke"),
+ &radio_group);
+
+ box3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (box2), box3,FALSE,FALSE, 0);
+ gtk_widget_show (box3);
+
+ create_size_radio_button (box3, SIZE_TYPE_FLOWING, _("Flowing"),
+ _("The strokes follow a \"flowing\" pattern"),
+ &radio_group);
+
+ create_size_radio_button (box3, SIZE_TYPE_HUE, _("Hue"),
+ _("The hue of the region determines the size of the stroke"),
+ &radio_group);
+
+ create_size_radio_button (box3, SIZE_TYPE_ADAPTIVE, _("Adaptive"),
+ _("The brush-size that matches the original image the closest is selected"),
+ &radio_group);
+
+
+ box4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (box3), box4, FALSE, FALSE, 0);
+ gtk_widget_show (box4);
+
+ create_size_radio_button (box4, SIZE_TYPE_MANUAL, _("Manual"),
+ _("Manually specify the stroke size"),
+ &radio_group
+ );
+
+ size_type_restore ();
+
+ tmpw = gtk_button_new_with_mnemonic (_("_Edit"));
+ gtk_box_pack_start (GTK_BOX (box4), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (create_sizemap_dialog_helper), NULL);
+ gimp_help_set_help_data (tmpw, _("Opens up the Size Map Editor"), NULL);
+
+ gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
+}
diff --git a/plug-ins/gimpressionist/size.h b/plug-ins/gimpressionist/size.h
new file mode 100644
index 0000000..b2891f0
--- /dev/null
+++ b/plug-ins/gimpressionist/size.h
@@ -0,0 +1,40 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SIZE_H
+#define __SIZE_H
+
+enum SIZE_TYPE_ENUM
+{
+ SIZE_TYPE_VALUE = 0,
+ SIZE_TYPE_RADIUS = 1,
+ SIZE_TYPE_RANDOM = 2,
+ SIZE_TYPE_RADIAL = 3,
+ SIZE_TYPE_FLOWING = 4,
+ SIZE_TYPE_HUE = 5,
+ SIZE_TYPE_ADAPTIVE = 6,
+ SIZE_TYPE_MANUAL = 7,
+};
+
+void size_restore (void);
+
+void create_sizepage (GtkNotebook *);
+
+int size_type_input (int in);
+void size_map_free_resources (void);
+
+#endif /* #ifndef __SIZE_H */
diff --git a/plug-ins/gimpressionist/sizemap.c b/plug-ins/gimpressionist/sizemap.c
new file mode 100644
index 0000000..591c299
--- /dev/null
+++ b/plug-ins/gimpressionist/sizemap.c
@@ -0,0 +1,553 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "gimpressionist.h"
+#include "ppmtool.h"
+#include "size.h"
+#include "infile.h"
+
+#include "preview.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define RESPONSE_APPLY 1
+
+#define MAPFILE "data.out"
+
+static GtkWidget *smwindow;
+static GtkWidget *smvectorprev;
+static GtkWidget *smpreviewprev;
+static GtkWidget *prev_button;
+static GtkWidget *next_button;
+static GtkWidget *add_button;
+static GtkWidget *kill_button;
+
+static GtkAdjustment *smvectprevbrightadjust = NULL;
+
+static GtkAdjustment *sizadjust = NULL;
+static GtkAdjustment *smstradjust = NULL;
+static GtkAdjustment *smstrexpadjust = NULL;
+static GtkWidget *size_voronoi = NULL;
+
+#define OMWIDTH 150
+#define OMHEIGHT 150
+
+static smvector_t smvector[MAXSIZEVECT];
+static int numsmvect = 0;
+
+static double
+getsiz_from_gui (double x, double y)
+{
+ return getsiz_proto (x,y, numsmvect, smvector,
+ gtk_adjustment_get_value (smstrexpadjust),
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (size_voronoi)));
+}
+
+static void
+updatesmpreviewprev (void)
+{
+ gint x, y;
+ static ppm_t nsbuffer;
+ guchar black[3] = {0, 0, 0};
+ guchar gray[3] = {120, 120, 120};
+
+ if (! PPM_IS_INITED (&nsbuffer))
+ {
+ ppm_new (&nsbuffer, OMWIDTH, OMHEIGHT);
+ }
+ fill (&nsbuffer, black);
+
+ for (y = 6; y < OMHEIGHT-4; y += 10)
+ {
+ for (x = 6; x < OMWIDTH-4; x += 10)
+ {
+ gdouble siz = 5 * getsiz_from_gui (x / (double)OMWIDTH,
+ y / (double)OMHEIGHT);
+ ppm_drawline (&nsbuffer, x-siz, y-siz, x+siz, y-siz, gray);
+ ppm_drawline (&nsbuffer, x+siz, y-siz, x+siz, y+siz, gray);
+ ppm_drawline (&nsbuffer, x+siz, y+siz, x-siz, y+siz, gray);
+ ppm_drawline (&nsbuffer, x-siz, y+siz, x-siz, y-siz, gray);
+ }
+ }
+
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (smpreviewprev),
+ 0, 0, OMWIDTH, OMHEIGHT,
+ GIMP_RGB_IMAGE,
+ nsbuffer.col,
+ OMWIDTH * 3);
+}
+
+static gint selectedsmvector = 0;
+static ppm_t update_vector_preview_backup = {0,0,NULL};
+static ppm_t update_vector_preview_sbuffer = {0,0,NULL};
+
+static void
+updatesmvectorprev (void)
+{
+ static int ok = 0;
+ gint i, x, y;
+ gdouble val;
+ static gdouble last_val = 0.0;
+ guchar gray[3] = {120, 120, 120};
+ guchar red[3] = {255, 0, 0};
+ guchar white[3] = {255, 255, 255};
+
+ if (smvectprevbrightadjust)
+ val = 1.0 - gtk_adjustment_get_value (smvectprevbrightadjust) / 100.0;
+ else
+ val = 0.5;
+
+ if (!ok || (val != last_val))
+ {
+#if 0
+ if (!PPM_IS_INITED (&infile))
+ updatepreview (NULL, (void *)2); /* Force grabarea () */
+ ppm_copy (&infile, &update_vector_preview_backup);
+#else
+ infile_copy_to_ppm (&update_vector_preview_backup);
+#endif
+ ppm_apply_brightness (&update_vector_preview_backup, val, 1,1,1);
+
+ if (update_vector_preview_backup.width != OMWIDTH ||
+ update_vector_preview_backup.height != OMHEIGHT)
+ resize_fast (&update_vector_preview_backup, OMWIDTH, OMHEIGHT);
+
+ ok = 1;
+ }
+ ppm_copy (&update_vector_preview_backup, &update_vector_preview_sbuffer);
+
+ for (i = 0; i < numsmvect; i++)
+ {
+ x = smvector[i].x * OMWIDTH;
+ y = smvector[i].y * OMHEIGHT;
+ if (i == selectedsmvector)
+ {
+ ppm_drawline (&update_vector_preview_sbuffer, x-5, y, x+5, y, red);
+ ppm_drawline (&update_vector_preview_sbuffer, x, y-5, x, y+5, red);
+ }
+ else
+ {
+ ppm_drawline (&update_vector_preview_sbuffer, x-5, y, x+5, y, gray);
+ ppm_drawline (&update_vector_preview_sbuffer, x, y-5, x, y+5, gray);
+ }
+ ppm_put_rgb (&update_vector_preview_sbuffer, x, y, white);
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (smvectorprev),
+ 0, 0, OMWIDTH, OMHEIGHT,
+ GIMP_RGB_IMAGE,
+ update_vector_preview_sbuffer.col,
+ OMWIDTH * 3);
+
+ gtk_widget_set_sensitive (prev_button, (numsmvect > 1));
+ gtk_widget_set_sensitive (next_button, (numsmvect > 1));
+ gtk_widget_set_sensitive (add_button, (numsmvect < MAXORIENTVECT));
+ gtk_widget_set_sensitive (kill_button, (numsmvect > 1));
+}
+
+void
+size_map_free_resources (void)
+{
+ ppm_kill (&update_vector_preview_backup);
+ ppm_kill (&update_vector_preview_sbuffer);
+}
+
+static gboolean smadjignore = FALSE;
+
+static void
+updatesmsliders (void)
+{
+ smadjignore = TRUE;
+ gtk_adjustment_set_value (sizadjust, smvector[selectedsmvector].siz);
+ gtk_adjustment_set_value (smstradjust, smvector[selectedsmvector].str);
+ smadjignore = FALSE;
+}
+
+static void
+smprevclick (GtkWidget *w, gpointer data)
+{
+ selectedsmvector--;
+ if (selectedsmvector < 0)
+ selectedsmvector = numsmvect-1;
+ updatesmsliders ();
+ updatesmvectorprev ();
+}
+
+static void
+smnextclick (GtkWidget *w, gpointer data)
+{
+ selectedsmvector++;
+
+ if (selectedsmvector == numsmvect)
+ selectedsmvector = 0;
+ updatesmsliders ();
+ updatesmvectorprev ();
+}
+
+static void
+smaddclick (GtkWidget *w, gpointer data)
+{
+ smvector[numsmvect].x = 0.5;
+ smvector[numsmvect].y = 0.5;
+ smvector[numsmvect].siz = 50.0;
+ smvector[numsmvect].str = 1.0;
+ selectedsmvector = numsmvect;
+ numsmvect++;
+ updatesmsliders ();
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+}
+
+static void
+smdeleteclick (GtkWidget *w, gpointer data)
+{
+ int i;
+
+ for (i = selectedsmvector; i < numsmvect-1; i++)
+ {
+ smvector[i] = smvector[i+1];
+ }
+ numsmvect--;
+ if (selectedsmvector >= numsmvect)
+ selectedsmvector = 0;
+ updatesmsliders ();
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+}
+
+static void
+smmapclick (GtkWidget *w, GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ smvector[selectedsmvector].x = event->x / (double)OMWIDTH;
+ smvector[selectedsmvector].y = event->y / (double)OMHEIGHT;
+ }
+ else if (event->button == 2)
+ {
+ if (numsmvect + 1 == MAXSIZEVECT)
+ return;
+ smvector[numsmvect].x = event->x / (double)OMWIDTH;
+ smvector[numsmvect].y = event->y / (double)OMHEIGHT;
+ smvector[numsmvect].siz = 0.0;
+ smvector[numsmvect].str = 1.0;
+ selectedsmvector = numsmvect;
+ numsmvect++;
+ updatesmsliders ();
+ }
+#if 0
+ else if (event->button == 3)
+ {
+ double d;
+ d = atan2 (OMWIDTH * smvector[selectedsmvector].x - event->x,
+ OMHEIGHT * smvector[selectedsmvector].y - event->y);
+ smvector[selectedsmvector].dir = radtodeg (d);
+ updatesmsliders ();
+ */
+ }
+#endif
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+}
+
+static void
+angsmadjmove (GtkWidget *w, gpointer data)
+{
+ if (!smadjignore)
+ {
+ smvector[selectedsmvector].siz = gtk_adjustment_get_value (sizadjust);
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+ }
+}
+
+static void
+strsmadjmove (GtkWidget *w, gpointer data)
+{
+ if (!smadjignore)
+ {
+ smvector[selectedsmvector].str = gtk_adjustment_get_value (smstradjust);
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+ }
+}
+
+static void
+smstrexpsmadjmove (GtkWidget *w, gpointer data)
+{
+ if (!smadjignore)
+ {
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+ }
+}
+
+static void
+smresponse (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_APPLY:
+ case GTK_RESPONSE_OK:
+ {
+ gint i;
+
+ for (i = 0; i < numsmvect; i++)
+ pcvals.size_vectors[i] = smvector[i];
+
+ pcvals.num_size_vectors = numsmvect;
+ pcvals.size_strength_exponent = gtk_adjustment_get_value (smstrexpadjust);
+ pcvals.size_voronoi = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (size_voronoi));
+ }
+ break;
+ }
+
+ if (response_id != RESPONSE_APPLY)
+ gtk_widget_hide (widget);
+}
+
+static void
+initsmvectors (void)
+{
+ if (pcvals.num_size_vectors)
+ {
+ gint i;
+
+ numsmvect = pcvals.num_size_vectors;
+ for (i = 0; i < numsmvect; i++)
+ {
+ smvector[i] = pcvals.size_vectors[i];
+ }
+ }
+ else
+ {
+ /* Shouldn't happen */
+ numsmvect = 1;
+ smvector[0].x = 0.5;
+ smvector[0].y = 0.5;
+ smvector[0].siz = 0.0;
+ smvector[0].str = 1.0;
+ }
+ if (selectedsmvector >= numsmvect)
+ selectedsmvector = numsmvect-1;
+}
+
+#if 0
+static void
+update_sizemap_dialog (void)
+{
+ if (smwindow)
+ {
+ initsmvectors ();
+
+ gtk_adjustment_set_value (smstrexpadjust, pcvals.size_strength_exponent);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (size_voronoi),
+ pcvals.size_voronoi);
+
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+ }
+}
+#endif
+
+void
+create_sizemap_dialog (GtkWidget *parent)
+{
+ GtkWidget *tmpw, *tmpw2;
+ GtkWidget *table1;
+ GtkWidget *table2;
+ GtkWidget *hbox;
+
+ initsmvectors ();
+
+ if (smwindow)
+ {
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+ gtk_window_present (GTK_WINDOW (smwindow));
+ return;
+ }
+
+ smwindow = gimp_dialog_new (_("Size Map Editor"), PLUG_IN_ROLE,
+ gtk_widget_get_toplevel (parent), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Apply"), RESPONSE_APPLY,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (smwindow),
+ GTK_RESPONSE_OK,
+ RESPONSE_APPLY,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (smwindow, "response",
+ G_CALLBACK (smresponse),
+ NULL);
+ g_signal_connect (smwindow, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &smwindow);
+
+ table1 = gtk_table_new (2, 5, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (smwindow))),
+ table1, TRUE, TRUE, 0);
+ gtk_widget_show (table1);
+
+ tmpw2 = tmpw = gtk_frame_new (_("Smvectors"));
+ gtk_container_set_border_width (GTK_CONTAINER (tmpw), 2);
+ gtk_table_attach (GTK_TABLE (table1), tmpw, 0,1,0,1,GTK_EXPAND,GTK_EXPAND,0,0);
+ gtk_widget_show (tmpw);
+
+ tmpw = hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,0);
+ gtk_container_add (GTK_CONTAINER (tmpw2), tmpw);
+ gtk_widget_show (tmpw);
+
+ tmpw = gtk_event_box_new ();
+ gimp_help_set_help_data (tmpw, _("The smvector-field. Left-click to move selected smvector, Right-click to point it towards mouse, Middle-click to add a new smvector."), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, FALSE, 0);
+ tmpw2 = tmpw;
+
+ tmpw = smvectorprev = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tmpw, OMWIDTH, OMHEIGHT);
+ gtk_container_add (GTK_CONTAINER (tmpw2), tmpw);
+ gtk_widget_show (tmpw);
+ gtk_widget_add_events (tmpw2, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect (tmpw2, "button-press-event",
+ G_CALLBACK (smmapclick), NULL);
+ gtk_widget_show (tmpw2);
+
+ smvectprevbrightadjust = (GtkAdjustment *)
+ gtk_adjustment_new (50.0, 0.0, 100.0, 1.0, 1.0, 1.0);
+ tmpw = gtk_scale_new (GTK_ORIENTATION_VERTICAL, smvectprevbrightadjust);
+ gtk_scale_set_draw_value (GTK_SCALE (tmpw), FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw,FALSE,FALSE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (smvectprevbrightadjust, "value-changed",
+ G_CALLBACK (updatesmvectorprev), NULL);
+ gimp_help_set_help_data (tmpw, _("Adjust the preview's brightness"), NULL);
+
+ tmpw2 = tmpw = gtk_frame_new (_("Preview"));
+ gtk_container_set_border_width (GTK_CONTAINER (tmpw), 2);
+ gtk_table_attach (GTK_TABLE (table1), tmpw, 1,2,0,1,GTK_EXPAND,GTK_EXPAND,0,0);
+ gtk_widget_show (tmpw);
+
+ tmpw = smpreviewprev = gimp_preview_area_new ();
+ gtk_widget_set_size_request (tmpw, OMWIDTH, OMHEIGHT);
+ gtk_container_add (GTK_CONTAINER (tmpw2), tmpw);
+ gtk_widget_show (tmpw);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
+ gtk_table_attach_defaults (GTK_TABLE (table1), hbox, 0, 1, 1, 2);
+ gtk_widget_show (hbox);
+
+ prev_button = tmpw = gtk_button_new_with_mnemonic ("_<<");
+ gtk_box_pack_start (GTK_BOX (hbox), tmpw, FALSE, TRUE, 0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (smprevclick), NULL);
+ gimp_help_set_help_data (tmpw, _("Select previous smvector"), NULL);
+
+ next_button = tmpw = gtk_button_new_with_mnemonic ("_>>");
+ gtk_box_pack_start (GTK_BOX (hbox),tmpw,FALSE,TRUE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (smnextclick), NULL);
+ gimp_help_set_help_data (tmpw, _("Select next smvector"), NULL);
+
+ add_button = tmpw = gtk_button_new_with_mnemonic ( _("A_dd"));
+ gtk_box_pack_start (GTK_BOX (hbox),tmpw,FALSE,TRUE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (smaddclick), NULL);
+ gimp_help_set_help_data (tmpw, _("Add new smvector"), NULL);
+
+ kill_button = tmpw = gtk_button_new_with_mnemonic (_("_Kill"));
+ gtk_box_pack_start (GTK_BOX (hbox),tmpw,FALSE,TRUE,0);
+ gtk_widget_show (tmpw);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (smdeleteclick), NULL);
+ gimp_help_set_help_data (tmpw, _("Delete selected smvector"), NULL);
+
+ table2 = gtk_table_new (3, 4, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table2), 4);
+ gtk_table_attach_defaults (GTK_TABLE (table1), table2, 0, 2, 2, 3);
+ gtk_widget_show (table2);
+
+ sizadjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 0,
+ _("_Size:"),
+ 150, 6, 50.0,
+ 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ _("Change the angle of the selected smvector"),
+ NULL);
+ g_signal_connect (sizadjust, "value-changed",
+ G_CALLBACK (angsmadjmove), NULL);
+
+ smstradjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 1,
+ _("S_trength:"),
+ 150, 6, 1.0,
+ 0.1, 5.0, 0.1, 0.5, 1,
+ TRUE, 0, 0,
+ _("Change the strength of the selected smvector"),
+ NULL);
+ g_signal_connect (smstradjust, "value-changed",
+ G_CALLBACK (strsmadjmove), NULL);
+
+ smstrexpadjust = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 2,
+ _("St_rength exp.:"),
+ 150, 6, 1.0,
+ 0.1, 10.9, 0.1, 0.5, 1,
+ TRUE, 0, 0,
+ _("Change the exponent of the strength"),
+ NULL);
+ g_signal_connect (smstrexpadjust, "value-changed",
+ G_CALLBACK (smstrexpsmadjmove), NULL);
+
+ size_voronoi = tmpw = gtk_check_button_new_with_mnemonic ( _("_Voronoi"));
+ gtk_table_attach_defaults (GTK_TABLE (table2), tmpw, 3, 4, 0, 1);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw), FALSE);
+ gtk_widget_show (tmpw);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tmpw), pcvals.size_voronoi);
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (smstrexpsmadjmove), NULL);
+ gimp_help_set_help_data (tmpw, _("Voronoi-mode makes only the smvector closest to the given point have any influence"), NULL);
+
+ gtk_widget_show (smwindow);
+
+ updatesmvectorprev ();
+ updatesmpreviewprev ();
+}
diff --git a/plug-ins/gimpressionist/utils.c b/plug-ins/gimpressionist/utils.c
new file mode 100644
index 0000000..16244ae
--- /dev/null
+++ b/plug-ins/gimpressionist/utils.c
@@ -0,0 +1,411 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * utils.c - various utility routines that don't fit anywhere else. Usually
+ * these routines don't affect the state of the program.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+
+#include "gimpressionist.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Mathematical Utilities */
+
+double
+dist (double x, double y, double end_x, double end_y)
+{
+ double dx = end_x - x;
+ double dy = end_y - y;
+ return sqrt (dx * dx + dy * dy);
+}
+
+double
+getsiz_proto (double x, double y, int n, smvector_t *vec,
+ double smstrexp, int voronoi)
+{
+ int i;
+ double sum, ssum, dst;
+ int first = 0, last;
+
+ if ((x < 0.0) || (x > 1.0))
+ g_warning ("HUH? x = %f\n",x);
+
+#if 0
+ if (from == 0)
+ {
+ n = numsmvect;
+ vec = smvector;
+ smstrexp = gtk_adjustment_get_value (GTK_ADJUSTMENT (smstrexpadjust));
+ voronoi = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (size_voronoi));
+ }
+ else
+ {
+ n = pcvals.num_size_vectors;
+ vec = pcvals.size_vectors;
+ smstrexp = pcvals.size_strength_exponent;
+ voronoi = pcvals.size_voronoi;
+ }
+#endif
+
+ if (voronoi)
+ {
+ gdouble bestdist = -1.0;
+ for (i = 0; i < n; i++)
+ {
+ dst = dist (x, y, vec[i].x, vec[i].y);
+ if ((bestdist < 0.0) || (dst < bestdist))
+ {
+ bestdist = dst;
+ first = i;
+ }
+ }
+ last = first+1;
+ }
+ else
+ {
+ first = 0;
+ last = n;
+ }
+
+ sum = ssum = 0.0;
+ for (i = first; i < last; i++)
+ {
+ gdouble s = vec[i].str;
+
+ dst = dist (x,y,vec[i].x,vec[i].y);
+ dst = pow (dst, smstrexp);
+ if (dst < 0.0001)
+ dst = 0.0001;
+ s = s / dst;
+
+ sum += vec[i].siz * s;
+ ssum += 1.0/dst;
+ }
+ sum = sum / ssum / 100.0;
+ return CLAMP (sum, 0.0, 1.0);
+}
+
+
+/* String and Path Manipulation Routines */
+
+
+static GList *parsepath_cached_path = NULL;
+
+/* This function is memoized. Once it finds the value it permanently
+ * caches it
+ * */
+GList *
+parsepath (void)
+{
+ gchar *rc_path, *path;
+
+ if (parsepath_cached_path)
+ return parsepath_cached_path;
+
+ path = gimp_gimprc_query ("gimpressionist-path");
+ if (path)
+ {
+ rc_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+ g_free (path);
+ }
+ else
+ {
+ gchar *gimprc = gimp_personal_rc_file ("gimprc");
+ gchar *full_path = gimp_config_build_data_path ("gimpressionist");
+ gchar *esc_path = g_strescape (full_path, NULL);
+
+ g_message (_("No %s in gimprc:\n"
+ "You need to add an entry like\n"
+ "(%s \"%s\")\n"
+ "to your %s file."),
+ "gflare-path", "gflare-path",
+ esc_path, gimp_filename_to_utf8 (gimprc));
+
+ g_free (gimprc);
+ g_free (esc_path);
+
+ rc_path = gimp_config_path_expand (full_path, TRUE, NULL);
+ g_free (full_path);
+ }
+
+ parsepath_cached_path = gimp_path_parse (rc_path, 256, FALSE, NULL);
+
+ g_free (rc_path);
+
+ return parsepath_cached_path;
+}
+
+void
+free_parsepath_cache (void)
+{
+ if (parsepath_cached_path != NULL)
+ return;
+
+ g_list_free_full (parsepath_cached_path, (GDestroyNotify) g_free);
+ parsepath_cached_path = NULL;
+}
+
+gchar *
+findfile (const gchar *fn)
+{
+ GList *rcpath;
+ GList *thispath;
+ gchar *filename;
+
+ g_return_val_if_fail (fn != NULL, NULL);
+
+ rcpath = parsepath ();
+
+ thispath = rcpath;
+
+ while (thispath)
+ {
+ filename = g_build_filename (thispath->data, fn, NULL);
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ return filename;
+ g_free (filename);
+ thispath = thispath->next;
+ }
+ return NULL;
+}
+
+/* GUI Routines */
+
+void
+reselect (GtkWidget *view,
+ gchar *fname)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ char *tmpfile;
+
+ tmpfile = strrchr (fname, '/');
+ if (tmpfile)
+ fname = ++tmpfile;
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ gboolean quit = FALSE;
+ do
+ {
+ gchar *name;
+
+ gtk_tree_model_get (model, &iter, 0, &name, -1);
+ if (!strcmp(name, fname))
+ {
+ GtkTreePath *tree_path;
+ gtk_tree_selection_select_iter (selection, &iter);
+ tree_path = gtk_tree_model_get_path (model,
+ &iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view),
+ tree_path,
+ NULL,
+ TRUE,
+ 0.5,
+ 0.5);
+ gtk_tree_path_free (tree_path);
+ quit = TRUE;
+ }
+ g_free (name);
+
+ } while ((!quit) && gtk_tree_model_iter_next (model, &iter));
+ }
+}
+
+static void
+readdirintolist_real (const char *subdir,
+ GtkWidget *view,
+ char *selected,
+ gboolean with_filename_column,
+ gchar *(*get_object_name_cb) (const gchar *dir,
+ gchar *filename,
+ void *context),
+ void *context)
+{
+ gchar *fpath;
+ const gchar *de;
+ GDir *dir;
+ GList *flist = NULL;
+ GtkTreeIter iter;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
+
+ if (selected)
+ {
+ if (!selected[0])
+ selected = NULL;
+ else
+ {
+ char *nsel;
+
+ nsel = strrchr (selected, '/');
+ if (nsel) selected = ++nsel;
+ }
+ }
+
+ dir = g_dir_open (subdir, 0, NULL);
+
+ if (!dir)
+ return;
+
+ for (;;)
+ {
+ gboolean file_exists;
+
+ de = g_dir_read_name (dir);
+ if (!de)
+ break;
+
+ fpath = g_build_filename (subdir, de, NULL);
+ file_exists = g_file_test (fpath, G_FILE_TEST_IS_REGULAR);
+ g_free (fpath);
+
+ if (!file_exists)
+ continue;
+
+ flist = g_list_insert_sorted (flist, g_strdup (de),
+ (GCompareFunc)g_ascii_strcasecmp);
+ }
+ g_dir_close (dir);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+
+ while (flist)
+ {
+ gtk_list_store_append (store, &iter);
+ /* Set the filename */
+ gtk_list_store_set (store, &iter, PRESETS_LIST_COLUMN_FILENAME,
+ flist->data, -1);
+ /* Set the object name */
+ if (with_filename_column)
+ {
+ gchar * object_name;
+ object_name = get_object_name_cb (subdir, flist->data, context);
+ if (object_name)
+ {
+ gtk_list_store_set (store, &iter,
+ PRESETS_LIST_COLUMN_OBJECT_NAME,
+ object_name, -1);
+ g_free (object_name);
+ }
+ else
+ {
+ /* Default to the filename */
+ gtk_list_store_set (store, &iter, 1, flist->data, -1);
+ }
+ }
+
+ if (selected)
+ {
+ if (!strcmp (flist->data, selected))
+ {
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+ }
+ g_free (flist->data);
+ flist = g_list_remove (flist, flist->data);
+ }
+
+ if (!selected)
+ {
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+}
+
+void
+readdirintolist_extended (const char *subdir,
+ GtkWidget *view,
+ char *selected,
+ gboolean with_filename_column,
+ gchar *(*get_object_name_cb) (const gchar *dir,
+ gchar *filename,
+ void *context),
+ void *context)
+{
+ char *tmpdir;
+ GList *thispath = parsepath ();
+
+ while (thispath)
+ {
+ tmpdir = g_build_filename ((gchar *) thispath->data, subdir, NULL);
+ readdirintolist_real (tmpdir, view, selected, with_filename_column,
+ get_object_name_cb, context);
+ g_free (tmpdir);
+ thispath = thispath->next;
+ }
+}
+
+void
+readdirintolist (const char *subdir,
+ GtkWidget *view,
+ char *selected)
+{
+ readdirintolist_extended (subdir, view, selected, FALSE, NULL, NULL);
+}
+
+/*
+ * Creates a radio button.
+ * box - the containing box.
+ * orient_type - The orientation ID
+ * label, help_string - self-describing
+ * radio_group -
+ * A pointer to a radio group. The function assigns its value
+ * as the radio group of the radio button. Afterwards, it assigns it
+ * a new value of the new radio group of the button.
+ * This is useful to group buttons. Just reset the variable to NULL,
+ * to create a new group.
+ * */
+GtkWidget *
+create_radio_button (GtkWidget *box,
+ int orient_type,
+ void (*callback) (GtkWidget *wg, void *d),
+ const gchar *label,
+ const gchar *help_string,
+ GSList **radio_group,
+ GtkWidget **buttons_array)
+{
+ GtkWidget *tmpw;
+
+ buttons_array[orient_type] = tmpw =
+ gtk_radio_button_new_with_label ((*radio_group), label);
+ gtk_box_pack_start (GTK_BOX (box), tmpw, FALSE, FALSE, 0);
+ gtk_widget_show (tmpw);
+
+ g_signal_connect (tmpw, "clicked",
+ G_CALLBACK (callback), GINT_TO_POINTER (orient_type));
+ gimp_help_set_help_data (tmpw, help_string, NULL);
+
+ *radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (tmpw));
+
+ return tmpw;
+}
+
diff --git a/plug-ins/gradient-flare/Makefile.am b/plug-ins/gradient-flare/Makefile.am
new file mode 100644
index 0000000..f531be4
--- /dev/null
+++ b/plug-ins/gradient-flare/Makefile.am
@@ -0,0 +1,51 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+gradient_flare_RC = gradient-flare.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+SUBDIRS = flares
+
+libexecdir = $(gimpplugindir)/plug-ins/gradient-flare
+
+libexec_PROGRAMS = gradient-flare
+
+gradient_flare_SOURCES = gradient-flare.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gradient_flare_RC)
diff --git a/plug-ins/gradient-flare/Makefile.in b/plug-ins/gradient-flare/Makefile.in
new file mode 100644
index 0000000..6d6b258
--- /dev/null
+++ b/plug-ins/gradient-flare/Makefile.in
@@ -0,0 +1,1117 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = gradient-flare$(EXEEXT)
+subdir = plug-ins/gradient-flare
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_gradient_flare_OBJECTS = gradient-flare.$(OBJEXT)
+gradient_flare_OBJECTS = $(am_gradient_flare_OBJECTS)
+gradient_flare_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+gradient_flare_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(gradient_flare_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gradient-flare.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(gradient_flare_SOURCES)
+DIST_SOURCES = $(gradient_flare_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/gradient-flare
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@gradient_flare_RC = gradient-flare.rc.o
+AM_LDFLAGS = $(mwindows)
+SUBDIRS = flares
+gradient_flare_SOURCES = gradient-flare.c
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(gradient_flare_RC)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gradient-flare/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gradient-flare/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+gradient-flare$(EXEEXT): $(gradient_flare_OBJECTS) $(gradient_flare_DEPENDENCIES) $(EXTRA_gradient_flare_DEPENDENCIES)
+ @rm -f gradient-flare$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gradient_flare_OBJECTS) $(gradient_flare_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gradient-flare.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/gradient-flare.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/gradient-flare.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gradient-flare/README b/plug-ins/gradient-flare/README
new file mode 100644
index 0000000..92c1095
--- /dev/null
+++ b/plug-ins/gradient-flare/README
@@ -0,0 +1,99 @@
+==========================================================================
+
+ GFlare plug-in ver 0.25
+
+ Eiichi Takamori <taka@ma1.seikyou.ne.jp>
+
+==========================================================================
+
+ GFlare is a plug-in for GIMP. The name "GFlare" is short for
+"Gradient Flare". It renders lense flare effect using custom
+gradients. The basic idea is suggested by Marcelo Malheiros,
+originally based on a 3DStudio MAX plug-in called LenZFX:
+ http://www.digimation.com/techsupp/lzfxfeat.htm
+
+INSTALL:
+
+1) Edit Makefile, if needed.
+
+2) Type "make install".
+
+3) Type "make install-data" if needed. (It copies sample gradients and
+ gflares under ~/.gimp)
+
+3) Add new entry to ~/.gimp/gimprc as
+ (gflare-path "${gimp_dir}/gflares:${gimp_data_dir}/gflares")
+
+3) Run GIMP. The menu path is <Image>/Filters/Effects/GFlare.
+
+
+HOW TO USE IT:
+
+ The document is not yet done. Try and see.
+
+ Main Dialog:
+ - Preview
+ click on it changes the position of GFlare
+ - Settings page
+ - Center, Radius (pixel), Rotation (degree), Hue Rotation (degree)
+ - Vector Angle (degree) and Vector Length (percentage to Radius)
+ - adaptive supersampling ... same as "Blend tool"
+ - Selector page
+ - ListBox
+ - New, Edit, Copy, Delete buttons. Edit button invokes GFlare Editor.
+
+ GFlare Editor:
+ - General page:
+ - Glow page:
+ - Rays page:
+ - Second Flares page:
+
+ WARNING: This plug-in is in development stage, and the code is very
+alpha. The GFlare datafile format may be changed in future version.
+
+ Suggestions and ideas for user interface, flare algorithm, etc. are
+very welcome.
+
+
+KNOWN BUGS:
+
+ There are still lots of bugs, of course. ;-)
+ They seem related to GTK, and I don't know exactly how I can fix them.
+
+ * It warns at startup as:
+ ** WARNING **: file gdkwindow.c: line 422 (gdk_window_move_resize): "window != NULL"
+ If you are annoyed, accompanying "gtkviewport.c.patch" will shut up
+ the warning.
+ * Edit button remains prelighted when GFlare Editor dialog is done.
+ * Dialogs are not shown quickly at startup.
+ * Sometimes note tab labels are disappeared.
+ * Sometimes it crashes, but I don't know yet why it happens.
+
+TODO:
+ * Random hue scattering for Second Flares.
+ * Improve internal gradients.
+ * Currently number of Second Flares is fixed(30).
+ * Change opacity for one second flare dynamically, in inverse
+ proportion to the size of it.
+ * Reduce dialog size
+ * Add tweak ability "How to combine Glow, Rays, Second Flares in
+ what order"
+
+
+CREDITS:
+
+ Marcelo Marheiros and Federico Mena Quintero suggested a lot of
+ideas. Very Thanks!!
+
+ Marcelo made gradients whose names end with _1, _2, etc. The
+gradients whose names end with _101, _102 etc. are mine.
+
+ Quartic did awful Gradient Editor, and gradients PDB procedures.
+
+ A fair proportion of this code was taken from:
+
+ GIMP - The GNU Image Manipulation Program
+ Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+ Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
+ federico@nuclecu.unam.mx
diff --git a/plug-ins/gradient-flare/flares/Bright_Star b/plug-ins/gradient-flare/flares/Bright_Star
new file mode 100644
index 0000000..e14ba49
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Bright_Star
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+100.000000 NORMAL
+75.000000 SCREEN
+Flare\040Glow\040Radial\0401
+%white
+%white
+75.000000 0.000000 0.000000
+Flare\040Rays\040Radial\0401
+%white
+Flare\040Rays\040Size\0401
+100.000000 -75.000000 0.000000
+16 20.000000
+%blue_grad
+%random
+%random
+16.000000 0.000000 13.600000
+POLYGON 6 1
diff --git a/plug-ins/gradient-flare/flares/Classic b/plug-ins/gradient-flare/flares/Classic
new file mode 100644
index 0000000..81a7562
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Classic
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+40.000000 SCREEN
+20.000000 SCREEN
+Flare\040Glow\040Radial\0403
+%white
+%white
+85.000000 0.000000 0.000000
+%white_grad
+%white
+%random
+125.000000 0.000000 0.000000
+85 60.000000
+Flare\040Glow\040Radial\0404
+%random
+%random
+20.000000 0.000000 0.000000
+CIRCLE 6 1
diff --git a/plug-ins/gradient-flare/flares/Default b/plug-ins/gradient-flare/flares/Default
new file mode 100644
index 0000000..95e257f
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Default
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+95.300003 NORMAL
+23.000000 ADDITION
+20.900000 SCREEN
+Radial\040Glow\0401
+%white
+%white
+100.000000 0.000000 -145.899994
+%white_grad
+%white_grad
+%random
+70.400002 0.000000 0.000000
+21 87.300003
+Flare\040Radial\040103
+Flare\040Sizefac\040101
+%random
+40.000000 0 0
+CIRCLE 6 1
diff --git a/plug-ins/gradient-flare/flares/Distant_Sun b/plug-ins/gradient-flare/flares/Distant_Sun
new file mode 100644
index 0000000..9d71357
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Distant_Sun
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+90.000000 ADDITION
+50.000000 SCREEN
+Flare\040Radial\040102
+%white
+%white
+136.000000 0.000000 55.000000
+%yellow_grad
+Flare\040Glow\040Angular\0401
+%random
+110.000000 140.000000 -12.000000
+63 90.000000
+Flare\040Glow\040Radial\0404
+Flare\040Rays\040Size\0401
+%yellow_grad
+30.000000 30.000000 40.000000
+POLYGON 6 1
diff --git a/plug-ins/gradient-flare/flares/GFlare_101 b/plug-ins/gradient-flare/flares/GFlare_101
new file mode 100644
index 0000000..ce064c6
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/GFlare_101
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+50.000000 NORMAL
+35.500000 NORMAL
+Flare\040Radial\040101
+%white
+%white
+100.000000 0.000000 0.000000
+%white_grad
+%random
+%random
+100.000000 0.000000 0.000000
+40 50.000000
+Flare\040Glow\040Radial\0401
+%random
+%random
+40.000000 0 0
+CIRCLE 6 1
diff --git a/plug-ins/gradient-flare/flares/GFlare_102 b/plug-ins/gradient-flare/flares/GFlare_102
new file mode 100644
index 0000000..1540c2d
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/GFlare_102
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+62.799999 NORMAL
+37.900002 SCREEN
+Flare\040Radial\040102
+%white
+%white
+100.000000 0.000000 -85.300003
+%red_grad
+%random
+%random
+100.000000 0.000000 -155.899994
+40 20.000000
+Flare\040Radial\040102
+%random
+Skyline
+40.000000 0 0
+CIRCLE 6 3
diff --git a/plug-ins/gradient-flare/flares/Hidden_Planet b/plug-ins/gradient-flare/flares/Hidden_Planet
new file mode 100644
index 0000000..ee5ec6a
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Hidden_Planet
@@ -0,0 +1,18 @@
+GIMP GFlare 0.25
+100.000000 NORMAL
+100.000000 NORMAL
+100.000000 NORMAL
+Flare\040Glow\040Radial\0402
+Flare\040Glow\040Angular\0401
+%white
+100.000000 0.000000 0.000000
+Flare\040Rays\040Radial\0402
+%white
+%white
+50.000000 90.000000 0.000000
+2 85.000000
+%white
+%white
+%white
+0.000000 0.000000 0.000000
+CIRCLE 6 1
diff --git a/plug-ins/gradient-flare/flares/Makefile.am b/plug-ins/gradient-flare/flares/Makefile.am
new file mode 100644
index 0000000..930cf1f
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Makefile.am
@@ -0,0 +1,14 @@
+## Process this file with automake to produce Makefile.in
+
+flaredir = $(gimpdatadir)/gflare
+
+flare_DATA = \
+ Bright_Star \
+ Classic \
+ Distant_Sun \
+ Default \
+ GFlare_101 \
+ GFlare_102 \
+ Hidden_Planet
+
+EXTRA_DIST = $(flare_DATA)
diff --git a/plug-ins/gradient-flare/flares/Makefile.in b/plug-ins/gradient-flare/flares/Makefile.in
new file mode 100644
index 0000000..6689121
--- /dev/null
+++ b/plug-ins/gradient-flare/flares/Makefile.in
@@ -0,0 +1,811 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/gradient-flare/flares
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(flaredir)"
+DATA = $(flare_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+flaredir = $(gimpdatadir)/gflare
+flare_DATA = \
+ Bright_Star \
+ Classic \
+ Distant_Sun \
+ Default \
+ GFlare_101 \
+ GFlare_102 \
+ Hidden_Planet
+
+EXTRA_DIST = $(flare_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/gradient-flare/flares/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/gradient-flare/flares/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-flareDATA: $(flare_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(flare_DATA)'; test -n "$(flaredir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(flaredir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(flaredir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(flaredir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(flaredir)" || exit $$?; \
+ done
+
+uninstall-flareDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(flare_DATA)'; test -n "$(flaredir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(flaredir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(flaredir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-flareDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-flareDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-flareDATA \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags-am uninstall uninstall-am uninstall-flareDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/gradient-flare/gradient-flare.c b/plug-ins/gradient-flare/gradient-flare.c
new file mode 100644
index 0000000..731d76e
--- /dev/null
+++ b/plug-ins/gradient-flare/gradient-flare.c
@@ -0,0 +1,5123 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GFlare plug-in -- lense flare effect by using custom gradients
+ * Copyright (C) 1997 Eiichi Takamori <taka@ma1.sekyou.ne.jp>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * A fair proportion of this code was taken from GIMP & Script-fu
+ * copyrighted by Spencer Kimball and Peter Mattis, and from Gradient
+ * Editor copyrighted by Federico Mena Quintero. (See copyright notice
+ * below) Thanks for senior GIMP hackers!!
+ *
+ * GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Gradient editor module copyight (C) 1996-1997 Federico Mena Quintero
+ * federico@nuclecu.unam.mx
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <glib-object.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+#define DEBUG_PRINT(X) g_print X
+#else
+#define DEBUG_PRINT(X)
+#endif
+
+#define LUMINOSITY(PIX) (GIMP_RGB_LUMINANCE (PIX[0], PIX[1], PIX[2]) + 0.5)
+
+#define RESPONSE_RESCAN 1
+
+#define PLUG_IN_PROC "plug-in-gflare"
+#define PLUG_IN_BINARY "gradient-flare"
+#define PLUG_IN_ROLE "gimp-gradient-flare"
+
+#define GRADIENT_NAME_MAX 256
+#define GRADIENT_RESOLUTION 360
+
+#define GFLARE_NAME_MAX 256
+#define GFLARE_FILE_HEADER "GIMP GFlare 0.25\n"
+#define SFLARE_NUM 30
+
+#define DLG_PREVIEW_WIDTH 256
+#define DLG_PREVIEW_HEIGHT 256
+#define DLG_PREVIEW_MASK GDK_EXPOSURE_MASK | \
+ GDK_BUTTON_PRESS_MASK
+#define DLG_LISTBOX_WIDTH 80
+#define DLG_LISTBOX_HEIGHT 40
+
+#define ED_PREVIEW_WIDTH 256
+#define ED_PREVIEW_HEIGHT 256
+
+#define GM_PREVIEW_WIDTH 80
+#define GM_PREVIEW_HEIGHT 16
+
+#define SCALE_WIDTH 80
+
+#ifndef OPAQUE
+#define OPAQUE 255
+#endif
+#define GRAY50 128
+
+#define GRADIENT_CACHE_SIZE 32
+
+#define CALC_GLOW 0x01
+#define CALC_RAYS 0x02
+#define CALC_SFLARE 0x04
+
+typedef struct _Preview Preview;
+
+typedef gchar GradientName[GRADIENT_NAME_MAX];
+
+typedef enum
+{
+ GF_NORMAL = 0,
+ GF_ADDITION,
+ GF_OVERLAY,
+ GF_SCREEN,
+ GF_NUM_MODES
+} GFlareMode;
+
+typedef enum
+{
+ GF_CIRCLE = 0,
+ GF_POLYGON,
+ GF_NUM_SHAPES
+} GFlareShape;
+
+typedef struct
+{
+ gchar *name;
+ gchar *filename;
+ gdouble glow_opacity;
+ GFlareMode glow_mode;
+ gdouble rays_opacity;
+ GFlareMode rays_mode;
+ gdouble sflare_opacity;
+ GFlareMode sflare_mode;
+ GradientName glow_radial;
+ GradientName glow_angular;
+ GradientName glow_angular_size;
+ gdouble glow_size;
+ gdouble glow_rotation;
+ gdouble glow_hue;
+ GradientName rays_radial;
+ GradientName rays_angular;
+ GradientName rays_angular_size;
+ gdouble rays_size;
+ gdouble rays_rotation;
+ gdouble rays_hue;
+ gint rays_nspikes;
+ gdouble rays_thickness;
+ GradientName sflare_radial;
+ GradientName sflare_sizefac;
+ GradientName sflare_probability;
+ gdouble sflare_size;
+ gdouble sflare_rotation;
+ gdouble sflare_hue;
+ GFlareShape sflare_shape;
+ gint sflare_nverts;
+ guint32 sflare_seed;
+ gboolean random_seed;
+} GFlare;
+
+typedef struct
+{
+ FILE *fp;
+ gint error;
+} GFlareFile;
+
+
+typedef enum
+{
+ PAGE_SETTINGS,
+ PAGE_SELECTOR,
+ PAGE_GENERAL,
+ PAGE_GLOW,
+ PAGE_RAYS,
+ PAGE_SFLARE
+} PageNum;
+
+
+typedef struct
+{
+ gint init;
+ GFlare *gflare;
+ GtkWidget *shell;
+ Preview *preview;
+ struct
+ {
+ gdouble x0, y0, x1, y1;
+ } pwin;
+ gboolean update_preview;
+ GtkWidget *notebook;
+ GtkWidget *sizeentry;
+ GtkWidget *asupsample_frame;
+ GtkListStore *selector_list;
+ GtkTreeSelection *selection;
+ gint init_params_done;
+} GFlareDialog;
+
+typedef void (* GFlareEditorCallback) (gint updated, gpointer data);
+
+typedef struct
+{
+ gint init;
+ gint run;
+ GFlareEditorCallback callback;
+ gpointer calldata;
+ GFlare *target_gflare;
+ GFlare *gflare;
+ GtkWidget *shell;
+ Preview *preview;
+ GtkWidget *notebook;
+ PageNum cur_page;
+ GtkWidget *polygon_entry;
+ GtkWidget *polygon_toggle;
+ gint init_params_done;
+} GFlareEditor;
+
+typedef struct
+{
+ gdouble x0;
+ gdouble y0;
+ gdouble x1;
+ gdouble y1;
+} CalcBounds;
+
+typedef struct
+{
+ gint init;
+ gint type;
+ GFlare *gflare;
+ gdouble xcenter;
+ gdouble ycenter;
+ gdouble radius;
+ gdouble rotation;
+ gdouble hue;
+ gdouble vangle;
+ gdouble vlength;
+
+ gint glow_opacity;
+ CalcBounds glow_bounds;
+ guchar *glow_radial;
+ guchar *glow_angular;
+ guchar *glow_angular_size;
+ gdouble glow_radius;
+ gdouble glow_rotation;
+
+ gint rays_opacity;
+ CalcBounds rays_bounds;
+ guchar *rays_radial;
+ guchar *rays_angular;
+ guchar *rays_angular_size;
+ gdouble rays_radius;
+ gdouble rays_rotation;
+ gdouble rays_spike_mod;
+ gdouble rays_thinness;
+
+ gint sflare_opacity;
+ GList *sflare_list;
+ guchar *sflare_radial;
+ guchar *sflare_sizefac;
+ guchar *sflare_probability;
+ gdouble sflare_radius;
+ gdouble sflare_rotation;
+ GFlareShape sflare_shape;
+ gdouble sflare_angle;
+ gdouble sflare_factor;
+} CalcParams;
+
+/*
+ * What's the difference between (structure) CalcParams and GFlare ?
+ * well, radius and lengths are actual length for CalcParams where
+ * they are typically 0 to 100 for GFlares, and angles are G_PI based
+ * (radian) for CalcParams where they are degree for GFlares. et cetra.
+ * This is because convenience for dialog processing and for calculating.
+ * these conversion is taken place in calc init routines. see below.
+ */
+
+typedef struct
+{
+ gdouble xcenter;
+ gdouble ycenter;
+ gdouble radius;
+ CalcBounds bounds;
+} CalcSFlare;
+
+typedef struct
+{
+ const Babl *format;
+ gint bpp;
+ gint is_color;
+ gint has_alpha;
+ gint x, y, w, h; /* mask bounds */
+} DrawableInfo;
+
+typedef struct _GradientMenu GradientMenu;
+typedef void (* GradientMenuCallback) (const gchar *gradient_name,
+ gpointer data);
+struct _GradientMenu
+{
+ GtkWidget *preview;
+ GtkWidget *combo;
+ GradientMenuCallback callback;
+ gpointer callback_data;
+ GradientName gradient_name;
+};
+
+typedef gint (* PreviewInitFunc) (Preview *preview,
+ gpointer data);
+typedef void (* PreviewRenderFunc) (Preview *preview,
+ guchar *buffer,
+ gint y,
+ gpointer data);
+typedef void (* PreviewDeinitFunc) (Preview *preview,
+ gpointer data);
+
+struct _Preview
+{
+ GtkWidget *widget;
+ gint width;
+ gint height;
+ PreviewInitFunc init_func;
+ gpointer init_data;
+ PreviewRenderFunc render_func;
+ gpointer render_data;
+ PreviewDeinitFunc deinit_func;
+ gpointer deinit_data;
+ guint timeout_tag;
+ guint idle_tag;
+ gint init_done;
+ gint current_y;
+ gint drawn_y;
+ guchar *buffer;
+ guchar *full_image_buffer;
+};
+
+typedef struct
+{
+ gint tag;
+ gint got_gradients;
+ gint current_y;
+ gint drawn_y;
+ PreviewRenderFunc render_func;
+ guchar *buffer;
+} PreviewIdle;
+
+typedef struct _GradientCacheItem GradientCacheItem;
+
+struct _GradientCacheItem
+{
+ GradientCacheItem *next;
+ GradientCacheItem *prev;
+ GradientName name;
+ guchar values[4 * GRADIENT_RESOLUTION];
+};
+
+typedef struct
+{
+ gint xcenter;
+ gint ycenter;
+ gdouble radius;
+ gdouble rotation;
+ gdouble hue;
+ gdouble vangle;
+ gdouble vlength;
+ gint use_asupsample;
+ gint asupsample_max_depth;
+ gdouble asupsample_threshold;
+ gchar gflare_name[GFLARE_NAME_MAX];
+} PluginValues;
+
+
+typedef void (* QueryFunc) (GtkWidget *,
+ gpointer,
+ gpointer);
+
+/***
+ *** Global Functions Prototypes
+ **/
+
+static void plugin_query (void);
+static void plugin_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GFlare * gflare_new_with_default (const gchar *new_name);
+static GFlare * gflare_dup (const GFlare *src,
+ const gchar *new_name);
+static void gflare_copy (GFlare *dest,
+ const GFlare *src);
+static GFlare * gflare_load (const gchar *filename,
+ const gchar *name);
+static void gflare_save (GFlare *gflare);
+static void gflare_name_copy (gchar *dest,
+ const gchar *src);
+
+static gint gflares_list_insert (GFlare *gflare);
+static GFlare * gflares_list_lookup (const gchar *name);
+static gint gflares_list_index (GFlare *gflare);
+static gint gflares_list_remove (GFlare *gflare);
+static void gflares_list_load_all (void);
+static void gflares_list_free_all (void);
+
+static void calc_init_params (GFlare *gflare,
+ gint calc_type,
+ gdouble xcenter,
+ gdouble ycenter,
+ gdouble radius,
+ gdouble rotation,
+ gdouble hue,
+ gdouble vangle,
+ gdouble vlength);
+static gint calc_init_progress (void);
+static void calc_deinit (void);
+static void calc_glow_pix (guchar *dest_pix,
+ gdouble x,
+ gdouble y);
+static void calc_rays_pix (guchar *dest_pix,
+ gdouble x,
+ gdouble y);
+static void calc_sflare_pix (guchar *dest_pix,
+ gdouble x,
+ gdouble y,
+ guchar *src_pix);
+static void calc_gflare_pix (guchar *dest_pix,
+ gdouble x,
+ gdouble y,
+ guchar *src_pix);
+
+static gboolean dlg_run (void);
+static void dlg_preview_calc_window (void);
+static void ed_preview_calc_window (void);
+static GtkWidget * ed_mode_menu_new (GFlareMode *mode_var);
+
+static Preview * preview_new (gint width,
+ gint height,
+ PreviewInitFunc init_func,
+ gpointer init_data,
+ PreviewRenderFunc render_func,
+ gpointer render_data,
+ PreviewDeinitFunc deinit_func,
+ gpointer deinit_data);
+static void preview_free (Preview *preview);
+static void preview_render_start (Preview *preview);
+static void preview_render_end (Preview *preview);
+static void preview_rgba_to_rgb (guchar *dest,
+ gint x,
+ gint y,
+ guchar *src);
+
+static void gradient_menu_init (void);
+static void gradient_menu_rescan (void);
+static GradientMenu * gradient_menu_new (GradientMenuCallback callback,
+ gpointer callback_data,
+ const gchar *default_gradient_name);
+static void gradient_name_copy (gchar *dest,
+ const gchar *src);
+static void gradient_name_encode (gchar *dest,
+ const gchar *src);
+static void gradient_name_decode (gchar *dest,
+ const gchar *src);
+static void gradient_init (void);
+static void gradient_free (void);
+static gchar ** gradient_get_list (gint *num_gradients);
+static void gradient_get_values (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues);
+static void gradient_cache_flush (void);
+
+/* *** INSERT-FILE-END *** */
+
+/**
+*** Variables
+**/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ plugin_query, /* query_proc */
+ plugin_run, /* run_proc */
+};
+
+PluginValues pvals =
+{
+ 128, /* xcenter */
+ 128, /* ycenter */
+ 100.0, /* radius */
+ 0.0, /* rotation */
+ 0.0, /* hue */
+ 60.0, /* vangle */
+ 400.0, /* vlength */
+ FALSE, /* use_asupsample */
+ 3, /* asupsample_max_depth */
+ 0.2, /* asupsample_threshold */
+ "Default" /* gflare_name */
+};
+
+GFlare default_gflare =
+{
+ NULL, /* name */
+ NULL, /* filename */
+ 100, /* glow_opacity */
+ GF_NORMAL, /* glow_mode */
+ 100, /* rays_opacity */
+ GF_NORMAL, /* rays_mode */
+ 100, /* sflare_opacity */
+ GF_NORMAL, /* sflare_mode */
+ "%red_grad", /* glow_radial */
+ "%white", /* glow_angular */
+ "%white", /* glow_angular_size */
+ 100.0, /* glow_size */
+ 0.0, /* glow_rotation */
+ 0.0, /* glow_hue */
+ "%white_grad",/* rays_radial */
+ "%random", /* rays_angular */
+ "%random", /* rays_angular_size */
+ 100.0, /* rays_size */
+ 0.0, /* rays_rotation */
+ 0.0, /* rays_hue */
+ 40, /* rays_nspikes */
+ 20.0, /* rays_thickness */
+ "%white_grad",/* sflare_radial */
+ "%random", /* sflare_sizefac */
+ "%random", /* sflare_probability */
+ 40.0, /* sflare_size */
+ 0.0, /* sflare_rotation */
+ 0.0, /* sflare_hue */
+ GF_CIRCLE, /* sflare_shape */
+ 6, /* sflare_nverts */
+ 0, /* sflare_seed */
+ TRUE, /* random_seed */
+};
+
+/* These are keywords to be written to disk files specifying flares. */
+/* They are not translated since we want gflare files to be compatible
+ across languages. */
+static const gchar *gflare_modes[] =
+{
+ "NORMAL",
+ "ADDITION",
+ "OVERLAY",
+ "SCREEN"
+};
+
+static const gchar *gflare_shapes[] =
+{
+ "CIRCLE",
+ "POLYGON"
+};
+
+/* These are for menu entries, so they are translated. */
+static const gchar *gflare_menu_modes[] =
+{
+ N_("Normal"),
+ N_("Addition"),
+ N_("Overlay"),
+ N_("Screen")
+};
+
+static gint32 image_ID;
+static gint32 drawable_ID;
+static DrawableInfo dinfo;
+static GFlareDialog *dlg = NULL;
+static GFlareEditor *ed = NULL;
+static GList *gflares_list = NULL;
+static gint num_gflares = 0;
+static gchar *gflare_path = NULL;
+static CalcParams calc;
+static GList *gradient_menus;
+static gchar **gradient_names = NULL;
+static gint num_gradient_names = 0;
+static GradientCacheItem *gradient_cache_head = NULL;
+static gint gradient_cache_count = 0;
+
+
+static const gchar *internal_gradients[] =
+{
+ "%white", "%white_grad", "%red_grad", "%blue_grad", "%yellow_grad", "%random"
+};
+
+#ifdef DEBUG
+static gint get_values_external_count = 0;
+static clock_t get_values_external_clock = 0;
+#endif
+
+
+/**
+*** +++ Static Functions Prototypes
+**/
+
+static void plugin_do (void);
+
+static void plugin_do_non_asupsample (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer);
+static void plugin_do_asupsample (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer);
+static void plugin_render_func (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gpointer data);
+static void plugin_put_pixel_func (gint ix,
+ gint iy,
+ GimpRGB *color,
+ gpointer data);
+static void plugin_progress_func (gint y1,
+ gint y2,
+ gint curr_y,
+ gpointer data);
+
+static GFlare * gflare_new (void);
+static void gflare_free (GFlare *gflare);
+static void gflare_read_int (gint *intvar,
+ GFlareFile *gf);
+static void gflare_read_double (gdouble *dblvar,
+ GFlareFile *gf);
+static void gflare_read_gradient_name (gchar *name,
+ GFlareFile *gf);
+static void gflare_read_shape (GFlareShape *shape,
+ GFlareFile *gf);
+static void gflare_read_mode (GFlareMode *mode,
+ GFlareFile *gf);
+static void gflare_write_gradient_name (gchar *name,
+ FILE *fp);
+
+static gint calc_sample_one_gradient (void);
+static void calc_place_sflare (void);
+static void calc_get_gradient (guchar *pix,
+ guchar *gradient,
+ gdouble pos);
+static gdouble fmod_positive (gdouble x,
+ gdouble m);
+static void calc_paint_func (guchar *dest,
+ guchar *src1,
+ guchar *src2,
+ gint opacity,
+ GFlareMode mode);
+static void calc_combine (guchar *dest,
+ guchar *src1,
+ guchar *src2,
+ gint opacity);
+static void calc_addition (guchar *dest,
+ guchar *src1,
+ guchar *src2);
+static void calc_screen (guchar *dest,
+ guchar *src1,
+ guchar *src2);
+static void calc_overlay (guchar *dest,
+ guchar *src1,
+ guchar *src2);
+
+static void dlg_setup_gflare (void);
+static void dlg_preview_realize (GtkWidget *widget);
+static gboolean dlg_preview_handle_event (GtkWidget *widget,
+ GdkEvent *event);
+static void dlg_preview_update (void);
+static gint dlg_preview_init_func (Preview *preview,
+ gpointer data);
+static void dlg_preview_render_func (Preview *preview,
+ guchar *dest,
+ gint y,
+ gpointer data);
+static void dlg_preview_deinit_func (Preview *preview,
+ gpointer data);
+static void dlg_make_page_settings (GFlareDialog *dlg,
+ GtkWidget *notebook);
+static void dlg_position_entry_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_update_preview_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_make_page_selector (GFlareDialog *dlg,
+ GtkWidget *notebook);
+
+static void dlg_selector_setup_listbox (void);
+static void dlg_selector_list_item_callback (GtkTreeSelection *selection);
+
+static void dlg_selector_new_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_selector_new_ok_callback (GtkWidget *widget,
+ const gchar *new_name,
+ gpointer data);
+
+static void dlg_selector_edit_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_selector_edit_done_callback (gint updated,
+ gpointer data);
+
+static void dlg_selector_copy_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_selector_copy_ok_callback (GtkWidget *widget,
+ const gchar *copy_name,
+ gpointer data);
+
+static void dlg_selector_delete_callback (GtkWidget *widget,
+ gpointer data);
+static void dlg_selector_do_delete_callback (GtkWidget *widget,
+ gboolean delete,
+ gpointer data);
+
+static void ed_run (GtkWindow *parent,
+ GFlare *target_gflare,
+ GFlareEditorCallback callback,
+ gpointer calldata);
+static void ed_destroy_callback (GtkWidget *widget,
+ GFlareEditor *ed);
+static void ed_response (GtkWidget *widget,
+ gint response_id,
+ GFlareEditor *ed);
+static void ed_make_page_general (GFlareEditor *ed,
+ GtkWidget *notebook);
+static void ed_make_page_glow (GFlareEditor *ed,
+ GtkWidget *notebook);
+static void ed_make_page_rays (GFlareEditor *ed,
+ GtkWidget *notebook);
+static void ed_make_page_sflare (GFlareEditor *ed,
+ GtkWidget *notebook);
+static void ed_put_gradient_menu (GtkWidget *table,
+ gint x,
+ gint y,
+ const gchar *caption,
+ GradientMenu *gm);
+static void ed_mode_menu_callback (GtkWidget *widget,
+ gpointer data);
+static void ed_gradient_menu_callback (const gchar *gradient_name,
+ gpointer data);
+static void ed_shape_radio_callback (GtkWidget *widget, gpointer data);
+static void ed_ientry_callback (GtkWidget *widget, gpointer data);
+static void ed_page_map_callback (GtkWidget *widget, gpointer data);
+static void ed_preview_update (void);
+static gint ed_preview_init_func (Preview *preview, gpointer data);
+static void ed_preview_deinit_func (Preview *preview, gpointer data);
+static void ed_preview_render_func (Preview *preview,
+ guchar *buffer, gint y, gpointer data);
+static void ed_preview_render_general (guchar *buffer, gint y);
+static void ed_preview_render_glow (guchar *buffer, gint y);
+static void ed_preview_render_rays (guchar *buffer, gint y);
+static void ed_preview_render_sflare (guchar *buffer, gint y);
+
+static gint preview_render_start_2 (Preview *preview);
+static gint preview_handle_idle (Preview *preview);
+
+static void gm_gradient_get_list (void);
+static void gm_gradient_combo_fill (GradientMenu *gm,
+ const gchar *default_gradient);
+static void gm_gradient_combo_callback (GtkWidget *widget,
+ gpointer data);
+static void gm_preview_draw (GtkWidget *preview,
+ const gchar *gradient_name);
+static void gm_combo_destroy_callback (GtkWidget *widget,
+ gpointer data);
+
+static void gradient_get_values_internal (const gchar *gradient_name,
+ guchar *values, gint nvalues);
+static void gradient_get_blend (const guchar *fg,
+ const guchar *bg,
+ guchar *values, gint nvalues);
+static void gradient_get_random (guchar *values, gint nvalues);
+static void gradient_get_default (const gchar *name,
+ guchar *values, gint nvalues);
+static void gradient_get_values_external (const gchar *gradient_name,
+ guchar *values, gint nvalues);
+static void gradient_get_values_real_external (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues,
+ gboolean reverse);
+static GradientCacheItem *gradient_cache_lookup (const gchar *name,
+ gboolean *found);
+static void gradient_cache_zorch (void);
+
+/* *** INSERT-FILE-END *** */
+
+
+/*************************************************************************/
+/** **/
+/** +++ Plug-in Interfaces **/
+/** **/
+/*************************************************************************/
+
+MAIN ()
+
+void
+plugin_query (void)
+{
+ static const GimpParamDef args[]=
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_STRING, "gflare-name", "The name of GFlare" },
+ { GIMP_PDB_INT32, "xcenter", "X coordinate of center of GFlare" },
+ { GIMP_PDB_INT32, "ycenter", "Y coordinate of center of GFlare" },
+ { GIMP_PDB_FLOAT, "radius", "Radius of GFlare (pixel)" },
+ { GIMP_PDB_FLOAT, "rotation", "Rotation of GFlare (degree)" },
+ { GIMP_PDB_FLOAT, "hue", "Hue rotation of GFlare (degree)" },
+ { GIMP_PDB_FLOAT, "vangle", "Vector angle for second flares (degree)" },
+ { GIMP_PDB_FLOAT, "vlength", "Vector length for second flares (percentage to Radius)" },
+ { GIMP_PDB_INT32, "use-asupsample", "Whether it uses or not adaptive supersampling while rendering (boolean)" },
+ { GIMP_PDB_INT32, "asupsample-max-depth", "Max depth for adaptive supersampling"},
+ { GIMP_PDB_FLOAT, "asupsample-threshold", "Threshold for adaptive supersampling"}
+ };
+
+ const gchar *help_string =
+ "This plug-in produces a lense flare effect using custom gradients. "
+ "In interactive call, the user can edit his/her own favorite lense flare "
+ "(GFlare) and render it. Edited gflare is saved automatically to "
+ "the folder in gflare-path, if it is defined in gimprc. "
+ "In non-interactive call, the user can only render one of GFlare "
+ "which has been stored in gflare-path already.";
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Produce a lense flare effect using gradients"),
+ help_string,
+ "Eiichi Takamori",
+ "Eiichi Takamori, and a lot of GIMP people",
+ "1997",
+ N_("_Gradient Flare..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC,
+ "<Image>/Filters/Light and Shadow/Light");
+}
+
+void
+plugin_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gchar *path;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ run_mode = param[0].data.d_int32;
+ image_ID = param[1].data.d_image;
+ drawable_ID = param[2].data.d_drawable;
+
+ dinfo.is_color = gimp_drawable_is_rgb (drawable_ID);
+ dinfo.has_alpha = gimp_drawable_has_alpha (drawable_ID);
+
+ if (dinfo.is_color)
+ {
+ if (dinfo.has_alpha)
+ dinfo.format = babl_format ("R'G'B'A u8");
+ else
+ dinfo.format = babl_format ("R'G'B' u8");
+ }
+ else
+ {
+ if (dinfo.has_alpha)
+ dinfo.format = babl_format ("Y'A u8");
+ else
+ dinfo.format = babl_format ("Y u8");
+ }
+
+ dinfo.bpp = babl_format_get_bytes_per_pixel (dinfo.format);
+
+ if (! gimp_drawable_mask_intersect (drawable_ID,
+ &dinfo.x, &dinfo.y, &dinfo.w, &dinfo.h))
+ return;
+
+ /*
+ * Start gradient caching
+ */
+
+ gradient_init ();
+
+ /*
+ * Parse gflare path from gimprc and load gflares
+ */
+
+ path = gimp_gimprc_query ("gflare-path");
+ if (path)
+ {
+ gflare_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+ g_free (path);
+ }
+ else
+ {
+ gchar *gimprc = gimp_personal_rc_file ("gimprc");
+ gchar *full_path = gimp_config_build_data_path ("gflare");
+ gchar *esc_path = g_strescape (full_path, NULL);
+ g_free (full_path);
+
+ g_message (_("No %s in gimprc:\n"
+ "You need to add an entry like\n"
+ "(%s \"%s\")\n"
+ "to your %s file."),
+ "gflare-path", "gflare-path",
+ esc_path, gimp_filename_to_utf8 (gimprc));
+
+ g_free (gimprc);
+ g_free (esc_path);
+ }
+
+ gflares_list_load_all ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+
+ /* First acquire information with a dialog */
+ if (! dlg_run ())
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 14)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ gflare_name_copy (pvals.gflare_name, param[3].data.d_string);
+ pvals.xcenter = param[4].data.d_int32;
+ pvals.ycenter = param[5].data.d_int32;
+ pvals.radius = param[6].data.d_float;
+ pvals.rotation = param[7].data.d_float;
+ pvals.hue = param[8].data.d_float;
+ pvals.vangle = param[9].data.d_float;
+ pvals.vlength = param[10].data.d_float;
+ pvals.use_asupsample = param[11].data.d_int32;
+ pvals.asupsample_max_depth = param[12].data.d_int32;
+ pvals.asupsample_threshold = param[13].data.d_float;
+
+ if (pvals.radius <= 0)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &pvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is gray or RGB color */
+ if (gimp_drawable_is_rgb (drawable_ID) ||
+ gimp_drawable_is_gray (drawable_ID))
+ {
+ gimp_progress_init (_("Gradient Flare"));
+ plugin_do ();
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ /* Store data */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &pvals, sizeof (PluginValues));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = _("Cannot operate on indexed color images.");
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ /*
+ * Deinitialization
+ */
+ gradient_free ();
+}
+
+static void
+plugin_do (void)
+{
+ GeglBuffer *src_buffer;
+ GeglBuffer *dest_buffer;
+ GFlare *gflare;
+
+ gflare = gflares_list_lookup (pvals.gflare_name);
+ if (gflare == NULL)
+ {
+ /* FIXME */
+ g_warning ("Not found %s\n", pvals.gflare_name);
+ return;
+ }
+
+ /* Initialize calc params and gradients */
+ calc_init_params (gflare, CALC_GLOW | CALC_RAYS | CALC_SFLARE,
+ pvals.xcenter, pvals.ycenter,
+ pvals.radius, pvals.rotation, pvals.hue,
+ pvals.vangle, pvals.vlength);
+ while (calc_init_progress ()) ;
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+ dest_buffer = gimp_drawable_get_shadow_buffer (drawable_ID);
+
+ /* Render it ! */
+ if (pvals.use_asupsample)
+ plugin_do_asupsample (src_buffer, dest_buffer);
+ else
+ plugin_do_non_asupsample (src_buffer, dest_buffer);
+
+ g_object_unref (src_buffer);
+ g_object_unref (dest_buffer);
+
+ gimp_progress_update (1.0);
+
+ /* Clean up */
+ calc_deinit ();
+
+ gimp_drawable_merge_shadow (drawable_ID, TRUE);
+ gimp_drawable_update (drawable_ID, dinfo.x, dinfo.y, dinfo.w, dinfo.h);
+}
+
+/* these routines should be almost rewritten anyway */
+
+static void
+plugin_do_non_asupsample (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer)
+{
+ GeglBufferIterator *iter;
+ gint progress;
+ gint max_progress;
+
+ progress = 0;
+ max_progress = dinfo.w * dinfo.h;
+
+ iter = gegl_buffer_iterator_new (src_buffer,
+ GEGL_RECTANGLE (dinfo.x, dinfo.y,
+ dinfo.w, dinfo.h), 0,
+ dinfo.format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2);
+
+ gegl_buffer_iterator_add (iter, dest_buffer,
+ GEGL_RECTANGLE (dinfo.x, dinfo.y,
+ dinfo.w, dinfo.h), 0,
+ dinfo.format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src_row = iter->items[0].data;
+ guchar *dest_row = iter->items[1].data;
+ gint row, y;
+
+ for (row = 0, y = iter->items[0].roi.y;
+ row < iter->items[0].roi.height;
+ row++, y++)
+ {
+ const guchar *src = src_row;
+ guchar *dest = dest_row;
+ gint col, x;
+
+ for (col = 0, x = iter->items[0].roi.x;
+ col < iter->items[0].roi.width;
+ col++, x++)
+ {
+ guchar src_pix[4];
+ guchar dest_pix[4];
+ gint b;
+
+ for (b = 0; b < 3; b++)
+ src_pix[b] = dinfo.is_color ? src[b] : src[0];
+
+ src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
+
+ calc_gflare_pix (dest_pix, x, y, src_pix);
+
+ if (dinfo.is_color)
+ {
+ for (b = 0; b < 3; b++)
+ dest[b] = dest_pix[b];
+ }
+ else
+ {
+ dest[0] = LUMINOSITY (dest_pix);
+ }
+
+ if (dinfo.has_alpha)
+ dest[dinfo.bpp - 1] = dest_pix[3];
+
+ src += dinfo.bpp;
+ dest += dinfo.bpp;
+ }
+
+ src_row += dinfo.bpp * iter->items[0].roi.width;
+ dest_row += dinfo.bpp * iter->items[1].roi.width;
+ }
+
+ /* Update progress */
+ progress += iter->items[0].roi.width * iter->items[0].roi.height;
+ gimp_progress_update ((double) progress / (double) max_progress);
+ }
+}
+
+static void
+plugin_do_asupsample (GeglBuffer *src_buffer,
+ GeglBuffer *dest_buffer)
+{
+ gimp_adaptive_supersample_area (dinfo.x, dinfo.y,
+ dinfo.x + dinfo.w - 1, dinfo.y + dinfo.h - 1,
+ pvals.asupsample_max_depth,
+ pvals.asupsample_threshold,
+ plugin_render_func,
+ src_buffer,
+ plugin_put_pixel_func,
+ dest_buffer,
+ plugin_progress_func,
+ NULL);
+}
+
+/*
+ Adaptive supersampling callback functions
+
+ These routines may look messy, since adaptive supersampling needs
+ pixel values in `double' (from 0.0 to 1.0) but calc_*_pix () returns
+ guchar values. */
+
+static void
+plugin_render_func (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gpointer data)
+{
+ GeglBuffer *src_buffer = data;
+ guchar src_pix[4];
+ guchar flare_pix[4];
+ guchar src[4];
+ gint b;
+ gint ix, iy;
+
+ /* translate (0.5, 0.5) before convert to `int' so that it can surely
+ point the center of pixel */
+ ix = floor (x + 0.5);
+ iy = floor (y + 0.5);
+
+ gegl_buffer_sample (src_buffer, ix, iy, NULL, src, dinfo.format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ for (b = 0; b < 3; b++)
+ src_pix[b] = dinfo.is_color ? src[b] : src[0];
+ src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
+
+ calc_gflare_pix (flare_pix, x, y, src_pix);
+
+ color->r = flare_pix[0] / 255.0;
+ color->g = flare_pix[1] / 255.0;
+ color->b = flare_pix[2] / 255.0;
+ color->a = flare_pix[3] / 255.0;
+}
+
+static void
+plugin_put_pixel_func (gint ix,
+ gint iy,
+ GimpRGB *color,
+ gpointer data)
+{
+ GeglBuffer *dest_buffer = data;
+ guchar dest[4];
+
+ if (dinfo.is_color)
+ {
+ dest[0] = color->r * 255;
+ dest[1] = color->g * 255;
+ dest[2] = color->b * 255;
+ }
+ else
+ {
+ dest[0] = gimp_rgb_luminance_uchar (color);
+ }
+
+ if (dinfo.has_alpha)
+ dest[dinfo.bpp - 1] = color->a * 255;
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (ix, iy, 1, 1), 0,
+ dinfo.format, dest, GEGL_AUTO_ROWSTRIDE);
+}
+
+static void
+plugin_progress_func (gint y1,
+ gint y2,
+ gint curr_y,
+ gpointer data)
+{
+ gimp_progress_update ((double) curr_y / (double) (y2 - y1));
+}
+
+/*************************************************************************/
+/** **/
+/** +++ GFlare Routines **/
+/** **/
+/*************************************************************************/
+
+/*
+ * These code are more or less based on Quartic's gradient.c,
+ * other gimp sources, and script-fu.
+ */
+
+static GFlare *
+gflare_new (void)
+{
+ GFlare *gflare = g_new0 (GFlare, 1);
+
+ gflare->name = NULL;
+ gflare->filename = NULL;
+
+ return gflare;
+}
+
+static GFlare *
+gflare_new_with_default (const gchar *new_name)
+{
+ return gflare_dup (&default_gflare, new_name);
+}
+
+static GFlare *
+gflare_dup (const GFlare *src,
+ const gchar *new_name)
+{
+ GFlare *dest = g_new0 (GFlare, 1);
+
+ *dest = *src;
+
+ dest->name = g_strdup (new_name);
+ dest->filename = NULL;
+
+ return dest;
+}
+
+static void
+gflare_copy (GFlare *dest,
+ const GFlare *src)
+{
+ gchar *name, *filename;
+
+ name = dest->name;
+ filename = dest->filename;
+
+ *dest = *src;
+
+ dest->name = name;
+ dest->filename =filename;
+}
+
+
+static void
+gflare_free (GFlare *gflare)
+{
+ g_return_if_fail (gflare != NULL);
+
+ g_free (gflare->name);
+ g_free (gflare->filename);
+ g_free (gflare);
+}
+
+GFlare *
+gflare_load (const gchar *filename,
+ const gchar *name)
+{
+ FILE *fp;
+ GFlareFile *gf;
+ GFlare *gflare;
+ gchar header[256];
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ fp = g_fopen (filename, "rb");
+ if (!fp)
+ {
+ g_message (_("Failed to open GFlare file '%s': %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return NULL;
+ }
+
+ if (fgets (header, sizeof(header), fp) == NULL
+ || strcmp (header, GFLARE_FILE_HEADER) != 0)
+ {
+ g_warning (_("'%s' is not a valid GFlare file."),
+ gimp_filename_to_utf8 (filename));
+ fclose (fp);
+ return NULL;
+ }
+
+ gf = g_new (GFlareFile, 1);
+ gf->fp = fp;
+ gf->error = FALSE;
+
+ gflare = gflare_new ();
+ gflare->name = g_strdup (name);
+ gflare->filename = g_strdup (filename);
+
+ gflare_read_double (&gflare->glow_opacity, gf);
+ gflare_read_mode (&gflare->glow_mode, gf);
+ gflare_read_double (&gflare->rays_opacity, gf);
+ gflare_read_mode (&gflare->rays_mode, gf);
+ gflare_read_double (&gflare->sflare_opacity, gf);
+ gflare_read_mode (&gflare->sflare_mode, gf);
+
+ gflare_read_gradient_name (gflare->glow_radial, gf);
+ gflare_read_gradient_name (gflare->glow_angular, gf);
+ gflare_read_gradient_name (gflare->glow_angular_size, gf);
+ gflare_read_double (&gflare->glow_size, gf);
+ gflare_read_double (&gflare->glow_rotation, gf);
+ gflare_read_double (&gflare->glow_hue, gf);
+
+ gflare_read_gradient_name (gflare->rays_radial, gf);
+ gflare_read_gradient_name (gflare->rays_angular, gf);
+ gflare_read_gradient_name (gflare->rays_angular_size, gf);
+ gflare_read_double (&gflare->rays_size, gf);
+ gflare_read_double (&gflare->rays_rotation, gf);
+ gflare_read_double (&gflare->rays_hue, gf);
+ gflare_read_int (&gflare->rays_nspikes, gf);
+ gflare_read_double (&gflare->rays_thickness, gf);
+
+ gflare_read_gradient_name (gflare->sflare_radial, gf);
+ gflare_read_gradient_name (gflare->sflare_sizefac, gf);
+ gflare_read_gradient_name (gflare->sflare_probability, gf);
+ gflare_read_double (&gflare->sflare_size, gf);
+ gflare_read_double (&gflare->sflare_hue, gf);
+ gflare_read_double (&gflare->sflare_rotation, gf);
+ gflare_read_shape (&gflare->sflare_shape, gf);
+ gflare_read_int (&gflare->sflare_nverts, gf);
+ gflare_read_int ((gint *) &gflare->sflare_seed, gf);
+
+ if (gflare->sflare_seed == 0)
+ gflare->sflare_seed = g_random_int();
+
+ fclose (gf->fp);
+
+ if (gf->error)
+ {
+ g_warning (_("invalid formatted GFlare file: %s\n"), filename);
+ g_free (gflare);
+ g_free (gf);
+ return NULL;
+ }
+
+ g_free (gf);
+
+ return gflare;
+}
+
+static void
+gflare_read_int (gint *intvar,
+ GFlareFile *gf)
+{
+ if (gf->error)
+ return;
+
+ if (fscanf (gf->fp, "%d", intvar) != 1)
+ gf->error = TRUE;
+}
+
+static void
+gflare_read_double (gdouble *dblvar,
+ GFlareFile *gf)
+{
+ gchar buf[31];
+
+ if (gf->error)
+ return;
+
+ if (fscanf (gf->fp, "%30s", buf) == 1)
+ *dblvar = g_ascii_strtod (buf, NULL);
+ else
+ gf->error = TRUE;
+}
+
+static void
+gflare_read_gradient_name (GradientName name,
+ GFlareFile *gf)
+{
+ gchar tmp[1024], dec[1024];
+
+ if (gf->error)
+ return;
+
+ /* FIXME: this is buggy */
+
+ if (fscanf (gf->fp, "%1023s", tmp) == 1)
+ {
+ /* @GRADIENT_NAME */
+ gradient_name_decode (dec, tmp);
+ gradient_name_copy (name, dec);
+ }
+ else
+ gf->error = TRUE;
+}
+
+static void
+gflare_read_shape (GFlareShape *shape,
+ GFlareFile *gf)
+{
+ gchar tmp[1024];
+ gint i;
+
+ if (gf->error)
+ return;
+
+ if (fscanf (gf->fp, "%1023s", tmp) == 1)
+ {
+ for (i = 0; i < GF_NUM_SHAPES; i++)
+ if (strcmp (tmp, gflare_shapes[i]) == 0)
+ {
+ *shape = i;
+ return;
+ }
+ }
+ gf->error = TRUE;
+}
+
+static void
+gflare_read_mode (GFlareMode *mode,
+ GFlareFile *gf)
+{
+ gchar tmp[1024];
+ gint i;
+
+ if (gf->error)
+ return;
+
+ if (fscanf (gf->fp, "%1023s", tmp) == 1)
+ {
+ for (i = 0; i < GF_NUM_MODES; i++)
+ if (strcmp (tmp, gflare_modes[i]) == 0)
+ {
+ *mode = i;
+ return;
+ }
+ }
+ gf->error = TRUE;
+}
+
+static void
+gflare_save (GFlare *gflare)
+{
+ FILE *fp;
+ gchar *path;
+ gchar buf[3][G_ASCII_DTOSTR_BUF_SIZE];
+ static gboolean message_ok = FALSE;
+
+ if (gflare->filename == NULL)
+ {
+ GList *list;
+
+ if (gflare_path == NULL)
+ {
+ if (! message_ok)
+ {
+ gchar *gimprc = gimp_personal_rc_file ("gimprc");
+ gchar *dir = gimp_personal_rc_file ("gflare");
+ gchar *gflare_dir;
+
+ gflare_dir =
+ g_strescape ("${gimp_dir}" G_DIR_SEPARATOR_S "gflare", NULL);
+
+ g_message (_("GFlare '%s' is not saved. If you add a new entry "
+ "in '%s', like:\n"
+ "(gflare-path \"%s\")\n"
+ "and make a folder '%s', then you can save "
+ "your own GFlares into that folder."),
+ gflare->name, gimprc, gflare_dir,
+ gimp_filename_to_utf8 (dir));
+
+ g_free (gimprc);
+ g_free (gflare_dir);
+ g_free (dir);
+
+ message_ok = TRUE;
+ }
+
+ return;
+ }
+
+ list = gimp_path_parse (gflare_path, 256, FALSE, NULL);
+ path = gimp_path_get_user_writable_dir (list);
+ gimp_path_free (list);
+
+ if (! path)
+ path = g_strdup (gimp_directory ());
+
+ gflare->filename = g_build_filename (path, gflare->name, NULL);
+
+ g_free (path);
+ }
+
+ fp = g_fopen (gflare->filename, "wb");
+ if (!fp)
+ {
+ g_message (_("Failed to write GFlare file '%s': %s"),
+ gimp_filename_to_utf8 (gflare->filename), g_strerror (errno));
+ return;
+ }
+
+ fprintf (fp, "%s", GFLARE_FILE_HEADER);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_opacity);
+ fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->glow_mode]);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_opacity);
+ fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->rays_mode]);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_opacity);
+ fprintf (fp, "%s %s\n", buf[0], gflare_modes[gflare->sflare_mode]);
+
+ gflare_write_gradient_name (gflare->glow_radial, fp);
+ gflare_write_gradient_name (gflare->glow_angular, fp);
+ gflare_write_gradient_name (gflare->glow_angular_size, fp);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_size);
+ g_ascii_dtostr (buf[1],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_rotation);
+ g_ascii_dtostr (buf[2],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->glow_hue);
+ fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
+
+ gflare_write_gradient_name (gflare->rays_radial, fp);
+ gflare_write_gradient_name (gflare->rays_angular, fp);
+ gflare_write_gradient_name (gflare->rays_angular_size, fp);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_size);
+ g_ascii_dtostr (buf[1],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_rotation);
+ g_ascii_dtostr (buf[2],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_hue);
+ fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->rays_thickness);
+ fprintf (fp, "%d %s\n", gflare->rays_nspikes, buf[0]);
+
+ gflare_write_gradient_name (gflare->sflare_radial, fp);
+ gflare_write_gradient_name (gflare->sflare_sizefac, fp);
+ gflare_write_gradient_name (gflare->sflare_probability, fp);
+ g_ascii_dtostr (buf[0],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_size);
+ g_ascii_dtostr (buf[1],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_rotation);
+ g_ascii_dtostr (buf[2],
+ G_ASCII_DTOSTR_BUF_SIZE, gflare->sflare_hue);
+ fprintf (fp, "%s %s %s\n", buf[0], buf[1], buf[2]);
+ fprintf (fp, "%s %d %d\n",
+ gflare_shapes[gflare->sflare_shape],
+ gflare->sflare_nverts, gflare->sflare_seed);
+
+ fclose (fp);
+}
+
+static void
+gflare_write_gradient_name (GradientName name,
+ FILE *fp)
+{
+ gchar enc[1024];
+
+ /* @GRADIENT_NAME */
+
+ /* encode white spaces and control characters (if any) */
+ gradient_name_encode (enc, name);
+
+ fprintf (fp, "%s\n", enc);
+}
+
+static void
+gflare_name_copy (gchar *dest,
+ const gchar *src)
+{
+ strncpy (dest, src, GFLARE_NAME_MAX - 1);
+ dest[GFLARE_NAME_MAX - 1] = '\0';
+}
+
+/*************************************************************************/
+/** **/
+/** +++ GFlares List **/
+/** **/
+/*************************************************************************/
+
+static gint
+gflare_compare (const GFlare *flare1,
+ const GFlare *flare2)
+{
+ return strcmp (flare1->name, flare2->name);
+}
+
+static gint
+gflares_list_insert (GFlare *gflare)
+{
+ num_gflares++;
+ gflares_list = g_list_insert_sorted (gflares_list, gflare,
+ (GCompareFunc) gflare_compare);
+ return gflares_list_index (gflare);
+}
+
+static gint
+gflare_compare_name (const GFlare *flare,
+ const gchar *name)
+{
+ return strcmp (flare->name, name);
+}
+
+static GFlare *
+gflares_list_lookup (const gchar *name)
+{
+ GList *llink;
+ llink = g_list_find_custom (gflares_list, name,
+ (GCompareFunc) gflare_compare_name);
+ return (llink) ? llink->data : NULL;
+}
+
+static gint
+gflares_list_index (GFlare *gflare)
+{
+ return g_list_index (gflares_list, gflare);
+}
+
+static gint
+gflares_list_remove (GFlare *gflare)
+{
+ GList *tmp;
+ gint n;
+
+ n = 0;
+ tmp = gflares_list;
+ while (tmp)
+ {
+ if (tmp->data == gflare)
+ {
+ /* Found! */
+ if (tmp->next == NULL)
+ num_gflares--;
+ gflares_list = g_list_remove (gflares_list, gflare);
+ return n;
+ }
+ tmp = tmp->next;
+ n++;
+ }
+ return -1;
+}
+
+/*
+ * Load all gflares, which are founded in gflare-path-list, into gflares_list.
+ */
+static void
+gflares_list_load_all (void)
+{
+ GList *path;
+ GList *list;
+
+ /* Make sure to clear any existing gflares */
+ gflares_list_free_all ();
+
+ path = gimp_config_path_expand_to_files (gflare_path, NULL);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children (list->data,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFileType file_type = g_file_info_get_file_type (info);
+
+ if (file_type == G_FILE_TYPE_REGULAR &&
+ ! g_file_info_get_is_hidden (info))
+ {
+ GFlare *gflare;
+ GFile *child;
+ gchar *filename;
+ gchar *basename;
+
+ child = g_file_enumerator_get_child (enumerator, info);
+
+ filename = g_file_get_path (child);
+ basename = g_file_get_basename (child);
+
+ gflare = gflare_load (filename, basename);
+
+ g_free (filename);
+ g_free (basename);
+
+ if (gflare)
+ gflares_list_insert (gflare);
+
+ g_object_unref (child);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+ }
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+}
+
+static void
+gflares_list_free_all (void)
+{
+ g_list_free_full (gflares_list, (GDestroyNotify) gflare_free);
+ gflares_list = NULL;
+}
+
+/*************************************************************************/
+/** **/
+/** +++ Calculator **/
+/** **/
+/*************************************************************************/
+
+
+/*
+ * These routines calculates pixel values of particular gflare, at
+ * specified point. The client which wants to get benefit from these
+ * calculation routines must call calc_init_params() first, and
+ * iterate calling calc_init_progress() until it returns FALSE. and
+ * must call calc_deinit() when job is done.
+ */
+
+static void
+calc_init_params (GFlare *gflare,
+ gint calc_type,
+ gdouble xcenter,
+ gdouble ycenter,
+ gdouble radius,
+ gdouble rotation,
+ gdouble hue,
+ gdouble vangle,
+ gdouble vlength)
+{
+ calc.type = calc_type;
+ calc.gflare = gflare;
+ calc.xcenter = xcenter;
+ calc.ycenter = ycenter;
+ calc.radius = radius;
+ calc.rotation = rotation * G_PI / 180.0;
+ calc.hue = hue;
+ calc.vangle = vangle * G_PI / 180.0;
+ calc.vlength = radius * vlength / 100.0;
+ calc.glow_radius = radius * gflare->glow_size / 100.0;
+ calc.rays_radius = radius * gflare->rays_size / 100.0;
+ calc.sflare_radius = radius * gflare->sflare_size / 100.0;
+ calc.glow_rotation = (rotation + gflare->glow_rotation) * G_PI / 180.0;
+ calc.rays_rotation = (rotation + gflare->rays_rotation) * G_PI / 180.0;
+ calc.sflare_rotation = (rotation + gflare->sflare_rotation) * G_PI / 180.0;
+ calc.glow_opacity = gflare->glow_opacity * 255 / 100.0;
+ calc.rays_opacity = gflare->rays_opacity * 255 / 100.0;
+ calc.sflare_opacity = gflare->sflare_opacity * 255 / 100.0;
+
+ calc.glow_bounds.x0 = calc.xcenter - calc.glow_radius - 0.1;
+ calc.glow_bounds.x1 = calc.xcenter + calc.glow_radius + 0.1;
+ calc.glow_bounds.y0 = calc.ycenter - calc.glow_radius - 0.1;
+ calc.glow_bounds.y1 = calc.ycenter + calc.glow_radius + 0.1;
+ calc.rays_bounds.x0 = calc.xcenter - calc.rays_radius - 0.1;
+ calc.rays_bounds.x1 = calc.xcenter + calc.rays_radius + 0.1;
+ calc.rays_bounds.y0 = calc.ycenter - calc.rays_radius - 0.1;
+ calc.rays_bounds.y1 = calc.ycenter + calc.rays_radius + 0.1;
+
+ /* Thanks to Marcelo Malheiros for this algorithm */
+ calc.rays_thinness = log (gflare->rays_thickness / 100.0) / log(0.8);
+
+ calc.rays_spike_mod = 1.0 / (2 * gflare->rays_nspikes);
+
+ /*
+ Initialize part of sflare
+ The rest will be initialized in calc_sflare()
+ */
+ calc.sflare_list = NULL;
+ calc.sflare_shape = gflare->sflare_shape;
+ if (calc.sflare_shape == GF_POLYGON)
+ {
+ calc.sflare_angle = 2 * G_PI / (2 * gflare->sflare_nverts);
+ calc.sflare_factor = 1.0 / cos (calc.sflare_angle);
+ }
+
+ calc.glow_radial = NULL;
+ calc.glow_angular = NULL;
+ calc.glow_angular_size = NULL;
+ calc.rays_radial = NULL;
+ calc.rays_angular = NULL;
+ calc.rays_angular_size = NULL;
+ calc.sflare_radial = NULL;
+ calc.sflare_sizefac = NULL;
+ calc.sflare_probability = NULL;
+
+ calc.init = TRUE;
+}
+
+static int
+calc_init_progress (void)
+{
+ if (calc_sample_one_gradient ())
+ return TRUE;
+ calc_place_sflare ();
+ return FALSE;
+}
+
+/*
+ Store samples of gradient into an array
+ this routine is called during Calc initialization
+ this code is very messy... :( */
+static int
+calc_sample_one_gradient (void)
+{
+ static struct
+ {
+ guchar **values;
+ gint name_offset;
+ gint hue_offset;
+ gint gray;
+ } table[] = {
+ {
+ &calc.glow_radial,
+ G_STRUCT_OFFSET (GFlare, glow_radial),
+ G_STRUCT_OFFSET (GFlare, glow_hue),
+ FALSE
+ },
+ {
+ &calc.glow_angular,
+ G_STRUCT_OFFSET (GFlare, glow_angular),
+ 0,
+ FALSE
+ },
+ {
+ &calc.glow_angular_size,
+ G_STRUCT_OFFSET (GFlare, glow_angular_size),
+ 0,
+ TRUE
+ },
+ {
+ &calc.rays_radial,
+ G_STRUCT_OFFSET (GFlare, rays_radial),
+ G_STRUCT_OFFSET (GFlare, rays_hue),
+ FALSE
+ },
+ {
+ &calc.rays_angular,
+ G_STRUCT_OFFSET (GFlare, rays_angular),
+ 0,
+ FALSE
+ },
+ {
+ &calc.rays_angular_size,
+ G_STRUCT_OFFSET (GFlare, rays_angular_size),
+ 0,
+ TRUE
+ },
+ {
+ &calc.sflare_radial,
+ G_STRUCT_OFFSET (GFlare, sflare_radial),
+ G_STRUCT_OFFSET (GFlare, sflare_hue),
+ FALSE
+ },
+ {
+ &calc.sflare_sizefac,
+ G_STRUCT_OFFSET (GFlare, sflare_sizefac),
+ 0,
+ TRUE
+ },
+ {
+ &calc.sflare_probability,
+ G_STRUCT_OFFSET (GFlare, sflare_probability),
+ 0,
+ TRUE
+ }
+ };
+ GFlare *gflare = calc.gflare;
+ GradientName *grad_name;
+ guchar *gradient;
+ gdouble hue_deg;
+ gint i, j, hue;
+
+ for (i = 0; i < G_N_ELEMENTS (table); i++)
+ {
+ if (*(table[i].values) == NULL)
+ {
+ /* @GRADIENT_NAME */
+ grad_name = (GradientName *) ((char*) gflare + table[i].name_offset);
+ gradient = *(table[i].values) = g_new (guchar, 4 * GRADIENT_RESOLUTION);
+ gradient_get_values (*grad_name, gradient, GRADIENT_RESOLUTION);
+
+ /*
+ * Do hue rotation, if needed
+ */
+
+ if (table[i].hue_offset != 0)
+ {
+ hue_deg = calc.hue + *(gdouble *) ((char*) gflare + table[i].hue_offset);
+ hue = (gint) (hue_deg / 360.0 * 256.0) % 256;
+ if (hue < 0)
+ hue += 256;
+ g_assert (0 <= hue && hue < 256);
+
+ if (hue > 0)
+ {
+ for (j = 0; j < GRADIENT_RESOLUTION; j++)
+ {
+ GimpRGB rgb;
+ GimpHSV hsv;
+
+ rgb.r = (gdouble) gradient[j*4] / 255.0;
+ rgb.g = (gdouble) gradient[j*4+1] / 255.0;
+ rgb.b = (gdouble) gradient[j*4+2] / 255.0;
+
+ gimp_rgb_to_hsv (&rgb, &hsv);
+
+ hsv.h = (hsv.h + ((gdouble) hue / 255.0));
+ if (hsv.h > 1.0)
+ hsv.h -= 1.0;
+
+ gimp_hsv_to_rgb (&hsv, &rgb);
+
+ gradient[j*4] = ROUND (rgb.r * 255.0);
+ gradient[j*4+1] = ROUND (rgb.g * 255.0);
+ gradient[j*4+2] = ROUND (rgb.b * 255.0);
+ }
+ }
+ }
+
+ /*
+ * Grayfy gradient, if needed
+ */
+
+ if (table[i].gray)
+ {
+ for (j = 0; j < GRADIENT_RESOLUTION; j++)
+ /* the first byte is enough */
+ gradient[j*4] = LUMINOSITY ((gradient + j*4));
+
+ }
+
+ /* sampling of one gradient is done */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+calc_place_sflare (void)
+{
+ GFlare *gflare;
+ CalcSFlare *sflare;
+ gdouble prob[GRADIENT_RESOLUTION];
+ gdouble sum, sum2;
+ gdouble pos;
+ gdouble rnd, sizefac;
+ int n;
+ int i;
+ GRand *gr;
+
+ gr = g_rand_new ();
+ if ((calc.type & CALC_SFLARE) == 0)
+ return;
+
+ gflare = calc.gflare;
+
+ /*
+ Calc cumulative probability
+ */
+
+ sum = 0.0;
+ for (i = 0; i < GRADIENT_RESOLUTION; i++)
+ {
+ /* probability gradient was grayfied already */
+ prob[i] = calc.sflare_probability[i*4];
+ sum += prob[i];
+ }
+
+ if (sum == 0.0)
+ sum = 1.0;
+
+ sum2 = 0;
+ for (i = 0; i < GRADIENT_RESOLUTION; i++)
+ {
+ sum2 += prob[i]; /* cumulation */
+ prob[i] = sum2 / sum;
+ }
+
+ g_rand_set_seed (gr, gflare->sflare_seed);
+
+ for (n = 0; n < SFLARE_NUM; n++)
+ {
+ sflare = g_new (CalcSFlare, 1);
+ rnd = g_rand_double (gr);
+ for (i = 0; i < GRADIENT_RESOLUTION; i++)
+ if (prob[i] >= rnd)
+ break;
+ if (i >= GRADIENT_RESOLUTION)
+ i = GRADIENT_RESOLUTION - 1;
+
+ /* sizefac gradient was grayfied already */
+ sizefac = calc.sflare_sizefac[i*4] / 255.0;
+ sizefac = pow (sizefac, 5.0);
+
+ pos = (double) (i - GRADIENT_RESOLUTION / 2) / GRADIENT_RESOLUTION;
+ sflare->xcenter = calc.xcenter + cos (calc.vangle) * calc.vlength * pos;
+ sflare->ycenter = calc.ycenter - sin (calc.vangle) * calc.vlength * pos;
+ sflare->radius = sizefac * calc.sflare_radius; /* FIXME */
+ sflare->bounds.x0 = sflare->xcenter - sflare->radius - 1;
+ sflare->bounds.x1 = sflare->xcenter + sflare->radius + 1;
+ sflare->bounds.y0 = sflare->ycenter - sflare->radius - 1;
+ sflare->bounds.y1 = sflare->ycenter + sflare->radius + 1;
+ calc.sflare_list = g_list_append (calc.sflare_list, sflare);
+ }
+
+ g_rand_free (gr);
+}
+
+static void
+calc_deinit (void)
+{
+ if (!calc.init)
+ {
+ g_warning("calc_deinit: not initialized");
+ return;
+ }
+
+ g_list_free_full (calc.sflare_list, (GDestroyNotify) g_free);
+
+ g_free (calc.glow_radial);
+ g_free (calc.glow_angular);
+ g_free (calc.glow_angular_size);
+ g_free (calc.rays_radial);
+ g_free (calc.rays_angular);
+ g_free (calc.rays_angular_size);
+ g_free (calc.sflare_radial);
+ g_free (calc.sflare_sizefac);
+ g_free (calc.sflare_probability);
+
+ calc.init = FALSE;
+}
+
+/*
+ * Get sample value at specified position of a gradient
+ *
+ * gradient samples are stored into array at the time of
+ * calc_sample_one_gradients (), and it is now linear interpolated.
+ *
+ * INPUT:
+ * guchar gradient[4*GRADIENT_RESOLUTION] gradient array(RGBA)
+ * gdouble pos position (0<=pos<=1)
+ * OUTPUT:
+ * guchar pix[4]
+ */
+static void
+calc_get_gradient (guchar *pix, guchar *gradient, gdouble pos)
+{
+ gint ipos;
+ gdouble frac;
+ gint i;
+
+ if (pos < 0 || pos > 1)
+ {
+ pix[0] = pix[1] = pix[2] = pix[3] = 0;
+ return;
+ }
+ pos *= GRADIENT_RESOLUTION - 1.0001;
+ ipos = (gint) pos; frac = pos - ipos;
+ gradient += ipos * 4;
+
+ for (i = 0; i < 4; i++)
+ {
+ pix[i] = gradient[i] * (1 - frac) + gradient[i+4] * frac;
+ }
+}
+
+/* I need fmod to return always positive value */
+static gdouble
+fmod_positive (gdouble x, gdouble m)
+{
+ return x - floor (x/m) * m;
+}
+
+/*
+ * Calc glow's pixel (RGBA) value
+ * INPUT:
+ * gdouble x, y image coordinates
+ * OUTPUT:
+ * guchar pix[4]
+ */
+static void
+calc_glow_pix (guchar *dest_pix, gdouble x, gdouble y)
+{
+ gdouble radius, angle;
+ gdouble angular_size;
+ guchar radial_pix[4], angular_pix[4], size_pix[4];
+ gint i;
+
+ if ((calc.type & CALC_GLOW) == 0
+ || x < calc.glow_bounds.x0 || x > calc.glow_bounds.x1
+ || y < calc.glow_bounds.y0 || y > calc.glow_bounds.y1)
+ {
+ memset (dest_pix, 0, 4);
+ return;
+ }
+
+ x -= calc.xcenter;
+ y -= calc.ycenter;
+ radius = sqrt (x*x + y*y) / calc.glow_radius;
+ angle = (atan2 (-y, x) + calc.glow_rotation ) / (2 * G_PI);
+ angle = fmod_positive (angle, 1.0);
+
+ calc_get_gradient (size_pix, calc.glow_angular_size, angle);
+ /* angular_size gradient was grayfied already */
+ angular_size = size_pix[0] / 255.0;
+ radius /= (angular_size+0.0001); /* in case angular_size == 0.0 */
+ if (radius < 0 || radius > 1)
+ {
+ memset (dest_pix, 0, 4);
+ return;
+ }
+
+ calc_get_gradient (radial_pix, calc.glow_radial, radius);
+ calc_get_gradient (angular_pix, calc.glow_angular, angle);
+
+ for (i = 0; i < 4; i++)
+ dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
+}
+
+/*
+ * Calc rays's pixel (RGBA) value
+ *
+ */
+static void
+calc_rays_pix (guchar *dest_pix, gdouble x, gdouble y)
+{
+ gdouble radius, angle;
+ gdouble angular_size;
+ gdouble spike_frac, spike_inten, spike_angle;
+ guchar radial_pix[4], angular_pix[4], size_pix[4];
+ gint i;
+
+ if ((calc.type & CALC_RAYS) == 0
+ || x < calc.rays_bounds.x0 || x > calc.rays_bounds.x1
+ || y < calc.rays_bounds.y0 || y > calc.rays_bounds.y1)
+ {
+ memset (dest_pix, 0, 4);
+ return;
+ }
+
+ x -= calc.xcenter;
+ y -= calc.ycenter;
+ radius = sqrt (x*x + y*y) / calc.rays_radius;
+ angle = (atan2 (-y, x) + calc.rays_rotation ) / (2 * G_PI);
+ angle = fmod_positive (angle, 1.0); /* make sure 0 <= angle < 1.0 */
+ spike_frac = fmod (angle, calc.rays_spike_mod * 2);
+ spike_angle = angle - spike_frac + calc.rays_spike_mod;
+ spike_frac = (angle - spike_angle) / calc.rays_spike_mod;
+ /* spike_frac is between -1.0 and 1.0 here (except round error...) */
+
+ spike_inten = pow (1.0 - fabs (spike_frac), calc.rays_thinness);
+
+ calc_get_gradient (size_pix, calc.rays_angular_size, spike_angle);
+ /* angular_size gradient was grayfied already */
+ angular_size = size_pix[0] / 255.0;
+ radius /= (angular_size+0.0001); /* in case angular_size == 0.0 */
+ if(radius < 0 || radius > 1)
+ {
+ memset (dest_pix, 0, 4);
+ return;
+ }
+
+ calc_get_gradient (radial_pix, calc.rays_radial, radius);
+ calc_get_gradient (angular_pix, calc.rays_angular, spike_angle);
+
+ for (i = 0; i < 3; i++)
+ dest_pix[i] = radial_pix[i] * angular_pix[i] / 255;
+ dest_pix[3] = spike_inten * radial_pix[3] * angular_pix[3] / 255;
+}
+
+/*
+ * Calc sflare's pixel (RGBA) value
+ *
+ * the sflare (second flares) are needed to be rendered one each
+ * sequentially, onto the source image, such as like usual layer
+ * operations. So the function takes src_pix as argument. glow, rays
+ * routines don't have src_pix as argument, because of convenience.
+ *
+ * @JAPANESE
+ * sflare $B$OJ#?t$N%U%l%"$r=g$K(B($B%l%$%dE*$K(B)$B$+$V$;$J$,$iIA2h$9$kI,MW$,(B
+ * $B$"$k$N$G!"$3$l$@$1(B src_pix $B$r0z?t$K$H$C$F(B paint_func $B$rE,MQ$9$k!#(B
+ * glow, rays $B$O4J0W2=$N$?$a$K$J$7!#(B
+ */
+void
+calc_sflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
+{
+ GList *list;
+ CalcSFlare *sflare;
+ gdouble sx, sy, th;
+ gdouble radius, angle;
+ guchar radial_pix[4], tmp_pix[4];
+
+ memcpy (dest_pix, src_pix, 4);
+
+ if ((calc.type & CALC_SFLARE) == 0)
+ return;
+
+ list = calc.sflare_list;
+ while (list)
+ {
+ sflare = list->data;
+ list = list->next;
+
+ if (x < sflare->bounds.x0 || x > sflare->bounds.x1
+ || y < sflare->bounds.y0 || y > sflare->bounds.y1)
+ continue;
+ sx = x - sflare->xcenter;
+ sy = y - sflare->ycenter;
+ radius = sqrt (sx * sx + sy * sy) / sflare->radius;
+ if (calc.sflare_shape == GF_POLYGON)
+ {
+ angle = atan2 (-sy, sx) - calc.vangle + calc.sflare_rotation;
+ th = fmod_positive (angle, calc.sflare_angle * 2) - calc.sflare_angle;
+ radius *= cos (th) * calc.sflare_factor;
+ }
+ if (radius < 0 || radius > 1)
+ continue;
+
+ calc_get_gradient (radial_pix, calc.sflare_radial, radius);
+ memcpy (tmp_pix, dest_pix, 4);
+ calc_paint_func (dest_pix, tmp_pix, radial_pix,
+ calc.sflare_opacity, calc.gflare->sflare_mode);
+ }
+}
+
+static void
+calc_gflare_pix (guchar *dest_pix, gdouble x, gdouble y, guchar *src_pix)
+{
+ GFlare *gflare = calc.gflare;
+ guchar glow_pix[4], rays_pix[4];
+ guchar tmp_pix[4];
+
+ memcpy (dest_pix, src_pix, 4);
+
+ if (calc.type & CALC_GLOW)
+ {
+ memcpy (tmp_pix, dest_pix, 4);
+ calc_glow_pix (glow_pix, x, y);
+ calc_paint_func (dest_pix, tmp_pix, glow_pix,
+ calc.glow_opacity, gflare->glow_mode);
+ }
+ if (calc.type & CALC_RAYS)
+ {
+ memcpy (tmp_pix, dest_pix, 4);
+ calc_rays_pix (rays_pix, x, y);
+ calc_paint_func (dest_pix, tmp_pix, rays_pix,
+ calc.rays_opacity, gflare->rays_mode);
+ }
+ if (calc.type & CALC_SFLARE)
+ {
+ memcpy (tmp_pix, dest_pix, 4);
+ calc_sflare_pix (dest_pix, x, y, tmp_pix);
+ }
+}
+
+/*
+ Paint func routines, such as Normal, Addition, ...
+ */
+static void
+calc_paint_func (guchar *dest, guchar *src1, guchar *src2, gint opacity,
+ GFlareMode mode)
+{
+ guchar buf[4], *s=buf;
+
+ if (src2[3] == 0 || opacity <= 0)
+ {
+ memcpy (dest, src1, 4);
+ return;
+ }
+
+ switch (mode)
+ {
+ case GF_NORMAL:
+ s = src2;
+ break;
+ case GF_ADDITION:
+ calc_addition (s, src1, src2);
+ break;
+ case GF_OVERLAY:
+ calc_overlay (s, src1, src2);
+ break;
+ case GF_SCREEN:
+ calc_screen (s, src1, src2);
+ break;
+ default:
+ s = src2;
+ break;
+ }
+ calc_combine (dest, src1, s, opacity);
+}
+
+static void
+calc_combine (guchar *dest, guchar *src1, guchar *src2, gint opacity)
+{
+ gdouble s1_a, s2_a, new_a;
+ gdouble ratio, compl_ratio;
+ gint i;
+
+ s1_a = src1[3] / 255.0;
+ s2_a = src2[3] * opacity / 65025.0;
+ new_a = s1_a + (1.0 - s1_a) * s2_a;
+
+ if (new_a != 0.0)
+ ratio = s2_a / new_a;
+ else
+ ratio = 0.0;
+
+ compl_ratio = 1.0 - ratio;
+
+ for (i = 0; i < 3; i++)
+ dest[i] = src1[i] * compl_ratio + src2[i] * ratio;
+
+ dest[3] = new_a * 255.0;
+}
+
+static void
+calc_addition (guchar *dest, guchar *src1, guchar *src2)
+{
+ gint tmp, i;
+
+ for (i = 0; i < 3; i++)
+ {
+ tmp = src1[i] + src2[i];
+ dest[i] = tmp <= 255 ? tmp: 255;
+ }
+ dest[3] = MIN (src1[3], src2[3]);
+}
+
+static void
+calc_screen (guchar *dest, guchar *src1, guchar *src2)
+{
+ gint i;
+
+ for (i = 0; i < 3; i++)
+ {
+ dest[i] = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
+ }
+ dest[3] = MIN (src1[3], src2[3]);
+}
+
+static void
+calc_overlay (guchar *dest, guchar *src1, guchar *src2)
+{
+ gint screen, mult, i;
+
+ for (i = 0; i < 3; i++)
+ {
+ screen = 255 - ((255 - src1[i]) * (255 - src2[i])) / 255;
+ mult = (src1[i] * src2[i]) / 255;
+ dest[i] = (screen * src1[i] + mult * (255 - src1[i])) / 255;
+ }
+ dest[3] = MIN (src1[3], src2[3]);
+}
+
+/*************************************************************************/
+/** **/
+/** Main Dialog **/
+/** +++ dlg **/
+/** **/
+/*************************************************************************/
+
+/*
+ This is gflare main dialog, one which opens in first.
+ */
+
+static gboolean
+dlg_run (void)
+{
+ GeglBuffer *src_buffer;
+ GtkWidget *shell;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *abox;
+ GtkWidget *button;
+ GtkWidget *notebook;
+ gboolean run = FALSE;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ /*
+ * Init Main Dialog
+ */
+
+ dlg = g_new0 (GFlareDialog, 1);
+ dlg->init = TRUE;
+ dlg->update_preview = TRUE;
+
+ gradient_menu_init (); /* FIXME: this should go elsewhere */
+ dlg_setup_gflare ();
+
+ g_assert (gflares_list != NULL);
+ g_assert (dlg->gflare != NULL);
+ g_assert (dlg->gflare->name != NULL);
+
+ /*
+ * Dialog Shell
+ */
+
+ shell = dlg->shell = gimp_dialog_new (_("Gradient Flare"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (shell),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ /*
+ * main hbox
+ */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell))),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /*
+ * Preview
+ */
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (vbox), abox, TRUE, TRUE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ src_buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ dlg->preview = preview_new (DLG_PREVIEW_WIDTH, DLG_PREVIEW_HEIGHT,
+ dlg_preview_init_func, NULL,
+ dlg_preview_render_func, src_buffer,
+ dlg_preview_deinit_func, NULL);
+ gtk_widget_set_events (GTK_WIDGET (dlg->preview->widget), DLG_PREVIEW_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), dlg->preview->widget);
+
+ g_signal_connect (dlg->preview->widget, "realize",
+ G_CALLBACK (dlg_preview_realize),
+ NULL);
+ g_signal_connect (dlg->preview->widget, "event",
+ G_CALLBACK (dlg_preview_handle_event),
+ NULL);
+
+ dlg_preview_calc_window ();
+
+ button = gtk_check_button_new_with_mnemonic (_("A_uto update preview"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ dlg->update_preview);
+ gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (dlg_update_preview_callback),
+ &dlg->update_preview);
+
+ /*
+ * Notebook
+ */
+
+ notebook = dlg->notebook = gtk_notebook_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
+ gtk_widget_show (notebook);
+
+ dlg_make_page_settings (dlg, notebook);
+ dlg_make_page_selector (dlg, notebook);
+
+ gtk_widget_show (shell);
+
+ /*
+ * Initialization done
+ */
+ dlg->init = FALSE;
+ dlg_preview_update ();
+
+ if (gimp_dialog_run (GIMP_DIALOG (shell)) == GTK_RESPONSE_OK)
+ {
+ gflare_name_copy (pvals.gflare_name, dlg->gflare->name);
+
+ run = TRUE;
+ }
+
+ g_object_unref (src_buffer);
+
+ gtk_widget_destroy (shell);
+
+ return run;
+}
+
+static void
+dlg_setup_gflare (void)
+{
+ dlg->gflare = gflares_list_lookup (pvals.gflare_name);
+
+ if (!dlg->gflare)
+ {
+ dlg->gflare = gflares_list_lookup ("Default");
+ if (!dlg->gflare)
+ {
+ g_warning (_("'Default' is created."));
+ dlg->gflare = gflare_new_with_default (_("Default"));
+ gflares_list_insert (dlg->gflare);
+ }
+ }
+}
+
+/***********************************/
+/** Main Dialog / Preview **/
+/***********************************/
+
+/*
+ * Calculate preview's window, ie. translation of preview widget and
+ * drawable.
+ *
+ * x0, x1, y0, y1 are drawable coord, corresponding with top left
+ * corner of preview widget, etc.
+ */
+void
+dlg_preview_calc_window (void)
+{
+ gint width = gimp_drawable_width (drawable_ID);
+ gint height = gimp_drawable_height (drawable_ID);
+ gint is_wide;
+ gdouble offx, offy;
+
+ is_wide = ((double) DLG_PREVIEW_HEIGHT * width >=
+ (double) DLG_PREVIEW_WIDTH * height);
+
+ if (is_wide)
+ {
+ offy = ((double) width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
+
+ dlg->pwin.x0 = 0;
+ dlg->pwin.x1 = width;
+ dlg->pwin.y0 = height / 2.0 - offy;
+ dlg->pwin.y1 = height / 2.0 + offy;
+ }
+ else
+ {
+ offx = ((double) height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
+
+ dlg->pwin.x0 = width / 2.0 - offx;
+ dlg->pwin.x1 = width / 2.0 + offx;
+ dlg->pwin.y0 = 0;
+ dlg->pwin.y1 = height;
+ }
+}
+
+void
+ed_preview_calc_window (void)
+{
+ gint width = gimp_drawable_width (drawable_ID);
+ gint height = gimp_drawable_height (drawable_ID);
+ gint is_wide;
+ gdouble offx, offy;
+
+ is_wide = ((double) DLG_PREVIEW_HEIGHT * width >=
+ (double) DLG_PREVIEW_WIDTH * height);
+
+ if (is_wide)
+ {
+ offy = ((double) width * DLG_PREVIEW_HEIGHT / DLG_PREVIEW_WIDTH) / 2.0;
+
+ dlg->pwin.x0 = 0;
+ dlg->pwin.x1 = width;
+ dlg->pwin.y0 = height / 2.0 - offy;
+ dlg->pwin.y1 = height / 2.0 + offy;
+ }
+ else
+ {
+ offx = ((double) height * DLG_PREVIEW_WIDTH / DLG_PREVIEW_HEIGHT) / 2.0;
+
+ dlg->pwin.x0 = width / 2.0 - offx; dlg->pwin.x1 = width / 2.0 + offx;
+ dlg->pwin.y0 = 0;
+ dlg->pwin.y1 = height;
+ }
+}
+
+static void
+dlg_preview_realize (GtkWidget *widget)
+{
+ GdkDisplay *display = gtk_widget_get_display (widget);
+ GdkCursor *cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
+
+ gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+ gdk_cursor_unref (cursor);
+}
+
+static gboolean
+dlg_preview_handle_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkEventButton *bevent;
+ gint bx, by, x, y;
+
+ switch (event->type)
+ {
+ case GDK_BUTTON_PRESS:
+ bevent = (GdkEventButton *) event;
+ bx = bevent->x;
+ by = bevent->y;
+
+ /* convert widget coord to drawable coord */
+ x = dlg->pwin.x0 + (double) (dlg->pwin.x1 - dlg->pwin.x0)
+ * bx / DLG_PREVIEW_WIDTH;
+ y = dlg->pwin.y0 + (double) (dlg->pwin.y1 - dlg->pwin.y0)
+ * by / DLG_PREVIEW_HEIGHT;
+
+ if ((x != pvals.xcenter || y != pvals.ycenter))
+ {
+ if (x != pvals.xcenter)
+ {
+ pvals.xcenter = x;
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
+ 0, x);
+ }
+ if (y != pvals.ycenter)
+ {
+ pvals.ycenter = y;
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (dlg->sizeentry),
+ 1, y);
+ }
+ dlg_preview_update ();
+ }
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static void
+dlg_preview_update (void)
+{
+ if (!dlg->init && dlg->update_preview)
+ {
+ dlg->init_params_done = FALSE;
+ preview_render_start (dlg->preview);
+ }
+}
+
+/* preview callbacks */
+static gint
+dlg_preview_init_func (Preview *preview, gpointer data)
+{
+ /* call init_params first, and iterate init_progress while
+ it returns true */
+ if (dlg->init_params_done == FALSE)
+ {
+ calc_init_params (dlg->gflare,
+ CALC_GLOW | CALC_RAYS | CALC_SFLARE,
+ pvals.xcenter, pvals.ycenter,
+ pvals.radius, pvals.rotation, pvals.hue,
+ pvals.vangle, pvals.vlength);
+ dlg->init_params_done = TRUE;
+ return TRUE;
+ }
+ return calc_init_progress ();
+}
+
+/* render preview
+ do what "preview" means, ie. render lense flare effect onto drawable */
+static void
+dlg_preview_render_func (Preview *preview,
+ guchar *dest,
+ gint y,
+ gpointer data)
+{
+ GeglBuffer *src_buffer = data;
+ gint width = gimp_drawable_width (drawable_ID);
+ gint height = gimp_drawable_height (drawable_ID);
+ gint x;
+ gint dx, dy; /* drawable x, y */
+ guchar *src_row, *src;
+ guchar src_pix[4], dest_pix[4];
+ gint b;
+
+ dy = (dlg->pwin.y0 +
+ (gdouble) (dlg->pwin.y1 - dlg->pwin.y0) * y / DLG_PREVIEW_HEIGHT);
+
+ if (dy < 0 || dy >= height)
+ {
+ memset (dest, GRAY50, 3 * DLG_PREVIEW_WIDTH);
+ return;
+ }
+
+ src_row = g_new (guchar, dinfo.bpp * width);
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, dy, width, 1), 1.0,
+ dinfo.format, src_row,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (x = 0; x < DLG_PREVIEW_HEIGHT; x++)
+ {
+ dx = (dlg->pwin.x0 +
+ (double) (dlg->pwin.x1 - dlg->pwin.x0) * x / DLG_PREVIEW_WIDTH);
+
+ if (dx < 0 || dx >= width)
+ {
+ for (b = 0; b < 3; b++)
+ *dest++ = GRAY50;
+ continue;
+ }
+
+ /* Get drawable pix value */
+ src = &src_row[dx * dinfo.bpp];
+
+ for (b = 0; b < 3; b++)
+ src_pix[b] = dinfo.is_color ? src[b] : src[0];
+ src_pix[3] = dinfo.has_alpha ? src[dinfo.bpp - 1] : OPAQUE;
+
+ /* Get GFlare pix value */
+
+ calc_gflare_pix (dest_pix, dx, dy, src_pix);
+
+ /* Draw gray check if needed */
+ preview_rgba_to_rgb (dest, x, y, dest_pix);
+ dest += 3;
+ }
+
+ g_free (src_row);
+}
+
+static void
+dlg_preview_deinit_func (Preview *preview, gpointer data)
+{
+ if (dlg->init_params_done)
+ {
+ calc_deinit ();
+ dlg->init_params_done = TRUE;
+ }
+}
+
+/*****************************************/
+/** Main Dialog / Settings Page **/
+/*****************************************/
+
+static void
+dlg_make_page_settings (GFlareDialog *dlg,
+ GtkWidget *notebook)
+{
+ GtkWidget *main_vbox;
+ GtkWidget *frame;
+ GtkWidget *center;
+ GtkWidget *chain;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *asup_table;
+ GtkObject *adj;
+ gdouble xres, yres;
+ gint row;
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+
+ frame = gimp_frame_new (_("Center"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gimp_image_get_resolution (image_ID, &xres, &yres);
+
+ center = dlg->sizeentry =
+ gimp_coordinates_new (gimp_image_get_unit (image_ID), "%a",
+ TRUE, TRUE, 75, GIMP_SIZE_ENTRY_UPDATE_SIZE,
+
+ FALSE, FALSE,
+
+ _("_X:"), pvals.xcenter, xres,
+ -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
+ 0, gimp_drawable_width (drawable_ID),
+
+ _("_Y:"), pvals.ycenter, yres,
+ -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE,
+ 0, gimp_drawable_height (drawable_ID));
+
+ chain = GTK_WIDGET (GIMP_COORDINATES_CHAINBUTTON (center));
+
+ gtk_container_add (GTK_CONTAINER (frame), center);
+ g_signal_connect (center, "value-changed",
+ G_CALLBACK (dlg_position_entry_callback),
+ NULL);
+ g_signal_connect (center, "refval-changed",
+ G_CALLBACK (dlg_position_entry_callback),
+ NULL);
+ gtk_widget_hide (chain);
+ gtk_widget_show (center);
+
+ frame = gimp_frame_new (_("Parameters"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (5, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Radius:"), SCALE_WIDTH, 6,
+ pvals.radius, 0.0,
+ gimp_drawable_width (drawable_ID) / 2,
+ 1.0, 10.0, 1,
+ FALSE, 0.0, GIMP_MAX_IMAGE_SIZE,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.radius);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dlg_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Ro_tation:"), SCALE_WIDTH, 6,
+ pvals.rotation, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.rotation);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dlg_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("_Hue rotation:"), SCALE_WIDTH, 6,
+ pvals.hue, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.hue);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dlg_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Vector _angle:"), SCALE_WIDTH, 6,
+ pvals.vangle, 0.0, 359.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.vangle);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dlg_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Vector _length:"), SCALE_WIDTH, 6,
+ pvals.vlength, 1, 1000, 1.0, 10.0, 1,
+ FALSE, 1, GIMP_MAX_IMAGE_SIZE,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.vlength);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (dlg_preview_update),
+ NULL);
+
+ /**
+ *** Asupsample settings
+ *** This code is stolen from gimp-0.99.x/app/blend.c
+ **/
+
+ /* asupsample frame */
+ frame = dlg->asupsample_frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ button = gtk_check_button_new_with_mnemonic (_("A_daptive supersampling"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ pvals.use_asupsample);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), button);
+ gtk_widget_show (button);
+
+ asup_table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (asup_table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (asup_table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), asup_table);
+ gtk_widget_show (asup_table);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &pvals.use_asupsample);
+
+ g_object_bind_property (button, "active",
+ asup_table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (asup_table), 0, 0,
+ _("_Max depth:"), -1, 4,
+ pvals.asupsample_max_depth,
+ 1.0, 10.0, 1.0, 1.0, 0,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &pvals.asupsample_max_depth);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (asup_table), 0, 1,
+ _("_Threshold"), -1, 4,
+ pvals.asupsample_threshold,
+ 0.0, 4.0, 0.01, 0.01, 2,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &pvals.asupsample_threshold);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), main_vbox,
+ gtk_label_new_with_mnemonic (_("_Settings")));
+ gtk_widget_show (main_vbox);
+}
+
+static void
+dlg_position_entry_callback (GtkWidget *widget, gpointer data)
+{
+ gint x, y;
+
+ x = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 0));
+ y = RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (widget), 1));
+
+ DEBUG_PRINT (("dlg_position_entry_callback\n"));
+
+ if (pvals.xcenter != x ||
+ pvals.ycenter != y)
+ {
+ pvals.xcenter = x;
+ pvals.ycenter = y;
+
+ dlg_preview_update ();
+ }
+}
+
+static void
+dlg_update_preview_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ dlg_preview_update ();
+}
+
+/*****************************************/
+/** Main Dialog / Selector Page **/
+/*****************************************/
+
+static void
+dlg_make_page_selector (GFlareDialog *dlg,
+ GtkWidget *notebook)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *listbox;
+ GtkListStore *list;
+ GtkWidget *listview;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkWidget *button;
+ gint i;
+
+ static struct
+ {
+ const gchar *label;
+ GCallback callback;
+ }
+ buttons[] =
+ {
+ { N_("_New"), G_CALLBACK (dlg_selector_new_callback) },
+ { N_("_Edit"), G_CALLBACK (dlg_selector_edit_callback) },
+ { N_("_Copy"), G_CALLBACK (dlg_selector_copy_callback) },
+ { N_("_Delete"), G_CALLBACK (dlg_selector_delete_callback) }
+ };
+
+ DEBUG_PRINT (("dlg_make_page_selector\n"));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /*
+ * List Box
+ */
+
+ listbox = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_set_size_request (listbox, DLG_LISTBOX_WIDTH, DLG_LISTBOX_HEIGHT);
+ gtk_box_pack_start (GTK_BOX (vbox), listbox, TRUE, TRUE, 0);
+
+ list = dlg->selector_list = gtk_list_store_new (2,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ listview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list));
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (listview), FALSE);
+
+ gtk_container_add (GTK_CONTAINER (listbox), listview);
+ gtk_widget_show (listbox);
+ dlg->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (listview));
+ gtk_tree_selection_set_mode (dlg->selection, GTK_SELECTION_BROWSE);
+ gtk_widget_show (listview);
+ g_signal_connect (dlg->selection, "changed",
+ G_CALLBACK (dlg_selector_list_item_callback),
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ /* Note: the title isn't shown, so it doesn't need to be translated. */
+ column = gtk_tree_view_column_new_with_attributes ("GFlare", renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (listview), column);
+
+ dlg_selector_setup_listbox ();
+
+ /*
+ * The buttons for the possible listbox operations
+ */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ for (i = 0; i < G_N_ELEMENTS (buttons); i++)
+ {
+ button = gtk_button_new_with_mnemonic (gettext (buttons[i].label));
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ buttons[i].callback,
+ button);
+ }
+
+ gtk_widget_show (vbox);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("S_elector")));
+ gtk_widget_show (vbox);
+}
+
+/*
+ * Set up selector's listbox, according to gflares_list
+ */
+static void
+dlg_selector_setup_listbox (void)
+{
+ GList *list;
+ GFlare *gflare;
+ gint n;
+
+ list = gflares_list;
+ n = 0;
+
+ while (list)
+ {
+ GtkTreeIter iter;
+
+ gflare = list->data;
+
+ /*
+ dlg->gflare should be valid (ie. not NULL) here.
+ */
+ gtk_list_store_append (dlg->selector_list, &iter);
+
+ gtk_list_store_set (dlg->selector_list, &iter,
+ 0, gflare->name,
+ 1, gflare,
+ -1);
+ if (gflare == dlg->gflare)
+ gtk_tree_selection_select_iter (dlg->selection,
+ &iter);
+
+ list = list->next;
+ n++;
+ }
+}
+
+static void
+dlg_selector_list_item_callback (GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (dlg->selector_list), &iter,
+ 1, &dlg->gflare, -1);
+ }
+
+ dlg_preview_update ();
+}
+
+/*
+ * "New" button in Selector page
+ */
+static void
+dlg_selector_new_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *query_box;
+
+ query_box = gimp_query_string_box (_("New Gradient Flare"),
+ gtk_widget_get_toplevel (widget),
+ gimp_standard_help_func, PLUG_IN_PROC,
+ _("Enter a name for the new GFlare"),
+ _("Unnamed"),
+ NULL, NULL,
+ dlg_selector_new_ok_callback, dlg);
+ gtk_widget_show (query_box);
+}
+
+static void
+dlg_selector_new_ok_callback (GtkWidget *widget,
+ const gchar *new_name,
+ gpointer data)
+{
+ GFlare *gflare;
+ GtkTreeIter iter;
+ gint pos;
+
+ g_return_if_fail (new_name != NULL);
+
+ if (gflares_list_lookup (new_name))
+ {
+ g_message (_("The name '%s' is used already!"), new_name);
+ return;
+ }
+
+ gflare = gflare_new_with_default (new_name);
+
+ pos = gflares_list_insert (gflare);
+
+ gtk_list_store_insert (dlg->selector_list, &iter, pos);
+ gtk_list_store_set (dlg->selector_list, &iter,
+ 0, gflare->name,
+ 1, gflare,
+ -1);
+ gtk_tree_selection_select_iter (dlg->selection, &iter);
+
+ dlg->gflare = gflare;
+ dlg_preview_update ();
+}
+
+/*
+ * "Edit" button in Selector page
+ */
+static void
+dlg_selector_edit_callback (GtkWidget *widget,
+ gpointer data)
+{
+ preview_render_end (dlg->preview);
+ gtk_widget_set_sensitive (dlg->shell, FALSE);
+ ed_run (GTK_WINDOW (dlg->shell),
+ dlg->gflare, dlg_selector_edit_done_callback, NULL);
+}
+
+static void
+dlg_selector_edit_done_callback (gint updated,
+ gpointer data)
+{
+ gtk_widget_set_sensitive (dlg->shell, TRUE);
+ if (updated)
+ {
+ gflare_save (dlg->gflare);
+ }
+ dlg_preview_update ();
+}
+
+/*
+ * "Copy" button in Selector page
+ */
+static void
+dlg_selector_copy_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *query_box;
+ gchar *name;
+
+ name = g_strdup_printf ("%s copy", dlg->gflare->name);
+
+ query_box = gimp_query_string_box (_("Copy Gradient Flare"),
+ gtk_widget_get_toplevel (widget),
+ gimp_standard_help_func, PLUG_IN_PROC,
+ _("Enter a name for the copied GFlare"),
+ name,
+ NULL, NULL,
+ dlg_selector_copy_ok_callback, dlg);
+ g_free (name);
+
+ gtk_widget_show (query_box);
+}
+
+static void
+dlg_selector_copy_ok_callback (GtkWidget *widget,
+ const gchar *copy_name,
+ gpointer data)
+{
+ GFlare *gflare;
+ GtkTreeIter iter;
+ gint pos;
+
+ g_return_if_fail (copy_name != NULL);
+
+ if (gflares_list_lookup (copy_name))
+ {
+ g_warning (_("The name '%s' is used already!"), copy_name);
+ return;
+ }
+
+ gflare = gflare_dup (dlg->gflare, copy_name);
+
+ pos = gflares_list_insert (gflare);
+ gtk_list_store_insert (dlg->selector_list, &iter, pos);
+ gtk_list_store_set (dlg->selector_list, &iter,
+ 0, gflare->name,
+ 1, gflare,
+ -1);
+ gtk_tree_selection_select_iter (dlg->selection, &iter);
+
+ dlg->gflare = gflare;
+ gflare_save (dlg->gflare);
+ dlg_preview_update ();
+}
+
+/*
+ * "Delete" button in Selector page
+ */
+static void
+dlg_selector_delete_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GtkWidget *dialog;
+ gchar *str;
+
+ if (num_gflares <= 1)
+ {
+ g_message (_("Cannot delete!! There must be at least one GFlare."));
+ return;
+ }
+
+ gtk_widget_set_sensitive (dlg->shell, FALSE);
+
+ str = g_strdup_printf (_("Are you sure you want to delete "
+ "\"%s\" from the list and from disk?"),
+ dlg->gflare->name);
+
+ dialog = gimp_query_boolean_box (_("Delete Gradient Flare"),
+ dlg->shell,
+ gimp_standard_help_func, PLUG_IN_PROC,
+ GIMP_ICON_DIALOG_QUESTION,
+ str,
+ _("_Delete"), _("_Cancel"),
+ NULL, NULL,
+ dlg_selector_do_delete_callback,
+ NULL);
+
+ g_free (str);
+
+ gtk_widget_show (dialog);
+}
+
+static void
+dlg_selector_do_delete_callback (GtkWidget *widget,
+ gboolean delete,
+ gpointer data)
+{
+ GFlare *old_gflare;
+ GList *tmp;
+ gint i, new_i;
+ GtkTreeIter iter;
+
+ gtk_widget_set_sensitive (dlg->shell, TRUE);
+
+ if (!delete)
+ return;
+
+ i = gflares_list_index (dlg->gflare);
+
+ if (i >= 0)
+ {
+ /* Remove current gflare from gflares_list and free it */
+ old_gflare = dlg->gflare;
+ gflares_list_remove (dlg->gflare);
+ dlg->gflare = NULL;
+
+ /* Remove from listbox */
+ if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (dlg->selector_list),
+ &iter, NULL, i))
+ {
+ g_warning ("Unsynchronized lists. Bad things will happen!");
+ return;
+ }
+ gtk_list_store_remove (dlg->selector_list, &iter);
+
+ /* Calculate new position of gflare and select it */
+ new_i = (i < num_gflares) ? i : num_gflares - 1;
+ if ((tmp = g_list_nth (gflares_list, new_i)))
+ dlg->gflare = tmp->data;
+ if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (dlg->selector_list),
+ &iter, NULL, i))
+ {
+ g_warning ("Unsynchronized lists. Bad things will happen!");
+ return;
+ }
+ gtk_tree_selection_select_iter (dlg->selection,
+ &iter);
+
+ /* Delete old one from disk and memory */
+ if (old_gflare->filename)
+ g_unlink (old_gflare->filename);
+
+ gflare_free (old_gflare);
+
+ /* Update */
+ dlg_preview_update ();
+ }
+ else
+ {
+ g_warning (_("not found %s in gflares_list"), dlg->gflare->name);
+ }
+}
+
+/*************************************************************************/
+/** **/
+/** GFlare Editor **/
+/** +++ ed **/
+/** **/
+/*************************************************************************/
+
+/*
+ This is gflare editor dialog, one which opens by clicking
+ "Edit" button on the selector page in the main dialog.
+ */
+
+static void
+ed_run (GtkWindow *parent,
+ GFlare *target_gflare,
+ GFlareEditorCallback callback,
+ gpointer calldata)
+{
+ GtkWidget *shell;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *abox;
+ GtkWidget *notebook;
+
+ if (!ed)
+ ed = g_new0 (GFlareEditor, 1);
+ ed->init = TRUE;
+ ed->run = FALSE;
+ ed->target_gflare = target_gflare;
+ ed->gflare = gflare_dup (target_gflare, target_gflare->name);
+ ed->callback = callback;
+ ed->calldata = calldata;
+
+ /*
+ * Dialog Shell
+ */
+ ed->shell =
+ shell = gimp_dialog_new (_("Gradient Flare Editor"), PLUG_IN_ROLE,
+ GTK_WIDGET (parent), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Rescan Gradients"), RESPONSE_RESCAN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (shell),
+ RESPONSE_RESCAN,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (shell, "response",
+ G_CALLBACK (ed_response),
+ ed);
+ g_signal_connect (shell, "destroy",
+ G_CALLBACK (ed_destroy_callback),
+ ed);
+
+ /*
+ * main hbox
+ */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (shell))),
+ hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /*
+ * Preview
+ */
+
+ abox = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), abox, FALSE, FALSE, 0);
+ gtk_widget_show (abox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (abox), frame);
+ gtk_widget_show (frame);
+
+ ed->preview = preview_new (ED_PREVIEW_WIDTH, ED_PREVIEW_HEIGHT,
+ ed_preview_init_func, NULL,
+ ed_preview_render_func, NULL,
+ ed_preview_deinit_func, NULL);
+ gtk_widget_set_events (GTK_WIDGET (ed->preview->widget), DLG_PREVIEW_MASK);
+ gtk_container_add (GTK_CONTAINER (frame), ed->preview->widget);
+ g_signal_connect (ed->preview->widget, "event",
+ G_CALLBACK (dlg_preview_handle_event),
+ NULL);
+ ed_preview_calc_window ();
+
+ /*
+ * Notebook
+ */
+ notebook = ed->notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (hbox), notebook, TRUE, TRUE, 0);
+ gtk_widget_show (notebook);
+
+ ed_make_page_general (ed, notebook);
+ ed_make_page_glow (ed, notebook);
+ ed_make_page_rays (ed, notebook);
+ ed_make_page_sflare (ed, notebook);
+
+ gtk_widget_show (shell);
+
+ ed->init = FALSE;
+ ed_preview_update ();
+}
+
+static void
+ed_destroy_callback (GtkWidget *widget,
+ GFlareEditor *ed)
+{
+ preview_free (ed->preview);
+ gflare_free (ed->gflare);
+ if (ed->callback)
+ (*ed->callback) (ed->run, ed->calldata);
+}
+
+static void
+ed_response (GtkWidget *widget,
+ gint response_id,
+ GFlareEditor *ed)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESCAN:
+ ed->init = TRUE;
+ gradient_menu_rescan ();
+ ed->init = FALSE;
+ ed_preview_update ();
+ gtk_widget_queue_draw (ed->notebook);
+ break;
+
+ case GTK_RESPONSE_OK:
+ ed->run = TRUE;
+ gflare_copy (ed->target_gflare, ed->gflare);
+ gtk_widget_destroy (ed->shell);
+ break;
+
+ default:
+ gtk_widget_destroy (ed->shell);
+ break;
+ }
+}
+
+static void
+ed_make_page_general (GFlareEditor *ed,
+ GtkWidget *notebook)
+{
+ GFlare *gflare = ed->gflare;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkObject *adj;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /* Glow */
+
+ frame = gimp_frame_new (_("Glow Paint Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Opacity:"), SCALE_WIDTH, 6,
+ gflare->glow_opacity, 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->glow_opacity);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ combo = ed_mode_menu_new (&gflare->glow_mode);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Paint mode:"), 0.0, 0.5, combo, 1, FALSE);
+
+ /* Rays */
+
+ frame = gimp_frame_new (_("Rays Paint Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Opacity:"), SCALE_WIDTH, 6,
+ gflare->rays_opacity, 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->rays_opacity);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ combo = ed_mode_menu_new (&gflare->rays_mode);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Paint mode:"), 0.0, 0.5, combo, 1, FALSE);
+
+ /* Rays */
+
+ frame = gimp_frame_new (_("Second Flares Paint Options"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Opacity:"), SCALE_WIDTH, 6,
+ gflare->sflare_opacity, 0.0, 100.0, 1.0, 10.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->sflare_opacity);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ combo = ed_mode_menu_new (&gflare->sflare_mode);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Paint mode:"), 0.0, 0.5, combo, 1, FALSE);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("_General")));
+ g_signal_connect (vbox, "map",
+ G_CALLBACK (ed_page_map_callback),
+ (gpointer) PAGE_GENERAL);
+ gtk_widget_show (vbox);
+}
+
+static void
+ed_make_page_glow (GFlareEditor *ed,
+ GtkWidget *notebook)
+{
+ GFlare *gflare = ed->gflare;
+ GradientMenu *gm;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkObject *adj;
+ gint row;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /*
+ * Gradient Menus
+ */
+
+ frame = gimp_frame_new (_("Gradients"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->glow_radial, gflare->glow_radial);
+ ed_put_gradient_menu (table, 0, 0, _("Radial gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->glow_angular, gflare->glow_angular);
+ ed_put_gradient_menu (table, 0, 1, _("Angular gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->glow_angular_size, gflare->glow_angular_size);
+ ed_put_gradient_menu (table, 0, 2, _("Angular size gradient:"), gm);
+
+ gtk_widget_show (table);
+
+ /*
+ * Scales
+ */
+
+ frame = gimp_frame_new (_("Parameters"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Size (%):"), SCALE_WIDTH, 6,
+ gflare->glow_size, 0.0, 200.0, 1.0, 10.0, 1,
+ FALSE, 0, G_MAXINT,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->glow_size);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Rotation:"), SCALE_WIDTH, 6,
+ gflare->glow_rotation, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->glow_rotation);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hue rotation:"), SCALE_WIDTH, 6,
+ gflare->glow_hue, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->glow_hue);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ gtk_widget_show (table);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("G_low")));
+ g_signal_connect (vbox, "map",
+ G_CALLBACK (ed_page_map_callback),
+ (gpointer) PAGE_GLOW);
+ gtk_widget_show (vbox);
+}
+
+static void
+ed_make_page_rays (GFlareEditor *ed,
+ GtkWidget *notebook)
+{
+ GFlare *gflare = ed->gflare;
+ GradientMenu *gm;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkObject *adj;
+ gint row;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /*
+ * Gradient Menus
+ */
+
+ frame = gimp_frame_new (_("Gradients"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ row = 0;
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->rays_radial, gflare->rays_radial);
+ ed_put_gradient_menu (table, 0, row++, _("Radial gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->rays_angular, gflare->rays_angular);
+ ed_put_gradient_menu (table, 0, row++, _("Angular gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->rays_angular_size, gflare->rays_angular_size);
+ ed_put_gradient_menu (table, 0, row++, _("Angular size gradient:"), gm);
+
+ gtk_widget_show (table);
+
+ /*
+ * Scales
+ */
+
+ frame = gimp_frame_new (_("Parameters"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (5, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Size (%):"), SCALE_WIDTH, 6,
+ gflare->rays_size, 0.0, 200.0, 1.0, 10.0, 1,
+ FALSE, 0, G_MAXINT,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->rays_size);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Rotation:"), SCALE_WIDTH, 6,
+ gflare->rays_rotation,
+ -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->rays_rotation);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hue rotation:"), SCALE_WIDTH, 6,
+ gflare->rays_hue, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->rays_hue);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("# of Spikes:"), SCALE_WIDTH, 6,
+ gflare->rays_nspikes, 1, 300, 1.0, 10.0, 0,
+ FALSE, 0, G_MAXINT,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &gflare->rays_nspikes);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Spike thickness:"), SCALE_WIDTH, 6,
+ gflare->rays_thickness, 1.0, 100.0, 1.0, 10.0, 1,
+ FALSE, 0, GIMP_MAX_IMAGE_SIZE,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->rays_thickness);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ gtk_widget_show (table);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("_Rays")));
+ g_signal_connect (vbox, "map",
+ G_CALLBACK (ed_page_map_callback),
+ (gpointer) PAGE_RAYS);
+ gtk_widget_show (vbox);
+}
+
+static void
+ed_make_page_sflare (GFlareEditor *ed,
+ GtkWidget *notebook)
+{
+ GFlare *gflare = ed->gflare;
+ GradientMenu *gm;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *shape_vbox;
+ GSList *shape_group = NULL;
+ GtkWidget *polygon_hbox;
+ GtkWidget *seed_hbox;
+ GtkWidget *toggle;
+ GtkWidget *label;
+ GtkWidget *seed;
+ GtkWidget *entry;
+ GtkObject *adj;
+ gchar buf[256];
+ gint row;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ /*
+ * Gradient Menus
+ */
+
+ frame = gimp_frame_new (_("Gradients"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->sflare_radial, gflare->sflare_radial);
+ ed_put_gradient_menu (table, 0, 0, _("Radial gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->sflare_sizefac, gflare->sflare_sizefac);
+ ed_put_gradient_menu (table, 0, 1, _("Size factor gradient:"), gm);
+
+ gm = gradient_menu_new ((GradientMenuCallback) &ed_gradient_menu_callback,
+ gflare->sflare_probability, gflare->sflare_probability);
+ ed_put_gradient_menu (table, 0, 2, _("Probability gradient:"), gm);
+
+ gtk_widget_show (table);
+
+ /*
+ * Scales
+ */
+
+ frame = gimp_frame_new (_("Parameters"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Size (%):"), SCALE_WIDTH, 6,
+ gflare->sflare_size, 0.0, 200.0, 1.0, 10.0, 1,
+ FALSE, 0, G_MAXINT,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->sflare_size);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Rotation:"), SCALE_WIDTH, 6,
+ gflare->sflare_rotation,
+ -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->sflare_rotation);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Hue rotation:"), SCALE_WIDTH, 6,
+ gflare->sflare_hue, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &gflare->sflare_hue);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ gtk_widget_show (table);
+
+ /*
+ * Shape Radio Button Frame
+ */
+
+ frame = gimp_frame_new (_("Shape of Second Flares"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ shape_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), shape_vbox);
+ gtk_widget_show (shape_vbox);
+
+ toggle = gtk_radio_button_new_with_label (shape_group, _("Circle"));
+ shape_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (GF_CIRCLE));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (ed_shape_radio_callback),
+ &gflare->sflare_shape);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gflare->sflare_shape == GF_CIRCLE);
+ gtk_box_pack_start (GTK_BOX (shape_vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ polygon_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (shape_vbox), polygon_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (polygon_hbox);
+
+ toggle = ed->polygon_toggle =
+ gtk_radio_button_new_with_label (shape_group, _("Polygon"));
+ shape_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
+ g_object_set_data (G_OBJECT (toggle), "gimp-item-data",
+ GINT_TO_POINTER (GF_POLYGON));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (ed_shape_radio_callback),
+ &gflare->sflare_shape);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gflare->sflare_shape == GF_POLYGON);
+ gtk_box_pack_start (GTK_BOX (polygon_hbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ entry = ed->polygon_entry = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (entry), 4);
+ g_snprintf (buf, sizeof (buf), "%d", gflare->sflare_nverts);
+ gtk_entry_set_text (GTK_ENTRY (entry), buf);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (ed_ientry_callback),
+ &gflare->sflare_nverts);
+ gtk_box_pack_start (GTK_BOX (polygon_hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show (entry);
+
+ g_object_bind_property (toggle, "active",
+ entry, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ /*
+ * Random Seed Entry
+ */
+
+ seed_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), seed_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (seed_hbox);
+
+ label = gtk_label_new (_("Random seed:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (seed_hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ seed = gimp_random_seed_new (&gflare->sflare_seed, &gflare->random_seed);
+ gtk_box_pack_start (GTK_BOX (seed_hbox), seed, FALSE, TRUE, 0);
+ gtk_widget_show (seed);
+
+ g_signal_connect (GIMP_RANDOM_SEED_SPINBUTTON_ADJ (seed), "value-changed",
+ G_CALLBACK (ed_preview_update),
+ NULL);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox,
+ gtk_label_new_with_mnemonic (_("_Second Flares")));
+ g_signal_connect (vbox, "map",
+ G_CALLBACK (ed_page_map_callback),
+ (gpointer) PAGE_SFLARE);
+ gtk_widget_show (vbox);
+}
+
+GtkWidget *
+ed_mode_menu_new (GFlareMode *mode_var)
+{
+ GtkWidget *combo = gimp_int_combo_box_new_array (GF_NUM_MODES,
+ gflare_menu_modes);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), *mode_var,
+ G_CALLBACK (ed_mode_menu_callback),
+ mode_var);
+
+ return combo;
+}
+
+/*
+ puts gradient menu with caption into table
+ occupies 1 row and 3 cols in table
+ */
+static void
+ed_put_gradient_menu (GtkWidget *table,
+ gint x,
+ gint y,
+ const gchar *caption,
+ GradientMenu *gm)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new (caption);
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_widget_show (label);
+
+ gtk_table_attach (GTK_TABLE (table), label,
+ x , x + 1, y, y + 1,
+ GTK_FILL, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), gm->preview,
+ x + 1, x + 2, y, y + 1,
+ 0, 0, 0, 0);
+ gtk_table_attach (GTK_TABLE (table), gm->combo,
+ x + 2, x + 3, y, y + 1,
+ 0, 0, 0, 0);
+}
+
+static void
+ed_mode_menu_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
+
+ ed_preview_update ();
+}
+
+static void
+ed_gradient_menu_callback (const gchar *gradient_name,
+ gpointer data)
+{
+ gchar *dest_string = data;
+
+ /* @GRADIENT_NAME */
+ gradient_name_copy (dest_string, gradient_name);
+ ed_preview_update ();
+}
+
+static void
+ed_shape_radio_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_radio_button_update (widget, data);
+
+ ed_preview_update ();
+}
+
+static void
+ed_ientry_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint new_val;
+
+ new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
+ *(gint *)data = new_val;
+
+ ed_preview_update ();
+}
+
+/*
+ NOTE: This is hack, because this code depends on internal "map"
+ signal of changing pages of gtknotebook.
+ */
+static void
+ed_page_map_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint page_num = GPOINTER_TO_INT (data);
+
+ DEBUG_PRINT(("ed_page_map_callback\n"));
+
+ ed->cur_page = page_num;
+ ed_preview_update ();
+}
+
+
+static void
+ed_preview_update (void)
+{
+ if (ed->init)
+ return;
+
+ ed->init_params_done = FALSE;
+ preview_render_start (ed->preview);
+}
+
+static gint
+ed_preview_init_func (Preview *preview, gpointer data)
+{
+ int type = 0;
+
+ if (ed->init_params_done == FALSE)
+ {
+ switch (ed->cur_page)
+ {
+ case PAGE_GENERAL:
+ type = (CALC_GLOW | CALC_RAYS | CALC_SFLARE);
+ break;
+ case PAGE_GLOW:
+ type = CALC_GLOW;
+ break;
+ case PAGE_RAYS:
+ type = CALC_RAYS;
+ break;
+ case PAGE_SFLARE:
+ type = CALC_SFLARE;
+ break;
+ default:
+ g_warning ("ed_preview_edit_func: bad page");
+ break;
+ }
+ calc_init_params (ed->gflare, type,
+ ED_PREVIEW_WIDTH/2, ED_PREVIEW_HEIGHT/2,
+ ED_PREVIEW_WIDTH/2, 0.0, 0.0,
+ pvals.vangle, pvals.vlength);
+
+ ed->init_params_done = TRUE;
+ return TRUE;
+ }
+ return calc_init_progress ();
+}
+
+static void
+ed_preview_deinit_func (Preview *preview, gpointer data)
+{
+ if (ed->init_params_done)
+ {
+ calc_deinit ();
+ ed->init_params_done = FALSE;
+ }
+}
+
+static void
+ed_preview_render_func (Preview *preview,
+ guchar *buffer,
+ gint y,
+ gpointer data)
+{
+ switch (ed->cur_page)
+ {
+ case PAGE_GENERAL:
+ ed_preview_render_general (buffer, y);
+ break;
+ case PAGE_GLOW:
+ ed_preview_render_glow (buffer, y);
+ break;
+ case PAGE_RAYS:
+ ed_preview_render_rays (buffer, y);
+ break;
+ case PAGE_SFLARE:
+ ed_preview_render_sflare (buffer, y);
+ break;
+ default:
+ g_warning ("hmm, bad page in ed_preview_render_func ()");
+ break;
+ }
+}
+
+static void
+ed_preview_render_general (guchar *buffer, gint y)
+{
+ int x, i;
+ guchar gflare_pix[4];
+ static guchar src_pix[4] = {0, 0, 0, OPAQUE};
+ int gflare_a;
+
+ for (x = 0; x < ED_PREVIEW_WIDTH; x++)
+ {
+ calc_gflare_pix (gflare_pix, x, y, src_pix);
+ gflare_a = gflare_pix[3];
+
+ for (i = 0; i < 3; i++)
+ {
+ *buffer++ = gflare_pix[i] * gflare_a / 255;
+ }
+ }
+}
+
+static void
+ed_preview_render_glow (guchar *buffer, gint y)
+{
+ int x, i;
+ guchar pix[4];
+
+ for (x = 0; x < ED_PREVIEW_WIDTH; x++)
+ {
+ calc_glow_pix (pix, x, y);
+ for (i = 0; i < 3; i++)
+ *buffer++ = pix[i] * pix[3] / 255;
+ }
+}
+
+static void
+ed_preview_render_rays (guchar *buffer, gint y)
+{
+ int x, i;
+ guchar pix[4];
+
+ for (x = 0; x < ED_PREVIEW_WIDTH; x++)
+ {
+ calc_rays_pix (pix, x, y);
+ for (i = 0; i < 3; i++)
+ *buffer++ = pix[i] * pix[3] / 255;
+ }
+}
+
+static void
+ed_preview_render_sflare (guchar *buffer, gint y)
+{
+ int x, i;
+ guchar pix[4];
+ static guchar src_pix[4] = {0, 0, 0, OPAQUE};
+
+ for (x = 0; x < ED_PREVIEW_WIDTH; x++)
+ {
+ calc_sflare_pix (pix, x, y, src_pix);
+ for (i = 0; i < 3; i++)
+ *buffer++ = pix[i] * pix[3] / 255;
+ }
+}
+
+/*************************************************************************/
+/** **/
+/** +++ Preview **/
+/** **/
+/*************************************************************************/
+
+/*
+ this is generic preview routines.
+ */
+
+
+/*
+ Routines to render the preview in background
+ */
+static Preview *
+preview_new (gint width,
+ gint height,
+ PreviewInitFunc init_func,
+ gpointer init_data,
+ PreviewRenderFunc render_func,
+ gpointer render_data,
+ PreviewDeinitFunc deinit_func,
+ gpointer deinit_data)
+{
+ Preview *preview;
+
+ preview = g_new0 (Preview, 1);
+
+ preview->widget = gimp_preview_area_new ();
+ gtk_widget_set_size_request (preview->widget, width, height);
+ gtk_widget_show (preview->widget);
+
+ preview->width = width;
+ preview->height = height;
+ preview->init_func = init_func;
+ preview->init_data = init_data;
+ preview->render_func = render_func;
+ preview->render_data = render_data;
+ preview->deinit_func = deinit_func;
+ preview->deinit_data = deinit_data;
+ preview->idle_tag = 0;
+ preview->buffer = g_new (guchar, width * 3);
+ preview->full_image_buffer = g_new (guchar, width * height * 3);
+
+ return preview;
+}
+
+static void
+preview_free (Preview *preview)
+{
+ preview_render_end (preview);
+ /* not destroy preview->widget */
+ g_free (preview->buffer);
+ g_free (preview->full_image_buffer);
+ g_free (preview);
+}
+
+/*
+ Start rendering of the preview in background using an idle event.
+ If already started and not yet finished, stop it first.
+ */
+static void
+preview_render_start (Preview *preview)
+{
+ preview_render_end (preview);
+
+ preview->init_done = FALSE;
+ preview->current_y = 0;
+ preview->drawn_y = 0;
+ preview->timeout_tag = g_timeout_add (100,
+ (GSourceFunc) preview_render_start_2,
+ preview);
+}
+
+static gint
+preview_render_start_2 (Preview *preview)
+{
+ preview->timeout_tag = 0;
+ preview->idle_tag = g_idle_add ((GSourceFunc) preview_handle_idle, preview);
+ return FALSE;
+}
+
+static void
+preview_render_end (Preview *preview)
+{
+ if (preview->timeout_tag > 0)
+ {
+ g_source_remove (preview->timeout_tag);
+ preview->timeout_tag = 0;
+ }
+ if (preview->idle_tag > 0)
+ {
+ if (preview->deinit_func)
+ (*preview->deinit_func) (preview, preview->deinit_data);
+
+ g_source_remove (preview->idle_tag);
+ preview->idle_tag = 0;
+ }
+}
+
+/*
+ Handle an idle event.
+ Return FALSE if done, TRUE otherwise.
+ */
+static gboolean
+preview_handle_idle (Preview *preview)
+{
+ gboolean done = FALSE;
+
+ if (preview->init_done == FALSE)
+ {
+ if (preview->init_func &&
+ (*preview->init_func) (preview, preview->init_data))
+ return TRUE;
+
+ preview->init_done = TRUE;
+ }
+
+ if (preview->render_func)
+ (*preview->render_func) (preview, preview->buffer, preview->current_y,
+ preview->render_data);
+ else
+ memset (preview->buffer, 0, preview->width * 3);
+
+ memcpy (preview->full_image_buffer + preview->width * 3 * preview->current_y,
+ preview->buffer,
+ preview->width * 3);
+
+ if (++preview->current_y >= preview->height)
+ done = TRUE;
+
+ if (done)
+ {
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview->widget),
+ 0, 0, preview->width, preview->height,
+ GIMP_RGB_IMAGE,
+ preview->full_image_buffer,
+ preview->width * 3);
+
+ preview->drawn_y = preview->current_y;
+ preview_render_end (preview);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ Convert RGBA to RGB with rendering gray check if needed.
+ (from nova.c)
+ input: guchar src[4] RGBA pixel
+ output: guchar dest[3] RGB pixel
+ */
+
+static void
+preview_rgba_to_rgb (guchar *dest,
+ gint x,
+ gint y,
+ guchar *src)
+{
+ gint src_a;
+ gint check;
+ gint b;
+
+ src_a = src[3];
+
+ if (src_a == OPAQUE) /* full opaque */
+ {
+ for (b = 0; b < 3; b++)
+ dest[b] = src[b];
+ }
+ else
+ {
+ if ((x % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
+ (y % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
+ check = GIMP_CHECK_LIGHT * 255;
+ else
+ check = GIMP_CHECK_DARK * 255;
+
+ if (src_a == 0) /* full transparent */
+ {
+ for (b = 0; b < 3; b++)
+ dest[b] = check;
+ }
+ else
+ {
+ for (b = 0; b < 3; b++)
+ dest[b] = (src[b] * src_a + check * (OPAQUE-src_a)) / OPAQUE;
+ }
+ }
+}
+
+/*************************************************************************/
+/** **/
+/** +++ Gradient Menu **/
+/** +++ gm **/
+/** **/
+/*************************************************************************/
+
+static void
+gradient_menu_init (void)
+{
+ gm_gradient_get_list ();
+ gradient_menus = NULL;
+}
+
+static void
+gradient_menu_rescan (void)
+{
+ GList *tmp;
+ GradientMenu *gm;
+ GtkTreeModel *model;
+
+ /* Detach and destroy menus first */
+ tmp = gradient_menus;
+ while (tmp)
+ {
+ gm = tmp->data;
+ tmp = tmp->next;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (gm->combo));
+ gtk_list_store_clear (GTK_LIST_STORE (model));
+ }
+
+ gm_gradient_get_list ();
+
+ tmp = gradient_menus;
+ while (tmp)
+ {
+ gm = tmp->data;
+ tmp = tmp->next;
+
+ gm_gradient_combo_fill (gm, gm->gradient_name);
+ }
+}
+
+static GradientMenu *
+gradient_menu_new (GradientMenuCallback callback,
+ gpointer callback_data,
+ const gchar *default_gradient_name)
+{
+ GradientMenu *gm = g_new (GradientMenu, 1);
+
+ gm->callback = callback;
+ gm->callback_data = callback_data;
+
+ gm->preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (gm->preview,
+ GM_PREVIEW_WIDTH, GM_PREVIEW_HEIGHT);
+
+ gm->combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+
+ g_signal_connect (gm->combo, "changed",
+ G_CALLBACK (gm_gradient_combo_callback),
+ gm);
+
+ gm_gradient_combo_fill (gm, default_gradient_name);
+
+ gtk_widget_show (gm->preview);
+ gtk_widget_show (gm->combo);
+
+ g_signal_connect (gm->combo, "destroy",
+ G_CALLBACK (gm_combo_destroy_callback),
+ gm);
+
+ gradient_menus = g_list_append (gradient_menus, gm);
+
+ return gm;
+}
+
+/* Local Functions */
+
+static void
+gm_gradient_get_list (void)
+{
+ int i;
+
+ if (gradient_names)
+ {
+ for (i = 0; i < num_gradient_names; i++)
+ g_free (gradient_names[i]);
+ g_free (gradient_names);
+ }
+
+ gradient_cache_flush (); /* to make sure */
+ gradient_names = gradient_get_list (&num_gradient_names);
+}
+
+static void
+gm_gradient_combo_fill (GradientMenu *gm,
+ const gchar *default_gradient)
+{
+ gint active = 0;
+ gint i;
+
+ for (i = 0; i < num_gradient_names; i++)
+ {
+ const gchar *name = gradient_names[i];
+
+ if (strcmp (name, default_gradient) == 0)
+ active = i;
+
+ gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (gm->combo),
+ GIMP_INT_STORE_VALUE, i,
+ GIMP_INT_STORE_LABEL, name,
+ -1);
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (gm->combo), active);
+}
+
+static void
+gm_gradient_combo_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GradientMenu *gm = data;
+ const gchar *gradient_name;
+ gint index;
+
+ if (! gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &index) ||
+ index < 0 ||
+ index >= num_gradient_names)
+ return;
+
+ gradient_name = gradient_names[index];
+
+ DEBUG_PRINT(("gm_combo_callback\n"));
+
+ gradient_name_copy (gm->gradient_name, gradient_name);
+
+ gm_preview_draw (gm->preview, gradient_name);
+
+ if (gm->callback)
+ (* gm->callback) (gradient_name, gm->callback_data);
+}
+
+static void
+gm_preview_draw (GtkWidget *preview,
+ const gchar *gradient_name)
+{
+ guchar values[GM_PREVIEW_WIDTH][4];
+ gint nvalues = GM_PREVIEW_WIDTH;
+ gint row, irow, col;
+ guchar dest_row[GM_PREVIEW_WIDTH][3];
+ guchar *dest;
+ guchar *src;
+ gint check, b;
+ guchar *dest_total_preview_buffer;
+ const gint alpha = 3;
+
+ gradient_get_values (gradient_name, (guchar *)values, nvalues);
+
+ dest_total_preview_buffer = g_new (guchar,
+ GM_PREVIEW_HEIGHT * GM_PREVIEW_WIDTH * 3);
+
+ for (row = 0; row < GM_PREVIEW_HEIGHT; row += GIMP_CHECK_SIZE_SM)
+ {
+ for (col = 0; col < GM_PREVIEW_WIDTH; col++)
+ {
+ dest = dest_row[col];
+ src = values[col];
+
+ if (src[alpha] == OPAQUE)
+ {
+ /* no alpha channel or opaque -- simple way */
+ for (b = 0; b < alpha; b++)
+ dest[b] = src[b];
+ }
+ else
+ {
+ /* more or less transparent */
+ if((col % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM) ^
+ (row % (GIMP_CHECK_SIZE) < GIMP_CHECK_SIZE_SM))
+ {
+ check = GIMP_CHECK_LIGHT * 255;
+ }
+ else
+ {
+ check = GIMP_CHECK_DARK * 255;
+ }
+
+ if (src[alpha] == 0)
+ {
+ /* full transparent -- check */
+ for (b = 0; b < alpha; b++)
+ dest[b] = check;
+ }
+ else
+ {
+ /* middlemost transparent -- mix check and src */
+ for (b = 0; b < alpha; b++)
+ dest[b] = (src[b] * src[alpha] +
+ check * (OPAQUE - src[alpha])) / OPAQUE;
+ }
+ }
+ }
+
+ for (irow = 0;
+ irow < GIMP_CHECK_SIZE_SM && row + irow < GM_PREVIEW_HEIGHT;
+ irow++)
+ {
+ memcpy (dest_total_preview_buffer + (row+irow) * 3 * GM_PREVIEW_WIDTH,
+ dest_row,
+ GM_PREVIEW_WIDTH * 3);
+ }
+ }
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, GM_PREVIEW_WIDTH, GM_PREVIEW_HEIGHT,
+ GIMP_RGB_IMAGE,
+ (const guchar *) dest_total_preview_buffer,
+ GM_PREVIEW_WIDTH * 3);
+
+ g_free (dest_total_preview_buffer);
+}
+
+static void
+gm_combo_destroy_callback (GtkWidget *w,
+ gpointer data)
+{
+ GradientMenu *gm = data;
+ gradient_menus = g_list_remove (gradient_menus, gm);
+}
+
+/*************************************************************************/
+/** **/
+/** +++ Gradients **/
+/** **/
+/*************************************************************************/
+
+/*
+ Manage both internal and external gradients: list up, cache,
+ sampling, etc.
+
+ External gradients are cached.
+ */
+
+
+static void
+gradient_name_copy (gchar *dest,
+ const gchar *src)
+{
+ strncpy (dest, src, GRADIENT_NAME_MAX - 1);
+ dest[GRADIENT_NAME_MAX - 1] = '\0';
+}
+
+/*
+ Translate SPACE to "\\040", etc.
+ */
+static void
+gradient_name_encode (gchar *dest,
+ const gchar *src)
+{
+ gint cnt = GRADIENT_NAME_MAX - 1;
+
+ while (*src && cnt--)
+ {
+ if (g_ascii_iscntrl (*src) || g_ascii_isspace (*src) || *src == '\\')
+ {
+ sprintf (dest, "\\%03o", *src++);
+ dest += 4;
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+}
+
+/*
+ Translate "\\040" to SPACE, etc.
+ */
+static void
+gradient_name_decode (gchar *dest,
+ const gchar *src)
+{
+ gint cnt = GRADIENT_NAME_MAX - 1;
+ guint tmp;
+
+ while (*src && cnt--)
+ {
+ if (*src == '\\' && *(src+1) && *(src+2) && *(src+3))
+ {
+ sscanf (src+1, "%3o", &tmp);
+ *dest++ = tmp;
+ src += 4;
+ }
+ else
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+}
+
+static void
+gradient_init (void)
+{
+ gradient_cache_head = NULL;
+ gradient_cache_count = 0;
+}
+
+static void
+gradient_free (void)
+{
+ gradient_cache_flush ();
+}
+
+static gchar **
+gradient_get_list (gint *num_gradients)
+{
+ gchar **gradients;
+ gchar **external_gradients = NULL;
+ gint external_ngradients = 0;
+ gint i, n;
+
+ gradient_cache_flush ();
+ external_gradients = gimp_gradients_get_list (NULL, &external_ngradients);
+
+ *num_gradients = G_N_ELEMENTS (internal_gradients) + external_ngradients;
+ gradients = g_new (gchar *, *num_gradients);
+
+ n = 0;
+ for (i = 0; i < G_N_ELEMENTS (internal_gradients); i++)
+ {
+ gradients[n++] = g_strdup (internal_gradients[i]);
+ }
+ for (i = 0; i < external_ngradients; i++)
+ {
+ gradients[n++] = g_strdup (external_gradients[i]);
+ }
+
+ g_strfreev (external_gradients);
+
+ return gradients;
+}
+
+static void
+gradient_get_values (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues)
+{
+ /*
+ Criteria to distinguish internal and external is rather simple here.
+ It should be fixed later.
+ */
+ if (gradient_name[0] == '%')
+ gradient_get_values_internal (gradient_name, values, nvalues);
+ else
+ gradient_get_values_external (gradient_name, values, nvalues);
+}
+
+static void
+gradient_get_values_internal (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues)
+{
+ const guchar white[4] = { 255, 255, 255, 255 };
+ const guchar white_trans[4] = { 255, 255, 255, 0 };
+ const guchar red_trans[4] = { 255, 0, 0, 0 };
+ const guchar blue_trans[4] = { 0, 0, 255, 0 };
+ const guchar yellow_trans[4] = { 255, 255, 0, 0 };
+
+ /*
+ The internal gradients here are example --
+ What kind of internals would be useful ?
+ */
+ if(!strcmp(gradient_name, "%white"))
+ {
+ gradient_get_blend (white, white, values, nvalues);
+ }
+ else if(!strcmp(gradient_name, "%white_grad"))
+ {
+ gradient_get_blend (white, white_trans, values, nvalues);
+ }
+ else if (!strcmp (gradient_name, "%red_grad"))
+ {
+ gradient_get_blend (white, red_trans, values, nvalues);
+ }
+ else if (!strcmp (gradient_name, "%blue_grad"))
+ {
+ gradient_get_blend (white, blue_trans, values, nvalues);
+ }
+ else if (!strcmp (gradient_name, "%yellow_grad"))
+ {
+ gradient_get_blend (white, yellow_trans, values, nvalues);
+ }
+ else if (!strcmp (gradient_name, "%random"))
+ {
+ gradient_get_random (values, nvalues);
+ }
+ else
+ {
+ gradient_get_default (gradient_name, values, nvalues);
+ }
+}
+
+static void
+gradient_get_blend (const guchar *fg,
+ const guchar *bg,
+ guchar *values,
+ gint nvalues)
+{
+ gdouble x;
+ gint i;
+ gint j;
+ guchar *v = values;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ x = (double) i / nvalues;
+ for (j = 0; j < 4; j++)
+ *v++ = fg[j] * (1 - x) + bg[j] * x;
+ }
+}
+
+static void
+gradient_get_random (guchar *values,
+ gint nvalues)
+{
+ gint i;
+ gint j;
+ gint inten;
+ guchar *v = values;
+
+ /*
+ This is really simple -- gaussian noise might be better
+ */
+ for (i = 0; i < nvalues; i++)
+ {
+ inten = g_random_int_range (0, 256);
+ for (j = 0; j < 3; j++)
+ *v++ = inten;
+ *v++ = 255;
+ }
+}
+
+static void
+gradient_get_default (const gchar *name,
+ guchar *values,
+ gint nvalues)
+{
+ gdouble e[3];
+ gdouble x;
+ gint i;
+ gint j;
+ guchar *v = values;
+
+ /*
+ Create gradient by name
+ */
+ name++;
+ for (j = 0; j < 3; j++)
+ e[j] = name[j] / 255.0;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ x = (double) i / nvalues;
+ for (j = 0; j < 3; j++)
+ *v++ = 255 * pow (x, e[j]);
+ *v++ = 255;
+ }
+}
+
+/*
+ Caching gradients is really needed. It really takes 0.2 seconds each
+ time to resample an external gradient. (And this plug-in has
+ currently 6 gradient menus.)
+
+ However, this caching routine is not too good. It picks up just
+ GRADIENT_RESOLUTION samples every time, and rescales it later. And
+ cached values are stored in guchar array. No accuracy.
+ */
+static void
+gradient_get_values_external (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues)
+{
+ GradientCacheItem *ci;
+ gboolean found;
+#ifdef DEBUG
+ clock_t clk = clock ();
+#endif
+
+ g_return_if_fail (nvalues >= 2);
+
+ ci = gradient_cache_lookup (gradient_name, &found);
+ if (!found)
+ {
+ /* FIXME: "reverse" hardcoded to FALSE. */
+ gradient_get_values_real_external (gradient_name, ci->values,
+ GRADIENT_RESOLUTION, FALSE);
+ }
+ if (nvalues == GRADIENT_RESOLUTION)
+ {
+ memcpy (values, ci->values, 4 * GRADIENT_RESOLUTION);
+ }
+ else
+ {
+ double pos, frac;
+ int ipos;
+ int i, j;
+
+ for (i = 0; i < nvalues; i++)
+ {
+ pos = ((double) i / (nvalues - 1)) * (GRADIENT_RESOLUTION - 1);
+ g_assert (0 <= pos && pos <= GRADIENT_RESOLUTION - 1);
+ ipos = (int) pos; frac = pos - ipos;
+ if (frac == 0.0)
+ {
+ memcpy (&values[4 * i], &ci->values[4 * ipos], 4);
+ }
+ else
+ for (j = 0; j < 4; j++)
+ values[4 * i + j] = ci->values[4 * ipos + j] * (1 - frac)
+ + ci->values[4 * (ipos + 1) + j] * frac;
+ }
+ }
+
+#ifdef DEBUG
+ get_values_external_clock += clock () - clk;
+ get_values_external_count ++;
+#endif
+
+}
+
+static void
+gradient_get_values_real_external (const gchar *gradient_name,
+ guchar *values,
+ gint nvalues,
+ gboolean reverse)
+{
+ gint n_tmp_values;
+ gdouble *tmp_values;
+ gint i;
+ gint j;
+
+ gimp_gradient_get_uniform_samples (gradient_name, nvalues, reverse,
+ &n_tmp_values, &tmp_values);
+
+ for (i = 0; i < nvalues; i++)
+ for (j = 0; j < 4; j++)
+ values[4 * i + j] = (guchar) (tmp_values[4 * i + j] * 255);
+
+ g_free (tmp_values);
+}
+
+void
+gradient_cache_flush (void)
+{
+ GradientCacheItem *ci;
+ GradientCacheItem *tmp;
+
+ ci = gradient_cache_head;
+ while (ci)
+ {
+ tmp = ci->next;
+ g_free (ci);
+ ci = tmp;
+ }
+ gradient_cache_head = NULL;
+ gradient_cache_count = 0;
+}
+
+static GradientCacheItem *
+gradient_cache_lookup (const gchar *name,
+ gboolean *found)
+{
+ GradientCacheItem *ci;
+
+ ci = gradient_cache_head;
+ while (ci)
+ {
+ if (!strcmp (ci->name, name))
+ break;
+ ci = ci->next;
+ }
+ if (ci)
+ {
+ *found = TRUE;
+ if (!ci->prev)
+ {
+ g_assert (ci == gradient_cache_head);
+ return ci;
+ }
+ ci->prev->next = ci->next;
+ if (ci->next)
+ ci->next->prev = ci->prev;
+ ci->next = gradient_cache_head;
+ gradient_cache_head->prev = ci;
+ gradient_cache_head = ci;
+ ci->prev = NULL;
+ return ci;
+ }
+ else
+ {
+ *found = FALSE;
+ while (gradient_cache_count >= GRADIENT_CACHE_SIZE)
+ gradient_cache_zorch();
+ ci = g_new (GradientCacheItem, 1);
+ strncpy (ci->name, name, GRADIENT_NAME_MAX - 1);
+ ci->name[GRADIENT_NAME_MAX - 1] = '\0';
+ ci->next = gradient_cache_head;
+ ci->prev = NULL;
+ if (gradient_cache_head)
+ gradient_cache_head->prev = ci;
+ gradient_cache_head = ci;
+ ++gradient_cache_count;
+ return ci;
+ }
+}
+
+static void
+gradient_cache_zorch (void)
+{
+ GradientCacheItem *ci = gradient_cache_head;
+
+ while (ci && ci->next)
+ {
+ ci = ci->next;
+ }
+ if (ci)
+ {
+ g_assert (ci->next == NULL);
+ if (ci->prev)
+ ci->prev->next = NULL;
+ else
+ gradient_cache_head = NULL;
+ g_free (ci);
+ --gradient_cache_count;
+ }
+}
+
+#ifdef DEBUG
+void
+gradient_report (void)
+{
+ double total = (double) get_values_external_clock / CLOCKS_PER_SEC;
+
+ g_printerr ("gradient_get_values_external "
+ "%.2f sec. / %d times (ave %.2f sec.)\n",
+ total,
+ get_values_external_count,
+ total / get_values_external_count);
+}
+#endif
diff --git a/plug-ins/help-browser/Makefile.am b/plug-ins/help-browser/Makefile.am
new file mode 100644
index 0000000..e4f7a3b
--- /dev/null
+++ b/plug-ins/help-browser/Makefile.am
@@ -0,0 +1,59 @@
+## Process this file with automake to produce Makefile.in
+
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+
+libgimphelp = $(top_builddir)/plug-ins/help/libgimphelp.a
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+help_browser_RC = help-browser.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/help-browser
+
+libexec_PROGRAMS = help-browser
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(WEBKIT_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimphelp) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(WEBKIT_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(help_browser_RC)
+
+help_browser_SOURCES = \
+ gimpthrobber.c \
+ gimpthrobber.h \
+ gimpthrobberaction.c \
+ gimpthrobberaction.h \
+ help-browser.c \
+ dialog.c \
+ dialog.h \
+ uri.c \
+ uri.h
diff --git a/plug-ins/help-browser/Makefile.in b/plug-ins/help-browser/Makefile.in
new file mode 100644
index 0000000..1c4601f
--- /dev/null
+++ b/plug-ins/help-browser/Makefile.in
@@ -0,0 +1,1027 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = help-browser$(EXEEXT)
+subdir = plug-ins/help-browser
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_help_browser_OBJECTS = gimpthrobber.$(OBJEXT) \
+ gimpthrobberaction.$(OBJEXT) help-browser.$(OBJEXT) \
+ dialog.$(OBJEXT) uri.$(OBJEXT)
+help_browser_OBJECTS = $(am_help_browser_OBJECTS)
+help_browser_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+help_browser_DEPENDENCIES = $(libgimphelp) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(help_browser_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dialog.Po \
+ ./$(DEPDIR)/gimpthrobber.Po ./$(DEPDIR)/gimpthrobberaction.Po \
+ ./$(DEPDIR)/help-browser.Po ./$(DEPDIR)/uri.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(help_browser_SOURCES)
+DIST_SOURCES = $(help_browser_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/help-browser
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimphelp = $(top_builddir)/plug-ins/help/libgimphelp.a
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@help_browser_RC = help-browser.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(WEBKIT_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimphelp) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(WEBKIT_LIBS) \
+ $(GIO_LIBS) \
+ $(GLIB_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(help_browser_RC)
+
+help_browser_SOURCES = \
+ gimpthrobber.c \
+ gimpthrobber.h \
+ gimpthrobberaction.c \
+ gimpthrobberaction.h \
+ help-browser.c \
+ dialog.c \
+ dialog.h \
+ uri.c \
+ uri.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/help-browser/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/help-browser/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+help-browser$(EXEEXT): $(help_browser_OBJECTS) $(help_browser_DEPENDENCIES) $(EXTRA_help_browser_DEPENDENCIES)
+ @rm -f help-browser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(help_browser_OBJECTS) $(help_browser_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthrobber.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthrobberaction.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help-browser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uri.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/dialog.Po
+ -rm -f ./$(DEPDIR)/gimpthrobber.Po
+ -rm -f ./$(DEPDIR)/gimpthrobberaction.Po
+ -rm -f ./$(DEPDIR)/help-browser.Po
+ -rm -f ./$(DEPDIR)/uri.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/dialog.Po
+ -rm -f ./$(DEPDIR)/gimpthrobber.Po
+ -rm -f ./$(DEPDIR)/gimpthrobberaction.Po
+ -rm -f ./$(DEPDIR)/help-browser.Po
+ -rm -f ./$(DEPDIR)/uri.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/help-browser/dialog.c b/plug-ins/help-browser/dialog.c
new file mode 100644
index 0000000..af95ae9
--- /dev/null
+++ b/plug-ins/help-browser/dialog.c
@@ -0,0 +1,1255 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GIMP Help Browser
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Róman Joost <romanofski@gimp.org>
+ *
+ * dialog.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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <gdk/gdkkeysyms.h>
+
+#include <webkit/webkit.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "plug-ins/help/gimphelp.h"
+
+#include "gimpthrobber.h"
+#include "gimpthrobberaction.h"
+
+#include "dialog.h"
+#include "uri.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define GIMP_HELP_BROWSER_DIALOG_DATA "gimp-help-browser-dialog"
+
+#define GIMP_HELP_BROWSER_INDEX_MAX_DEPTH 4
+
+
+typedef struct
+{
+ gint width;
+ gint height;
+ gint paned_position;
+ gboolean show_index;
+ gdouble zoom;
+} DialogData;
+
+enum
+{
+ HISTORY_TITLE,
+ HISTORY_URI
+};
+
+/* local function prototypes */
+
+static GtkUIManager * ui_manager_new (GtkWidget *window);
+
+static GtkWidget * build_searchbar (void);
+
+static void back_callback (GtkAction *action,
+ gpointer data);
+static void forward_callback (GtkAction *action,
+ gpointer data);
+static void reload_callback (GtkAction *action,
+ gpointer data);
+static void stop_callback (GtkAction *action,
+ gpointer data);
+static void home_callback (GtkAction *action,
+ gpointer data);
+static void find_callback (GtkAction *action,
+ gpointer data);
+static void find_again_callback (GtkAction *action,
+ gpointer data);
+static void copy_location_callback (GtkAction *action,
+ gpointer data);
+static void copy_selection_callback (GtkAction *action,
+ gpointer data);
+static void show_index_callback (GtkAction *action,
+ gpointer data);
+static void zoom_in_callback (GtkAction *action,
+ gpointer data);
+static void zoom_out_callback (GtkAction *action,
+ gpointer data);
+static void close_callback (GtkAction *action,
+ gpointer data);
+static void website_callback (GtkAction *action,
+ gpointer data);
+
+static void update_actions (void);
+
+static void row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column);
+static void dialog_unmap (GtkWidget *window,
+ GtkWidget *paned);
+
+static void view_realize (GtkWidget *widget);
+static void view_unrealize (GtkWidget *widget);
+static gboolean view_popup_menu (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean view_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean view_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+
+static void title_changed (GtkWidget *view,
+ WebKitWebFrame *frame,
+ const gchar *title,
+ GtkWidget *window);
+static void load_started (GtkWidget *view,
+ WebKitWebFrame *frame);
+static void load_finished (GtkWidget *view,
+ WebKitWebFrame *frame);
+
+static void select_index (const gchar *uri);
+
+static void search_entry_changed (GtkWidget *entry);
+static gboolean search_entry_key_press (GtkWidget *entry,
+ GdkEventKey *event);
+static void search_prev_clicked (GtkWidget *button,
+ GtkWidget *entry);
+static void search_next_clicked (GtkWidget *button,
+ GtkWidget *entry);
+static void search_close_clicked (GtkWidget *button);
+static void search (const gchar *text,
+ gboolean forward);
+
+
+/* private variables */
+
+static GHashTable *uri_hash_table = NULL;
+
+static GtkWidget *view = NULL;
+static GtkWidget *sidebar = NULL;
+static GtkWidget *searchbar = NULL;
+static GtkWidget *tree_view = NULL;
+static GtkUIManager *ui_manager = NULL;
+static GtkWidget *button_prev = NULL;
+static GtkWidget *button_next = NULL;
+static GdkCursor *busy_cursor = NULL;
+
+
+/* public functions */
+
+void
+browser_dialog_open (const gchar *plug_in_binary)
+{
+ GtkWidget *window;
+ GtkWidget *main_vbox;
+ GtkWidget *vbox;
+ GtkWidget *toolbar;
+ GtkWidget *paned;
+ GtkWidget *scrolled;
+ GtkToolItem *item;
+ GtkAction *action;
+ DialogData data = { 720, 560, 240, TRUE, 1.0 };
+
+ gimp_ui_init (plug_in_binary, TRUE);
+
+ gimp_get_data (GIMP_HELP_BROWSER_DIALOG_DATA, &data);
+
+ /* the dialog window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), _("GIMP Help Browser"));
+ gtk_window_set_role (GTK_WINDOW (window), plug_in_binary);
+ gtk_window_set_icon_name (GTK_WINDOW (window), GIMP_ICON_HELP_USER_MANUAL);
+
+ gtk_window_set_default_size (GTK_WINDOW (window), data.width, data.height);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_widget_show (vbox);
+
+ ui_manager = ui_manager_new (window);
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/help-browser-toolbar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ item = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/forward");
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (item), action);
+ g_object_notify (G_OBJECT (action), "tooltip");
+ button_next = GTK_WIDGET (item);
+
+ item = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
+ gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, 0);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/back");
+ gtk_activatable_set_related_action (GTK_ACTIVATABLE (item), action);
+ g_object_notify (G_OBJECT (action), "tooltip");
+ button_prev = GTK_WIDGET (item);
+
+ item =
+ GTK_TOOL_ITEM (gtk_ui_manager_get_widget (ui_manager,
+ "/help-browser-toolbar/space"));
+ gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE);
+ gtk_tool_item_set_expand (item, TRUE);
+
+ /* the horizontal paned */
+ paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_box_pack_start (GTK_BOX (vbox), paned, TRUE, TRUE, 0);
+ gtk_widget_show (paned);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_paned_add1 (GTK_PANED (paned), scrolled);
+ gtk_paned_set_position (GTK_PANED (paned), data.paned_position);
+
+ sidebar = scrolled;
+
+ if (data.show_index)
+ gtk_widget_show (sidebar);
+
+ tree_view = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
+ gtk_container_add (GTK_CONTAINER (scrolled), tree_view);
+ gtk_widget_show (tree_view);
+
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), -1,
+ NULL,
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ NULL);
+
+ g_signal_connect (tree_view, "row-activated",
+ G_CALLBACK (row_activated),
+ NULL);
+
+ /* HTML view */
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (main_vbox);
+ gtk_paned_pack2 (GTK_PANED (paned), main_vbox, TRUE, TRUE);
+
+ scrolled = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_widget_set_size_request (scrolled, 300, 200);
+ gtk_box_pack_start (GTK_BOX (main_vbox), scrolled, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled);
+
+ view = webkit_web_view_new ();
+ webkit_web_view_set_maintains_back_forward_list (WEBKIT_WEB_VIEW (view),
+ TRUE);
+ gtk_container_add (GTK_CONTAINER (scrolled), view);
+ gtk_widget_show (view);
+
+ g_signal_connect (view, "realize",
+ G_CALLBACK (view_realize),
+ NULL);
+ g_signal_connect (view, "unrealize",
+ G_CALLBACK (view_unrealize),
+ NULL);
+
+ g_signal_connect (view, "popup-menu",
+ G_CALLBACK (view_popup_menu),
+ NULL);
+ g_signal_connect (view, "button-press-event",
+ G_CALLBACK (view_button_press),
+ NULL);
+ g_signal_connect (view, "key-press-event",
+ G_CALLBACK (view_key_press),
+ NULL);
+
+ webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (view), data.zoom);
+
+ g_signal_connect (view, "title-changed",
+ G_CALLBACK (title_changed),
+ window);
+
+ g_signal_connect (view, "load-started",
+ G_CALLBACK (load_started),
+ NULL);
+ g_signal_connect (view, "load-finished",
+ G_CALLBACK (load_finished),
+ NULL);
+
+ gtk_widget_grab_focus (view);
+
+ g_signal_connect (window, "unmap",
+ G_CALLBACK (dialog_unmap),
+ paned);
+
+ update_actions ();
+
+ /* Searchbar */
+ searchbar = build_searchbar ();
+ gtk_box_pack_start (GTK_BOX (main_vbox), searchbar, FALSE, FALSE, 0);
+}
+
+void
+browser_dialog_load (const gchar *uri)
+{
+ g_return_if_fail (uri != NULL);
+
+ webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), uri);
+
+ select_index (uri);
+
+ gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (view)));
+}
+
+static void
+browser_dialog_make_index_foreach (const gchar *help_id,
+ GimpHelpItem *item,
+ GimpHelpLocale *locale)
+{
+ gchar *sort_key = item->title;
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: processing %s (parent %s)\n",
+ G_STRFUNC,
+ item->title ? item->title : "NULL",
+ item->parent ? item->parent : "NULL");
+#endif
+
+ if (item->sort &&
+ g_regex_match_simple ("^[0-9]+([.][0-9]+)*$", item->sort, 0, 0))
+ {
+ sort_key = item->sort;
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: sort key = %s\n", G_STRFUNC, sort_key);
+#endif
+ }
+
+ item->index = 0;
+
+ if (sort_key)
+ {
+ const gint max_tokens = GIMP_HELP_BROWSER_INDEX_MAX_DEPTH;
+ gchar* *indices = g_strsplit (sort_key, ".", max_tokens + 1);
+ gint i;
+
+ for (i = 0; i < max_tokens; i++)
+ {
+ gunichar c;
+
+ if (! indices[i])
+ {
+ /* make sure that all item->index's are comparable */
+ item->index <<= (8 * (max_tokens - i));
+ break;
+ }
+
+ item->index <<= 8; /* NOP if i = 0 */
+ c = g_utf8_get_char (indices[i]);
+ if (g_unichar_isdigit (c))
+ {
+ item->index += atoi (indices[i]);
+ }
+ else if (g_utf8_strlen (indices[i], -1) == 1)
+ {
+ item->index += (c & 0xFF);
+ }
+ }
+
+ g_strfreev (indices);
+
+#if DEBUG_SORT_HELP_ITEMS
+ g_printerr ("%s: index = %lu\n", G_STRFUNC, item->index);
+#endif
+ }
+
+ if (item->parent && strlen (item->parent))
+ {
+ GimpHelpItem *parent;
+
+ parent = g_hash_table_lookup (locale->help_id_mapping, item->parent);
+
+ if (parent)
+ {
+ parent->children = g_list_prepend (parent->children, item);
+ }
+ }
+ else
+ {
+ locale->toplevel_items = g_list_prepend (locale->toplevel_items, item);
+ }
+}
+
+static gint
+help_item_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const GimpHelpItem *item_a = a;
+ const GimpHelpItem *item_b = b;
+
+ if (item_a->index > item_b->index)
+ return 1;
+ else if (item_a->index < item_b->index)
+ return -1;
+
+ return 0;
+}
+
+static void
+add_child (GtkTreeStore *store,
+ GimpHelpDomain *domain,
+ GimpHelpLocale *locale,
+ GtkTreeIter *parent,
+ GimpHelpItem *item,
+ gint depth)
+{
+ GtkTreeIter iter;
+ GList *list;
+ gchar *uri;
+
+ gtk_tree_store_append (store, &iter, parent);
+
+ gtk_tree_store_set (store, &iter,
+ 0, item,
+ 1, item->title,
+ -1);
+
+ uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ item->ref,
+ NULL);
+
+ g_hash_table_insert (uri_hash_table,
+ uri,
+ gtk_tree_iter_copy (&iter));
+
+ if (depth + 1 == GIMP_HELP_BROWSER_INDEX_MAX_DEPTH)
+ return;
+
+ item->children = g_list_sort (item->children, help_item_compare);
+
+ for (list = item->children; list; list = g_list_next (list))
+ {
+ GimpHelpItem *item = list->data;
+
+ add_child (store, domain, locale, &iter, item, depth + 1);
+ }
+}
+
+void
+browser_dialog_make_index (GimpHelpDomain *domain,
+ GimpHelpLocale *locale)
+{
+ GtkTreeStore *store;
+ GList *list;
+
+ if (! locale->toplevel_items)
+ {
+ g_hash_table_foreach (locale->help_id_mapping,
+ (GHFunc) browser_dialog_make_index_foreach,
+ locale);
+
+ locale->toplevel_items = g_list_sort (locale->toplevel_items,
+ help_item_compare);
+ }
+
+ store = gtk_tree_store_new (2,
+ G_TYPE_POINTER,
+ G_TYPE_STRING);
+
+ g_object_set_data (G_OBJECT (store), "domain", domain);
+ g_object_set_data (G_OBJECT (store), "locale", locale);
+
+ if (uri_hash_table)
+ g_hash_table_unref (uri_hash_table);
+
+ uri_hash_table = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gtk_tree_iter_free);
+
+ for (list = locale->toplevel_items; list; list = g_list_next (list))
+ {
+ GimpHelpItem *item = list->data;
+
+ add_child (store, domain, locale, NULL, item, 0);
+ }
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+}
+
+static void
+select_index (const gchar *uri)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter *iter = NULL;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+
+ if (uri)
+ iter = g_hash_table_lookup (uri_hash_table, uri);
+
+ if (iter)
+ {
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+ GtkTreePath *path;
+ GtkTreePath *scroll_path;
+
+ path = gtk_tree_model_get_path (model, iter);
+ scroll_path = gtk_tree_path_copy (path);
+
+ gtk_tree_path_up (path);
+ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (tree_view), path);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view), scroll_path,
+ NULL, FALSE, 0.0, 0.0);
+
+ gtk_tree_path_free (path);
+ gtk_tree_path_free (scroll_path);
+
+ gtk_tree_selection_select_iter (selection, iter);
+ }
+ else
+ {
+ gtk_tree_selection_unselect_all (selection);
+ }
+}
+
+
+/* private functions */
+
+static GtkUIManager *
+ui_manager_new (GtkWidget *window)
+{
+ static const GtkActionEntry actions[] =
+ {
+ {
+ "back", GIMP_ICON_GO_PREVIOUS,
+ NULL, "<alt>Left", N_("Go back one page"),
+ G_CALLBACK (back_callback)
+ },
+ {
+ "forward", GIMP_ICON_GO_NEXT,
+ NULL, "<alt>Right", N_("Go forward one page"),
+ G_CALLBACK (forward_callback)
+ },
+ {
+ "reload", GIMP_ICON_VIEW_REFRESH,
+ N_("_Reload"), "<control>R", N_("Reload current page"),
+ G_CALLBACK (reload_callback)
+ },
+ {
+ "stop", GIMP_ICON_PROCESS_STOP,
+ N_("_Stop"), "Escape", N_("Stop loading this page"),
+ G_CALLBACK (stop_callback)
+ },
+ {
+ "home", GIMP_ICON_GO_HOME,
+ NULL, "<alt>Home", N_("Go to the index page"),
+ G_CALLBACK (home_callback)
+ },
+ {
+ "copy-location", GIMP_ICON_EDIT_COPY,
+ N_("C_opy location"), "",
+ N_("Copy the location of this page to the clipboard"),
+ G_CALLBACK (copy_location_callback)
+ },
+ {
+ "copy-selection", GIMP_ICON_EDIT_COPY,
+ NULL, "<control>C", NULL,
+ G_CALLBACK (copy_selection_callback)
+ },
+ {
+ "zoom-in", GIMP_ICON_ZOOM_IN,
+ NULL, "<control>plus", NULL,
+ G_CALLBACK (zoom_in_callback)
+ },
+ {
+ "zoom-out", GIMP_ICON_ZOOM_OUT,
+ NULL, "<control>minus", NULL,
+ G_CALLBACK (zoom_out_callback)
+ },
+ {
+ "find", GIMP_ICON_EDIT_FIND,
+ NULL, "<control>F", N_("Find text in current page"),
+ G_CALLBACK (find_callback)
+ },
+ {
+ "find-again", NULL,
+ N_("Find _Again"), "<control>G", NULL,
+ G_CALLBACK (find_again_callback)
+ },
+ {
+ "close", GIMP_ICON_WINDOW_CLOSE,
+ NULL, "<control>W", NULL,
+ G_CALLBACK (close_callback)
+ },
+ {
+ "quit", GIMP_ICON_APPLICATION_EXIT,
+ NULL, "<control>Q", NULL,
+ G_CALLBACK (close_callback)
+ }
+ };
+
+ static const GtkToggleActionEntry toggle_actions[] =
+ {
+ {
+ "show-index", NULL,
+ N_("S_how Index"), "<control>I",
+ N_("Toggle the visibility of the sidebar"),
+ G_CALLBACK (show_index_callback), FALSE
+ }
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GtkActionGroup *group = gtk_action_group_new ("Actions");
+ GtkAction *action;
+ GError *error = NULL;
+
+ gtk_action_group_set_translation_domain (group, NULL);
+ gtk_action_group_add_actions (group,
+ actions, G_N_ELEMENTS (actions),
+ NULL);
+ gtk_action_group_add_toggle_actions (group,
+ toggle_actions,
+ G_N_ELEMENTS (toggle_actions),
+ NULL);
+
+ action = gimp_throbber_action_new ("website",
+ "docs.gimp.org",
+ _("Visit the GIMP documentation website"),
+ GIMP_ICON_HELP_USER_MANUAL);
+ g_signal_connect_closure (action, "activate",
+ g_cclosure_new (G_CALLBACK (website_callback),
+ NULL, NULL),
+ FALSE);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <toolbar name=\"help-browser-toolbar\">"
+ " <toolitem action=\"reload\" />"
+ " <toolitem action=\"stop\" />"
+ " <toolitem action=\"home\" />"
+ " <separator name=\"space\" />"
+ " <toolitem action=\"website\" />"
+ " </toolbar>"
+ " <accelerator action=\"close\" />"
+ " <accelerator action=\"quit\" />"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"help-browser-popup\">"
+ " <menuitem action=\"back\" />"
+ " <menuitem action=\"forward\" />"
+ " <menuitem action=\"reload\" />"
+ " <menuitem action=\"stop\" />"
+ " <separator />"
+ " <menuitem action=\"home\" />"
+ " <menuitem action=\"copy-location\" />"
+ " <menuitem action=\"show-index\" />"
+ " <separator />"
+ " <menuitem action=\"find\" />"
+ " <menuitem action=\"find-again\" />"
+ " <separator />"
+ " <menuitem action=\"zoom-in\" />"
+ " <menuitem action=\"zoom-out\" />"
+ " <separator />"
+ " <menuitem action=\"close\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <popup name=\"help-browser-copy-popup\">"
+ " <menuitem action=\"copy-selection\" />"
+ " </popup>"
+ "</ui>",
+ -1, &error);
+
+ if (error)
+ {
+ g_warning ("error parsing ui: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ return ui_manager;
+}
+
+static void
+back_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_go_back (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+forward_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_go_forward (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+reload_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_reload (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+stop_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_stop_loading (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+home_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
+ GimpHelpDomain *domain = g_object_get_data (G_OBJECT (model), "domain");
+ GimpHelpLocale *locale = g_object_get_data (G_OBJECT (model), "locale");
+
+ if (domain && locale)
+ {
+ gchar *uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ gimp_help_locale_map (locale,
+ GIMP_HELP_DEFAULT_ID),
+ NULL);
+ browser_dialog_load (uri);
+ g_free (uri);
+ }
+}
+
+static void
+find_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT (searchbar), "entry");
+
+ gtk_widget_show (searchbar);
+ gtk_widget_grab_focus (entry);
+}
+
+static void
+find_again_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT (searchbar), "entry");
+
+ gtk_widget_show (searchbar);
+ gtk_widget_grab_focus (entry);
+
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static void
+copy_location_callback (GtkAction *action,
+ gpointer data)
+{
+ WebKitWebFrame *frame;
+ const gchar *uri;
+
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view));
+ uri = webkit_web_frame_get_uri (frame);
+
+ if (uri)
+ {
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_clipboard_get_for_display (gtk_widget_get_display (view),
+ GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, uri, -1);
+ }
+}
+
+static void
+copy_selection_callback (GtkAction *action,
+ gpointer data)
+{
+ if (webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (view)))
+ {
+ webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
+ }
+}
+
+static void
+show_index_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_widget_set_visible (sidebar,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
+zoom_in_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_zoom_in (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+zoom_out_callback (GtkAction *action,
+ gpointer data)
+{
+ webkit_web_view_zoom_out (WEBKIT_WEB_VIEW (view));
+}
+
+static void
+website_callback (GtkAction *action,
+ gpointer data)
+{
+ browser_dialog_load ("https://docs.gimp.org/");
+}
+
+static void
+close_callback (GtkAction *action,
+ gpointer data)
+{
+ gtk_widget_destroy (gtk_widget_get_toplevel (view));
+}
+
+static void
+menu_callback (GtkWidget *menu,
+ gpointer data)
+{
+ gint steps = GPOINTER_TO_INT (data);
+
+ webkit_web_view_go_back_or_forward (WEBKIT_WEB_VIEW (view), steps);
+}
+
+static GtkWidget *
+build_menu (const GList *items,
+ gboolean back)
+{
+ GtkWidget *menu;
+ const GList *iter;
+ gint steps;
+
+ if (! items)
+ return NULL;
+
+ menu = gtk_menu_new ();
+
+ for (iter = items, steps = 1; iter; iter = g_list_next (iter), steps++)
+ {
+ WebKitWebHistoryItem *item = iter->data;
+ const gchar *title;
+
+ title = webkit_web_history_item_get_title (item);
+
+ if (title)
+ {
+ GtkWidget *menu_item = gtk_menu_item_new_with_label (title);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ g_signal_connect (menu_item, "activate",
+ G_CALLBACK (menu_callback),
+ GINT_TO_POINTER (back ? - steps : steps));
+ }
+ }
+
+ return menu;
+}
+
+static void
+update_actions (void)
+{
+ GtkAction *action;
+ WebKitWebBackForwardList *back_forward_list;
+ WebKitWebFrame *frame;
+
+ back_forward_list =
+ webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (view));
+
+ /* update the back button and its menu */
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/back");
+ gtk_action_set_sensitive (action,
+ webkit_web_view_can_go_back (WEBKIT_WEB_VIEW (view)));
+
+ if (back_forward_list)
+ {
+ const GList *list;
+
+ list = webkit_web_back_forward_list_get_back_list_with_limit (back_forward_list,
+ 12);
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_prev),
+ build_menu (list, TRUE));
+ }
+ else
+ {
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_prev), NULL);
+ }
+
+ /* update the forward button and its menu */
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/forward");
+ gtk_action_set_sensitive (action,
+ webkit_web_view_can_go_forward (WEBKIT_WEB_VIEW (view)));
+
+ if (back_forward_list)
+ {
+ const GList *list;
+
+ list = webkit_web_back_forward_list_get_forward_list_with_limit (back_forward_list,
+ 12);
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_next),
+ build_menu (list, FALSE));
+ }
+ else
+ {
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (button_next), NULL);
+ }
+
+ /* update the copy-location action */
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/copy-location");
+
+ frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (view));
+ gtk_action_set_sensitive (action, webkit_web_frame_get_uri (frame) != NULL);
+
+ /* update the show-index action */
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/show-index");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ gtk_widget_get_visible (sidebar));
+}
+
+static void
+row_activated (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+ GtkTreeIter iter;
+ GimpHelpDomain *domain;
+ GimpHelpLocale *locale;
+ GimpHelpItem *item;
+ gchar *uri;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_tree_model_get (model, &iter,
+ 0, &item,
+ -1);
+
+ domain = g_object_get_data (G_OBJECT (model), "domain");
+ locale = g_object_get_data (G_OBJECT (model), "locale");
+
+ uri = g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ item->ref,
+ NULL);
+
+ browser_dialog_load (uri);
+
+ g_free (uri);
+}
+
+static void
+dialog_unmap (GtkWidget *window,
+ GtkWidget *paned)
+{
+ DialogData data;
+
+ gtk_window_get_size (GTK_WINDOW (window), &data.width, &data.height);
+
+ data.paned_position = gtk_paned_get_position (GTK_PANED (paned));
+ data.show_index = gtk_widget_get_visible (sidebar);
+
+ data.zoom = (view ?
+ webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (view)) : 1.0);
+
+ gimp_set_data (GIMP_HELP_BROWSER_DIALOG_DATA, &data, sizeof (data));
+
+ gtk_main_quit ();
+}
+
+static void
+view_realize (GtkWidget *widget)
+{
+ g_return_if_fail (busy_cursor == NULL);
+
+ busy_cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_WATCH);
+}
+
+static void
+view_unrealize (GtkWidget *widget)
+{
+ if (busy_cursor)
+ {
+ gdk_cursor_unref (busy_cursor);
+ busy_cursor = NULL;
+ }
+}
+
+static gboolean
+view_popup_menu (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkWidget *menu;
+ const gchar *path;
+
+ if (webkit_web_view_can_copy_clipboard (WEBKIT_WEB_VIEW (view)))
+ path = "/help-browser-copy-popup";
+ else
+ path = "/help-browser-popup";
+
+ menu = gtk_ui_manager_get_widget (ui_manager, path);
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ event ? event->time : gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static gboolean
+view_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ return view_popup_menu (widget, event);
+
+ return FALSE;
+}
+
+static gboolean
+view_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ if (event->keyval == GDK_KEY_slash)
+ {
+ GtkAction *action;
+
+ action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/find");
+ gtk_action_activate (action);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+title_changed (GtkWidget *view,
+ WebKitWebFrame *frame,
+ const gchar *title,
+ GtkWidget *window)
+{
+ gchar *full_title;
+
+ full_title = g_strdup_printf ("%s - %s",
+ title ? title : _("Untitled"),
+ _("GIMP Help Browser"));
+
+ gtk_window_set_title (GTK_WINDOW (window), full_title);
+ g_free (full_title);
+
+ update_actions ();
+}
+
+static void
+load_started (GtkWidget *view,
+ WebKitWebFrame *frame)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/stop");
+ gtk_action_set_sensitive (action, TRUE);
+}
+
+static void
+load_finished (GtkWidget *view,
+ WebKitWebFrame *frame)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager,
+ "/ui/help-browser-popup/stop");
+ gtk_action_set_sensitive (action, FALSE);
+
+ update_actions ();
+
+ select_index (webkit_web_frame_get_uri (frame));
+}
+
+static GtkWidget *
+build_searchbar (void)
+{
+ GtkWidget *button;
+ GtkWidget *entry;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+
+ label = gtk_label_new (_("Find:"));
+ gtk_widget_show (label);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ entry = gtk_entry_new ();
+ gtk_widget_show (entry);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
+ g_object_set_data (G_OBJECT (hbox), "entry", entry);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (search_entry_changed),
+ NULL);
+
+ g_signal_connect (entry, "key-press-event",
+ G_CALLBACK (search_entry_key_press),
+ NULL);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Previous"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_icon_name (GIMP_ICON_GO_PREVIOUS,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_prev_clicked),
+ entry);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Next"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_button_set_image (GTK_BUTTON (button),
+ gtk_image_new_from_icon_name (GIMP_ICON_GO_NEXT,
+ GTK_ICON_SIZE_BUTTON));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_next_clicked),
+ entry);
+
+ button = gtk_button_new_with_mnemonic (C_("search", "_Close"));
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_close_clicked),
+ NULL);
+
+ return hbox;
+}
+
+static void
+search_entry_changed (GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static gboolean
+search_entry_key_press (GtkWidget *entry,
+ GdkEventKey *event)
+{
+ switch (event->keyval)
+ {
+ case GDK_KEY_Escape:
+ gtk_widget_hide (searchbar);
+ webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
+ return TRUE;
+
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_ISO_Enter:
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+search_prev_clicked (GtkWidget *button,
+ GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), FALSE);
+}
+
+static void
+search_next_clicked (GtkWidget *button,
+ GtkWidget *entry)
+{
+ search (gtk_entry_get_text (GTK_ENTRY (entry)), TRUE);
+}
+
+static void
+search (const gchar *text,
+ gboolean forward)
+{
+ if (text)
+ webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
+ text, FALSE, forward, TRUE);
+}
+
+static void
+search_close_clicked (GtkWidget *button)
+{
+ gtk_widget_hide (searchbar);
+ webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
+}
diff --git a/plug-ins/help-browser/dialog.h b/plug-ins/help-browser/dialog.h
new file mode 100644
index 0000000..a895af9
--- /dev/null
+++ b/plug-ins/help-browser/dialog.h
@@ -0,0 +1,35 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help Browser
+ * Copyright (C) 1999-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * dialog.h
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DIALOG_H__
+#define __DIALOG_H__
+
+
+void browser_dialog_open (const gchar *plug_in_binary);
+void browser_dialog_load (const gchar *uri);
+
+void browser_dialog_make_index (GimpHelpDomain *domain,
+ GimpHelpLocale *locale);
+
+
+#endif /* ! __DIALOG_H__ */
diff --git a/plug-ins/help-browser/gimpthrobber.c b/plug-ins/help-browser/gimpthrobber.c
new file mode 100644
index 0000000..81866f3
--- /dev/null
+++ b/plug-ins/help-browser/gimpthrobber.c
@@ -0,0 +1,366 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpThrobber
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpthrobber.h"
+
+
+enum
+{
+ CLICKED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ICON_NAME,
+ PROP_IMAGE
+};
+
+
+static void gimp_throbber_class_init (GimpThrobberClass *klass);
+
+static void gimp_throbber_init (GimpThrobber *button);
+
+static void gimp_throbber_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gimp_throbber_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gimp_throbber_finalize (GObject *object);
+
+static gboolean gimp_throbber_create_menu_proxy (GtkToolItem *tool_item);
+static void gimp_throbber_toolbar_reconfigured (GtkToolItem *tool_item);
+static void gimp_throbber_button_clicked (GtkWidget *widget,
+ GimpThrobber *button);
+
+static void gimp_throbber_construct_contents (GtkToolItem *tool_item);
+
+
+static GObjectClass *parent_class = NULL;
+static guint toolbutton_signals[LAST_SIGNAL] = { 0 };
+
+
+#define GIMP_THROBBER_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIMP_TYPE_THROBBER, GimpThrobberPrivate))
+
+
+struct _GimpThrobberPrivate
+{
+ GtkWidget *button;
+ GtkWidget *image;
+ gchar *icon_name;
+};
+
+
+GType
+gimp_throbber_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ static const GTypeInfo type_info =
+ {
+ sizeof (GimpThrobberClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_throbber_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (GimpThrobber),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gimp_throbber_init,
+ };
+
+ type = g_type_register_static (GTK_TYPE_TOOL_ITEM,
+ "GimpThrobber",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+static void
+gimp_throbber_class_init (GimpThrobberClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkToolItemClass *tool_item_class = GTK_TOOL_ITEM_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->set_property = gimp_throbber_set_property;
+ object_class->get_property = gimp_throbber_get_property;
+ object_class->finalize = gimp_throbber_finalize;
+
+ tool_item_class->create_menu_proxy = gimp_throbber_create_menu_proxy;
+ tool_item_class->toolbar_reconfigured = gimp_throbber_toolbar_reconfigured;
+
+ g_object_class_install_property (object_class,
+ PROP_ICON_NAME,
+ g_param_spec_string ("icon-name", NULL, NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (object_class,
+ PROP_IMAGE,
+ g_param_spec_object ("image", NULL, NULL,
+ GTK_TYPE_IMAGE,
+ G_PARAM_READWRITE));
+
+ toolbutton_signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GimpThrobberClass, clicked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (object_class, sizeof (GimpThrobberPrivate));
+}
+
+static void
+gimp_throbber_init (GimpThrobber *button)
+{
+ GtkToolItem *toolitem = GTK_TOOL_ITEM (button);
+
+ button->priv = GIMP_THROBBER_GET_PRIVATE (button);
+
+ gtk_tool_item_set_homogeneous (toolitem, TRUE);
+
+ button->priv->button = g_object_new (GTK_TYPE_BUTTON,
+ "yalign", 0.0,
+ "focus-on-click", FALSE,
+ NULL);
+
+ g_signal_connect_object (button->priv->button, "clicked",
+ G_CALLBACK (gimp_throbber_button_clicked),
+ button, 0);
+
+ gtk_container_add (GTK_CONTAINER (button), button->priv->button);
+ gtk_widget_show (button->priv->button);
+}
+
+static void
+gimp_throbber_construct_contents (GtkToolItem *tool_item)
+{
+ GimpThrobber *button = GIMP_THROBBER (tool_item);
+ GtkWidget *image;
+ GtkToolbarStyle style;
+
+ if (button->priv->image && gtk_widget_get_parent (button->priv->image))
+ gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (button->priv->image)),
+ button->priv->image);
+
+ if (gtk_bin_get_child (GTK_BIN (button->priv->button)))
+ gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (button->priv->button)));
+
+ style = gtk_tool_item_get_toolbar_style (tool_item);
+
+ if (style == GTK_TOOLBAR_TEXT)
+ {
+ image = gtk_image_new_from_icon_name (button->priv->icon_name,
+ GTK_ICON_SIZE_MENU);
+ }
+ else if (style == GTK_TOOLBAR_ICONS)
+ {
+ image = gtk_image_new_from_icon_name (button->priv->icon_name,
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ }
+ else if (button->priv->image)
+ {
+ image = button->priv->image;
+ }
+ else
+ {
+ image = gtk_image_new_from_icon_name (button->priv->icon_name,
+ GTK_ICON_SIZE_DND);
+ }
+
+ gtk_container_add (GTK_CONTAINER (button->priv->button), image);
+ gtk_widget_show (image);
+
+ gtk_button_set_relief (GTK_BUTTON (button->priv->button),
+ gtk_tool_item_get_relief_style (tool_item));
+
+ gtk_widget_queue_resize (GTK_WIDGET (button));
+}
+
+static void
+gimp_throbber_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GimpThrobber *button = GIMP_THROBBER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_NAME:
+ gimp_throbber_set_icon_name (button, g_value_get_string (value));
+ break;
+
+ case PROP_IMAGE:
+ gimp_throbber_set_image (button, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_throbber_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GimpThrobber *button = GIMP_THROBBER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_NAME:
+ g_value_set_string (value, button->priv->icon_name);
+ break;
+
+ case PROP_IMAGE:
+ g_value_set_object (value, button->priv->image);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gimp_throbber_finalize (GObject *object)
+{
+ GimpThrobber *button = GIMP_THROBBER (object);
+
+ if (button->priv->icon_name)
+ g_free (button->priv->icon_name);
+
+ if (button->priv->image)
+ g_object_unref (button->priv->image);
+
+ parent_class->finalize (object);
+}
+
+static void
+gimp_throbber_button_clicked (GtkWidget *widget,
+ GimpThrobber *button)
+{
+ g_signal_emit_by_name (button, "clicked");
+}
+
+static gboolean
+gimp_throbber_create_menu_proxy (GtkToolItem *tool_item)
+{
+ gtk_tool_item_set_proxy_menu_item (tool_item, "gimp-throbber-menu-id", NULL);
+
+ return FALSE;
+}
+
+static void
+gimp_throbber_toolbar_reconfigured (GtkToolItem *tool_item)
+{
+ gimp_throbber_construct_contents (tool_item);
+}
+
+GtkToolItem *
+gimp_throbber_new (const gchar *icon_name)
+{
+ return g_object_new (GIMP_TYPE_THROBBER,
+ "icon-name", icon_name,
+ NULL);
+}
+
+void
+gimp_throbber_set_icon_name (GimpThrobber *button,
+ const gchar *icon_name)
+{
+ gchar *old_icon_name;
+
+ g_return_if_fail (GIMP_IS_THROBBER (button));
+
+ old_icon_name = button->priv->icon_name;
+
+ button->priv->icon_name = g_strdup (icon_name);
+ gimp_throbber_construct_contents (GTK_TOOL_ITEM (button));
+
+ g_object_notify (G_OBJECT (button), "icon-name");
+
+ g_free (old_icon_name);
+}
+
+const gchar *
+gimp_throbber_get_icon_name (GimpThrobber *button)
+{
+ g_return_val_if_fail (GIMP_IS_THROBBER (button), NULL);
+
+ return button->priv->icon_name;
+}
+
+void
+gimp_throbber_set_image (GimpThrobber *button,
+ GtkWidget *image)
+{
+ g_return_if_fail (GIMP_IS_THROBBER (button));
+ g_return_if_fail (image == NULL || GTK_IS_IMAGE (image));
+
+ if (image != button->priv->image)
+ {
+ if (button->priv->image)
+ {
+ if (gtk_widget_get_parent (button->priv->image))
+ gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (button->priv->image)),
+ button->priv->image);
+
+ g_object_unref (button->priv->image);
+ }
+
+ if (image)
+ g_object_ref_sink (image);
+
+ button->priv->image = image;
+
+ gimp_throbber_construct_contents (GTK_TOOL_ITEM (button));
+
+ g_object_notify (G_OBJECT (button), "image");
+ }
+}
+
+GtkWidget *
+gimp_throbber_get_image (GimpThrobber *button)
+{
+ g_return_val_if_fail (GIMP_IS_THROBBER (button), NULL);
+
+ return button->priv->image;
+}
diff --git a/plug-ins/help-browser/gimpthrobber.h b/plug-ins/help-browser/gimpthrobber.h
new file mode 100644
index 0000000..215fdff
--- /dev/null
+++ b/plug-ins/help-browser/gimpthrobber.h
@@ -0,0 +1,68 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpThrobber
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_THROBBER_H__
+#define __GIMP_THROBBER_H__
+
+G_BEGIN_DECLS
+
+
+#define GIMP_TYPE_THROBBER (gimp_throbber_get_type ())
+#define GIMP_THROBBER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_THROBBER, GimpThrobber))
+#define GIMP_THROBBER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_THROBBER, GimpThrobberClass))
+#define GIMP_IS_THROBBER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_THROBBER))
+#define GIMP_IS_THROBBER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_THROBBER))
+#define GIMP_THROBBER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GIMP_TYPE_THROBBER, GimpThrobberClass))
+
+
+typedef struct _GimpThrobber GimpThrobber;
+typedef struct _GimpThrobberClass GimpThrobberClass;
+typedef struct _GimpThrobberPrivate GimpThrobberPrivate;
+
+struct _GimpThrobber
+{
+ GtkToolItem parent;
+
+ /*< private >*/
+ GimpThrobberPrivate *priv;
+};
+
+struct _GimpThrobberClass
+{
+ GtkToolItemClass parent_class;
+
+ /* signal */
+ void (* clicked) (GimpThrobber *button);
+};
+
+GType gimp_throbber_get_type (void) G_GNUC_CONST;
+
+GtkToolItem * gimp_throbber_new (const gchar *icon_name);
+void gimp_throbber_set_icon_name (GimpThrobber *button,
+ const gchar *icon_name);
+const gchar * gimp_throbber_get_icon_name (GimpThrobber *button);
+void gimp_throbber_set_image (GimpThrobber *button,
+ GtkWidget *image);
+GtkWidget * gimp_throbber_get_image (GimpThrobber *button);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THROBBER_H__ */
diff --git a/plug-ins/help-browser/gimpthrobberaction.c b/plug-ins/help-browser/gimpthrobberaction.c
new file mode 100644
index 0000000..56f617f
--- /dev/null
+++ b/plug-ins/help-browser/gimpthrobberaction.c
@@ -0,0 +1,133 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpThrobberAction
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "gimpthrobberaction.h"
+#include "gimpthrobber.h"
+
+
+
+static void gimp_throbber_action_class_init (GimpThrobberActionClass *klass);
+
+static void gimp_throbber_action_connect_proxy (GtkAction *action,
+ GtkWidget *proxy);
+static void gimp_throbber_action_sync_property (GtkAction *action,
+ GParamSpec *pspec,
+ GtkWidget *proxy);
+
+
+static GtkActionClass *parent_class = NULL;
+
+
+GType
+gimp_throbber_action_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type)
+ {
+ static const GTypeInfo type_info =
+ {
+ sizeof (GtkActionClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gimp_throbber_action_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL,
+ sizeof (GtkAction),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) NULL
+ };
+
+ type = g_type_register_static (GTK_TYPE_ACTION,
+ "GimpThrobberAction",
+ &type_info, 0);
+ }
+
+ return type;
+}
+
+static void
+gimp_throbber_action_class_init (GimpThrobberActionClass *klass)
+{
+ GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ action_class->connect_proxy = gimp_throbber_action_connect_proxy;
+ action_class->toolbar_item_type = GIMP_TYPE_THROBBER;
+}
+
+static void
+gimp_throbber_action_connect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+
+ GTK_ACTION_CLASS (parent_class)->connect_proxy (action, proxy);
+
+ if (GIMP_IS_THROBBER (proxy))
+ {
+ GParamSpec *pspec;
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (action),
+ "icon-name");
+
+ gimp_throbber_action_sync_property (action, pspec, proxy);
+ g_signal_connect_object (action, "notify::icon-name",
+ G_CALLBACK (gimp_throbber_action_sync_property),
+ proxy, 0);
+
+ g_signal_connect_object (proxy, "clicked",
+ G_CALLBACK (gtk_action_activate), action,
+ G_CONNECT_SWAPPED);
+ }
+}
+
+static void
+gimp_throbber_action_sync_property (GtkAction *action,
+ GParamSpec *pspec,
+ GtkWidget *proxy)
+{
+ const gchar *property = g_param_spec_get_name (pspec);
+ GValue value = G_VALUE_INIT;
+
+ g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+ g_object_get_property (G_OBJECT (action), property, &value);
+
+ g_object_set_property (G_OBJECT (proxy), property, &value);
+ g_value_unset (&value);
+}
+
+GtkAction *
+gimp_throbber_action_new (const gchar *name,
+ const gchar *label,
+ const gchar *tooltip,
+ const gchar *icon_name)
+{
+ return g_object_new (GIMP_TYPE_THROBBER_ACTION,
+ "name", name,
+ "label", label,
+ "tooltip", tooltip,
+ "icon-name", icon_name,
+ NULL);
+}
diff --git a/plug-ins/help-browser/gimpthrobberaction.h b/plug-ins/help-browser/gimpthrobberaction.h
new file mode 100644
index 0000000..502a32b
--- /dev/null
+++ b/plug-ins/help-browser/gimpthrobberaction.h
@@ -0,0 +1,48 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * GimpThrobberAction
+ * Copyright (C) 2005 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_THROBBER_ACTION_H__
+#define __GIMP_THROBBER_ACTION_H__
+
+G_BEGIN_DECLS
+
+#define GIMP_TYPE_THROBBER_ACTION (gimp_throbber_action_get_type ())
+#define GIMP_THROBBER_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_THROBBER_ACTION, GimpThrobberAction))
+#define GIMP_THROBBER_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_THROBBER_ACTION, GimpThrobberActionClass))
+#define GIMP_IS_THROBBER_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_THROBBER_ACTION))
+#define GIMP_IS_THROBBER_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GIMP_TYPE_THROBBER_ACTION))
+#define GIMP_THROBBER_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GIMP_TYPE_THROBBER_ACTION, GimpThrobberActionClass))
+
+
+typedef GtkAction GimpThrobberAction;
+typedef GtkActionClass GimpThrobberActionClass;
+
+
+GType gimp_throbber_action_get_type (void) G_GNUC_CONST;
+
+GtkAction * gimp_throbber_action_new (const gchar *name,
+ const gchar *label,
+ const gchar *tooltip,
+ const gchar *icon_name);
+
+
+G_END_DECLS
+
+#endif /* __GIMP_THROBBER_ACTION_H__ */
diff --git a/plug-ins/help-browser/help-browser.c b/plug-ins/help-browser/help-browser.c
new file mode 100644
index 0000000..35a2ea6
--- /dev/null
+++ b/plug-ins/help-browser/help-browser.c
@@ -0,0 +1,322 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help Browser
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * Some code & ideas taken from the GNOME help browser.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h> /* strlen, strcmp */
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "plug-ins/help/gimphelp.h"
+
+#include "dialog.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* defines */
+
+#define GIMP_HELP_BROWSER_EXT_PROC "extension-gimp-help-browser"
+#define GIMP_HELP_BROWSER_TEMP_EXT_PROC "extension-gimp-help-browser-temp"
+#define PLUG_IN_BINARY "help-browser"
+#define PLUG_IN_ROLE "gimp-help-browser"
+
+
+/* forward declarations */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void temp_proc_install (void);
+static void temp_proc_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean help_browser_show_help (const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id);
+
+static GimpHelpProgress * help_browser_progress_new (void);
+
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_INT32, "num-domain-names", "" },
+ { GIMP_PDB_STRINGARRAY, "domain-names", "" },
+ { GIMP_PDB_INT32, "num-domain-uris", "" },
+ { GIMP_PDB_STRINGARRAY, "domain-uris", "" }
+ };
+
+ gimp_install_procedure (GIMP_HELP_BROWSER_EXT_PROC,
+ "Browse the GIMP user manual",
+ "A small and simple HTML browser optimized for "
+ "browsing the GIMP user manual.",
+ "Sven Neumann <sven@gimp.org>, "
+ "Michael Natterer <mitch@gimp.org>"
+ "Henrik Brix Andersen <brix@gimp.org>",
+ "Sven Neumann, Michael Natterer & Henrik Brix Andersen",
+ "1999-2008",
+ NULL,
+ "",
+ GIMP_EXTENSION,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpRunMode run_mode = param[0].data.d_int32;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ static GimpParam values[1];
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ INIT_I18N ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_NONINTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Make sure all the arguments are there! */
+ if (nparams >= 1)
+ {
+ if (nparams == 5)
+ {
+ if (! gimp_help_init (param[1].data.d_int32,
+ param[2].data.d_stringarray,
+ param[3].data.d_int32,
+ param[4].data.d_stringarray))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ browser_dialog_open (PLUG_IN_BINARY);
+
+ temp_proc_install ();
+
+ gimp_extension_ack ();
+ gimp_extension_enable ();
+
+ gtk_main ();
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ default:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static void
+temp_proc_install (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "help-domain", "Help domain to use" },
+ { GIMP_PDB_STRING, "help-locales", "Language to use" },
+ { GIMP_PDB_STRING, "help-id", "Help ID to open" }
+ };
+
+ gimp_install_temp_proc (GIMP_HELP_BROWSER_TEMP_EXT_PROC,
+ "DON'T USE THIS ONE",
+ "(Temporary procedure)",
+ "Sven Neumann <sven@gimp.org>, "
+ "Michael Natterer <mitch@gimp.org>"
+ "Henrik Brix Andersen <brix@gimp.org>",
+ "Sven Neumann, Michael Natterer & Henrik Brix Andersen",
+ "1999-2008",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ temp_proc_run);
+}
+
+static void
+temp_proc_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ /* make sure all the arguments are there */
+ if (nparams == 3)
+ {
+ const gchar *help_domain = GIMP_HELP_DEFAULT_DOMAIN;
+ const gchar *help_locales = NULL;
+ const gchar *help_id = GIMP_HELP_DEFAULT_ID;
+
+ if (param[0].data.d_string && strlen (param[0].data.d_string))
+ help_domain = param[0].data.d_string;
+
+ if (param[1].data.d_string && strlen (param[1].data.d_string))
+ help_locales = param[1].data.d_string;
+
+ if (param[2].data.d_string && strlen (param[2].data.d_string))
+ help_id = param[2].data.d_string;
+
+ if (! help_browser_show_help (help_domain, help_locales, help_id))
+ {
+ gtk_main_quit ();
+ }
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static gboolean
+help_browser_show_help (const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id)
+{
+ GimpHelpDomain *domain;
+ gboolean success = TRUE;
+
+ domain = gimp_help_lookup_domain (help_domain);
+
+ if (domain)
+ {
+ GimpHelpProgress *progress = NULL;
+ GimpHelpLocale *locale;
+ GList *locales;
+ gchar *uri;
+ gboolean fatal_error;
+
+ locales = gimp_help_parse_locales (help_locales);
+
+ if (! g_str_has_prefix (domain->help_uri, "file:"))
+ progress = help_browser_progress_new ();
+
+ uri = gimp_help_domain_map (domain, locales, help_id,
+ progress, &locale, &fatal_error);
+
+ if (progress)
+ gimp_help_progress_free (progress);
+
+ g_list_free_full (locales, (GDestroyNotify) g_free);
+
+ if (uri)
+ {
+ browser_dialog_make_index (domain, locale);
+ browser_dialog_load (uri);
+
+ g_free (uri);
+ }
+ else if (fatal_error)
+ {
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
+
+static void
+help_browser_progress_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ gimp_progress_init (message);
+}
+
+static void
+help_browser_progress_update (gdouble value,
+ gpointer user_data)
+{
+ gimp_progress_update (value);
+}
+
+static void
+help_browser_progress_end (gpointer user_data)
+{
+ gimp_progress_end ();
+}
+
+static GimpHelpProgress *
+help_browser_progress_new (void)
+{
+ static const GimpHelpProgressVTable vtable =
+ {
+ help_browser_progress_start,
+ help_browser_progress_end,
+ help_browser_progress_update
+ };
+
+ return gimp_help_progress_new (&vtable, NULL);
+}
diff --git a/plug-ins/help-browser/uri.c b/plug-ins/help-browser/uri.c
new file mode 100644
index 0000000..c0493fa
--- /dev/null
+++ b/plug-ins/help-browser/uri.c
@@ -0,0 +1,386 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help Browser - URI functions
+ * Copyright (C) 2001 Jacob Schroeder <jacob@convergence.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "uri.h"
+
+/* #define URI_DEBUG 1 */
+
+typedef enum
+{
+ URI_UNKNOWN,
+ URI_ABSURI,
+ URI_NETPATH,
+ URI_ABSPATH,
+ URI_RELPATH,
+ URI_QUERY,
+ URI_EMPTY,
+ URI_FRAGMENT,
+ URI_INVALID
+} UriType;
+
+
+static UriType
+uri_get_type (const gchar *uri)
+{
+ gchar c;
+ const gchar *cptr;
+ UriType type = URI_UNKNOWN;
+
+ if (!uri)
+ return type;
+
+ cptr = uri;
+ c = *cptr++;
+
+ if (g_ascii_isalpha (c))
+ {
+ type = URI_RELPATH; /* assume relative path */
+
+ while ((c = *cptr++))
+ {
+ if (g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.')
+ continue;
+
+ if (c == ':')
+ {
+ /* it was a scheme */
+ type = URI_ABSURI;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (c)
+ {
+ case '/':
+ if (*cptr == '/')
+ {
+ cptr++;
+ type = URI_NETPATH;
+ }
+ else
+ {
+ type = URI_ABSPATH;
+ }
+ break;
+ case '?':
+ type = URI_QUERY;
+ break;
+ case '#':
+ type = URI_FRAGMENT;
+ break;
+ case '\0':
+ type = URI_EMPTY;
+ break;
+ default:
+ type = URI_RELPATH;
+ break;
+ }
+ }
+
+#ifdef URI_DEBUG
+ g_print ("uri_get_type (\"%s\") -> ", uri);
+ switch (type)
+ {
+ case URI_UNKNOWN: g_print ("unknown"); break;
+ case URI_ABSURI: g_print ("absuri"); break;
+ case URI_NETPATH: g_print ("netpath"); break;
+ case URI_ABSPATH: g_print ("abspath"); break;
+ case URI_RELPATH: g_print ("relpath"); break;
+ case URI_QUERY: g_print ("query"); break;
+ case URI_EMPTY: g_print ("empty"); break;
+ case URI_FRAGMENT: g_print ("fragment"); break;
+ case URI_INVALID: g_print ("invalid"); break;
+ }
+ g_print ("\n");
+#endif
+
+ return type;
+}
+
+gchar *
+uri_to_abs (const gchar *uri,
+ const gchar *base_uri)
+{
+ gchar c;
+ const gchar *cptr;
+ gchar *retval = NULL;
+ UriType uri_type = URI_UNKNOWN;
+ UriType base_type = URI_UNKNOWN;
+
+ gint base_cnt = 0; /* no of chars to be copied from base URI */
+ gint uri_cnt = 0; /* no of chars to be copied from URI */
+ gint sep_cnt = 0; /* no of chars to be inserted between them */
+
+ const gchar *sep_str = ""; /* string to insert between base and uri */
+ const gchar *part;
+ const gchar *last_segment = NULL;
+
+#ifdef URI_DEBUG
+ g_print ("uri_to_abs (\"%s\", \"%s\")\n", uri, base_uri);
+#endif
+
+ /* this function does not use the algorithm that is being proposed
+ * in RFC 2396. Instead it analyses the first characters of each
+ * URI to determine its kind (abs, net, path, ...).
+ * After that it locates the missing parts in the base URI and then
+ * concats everything into a newly allocated string.
+ */
+
+ /* determine the kind of the URIs */
+ uri_type = uri_get_type (uri);
+
+ if (uri_type != URI_ABSURI)
+ {
+ base_type = uri_get_type (base_uri);
+
+ if (base_type != URI_ABSURI)
+ return NULL; /* neither uri nor base uri are absolute */
+ }
+
+ /* find missing parts in base URI */
+ switch (uri_type)
+ {
+ case URI_ABSURI:
+ /* base uri not needed */
+ break;
+
+ case URI_QUERY:
+ /* ??? last segment? */
+ uri_type = URI_RELPATH;
+ case URI_NETPATH: /* base scheme */
+ case URI_ABSPATH: /* base scheme and authority */
+ case URI_RELPATH: /* base scheme, authority and path */
+ cptr = base_uri;
+
+ /* skip scheme */
+ while ((c = *cptr++) && c != ':')
+ ; /* nada */
+
+ base_cnt = cptr - base_uri; /* incl : */
+
+ if (*cptr != '/')
+ {
+ /* completion not possible */
+ return NULL;
+ }
+
+ if (uri_type == URI_NETPATH)
+ break;
+
+ /* skip authority */
+ if (cptr[0] == '/' && cptr[1] == '/')
+ {
+ part = cptr;
+ cptr += 2;
+
+ while ((c = *cptr++) && c != '/' && c != '?' && c != '#')
+ ; /* nada */
+
+ cptr--;
+ base_cnt += cptr - part;
+ }
+
+ if (uri_type == URI_ABSPATH)
+ break;
+
+ /* skip path */
+ if (*cptr != '/')
+ {
+ sep_cnt = 1;
+ sep_str = "/";
+ break;
+ }
+
+ part = cptr;
+
+ g_assert (*cptr == '/');
+
+ while ((c = *cptr++) && c != '?' && c != '#')
+ {
+ if (c == '/')
+ last_segment = cptr - 1;
+ };
+
+ g_assert (last_segment);
+
+ cptr = last_segment;
+
+ while ((c = *uri) && c == '.' && cptr > part)
+ {
+ gint shift_segment = 0;
+
+ c = uri[1];
+
+ if (c == '.' )
+ {
+ c = uri[2];
+ shift_segment = 1;
+ }
+
+ if (c == '/')
+ {
+ uri += 2;
+ }
+ else if (c == 0 || c == '?' || c == '#')
+ {
+ uri += 1;
+ }
+ else
+ {
+ break;
+ }
+
+ g_assert (*cptr == '/');
+
+ if (shift_segment)
+ {
+ uri += 1;
+ while (cptr > part && *--cptr != '/')
+ ; /* nada */
+ }
+ }
+
+ base_cnt += cptr - part + 1;
+ break;
+
+ case URI_EMPTY:
+ case URI_FRAGMENT:
+ /* use whole base uri */
+ base_cnt = strlen (base_uri);
+ break;
+
+ case URI_UNKNOWN:
+ case URI_INVALID:
+ return NULL;
+ }
+
+ /* do not include fragment part from the URI reference */
+ for (cptr = uri; (c = *cptr) && c != '#'; cptr++)
+ ; /* nada */
+
+ uri_cnt = cptr - uri;
+
+ /* allocate string and copy characters */
+
+ retval = g_new (gchar, base_cnt + sep_cnt + uri_cnt + 1);
+
+ if (base_cnt)
+ strncpy (retval, base_uri, base_cnt);
+
+ if (sep_cnt)
+ strncpy (retval + base_cnt, sep_str, sep_cnt);
+
+ if (uri_cnt)
+ strncpy (retval + base_cnt + sep_cnt, uri, uri_cnt);
+
+ retval[base_cnt + sep_cnt + uri_cnt] = '\0';
+
+#ifdef URI_DEBUG
+ g_print (" -> \"%s\"\n", retval);
+#endif
+
+ return retval;
+}
+
+#if 0
+RFC 2396 URI Generic Syntax August 1998
+
+
+A. Collected BNF for URI
+
+ URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ absoluteURI = scheme ":" ( hier_part | opaque_part )
+ relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+
+ hier_part = ( net_path | abs_path ) [ "?" query ]
+ opaque_part = uric_no_slash *uric
+
+ uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ "&" | "=" | "+" | "$" | ","
+
+ net_path = "//" authority [ abs_path ]
+ abs_path = "/" path_segments
+ rel_path = rel_segment [ abs_path ]
+
+ rel_segment = 1*( unreserved | escaped |
+ ";" | "@" | "&" | "=" | "+" | "$" | "," )
+
+ scheme = alpha *( alpha | digit | "+" | "-" | "." )
+
+ authority = server | reg_name
+
+ reg_name = 1*( unreserved | escaped | "$" | "," |
+ ";" | ":" | "@" | "&" | "=" | "+" )
+
+ server = [ [ userinfo "@" ] hostport ]
+ userinfo = *( unreserved | escaped |
+ ";" | ":" | "&" | "=" | "+" | "$" | "," )
+
+ hostport = host [ ":" port ]
+ host = hostname | IPv4address
+ hostname = *( domainlabel "." ) toplabel [ "." ]
+ domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
+ port = *digit
+
+ path = [ abs_path | opaque_part ]
+ path_segments = segment *( "/" segment )
+ segment = *pchar *( ";" param )
+ param = *pchar
+ pchar = unreserved | escaped |
+ ":" | "@" | "&" | "=" | "+" | "$" | ","
+
+ query = *uric
+
+ fragment = *uric
+
+ uric = reserved | unreserved | escaped
+ reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ "$" | ","
+ unreserved = alphanum | mark
+ mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ "(" | ")"
+
+ escaped = "%" hex hex
+ hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ "a" | "b" | "c" | "d" | "e" | "f"
+
+ alphanum = alpha | digit
+ alpha = lowalpha | upalpha
+
+ lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
+ "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
+ "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
+ upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ "8" | "9"
+
+#endif
diff --git a/plug-ins/help-browser/uri.h b/plug-ins/help-browser/uri.h
new file mode 100644
index 0000000..a1aaf1e
--- /dev/null
+++ b/plug-ins/help-browser/uri.h
@@ -0,0 +1,22 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help Browser - URI functions
+ * Copyright (C) 2001 Jacob Schroeder <jacob@convergence.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+gchar * uri_to_abs (const gchar *uri,
+ const gchar *base_uri);
diff --git a/plug-ins/help/Makefile.am b/plug-ins/help/Makefile.am
new file mode 100644
index 0000000..4e0dd41
--- /dev/null
+++ b/plug-ins/help/Makefile.am
@@ -0,0 +1,90 @@
+## Process this file with automake to produce Makefile.in
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+libgimphelp = libgimphelp.a
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+help_RC = help.rc.o
+endif
+
+AM_CPPFLAGS = \
+ -DDATADIR=\""$(gimpdatadir)"\" \
+ -I$(top_srcdir) \
+ $(CAIRO_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(mwindows) \
+ $(framework_cocoa) \
+ $(xnone)
+
+noinst_LIBRARIES = libgimphelp.a
+
+libgimphelp_a_SOURCES = \
+ gimphelptypes.h \
+ gimphelp.c \
+ gimphelp.h \
+ gimphelpdomain.c \
+ gimphelpdomain.h \
+ gimphelpitem.c \
+ gimphelpitem.h \
+ gimphelplocale.c \
+ gimphelplocale.h \
+ gimphelpprogress.c \
+ gimphelpprogress.h \
+ gimphelpprogress-private.h
+
+libexecdir = $(gimpplugindir)/plug-ins/help
+
+libexec_PROGRAMS = help
+
+help_SOURCES = help.c
+
+LDADD = \
+ $(libgimphelp) \
+ $(libgimp) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmath) \
+ $(CAIRO_LIBS) \
+ $(GIO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(help_RC)
+
+
+noinst_PROGRAMS = gimp-help-lookup
+
+gimp_help_lookup_SOURCES = gimp-help-lookup.c
+
+gimp_help_lookup_LDADD = \
+ $(libgimphelp) \
+ $(libgimpbase) \
+ $(GIO_LIBS)
diff --git a/plug-ins/help/Makefile.in b/plug-ins/help/Makefile.in
new file mode 100644
index 0000000..e9a858d
--- /dev/null
+++ b/plug-ins/help/Makefile.in
@@ -0,0 +1,1098 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = help$(EXEEXT)
+noinst_PROGRAMS = gimp-help-lookup$(EXEEXT)
+subdir = plug-ins/help
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS)
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libgimphelp_a_AR = $(AR) $(ARFLAGS)
+libgimphelp_a_LIBADD =
+am_libgimphelp_a_OBJECTS = gimphelp.$(OBJEXT) gimphelpdomain.$(OBJEXT) \
+ gimphelpitem.$(OBJEXT) gimphelplocale.$(OBJEXT) \
+ gimphelpprogress.$(OBJEXT)
+libgimphelp_a_OBJECTS = $(am_libgimphelp_a_OBJECTS)
+am_gimp_help_lookup_OBJECTS = gimp-help-lookup.$(OBJEXT)
+gimp_help_lookup_OBJECTS = $(am_gimp_help_lookup_OBJECTS)
+am__DEPENDENCIES_1 =
+gimp_help_lookup_DEPENDENCIES = $(libgimphelp) $(libgimpbase) \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_help_OBJECTS = help.$(OBJEXT)
+help_OBJECTS = $(am_help_OBJECTS)
+help_LDADD = $(LDADD)
+help_DEPENDENCIES = $(libgimphelp) $(libgimp) $(libgimpconfig) \
+ $(libgimpcolor) $(libgimpbase) $(libgimpmath) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(help_RC)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimp-help-lookup.Po \
+ ./$(DEPDIR)/gimphelp.Po ./$(DEPDIR)/gimphelpdomain.Po \
+ ./$(DEPDIR)/gimphelpitem.Po ./$(DEPDIR)/gimphelplocale.Po \
+ ./$(DEPDIR)/gimphelpprogress.Po ./$(DEPDIR)/help.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libgimphelp_a_SOURCES) $(gimp_help_lookup_SOURCES) \
+ $(help_SOURCES)
+DIST_SOURCES = $(libgimphelp_a_SOURCES) $(gimp_help_lookup_SOURCES) \
+ $(help_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/help
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+libgimphelp = libgimphelp.a
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@help_RC = help.rc.o
+AM_CPPFLAGS = \
+ -DDATADIR=\""$(gimpdatadir)"\" \
+ -I$(top_srcdir) \
+ $(CAIRO_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GDK_PIXBUF_CFLAGS) \
+ -I$(includedir)
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(mwindows) \
+ $(framework_cocoa) \
+ $(xnone)
+
+noinst_LIBRARIES = libgimphelp.a
+libgimphelp_a_SOURCES = \
+ gimphelptypes.h \
+ gimphelp.c \
+ gimphelp.h \
+ gimphelpdomain.c \
+ gimphelpdomain.h \
+ gimphelpitem.c \
+ gimphelpitem.h \
+ gimphelplocale.c \
+ gimphelplocale.h \
+ gimphelpprogress.c \
+ gimphelpprogress.h \
+ gimphelpprogress-private.h
+
+help_SOURCES = help.c
+LDADD = \
+ $(libgimphelp) \
+ $(libgimp) \
+ $(libgimpconfig) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpmath) \
+ $(CAIRO_LIBS) \
+ $(GIO_LIBS) \
+ $(GDK_PIXBUF_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(help_RC)
+
+gimp_help_lookup_SOURCES = gimp-help-lookup.c
+gimp_help_lookup_LDADD = \
+ $(libgimphelp) \
+ $(libgimpbase) \
+ $(GIO_LIBS)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/help/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/help/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libgimphelp.a: $(libgimphelp_a_OBJECTS) $(libgimphelp_a_DEPENDENCIES) $(EXTRA_libgimphelp_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libgimphelp.a
+ $(AM_V_AR)$(libgimphelp_a_AR) libgimphelp.a $(libgimphelp_a_OBJECTS) $(libgimphelp_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libgimphelp.a
+
+gimp-help-lookup$(EXEEXT): $(gimp_help_lookup_OBJECTS) $(gimp_help_lookup_DEPENDENCIES) $(EXTRA_gimp_help_lookup_DEPENDENCIES)
+ @rm -f gimp-help-lookup$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gimp_help_lookup_OBJECTS) $(gimp_help_lookup_LDADD) $(LIBS)
+
+help$(EXEEXT): $(help_OBJECTS) $(help_DEPENDENCIES) $(EXTRA_help_DEPENDENCIES)
+ @rm -f help$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(help_OBJECTS) $(help_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimp-help-lookup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelpdomain.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelpitem.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelplocale.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimphelpprogress.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(LIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ clean-noinstLIBRARIES clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/gimp-help-lookup.Po
+ -rm -f ./$(DEPDIR)/gimphelp.Po
+ -rm -f ./$(DEPDIR)/gimphelpdomain.Po
+ -rm -f ./$(DEPDIR)/gimphelpitem.Po
+ -rm -f ./$(DEPDIR)/gimphelplocale.Po
+ -rm -f ./$(DEPDIR)/gimphelpprogress.Po
+ -rm -f ./$(DEPDIR)/help.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/gimp-help-lookup.Po
+ -rm -f ./$(DEPDIR)/gimphelp.Po
+ -rm -f ./$(DEPDIR)/gimphelpdomain.Po
+ -rm -f ./$(DEPDIR)/gimphelpitem.Po
+ -rm -f ./$(DEPDIR)/gimphelplocale.Po
+ -rm -f ./$(DEPDIR)/gimphelpprogress.Po
+ -rm -f ./$(DEPDIR)/help.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ clean-noinstLIBRARIES clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/help/gimp-help-lookup.c b/plug-ins/help/gimp-help-lookup.c
new file mode 100644
index 0000000..6b9f20e
--- /dev/null
+++ b/plug-ins/help/gimp-help-lookup.c
@@ -0,0 +1,195 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimp-help-lookup - a standalone gimp-help ID to filename mapper
+ * Copyright (C) 2004-2008 Sven Neumann <sven@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libgimp/gimp.h"
+
+#include "gimphelp.h"
+
+
+static void show_version (void) G_GNUC_NORETURN;
+
+static gchar * lookup (const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id);
+
+static GimpHelpProgress * progress_new (void);
+
+
+static const gchar *help_base = NULL;
+static gchar *help_root = NULL;
+static const gchar *help_locales = NULL;
+static const gchar **help_ids = NULL;
+
+static gboolean be_verbose = FALSE;
+
+
+static const GOptionEntry entries[] =
+{
+ { "version", 'v', 0,
+ G_OPTION_ARG_CALLBACK, (GOptionArgFunc) show_version,
+ "Show version information and exit", NULL
+ },
+ { "base", 'b', 0,
+ G_OPTION_ARG_STRING, &help_base,
+ "Specifies base URI", "URI"
+ },
+ { "root", 'r', 0,
+ G_OPTION_ARG_FILENAME, &help_root,
+ "Specifies root directory for index files", "DIR"
+ },
+ { "lang", 'l', 0,
+ G_OPTION_ARG_STRING, &help_locales,
+ "Specifies help language", "LANG"
+ },
+ {
+ "verbose", 0, 0,
+ G_OPTION_ARG_NONE, &be_verbose,
+ "Be more verbose", NULL
+ },
+ {
+ G_OPTION_REMAINING, 0, 0,
+ G_OPTION_ARG_STRING_ARRAY, &help_ids,
+ NULL, NULL
+ },
+ { NULL }
+};
+
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ GOptionContext *context;
+ gchar *uri;
+ GError *error = NULL;
+
+ help_base = g_getenv (GIMP_HELP_ENV_URI);
+ help_root = g_build_filename (gimp_data_directory (), GIMP_HELP_PREFIX, NULL);
+
+ context = g_option_context_new ("HELP-ID");
+ g_option_context_add_main_entries (context, entries, NULL);
+
+ if (! g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ return EXIT_FAILURE;
+ }
+
+ if (help_base)
+ uri = g_strdup (help_base);
+ else
+ uri = g_filename_to_uri (help_root, NULL, NULL);
+
+ gimp_help_register_domain (GIMP_HELP_DEFAULT_DOMAIN, uri);
+ g_free (uri);
+
+ uri = lookup (GIMP_HELP_DEFAULT_DOMAIN,
+ help_locales ? help_locales : GIMP_HELP_DEFAULT_LOCALE,
+ help_ids ? help_ids[0] : GIMP_HELP_DEFAULT_ID);
+
+ if (uri)
+ {
+ g_print ("%s\n", uri);
+ g_free (uri);
+ }
+
+ g_option_context_free (context);
+ g_free (help_root);
+
+ return uri ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static gchar *
+lookup (const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id)
+{
+ GimpHelpDomain *domain = gimp_help_lookup_domain (help_domain);
+
+ if (domain)
+ {
+ GimpHelpProgress *progress = progress_new ();
+ GList *locales;
+ gchar *full_uri;
+
+ locales = gimp_help_parse_locales (help_locales);
+ full_uri = gimp_help_domain_map (domain, locales, help_id, progress,
+ NULL, NULL);
+
+ gimp_help_progress_free (progress);
+
+ g_list_free_full (locales, (GDestroyNotify) g_free);
+
+ return full_uri;
+ }
+
+ return NULL;
+}
+
+static void
+show_version (void)
+{
+ g_print ("gimp-help-lookup version %s\n", GIMP_VERSION);
+ exit (EXIT_SUCCESS);
+}
+
+
+static void
+progress_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ if (be_verbose)
+ g_printerr ("\n%s\n", message);
+}
+
+static void
+progress_end (gpointer user_data)
+{
+ if (be_verbose)
+ g_printerr ("done\n");
+}
+
+static void
+progress_set_value (gdouble percentage,
+ gpointer user_data)
+{
+ if (be_verbose)
+ g_printerr (".");
+}
+
+static GimpHelpProgress *
+progress_new (void)
+{
+ const GimpHelpProgressVTable vtable =
+ {
+ progress_start,
+ progress_end,
+ progress_set_value
+ };
+
+ return gimp_help_progress_new (&vtable, NULL);
+}
diff --git a/plug-ins/help/gimphelp.c b/plug-ins/help/gimphelp.c
new file mode 100644
index 0000000..111c10b
--- /dev/null
+++ b/plug-ins/help/gimphelp.c
@@ -0,0 +1,151 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is written so that it can also be compiled standalone.
+ * It shouldn't depend on libgimp.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "libgimp/gimp.h"
+
+#include "gimphelp.h"
+
+#ifdef DISABLE_NLS
+#define _(String) (String)
+#else
+#include "libgimp/stdplugins-intl.h"
+#endif
+
+
+/* private variables */
+
+static GHashTable *domain_hash = NULL;
+
+
+/* public functions */
+
+gboolean
+gimp_help_init (gint num_domain_names,
+ gchar **domain_names,
+ gint num_domain_uris,
+ gchar **domain_uris)
+{
+ gint i;
+
+ if (num_domain_names != num_domain_uris)
+ {
+ g_printerr ("help: number of names doesn't match number of URIs.\n");
+
+ return FALSE;
+ }
+
+ for (i = 0; i < num_domain_names; i++)
+ gimp_help_register_domain (domain_names[i], domain_uris[i]);
+
+ return TRUE;
+}
+
+void
+gimp_help_exit (void)
+{
+ if (domain_hash)
+ {
+ g_hash_table_destroy (domain_hash);
+ domain_hash = NULL;
+ }
+}
+
+void
+gimp_help_register_domain (const gchar *domain_name,
+ const gchar *domain_uri)
+{
+ g_return_if_fail (domain_name != NULL);
+ g_return_if_fail (domain_uri != NULL);
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help: registering help domain \"%s\" with base uri \"%s\"\n",
+ domain_name, domain_uri);
+#endif
+
+ if (! domain_hash)
+ domain_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_help_domain_free);
+
+ g_hash_table_insert (domain_hash,
+ g_strdup (domain_name),
+ gimp_help_domain_new (domain_name, domain_uri));
+}
+
+GimpHelpDomain *
+gimp_help_lookup_domain (const gchar *domain_name)
+{
+ g_return_val_if_fail (domain_name, NULL);
+
+ if (domain_hash)
+ return g_hash_table_lookup (domain_hash, domain_name);
+
+ return NULL;
+}
+
+GList *
+gimp_help_parse_locales (const gchar *help_locales)
+{
+ GList *locales = NULL;
+ GList *list;
+ const gchar *s;
+ const gchar *p;
+
+ g_return_val_if_fail (help_locales != NULL, NULL);
+
+ /* split the string at colons, building a list */
+ s = help_locales;
+ for (p = strchr (s, ':'); p; p = strchr (s, ':'))
+ {
+ gchar *new = g_strndup (s, p - s);
+ locales = g_list_append (locales, new);
+ s = p + 1;
+ }
+
+ if (*s)
+ locales = g_list_append (locales, g_strdup (s));
+
+ /* if the list doesn't contain the default locale yet, append it */
+ for (list = locales; list; list = list->next)
+ if (strcmp ((const gchar *) list->data, GIMP_HELP_DEFAULT_LOCALE) == 0)
+ break;
+
+ if (! list)
+ locales = g_list_append (locales, g_strdup (GIMP_HELP_DEFAULT_LOCALE));
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help: locales: ");
+ for (list = locales; list; list = list->next)
+ g_printerr ("%s ", (const gchar *) list->data);
+ g_printerr ("\n");
+#endif
+
+ return locales;
+}
diff --git a/plug-ins/help/gimphelp.h b/plug-ins/help/gimphelp.h
new file mode 100644
index 0000000..0ba9db2
--- /dev/null
+++ b/plug-ins/help/gimphelp.h
@@ -0,0 +1,58 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_H__
+#define __GIMP_HELP_H__
+
+
+#include "gimphelptypes.h"
+
+#include "gimphelpdomain.h"
+#include "gimphelpitem.h"
+#include "gimphelplocale.h"
+#include "gimphelpprogress.h"
+
+
+#define GIMP_HELP_DEFAULT_DOMAIN "https://www.gimp.org/help"
+#define GIMP_HELP_DEFAULT_ID "gimp-main"
+#define GIMP_HELP_DEFAULT_LOCALE "en"
+
+#define GIMP_HELP_PREFIX "help"
+#define GIMP_HELP_ENV_URI "GIMP2_HELP_URI"
+
+/* #define GIMP_HELP_DEBUG */
+
+
+gboolean gimp_help_init (gint n_domain_names,
+ gchar **domain_names,
+ gint n_domain_uris,
+ gchar **domain_uris);
+void gimp_help_exit (void);
+
+void gimp_help_register_domain (const gchar *domain_name,
+ const gchar *domain_uri);
+GimpHelpDomain * gimp_help_lookup_domain (const gchar *domain_name);
+
+GList * gimp_help_parse_locales (const gchar *help_locales);
+
+
+#endif /* ! __GIMP_HELP_H__ */
diff --git a/plug-ins/help/gimphelpdomain.c b/plug-ins/help/gimphelpdomain.c
new file mode 100644
index 0000000..34997e3
--- /dev/null
+++ b/plug-ins/help/gimphelpdomain.c
@@ -0,0 +1,253 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is written so that it can also be compiled standalone.
+ * It shouldn't depend on libgimp.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "libgimpbase/gimpbase.h"
+
+#include "gimphelp.h"
+
+#ifdef DISABLE_NLS
+#define _(String) (String)
+#else
+#include "libgimp/stdplugins-intl.h"
+#endif
+
+
+/* local function prototypes */
+
+static gboolean domain_locale_parse (GimpHelpDomain *domain,
+ GimpHelpLocale *locale,
+ GimpHelpProgress *progress,
+ GError **error);
+
+
+/* public functions */
+
+GimpHelpDomain *
+gimp_help_domain_new (const gchar *domain_name,
+ const gchar *domain_uri)
+{
+ GimpHelpDomain *domain = g_slice_new0 (GimpHelpDomain);
+
+ domain->help_domain = g_strdup (domain_name);
+ domain->help_uri = g_strdup (domain_uri);
+
+ if (domain_uri)
+ {
+ /* strip trailing slash */
+ if (g_str_has_suffix (domain->help_uri, "/"))
+ domain->help_uri[strlen (domain->help_uri) - 1] = '\0';
+ }
+
+ return domain;
+}
+
+void
+gimp_help_domain_free (GimpHelpDomain *domain)
+{
+ g_return_if_fail (domain != NULL);
+
+ if (domain->help_locales)
+ g_hash_table_destroy (domain->help_locales);
+
+ g_free (domain->help_domain);
+ g_free (domain->help_uri);
+
+ g_slice_free (GimpHelpDomain, domain);
+}
+
+GimpHelpLocale *
+gimp_help_domain_lookup_locale (GimpHelpDomain *domain,
+ const gchar *locale_id,
+ GimpHelpProgress *progress)
+{
+ GimpHelpLocale *locale = NULL;
+
+ if (domain->help_locales)
+ locale = g_hash_table_lookup (domain->help_locales, locale_id);
+ else
+ domain->help_locales =
+ g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_help_locale_free);
+
+ if (locale)
+ return locale;
+
+ locale = gimp_help_locale_new (locale_id);
+ g_hash_table_insert (domain->help_locales, g_strdup (locale_id), locale);
+
+ domain_locale_parse (domain, locale, progress, NULL);
+
+ return locale;
+}
+
+gchar *
+gimp_help_domain_map (GimpHelpDomain *domain,
+ GList *help_locales,
+ const gchar *help_id,
+ GimpHelpProgress *progress,
+ GimpHelpLocale **ret_locale,
+ gboolean *fatal_error)
+{
+ GimpHelpLocale *locale = NULL;
+ const gchar *ref = NULL;
+ GList *list;
+
+ g_return_val_if_fail (domain != NULL, NULL);
+ g_return_val_if_fail (help_locales != NULL, NULL);
+ g_return_val_if_fail (help_id != NULL, NULL);
+
+ if (fatal_error)
+ *fatal_error = FALSE;
+
+ /* first pass: look for a reference matching the help_id */
+ for (list = help_locales; list && !ref; list = list->next)
+ {
+ locale = gimp_help_domain_lookup_locale (domain,
+ (const gchar *) list->data,
+ progress);
+ ref = gimp_help_locale_map (locale, help_id);
+ }
+
+ /* second pass: look for a fallback */
+ for (list = help_locales; list && !ref; list = list->next)
+ {
+ locale = gimp_help_domain_lookup_locale (domain,
+ (const gchar *) list->data,
+ progress);
+ ref = locale->help_missing;
+ }
+
+ if (ret_locale)
+ *ret_locale = locale;
+
+ if (ref)
+ {
+ return g_strconcat (domain->help_uri, "/",
+ locale->locale_id, "/",
+ ref,
+ NULL);
+ }
+ else /* try to assemble a useful error message */
+ {
+ GError *error = NULL;
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help: help_id lookup and all fallbacks failed for '%s'\n",
+ help_id);
+#endif
+
+ locale = gimp_help_domain_lookup_locale (domain,
+ GIMP_HELP_DEFAULT_LOCALE, NULL);
+
+ if (! domain_locale_parse (domain, locale, NULL, &error))
+ {
+ switch (error->code)
+ {
+ case G_IO_ERROR_NOT_FOUND:
+ if (domain->help_domain)
+ {
+ g_message (_("The help pages for '%s' are not available."),
+ domain->help_domain);
+ }
+ else
+ {
+ g_message ("%s\n\n%s",
+ _("The GIMP user manual is not available."),
+ /* TRANSLATORS: do not end the URL with a dot,
+ * it would be in the link. Because of
+ * technical limitations, make sure the URL
+ * ends with a space, a newline or is end of text.
+ * Cf. bug 762282.
+ */
+ _("Please install the additional help package "
+ "or use the online user manual at: "
+ "https://docs.gimp.org/"));
+ }
+ break;
+
+ case G_IO_ERROR_NOT_SUPPORTED:
+ g_message ("%s\n\n%s",
+ error->message,
+ _("Perhaps you are missing GIO backends and need "
+ "to install GVFS?"));
+ break;
+
+ case G_IO_ERROR_CANCELLED:
+ break;
+
+ default:
+ g_message ("%s", error->message);
+ break;
+ }
+
+ g_error_free (error);
+
+ if (fatal_error)
+ *fatal_error = TRUE;
+ }
+ else
+ {
+ g_message (_("Help ID '%s' unknown"), help_id);
+ }
+
+ return NULL;
+ }
+}
+
+
+/* private functions */
+
+static gboolean
+domain_locale_parse (GimpHelpDomain *domain,
+ GimpHelpLocale *locale,
+ GimpHelpProgress *progress,
+ GError **error)
+{
+ gchar *uri;
+ gboolean success;
+
+ g_return_val_if_fail (domain != NULL, FALSE);
+ g_return_val_if_fail (locale != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ uri = g_strdup_printf ("%s/%s/gimp-help.xml",
+ domain->help_uri, locale->locale_id);
+
+ success = gimp_help_locale_parse (locale, uri, domain->help_domain,
+ progress, error);
+
+ g_free (uri);
+
+ return success;
+}
diff --git a/plug-ins/help/gimphelpdomain.h b/plug-ins/help/gimphelpdomain.h
new file mode 100644
index 0000000..b7e6920
--- /dev/null
+++ b/plug-ins/help/gimphelpdomain.h
@@ -0,0 +1,51 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_DOMAIN_H__
+#define __GIMP_HELP_DOMAIN_H__
+
+
+struct _GimpHelpDomain
+{
+ gchar *help_domain;
+ gchar *help_uri;
+ GHashTable *help_locales;
+};
+
+
+GimpHelpDomain * gimp_help_domain_new (const gchar *domain_name,
+ const gchar *domain_uri);
+void gimp_help_domain_free (GimpHelpDomain *domain);
+
+GimpHelpLocale * gimp_help_domain_lookup_locale (GimpHelpDomain *domain,
+ const gchar *locale_id,
+ GimpHelpProgress *progress);
+gchar * gimp_help_domain_map (GimpHelpDomain *domain,
+ GList *help_locales,
+ const gchar *help_id,
+ GimpHelpProgress *progress,
+ GimpHelpLocale **locale,
+ gboolean *fatal_error);
+void gimp_help_domain_exit (void);
+
+
+#endif /* ! __GIMP_HELP_DOMAIN_H__ */
diff --git a/plug-ins/help/gimphelpitem.c b/plug-ins/help/gimphelpitem.c
new file mode 100644
index 0000000..b38c453
--- /dev/null
+++ b/plug-ins/help/gimphelpitem.c
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is written so that it can also be compiled standalone.
+ * It shouldn't depend on libgimp.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "gimphelptypes.h"
+
+#include "gimphelpitem.h"
+
+#ifdef DISABLE_NLS
+#define _(String) (String)
+#else
+#include "libgimp/stdplugins-intl.h"
+#endif
+
+
+/* public functions */
+
+GimpHelpItem *
+gimp_help_item_new (const gchar *ref,
+ const gchar *title,
+ const gchar *sort,
+ const gchar *parent)
+{
+ GimpHelpItem *item = g_slice_new0 (GimpHelpItem);
+
+ item->ref = g_strdup (ref);
+ item->title = g_strdup (title);
+ item->sort = g_strdup (sort);
+ item->parent = g_strdup (parent);
+
+ return item;
+}
+
+void
+gimp_help_item_free (GimpHelpItem *item)
+{
+ g_free (item->ref);
+ g_free (item->title);
+ g_free (item->sort);
+ g_free (item->parent);
+
+ g_list_free (item->children);
+
+ g_slice_free (GimpHelpItem, item);
+}
diff --git a/plug-ins/help/gimphelpitem.h b/plug-ins/help/gimphelpitem.h
new file mode 100644
index 0000000..15c3c7b
--- /dev/null
+++ b/plug-ins/help/gimphelpitem.h
@@ -0,0 +1,47 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_ITEM_H__
+#define __GIMP_HELP_ITEM_H__
+
+
+struct _GimpHelpItem
+{
+ gchar *ref;
+ gchar *title;
+ gchar *sort; /* optional sort key provided by doc team */
+ gchar *parent;
+
+ /* extra fields used by the help-browser */
+ GList *children;
+ gulong index;
+};
+
+
+GimpHelpItem * gimp_help_item_new (const gchar *ref,
+ const gchar *title,
+ const gchar *sort,
+ const gchar *parent);
+void gimp_help_item_free (GimpHelpItem *item);
+
+
+#endif /* __GIMP_HELP_ITEM_H__ */
diff --git a/plug-ins/help/gimphelplocale.c b/plug-ins/help/gimphelplocale.c
new file mode 100644
index 0000000..c31d044
--- /dev/null
+++ b/plug-ins/help/gimphelplocale.c
@@ -0,0 +1,581 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is written so that it can also be compiled standalone.
+ * It shouldn't depend on libgimp.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gimphelp.h"
+#include "gimphelpprogress-private.h"
+
+#ifdef DISABLE_NLS
+#define _(String) (String)
+#else
+#include "libgimp/stdplugins-intl.h"
+#endif
+
+#ifdef PLATFORM_OSX
+#include <Foundation/Foundation.h>
+#endif
+
+/* local function prototypes */
+
+static void locale_set_error (GError **error,
+ const gchar *format,
+ GFile *file);
+
+
+/* public functions */
+
+GimpHelpLocale *
+gimp_help_locale_new (const gchar *locale_id)
+{
+ GimpHelpLocale *locale = g_slice_new0 (GimpHelpLocale);
+
+ locale->locale_id = g_strdup (locale_id);
+
+ return locale;
+}
+
+void
+gimp_help_locale_free (GimpHelpLocale *locale)
+{
+ g_return_if_fail (locale != NULL);
+
+ if (locale->help_id_mapping)
+ g_hash_table_destroy (locale->help_id_mapping);
+
+ g_free (locale->locale_id);
+ g_free (locale->help_missing);
+
+ g_list_free (locale->toplevel_items);
+
+ g_slice_free (GimpHelpLocale, locale);
+}
+
+const gchar *
+gimp_help_locale_map (GimpHelpLocale *locale,
+ const gchar *help_id)
+{
+ g_return_val_if_fail (locale != NULL, NULL);
+ g_return_val_if_fail (help_id != NULL, NULL);
+
+ if (locale->help_id_mapping)
+ {
+ GimpHelpItem *item = g_hash_table_lookup (locale->help_id_mapping,
+ help_id);
+
+ if (item)
+ return item->ref;
+ }
+
+ return NULL;
+}
+
+
+/* the locale mapping parser */
+
+typedef enum
+{
+ LOCALE_START,
+ LOCALE_IN_HELP,
+ LOCALE_IN_ITEM,
+ LOCALE_IN_MISSING,
+ LOCALE_IN_UNKNOWN
+} LocaleParserState;
+
+typedef struct
+{
+ GFile *file;
+ LocaleParserState state;
+ LocaleParserState last_known_state;
+ gint markup_depth;
+ gint unknown_depth;
+ GString *value;
+
+ GimpHelpLocale *locale;
+ const gchar *help_domain;
+ gchar *id_attr_name;
+} LocaleParser;
+
+static gboolean locale_parser_parse (GMarkupParseContext *context,
+ GimpHelpProgress *progress,
+ GInputStream *stream,
+ goffset size,
+ GCancellable *cancellable,
+ GError **error);
+static void locale_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+static void locale_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+static void locale_parser_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data);
+static void locale_parser_start_unknown (LocaleParser *parser);
+static void locale_parser_end_unknown (LocaleParser *parser);
+static void locale_parser_parse_namespace (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values);
+static void locale_parser_parse_item (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values);
+static void locale_parser_parse_missing (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values);
+
+static const GMarkupParser markup_parser =
+{
+ locale_parser_start_element,
+ locale_parser_end_element,
+ NULL, /* characters */
+ NULL, /* passthrough */
+ locale_parser_error
+};
+
+gboolean
+gimp_help_locale_parse (GimpHelpLocale *locale,
+ const gchar *uri,
+ const gchar *help_domain,
+ GimpHelpProgress *progress,
+ GError **error)
+{
+ GMarkupParseContext *context;
+ GFile *file = NULL;
+ GCancellable *cancellable = NULL;
+ LocaleParser parser = { NULL, };
+#ifdef PLATFORM_OSX
+ NSURL *fileURL;
+ NSString *nsUri;
+ NSData *data;
+ const gchar *str;
+#else
+ GFileInputStream *stream;
+ goffset size = 0;
+#endif
+ gboolean success;
+
+ g_return_val_if_fail (locale != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (locale->help_id_mapping)
+ {
+ g_hash_table_destroy (locale->help_id_mapping);
+ locale->help_id_mapping = NULL;
+ }
+
+ if (locale->help_missing)
+ {
+ g_free (locale->help_missing);
+ locale->help_missing = NULL;
+ }
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help (%s): parsing '%s' for \"%s\"\n",
+ locale->locale_id, uri, help_domain);
+#endif
+
+ file = g_file_new_for_uri (uri);
+
+ if (progress)
+ {
+ gchar *name = g_file_get_parse_name (file);
+
+ cancellable = g_cancellable_new ();
+ _gimp_help_progress_start (progress, cancellable,
+ _("Loading index from '%s'"), name);
+
+ g_clear_object (&cancellable);
+ g_free (name);
+ }
+
+#ifdef PLATFORM_OSX
+ nsUri = [NSString stringWithUTF8String: uri];
+ fileURL = [NSURL URLWithString: nsUri];
+ [nsUri release];
+
+ if (progress)
+ _gimp_help_progress_pulse (progress);
+
+ /* Load the data from the remote URL into the NSData object */
+ data = [NSData dataWithContentsOfURL:fileURL];
+ [fileURL release];
+
+ if (! data)
+ {
+ locale_set_error (error,
+ _("Could not load data from '%s': %s"), file);
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ if (progress)
+ _gimp_help_progress_pulse (progress);
+#else /* PLATFORM_OSX */
+ if (progress)
+ {
+ GFileInfo *info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE, 0,
+ cancellable, error);
+ if (! info)
+ {
+ locale_set_error (error,
+ _("Could not open '%s' for reading: %s"), file);
+ g_object_unref (file);
+
+ return FALSE;
+ }
+
+ size = g_file_info_get_size (info);
+
+ g_object_unref (info);
+ }
+
+ stream = g_file_read (file, cancellable, error);
+
+ if (! stream)
+ {
+ locale_set_error (error,
+ _("Could not open '%s' for reading: %s"), file);
+ g_object_unref (file);
+
+ return FALSE;
+ }
+#endif /* ! PLATFORM_OSX */
+
+ parser.file = file;
+ parser.value = g_string_new (NULL);
+ parser.locale = locale;
+ parser.help_domain = help_domain;
+ parser.id_attr_name = g_strdup ("id");
+
+ context = g_markup_parse_context_new (&markup_parser, 0, &parser, NULL);
+
+#ifdef PLATFORM_OSX
+ str = (const char *)[data bytes];
+
+ if (! g_markup_parse_context_parse (context, str, [data length], error))
+ success = FALSE;
+ else
+ success = g_markup_parse_context_end_parse (context, error);
+
+ [data release];
+#else /* PLATFORM_OSX */
+ success = locale_parser_parse (context, progress,
+ G_INPUT_STREAM (stream), size,
+ cancellable, error);
+
+ g_object_unref (stream);
+#endif /* ! PLATFORM_OSX */
+ if (progress)
+ _gimp_help_progress_finish (progress);
+
+ g_markup_parse_context_free (context);
+
+ g_string_free (parser.value, TRUE);
+ g_free (parser.id_attr_name);
+
+ if (! success)
+ locale_set_error (error, _("Parse error in '%s':\n%s"), file);
+
+ g_object_unref (file);
+
+ return success;
+}
+
+static gboolean
+locale_parser_parse (GMarkupParseContext *context,
+ GimpHelpProgress *progress,
+ GInputStream *stream,
+ goffset size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gssize len;
+ goffset done = 0;
+ gchar buffer[4096];
+
+ while ((len = g_input_stream_read (stream, buffer, sizeof (buffer),
+ cancellable, error)) != -1)
+ {
+ switch (len)
+ {
+ case 0:
+ return g_markup_parse_context_end_parse (context, error);
+
+ default:
+ done += len;
+
+ if (progress)
+ {
+ if (size > 0)
+ _gimp_help_progress_update (progress, (gdouble) done / size);
+ else
+ _gimp_help_progress_pulse (progress);
+ }
+
+ if (! g_markup_parse_context_parse (context, buffer, len, error))
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+locale_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ LocaleParser *parser = (LocaleParser *) user_data;
+
+ switch (parser->state)
+ {
+ case LOCALE_START:
+ if (strcmp (element_name, "gimp-help") == 0)
+ {
+ parser->state = LOCALE_IN_HELP;
+ locale_parser_parse_namespace (parser,
+ attribute_names, attribute_values);
+ }
+ else
+ locale_parser_start_unknown (parser);
+ break;
+
+ case LOCALE_IN_HELP:
+ if (strcmp (element_name, "help-item") == 0)
+ {
+ parser->state = LOCALE_IN_ITEM;
+ locale_parser_parse_item (parser,
+ attribute_names, attribute_values);
+ }
+ else if (strcmp (element_name, "help-missing") == 0)
+ {
+ parser->state = LOCALE_IN_MISSING;
+ locale_parser_parse_missing (parser,
+ attribute_names, attribute_values);
+ }
+ else
+ locale_parser_start_unknown (parser);
+ break;
+
+ case LOCALE_IN_ITEM:
+ case LOCALE_IN_MISSING:
+ case LOCALE_IN_UNKNOWN:
+ locale_parser_start_unknown (parser);
+ break;
+ }
+}
+
+static void
+locale_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ LocaleParser *parser = (LocaleParser *) user_data;
+
+ switch (parser->state)
+ {
+ case LOCALE_START:
+ g_warning ("locale_parser: This shouldn't happen.");
+ break;
+
+ case LOCALE_IN_HELP:
+ parser->state = LOCALE_START;
+ break;
+
+ case LOCALE_IN_ITEM:
+ case LOCALE_IN_MISSING:
+ parser->state = LOCALE_IN_HELP;
+ break;
+
+ case LOCALE_IN_UNKNOWN:
+ locale_parser_end_unknown (parser);
+ break;
+ }
+}
+
+static void
+locale_parser_error (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ LocaleParser *parser = (LocaleParser *) user_data;
+ gchar *name = g_file_get_parse_name (parser->file);
+
+ g_printerr ("help (parsing %s): %s", name, error->message);
+
+ g_free (name);
+}
+
+static void
+locale_parser_start_unknown (LocaleParser *parser)
+{
+ if (parser->unknown_depth == 0)
+ parser->last_known_state = parser->state;
+
+ parser->state = LOCALE_IN_UNKNOWN;
+ parser->unknown_depth++;
+}
+
+static void
+locale_parser_end_unknown (LocaleParser *parser)
+{
+ g_assert (parser->unknown_depth > 0 && parser->state == LOCALE_IN_UNKNOWN);
+
+ parser->unknown_depth--;
+
+ if (parser->unknown_depth == 0)
+ parser->state = parser->last_known_state;
+}
+
+static void
+locale_parser_parse_namespace (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values)
+{
+ for (; *names && *values; names++, values++)
+ {
+ if (! strncmp (*names, "xmlns:", 6) &&
+ ! strcmp (*values, parser->help_domain))
+ {
+ g_free (parser->id_attr_name);
+ parser->id_attr_name = g_strdup_printf ("%s:id", *names + 6);
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help (%s): id attribute name for \"%s\" is \"%s\"\n",
+ parser->locale->locale_id,
+ parser->help_domain,
+ parser->id_attr_name);
+#endif
+ }
+ }
+}
+
+static void
+locale_parser_parse_item (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values)
+{
+ const gchar *id = NULL;
+ const gchar *ref = NULL;
+ const gchar *title = NULL;
+ const gchar *sort = NULL; /* optional sort key provided by doc team */
+ const gchar *parent = NULL;
+
+ for (; *names && *values; names++, values++)
+ {
+ if (! strcmp (*names, parser->id_attr_name))
+ id = *values;
+
+ if (! strcmp (*names, "ref"))
+ ref = *values;
+
+ if (! strcmp (*names, "title"))
+ title = *values;
+
+ if (! strcmp (*names, "sort"))
+ sort = *values;
+
+ if (! strcmp (*names, "parent"))
+ parent = *values;
+ }
+
+ if (id && ref)
+ {
+ if (! parser->locale->help_id_mapping)
+ parser->locale->help_id_mapping =
+ g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) gimp_help_item_free);
+
+ g_hash_table_insert (parser->locale->help_id_mapping,
+ g_strdup (id),
+ gimp_help_item_new (ref, title, sort, parent));
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help (%s): added mapping \"%s\" -> \"%s\"\n",
+ parser->locale->locale_id, id, ref);
+#endif
+ }
+}
+
+static void
+locale_parser_parse_missing (LocaleParser *parser,
+ const gchar **names,
+ const gchar **values)
+{
+ const gchar *ref = NULL;
+
+ for (; *names && *values; names++, values++)
+ {
+ if (! strcmp (*names, "ref"))
+ ref = *values;
+ }
+
+ if (ref &&
+ parser->locale->help_missing == NULL)
+ {
+ parser->locale->help_missing = g_strdup (ref);
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help (%s): added fallback for missing help -> \"%s\"\n",
+ parser->locale->locale_id, ref);
+#endif
+ }
+}
+
+static void
+locale_set_error (GError **error,
+ const gchar *format,
+ GFile *file)
+{
+ if (error && *error)
+ {
+ gchar *name = g_file_get_parse_name (file);
+ gchar *msg;
+
+ msg = g_strdup_printf (format, name, (*error)->message);
+ g_free (name);
+
+ g_free ((*error)->message);
+ (*error)->message = msg;
+ }
+}
diff --git a/plug-ins/help/gimphelplocale.h b/plug-ins/help/gimphelplocale.h
new file mode 100644
index 0000000..8dd57a6
--- /dev/null
+++ b/plug-ins/help/gimphelplocale.h
@@ -0,0 +1,51 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_LOCALE_H__
+#define __GIMP_HELP_LOCALE_H__
+
+
+struct _GimpHelpLocale
+{
+ gchar *locale_id;
+ GHashTable *help_id_mapping;
+ gchar *help_missing;
+
+ /* eek */
+ GList *toplevel_items;
+};
+
+
+GimpHelpLocale * gimp_help_locale_new (const gchar *locale_id);
+void gimp_help_locale_free (GimpHelpLocale *locale);
+
+const gchar * gimp_help_locale_map (GimpHelpLocale *locale,
+ const gchar *help_id);
+
+gboolean gimp_help_locale_parse (GimpHelpLocale *locale,
+ const gchar *uri,
+ const gchar *help_domain,
+ GimpHelpProgress *progress,
+ GError **error);
+
+
+#endif /* __GIMP_HELP_LOCALE_H__ */
diff --git a/plug-ins/help/gimphelpprogress-private.h b/plug-ins/help/gimphelpprogress-private.h
new file mode 100644
index 0000000..d83e45f
--- /dev/null
+++ b/plug-ins/help/gimphelpprogress-private.h
@@ -0,0 +1,39 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_PROGRESS_PRIVATE_H__
+#define __GIMP_HELP_PROGRESS_PRIVATE_H__
+
+
+/* internal API */
+
+void _gimp_help_progress_start (GimpHelpProgress *progress,
+ GCancellable *cancellable,
+ const gchar *format,
+ ...) G_GNUC_PRINTF (3, 4) G_GNUC_INTERNAL;
+void _gimp_help_progress_update (GimpHelpProgress *progress,
+ gdouble percentage) G_GNUC_INTERNAL;
+void _gimp_help_progress_pulse (GimpHelpProgress *progress) G_GNUC_INTERNAL;
+void _gimp_help_progress_finish (GimpHelpProgress *progress) G_GNUC_INTERNAL;
+
+
+#endif /* ! __GIMP_HELP_PROGRESS_PRIVATE_H__ */
diff --git a/plug-ins/help/gimphelpprogress.c b/plug-ins/help/gimphelpprogress.c
new file mode 100644
index 0000000..dd4b224
--- /dev/null
+++ b/plug-ins/help/gimphelpprogress.c
@@ -0,0 +1,150 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* This code is written so that it can also be compiled standalone.
+ * It shouldn't depend on libgimp.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gio/gio.h>
+
+#include "gimphelptypes.h"
+#include "gimphelpprogress.h"
+#include "gimphelpprogress-private.h"
+
+
+struct _GimpHelpProgress
+{
+ GimpHelpProgressVTable vtable;
+ gpointer user_data;
+
+ GCancellable *cancellable;
+};
+
+
+GimpHelpProgress *
+gimp_help_progress_new (const GimpHelpProgressVTable *vtable,
+ gpointer user_data)
+{
+ GimpHelpProgress *progress;
+
+ g_return_val_if_fail (vtable != NULL, NULL);
+
+ progress = g_slice_new0 (GimpHelpProgress);
+
+ progress->vtable.start = vtable->start;
+ progress->vtable.end = vtable->end;
+ progress->vtable.set_value = vtable->set_value;
+
+ progress->user_data = user_data;
+
+ return progress;
+}
+
+void
+gimp_help_progress_free (GimpHelpProgress *progress)
+{
+ g_return_if_fail (progress != NULL);
+
+ if (progress->cancellable)
+ {
+ g_object_unref (progress->cancellable);
+ progress->cancellable = NULL;
+ }
+
+ g_slice_free (GimpHelpProgress, progress);
+}
+
+void
+gimp_help_progress_cancel (GimpHelpProgress *progress)
+{
+ g_return_if_fail (progress != NULL);
+
+ if (progress->cancellable)
+ g_cancellable_cancel (progress->cancellable);
+}
+
+
+void
+_gimp_help_progress_start (GimpHelpProgress *progress,
+ GCancellable *cancellable,
+ const gchar *format,
+ ...)
+{
+ gchar *message;
+ va_list args;
+
+ g_return_if_fail (progress != NULL);
+
+ if (cancellable)
+ g_object_ref (cancellable);
+
+ if (progress->cancellable)
+ g_object_unref (progress->cancellable);
+
+ progress->cancellable = cancellable;
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ if (progress->vtable.start)
+ progress->vtable.start (message, cancellable != NULL, progress->user_data);
+
+ g_free (message);
+}
+
+void
+_gimp_help_progress_update (GimpHelpProgress *progress,
+ gdouble percentage)
+{
+ g_return_if_fail (progress != NULL);
+
+ if (progress->vtable.set_value)
+ progress->vtable.set_value (percentage, progress->user_data);
+}
+
+void
+_gimp_help_progress_pulse (GimpHelpProgress *progress)
+{
+ g_return_if_fail (progress != NULL);
+
+ _gimp_help_progress_update (progress, -1.0);
+}
+
+void
+_gimp_help_progress_finish (GimpHelpProgress *progress)
+{
+ g_return_if_fail (progress != NULL);
+
+ if (progress->vtable.end)
+ progress->vtable.end (progress->user_data);
+
+ if (progress->cancellable)
+ {
+ g_object_unref (progress->cancellable);
+ progress->cancellable = NULL;
+ }
+}
diff --git a/plug-ins/help/gimphelpprogress.h b/plug-ins/help/gimphelpprogress.h
new file mode 100644
index 0000000..d7fdb32
--- /dev/null
+++ b/plug-ins/help/gimphelpprogress.h
@@ -0,0 +1,51 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_PROGRESS_H__
+#define __GIMP_HELP_PROGRESS_H__
+
+
+typedef struct
+{
+ void (* start) (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data);
+ void (* end) (gpointer user_data);
+ void (* set_value) (gdouble percentage,
+ gpointer user_data);
+
+ /* Padding for future expansion. Must be initialized with NULL! */
+ void (* _gimp_reserved1) (void);
+ void (* _gimp_reserved2) (void);
+ void (* _gimp_reserved3) (void);
+ void (* _gimp_reserved4) (void);
+} GimpHelpProgressVTable;
+
+
+GimpHelpProgress * gimp_help_progress_new (const GimpHelpProgressVTable *vtable,
+ gpointer user_data);
+void gimp_help_progress_free (GimpHelpProgress *progress);
+
+void gimp_help_progress_cancel (GimpHelpProgress *progress);
+
+
+#endif /* ! __GIMP_HELP_PROGRESS_H__ */
diff --git a/plug-ins/help/gimphelptypes.h b/plug-ins/help/gimphelptypes.h
new file mode 100644
index 0000000..4937d6f
--- /dev/null
+++ b/plug-ins/help/gimphelptypes.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_HELP_TYPES_H__
+#define __GIMP_HELP_TYPES_H__
+
+
+typedef struct _GimpHelpDomain GimpHelpDomain;
+typedef struct _GimpHelpItem GimpHelpItem;
+typedef struct _GimpHelpLocale GimpHelpLocale;
+typedef struct _GimpHelpProgress GimpHelpProgress;
+
+
+#endif /* ! __GIMP_HELP_TYPES_H__ */
diff --git a/plug-ins/help/help.c b/plug-ins/help/help.c
new file mode 100644
index 0000000..8d913b7
--- /dev/null
+++ b/plug-ins/help/help.c
@@ -0,0 +1,352 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * The GIMP Help plug-in
+ * Copyright (C) 1999-2008 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ * Henrik Brix Andersen <brix@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h> /* strlen */
+
+#include <glib.h>
+
+#include "libgimp/gimp.h"
+
+#include "gimphelp.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* defines */
+
+#define GIMP_HELP_EXT_PROC "extension-gimp-help"
+#define GIMP_HELP_TEMP_EXT_PROC "extension-gimp-help-temp"
+
+
+typedef struct
+{
+ gchar *procedure;
+ gchar *help_domain;
+ gchar *help_locales;
+ gchar *help_id;
+} IdleHelp;
+
+
+/* forward declarations */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void temp_proc_install (void);
+static void temp_proc_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static void load_help (const gchar *procedure,
+ const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id);
+static gboolean load_help_idle (gpointer data);
+
+static GimpHelpProgress * load_help_progress_new (void);
+
+
+/* local variables */
+
+static GMainLoop *main_loop = NULL;
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "num-domain-names", "" },
+ { GIMP_PDB_STRINGARRAY, "domain-names", "" },
+ { GIMP_PDB_INT32, "num-domain-uris", "" },
+ { GIMP_PDB_STRINGARRAY, "domain-uris", "" }
+ };
+
+ gimp_install_procedure (GIMP_HELP_EXT_PROC,
+ "", /* FIXME */
+ "", /* FIXME */
+ "Sven Neumann <sven@gimp.org>, "
+ "Michael Natterer <mitch@gimp.org>, "
+ "Henrik Brix Andersen <brix@gimp.org>",
+ "Sven Neumann, Michael Natterer & Henrik Brix Andersen",
+ "1999-2008",
+ NULL,
+ "",
+ GIMP_EXTENSION,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+
+ /* make sure all the arguments are there */
+ if (nparams == 4)
+ {
+ if (! gimp_help_init (param[0].data.d_int32,
+ param[1].data.d_stringarray,
+ param[2].data.d_int32,
+ param[3].data.d_stringarray))
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ else
+ {
+ g_printerr ("help: wrong number of arguments in procedure call.\n");
+
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ temp_proc_install ();
+
+ gimp_extension_ack ();
+ gimp_extension_enable ();
+
+ g_main_loop_run (main_loop);
+
+ g_main_loop_unref (main_loop);
+ main_loop = NULL;
+
+ gimp_uninstall_temp_proc (GIMP_HELP_TEMP_EXT_PROC);
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+}
+
+static void
+temp_proc_install (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_STRING, "procedure", "The procedure of the browser to use" },
+ { GIMP_PDB_STRING, "help-domain", "Help domain to use" },
+ { GIMP_PDB_STRING, "help-locales", "Language to use" },
+ { GIMP_PDB_STRING, "help-id", "Help ID to open" }
+ };
+
+ gimp_install_temp_proc (GIMP_HELP_TEMP_EXT_PROC,
+ "DON'T USE THIS ONE",
+ "(Temporary procedure)",
+ "Sven Neumann <sven@gimp.org>, "
+ "Michael Natterer <mitch@gimp.org>"
+ "Henrik Brix Andersen <brix@gimp.org",
+ "Sven Neumann, Michael Natterer & Henrik Brix Andersen",
+ "1999-2008",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ temp_proc_run);
+}
+
+static void
+temp_proc_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ const gchar *procedure = NULL;
+ const gchar *help_domain = GIMP_HELP_DEFAULT_DOMAIN;
+ const gchar *help_locales = NULL;
+ const gchar *help_id = GIMP_HELP_DEFAULT_ID;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ /* make sure all the arguments are there */
+ if (nparams == 4)
+ {
+ if (param[0].data.d_string && strlen (param[0].data.d_string))
+ procedure = param[0].data.d_string;
+
+ if (param[1].data.d_string && strlen (param[1].data.d_string))
+ help_domain = param[1].data.d_string;
+
+ if (param[2].data.d_string && strlen (param[2].data.d_string))
+ help_locales = param[2].data.d_string;
+
+ if (param[3].data.d_string && strlen (param[3].data.d_string))
+ help_id = param[3].data.d_string;
+ }
+
+ if (! procedure)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ if (status == GIMP_PDB_SUCCESS)
+ load_help (procedure, help_domain, help_locales, help_id);
+}
+
+static void
+load_help (const gchar *procedure,
+ const gchar *help_domain,
+ const gchar *help_locales,
+ const gchar *help_id)
+{
+ IdleHelp *idle_help = g_slice_new (IdleHelp);
+
+ idle_help->procedure = g_strdup (procedure);
+ idle_help->help_domain = g_strdup (help_domain);
+ idle_help->help_locales = g_strdup (help_locales);
+ idle_help->help_id = g_strdup (help_id);
+
+ g_idle_add (load_help_idle, idle_help);
+}
+
+static gboolean
+load_help_idle (gpointer data)
+{
+ IdleHelp *idle_help = data;
+ GimpHelpDomain *domain;
+
+ domain = gimp_help_lookup_domain (idle_help->help_domain);
+
+ if (domain)
+ {
+ GimpHelpProgress *progress = NULL;
+ GList *locales;
+ gchar *uri;
+ gboolean fatal_error;
+
+ locales = gimp_help_parse_locales (idle_help->help_locales);
+
+ if (! g_str_has_prefix (domain->help_uri, "file:"))
+ progress = load_help_progress_new ();
+
+ uri = gimp_help_domain_map (domain, locales, idle_help->help_id,
+ progress, NULL, &fatal_error);
+
+ if (progress)
+ gimp_help_progress_free (progress);
+
+ g_list_free_full (locales, (GDestroyNotify) g_free);
+
+ if (uri)
+ {
+ GimpParam *return_vals;
+ gint n_return_vals;
+
+#ifdef GIMP_HELP_DEBUG
+ g_printerr ("help: calling '%s' for '%s'\n",
+ idle_help->procedure, uri);
+#endif
+
+ return_vals = gimp_run_procedure (idle_help->procedure,
+ &n_return_vals,
+ GIMP_PDB_STRING, uri,
+ GIMP_PDB_END);
+
+ gimp_destroy_params (return_vals, n_return_vals);
+
+ g_free (uri);
+ }
+ else if (fatal_error)
+ {
+ g_main_loop_quit (main_loop);
+ }
+ }
+
+ g_free (idle_help->procedure);
+ g_free (idle_help->help_domain);
+ g_free (idle_help->help_locales);
+ g_free (idle_help->help_id);
+
+ g_slice_free (IdleHelp, idle_help);
+
+ return FALSE;
+}
+
+static void
+load_help_progress_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ gimp_progress_init (message);
+}
+
+static void
+load_help_progress_update (gdouble value,
+ gpointer user_data)
+{
+ gimp_progress_update (value);
+}
+
+static void
+load_help_progress_end (gpointer user_data)
+{
+ gimp_progress_end ();
+}
+
+static GimpHelpProgress *
+load_help_progress_new (void)
+{
+ static const GimpHelpProgressVTable vtable =
+ {
+ load_help_progress_start,
+ load_help_progress_end,
+ load_help_progress_update
+ };
+
+ return gimp_help_progress_new (&vtable, NULL);
+}
diff --git a/plug-ins/ifs-compose/Makefile.am b/plug-ins/ifs-compose/Makefile.am
new file mode 100644
index 0000000..c111473
--- /dev/null
+++ b/plug-ins/ifs-compose/Makefile.am
@@ -0,0 +1,55 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+ifs_compose_RC = ifs-compose.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/ifs-compose
+
+libexec_PROGRAMS = ifs-compose
+
+ifs_compose_SOURCES = \
+ ifs-compose.c \
+ ifs-compose.h \
+ ifs-compose-utils.c \
+ ifs-compose-storage.c
+
+EXTRA_DIST = README.ifscompose
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(ifs_compose_RC)
diff --git a/plug-ins/ifs-compose/Makefile.in b/plug-ins/ifs-compose/Makefile.in
new file mode 100644
index 0000000..5c28298
--- /dev/null
+++ b/plug-ins/ifs-compose/Makefile.in
@@ -0,0 +1,1014 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = ifs-compose$(EXEEXT)
+subdir = plug-ins/ifs-compose
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_ifs_compose_OBJECTS = ifs-compose.$(OBJEXT) \
+ ifs-compose-utils.$(OBJEXT) ifs-compose-storage.$(OBJEXT)
+ifs_compose_OBJECTS = $(am_ifs_compose_OBJECTS)
+ifs_compose_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+ifs_compose_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(ifs_compose_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ifs-compose-storage.Po \
+ ./$(DEPDIR)/ifs-compose-utils.Po ./$(DEPDIR)/ifs-compose.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(ifs_compose_SOURCES)
+DIST_SOURCES = $(ifs_compose_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/ifs-compose
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@ifs_compose_RC = ifs-compose.rc.o
+AM_LDFLAGS = $(mwindows)
+ifs_compose_SOURCES = \
+ ifs-compose.c \
+ ifs-compose.h \
+ ifs-compose-utils.c \
+ ifs-compose-storage.c
+
+EXTRA_DIST = README.ifscompose
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(ifs_compose_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/ifs-compose/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/ifs-compose/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+ifs-compose$(EXEEXT): $(ifs_compose_OBJECTS) $(ifs_compose_DEPENDENCIES) $(EXTRA_ifs_compose_DEPENDENCIES)
+ @rm -f ifs-compose$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(ifs_compose_OBJECTS) $(ifs_compose_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifs-compose-storage.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifs-compose-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ifs-compose.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ifs-compose-storage.Po
+ -rm -f ./$(DEPDIR)/ifs-compose-utils.Po
+ -rm -f ./$(DEPDIR)/ifs-compose.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ifs-compose-storage.Po
+ -rm -f ./$(DEPDIR)/ifs-compose-utils.Po
+ -rm -f ./$(DEPDIR)/ifs-compose.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/ifs-compose/README.ifscompose b/plug-ins/ifs-compose/README.ifscompose
new file mode 100644
index 0000000..cbaa331
--- /dev/null
+++ b/plug-ins/ifs-compose/README.ifscompose
@@ -0,0 +1,83 @@
+IfsCompose
+----------
+
+IfsCompose is a plug-in for GIMP that allows
+the creation of Iterated Function System fractals by direct
+manipulation onscreen of the component transforms.
+
+
+IFS Fractals
+------------
+
+You may be familiar with IFS's from the screen
+hack 'Flame'. They are also the basis of fractal image compression.
+
+For a brief introduction to IFS's see Foley and van Dam, et
+al,. _Computer Graphics, Principles and Practice_, 2nd Ed.,
+(Addison Wesley, 1990).
+
+The standard references in the field are Michael Barnsley's books (though
+I haven't looked at them yet):
+
+M. Barnsley, _Fractals Everywhere_, Academic Press Inc., 1988.
+M. Barnsley and L. Hurd, _Fractal Image Compression_, Jones and
+Bartlett.
+
+Briefly, you take a point and repeatedly apply one of a set of
+transformations to it, choosing randomly between them, and plot the
+point at each step. An interesting result (the Collage Theorem) says
+that if you can find a set of transformations that break up an image
+into smaller copies of itself, then the resulting fractal exactly
+reproduces the original image. For example, here is a classic image
+of a leaf and the same image with the four component transforms
+colored distinctively.
+
+But the best way to appreciate this may to install this program and
+try it out. I've extended the basic concept as found in
+Foley and van Dam to include transformations in color space as
+well as in real space.
+
+Installation
+------------
+The included Makefile should work with minor modifications on most
+systems if you have installed Gimp normally. Put the resulting binary
+in ~/.gimp/plug-ins or the system-wide plug-ins directory.
+
+The included files gtkaspectframe.c/.h implement a modified frame
+widget that guarantees that the aspect ratio of the child widget
+remains constant when the parent is resized. It's sort of specialized,
+but if you think it would be useful for other purposes, let me know
+and I'll lobby for its inclusion in the standard gtk.
+
+Use
+---
+The interface is somewhat complex and it may take you a little while
+to get the hang of it. (There are 19 parameters for each
+transformation in your fractal, after all). The best way to learn is
+probably to start by making small changes, and seeing what they
+do. Click on the transformations (represented by polygons) in the
+design window to manipulate them interactively.
+
+Button-1: rotate/scale
+Button-2: distort
+Button-3: move
+
+If you hold down shift while clicking, you can select multiple polygons
+to apply the transformation to.
+
+Try not to click too near the center of a polygon, as this will
+amplify your actions.
+
+Note that if you render onto an image with an alpha channel, the
+background will be transparent (very useful for compositing several
+fractals), otherwise the background will be the current background.
+
+There are tutorials and some example images at:
+
+ http://www.gtk.org/~otaylor/IfsCompose/ifs_tutorial/tutorial.html
+ http://tigert.gimp.org/gimp/ifs-compose/
+
+
+Have fun!
+
+Owen Taylor
diff --git a/plug-ins/ifs-compose/ifs-compose-storage.c b/plug-ins/ifs-compose/ifs-compose-storage.c
new file mode 100644
index 0000000..24705be
--- /dev/null
+++ b/plug-ins/ifs-compose/ifs-compose-storage.c
@@ -0,0 +1,551 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * IfsCompose is a interface for creating IFS fractals by
+ * direct manipulation.
+ * Copyright (C) 1997 Owen Taylor
+ *
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h> /* strlen */
+
+#include <gdk/gdk.h>
+
+#include <libgimp/gimp.h>
+
+#include "ifs-compose.h"
+
+
+typedef enum {
+ TOKEN_INVALID = G_TOKEN_LAST,
+ TOKEN_ITERATIONS,
+ TOKEN_MAX_MEMORY,
+ TOKEN_SUBDIVIDE,
+ TOKEN_RADIUS,
+ TOKEN_ASPECT_RATIO,
+ TOKEN_CENTER_X,
+ TOKEN_CENTER_Y,
+ TOKEN_ELEMENT,
+ TOKEN_X,
+ TOKEN_Y,
+ TOKEN_THETA,
+ TOKEN_SCALE,
+ TOKEN_ASYM,
+ TOKEN_SHEAR,
+ TOKEN_FLIP,
+ TOKEN_RED_COLOR,
+ TOKEN_GREEN_COLOR,
+ TOKEN_BLUE_COLOR,
+ TOKEN_BLACK_COLOR,
+ TOKEN_TARGET_COLOR,
+ TOKEN_HUE_SCALE,
+ TOKEN_VALUE_SCALE,
+ TOKEN_SIMPLE_COLOR,
+ TOKEN_PROB
+} IfsComposeToken;
+
+static struct
+{
+ const gchar *name;
+ IfsComposeToken token;
+} symbols[] = {
+ { "iterations", TOKEN_ITERATIONS },
+ { "max_memory", TOKEN_MAX_MEMORY },
+ { "subdivide", TOKEN_SUBDIVIDE },
+ { "radius", TOKEN_RADIUS },
+ { "aspect_ratio", TOKEN_ASPECT_RATIO },
+ { "center_x", TOKEN_CENTER_X },
+ { "center_y", TOKEN_CENTER_Y },
+ { "element", TOKEN_ELEMENT },
+ { "x", TOKEN_X },
+ { "y", TOKEN_Y },
+ { "theta", TOKEN_THETA },
+ { "scale", TOKEN_SCALE },
+ { "asym", TOKEN_ASYM },
+ { "shear", TOKEN_SHEAR },
+ { "flip", TOKEN_FLIP },
+ { "red_color", TOKEN_RED_COLOR },
+ { "green_color", TOKEN_GREEN_COLOR },
+ { "blue_color", TOKEN_BLUE_COLOR },
+ { "black_color", TOKEN_BLACK_COLOR },
+ { "target_color", TOKEN_TARGET_COLOR },
+ { "hue_scale", TOKEN_HUE_SCALE },
+ { "value_scale", TOKEN_VALUE_SCALE },
+ { "simple_color", TOKEN_SIMPLE_COLOR },
+ { "prob", TOKEN_PROB }
+};
+
+static GTokenType
+ifsvals_parse_color (GScanner *scanner,
+ GimpRGB *result)
+{
+ GTokenType token;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_LEFT_CURLY)
+ return G_TOKEN_LEFT_CURLY;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_FLOAT)
+ result->r = scanner->value.v_float;
+ else if (token == G_TOKEN_INT)
+ result->r = scanner->value.v_int;
+ else
+ return G_TOKEN_FLOAT;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_COMMA)
+ return G_TOKEN_COMMA;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_FLOAT)
+ result->g = scanner->value.v_float;
+ else if (token == G_TOKEN_INT)
+ result->g = scanner->value.v_int;
+ else
+ return G_TOKEN_FLOAT;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_COMMA)
+ return G_TOKEN_COMMA;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_FLOAT)
+ result->b = scanner->value.v_float;
+ else if (token == G_TOKEN_INT)
+ result->b = scanner->value.v_int;
+ else
+ return G_TOKEN_FLOAT;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_RIGHT_CURLY)
+ return G_TOKEN_RIGHT_CURLY;
+
+ return G_TOKEN_NONE;
+}
+
+/* Parse a float which (unlike G_TOKEN_FLOAT) can be negative
+ */
+static GTokenType
+parse_genuine_float (GScanner *scanner,
+ gdouble *result)
+{
+ gboolean negate = FALSE;
+ GTokenType token;
+
+ token = g_scanner_get_next_token (scanner);
+
+ if (token == '-')
+ {
+ negate = TRUE;
+ token = g_scanner_get_next_token (scanner);
+ }
+
+ if (token == G_TOKEN_FLOAT)
+ {
+ *result = negate ? -scanner->value.v_float : scanner->value.v_float;
+ return G_TOKEN_NONE;
+ }
+ else if (token == G_TOKEN_INT)
+ {
+ *result = negate ? -scanner->value.v_int : scanner->value.v_int;
+ return G_TOKEN_NONE;
+ }
+ else
+ return G_TOKEN_FLOAT;
+}
+
+static GTokenType
+ifsvals_parse_element (GScanner *scanner,
+ AffElementVals *result)
+{
+ GTokenType token;
+ GTokenType expected_token;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_LEFT_CURLY)
+ return G_TOKEN_LEFT_CURLY;
+
+ token = g_scanner_get_next_token (scanner);
+ while (token != G_TOKEN_RIGHT_CURLY)
+ {
+ switch ((IfsComposeToken) token)
+ {
+ case TOKEN_X:
+ expected_token = parse_genuine_float (scanner, &result->x);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_Y:
+ expected_token = parse_genuine_float (scanner, &result->y);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_THETA:
+ expected_token = parse_genuine_float (scanner, &result->theta);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_SCALE:
+ expected_token = parse_genuine_float (scanner, &result->scale);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_ASYM:
+ expected_token = parse_genuine_float (scanner, &result->asym);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_SHEAR:
+ expected_token = parse_genuine_float (scanner, &result->shear);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_FLIP:
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_INT)
+ return G_TOKEN_INT;
+
+ result->flip = scanner->value.v_int;
+ break;
+
+ case TOKEN_RED_COLOR:
+ token = ifsvals_parse_color (scanner, &result->red_color);
+ if (token != G_TOKEN_NONE)
+ return token;
+ break;
+
+ case TOKEN_GREEN_COLOR:
+ token = ifsvals_parse_color (scanner, &result->green_color);
+ if (token != G_TOKEN_NONE)
+ return token;
+ break;
+
+ case TOKEN_BLUE_COLOR:
+ token = ifsvals_parse_color (scanner, &result->blue_color);
+ if (token != G_TOKEN_NONE)
+ return token;
+ break;
+
+ case TOKEN_BLACK_COLOR:
+ token = ifsvals_parse_color (scanner, &result->black_color);
+ if (token != G_TOKEN_NONE)
+ return token;
+ break;
+
+ case TOKEN_TARGET_COLOR:
+ token = ifsvals_parse_color (scanner, &result->target_color);
+ if (token != G_TOKEN_NONE)
+ return token;
+ break;
+
+ case TOKEN_HUE_SCALE:
+ expected_token = parse_genuine_float (scanner, &result->hue_scale);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_VALUE_SCALE:
+ expected_token = parse_genuine_float (scanner, &result->value_scale);
+ if (expected_token != G_TOKEN_NONE)
+ return expected_token;
+ break;
+
+ case TOKEN_SIMPLE_COLOR:
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_INT)
+ return G_TOKEN_INT;
+
+ result->simple_color = scanner->value.v_int;
+ break;
+
+ case TOKEN_PROB:
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_FLOAT)
+ result->prob = scanner->value.v_float;
+ else if (token == G_TOKEN_INT)
+ result->prob = scanner->value.v_int;
+ else
+ return G_TOKEN_FLOAT;
+
+ break;
+
+ default:
+ return G_TOKEN_SYMBOL;
+ }
+
+ token = g_scanner_get_next_token (scanner);
+ }
+
+ return G_TOKEN_NONE;
+}
+
+/*************************************************************
+ * ifsvals_parse:
+ * Read in ifsvalues from a GScanner
+ * arguments:
+ * scanner:
+ * vals:
+ * elements:
+ *
+ * results:
+ * If parsing succeeded, TRUE; otherwise FALSE, in which
+ * case vals and elements are unchanged
+ *************************************************************/
+
+static gboolean
+ifsvals_parse (GScanner *scanner,
+ IfsComposeVals *vals,
+ AffElement ***elements)
+{
+ GTokenType token, expected_token;
+ AffElement *el;
+ IfsComposeVals new_vals;
+ GimpRGB color;
+
+ GList *el_list = NULL;
+ GList *tmp_list;
+ gint i;
+
+ new_vals = *vals;
+ new_vals.num_elements = 0;
+ i = 0;
+
+ expected_token = G_TOKEN_NONE;
+ while (expected_token == G_TOKEN_NONE)
+ {
+ token = g_scanner_get_next_token (scanner);
+
+ if (g_scanner_eof (scanner))
+ break;
+
+ switch ((IfsComposeToken) token)
+ {
+ case TOKEN_ITERATIONS:
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_INT)
+ new_vals.iterations = scanner->value.v_int;
+ else
+ expected_token = G_TOKEN_INT;
+ break;
+
+ case TOKEN_MAX_MEMORY:
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_INT)
+ new_vals.max_memory = scanner->value.v_int;
+ else
+ expected_token = G_TOKEN_INT;
+ break;
+
+ case TOKEN_SUBDIVIDE:
+ token = g_scanner_get_next_token (scanner);
+ if (token == G_TOKEN_INT)
+ new_vals.subdivide = scanner->value.v_int;
+ else
+ expected_token = G_TOKEN_INT;
+ break;
+
+ case TOKEN_RADIUS:
+ expected_token = parse_genuine_float (scanner, &new_vals.radius);
+ break;
+
+ case TOKEN_ASPECT_RATIO:
+ expected_token = parse_genuine_float (scanner, &new_vals.aspect_ratio);
+ break;
+
+ case TOKEN_CENTER_X:
+ expected_token = parse_genuine_float (scanner, &new_vals.center_x);
+ break;
+
+ case TOKEN_CENTER_Y:
+ expected_token = parse_genuine_float (scanner, &new_vals.center_y);
+ break;
+
+ case TOKEN_ELEMENT:
+ el = aff_element_new (0.0,0.0, &color, ++i);
+ expected_token = ifsvals_parse_element (scanner, &el->v);
+
+ if (expected_token == G_TOKEN_NONE)
+ {
+ el_list = g_list_prepend (el_list, el);
+ new_vals.num_elements++;
+ }
+ else
+ aff_element_free (el);
+
+ break;
+
+ default:
+ expected_token = G_TOKEN_SYMBOL;
+ }
+ }
+
+ if (expected_token != G_TOKEN_NONE)
+ {
+ g_scanner_unexp_token (scanner,
+ expected_token,
+ NULL,
+ NULL,
+ NULL,
+ "using default values...",
+ TRUE);
+ g_list_free_full (el_list, (GDestroyNotify) g_free);
+ return FALSE;
+ }
+
+ *vals = new_vals;
+
+ el_list = g_list_reverse (el_list);
+ *elements = g_new (AffElement *, new_vals.num_elements);
+
+ tmp_list = el_list;
+ for (i=0; i<new_vals.num_elements; i++)
+ {
+ (*elements)[i] = tmp_list->data;
+ tmp_list = tmp_list->next;
+ }
+
+ g_list_free (el_list);
+
+ return TRUE;
+}
+
+gboolean
+ifsvals_parse_string (const gchar *str,
+ IfsComposeVals *vals,
+ AffElement ***elements)
+{
+ GScanner *scanner = g_scanner_new (NULL);
+ gboolean result;
+ gint i;
+
+ scanner->config->symbol_2_token = TRUE;
+ scanner->config->scan_identifier_1char = TRUE;
+ scanner->input_name = "IfsCompose Saved Data";
+
+ for (i = 0; i < G_N_ELEMENTS (symbols); i++)
+ g_scanner_scope_add_symbol (scanner, 0,
+ symbols[i].name,
+ GINT_TO_POINTER (symbols[i].token));
+
+ g_scanner_input_text (scanner, str, strlen (str));
+
+ result = ifsvals_parse (scanner, vals, elements);
+
+ g_scanner_destroy (scanner);
+
+ return result;
+}
+
+/*************************************************************
+ * ifsvals_stringify:
+ * Stringify a set of vals and elements
+ * arguments:
+ * vals:
+ * elements
+ * results:
+ * The stringified result (free with g_free)
+ *************************************************************/
+
+gchar *
+ifsvals_stringify (IfsComposeVals *vals,
+ AffElement **elements)
+{
+ gint i;
+ gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar cbuf[3][G_ASCII_DTOSTR_BUF_SIZE];
+ GString *result;
+
+ result = g_string_new (NULL);
+
+ g_string_append_printf (result, "iterations %d\n", vals->iterations);
+ g_string_append_printf (result, "max_memory %d\n", vals->max_memory);
+ g_string_append_printf (result, "subdivide %d\n", vals->subdivide);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->radius);
+ g_string_append_printf (result, "radius %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->aspect_ratio);
+ g_string_append_printf (result, "aspect_ratio %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->center_x);
+ g_string_append_printf (result, "center_x %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->center_y);
+ g_string_append_printf (result, "center_y %s\n", buf);
+
+ for (i=0; i<vals->num_elements; i++)
+ {
+ g_string_append (result, "element {\n");
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.x);
+ g_string_append_printf (result, " x %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.y);
+ g_string_append_printf (result, " y %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.theta);
+ g_string_append_printf (result, " theta %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.scale);
+ g_string_append_printf (result, " scale %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.asym);
+ g_string_append_printf (result, " asym %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.shear);
+ g_string_append_printf (result, " shear %s\n", buf);
+ g_string_append_printf (result, " flip %d\n", elements[i]->v.flip);
+
+ g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.r);
+ g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.g);
+ g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.b);
+ g_string_append_printf (result, " red_color { %s,%s,%s }\n",
+ cbuf[0], cbuf[1], cbuf[2]);
+
+ g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.r);
+ g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.g);
+ g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.b);
+ g_string_append_printf (result, " green_color { %s,%s,%s }\n",
+ cbuf[0], cbuf[1], cbuf[2]);
+
+ g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.r);
+ g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.g);
+ g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.b);
+ g_string_append_printf (result, " blue_color { %s,%s,%s }\n",
+ cbuf[0], cbuf[1], cbuf[2]);
+
+ g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.r);
+ g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.g);
+ g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.b);
+ g_string_append_printf (result, " black_color { %s,%s,%s }\n",
+ cbuf[0], cbuf[1], cbuf[2]);
+
+ g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.r);
+ g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.g);
+ g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.b);
+ g_string_append_printf (result, " target_color { %s,%s,%s }\n",
+ cbuf[0], cbuf[1], cbuf[2]);
+
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.hue_scale);
+ g_string_append_printf (result, " hue_scale %s\n", buf);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.value_scale);
+ g_string_append_printf (result, " value_scale %s\n", buf);
+ g_string_append_printf (result, " simple_color %d\n",
+ elements[i]->v.simple_color);
+ g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.prob);
+ g_string_append_printf (result, " prob %s\n", buf);
+ g_string_append (result, "}\n");
+ }
+
+ return g_string_free (result, FALSE);
+}
diff --git a/plug-ins/ifs-compose/ifs-compose-utils.c b/plug-ins/ifs-compose/ifs-compose-utils.c
new file mode 100644
index 0000000..32c05cc
--- /dev/null
+++ b/plug-ins/ifs-compose/ifs-compose-utils.c
@@ -0,0 +1,1092 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * IfsCompose is a interface for creating IFS fractals by
+ * direct manipulation.
+ * Copyright (C) 1997 Owen Taylor
+ *
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <gdk/gdk.h>
+
+#include <libgimp/gimp.h>
+
+#include "ifs-compose.h"
+
+
+typedef struct
+{
+ GdkPoint point;
+ gdouble angle;
+} SortPoint;
+
+
+/* local functions */
+static void aff_element_compute_click_boundary (AffElement *elem,
+ gint num_elements,
+ gdouble *points_x,
+ gdouble *points_y);
+static guchar * create_brush (IfsComposeVals *ifsvals,
+ gint *brush_size,
+ gdouble *brush_offset);
+
+
+void
+aff2_translate (Aff2 *naff,
+ gdouble x,
+ gdouble y)
+{
+ naff->a11 = 1.0;
+ naff->a12 = 0;
+ naff->a21 = 0;
+ naff->a22 = 1.0;
+ naff->b1 = x;
+ naff->b2 = y;
+}
+
+void
+aff2_rotate (Aff2 *naff,
+ gdouble theta)
+{
+ naff->a11 = cos(theta);
+ naff->a12 = sin(theta);
+ naff->a21 = -naff->a12;
+ naff->a22 = naff->a11;
+ naff->b1 = 0;
+ naff->b2 = 0;
+}
+
+void
+aff2_scale (Aff2 *naff,
+ gdouble s,
+ gboolean flip)
+{
+ if (flip)
+ naff->a11 = -s;
+ else
+ naff->a11 = s;
+
+ naff->a12 = 0;
+ naff->a21 = 0;
+ naff->a22 = s;
+ naff->b1 = 0;
+ naff->b2 = 0;
+}
+
+/* Create a unitary transform with given x-y asymmetry and shear */
+void
+aff2_distort (Aff2 *naff,
+ gdouble asym,
+ gdouble shear)
+{
+ naff->a11 = asym;
+ naff->a22 = 1/asym;
+ naff->a12 = shear;
+ naff->a21 = 0;
+ naff->b1 = 0;
+ naff->b2 = 0;
+}
+
+/* Find a pure stretch in some direction that brings xo,yo to xn,yn */
+void
+aff2_compute_stretch (Aff2 *naff,
+ gdouble xo,
+ gdouble yo,
+ gdouble xn,
+ gdouble yn)
+{
+ gdouble denom = xo*xn + yo*yn;
+
+ if (denom == 0.0) /* singular */
+ {
+ naff->a11 = 1.0;
+ naff->a12 = 0.0;
+ naff->a21 = 0.0;
+ naff->a22 = 1.0;
+ }
+ else
+ {
+ naff->a11 = (SQR(xn) + SQR(yo)) / denom;
+ naff->a22 = (SQR(xo) + SQR(yn)) / denom;
+ naff->a12 = naff->a21 = (xn * yn - xo * yo) / denom;
+ }
+
+ naff->b1 = 0.0;
+ naff->b2 = 0.0;
+}
+
+void
+aff2_compose (Aff2 *naff,
+ Aff2 *aff1,
+ Aff2 *aff2)
+{
+ naff->a11 = aff1->a11 * aff2->a11 + aff1->a12 * aff2->a21;
+ naff->a12 = aff1->a11 * aff2->a12 + aff1->a12 * aff2->a22;
+ naff->b1 = aff1->a11 * aff2->b1 + aff1->a12 * aff2->b2 + aff1->b1;
+ naff->a21 = aff1->a21 * aff2->a11 + aff1->a22 * aff2->a21;
+ naff->a22 = aff1->a21 * aff2->a12 + aff1->a22 * aff2->a22;
+ naff->b2 = aff1->a21 * aff2->b1 + aff1->a22 * aff2->b2 + aff1->b2;
+}
+
+/* Returns the identity matrix if the original matrix was singular */
+void
+aff2_invert (Aff2 *naff,
+ Aff2 *aff)
+{
+ gdouble det = aff->a11 * aff->a22 - aff->a12 * aff->a21;
+
+ if (det==0)
+ {
+ aff2_scale (naff, 1.0, 0);
+ }
+ else
+ {
+ naff->a11 = aff->a22 / det;
+ naff->a22 = aff->a11 / det;
+ naff->a21 = - aff->a21 / det;
+ naff->a12 = - aff->a12 / det;
+ naff->b1 = - naff->a11 * aff->b1 - naff->a12 * aff->b2;
+ naff->b2 = - naff->a21 * aff->b1 - naff->a22 * aff->b2;
+ }
+}
+
+void
+aff2_apply (Aff2 *aff,
+ gdouble x,
+ gdouble y,
+ gdouble *xf,
+ gdouble *yf)
+{
+ gdouble xt = aff->a11 * x + aff->a12 * y + aff->b1;
+ gdouble yt = aff->a21 * x + aff->a22 * y + aff->b2;
+
+ *xf = xt;
+ *yf = yt;
+}
+
+/* Find the fixed point of an affine transformation
+ (Will return garbage for pure translations) */
+
+void
+aff2_fixed_point (Aff2 *aff,
+ gdouble *xf,
+ gdouble *yf)
+{
+ Aff2 t1,t2;
+
+ t1.a11 = 1 - aff->a11;
+ t1.a22 = 1 - aff->a22;
+ t1.a12 = -aff->a12;
+ t1.a21 = -aff->a21;
+ t1.b1 = 0;
+ t1.b2 = 0;
+
+ aff2_invert (&t2, &t1);
+ aff2_apply (&t2, aff->b1, aff->b2, xf, yf);
+}
+
+void
+aff3_apply (Aff3 *t,
+ gdouble x,
+ gdouble y,
+ gdouble z,
+ gdouble *xf,
+ gdouble *yf,
+ gdouble *zf)
+{
+ gdouble xt = (t->vals[0][0] * x +
+ t->vals[0][1] * y +
+ t->vals[0][2] * z + t->vals[0][3]);
+ gdouble yt = (t->vals[1][0] * x +
+ t->vals[1][1] * y +
+ t->vals[1][2] * z + t->vals[1][3]);
+ gdouble zt = (t->vals[2][0] * x +
+ t->vals[2][1] * y +
+ t->vals[2][2] * z + t->vals[2][3]);
+
+ *xf = xt;
+ *yf = yt;
+ *zf = zt;
+}
+
+static int
+ipolygon_sort_func (const void *a,
+ const void *b)
+{
+ if (((SortPoint *)a)->angle < ((SortPoint *)b)->angle)
+ return -1;
+ else if (((SortPoint *)a)->angle > ((SortPoint *)b)->angle)
+ return 1;
+ else
+ return 0;
+}
+
+/* Return a newly-allocated polygon which is the convex hull
+ of the given polygon.
+
+ Uses the Graham scan. see
+ http://www.cs.curtin.edu.au/units/cg201/notes/node77.html
+
+ for a description
+*/
+
+IPolygon *
+ipolygon_convex_hull (IPolygon *poly)
+{
+ gint num_new = poly->npoints;
+ GdkPoint *new_points = g_new (GdkPoint, num_new);
+ SortPoint *sort_points = g_new (SortPoint, num_new);
+ IPolygon *new_poly = g_new (IPolygon, 1);
+
+ gint i, j;
+ gint x1, x2, y1, y2;
+ gint lowest;
+ GdkPoint lowest_pt;
+
+ new_poly->points = new_points;
+ if (num_new <= 3)
+ {
+ memcpy (new_points, poly->points, num_new * sizeof (GdkPoint));
+ new_poly->npoints = num_new;
+ g_free (sort_points);
+ return new_poly;
+ }
+
+ /* scan for the lowest point */
+ lowest_pt = poly->points[0];
+ lowest = 0;
+
+ for (i = 1; i < num_new; i++)
+ if (poly->points[i].y < lowest_pt.y)
+ {
+ lowest_pt = poly->points[i];
+ lowest = i;
+ }
+
+ /* sort by angle from lowest point */
+
+ for (i = 0, j = 0; i < num_new; i++, j++)
+ {
+ if (i==lowest)
+ j--;
+ else
+ {
+ gdouble dy = poly->points[i].y - lowest_pt.y;
+ gdouble dx = poly->points[i].x - lowest_pt.x;
+
+ if (dy == 0 && dx == 0)
+ {
+ j--;
+ num_new--;
+ continue;
+ }
+ sort_points[j].point = poly->points[i];
+ sort_points[j].angle = atan2 (dy, dx);
+ }
+ }
+
+ qsort (sort_points, num_new - 1, sizeof (SortPoint), ipolygon_sort_func);
+
+ /* now ensure that all turns as we trace the perimeter are
+ counter-clockwise */
+
+ new_points[0] = lowest_pt;
+ new_points[1] = sort_points[0].point;
+ x1 = new_points[1].x - new_points[0].x;
+ y1 = new_points[1].y - new_points[0].y;
+
+ for (i = 1, j = 2; j < num_new; i++, j++)
+ {
+ x2 = sort_points[i].point.x - new_points[j - 1].x;
+ y2 = sort_points[i].point.y - new_points[j - 1].y;
+
+ if (x2 == 0 && y2 == 0)
+ {
+ num_new--;
+ j--;
+ continue;
+ }
+
+ while (x1 * y2 - x2 * y1 < 0) /* clockwise rotation */
+ {
+ num_new--;
+ j--;
+ x1 = new_points[j - 1].x - new_points[j - 2].x;
+ y1 = new_points[j - 1].y - new_points[j - 2].y;
+ x2 = sort_points[i].point.x - new_points[j - 1].x;
+ y2 = sort_points[i].point.y - new_points[j - 1].y;
+ }
+ new_points[j] = sort_points[i].point;
+ x1 = x2;
+ y1 = y2;
+ }
+
+ g_free (sort_points);
+
+ new_poly->npoints = num_new;
+
+ return new_poly;
+}
+
+/* Determines whether a specified point is in the given polygon.
+ Based on
+
+ inpoly.c by Bob Stein and Craig Yap.
+
+ (Linux Journal, Issue 35 (March 1997), p 68)
+ */
+
+gint
+ipolygon_contains (IPolygon *poly,
+ gint xt,
+ gint yt)
+{
+ gint xnew, ynew;
+ gint xold, yold;
+ gint x1,y1;
+ gint x2,y2;
+
+ gint i;
+ gint inside = 0;
+
+ if (poly->npoints < 3)
+ return 0;
+
+ xold=poly->points[poly->npoints - 1].x;
+ yold=poly->points[poly->npoints - 1].y;
+ for (i = 0; i < poly->npoints; i++)
+ {
+ xnew = poly->points[i].x;
+ ynew = poly->points[i].y;
+ if (xnew > xold)
+ {
+ x1 = xold;
+ x2 = xnew;
+ y1 = yold;
+ y2 = ynew;
+ }
+ else
+ {
+ x1 = xnew;
+ x2 = xold;
+ y1 = ynew;
+ y2 = yold;
+ }
+ if ((xnew < xt) == (xt <= xold) &&
+ (yt - y1)*(x2 - x1) < (y2 - y1)*(xt - x1))
+ inside = !inside;
+ xold = xnew;
+ yold = ynew;
+ }
+ return inside;
+}
+
+void
+aff_element_compute_color_trans (AffElement *elem)
+{
+ int i, j;
+
+ if (elem->v.simple_color)
+ {
+ gdouble mag2;
+
+ mag2 = SQR (elem->v.target_color.r);
+ mag2 += SQR (elem->v.target_color.g);
+ mag2 += SQR (elem->v.target_color.b);
+
+ /* For mag2 == 0, the transformation blows up in general
+ but is well defined for hue_scale == value_scale, so
+ we assume that special case. */
+ if (mag2 == 0)
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 4; j++)
+ elem->color_trans.vals[i][j] = 0.0;
+
+ elem->color_trans.vals[i][i] = elem->v.hue_scale;
+ }
+ else
+ {
+ /* red */
+ for (j = 0; j < 3; j++)
+ {
+ elem->color_trans.vals[0][j] = elem->v.target_color.r
+ / mag2 * (elem->v.value_scale - elem->v.hue_scale);
+ }
+
+ /* green */
+ for (j = 0; j < 3; j++)
+ {
+ elem->color_trans.vals[1][j] = elem->v.target_color.g
+ / mag2 * (elem->v.value_scale - elem->v.hue_scale);
+ }
+
+ /* blue */
+ for (j = 0; j < 3; j++)
+ {
+ elem->color_trans.vals[2][j] = elem->v.target_color.g
+ / mag2 * (elem->v.value_scale - elem->v.hue_scale);
+ }
+
+ elem->color_trans.vals[0][0] += elem->v.hue_scale;
+ elem->color_trans.vals[1][1] += elem->v.hue_scale;
+ elem->color_trans.vals[2][2] += elem->v.hue_scale;
+
+ elem->color_trans.vals[0][3] =
+ (1 - elem->v.value_scale) * elem->v.target_color.r;
+ elem->color_trans.vals[1][3] =
+ (1 - elem->v.value_scale) * elem->v.target_color.g;
+ elem->color_trans.vals[2][3] =
+ (1 - elem->v.value_scale) * elem->v.target_color.b;
+
+ }
+
+
+ aff3_apply (&elem->color_trans, 1.0, 0.0, 0.0,
+ &elem->v.red_color.r,
+ &elem->v.red_color.g,
+ &elem->v.red_color.b);
+ aff3_apply (&elem->color_trans, 0.0, 1.0, 0.0,
+ &elem->v.green_color.r,
+ &elem->v.green_color.g,
+ &elem->v.green_color.b);
+ aff3_apply (&elem->color_trans, 0.0, 0.0, 1.0,
+ &elem->v.blue_color.r,
+ &elem->v.blue_color.g,
+ &elem->v.blue_color.b);
+ aff3_apply (&elem->color_trans, 0.0, 0.0, 0.0,
+ &elem->v.black_color.r,
+ &elem->v.black_color.g,
+ &elem->v.black_color.b);
+ }
+ else
+ {
+ elem->color_trans.vals[0][0] =
+ elem->v.red_color.r - elem->v.black_color.r;
+ elem->color_trans.vals[1][0] =
+ elem->v.red_color.g - elem->v.black_color.g;
+ elem->color_trans.vals[2][0] =
+ elem->v.red_color.b - elem->v.black_color.b;
+
+ elem->color_trans.vals[0][1] =
+ elem->v.green_color.r - elem->v.black_color.r;
+ elem->color_trans.vals[1][1] =
+ elem->v.green_color.g - elem->v.black_color.g;
+ elem->color_trans.vals[2][1] =
+ elem->v.green_color.b - elem->v.black_color.b;
+
+ elem->color_trans.vals[0][2] =
+ elem->v.blue_color.r - elem->v.black_color.r;
+ elem->color_trans.vals[1][2] =
+ elem->v.blue_color.g - elem->v.black_color.g;
+ elem->color_trans.vals[2][2] =
+ elem->v.blue_color.b - elem->v.black_color.b;
+
+ elem->color_trans.vals[0][3] = elem->v.black_color.r;
+ elem->color_trans.vals[1][3] = elem->v.black_color.g;
+ elem->color_trans.vals[2][3] = elem->v.black_color.b;
+ }
+}
+
+void
+aff_element_compute_trans (AffElement *elem,
+ gdouble width,
+ gdouble height,
+ gdouble center_x,
+ gdouble center_y)
+{
+ Aff2 t1, t2, t3;
+
+ /* create the rotation, scaling and shearing part of the transform */
+ aff2_distort (&t1, elem->v.asym, elem->v.shear);
+ aff2_scale (&t2, elem->v.scale, elem->v.flip);
+ aff2_compose (&t3, &t2, &t1);
+ aff2_rotate (&t2, elem->v.theta);
+ aff2_compose (&t1, &t2, &t3);
+
+ /* now create the translational part */
+ aff2_translate (&t2, -center_x*width, -center_y*width);
+ aff2_compose (&t3, &t1, &t2);
+ aff2_translate (&t2, elem->v.x*width, elem->v.y*width);
+ aff2_compose (&elem->trans, &t2, &t3);
+}
+
+void
+aff_element_decompose_trans (AffElement *elem,
+ Aff2 *aff,
+ gdouble width,
+ gdouble height,
+ gdouble center_x,
+ gdouble center_y)
+{
+ Aff2 t1, t2;
+ gdouble det, scale, sign;
+
+ /* pull of the translational parts */
+ aff2_translate (&t1,center_x * width, center_y * width);
+ aff2_compose (&t2, aff, &t1);
+
+ elem->v.x = t2.b1 / width;
+ elem->v.y = t2.b2 / width;
+
+ det = t2.a11 * t2.a22 - t2.a12 * t2.a21;
+
+ if (det == 0.0)
+ {
+ elem->v.scale = 0.0;
+ elem->v.theta = 0.0;
+ elem->v.asym = 1.0;
+ elem->v.shear = 0.0;
+ elem->v.flip = 0;
+ }
+ else
+ {
+ if (det >= 0)
+ {
+ scale = elem->v.scale = sqrt (det);
+ sign = 1;
+ elem->v.flip = 0;
+ }
+ else
+ {
+ scale = elem->v.scale = sqrt (-det);
+ sign = -1;
+ elem->v.flip = 1;
+ }
+
+ elem->v.theta = atan2 (-t2.a21, t2.a11);
+
+ if (cos (elem->v.theta) == 0.0)
+ {
+ elem->v.asym = - t2.a21 / scale / sin (elem->v.theta);
+ elem->v.shear = - sign * t2.a22 / scale / sin (elem->v.theta);
+ }
+ else
+ {
+ elem->v.asym = sign * t2.a11 / scale / cos (elem->v.theta);
+ elem->v.shear = sign *
+ (t2.a12/scale - sin (elem->v.theta)/elem->v.asym)
+ / cos (elem->v.theta);
+ }
+ }
+}
+
+static void
+aff_element_compute_click_boundary (AffElement *elem,
+ int num_elements,
+ gdouble *points_x,
+ gdouble *points_y)
+{
+ gint i;
+ gdouble xtot = 0;
+ gdouble ytot = 0;
+ gdouble xc, yc;
+ gdouble theta;
+ gdouble sth, cth; /* sin(theta), cos(theta) */
+ gdouble axis1, axis2;
+ gdouble axis1max, axis2max, axis1min, axis2min;
+
+ /* compute the center of mass of the points */
+ for (i = 0; i < num_elements; i++)
+ {
+ xtot += points_x[i];
+ ytot += points_y[i];
+ }
+ xc = xtot / num_elements;
+ yc = ytot / num_elements;
+
+ /* compute the sum of the (x+iy)^2, and take half the the resulting
+ angle (xtot+iytot = A*exp(2i*theta)), to get an average direction */
+
+ xtot = 0;
+ ytot = 0;
+ for (i = 0; i < num_elements; i++)
+ {
+ xtot += SQR (points_x[i] - xc) - SQR (points_y[i] - yc);
+ ytot += 2 * (points_x[i] - xc) * (points_y[i] - yc);
+ }
+ theta = 0.5 * atan2 (ytot, xtot);
+ sth = sin (theta);
+ cth = cos (theta);
+
+ /* compute the minimum rectangle at angle theta that bounds the points,
+ 1/2 side lengths left in axis1, axis2, center in xc, yc */
+
+ axis1max = axis1min = 0.0;
+ axis2max = axis2min = 0.0;
+ for (i = 0; i < num_elements; i++)
+ {
+ gdouble proj1 = (points_x[i] - xc) * cth + (points_y[i] - yc) * sth;
+ gdouble proj2 = -(points_x[i] - xc) * sth + (points_y[i] - yc) * cth;
+ if (proj1 < axis1min)
+ axis1min = proj1;
+ if (proj1 > axis1max)
+ axis1max = proj1;
+ if (proj2 < axis2min)
+ axis2min = proj2;
+ if (proj2 > axis2max)
+ axis2max = proj2;
+ }
+ axis1 = 0.5 * (axis1max - axis1min);
+ axis2 = 0.5 * (axis2max - axis2min);
+ xc += 0.5 * ((axis1max + axis1min) * cth - (axis2max + axis2min) * sth);
+ yc += 0.5 * ((axis1max + axis1min) * sth + (axis2max + axis2min) * cth);
+
+ /* if the the rectangle is less than 10 pixels in any dimension,
+ make it click_boundary, otherwise set click_boundary = draw_boundary */
+
+ if (axis1 < 8.0 || axis2 < 8.0)
+ {
+ GdkPoint *points = g_new (GdkPoint, 4);
+
+ elem->click_boundary = g_new (IPolygon, 1);
+ elem->click_boundary->points = points;
+ elem->click_boundary->npoints = 4;
+
+ if (axis1 < 8.0) axis1 = 8.0;
+ if (axis2 < 8.0) axis2 = 8.0;
+
+ points[0].x = xc + axis1 * cth - axis2 * sth;
+ points[0].y = yc + axis1 * sth + axis2 * cth;
+ points[1].x = xc - axis1 * cth - axis2 * sth;
+ points[1].y = yc - axis1 * sth + axis2 * cth;
+ points[2].x = xc - axis1 * cth + axis2 * sth;
+ points[2].y = yc - axis1 * sth - axis2 * cth;
+ points[3].x = xc + axis1 * cth + axis2 * sth;
+ points[3].y = yc + axis1 * sth - axis2 * cth;
+ }
+ else
+ elem->click_boundary = elem->draw_boundary;
+}
+
+void
+aff_element_compute_boundary (AffElement *elem,
+ gint width,
+ gint height,
+ AffElement **elements,
+ gint num_elements)
+{
+ gint i;
+ IPolygon tmp_poly;
+ gdouble *points_x;
+ gdouble *points_y;
+
+ if (elem->click_boundary && elem->click_boundary != elem->draw_boundary)
+ g_free (elem->click_boundary);
+ if (elem->draw_boundary)
+ g_free (elem->draw_boundary);
+
+ tmp_poly.npoints = num_elements;
+ tmp_poly.points = g_new (GdkPoint, num_elements);
+ points_x = g_new (gdouble, num_elements);
+ points_y = g_new (gdouble, num_elements);
+
+ for (i = 0; i < num_elements; i++)
+ {
+ aff2_apply (&elem->trans,
+ elements[i]->v.x * width, elements[i]->v.y * width,
+ &points_x[i],&points_y[i]);
+ tmp_poly.points[i].x = (gint)points_x[i];
+ tmp_poly.points[i].y = (gint)points_y[i];
+ }
+
+ elem->draw_boundary = ipolygon_convex_hull (&tmp_poly);
+ aff_element_compute_click_boundary (elem, num_elements, points_x, points_y);
+
+ g_free (tmp_poly.points);
+}
+
+void
+aff_element_draw (AffElement *elem,
+ gboolean selected,
+ gint width,
+ gint height,
+ cairo_t *cr,
+ GdkColor *color,
+ PangoLayout *layout)
+{
+ PangoRectangle rect;
+ gint i;
+
+ pango_layout_set_text (layout, elem->name, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+
+ gdk_cairo_set_source_color (cr, color);
+
+ cairo_move_to (cr,
+ elem->v.x * width - rect.width / 2,
+ elem->v.y * width + rect.height / 2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_fill (cr);
+
+ cairo_set_line_width (cr, 1.0);
+
+ if (elem->click_boundary != elem->draw_boundary)
+ {
+ cairo_move_to (cr,
+ elem->click_boundary->points[0].x,
+ elem->click_boundary->points[0].y);
+
+ for (i = 1; i < elem->click_boundary->npoints; i++)
+ cairo_line_to (cr,
+ elem->click_boundary->points[i].x,
+ elem->click_boundary->points[i].y);
+
+ cairo_close_path (cr);
+
+ cairo_stroke (cr);
+ }
+
+ if (selected)
+ cairo_set_line_width (cr, 3.0);
+
+ cairo_move_to (cr,
+ elem->draw_boundary->points[0].x,
+ elem->draw_boundary->points[0].y);
+
+ for (i = 1; i < elem->draw_boundary->npoints; i++)
+ cairo_line_to (cr,
+ elem->draw_boundary->points[i].x,
+ elem->draw_boundary->points[i].y);
+
+ cairo_close_path (cr);
+
+ cairo_stroke (cr);
+}
+
+AffElement *
+aff_element_new (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gint count)
+{
+ AffElement *elem = g_new (AffElement, 1);
+ gchar buffer[16];
+
+ elem->v.x = x;
+ elem->v.y = y;
+ elem->v.theta = 0.0;
+ elem->v.scale = 0.5;
+ elem->v.asym = 1.0;
+ elem->v.shear = 0.0;
+ elem->v.flip = 0;
+
+ elem->v.red_color = *color;
+ elem->v.blue_color = *color;
+ elem->v.green_color = *color;
+ elem->v.black_color = *color;
+
+ elem->v.target_color = *color;
+ elem->v.hue_scale = 0.5;
+ elem->v.value_scale = 0.5;
+
+ elem->v.simple_color = TRUE;
+
+ elem->draw_boundary = NULL;
+ elem->click_boundary = NULL;
+
+ aff_element_compute_color_trans (elem);
+
+ elem->v.prob = 1.0;
+
+ sprintf (buffer,"%d", count);
+ elem->name = g_strdup (buffer);
+
+ return elem;
+}
+
+void
+aff_element_free (AffElement *elem)
+{
+ if (elem->click_boundary != elem->draw_boundary)
+ g_free (elem->click_boundary);
+
+ g_free (elem->draw_boundary);
+ g_free (elem);
+}
+
+#ifdef DEBUG_BRUSH
+static brush_chars[] = {' ',':','*','@'};
+#endif
+
+static guchar *
+create_brush (IfsComposeVals *ifsvals,
+ gint *brush_size,
+ gdouble *brush_offset)
+{
+ gint i, j;
+ gint ii, jj;
+ guchar *brush;
+#ifdef DEBUG_BRUSH
+ gdouble totpix = 0.0;
+#endif
+
+ gdouble radius = ifsvals->radius * ifsvals->subdivide;
+
+ *brush_size = ceil (2 * radius);
+ *brush_offset = 0.5 * (*brush_size - 1);
+
+ brush = g_new (guchar, SQR (*brush_size));
+
+ for (i = 0; i < *brush_size; i++)
+ {
+ for (j = 0; j < *brush_size; j++)
+ {
+ gdouble pixel = 0.0;
+ gdouble d = sqrt (SQR (i - *brush_offset) +
+ SQR (j - *brush_offset));
+
+ if (d - 0.5 * G_SQRT2 > radius)
+ pixel = 0.0;
+ else if (d + 0.5 * G_SQRT2 < radius)
+ pixel = 1.0;
+ else
+ for (ii = 0; ii < 10; ii++)
+ for (jj = 0; jj < 10; jj++)
+ {
+ d = sqrt (SQR (i - *brush_offset + ii * 0.1 - 0.45) +
+ SQR (j - *brush_offset + jj * 0.1 - 0.45));
+ pixel += (d < radius) / 100.0;
+ }
+
+ brush[i * *brush_size + j] = 255.999 * pixel;
+
+#ifdef DEBUG_BRUSH
+ putchar(brush_chars[(gint)(pixel * 3.999)]);
+ totpix += pixel;
+#endif /* DEBUG_BRUSH */
+ }
+#ifdef DEBUG_BRUSH
+ putchar('\n');
+#endif /* DEBUG_BRUSH */
+ }
+#ifdef DEBUG_BRUSH
+ printf ("Brush total / area = %f\n", totpix / SQR (ifsvals->subdivide));
+#endif /* DEBUG_BRUSH */
+ return brush;
+}
+
+void
+ifs_render (AffElement **elements,
+ gint num_elements,
+ gint width,
+ gint height,
+ gint nsteps,
+ IfsComposeVals *vals,
+ gint band_y,
+ gint band_height,
+ guchar *data,
+ guchar *mask,
+ guchar *nhits,
+ gboolean preview)
+{
+ gint i, k, n;
+ gdouble x, y;
+ gdouble r, g, b;
+ gint ri, gi, bi;
+ guint32 p0, psum;
+ gdouble pt;
+ guchar *ptr;
+ guint32 *prob;
+ gdouble *fprob;
+ gint subdivide;
+ guchar *brush = NULL;
+ gint brush_size = 1;
+ gdouble brush_offset = 0.0;
+
+ if (preview)
+ subdivide = 1;
+ else
+ subdivide = vals->subdivide;
+
+ /* compute the probabilities and transforms */
+ fprob = g_new (gdouble, num_elements);
+ prob = g_new (guint32, num_elements);
+ pt = 0.0;
+
+ for (i = 0; i < num_elements; i++)
+ {
+ aff_element_compute_trans(elements[i],
+ width * subdivide,
+ height * subdivide,
+ vals->center_x,
+ vals->center_y);
+ fprob[i] = fabs(
+ elements[i]->trans.a11 * elements[i]->trans.a22
+ - elements[i]->trans.a12 * elements[i]->trans.a21);
+
+ /* As a heuristic, if the determinant is really small, it's
+ probably a line element, so increase the probability so
+ it gets rendered */
+
+ /* FIXME: figure out what 0.01 really should be */
+ if (fprob[i] < 0.01)
+ fprob[i] = 0.01;
+
+ fprob[i] *= elements[i]->v.prob;
+
+ pt += fprob[i];
+ }
+
+ psum = 0;
+ for (i = 0; i < num_elements; i++)
+ {
+ psum += (guint32) -1 * (fprob[i] / pt);
+ prob[i] = psum;
+ }
+
+ prob[i - 1] = (guint32) -1; /* make sure we don't get bitten by roundoff */
+
+ /* create the brush */
+ if (!preview)
+ brush = create_brush (vals, &brush_size, &brush_offset);
+
+ x = y = 0;
+ r = g = b = 0;
+
+ /* n is used to limit the number of progress updates */
+ n = nsteps / 32;
+
+ /* now run the iteration */
+ for (i = 0; i < nsteps; i++)
+ {
+ if (!preview && ((i % n) == 0))
+ gimp_progress_update ((gdouble) i / (gdouble) nsteps);
+
+ p0 = g_random_int ();
+ k = 0;
+
+ while (p0 > prob[k])
+ k++;
+
+ aff2_apply (&elements[k]->trans, x, y, &x, &y);
+ aff3_apply (&elements[k]->color_trans, r, g, b, &r, &g, &b);
+
+ if (i < 50)
+ continue;
+
+ ri = (gint) (255.0 * r + 0.5);
+ gi = (gint) (255.0 * g + 0.5);
+ bi = (gint) (255.0 * b + 0.5);
+
+ if ((ri < 0) || (ri > 255) ||
+ (gi < 0) || (gi > 255) ||
+ (bi < 0) || (bi > 255))
+ continue;
+
+ if (preview)
+ {
+ if ((x < width) && (y < (band_y + band_height)) &&
+ (x >= 0) && (y >= band_y))
+ {
+ ptr = data + 3 * (((gint) (y - band_y)) * width + (gint) x);
+
+ *ptr++ = ri;
+ *ptr++ = gi;
+ *ptr = bi;
+ }
+ }
+ else
+ {
+ if ((x < width * subdivide) && (y < height * subdivide) &&
+ (x >= 0) && (y >= 0))
+ {
+ gint ii;
+ gint jj;
+ gint jj0 = floor (y - brush_offset - band_y * subdivide);
+ gint ii0 = floor (x - brush_offset);
+ gint jjmin = 0;
+ gint iimin = 0;
+ gint jjmax;
+ gint iimax;
+
+ if (ii0 < 0)
+ iimin = - ii0;
+ else
+ iimin = 0;
+
+ if (jj0 < 0)
+ jjmin = - jj0;
+ else
+ jjmin = 0;
+
+ if (jj0 + brush_size >= subdivide * band_height)
+ jjmax = subdivide * band_height - jj0;
+ else
+ jjmax = brush_size;
+
+ if (ii0 + brush_size >= subdivide * width)
+ iimax = subdivide * width - ii0;
+ else
+ iimax = brush_size;
+
+ for (jj = jjmin; jj < jjmax; jj++)
+ for (ii = iimin; ii < iimax; ii++)
+ {
+ guint m_old;
+ guint m_new;
+ guint m_pix;
+ guint n_hits;
+ guint old_scale;
+ guint pix_scale;
+ gint index = (jj0 + jj) * width * subdivide + ii0 + ii;
+
+ n_hits = nhits[index];
+ if (n_hits == 255)
+ continue;
+
+ m_pix = brush[jj * brush_size + ii];
+ if (!m_pix)
+ continue;
+
+ nhits[index] = ++n_hits;
+ m_old = mask[index];
+ m_new = m_old + m_pix - m_old * m_pix / 255;
+ mask[index] = m_new;
+
+ /* relative probability that old colored pixel is on top */
+ old_scale = m_old * (255 * n_hits - m_pix);
+
+ /* relative probability that new colored pixel is on top */
+ pix_scale = m_pix * ((255 - m_old) * n_hits + m_old);
+
+ ptr = data + 3 * index;
+
+ *ptr = ((old_scale * (*ptr) + pix_scale * ri) /
+ (old_scale + pix_scale));
+ ptr++;
+
+ *ptr = ((old_scale * (*ptr) + pix_scale * gi) /
+ (old_scale + pix_scale));
+ ptr++;
+
+ *ptr = ((old_scale * (*ptr) + pix_scale * bi) /
+ (old_scale + pix_scale));
+ }
+ }
+ }
+ } /* main iteration */
+
+ if (!preview )
+ gimp_progress_update (1.0);
+
+ g_free (brush);
+ g_free (prob);
+ g_free (fprob);
+}
diff --git a/plug-ins/ifs-compose/ifs-compose.c b/plug-ins/ifs-compose/ifs-compose.c
new file mode 100644
index 0000000..27cad9c
--- /dev/null
+++ b/plug-ins/ifs-compose/ifs-compose.c
@@ -0,0 +1,2799 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * IfsCompose is a interface for creating IFS fractals by
+ * direct manipulation.
+ * Copyright (C) 1997 Owen Taylor
+ *
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* TODO
+ * ----
+ *
+ * 1. Run in non-interactive mode (need to figure out useful way for a
+ * script to give the 19N parameters for an image). Perhaps just
+ * support saving parameters to a file, script passes file name.
+ * 2. Figure out if we need multiple phases for supersampled brushes.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "ifs-compose.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define RESPONSE_RESET 1
+#define RESPONSE_OPEN 2
+#define RESPONSE_SAVE 3
+
+#define DESIGN_AREA_MAX_SIZE 300
+
+#define PREVIEW_RENDER_CHUNK 10000
+
+#define UNDO_LEVELS 24
+
+#define PLUG_IN_PARASITE "ifscompose-parasite"
+#define PLUG_IN_PROC "plug-in-ifscompose"
+#define PLUG_IN_BINARY "ifs-compose"
+#define PLUG_IN_ROLE "gimp-ifs-compose"
+
+typedef enum
+{
+ OP_TRANSLATE,
+ OP_ROTATE, /* or scale */
+ OP_STRETCH
+} DesignOp;
+
+typedef enum
+{
+ VALUE_PAIR_INT,
+ VALUE_PAIR_DOUBLE
+} ValuePairType;
+
+typedef struct
+{
+ GtkAdjustment *adjustment;
+ GtkWidget *scale;
+ GtkWidget *spin;
+
+ ValuePairType type;
+ guint timeout_id;
+
+ union
+ {
+ gdouble *d;
+ gint *i;
+ } data;
+} ValuePair;
+
+typedef struct
+{
+ IfsComposeVals ifsvals;
+ AffElement **elements;
+ gboolean *element_selected;
+ gint current_element;
+} UndoItem;
+
+typedef struct
+{
+ GimpRGB *color;
+ GtkWidget *hbox;
+ GtkWidget *orig_preview;
+ GtkWidget *button;
+ gboolean fixed_point;
+} ColorMap;
+
+typedef struct
+{
+ GtkWidget *dialog;
+
+ ValuePair *iterations_pair;
+ ValuePair *subdivide_pair;
+ ValuePair *radius_pair;
+ ValuePair *memory_pair;
+} IfsOptionsDialog;
+
+typedef struct
+{
+ GtkWidget *area;
+ GtkUIManager *ui_manager;
+ GdkPixmap *pixmap;
+
+ DesignOp op;
+ gdouble op_x;
+ gdouble op_y;
+ gdouble op_xcenter;
+ gdouble op_ycenter;
+ gdouble op_center_x;
+ gdouble op_center_y;
+ guint button_state;
+ gint num_selected;
+} IfsDesignArea;
+
+typedef struct
+{
+ ValuePair *prob_pair;
+ ValuePair *x_pair;
+ ValuePair *y_pair;
+ ValuePair *scale_pair;
+ ValuePair *angle_pair;
+ ValuePair *asym_pair;
+ ValuePair *shear_pair;
+ GtkWidget *flip_check_button;
+
+ ColorMap *red_cmap;
+ ColorMap *green_cmap;
+ ColorMap *blue_cmap;
+ ColorMap *black_cmap;
+ ColorMap *target_cmap;
+ ValuePair *hue_scale_pair;
+ ValuePair *value_scale_pair;
+ GtkWidget *simple_button;
+ GtkWidget *full_button;
+ GtkWidget *current_frame;
+
+ GtkWidget *preview;
+ guchar *preview_data;
+ gint preview_iterations;
+
+ gint drawable_width;
+ gint drawable_height;
+ gint preview_width;
+ gint preview_height;
+
+ AffElement *selected_orig;
+ gint current_element;
+ AffElementVals current_vals;
+
+ gboolean in_update; /* true if we're currently in
+ update_values() - don't do anything
+ on updates */
+} IfsDialog;
+
+typedef struct
+{
+ gboolean run;
+} IfsComposeInterface;
+
+/* Declare local functions.
+ */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+/* user interface functions */
+static gint ifs_compose_dialog (gint32 drawable_id);
+static void ifs_options_dialog (GtkWidget *parent);
+static GtkWidget * ifs_compose_trans_page (void);
+static GtkWidget * ifs_compose_color_page (void);
+static GtkUIManager * design_op_menu_create (GtkWidget *window);
+static void design_op_actions_update (void);
+static void design_area_create (GtkWidget *window,
+ gint design_width,
+ gint design_height);
+
+/* functions for drawing design window */
+static void update_values (void);
+static void set_current_element (gint index);
+static void design_area_realize (GtkWidget *widget);
+static gint design_area_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint design_area_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint design_area_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static void design_area_select_all_callback (GtkWidget *widget,
+ gpointer data);
+static gint design_area_configure (GtkWidget *widget,
+ GdkEventConfigure *event);
+static gint design_area_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static void design_area_redraw (void);
+
+/* Undo ring functions */
+static void undo_begin (void);
+static void undo_update (gint element);
+static void undo_exchange (gint el);
+static void undo (void);
+static void redo (void);
+
+static void recompute_center (gboolean save_undo);
+static void recompute_center_cb (GtkWidget *widget,
+ gpointer data);
+
+static void ifs_compose (gint32 drawable_id);
+
+static ColorMap *color_map_create (const gchar *name,
+ GimpRGB *orig_color,
+ GimpRGB *data,
+ gboolean fixed_point);
+static void color_map_color_changed_cb (GtkWidget *widget,
+ ColorMap *color_map);
+static void color_map_update (ColorMap *color_map);
+
+/* interface functions */
+static void simple_color_toggled (GtkWidget *widget, gpointer data);
+static void simple_color_set_sensitive (void);
+static void val_changed_update (void);
+static ValuePair *value_pair_create (gpointer data,
+ gdouble lower,
+ gdouble upper,
+ gboolean create_scale,
+ ValuePairType type);
+static void value_pair_update (ValuePair *value_pair);
+static void value_pair_scale_callback (GtkAdjustment *adjustment,
+ ValuePair *value_pair);
+
+static void design_op_update_callback (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer data);
+static void flip_check_button_callback (GtkWidget *widget, gpointer data);
+static gint preview_idle_render (gpointer data);
+
+static void ifs_compose_preview (void);
+static void ifs_compose_set_defaults (void);
+static void ifs_compose_new_callback (GtkAction *action,
+ gpointer data);
+static void ifs_compose_delete_callback (GtkAction *action,
+ gpointer data);
+static void ifs_compose_options_callback (GtkAction *action,
+ gpointer data);
+static void ifs_compose_load (GtkWidget *parent);
+static void ifs_compose_save (GtkWidget *parent);
+static void ifs_compose_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+
+/*
+ * Some static variables
+ */
+
+static IfsDialog *ifsD = NULL;
+static IfsOptionsDialog *ifsOptD = NULL;
+static IfsDesignArea *ifsDesign = NULL;
+
+
+static AffElement **elements = NULL;
+static gint *element_selected = NULL;
+/* labels are generated by printing this int */
+static gint count_for_naming = 0;
+
+static UndoItem undo_ring[UNDO_LEVELS];
+static gint undo_cur = -1;
+static gint undo_num = 0;
+static gint undo_start = 0;
+
+
+/* num_elements = 0, signals not inited */
+static IfsComposeVals ifsvals =
+{
+ 0, /* num_elements */
+ 50000, /* iterations */
+ 4096, /* max_memory */
+ 4, /* subdivide */
+ 0.75, /* radius */
+ 1.0, /* aspect ratio */
+ 0.5, /* center_x */
+ 0.5, /* center_y */
+};
+
+static IfsComposeInterface ifscint =
+{
+ FALSE, /* run */
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ };
+
+ static const GimpParamDef *return_vals = NULL;
+ static int nreturn_vals = 0;
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create an Iterated Function System (IFS) fractal"),
+ "Interactively create an Iterated Function System "
+ "fractal. Use the window on the upper left to adjust "
+ "the component transformations of the fractal. The "
+ "operation that is performed is selected by the "
+ "buttons underneath the window, or from a menu "
+ "popped up by the right mouse button. The fractal "
+ "will be rendered with a transparent background if "
+ "the current image has an alpha channel.",
+ "Owen Taylor",
+ "Owen Taylor",
+ "1997",
+ N_("_IFS Fractal..."),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), nreturn_vals,
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC,
+ "<Image>/Filters/Render/Fractals");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpParasite *parasite = NULL;
+ gint32 image_id;
+ gint32 drawable_id;
+ gboolean found_parasite = FALSE;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ image_id = param[1].data.d_image;
+ drawable_id = param[2].data.d_drawable;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data; first look for a parasite -
+ * if not found, fall back to global values
+ */
+ parasite = gimp_item_get_parasite (drawable_id,
+ PLUG_IN_PARASITE);
+ if (parasite)
+ {
+ found_parasite = ifsvals_parse_string (gimp_parasite_data (parasite),
+ &ifsvals, &elements);
+ gimp_parasite_free (parasite);
+ }
+
+ if (!found_parasite)
+ {
+ gint length = gimp_get_data_size (PLUG_IN_PROC);
+
+ if (length > 0)
+ {
+ gchar *data = g_new (gchar, length);
+
+ gimp_get_data (PLUG_IN_PROC, data);
+ ifsvals_parse_string (data, &ifsvals, &elements);
+ g_free (data);
+ }
+ }
+
+ /* after ifsvals_parse_string, need to set up naming */
+ count_for_naming = ifsvals.num_elements;
+
+ /* First acquire information with a dialog */
+ if (! ifs_compose_dialog (drawable_id))
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ status = GIMP_PDB_CALLING_ERROR;
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ gint length = gimp_get_data_size (PLUG_IN_PROC);
+
+ if (length > 0)
+ {
+ gchar *data = g_new (gchar, length);
+
+ gimp_get_data (PLUG_IN_PROC, data);
+ ifsvals_parse_string (data, &ifsvals, &elements);
+ g_free (data);
+ }
+ else
+ {
+ ifs_compose_set_defaults ();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Render the fractal */
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ gchar *str;
+ GimpParasite *parasite;
+
+ gimp_image_undo_group_start (image_id);
+
+ /* run the effect */
+ ifs_compose (drawable_id);
+
+ /* Store data for next invocation - both globally and
+ * as a parasite on this layer
+ */
+ str = ifsvals_stringify (&ifsvals, elements);
+
+ gimp_set_data (PLUG_IN_PROC, str, strlen (str) + 1);
+
+ parasite = gimp_parasite_new (PLUG_IN_PARASITE,
+ GIMP_PARASITE_PERSISTENT |
+ GIMP_PARASITE_UNDOABLE,
+ strlen (str) + 1, str);
+ gimp_item_attach_parasite (drawable_id, parasite);
+ gimp_parasite_free (parasite);
+
+ g_free (str);
+
+ gimp_image_undo_group_end (image_id);
+
+ gimp_displays_flush ();
+ }
+ else
+ {
+ /* run the effect */
+ ifs_compose (drawable_id);
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+static GtkWidget *
+ifs_compose_trans_page (void)
+{
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *label;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ table = gtk_table_new (3, 6, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 2, 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 4, 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 2, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* X */
+
+ label = gtk_label_new (_("X:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->x_pair = value_pair_create (&ifsD->current_vals.x, 0.0, 1.0, FALSE,
+ VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->x_pair->spin, 1, 2, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->x_pair->spin);
+
+ /* Y */
+
+ label = gtk_label_new (_("Y:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->y_pair = value_pair_create (&ifsD->current_vals.y, 0.0, 1.0, FALSE,
+ VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->y_pair->spin, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->y_pair->spin);
+
+ /* Scale */
+
+ label = gtk_label_new (_("Scale:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->scale_pair = value_pair_create (&ifsD->current_vals.scale, 0.0, 1.0,
+ FALSE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->scale_pair->spin, 3, 4, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->scale_pair->spin);
+
+ /* Angle */
+
+ label = gtk_label_new (_("Angle:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->angle_pair = value_pair_create (&ifsD->current_vals.theta, -180, 180,
+ FALSE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->angle_pair->spin, 3, 4, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->angle_pair->spin);
+
+ /* Asym */
+
+ label = gtk_label_new (_("Asymmetry:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 4, 5, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->asym_pair = value_pair_create (&ifsD->current_vals.asym, 0.10, 10.0,
+ FALSE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->asym_pair->spin, 5, 6, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->asym_pair->spin);
+
+ /* Shear */
+
+ label = gtk_label_new (_("Shear:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 4, 5, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->shear_pair = value_pair_create (&ifsD->current_vals.shear, -10.0, 10.0,
+ FALSE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->shear_pair->spin, 5, 6, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->shear_pair->spin);
+
+ /* Flip */
+
+ ifsD->flip_check_button = gtk_check_button_new_with_label (_("Flip"));
+ gtk_table_attach (GTK_TABLE (table), ifsD->flip_check_button, 0, 6, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ g_signal_connect (ifsD->flip_check_button, "toggled",
+ G_CALLBACK (flip_check_button_callback),
+ NULL);
+ gtk_widget_show (ifsD->flip_check_button);
+
+ return vbox;
+}
+
+static GtkWidget *
+ifs_compose_color_page (void)
+{
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ GSList *group = NULL;
+ GimpRGB color;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+
+ table = gtk_table_new (3, 5, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Simple color control section */
+
+ ifsD->simple_button = gtk_radio_button_new_with_label (group, _("Simple"));
+ gtk_table_attach (GTK_TABLE (table), ifsD->simple_button, 0, 1, 0, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ifsD->simple_button));
+ g_signal_connect (ifsD->simple_button, "toggled",
+ G_CALLBACK (simple_color_toggled),
+ NULL);
+ gtk_widget_show (ifsD->simple_button);
+
+ ifsD->target_cmap = color_map_create (_("IFS Fractal: Target"), NULL,
+ &ifsD->current_vals.target_color, TRUE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->target_cmap->hbox, 1, 2, 0, 2,
+ GTK_FILL, 0, 0, 0);
+ gtk_widget_show (ifsD->target_cmap->hbox);
+
+ label = gtk_label_new (_("Scale hue by:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->hue_scale_pair = value_pair_create (&ifsD->current_vals.hue_scale,
+ 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->scale, 3, 4, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->hue_scale_pair->scale);
+ gtk_table_attach (GTK_TABLE (table), ifsD->hue_scale_pair->spin, 4, 5, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->hue_scale_pair->spin);
+
+ label = gtk_label_new (_("Scale value by:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsD->value_scale_pair = value_pair_create (&ifsD->current_vals.value_scale,
+ 0.0, 1.0, TRUE, VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->scale,
+ 3, 4, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->value_scale_pair->scale);
+ gtk_table_attach (GTK_TABLE (table), ifsD->value_scale_pair->spin,
+ 4, 5, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->value_scale_pair->spin);
+
+ /* Full color control section */
+
+ ifsD->full_button = gtk_radio_button_new_with_label (group, _("Full"));
+ gtk_table_attach (GTK_TABLE (table), ifsD->full_button, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ifsD->full_button));
+ gtk_widget_show (ifsD->full_button);
+
+ gimp_rgb_parse_name (&color, "red", -1);
+ gimp_rgb_set_alpha (&color, 1.0);
+ ifsD->red_cmap = color_map_create (_("IFS Fractal: Red"), &color,
+ &ifsD->current_vals.red_color, FALSE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->red_cmap->hbox, 1, 2, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->red_cmap->hbox);
+
+ gimp_rgb_parse_name (&color, "green", -1);
+ gimp_rgb_set_alpha (&color, 1.0);
+ ifsD->green_cmap = color_map_create (_("IFS Fractal: Green"), &color,
+ &ifsD->current_vals.green_color, FALSE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->green_cmap->hbox, 2, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->green_cmap->hbox);
+
+ gimp_rgb_parse_name (&color, "blue", -1);
+ gimp_rgb_set_alpha (&color, 1.0);
+ ifsD->blue_cmap = color_map_create (_("IFS Fractal: Blue"), &color,
+ &ifsD->current_vals.blue_color, FALSE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->blue_cmap->hbox, 3, 4, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->blue_cmap->hbox);
+
+ gimp_rgb_parse_name (&color, "black", -1);
+ gimp_rgb_set_alpha (&color, 1.0);
+ ifsD->black_cmap = color_map_create (_("IFS Fractal: Black"), &color,
+ &ifsD->current_vals.black_color, FALSE);
+ gtk_table_attach (GTK_TABLE (table), ifsD->black_cmap->hbox, 4, 5, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsD->black_cmap->hbox);
+
+ return vbox;
+}
+
+static gint
+ifs_compose_dialog (gint32 drawable_id)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *toolbar;
+ GtkWidget *aspect_frame;
+ GtkWidget *notebook;
+ GtkWidget *page;
+ gint design_width = gimp_drawable_width (drawable_id);
+ gint design_height = gimp_drawable_height (drawable_id);
+
+ if (design_width > design_height)
+ {
+ if (design_width > DESIGN_AREA_MAX_SIZE)
+ {
+ design_height = design_height * DESIGN_AREA_MAX_SIZE / design_width;
+ design_width = DESIGN_AREA_MAX_SIZE;
+ }
+ }
+ else
+ {
+ if (design_height > DESIGN_AREA_MAX_SIZE)
+ {
+ design_width = design_width * DESIGN_AREA_MAX_SIZE / design_height;
+ design_height = DESIGN_AREA_MAX_SIZE;
+ }
+ }
+
+ ifsD = g_new0 (IfsDialog, 1);
+
+ ifsD->drawable_width = gimp_drawable_width (drawable_id);
+ ifsD->drawable_height = gimp_drawable_height (drawable_id);
+ ifsD->preview_width = design_width;
+ ifsD->preview_height = design_height;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ dialog = gimp_dialog_new (_("IFS Fractal"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Open"), RESPONSE_OPEN,
+ _("_Save"), RESPONSE_SAVE,
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_OPEN,
+ RESPONSE_SAVE,
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_object_add_weak_pointer (G_OBJECT (dialog), (gpointer) &dialog);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (ifs_compose_response),
+ NULL);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ design_area_create (dialog, design_width, design_height);
+
+ toolbar = gtk_ui_manager_get_widget (ifsDesign->ui_manager,
+ "/ifs-compose-toolbar");
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ /* The main vbox */
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, TRUE, TRUE, 0);
+
+ /* The design area */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+
+ aspect_frame = gtk_aspect_frame_new (NULL,
+ 0.5, 0.5,
+ (gdouble) design_width / design_height,
+ 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
+ gtk_widget_show (aspect_frame);
+
+ gtk_container_add (GTK_CONTAINER (aspect_frame), ifsDesign->area);
+ gtk_widget_show (ifsDesign->area);
+
+ /* The Preview */
+
+ aspect_frame = gtk_aspect_frame_new (NULL,
+ 0.5, 0.5,
+ (gdouble) design_width / design_height,
+ 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (aspect_frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 0);
+
+ ifsD->preview = gimp_preview_area_new ();
+ gtk_widget_set_size_request (ifsD->preview,
+ ifsD->preview_width,
+ ifsD->preview_height);
+ gtk_container_add (GTK_CONTAINER (aspect_frame), ifsD->preview);
+ gtk_widget_show (ifsD->preview);
+
+ gtk_widget_show (aspect_frame);
+
+ gtk_widget_show (hbox);
+
+ /* The current transformation frame */
+
+ ifsD->current_frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (main_vbox), ifsD->current_frame,
+ FALSE, FALSE, 0);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (ifsD->current_frame), vbox);
+
+ /* The notebook */
+
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (vbox), notebook, FALSE, FALSE, 0);
+ gtk_widget_show (notebook);
+
+ page = ifs_compose_trans_page ();
+ label = gtk_label_new (_("Spatial Transformation"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.5);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
+ gtk_widget_show (page);
+
+ page = ifs_compose_color_page ();
+ label = gtk_label_new (_("Color Transformation"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.5);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), page, label);
+ gtk_widget_show (page);
+
+ /* The probability entry */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new (_("Relative probability:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ ifsD->prob_pair = value_pair_create (&ifsD->current_vals.prob, 0.0, 5.0, TRUE,
+ VALUE_PAIR_DOUBLE);
+ gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->scale, TRUE, TRUE, 0);
+ gtk_widget_show (ifsD->prob_pair->scale);
+ gtk_box_pack_start (GTK_BOX (hbox), ifsD->prob_pair->spin, FALSE, TRUE, 0);
+ gtk_widget_show (ifsD->prob_pair->spin);
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (vbox);
+ gtk_widget_show (ifsD->current_frame);
+
+ gtk_widget_show (main_vbox);
+
+ if (ifsvals.num_elements == 0)
+ {
+ ifs_compose_set_defaults ();
+ }
+ else
+ {
+ gint i;
+ gdouble ratio = (gdouble) ifsD->drawable_height / ifsD->drawable_width;
+
+ element_selected = g_new (gint, ifsvals.num_elements);
+ element_selected[0] = TRUE;
+ for (i = 1; i < ifsvals.num_elements; i++)
+ element_selected[i] = FALSE;
+
+ if (ratio != ifsvals.aspect_ratio)
+ {
+ /* Adjust things so that what fit onto the old image, fits
+ onto the new image */
+ Aff2 t1, t2, t3;
+ gdouble x_offset, y_offset;
+ gdouble center_x, center_y;
+ gdouble scale;
+
+ if (ratio < ifsvals.aspect_ratio)
+ {
+ scale = ratio/ifsvals.aspect_ratio;
+ x_offset = (1-scale)/2;
+ y_offset = 0;
+ }
+ else
+ {
+ scale = 1;
+ x_offset = 0;
+ y_offset = (ratio - ifsvals.aspect_ratio)/2;
+ }
+ aff2_scale (&t1, scale, 0);
+ aff2_translate (&t2, x_offset, y_offset);
+ aff2_compose (&t3, &t2, &t1);
+ aff2_invert (&t1, &t3);
+
+ aff2_apply (&t3, ifsvals.center_x, ifsvals.center_y, &center_x,
+ &center_y);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ aff_element_compute_trans (elements[i],1, ifsvals.aspect_ratio,
+ ifsvals.center_x, ifsvals.center_y);
+ aff2_compose (&t2, &elements[i]->trans, &t1);
+ aff2_compose (&elements[i]->trans, &t3, &t2);
+ aff_element_decompose_trans (elements[i],&elements[i]->trans,
+ 1, ifsvals.aspect_ratio,
+ center_x, center_y);
+ }
+ ifsvals.center_x = center_x;
+ ifsvals.center_y = center_y;
+
+ ifsvals.aspect_ratio = ratio;
+ }
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_color_trans (elements[i]);
+ /* boundary and spatial transformations will be computed
+ when the design_area gets a ConfigureNotify event */
+
+ set_current_element (0);
+
+ ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
+ }
+
+ gtk_widget_show (dialog);
+
+ ifs_compose_preview ();
+
+ gtk_main ();
+
+ g_object_unref (ifsDesign->ui_manager);
+
+ if (dialog)
+ gtk_widget_destroy (dialog);
+
+ if (ifsOptD)
+ gtk_widget_destroy (ifsOptD->dialog);
+
+ gdk_flush ();
+
+ g_free (ifsD);
+
+ return ifscint.run;
+}
+
+static void
+design_area_create (GtkWidget *window,
+ gint design_width,
+ gint design_height)
+{
+ ifsDesign = g_new0 (IfsDesignArea, 1);
+
+ ifsDesign->op = OP_TRANSLATE;
+
+ ifsDesign->area = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (ifsDesign->area, design_width, design_height);
+
+ g_signal_connect (ifsDesign->area, "realize",
+ G_CALLBACK (design_area_realize),
+ NULL);
+ g_signal_connect (ifsDesign->area, "expose-event",
+ G_CALLBACK (design_area_expose),
+ NULL);
+ g_signal_connect (ifsDesign->area, "button-press-event",
+ G_CALLBACK (design_area_button_press),
+ NULL);
+ g_signal_connect (ifsDesign->area, "button-release-event",
+ G_CALLBACK (design_area_button_release),
+ NULL);
+ g_signal_connect (ifsDesign->area, "motion-notify-event",
+ G_CALLBACK (design_area_motion),
+ NULL);
+ g_signal_connect (ifsDesign->area, "configure-event",
+ G_CALLBACK (design_area_configure),
+ NULL);
+ gtk_widget_set_events (ifsDesign->area,
+ GDK_EXPOSURE_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK);
+
+ ifsDesign->ui_manager = design_op_menu_create (window);
+ design_op_actions_update ();
+}
+
+static GtkUIManager *
+design_op_menu_create (GtkWidget *window)
+{
+ static GtkActionEntry actions[] =
+ {
+ { "ifs-compose-menu", NULL, "IFS Fractal Menu" },
+
+ { "new", GIMP_ICON_DOCUMENT_NEW,
+ N_("_New"), "<primary>N", NULL,
+ G_CALLBACK (ifs_compose_new_callback) },
+
+ { "delete", GIMP_ICON_EDIT_DELETE,
+ N_("_Delete"), "<primary>D", NULL,
+ G_CALLBACK (ifs_compose_delete_callback) },
+
+ { "undo", GIMP_ICON_EDIT_UNDO,
+ N_("_Undo"), "<primary>Z", NULL,
+ G_CALLBACK (undo) },
+
+ { "redo", GIMP_ICON_EDIT_REDO,
+ N_("_Redo"), "<primary>Y", NULL,
+ G_CALLBACK (redo) },
+
+ { "select-all", GIMP_ICON_SELECTION_ALL,
+ N_("Select _All"), "<primary>A", NULL,
+ G_CALLBACK (design_area_select_all_callback) },
+
+ { "center", GIMP_ICON_CENTER,
+ N_("Re_center"), "<primary>C", N_("Recompute Center"),
+ G_CALLBACK (recompute_center_cb) },
+
+ { "options", GIMP_ICON_PREFERENCES_SYSTEM,
+ N_("Render Options"), NULL, NULL,
+ G_CALLBACK (ifs_compose_options_callback) }
+ };
+ static GtkRadioActionEntry radio_actions[] =
+ {
+ { "move", GIMP_ICON_TOOL_MOVE,
+ N_("Move"), "M", NULL, OP_TRANSLATE },
+
+ { "rotate", GIMP_ICON_TOOL_ROTATE,
+ N_("Rotate"), "R", N_("Rotate / Scale"), OP_ROTATE },
+
+ { "stretch", GIMP_ICON_TOOL_PERSPECTIVE,
+ N_("Stretch"), "S", NULL, OP_STRETCH }
+ };
+
+ GtkUIManager *ui_manager = gtk_ui_manager_new ();
+ GtkActionGroup *group = gtk_action_group_new ("Actions");
+
+ gtk_action_group_set_translation_domain (group, NULL);
+
+ gtk_action_group_add_actions (group,
+ actions,
+ G_N_ELEMENTS (actions),
+ window);
+ gtk_action_group_add_radio_actions (group,
+ radio_actions,
+ G_N_ELEMENTS (radio_actions),
+ ifsDesign->op,
+ G_CALLBACK (design_op_update_callback),
+ window);
+
+ gtk_window_add_accel_group (GTK_WINDOW (window),
+ gtk_ui_manager_get_accel_group (ui_manager));
+ gtk_accel_group_lock (gtk_ui_manager_get_accel_group (ui_manager));
+
+ gtk_ui_manager_insert_action_group (ui_manager, group, -1);
+ g_object_unref (group);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <menubar name=\"dummy-menubar\">"
+ " <menu action=\"ifs-compose-menu\">"
+ " <menuitem action=\"move\" />"
+ " <menuitem action=\"rotate\" />"
+ " <menuitem action=\"stretch\" />"
+ " <separator />"
+ " <menuitem action=\"new\" />"
+ " <menuitem action=\"delete\" />"
+ " <menuitem action=\"undo\" />"
+ " <menuitem action=\"redo\" />"
+ " <menuitem action=\"select-all\" />"
+ " <menuitem action=\"center\" />"
+ " <separator />"
+ " <menuitem action=\"options\" />"
+ " </menu>"
+ " </menubar>"
+ "</ui>",
+ -1, NULL);
+
+ gtk_ui_manager_add_ui_from_string (ui_manager,
+ "<ui>"
+ " <toolbar name=\"ifs-compose-toolbar\">"
+ " <toolitem action=\"move\" />"
+ " <toolitem action=\"rotate\" />"
+ " <toolitem action=\"stretch\" />"
+ " <separator />"
+ " <toolitem action=\"new\" />"
+ " <toolitem action=\"delete\" />"
+ " <toolitem action=\"undo\" />"
+ " <toolitem action=\"redo\" />"
+ " <toolitem action=\"select-all\" />"
+ " <toolitem action=\"center\" />"
+ " <separator />"
+ " <toolitem action=\"options\" />"
+ " </toolbar>"
+ "</ui>",
+ -1, NULL);
+
+ return ui_manager;
+}
+
+static void
+design_op_actions_update (void)
+{
+ GtkAction *act;
+
+ act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
+ "/ui/dummy-menubar/ifs-compose-menu/undo");
+ gtk_action_set_sensitive (act, undo_cur >= 0);
+
+ act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
+ "/ui/dummy-menubar/ifs-compose-menu/redo");
+ gtk_action_set_sensitive (act, undo_cur != undo_num - 1);
+
+ act = gtk_ui_manager_get_action (ifsDesign->ui_manager,
+ "/ui/dummy-menubar/ifs-compose-menu/delete");
+ gtk_action_set_sensitive (act, ifsvals.num_elements > 2);
+}
+
+static void
+ifs_options_dialog (GtkWidget *parent)
+{
+ if (!ifsOptD)
+ {
+ GtkWidget *table;
+ GtkWidget *label;
+
+ ifsOptD = g_new0 (IfsOptionsDialog, 1);
+
+ ifsOptD->dialog =
+ gimp_dialog_new (_("IFS Fractal Render Options"), PLUG_IN_ROLE,
+ parent, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ g_signal_connect (ifsOptD->dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ NULL);
+
+ /* Table of options */
+
+ table = gtk_table_new (4, 3, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (ifsOptD->dialog))),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label = gtk_label_new (_("Max. memory:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsOptD->memory_pair = value_pair_create (&ifsvals.max_memory,
+ 1, 1000000, FALSE,
+ VALUE_PAIR_INT);
+ gtk_table_attach (GTK_TABLE (table), ifsOptD->memory_pair->spin,
+ 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsOptD->memory_pair->spin);
+
+ label = gtk_label_new (_("Iterations:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsOptD->iterations_pair = value_pair_create (&ifsvals.iterations,
+ 1, 10000000, FALSE,
+ VALUE_PAIR_INT);
+ gtk_table_attach (GTK_TABLE (table), ifsOptD->iterations_pair->spin,
+ 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsOptD->iterations_pair->spin);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("Subdivide:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsOptD->subdivide_pair = value_pair_create (&ifsvals.subdivide,
+ 1, 10, FALSE,
+ VALUE_PAIR_INT);
+ gtk_table_attach (GTK_TABLE (table), ifsOptD->subdivide_pair->spin,
+ 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsOptD->subdivide_pair->spin);
+
+ label = gtk_label_new (_("Spot radius:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ ifsOptD->radius_pair = value_pair_create (&ifsvals.radius,
+ 0, 5, TRUE,
+ VALUE_PAIR_DOUBLE);
+ gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->scale,
+ 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsOptD->radius_pair->scale);
+ gtk_table_attach (GTK_TABLE (table), ifsOptD->radius_pair->spin,
+ 2, 3, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ifsOptD->radius_pair->spin);
+
+ value_pair_update (ifsOptD->iterations_pair);
+ value_pair_update (ifsOptD->subdivide_pair);
+ value_pair_update (ifsOptD->memory_pair);
+ value_pair_update (ifsOptD->radius_pair);
+
+ gtk_widget_show (ifsOptD->dialog);
+ }
+ else
+ {
+ gtk_window_present (GTK_WINDOW (ifsOptD->dialog));
+ }
+}
+
+static void
+ifs_compose (gint32 drawable_id)
+{
+ GeglBuffer *buffer = gimp_drawable_get_shadow_buffer (drawable_id);
+ gint width = gimp_drawable_width (drawable_id);
+ gint height = gimp_drawable_height (drawable_id);
+ gboolean alpha = gimp_drawable_has_alpha (drawable_id);
+ const Babl *format;
+ gint num_bands;
+ gint band_height;
+ gint band_y;
+ gint band_no;
+ gint i, j;
+ guchar *data;
+ guchar *mask = NULL;
+ guchar *nhits;
+ guchar rc, gc, bc;
+ GimpRGB color;
+
+ if (alpha)
+ format = babl_format ("R'G'B'A u8");
+ else
+ format = babl_format ("R'G'B' u8");
+
+ num_bands = ceil ((gdouble) (width * height * SQR (ifsvals.subdivide) * 5)
+ / (1024 * ifsvals.max_memory));
+ band_height = (height + num_bands - 1) / num_bands;
+
+ if (band_height > height)
+ band_height = height;
+
+ mask = g_new (guchar, width * band_height * SQR (ifsvals.subdivide));
+ data = g_new (guchar, width * band_height * SQR (ifsvals.subdivide) * 3);
+ nhits = g_new (guchar, width * band_height * SQR (ifsvals.subdivide));
+
+ gimp_context_get_background (&color);
+ gimp_rgb_get_uchar (&color, &rc, &gc, &bc);
+
+ for (band_no = 0, band_y = 0; band_no < num_bands; band_no++)
+ {
+ GeglBufferIterator *iter;
+ GeglRectangle *roi;
+
+ gimp_progress_init_printf (_("Rendering IFS (%d/%d)"),
+ band_no + 1, num_bands);
+
+ /* render the band to a buffer */
+ if (band_y + band_height > height)
+ band_height = height - band_y;
+
+ /* we don't need to clear data since we store nhits */
+ memset (mask, 0, width * band_height * SQR (ifsvals.subdivide));
+ memset (nhits, 0, width * band_height * SQR (ifsvals.subdivide));
+
+ ifs_render (elements,
+ ifsvals.num_elements, width, height, ifsvals.iterations,
+ &ifsvals, band_y, band_height, data, mask, nhits, FALSE);
+
+ /* transfer the image to the drawable */
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, band_y,
+ width, band_height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+ roi = &iter->items[0].roi;
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *destrow = iter->items[0].data;
+
+ for (j = roi->y; j < (roi->y + roi->height); j++)
+ {
+ guchar *dest = destrow;
+
+ for (i = roi->x; i < (roi->x + roi->width); i++)
+ {
+ /* Accumulate a reduced pixel */
+
+ gint rtot = 0;
+ gint btot = 0;
+ gint gtot = 0;
+ gint mtot = 0;
+ gint ii, jj;
+
+ for (jj = 0; jj < ifsvals.subdivide; jj++)
+ {
+ guchar *ptr;
+ guchar *maskptr;
+
+ ptr = data +
+ 3 * (((j - band_y) * ifsvals.subdivide + jj) *
+ ifsvals.subdivide * width +
+ i * ifsvals.subdivide);
+
+ maskptr = mask +
+ ((j - band_y) * ifsvals.subdivide + jj) *
+ ifsvals.subdivide * width +
+ i * ifsvals.subdivide;
+
+ for (ii = 0; ii < ifsvals.subdivide; ii++)
+ {
+ guchar maskval = *maskptr++;
+
+ mtot += maskval;
+ rtot += maskval* *ptr++;
+ gtot += maskval* *ptr++;
+ btot += maskval* *ptr++;
+ }
+ }
+
+ if (mtot)
+ {
+ rtot /= mtot;
+ gtot /= mtot;
+ btot /= mtot;
+ mtot /= SQR (ifsvals.subdivide);
+ }
+
+ if (alpha)
+ {
+ *dest++ = rtot;
+ *dest++ = gtot;
+ *dest++ = btot;
+ *dest++ = mtot;
+ }
+ else
+ {
+ *dest++ = (mtot * rtot + (255 - mtot) * rc) / 255;
+ *dest++ = (mtot * gtot + (255 - mtot) * gc) / 255;
+ *dest++ = (mtot * btot + (255 - mtot) * bc) / 255;
+ }
+ }
+
+ if (alpha)
+ destrow += roi->width * 4;
+ else
+ destrow += roi->width * 3;
+ }
+ }
+
+ band_y += band_height;
+ }
+
+ g_free (mask);
+ g_free (data);
+ g_free (nhits);
+
+ g_object_unref (buffer);
+
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id, 0, 0, width, height);
+}
+
+static void
+update_values (void)
+{
+ ifsD->in_update = TRUE;
+
+ ifsD->current_vals = elements[ifsD->current_element]->v;
+ ifsD->current_vals.theta *= 180/G_PI;
+
+ value_pair_update (ifsD->prob_pair);
+ value_pair_update (ifsD->x_pair);
+ value_pair_update (ifsD->y_pair);
+ value_pair_update (ifsD->scale_pair);
+ value_pair_update (ifsD->angle_pair);
+ value_pair_update (ifsD->asym_pair);
+ value_pair_update (ifsD->shear_pair);
+ color_map_update (ifsD->red_cmap);
+ color_map_update (ifsD->green_cmap);
+ color_map_update (ifsD->blue_cmap);
+ color_map_update (ifsD->black_cmap);
+ color_map_update (ifsD->target_cmap);
+ value_pair_update (ifsD->hue_scale_pair);
+ value_pair_update (ifsD->value_scale_pair);
+
+ if (elements[ifsD->current_element]->v.simple_color)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->simple_button),
+ TRUE);
+ else
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->full_button),
+ TRUE);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ifsD->flip_check_button),
+ elements[ifsD->current_element]->v.flip);
+
+ ifsD->in_update = FALSE;
+
+ simple_color_set_sensitive ();
+}
+
+static void
+set_current_element (gint index)
+{
+ gchar *frame_name = g_strdup_printf (_("Transformation %s"),
+ elements[index]->name);
+
+ ifsD->current_element = index;
+
+ gtk_frame_set_label (GTK_FRAME (ifsD->current_frame),frame_name);
+ g_free (frame_name);
+
+ update_values ();
+}
+
+static void
+design_area_realize (GtkWidget *widget)
+{
+ const gint cursors[3] =
+ {
+ GDK_FLEUR, /* OP_TRANSLATE */
+ GDK_EXCHANGE, /* OP_ROTATE */
+ GDK_CROSSHAIR /* OP_SHEAR */
+ };
+
+ GdkDisplay *display = gtk_widget_get_display (widget);
+ GdkCursor *cursor = gdk_cursor_new_for_display (display,
+ cursors[ifsDesign->op]);
+ gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+ gdk_cursor_unref (cursor);
+}
+
+static gboolean
+design_area_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GtkStateType state = gtk_widget_get_state (widget);
+ cairo_t *cr;
+ GtkAllocation allocation;
+ PangoLayout *layout;
+ gint i;
+ gint cx, cy;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cr = gdk_cairo_create (ifsDesign->pixmap);
+
+ gdk_cairo_set_source_color (cr, &style->bg[state]);
+ cairo_paint (cr);
+
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+ cairo_translate (cr, 0.5, 0.5);
+
+ /* draw an indicator for the center */
+
+ cx = ifsvals.center_x * allocation.width;
+ cy = ifsvals.center_y * allocation.width;
+
+ cairo_move_to (cr, cx - 10, cy);
+ cairo_line_to (cr, cx + 10, cy);
+
+ cairo_move_to (cr, cx, cy - 10);
+ cairo_line_to (cr, cx, cy + 10);
+
+ gdk_cairo_set_source_color (cr, &style->fg[state]);
+ cairo_set_line_width (cr, 1.0);
+ cairo_stroke (cr);
+
+ layout = gtk_widget_create_pango_layout (widget, NULL);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ aff_element_draw (elements[i], element_selected[i],
+ allocation.width,
+ allocation.height,
+ cr,
+ &style->fg[state],
+ layout);
+ }
+
+ g_object_unref (layout);
+
+ cairo_destroy (cr);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+
+ gdk_cairo_set_source_pixmap (cr, ifsDesign->pixmap, 0.0, 0.0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+static gboolean
+design_area_configure (GtkWidget *widget,
+ GdkEventConfigure *event)
+{
+ GtkAllocation allocation;
+ gint i;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_boundary (elements[i],
+ allocation.width, allocation.height,
+ elements, ifsvals.num_elements);
+
+ if (ifsDesign->pixmap)
+ {
+ g_object_unref (ifsDesign->pixmap);
+ }
+ ifsDesign->pixmap = gdk_pixmap_new (gtk_widget_get_window (widget),
+ allocation.width,
+ allocation.height,
+ -1); /* Is this correct? */
+
+ return FALSE;
+}
+
+static gint
+design_area_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GtkAllocation allocation;
+ gint i;
+ gint old_current;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ gtk_widget_grab_focus (widget);
+
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ {
+ GtkWidget *menu =
+ gtk_ui_manager_get_widget (ifsDesign->ui_manager,
+ "/dummy-menubar/ifs-compose-menu");
+
+ if (GTK_IS_MENU_ITEM (menu))
+ menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu));
+
+ gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (widget));
+
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ event->button, event->time);
+
+ return FALSE;
+ }
+
+ old_current = ifsD->current_element;
+ ifsD->current_element = -1;
+
+ /* Find out where the button press was */
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ if (ipolygon_contains (elements[i]->click_boundary, event->x, event->y))
+ {
+ set_current_element (i);
+ break;
+ }
+ }
+
+ /* if the user started manipulating an object, set up a new
+ position on the undo ring */
+ if (ifsD->current_element >= 0)
+ undo_begin ();
+
+ if (!(event->state & GDK_SHIFT_MASK)
+ && ( (ifsD->current_element<0)
+ || !element_selected[ifsD->current_element] ))
+ {
+ for (i = 0; i < ifsvals.num_elements; i++)
+ element_selected[i] = FALSE;
+ }
+
+ if (ifsD->current_element >= 0)
+ {
+ ifsDesign->button_state |= GDK_BUTTON1_MASK;
+
+ element_selected[ifsD->current_element] = TRUE;
+
+ ifsDesign->num_selected = 0;
+ ifsDesign->op_xcenter = 0.0;
+ ifsDesign->op_ycenter = 0.0;
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ if (element_selected[i])
+ {
+ ifsD->selected_orig[i] = *elements[i];
+ ifsDesign->op_xcenter += elements[i]->v.x;
+ ifsDesign->op_ycenter += elements[i]->v.y;
+ ifsDesign->num_selected++;
+ undo_update (i);
+ }
+ }
+ ifsDesign->op_xcenter /= ifsDesign->num_selected;
+ ifsDesign->op_ycenter /= ifsDesign->num_selected;
+ ifsDesign->op_x = (gdouble)event->x / allocation.width;
+ ifsDesign->op_y = (gdouble)event->y / allocation.width;
+ ifsDesign->op_center_x = ifsvals.center_x;
+ ifsDesign->op_center_y = ifsvals.center_y;
+ }
+ else
+ {
+ ifsD->current_element = old_current;
+ element_selected[old_current] = TRUE;
+ }
+
+ design_area_redraw ();
+
+ return FALSE;
+}
+
+static gint
+design_area_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 1 &&
+ (ifsDesign->button_state & GDK_BUTTON1_MASK))
+ {
+ ifsDesign->button_state &= ~GDK_BUTTON1_MASK;
+ ifs_compose_preview ();
+ }
+ return FALSE;
+}
+
+static gint
+design_area_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GtkAllocation allocation;
+ gint i;
+ gdouble xo;
+ gdouble yo;
+ gdouble xn;
+ gdouble yn;
+ Aff2 trans, t1, t2, t3;
+
+ if (! (ifsDesign->button_state & GDK_BUTTON1_MASK))
+ return FALSE;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ xo = (ifsDesign->op_x - ifsDesign->op_xcenter);
+ yo = (ifsDesign->op_y - ifsDesign->op_ycenter);
+ xn = (gdouble) event->x / allocation.width - ifsDesign->op_xcenter;
+ yn = (gdouble) event->y / allocation.width - ifsDesign->op_ycenter;
+
+ switch (ifsDesign->op)
+ {
+ case OP_ROTATE:
+ aff2_translate (&t1,-ifsDesign->op_xcenter * allocation.width,
+ -ifsDesign->op_ycenter * allocation.width);
+ aff2_scale (&t2,
+ sqrt((SQR(xn)+SQR(yn))/(SQR(xo)+SQR(yo))),
+ 0);
+ aff2_compose (&t3, &t2, &t1);
+ aff2_rotate (&t1, - atan2(yn, xn) + atan2(yo, xo));
+ aff2_compose (&t2, &t1, &t3);
+ aff2_translate (&t3, ifsDesign->op_xcenter * allocation.width,
+ ifsDesign->op_ycenter * allocation.width);
+ aff2_compose (&trans, &t3, &t2);
+ break;
+
+ case OP_STRETCH:
+ aff2_translate (&t1,-ifsDesign->op_xcenter * allocation.width,
+ -ifsDesign->op_ycenter * allocation.width);
+ aff2_compute_stretch (&t2, xo, yo, xn, yn);
+ aff2_compose (&t3, &t2, &t1);
+ aff2_translate (&t1, ifsDesign->op_xcenter * allocation.width,
+ ifsDesign->op_ycenter * allocation.width);
+ aff2_compose (&trans, &t1, &t3);
+ break;
+
+ case OP_TRANSLATE:
+ aff2_translate (&trans,
+ (xn-xo) * allocation.width,
+ (yn-yo) * allocation.width);
+ break;
+ }
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ if (element_selected[i])
+ {
+ if (ifsDesign->num_selected == ifsvals.num_elements)
+ {
+ gdouble cx, cy;
+ aff2_invert (&t1, &trans);
+ aff2_compose (&t2, &trans, &ifsD->selected_orig[i].trans);
+ aff2_compose (&elements[i]->trans, &t2, &t1);
+
+ cx = ifsDesign->op_center_x * allocation.width;
+ cy = ifsDesign->op_center_y * allocation.width;
+ aff2_apply (&trans, cx, cy, &cx, &cy);
+ ifsvals.center_x = cx / allocation.width;
+ ifsvals.center_y = cy / allocation.width;
+ }
+ else
+ {
+ aff2_compose (&elements[i]->trans, &trans,
+ &ifsD->selected_orig[i].trans);
+ }
+
+ aff_element_decompose_trans (elements[i],&elements[i]->trans,
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ }
+
+ update_values ();
+ design_area_redraw ();
+
+ /* Ask for more motion events in case the event was a hint */
+ gdk_event_request_motions (event);
+
+ return FALSE;
+}
+
+static void
+design_area_redraw (void)
+{
+ GtkAllocation allocation;
+ gint i;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_boundary (elements[i],
+ allocation.width, allocation.height,
+ elements, ifsvals.num_elements);
+
+ gtk_widget_queue_draw (ifsDesign->area);
+}
+
+/* Undo ring functions */
+static void
+undo_begin (void)
+{
+ gint i, j;
+ gint to_delete;
+ gint new_index;
+
+ if (undo_cur == UNDO_LEVELS-1)
+ {
+ to_delete = 1;
+ undo_start = (undo_start + 1) % UNDO_LEVELS;
+ }
+ else
+ {
+ undo_cur++;
+ to_delete = undo_num - undo_cur;
+ }
+
+ undo_num = undo_num - to_delete + 1;
+ new_index = (undo_start + undo_cur) % UNDO_LEVELS;
+
+ /* remove any redo elements or the oldest element if necessary */
+ for (j = new_index; to_delete > 0; j = (j+1) % UNDO_LEVELS, to_delete--)
+ {
+ for (i = 0; i < undo_ring[j].ifsvals.num_elements; i++)
+ if (undo_ring[j].elements[i])
+ aff_element_free (undo_ring[j].elements[i]);
+ g_free (undo_ring[j].elements);
+ g_free (undo_ring[j].element_selected);
+ }
+
+ undo_ring[new_index].ifsvals = ifsvals;
+ undo_ring[new_index].elements = g_new (AffElement *,ifsvals.num_elements);
+ undo_ring[new_index].element_selected = g_new (gboolean,
+ ifsvals.num_elements);
+ undo_ring[new_index].current_element = ifsD->current_element;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ undo_ring[new_index].elements[i] = NULL;
+ undo_ring[new_index].element_selected[i] = element_selected[i];
+ }
+
+ design_op_actions_update ();
+}
+
+static void
+undo_update (gint el)
+{
+ AffElement *elem;
+ /* initialize */
+
+ elem = NULL;
+
+ if (!undo_ring[(undo_start + undo_cur) % UNDO_LEVELS].elements[el])
+ undo_ring[(undo_start + undo_cur) % UNDO_LEVELS].elements[el]
+ = elem = g_new (AffElement, 1);
+
+ *elem = *elements[el];
+ elem->draw_boundary = NULL;
+ elem->click_boundary = NULL;
+}
+
+static void
+undo_exchange (gint el)
+{
+ GtkAllocation allocation;
+ gint i;
+ AffElement **telements;
+ gboolean *tselected;
+ IfsComposeVals tifsvals;
+ gint tcurrent;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ /* swap the arrays and values*/
+ telements = elements;
+ elements = undo_ring[el].elements;
+ undo_ring[el].elements = telements;
+
+ tifsvals = ifsvals;
+ ifsvals = undo_ring[el].ifsvals;
+ undo_ring[el].ifsvals = tifsvals;
+
+ tselected = element_selected;
+ element_selected = undo_ring[el].element_selected;
+ undo_ring[el].element_selected = tselected;
+
+ tcurrent = ifsD->current_element;
+ ifsD->current_element = undo_ring[el].current_element;
+ undo_ring[el].current_element = tcurrent;
+
+ /* now swap back any unchanged elements */
+ for (i = 0; i < ifsvals.num_elements; i++)
+ if (!elements[i])
+ {
+ elements[i] = undo_ring[el].elements[i];
+ undo_ring[el].elements[i] = NULL;
+ }
+ else
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ set_current_element (ifsD->current_element);
+
+ design_area_redraw ();
+
+ ifs_compose_preview ();
+}
+
+static void
+undo (void)
+{
+ if (undo_cur >= 0)
+ {
+ undo_exchange ((undo_start + undo_cur) % UNDO_LEVELS);
+ undo_cur--;
+ }
+
+ design_op_actions_update ();
+}
+
+static void
+redo (void)
+{
+ if (undo_cur != undo_num - 1)
+ {
+ undo_cur++;
+ undo_exchange ((undo_start + undo_cur) % UNDO_LEVELS);
+ }
+
+ design_op_actions_update ();
+}
+
+static void
+design_area_select_all_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gint i;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ element_selected[i] = TRUE;
+
+ design_area_redraw ();
+}
+
+/* Interface functions */
+
+static void
+val_changed_update (void)
+{
+ GtkAllocation allocation;
+ AffElement *cur;
+
+ if (ifsD->in_update)
+ return;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ cur = elements[ifsD->current_element];
+
+ undo_begin ();
+ undo_update (ifsD->current_element);
+
+ cur->v = ifsD->current_vals;
+ cur->v.theta *= G_PI/180.0;
+ aff_element_compute_trans (cur,
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ aff_element_compute_color_trans (cur);
+
+ design_area_redraw ();
+
+ ifs_compose_preview ();
+}
+
+/* Pseudo-widget representing a color mapping */
+
+#define COLOR_SAMPLE_SIZE 30
+
+static ColorMap *
+color_map_create (const gchar *name,
+ GimpRGB *orig_color,
+ GimpRGB *data,
+ gboolean fixed_point)
+{
+ GtkWidget *frame;
+ GtkWidget *arrow;
+ ColorMap *color_map = g_new (ColorMap, 1);
+
+ gimp_rgb_set_alpha (data, 1.0);
+ color_map->color = data;
+ color_map->fixed_point = fixed_point;
+ color_map->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (color_map->hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ color_map->orig_preview =
+ gimp_color_area_new (fixed_point ? data : orig_color,
+ GIMP_COLOR_AREA_FLAT, 0);
+ gtk_drag_dest_unset (color_map->orig_preview);
+ gtk_widget_set_size_request (color_map->orig_preview,
+ COLOR_SAMPLE_SIZE, COLOR_SAMPLE_SIZE);
+ gtk_container_add (GTK_CONTAINER (frame), color_map->orig_preview);
+ gtk_widget_show (color_map->orig_preview);
+
+ arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (color_map->hbox), arrow, FALSE, FALSE, 0);
+ gtk_widget_show (arrow);
+
+ color_map->button = gimp_color_button_new (name,
+ COLOR_SAMPLE_SIZE,
+ COLOR_SAMPLE_SIZE,
+ data,
+ GIMP_COLOR_AREA_FLAT);
+ gtk_box_pack_start (GTK_BOX (color_map->hbox), color_map->button,
+ FALSE, FALSE, 0);
+ gtk_widget_show (color_map->button);
+
+ g_signal_connect (color_map->button, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ data);
+
+ g_signal_connect (color_map->button, "color-changed",
+ G_CALLBACK (color_map_color_changed_cb),
+ color_map);
+
+ return color_map;
+}
+
+static void
+color_map_color_changed_cb (GtkWidget *widget,
+ ColorMap *color_map)
+{
+ if (ifsD->in_update)
+ return;
+
+ undo_begin ();
+ undo_update (ifsD->current_element);
+
+ elements[ifsD->current_element]->v = ifsD->current_vals;
+ elements[ifsD->current_element]->v.theta *= G_PI/180.0;
+ aff_element_compute_color_trans (elements[ifsD->current_element]);
+
+ update_values ();
+
+ ifs_compose_preview ();
+}
+
+static void
+color_map_update (ColorMap *color_map)
+{
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (color_map->button),
+ color_map->color);
+
+ if (color_map->fixed_point)
+ gimp_color_area_set_color (GIMP_COLOR_AREA (color_map->orig_preview),
+ color_map->color);
+}
+
+static void
+simple_color_set_sensitive (void)
+{
+ gint sc = elements[ifsD->current_element]->v.simple_color;
+
+ gtk_widget_set_sensitive (ifsD->target_cmap->hbox, sc);
+ gtk_widget_set_sensitive (ifsD->hue_scale_pair->scale, sc);
+ gtk_widget_set_sensitive (ifsD->hue_scale_pair->spin, sc);
+ gtk_widget_set_sensitive (ifsD->value_scale_pair->scale, sc);
+ gtk_widget_set_sensitive (ifsD->value_scale_pair->spin, sc);
+
+ gtk_widget_set_sensitive (ifsD->red_cmap->hbox, !sc);
+ gtk_widget_set_sensitive (ifsD->green_cmap->hbox, !sc);
+ gtk_widget_set_sensitive (ifsD->blue_cmap->hbox, !sc);
+ gtk_widget_set_sensitive (ifsD->black_cmap->hbox, !sc);
+}
+
+static void
+simple_color_toggled (GtkWidget *widget,
+ gpointer data)
+{
+ AffElement *cur = elements[ifsD->current_element];
+
+ cur->v.simple_color = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ ifsD->current_vals.simple_color = cur->v.simple_color;
+
+ if (cur->v.simple_color)
+ aff_element_compute_color_trans (cur);
+
+ val_changed_update ();
+ simple_color_set_sensitive ();
+}
+
+/* Generic mechanism for scale/entry combination (possibly without
+ scale) */
+
+static ValuePair *
+value_pair_create (gpointer data,
+ gdouble lower,
+ gdouble upper,
+ gboolean create_scale,
+ ValuePairType type)
+{
+
+ ValuePair *value_pair = g_new (ValuePair, 1);
+
+ value_pair->data.d = data;
+ value_pair->type = type;
+ value_pair->timeout_id = 0;
+
+ value_pair->adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (1.0, lower, upper,
+ (upper - lower) / 100,
+ (upper - lower) / 10,
+ 0.0);
+ value_pair->spin = gimp_spin_button_new (value_pair->adjustment, 1.0, 3);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (value_pair->spin), TRUE);
+ gtk_widget_set_size_request (value_pair->spin, 72, -1);
+
+ g_signal_connect (value_pair->adjustment, "value-changed",
+ G_CALLBACK (value_pair_scale_callback),
+ value_pair);
+
+ if (create_scale)
+ {
+ value_pair->scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL,
+ value_pair->adjustment);
+
+ if (type == VALUE_PAIR_INT)
+ gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 0);
+ else
+ gtk_scale_set_digits (GTK_SCALE (value_pair->scale), 3);
+
+ gtk_scale_set_draw_value (GTK_SCALE (value_pair->scale), FALSE);
+ }
+ else
+ {
+ value_pair->scale = NULL;
+ }
+
+ return value_pair;
+}
+
+static void
+value_pair_update (ValuePair *value_pair)
+{
+ if (value_pair->type == VALUE_PAIR_INT)
+ gtk_adjustment_set_value (value_pair->adjustment, *value_pair->data.i);
+ else
+ gtk_adjustment_set_value (value_pair->adjustment, *value_pair->data.d);
+
+}
+
+static gboolean
+value_pair_scale_callback_real (gpointer data)
+{
+ ValuePair *value_pair = data;
+ gint changed = FALSE;
+
+ if (value_pair->type == VALUE_PAIR_DOUBLE)
+ {
+ if ((gdouble) *value_pair->data.d !=
+ gtk_adjustment_get_value (value_pair->adjustment))
+ {
+ changed = TRUE;
+ *value_pair->data.d = gtk_adjustment_get_value (value_pair->adjustment);
+ }
+ }
+ else
+ {
+ if (*value_pair->data.i !=
+ (gint) gtk_adjustment_get_value (value_pair->adjustment))
+ {
+ changed = TRUE;
+ *value_pair->data.i = gtk_adjustment_get_value (value_pair->adjustment);
+ }
+ }
+
+ if (changed)
+ val_changed_update ();
+
+ value_pair->timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+value_pair_scale_callback (GtkAdjustment *adjustment,
+ ValuePair *value_pair)
+{
+ if (value_pair->timeout_id != 0)
+ return;
+
+ value_pair->timeout_id = g_timeout_add (500, /* update every half second */
+ value_pair_scale_callback_real,
+ value_pair);
+}
+
+static void
+design_op_update_callback (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer data)
+{
+ ifsDesign->op = gtk_radio_action_get_current_value (action);
+
+ /* cursor switch */
+ if (gtk_widget_get_realized (ifsDesign->area))
+ design_area_realize (ifsDesign->area);
+}
+
+static void
+recompute_center_cb (GtkWidget *widget,
+ gpointer data)
+{
+ recompute_center (TRUE);
+}
+
+static void
+recompute_center (gboolean save_undo)
+{
+ GtkAllocation allocation;
+ gint i;
+ gdouble x, y;
+ gdouble center_x = 0.0;
+ gdouble center_y = 0.0;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ if (save_undo)
+ undo_begin ();
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ if (save_undo)
+ undo_update (i);
+
+ aff_element_compute_trans (elements[i],1, ifsvals.aspect_ratio,
+ ifsvals.center_x, ifsvals.center_y);
+ aff2_fixed_point (&elements[i]->trans, &x, &y);
+ center_x += x;
+ center_y += y;
+ }
+
+ ifsvals.center_x = center_x/ifsvals.num_elements;
+ ifsvals.center_y = center_y/ifsvals.num_elements;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ aff_element_decompose_trans (elements[i],&elements[i]->trans,
+ 1, ifsvals.aspect_ratio,
+ ifsvals.center_x, ifsvals.center_y);
+ }
+
+ if (allocation.width > 1 && allocation.height > 1)
+ {
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ design_area_redraw ();
+ update_values ();
+ }
+}
+
+static void
+flip_check_button_callback (GtkWidget *widget,
+ gpointer data)
+{
+ GtkAllocation allocation;
+ guint i;
+ gboolean active;
+
+ if (ifsD->in_update)
+ return;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ undo_begin ();
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ if (element_selected[i])
+ {
+ undo_update (i);
+ elements[i]->v.flip = active;
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ }
+ }
+
+ update_values ();
+ design_area_redraw ();
+
+ ifs_compose_preview ();
+}
+
+static void
+ifs_compose_set_defaults (void)
+{
+ gint i;
+ GimpRGB color;
+
+ gimp_context_get_foreground (&color);
+
+ ifsvals.aspect_ratio =
+ (gdouble)ifsD->drawable_height / ifsD->drawable_width;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_free (elements[i]);
+ count_for_naming = 0;
+
+ ifsvals.num_elements = 3;
+ elements = g_realloc (elements, ifsvals.num_elements * sizeof(AffElement *));
+ element_selected = g_realloc (element_selected,
+ ifsvals.num_elements * sizeof(gboolean));
+
+ elements[0] = aff_element_new (0.3, 0.37 * ifsvals.aspect_ratio, &color,
+ ++count_for_naming);
+ element_selected[0] = FALSE;
+ elements[1] = aff_element_new (0.7, 0.37 * ifsvals.aspect_ratio, &color,
+ ++count_for_naming);
+ element_selected[1] = FALSE;
+ elements[2] = aff_element_new (0.5, 0.7 * ifsvals.aspect_ratio, &color,
+ ++count_for_naming);
+ element_selected[2] = FALSE;
+
+ ifsvals.center_x = 0.5;
+ ifsvals.center_y = 0.5 * ifsvals.aspect_ratio;
+ ifsvals.iterations = ifsD->drawable_height * ifsD->drawable_width;
+ ifsvals.subdivide = 3;
+ ifsvals.max_memory = 4096;
+
+ if (ifsOptD)
+ {
+ value_pair_update (ifsOptD->iterations_pair);
+ value_pair_update (ifsOptD->subdivide_pair);
+ value_pair_update (ifsOptD->radius_pair);
+ value_pair_update (ifsOptD->memory_pair);
+ }
+
+ ifsvals.radius = 0.7;
+
+ set_current_element (0);
+ element_selected[0] = TRUE;
+ recompute_center (FALSE);
+
+ if (ifsD->selected_orig)
+ g_free (ifsD->selected_orig);
+
+ ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
+}
+
+/* show a transient message dialog */
+static void
+ifscompose_message_dialog (GtkMessageType type,
+ GtkWindow *parent,
+ const gchar *title,
+ const gchar *message)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (parent, 0, type, GTK_BUTTONS_OK,
+ "%s", message);
+
+ if (title)
+ gtk_window_set_title (GTK_WINDOW (dialog), title);
+
+ gtk_window_set_role (GTK_WINDOW (dialog), "ifscompose-message");
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+/* save an ifs file */
+static void
+ifsfile_save_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+ gchar *str;
+ FILE *fh;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ str = ifsvals_stringify (&ifsvals, elements);
+
+ fh = g_fopen (filename, "wb");
+ if (! fh)
+ {
+ gchar *message =
+ g_strdup_printf (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename),
+ g_strerror (errno));
+
+ ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
+ _("Save failed"), message);
+
+ g_free (message);
+ g_free (filename);
+
+ return;
+ }
+
+ fputs (str, fh);
+ fclose (fh);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/* replace ifsvals and elements with specified new values
+ * recompute and update everything necessary */
+static void
+ifsfile_replace_ifsvals (IfsComposeVals *new_ifsvals,
+ AffElement **new_elements)
+{
+ GtkAllocation allocation;
+ guint i;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_free (elements[i]);
+ g_free (elements);
+
+ ifsvals = *new_ifsvals;
+ elements = new_elements;
+ for (i = 0; i < ifsvals.num_elements; i++)
+ {
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+ aff_element_compute_color_trans (elements[i]);
+ }
+
+ element_selected = g_realloc (element_selected,
+ ifsvals.num_elements * sizeof(gboolean));
+ for (i = 0; i < ifsvals.num_elements; i++)
+ element_selected[i] = FALSE;
+
+ if (ifsOptD)
+ {
+ value_pair_update (ifsOptD->iterations_pair);
+ value_pair_update (ifsOptD->subdivide_pair);
+ value_pair_update (ifsOptD->radius_pair);
+ value_pair_update (ifsOptD->memory_pair);
+ }
+
+ set_current_element (0);
+ element_selected[0] = TRUE;
+ recompute_center (FALSE);
+
+ if (ifsD->selected_orig)
+ g_free (ifsD->selected_orig);
+
+ ifsD->selected_orig = g_new (AffElement, ifsvals.num_elements);
+}
+
+/* load an ifs file */
+static void
+ifsfile_load_response (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+ gchar *buffer;
+ AffElement **new_elements;
+ IfsComposeVals new_ifsvals;
+ GError *error = NULL;
+ guint i;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ if (! g_file_get_contents (filename, &buffer, NULL, &error))
+ {
+ ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
+ _("Open failed"), error->message);
+ g_error_free (error);
+ g_free (filename);
+ return;
+ }
+
+ if (! ifsvals_parse_string (buffer, &new_ifsvals, &new_elements))
+ {
+ gchar *message = g_strdup_printf (_("File '%s' doesn't seem to be "
+ "an IFS Fractal file."),
+ gimp_filename_to_utf8 (filename));
+
+ ifscompose_message_dialog (GTK_MESSAGE_ERROR, GTK_WINDOW (dialog),
+ _("Open failed"), message);
+ g_free (filename);
+ g_free (message);
+ g_free (buffer);
+
+ return;
+ }
+
+ g_free (buffer);
+ g_free (filename);
+
+ undo_begin ();
+ for (i = 0; i < ifsvals.num_elements; i++)
+ undo_update (i);
+
+ ifsfile_replace_ifsvals (&new_ifsvals, new_elements);
+
+ design_op_actions_update ();
+
+ ifs_compose_preview ();
+
+ design_area_redraw ();
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+ifs_compose_save (GtkWidget *parent)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (! dialog)
+ {
+ dialog =
+ gtk_file_chooser_dialog_new (_("Save as IFS Fractal file"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (ifsfile_save_response),
+ NULL);
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+ifs_compose_load (GtkWidget *parent)
+{
+ static GtkWidget *dialog = NULL;
+
+ if (! dialog)
+ {
+ dialog =
+ gtk_file_chooser_dialog_new (_("Open IFS Fractal file"),
+ GTK_WINDOW (parent),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (ifsfile_load_response),
+ NULL);
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+ifs_compose_new_callback (GtkAction *action,
+ gpointer data)
+{
+ GtkAllocation allocation;
+ GimpRGB color;
+ gint i;
+ AffElement *elem;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ undo_begin ();
+
+ gimp_context_get_foreground (&color);
+
+ elem = aff_element_new (0.5, 0.5 * allocation.height / allocation.width,
+ &color,
+ ++count_for_naming);
+
+ ifsvals.num_elements++;
+ elements = g_realloc (elements, ifsvals.num_elements * sizeof (AffElement *));
+ element_selected = g_realloc (element_selected,
+ ifsvals.num_elements * sizeof (gboolean));
+
+ for (i = 0; i < ifsvals.num_elements-1; i++)
+ element_selected[i] = FALSE;
+ element_selected[ifsvals.num_elements-1] = TRUE;
+
+ elements[ifsvals.num_elements-1] = elem;
+ set_current_element (ifsvals.num_elements-1);
+
+ ifsD->selected_orig = g_realloc (ifsD->selected_orig,
+ ifsvals.num_elements * sizeof(AffElement));
+ aff_element_compute_trans (elem,
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ design_area_redraw ();
+
+ ifs_compose_preview ();
+
+ design_op_actions_update ();
+}
+
+static void
+ifs_compose_delete_callback (GtkAction *action,
+ gpointer data)
+{
+ gint i;
+ gint new_current;
+
+ undo_begin ();
+ undo_update (ifsD->current_element);
+
+ aff_element_free (elements[ifsD->current_element]);
+
+ if (ifsD->current_element < ifsvals.num_elements-1)
+ {
+ undo_update (ifsvals.num_elements-1);
+ elements[ifsD->current_element] = elements[ifsvals.num_elements-1];
+ new_current = ifsD->current_element;
+ }
+ else
+ new_current = ifsvals.num_elements-2;
+
+ ifsvals.num_elements--;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ if (element_selected[i])
+ {
+ new_current = i;
+ break;
+ }
+
+ element_selected[new_current] = TRUE;
+ set_current_element (new_current);
+
+ design_area_redraw ();
+
+ ifs_compose_preview ();
+
+ design_op_actions_update ();
+}
+
+static void
+ifs_compose_options_callback (GtkAction *action,
+ gpointer data)
+{
+ ifs_options_dialog (GTK_WIDGET (data));
+}
+
+static gint
+preview_idle_render (gpointer data)
+{
+ GtkAllocation allocation;
+ gint iterations = PREVIEW_RENDER_CHUNK;
+ gint i;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ if (iterations > ifsD->preview_iterations)
+ iterations = ifsD->preview_iterations;
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ ifs_render (elements, ifsvals.num_elements,
+ allocation.width, allocation.height,
+ iterations,&ifsvals, 0, allocation.height,
+ ifsD->preview_data, NULL, NULL, TRUE);
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ ifsD->preview_iterations -= iterations;
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (ifsD->preview),
+ 0, 0, allocation.width, allocation.height,
+ GIMP_RGB_IMAGE,
+ ifsD->preview_data,
+ allocation.width * 3);
+
+ return (ifsD->preview_iterations != 0);
+}
+
+static void
+ifs_compose_preview (void)
+{
+ /* Expansion isn't really supported for previews */
+ gint i;
+ gint width = ifsD->preview_width;
+ gint height = ifsD->preview_height;
+ guchar rc, gc, bc;
+ guchar *ptr;
+ GimpRGB color;
+
+ if (!ifsD->preview_data)
+ ifsD->preview_data = g_new (guchar, 3 * width * height);
+
+ gimp_context_get_background (&color);
+ gimp_rgb_get_uchar (&color, &rc, &gc, &bc);
+
+ ptr = ifsD->preview_data;
+ for (i = 0; i < width * height; i++)
+ {
+ *ptr++ = rc;
+ *ptr++ = gc;
+ *ptr++ = bc;
+ }
+
+ if (ifsD->preview_iterations == 0)
+ g_idle_add (preview_idle_render, NULL);
+
+ ifsD->preview_iterations =
+ ifsvals.iterations * ((gdouble) width * height /
+ (ifsD->drawable_width * ifsD->drawable_height));
+}
+
+static void
+ifs_compose_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_OPEN:
+ ifs_compose_load (widget);
+ break;
+
+ case RESPONSE_SAVE:
+ ifs_compose_save (widget);
+ break;
+
+ case RESPONSE_RESET:
+ {
+ GtkAllocation allocation;
+ gint i;
+
+ gtk_widget_get_allocation (ifsDesign->area, &allocation);
+
+ undo_begin ();
+ for (i = 0; i < ifsvals.num_elements; i++)
+ undo_update (i);
+
+ ifs_compose_set_defaults ();
+
+ ifs_compose_preview ();
+
+ for (i = 0; i < ifsvals.num_elements; i++)
+ aff_element_compute_trans (elements[i],
+ allocation.width, allocation.height,
+ ifsvals.center_x, ifsvals.center_y);
+
+ design_area_redraw ();
+ design_op_actions_update ();
+ }
+ break;
+
+ case GTK_RESPONSE_OK:
+ ifscint.run = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
diff --git a/plug-ins/ifs-compose/ifs-compose.h b/plug-ins/ifs-compose/ifs-compose.h
new file mode 100644
index 0000000..28b9a3b
--- /dev/null
+++ b/plug-ins/ifs-compose/ifs-compose.h
@@ -0,0 +1,180 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * IfsCompose is a interface for creating IFS fractals by
+ * direct manipulation.
+ * Copyright (C) 1997 Owen Taylor
+ *
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+typedef struct {
+ gdouble a11,a12,a21,a22,b1,b2;
+} Aff2;
+
+typedef struct {
+ gdouble vals[3][4];
+} Aff3;
+
+typedef struct {
+ GdkPoint *points;
+ gint npoints;
+} IPolygon;
+
+typedef struct {
+ gdouble x, y;
+ gdouble theta;
+ gdouble scale;
+ gdouble asym;
+ gdouble shear;
+ gint flip;
+
+ GimpRGB red_color;
+ GimpRGB green_color;
+ GimpRGB blue_color;
+ GimpRGB black_color;
+
+ GimpRGB target_color;
+ gdouble hue_scale;
+ gdouble value_scale;
+
+ gint simple_color;
+ gdouble prob;
+} AffElementVals;
+
+typedef struct
+{
+ gint num_elements;
+ gint iterations;
+ gint max_memory;
+ gint subdivide;
+ gdouble radius;
+ gdouble aspect_ratio;
+ gdouble center_x;
+ gdouble center_y;
+} IfsComposeVals;
+
+typedef struct {
+ AffElementVals v;
+
+ Aff2 trans;
+ Aff3 color_trans;
+
+ gchar *name;
+
+ IPolygon *click_boundary;
+ IPolygon *draw_boundary;
+} AffElement;
+
+
+/* manipulation of affine transforms */
+void aff2_translate (Aff2 *naff,
+ gdouble x,
+ gdouble y);
+void aff2_rotate (Aff2 *naff,
+ gdouble theta);
+void aff2_scale (Aff2 *naff,
+ gdouble s,
+ gint flip);
+void aff2_distort (Aff2 *naff,
+ gdouble asym,
+ gdouble shear);
+void aff2_compute_stretch (Aff2 *naff,
+ gdouble xo,
+ gdouble yo,
+ gdouble xn,
+ gdouble yn);
+void aff2_compute_distort (Aff2 *naff,
+ gdouble xo,
+ gdouble yo,
+ gdouble xn,
+ gdouble yn);
+void aff2_compose (Aff2 *naff,
+ Aff2 *aff1,
+ Aff2 *aff2);
+void aff2_invert (Aff2 *naff,
+ Aff2 *aff);
+void aff2_apply (Aff2 *aff,
+ gdouble x,
+ gdouble y,
+ gdouble *xf,
+ gdouble *yf);
+void aff2_fixed_point (Aff2 *aff,
+ gdouble *xf,
+ gdouble *yf);
+void aff3_apply (Aff3 *t,
+ gdouble x,
+ gdouble y,
+ gdouble z,
+ gdouble *xf,
+ gdouble *yf,
+ gdouble *zf);
+
+
+/* manipulation of polygons */
+IPolygon *ipolygon_convex_hull (IPolygon *poly);
+gint ipolygon_contains (IPolygon *poly,
+ gint xt,
+ gint yt);
+
+
+/* manipulation of composite transforms */
+AffElement *aff_element_new (gdouble x,
+ gdouble y,
+ GimpRGB *color,
+ gint count);
+void aff_element_free (AffElement *elem);
+void aff_element_compute_trans (AffElement *elem,
+ gdouble width,
+ gdouble height,
+ gdouble center_x,
+ gdouble center_y);
+void aff_element_compute_color_trans (AffElement *elem);
+void aff_element_decompose_trans (AffElement *elem,
+ Aff2 *aff,
+ gdouble width,
+ gdouble height,
+ gdouble center_x,
+ gdouble center_y);
+void aff_element_compute_boundary (AffElement *elem,
+ gint width,
+ gint height,
+ AffElement **elements,
+ gint num_elements);
+void aff_element_draw (AffElement *elem,
+ gint selected,
+ gint width,
+ gint height,
+ cairo_t *cr,
+ GdkColor *color,
+ PangoLayout *layout);
+
+
+void ifs_render (AffElement **elements,
+ gint num_elements,
+ gint width,
+ gint height,
+ gint nsteps,
+ IfsComposeVals *vals,
+ gint band_y,
+ gint band_height,
+ guchar *data,
+ guchar *mask,
+ guchar *nhits,
+ gboolean preview);
+
+gchar * ifsvals_stringify (IfsComposeVals *vals,
+ AffElement **elements);
+gboolean ifsvals_parse_string (const gchar *str,
+ IfsComposeVals *vals,
+ AffElement ***elements);
diff --git a/plug-ins/imagemap/Makefile.am b/plug-ins/imagemap/Makefile.am
new file mode 100644
index 0000000..f24ddb1
--- /dev/null
+++ b/plug-ins/imagemap/Makefile.am
@@ -0,0 +1,216 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+imagemap_RC = imagemap.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+SUBDIRS = images
+
+libexecdir = $(gimpplugindir)/plug-ins/imagemap
+
+libexec_PROGRAMS = imagemap
+
+EXTRA_DIST = \
+ imap_cern.l \
+ imap_csim.l \
+ imap_ncsa.l \
+ imap_cern.y \
+ imap_csim.y \
+ imap_ncsa.y
+
+imagemap_SOURCES = \
+ imap_about.c \
+ imap_about.h \
+ imap_browse.c \
+ imap_browse.h \
+ imap_cern_lex.c \
+ imap_cern_parse.c \
+ imap_cern_parse.h \
+ imap_circle.c \
+ imap_circle.h \
+ imap_cmd_clear.c \
+ imap_cmd_copy.c \
+ imap_cmd_copy_object.c \
+ imap_cmd_create.c \
+ imap_cmd_cut.c \
+ imap_cmd_cut_object.c \
+ imap_cmd_delete.c \
+ imap_cmd_delete_point.c \
+ imap_cmd_edit_object.c \
+ imap_cmd_gimp_guides.c \
+ imap_cmd_guides.c \
+ imap_cmd_insert_point.c \
+ imap_cmd_move.c \
+ imap_cmd_move_down.c \
+ imap_cmd_move_sash.c \
+ imap_cmd_move_selected.c \
+ imap_cmd_move_to_front.c \
+ imap_cmd_move_up.c \
+ imap_cmd_object_down.c \
+ imap_cmd_object_move.c \
+ imap_cmd_object_up.c \
+ imap_cmd_paste.c \
+ imap_cmd_select.c \
+ imap_cmd_select_all.c \
+ imap_cmd_select_next.c \
+ imap_cmd_select_prev.c \
+ imap_cmd_select_region.c \
+ imap_cmd_send_to_back.c \
+ imap_cmd_unselect.c \
+ imap_cmd_unselect_all.c \
+ imap_command.c \
+ imap_command.h \
+ imap_commands.h \
+ imap_csim_lex.c \
+ imap_csim_parse.c \
+ imap_csim_parse.h \
+ imap_default_dialog.c \
+ imap_default_dialog.h \
+ imap_edit_area_info.c \
+ imap_edit_area_info.h \
+ imap_file.c \
+ imap_file.h \
+ imap_grid.c \
+ imap_grid.h \
+ imap_main.c \
+ imap_main.h \
+ imap_menu.c \
+ imap_menu.h \
+ imap_menu_funcs.c \
+ imap_menu_funcs.h \
+ imap_misc.c \
+ imap_misc.h \
+ imap_mru.c \
+ imap_mru.h \
+ imap_ncsa_lex.c \
+ imap_ncsa_parse.c \
+ imap_ncsa_parse.h \
+ imap_object.c \
+ imap_object.h \
+ imap_object_popup.c \
+ imap_object_popup.h \
+ imap_polygon.c \
+ imap_polygon.h \
+ imap_preferences.c \
+ imap_preferences.h \
+ imap_preview.c \
+ imap_preview.h \
+ imap_rectangle.c \
+ imap_rectangle.h \
+ imap_selection.c \
+ imap_selection.h \
+ imap_settings.c \
+ imap_settings.h \
+ imap_source.c \
+ imap_source.h \
+ imap_stock.c \
+ imap_stock.h \
+ imap_statusbar.c \
+ imap_statusbar.h \
+ imap_string.c \
+ imap_string.h \
+ imap_table.c \
+ imap_table.h \
+ imap_taglist.c \
+ imap_taglist.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(imagemap)
+
+CLEANFILES = y.tab.c y.tab.h
+
+## The following rules are not necessary for most users. They are
+## only used by the maintainers who modify the symbols and grammar
+## that are used for parsing the map files. These rules are very
+## specific and a test for flex and bison in configure.ac would not be
+## appropriate in most cases, so the Makefile rules are included here.
+## In addition, the default rules provided by automake would not be
+## sufficient because the source and target files have different base
+## names and because of the non-standard prefix used in the output
+## code (cern_, csim_, ncsa_).
+
+## Require flex because the standard lex does not support the -P option.
+LEX=flex
+YACC=bison -y
+
+REBUILD_FILES = \
+ imap_cern_lex.c.rebuild \
+ imap_csim_lex.c.rebuild \
+ imap_ncsa_lex.c.rebuild \
+ imap_cern_parse.c.rebuild \
+ imap_csim_parse.c.rebuild \
+ imap_ncsa_parse.c.rebuild \
+ imap_cern_parse.h.rebuild \
+ imap_csim_parse.h.rebuild \
+ imap_ncsa_parse.h.rebuild
+
+rebuild-parsers: $(REBUILD_FILES)
+ @list='$(REBUILD_FILES)'; for p in $$list; do \
+ newfile="`echo $$p | sed -e 's|.rebuild||'`"; \
+ cp $$p $(srcdir)/$$newfile; \
+ done
+
+imap_cern_lex.c.rebuild: imap_cern.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pcern_ -i -t $< > $@
+imap_csim_lex.c.rebuild: imap_csim.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pcsim_ -i -t $< > $@
+imap_ncsa_lex.c.rebuild: imap_ncsa.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pncsa_ -i -t $< > $@
+
+imap_cern_parse.c.rebuild: imap_cern.y
+ $(YACC) $(YFLAGS) -d -p cern_ $<
+ mv -f y.tab.c $@
+imap_csim_parse.c.rebuild: imap_csim.y
+ $(YACC) $(YFLAGS) -d -p csim_ $<
+ mv -f y.tab.c $@
+imap_ncsa_parse.c.rebuild: imap_ncsa.y
+ $(YACC) $(YFLAGS) -d -p ncsa_ $<
+ mv -f y.tab.c $@
+
+imap_cern_parse.h.rebuild: imap_cern.y
+ $(YACC) $(YFLAGS) -d -p cern_ $<
+ mv -f y.tab.h $@
+imap_csim_parse.h.rebuild: imap_csim.y
+ $(YACC) $(YFLAGS) -d -p csim_ $<
+ mv -f y.tab.h $@
+imap_ncsa_parse.h.rebuild: imap_ncsa.y
+ $(YACC) $(YFLAGS) -d -p ncsa_ $<
+ mv -f y.tab.h $@
diff --git a/plug-ins/imagemap/Makefile.in b/plug-ins/imagemap/Makefile.in
new file mode 100644
index 0000000..efb8942
--- /dev/null
+++ b/plug-ins/imagemap/Makefile.in
@@ -0,0 +1,1531 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = imagemap$(EXEEXT)
+subdir = plug-ins/imagemap
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_imagemap_OBJECTS = imap_about.$(OBJEXT) imap_browse.$(OBJEXT) \
+ imap_cern_lex.$(OBJEXT) imap_cern_parse.$(OBJEXT) \
+ imap_circle.$(OBJEXT) imap_cmd_clear.$(OBJEXT) \
+ imap_cmd_copy.$(OBJEXT) imap_cmd_copy_object.$(OBJEXT) \
+ imap_cmd_create.$(OBJEXT) imap_cmd_cut.$(OBJEXT) \
+ imap_cmd_cut_object.$(OBJEXT) imap_cmd_delete.$(OBJEXT) \
+ imap_cmd_delete_point.$(OBJEXT) imap_cmd_edit_object.$(OBJEXT) \
+ imap_cmd_gimp_guides.$(OBJEXT) imap_cmd_guides.$(OBJEXT) \
+ imap_cmd_insert_point.$(OBJEXT) imap_cmd_move.$(OBJEXT) \
+ imap_cmd_move_down.$(OBJEXT) imap_cmd_move_sash.$(OBJEXT) \
+ imap_cmd_move_selected.$(OBJEXT) \
+ imap_cmd_move_to_front.$(OBJEXT) imap_cmd_move_up.$(OBJEXT) \
+ imap_cmd_object_down.$(OBJEXT) imap_cmd_object_move.$(OBJEXT) \
+ imap_cmd_object_up.$(OBJEXT) imap_cmd_paste.$(OBJEXT) \
+ imap_cmd_select.$(OBJEXT) imap_cmd_select_all.$(OBJEXT) \
+ imap_cmd_select_next.$(OBJEXT) imap_cmd_select_prev.$(OBJEXT) \
+ imap_cmd_select_region.$(OBJEXT) \
+ imap_cmd_send_to_back.$(OBJEXT) imap_cmd_unselect.$(OBJEXT) \
+ imap_cmd_unselect_all.$(OBJEXT) imap_command.$(OBJEXT) \
+ imap_csim_lex.$(OBJEXT) imap_csim_parse.$(OBJEXT) \
+ imap_default_dialog.$(OBJEXT) imap_edit_area_info.$(OBJEXT) \
+ imap_file.$(OBJEXT) imap_grid.$(OBJEXT) imap_main.$(OBJEXT) \
+ imap_menu.$(OBJEXT) imap_menu_funcs.$(OBJEXT) \
+ imap_misc.$(OBJEXT) imap_mru.$(OBJEXT) imap_ncsa_lex.$(OBJEXT) \
+ imap_ncsa_parse.$(OBJEXT) imap_object.$(OBJEXT) \
+ imap_object_popup.$(OBJEXT) imap_polygon.$(OBJEXT) \
+ imap_preferences.$(OBJEXT) imap_preview.$(OBJEXT) \
+ imap_rectangle.$(OBJEXT) imap_selection.$(OBJEXT) \
+ imap_settings.$(OBJEXT) imap_source.$(OBJEXT) \
+ imap_stock.$(OBJEXT) imap_statusbar.$(OBJEXT) \
+ imap_string.$(OBJEXT) imap_table.$(OBJEXT) \
+ imap_taglist.$(OBJEXT)
+imagemap_OBJECTS = $(am_imagemap_OBJECTS)
+imagemap_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+imagemap_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/imap_about.Po \
+ ./$(DEPDIR)/imap_browse.Po ./$(DEPDIR)/imap_cern_lex.Po \
+ ./$(DEPDIR)/imap_cern_parse.Po ./$(DEPDIR)/imap_circle.Po \
+ ./$(DEPDIR)/imap_cmd_clear.Po ./$(DEPDIR)/imap_cmd_copy.Po \
+ ./$(DEPDIR)/imap_cmd_copy_object.Po \
+ ./$(DEPDIR)/imap_cmd_create.Po ./$(DEPDIR)/imap_cmd_cut.Po \
+ ./$(DEPDIR)/imap_cmd_cut_object.Po \
+ ./$(DEPDIR)/imap_cmd_delete.Po \
+ ./$(DEPDIR)/imap_cmd_delete_point.Po \
+ ./$(DEPDIR)/imap_cmd_edit_object.Po \
+ ./$(DEPDIR)/imap_cmd_gimp_guides.Po \
+ ./$(DEPDIR)/imap_cmd_guides.Po \
+ ./$(DEPDIR)/imap_cmd_insert_point.Po \
+ ./$(DEPDIR)/imap_cmd_move.Po ./$(DEPDIR)/imap_cmd_move_down.Po \
+ ./$(DEPDIR)/imap_cmd_move_sash.Po \
+ ./$(DEPDIR)/imap_cmd_move_selected.Po \
+ ./$(DEPDIR)/imap_cmd_move_to_front.Po \
+ ./$(DEPDIR)/imap_cmd_move_up.Po \
+ ./$(DEPDIR)/imap_cmd_object_down.Po \
+ ./$(DEPDIR)/imap_cmd_object_move.Po \
+ ./$(DEPDIR)/imap_cmd_object_up.Po \
+ ./$(DEPDIR)/imap_cmd_paste.Po ./$(DEPDIR)/imap_cmd_select.Po \
+ ./$(DEPDIR)/imap_cmd_select_all.Po \
+ ./$(DEPDIR)/imap_cmd_select_next.Po \
+ ./$(DEPDIR)/imap_cmd_select_prev.Po \
+ ./$(DEPDIR)/imap_cmd_select_region.Po \
+ ./$(DEPDIR)/imap_cmd_send_to_back.Po \
+ ./$(DEPDIR)/imap_cmd_unselect.Po \
+ ./$(DEPDIR)/imap_cmd_unselect_all.Po \
+ ./$(DEPDIR)/imap_command.Po ./$(DEPDIR)/imap_csim_lex.Po \
+ ./$(DEPDIR)/imap_csim_parse.Po \
+ ./$(DEPDIR)/imap_default_dialog.Po \
+ ./$(DEPDIR)/imap_edit_area_info.Po ./$(DEPDIR)/imap_file.Po \
+ ./$(DEPDIR)/imap_grid.Po ./$(DEPDIR)/imap_main.Po \
+ ./$(DEPDIR)/imap_menu.Po ./$(DEPDIR)/imap_menu_funcs.Po \
+ ./$(DEPDIR)/imap_misc.Po ./$(DEPDIR)/imap_mru.Po \
+ ./$(DEPDIR)/imap_ncsa_lex.Po ./$(DEPDIR)/imap_ncsa_parse.Po \
+ ./$(DEPDIR)/imap_object.Po ./$(DEPDIR)/imap_object_popup.Po \
+ ./$(DEPDIR)/imap_polygon.Po ./$(DEPDIR)/imap_preferences.Po \
+ ./$(DEPDIR)/imap_preview.Po ./$(DEPDIR)/imap_rectangle.Po \
+ ./$(DEPDIR)/imap_selection.Po ./$(DEPDIR)/imap_settings.Po \
+ ./$(DEPDIR)/imap_source.Po ./$(DEPDIR)/imap_statusbar.Po \
+ ./$(DEPDIR)/imap_stock.Po ./$(DEPDIR)/imap_string.Po \
+ ./$(DEPDIR)/imap_table.Po ./$(DEPDIR)/imap_taglist.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(imagemap_SOURCES)
+DIST_SOURCES = $(imagemap_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/imagemap
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@imagemap_RC = imagemap.rc.o
+AM_LDFLAGS = $(mwindows)
+SUBDIRS = images
+EXTRA_DIST = \
+ imap_cern.l \
+ imap_csim.l \
+ imap_ncsa.l \
+ imap_cern.y \
+ imap_csim.y \
+ imap_ncsa.y
+
+imagemap_SOURCES = \
+ imap_about.c \
+ imap_about.h \
+ imap_browse.c \
+ imap_browse.h \
+ imap_cern_lex.c \
+ imap_cern_parse.c \
+ imap_cern_parse.h \
+ imap_circle.c \
+ imap_circle.h \
+ imap_cmd_clear.c \
+ imap_cmd_copy.c \
+ imap_cmd_copy_object.c \
+ imap_cmd_create.c \
+ imap_cmd_cut.c \
+ imap_cmd_cut_object.c \
+ imap_cmd_delete.c \
+ imap_cmd_delete_point.c \
+ imap_cmd_edit_object.c \
+ imap_cmd_gimp_guides.c \
+ imap_cmd_guides.c \
+ imap_cmd_insert_point.c \
+ imap_cmd_move.c \
+ imap_cmd_move_down.c \
+ imap_cmd_move_sash.c \
+ imap_cmd_move_selected.c \
+ imap_cmd_move_to_front.c \
+ imap_cmd_move_up.c \
+ imap_cmd_object_down.c \
+ imap_cmd_object_move.c \
+ imap_cmd_object_up.c \
+ imap_cmd_paste.c \
+ imap_cmd_select.c \
+ imap_cmd_select_all.c \
+ imap_cmd_select_next.c \
+ imap_cmd_select_prev.c \
+ imap_cmd_select_region.c \
+ imap_cmd_send_to_back.c \
+ imap_cmd_unselect.c \
+ imap_cmd_unselect_all.c \
+ imap_command.c \
+ imap_command.h \
+ imap_commands.h \
+ imap_csim_lex.c \
+ imap_csim_parse.c \
+ imap_csim_parse.h \
+ imap_default_dialog.c \
+ imap_default_dialog.h \
+ imap_edit_area_info.c \
+ imap_edit_area_info.h \
+ imap_file.c \
+ imap_file.h \
+ imap_grid.c \
+ imap_grid.h \
+ imap_main.c \
+ imap_main.h \
+ imap_menu.c \
+ imap_menu.h \
+ imap_menu_funcs.c \
+ imap_menu_funcs.h \
+ imap_misc.c \
+ imap_misc.h \
+ imap_mru.c \
+ imap_mru.h \
+ imap_ncsa_lex.c \
+ imap_ncsa_parse.c \
+ imap_ncsa_parse.h \
+ imap_object.c \
+ imap_object.h \
+ imap_object_popup.c \
+ imap_object_popup.h \
+ imap_polygon.c \
+ imap_polygon.h \
+ imap_preferences.c \
+ imap_preferences.h \
+ imap_preview.c \
+ imap_preview.h \
+ imap_rectangle.c \
+ imap_rectangle.h \
+ imap_selection.c \
+ imap_selection.h \
+ imap_settings.c \
+ imap_settings.h \
+ imap_source.c \
+ imap_source.h \
+ imap_stock.c \
+ imap_stock.h \
+ imap_statusbar.c \
+ imap_statusbar.h \
+ imap_string.c \
+ imap_string.h \
+ imap_table.c \
+ imap_table.h \
+ imap_taglist.c \
+ imap_taglist.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(imagemap)
+
+CLEANFILES = y.tab.c y.tab.h
+LEX = flex
+YACC = bison -y
+REBUILD_FILES = \
+ imap_cern_lex.c.rebuild \
+ imap_csim_lex.c.rebuild \
+ imap_ncsa_lex.c.rebuild \
+ imap_cern_parse.c.rebuild \
+ imap_csim_parse.c.rebuild \
+ imap_ncsa_parse.c.rebuild \
+ imap_cern_parse.h.rebuild \
+ imap_csim_parse.h.rebuild \
+ imap_ncsa_parse.h.rebuild
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/imagemap/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/imagemap/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+imagemap$(EXEEXT): $(imagemap_OBJECTS) $(imagemap_DEPENDENCIES) $(EXTRA_imagemap_DEPENDENCIES)
+ @rm -f imagemap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(imagemap_OBJECTS) $(imagemap_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_about.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_browse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cern_lex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cern_parse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_circle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_clear.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_copy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_copy_object.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_create.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_cut.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_cut_object.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_delete.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_delete_point.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_edit_object.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_gimp_guides.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_guides.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_insert_point.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move_down.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move_sash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move_selected.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move_to_front.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_move_up.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_object_down.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_object_move.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_object_up.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_paste.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_select.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_select_all.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_select_next.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_select_prev.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_select_region.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_send_to_back.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_unselect.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_cmd_unselect_all.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_command.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_csim_lex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_csim_parse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_default_dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_edit_area_info.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_file.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_grid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_menu.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_menu_funcs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_misc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_mru.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_ncsa_lex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_ncsa_parse.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_object.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_object_popup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_polygon.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_preferences.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_rectangle.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_selection.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_source.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_statusbar.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_stock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_string.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_table.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap_taglist.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/imap_about.Po
+ -rm -f ./$(DEPDIR)/imap_browse.Po
+ -rm -f ./$(DEPDIR)/imap_cern_lex.Po
+ -rm -f ./$(DEPDIR)/imap_cern_parse.Po
+ -rm -f ./$(DEPDIR)/imap_circle.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_clear.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_copy.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_copy_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_create.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_cut.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_cut_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_delete.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_delete_point.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_edit_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_gimp_guides.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_guides.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_insert_point.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_down.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_sash.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_selected.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_to_front.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_up.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_down.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_move.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_up.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_paste.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_all.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_next.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_prev.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_region.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_send_to_back.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_unselect.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_unselect_all.Po
+ -rm -f ./$(DEPDIR)/imap_command.Po
+ -rm -f ./$(DEPDIR)/imap_csim_lex.Po
+ -rm -f ./$(DEPDIR)/imap_csim_parse.Po
+ -rm -f ./$(DEPDIR)/imap_default_dialog.Po
+ -rm -f ./$(DEPDIR)/imap_edit_area_info.Po
+ -rm -f ./$(DEPDIR)/imap_file.Po
+ -rm -f ./$(DEPDIR)/imap_grid.Po
+ -rm -f ./$(DEPDIR)/imap_main.Po
+ -rm -f ./$(DEPDIR)/imap_menu.Po
+ -rm -f ./$(DEPDIR)/imap_menu_funcs.Po
+ -rm -f ./$(DEPDIR)/imap_misc.Po
+ -rm -f ./$(DEPDIR)/imap_mru.Po
+ -rm -f ./$(DEPDIR)/imap_ncsa_lex.Po
+ -rm -f ./$(DEPDIR)/imap_ncsa_parse.Po
+ -rm -f ./$(DEPDIR)/imap_object.Po
+ -rm -f ./$(DEPDIR)/imap_object_popup.Po
+ -rm -f ./$(DEPDIR)/imap_polygon.Po
+ -rm -f ./$(DEPDIR)/imap_preferences.Po
+ -rm -f ./$(DEPDIR)/imap_preview.Po
+ -rm -f ./$(DEPDIR)/imap_rectangle.Po
+ -rm -f ./$(DEPDIR)/imap_selection.Po
+ -rm -f ./$(DEPDIR)/imap_settings.Po
+ -rm -f ./$(DEPDIR)/imap_source.Po
+ -rm -f ./$(DEPDIR)/imap_statusbar.Po
+ -rm -f ./$(DEPDIR)/imap_stock.Po
+ -rm -f ./$(DEPDIR)/imap_string.Po
+ -rm -f ./$(DEPDIR)/imap_table.Po
+ -rm -f ./$(DEPDIR)/imap_taglist.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/imap_about.Po
+ -rm -f ./$(DEPDIR)/imap_browse.Po
+ -rm -f ./$(DEPDIR)/imap_cern_lex.Po
+ -rm -f ./$(DEPDIR)/imap_cern_parse.Po
+ -rm -f ./$(DEPDIR)/imap_circle.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_clear.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_copy.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_copy_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_create.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_cut.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_cut_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_delete.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_delete_point.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_edit_object.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_gimp_guides.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_guides.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_insert_point.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_down.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_sash.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_selected.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_to_front.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_move_up.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_down.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_move.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_object_up.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_paste.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_all.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_next.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_prev.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_select_region.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_send_to_back.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_unselect.Po
+ -rm -f ./$(DEPDIR)/imap_cmd_unselect_all.Po
+ -rm -f ./$(DEPDIR)/imap_command.Po
+ -rm -f ./$(DEPDIR)/imap_csim_lex.Po
+ -rm -f ./$(DEPDIR)/imap_csim_parse.Po
+ -rm -f ./$(DEPDIR)/imap_default_dialog.Po
+ -rm -f ./$(DEPDIR)/imap_edit_area_info.Po
+ -rm -f ./$(DEPDIR)/imap_file.Po
+ -rm -f ./$(DEPDIR)/imap_grid.Po
+ -rm -f ./$(DEPDIR)/imap_main.Po
+ -rm -f ./$(DEPDIR)/imap_menu.Po
+ -rm -f ./$(DEPDIR)/imap_menu_funcs.Po
+ -rm -f ./$(DEPDIR)/imap_misc.Po
+ -rm -f ./$(DEPDIR)/imap_mru.Po
+ -rm -f ./$(DEPDIR)/imap_ncsa_lex.Po
+ -rm -f ./$(DEPDIR)/imap_ncsa_parse.Po
+ -rm -f ./$(DEPDIR)/imap_object.Po
+ -rm -f ./$(DEPDIR)/imap_object_popup.Po
+ -rm -f ./$(DEPDIR)/imap_polygon.Po
+ -rm -f ./$(DEPDIR)/imap_preferences.Po
+ -rm -f ./$(DEPDIR)/imap_preview.Po
+ -rm -f ./$(DEPDIR)/imap_rectangle.Po
+ -rm -f ./$(DEPDIR)/imap_selection.Po
+ -rm -f ./$(DEPDIR)/imap_settings.Po
+ -rm -f ./$(DEPDIR)/imap_source.Po
+ -rm -f ./$(DEPDIR)/imap_statusbar.Po
+ -rm -f ./$(DEPDIR)/imap_stock.Po
+ -rm -f ./$(DEPDIR)/imap_string.Po
+ -rm -f ./$(DEPDIR)/imap_table.Po
+ -rm -f ./$(DEPDIR)/imap_taglist.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+rebuild-parsers: $(REBUILD_FILES)
+ @list='$(REBUILD_FILES)'; for p in $$list; do \
+ newfile="`echo $$p | sed -e 's|.rebuild||'`"; \
+ cp $$p $(srcdir)/$$newfile; \
+ done
+
+imap_cern_lex.c.rebuild: imap_cern.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pcern_ -i -t $< > $@
+imap_csim_lex.c.rebuild: imap_csim.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pcsim_ -i -t $< > $@
+imap_ncsa_lex.c.rebuild: imap_ncsa.l
+ @$(RM) $@
+ $(LEX) $(LFLAGS) -Pncsa_ -i -t $< > $@
+
+imap_cern_parse.c.rebuild: imap_cern.y
+ $(YACC) $(YFLAGS) -d -p cern_ $<
+ mv -f y.tab.c $@
+imap_csim_parse.c.rebuild: imap_csim.y
+ $(YACC) $(YFLAGS) -d -p csim_ $<
+ mv -f y.tab.c $@
+imap_ncsa_parse.c.rebuild: imap_ncsa.y
+ $(YACC) $(YFLAGS) -d -p ncsa_ $<
+ mv -f y.tab.c $@
+
+imap_cern_parse.h.rebuild: imap_cern.y
+ $(YACC) $(YFLAGS) -d -p cern_ $<
+ mv -f y.tab.h $@
+imap_csim_parse.h.rebuild: imap_csim.y
+ $(YACC) $(YFLAGS) -d -p csim_ $<
+ mv -f y.tab.h $@
+imap_ncsa_parse.h.rebuild: imap_ncsa.y
+ $(YACC) $(YFLAGS) -d -p ncsa_ $<
+ mv -f y.tab.h $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/imagemap/images/Makefile.am b/plug-ins/imagemap/images/Makefile.am
new file mode 100644
index 0000000..9cf0b28
--- /dev/null
+++ b/plug-ins/imagemap/images/Makefile.am
@@ -0,0 +1,27 @@
+## Process this file with automake to produce Makefile.in
+
+STOCK_IMAGES = \
+ stock-circle.png \
+ stock-coord.png \
+ stock-dimension.png \
+ stock-java.png \
+ stock-polygon.png \
+ stock-rectangle.png \
+ stock-to-back.png \
+ stock-to-front.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+
+noinst_DATA = imap-stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/imap-stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
diff --git a/plug-ins/imagemap/images/Makefile.in b/plug-ins/imagemap/images/Makefile.in
new file mode 100644
index 0000000..48c8353
--- /dev/null
+++ b/plug-ins/imagemap/images/Makefile.in
@@ -0,0 +1,773 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/imagemap/images
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+STOCK_IMAGES = \
+ stock-circle.png \
+ stock-coord.png \
+ stock-dimension.png \
+ stock-java.png \
+ stock-polygon.png \
+ stock-rectangle.png \
+ stock-to-back.png \
+ stock-to-front.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+noinst_DATA = imap-stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/imagemap/images/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/imagemap/images/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/imap-stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/imagemap/images/stock-circle.png b/plug-ins/imagemap/images/stock-circle.png
new file mode 100644
index 0000000..e81b2ea
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-circle.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-coord.png b/plug-ins/imagemap/images/stock-coord.png
new file mode 100644
index 0000000..6276054
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-coord.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-dimension.png b/plug-ins/imagemap/images/stock-dimension.png
new file mode 100644
index 0000000..d556717
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-dimension.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-java.png b/plug-ins/imagemap/images/stock-java.png
new file mode 100644
index 0000000..5c1a8d9
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-java.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-polygon.png b/plug-ins/imagemap/images/stock-polygon.png
new file mode 100644
index 0000000..4ec7754
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-polygon.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-rectangle.png b/plug-ins/imagemap/images/stock-rectangle.png
new file mode 100644
index 0000000..901c3e2
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-rectangle.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-to-back.png b/plug-ins/imagemap/images/stock-to-back.png
new file mode 100644
index 0000000..6438104
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-to-back.png
Binary files differ
diff --git a/plug-ins/imagemap/images/stock-to-front.png b/plug-ins/imagemap/images/stock-to-front.png
new file mode 100644
index 0000000..ef0880d
--- /dev/null
+++ b/plug-ins/imagemap/images/stock-to-front.png
Binary files differ
diff --git a/plug-ins/imagemap/imap_about.c b/plug-ins/imagemap/imap_about.c
new file mode 100644
index 0000000..81b4414
--- /dev/null
+++ b/plug-ins/imagemap/imap_about.c
@@ -0,0 +1,62 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_about.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+void
+do_about_dialog(void)
+{
+ static GtkWidget *dialog;
+ if (!dialog)
+ {
+ const gchar* authors[] = {"Maurits Rijk (m.rijk@chello.nl)", NULL};
+
+ dialog = g_object_new (GTK_TYPE_ABOUT_DIALOG,
+ "transient-for", get_dialog(),
+ "program-name", _("Image Map Plug-in"),
+ "version", "2.3",
+ "authors", authors,
+ "copyright",
+ _("Copyright © 1999-2005 by Maurits Rijk"),
+ "license",
+ _("Released under the GNU General Public License"),
+ NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ dialog);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
diff --git a/plug-ins/imagemap/imap_about.h b/plug-ins/imagemap/imap_about.h
new file mode 100644
index 0000000..4834a75
--- /dev/null
+++ b/plug-ins/imagemap/imap_about.h
@@ -0,0 +1,28 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_ABOUT_H
+#define _IMAP_ABOUT_H
+
+void do_about_dialog(void);
+
+#endif /* _IMAP_ABOUT_H */
diff --git a/plug-ins/imagemap/imap_browse.c b/plug-ins/imagemap/imap_browse.c
new file mode 100644
index 0000000..11a8522
--- /dev/null
+++ b/plug-ins/imagemap/imap_browse.c
@@ -0,0 +1,172 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_browse.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static const GtkTargetEntry target_table[] =
+{
+ {"STRING", 0, 1 },
+ {"text/plain", 0, 2 }
+};
+
+
+static void
+select_cb (GtkWidget *dialog,
+ gint response_id,
+ BrowseWidget_t *browse)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *p;
+ gchar *file;
+
+ file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ p = (browse->filter ?
+ browse->filter (file, browse->filter_data) : file);
+
+ gtk_entry_set_text (GTK_ENTRY (browse->file), p);
+
+ if (browse->filter)
+ g_free (p);
+
+ g_free (file);
+ }
+
+ gtk_widget_hide (dialog);
+ gtk_widget_grab_focus (browse->file);
+}
+
+static void
+browse_cb (GtkWidget *widget,
+ BrowseWidget_t *browse)
+{
+ if (!browse->file_chooser)
+ {
+ GtkWidget *dialog;
+
+ dialog = browse->file_chooser =
+ gtk_file_chooser_dialog_new (browse->name,
+ GTK_WINDOW (gtk_widget_get_toplevel (widget)),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (select_cb),
+ browse);
+ }
+
+ gtk_window_present (GTK_WINDOW (browse->file_chooser));
+}
+
+static void
+handle_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *data, guint info, guint time)
+{
+ gboolean success = FALSE;
+
+ if (gtk_selection_data_get_length (data) >= 0 &&
+ gtk_selection_data_get_format (data) == 8)
+ {
+ const gchar *text = (const gchar *) gtk_selection_data_get_data (data);
+
+ if (g_utf8_validate (text, -1, NULL))
+ {
+ gtk_entry_set_text (GTK_ENTRY (widget), text);
+ success = TRUE;
+ }
+ }
+
+ gtk_drag_finish(context, success, FALSE, time);
+}
+
+BrowseWidget_t*
+browse_widget_new (const gchar *name)
+{
+ BrowseWidget_t *browse = g_new(BrowseWidget_t, 1);
+ GtkWidget *button;
+ GtkWidget *icon;
+
+ browse->file_chooser = NULL;
+ browse->name = name;
+ browse->filter = NULL;
+
+ browse->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_widget_show (browse->hbox);
+
+ browse->file = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX(browse->hbox), browse->file, TRUE, TRUE, 0);
+ gtk_drag_dest_set (browse->file, GTK_DEST_DEFAULT_ALL, target_table,
+ 2, GDK_ACTION_COPY);
+ g_signal_connect (browse->file, "drag-data-received",
+ G_CALLBACK(handle_drop), NULL);
+
+ gtk_widget_show (browse->file);
+
+ browse->button = button = gtk_button_new ();
+ icon = gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_OPEN,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_container_add (GTK_CONTAINER (button), icon);
+ gtk_widget_show (icon);
+
+ gtk_box_pack_end(GTK_BOX (browse->hbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK(browse_cb), (gpointer) browse);
+ gtk_widget_show (button);
+
+ return browse;
+}
+
+void
+browse_widget_set_filename(BrowseWidget_t *browse, const gchar *filename)
+{
+ gtk_entry_set_text (GTK_ENTRY (browse->file), filename);
+}
+
+void
+browse_widget_set_filter(BrowseWidget_t *browse, BrowseFilter_t filter,
+ gpointer data)
+{
+ browse->filter = filter;
+ browse->filter_data = data;
+}
diff --git a/plug-ins/imagemap/imap_browse.h b/plug-ins/imagemap/imap_browse.h
new file mode 100644
index 0000000..e86c1c8
--- /dev/null
+++ b/plug-ins/imagemap/imap_browse.h
@@ -0,0 +1,46 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_BROWSE_H
+#define _IMAP_BROWSE_H
+
+typedef gchar* (*BrowseFilter_t) (const gchar *, gpointer data);
+
+typedef struct
+{
+ const gchar *name;
+ BrowseFilter_t filter;
+ gpointer filter_data;
+ GtkWidget *hbox;
+ GtkWidget *file;
+ GtkWidget *button;
+ GtkWidget *file_chooser;
+} BrowseWidget_t;
+
+BrowseWidget_t * browse_widget_new (const gchar *name);
+void browse_widget_set_filename (BrowseWidget_t *browse,
+ const gchar *filename);
+void browse_widget_set_filter (BrowseWidget_t *browse,
+ BrowseFilter_t filter,
+ gpointer data);
+
+#endif /* _IMAP_BROWSE_H */
diff --git a/plug-ins/imagemap/imap_cern.l b/plug-ins/imagemap/imap_cern.l
new file mode 100644
index 0000000..95e7ad0
--- /dev/null
+++ b/plug-ins/imagemap/imap_cern.l
@@ -0,0 +1,93 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_cern_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+%}
+
+%option noyywrap
+%option noinput
+%option nounput
+
+DIGIT [0-9]
+ID [a-zA-Z_][a-zA-Z0-9_\-]*
+WS [ \t\n]+
+
+%x comment
+
+%%
+
+RECT{WS}\(4096,4096\){WS}\(4096,4096\).*#\$AUTHOR: {
+ BEGIN(comment);
+ return AUTHOR;
+ }
+
+RECT{WS}\(4096,4096\){WS}\(4096,4096\).*#\$DESCRIPTION: {
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+
+RECT{WS}\(4096,4096\){WS}\(4096,4096\) {
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+
+<comment>.*$ {
+ BEGIN(INITIAL);
+ cern_lval.id = g_strndup (yytext, yyleng);
+ return COMMENT;
+ }
+
+RECT return RECTANGLE;
+
+CIRC return CIRCLE;
+
+POLY return POLYGON;
+
+DEFAULT return DEFAULT;
+
+[^ ,\t\n]+$ {
+ cern_lval.id = g_strndup (yytext, yyleng);
+ return LINK;
+ }
+
+-?{DIGIT}*"."?{DIGIT}*([Ee][-+]?{DIGIT}*)? {
+ cern_lval.value = g_ascii_strtod (yytext, NULL);
+ return FLOAT;
+ }
+
+{WS} ; /* Eat white space */
+
+. return *yytext;
+
+%%
+
+
diff --git a/plug-ins/imagemap/imap_cern.y b/plug-ins/imagemap/imap_cern.y
new file mode 100644
index 0000000..251eb16
--- /dev/null
+++ b/plug-ins/imagemap/imap_cern.y
@@ -0,0 +1,184 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <math.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int cern_lex(void);
+extern int cern_restart(FILE *cern_in);
+static void cern_error(char* s);
+
+static Object_t *current_object;
+
+%}
+
+%union {
+ int val;
+ double value;
+ char *id;
+}
+
+%token<val> RECTANGLE POLYGON CIRCLE DEFAULT
+%token<val> AUTHOR DESCRIPTION BEGIN_COMMENT
+%token<value> FLOAT
+%token<id> COMMENT LINK
+
+%%
+
+cern_file : area_list
+ ;
+
+area_list : /* Empty */
+ | area_list area
+ ;
+
+area : default
+ | rectangle
+ | circle
+ | polygon
+ | comment_line
+ ;
+
+default : DEFAULT LINK
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->default_url, $2);
+ g_free ($2);
+ }
+ ;
+
+
+rectangle : RECTANGLE '(' FLOAT ',' FLOAT ')' '(' FLOAT ',' FLOAT ')' LINK
+ {
+ gint x = (gint) $3;
+ gint y = (gint) $5;
+ gint width = (gint) fabs($8 - x);
+ gint height = (gint) fabs($10 - y);
+ current_object = create_rectangle(x, y, width, height);
+ object_set_url(current_object, $12);
+ add_shape(current_object);
+ g_free ($12);
+ }
+ ;
+
+circle : CIRCLE '(' FLOAT ',' FLOAT ')' FLOAT LINK
+ {
+ gint x = (gint) $3;
+ gint y = (gint) $5;
+ gint r = (gint) $7;
+ current_object = create_circle(x, y, r);
+ object_set_url(current_object, $8);
+ add_shape(current_object);
+ g_free ($8);
+ }
+ ;
+
+polygon : POLYGON {current_object = create_polygon(NULL);} coord_list LINK
+ {
+ object_set_url(current_object, $4);
+ add_shape(current_object);
+ g_free ($4);
+ }
+ ;
+
+coord_list : /* Empty */
+ | coord_list coord
+ {
+ }
+ ;
+
+coord : '(' FLOAT ',' FLOAT ')'
+ {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GdkPoint *point = new_point((gint) $2, (gint) $4);
+ polygon->points = g_list_append(polygon->points,
+ (gpointer) point);
+ }
+ ;
+
+comment_line : author_line
+ | description_line
+ | real_comment
+ ;
+
+real_comment : BEGIN_COMMENT COMMENT
+ {
+ g_free ($2);
+ }
+ ;
+
+author_line : AUTHOR COMMENT
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->author, $2);
+ g_free ($2);
+ }
+ ;
+
+description_line: DESCRIPTION COMMENT
+ {
+ MapInfo_t *info = get_map_info();
+ gchar *description;
+
+ description = g_strconcat(info->description, $2, "\n",
+ NULL);
+ g_strreplace(&info->description, description);
+ g_free ($2);
+ }
+ ;
+
+
+%%
+
+static void
+cern_error(char* s)
+{
+ extern FILE *cern_in;
+ cern_restart(cern_in);
+}
+
+gboolean
+load_cern(const char* filename)
+{
+ gboolean status;
+ extern FILE *cern_in;
+ cern_in = g_fopen(filename, "r");
+ if (cern_in) {
+ status = !cern_parse();
+ fclose(cern_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
diff --git a/plug-ins/imagemap/imap_cern_lex.c b/plug-ins/imagemap/imap_cern_lex.c
new file mode 100644
index 0000000..eb761d2
--- /dev/null
+++ b/plug-ins/imagemap/imap_cern_lex.c
@@ -0,0 +1,1939 @@
+
+#line 3 "<stdout>"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer cern__create_buffer
+#define yy_delete_buffer cern__delete_buffer
+#define yy_flex_debug cern__flex_debug
+#define yy_init_buffer cern__init_buffer
+#define yy_flush_buffer cern__flush_buffer
+#define yy_load_buffer_state cern__load_buffer_state
+#define yy_switch_to_buffer cern__switch_to_buffer
+#define yyin cern_in
+#define yyleng cern_leng
+#define yylex cern_lex
+#define yylineno cern_lineno
+#define yyout cern_out
+#define yyrestart cern_restart
+#define yytext cern_text
+#define yywrap cern_wrap
+#define yyalloc cern_alloc
+#define yyrealloc cern_realloc
+#define yyfree cern_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 36
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE cern_restart(cern_in )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+extern yy_size_t cern_leng;
+
+extern FILE *cern_in, *cern_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up cern_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up cern_text again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via cern_restart()), so that the user can continue scanning by
+ * just pointing cern_in at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when cern_text is formed. */
+static char yy_hold_char;
+static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */
+yy_size_t cern_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow cern_wrap()'s to do buffer switches
+ * instead of setting up a fresh cern_in. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void cern_restart (FILE *input_file );
+void cern__switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE cern__create_buffer (FILE *file,int size );
+void cern__delete_buffer (YY_BUFFER_STATE b );
+void cern__flush_buffer (YY_BUFFER_STATE b );
+void cern_push_buffer_state (YY_BUFFER_STATE new_buffer );
+void cern_pop_buffer_state (void );
+
+static void cern_ensure_buffer_stack (void );
+static void cern__load_buffer_state (void );
+static void cern__init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER cern__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE cern__scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE cern__scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE cern__scan_bytes (yyconst char *bytes,yy_size_t len );
+
+void *cern_alloc (yy_size_t );
+void *cern_realloc (void *,yy_size_t );
+void cern_free (void * );
+
+#define yy_new_buffer cern__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ cern_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ cern__create_buffer(cern_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ cern_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ cern__create_buffer(cern_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define cern_wrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *cern_in = (FILE *) 0, *cern_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int cern_lineno;
+
+int cern_lineno = 1;
+
+extern char *cern_text;
+#define yytext_ptr cern_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up cern_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ cern_leng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 13
+#define YY_END_OF_BUFFER 14
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[94] =
+ { 0,
+ 10, 10, 0, 0, 14, 12, 11, 11, 12, 10,
+ 10, 10, 12, 12, 10, 12, 12, 13, 4, 0,
+ 9, 11, 10, 10, 10, 10, 0, 0, 10, 10,
+ 0, 0, 0, 4, 0, 0, 0, 0, 6, 0,
+ 7, 5, 0, 0, 9, 0, 0, 8, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 2, 0
+
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 4, 5, 1, 1, 1, 6,
+ 7, 1, 8, 9, 10, 11, 1, 12, 13, 13,
+ 13, 14, 13, 15, 13, 13, 16, 17, 1, 1,
+ 1, 1, 1, 1, 18, 1, 19, 20, 21, 22,
+ 1, 23, 24, 1, 1, 25, 1, 26, 27, 28,
+ 1, 29, 30, 31, 32, 1, 1, 1, 33, 1,
+ 1, 1, 1, 1, 1, 1, 34, 1, 35, 36,
+
+ 37, 38, 1, 39, 40, 1, 1, 41, 1, 42,
+ 43, 44, 1, 45, 46, 47, 48, 1, 1, 1,
+ 49, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[50] =
+ { 0,
+ 1, 2, 3, 1, 1, 1, 1, 1, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[98] =
+ { 0,
+ 0, 0, 276, 275, 277, 270, 48, 50, 309, 51,
+ 86, 121, 52, 53, 156, 54, 56, 269, 309, 268,
+ 309, 58, 170, 205, 240, 254, 65, 81, 101, 115,
+ 55, 76, 262, 309, 103, 122, 72, 143, 261, 109,
+ 260, 67, 136, 80, 141, 145, 248, 258, 235, 230,
+ 230, 235, 224, 224, 218, 217, 223, 106, 147, 213,
+ 199, 189, 187, 184, 161, 151, 139, 111, 113, 83,
+ 81, 147, 161, 156, 141, 163, 150, 183, 194, 185,
+ 195, 196, 199, 74, 205, 69, 227, 211, 233, 255,
+ 197, 67, 309, 297, 300, 303, 306
+
+ } ;
+
+static yyconst flex_int16_t yy_def[98] =
+ { 0,
+ 93, 1, 94, 94, 93, 95, 93, 93, 93, 95,
+ 95, 95, 95, 95, 95, 95, 95, 96, 93, 95,
+ 93, 93, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 96, 93, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 93, 93, 95, 93, 95, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 0, 93, 93, 93, 93
+
+ } ;
+
+static yyconst flex_int16_t yy_nxt[359] =
+ { 0,
+ 6, 7, 8, 6, 6, 6, 6, 6, 9, 10,
+ 11, 12, 12, 12, 12, 12, 6, 6, 13, 14,
+ 15, 6, 6, 6, 6, 6, 6, 16, 17, 6,
+ 6, 6, 6, 6, 13, 14, 15, 6, 6, 6,
+ 6, 6, 6, 16, 17, 6, 6, 6, 6, 22,
+ 22, 22, 22, 21, 21, 21, 21, 21, 21, 22,
+ 22, 23, 24, 24, 24, 24, 24, 21, 44, 45,
+ 72, 25, 72, 28, 21, 27, 32, 72, 21, 37,
+ 31, 44, 44, 21, 72, 47, 72, 25, 21, 28,
+ 86, 27, 32, 35, 38, 37, 31, 26, 26, 26,
+
+ 26, 26, 36, 21, 41, 21, 25, 59, 59, 35,
+ 38, 21, 30, 30, 30, 30, 30, 21, 36, 70,
+ 41, 39, 25, 21, 21, 69, 30, 30, 30, 30,
+ 30, 23, 24, 24, 24, 24, 24, 39, 21, 40,
+ 43, 25, 44, 44, 72, 21, 47, 21, 59, 59,
+ 72, 73, 60, 72, 68, 40, 43, 25, 21, 72,
+ 46, 77, 67, 29, 72, 29, 72, 30, 30, 30,
+ 30, 30, 21, 42, 66, 48, 46, 77, 74, 79,
+ 75, 26, 26, 26, 26, 26, 72, 76, 72, 42,
+ 25, 48, 65, 78, 74, 79, 75, 72, 72, 72,
+
+ 72, 64, 72, 76, 63, 80, 25, 21, 72, 78,
+ 62, 82, 81, 92, 72, 23, 24, 24, 24, 24,
+ 24, 80, 85, 83, 84, 25, 61, 82, 81, 58,
+ 72, 57, 87, 56, 89, 55, 72, 54, 85, 83,
+ 84, 25, 21, 53, 52, 51, 50, 29, 87, 29,
+ 89, 30, 30, 30, 30, 30, 21, 88, 72, 90,
+ 21, 49, 21, 21, 34, 26, 26, 26, 26, 26,
+ 21, 34, 21, 88, 25, 90, 93, 19, 19, 93,
+ 91, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 25, 93, 93, 93, 93, 93, 91, 18, 18, 18,
+
+ 20, 93, 20, 33, 33, 33, 71, 71, 5, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93
+ } ;
+
+static yyconst flex_int16_t yy_chk[359] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 7,
+ 7, 8, 8, 10, 13, 14, 16, 31, 17, 22,
+ 22, 10, 10, 10, 10, 10, 10, 27, 42, 42,
+ 92, 10, 86, 14, 37, 13, 17, 84, 32, 31,
+ 16, 44, 44, 28, 71, 44, 70, 10, 11, 14,
+ 84, 13, 17, 27, 32, 31, 16, 11, 11, 11,
+
+ 11, 11, 28, 29, 37, 35, 11, 58, 58, 27,
+ 32, 40, 29, 29, 29, 29, 29, 30, 28, 69,
+ 37, 35, 11, 12, 36, 68, 30, 30, 30, 30,
+ 30, 12, 12, 12, 12, 12, 12, 35, 43, 36,
+ 40, 12, 45, 45, 75, 38, 45, 46, 59, 59,
+ 72, 72, 59, 77, 67, 36, 40, 12, 15, 74,
+ 43, 75, 66, 15, 73, 15, 76, 15, 15, 15,
+ 15, 15, 23, 38, 65, 46, 43, 75, 73, 77,
+ 73, 23, 23, 23, 23, 23, 78, 74, 80, 38,
+ 23, 46, 64, 76, 73, 77, 73, 79, 81, 82,
+
+ 91, 63, 83, 74, 62, 78, 23, 24, 85, 76,
+ 61, 80, 79, 91, 88, 24, 24, 24, 24, 24,
+ 24, 78, 83, 81, 82, 24, 60, 80, 79, 57,
+ 87, 56, 85, 55, 88, 54, 89, 53, 83, 81,
+ 82, 24, 25, 52, 51, 50, 49, 25, 85, 25,
+ 88, 25, 25, 25, 25, 25, 26, 87, 90, 89,
+ 48, 47, 41, 39, 33, 26, 26, 26, 26, 26,
+ 20, 18, 6, 87, 26, 89, 5, 4, 3, 0,
+ 90, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 26, 0, 0, 0, 0, 0, 90, 94, 94, 94,
+
+ 95, 0, 95, 96, 96, 96, 97, 97, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,
+ 93, 93, 93, 93, 93, 93, 93, 93
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int cern__flex_debug;
+int cern__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *cern_text;
+#line 1 "imap_cern.l"
+#line 2 "imap_cern.l"
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_cern_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+#define YY_NO_INPUT 1
+
+#line 617 "<stdout>"
+
+#define INITIAL 0
+#define comment 1
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int cern_lex_destroy (void );
+
+int cern_get_debug (void );
+
+void cern_set_debug (int debug_flag );
+
+YY_EXTRA_TYPE cern_get_extra (void );
+
+void cern_set_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *cern_get_in (void );
+
+void cern_set_in (FILE * in_str );
+
+FILE *cern_get_out (void );
+
+void cern_set_out (FILE * out_str );
+
+yy_size_t cern_get_leng (void );
+
+char *cern_get_text (void );
+
+int cern_get_lineno (void );
+
+void cern_set_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int cern_wrap (void );
+#else
+extern int cern_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( cern_text, cern_leng, 1, cern_out )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( cern_in )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( cern_in ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, cern_in))==0 && ferror(cern_in)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(cern_in); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int cern_lex (void);
+
+#define YY_DECL int cern_lex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after cern_text and cern_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 46 "imap_cern.l"
+
+
+#line 801 "<stdout>"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! cern_in )
+ cern_in = stdin;
+
+ if ( ! cern_out )
+ cern_out = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ cern_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ cern__create_buffer(cern_in,YY_BUF_SIZE );
+ }
+
+ cern__load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of cern_text. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 94 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 309 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+/* rule 1 can match eol */
+YY_RULE_SETUP
+#line 48 "imap_cern.l"
+{
+ BEGIN(comment);
+ return AUTHOR;
+ }
+ YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 53 "imap_cern.l"
+{
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+ YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 58 "imap_cern.l"
+{
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+ YY_BREAK
+case 4:
+*yy_cp = (yy_hold_char); /* undo effects of setting up cern_text */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up cern_text again */
+YY_RULE_SETUP
+#line 63 "imap_cern.l"
+{
+ BEGIN(INITIAL);
+ cern_lval.id = g_strndup (cern_text, cern_leng);
+ return COMMENT;
+ }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 69 "imap_cern.l"
+return RECTANGLE;
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 71 "imap_cern.l"
+return CIRCLE;
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 73 "imap_cern.l"
+return POLYGON;
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 75 "imap_cern.l"
+return DEFAULT;
+ YY_BREAK
+case 9:
+*yy_cp = (yy_hold_char); /* undo effects of setting up cern_text */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up cern_text again */
+YY_RULE_SETUP
+#line 77 "imap_cern.l"
+{
+ cern_lval.id = g_strndup (cern_text, cern_leng);
+ return LINK;
+ }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 82 "imap_cern.l"
+{
+ cern_lval.value = g_ascii_strtod (cern_text, NULL);
+ return FLOAT;
+ }
+ YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+#line 87 "imap_cern.l"
+; /* Eat white space */
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 89 "imap_cern.l"
+return *cern_text;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 91 "imap_cern.l"
+ECHO;
+ YY_BREAK
+#line 978 "<stdout>"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(comment):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed cern_in at a new source and called
+ * cern_lex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = cern_in;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( cern_wrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * cern_text, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of cern_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ yy_size_t new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ cern_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ cern_restart(cern_in );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) cern_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 94 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 94 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 93);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ cern_restart(cern_in );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( cern_wrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve cern_text */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void cern_restart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ cern_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ cern__create_buffer(cern_in,YY_BUF_SIZE );
+ }
+
+ cern__init_buffer(YY_CURRENT_BUFFER,input_file );
+ cern__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void cern__switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * cern_pop_buffer_state();
+ * cern_push_buffer_state(new_buffer);
+ */
+ cern_ensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ cern__load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (cern_wrap()) processing, but the only time this flag
+ * is looked at is after cern_wrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void cern__load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ cern_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE cern__create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) cern_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in cern__create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) cern_alloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in cern__create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ cern__init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with cern__create_buffer()
+ *
+ */
+ void cern__delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ cern_free((void *) b->yy_ch_buf );
+
+ cern_free((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a cern_restart() or at EOF.
+ */
+ static void cern__init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ cern__flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then cern__init_buffer was _probably_
+ * called from cern_restart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void cern__flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ cern__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void cern_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ cern_ensure_buffer_stack();
+
+ /* This block is copied from cern__switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from cern__switch_to_buffer. */
+ cern__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void cern_pop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ cern__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ cern__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void cern_ensure_buffer_stack (void)
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)cern_alloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in cern_ensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)cern_realloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in cern_ensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE cern__scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) cern_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in cern__scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ cern__switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to cern_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * cern__scan_bytes() instead.
+ */
+YY_BUFFER_STATE cern__scan_string (yyconst char * yystr )
+{
+
+ return cern__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to cern_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE cern__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) cern_alloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in cern__scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = cern__scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in cern__scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up cern_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ cern_text[cern_leng] = (yy_hold_char); \
+ (yy_c_buf_p) = cern_text + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ cern_leng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int cern_get_lineno (void)
+{
+
+ return cern_lineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *cern_get_in (void)
+{
+ return cern_in;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *cern_get_out (void)
+{
+ return cern_out;
+}
+
+/** Get the length of the current token.
+ *
+ */
+yy_size_t cern_get_leng (void)
+{
+ return cern_leng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *cern_get_text (void)
+{
+ return cern_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void cern_set_lineno (int line_number )
+{
+
+ cern_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see cern__switch_to_buffer
+ */
+void cern_set_in (FILE * in_str )
+{
+ cern_in = in_str ;
+}
+
+void cern_set_out (FILE * out_str )
+{
+ cern_out = out_str ;
+}
+
+int cern_get_debug (void)
+{
+ return cern__flex_debug;
+}
+
+void cern_set_debug (int bdebug )
+{
+ cern__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from cern_lex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ cern_in = stdin;
+ cern_out = stdout;
+#else
+ cern_in = (FILE *) 0;
+ cern_out = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * cern_lex_init()
+ */
+ return 0;
+}
+
+/* cern_lex_destroy is for both reentrant and non-reentrant scanners. */
+int cern_lex_destroy (void)
+{
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ cern__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ cern_pop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ cern_free((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * cern_lex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *cern_alloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *cern_realloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void cern_free (void * ptr )
+{
+ free( (char *) ptr ); /* see cern_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 91 "imap_cern.l"
+
+
+
+
+
diff --git a/plug-ins/imagemap/imap_cern_parse.c b/plug-ins/imagemap/imap_cern_parse.c
new file mode 100644
index 0000000..56714a2
--- /dev/null
+++ b/plug-ins/imagemap/imap_cern_parse.c
@@ -0,0 +1,1822 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.6.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names. */
+#define yyparse cern_parse
+#define yylex cern_lex
+#define yyerror cern_error
+#define yylval cern_lval
+#define yychar cern_char
+#define yydebug cern_debug
+#define yynerrs cern_nerrs
+
+/* Copy the first part of user declarations. */
+/* Line 336 of yacc.c */
+#line 1 "imap_cern.y"
+
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <math.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int cern_lex(void);
+extern int cern_restart(FILE *cern_in);
+static void cern_error(char* s);
+
+static Object_t *current_object;
+
+
+/* Line 336 of yacc.c */
+#line 120 "y.tab.c"
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef CERN_Y_TAB_H
+# define CERN_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int cern_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ RECTANGLE = 258,
+ POLYGON = 259,
+ CIRCLE = 260,
+ DEFAULT = 261,
+ AUTHOR = 262,
+ DESCRIPTION = 263,
+ BEGIN_COMMENT = 264,
+ FLOAT = 265,
+ COMMENT = 266,
+ LINK = 267
+ };
+#endif
+/* Tokens. */
+#define RECTANGLE 258
+#define POLYGON 259
+#define CIRCLE 260
+#define DEFAULT 261
+#define AUTHOR 262
+#define DESCRIPTION 263
+#define BEGIN_COMMENT 264
+#define FLOAT 265
+#define COMMENT 266
+#define LINK 267
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 350 of yacc.c */
+#line 46 "imap_cern.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 350 of yacc.c */
+#line 194 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE cern_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int cern_parse (void *YYPARSE_PARAM);
+#else
+int cern_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int cern_parse (void);
+#else
+int cern_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !CERN_Y_TAB_H */
+
+/* Copy the second part of user declarations. */
+
+/* Line 353 of yacc.c */
+#line 222 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 39
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 16
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 15
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 23
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 51
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 267
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 13, 15, 2, 2, 14, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 9, 11, 13, 15, 17,
+ 19, 22, 35, 44, 45, 50, 51, 54, 60, 62,
+ 64, 66, 69, 72
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 17, 0, -1, 18, -1, -1, 18, 19, -1, 20,
+ -1, 21, -1, 22, -1, 23, -1, 27, -1, 6,
+ 12, -1, 3, 13, 10, 14, 10, 15, 13, 10,
+ 14, 10, 15, 12, -1, 5, 13, 10, 14, 10,
+ 15, 10, 12, -1, -1, 4, 24, 25, 12, -1,
+ -1, 25, 26, -1, 13, 10, 14, 10, 15, -1,
+ 29, -1, 30, -1, 28, -1, 9, 11, -1, 7,
+ 11, -1, 8, 11, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 59, 59, 62, 63, 66, 67, 68, 69, 70,
+ 73, 82, 95, 107, 107, 115, 116, 121, 130, 131,
+ 132, 135, 141, 149
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "RECTANGLE", "POLYGON", "CIRCLE",
+ "DEFAULT", "AUTHOR", "DESCRIPTION", "BEGIN_COMMENT", "FLOAT", "COMMENT",
+ "LINK", "'('", "','", "')'", "$accept", "cern_file", "area_list", "area",
+ "default", "rectangle", "circle", "polygon", "$@1", "coord_list",
+ "coord", "comment_line", "real_comment", "author_line",
+ "description_line", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 40, 44, 41
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 16, 17, 18, 18, 19, 19, 19, 19, 19,
+ 20, 21, 22, 24, 23, 25, 25, 26, 27, 27,
+ 27, 28, 29, 30
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 0, 2, 1, 1, 1, 1, 1,
+ 2, 12, 8, 0, 4, 0, 2, 5, 1, 1,
+ 1, 2, 2, 2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 3, 0, 2, 1, 0, 13, 0, 0, 0, 0,
+ 0, 4, 5, 6, 7, 8, 9, 20, 18, 19,
+ 0, 15, 0, 10, 22, 23, 21, 0, 0, 0,
+ 0, 14, 0, 16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 17, 12, 0, 0, 0,
+ 11
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 2, 11, 12, 13, 14, 15, 21, 28,
+ 33, 16, 17, 18, 19
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -6
+static const yytype_int8 yypact[] =
+{
+ -6, 9, -3, -6, -2, -6, -1, 1, 3, 4,
+ 5, -6, -6, -6, -6, -6, -6, -6, -6, -6,
+ 0, -6, 7, -6, -6, -6, -6, 6, -5, 8,
+ 11, -6, 13, -6, 14, 10, 12, 15, 16, 17,
+ 18, 21, 19, 20, 22, -6, -6, 23, 24, 25,
+ -6
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
+ -6, -6, -6, -6, -6
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 4, 5, 6, 7, 8, 9, 10, 31, 32, 3,
+ 27, 20, 22, 23, 24, 25, 26, 29, 0, 0,
+ 30, 35, 34, 36, 37, 38, 39, 42, 43, 41,
+ 40, 44, 46, 48, 45, 0, 47, 50, 0, 49
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-6))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 3, 4, 5, 6, 7, 8, 9, 12, 13, 0,
+ 10, 13, 13, 12, 11, 11, 11, 10, -1, -1,
+ 14, 10, 14, 10, 10, 15, 14, 10, 10, 13,
+ 15, 10, 12, 10, 15, -1, 14, 12, -1, 15
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 17, 18, 0, 3, 4, 5, 6, 7, 8,
+ 9, 19, 20, 21, 22, 23, 27, 28, 29, 30,
+ 13, 24, 13, 12, 11, 11, 11, 10, 25, 10,
+ 14, 12, 13, 26, 14, 10, 10, 10, 15, 14,
+ 15, 13, 10, 10, 10, 15, 12, 14, 10, 15,
+ 12
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 10:
+/* Line 1787 of yacc.c */
+#line 74 "imap_cern.y"
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->default_url, (yyvsp[(2) - (2)].id));
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 11:
+/* Line 1787 of yacc.c */
+#line 83 "imap_cern.y"
+ {
+ gint x = (gint) (yyvsp[(3) - (12)].value);
+ gint y = (gint) (yyvsp[(5) - (12)].value);
+ gint width = (gint) fabs((yyvsp[(8) - (12)].value) - x);
+ gint height = (gint) fabs((yyvsp[(10) - (12)].value) - y);
+ current_object = create_rectangle(x, y, width, height);
+ object_set_url(current_object, (yyvsp[(12) - (12)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(12) - (12)].id));
+ }
+ break;
+
+ case 12:
+/* Line 1787 of yacc.c */
+#line 96 "imap_cern.y"
+ {
+ gint x = (gint) (yyvsp[(3) - (8)].value);
+ gint y = (gint) (yyvsp[(5) - (8)].value);
+ gint r = (gint) (yyvsp[(7) - (8)].value);
+ current_object = create_circle(x, y, r);
+ object_set_url(current_object, (yyvsp[(8) - (8)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(8) - (8)].id));
+ }
+ break;
+
+ case 13:
+/* Line 1787 of yacc.c */
+#line 107 "imap_cern.y"
+ {current_object = create_polygon(NULL);}
+ break;
+
+ case 14:
+/* Line 1787 of yacc.c */
+#line 108 "imap_cern.y"
+ {
+ object_set_url(current_object, (yyvsp[(4) - (4)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(4) - (4)].id));
+ }
+ break;
+
+ case 16:
+/* Line 1787 of yacc.c */
+#line 117 "imap_cern.y"
+ {
+ }
+ break;
+
+ case 17:
+/* Line 1787 of yacc.c */
+#line 122 "imap_cern.y"
+ {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GdkPoint *point = new_point((gint) (yyvsp[(2) - (5)].value), (gint) (yyvsp[(4) - (5)].value));
+ polygon->points = g_list_append(polygon->points,
+ (gpointer) point);
+ }
+ break;
+
+ case 21:
+/* Line 1787 of yacc.c */
+#line 136 "imap_cern.y"
+ {
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 22:
+/* Line 1787 of yacc.c */
+#line 142 "imap_cern.y"
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->author, (yyvsp[(2) - (2)].id));
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 23:
+/* Line 1787 of yacc.c */
+#line 150 "imap_cern.y"
+ {
+ MapInfo_t *info = get_map_info();
+ gchar *description;
+
+ description = g_strconcat(info->description, (yyvsp[(2) - (2)].id), "\n",
+ NULL);
+ g_strreplace(&info->description, description);
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+
+/* Line 1787 of yacc.c */
+#line 1569 "y.tab.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+/* Line 2048 of yacc.c */
+#line 162 "imap_cern.y"
+
+
+static void
+cern_error(char* s)
+{
+ extern FILE *cern_in;
+ cern_restart(cern_in);
+}
+
+gboolean
+load_cern(const char* filename)
+{
+ gboolean status;
+ extern FILE *cern_in;
+ cern_in = g_fopen(filename, "r");
+ if (cern_in) {
+ status = !cern_parse();
+ fclose(cern_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
+
diff --git a/plug-ins/imagemap/imap_cern_parse.h b/plug-ins/imagemap/imap_cern_parse.h
new file mode 100644
index 0000000..f46caff
--- /dev/null
+++ b/plug-ins/imagemap/imap_cern_parse.h
@@ -0,0 +1,110 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef CERN_Y_TAB_H
+# define CERN_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int cern_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ RECTANGLE = 258,
+ POLYGON = 259,
+ CIRCLE = 260,
+ DEFAULT = 261,
+ AUTHOR = 262,
+ DESCRIPTION = 263,
+ BEGIN_COMMENT = 264,
+ FLOAT = 265,
+ COMMENT = 266,
+ LINK = 267
+ };
+#endif
+/* Tokens. */
+#define RECTANGLE 258
+#define POLYGON 259
+#define CIRCLE 260
+#define DEFAULT 261
+#define AUTHOR 262
+#define DESCRIPTION 263
+#define BEGIN_COMMENT 264
+#define FLOAT 265
+#define COMMENT 266
+#define LINK 267
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 2049 of yacc.c */
+#line 46 "imap_cern.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 2049 of yacc.c */
+#line 88 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE cern_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int cern_parse (void *YYPARSE_PARAM);
+#else
+int cern_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int cern_parse (void);
+#else
+int cern_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !CERN_Y_TAB_H */
diff --git a/plug-ins/imagemap/imap_circle.c b/plug-ins/imagemap/imap_circle.c
new file mode 100644
index 0000000..230b547
--- /dev/null
+++ b/plug-ins/imagemap/imap_circle.c
@@ -0,0 +1,405 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_main.h"
+#include "imap_misc.h"
+#include "imap_object_popup.h"
+#include "imap_stock.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gboolean circle_is_valid(Object_t *obj);
+static Object_t *circle_clone(Object_t *obj);
+static void circle_assign(Object_t *obj, Object_t *des);
+static void circle_draw(Object_t* obj, cairo_t *cr);
+static void circle_draw_sashes(Object_t* obj, cairo_t *cr);
+static MoveSashFunc_t circle_near_sash(Object_t *obj, gint x, gint y);
+static gboolean circle_point_is_on(Object_t *obj, gint x, gint y);
+static void circle_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height);
+static void circle_resize(Object_t *obj, gint percentage_x, gint percentage_y);
+static void circle_move(Object_t *obj, gint dx, gint dy);
+static gpointer circle_create_info_widget(GtkWidget *frame);
+static void circle_fill_info_tab(Object_t *obj, gpointer data);
+static void circle_set_initial_focus(Object_t *obj, gpointer data);
+static void circle_update(Object_t* obj, gpointer data);
+static void circle_write_csim(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static void circle_write_cern(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static void circle_write_ncsa(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static const gchar* circle_get_stock_icon_name(void);
+
+static ObjectClass_t circle_class = {
+ N_("C_ircle"),
+ NULL, /* info_dialog */
+
+ circle_is_valid,
+ NULL, /* circle_destruct */
+ circle_clone,
+ circle_assign,
+ NULL, /* circle_normalize */
+ circle_draw,
+ circle_draw_sashes,
+ circle_near_sash,
+ circle_point_is_on,
+ circle_get_dimensions,
+ circle_resize,
+ circle_move,
+ circle_create_info_widget,
+ circle_fill_info_tab, /* circle_update_info_widget */
+ circle_fill_info_tab,
+ circle_set_initial_focus,
+ circle_update,
+ circle_write_csim,
+ circle_write_cern,
+ circle_write_ncsa,
+ object_do_popup,
+ circle_get_stock_icon_name
+};
+
+Object_t*
+create_circle(gint x, gint y, gint r)
+{
+ Circle_t *circle = g_new(Circle_t, 1);
+ circle->x = x;
+ circle->y = y;
+ circle->r = r;
+ return object_init(&circle->obj, &circle_class);
+}
+
+static gboolean
+circle_is_valid(Object_t *obj)
+{
+ return ObjectToCircle(obj)->r > 0;
+}
+
+static Object_t*
+circle_clone(Object_t *obj)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ Circle_t *clone = g_new(Circle_t, 1);
+
+ clone->x = circle->x;
+ clone->y = circle->y;
+ clone->r = circle->r;
+ return &clone->obj;
+}
+
+static void
+circle_assign(Object_t *obj, Object_t *des)
+{
+ Circle_t *src_circle = ObjectToCircle(obj);
+ Circle_t *des_circle = ObjectToCircle(des);
+ des_circle->x = src_circle->x;
+ des_circle->y = src_circle->y;
+ des_circle->r = src_circle->r;
+}
+
+static void
+circle_draw(Object_t *obj, cairo_t *cr)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ draw_circle(cr, circle->x, circle->y, circle->r);
+}
+
+static void
+circle_draw_sashes(Object_t *obj, cairo_t *cr)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ draw_sash(cr, circle->x - circle->r, circle->y - circle->r);
+ draw_sash(cr, circle->x + circle->r, circle->y - circle->r);
+ draw_sash(cr, circle->x - circle->r, circle->y + circle->r);
+ draw_sash(cr, circle->x + circle->r, circle->y + circle->r);
+}
+
+static gint sash_x;
+static gint sash_y;
+
+static void
+move_sash(Object_t *obj, gint dx, gint dy)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ gint rx, ry;
+ sash_x += dx;
+ sash_y += dy;
+
+ rx = abs(circle->x - sash_x);
+ ry = abs(circle->y - sash_y);
+ circle->r = (rx > ry) ? rx : ry;
+}
+
+static void
+circle_resize(Object_t *obj, gint percentage_x, gint percentage_y)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ circle->x = circle->x * percentage_x / 100;
+ circle->y = circle->y * percentage_y / 100;
+ circle->r = circle->r * ((percentage_x < percentage_y)
+ ? percentage_x : percentage_y) / 100;
+}
+
+static MoveSashFunc_t
+circle_near_sash(Object_t *obj, gint x, gint y)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ sash_x = x;
+ sash_y = y;
+ if (near_sash(circle->x - circle->r, circle->y - circle->r, x, y) ||
+ near_sash(circle->x + circle->r, circle->y - circle->r, x, y) ||
+ near_sash(circle->x - circle->r, circle->y + circle->r, x, y) ||
+ near_sash(circle->x + circle->r, circle->y + circle->r, x, y))
+ return move_sash;
+ return NULL;
+}
+
+static gboolean
+circle_point_is_on(Object_t *obj, gint x, gint y)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ x -= circle->x;
+ y -= circle->y;
+ return x * x + y * y <= circle->r * circle->r;
+}
+
+static void
+circle_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ *x = circle->x - circle->r;
+ *y = circle->y - circle->r;
+ *width = *height = 2 * circle->r;
+}
+
+static void
+circle_move(Object_t *obj, gint dx, gint dy)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ circle->x += dx;
+ circle->y += dy;
+}
+
+typedef struct {
+ Object_t *obj;
+ GtkWidget *x;
+ GtkWidget *y;
+ GtkWidget *r;
+} CircleProperties_t;
+
+static void
+x_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((CircleProperties_t*) data)->obj;
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToCircle(obj)->x = x;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+y_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((CircleProperties_t*) data)->obj;
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToCircle(obj)->y = y;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+r_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((CircleProperties_t*) data)->obj;
+ gint r = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToCircle(obj)->r = r;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static gpointer
+circle_create_info_widget(GtkWidget *frame)
+{
+ CircleProperties_t *props = g_new(CircleProperties_t, 1);
+ GtkWidget *table, *label;
+ gint max_width = get_image_width();
+ gint max_height = get_image_height();
+
+ table = gtk_table_new(3, 3, FALSE);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_widget_show(table);
+
+ label = create_label_in_table(table, 0, 0, _("Center _x:"));
+ props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
+ max_width - 1);
+ g_signal_connect(props->x, "value-changed",
+ G_CALLBACK (x_changed_cb), (gpointer) props);
+ create_label_in_table(table, 0, 2, _("pixels"));
+
+ label = create_label_in_table(table, 1, 0, _("Center _y:"));
+ props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
+ max_height - 1);
+ g_signal_connect(props->y, "value-changed",
+ G_CALLBACK (y_changed_cb), (gpointer) props);
+ create_label_in_table(table, 1, 2, _("pixels"));
+
+ label = create_label_in_table(table, 2, 0, _("_Radius:"));
+ props->r = create_spin_button_in_table(table, label, 2, 1, 1, 1, G_MAXINT);
+ g_signal_connect(props->r, "value-changed",
+ G_CALLBACK (r_changed_cb), (gpointer) props);
+ create_label_in_table(table, 2, 2, _("pixels"));
+
+ return props;
+}
+
+static void
+circle_fill_info_tab(Object_t *obj, gpointer data)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ CircleProperties_t *props = (CircleProperties_t*) data;
+
+ props->obj = obj;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), circle->x);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), circle->y);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->r), circle->r);
+}
+
+static void
+circle_set_initial_focus(Object_t *obj, gpointer data)
+{
+ CircleProperties_t *props = (CircleProperties_t*) data;
+ gtk_widget_grab_focus(props->x);
+}
+
+static void
+circle_update(Object_t* obj, gpointer data)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ CircleProperties_t *props = (CircleProperties_t*) data;
+
+ circle->x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->x));
+ circle->y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->y));
+ circle->r = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->r));
+}
+
+static void
+circle_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ output(param, "\"circle\" coords=\"%d,%d,%d\"", circle->x, circle->y,
+ circle->r);
+}
+
+static void
+circle_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ output(param, "circ (%d,%d) %d", circle->x, circle->y, circle->r);
+}
+
+static void
+circle_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+ output(param, "circle %s %d,%d %d,%d", obj->url,
+ circle->x, circle->y, circle->x, circle->y + circle->r);
+}
+
+static const gchar*
+circle_get_stock_icon_name(void)
+{
+ return IMAP_STOCK_CIRCLE;
+}
+
+static gint _start_x, _start_y;
+
+static Object_t*
+circle_factory_create_object1(gint x, gint y)
+{
+ _start_x = x;
+ _start_y = y;
+ return create_circle(x, y, 0);
+}
+
+static void
+circle_factory_set_xy1(Object_t *obj, guint state, gint x, gint y)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+
+ circle->x = (_start_x + x) / 2;
+ circle->y = (_start_y + y) / 2;
+ x -= _start_x;
+ y -= _start_y;
+ circle->r = (gint) sqrt(x * x + y * y) / 2;
+
+ main_set_dimension(circle->r, circle->r);
+}
+
+static ObjectFactory_t circle_factory1 = {
+ NULL, /* Object pointer */
+ NULL, /* Finish func */
+ NULL, /* Cancel func */
+ circle_factory_create_object1,
+ circle_factory_set_xy1
+};
+
+static Object_t*
+circle_factory_create_object2(gint x, gint y)
+{
+ return create_circle(x, y, 0);
+}
+
+static void
+circle_factory_set_xy2(Object_t *obj, guint state, gint x, gint y)
+{
+ Circle_t *circle = ObjectToCircle(obj);
+
+ x -= circle->x;
+ y -= circle->y;
+ circle->r = (gint) sqrt(x * x + y * y);
+
+ main_set_dimension(circle->r, circle->r);
+}
+
+static ObjectFactory_t circle_factory2 = {
+ NULL, /* Object pointer */
+ NULL, /* Finish func */
+ NULL, /* Cancel func */
+ circle_factory_create_object2,
+ circle_factory_set_xy2
+};
+
+ObjectFactory_t*
+get_circle_factory(guint state)
+{
+ return (state & GDK_SHIFT_MASK) ? &circle_factory1 : &circle_factory2;
+}
diff --git a/plug-ins/imagemap/imap_circle.h b/plug-ins/imagemap/imap_circle.h
new file mode 100644
index 0000000..f56263a
--- /dev/null
+++ b/plug-ins/imagemap/imap_circle.h
@@ -0,0 +1,40 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_CIRCLE_H
+#define _IMAP_CIRCLE_H
+
+#include "imap_object.h"
+
+typedef struct {
+ Object_t obj;
+ gint x;
+ gint y;
+ gint r;
+} Circle_t;
+
+#define ObjectToCircle(obj) ((Circle_t*) (obj))
+
+Object_t *create_circle(gint x, gint y, gint r);
+ObjectFactory_t *get_circle_factory(guint state);
+
+#endif /* _IMAP_CIRCLE_H */
diff --git a/plug-ins/imagemap/imap_cmd_clear.c b/plug-ins/imagemap/imap_cmd_clear.c
new file mode 100644
index 0000000..676e8b8
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_clear.c
@@ -0,0 +1,72 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t clear_command_execute(Command_t *parent);
+
+static CommandClass_t clear_command_class = {
+ NULL, /* clear_command_destruct */
+ clear_command_execute,
+ NULL, /* clear_command_undo */
+ NULL /* clear_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} ClearCommand_t;
+
+Command_t*
+clear_command_new(ObjectList_t *list)
+{
+ ClearCommand_t *command = g_new(ClearCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Clear"), &clear_command_class);
+}
+
+static void
+remove_one_object(Object_t *obj, gpointer data)
+{
+ ClearCommand_t *command = (ClearCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ delete_command_new(command->list, obj));
+}
+
+static CmdExecuteValue_t
+clear_command_execute(Command_t *parent)
+{
+ ClearCommand_t *command = (ClearCommand_t*) parent;
+ gpointer id;
+
+ id = object_list_add_remove_cb(command->list, remove_one_object, command);
+ object_list_delete_selected(command->list);
+ object_list_remove_remove_cb(command->list, id);
+
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_copy.c b/plug-ins/imagemap/imap_cmd_copy.c
new file mode 100644
index 0000000..453a5ee
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_copy.c
@@ -0,0 +1,71 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t copy_command_execute(Command_t *parent);
+static void copy_command_undo(Command_t *parent);
+
+static CommandClass_t copy_command_class = {
+ NULL, /* copy_command_destruct */
+ copy_command_execute,
+ copy_command_undo,
+ NULL /* copy_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ ObjectList_t *paste_buffer;
+} CopyCommand_t;
+
+Command_t*
+copy_command_new(ObjectList_t *list)
+{
+ CopyCommand_t *command = g_new(CopyCommand_t, 1);
+ command->list = list;
+ command->paste_buffer = NULL;
+ return command_init(&command->parent, _("Copy"), &copy_command_class);
+}
+
+static CmdExecuteValue_t
+copy_command_execute(Command_t *parent)
+{
+ CopyCommand_t *command = (CopyCommand_t*) parent;
+ command->paste_buffer = object_list_copy(command->paste_buffer,
+ get_paste_buffer());
+ object_list_copy_to_paste_buffer(command->list);
+ return CMD_APPEND;
+}
+
+static void
+copy_command_undo(Command_t *parent)
+{
+ CopyCommand_t *command = (CopyCommand_t*) parent;
+ object_list_copy(get_paste_buffer(), command->paste_buffer);
+}
diff --git a/plug-ins/imagemap/imap_cmd_copy_object.c b/plug-ins/imagemap/imap_cmd_copy_object.c
new file mode 100644
index 0000000..f59fd81
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_copy_object.c
@@ -0,0 +1,84 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void copy_object_command_destruct(Command_t *parent);
+static CmdExecuteValue_t copy_object_command_execute(Command_t *parent);
+static void copy_object_command_undo(Command_t *parent);
+
+static CommandClass_t copy_object_command_class = {
+ copy_object_command_destruct,
+ copy_object_command_execute,
+ copy_object_command_undo,
+ NULL /* copy_object_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Object_t *obj;
+ ObjectList_t *paste_buffer;
+} CopyObjectCommand_t;
+
+Command_t*
+copy_object_command_new(Object_t *obj)
+{
+ CopyObjectCommand_t *command = g_new(CopyObjectCommand_t, 1);
+ command->obj = object_ref(obj);
+ command->paste_buffer = NULL;
+ return command_init(&command->parent, _("Copy"),
+ &copy_object_command_class);
+}
+
+static void
+copy_object_command_destruct(Command_t *parent)
+{
+ CopyObjectCommand_t *command = (CopyObjectCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+copy_object_command_execute(Command_t *parent)
+{
+ CopyObjectCommand_t *command = (CopyObjectCommand_t*) parent;
+ ObjectList_t *paste_buffer = get_paste_buffer();
+
+ command->paste_buffer = object_list_copy(command->paste_buffer,
+ paste_buffer);
+ clear_paste_buffer();
+ object_list_append(paste_buffer, object_clone(command->obj));
+
+ return CMD_APPEND;
+}
+
+static void
+copy_object_command_undo(Command_t *parent)
+{
+ CopyObjectCommand_t *command = (CopyObjectCommand_t*) parent;
+ object_list_copy(get_paste_buffer(), command->paste_buffer);
+}
diff --git a/plug-ins/imagemap/imap_cmd_create.c b/plug-ins/imagemap/imap_cmd_create.c
new file mode 100644
index 0000000..49719d5
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_create.c
@@ -0,0 +1,82 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t create_command_execute(Command_t *parent);
+static void create_command_destruct(Command_t *parent);
+static void create_command_undo(Command_t *parent);
+
+static CommandClass_t create_command_class = {
+ create_command_destruct,
+ create_command_execute,
+ create_command_undo,
+ NULL /* create_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ Object_t *obj;
+ gboolean changed;
+} CreateCommand_t;
+
+Command_t*
+create_command_new(ObjectList_t *list, Object_t *obj)
+{
+ CreateCommand_t *command = g_new(CreateCommand_t, 1);
+ command->list = list;
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Create"), &create_command_class);
+}
+
+static void
+create_command_destruct(Command_t *parent)
+{
+ CreateCommand_t *command = (CreateCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+create_command_execute(Command_t *parent)
+{
+ CreateCommand_t *command = (CreateCommand_t*) parent;
+ command->changed = object_list_get_changed(command->list);
+ object_list_append(command->list, object_ref(command->obj));
+ return CMD_APPEND;
+}
+
+static void
+create_command_undo(Command_t *parent)
+{
+ CreateCommand_t *command = (CreateCommand_t*) parent;
+ object_list_remove(command->list, command->obj);
+ object_list_set_changed(command->list, command->changed);
+}
diff --git a/plug-ins/imagemap/imap_cmd_cut.c b/plug-ins/imagemap/imap_cmd_cut.c
new file mode 100644
index 0000000..0bf06c1
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_cut.c
@@ -0,0 +1,92 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void cut_command_destruct(Command_t *parent);
+static CmdExecuteValue_t cut_command_execute(Command_t *parent);
+static void cut_command_undo(Command_t *parent);
+
+static CommandClass_t cut_command_class = {
+ cut_command_destruct,
+ cut_command_execute,
+ cut_command_undo,
+ NULL /* cut_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ ObjectList_t *paste_buffer;
+} CutCommand_t;
+
+Command_t*
+cut_command_new(ObjectList_t *list)
+{
+ CutCommand_t *command = g_new(CutCommand_t, 1);
+ command->list = list;
+ command->paste_buffer = NULL;
+ return command_init(&command->parent, _("Cut"), &cut_command_class);
+}
+
+static void
+cut_command_destruct(Command_t *parent)
+{
+ CutCommand_t *command = (CutCommand_t*) parent;
+ object_list_destruct(command->paste_buffer);
+}
+
+static void
+remove_one_object(Object_t *obj, gpointer data)
+{
+ CutCommand_t *command = (CutCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ delete_command_new(command->list, obj));
+}
+
+static CmdExecuteValue_t
+cut_command_execute(Command_t *parent)
+{
+ CutCommand_t *command = (CutCommand_t*) parent;
+ gpointer id;
+
+ command->paste_buffer = object_list_copy(command->paste_buffer,
+ get_paste_buffer());
+ id = object_list_add_remove_cb(command->list, remove_one_object, command);
+ object_list_cut(command->list);
+ object_list_remove_remove_cb(command->list, id);
+
+ return CMD_APPEND;
+}
+
+static void
+cut_command_undo(Command_t *parent)
+{
+ CutCommand_t *command = (CutCommand_t*) parent;
+ object_list_copy(get_paste_buffer(), command->paste_buffer);
+}
diff --git a/plug-ins/imagemap/imap_cmd_cut_object.c b/plug-ins/imagemap/imap_cmd_cut_object.c
new file mode 100644
index 0000000..780c4f8
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_cut_object.c
@@ -0,0 +1,62 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t cut_object_command_execute(Command_t *parent);
+
+static CommandClass_t cut_object_command_class = {
+ NULL, /* cut_object_command_destruct */
+ cut_object_command_execute,
+ NULL, /* cut_object_command_undo */
+ NULL /* cut_object_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+} CutObjectCommand_t;
+
+Command_t*
+cut_object_command_new(Object_t *obj)
+{
+ CutObjectCommand_t *command = g_new(CutObjectCommand_t, 1);
+ Command_t *parent;
+
+ parent = command_init(&command->parent, _("Cut"),
+ &cut_object_command_class);
+ command_add_subcommand(parent, copy_object_command_new(obj));
+ command_add_subcommand(parent, delete_command_new(obj->list, obj));
+
+ return parent;
+}
+
+static CmdExecuteValue_t
+cut_object_command_execute(Command_t *parent)
+{
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_delete.c b/plug-ins/imagemap/imap_cmd_delete.c
new file mode 100644
index 0000000..6c85984
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_delete.c
@@ -0,0 +1,83 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void delete_command_destruct(Command_t *parent);
+static CmdExecuteValue_t delete_command_execute(Command_t *parent);
+static void delete_command_undo(Command_t *parent);
+
+static CommandClass_t delete_command_class = {
+ delete_command_destruct,
+ delete_command_execute,
+ delete_command_undo,
+ NULL /* delete_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ Object_t *obj;
+ gint position;
+ gboolean changed;
+} DeleteCommand_t;
+
+Command_t*
+delete_command_new(ObjectList_t *list, Object_t *obj)
+{
+ DeleteCommand_t *command = g_new(DeleteCommand_t, 1);
+ command->list = list;
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Delete"),
+ &delete_command_class);
+}
+
+static void
+delete_command_destruct(Command_t *parent)
+{
+ DeleteCommand_t *command = (DeleteCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+delete_command_execute(Command_t *parent)
+{
+ DeleteCommand_t *command = (DeleteCommand_t*) parent;
+ command->changed = object_list_get_changed(command->list);
+ command->position = object_get_position_in_list(command->obj);
+ object_list_remove(command->list, command->obj);
+ return CMD_APPEND;
+}
+
+static void
+delete_command_undo(Command_t *parent)
+{
+ DeleteCommand_t *command = (DeleteCommand_t*) parent;
+ object_list_insert(command->list, command->position, command->obj);
+ object_list_set_changed(command->list, command->changed);
+}
diff --git a/plug-ins/imagemap/imap_cmd_delete_point.c b/plug-ins/imagemap/imap_cmd_delete_point.c
new file mode 100644
index 0000000..1dd6092
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_delete_point.c
@@ -0,0 +1,86 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_polygon.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t delete_point_command_execute(Command_t *parent);
+static void delete_point_command_undo(Command_t *parent);
+
+static CommandClass_t delete_point_command_class = {
+ NULL, /* delete_point_command_destruct */
+ delete_point_command_execute,
+ delete_point_command_undo,
+ NULL /* delete_point_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Polygon_t *polygon;
+ GdkPoint *point;
+ GdkPoint copy;
+ gint position;
+} DeletePointCommand_t;
+
+Command_t*
+delete_point_command_new(Object_t *obj, GdkPoint *point)
+{
+ DeletePointCommand_t *command = g_new(DeletePointCommand_t, 1);
+
+ command->polygon = ObjectToPolygon(obj);
+ command->point = point;
+ command->copy = *point;
+ command->position = g_list_index(command->polygon->points,
+ (gpointer) point);
+ return command_init(&command->parent, _("Delete Point"),
+ &delete_point_command_class);
+}
+
+static CmdExecuteValue_t
+delete_point_command_execute(Command_t *parent)
+{
+ DeletePointCommand_t *command = (DeletePointCommand_t*) parent;
+ Polygon_t *polygon = command->polygon;
+ GList *p = g_list_find(polygon->points, (gpointer) command->point);
+
+ g_free(p->data);
+ polygon->points = g_list_remove_link(polygon->points, p);
+ return CMD_APPEND;
+}
+
+static void
+delete_point_command_undo(Command_t *parent)
+{
+ DeletePointCommand_t *command = (DeletePointCommand_t*) parent;
+ Polygon_t *polygon = command->polygon;
+ GdkPoint *point = &command->copy;
+
+ command->point = new_point(point->x, point->y);
+ polygon->points = g_list_insert(polygon->points, (gpointer) command->point,
+ command->position);
+}
diff --git a/plug-ins/imagemap/imap_cmd_edit_object.c b/plug-ins/imagemap/imap_cmd_edit_object.c
new file mode 100644
index 0000000..d2f0c39
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_edit_object.c
@@ -0,0 +1,73 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void edit_object_command_destruct(Command_t *parent);
+static void edit_object_command_undo(Command_t *parent);
+
+static CommandClass_t edit_object_command_class = {
+ edit_object_command_destruct,
+ NULL, /* edit_object_command_execute */
+ edit_object_command_undo,
+ edit_object_command_undo
+};
+
+typedef struct {
+ Command_t parent;
+ Object_t *obj;
+ Object_t *copy;
+} EditObjectCommand_t;
+
+Command_t*
+edit_object_command_new(Object_t *obj)
+{
+ EditObjectCommand_t *command = g_new(EditObjectCommand_t, 1);
+ command->obj = object_ref(obj);
+ command->copy = object_clone(obj);
+ return command_init(&command->parent, _("Edit Object"),
+ &edit_object_command_class);
+}
+
+static void
+edit_object_command_destruct(Command_t *parent)
+{
+ EditObjectCommand_t *command = (EditObjectCommand_t*) parent;
+ object_unref(command->copy);
+ object_unref(command->obj);
+}
+
+static void
+edit_object_command_undo(Command_t *parent)
+{
+ EditObjectCommand_t *command = (EditObjectCommand_t*) parent;
+ Object_t *copy = object_clone(command->obj);
+
+ object_assign(command->copy, command->obj);
+ object_assign(copy, command->copy);
+}
diff --git a/plug-ins/imagemap/imap_cmd_gimp_guides.c b/plug-ins/imagemap/imap_cmd_gimp_guides.c
new file mode 100644
index 0000000..3f8fdd2
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_gimp_guides.c
@@ -0,0 +1,260 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimp/gimp.h"
+#include "libgimpwidgets/gimpwidgets.h"
+
+#include "imap_commands.h"
+#include "imap_default_dialog.h"
+#include "imap_main.h"
+#include "imap_rectangle.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+typedef struct {
+ DefaultDialog_t *dialog;
+
+ ObjectList_t *list;
+ gint32 drawable_id;
+
+ GtkWidget *alternate;
+ GtkWidget *all;
+ GtkWidget *left_border;
+ GtkWidget *right_border;
+ GtkWidget *upper_border;
+ GtkWidget *lower_border;
+ GtkWidget *url;
+} GimpGuidesDialog_t;
+
+static gint
+guide_sort_func(gconstpointer a, gconstpointer b)
+{
+ return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+}
+
+static void
+gimp_guides_ok_cb(gpointer data)
+{
+ GimpGuidesDialog_t *param = (GimpGuidesDialog_t*) data;
+ gint guide_num;
+ GSList *hguides, *hg;
+ GSList *vguides, *vg;
+ gboolean all;
+ const gchar *url;
+ gint32 image_ID = gimp_item_get_image (param->drawable_id);
+
+ /* First get some dialog values */
+
+ all = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->all));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->left_border)))
+ vguides = g_slist_append(NULL, GINT_TO_POINTER(0));
+ else
+ vguides = NULL;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->right_border)))
+ vguides = g_slist_append(vguides,
+ GINT_TO_POINTER(gimp_image_width(image_ID)));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->upper_border)))
+ hguides = g_slist_append(NULL, GINT_TO_POINTER(0));
+ else
+ hguides = NULL;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->lower_border)))
+ hguides = g_slist_append(hguides,
+ GINT_TO_POINTER(gimp_image_height(image_ID)));
+
+ url = gtk_entry_get_text(GTK_ENTRY(param->url));
+
+ /* Next get all the GIMP guides */
+
+ guide_num = gimp_image_find_next_guide(image_ID, 0);
+
+ while (guide_num > 0) {
+ gint position = gimp_image_get_guide_position(image_ID, guide_num);
+
+ if (gimp_image_get_guide_orientation(image_ID, guide_num)
+ == GIMP_ORIENTATION_HORIZONTAL) {
+ hguides = g_slist_insert_sorted(hguides, GINT_TO_POINTER(position),
+ guide_sort_func);
+ } else { /* GIMP_ORIENTATION_VERTICAL */
+ vguides = g_slist_insert_sorted(vguides, GINT_TO_POINTER(position),
+ guide_sort_func);
+ }
+ guide_num = gimp_image_find_next_guide(image_ID, guide_num);
+ }
+
+ /* Create the areas */
+
+ subcommand_start(_("Use Gimp Guides"));
+
+ for (hg = hguides; hg && hg->next;
+ hg = (all) ? hg->next : hg->next->next) {
+ gint y = GPOINTER_TO_INT(hg->data);
+ gint height = GPOINTER_TO_INT(hg->next->data) - y;
+ for (vg = vguides; vg && vg->next;
+ vg = (all) ? vg->next : vg->next->next) {
+ gint x = GPOINTER_TO_INT(vg->data);
+ gint width = GPOINTER_TO_INT(vg->next->data) - x;
+ Object_t *obj = create_rectangle(x, y, width, height);
+ Command_t *command = create_command_new(param->list, obj);
+
+ object_set_url(obj, url);
+ command_execute(command);
+ }
+ }
+
+ subcommand_end();
+ preview_redraw();
+}
+
+static GimpGuidesDialog_t*
+make_gimp_guides_dialog(void)
+{
+ GimpGuidesDialog_t *data = g_new(GimpGuidesDialog_t, 1);
+ DefaultDialog_t *dialog;
+ GtkWidget *table, *frame, *hbox, *vbox;
+ GtkWidget *label;
+
+ dialog = data->dialog = make_default_dialog(_("Use Gimp Guides"));
+ default_dialog_set_ok_cb(dialog, gimp_guides_ok_cb, data);
+ table = default_dialog_add_table(dialog, 3, 2);
+
+ frame = gimp_frame_new(_("Create"));
+ gtk_widget_show(frame);
+ gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 1, 0, 1);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+
+ data->alternate =
+ gtk_radio_button_new_with_mnemonic_from_widget(NULL, _("Al_ternate"));
+ gtk_box_pack_start(GTK_BOX(hbox), data->alternate, FALSE, FALSE, 0);
+ gtk_widget_show(data->alternate);
+
+ data->all = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(data->alternate), _("A_ll"));
+ gtk_box_pack_start(GTK_BOX(hbox), data->all, FALSE, FALSE, 0);
+ gtk_widget_show(data->all);
+
+ frame = gimp_frame_new(_("Add Additional Guides"));
+ gtk_widget_show(frame);
+ gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 1, 1, 2);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+
+ data->left_border = gtk_check_button_new_with_mnemonic(_("L_eft border"));
+ gtk_container_add(GTK_CONTAINER(vbox), data->left_border);
+ gtk_widget_show(data->left_border);
+
+ data->right_border = gtk_check_button_new_with_mnemonic(_("_Right border"));
+ gtk_container_add(GTK_CONTAINER(vbox), data->right_border);
+ gtk_widget_show(data->right_border);
+
+ data->upper_border = gtk_check_button_new_with_mnemonic(_("_Upper border"));
+ gtk_container_add(GTK_CONTAINER(vbox), data->upper_border);
+ gtk_widget_show(data->upper_border);
+
+ data->lower_border = gtk_check_button_new_with_mnemonic(_("Lo_wer border"));
+ gtk_container_add(GTK_CONTAINER(vbox), data->lower_border);
+ gtk_widget_show(data->lower_border);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_table_attach_defaults(GTK_TABLE(table), hbox, 0, 2, 2, 3);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new_with_mnemonic(_("_Base URL:"));
+ gtk_widget_show(label);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ data->url = gtk_entry_new();
+ gtk_container_add(GTK_CONTAINER(hbox), data->url);
+ gtk_widget_show(data->url);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), data->url);
+
+ return data;
+}
+
+static void
+init_gimp_guides_dialog(GimpGuidesDialog_t *dialog, ObjectList_t *list,
+ gint32 drawable_id)
+{
+ dialog->list = list;
+ dialog->drawable_id = drawable_id;
+}
+
+static void
+do_create_gimp_guides_dialog(ObjectList_t *list, gint32 drawable_id)
+{
+ static GimpGuidesDialog_t *dialog;
+
+ if (!dialog)
+ dialog = make_gimp_guides_dialog();
+
+ init_gimp_guides_dialog(dialog, list, drawable_id);
+ default_dialog_show(dialog->dialog);
+}
+
+static CmdExecuteValue_t gimp_guides_command_execute(Command_t *parent);
+
+static CommandClass_t gimp_guides_command_class = {
+ NULL, /* guides_command_destruct */
+ gimp_guides_command_execute,
+ NULL, /* guides_command_undo */
+ NULL /* guides_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ gint32 drawable_id;
+} GimpGuidesCommand_t;
+
+Command_t*
+gimp_guides_command_new(ObjectList_t *list, gint32 drawable_id)
+{
+ GimpGuidesCommand_t *command = g_new(GimpGuidesCommand_t, 1);
+ command->list = list;
+ command->drawable_id = drawable_id;
+ return command_init(&command->parent, _("Use Gimp Guides"),
+ &gimp_guides_command_class);
+}
+
+static CmdExecuteValue_t
+gimp_guides_command_execute(Command_t *parent)
+{
+ GimpGuidesCommand_t *command = (GimpGuidesCommand_t*) parent;
+ do_create_gimp_guides_dialog(command->list, command->drawable_id);
+ return CMD_DESTRUCT;
+}
diff --git a/plug-ins/imagemap/imap_cmd_guides.c b/plug-ins/imagemap/imap_cmd_guides.c
new file mode 100644
index 0000000..aa9667f
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_guides.c
@@ -0,0 +1,279 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_commands.h"
+#include "imap_default_dialog.h"
+#include "imap_main.h"
+#include "imap_rectangle.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+typedef struct {
+ DefaultDialog_t *dialog;
+ GtkWidget *image_dimensions;
+ GtkWidget *guide_bounds;
+ GtkWidget *width;
+ GtkWidget *height;
+ GtkWidget *left;
+ GtkWidget *top;
+ GtkWidget *horz_spacing;
+ GtkWidget *vert_spacing;
+ GtkWidget *no_across;
+ GtkWidget *no_down;
+ GtkWidget *base_url;
+
+ ObjectList_t *list;
+} GuidesDialog_t;
+
+static void
+guides_ok_cb(gpointer data)
+{
+ GuidesDialog_t *param = (GuidesDialog_t*) data;
+ gint y;
+ int i, j;
+ gint width, height, left, top, hspace, vspace, rows, cols;
+
+ width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->width));
+ height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->height));
+ left = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->left));
+ top = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->top));
+ hspace = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->horz_spacing));
+ vspace = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->vert_spacing));
+ rows = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->no_down));
+ cols = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->no_across));
+
+ subcommand_start(_("Create Guides"));
+ y = top;
+ for (i = 0; i < rows; i++) {
+ gint x = left;
+ for (j = 0; j < cols; j++) {
+ Object_t *obj = create_rectangle(x, y, width, height);
+ Command_t *command = create_command_new(param->list, obj);
+
+ object_set_url(obj, gtk_entry_get_text(GTK_ENTRY(param->base_url)));
+ command_execute(command);
+ x += width + hspace;
+ }
+ y += height + vspace;
+ }
+ subcommand_end();
+}
+
+static void
+recalc_bounds(GtkWidget *widget, gpointer data)
+{
+ GuidesDialog_t *param = (GuidesDialog_t*) data;
+ gint width, height, left, top, hspace, vspace, rows, cols;
+ gint bound_w, bound_h;
+ gchar *bounds;
+
+ width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->width));
+ height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->height));
+ left = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->left));
+ top = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->top));
+ hspace = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->horz_spacing));
+ vspace = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->vert_spacing));
+ rows = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->no_down));
+ cols = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->no_across));
+
+ bound_w = (width + hspace) * cols - hspace;
+ bound_h = (height + vspace) * rows - vspace;
+
+ bounds = g_strdup_printf (_("Resulting Guide Bounds: %d,%d to %d,%d (%d areas)"),
+ left, top, left + bound_w, top + bound_h, rows * cols);
+ if (left + bound_w > get_image_width() ||
+ top + bound_h > get_image_height())
+ {
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (param->dialog->dialog),
+ GTK_RESPONSE_OK, FALSE);
+ }
+ else
+ {
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (param->dialog->dialog),
+ GTK_RESPONSE_OK, TRUE);
+ }
+
+ gtk_label_set_text(GTK_LABEL(param->guide_bounds), bounds);
+ g_free (bounds);
+}
+
+static GuidesDialog_t*
+make_guides_dialog (void)
+{
+ GuidesDialog_t *data = g_new(GuidesDialog_t, 1);
+ DefaultDialog_t *dialog;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *hbox;
+
+ dialog = data->dialog = make_default_dialog(_("Create Guides"));
+ default_dialog_set_ok_cb (dialog, guides_ok_cb, data);
+
+ hbox = gimp_hint_box_new (
+ _("Guides are pre-defined rectangles covering the image. You define "
+ "them by their width, height, and spacing from each other. This "
+ "allows you to rapidly create the most common image map type - "
+ "image collection of \"thumbnails\", suitable for navigation bars."));
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ data->image_dimensions = gtk_label_new ("");
+ gtk_label_set_xalign (GTK_LABEL (data->image_dimensions), 0.0);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox),
+ data->image_dimensions, FALSE, FALSE, 0);
+ gtk_widget_show (data->image_dimensions);
+
+ data->guide_bounds = gtk_label_new ("");
+ gtk_label_set_xalign (GTK_LABEL (data->guide_bounds), 0.0);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox),
+ data->guide_bounds, FALSE, FALSE, 0);
+ gtk_widget_show (data->guide_bounds);
+
+ table = default_dialog_add_table (dialog, 4, 4);
+
+ label = create_label_in_table (table, 0, 0, _("_Width:"));
+ data->width = create_spin_button_in_table (table, label, 0, 1, 32, 1, 100);
+ g_signal_connect (data->width, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table(table, 0, 2, _("_Left start at:"));
+ data->left = create_spin_button_in_table (table, label, 0, 3, 0, 0, 100);
+ g_signal_connect (data->left, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table(table, 1, 0, _("_Height:"));
+ data->height = create_spin_button_in_table (table, label, 1, 1, 32, 1, 100);
+ g_signal_connect (data->height, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table (table, 1, 2, _("_Top start at:"));
+ data->top = create_spin_button_in_table (table, label, 1, 3, 0, 0, 100);
+ g_signal_connect (data->top, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table (table, 2, 0, _("_Horz. spacing:"));
+ data->horz_spacing = create_spin_button_in_table (table, label, 2, 1, 0, 0,
+ 100);
+ g_signal_connect (data->horz_spacing, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table(table, 2, 2, _("_No. across:"));
+ data->no_across = create_spin_button_in_table(table, label, 2, 3, 0, 0,
+ 100);
+ g_signal_connect (data->no_across, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table(table, 3, 0, _("_Vert. spacing:"));
+ data->vert_spacing = create_spin_button_in_table(table, label, 3, 1, 0, 0,
+ 100);
+ g_signal_connect (data->vert_spacing, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ label = create_label_in_table(table, 3, 2, _("No. _down:"));
+ data->no_down = create_spin_button_in_table (table, label, 3, 3, 0, 0, 100);
+ g_signal_connect (data->no_down, "changed",
+ G_CALLBACK(recalc_bounds), (gpointer) data);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ label = gtk_label_new_with_mnemonic(_("Base _URL:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ data->base_url = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), data->base_url, TRUE, TRUE, 0);
+ gtk_widget_show(data->base_url);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), data->base_url);
+
+ return data;
+}
+
+static void
+init_guides_dialog(GuidesDialog_t *dialog, ObjectList_t *list)
+{
+ gchar *dimension;
+
+ dialog->list = list;
+ dimension = g_strdup_printf (_("Image dimensions: %d × %d"),
+ get_image_width(),
+ get_image_height());
+ gtk_label_set_text (GTK_LABEL(dialog->image_dimensions), dimension);
+ g_free (dimension);
+ gtk_label_set_text (GTK_LABEL(dialog->guide_bounds),
+ _("Resulting Guide Bounds: 0,0 to 0,0 (0 areas)"));
+ gtk_widget_grab_focus (dialog->width);
+}
+
+static void
+do_create_guides_dialog_local (ObjectList_t *list)
+{
+ static GuidesDialog_t *dialog;
+
+ if (!dialog)
+ dialog = make_guides_dialog();
+
+ init_guides_dialog(dialog, list);
+ default_dialog_show(dialog->dialog);
+}
+
+static CmdExecuteValue_t guides_command_execute(Command_t *parent);
+
+static CommandClass_t guides_command_class = {
+ NULL, /* guides_command_destruct */
+ guides_command_execute,
+ NULL, /* guides_command_undo */
+ NULL /* guides_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} GuidesCommand_t;
+
+Command_t*
+guides_command_new(ObjectList_t *list)
+{
+ GuidesCommand_t *command = g_new(GuidesCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Guides"), &guides_command_class);
+}
+
+static CmdExecuteValue_t
+guides_command_execute(Command_t *parent)
+{
+ GuidesCommand_t *command = (GuidesCommand_t*) parent;
+ do_create_guides_dialog_local (command->list);
+ return CMD_DESTRUCT;
+}
diff --git a/plug-ins/imagemap/imap_cmd_insert_point.c b/plug-ins/imagemap/imap_cmd_insert_point.c
new file mode 100644
index 0000000..ad887d3
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_insert_point.c
@@ -0,0 +1,96 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t insert_point_command_execute(Command_t *parent);
+static void insert_point_command_undo(Command_t *parent);
+
+static CommandClass_t insert_point_command_class = {
+ NULL, /* insert_point_command_destruct */
+ insert_point_command_execute,
+ insert_point_command_undo,
+ NULL /* insert_point_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Polygon_t *polygon;
+ gint x;
+ gint y;
+ gint edge;
+ gint position;
+} InsertPointCommand_t;
+
+Command_t*
+insert_point_command_new(Object_t *obj, gint x, gint y, gint edge)
+{
+ InsertPointCommand_t *command = g_new(InsertPointCommand_t, 1);
+
+ command->polygon = ObjectToPolygon(obj);
+ command->x = x;
+ command->y = y;
+ command->edge = edge;
+ return command_init(&command->parent, _("Insert Point"),
+ &insert_point_command_class);
+}
+
+static CmdExecuteValue_t
+insert_point_command_execute(Command_t *parent)
+{
+ InsertPointCommand_t *command = (InsertPointCommand_t*) parent;
+ Polygon_t *polygon = command->polygon;
+
+ GdkPoint *point = new_point(command->x, command->y);
+
+ if (g_list_length(polygon->points) == command->edge - 1) {
+ polygon->points = g_list_append(polygon->points, (gpointer) point);
+ command->position = command->edge - 1;
+ } else {
+ polygon->points = g_list_insert(polygon->points, (gpointer) point,
+ command->edge);
+ command->position = command->edge;
+ }
+ preview_redraw();
+
+ return CMD_APPEND;
+}
+
+static void
+insert_point_command_undo(Command_t *parent)
+{
+ InsertPointCommand_t *command = (InsertPointCommand_t*) parent;
+ Polygon_t *polygon = command->polygon;
+ GList *p = g_list_nth(polygon->points, command->position);
+
+ g_free(p->data);
+ polygon->points = g_list_remove_link(polygon->points, p);
+ preview_redraw(); /* Fix me! */
+}
diff --git a/plug-ins/imagemap/imap_cmd_move.c b/plug-ins/imagemap/imap_cmd_move.c
new file mode 100644
index 0000000..491b12d
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move.c
@@ -0,0 +1,166 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "libgimp/gimp.h"
+
+#include "imap_commands.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void move_command_destruct(Command_t *parent);
+static CmdExecuteValue_t move_command_execute(Command_t *parent);
+
+static CommandClass_t move_command_class = {
+ move_command_destruct,
+ move_command_execute,
+ NULL, /* move_command_undo */
+ NULL /* move_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ PreferencesData_t *preferences;
+ Preview_t *preview;
+ Object_t *obj;
+ gint start_x;
+ gint start_y;
+ gint obj_start_x;
+ gint obj_start_y;
+ gint obj_x;
+ gint obj_y;
+ gint obj_width;
+ gint obj_height;
+
+ gint image_width;
+ gint image_height;
+
+ GdkCursorType cursor; /* Remember previous cursor */
+ gboolean moved_first_time;
+} MoveCommand_t;
+
+Command_t*
+move_command_new(Preview_t *preview, Object_t *obj, gint x, gint y)
+{
+ MoveCommand_t *command = g_new(MoveCommand_t, 1);
+
+ command->preferences = get_preferences();
+ command->preview = preview;
+ command->obj = object_ref(obj);
+ command->start_x = x;
+ command->start_y = y;
+ object_get_dimensions(obj, &command->obj_x, &command->obj_y,
+ &command->obj_width, &command->obj_height);
+ command->obj_start_x = command->obj_x;
+ command->obj_start_y = command->obj_y;
+
+ command->image_width = get_image_width();
+ command->image_height = get_image_height();
+
+ command->moved_first_time = TRUE;
+
+ return command_init(&command->parent, _("Move"), &move_command_class);
+}
+
+static void
+move_command_destruct(Command_t *parent)
+{
+ MoveCommand_t *command = (MoveCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static void
+button_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+ MoveCommand_t *command = (MoveCommand_t*) data;
+ Object_t *obj = command->obj;
+ gint dx = get_real_coord((gint) event->x) - command->start_x;
+ gint dy = get_real_coord((gint) event->y) - command->start_y;
+
+ if (command->moved_first_time) {
+ command->moved_first_time = FALSE;
+ command->cursor = preview_set_cursor(command->preview, GDK_FLEUR);
+ hide_url();
+ }
+
+ if (command->obj_x + dx < 0)
+ dx = -command->obj_x;
+ if (command->obj_x + command->obj_width + dx > command->image_width)
+ dx = command->image_width - command->obj_width - command->obj_x;
+ if (command->obj_y + dy < 0)
+ dy = -command->obj_y;
+ if (command->obj_y + command->obj_height + dy > command->image_height)
+ dy = command->image_height - command->obj_height - command->obj_y;
+
+ if (dx || dy) {
+
+ command->start_x = get_real_coord((gint) event->x);
+ command->start_y = get_real_coord((gint) event->y);
+ command->obj_x += dx;
+ command->obj_y += dy;
+
+ object_move(obj, dx, dy);
+
+ preview_redraw ();
+ }
+}
+
+static void
+button_release(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ MoveCommand_t *command = (MoveCommand_t*) data;
+
+ g_signal_handlers_disconnect_by_func (widget,
+ button_motion, data);
+ g_signal_handlers_disconnect_by_func (widget,
+ button_release, data);
+
+ if (!command->moved_first_time) {
+ preview_set_cursor(command->preview, command->cursor);
+ show_url();
+ }
+ command->obj_x -= command->obj_start_x;
+ command->obj_y -= command->obj_start_y;
+ if (command->obj_x || command->obj_y)
+ command_list_add(object_move_command_new(command->obj, command->obj_x,
+ command->obj_y));
+
+ /* preview_thaw(); */
+}
+
+static CmdExecuteValue_t
+move_command_execute(Command_t *parent)
+{
+ MoveCommand_t *command = (MoveCommand_t*) parent;
+ GtkWidget *widget = command->preview->preview;
+
+ /* preview_freeze(); */
+ g_signal_connect(widget, "button-release-event",
+ G_CALLBACK (button_release), command);
+ g_signal_connect(widget, "motion-notify-event",
+ G_CALLBACK (button_motion), command);
+ return CMD_DESTRUCT;
+}
diff --git a/plug-ins/imagemap/imap_cmd_move_down.c b/plug-ins/imagemap/imap_cmd_move_down.c
new file mode 100644
index 0000000..401e597
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move_down.c
@@ -0,0 +1,82 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t move_down_command_execute(Command_t *parent);
+
+static CommandClass_t move_down_command_class = {
+ NULL, /* move_down_command_destruct */
+ move_down_command_execute,
+ NULL, /* move_down_command_undo */
+ NULL /* move_down_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ gboolean add;
+} MoveDownCommand_t;
+
+Command_t*
+move_down_command_new(ObjectList_t *list)
+{
+ MoveDownCommand_t *command = g_new(MoveDownCommand_t, 1);
+ command->list = list;
+ command->add = FALSE;
+ return command_init(&command->parent, _("Move Down"),
+ &move_down_command_class);
+}
+
+static void
+move_down_one_object(Object_t *obj, gpointer data)
+{
+ MoveDownCommand_t *command = (MoveDownCommand_t*) data;
+
+ if (command->add) {
+ command_add_subcommand(&command->parent,
+ object_down_command_new(command->list, obj));
+ command->add = FALSE;
+ }
+ else {
+ command->add = TRUE;
+ }
+}
+
+static CmdExecuteValue_t
+move_down_command_execute(Command_t *parent)
+{
+ MoveDownCommand_t *command = (MoveDownCommand_t*) parent;
+ gpointer id;
+
+ id = object_list_add_move_cb(command->list, move_down_one_object, command);
+ object_list_move_selected_down(command->list);
+ object_list_remove_move_cb(command->list, id);
+
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_move_sash.c b/plug-ins/imagemap/imap_cmd_move_sash.c
new file mode 100644
index 0000000..85c1db8
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move_sash.c
@@ -0,0 +1,150 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void move_sash_command_destruct(Command_t *command);
+static CmdExecuteValue_t move_sash_command_execute(Command_t *command);
+static void move_sash_command_redo(Command_t *command);
+
+static CommandClass_t move_sash_command_class = {
+ move_sash_command_destruct,
+ move_sash_command_execute,
+ NULL /*undo*/,
+ move_sash_command_redo
+};
+
+typedef struct {
+ Command_t parent;
+ GtkWidget *widget;
+ Object_t *obj;
+ gint x;
+ gint y;
+ gint image_width;
+ gint image_height;
+ MoveSashFunc_t sash_func;
+} MoveSashCommand_t;
+
+Command_t*
+move_sash_command_new(GtkWidget *widget, Object_t *obj,
+ gint x, gint y, MoveSashFunc_t sash_func)
+{
+ MoveSashCommand_t *command = g_new(MoveSashCommand_t, 1);
+ Command_t *parent;
+
+ command->widget = widget;
+ command->obj = object_ref(obj);
+ command->x = x;
+ command->y = y;
+ command->image_width = get_image_width();
+ command->image_height = get_image_height();
+ command->sash_func = sash_func;
+
+ parent = command_init(&command->parent, _("Move Sash"),
+ &move_sash_command_class);
+ command_add_subcommand(parent, edit_object_command_new(obj));
+
+ return parent;
+}
+
+static void
+move_sash_command_destruct(Command_t *parent)
+{
+ MoveSashCommand_t *command = (MoveSashCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static void
+sash_move(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+ MoveSashCommand_t *command = (MoveSashCommand_t*) data;
+ Object_t *obj = command->obj;
+ gint x, y, dx, dy;
+
+ x = get_real_coord((gint) event->x);
+ y = get_real_coord((gint) event->y);
+
+ if (x < 0)
+ x = 0;
+ if (x > command->image_width)
+ x = command->image_width;
+
+ if (y < 0)
+ y = 0;
+ if (y > command->image_height)
+ y = command->image_height;
+
+ dx = x - command->x;
+ dy = y - command->y;
+
+ command->x = x;
+ command->y = y;
+
+ command->sash_func(obj, dx, dy);
+ object_emit_geometry_signal(obj);
+
+ preview_redraw ();
+}
+
+static void
+sash_end(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ MoveSashCommand_t *command = (MoveSashCommand_t*) data;
+ Object_t *obj = command->obj;
+
+ g_signal_handlers_disconnect_by_func(widget,
+ sash_move, data);
+ g_signal_handlers_disconnect_by_func(widget,
+ sash_end, data);
+ if (obj->class->normalize)
+ object_normalize(obj);
+ preview_unset_tmp_obj(command->obj);
+ preview_redraw();
+ show_url();
+}
+
+static CmdExecuteValue_t
+move_sash_command_execute(Command_t *parent)
+{
+ MoveSashCommand_t *command = (MoveSashCommand_t*) parent;
+
+ hide_url();
+ g_signal_connect(command->widget, "button-release-event",
+ G_CALLBACK (sash_end), command);
+ g_signal_connect(command->widget, "motion-notify-event",
+ G_CALLBACK (sash_move), command);
+ preview_set_tmp_obj(command->obj);
+
+ return CMD_APPEND;
+}
+
+static void move_sash_command_redo(Command_t *command)
+{
+ /* do nothing, but avoid running execute again which will break event handling */
+}
diff --git a/plug-ins/imagemap/imap_cmd_move_selected.c b/plug-ins/imagemap/imap_cmd_move_selected.c
new file mode 100644
index 0000000..690a86a
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move_selected.c
@@ -0,0 +1,72 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t move_selected_command_execute(Command_t *parent);
+static void move_selected_command_undo(Command_t *parent);
+
+static CommandClass_t move_selected_command_class = {
+ NULL, /* move_selected_command_destruct */
+ move_selected_command_execute,
+ move_selected_command_undo,
+ NULL /* move_selected_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ gint dx;
+ gint dy;
+} MoveSelectedCommand_t;
+
+Command_t*
+move_selected_command_new(ObjectList_t *list, gint dx, gint dy)
+{
+ MoveSelectedCommand_t *command = g_new(MoveSelectedCommand_t, 1);
+ command->list = list;
+ command->dx = dx;
+ command->dy = dy;
+ return command_init(&command->parent, _("Move Selected Objects"),
+ &move_selected_command_class);
+}
+
+static CmdExecuteValue_t
+move_selected_command_execute(Command_t *parent)
+{
+ MoveSelectedCommand_t *command = (MoveSelectedCommand_t*) parent;
+ object_list_move_selected(command->list, command->dx, command->dy);
+ return CMD_APPEND;
+}
+
+static void
+move_selected_command_undo(Command_t *parent)
+{
+ MoveSelectedCommand_t *command = (MoveSelectedCommand_t*) parent;
+ object_list_move_selected(command->list, -command->dx, -command->dy);
+}
diff --git a/plug-ins/imagemap/imap_cmd_move_to_front.c b/plug-ins/imagemap/imap_cmd_move_to_front.c
new file mode 100644
index 0000000..c8f81e1
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move_to_front.c
@@ -0,0 +1,83 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t move_to_front_command_execute(Command_t *parent);
+
+static CommandClass_t move_to_front_command_class = {
+ NULL, /* move_to_front_command_destruct, */
+ move_to_front_command_execute,
+ NULL, /* move_to_front_command_undo */
+ NULL /* move_to_front_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} MoveToFrontCommand_t;
+
+Command_t*
+move_to_front_command_new(ObjectList_t *list)
+{
+ MoveToFrontCommand_t *command = g_new(MoveToFrontCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Move To Front"),
+ &move_to_front_command_class);
+}
+
+static void
+remove_one_object(Object_t *obj, gpointer data)
+{
+ MoveToFrontCommand_t *command = (MoveToFrontCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ delete_command_new(command->list, obj));
+}
+
+static void
+add_one_object(Object_t *obj, gpointer data)
+{
+ MoveToFrontCommand_t *command = (MoveToFrontCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ create_command_new(command->list, obj));
+}
+
+static CmdExecuteValue_t
+move_to_front_command_execute(Command_t *parent)
+{
+ MoveToFrontCommand_t *command = (MoveToFrontCommand_t*) parent;
+ gpointer id1, id2;
+
+ id1 = object_list_add_remove_cb(command->list, remove_one_object, command);
+ id2 = object_list_add_add_cb(command->list, add_one_object, command);
+
+ object_list_move_to_front(command->list);
+ object_list_remove_remove_cb(command->list, id1);
+ object_list_remove_add_cb(command->list, id2);
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_move_up.c b/plug-ins/imagemap/imap_cmd_move_up.c
new file mode 100644
index 0000000..ed253a2
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_move_up.c
@@ -0,0 +1,81 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t move_up_command_execute(Command_t *parent);
+
+static CommandClass_t move_up_command_class = {
+ NULL, /* move_up_command_destruct */
+ move_up_command_execute,
+ NULL, /* move_up_command_undo */
+ NULL /* move_up_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ gboolean add;
+} MoveUpCommand_t;
+
+Command_t*
+move_up_command_new(ObjectList_t *list)
+{
+ MoveUpCommand_t *command = g_new(MoveUpCommand_t, 1);
+ command->list = list;
+ command->add = FALSE;
+ return command_init(&command->parent, _("Move Up"), &move_up_command_class);
+}
+
+static void
+move_up_one_object(Object_t *obj, gpointer data)
+{
+ MoveUpCommand_t *command = (MoveUpCommand_t*) data;
+
+ if (command->add) {
+ command_add_subcommand(&command->parent,
+ object_up_command_new(command->list, obj));
+ command->add = FALSE;
+ }
+ else {
+ command->add = TRUE;
+ }
+}
+
+static CmdExecuteValue_t
+move_up_command_execute(Command_t *parent)
+{
+ MoveUpCommand_t *command = (MoveUpCommand_t*) parent;
+ gpointer id;
+
+ id = object_list_add_move_cb(command->list, move_up_one_object, command);
+ object_list_move_selected_up(command->list);
+ object_list_remove_move_cb(command->list, id);
+
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_object_down.c b/plug-ins/imagemap/imap_cmd_object_down.c
new file mode 100644
index 0000000..2849b71
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_object_down.c
@@ -0,0 +1,78 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void object_down_command_destruct(Command_t *parent);
+static CmdExecuteValue_t object_down_command_execute(Command_t *parent);
+static void object_down_command_undo(Command_t *parent);
+
+static CommandClass_t object_down_command_class = {
+ object_down_command_destruct,
+ object_down_command_execute,
+ object_down_command_undo,
+ NULL /* object_down_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ Object_t *obj;
+} ObjectDownCommand_t;
+
+Command_t*
+object_down_command_new(ObjectList_t *list, Object_t *obj)
+{
+ ObjectDownCommand_t *command = g_new(ObjectDownCommand_t, 1);
+ command->list = list;
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Move Down"),
+ &object_down_command_class);
+}
+
+static void
+object_down_command_destruct(Command_t *parent)
+{
+ ObjectDownCommand_t *command = (ObjectDownCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+object_down_command_execute(Command_t *parent)
+{
+ ObjectDownCommand_t *command = (ObjectDownCommand_t*) parent;
+ object_list_move_down(command->list, command->obj);
+ return CMD_APPEND;
+}
+
+static void
+object_down_command_undo(Command_t *parent)
+{
+ ObjectDownCommand_t *command = (ObjectDownCommand_t*) parent;
+ object_list_move_up(command->list, command->obj);
+}
diff --git a/plug-ins/imagemap/imap_cmd_object_move.c b/plug-ins/imagemap/imap_cmd_object_move.c
new file mode 100644
index 0000000..39c575c
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_object_move.c
@@ -0,0 +1,80 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void object_move_command_destruct(Command_t *parent);
+static CmdExecuteValue_t object_move_command_execute(Command_t *parent);
+static void object_move_command_undo(Command_t *parent);
+
+static CommandClass_t object_move_command_class = {
+ object_move_command_destruct,
+ object_move_command_execute,
+ object_move_command_undo,
+ NULL /* object_move_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Object_t *obj;
+ gint dx;
+ gint dy;
+} ObjectMoveCommand_t;
+
+Command_t*
+object_move_command_new(Object_t *obj, gint dx, gint dy)
+{
+ ObjectMoveCommand_t *command = g_new(ObjectMoveCommand_t, 1);
+ command->obj = object_ref(obj);
+ command->dx = dx;
+ command->dy = dy;
+ return command_init(&command->parent, _("Move"),
+ &object_move_command_class);
+}
+
+static void
+object_move_command_destruct(Command_t *parent)
+{
+ ObjectMoveCommand_t *command = (ObjectMoveCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+object_move_command_execute(Command_t *parent)
+{
+ ObjectMoveCommand_t *command = (ObjectMoveCommand_t*) parent;
+ object_move(command->obj, command->dx, command->dy);
+ return CMD_APPEND;
+}
+
+static void
+object_move_command_undo(Command_t *parent)
+{
+ ObjectMoveCommand_t *command = (ObjectMoveCommand_t*) parent;
+ object_move(command->obj, -command->dx, -command->dy);
+}
diff --git a/plug-ins/imagemap/imap_cmd_object_up.c b/plug-ins/imagemap/imap_cmd_object_up.c
new file mode 100644
index 0000000..edc8f40
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_object_up.c
@@ -0,0 +1,78 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void object_up_command_destruct(Command_t *parent);
+static CmdExecuteValue_t object_up_command_execute(Command_t *parent);
+static void object_up_command_undo(Command_t *parent);
+
+static CommandClass_t object_up_command_class = {
+ object_up_command_destruct,
+ object_up_command_execute,
+ object_up_command_undo,
+ NULL /* object_up_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ Object_t *obj;
+} ObjectUpCommand_t;
+
+Command_t*
+object_up_command_new(ObjectList_t *list, Object_t *obj)
+{
+ ObjectUpCommand_t *command = g_new(ObjectUpCommand_t, 1);
+ command->list = list;
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Move Up"),
+ &object_up_command_class);
+}
+
+static void
+object_up_command_destruct(Command_t *parent)
+{
+ ObjectUpCommand_t *command = (ObjectUpCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+object_up_command_execute(Command_t *parent)
+{
+ ObjectUpCommand_t *command = (ObjectUpCommand_t*) parent;
+ object_list_move_up(command->list, command->obj);
+ return CMD_APPEND;
+}
+
+static void
+object_up_command_undo(Command_t *parent)
+{
+ ObjectUpCommand_t *command = (ObjectUpCommand_t*) parent;
+ object_list_move_down(command->list, command->obj);
+}
diff --git a/plug-ins/imagemap/imap_cmd_paste.c b/plug-ins/imagemap/imap_cmd_paste.c
new file mode 100644
index 0000000..5855548
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_paste.c
@@ -0,0 +1,71 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t paste_command_execute(Command_t *parent);
+
+static CommandClass_t paste_command_class = {
+ NULL, /* paste_command_destruct, */
+ paste_command_execute,
+ NULL, /* paste_command_undo */
+ NULL /* paste_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} PasteCommand_t;
+
+Command_t*
+paste_command_new(ObjectList_t *list)
+{
+ PasteCommand_t *command = g_new(PasteCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Paste"), &paste_command_class);
+}
+
+static void
+paste_one_object(Object_t *obj, gpointer data)
+{
+ PasteCommand_t *command = (PasteCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ create_command_new(command->list, obj));
+}
+
+static CmdExecuteValue_t
+paste_command_execute(Command_t *parent)
+{
+ PasteCommand_t *command = (PasteCommand_t*) parent;
+ gpointer id;
+
+ id = object_list_add_add_cb(command->list, paste_one_object, command);
+ object_list_paste(command->list);
+ object_list_remove_add_cb(command->list, id);
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_select.c b/plug-ins/imagemap/imap_cmd_select.c
new file mode 100644
index 0000000..cbfa38d
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_select.c
@@ -0,0 +1,75 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void select_command_destruct(Command_t *parent);
+static CmdExecuteValue_t select_command_execute(Command_t *parent);
+static void select_command_undo(Command_t *parent);
+
+static CommandClass_t select_command_class = {
+ select_command_destruct,
+ select_command_execute,
+ select_command_undo,
+ NULL /* select_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Object_t *obj;
+} SelectCommand_t;
+
+Command_t*
+select_command_new(Object_t *obj)
+{
+ SelectCommand_t *command = g_new(SelectCommand_t, 1);
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Select"), &select_command_class);
+}
+
+static void
+select_command_destruct(Command_t *parent)
+{
+ SelectCommand_t *command = (SelectCommand_t*) parent;
+ object_unref(command->obj);
+}
+
+static CmdExecuteValue_t
+select_command_execute(Command_t *parent)
+{
+ SelectCommand_t *command = (SelectCommand_t*) parent;
+ object_select(command->obj);
+ return CMD_APPEND;
+}
+
+static void
+select_command_undo(Command_t *parent)
+{
+ SelectCommand_t *command = (SelectCommand_t*) parent;
+ object_unselect(command->obj);
+}
diff --git a/plug-ins/imagemap/imap_cmd_select_all.c b/plug-ins/imagemap/imap_cmd_select_all.c
new file mode 100644
index 0000000..286db3e
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_select_all.c
@@ -0,0 +1,74 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t select_all_command_execute(Command_t *parent);
+
+static CommandClass_t select_all_command_class = {
+ NULL, /* select_all_command_destruct, */
+ select_all_command_execute,
+ NULL, /* select_all_command_undo */
+ NULL /* select_all_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} SelectAllCommand_t;
+
+Command_t*
+select_all_command_new(ObjectList_t *list)
+{
+ SelectAllCommand_t *command = g_new(SelectAllCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Select All"),
+ &select_all_command_class);
+}
+
+static void
+select_one_object(Object_t *obj, gpointer data)
+{
+ SelectAllCommand_t *command = (SelectAllCommand_t*) data;
+ command_add_subcommand(&command->parent, select_command_new(obj));
+}
+
+static CmdExecuteValue_t
+select_all_command_execute(Command_t *parent)
+{
+ SelectAllCommand_t *command = (SelectAllCommand_t*) parent;
+ gpointer id;
+ CmdExecuteValue_t rvalue;
+
+ id = object_list_add_select_cb(command->list, select_one_object, command);
+ rvalue = (object_list_select_all(command->list))
+ ? CMD_APPEND : CMD_DESTRUCT;
+ object_list_remove_select_cb(command->list, id);
+
+ return rvalue;
+}
diff --git a/plug-ins/imagemap/imap_cmd_select_next.c b/plug-ins/imagemap/imap_cmd_select_next.c
new file mode 100644
index 0000000..325cacb
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_select_next.c
@@ -0,0 +1,76 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t select_next_command_execute(Command_t *parent);
+
+static CommandClass_t select_next_command_class = {
+ NULL, /* select_next_command_destruct */
+ select_next_command_execute,
+ NULL, /* select_next_command_undo */
+ NULL /* select_next_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} SelectNextCommand_t;
+
+Command_t*
+select_next_command_new(ObjectList_t *list)
+{
+ SelectNextCommand_t *command = g_new(SelectNextCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Select Next"),
+ &select_next_command_class);
+}
+
+static void
+select_one_object(Object_t *obj, gpointer data)
+{
+ SelectNextCommand_t *command = (SelectNextCommand_t*) data;
+ Command_t *sub_command;
+
+ sub_command = (obj->selected)
+ ? select_command_new(obj) : unselect_command_new(obj);
+ command_add_subcommand(&command->parent, sub_command);
+}
+
+static CmdExecuteValue_t
+select_next_command_execute(Command_t *parent)
+{
+ SelectNextCommand_t *command = (SelectNextCommand_t*) parent;
+ ObjectList_t *list = command->list;
+ gpointer id;
+
+ id = object_list_add_select_cb(list, select_one_object, command);
+ object_list_select_next(list);
+ object_list_remove_select_cb(list, id);
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_select_prev.c b/plug-ins/imagemap/imap_cmd_select_prev.c
new file mode 100644
index 0000000..9f5b8eb
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_select_prev.c
@@ -0,0 +1,76 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t select_prev_command_execute(Command_t *parent);
+
+static CommandClass_t select_prev_command_class = {
+ NULL, /* select_prev_command_destruct */
+ select_prev_command_execute,
+ NULL, /* select_prev_command_undo */
+ NULL /* select_prev_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} SelectPrevCommand_t;
+
+Command_t*
+select_prev_command_new(ObjectList_t *list)
+{
+ SelectPrevCommand_t *command = g_new(SelectPrevCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Select Previous"),
+ &select_prev_command_class);
+}
+
+static void
+select_one_object(Object_t *obj, gpointer data)
+{
+ SelectPrevCommand_t *command = (SelectPrevCommand_t*) data;
+ Command_t *sub_command;
+
+ sub_command = (obj->selected)
+ ? select_command_new(obj) : unselect_command_new(obj);
+ command_add_subcommand(&command->parent, sub_command);
+}
+
+static CmdExecuteValue_t
+select_prev_command_execute(Command_t *parent)
+{
+ SelectPrevCommand_t *command = (SelectPrevCommand_t*) parent;
+ ObjectList_t *list = command->list;
+ gpointer id;
+
+ id = object_list_add_select_cb(list, select_one_object, command);
+ object_list_select_prev(list);
+ object_list_remove_select_cb(list, id);
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_select_region.c b/plug-ins/imagemap/imap_cmd_select_region.c
new file mode 100644
index 0000000..6e0029b
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_select_region.c
@@ -0,0 +1,142 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_rectangle.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t select_region_command_execute(Command_t *parent);
+
+static CommandClass_t select_region_command_class = {
+ NULL, /* select_region_command_destruct, */
+ select_region_command_execute,
+ NULL, /* select_region_command_undo */
+ NULL /* select_region_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ GtkWidget *widget;
+ ObjectList_t *list;
+ gint x;
+ gint y;
+ Object_t *obj;
+ Command_t *unselect_command;
+} SelectRegionCommand_t;
+
+Command_t*
+select_region_command_new(GtkWidget *widget, ObjectList_t *list, gint x,
+ gint y)
+{
+ SelectRegionCommand_t *command = g_new(SelectRegionCommand_t, 1);
+ Command_t *sub_command;
+
+ command->widget = widget;
+ command->list = list;
+ command->x = x;
+ command->y = y;
+ (void) command_init(&command->parent, _("Select Region"),
+ &select_region_command_class);
+
+ sub_command = unselect_all_command_new(list, NULL);
+ command_add_subcommand(&command->parent, sub_command);
+ command->unselect_command = sub_command;
+
+ return &command->parent;
+}
+
+static void
+select_one_object(Object_t *obj, gpointer data)
+{
+ SelectRegionCommand_t *command = (SelectRegionCommand_t*) data;
+ command_add_subcommand(&command->parent, select_command_new(obj));
+}
+
+static void
+select_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
+{
+ SelectRegionCommand_t *command = (SelectRegionCommand_t*) data;
+ Object_t *obj = command->obj;
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+
+ gint x = get_real_coord((gint) event->x);
+ gint y = get_real_coord((gint) event->y);
+
+ rectangle->width = x - rectangle->x;
+ rectangle->height = y - rectangle->y;
+
+ preview_redraw ();
+}
+
+static void
+select_release(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ SelectRegionCommand_t *command = (SelectRegionCommand_t*) data;
+ Object_t *obj = command->obj;
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ gpointer id;
+ gint count;
+
+ g_signal_handlers_disconnect_by_func(widget,
+ select_motion, data);
+ g_signal_handlers_disconnect_by_func(widget,
+ select_release, data);
+
+ object_normalize(obj);
+
+ id = object_list_add_select_cb(command->list, select_one_object, command);
+ count = object_list_select_region(command->list, rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+ object_list_remove_select_cb(command->list, id);
+
+ if (count) {
+ command_list_add(&command->parent);
+ } else { /* Nothing selected */
+ if (command->unselect_command->sub_commands)
+ command_list_add(&command->parent);
+ }
+ preview_unset_tmp_obj (command->obj);
+
+ preview_redraw ();
+}
+
+static CmdExecuteValue_t
+select_region_command_execute(Command_t *parent)
+{
+ SelectRegionCommand_t *command = (SelectRegionCommand_t*) parent;
+
+ command->obj = create_rectangle(command->x, command->y, 0, 0);
+ preview_set_tmp_obj (command->obj);
+
+ g_signal_connect(command->widget, "button-release-event",
+ G_CALLBACK (select_release), command);
+ g_signal_connect(command->widget, "motion-notify-event",
+ G_CALLBACK (select_motion), command);
+
+ return CMD_IGNORE;
+}
diff --git a/plug-ins/imagemap/imap_cmd_send_to_back.c b/plug-ins/imagemap/imap_cmd_send_to_back.c
new file mode 100644
index 0000000..99e2e1e
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_send_to_back.c
@@ -0,0 +1,83 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static CmdExecuteValue_t send_to_back_command_execute(Command_t *parent);
+
+static CommandClass_t send_to_back_command_class = {
+ NULL, /* send_to_back_command_destruct, */
+ send_to_back_command_execute,
+ NULL, /* send_to_back_command_undo */
+ NULL /* send_to_back_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+} SendToBackCommand_t;
+
+Command_t*
+send_to_back_command_new(ObjectList_t *list)
+{
+ SendToBackCommand_t *command = g_new(SendToBackCommand_t, 1);
+ command->list = list;
+ return command_init(&command->parent, _("Send To Back"),
+ &send_to_back_command_class);
+}
+
+static void
+remove_one_object(Object_t *obj, gpointer data)
+{
+ SendToBackCommand_t *command = (SendToBackCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ delete_command_new(command->list, obj));
+}
+
+static void
+add_one_object(Object_t *obj, gpointer data)
+{
+ SendToBackCommand_t *command = (SendToBackCommand_t*) data;
+ command_add_subcommand(&command->parent,
+ create_command_new(command->list, obj));
+}
+
+static CmdExecuteValue_t
+send_to_back_command_execute(Command_t *parent)
+{
+ SendToBackCommand_t *command = (SendToBackCommand_t*) parent;
+ gpointer id1, id2;
+
+ id1 = object_list_add_remove_cb(command->list, remove_one_object, command);
+ id2 = object_list_add_add_cb(command->list, add_one_object, command);
+
+ object_list_send_to_back(command->list);
+ object_list_remove_remove_cb(command->list, id1);
+ object_list_remove_add_cb(command->list, id2);
+ return CMD_APPEND;
+}
diff --git a/plug-ins/imagemap/imap_cmd_unselect.c b/plug-ins/imagemap/imap_cmd_unselect.c
new file mode 100644
index 0000000..23bac7c
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_unselect.c
@@ -0,0 +1,76 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void unselect_command_destruct(Command_t *parent);
+static CmdExecuteValue_t unselect_command_execute(Command_t *parent);
+static void unselect_command_undo(Command_t *parent);
+
+static CommandClass_t unselect_command_class = {
+ unselect_command_destruct,
+ unselect_command_execute,
+ unselect_command_undo,
+ NULL /* unselect_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ Object_t *obj;
+} UnselectCommand_t;
+
+Command_t*
+unselect_command_new(Object_t *obj)
+{
+ UnselectCommand_t *command = g_new(UnselectCommand_t, 1);
+ command->obj = object_ref(obj);
+ return command_init(&command->parent, _("Unselect"),
+ &unselect_command_class);
+}
+
+static void
+unselect_command_destruct(Command_t *command)
+{
+ UnselectCommand_t *unselect_command = (UnselectCommand_t*) command;
+ object_unref(unselect_command->obj);
+}
+
+static CmdExecuteValue_t
+unselect_command_execute(Command_t *command)
+{
+ UnselectCommand_t *unselect_command = (UnselectCommand_t*) command;
+ object_unselect(unselect_command->obj);
+ return CMD_APPEND;
+}
+
+static void
+unselect_command_undo(Command_t *command)
+{
+ UnselectCommand_t *unselect_command = (UnselectCommand_t*) command;
+ object_select(unselect_command->obj);
+}
diff --git a/plug-ins/imagemap/imap_cmd_unselect_all.c b/plug-ins/imagemap/imap_cmd_unselect_all.c
new file mode 100644
index 0000000..f1e45e4
--- /dev/null
+++ b/plug-ins/imagemap/imap_cmd_unselect_all.c
@@ -0,0 +1,90 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void unselect_all_command_destruct(Command_t *command);
+static CmdExecuteValue_t unselect_all_command_execute(Command_t *command);
+
+/* COMMAND_PROTO(unselect_all_command); */
+
+static CommandClass_t unselect_all_command_class = {
+ unselect_all_command_destruct,
+ unselect_all_command_execute,
+ NULL, /* unselect_all_command_undo */
+ NULL /* unselect_all_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ ObjectList_t *list;
+ Object_t *exception;
+} UnselectAllCommand_t;
+
+Command_t*
+unselect_all_command_new(ObjectList_t *list, Object_t *exception)
+{
+ UnselectAllCommand_t *command = g_new(UnselectAllCommand_t, 1);
+ command->list = list;
+ command->exception = (exception) ? object_ref(exception) : exception;
+ return command_init(&command->parent, _("Unselect All"),
+ &unselect_all_command_class);
+}
+
+static void
+unselect_all_command_destruct(Command_t *parent)
+{
+ UnselectAllCommand_t *command = (UnselectAllCommand_t*) parent;
+ if (command->exception)
+ object_unref(command->exception);
+}
+
+static void
+select_one_object(Object_t *obj, gpointer data)
+{
+ UnselectAllCommand_t *command = (UnselectAllCommand_t*) data;
+ command_add_subcommand(&command->parent, unselect_command_new(obj));
+}
+
+static CmdExecuteValue_t
+unselect_all_command_execute(Command_t *parent)
+{
+ UnselectAllCommand_t *command = (UnselectAllCommand_t*) parent;
+ gpointer id;
+ CmdExecuteValue_t rvalue;
+
+ id = object_list_add_select_cb(command->list, select_one_object,
+ command);
+ if (object_list_deselect_all(command->list, command->exception)) {
+ rvalue = CMD_APPEND;
+ } else {
+ rvalue = CMD_DESTRUCT;
+ }
+ object_list_remove_select_cb(command->list, id);
+ return rvalue;
+}
diff --git a/plug-ins/imagemap/imap_command.c b/plug-ins/imagemap/imap_command.c
new file mode 100644
index 0000000..ae81c1f
--- /dev/null
+++ b/plug-ins/imagemap/imap_command.c
@@ -0,0 +1,384 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_command.h"
+
+#define INFINITE_UNDO_LEVELS -1
+
+static void command_destruct(Command_t *command);
+
+static CommandList_t _command_list = {NULL, DEFAULT_UNDO_LEVELS};
+static CommandList_t *_current_command_list = &_command_list;
+
+static void
+command_list_callback_add(CommandListCallback_t *list,
+ CommandListCallbackFunc_t func, gpointer data)
+{
+ CommandListCB_t *cb = g_new(CommandListCB_t, 1);
+ cb->func = func;
+ cb->data = data;
+ list->list = g_list_append(list->list, cb);
+}
+
+static void
+command_list_callback_call(CommandListCallback_t *list, Command_t *command)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ CommandListCB_t *cb = (CommandListCB_t*) p->data;
+ cb->func(command, cb->data);
+ }
+}
+
+CommandList_t*
+command_list_new(gint undo_levels)
+{
+ CommandList_t *list = g_new(CommandList_t, 1);
+ list->parent = NULL;
+ list->undo_levels = undo_levels;
+ list->list = NULL;
+ list->undo = NULL;
+ list->redo = NULL;
+ list->update_cb.list = NULL;
+ return list;
+}
+
+static void
+command_list_clear(CommandList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next)
+ command_destruct((Command_t*) p->data);
+ g_list_free(list->list);
+ list->list = NULL;
+ list->undo = NULL;
+ list->redo = NULL;
+ command_list_callback_call(&list->update_cb, NULL);
+}
+
+void
+command_list_destruct(CommandList_t *list)
+{
+ command_list_clear(list);
+ g_free(list);
+}
+
+void
+command_list_remove_all(void)
+{
+ command_list_clear(&_command_list);
+}
+
+static void
+_command_list_add(CommandList_t *list, Command_t *command)
+{
+ GList *p, *q;
+
+ /* Remove rest */
+ for (p = list->redo; p; p = q) {
+ Command_t *curr = (Command_t*) p->data;
+ q = p->next;
+ command_destruct(curr);
+ list->list = g_list_remove_link(list->list, p);
+ }
+ if (g_list_length(list->list) == list->undo_levels) {
+ GList *first = g_list_first(list->list);
+ Command_t *curr = (Command_t*) first->data;
+ command_destruct(curr);
+ list->list = g_list_remove_link(list->list, first);
+ }
+ list->list = g_list_append(list->list, (gpointer) command);
+ list->undo = g_list_last(list->list);
+ list->redo = NULL;
+
+ command_list_callback_call(&list->update_cb, command);
+}
+
+void
+command_list_add(Command_t *command)
+{
+ _command_list_add(_current_command_list, command);
+}
+
+/* Fix me! */
+void
+subcommand_list_add(CommandList_t *list, Command_t *command)
+{
+ _command_list_add(list, command);
+}
+
+static CommandClass_t parent_command_class = {
+ NULL, /* parent_command_destruct */
+ NULL, /* parent_command_execute */
+ NULL, /* parent_command_undo */
+ NULL /* parent_command_redo */
+};
+
+static Command_t*
+command_list_start(CommandList_t *list, const gchar *name)
+{
+ Command_t *command = g_new(Command_t, 1);
+ command_init(command, name, &parent_command_class);
+ command->sub_commands = command_list_new(INFINITE_UNDO_LEVELS);
+
+ command_list_add(command);
+ command->sub_commands->parent = _current_command_list;
+ _current_command_list = command->sub_commands;
+
+ return command;
+}
+
+static void
+command_list_end(CommandList_t *list)
+{
+ _current_command_list = list->parent;
+}
+
+Command_t*
+subcommand_start(const gchar *name)
+{
+ return command_list_start(_current_command_list, name);
+}
+
+void
+subcommand_end(void)
+{
+ command_list_end(_current_command_list);
+}
+
+static void
+_command_list_set_undo_level(CommandList_t *list, gint level)
+{
+ gint diff = g_list_length(list->list) - level;
+ if (diff > 0) {
+ GList *p, *q;
+ /* first remove data at the front */
+ for (p = list->list; diff && p != list->undo; p = q, diff--) {
+ Command_t *curr = (Command_t*) p->data;
+ q = p->next;
+ command_destruct(curr);
+ list->list = g_list_remove_link(list->list, p);
+ }
+
+ /* If still to long start removing redo levels at the end */
+ for (p = g_list_last(list->list); diff && p != list->undo; p = q,
+ diff--) {
+ Command_t *curr = (Command_t*) p->data;
+ q = p->prev;
+ command_destruct(curr);
+ list->list = g_list_remove_link(list->list, p);
+ }
+ command_list_callback_call(&list->update_cb,
+ (Command_t*) list->undo->data);
+ }
+ list->undo_levels = level;
+}
+
+void
+command_list_set_undo_level(gint level)
+{
+ _command_list_set_undo_level(&_command_list, level);
+}
+
+Command_t*
+command_list_get_redo_command(void)
+{
+ return (_command_list.redo) ? (Command_t*) _command_list.redo->data : NULL;
+}
+
+void
+command_list_add_update_cb(CommandListCallbackFunc_t func, gpointer data)
+{
+ command_list_callback_add(&_command_list.update_cb, func, data);
+}
+
+static void
+command_destruct(Command_t *command)
+{
+ if (command->sub_commands)
+ command_list_destruct(command->sub_commands);
+ if (command->class->destruct)
+ command->class->destruct(command);
+}
+
+static void
+command_list_execute(CommandList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Command_t *command = (Command_t*) p->data;
+ if (command->sub_commands)
+ command_list_execute(command->sub_commands);
+ if (command->class->execute)
+ (void) command->class->execute(command);
+ }
+}
+
+void
+command_execute(Command_t *command)
+{
+ if (command->locked) {
+ command->locked = FALSE;
+ } else {
+ if (command->sub_commands)
+ command_list_execute(command->sub_commands);
+ if (command->class->execute) {
+ CmdExecuteValue_t value = command->class->execute(command);
+ if (value == CMD_APPEND)
+ command_list_add(command);
+ else if (value == CMD_DESTRUCT)
+ command_destruct(command);
+ }
+ }
+}
+
+void
+command_redo(Command_t *command)
+{
+ if (command->sub_commands)
+ command_list_redo_all(command->sub_commands);
+ if (command->class->redo)
+ command->class->redo(command);
+ else if (command->class->execute)
+ (void) command->class->execute(command);
+}
+
+void
+command_undo(Command_t *command)
+{
+ if (command->sub_commands)
+ command_list_undo_all(command->sub_commands);
+ if (command->class->undo)
+ command->class->undo(command);
+}
+
+void
+command_set_name(Command_t *command, const gchar *name)
+{
+ command->name = name;
+ command_list_callback_call(&_command_list.update_cb, command);
+}
+
+void
+command_list_undo(CommandList_t *list)
+{
+ Command_t *command = (Command_t*) list->undo->data;
+ command_undo(command);
+
+ list->redo = list->undo;
+ list->undo = list->undo->prev;
+ if (list->undo)
+ command = (Command_t*) list->undo->data;
+ else
+ command = NULL;
+ command_list_callback_call(&list->update_cb, command);
+}
+
+void
+command_list_undo_all(CommandList_t *list)
+{
+ while (list->undo)
+ command_list_undo(list);
+}
+
+void
+last_command_undo(void)
+{
+ command_list_undo(&_command_list);
+}
+
+void
+command_list_redo(CommandList_t *list)
+{
+ Command_t *command = (Command_t*) list->redo->data;
+ command_redo(command);
+
+ list->undo = list->redo;
+ list->redo = list->redo->next;
+ command_list_callback_call(&list->update_cb, command);
+}
+
+void
+command_list_redo_all(CommandList_t *list)
+{
+ while (list->redo)
+ command_list_redo(list);
+}
+
+void
+last_command_redo(void)
+{
+ command_list_redo(&_command_list);
+}
+
+Command_t*
+command_init(Command_t *command, const gchar *name, CommandClass_t *class)
+{
+ command->sub_commands = NULL;
+ command->name = name;
+ command->class = class;
+ command->locked = FALSE;
+ return command;
+}
+
+void
+command_add_subcommand(Command_t *command, Command_t *sub_command)
+{
+ if (!command->sub_commands)
+ command->sub_commands = command_list_new(INFINITE_UNDO_LEVELS);
+ subcommand_list_add(command->sub_commands, sub_command);
+}
+
+static CmdExecuteValue_t basic_command_execute(Command_t *command);
+
+static CommandClass_t basic_command_class = {
+ NULL, /* basic_command_destruct */
+ basic_command_execute,
+ NULL,
+ NULL /* basic_command_redo */
+};
+
+typedef struct {
+ Command_t parent;
+ void (*func)(void);
+} BasicCommand_t;
+
+Command_t*
+command_new(void (*func)(void))
+{
+ BasicCommand_t *command = g_new(BasicCommand_t, 1);
+ command->func = func;
+ return command_init(&command->parent, "Unknown", &basic_command_class);
+}
+
+static CmdExecuteValue_t
+basic_command_execute(Command_t *command)
+{
+ ((BasicCommand_t*) command)->func();
+ return CMD_DESTRUCT;
+}
diff --git a/plug-ins/imagemap/imap_command.h b/plug-ins/imagemap/imap_command.h
new file mode 100644
index 0000000..1676d2d
--- /dev/null
+++ b/plug-ins/imagemap/imap_command.h
@@ -0,0 +1,103 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_COMMAND_H
+#define _IMAP_COMMAND_H
+
+#include "imap_object.h"
+
+#define DEFAULT_UNDO_LEVELS 10
+
+typedef struct CommandClass_t CommandClass_t;
+typedef struct Command_t Command_t;
+typedef struct CommandList_t CommandList_t;
+
+typedef enum {CMD_APPEND, CMD_DESTRUCT, CMD_IGNORE} CmdExecuteValue_t;
+
+struct CommandClass_t {
+ void (*destruct)(Command_t*);
+ CmdExecuteValue_t (*execute)(Command_t*);
+ void (*undo)(Command_t*);
+ void (*redo)(Command_t*);
+};
+
+struct Command_t {
+ CommandClass_t *class;
+ CommandList_t *sub_commands;
+ const gchar *name;
+ gboolean locked;
+};
+
+
+typedef Command_t* (*CommandFactory_t)(void);
+
+typedef void (*CommandListCallbackFunc_t)(Command_t*, gpointer);
+
+typedef struct {
+ CommandListCallbackFunc_t func;
+ gpointer data;
+} CommandListCB_t;
+
+typedef struct {
+ GList *list;
+} CommandListCallback_t;
+
+struct CommandList_t {
+ CommandList_t *parent;
+ gint undo_levels;
+ GList *list;
+ GList *undo; /* Pointer to current undo command */
+ GList *redo; /* Pointer to current redo command */
+ CommandListCallback_t update_cb;
+};
+
+CommandList_t *command_list_new(gint undo_levels);
+void command_list_destruct(CommandList_t *list);
+void command_list_set_undo_level(gint level);
+void command_list_add(Command_t *command);
+void command_list_remove_all(void);
+void command_list_undo(CommandList_t *list);
+void command_list_undo_all(CommandList_t *list);
+void command_list_redo(CommandList_t *list);
+void command_list_redo_all(CommandList_t *list);
+void command_list_add_update_cb(CommandListCallbackFunc_t func, gpointer data);
+Command_t *command_list_get_redo_command(void);
+
+Command_t *command_new(void (*func)(void));
+Command_t *command_init(Command_t *command, const gchar *name,
+ CommandClass_t *class);
+void command_execute(Command_t *command);
+void command_undo(Command_t *command);
+void command_redo(Command_t *command);
+void command_set_name(Command_t *command, const gchar *name);
+void command_add_subcommand(Command_t *command, Command_t *sub_command);
+
+void last_command_undo(void);
+void last_command_redo(void);
+
+void subcommand_list_add(CommandList_t *list, Command_t *command);
+Command_t *subcommand_start(const gchar *name);
+void subcommand_end(void);
+
+#define command_lock(command) ((command)->locked = TRUE)
+
+#endif /* _IMAP_COMMAND_H */
diff --git a/plug-ins/imagemap/imap_commands.h b/plug-ins/imagemap/imap_commands.h
new file mode 100644
index 0000000..af8637b
--- /dev/null
+++ b/plug-ins/imagemap/imap_commands.h
@@ -0,0 +1,64 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.i
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_COMMANDS_H
+#define _IMAP_COMMANDS_H
+
+#include "imap_command.h"
+#include "imap_object.h"
+#include "imap_preview.h"
+
+Command_t *clear_command_new(ObjectList_t *list);
+Command_t *copy_command_new(ObjectList_t *list);
+Command_t *copy_object_command_new(Object_t *obj);
+Command_t *create_command_new(ObjectList_t *list, Object_t *obj);
+Command_t *cut_command_new(ObjectList_t *list);
+Command_t *cut_object_command_new(Object_t *obj);
+Command_t *delete_command_new(ObjectList_t *list, Object_t *obj);
+Command_t *delete_point_command_new(Object_t *obj, GdkPoint *point);
+Command_t *edit_object_command_new(Object_t *obj);
+Command_t *gimp_guides_command_new(ObjectList_t *list,
+ gint32 _drawable_id);
+Command_t *guides_command_new(ObjectList_t *list);
+Command_t *insert_point_command_new(Object_t *obj, gint x, gint y, gint edge);
+Command_t *move_down_command_new(ObjectList_t *list);
+Command_t *move_command_new(Preview_t *preview, Object_t *obj, gint x, gint y);
+Command_t *move_sash_command_new(GtkWidget *widget, Object_t *obj,
+ gint x, gint y, MoveSashFunc_t sash_func);
+Command_t *move_selected_command_new(ObjectList_t *list, gint dx, gint dy);
+Command_t *move_to_front_command_new(ObjectList_t *list);
+Command_t *move_up_command_new(ObjectList_t *list);
+Command_t *object_down_command_new(ObjectList_t *list, Object_t *obj);
+Command_t *object_move_command_new(Object_t *obj, gint x, gint y);
+Command_t *object_up_command_new(ObjectList_t *list, Object_t *obj);
+Command_t *paste_command_new(ObjectList_t *list);
+Command_t *select_all_command_new(ObjectList_t *list);
+Command_t *select_command_new(Object_t *obj);
+Command_t *select_next_command_new(ObjectList_t *list);
+Command_t *select_prev_command_new(ObjectList_t *list);
+Command_t *select_region_command_new(GtkWidget *widget, ObjectList_t *list,
+ gint x, gint y);
+Command_t *send_to_back_command_new(ObjectList_t *list);
+Command_t *unselect_all_command_new(ObjectList_t *list, Object_t *exception);
+Command_t *unselect_command_new(Object_t *obj);
+
+#endif /* _IMAP_COMMANDS_H */
diff --git a/plug-ins/imagemap/imap_csim.l b/plug-ins/imagemap/imap_csim.l
new file mode 100644
index 0000000..32b9803
--- /dev/null
+++ b/plug-ins/imagemap/imap_csim.l
@@ -0,0 +1,137 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2000 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_csim_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+%}
+
+%option noyywrap
+%option noinput
+%option nounput
+
+DIGIT [0-9]
+ID [a-zA-Z_][a-zA-Z0-9_\-]*
+WS [ \t\n]+
+
+%x quoted_string
+%x comment
+
+%%
+
+\<!--\ #$AUTHOR: {
+ BEGIN(comment);
+ return AUTHOR;
+ }
+
+\<!--\ #$DESCRIPTION: {
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+
+\<!-- {
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+
+<comment>--\> {
+ BEGIN(INITIAL);
+ return END_COMMENT;
+ }
+
+<comment>.*/--\> {
+ csim_lval.id = g_strndup (yytext, yyleng);
+ return STRING;
+ }
+
+IMG return IMG;
+
+SRC return SRC;
+
+WIDTH return WIDTH;
+
+HEIGHT return HEIGHT;
+
+BORDER return BORDER;
+
+USEMAP return USEMAP;
+
+MAP return START_MAP;
+
+\/MAP return END_MAP;
+
+NAME return NAME;
+
+AREA return AREA;
+
+SHAPE return SHAPE;
+
+COORDS return COORDS;
+
+TARGET return TARGET;
+
+ONMOUSEOVER return ONMOUSEOVER;
+
+ONMOUSEOUT return ONMOUSEOUT;
+
+ONFOCUS return ONFOCUS;
+
+ONBLUR return ONBLUR;
+
+ALT return ALT;
+
+HREF return HREF;
+
+NOHREF return NOHREF;
+
+\" {
+ BEGIN(quoted_string);
+ }
+
+<quoted_string>\" {
+ BEGIN(INITIAL);
+ return STRING;
+ }
+
+<quoted_string>[^\"]* {
+ csim_lval.id = g_strndup (yytext, yyleng);
+ }
+
+-?{DIGIT}*"."?{DIGIT}*([Ee][-+]?{DIGIT}*)? {
+ csim_lval.value = g_ascii_strtod (yytext, NULL);
+ return FLOAT;
+ }
+
+{WS} ; /* Eat white space */
+
+. return *yytext;
+
+%%
+
diff --git a/plug-ins/imagemap/imap_csim.y b/plug-ins/imagemap/imap_csim.y
new file mode 100644
index 0000000..abc2dff
--- /dev/null
+++ b/plug-ins/imagemap/imap_csim.y
@@ -0,0 +1,396 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int csim_lex(void);
+extern int csim_restart(FILE *csim_in);
+static void csim_error(char* s);
+static gchar * unescape_text(gchar *input);
+
+static enum {UNDEFINED, RECTANGLE, CIRCLE, POLYGON} current_type;
+static Object_t *current_object;
+static MapInfo_t *_map_info;
+
+%}
+
+%union {
+ int val;
+ double value;
+ char *id;
+}
+
+%token<val> IMG SRC WIDTH HEIGHT BORDER USEMAP
+%token<val> START_MAP END_MAP NAME AREA SHAPE COORDS ALT HREF NOHREF
+%token<val> TARGET ONMOUSEOVER ONMOUSEOUT ONFOCUS ONBLUR
+%token<val> AUTHOR DESCRIPTION BEGIN_COMMENT END_COMMENT
+%token<value> FLOAT
+%token<id> STRING
+
+%type<val> integer_value
+
+%%
+
+csim_file : image start_map comment_lines area_list end_map
+ ;
+
+image : '<' IMG SRC '=' STRING image_tags xhtml_close
+ {
+ g_strreplace(&_map_info->image_name, $5);
+ g_free ($5);
+ }
+ ;
+
+image_tags : /* Empty */
+ | image_tags image_tag
+ ;
+
+image_tag : image_width
+ | image_height
+ | BORDER '=' integer_value {}
+ | USEMAP '=' STRING { g_free ($3); }
+ | ALT '=' STRING { g_free ($3); }
+ ;
+
+image_width : WIDTH '=' integer_value
+ {
+ _map_info->old_image_width = $3;
+ }
+ ;
+
+image_height : HEIGHT '=' integer_value
+ {
+ _map_info->old_image_height = $3;
+ }
+ ;
+
+integer_value : FLOAT
+ {
+ $$ = (gint) $1;
+ }
+ | STRING
+ {
+ $$ = (gint) g_ascii_strtod ($1, NULL);
+ g_free ($1);
+ }
+ ;
+
+start_map : '<' START_MAP NAME '=' STRING '>'
+ {
+ g_strreplace(&_map_info->title, $5);
+ g_free ($5);
+ }
+ ;
+
+comment_lines : /* empty */
+ | comment_lines comment_line
+ ;
+
+comment_line : author_line
+ | description_line
+ | real_comment
+ ;
+
+real_comment : BEGIN_COMMENT STRING END_COMMENT
+ {
+ g_free ($2);
+ }
+ ;
+
+author_line : AUTHOR STRING END_COMMENT
+ {
+ g_strreplace(&_map_info->author, $2);
+ g_free ($2);
+ }
+ ;
+
+description_line: DESCRIPTION STRING END_COMMENT
+ {
+ gchar *description;
+
+ description = g_strconcat(_map_info->description, $2, "\n",
+ NULL);
+ g_strreplace(&_map_info->description, description);
+ g_free ($2);
+ }
+ ;
+
+area_list : /* empty */
+ | area_list area
+ ;
+
+area : '<' AREA tag_list xhtml_close
+ {
+ if (current_type != UNDEFINED)
+ add_shape(current_object);
+ }
+ ;
+
+xhtml_close : '>'
+ | '/' '>'
+ ;
+
+tag_list : /* Empty */
+ | tag_list tag
+ ;
+
+tag : shape_tag
+ | coords_tag
+ | href_tag
+ | nohref_tag
+ | alt_tag
+ | target_tag
+ | onmouseover_tag
+ | onmouseout_tag
+ | onfocus_tag
+ | onblur_tag
+ ;
+
+shape_tag : SHAPE '=' STRING
+ {
+ if (!g_ascii_strcasecmp($3, "RECT")) {
+ current_object = create_rectangle(0, 0, 0, 0);
+ current_type = RECTANGLE;
+ } else if (!g_ascii_strcasecmp($3, "CIRCLE")) {
+ current_object = create_circle(0, 0, 0);
+ current_type = CIRCLE;
+ } else if (!g_ascii_strcasecmp($3, "POLY")) {
+ current_object = create_polygon(NULL);
+ current_type = POLYGON;
+ } else if (!g_ascii_strcasecmp($3, "DEFAULT")) {
+ current_type = UNDEFINED;
+ }
+ g_free ($3);
+ }
+ ;
+
+coords_tag : COORDS '=' STRING
+ {
+ char *p;
+ if (current_type == RECTANGLE) {
+ Rectangle_t *rectangle;
+
+ rectangle = ObjectToRectangle(current_object);
+ p = strtok($3, ",");
+ rectangle->x = atoi(p);
+ p = strtok(NULL, ",");
+ rectangle->y = atoi(p);
+ p = strtok(NULL, ",");
+ rectangle->width = atoi(p) - rectangle->x;
+ p = strtok(NULL, ",");
+ rectangle->height = atoi(p) - rectangle->y;
+ } else if (current_type == CIRCLE) {
+ Circle_t *circle;
+
+ circle = ObjectToCircle(current_object);
+ p = strtok($3, ",");
+ circle->x = atoi(p);
+ p = strtok(NULL, ",");
+ circle->y = atoi(p);
+ p = strtok(NULL, ",");
+ circle->r = atoi(p);
+ } else if (current_type == POLYGON) {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GList *points;
+ GdkPoint *point, *first;
+ gint x, y;
+
+ p = strtok($3, ",");
+ x = atoi(p);
+ p = strtok(NULL, ",");
+ y = atoi(p);
+ point = new_point(x, y);
+ points = g_list_append(NULL, (gpointer) point);
+
+ while(1) {
+ p = strtok(NULL, ",");
+ if (!p)
+ break;
+ x = atoi(p);
+ p = strtok(NULL, ",");
+ y = atoi(p);
+ point = new_point(x, y);
+ points = g_list_append(points, (gpointer) point);
+ }
+ /* Remove last point if duplicate */
+ first = (GdkPoint*) points->data;
+ polygon->points = points;
+ if (first->x == point->x && first->y == point->y)
+ polygon_remove_last_point(polygon);
+ polygon->points = points;
+ }
+
+ g_free ($3);
+ }
+ ;
+
+href_tag : HREF '=' STRING
+ {
+ if (current_type == UNDEFINED) {
+ g_strreplace(&_map_info->default_url, $3);
+ } else {
+ object_set_url(current_object, unescape_text($3));
+ }
+ g_free ($3);
+ }
+ ;
+
+nohref_tag : NOHREF optional_value
+ {
+ }
+ ;
+
+optional_value : /* Empty */
+ | '=' STRING
+ {
+ g_free ($2);
+ }
+ ;
+
+alt_tag : ALT '=' STRING
+ {
+ object_set_comment(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+target_tag : TARGET '=' STRING
+ {
+ object_set_target(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+onmouseover_tag : ONMOUSEOVER '=' STRING
+ {
+ object_set_mouse_over(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+onmouseout_tag : ONMOUSEOUT '=' STRING
+ {
+ object_set_mouse_out(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+onfocus_tag : ONFOCUS '=' STRING
+ {
+ object_set_focus(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+onblur_tag : ONBLUR '=' STRING
+ {
+ object_set_blur(current_object, unescape_text($3));
+ g_free ($3);
+ }
+ ;
+
+end_map : '<' END_MAP '>'
+ ;
+
+%%
+
+static void
+csim_error(char* s)
+{
+ extern FILE *csim_in;
+ csim_restart(csim_in);
+}
+
+gboolean
+load_csim (const char* filename)
+{
+ gboolean status;
+ extern FILE *csim_in;
+ csim_in = g_fopen(filename, "r");
+ if (csim_in) {
+ _map_info = get_map_info();
+ status = !csim_parse();
+ fclose(csim_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
+
+static gchar*
+unescape_text (gchar *input)
+{
+ /*
+ * We "unescape" simple things "in place", knowing that unescaped
+ * strings always are shorter than the original input.
+ *
+ * It is a shame there is no g_markup_unescape_text() function, but
+ * instead you have to create a full GMarkupParser/Context.
+ */
+ struct token {
+ const char *escaped;
+ const char unescaped;
+ };
+ const struct token tab[] = {
+ { "&quot;", '"' },
+ { "&apos;", '\'' },
+ { "&amp;", '&' },
+ { "&lt;", '<' },
+ { "&gt;", '>' }
+ };
+
+ size_t i;
+ for (i = 0; i < (sizeof tab / sizeof tab[0]); i++)
+ {
+ const size_t escaped_len = strlen (tab[i].escaped);
+ char *p;
+
+ /* FIXME: The following code does not perform a UTF-8 substring
+ search. */
+ for (p = strstr (input, tab[i].escaped);
+ p != NULL;
+ p = strstr (p, tab[i].escaped))
+ {
+ size_t copy_len;
+ *p++ = tab[i].unescaped;
+ copy_len = strlen (p) - escaped_len + 2;
+ memmove (p, p + escaped_len - 1, copy_len);
+ if (*p == 0)
+ break;
+ }
+ }
+
+ return input;
+}
diff --git a/plug-ins/imagemap/imap_csim_lex.c b/plug-ins/imagemap/imap_csim_lex.c
new file mode 100644
index 0000000..88c8b86
--- /dev/null
+++ b/plug-ins/imagemap/imap_csim_lex.c
@@ -0,0 +1,2054 @@
+
+#line 3 "<stdout>"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer csim__create_buffer
+#define yy_delete_buffer csim__delete_buffer
+#define yy_flex_debug csim__flex_debug
+#define yy_init_buffer csim__init_buffer
+#define yy_flush_buffer csim__flush_buffer
+#define yy_load_buffer_state csim__load_buffer_state
+#define yy_switch_to_buffer csim__switch_to_buffer
+#define yyin csim_in
+#define yyleng csim_leng
+#define yylex csim_lex
+#define yylineno csim_lineno
+#define yyout csim_out
+#define yyrestart csim_restart
+#define yytext csim_text
+#define yywrap csim_wrap
+#define yyalloc csim_alloc
+#define yyrealloc csim_realloc
+#define yyfree csim_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 36
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE csim_restart(csim_in )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+extern yy_size_t csim_leng;
+
+extern FILE *csim_in, *csim_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up csim_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up csim_text again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via csim_restart()), so that the user can continue scanning by
+ * just pointing csim_in at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when csim_text is formed. */
+static char yy_hold_char;
+static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */
+yy_size_t csim_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow csim_wrap()'s to do buffer switches
+ * instead of setting up a fresh csim_in. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void csim_restart (FILE *input_file );
+void csim__switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE csim__create_buffer (FILE *file,int size );
+void csim__delete_buffer (YY_BUFFER_STATE b );
+void csim__flush_buffer (YY_BUFFER_STATE b );
+void csim_push_buffer_state (YY_BUFFER_STATE new_buffer );
+void csim_pop_buffer_state (void );
+
+static void csim_ensure_buffer_stack (void );
+static void csim__load_buffer_state (void );
+static void csim__init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER csim__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE csim__scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE csim__scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE csim__scan_bytes (yyconst char *bytes,yy_size_t len );
+
+void *csim_alloc (yy_size_t );
+void *csim_realloc (void *,yy_size_t );
+void csim_free (void * );
+
+#define yy_new_buffer csim__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ csim_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ csim__create_buffer(csim_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ csim_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ csim__create_buffer(csim_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define csim_wrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *csim_in = (FILE *) 0, *csim_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int csim_lineno;
+
+int csim_lineno = 1;
+
+extern char *csim_text;
+#define yytext_ptr csim_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up csim_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ csim_leng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 32
+#define YY_END_OF_BUFFER 33
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[154] =
+ { 0,
+ 29, 29, 28, 28, 0, 0, 33, 31, 30, 30,
+ 26, 29, 29, 31, 29, 31, 31, 31, 31, 29,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 28,
+ 27, 32, 32, 32, 30, 29, 29, 29, 29, 0,
+ 0, 0, 0, 0, 0, 29, 29, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,
+ 0, 0, 0, 0, 0, 23, 0, 0, 0, 0,
+ 0, 6, 12, 0, 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 0, 4, 13, 3, 15, 0, 0,
+ 0, 24, 14, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 5, 0, 0, 0, 0, 0, 0, 0, 0,
+ 16, 0, 0, 8, 0, 10, 17, 9, 25, 22,
+ 0, 0, 18, 11, 0, 21, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 20, 0, 0, 0,
+ 19, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 2, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 5, 6, 7, 8, 1, 1, 1, 1,
+ 1, 1, 9, 1, 10, 11, 12, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 14, 1, 15,
+ 1, 16, 1, 1, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 1, 1, 26, 27, 28, 29, 30,
+ 1, 31, 32, 33, 34, 35, 36, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 37, 38, 39, 40,
+
+ 41, 42, 43, 44, 45, 1, 1, 46, 47, 48,
+ 49, 50, 1, 51, 52, 53, 54, 55, 56, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[57] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[158] =
+ { 0,
+ 0, 0, 315, 314, 54, 55, 318, 322, 57, 64,
+ 322, 58, 49, 36, 61, 301, 47, 46, 47, 71,
+ 56, 58, 69, 71, 61, 70, 74, 60, 78, 0,
+ 322, 295, 322, 294, 113, 97, 111, 116, 106, 96,
+ 293, 95, 109, 100, 105, 289, 288, 110, 115, 114,
+ 109, 113, 117, 131, 125, 124, 113, 124, 126, 0,
+ 290, 289, 158, 137, 287, 322, 153, 151, 141, 152,
+ 154, 322, 322, 156, 148, 154, 152, 153, 153, 322,
+ 161, 158, 153, 178, 286, 322, 291, 322, 168, 173,
+ 174, 322, 322, 186, 174, 191, 177, 191, 193, 198,
+
+ 192, 284, 285, 186, 187, 187, 199, 191, 189, 192,
+ 322, 192, 196, 322, 283, 322, 322, 322, 322, 322,
+ 197, 212, 322, 322, 230, 322, 209, 214, 230, 220,
+ 219, 224, 224, 238, 236, 242, 322, 231, 234, 233,
+ 322, 234, 241, 276, 239, 322, 240, 253, 258, 260,
+ 90, 322, 322, 308, 311, 314, 316
+ } ;
+
+static yyconst flex_int16_t yy_def[158] =
+ { 0,
+ 153, 1, 154, 154, 155, 155, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 156,
+ 153, 157, 153, 157, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 156,
+ 157, 157, 157, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 157, 157, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+
+ 153, 157, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 0, 153, 153, 153, 153
+ } ;
+
+static yyconst flex_int16_t yy_nxt[379] =
+ { 0,
+ 8, 9, 10, 9, 8, 11, 8, 8, 8, 12,
+ 13, 14, 15, 8, 16, 8, 17, 18, 19, 8,
+ 20, 8, 8, 21, 22, 8, 23, 24, 25, 8,
+ 8, 26, 27, 28, 8, 29, 17, 18, 19, 8,
+ 20, 8, 8, 21, 22, 8, 23, 24, 25, 8,
+ 8, 26, 27, 28, 8, 29, 33, 33, 35, 35,
+ 35, 39, 40, 34, 34, 35, 35, 35, 36, 38,
+ 37, 36, 42, 37, 44, 45, 48, 43, 38, 46,
+ 46, 38, 40, 47, 50, 51, 49, 52, 54, 38,
+ 57, 58, 42, 55, 44, 45, 48, 43, 38, 53,
+
+ 56, 38, 59, 152, 50, 51, 49, 52, 54, 39,
+ 57, 58, 64, 55, 35, 35, 35, 38, 39, 53,
+ 56, 36, 59, 37, 46, 46, 38, 66, 47, 67,
+ 68, 38, 64, 69, 70, 71, 72, 38, 73, 74,
+ 75, 79, 80, 81, 82, 83, 38, 66, 76, 67,
+ 68, 38, 77, 69, 70, 71, 72, 78, 73, 74,
+ 75, 79, 80, 81, 82, 83, 86, 84, 76, 88,
+ 89, 90, 77, 85, 91, 92, 93, 78, 94, 95,
+ 96, 97, 98, 99, 100, 101, 86, 84, 104, 88,
+ 89, 90, 105, 102, 91, 92, 93, 106, 94, 95,
+
+ 96, 97, 98, 99, 100, 101, 107, 108, 104, 109,
+ 110, 111, 105, 112, 113, 114, 116, 106, 117, 118,
+ 119, 120, 121, 122, 123, 124, 107, 108, 126, 109,
+ 110, 111, 127, 112, 113, 114, 116, 130, 117, 118,
+ 119, 120, 121, 122, 123, 124, 128, 131, 126, 129,
+ 132, 135, 127, 133, 134, 136, 137, 130, 138, 139,
+ 140, 141, 142, 143, 144, 145, 128, 131, 147, 129,
+ 132, 135, 148, 133, 134, 136, 137, 149, 138, 139,
+ 140, 141, 142, 143, 144, 145, 150, 151, 147, 146,
+ 125, 115, 148, 62, 103, 62, 87, 149, 84, 62,
+
+ 47, 47, 65, 63, 62, 41, 150, 151, 30, 30,
+ 30, 32, 32, 32, 60, 60, 61, 153, 61, 31,
+ 31, 7, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153
+ } ;
+
+static yyconst flex_int16_t yy_chk[379] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 5, 6, 9, 9,
+ 9, 13, 14, 5, 6, 10, 10, 10, 12, 13,
+ 12, 15, 17, 15, 18, 19, 21, 17, 12, 20,
+ 20, 15, 14, 20, 22, 23, 21, 24, 25, 13,
+ 27, 28, 17, 26, 18, 19, 21, 17, 12, 24,
+
+ 26, 15, 29, 151, 22, 23, 21, 24, 25, 36,
+ 27, 28, 40, 26, 35, 35, 35, 36, 39, 24,
+ 26, 37, 29, 37, 38, 38, 39, 42, 38, 43,
+ 44, 37, 40, 45, 48, 49, 50, 36, 51, 52,
+ 53, 55, 56, 57, 58, 59, 39, 42, 54, 43,
+ 44, 37, 54, 45, 48, 49, 50, 54, 51, 52,
+ 53, 55, 56, 57, 58, 59, 64, 63, 54, 67,
+ 68, 69, 54, 63, 70, 71, 74, 54, 75, 76,
+ 77, 78, 79, 81, 82, 83, 64, 84, 89, 67,
+ 68, 69, 90, 84, 70, 71, 74, 91, 75, 76,
+
+ 77, 78, 79, 81, 82, 83, 94, 95, 89, 96,
+ 97, 98, 90, 99, 100, 101, 104, 91, 105, 106,
+ 107, 108, 109, 110, 112, 113, 94, 95, 121, 96,
+ 97, 98, 122, 99, 100, 101, 104, 127, 105, 106,
+ 107, 108, 109, 110, 112, 113, 125, 128, 121, 125,
+ 129, 131, 122, 130, 130, 132, 133, 127, 134, 135,
+ 136, 138, 139, 140, 142, 143, 125, 128, 145, 125,
+ 129, 131, 147, 130, 130, 132, 133, 148, 134, 135,
+ 136, 138, 139, 140, 142, 143, 149, 150, 145, 144,
+ 115, 103, 147, 102, 87, 85, 65, 148, 62, 61,
+
+ 47, 46, 41, 34, 32, 16, 149, 150, 154, 154,
+ 154, 155, 155, 155, 156, 156, 157, 7, 157, 4,
+ 3, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,
+ 153, 153, 153, 153, 153, 153, 153, 153
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int csim__flex_debug;
+int csim__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *csim_text;
+#line 1 "imap_csim.l"
+#line 2 "imap_csim.l"
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2000 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_csim_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+#define YY_NO_INPUT 1
+
+
+#line 641 "<stdout>"
+
+#define INITIAL 0
+#define quoted_string 1
+#define comment 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int csim_lex_destroy (void );
+
+int csim_get_debug (void );
+
+void csim_set_debug (int debug_flag );
+
+YY_EXTRA_TYPE csim_get_extra (void );
+
+void csim_set_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *csim_get_in (void );
+
+void csim_set_in (FILE * in_str );
+
+FILE *csim_get_out (void );
+
+void csim_set_out (FILE * out_str );
+
+yy_size_t csim_get_leng (void );
+
+char *csim_get_text (void );
+
+int csim_get_lineno (void );
+
+void csim_set_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int csim_wrap (void );
+#else
+extern int csim_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( csim_text, csim_leng, 1, csim_out )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( csim_in )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( csim_in ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, csim_in))==0 && ferror(csim_in)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(csim_in); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int csim_lex (void);
+
+#define YY_DECL int csim_lex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after csim_text and csim_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 47 "imap_csim.l"
+
+
+#line 826 "<stdout>"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! csim_in )
+ csim_in = stdin;
+
+ if ( ! csim_out )
+ csim_out = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ csim_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ csim__create_buffer(csim_in,YY_BUF_SIZE );
+ }
+
+ csim__load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of csim_text. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 154 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 322 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 49 "imap_csim.l"
+{
+ BEGIN(comment);
+ return AUTHOR;
+ }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 54 "imap_csim.l"
+{
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 59 "imap_csim.l"
+{
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 64 "imap_csim.l"
+{
+ BEGIN(INITIAL);
+ return END_COMMENT;
+ }
+ YY_BREAK
+case 5:
+*yy_cp = (yy_hold_char); /* undo effects of setting up csim_text */
+(yy_c_buf_p) = yy_cp -= 3;
+YY_DO_BEFORE_ACTION; /* set up csim_text again */
+YY_RULE_SETUP
+#line 69 "imap_csim.l"
+{
+ csim_lval.id = g_strndup (csim_text, csim_leng);
+ return STRING;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 74 "imap_csim.l"
+return IMG;
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 76 "imap_csim.l"
+return SRC;
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 78 "imap_csim.l"
+return WIDTH;
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 80 "imap_csim.l"
+return HEIGHT;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 82 "imap_csim.l"
+return BORDER;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 84 "imap_csim.l"
+return USEMAP;
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 86 "imap_csim.l"
+return START_MAP;
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 88 "imap_csim.l"
+return END_MAP;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 90 "imap_csim.l"
+return NAME;
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 92 "imap_csim.l"
+return AREA;
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 94 "imap_csim.l"
+return SHAPE;
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 96 "imap_csim.l"
+return COORDS;
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 98 "imap_csim.l"
+return TARGET;
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 100 "imap_csim.l"
+return ONMOUSEOVER;
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 102 "imap_csim.l"
+return ONMOUSEOUT;
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 104 "imap_csim.l"
+return ONFOCUS;
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 106 "imap_csim.l"
+return ONBLUR;
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 108 "imap_csim.l"
+return ALT;
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 110 "imap_csim.l"
+return HREF;
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 112 "imap_csim.l"
+return NOHREF;
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 114 "imap_csim.l"
+{
+ BEGIN(quoted_string);
+ }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 118 "imap_csim.l"
+{
+ BEGIN(INITIAL);
+ return STRING;
+ }
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+#line 123 "imap_csim.l"
+{
+ csim_lval.id = g_strndup (csim_text, csim_leng);
+ }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 127 "imap_csim.l"
+{
+ csim_lval.value = g_ascii_strtod (csim_text, NULL);
+ return FLOAT;
+ }
+ YY_BREAK
+case 30:
+/* rule 30 can match eol */
+YY_RULE_SETUP
+#line 132 "imap_csim.l"
+; /* Eat white space */
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 134 "imap_csim.l"
+return *csim_text;
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 136 "imap_csim.l"
+ECHO;
+ YY_BREAK
+#line 1099 "<stdout>"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(quoted_string):
+case YY_STATE_EOF(comment):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed csim_in at a new source and called
+ * csim_lex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = csim_in;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( csim_wrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * csim_text, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of csim_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ yy_size_t new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ csim_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ csim_restart(csim_in );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) csim_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 154 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 154 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 153);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ csim_restart(csim_in );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( csim_wrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve csim_text */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void csim_restart (FILE * input_file )
+{
+ if ( ! YY_CURRENT_BUFFER ){
+ csim_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ csim__create_buffer(csim_in,YY_BUF_SIZE );
+ }
+
+ csim__init_buffer(YY_CURRENT_BUFFER,input_file );
+ csim__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void csim__switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * csim_pop_buffer_state();
+ * csim_push_buffer_state(new_buffer);
+ */
+ csim_ensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ csim__load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (csim_wrap()) processing, but the only time this flag
+ * is looked at is after csim_wrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void csim__load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ csim_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE csim__create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) csim_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in csim__create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) csim_alloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in csim__create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ csim__init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with csim__create_buffer()
+ *
+ */
+ void csim__delete_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ csim_free((void *) b->yy_ch_buf );
+
+ csim_free((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a csim_restart() or at EOF.
+ */
+ static void csim__init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ csim__flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then csim__init_buffer was _probably_
+ * called from csim_restart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void csim__flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ csim__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void csim_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ csim_ensure_buffer_stack();
+
+ /* This block is copied from csim__switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from csim__switch_to_buffer. */
+ csim__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void csim_pop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ csim__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ csim__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void csim_ensure_buffer_stack (void)
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)csim_alloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in csim_ensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)csim_realloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in csim_ensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE csim__scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) csim_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in csim__scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ csim__switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to csim_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * csim__scan_bytes() instead.
+ */
+YY_BUFFER_STATE csim__scan_string (yyconst char * yystr )
+{
+ return csim__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to csim_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE csim__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) csim_alloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in csim__scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = csim__scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in csim__scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up csim_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ csim_text[csim_leng] = (yy_hold_char); \
+ (yy_c_buf_p) = csim_text + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ csim_leng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int csim_get_lineno (void)
+{
+ return csim_lineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *csim_get_in (void)
+{
+ return csim_in;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *csim_get_out (void)
+{
+ return csim_out;
+}
+
+/** Get the length of the current token.
+ *
+ */
+yy_size_t csim_get_leng (void)
+{
+ return csim_leng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *csim_get_text (void)
+{
+ return csim_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void csim_set_lineno (int line_number )
+{
+ csim_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see csim__switch_to_buffer
+ */
+void csim_set_in (FILE * in_str )
+{
+ csim_in = in_str ;
+}
+
+void csim_set_out (FILE * out_str )
+{
+ csim_out = out_str ;
+}
+
+int csim_get_debug (void)
+{
+ return csim__flex_debug;
+}
+
+void csim_set_debug (int bdebug )
+{
+ csim__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from csim_lex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ csim_in = stdin;
+ csim_out = stdout;
+#else
+ csim_in = (FILE *) 0;
+ csim_out = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * csim_lex_init()
+ */
+ return 0;
+}
+
+/* csim_lex_destroy is for both reentrant and non-reentrant scanners. */
+int csim_lex_destroy (void)
+{
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ csim__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ csim_pop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ csim_free((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * csim_lex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *csim_alloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *csim_realloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void csim_free (void * ptr )
+{
+ free( (char *) ptr ); /* see csim_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 136 "imap_csim.l"
+
+
+
+
diff --git a/plug-ins/imagemap/imap_csim_parse.c b/plug-ins/imagemap/imap_csim_parse.c
new file mode 100644
index 0000000..b6411ac
--- /dev/null
+++ b/plug-ins/imagemap/imap_csim_parse.c
@@ -0,0 +1,2131 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.6.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names. */
+#define yyparse csim_parse
+#define yylex csim_lex
+#define yyerror csim_error
+#define yylval csim_lval
+#define yychar csim_char
+#define yydebug csim_debug
+#define yynerrs csim_nerrs
+
+/* Copy the first part of user declarations. */
+/* Line 336 of yacc.c */
+#line 1 "imap_csim.y"
+
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int csim_lex(void);
+extern int csim_restart(FILE *csim_in);
+static void csim_error(char* s);
+static gchar * unescape_text(gchar *input);
+
+static enum {UNDEFINED, RECTANGLE, CIRCLE, POLYGON} current_type;
+static Object_t *current_object;
+static MapInfo_t *_map_info;
+
+
+/* Line 336 of yacc.c */
+#line 123 "y.tab.c"
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef CSIM_Y_TAB_H
+# define CSIM_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int csim_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ IMG = 258,
+ SRC = 259,
+ WIDTH = 260,
+ HEIGHT = 261,
+ BORDER = 262,
+ USEMAP = 263,
+ START_MAP = 264,
+ END_MAP = 265,
+ NAME = 266,
+ AREA = 267,
+ SHAPE = 268,
+ COORDS = 269,
+ ALT = 270,
+ HREF = 271,
+ NOHREF = 272,
+ TARGET = 273,
+ ONMOUSEOVER = 274,
+ ONMOUSEOUT = 275,
+ ONFOCUS = 276,
+ ONBLUR = 277,
+ AUTHOR = 278,
+ DESCRIPTION = 279,
+ BEGIN_COMMENT = 280,
+ END_COMMENT = 281,
+ FLOAT = 282,
+ STRING = 283
+ };
+#endif
+/* Tokens. */
+#define IMG 258
+#define SRC 259
+#define WIDTH 260
+#define HEIGHT 261
+#define BORDER 262
+#define USEMAP 263
+#define START_MAP 264
+#define END_MAP 265
+#define NAME 266
+#define AREA 267
+#define SHAPE 268
+#define COORDS 269
+#define ALT 270
+#define HREF 271
+#define NOHREF 272
+#define TARGET 273
+#define ONMOUSEOVER 274
+#define ONMOUSEOUT 275
+#define ONFOCUS 276
+#define ONBLUR 277
+#define AUTHOR 278
+#define DESCRIPTION 279
+#define BEGIN_COMMENT 280
+#define END_COMMENT 281
+#define FLOAT 282
+#define STRING 283
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 350 of yacc.c */
+#line 49 "imap_csim.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 350 of yacc.c */
+#line 229 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE csim_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int csim_parse (void *YYPARSE_PARAM);
+#else
+int csim_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int csim_parse (void);
+#else
+int csim_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !CSIM_Y_TAB_H */
+
+/* Copy the second part of user declarations. */
+
+/* Line 353 of yacc.c */
+#line 257 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 5
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 84
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 33
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 31
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 53
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 106
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 283
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 32, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 29, 30, 31, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 9, 17, 18, 21, 23, 25, 29,
+ 33, 37, 41, 45, 47, 49, 56, 57, 60, 62,
+ 64, 66, 70, 74, 78, 79, 82, 87, 89, 92,
+ 93, 96, 98, 100, 102, 104, 106, 108, 110, 112,
+ 114, 116, 120, 124, 128, 131, 132, 135, 139, 143,
+ 147, 151, 155, 159
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 34, 0, -1, 35, 41, 42, 47, 63, -1, 29,
+ 3, 4, 30, 28, 36, 49, -1, -1, 36, 37,
+ -1, 38, -1, 39, -1, 7, 30, 40, -1, 8,
+ 30, 28, -1, 15, 30, 28, -1, 5, 30, 40,
+ -1, 6, 30, 40, -1, 27, -1, 28, -1, 29,
+ 9, 11, 30, 28, 31, -1, -1, 42, 43, -1,
+ 45, -1, 46, -1, 44, -1, 25, 28, 26, -1,
+ 23, 28, 26, -1, 24, 28, 26, -1, -1, 47,
+ 48, -1, 29, 12, 50, 49, -1, 31, -1, 32,
+ 31, -1, -1, 50, 51, -1, 52, -1, 53, -1,
+ 54, -1, 55, -1, 57, -1, 58, -1, 59, -1,
+ 60, -1, 61, -1, 62, -1, 13, 30, 28, -1,
+ 14, 30, 28, -1, 16, 30, 28, -1, 17, 56,
+ -1, -1, 30, 28, -1, 15, 30, 28, -1, 18,
+ 30, 28, -1, 19, 30, 28, -1, 20, 30, 28,
+ -1, 21, 30, 28, -1, 22, 30, 28, -1, 29,
+ 10, 31, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 66, 66, 69, 76, 77, 80, 81, 82, 83,
+ 84, 87, 93, 99, 103, 110, 117, 118, 121, 122,
+ 123, 126, 132, 139, 150, 151, 154, 161, 162, 165,
+ 166, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 181, 199, 259, 270, 275, 276, 282, 289, 296,
+ 303, 310, 317, 324
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "IMG", "SRC", "WIDTH", "HEIGHT",
+ "BORDER", "USEMAP", "START_MAP", "END_MAP", "NAME", "AREA", "SHAPE",
+ "COORDS", "ALT", "HREF", "NOHREF", "TARGET", "ONMOUSEOVER", "ONMOUSEOUT",
+ "ONFOCUS", "ONBLUR", "AUTHOR", "DESCRIPTION", "BEGIN_COMMENT",
+ "END_COMMENT", "FLOAT", "STRING", "'<'", "'='", "'>'", "'/'", "$accept",
+ "csim_file", "image", "image_tags", "image_tag", "image_width",
+ "image_height", "integer_value", "start_map", "comment_lines",
+ "comment_line", "real_comment", "author_line", "description_line",
+ "area_list", "area", "xhtml_close", "tag_list", "tag", "shape_tag",
+ "coords_tag", "href_tag", "nohref_tag", "optional_value", "alt_tag",
+ "target_tag", "onmouseover_tag", "onmouseout_tag", "onfocus_tag",
+ "onblur_tag", "end_map", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 60,
+ 61, 62, 47
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 33, 34, 35, 36, 36, 37, 37, 37, 37,
+ 37, 38, 39, 40, 40, 41, 42, 42, 43, 43,
+ 43, 44, 45, 46, 47, 47, 48, 49, 49, 50,
+ 50, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 60, 61, 62, 63
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 5, 7, 0, 2, 1, 1, 3, 3,
+ 3, 3, 3, 1, 1, 6, 0, 2, 1, 1,
+ 1, 3, 3, 3, 0, 2, 4, 1, 2, 0,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 3, 2, 0, 2, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 0, 0, 0, 0, 1, 0, 16, 0, 0,
+ 24, 0, 0, 0, 0, 0, 17, 20, 18, 19,
+ 0, 4, 0, 0, 0, 0, 0, 25, 2, 0,
+ 0, 22, 23, 21, 0, 29, 0, 0, 0, 0,
+ 0, 27, 0, 5, 6, 7, 3, 15, 53, 0,
+ 0, 0, 0, 0, 0, 28, 0, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0, 26, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 13, 14,
+ 11, 12, 8, 9, 10, 0, 0, 0, 0, 0,
+ 44, 0, 0, 0, 0, 0, 41, 42, 47, 43,
+ 46, 48, 49, 50, 51, 52
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 2, 3, 29, 43, 44, 45, 80, 7, 10,
+ 16, 17, 18, 19, 20, 27, 46, 49, 67, 68,
+ 69, 70, 71, 90, 72, 73, 74, 75, 76, 77,
+ 28
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -29
+static const yytype_int8 yypact[] =
+{
+ -21, 22, 28, 2, 29, -29, 23, -29, 4, 24,
+ -19, 8, 7, 10, 11, 12, -29, -29, -29, -29,
+ 13, -29, 15, 18, 19, 20, -3, -29, -29, -5,
+ 16, -29, -29, -29, 17, -29, 21, 25, 26, 27,
+ 30, -29, 31, -29, -29, -29, -29, -29, -29, -2,
+ -6, -6, -6, 33, 35, -29, 34, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, 47, 48, 49, 50, 51,
+ -29, 52, 53, 54, 55, 56, -29, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -29, -29, -29, -29, -29, -29, -29, -28, -29, -29,
+ -29, -29, -29, -29, -29, -29, -8, -29, -29, -29,
+ -29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
+ -29
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 36, 37, 38, 39, 13, 14, 15, 34, 1, 35,
+ 40, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 78, 79, 81, 82, 4, 41, 42, 5, 41,
+ 42, 6, 9, 8, 11, 12, 21, 22, 23, 24,
+ 25, 66, 26, 30, 31, 32, 33, 47, 48, 0,
+ 0, 50, 0, 0, 0, 51, 52, 53, 0, 0,
+ 54, 83, 55, 84, 85, 0, 86, 87, 88, 89,
+ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-29))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 5, 6, 7, 8, 23, 24, 25, 10, 29, 12,
+ 15, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 27, 28, 51, 52, 3, 31, 32, 0, 31,
+ 32, 29, 9, 4, 30, 11, 28, 30, 28, 28,
+ 28, 49, 29, 28, 26, 26, 26, 31, 31, -1,
+ -1, 30, -1, -1, -1, 30, 30, 30, -1, -1,
+ 30, 28, 31, 28, 30, -1, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 29, 34, 35, 3, 0, 29, 41, 4, 9,
+ 42, 30, 11, 23, 24, 25, 43, 44, 45, 46,
+ 47, 28, 30, 28, 28, 28, 29, 48, 63, 36,
+ 28, 26, 26, 26, 10, 12, 5, 6, 7, 8,
+ 15, 31, 32, 37, 38, 39, 49, 31, 31, 50,
+ 30, 30, 30, 30, 30, 31, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 49, 51, 52, 53,
+ 54, 55, 57, 58, 59, 60, 61, 62, 27, 28,
+ 40, 40, 40, 28, 28, 30, 30, 30, 30, 30,
+ 56, 30, 30, 30, 30, 30, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 3:
+/* Line 1787 of yacc.c */
+#line 70 "imap_csim.y"
+ {
+ g_strreplace(&_map_info->image_name, (yyvsp[(5) - (7)].id));
+ g_free ((yyvsp[(5) - (7)].id));
+ }
+ break;
+
+ case 8:
+/* Line 1787 of yacc.c */
+#line 82 "imap_csim.y"
+ {}
+ break;
+
+ case 9:
+/* Line 1787 of yacc.c */
+#line 83 "imap_csim.y"
+ { g_free ((yyvsp[(3) - (3)].id)); }
+ break;
+
+ case 10:
+/* Line 1787 of yacc.c */
+#line 84 "imap_csim.y"
+ { g_free ((yyvsp[(3) - (3)].id)); }
+ break;
+
+ case 11:
+/* Line 1787 of yacc.c */
+#line 88 "imap_csim.y"
+ {
+ _map_info->old_image_width = (yyvsp[(3) - (3)].val);
+ }
+ break;
+
+ case 12:
+/* Line 1787 of yacc.c */
+#line 94 "imap_csim.y"
+ {
+ _map_info->old_image_height = (yyvsp[(3) - (3)].val);
+ }
+ break;
+
+ case 13:
+/* Line 1787 of yacc.c */
+#line 100 "imap_csim.y"
+ {
+ (yyval.val) = (gint) (yyvsp[(1) - (1)].value);
+ }
+ break;
+
+ case 14:
+/* Line 1787 of yacc.c */
+#line 104 "imap_csim.y"
+ {
+ (yyval.val) = (gint) g_ascii_strtod ((yyvsp[(1) - (1)].id), NULL);
+ g_free ((yyvsp[(1) - (1)].id));
+ }
+ break;
+
+ case 15:
+/* Line 1787 of yacc.c */
+#line 111 "imap_csim.y"
+ {
+ g_strreplace(&_map_info->title, (yyvsp[(5) - (6)].id));
+ g_free ((yyvsp[(5) - (6)].id));
+ }
+ break;
+
+ case 21:
+/* Line 1787 of yacc.c */
+#line 127 "imap_csim.y"
+ {
+ g_free ((yyvsp[(2) - (3)].id));
+ }
+ break;
+
+ case 22:
+/* Line 1787 of yacc.c */
+#line 133 "imap_csim.y"
+ {
+ g_strreplace(&_map_info->author, (yyvsp[(2) - (3)].id));
+ g_free ((yyvsp[(2) - (3)].id));
+ }
+ break;
+
+ case 23:
+/* Line 1787 of yacc.c */
+#line 140 "imap_csim.y"
+ {
+ gchar *description;
+
+ description = g_strconcat(_map_info->description, (yyvsp[(2) - (3)].id), "\n",
+ NULL);
+ g_strreplace(&_map_info->description, description);
+ g_free ((yyvsp[(2) - (3)].id));
+ }
+ break;
+
+ case 26:
+/* Line 1787 of yacc.c */
+#line 155 "imap_csim.y"
+ {
+ if (current_type != UNDEFINED)
+ add_shape(current_object);
+ }
+ break;
+
+ case 41:
+/* Line 1787 of yacc.c */
+#line 182 "imap_csim.y"
+ {
+ if (!g_ascii_strcasecmp((yyvsp[(3) - (3)].id), "RECT")) {
+ current_object = create_rectangle(0, 0, 0, 0);
+ current_type = RECTANGLE;
+ } else if (!g_ascii_strcasecmp((yyvsp[(3) - (3)].id), "CIRCLE")) {
+ current_object = create_circle(0, 0, 0);
+ current_type = CIRCLE;
+ } else if (!g_ascii_strcasecmp((yyvsp[(3) - (3)].id), "POLY")) {
+ current_object = create_polygon(NULL);
+ current_type = POLYGON;
+ } else if (!g_ascii_strcasecmp((yyvsp[(3) - (3)].id), "DEFAULT")) {
+ current_type = UNDEFINED;
+ }
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 42:
+/* Line 1787 of yacc.c */
+#line 200 "imap_csim.y"
+ {
+ char *p;
+ if (current_type == RECTANGLE) {
+ Rectangle_t *rectangle;
+
+ rectangle = ObjectToRectangle(current_object);
+ p = strtok((yyvsp[(3) - (3)].id), ",");
+ rectangle->x = atoi(p);
+ p = strtok(NULL, ",");
+ rectangle->y = atoi(p);
+ p = strtok(NULL, ",");
+ rectangle->width = atoi(p) - rectangle->x;
+ p = strtok(NULL, ",");
+ rectangle->height = atoi(p) - rectangle->y;
+ } else if (current_type == CIRCLE) {
+ Circle_t *circle;
+
+ circle = ObjectToCircle(current_object);
+ p = strtok((yyvsp[(3) - (3)].id), ",");
+ circle->x = atoi(p);
+ p = strtok(NULL, ",");
+ circle->y = atoi(p);
+ p = strtok(NULL, ",");
+ circle->r = atoi(p);
+ } else if (current_type == POLYGON) {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GList *points;
+ GdkPoint *point, *first;
+ gint x, y;
+
+ p = strtok((yyvsp[(3) - (3)].id), ",");
+ x = atoi(p);
+ p = strtok(NULL, ",");
+ y = atoi(p);
+ point = new_point(x, y);
+ points = g_list_append(NULL, (gpointer) point);
+
+ while(1) {
+ p = strtok(NULL, ",");
+ if (!p)
+ break;
+ x = atoi(p);
+ p = strtok(NULL, ",");
+ y = atoi(p);
+ point = new_point(x, y);
+ points = g_list_append(points, (gpointer) point);
+ }
+ /* Remove last point if duplicate */
+ first = (GdkPoint*) points->data;
+ polygon->points = points;
+ if (first->x == point->x && first->y == point->y)
+ polygon_remove_last_point(polygon);
+ polygon->points = points;
+ }
+
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 43:
+/* Line 1787 of yacc.c */
+#line 260 "imap_csim.y"
+ {
+ if (current_type == UNDEFINED) {
+ g_strreplace(&_map_info->default_url, (yyvsp[(3) - (3)].id));
+ } else {
+ object_set_url(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ }
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 44:
+/* Line 1787 of yacc.c */
+#line 271 "imap_csim.y"
+ {
+ }
+ break;
+
+ case 46:
+/* Line 1787 of yacc.c */
+#line 277 "imap_csim.y"
+ {
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 47:
+/* Line 1787 of yacc.c */
+#line 283 "imap_csim.y"
+ {
+ object_set_comment(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 48:
+/* Line 1787 of yacc.c */
+#line 290 "imap_csim.y"
+ {
+ object_set_target(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 49:
+/* Line 1787 of yacc.c */
+#line 297 "imap_csim.y"
+ {
+ object_set_mouse_over(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 50:
+/* Line 1787 of yacc.c */
+#line 304 "imap_csim.y"
+ {
+ object_set_mouse_out(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 51:
+/* Line 1787 of yacc.c */
+#line 311 "imap_csim.y"
+ {
+ object_set_focus(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+ case 52:
+/* Line 1787 of yacc.c */
+#line 318 "imap_csim.y"
+ {
+ object_set_blur(current_object, unescape_text((yyvsp[(3) - (3)].id)));
+ g_free ((yyvsp[(3) - (3)].id));
+ }
+ break;
+
+
+/* Line 1787 of yacc.c */
+#line 1831 "y.tab.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+/* Line 2048 of yacc.c */
+#line 327 "imap_csim.y"
+
+
+static void
+csim_error(char* s)
+{
+ extern FILE *csim_in;
+ csim_restart(csim_in);
+}
+
+gboolean
+load_csim (const char* filename)
+{
+ gboolean status;
+ extern FILE *csim_in;
+ csim_in = g_fopen(filename, "r");
+ if (csim_in) {
+ _map_info = get_map_info();
+ status = !csim_parse();
+ fclose(csim_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
+
+static gchar*
+unescape_text (gchar *input)
+{
+ /*
+ * We "unescape" simple things "in place", knowing that unescaped
+ * strings always are shorter than the original input.
+ *
+ * It is a shame there is no g_markup_unescape_text() function, but
+ * instead you have to create a full GMarkupParser/Context.
+ */
+ struct token {
+ const char *escaped;
+ const char unescaped;
+ };
+ const struct token tab[] = {
+ { "&quot;", '"' },
+ { "&apos;", '\'' },
+ { "&amp;", '&' },
+ { "&lt;", '<' },
+ { "&gt;", '>' }
+ };
+
+ size_t i;
+ for (i = 0; i < (sizeof tab / sizeof tab[0]); i++)
+ {
+ const size_t escaped_len = strlen (tab[i].escaped);
+ char *p;
+
+ /* FIXME: The following code does not perform a UTF-8 substring
+ search. */
+ for (p = strstr (input, tab[i].escaped);
+ p != NULL;
+ p = strstr (p, tab[i].escaped))
+ {
+ size_t copy_len;
+ *p++ = tab[i].unescaped;
+ copy_len = strlen (p) - escaped_len + 2;
+ memmove (p, p + escaped_len - 1, copy_len);
+ if (*p == 0)
+ break;
+ }
+ }
+
+ return input;
+}
+
diff --git a/plug-ins/imagemap/imap_csim_parse.h b/plug-ins/imagemap/imap_csim_parse.h
new file mode 100644
index 0000000..f83d108
--- /dev/null
+++ b/plug-ins/imagemap/imap_csim_parse.h
@@ -0,0 +1,142 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef CSIM_Y_TAB_H
+# define CSIM_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int csim_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ IMG = 258,
+ SRC = 259,
+ WIDTH = 260,
+ HEIGHT = 261,
+ BORDER = 262,
+ USEMAP = 263,
+ START_MAP = 264,
+ END_MAP = 265,
+ NAME = 266,
+ AREA = 267,
+ SHAPE = 268,
+ COORDS = 269,
+ ALT = 270,
+ HREF = 271,
+ NOHREF = 272,
+ TARGET = 273,
+ ONMOUSEOVER = 274,
+ ONMOUSEOUT = 275,
+ ONFOCUS = 276,
+ ONBLUR = 277,
+ AUTHOR = 278,
+ DESCRIPTION = 279,
+ BEGIN_COMMENT = 280,
+ END_COMMENT = 281,
+ FLOAT = 282,
+ STRING = 283
+ };
+#endif
+/* Tokens. */
+#define IMG 258
+#define SRC 259
+#define WIDTH 260
+#define HEIGHT 261
+#define BORDER 262
+#define USEMAP 263
+#define START_MAP 264
+#define END_MAP 265
+#define NAME 266
+#define AREA 267
+#define SHAPE 268
+#define COORDS 269
+#define ALT 270
+#define HREF 271
+#define NOHREF 272
+#define TARGET 273
+#define ONMOUSEOVER 274
+#define ONMOUSEOUT 275
+#define ONFOCUS 276
+#define ONBLUR 277
+#define AUTHOR 278
+#define DESCRIPTION 279
+#define BEGIN_COMMENT 280
+#define END_COMMENT 281
+#define FLOAT 282
+#define STRING 283
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 2049 of yacc.c */
+#line 49 "imap_csim.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 2049 of yacc.c */
+#line 120 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE csim_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int csim_parse (void *YYPARSE_PARAM);
+#else
+int csim_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int csim_parse (void);
+#else
+int csim_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !CSIM_Y_TAB_H */
diff --git a/plug-ins/imagemap/imap_default_dialog.c b/plug-ins/imagemap/imap_default_dialog.c
new file mode 100644
index 0000000..5fff183
--- /dev/null
+++ b/plug-ins/imagemap/imap_default_dialog.c
@@ -0,0 +1,184 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_default_dialog.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void
+dialog_response (GtkWidget *widget,
+ gint response_id,
+ DefaultDialog_t *dialog)
+{
+ switch (response_id)
+ {
+ case GTK_RESPONSE_APPLY:
+ if (dialog->apply_cb)
+ dialog->apply_cb (dialog->apply_cb_data);
+ else if (dialog->ok_cb)
+ dialog->ok_cb (dialog->ok_cb_data);
+ break;
+
+ case GTK_RESPONSE_OK:
+ gtk_widget_hide (dialog->dialog);
+ if (dialog->ok_cb)
+ dialog->ok_cb (dialog->ok_cb_data);
+ break;
+
+ default:
+ gtk_widget_hide (dialog->dialog);
+ if (dialog->cancel_cb)
+ dialog->cancel_cb (dialog->cancel_cb_data);
+ break;
+ }
+}
+
+void
+default_dialog_set_ok_cb(DefaultDialog_t *dialog, void (*ok_cb)(gpointer),
+ gpointer ok_cb_data)
+{
+ dialog->ok_cb = ok_cb;
+ dialog->ok_cb_data = ok_cb_data;
+}
+
+void
+default_dialog_set_apply_cb(DefaultDialog_t *dialog,
+ void (*apply_cb)(gpointer),
+ gpointer apply_cb_data)
+{
+ dialog->apply_cb = apply_cb;
+ dialog->apply_cb_data = apply_cb_data;
+}
+
+void
+default_dialog_set_cancel_cb(DefaultDialog_t *dialog,
+ void (*cancel_cb)(gpointer),
+ gpointer cancel_cb_data)
+{
+ dialog->cancel_cb = cancel_cb;
+ dialog->cancel_cb_data = cancel_cb_data;
+}
+
+DefaultDialog_t *
+make_default_dialog (const gchar *title)
+{
+ DefaultDialog_t *data = g_new0 (DefaultDialog_t, 1);
+
+ data->ok_cb = NULL;
+ data->apply_cb = NULL;
+ data->cancel_cb = NULL;
+
+ data->dialog = gimp_dialog_new (title, PLUG_IN_ROLE,
+ get_dialog(), 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+ NULL);
+
+ data->apply = gtk_dialog_add_button (GTK_DIALOG (data->dialog),
+ _("_Apply"), GTK_RESPONSE_APPLY);
+
+ data->cancel = gtk_dialog_add_button (GTK_DIALOG (data->dialog),
+ _("_Cancel"), GTK_RESPONSE_CANCEL);
+
+ data->ok = gtk_dialog_add_button (GTK_DIALOG (data->dialog),
+ _("_OK"), GTK_RESPONSE_OK);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (data->dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_APPLY,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (data->dialog, "response",
+ G_CALLBACK (dialog_response),
+ data);
+ g_signal_connect (data->dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &data->dialog);
+
+ data->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (data->vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))),
+ data->vbox, TRUE, TRUE, 0);
+ gtk_widget_show (data->vbox);
+
+ return data;
+}
+
+void
+default_dialog_show(DefaultDialog_t *dialog)
+{
+ gtk_widget_show(dialog->dialog);
+}
+
+void
+default_dialog_hide_cancel_button(DefaultDialog_t *dialog)
+{
+ gtk_widget_hide(dialog->cancel);
+}
+
+void
+default_dialog_hide_apply_button(DefaultDialog_t *dialog)
+{
+ gtk_widget_hide(dialog->apply);
+}
+
+void
+default_dialog_hide_help_button(DefaultDialog_t *dialog)
+{
+ /* gtk_widget_hide(dialog->help); */
+}
+
+void
+default_dialog_set_title(DefaultDialog_t *dialog, const gchar *title)
+{
+ gtk_window_set_title(GTK_WINDOW(dialog->dialog), title);
+}
+
+void
+default_dialog_set_label(DefaultDialog_t *dialog, const gchar *text)
+{
+ GtkWidget *label = gtk_label_new(text);
+
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show(label);
+}
+
+GtkWidget*
+default_dialog_add_table(DefaultDialog_t *dialog, gint rows, gint cols)
+{
+ GtkWidget *table = gtk_table_new (rows, cols, FALSE);
+
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+
+ gtk_box_pack_start (GTK_BOX (dialog->vbox), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ return table;
+}
diff --git a/plug-ins/imagemap/imap_default_dialog.h b/plug-ins/imagemap/imap_default_dialog.h
new file mode 100644
index 0000000..a1b4ac5
--- /dev/null
+++ b/plug-ins/imagemap/imap_default_dialog.h
@@ -0,0 +1,59 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_DEFAULT_DIALOG_H
+#define _IMAP_DEFAULT_DIALOG_H
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *ok;
+ GtkWidget *apply;
+ GtkWidget *cancel;
+ void (*ok_cb)(gpointer);
+ gpointer ok_cb_data;
+ void (*apply_cb)(gpointer);
+ gpointer apply_cb_data;
+ void (*cancel_cb)(gpointer);
+ gpointer cancel_cb_data;
+} DefaultDialog_t;
+
+DefaultDialog_t *make_default_dialog(const gchar *title);
+void default_dialog_set_ok_cb(DefaultDialog_t *dialog, void (*ok_cb)(gpointer),
+ gpointer ok_cb_data);
+void default_dialog_set_apply_cb(DefaultDialog_t *dialog,
+ void (*apply_cb)(gpointer),
+ gpointer apply_cb_data);
+void default_dialog_set_cancel_cb(DefaultDialog_t *dialog,
+ void (*ok_cb)(gpointer),
+ gpointer ok_cb_data);
+void default_dialog_show(DefaultDialog_t *dialog);
+void default_dialog_hide_cancel_button(DefaultDialog_t *dialog);
+void default_dialog_hide_apply_button(DefaultDialog_t *dialog);
+void default_dialog_hide_help_button(DefaultDialog_t *dialog);
+void default_dialog_set_title(DefaultDialog_t *dialog, const gchar *title);
+void default_dialog_set_label(DefaultDialog_t *dialog, const gchar *text);
+GtkWidget *default_dialog_add_table(DefaultDialog_t *dialog, gint rows,
+ gint cols);
+
+#endif /* _IMAP_DEFAULT_DIALOG_H */
diff --git a/plug-ins/imagemap/imap_edit_area_info.c b/plug-ins/imagemap/imap_edit_area_info.c
new file mode 100644
index 0000000..698dae6
--- /dev/null
+++ b/plug-ins/imagemap/imap_edit_area_info.c
@@ -0,0 +1,515 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_browse.h"
+#include "imap_commands.h"
+#include "imap_default_dialog.h"
+#include "imap_edit_area_info.h"
+#include "imap_main.h"
+#include "imap_stock.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static gboolean callback_lock;
+
+
+static gchar*
+relative_filter(const char *name, gpointer data)
+{
+ AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->relative_link)))
+ return g_path_get_basename(name);
+ return g_strdup (name);
+}
+
+static void
+url_changed(GtkWidget *widget, gpointer data)
+{
+ AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
+ const gchar *url = gtk_entry_get_text(GTK_ENTRY(param->url));
+ GtkWidget *button;
+
+ if (!g_ascii_strncasecmp(url, "http://", sizeof("http://") - 1))
+ button = param->web_site;
+ else if (!g_ascii_strncasecmp(url, "ftp://", sizeof("ftp://") - 1))
+ button = param->ftp_site;
+ else if (!g_ascii_strncasecmp(url, "gopher://", sizeof("gopher://") - 1))
+ button = param->gopher;
+ else if (!g_ascii_strncasecmp(url, "file:/", sizeof("file:/") - 1))
+ button = param->file;
+ else if (!g_ascii_strncasecmp(url, "wais://", sizeof("wais://") - 1))
+ button = param->wais;
+ else if (!g_ascii_strncasecmp(url, "telnet://", sizeof("telnet://") - 1))
+ button = param->telnet;
+ else if (!g_ascii_strncasecmp(url, "mailto:", sizeof("mailto:") - 1))
+ button = param->email;
+ else
+ button = param->other;
+
+ callback_lock = TRUE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+}
+
+static void
+set_url(GtkWidget *widget, AreaInfoDialog_t *param, const gchar *prefix)
+{
+ if (callback_lock)
+ {
+ callback_lock = FALSE;
+ }
+ else
+ {
+ if (gtk_widget_get_state (widget) & GTK_STATE_SELECTED)
+ {
+ char *p;
+ gchar *url = g_strdup(gtk_entry_get_text(GTK_ENTRY(param->url)));
+
+ p = strstr(url, "//"); /* 'http://' */
+ if (p)
+ {
+ p += 2;
+ }
+ else
+ {
+ p = strchr(url, ':'); /* 'mailto:' */
+ if (p)
+ {
+ p++;
+ if (*p == '/') /* 'file:/' */
+ p++;
+ }
+ else
+ {
+ p = url;
+ }
+ }
+ p = g_strconcat(prefix, p, NULL);
+ gtk_entry_set_text(GTK_ENTRY(param->url), p);
+ g_free(p);
+ g_free(url);
+ }
+ }
+ gtk_widget_grab_focus(param->url);
+}
+
+static void
+select_web_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "http://");
+}
+
+static void
+select_ftp_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "ftp://");
+}
+
+static void
+select_gopher_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "gopher://");
+}
+
+static void
+select_other_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "");
+}
+
+static void
+select_file_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "file:/");
+}
+
+static void
+select_wais_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "wais://");
+}
+
+static void
+select_telnet_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "telnet://");
+}
+
+static void
+select_email_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ set_url(widget, param, "mailto:");
+}
+
+static void
+append_page (GtkWidget *notebook, GtkWidget *page, const gchar *icon_name,
+ const gchar *label_name)
+{
+ GtkWidget *hbox, *icon, *label;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_widget_show(hbox);
+
+ icon = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+ gtk_widget_show (icon);
+
+ label = gtk_label_new_with_mnemonic (label_name);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, hbox);
+}
+
+static void
+create_link_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
+{
+ BrowseWidget_t *browse;
+ GtkWidget *table, *label;
+ GtkWidget *subtable, *frame;
+ GSList *group;
+
+ table = gtk_table_new(11, 1, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_widget_show(table);
+
+ frame = gimp_frame_new(_("Link Type"));
+ gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 0, 1);
+ gtk_widget_show(frame);
+
+ subtable = gtk_table_new(2, 4, FALSE);
+ gtk_container_add (GTK_CONTAINER(frame), subtable);
+ gtk_widget_show(subtable);
+
+ dialog->web_site = create_radio_button_in_table(subtable, NULL, 0, 0,
+ _("_Web Site"));
+ g_signal_connect(dialog->web_site, "toggled",
+ G_CALLBACK (select_web_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->web_site));
+
+ dialog->ftp_site = create_radio_button_in_table(subtable, group, 0, 1,
+ _("_Ftp Site"));
+ g_signal_connect(dialog->ftp_site, "toggled",
+ G_CALLBACK (select_ftp_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->ftp_site));
+
+ dialog->gopher = create_radio_button_in_table(subtable, group, 0, 2,
+ _("_Gopher"));
+ g_signal_connect(dialog->gopher, "toggled",
+ G_CALLBACK (select_gopher_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->gopher));
+
+ dialog->other = create_radio_button_in_table(subtable, group, 0, 3,
+ _("Ot_her"));
+ g_signal_connect(dialog->other, "toggled",
+ G_CALLBACK (select_other_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->other));
+
+ dialog->file = create_radio_button_in_table(subtable, group, 1, 0,
+ _("F_ile"));
+ g_signal_connect(dialog->file, "toggled",
+ G_CALLBACK (select_file_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->file));
+
+ dialog->wais = create_radio_button_in_table(subtable, group, 1, 1,
+ _("WAI_S"));
+ g_signal_connect(dialog->wais, "toggled",
+ G_CALLBACK (select_wais_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->wais));
+
+ dialog->telnet = create_radio_button_in_table(subtable, group, 1, 2,
+ _("Tel_net"));
+ g_signal_connect(dialog->telnet, "toggled",
+ G_CALLBACK (select_telnet_cb), (gpointer) dialog);
+ group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(dialog->telnet));
+
+ dialog->email = create_radio_button_in_table(subtable, group, 1, 3,
+ _("e-_mail"));
+ g_signal_connect(dialog->email, "toggled",
+ G_CALLBACK (select_email_cb), (gpointer) dialog);
+
+ label = create_label_in_table(
+ table, 2, 0,
+ _("_URL to activate when this area is clicked: (required)"));
+
+ browse = browse_widget_new( _("Select HTML file"));
+ browse_widget_set_filter(browse, relative_filter, (gpointer) dialog);
+ gtk_table_attach_defaults(GTK_TABLE(table), browse->hbox, 0, 1, 3, 4);
+ dialog->url = browse->file;
+ g_signal_connect(dialog->url, "changed", G_CALLBACK(url_changed),
+ dialog);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), dialog->url);
+
+ dialog->relative_link = create_check_button_in_table(table, 4, 0,
+ _("Relati_ve link"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->relative_link),
+ TRUE);
+
+ label = create_label_in_table(
+ table, 6, 0,
+ _("_Target frame name/ID: (optional - used for FRAMES only)"));
+ dialog->target = create_entry_in_table(table, label, 7, 0);
+
+ label = create_label_in_table(table, 9, 0, _("ALT te_xt: (optional)"));
+ dialog->comment = create_entry_in_table(table, label, 10, 0);
+
+ append_page (notebook, table, GIMP_ICON_WEB, _("_Link"));
+}
+
+static void
+geometry_changed(Object_t *obj, gpointer data)
+{
+ AreaInfoDialog_t *dialog = (AreaInfoDialog_t*) data;
+ if (dialog->geometry_lock) {
+ dialog->geometry_lock = FALSE;
+ } else {
+ if (dialog->obj == obj) {
+ object_update_info_widget(obj, dialog->infotab);
+ obj->class->assign(obj, dialog->clone);
+ }
+ }
+}
+
+static void
+toggle_preview_cb(GtkWidget *widget, AreaInfoDialog_t *param)
+{
+ param->preview = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ edit_area_info_dialog_emit_geometry_signal(param);
+}
+
+static void
+create_info_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
+{
+ GtkWidget *vbox, *frame, *preview;
+ Object_t *obj = dialog->obj;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+ gtk_widget_show(vbox);
+
+ frame = gimp_frame_new(_("Dimensions"));
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show(frame);
+
+ preview = gtk_check_button_new_with_mnemonic(_("Pre_view"));
+ g_signal_connect(preview, "toggled",
+ G_CALLBACK (toggle_preview_cb), (gpointer) dialog);
+ gtk_box_pack_start(GTK_BOX(vbox), preview, FALSE, FALSE, 0);
+ gtk_widget_show(preview);
+
+ dialog->infotab = obj->class->create_info_widget(frame);
+
+ append_page (notebook, vbox, obj->class->get_stock_icon_name (),
+ gettext (obj->class->name));
+}
+
+static void
+create_java_script_tab(AreaInfoDialog_t *dialog, GtkWidget *notebook)
+{
+ GtkWidget *vbox, *table, *label;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ gtk_widget_show(vbox);
+
+ table = gtk_table_new(11, 1, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_widget_show(table);
+
+ label = create_label_in_table(table, 0, 0, "o_nMouseover:");
+ dialog->mouse_over = create_entry_in_table(table, label, 1, 0);
+
+ label = create_label_in_table(table, 3, 0, "on_Mouseout:");
+ dialog->mouse_out = create_entry_in_table(table, label, 4, 0);
+
+ label = create_label_in_table(table, 6, 0, "on_Focus (HTML 4.0):");
+ dialog->focus = create_entry_in_table(table, label, 7, 0);
+
+ label = create_label_in_table(table, 9, 0, "on_Blur (HTML 4.0):");
+ dialog->blur = create_entry_in_table(table, label, 10, 0);
+
+ append_page (notebook, vbox, IMAP_STOCK_JAVA, _("_JavaScript"));
+}
+
+static gboolean
+object_was_changed(AreaInfoDialog_t *dialog)
+{
+ Object_t *clone = dialog->clone;
+ Object_t *obj = dialog->obj;
+ gint old_x, old_y, old_width, old_height;
+ gint new_x, new_y, new_width, new_height;
+
+ object_get_dimensions(clone, &old_x, &old_y, &old_width, &old_height);
+ object_get_dimensions(obj, &new_x, &new_y, &new_width, &new_height);
+
+ return new_x != old_x || new_y != old_y || new_width != old_width ||
+ new_height != old_height || clone->selected != obj->selected;
+}
+
+static void
+edit_area_apply_cb(gpointer data)
+{
+ AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
+ Object_t *obj = param->obj;
+
+ object_set_url(obj, gtk_entry_get_text(GTK_ENTRY(param->url)));
+ object_set_target(obj, gtk_entry_get_text(GTK_ENTRY(param->target)));
+ object_set_comment(obj, gtk_entry_get_text(GTK_ENTRY(param->comment)));
+ object_set_mouse_over(obj,
+ gtk_entry_get_text(GTK_ENTRY(param->mouse_over)));
+ object_set_mouse_out(obj, gtk_entry_get_text(GTK_ENTRY(param->mouse_out)));
+ object_set_focus(obj, gtk_entry_get_text(GTK_ENTRY(param->focus)));
+ object_set_blur(obj, gtk_entry_get_text(GTK_ENTRY(param->blur)));
+ object_update(obj, param->infotab);
+ update_shape(obj);
+
+ if (object_was_changed(param))
+ preview_redraw();
+}
+
+static void
+edit_area_ok_cb(gpointer data)
+{
+ AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
+ Object_t *obj = param->obj;
+
+ object_list_remove_geometry_cb(obj->list, param->geometry_cb_id);
+
+ /* Fix me: nasty hack */
+ if (param->add)
+ command_list_add(edit_object_command_new(obj));
+
+ edit_area_apply_cb(data);
+ object_unlock(obj);
+ object_unref(param->clone);
+}
+
+static void
+edit_area_cancel_cb(gpointer data)
+{
+ AreaInfoDialog_t *dialog = (AreaInfoDialog_t*) data;
+ Object_t *obj = dialog->obj;
+ gboolean changed = object_was_changed(dialog);
+ gboolean selected = obj->selected;
+
+ object_list_remove_geometry_cb(obj->list, dialog->geometry_cb_id);
+ object_unlock(obj);
+ object_assign(dialog->clone, obj);
+ obj->selected = selected;
+ object_unref(dialog->clone);
+
+ if (changed)
+ preview_redraw();
+}
+
+static void
+switch_page(GtkWidget *widget, gpointer page, gint page_num,
+ gpointer data)
+{
+ AreaInfoDialog_t *param = (AreaInfoDialog_t*) data;
+ if (page_num == 0) {
+ gtk_widget_grab_focus(param->url);
+ } else if (page_num == 1) {
+ Object_t *obj = param->obj;
+ obj->class->set_initial_focus(obj, param->infotab);
+ } else {
+ gtk_widget_grab_focus(param->mouse_over);
+ }
+}
+
+AreaInfoDialog_t*
+create_edit_area_info_dialog(Object_t *obj)
+{
+ AreaInfoDialog_t *data = g_new(AreaInfoDialog_t, 1);
+ GtkWidget *notebook;
+
+ data->geometry_lock = FALSE;
+ data->preview = FALSE;
+ data->obj = obj;
+ data->browse = NULL;
+ data->dialog = make_default_dialog(_("Area Settings"));
+ default_dialog_set_ok_cb(data->dialog, edit_area_ok_cb, data);
+ default_dialog_set_apply_cb(data->dialog, edit_area_apply_cb, data);
+ default_dialog_set_cancel_cb(data->dialog, edit_area_cancel_cb, data);
+
+ data->notebook = notebook = gtk_notebook_new();
+ g_signal_connect_after(notebook, "switch-page",
+ G_CALLBACK(switch_page), (gpointer) data);
+
+ gtk_box_pack_start(GTK_BOX(data->dialog->vbox), notebook, TRUE, TRUE, 0);
+ create_link_tab(data, notebook);
+ create_info_tab(data, notebook);
+ create_java_script_tab(data, notebook);
+ gtk_widget_show(notebook);
+
+ return data;
+}
+
+void
+edit_area_info_dialog_show(AreaInfoDialog_t *dialog, Object_t *obj,
+ gboolean add)
+{
+ gchar *title;
+
+ object_unlock(dialog->obj);
+ object_lock(obj);
+ dialog->obj = obj;
+ dialog->clone = object_clone(obj);
+ dialog->add = add;
+ object_fill_info_tab(obj, dialog->infotab);
+ gtk_entry_set_text(GTK_ENTRY(dialog->url), obj->url);
+ gtk_entry_set_text(GTK_ENTRY(dialog->target), obj->target);
+ gtk_entry_set_text(GTK_ENTRY(dialog->comment), obj->comment);
+ gtk_entry_set_text(GTK_ENTRY(dialog->mouse_over), obj->mouse_over);
+ gtk_entry_set_text(GTK_ENTRY(dialog->mouse_out), obj->mouse_out);
+ gtk_entry_set_text(GTK_ENTRY(dialog->focus), obj->focus);
+ gtk_entry_set_text(GTK_ENTRY(dialog->blur), obj->blur);
+ gtk_widget_grab_focus(dialog->url);
+
+ dialog->geometry_cb_id =
+ object_list_add_geometry_cb(obj->list, geometry_changed, dialog);
+
+ title = g_strdup_printf (_("Area #%d Settings"),
+ object_get_position_in_list(obj) + 1);
+ default_dialog_set_title(dialog->dialog, title);
+ g_free (title);
+ default_dialog_show(dialog->dialog);
+}
+
+void
+edit_area_info_dialog_emit_geometry_signal(AreaInfoDialog_t *dialog)
+{
+ if (dialog->preview) {
+ dialog->geometry_lock = TRUE;
+ object_emit_geometry_signal(dialog->obj);
+ }
+}
diff --git a/plug-ins/imagemap/imap_edit_area_info.h b/plug-ins/imagemap/imap_edit_area_info.h
new file mode 100644
index 0000000..fbf45fb
--- /dev/null
+++ b/plug-ins/imagemap/imap_edit_area_info.h
@@ -0,0 +1,66 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_EDIT_AREA_INFO_H
+#define _IMAP_EDIT_AREA_INFO_H
+
+typedef struct AreaInfoDialog_t AreaInfoDialog_t;
+
+#include "imap_default_dialog.h"
+#include "imap_object.h"
+
+struct AreaInfoDialog_t {
+ DefaultDialog_t *dialog;
+ Object_t *obj;
+ Object_t *clone;
+ gboolean add;
+ gboolean geometry_lock;
+ gboolean preview;
+
+ GtkWidget *notebook;
+ GtkWidget *web_site;
+ GtkWidget *ftp_site;
+ GtkWidget *gopher;
+ GtkWidget *other;
+ GtkWidget *file;
+ GtkWidget *wais;
+ GtkWidget *telnet;
+ GtkWidget *email;
+ GtkWidget *url;
+ GtkWidget *relative_link;
+ GtkWidget *target;
+ GtkWidget *comment;
+ GtkWidget *mouse_over;
+ GtkWidget *mouse_out;
+ GtkWidget *focus;
+ GtkWidget *blur;
+ GtkWidget *browse;
+ gpointer infotab;
+ gpointer geometry_cb_id;
+};
+
+AreaInfoDialog_t *create_edit_area_info_dialog(Object_t *obj);
+void edit_area_info_dialog_show(AreaInfoDialog_t *dialog, Object_t *obj,
+ gboolean add);
+void edit_area_info_dialog_emit_geometry_signal(AreaInfoDialog_t *dialog);
+
+#endif /* _IMAP_EDIT_AREA_INFO_H */
diff --git a/plug-ins/imagemap/imap_file.c b/plug-ins/imagemap/imap_file.c
new file mode 100644
index 0000000..35ff832
--- /dev/null
+++ b/plug-ins/imagemap/imap_file.c
@@ -0,0 +1,180 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_file.h"
+#include "imap_main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void
+open_cb (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ if (! g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+ {
+ do_file_error_dialog (_("Error opening file"), filename);
+ g_free (filename);
+ return;
+ }
+
+ load (filename);
+ g_free (filename);
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+void
+do_file_open_dialog (void)
+{
+ static GtkWidget *dialog;
+
+ if (! dialog)
+ {
+ dialog =
+ gtk_file_chooser_dialog_new (_("Load Image Map"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (open_cb),
+ dialog);
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+save_cb (GtkWidget *dialog,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ save_as (filename);
+ g_free (filename);
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+void
+do_file_save_as_dialog (void)
+{
+ static GtkWidget *dialog;
+
+ if (! dialog)
+ {
+ gchar *filename;
+
+ dialog = gtk_file_chooser_dialog_new (_("Save Image Map"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+ TRUE);
+
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &dialog);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_cb),
+ dialog);
+
+ /* Suggest a filename based on the image name.
+ * The image name is in UTF-8 encoding.
+ */
+ filename = g_strconcat (get_image_name(), ".map", NULL);
+
+ if (filename)
+ {
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
+ filename);
+ g_free (filename);
+ }
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+void
+do_file_error_dialog (const char *error,
+ const char *filename)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new_with_markup
+ (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ error,
+ gimp_filename_to_utf8 (filename));
+
+ g_signal_connect_swapped (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ dialog);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+}
diff --git a/plug-ins/imagemap/imap_file.h b/plug-ins/imagemap/imap_file.h
new file mode 100644
index 0000000..c661eaa
--- /dev/null
+++ b/plug-ins/imagemap/imap_file.h
@@ -0,0 +1,34 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_FILE_H
+#define _IMAP_FILE_H
+
+void do_file_open_dialog(void);
+void do_file_save_as_dialog(void);
+void do_file_error_dialog(const char *error, const char *filename);
+
+gboolean load_csim (const char* filename);
+gboolean load_cern (const char* filename);
+gboolean load_ncsa (const char* filename);
+
+#endif /* _IMAP_FILE_H */
diff --git a/plug-ins/imagemap/imap_grid.c b/plug-ins/imagemap/imap_grid.c
new file mode 100644
index 0000000..48c6a54
--- /dev/null
+++ b/plug-ins/imagemap/imap_grid.c
@@ -0,0 +1,414 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_grid.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_preview.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+typedef enum {GRID_HIDDEN, GRID_LINES, GRID_CROSSES} GridType_t;
+
+typedef struct {
+ DefaultDialog_t *dialog;
+ GtkWidget *type_frame;
+ GtkWidget *granularity_frame;
+ GtkWidget *offset_frame;
+ GtkWidget *snap;
+ GtkWidget *width;
+ GtkWidget *height;
+ GtkWidget *chain_width_height;
+ GtkWidget *left;
+ GtkWidget *top;
+ GtkWidget *chain_left_top;
+ GtkWidget *hidden;
+ GtkWidget *lines;
+ GtkWidget *crosses;
+ GtkWidget *preview;
+
+ gboolean enable_preview;
+} GridDialog_t;
+
+
+static gboolean grid_snap = FALSE;
+static gint grid_width = 15;
+static gint grid_height = 15;
+static gint grid_left = 0;
+static gint grid_top = 0;
+static GridType_t grid_type = GRID_LINES;
+
+static void
+grid_settings_ok_cb(gpointer data)
+{
+ GridDialog_t *param = (GridDialog_t*) data;
+ gboolean new_snap;
+
+ new_snap = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->snap));
+ grid_width = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->width));
+ grid_height = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->height));
+ grid_left = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->left));
+ grid_top = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(param->top));
+
+ if (grid_snap != new_snap) {
+ grid_snap = new_snap;
+ menu_check_grid(grid_snap);
+ }
+ preview_redraw();
+}
+
+static void
+snap_toggled_cb(GtkWidget *widget, gpointer data)
+{
+ GridDialog_t *param = (GridDialog_t*) data;
+ gint sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+ gtk_widget_set_sensitive(param->type_frame, sensitive);
+ gtk_widget_set_sensitive(param->granularity_frame, sensitive);
+ gtk_widget_set_sensitive(param->offset_frame, sensitive);
+ gtk_widget_set_sensitive(param->preview, sensitive);
+}
+
+static void
+type_toggled_cb(GtkWidget *widget, gpointer data)
+{
+ if (gtk_widget_get_state (widget) & GTK_STATE_SELECTED)
+ {
+ grid_type = GPOINTER_TO_INT (data);
+ preview_redraw();
+ }
+}
+
+static void
+toggle_preview_cb(GtkWidget *widget, GridDialog_t *param)
+{
+ param->enable_preview =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ preview_redraw();
+}
+
+static void
+grid_assign_value(GtkWidget *widget, gpointer data, gint *value)
+{
+ GridDialog_t *dialog = (GridDialog_t*) data;
+ if (dialog->enable_preview) {
+ *value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ preview_redraw(); /* Fix me! */
+ }
+}
+
+static void
+width_changed_cb(GtkWidget *widget, gpointer data)
+{
+ GridDialog_t *dialog = (GridDialog_t*) data;
+
+ grid_assign_value(widget, data, &grid_width);
+ if (gimp_chain_button_get_active(
+ GIMP_CHAIN_BUTTON(dialog->chain_width_height))) {
+ gint value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->height), value);
+ }
+}
+
+static void
+height_changed_cb(GtkWidget *widget, gpointer data)
+{
+ GridDialog_t *dialog = (GridDialog_t*) data;
+
+ grid_assign_value(widget, data, &grid_height);
+ if (gimp_chain_button_get_active(
+ GIMP_CHAIN_BUTTON(dialog->chain_width_height))) {
+ gint value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->width), value);
+ }
+}
+
+static void
+left_changed_cb(GtkWidget *widget, gpointer data)
+{
+ GridDialog_t *dialog = (GridDialog_t*) data;
+
+ grid_assign_value(widget, data, &grid_left);
+ if (gimp_chain_button_get_active(
+ GIMP_CHAIN_BUTTON(dialog->chain_left_top))) {
+ gint value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->top), value);
+ }
+}
+
+static void
+top_changed_cb(GtkWidget *widget, gpointer data)
+{
+ GridDialog_t *dialog = (GridDialog_t*) data;
+
+ grid_assign_value(widget, data, &grid_top);
+ if (gimp_chain_button_get_active(
+ GIMP_CHAIN_BUTTON(dialog->chain_left_top))) {
+ gint value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->left), value);
+ }
+}
+
+static GridDialog_t*
+create_grid_settings_dialog(void)
+{
+ GridDialog_t *data = g_new(GridDialog_t, 1);
+ DefaultDialog_t *dialog;
+ GtkWidget *main_table, *table, *label;
+ GtkWidget *frame;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *chain_button;
+
+ data->dialog = dialog = make_default_dialog(_("Grid Settings"));
+ default_dialog_set_ok_cb(dialog, grid_settings_ok_cb, (gpointer) data);
+ main_table = default_dialog_add_table(dialog, 4, 2);
+
+ data->snap = gtk_check_button_new_with_mnemonic(_("_Snap-to grid enabled"));
+ g_signal_connect(data->snap, "toggled",
+ G_CALLBACK (snap_toggled_cb), data);
+ gtk_table_attach_defaults(GTK_TABLE(main_table), data->snap, 0, 1, 0, 1);
+ gtk_widget_show(data->snap);
+
+ data->type_frame = frame = gimp_frame_new(_("Grid Visibility and Type"));
+ gtk_widget_show(frame);
+ gtk_table_attach_defaults(GTK_TABLE(main_table), frame, 0, 2, 1, 2);
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+
+ button = gtk_radio_button_new_with_mnemonic_from_widget(NULL, _("_Hidden"));
+ data->hidden = button;
+ g_signal_connect(button, "toggled",
+ G_CALLBACK (type_toggled_cb), (gpointer) GRID_HIDDEN);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(button), _("_Lines"));
+ data->lines = button;
+ g_signal_connect(button, "toggled",
+ G_CALLBACK (type_toggled_cb), (gpointer) GRID_LINES);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(button), _("C_rosses"));
+ data->crosses = button;
+ g_signal_connect(button, "toggled",
+ G_CALLBACK (type_toggled_cb),
+ (gpointer) GRID_CROSSES);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ data->granularity_frame = frame = gimp_frame_new(_("Grid Granularity"));
+ gtk_table_attach_defaults(GTK_TABLE(main_table), frame, 0, 1, 2, 3);
+ table = gtk_table_new(2, 4, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ label = create_label_in_table(table, 0, 0, _("_Width"));
+ data->width = create_spin_button_in_table(table, label, 0, 1, 15, 1, 100);
+ g_signal_connect(data->width, "value-changed",
+ G_CALLBACK (width_changed_cb), (gpointer) data);
+ create_label_in_table(table, 0, 3, _("pixels"));
+
+ label = create_label_in_table(table, 1, 0, _("_Height"));
+ data->height = create_spin_button_in_table(table, label, 1, 1, 15, 1, 100);
+ g_signal_connect(data->height, "value-changed",
+ G_CALLBACK (height_changed_cb), (gpointer) data);
+ create_label_in_table(table, 1, 3, _("pixels"));
+
+ chain_button = gimp_chain_button_new(GIMP_CHAIN_RIGHT);
+ data->chain_width_height = chain_button;
+ gtk_table_attach_defaults(GTK_TABLE(table), chain_button, 2, 3, 0, 2);
+ gtk_widget_show(chain_button);
+
+ gtk_widget_show(table);
+ gtk_widget_show(frame);
+
+ data->offset_frame = frame = gimp_frame_new(_("Grid Offset"));
+ gtk_table_attach_defaults(GTK_TABLE(main_table), frame, 1, 2, 2, 3);
+ table = gtk_table_new(2, 3, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ label = create_label_in_table(table, 0, 2, _("pixels from l_eft"));
+ data->left = create_spin_button_in_table(table, label, 0, 0, 0, 0, 100);
+ g_signal_connect(data->left, "value-changed",
+ G_CALLBACK (left_changed_cb), (gpointer) data);
+
+ label = create_label_in_table(table, 1, 2, _("pixels from _top"));
+ data->top = create_spin_button_in_table(table, label, 1, 0, 0, 0, 100);
+ g_signal_connect(data->top, "value-changed",
+ G_CALLBACK (top_changed_cb), (gpointer) data);
+
+ chain_button = gimp_chain_button_new(GIMP_CHAIN_RIGHT);
+ data->chain_left_top = chain_button;
+ gtk_table_attach_defaults(GTK_TABLE(table), chain_button, 1, 2, 0, 2);
+ gtk_widget_show(chain_button);
+
+ data->preview = create_check_button_in_table(main_table, 3, 0,
+ _("_Preview"));
+ g_signal_connect(data->preview, "toggled",
+ G_CALLBACK (toggle_preview_cb), (gpointer) data);
+ gtk_widget_show(data->preview);
+
+ snap_toggled_cb(data->snap, data);
+
+ gtk_widget_show(table);
+ gtk_widget_show(frame);
+
+ return data;
+}
+
+void
+do_grid_settings_dialog(void)
+{
+ static GridDialog_t* dialog;
+ GtkWidget *type;
+
+ if (!dialog)
+ dialog = create_grid_settings_dialog();
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->snap), grid_snap);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->width), grid_width);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->height), grid_height);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->left), grid_left);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->top), grid_top);
+
+ if (grid_type == GRID_HIDDEN)
+ type = dialog->hidden;
+ else if (grid_type == GRID_LINES)
+ type = dialog->lines;
+ else
+ type = dialog->crosses;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(type), TRUE);
+
+ default_dialog_show(dialog->dialog);
+}
+
+static void
+draw_lines(cairo_t *cr, gint width, gint height)
+{
+ gint x, y;
+ gdouble dash = 4.;
+
+ cairo_set_dash (cr, &dash, 1, 0.);
+ for (x = grid_left % grid_width; x < width; x += grid_width)
+ draw_line(cr, x, 1, x, height);
+ for (y = grid_top % grid_height; y < height; y += grid_height)
+ draw_line(cr, 1, y, width, y);
+}
+
+static void
+draw_crosses(cairo_t *cr, gint width, gint height)
+{
+ gint x, y;
+ gdouble dash[4] = { 7., grid_height - 7., 7., grid_width - 7. };
+
+ cairo_set_dash (cr, dash, 2, 4.5 - grid_top);
+ for (x = grid_left % grid_width; x < width; x += grid_width)
+ draw_line(cr, x, 1, x, height);
+ cairo_set_dash (cr, dash+2, 2, 4.5 - grid_left);
+ for (y = grid_top % grid_height; y < height; y += grid_height)
+ draw_line(cr, 1, y, width, y);
+}
+
+void
+draw_grid(cairo_t *cr, gint width, gint height)
+{
+ if (grid_snap && grid_type != GRID_HIDDEN)
+ {
+ cairo_save (cr);
+ if (grid_type == GRID_LINES)
+ {
+ draw_lines(cr, width, height);
+ }
+ else
+ {
+ draw_crosses(cr, width, height);
+ }
+ cairo_restore (cr);
+ }
+}
+
+void
+toggle_grid(void)
+{
+ grid_snap = !grid_snap;
+ preview_redraw();
+}
+
+static gint
+grid_nearest_x(gint x)
+{
+ return grid_left + (x - grid_left + grid_width / 2) / grid_width
+ * grid_width;
+}
+
+static gint
+grid_nearest_y(gint y)
+{
+ return grid_top + (y - grid_top + grid_height / 2) / grid_height
+ * grid_height;
+}
+
+void
+round_to_grid(gint *x, gint *y)
+{
+ if (grid_snap) {
+ *x = grid_nearest_x(*x);
+ *y = grid_nearest_y(*y);
+ }
+}
+
+gboolean
+grid_near_x(gint x)
+{
+ return grid_snap && grid_type != GRID_HIDDEN
+ && abs(grid_nearest_x(x) - x) <= 1;
+}
+
+gboolean
+grid_near_y(gint y)
+{
+ return grid_snap && grid_type != GRID_HIDDEN
+ && abs(grid_nearest_x(y) - y) <= 1;
+}
diff --git a/plug-ins/imagemap/imap_grid.h b/plug-ins/imagemap/imap_grid.h
new file mode 100644
index 0000000..84deb95
--- /dev/null
+++ b/plug-ins/imagemap/imap_grid.h
@@ -0,0 +1,34 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_GRID_H
+#define _IMAP_GRID_H
+
+void do_grid_settings_dialog (void);
+void draw_grid (cairo_t *cr, gint width, gint height);
+void toggle_grid (void);
+void round_to_grid (gint *x, gint *y);
+
+gboolean grid_near_x (gint x);
+gboolean grid_near_y (gint y);
+
+#endif /* _IMAP_GRID_H */
diff --git a/plug-ins/imagemap/imap_main.c b/plug-ins/imagemap/imap_main.c
new file mode 100644
index 0000000..8254a87
--- /dev/null
+++ b/plug-ins/imagemap/imap_main.c
@@ -0,0 +1,1306 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2006 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h> /* for keyboard values */
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_about.h"
+#include "imap_circle.h"
+#include "imap_commands.h"
+#include "imap_default_dialog.h"
+#include "imap_edit_area_info.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_misc.h"
+#include "imap_object.h"
+#include "imap_polygon.h"
+#include "imap_preview.h"
+#include "imap_rectangle.h"
+#include "imap_selection.h"
+#include "imap_settings.h"
+#include "imap_source.h"
+#include "imap_statusbar.h"
+#include "imap_stock.h"
+#include "imap_string.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define MAX_ZOOM_FACTOR 8
+#define ZOOMED(x) (_zoom_factor * (x))
+#define GET_REAL_COORD(x) ((x) / _zoom_factor)
+
+static gint zoom_in (void);
+static gint zoom_out (void);
+
+
+/* Global variables */
+static MapInfo_t _map_info;
+static PreferencesData_t _preferences = {CSIM, TRUE, FALSE, TRUE, TRUE, FALSE,
+FALSE, TRUE, DEFAULT_UNDO_LEVELS, DEFAULT_MRU_SIZE};
+static MRU_t *_mru;
+
+static gint32 _drawable_id;
+static GdkCursorType _cursor = GDK_TOP_LEFT_ARROW;
+static gboolean _show_url = TRUE;
+static gchar *_filename = NULL;
+static gchar *_image_name;
+static gint _image_width;
+static gint _image_height;
+static GtkWidget *_dlg;
+static Preview_t *_preview;
+static Selection_t *_selection;
+static StatusBar_t *_statusbar;
+static ObjectList_t *_shapes;
+static gint _zoom_factor = 1;
+static gboolean (*_button_press_func)(GtkWidget*, GdkEventButton*, gpointer);
+static gpointer _button_press_param;
+
+/* Declare local functions. */
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static gint dialog (gint32 drawable_id);
+
+const GimpPlugInInfo PLUG_IN_INFO = {
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static int run_flag = 0;
+
+
+MAIN ()
+
+static void query(void)
+{
+ static const GimpParamDef args[] = {
+ {GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }"},
+ {GIMP_PDB_IMAGE, "image", "Input image (unused)"},
+ {GIMP_PDB_DRAWABLE, "drawable", "Input drawable"},
+ };
+ static const GimpParamDef *return_vals = NULL;
+ static int nreturn_vals = 0;
+
+ gimp_install_procedure(PLUG_IN_PROC,
+ N_("Create a clickable imagemap"),
+ "",
+ "Maurits Rijk",
+ "Maurits Rijk",
+ "1998-2005",
+ N_("_Image Map..."),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), nreturn_vals,
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Web");
+}
+
+static void
+run (const gchar *name,
+ gint n_params,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ _drawable_id = drawable_id;
+ _image_name = gimp_image_get_name(param[1].data.d_image);
+ _image_width = gimp_image_width(param[1].data.d_image);
+ _image_height = gimp_image_height(param[1].data.d_image);
+
+ _map_info.color = gimp_drawable_is_rgb(drawable_id);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE) {
+ if (!dialog(drawable_id)) {
+ /* The dialog was closed, or something similarly evil happened. */
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+GtkWidget*
+get_dialog(void)
+{
+ return _dlg;
+}
+
+MRU_t*
+get_mru(void)
+{
+ if (!_mru)
+ _mru = mru_create();
+ return _mru;
+}
+
+MapInfo_t*
+get_map_info(void)
+{
+ return &_map_info;
+}
+
+PreferencesData_t*
+get_preferences(void)
+{
+ return &_preferences;
+}
+
+static void
+init_preferences(void)
+{
+ ColorSelData_t *colors = &_preferences.colors;
+
+ colors->normal_fg.red = 0;
+ colors->normal_fg.green = 0xFFFF;
+ colors->normal_fg.blue = 0;
+
+ colors->normal_bg.red = 0;
+ colors->normal_bg.green = 0;
+ colors->normal_bg.blue = 0xFFFF;
+
+ colors->selected_fg.red = 0xFFFF;
+ colors->selected_fg.green = 0;
+ colors->selected_fg.blue = 0;
+
+ colors->selected_bg.red = 0;
+ colors->selected_bg.green = 0;
+ colors->selected_bg.blue = 0xFFFF;
+
+ colors->interactive_fg.red = 0xFFFF;
+ colors->interactive_fg.green = 0;
+ colors->interactive_fg.blue = 0xFFFF;
+
+ colors->interactive_bg.red = 0xFFFF;
+ colors->interactive_bg.green = 0xFFFF;
+ colors->interactive_bg.blue = 0;
+
+ preferences_load(&_preferences);
+
+ mru_set_size(_mru, _preferences.mru_size);
+ command_list_set_undo_level(_preferences.undo_levels);
+}
+
+gint
+get_image_width(void)
+{
+ return _image_width;
+}
+
+gint
+get_image_height(void)
+{
+ return _image_height;
+}
+
+void
+set_busy_cursor(void)
+{
+ preview_set_cursor(_preview, GDK_WATCH);
+}
+
+void
+remove_busy_cursor(void)
+{
+ gdk_window_set_cursor(gtk_widget_get_window (_dlg), NULL);
+}
+
+static gint
+zoom_in(void)
+{
+ if (_zoom_factor < MAX_ZOOM_FACTOR) {
+ set_zoom(_zoom_factor + 1);
+ menu_set_zoom(_zoom_factor);
+ }
+ return _zoom_factor;
+}
+
+static gint
+zoom_out(void)
+{
+ if (_zoom_factor > 1) {
+ set_zoom(_zoom_factor - 1);
+ menu_set_zoom(_zoom_factor);
+ }
+ return _zoom_factor;
+}
+
+void
+set_zoom(gint zoom_factor)
+{
+ set_busy_cursor();
+ _zoom_factor = zoom_factor;
+ preview_zoom(_preview, zoom_factor);
+ statusbar_set_zoom(_statusbar, zoom_factor);
+ remove_busy_cursor();
+}
+
+gint
+get_real_coord(gint coord)
+{
+ return GET_REAL_COORD(coord);
+}
+
+void
+draw_line(cairo_t *cr, gint x1, gint y1, gint x2, gint y2)
+{
+ cairo_move_to (cr, ZOOMED (x1) + .5, ZOOMED (y1) + .5);
+ cairo_line_to (cr, ZOOMED (x2) + .5, ZOOMED (y2) + .5);
+ cairo_stroke (cr);
+}
+
+void
+draw_rectangle(cairo_t *cr, gboolean filled, gint x, gint y,
+ gint width, gint height)
+{
+ cairo_rectangle (cr, ZOOMED (x) + (filled ? 0. : .5),
+ ZOOMED (y) + (filled ? 0. : .5),
+ ZOOMED (width), ZOOMED (height));
+ if (filled)
+ cairo_fill (cr);
+ else
+ cairo_stroke (cr);
+}
+
+void
+draw_circle(cairo_t *cr, gint x, gint y, gint r)
+{
+ cairo_arc (cr, ZOOMED (x), ZOOMED (y), ZOOMED (r), 0., 2 * G_PI);
+ cairo_stroke (cr);
+}
+
+void
+draw_polygon(cairo_t *cr, GList *list)
+{
+ GList *p;
+
+ for (p = list; p; p = p->next) {
+ GdkPoint *src = (GdkPoint*) p->data;
+ cairo_line_to (cr, ZOOMED (src->x) + .5, ZOOMED (src->y) + .5);
+ }
+ cairo_close_path (cr);
+ cairo_stroke (cr);
+}
+
+void
+set_preview_color (GtkRadioAction *action, GtkRadioAction *current,
+ gpointer user_data)
+{
+ _map_info.show_gray = (gtk_radio_action_get_current_value (current) == 1);
+ set_zoom(_zoom_factor);
+}
+
+void
+preview_redraw(void)
+{
+ gtk_widget_queue_draw(_preview->preview);
+}
+
+void
+set_zoom_factor (GtkRadioAction *action, GtkRadioAction *current,
+ gpointer user_data)
+{
+ gint factor = gtk_radio_action_get_current_value (current);
+ set_zoom (factor + 1);
+}
+
+const gchar *
+get_image_name(void)
+{
+ return _image_name;
+}
+
+const char*
+get_filename(void)
+{
+ return _filename;
+}
+
+static gboolean
+arrow_on_button_press (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ if (gdk_event_triggers_context_menu ((GdkEvent *) event))
+ {
+ do_popup_menu (event);
+ }
+ else if (event->button == 1)
+ {
+ if (event->type == GDK_2BUTTON_PRESS)
+ edit_shape((gint) event->x, (gint) event->y);
+ else
+ select_shape(widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+set_arrow_func(void)
+{
+ _button_press_func = arrow_on_button_press;
+ _cursor = GDK_TOP_LEFT_ARROW;
+}
+
+static void
+set_object_func(gboolean (*func)(GtkWidget*, GdkEventButton*,
+ gpointer), gpointer param)
+{
+ _button_press_func = func;
+ _button_press_param = param;
+ _cursor = GDK_CROSSHAIR;
+}
+
+void
+set_func(GtkRadioAction *action, GtkRadioAction *current,
+ gpointer user_data)
+{
+ gint value = gtk_radio_action_get_current_value (current);
+ switch (value)
+ {
+ case 0:
+ set_arrow_func();
+ break;
+ case 1:
+ set_object_func(object_on_button_press, get_rectangle_factory);
+ break;
+ case 2:
+ set_object_func(object_on_button_press, get_circle_factory);
+ break;
+ case 3:
+ set_object_func(object_on_button_press, get_polygon_factory);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+add_shape(Object_t *obj)
+{
+ object_list_append(_shapes, obj);
+}
+
+ObjectList_t*
+get_shapes(void)
+{
+ return _shapes;
+}
+
+void
+update_shape(Object_t *obj)
+{
+ object_list_update(_shapes, obj);
+}
+
+void
+do_edit_selected_shape(void)
+{
+ object_list_edit_selected(_shapes);
+}
+
+void
+do_popup_menu(GdkEventButton *event)
+{
+ gint x = GET_REAL_COORD((gint) event->x);
+ gint y = GET_REAL_COORD((gint) event->y);
+ Object_t *obj = object_list_find(_shapes, x, y);
+ if (obj) {
+ obj->class->do_popup(obj, event);
+ } else {
+ do_main_popup_menu(event);
+ }
+}
+
+static void
+set_all_sensitivities(void)
+{
+ gint count = object_list_nr_selected(_shapes);
+ menu_shapes_selected(count);
+}
+
+static void
+main_set_title(const char *filename)
+{
+ char *title, *p;
+
+ g_strreplace(&_filename, filename);
+ p = filename ? g_filename_display_basename (filename) : (gchar *) _("<Untitled>");
+ title = g_strdup_printf("%s - Image Map", p);
+ if (filename)
+ g_free (p);
+ gtk_window_set_title(GTK_WINDOW(_dlg), title);
+ g_free(title);
+}
+
+void
+main_set_dimension(gint width, gint height)
+{
+ statusbar_set_dimension(_statusbar,
+ width / _zoom_factor, height / _zoom_factor);
+}
+
+void
+main_clear_dimension(void)
+{
+ statusbar_clear_dimension(_statusbar);
+}
+
+void
+show_url(void)
+{
+ _show_url = TRUE;
+}
+
+void
+hide_url(void)
+{
+ _show_url = FALSE;
+ statusbar_clear_status(_statusbar);
+}
+
+void
+select_shape(GtkWidget *widget, GdkEventButton *event)
+{
+ Object_t *obj;
+ gint x = GET_REAL_COORD((gint) event->x);
+ gint y = GET_REAL_COORD((gint) event->y);
+ MoveSashFunc_t sash_func;
+
+ obj = object_list_near_sash(_shapes, x, y, &sash_func);
+ if (obj) { /* Start resizing */
+ Command_t *command = move_sash_command_new(widget, obj, x, y, sash_func);
+ command_execute(command);
+ } else {
+ Command_t *command;
+
+ obj = object_list_find(_shapes, x, y);
+ if (obj) {
+ if (event->state & GDK_SHIFT_MASK) {
+ if (obj->selected)
+ command = unselect_command_new(obj);
+ else
+ command = select_command_new(obj);
+ } else { /* No Shift key pressed */
+ if (obj->selected) {
+ command = unselect_all_command_new(_shapes, obj);
+ } else {
+ Command_t *sub_command;
+
+ command = subcommand_start(NULL);
+ sub_command = unselect_all_command_new(_shapes, NULL);
+ command_add_subcommand(command, sub_command);
+ sub_command = select_command_new(obj);
+ command_add_subcommand(command, sub_command);
+ command_set_name(command, sub_command->name);
+ subcommand_end();
+ }
+ }
+ command_execute(command);
+
+ command = move_command_new(_preview, obj, x, y);
+ command_execute(command);
+ } else { /* Start selection rectangle */
+ command = select_region_command_new(widget, _shapes, x, y);
+ command_execute(command);
+ }
+ }
+}
+
+void
+edit_shape(gint x, gint y)
+{
+ Object_t *obj;
+
+ x = GET_REAL_COORD(x);
+ y = GET_REAL_COORD(y);
+
+ obj = object_list_find(_shapes, x, y);
+ if (obj) {
+ object_select(obj);
+ object_edit(obj, TRUE);
+ }
+}
+
+void
+do_zoom_in(void)
+{
+ gint factor = zoom_in();
+ menu_set_zoom_sensitivity(factor);
+}
+
+void
+do_zoom_out(void)
+{
+ gint factor = zoom_out();
+ menu_set_zoom_sensitivity(factor);
+}
+
+void
+draw_shapes(cairo_t *cr)
+{
+ object_list_draw(_shapes, cr);
+}
+
+static void
+clear_map_info(void)
+{
+ const gchar *author = g_get_real_name();
+
+ if (!*author)
+ author = g_get_user_name();
+ g_strreplace(&_map_info.image_name, _image_name);
+ g_strreplace(&_map_info.title, "map");
+ g_strreplace(&_map_info.author, author);
+ g_strreplace(&_map_info.default_url, "");
+ g_strreplace(&_map_info.description, "");
+
+ _map_info.map_format = _preferences.default_map_type;
+ _map_info.show_gray = FALSE;
+}
+
+static void
+do_data_changed_dialog(void (*continue_cb)(gpointer), gpointer param)
+{
+ GtkWidget *dialog = gtk_message_dialog_new
+ (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ _("Some data has been changed!"));
+ gtk_message_dialog_format_secondary_text
+ (GTK_MESSAGE_DIALOG (dialog),
+ _("Do you really want to discard your changes?"));
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
+ continue_cb (param);
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+check_if_changed(void (*func)(gpointer), gpointer param)
+{
+ if (object_list_get_changed (_shapes))
+ do_data_changed_dialog (func, param);
+ else
+ func (param);
+}
+
+static void
+close_current(void)
+{
+ selection_freeze(_selection);
+ object_list_remove_all(_shapes);
+ selection_thaw(_selection);
+ clear_map_info();
+ main_set_title(NULL);
+ set_all_sensitivities();
+ preview_redraw();
+ object_list_clear_changed(_shapes);
+ command_list_remove_all();
+}
+
+static void
+really_close(gpointer data)
+{
+ close_current();
+}
+
+void
+do_close(void)
+{
+ check_if_changed(really_close, NULL);
+}
+
+static void
+really_quit(gpointer data)
+{
+ preferences_save(&_preferences);
+ run_flag = 1;
+ gtk_widget_destroy(_dlg);
+}
+
+void
+do_quit(void)
+{
+ check_if_changed(really_quit, NULL);
+}
+
+void
+do_undo(void)
+{
+ selection_freeze(_selection);
+ last_command_undo();
+ selection_thaw(_selection);
+ preview_redraw();
+}
+
+void
+do_redo(void)
+{
+ selection_freeze(_selection);
+ last_command_redo();
+ selection_thaw(_selection);
+ preview_redraw();
+}
+
+void
+save(void)
+{
+ if (_filename)
+ save_as(_filename);
+ else
+ do_file_save_as_dialog();
+}
+
+static void
+write_cern_comment(gpointer param, OutputFunc_t output)
+{
+ output(param, "rect (4096,4096) (4096,4096) imap:#$");
+}
+
+static void
+save_as_cern(gpointer param, OutputFunc_t output)
+{
+ char *p;
+ gchar *description;
+ gchar *next_token;
+
+ write_cern_comment(param, output);
+ output(param, "-:Image map file created by GIMP Image Map plug-in\n");
+ write_cern_comment(param, output);
+ output(param, "-:GIMP Image Map plug-in by Maurits Rijk\n");
+ write_cern_comment(param, output);
+ output(param, "-:Please do not edit lines starting with \"#$\"\n");
+ write_cern_comment(param, output);
+ output(param, "VERSION:2.3\n");
+ write_cern_comment(param, output);
+ output(param, "TITLE:%s\n", _map_info.title);
+ write_cern_comment(param, output);
+ output(param, "AUTHOR:%s\n", _map_info.author);
+ write_cern_comment(param, output);
+ output(param, "FORMAT:cern\n");
+
+ description = g_strdup(_map_info.description);
+ next_token = description;
+ for (p = strtok (next_token, "\n"); p; p = strtok(NULL, "\n")) {
+ write_cern_comment(param, output);
+ output(param, "DESCRIPTION:%s\n", p);
+ }
+ g_free(description);
+
+ if (*_map_info.default_url)
+ output(param, "default %s\n", _map_info.default_url);
+ object_list_write_cern(_shapes, param, output);
+}
+
+static void
+save_as_csim(gpointer param, OutputFunc_t output)
+{
+ char *p;
+ gchar *description;
+
+ output(param, "<img src=\"%s\" width=\"%d\" height=\"%d\" border=\"0\" "
+ "usemap=\"#%s\" />\n\n", _map_info.image_name,
+ _image_width, _image_height, _map_info.title);
+ output(param, "<map name=\"%s\">\n", _map_info.title);
+ output(param,
+ "<!-- #$-:Image map file created by GIMP Image Map plug-in -->\n");
+ output(param, "<!-- #$-:GIMP Image Map plug-in by Maurits Rijk -->\n");
+ output(param,
+ "<!-- #$-:Please do not edit lines starting with \"#$\" -->\n");
+ output(param, "<!-- #$VERSION:2.3 -->\n");
+ output(param, "<!-- #$AUTHOR:%s -->\n", _map_info.author);
+
+ description = g_strdup(_map_info.description);
+ for (p = strtok(description, "\n"); p; p = strtok(NULL, "\n"))
+ output(param, "<!-- #$DESCRIPTION:%s -->\n", p);
+ g_free(description);
+
+ object_list_write_csim(_shapes, param, output);
+ if (*_map_info.default_url)
+ output(param, "<area shape=\"default\" href=\"%s\" />\n",
+ _map_info.default_url);
+ output(param, "</map>\n");
+}
+
+static void
+save_as_ncsa(gpointer param, OutputFunc_t output)
+{
+ char *p;
+ gchar *description;
+
+ output(param, "#$-:Image map file created by GIMP Image Map plug-in\n");
+ output(param, "#$-:GIMP Image Map plug-in by Maurits Rijk\n");
+ output(param, "#$-:Please do not edit lines starting with \"#$\"\n");
+ output(param, "#$VERSION:2.3\n");
+ output(param, "#$TITLE:%s\n", _map_info.title);
+ output(param, "#$AUTHOR:%s\n", _map_info.author);
+ output(param, "#$FORMAT:ncsa\n");
+
+ description = g_strdup(_map_info.description);
+ for (p = strtok(description, "\n"); p; p = strtok(NULL, "\n"))
+ output(param, "#$DESCRIPTION:%s\n", p);
+ g_free(description);
+
+ if (*_map_info.default_url)
+ output(param, "default %s\n", _map_info.default_url);
+ object_list_write_ncsa(_shapes, param, output);
+}
+
+static void save_to_file (gpointer param,
+ const char *format,
+ ...) G_GNUC_PRINTF(2,3);
+
+static void
+save_to_file(gpointer param, const char* format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf((FILE*)param, format, ap);
+ va_end(ap);
+}
+
+void
+dump_output(gpointer param, OutputFunc_t output)
+{
+ if (_map_info.map_format == NCSA)
+ save_as_ncsa(param, output);
+ else if (_map_info.map_format == CERN)
+ save_as_cern(param, output);
+ else if (_map_info.map_format == CSIM)
+ save_as_csim(param, output);
+}
+
+void
+save_as(const gchar *filename)
+{
+ FILE *out = g_fopen(filename, "w");
+ if (out) {
+ dump_output(out, save_to_file);
+ fclose(out);
+
+ statusbar_set_status(_statusbar, _("File \"%s\" saved."), filename);
+ main_set_title(filename);
+ object_list_clear_changed(_shapes);
+ } else {
+ do_file_error_dialog( _("Couldn't save file:"), filename);
+ }
+}
+
+static void
+do_image_size_changed_dialog(void)
+{
+ GtkWidget *dialog = gtk_message_dialog_new_with_markup
+ (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ "<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ _("Image size has changed."),
+ _("Resize area's?"));
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
+ {
+ gint per_x = _image_width * 100 / _map_info.old_image_width;
+ gint per_y = _image_height * 100 / _map_info.old_image_height;
+ object_list_resize(_shapes, per_x, per_y);
+ }
+
+ preview_redraw();
+ gtk_widget_destroy (dialog);
+}
+
+static void
+really_load(gpointer data)
+{
+ gchar *filename = (gchar*) data;
+ close_current();
+
+ selection_freeze(_selection);
+ _map_info.old_image_width = _image_width;
+ _map_info.old_image_height = _image_height;
+ if (load_csim(filename)) {
+ _map_info.map_format = CSIM;
+ if (_image_width != _map_info.old_image_width ||
+ _image_height != _map_info.old_image_height) {
+ do_image_size_changed_dialog();
+ }
+ } else if (load_ncsa(filename)) {
+ _map_info.map_format = NCSA;
+ } else if (load_cern(filename)) {
+ _map_info.map_format = CERN;
+ } else {
+ do_file_error_dialog( _("Couldn't read file:"), filename);
+ selection_thaw(_selection);
+ close_current();
+ return;
+ }
+ mru_set_first(_mru, filename);
+ menu_build_mru_items(_mru);
+
+ selection_thaw(_selection);
+ main_set_title(filename);
+ object_list_clear_changed(_shapes);
+ preview_redraw();
+}
+
+void
+load(const gchar *filename)
+{
+ static gchar *tmp_filename;
+ g_strreplace(&tmp_filename, filename);
+ check_if_changed(really_load, (gpointer) tmp_filename);
+}
+
+void
+toggle_area_list(void)
+{
+ selection_toggle_visibility(_selection);
+}
+
+static gboolean
+close_callback(GtkWidget *widget, gpointer data)
+{
+ do_quit();
+ return TRUE;
+}
+
+static gboolean
+preview_move(GtkWidget *widget, GdkEventMotion *event)
+{
+ gint x = GET_REAL_COORD((gint) event->x);
+ gint y = GET_REAL_COORD((gint) event->y);
+ static Object_t *prev_obj = NULL;
+ Object_t *obj = object_list_find(_shapes, x, y);
+
+ statusbar_set_xy(_statusbar, x, y);
+ if (obj != prev_obj) {
+ prev_obj = obj;
+ if (obj && _show_url) {
+ statusbar_set_status(_statusbar, _("URL: %s"), obj->url);
+ } else {
+ statusbar_clear_status(_statusbar);
+ }
+ }
+#ifdef _NOT_READY_YET_
+ if (!obj) {
+ if (grid_near_x(x)) {
+ preview_set_cursor(_preview, GDK_SB_H_DOUBLE_ARROW);
+ } else if (grid_near_y(y)) {
+ preview_set_cursor(_preview, GDK_SB_V_DOUBLE_ARROW);
+ } else {
+ preview_set_cursor(_preview, _cursor);
+ }
+ }
+#endif
+ return FALSE;
+}
+
+static void
+preview_enter(GtkWidget *widget, GdkEventCrossing *event)
+{
+ preview_set_cursor(_preview, _cursor);
+}
+
+static void
+preview_leave(GtkWidget *widget, GdkEventCrossing *event)
+{
+ gdk_window_set_cursor(gtk_widget_get_window (_dlg), NULL);
+ statusbar_clear_xy(_statusbar);
+}
+
+static gboolean
+button_press(GtkWidget* widget, GdkEventButton* event, gpointer data)
+{
+ if (_button_press_func)
+ return _button_press_func(widget, event, _button_press_param);
+
+ return FALSE;
+}
+
+/* A few global vars for key movement */
+
+static guint _timeout;
+static guint _keyval;
+static gint _dx, _dy;
+
+static void
+move_sash_selected_objects(gint dx, gint dy, gboolean fast)
+{
+ if (fast) {
+ dx *= 5;
+ dy *= 5;
+ }
+
+ object_list_move_sash_selected(_shapes, dx, dy);
+
+ preview_redraw ();
+}
+
+static void
+move_selected_objects(gint dx, gint dy, gboolean fast)
+{
+ if (fast) {
+ dx *= 5;
+ dy *= 5;
+ }
+ _dx += dx;
+ _dy += dy;
+
+ object_list_move_selected(_shapes, dx, dy);
+
+ preview_redraw ();
+}
+
+static gboolean
+key_timeout_cb(gpointer data)
+{
+ switch (_keyval) {
+ case GDK_KEY_Left:
+ case GDK_KEY_Right:
+ case GDK_KEY_Up:
+ case GDK_KEY_Down:
+ command_list_add(move_selected_command_new(_shapes, _dx, _dy));
+ _dx = _dy = 0;
+ break;
+ }
+ preview_redraw();
+
+ _timeout = 0;
+ return FALSE;
+}
+
+static gboolean
+key_press_cb(GtkWidget *widget, GdkEventKey *event)
+{
+ gboolean handled = FALSE;
+ gboolean shift = event->state & GDK_SHIFT_MASK;
+ gboolean ctrl = event->state & GDK_CONTROL_MASK;
+ Command_t *command;
+
+ if (_timeout)
+ g_source_remove(_timeout);
+ _timeout = 0;
+
+ switch (event->keyval) {
+ case GDK_KEY_Left:
+ if (ctrl)
+ move_sash_selected_objects(-1, 0, shift);
+ else
+ move_selected_objects(-1, 0, shift);
+ handled = TRUE;
+ break;
+ case GDK_KEY_Right:
+ if (ctrl)
+ move_sash_selected_objects(1, 0, shift);
+ else
+ move_selected_objects(1, 0, shift);
+ handled = TRUE;
+ break;
+ case GDK_KEY_Up:
+ if (ctrl)
+ move_sash_selected_objects(0, -1, shift);
+ else
+ move_selected_objects(0, -1, shift);
+ handled = TRUE;
+ break;
+ case GDK_KEY_Down:
+ if (ctrl)
+ move_sash_selected_objects(0, 1, shift);
+ else
+ move_selected_objects(0, 1, shift);
+ handled = TRUE;
+ break;
+ case GDK_KEY_Tab:
+ if (shift)
+ command = select_prev_command_new(_shapes);
+ else
+ command = select_next_command_new(_shapes);
+ command_execute(command);
+ handled = TRUE;
+ break;
+ }
+ if (handled)
+ g_signal_stop_emission_by_name(widget, "key-press-event");
+
+ return handled;
+}
+
+static gboolean
+key_release_cb(GtkWidget *widget, GdkEventKey *event)
+{
+ _keyval = event->keyval;
+ _timeout = g_timeout_add(250, key_timeout_cb, NULL);
+ return FALSE;
+}
+
+static void
+geometry_changed(Object_t *obj, gpointer data)
+{
+ preview_redraw();
+}
+
+static void
+data_changed(Object_t *obj, gpointer data)
+{
+ preview_redraw();
+ set_all_sensitivities();
+}
+
+static void
+data_selected(Object_t *obj, gpointer data)
+{
+ set_all_sensitivities();
+}
+
+void
+imap_help (void)
+{
+ gimp_standard_help_func ("plug-in-imagemap", NULL);
+}
+
+void
+do_cut (void)
+{
+ command_execute (cut_command_new (_shapes));
+}
+
+void
+do_copy (void)
+{
+ command_execute (copy_command_new (_shapes));
+}
+
+void
+do_paste (void)
+{
+ command_execute (paste_command_new (_shapes));
+}
+
+void
+do_select_all(void)
+{
+ command_execute (select_all_command_new (_shapes));
+}
+
+void
+do_deselect_all(void)
+{
+ command_execute (unselect_all_command_new (_shapes, NULL));
+}
+
+void
+do_clear(void)
+{
+ command_execute (clear_command_new(_shapes));
+}
+
+void
+do_move_up(void)
+{
+ /* Fix me!
+ Command_t *command = object_up_command_new(_current_obj->list,
+ _current_obj);
+ command_execute(command);
+ */
+}
+
+void
+do_move_down(void)
+{
+ /* Fix me!
+ Command_t *command = object_down_command_new(_current_obj->list,
+ _current_obj);
+ command_execute(command);
+ */
+}
+
+void
+do_move_to_front(void)
+{
+ command_execute(move_to_front_command_new(_shapes));
+}
+
+void
+do_send_to_back(void)
+{
+ command_execute(send_to_back_command_new(_shapes));
+}
+
+void
+do_use_gimp_guides_dialog(void)
+{
+ command_execute (gimp_guides_command_new (_shapes, _drawable_id));
+}
+
+void
+do_create_guides_dialog(void)
+{
+ command_execute (guides_command_new (_shapes));
+}
+
+static Command_t*
+factory_move_up(void)
+{
+ return move_up_command_new(_shapes);
+}
+
+static Command_t*
+factory_move_down(void)
+{
+ return move_down_command_new(_shapes);
+}
+
+static gint
+dialog(gint32 drawable_id)
+{
+ GtkWidget *dlg;
+ GtkWidget *hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *tools;
+
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ set_arrow_func ();
+
+ _shapes = make_object_list();
+
+ _dlg = dlg = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_resizable(GTK_WINDOW(dlg), TRUE);
+
+ main_set_title(NULL);
+ gimp_help_connect (dlg, gimp_standard_help_func, PLUG_IN_PROC, NULL);
+
+ gtk_window_set_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ g_signal_connect (dlg, "delete-event",
+ G_CALLBACK (close_callback), NULL);
+ g_signal_connect (dlg, "key-press-event",
+ G_CALLBACK (key_press_cb), NULL);
+ g_signal_connect (dlg, "key-release-event",
+ G_CALLBACK (key_release_cb), NULL);
+
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_container_add (GTK_CONTAINER (dlg), main_vbox);
+ gtk_widget_show (main_vbox);
+
+ init_stock_icons();
+
+ /* Create menu */
+ make_menu(main_vbox, dlg);
+
+ /* Create toolbar */
+ make_toolbar(main_vbox, dlg);
+
+ /* Dialog area */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show(hbox);
+
+ tools = make_tools(dlg);
+ /* selection_set_edit_command(tools, factory_edit); */
+ gtk_box_pack_start(GTK_BOX(hbox), tools, FALSE, FALSE, 0);
+
+ _preview = make_preview(drawable_id);
+
+ g_signal_connect(_preview->preview, "motion-notify-event",
+ G_CALLBACK(preview_move), NULL);
+ g_signal_connect(_preview->preview, "enter-notify-event",
+ G_CALLBACK(preview_enter), NULL);
+ g_signal_connect(_preview->preview, "leave-notify-event",
+ G_CALLBACK(preview_leave), NULL);
+ g_signal_connect(_preview->preview, "button-press-event",
+ G_CALLBACK(button_press), NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), _preview->window, TRUE, TRUE, 0);
+
+ object_list_add_geometry_cb(_shapes, geometry_changed, NULL);
+ object_list_add_update_cb(_shapes, data_changed, NULL);
+ object_list_add_add_cb(_shapes, data_changed, NULL);
+ object_list_add_remove_cb(_shapes, data_changed, NULL);
+ object_list_add_move_cb(_shapes, data_changed, NULL);
+ object_list_add_select_cb(_shapes, data_selected, NULL);
+
+ /* Selection */
+ _selection = make_selection(_shapes);
+ selection_set_move_up_command(_selection, factory_move_up);
+ selection_set_move_down_command(_selection, factory_move_down);
+ gtk_box_pack_start(GTK_BOX(hbox), _selection->container, FALSE, FALSE, 0);
+
+ _statusbar = make_statusbar(main_vbox, dlg);
+ statusbar_set_zoom(_statusbar, 1);
+
+ gtk_widget_show(dlg);
+
+ _mru = mru_create();
+ init_preferences();
+
+ clear_map_info();
+
+ if (!mru_empty(_mru))
+ menu_build_mru_items(_mru);
+
+ gtk_main();
+
+ return run_flag;
+}
diff --git a/plug-ins/imagemap/imap_main.h b/plug-ins/imagemap/imap_main.h
new file mode 100644
index 0000000..b22327d
--- /dev/null
+++ b/plug-ins/imagemap/imap_main.h
@@ -0,0 +1,125 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_MAIN_H
+#define _IMAP_MAIN_H
+
+#include "imap_mru.h"
+#include "imap_object.h"
+#include "imap_preferences.h"
+#include "imap_preview.h"
+
+#define PLUG_IN_PROC "plug-in-imagemap"
+#define PLUG_IN_BINARY "imagemap"
+#define PLUG_IN_ROLE "gimp-imagemap"
+
+typedef enum {NCSA, CERN, CSIM} MapFormat_t;
+
+typedef struct {
+ MapFormat_t map_format;
+ gchar *image_name;
+ gchar *title;
+ gchar *author;
+ gchar *default_url;
+ gchar *description;
+ gint old_image_width;
+ gint old_image_height;
+ gboolean color; /* Color (TRUE) or Gray (FALSE) */
+ gboolean show_gray;
+} MapInfo_t;
+
+void main_set_dimension(gint width, gint height);
+void main_clear_dimension(void);
+void load(const gchar *filename);
+void save_as(const gchar *filename);
+void dump_output(gpointer param, OutputFunc_t output);
+GtkWidget *get_dialog(void);
+MRU_t *get_mru(void);
+MapInfo_t *get_map_info(void);
+PreferencesData_t *get_preferences(void);
+
+gint get_image_width(void);
+gint get_image_height(void);
+
+void set_busy_cursor(void);
+void remove_busy_cursor(void);
+
+void main_toolbar_set_grid(gboolean active);
+
+void set_zoom(gint zoom_factor);
+gint get_real_coord(gint coord);
+void draw_line(cairo_t *cr, gint x1, gint y1, gint x2,
+ gint y2);
+void draw_rectangle(cairo_t *cr, gboolean filled, gint x, gint y,
+ gint width, gint height);
+void draw_circle(cairo_t *cr, gint x, gint y,
+ gint r);
+void draw_polygon(cairo_t *cr, GList *list);
+
+const char *get_filename(void);
+
+ObjectList_t *get_shapes(void);
+void add_shape(Object_t *obj);
+void update_shape(Object_t *obj);
+void select_shape(GtkWidget *widget, GdkEventButton *event);
+void edit_shape(gint x, gint y);
+
+void do_popup_menu(GdkEventButton *event);
+void draw_shapes(cairo_t *cr);
+
+void show_url(void);
+void hide_url(void);
+
+void set_preview_color (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer user_data);
+void set_zoom_factor (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer user_data);
+void set_func (GtkRadioAction *action,
+ GtkRadioAction *current,
+ gpointer user_data);
+void do_edit_selected_shape (void);
+void do_zoom_in (void);
+void do_zoom_out (void);
+void do_close (void);
+void do_quit (void);
+void do_undo (void);
+void do_redo (void);
+void do_cut (void);
+void do_copy (void);
+void do_paste (void);
+void do_select_all (void);
+void do_deselect_all (void);
+void do_clear (void);
+void do_move_up (void);
+void do_move_down (void);
+void do_move_to_front (void);
+void do_send_to_back (void);
+void do_use_gimp_guides_dialog (void);
+void do_create_guides_dialog (void);
+void save (void);
+void imap_help (void);
+void toggle_area_list (void);
+const gchar * get_image_name (void);
+
+#endif /* _IMAP_MAIN_H */
diff --git a/plug-ins/imagemap/imap_menu.c b/plug-ins/imagemap/imap_menu.c
new file mode 100644
index 0000000..8e1d0d1
--- /dev/null
+++ b/plug-ins/imagemap/imap_menu.c
@@ -0,0 +1,551 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2006 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_about.h"
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_grid.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_menu_funcs.h"
+#include "imap_polygon.h"
+#include "imap_preferences.h"
+#include "imap_rectangle.h"
+#include "imap_settings.h"
+#include "imap_stock.h"
+#include "imap_source.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static Menu_t _menu;
+static GtkUIManager *ui_manager;
+
+GtkWidget*
+menu_get_widget(const gchar *path)
+{
+ return gtk_ui_manager_get_widget (ui_manager, path);
+}
+
+static void
+set_sensitive (const gchar *path, gboolean sensitive)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager, path);
+ g_object_set (action, "sensitive", sensitive, NULL);
+}
+
+static void
+menu_mru(GtkWidget *widget, gpointer data)
+{
+ MRU_t *mru = get_mru();
+ char *filename = (char*) data;
+
+ if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ load(filename);
+ } else {
+ do_file_error_dialog(_("Error opening file"), filename);
+ mru_remove(mru, filename);
+ menu_build_mru_items(mru);
+ }
+}
+
+void
+menu_set_zoom_sensitivity(gint factor)
+{
+ set_sensitive ("/MainMenu/ViewMenu/ZoomIn", factor < 8);
+ set_sensitive ("/MainMenu/ViewMenu/ZoomOut", factor > 1);
+}
+
+void
+menu_set_zoom(gint factor)
+{
+ menu_set_zoom_sensitivity (factor);
+}
+
+void
+menu_shapes_selected(gint count)
+{
+ gboolean sensitive = (count > 0);
+
+ set_sensitive ("/MainMenu/EditMenu/Cut", sensitive);
+ set_sensitive ("/MainMenu/EditMenu/Copy", sensitive);
+ set_sensitive ("/MainMenu/EditMenu/Clear", sensitive);
+ set_sensitive ("/MainMenu/EditMenu/EditAreaInfo", sensitive);
+ set_sensitive ("/MainMenu/EditMenu/DeselectAll", sensitive);
+}
+
+static void
+command_list_changed(Command_t *command, gpointer data)
+{
+ GtkAction *action;
+ gchar *label;
+
+ action = gtk_ui_manager_get_action (ui_manager, "/MainMenu/EditMenu/Undo");
+
+ label = g_strdup_printf (_("_Undo %s"),
+ command && command->name ? command->name : "");
+
+ g_object_set (action,
+ "label", label,
+ "sensitive", command != NULL,
+ NULL);
+ g_free (label);
+
+ command = command_list_get_redo_command();
+
+ action = gtk_ui_manager_get_action (ui_manager, "/MainMenu/EditMenu/Redo");
+
+ label = g_strdup_printf (_("_Redo %s"),
+ command && command->name ? command->name : "");
+ g_object_set (action,
+ "label", label,
+ "sensitive", command != NULL,
+ NULL);
+ g_free (label);
+}
+
+static void
+paste_buffer_added(Object_t *obj, gpointer data)
+{
+ set_sensitive("/MainMenu/EditMenu/Paste", TRUE);
+}
+
+static void
+paste_buffer_removed(Object_t *obj, gpointer data)
+{
+ set_sensitive("/MainMenu/EditMenu/Paste", FALSE);
+}
+
+/* Normal items */
+static const GtkActionEntry entries[] =
+{
+ { "FileMenu", NULL,
+ N_("_File") },
+ { "Open", GIMP_ICON_DOCUMENT_OPEN,
+ N_("_Open..."), NULL, N_("Open"),
+ do_file_open_dialog},
+ { "Save", GIMP_ICON_DOCUMENT_SAVE,
+ N_("_Save..."), NULL, N_("Save"),
+ save},
+ { "SaveAs", GIMP_ICON_DOCUMENT_SAVE_AS,
+ N_("Save _As..."), "<shift><control>S", NULL,
+ do_file_save_as_dialog},
+ { "Close", GIMP_ICON_CLOSE, N_("_Close"), "<primary>w", NULL, do_close},
+ { "Quit", GIMP_ICON_APPLICATION_EXIT,
+ N_("_Quit"), "<primary>q", NULL, do_quit},
+
+ { "EditMenu", NULL, N_("_Edit") },
+ { "Undo", GIMP_ICON_EDIT_UNDO,
+ N_("_Undo"), NULL, N_("Undo"), do_undo},
+ { "Redo", GIMP_ICON_EDIT_REDO,
+ N_("_Redo"), NULL, N_("Redo"), do_redo},
+ { "Cut", GIMP_ICON_EDIT_CUT,
+ N_("Cu_t"), "<primary>x", N_("Cut"), do_cut},
+ { "Copy", GIMP_ICON_EDIT_COPY,
+ N_("_Copy"), "<primary>c", N_("Copy"), do_copy},
+ { "Paste", GIMP_ICON_EDIT_PASTE,
+ N_("_Paste"), "<primary>v", N_("Paste"), do_paste},
+ { "Clear", GIMP_ICON_EDIT_DELETE,
+ N_("_Delete"), NULL, N_("Delete"), do_clear},
+ { "SelectAll", NULL,
+ N_("Select _All"), "<primary>A", NULL, do_select_all},
+ { "DeselectAll", NULL,
+ N_("D_eselect All"), "<shift><primary>A", NULL,
+ do_deselect_all},
+ { "EditAreaInfo", GIMP_ICON_EDIT
+ , N_("Edit Area _Info..."), NULL,
+ N_("Edit selected area info"), do_edit_selected_shape},
+ { "Preferences", GIMP_ICON_PREFERENCES_SYSTEM,
+ N_("_Preferences"), NULL, N_("Preferences"),
+ do_preferences_dialog},
+ { "MoveToFront", IMAP_STOCK_TO_FRONT, "", NULL, N_("Move Area to Front"),
+ do_move_to_front},
+ { "SendToBack", IMAP_STOCK_TO_BACK, "", NULL, N_("Move Area to Bottom"),
+ do_send_to_back},
+ { "DeleteArea", NULL, N_("Delete Area"), NULL, NULL, NULL},
+ { "MoveUp", GIMP_ICON_GO_UP, N_("Move Up"), NULL, NULL, NULL},
+ { "MoveDown", GIMP_ICON_GO_DOWN, N_("Move Down"), NULL, NULL, NULL},
+
+ { "InsertPoint", NULL, N_("Insert Point"), NULL, NULL, polygon_insert_point},
+ { "DeletePoint", NULL, N_("Delete Point"), NULL, NULL, polygon_delete_point},
+
+ { "ViewMenu", NULL, N_("_View") },
+ { "Source", NULL, N_("Source..."), NULL, NULL, do_source_dialog},
+ { "ZoomIn", GIMP_ICON_ZOOM_IN, N_("Zoom _In"), "plus", N_("Zoom in"), do_zoom_in},
+ { "ZoomOut", GIMP_ICON_ZOOM_OUT, N_("Zoom _Out"), "minus", N_("Zoom out"), do_zoom_out},
+ { "ZoomToMenu", NULL, N_("_Zoom To") },
+
+ { "MappingMenu", NULL, N_("_Mapping") },
+ { "EditMapInfo", GIMP_ICON_DIALOG_INFORMATION, N_("Edit Map Info..."), NULL,
+ N_("Edit Map Info"), do_settings_dialog},
+
+ { "ToolsMenu", NULL, N_("_Tools") },
+ { "GridSettings", NULL, N_("Grid Settings..."), NULL, NULL,
+ do_grid_settings_dialog},
+ { "UseGimpGuides", NULL, N_("Use GIMP Guides..."), NULL, NULL,
+ do_use_gimp_guides_dialog},
+ { "CreateGuides", NULL, N_("Create Guides..."), NULL, NULL,
+ do_create_guides_dialog},
+
+ { "HelpMenu", NULL, N_("_Help") },
+ { "Contents", GIMP_ICON_HELP, N_("_Contents"), NULL, NULL, imap_help},
+ { "About", GIMP_ICON_HELP_ABOUT, N_("_About"), NULL, NULL, do_about_dialog},
+
+ { "ZoomMenu", NULL, N_("_Zoom") },
+};
+
+/* Toggle items */
+static const GtkToggleActionEntry toggle_entries[] = {
+ { "AreaList", NULL, N_("Area List"), NULL, NULL, NULL, TRUE },
+ { "Grid", GIMP_ICON_GRID, N_("_Grid"), NULL, N_("Grid"), toggle_grid, FALSE }
+};
+
+static const GtkRadioActionEntry color_entries[] = {
+ { "Color", NULL, N_("Color"), NULL, NULL, 0},
+ { "Gray", NULL, N_("Gray"), NULL, NULL, 1},
+};
+
+static const GtkRadioActionEntry mapping_entries[] = {
+ { "Arrow", GIMP_ICON_CURSOR, N_("Arrow"), NULL,
+ N_("Select existing area"), 0},
+ { "Rectangle", IMAP_STOCK_RECTANGLE, N_("Rectangle"), NULL,
+ N_("Define Rectangle area"), 1},
+ { "Circle", IMAP_STOCK_CIRCLE, N_("Circle"), NULL,
+ N_("Define Circle/Oval area"), 2},
+ { "Polygon", IMAP_STOCK_POLYGON, N_("Polygon"), NULL,
+ N_("Define Polygon area"), 3},
+};
+
+static const GtkRadioActionEntry zoom_entries[] = {
+ { "Zoom1:1", NULL, "1:1", NULL, NULL, 0},
+ { "Zoom1:2", NULL, "1:2", NULL, NULL, 1},
+ { "Zoom1:3", NULL, "1:3", NULL, NULL, 2},
+ { "Zoom1:4", NULL, "1:4", NULL, NULL, 3},
+ { "Zoom1:5", NULL, "1:5", NULL, NULL, 4},
+ { "Zoom1:6", NULL, "1:6", NULL, NULL, 5},
+ { "Zoom1:7", NULL, "1:7", NULL, NULL, 6},
+ { "Zoom1:8", NULL, "1:8", NULL, NULL, 7},
+};
+
+static const gchar ui_description[] =
+"<ui>"
+" <menubar name='MainMenu'>"
+" <menu action='FileMenu'>"
+" <menuitem action='Open'/>"
+" <menuitem action='Save'/>"
+" <menuitem action='SaveAs'/>"
+" <separator/>"
+" <menuitem action='Close'/>"
+" <menuitem action='Quit'/>"
+" </menu>"
+" <menu action='EditMenu'>"
+" <menuitem action='Undo'/>"
+" <menuitem action='Redo'/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <menuitem action='Clear'/>"
+" <separator/>"
+" <menuitem action='SelectAll'/>"
+" <menuitem action='DeselectAll'/>"
+" <separator/>"
+" <menuitem action='EditAreaInfo'/>"
+" <separator/>"
+" <menuitem action='Preferences'/>"
+" </menu>"
+" <menu action='ViewMenu'>"
+" <menuitem action='AreaList'/>"
+" <menuitem action='Source'/>"
+" <separator/>"
+" <menuitem action='Color'/>"
+" <menuitem action='Gray'/>"
+" <separator/>"
+" <menuitem action='ZoomIn'/>"
+" <menuitem action='ZoomOut'/>"
+" <menu action='ZoomToMenu'>"
+" <menuitem action='Zoom1:1'/>"
+" <menuitem action='Zoom1:2'/>"
+" <menuitem action='Zoom1:3'/>"
+" <menuitem action='Zoom1:4'/>"
+" <menuitem action='Zoom1:5'/>"
+" <menuitem action='Zoom1:6'/>"
+" <menuitem action='Zoom1:7'/>"
+" <menuitem action='Zoom1:8'/>"
+" </menu>"
+" </menu>"
+" <menu action='MappingMenu'>"
+" <menuitem action='Arrow'/>"
+" <menuitem action='Rectangle'/>"
+" <menuitem action='Circle'/>"
+" <menuitem action='Polygon'/>"
+" <separator/>"
+" <menuitem action='EditMapInfo'/>"
+" </menu>"
+" <menu action='ToolsMenu'>"
+" <menuitem action='Grid'/>"
+" <menuitem action='GridSettings'/>"
+" <separator/>"
+" <menuitem action='UseGimpGuides'/>"
+" <menuitem action='CreateGuides'/>"
+" </menu>"
+" <menu action='HelpMenu'>"
+" <menuitem action='Contents'/>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+""
+" <popup name='PopupMenu'>"
+" <menuitem action='EditMapInfo'/>"
+" <menu action='ToolsMenu'>"
+" <menuitem action='Arrow'/>"
+" <menuitem action='Rectangle'/>"
+" <menuitem action='Circle'/>"
+" <menuitem action='Polygon'/>"
+" </menu>"
+" <menu action='ZoomMenu'>"
+" <menuitem action='ZoomIn'/>"
+" <menuitem action='ZoomOut'/>"
+" </menu>"
+" <menuitem action='Grid'/>"
+" <menuitem action='GridSettings'/>"
+" <menuitem action='CreateGuides'/>"
+" <menuitem action='Paste'/>"
+" </popup>"
+""
+" <popup name='ObjectPopupMenu'>"
+" <menuitem action='EditAreaInfo'/>"
+" <menuitem action='DeleteArea'/>"
+" <menuitem action='MoveUp'/>"
+" <menuitem action='MoveDown'/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" </popup>"
+""
+" <popup name='PolygonPopupMenu'>"
+" <menuitem action='InsertPoint'/>"
+" <menuitem action='DeletePoint'/>"
+" <menuitem action='EditAreaInfo'/>"
+" <menuitem action='DeleteArea'/>"
+" <menuitem action='MoveUp'/>"
+" <menuitem action='MoveDown'/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" </popup>"
+""
+" <toolbar name='Toolbar'>"
+" <toolitem action='Open'/>"
+" <toolitem action='Save'/>"
+" <separator/>"
+" <toolitem action='Preferences'/>"
+" <separator/>"
+" <toolitem action='Undo'/>"
+" <toolitem action='Redo'/>"
+" <separator/>"
+" <toolitem action='Cut'/>"
+" <toolitem action='Copy'/>"
+" <toolitem action='Paste'/>"
+" <separator/>"
+" <toolitem action='ZoomIn'/>"
+" <toolitem action='ZoomOut'/>"
+" <separator/>"
+" <toolitem action='EditMapInfo'/>"
+" <separator/>"
+" <toolitem action='MoveToFront'/>"
+" <toolitem action='SendToBack'/>"
+" <separator/>"
+" <toolitem action='Grid'/>"
+" </toolbar>"
+""
+" <toolbar name='Tools'>"
+" <toolitem action='Arrow'/>"
+" <toolitem action='Rectangle'/>"
+" <toolitem action='Circle'/>"
+" <toolitem action='Polygon'/>"
+" <separator/>"
+" <toolitem action='EditAreaInfo'/>"
+" </toolbar>"
+""
+" <toolbar name='Selection'>"
+" <toolitem action='MoveUp'/>"
+" <toolitem action='MoveDown'/>"
+" <toolitem action='EditAreaInfo'/>"
+" <toolitem action='Clear'/>"
+" </toolbar>"
+"</ui>";
+
+Menu_t*
+make_menu(GtkWidget *main_vbox, GtkWidget *window)
+{
+ GtkWidget *menubar;
+ GtkActionGroup *action_group;
+ GtkAccelGroup *accel_group;
+ GError *error;
+
+ action_group = gtk_action_group_new ("MenuActions");
+ gtk_action_group_set_translation_domain (action_group, NULL);
+
+ gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries),
+ window);
+ gtk_action_group_add_toggle_actions (action_group, toggle_entries,
+ G_N_ELEMENTS (toggle_entries), window);
+
+ gtk_action_group_add_radio_actions (action_group, color_entries,
+ G_N_ELEMENTS (color_entries), 0,
+ G_CALLBACK (set_preview_color), NULL);
+ gtk_action_group_add_radio_actions (action_group, zoom_entries,
+ G_N_ELEMENTS (zoom_entries), 0,
+ G_CALLBACK (set_zoom_factor), NULL);
+ gtk_action_group_add_radio_actions (action_group, mapping_entries,
+ G_N_ELEMENTS (mapping_entries), 0,
+ G_CALLBACK (set_func), window);
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
+ accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+ gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+ error = NULL;
+ if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1,
+ &error))
+ {
+ g_warning ("building menus failed: %s", error->message);
+ g_error_free (error);
+ /* exit (EXIT_FAILURE); */
+ }
+
+ menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
+ gtk_widget_show (menubar);
+ gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, FALSE, 0);
+
+ paste_buffer_add_add_cb(paste_buffer_added, NULL);
+ paste_buffer_add_remove_cb(paste_buffer_removed, NULL);
+
+ set_sensitive ("/MainMenu/EditMenu/Paste", FALSE);
+ menu_shapes_selected (0);
+
+ menu_set_zoom_sensitivity (1);
+
+ command_list_add_update_cb (command_list_changed, NULL);
+
+ command_list_changed (NULL, NULL);
+
+ return &_menu;
+}
+
+void
+menu_build_mru_items(MRU_t *mru)
+{
+ GList *p;
+ gint position = 0;
+ int i;
+
+ return;
+
+ if (_menu.nr_off_mru_items) {
+ GList *children;
+
+ children = gtk_container_get_children(GTK_CONTAINER(_menu.open_recent));
+ p = g_list_nth(children, position);
+ for (i = 0; i < _menu.nr_off_mru_items; i++, p = p->next) {
+ gtk_widget_destroy((GtkWidget*) p->data);
+ }
+ g_list_free(children);
+ }
+
+ i = 0;
+ for (p = mru->list; p; p = p->next, i++) {
+ GtkWidget *item = insert_item_with_label(_menu.open_recent, position++,
+ (gchar*) p->data,
+ menu_mru, p->data);
+ if (i < 9) {
+ guchar accelerator_key = '1' + i;
+ add_accelerator(item, accelerator_key, GDK_CONTROL_MASK);
+ }
+ }
+ _menu.nr_off_mru_items = i;
+}
+
+void
+do_main_popup_menu(GdkEventButton *event)
+{
+ GtkWidget *popup = gtk_ui_manager_get_widget (ui_manager, "/PopupMenu");
+ gtk_menu_popup (GTK_MENU (popup), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+}
+
+void
+menu_check_grid(gboolean check)
+{
+ GtkAction *action = gtk_ui_manager_get_action (ui_manager,
+ "/MainMenu/ToolsMenu/Grid");
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), check);
+}
+
+GtkWidget*
+make_toolbar(GtkWidget *main_vbox, GtkWidget *window)
+{
+ GtkWidget *toolbar;
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/Toolbar");
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0);
+ gtk_box_pack_start (GTK_BOX (main_vbox), toolbar, FALSE, FALSE, 0);
+ gtk_widget_show (toolbar);
+
+ return toolbar;
+}
+
+GtkWidget*
+make_tools(GtkWidget *window)
+{
+ GtkWidget *toolbar;
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/Tools");
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (toolbar),
+ GTK_ORIENTATION_VERTICAL);
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0);
+ gtk_widget_show (toolbar);
+
+ return toolbar;
+}
+
+GtkWidget*
+make_selection_toolbar(void)
+{
+ GtkWidget *toolbar;
+
+ toolbar = gtk_ui_manager_get_widget (ui_manager, "/Selection");
+
+ gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_ICONS);
+ gtk_orientable_set_orientation (GTK_ORIENTABLE (toolbar),
+ GTK_ORIENTATION_VERTICAL);
+ gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0);
+
+ gtk_widget_show (toolbar);
+ return toolbar;
+}
diff --git a/plug-ins/imagemap/imap_menu.h b/plug-ins/imagemap/imap_menu.h
new file mode 100644
index 0000000..e34743f
--- /dev/null
+++ b/plug-ins/imagemap/imap_menu.h
@@ -0,0 +1,65 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_MENU_H
+#define _IMAP_MENU_H
+
+#include "imap_command.h"
+#include "imap_mru.h"
+
+typedef struct {
+ GtkWidget *file_menu;
+ GtkWidget *edit_menu;
+ GtkWidget *open_recent;
+ GtkWidget *undo;
+ GtkWidget *redo;
+ GtkWidget *arrow;
+ GtkWidget *fuzzy_select;
+ GtkWidget *rectangle;
+ GtkWidget *circle;
+ GtkWidget *polygon;
+ GtkWidget *grid;
+ GtkWidget *gray;
+ GtkWidget *color;
+ GtkWidget *zoom[8];
+ GtkWidget *zoom_in;
+ GtkWidget *zoom_out;
+
+ gint nr_off_mru_items;
+} Menu_t;
+
+GtkWidget *menu_get_widget(const gchar *path);
+Menu_t *make_menu(GtkWidget *main_vbox, GtkWidget *window);
+void menu_build_mru_items(MRU_t *mru);
+void menu_set_zoom_sensitivity(gint factor);
+
+void menu_set_zoom(gint factor);
+void menu_check_grid(gboolean check);
+void menu_shapes_selected(gint count);
+
+void do_main_popup_menu(GdkEventButton *event);
+
+GtkWidget *make_toolbar(GtkWidget *main_vbox, GtkWidget *window);
+GtkWidget *make_tools(GtkWidget *window);
+GtkWidget *make_selection_toolbar(void);
+
+#endif /* _IMAP_MENU_H */
diff --git a/plug-ins/imagemap/imap_menu_funcs.c b/plug-ins/imagemap/imap_menu_funcs.c
new file mode 100644
index 0000000..264a6cb
--- /dev/null
+++ b/plug-ins/imagemap/imap_menu_funcs.c
@@ -0,0 +1,58 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_command.h"
+#include "imap_menu_funcs.h"
+
+static GtkAccelGroup *accelerator_group;
+
+void
+init_accel_group(GtkWidget *window)
+{
+ accelerator_group = gtk_accel_group_new();
+ gtk_window_add_accel_group(GTK_WINDOW(window), accelerator_group);
+}
+
+void
+add_accelerator(GtkWidget *widget, guint accelerator_key,
+ guint8 accelerator_mods)
+{
+ gtk_widget_add_accelerator(widget, "activate", accelerator_group,
+ accelerator_key, accelerator_mods,
+ GTK_ACCEL_VISIBLE);
+}
+
+GtkWidget*
+insert_item_with_label(GtkWidget *parent, gint position, gchar *label,
+ MenuCallback activate, gpointer data)
+{
+ GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(label);
+ gtk_menu_shell_insert(GTK_MENU_SHELL(parent), item, position);
+ g_signal_connect(item, "activate", G_CALLBACK(activate), data);
+ gtk_widget_show(item);
+
+ return item;
+}
diff --git a/plug-ins/imagemap/imap_menu_funcs.h b/plug-ins/imagemap/imap_menu_funcs.h
new file mode 100644
index 0000000..8c2b86f
--- /dev/null
+++ b/plug-ins/imagemap/imap_menu_funcs.h
@@ -0,0 +1,39 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_MENU_FUNCS_H
+#define _IMAP_MENU_FUNCS_H
+
+typedef void (*MenuCallback)(GtkWidget *widget, gpointer data);
+
+void init_accel_group(GtkWidget *window);
+GtkWidget *insert_item_with_label(GtkWidget *parent, gint position,
+ gchar *label, MenuCallback activate,
+ gpointer data);
+
+void menu_command(GtkWidget *widget, gpointer data);
+
+void add_accelerator(GtkWidget *widget, guint accelerator_key,
+ guint8 accelerator_mods);
+
+
+#endif /* _IMAP_MENU_FUNCS_H */
diff --git a/plug-ins/imagemap/imap_misc.c b/plug-ins/imagemap/imap_misc.c
new file mode 100644
index 0000000..4a6b760
--- /dev/null
+++ b/plug-ins/imagemap/imap_misc.c
@@ -0,0 +1,52 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_main.h"
+#include "imap_misc.h"
+
+#define SASH_SIZE 8
+
+static gint _sash_size = SASH_SIZE;
+
+void
+set_sash_size(gboolean double_size)
+{
+ _sash_size = (double_size) ? 2 * SASH_SIZE : SASH_SIZE;
+}
+
+void
+draw_sash(cairo_t *cr, gint x, gint y)
+{
+ draw_rectangle(cr, TRUE, x - _sash_size / 2, y - _sash_size / 2,
+ _sash_size, _sash_size);
+}
+
+gboolean
+near_sash(gint sash_x, gint sash_y, gint x, gint y)
+{
+ return x >= sash_x - _sash_size / 2 && x <= sash_x + _sash_size / 2 &&
+ y >= sash_y - _sash_size / 2 && y <= sash_y + _sash_size / 2;
+}
diff --git a/plug-ins/imagemap/imap_misc.h b/plug-ins/imagemap/imap_misc.h
new file mode 100644
index 0000000..f8a047d
--- /dev/null
+++ b/plug-ins/imagemap/imap_misc.h
@@ -0,0 +1,31 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_MISC_H
+#define _IMAP_MISC_H
+
+void set_sash_size(gboolean double_size);
+void draw_sash(cairo_t *cr, gint x, gint y);
+gboolean near_sash(gint sash_x, gint sash_y, gint x, gint y);
+
+#endif /* _IMAP_MISC_H */
+
diff --git a/plug-ins/imagemap/imap_mru.c b/plug-ins/imagemap/imap_mru.c
new file mode 100644
index 0000000..1afb0a5
--- /dev/null
+++ b/plug-ins/imagemap/imap_mru.c
@@ -0,0 +1,105 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_mru.h"
+
+MRU_t*
+mru_create(void)
+{
+ MRU_t *mru = g_new(MRU_t, 1);
+ mru->list = NULL;
+ mru->max_size = DEFAULT_MRU_SIZE;
+ return mru;
+}
+
+void
+mru_destruct (MRU_t *mru)
+{
+ g_list_free_full (mru->list, (GDestroyNotify) g_free);
+ g_free (mru);
+}
+
+static void
+mru_remove_link(MRU_t *mru, GList *link)
+{
+ if (link)
+ {
+ g_free(link->data);
+ mru->list = g_list_remove_link(mru->list, link);
+ }
+}
+
+static GList*
+mru_find_link(MRU_t *mru, const gchar *filename)
+{
+ return g_list_find_custom(mru->list, (gpointer) filename,
+ (GCompareFunc) strcmp);
+}
+
+void
+mru_add(MRU_t *mru, const gchar *filename)
+{
+ if (g_list_length(mru->list) == mru->max_size)
+ mru_remove_link(mru, g_list_last(mru->list));
+ mru->list = g_list_prepend(mru->list, g_strdup(filename));
+}
+
+void
+mru_remove(MRU_t *mru, const gchar *filename)
+{
+ mru_remove_link(mru, mru_find_link(mru, filename));
+}
+
+void
+mru_set_first(MRU_t *mru, const gchar *filename)
+{
+ GList *link = mru_find_link(mru, filename);
+ if (link)
+ mru->list = g_list_prepend(g_list_remove_link(mru->list, link),
+ link->data);
+ else
+ mru_add(mru, filename);
+}
+
+void
+mru_set_size(MRU_t *mru, gint size)
+{
+ gint diff;
+
+ for (diff = g_list_length(mru->list) - size; diff > 0; diff--)
+ mru_remove_link(mru, g_list_last(mru->list));
+ mru->max_size = size;
+}
+
+void
+mru_write(MRU_t *mru, FILE *out)
+{
+ GList *p;
+ for (p = mru->list; p; p = p->next)
+ fprintf(out, "(mru-entry %s)\n", (gchar*) p->data);
+}
diff --git a/plug-ins/imagemap/imap_mru.h b/plug-ins/imagemap/imap_mru.h
new file mode 100644
index 0000000..6d2d954
--- /dev/null
+++ b/plug-ins/imagemap/imap_mru.h
@@ -0,0 +1,45 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_MRU_H
+#define _IMAP_MRU_H
+
+#include <stdio.h>
+
+#define DEFAULT_MRU_SIZE 4
+
+typedef struct {
+ GList *list;
+ gint max_size;
+} MRU_t;
+
+MRU_t* mru_create(void);
+void mru_destruct(MRU_t *mru);
+void mru_add(MRU_t *mru, const gchar *filename);
+void mru_remove(MRU_t *mru, const gchar *filename);
+void mru_set_first(MRU_t *mru, const gchar *filename);
+void mru_set_size(MRU_t *mru, gint size);
+void mru_write(MRU_t *mru, FILE *out);
+
+#define mru_empty(mru) ((mru)->list == NULL)
+
+#endif /* _IMAP_MRU_H */
diff --git a/plug-ins/imagemap/imap_ncsa.l b/plug-ins/imagemap/imap_ncsa.l
new file mode 100644
index 0000000..73b59f2
--- /dev/null
+++ b/plug-ins/imagemap/imap_ncsa.l
@@ -0,0 +1,112 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_ncsa_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+%}
+
+%option noyywrap
+%option noinput
+%option nounput
+
+DIGIT [0-9]
+ID [a-zA-Z_][a-zA-Z0-9_\-]*
+WS [ \t\n]+
+
+%x imap_link
+%x comment
+
+%%
+
+#\$AUTHOR: {
+ BEGIN(comment);
+ return AUTHOR;
+ }
+
+#\$TITLE: {
+ BEGIN(comment);
+ return TITLE;
+ }
+
+#\$DESCRIPTION: {
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+
+# {
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+
+<comment>.* {
+ BEGIN(INITIAL);
+ ncsa_lval.id = g_strndup (yytext, yyleng);
+ return COMMENT;
+ }
+
+RECT {
+ BEGIN(imap_link);
+ return RECTANGLE;
+ }
+
+CIRCLE {
+ BEGIN(imap_link);
+ return CIRCLE;
+ }
+
+POLY {
+ BEGIN(imap_link);
+ return POLYGON;
+ }
+
+DEFAULT {
+ BEGIN(imap_link);
+ return DEFAULT;
+ }
+
+<imap_link>[^ ,\t\n]+ {
+ BEGIN(INITIAL);
+ ncsa_lval.id = g_strndup (yytext, yyleng);
+ return LINK;
+ }
+
+-?{DIGIT}*"."?{DIGIT}*([Ee][-+]?{DIGIT}*)? {
+ ncsa_lval.value = g_ascii_strtod (yytext, NULL);
+ return FLOAT;
+ }
+
+{WS} ; /* Eat white space */
+
+. return *yytext;
+
+%%
+
+
diff --git a/plug-ins/imagemap/imap_ncsa.y b/plug-ins/imagemap/imap_ncsa.y
new file mode 100644
index 0000000..c93f62d
--- /dev/null
+++ b/plug-ins/imagemap/imap_ncsa.y
@@ -0,0 +1,195 @@
+%{
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <math.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int ncsa_lex(void);
+extern int ncsa_restart(FILE *ncsa_in);
+static void ncsa_error(char* s);
+
+static Object_t *current_object;
+
+%}
+
+%union {
+ int val;
+ double value;
+ char *id;
+}
+
+%token<val> RECTANGLE POLYGON CIRCLE DEFAULT
+%token<val> AUTHOR TITLE DESCRIPTION BEGIN_COMMENT
+%token<value> FLOAT
+%token<id> LINK COMMENT
+
+%%
+
+ncsa_file : comment_lines area_list
+ ;
+
+comment_lines : /* empty */
+ | comment_lines comment_line
+ ;
+
+comment_line : author_line
+ | title_line
+ | description_line
+ | real_comment
+ ;
+
+real_comment : BEGIN_COMMENT COMMENT
+ {
+ g_free ($2);
+ }
+ ;
+
+author_line : AUTHOR COMMENT
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->author, $2);
+ g_free ($2);
+ }
+ ;
+
+title_line : TITLE COMMENT
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->title, $2);
+ g_free ($2);
+ }
+ ;
+
+description_line: DESCRIPTION COMMENT
+ {
+ MapInfo_t *info = get_map_info();
+ gchar *description;
+
+ description = g_strconcat(info->description, $2, "\n",
+ NULL);
+ g_strreplace(&info->description, description);
+ g_free ($2);
+ }
+ ;
+
+area_list : /* Empty */
+ | area_list area
+ ;
+
+area : default
+ | rectangle
+ | circle
+ | polygon
+ | real_comment
+ ;
+
+default : DEFAULT LINK
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->default_url, $2);
+ g_free ($2);
+ }
+ ;
+
+
+rectangle : RECTANGLE LINK FLOAT ',' FLOAT FLOAT ',' FLOAT
+ {
+ gint x = (gint) $3;
+ gint y = (gint) $5;
+ gint width = (gint) fabs($6 - x);
+ gint height = (gint) fabs($8 - y);
+ current_object = create_rectangle(x, y, width, height);
+ object_set_url(current_object, $2);
+ add_shape(current_object);
+ g_free ($2);
+ }
+ ;
+
+circle : CIRCLE LINK FLOAT ',' FLOAT FLOAT ',' FLOAT
+ {
+ gint x = (gint) $3;
+ gint y = (gint) $5;
+ gint r = (gint) fabs($8 - $5);
+ current_object = create_circle(x, y, r);
+ object_set_url(current_object, $2);
+ add_shape(current_object);
+ g_free ($2);
+ }
+ ;
+
+polygon : POLYGON LINK {current_object = create_polygon(NULL);} coord_list
+ {
+ object_set_url(current_object, $2);
+ add_shape(current_object);
+ g_free ($2);
+ }
+ ;
+
+coord_list : /* Empty */
+ | coord_list coord
+ {
+ }
+ ;
+
+coord : FLOAT ',' FLOAT
+ {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GdkPoint *point = new_point((gint) $1, (gint) $3);
+ polygon->points = g_list_append(polygon->points,
+ (gpointer) point);
+ }
+ ;
+
+%%
+
+static void
+ncsa_error(char* s)
+{
+ extern FILE *ncsa_in;
+ ncsa_restart(ncsa_in);
+}
+
+gboolean
+load_ncsa(const char* filename)
+{
+ gboolean status;
+ extern FILE *ncsa_in;
+ ncsa_in = g_fopen(filename, "r");
+ if (ncsa_in) {
+ status = !ncsa_parse();
+ fclose(ncsa_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
diff --git a/plug-ins/imagemap/imap_ncsa_lex.c b/plug-ins/imagemap/imap_ncsa_lex.c
new file mode 100644
index 0000000..56f2fe1
--- /dev/null
+++ b/plug-ins/imagemap/imap_ncsa_lex.c
@@ -0,0 +1,1917 @@
+
+#line 3 "<stdout>"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define yy_create_buffer ncsa__create_buffer
+#define yy_delete_buffer ncsa__delete_buffer
+#define yy_flex_debug ncsa__flex_debug
+#define yy_init_buffer ncsa__init_buffer
+#define yy_flush_buffer ncsa__flush_buffer
+#define yy_load_buffer_state ncsa__load_buffer_state
+#define yy_switch_to_buffer ncsa__switch_to_buffer
+#define yyin ncsa_in
+#define yyleng ncsa_leng
+#define yylex ncsa_lex
+#define yylineno ncsa_lineno
+#define yyout ncsa_out
+#define yyrestart ncsa_restart
+#define yytext ncsa_text
+#define yywrap ncsa_wrap
+#define yyalloc ncsa_alloc
+#define yyrealloc ncsa_realloc
+#define yyfree ncsa_free
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 36
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE ncsa_restart(ncsa_in )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+extern yy_size_t ncsa_leng;
+
+extern FILE *ncsa_in, *ncsa_out;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up ncsa_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up ncsa_text again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via ncsa_restart()), so that the user can continue scanning by
+ * just pointing ncsa_in at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when ncsa_text is formed. */
+static char yy_hold_char;
+static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */
+yy_size_t ncsa_leng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow ncsa_wrap()'s to do buffer switches
+ * instead of setting up a fresh ncsa_in. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void ncsa_restart (FILE *input_file );
+void ncsa__switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE ncsa__create_buffer (FILE *file,int size );
+void ncsa__delete_buffer (YY_BUFFER_STATE b );
+void ncsa__flush_buffer (YY_BUFFER_STATE b );
+void ncsa_push_buffer_state (YY_BUFFER_STATE new_buffer );
+void ncsa_pop_buffer_state (void );
+
+static void ncsa_ensure_buffer_stack (void );
+static void ncsa__load_buffer_state (void );
+static void ncsa__init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER ncsa__flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE ncsa__scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE ncsa__scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE ncsa__scan_bytes (yyconst char *bytes,yy_size_t len );
+
+void *ncsa_alloc (yy_size_t );
+void *ncsa_realloc (void *,yy_size_t );
+void ncsa_free (void * );
+
+#define yy_new_buffer ncsa__create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ncsa_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ncsa__create_buffer(ncsa_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ncsa_ensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ncsa__create_buffer(ncsa_in,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define ncsa_wrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *ncsa_in = (FILE *) 0, *ncsa_out = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int ncsa_lineno;
+
+int ncsa_lineno = 1;
+
+extern char *ncsa_text;
+#define yytext_ptr ncsa_text
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up ncsa_text.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ ncsa_leng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 14
+#define YY_END_OF_BUFFER 15
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[76] =
+ { 0,
+ 11, 11, 0, 0, 5, 5, 15, 13, 12, 12,
+ 4, 11, 11, 11, 13, 13, 11, 13, 13, 10,
+ 14, 5, 12, 0, 11, 11, 11, 11, 0, 0,
+ 11, 11, 0, 0, 10, 5, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 6,
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,
+ 0, 0, 0, 9, 0, 0, 2, 1, 0, 0,
+ 0, 0, 0, 3, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 4, 5, 1, 1, 1, 1,
+ 1, 1, 6, 7, 8, 9, 1, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 11, 1, 1,
+ 1, 1, 1, 1, 12, 1, 13, 14, 15, 16,
+ 1, 17, 18, 1, 1, 19, 1, 20, 21, 22,
+ 1, 23, 24, 25, 26, 1, 1, 1, 27, 1,
+ 1, 1, 1, 1, 1, 1, 28, 1, 29, 30,
+
+ 31, 32, 1, 33, 34, 1, 1, 35, 1, 36,
+ 37, 38, 1, 39, 40, 41, 42, 1, 1, 1,
+ 43, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[44] =
+ { 0,
+ 1, 2, 3, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[81] =
+ { 0,
+ 0, 38, 4, 14, 62, 61, 58, 178, 16, 22,
+ 49, 18, 35, 47, 2, 28, 64, 34, 48, 0,
+ 178, 0, 32, 61, 70, 72, 82, 83, 60, 68,
+ 34, 27, 75, 82, 0, 0, 70, 82, 86, 92,
+ 94, 80, 83, 84, 91, 91, 98, 92, 178, 178,
+ 102, 113, 108, 113, 110, 109, 113, 122, 178, 113,
+ 116, 122, 21, 178, 15, 119, 178, 178, 122, 130,
+ 128, 130, 1, 178, 178, 166, 169, 172, 0, 175
+ } ;
+
+static yyconst flex_int16_t yy_def[81] =
+ { 0,
+ 76, 76, 77, 77, 78, 78, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 79,
+ 75, 80, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 79, 80, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 0, 75, 75, 75, 75, 75
+ } ;
+
+static yyconst flex_int16_t yy_nxt[222] =
+ { 0,
+ 35, 9, 10, 11, 75, 21, 21, 12, 13, 14,
+ 21, 74, 15, 16, 17, 21, 21, 23, 23, 29,
+ 21, 18, 19, 23, 23, 68, 25, 26, 15, 16,
+ 17, 67, 27, 23, 23, 29, 32, 18, 19, 9,
+ 10, 11, 30, 32, 28, 12, 13, 14, 27, 27,
+ 15, 16, 17, 24, 33, 25, 26, 75, 30, 18,
+ 19, 27, 34, 21, 21, 27, 15, 16, 17, 31,
+ 33, 31, 37, 32, 38, 18, 19, 27, 34, 28,
+ 25, 26, 40, 41, 27, 39, 27, 31, 37, 31,
+ 38, 32, 28, 42, 43, 44, 45, 27, 40, 41,
+
+ 27, 39, 27, 46, 47, 48, 49, 50, 51, 42,
+ 43, 44, 45, 27, 52, 53, 54, 55, 56, 46,
+ 47, 48, 49, 50, 51, 57, 58, 59, 60, 61,
+ 52, 53, 54, 55, 56, 62, 63, 64, 65, 66,
+ 69, 57, 58, 59, 60, 61, 70, 71, 72, 73,
+ 75, 62, 63, 64, 65, 66, 69, 75, 75, 75,
+ 75, 75, 70, 71, 72, 73, 8, 8, 8, 20,
+ 20, 20, 22, 22, 22, 36, 36, 7, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75
+ } ;
+
+static yyconst flex_int16_t yy_chk[222] =
+ { 0,
+ 79, 1, 1, 1, 0, 3, 3, 1, 1, 1,
+ 3, 73, 1, 1, 1, 4, 4, 9, 9, 15,
+ 4, 1, 1, 10, 10, 65, 12, 12, 1, 1,
+ 1, 63, 12, 23, 23, 15, 32, 1, 1, 2,
+ 2, 2, 16, 31, 13, 2, 2, 2, 12, 13,
+ 2, 2, 2, 11, 18, 14, 14, 7, 16, 2,
+ 2, 14, 19, 6, 5, 13, 2, 2, 2, 17,
+ 18, 17, 24, 17, 24, 2, 2, 14, 19, 25,
+ 26, 26, 29, 30, 25, 24, 26, 27, 24, 27,
+ 24, 27, 28, 33, 34, 37, 38, 28, 29, 30,
+
+ 25, 24, 26, 39, 40, 41, 42, 43, 44, 33,
+ 34, 37, 38, 28, 45, 46, 47, 48, 51, 39,
+ 40, 41, 42, 43, 44, 52, 53, 54, 55, 56,
+ 45, 46, 47, 48, 51, 57, 58, 60, 61, 62,
+ 66, 52, 53, 54, 55, 56, 69, 70, 71, 72,
+ 0, 57, 58, 60, 61, 62, 66, 0, 0, 0,
+ 0, 0, 69, 70, 71, 72, 76, 76, 76, 77,
+ 77, 77, 78, 78, 78, 80, 80, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 75
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int ncsa__flex_debug;
+int ncsa__flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *ncsa_text;
+#line 1 "imap_ncsa.l"
+#line 2 "imap_ncsa.l"
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "imap_ncsa_parse.h"
+
+#ifdef FLEX_SCANNER
+#define YY_NO_UNPUT
+#endif /* FLEX_SCANNER */
+
+#define YY_NO_INPUT 1
+
+
+#line 581 "<stdout>"
+
+#define INITIAL 0
+#define imap_link 1
+#define comment 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int ncsa_lex_destroy (void );
+
+int ncsa_get_debug (void );
+
+void ncsa_set_debug (int debug_flag );
+
+YY_EXTRA_TYPE ncsa_get_extra (void );
+
+void ncsa_set_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *ncsa_get_in (void );
+
+void ncsa_set_in (FILE * in_str );
+
+FILE *ncsa_get_out (void );
+
+void ncsa_set_out (FILE * out_str );
+
+yy_size_t ncsa_get_leng (void );
+
+char *ncsa_get_text (void );
+
+int ncsa_get_lineno (void );
+
+void ncsa_set_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ncsa_wrap (void );
+#else
+extern int ncsa_wrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( ncsa_text, ncsa_leng, 1, ncsa_out )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( ncsa_in )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( ncsa_in ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, ncsa_in))==0 && ferror(ncsa_in)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(ncsa_in); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int ncsa_lex (void);
+
+#define YY_DECL int ncsa_lex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after ncsa_text and ncsa_leng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 47 "imap_ncsa.l"
+
+
+#line 766 "<stdout>"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! ncsa_in )
+ ncsa_in = stdin;
+
+ if ( ! ncsa_out )
+ ncsa_out = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ncsa_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ ncsa__create_buffer(ncsa_in,YY_BUF_SIZE );
+ }
+
+ ncsa__load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of ncsa_text. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 76 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 178 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 49 "imap_ncsa.l"
+{
+ BEGIN(comment);
+ return AUTHOR;
+ }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 54 "imap_ncsa.l"
+{
+ BEGIN(comment);
+ return TITLE;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 59 "imap_ncsa.l"
+{
+ BEGIN(comment);
+ return DESCRIPTION;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 64 "imap_ncsa.l"
+{
+ BEGIN(comment);
+ return BEGIN_COMMENT;
+ }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 69 "imap_ncsa.l"
+{
+ BEGIN(INITIAL);
+ ncsa_lval.id = g_strndup (ncsa_text, ncsa_leng);
+ return COMMENT;
+ }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 75 "imap_ncsa.l"
+{
+ BEGIN(imap_link);
+ return RECTANGLE;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 80 "imap_ncsa.l"
+{
+ BEGIN(imap_link);
+ return CIRCLE;
+ }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 85 "imap_ncsa.l"
+{
+ BEGIN(imap_link);
+ return POLYGON;
+ }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 90 "imap_ncsa.l"
+{
+ BEGIN(imap_link);
+ return DEFAULT;
+ }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 95 "imap_ncsa.l"
+{
+ BEGIN(INITIAL);
+ ncsa_lval.id = g_strndup (ncsa_text, ncsa_leng);
+ return LINK;
+ }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 101 "imap_ncsa.l"
+{
+ ncsa_lval.value = g_ascii_strtod (ncsa_text, NULL);
+ return FLOAT;
+ }
+ YY_BREAK
+case 12:
+/* rule 12 can match eol */
+YY_RULE_SETUP
+#line 106 "imap_ncsa.l"
+; /* Eat white space */
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 108 "imap_ncsa.l"
+return *ncsa_text;
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 110 "imap_ncsa.l"
+ECHO;
+ YY_BREAK
+#line 955 "<stdout>"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(imap_link):
+case YY_STATE_EOF(comment):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed ncsa_in at a new source and called
+ * ncsa_lex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = ncsa_in;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( ncsa_wrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * ncsa_text, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of ncsa_lex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ yy_size_t new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ ncsa_realloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ ncsa_restart(ncsa_in );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) ncsa_realloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 76 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 76 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 75);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ ncsa_restart(ncsa_in );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ncsa_wrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve ncsa_text */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void ncsa_restart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ncsa_ensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ ncsa__create_buffer(ncsa_in,YY_BUF_SIZE );
+ }
+
+ ncsa__init_buffer(YY_CURRENT_BUFFER,input_file );
+ ncsa__load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void ncsa__switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * ncsa_pop_buffer_state();
+ * ncsa_push_buffer_state(new_buffer);
+ */
+ ncsa_ensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ ncsa__load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ncsa_wrap()) processing, but the only time this flag
+ * is looked at is after ncsa_wrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void ncsa__load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ ncsa_in = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE ncsa__create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ncsa_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa__create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ncsa_alloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa__create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ ncsa__init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with ncsa__create_buffer()
+ *
+ */
+ void ncsa__delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ncsa_free((void *) b->yy_ch_buf );
+
+ ncsa_free((void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a ncsa_restart() or at EOF.
+ */
+ static void ncsa__init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ ncsa__flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then ncsa__init_buffer was _probably_
+ * called from ncsa_restart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void ncsa__flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ ncsa__load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void ncsa_push_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ ncsa_ensure_buffer_stack();
+
+ /* This block is copied from ncsa__switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from ncsa__switch_to_buffer. */
+ ncsa__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void ncsa_pop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ ncsa__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ ncsa__load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ncsa_ensure_buffer_stack (void)
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)ncsa_alloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa_ensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)ncsa_realloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa_ensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ncsa__scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) ncsa_alloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa__scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ ncsa__switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to ncsa_lex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * ncsa__scan_bytes() instead.
+ */
+YY_BUFFER_STATE ncsa__scan_string (yyconst char * yystr )
+{
+
+ return ncsa__scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to ncsa_lex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ncsa__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) ncsa_alloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ncsa__scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = ncsa__scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in ncsa__scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up ncsa_text. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ ncsa_text[ncsa_leng] = (yy_hold_char); \
+ (yy_c_buf_p) = ncsa_text + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ ncsa_leng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int ncsa_get_lineno (void)
+{
+
+ return ncsa_lineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *ncsa_get_in (void)
+{
+ return ncsa_in;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *ncsa_get_out (void)
+{
+ return ncsa_out;
+}
+
+/** Get the length of the current token.
+ *
+ */
+yy_size_t ncsa_get_leng (void)
+{
+ return ncsa_leng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *ncsa_get_text (void)
+{
+ return ncsa_text;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void ncsa_set_lineno (int line_number )
+{
+
+ ncsa_lineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see ncsa__switch_to_buffer
+ */
+void ncsa_set_in (FILE * in_str )
+{
+ ncsa_in = in_str ;
+}
+
+void ncsa_set_out (FILE * out_str )
+{
+ ncsa_out = out_str ;
+}
+
+int ncsa_get_debug (void)
+{
+ return ncsa__flex_debug;
+}
+
+void ncsa_set_debug (int bdebug )
+{
+ ncsa__flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from ncsa_lex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ ncsa_in = stdin;
+ ncsa_out = stdout;
+#else
+ ncsa_in = (FILE *) 0;
+ ncsa_out = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * ncsa_lex_init()
+ */
+ return 0;
+}
+
+/* ncsa_lex_destroy is for both reentrant and non-reentrant scanners. */
+int ncsa_lex_destroy (void)
+{
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ ncsa__delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ ncsa_pop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ ncsa_free((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * ncsa_lex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *ncsa_alloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *ncsa_realloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void ncsa_free (void * ptr )
+{
+ free( (char *) ptr ); /* see ncsa_realloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 110 "imap_ncsa.l"
+
+
+
+
+
diff --git a/plug-ins/imagemap/imap_ncsa_parse.c b/plug-ins/imagemap/imap_ncsa_parse.c
new file mode 100644
index 0000000..d2bdf47
--- /dev/null
+++ b/plug-ins/imagemap/imap_ncsa_parse.c
@@ -0,0 +1,1830 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.6.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names. */
+#define yyparse ncsa_parse
+#define yylex ncsa_lex
+#define yyerror ncsa_error
+#define yylval ncsa_lval
+#define yychar ncsa_char
+#define yydebug ncsa_debug
+#define yynerrs ncsa_nerrs
+
+/* Copy the first part of user declarations. */
+/* Line 336 of yacc.c */
+#line 1 "imap_ncsa.y"
+
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <math.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_circle.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_polygon.h"
+#include "imap_rectangle.h"
+#include "imap_string.h"
+
+extern int ncsa_lex(void);
+extern int ncsa_restart(FILE *ncsa_in);
+static void ncsa_error(char* s);
+
+static Object_t *current_object;
+
+
+/* Line 336 of yacc.c */
+#line 119 "y.tab.c"
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "y.tab.h". */
+#ifndef NCSA_Y_TAB_H
+# define NCSA_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int ncsa_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ RECTANGLE = 258,
+ POLYGON = 259,
+ CIRCLE = 260,
+ DEFAULT = 261,
+ AUTHOR = 262,
+ TITLE = 263,
+ DESCRIPTION = 264,
+ BEGIN_COMMENT = 265,
+ FLOAT = 266,
+ LINK = 267,
+ COMMENT = 268
+ };
+#endif
+/* Tokens. */
+#define RECTANGLE 258
+#define POLYGON 259
+#define CIRCLE 260
+#define DEFAULT 261
+#define AUTHOR 262
+#define TITLE 263
+#define DESCRIPTION 264
+#define BEGIN_COMMENT 265
+#define FLOAT 266
+#define LINK 267
+#define COMMENT 268
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 350 of yacc.c */
+#line 45 "imap_ncsa.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 350 of yacc.c */
+#line 195 "y.tab.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE ncsa_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int ncsa_parse (void *YYPARSE_PARAM);
+#else
+int ncsa_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int ncsa_parse (void);
+#else
+int ncsa_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !NCSA_Y_TAB_H */
+
+/* Copy the second part of user declarations. */
+
+/* Line 353 of yacc.c */
+#line 223 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 3
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 35
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 15
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 17
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 27
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 50
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 14, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 6, 7, 10, 12, 14, 16, 18,
+ 21, 24, 27, 30, 31, 34, 36, 38, 40, 42,
+ 44, 47, 56, 65, 66, 71, 72, 75
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 16, 0, -1, 17, 23, -1, -1, 17, 18, -1,
+ 20, -1, 21, -1, 22, -1, 19, -1, 10, 13,
+ -1, 7, 13, -1, 8, 13, -1, 9, 13, -1,
+ -1, 23, 24, -1, 25, -1, 26, -1, 27, -1,
+ 28, -1, 19, -1, 6, 12, -1, 3, 12, 11,
+ 14, 11, 11, 14, 11, -1, 5, 12, 11, 14,
+ 11, 11, 14, 11, -1, -1, 4, 12, 29, 30,
+ -1, -1, 30, 31, -1, 11, 14, 11, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 58, 58, 61, 62, 65, 66, 67, 68, 71,
+ 77, 85, 93, 105, 106, 109, 110, 111, 112, 113,
+ 116, 125, 138, 150, 150, 158, 159, 164
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "RECTANGLE", "POLYGON", "CIRCLE",
+ "DEFAULT", "AUTHOR", "TITLE", "DESCRIPTION", "BEGIN_COMMENT", "FLOAT",
+ "LINK", "COMMENT", "','", "$accept", "ncsa_file", "comment_lines",
+ "comment_line", "real_comment", "author_line", "title_line",
+ "description_line", "area_list", "area", "default", "rectangle",
+ "circle", "polygon", "$@1", "coord_list", "coord", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 15, 16, 17, 17, 18, 18, 18, 18, 19,
+ 20, 21, 22, 23, 23, 24, 24, 24, 24, 24,
+ 25, 26, 27, 29, 28, 30, 30, 31
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 2, 0, 2, 1, 1, 1, 1, 2,
+ 2, 2, 2, 0, 2, 1, 1, 1, 1, 1,
+ 2, 8, 8, 0, 4, 0, 2, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 3, 0, 13, 1, 0, 0, 0, 0, 4, 8,
+ 5, 6, 7, 2, 10, 11, 12, 9, 0, 0,
+ 0, 0, 19, 14, 15, 16, 17, 18, 0, 23,
+ 0, 20, 0, 25, 0, 0, 24, 0, 0, 0,
+ 26, 0, 0, 0, 0, 0, 27, 0, 21, 22
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 1, 2, 8, 9, 10, 11, 12, 13, 23,
+ 24, 25, 26, 27, 33, 36, 40
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -9
+static const yytype_int8 yypact[] =
+{
+ -9, 4, 1, -9, -8, -7, -1, 0, -9, -9,
+ -9, -9, -9, -3, -9, -9, -9, -9, 2, 3,
+ 5, 6, -9, -9, -9, -9, -9, -9, 8, -9,
+ 9, -9, 7, -9, 10, 11, 12, 14, 15, 13,
+ -9, 17, 16, 18, 19, 20, -9, 21, -9, -9
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -9, -9, -9, -9, 22, -9, -9, -9, -9, -9,
+ -9, -9, -9, -9, -9, -9, -9
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 18, 19, 20, 21, 3, 14, 15, 7, 4, 5,
+ 6, 7, 16, 17, 28, 29, 0, 30, 31, 32,
+ 34, 35, 38, 39, 37, 41, 42, 43, 44, 46,
+ 45, 48, 49, 47, 0, 22
+};
+
+#define yypact_value_is_default(yystate) \
+ ((yystate) == (-9))
+
+#define yytable_value_is_error(yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 3, 4, 5, 6, 0, 13, 13, 10, 7, 8,
+ 9, 10, 13, 13, 12, 12, -1, 12, 12, 11,
+ 11, 14, 11, 11, 14, 11, 11, 14, 11, 11,
+ 14, 11, 11, 14, -1, 13
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 16, 17, 0, 7, 8, 9, 10, 18, 19,
+ 20, 21, 22, 23, 13, 13, 13, 13, 3, 4,
+ 5, 6, 19, 24, 25, 26, 27, 28, 12, 12,
+ 12, 12, 11, 29, 11, 14, 30, 14, 11, 11,
+ 31, 11, 11, 14, 11, 14, 11, 14, 11, 11
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+
+
+/* The lookahead symbol. */
+int yychar;
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yytoken = 0;
+ yyss = yyssa;
+ yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+ yyssp = yyss;
+ yyvsp = yyvs;
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 9:
+/* Line 1787 of yacc.c */
+#line 72 "imap_ncsa.y"
+ {
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 10:
+/* Line 1787 of yacc.c */
+#line 78 "imap_ncsa.y"
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->author, (yyvsp[(2) - (2)].id));
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 11:
+/* Line 1787 of yacc.c */
+#line 86 "imap_ncsa.y"
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->title, (yyvsp[(2) - (2)].id));
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 12:
+/* Line 1787 of yacc.c */
+#line 94 "imap_ncsa.y"
+ {
+ MapInfo_t *info = get_map_info();
+ gchar *description;
+
+ description = g_strconcat(info->description, (yyvsp[(2) - (2)].id), "\n",
+ NULL);
+ g_strreplace(&info->description, description);
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 20:
+/* Line 1787 of yacc.c */
+#line 117 "imap_ncsa.y"
+ {
+ MapInfo_t *info = get_map_info();
+ g_strreplace(&info->default_url, (yyvsp[(2) - (2)].id));
+ g_free ((yyvsp[(2) - (2)].id));
+ }
+ break;
+
+ case 21:
+/* Line 1787 of yacc.c */
+#line 126 "imap_ncsa.y"
+ {
+ gint x = (gint) (yyvsp[(3) - (8)].value);
+ gint y = (gint) (yyvsp[(5) - (8)].value);
+ gint width = (gint) fabs((yyvsp[(6) - (8)].value) - x);
+ gint height = (gint) fabs((yyvsp[(8) - (8)].value) - y);
+ current_object = create_rectangle(x, y, width, height);
+ object_set_url(current_object, (yyvsp[(2) - (8)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(2) - (8)].id));
+ }
+ break;
+
+ case 22:
+/* Line 1787 of yacc.c */
+#line 139 "imap_ncsa.y"
+ {
+ gint x = (gint) (yyvsp[(3) - (8)].value);
+ gint y = (gint) (yyvsp[(5) - (8)].value);
+ gint r = (gint) fabs((yyvsp[(8) - (8)].value) - (yyvsp[(5) - (8)].value));
+ current_object = create_circle(x, y, r);
+ object_set_url(current_object, (yyvsp[(2) - (8)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(2) - (8)].id));
+ }
+ break;
+
+ case 23:
+/* Line 1787 of yacc.c */
+#line 150 "imap_ncsa.y"
+ {current_object = create_polygon(NULL);}
+ break;
+
+ case 24:
+/* Line 1787 of yacc.c */
+#line 151 "imap_ncsa.y"
+ {
+ object_set_url(current_object, (yyvsp[(2) - (4)].id));
+ add_shape(current_object);
+ g_free ((yyvsp[(2) - (4)].id));
+ }
+ break;
+
+ case 26:
+/* Line 1787 of yacc.c */
+#line 160 "imap_ncsa.y"
+ {
+ }
+ break;
+
+ case 27:
+/* Line 1787 of yacc.c */
+#line 165 "imap_ncsa.y"
+ {
+ Polygon_t *polygon = ObjectToPolygon(current_object);
+ GdkPoint *point = new_point((gint) (yyvsp[(1) - (3)].value), (gint) (yyvsp[(3) - (3)].value));
+ polygon->points = g_list_append(polygon->points,
+ (gpointer) point);
+ }
+ break;
+
+
+/* Line 1787 of yacc.c */
+#line 1577 "y.tab.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+/* Line 2048 of yacc.c */
+#line 173 "imap_ncsa.y"
+
+
+static void
+ncsa_error(char* s)
+{
+ extern FILE *ncsa_in;
+ ncsa_restart(ncsa_in);
+}
+
+gboolean
+load_ncsa(const char* filename)
+{
+ gboolean status;
+ extern FILE *ncsa_in;
+ ncsa_in = g_fopen(filename, "r");
+ if (ncsa_in) {
+ status = !ncsa_parse();
+ fclose(ncsa_in);
+ } else {
+ status = FALSE;
+ }
+ return status;
+}
+
diff --git a/plug-ins/imagemap/imap_ncsa_parse.h b/plug-ins/imagemap/imap_ncsa_parse.h
new file mode 100644
index 0000000..68791a2
--- /dev/null
+++ b/plug-ins/imagemap/imap_ncsa_parse.h
@@ -0,0 +1,112 @@
+/* A Bison parser, made by GNU Bison 2.6.1. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef NCSA_Y_TAB_H
+# define NCSA_Y_TAB_H
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int ncsa_debug;
+#endif
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ RECTANGLE = 258,
+ POLYGON = 259,
+ CIRCLE = 260,
+ DEFAULT = 261,
+ AUTHOR = 262,
+ TITLE = 263,
+ DESCRIPTION = 264,
+ BEGIN_COMMENT = 265,
+ FLOAT = 266,
+ LINK = 267,
+ COMMENT = 268
+ };
+#endif
+/* Tokens. */
+#define RECTANGLE 258
+#define POLYGON 259
+#define CIRCLE 260
+#define DEFAULT 261
+#define AUTHOR 262
+#define TITLE 263
+#define DESCRIPTION 264
+#define BEGIN_COMMENT 265
+#define FLOAT 266
+#define LINK 267
+#define COMMENT 268
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+/* Line 2049 of yacc.c */
+#line 45 "imap_ncsa.y"
+
+ int val;
+ double value;
+ char *id;
+
+
+/* Line 2049 of yacc.c */
+#line 90 "y.tab.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE ncsa_lval;
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int ncsa_parse (void *YYPARSE_PARAM);
+#else
+int ncsa_parse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int ncsa_parse (void);
+#else
+int ncsa_parse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !NCSA_Y_TAB_H */
diff --git a/plug-ins/imagemap/imap_object.c b/plug-ins/imagemap/imap_object.c
new file mode 100644
index 0000000..2f06e92
--- /dev/null
+++ b/plug-ins/imagemap/imap_object.c
@@ -0,0 +1,1040 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_default_dialog.h"
+#include "imap_grid.h"
+#include "imap_main.h"
+#include "imap_object.h"
+#include "imap_string.h"
+
+typedef struct {
+ ObjectListCallbackFunc_t func;
+ gpointer data;
+} ObjectListCB_t;
+
+static ObjectList_t *_paste_buffer;
+
+static gpointer
+object_list_callback_add(ObjectListCallback_t *list,
+ ObjectListCallbackFunc_t func, gpointer data)
+{
+ ObjectListCB_t *cb = g_new(ObjectListCB_t, 1);
+ cb->func = func;
+ cb->data = data;
+ list->list = g_list_append(list->list, cb);
+ return cb;
+}
+
+static void
+object_list_callback_remove(ObjectListCallback_t *list, gpointer id)
+{
+ list->list = g_list_remove(list->list, id);
+}
+
+static void
+object_list_callback_call(ObjectListCallback_t *list, Object_t *obj)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ ObjectListCB_t *cb = (ObjectListCB_t*) p->data;
+ cb->func(obj, cb->data);
+ }
+}
+
+gpointer
+object_list_add_changed_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->changed_cb, func, data);
+}
+
+gpointer
+object_list_add_update_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->update_cb, func, data);
+}
+
+gpointer
+object_list_add_add_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->add_cb, func, data);
+}
+
+gpointer
+object_list_add_remove_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->remove_cb, func, data);
+}
+
+gpointer
+object_list_add_select_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->select_cb, func, data);
+}
+
+gpointer
+object_list_add_move_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->move_cb, func, data);
+}
+
+gpointer
+object_list_add_geometry_cb(ObjectList_t *list, ObjectListCallbackFunc_t func,
+ gpointer data)
+{
+ return object_list_callback_add(&list->geometry_cb, func, data);
+}
+
+gpointer
+paste_buffer_add_add_cb(ObjectListCallbackFunc_t func, gpointer data)
+{
+ if (!_paste_buffer)
+ _paste_buffer = make_object_list();
+ return object_list_callback_add(&_paste_buffer->add_cb, func, data);
+}
+
+gpointer
+paste_buffer_add_remove_cb(ObjectListCallbackFunc_t func, gpointer data)
+{
+ if (!_paste_buffer)
+ _paste_buffer = make_object_list();
+ return object_list_callback_add(&_paste_buffer->remove_cb, func, data);
+}
+
+void
+object_list_remove_add_cb(ObjectList_t *list, gpointer id)
+{
+ object_list_callback_remove(&list->add_cb, id);
+}
+
+void
+object_list_remove_select_cb(ObjectList_t *list, gpointer id)
+{
+ object_list_callback_remove(&list->select_cb, id);
+}
+
+void
+object_list_remove_remove_cb(ObjectList_t *list, gpointer id)
+{
+ object_list_callback_remove(&list->remove_cb, id);
+}
+
+void
+object_list_remove_move_cb(ObjectList_t *list, gpointer id)
+{
+ object_list_callback_remove(&list->move_cb, id);
+}
+
+void
+object_list_remove_geometry_cb(ObjectList_t *list, gpointer id)
+{
+ object_list_callback_remove(&list->geometry_cb, id);
+}
+
+Object_t*
+object_init(Object_t *obj, ObjectClass_t *class)
+{
+ obj->class = class;
+ obj->refcount = 1;
+ obj->selected = FALSE;
+ obj->locked = FALSE;
+ obj->url = g_strdup("");
+ obj->target = g_strdup("");
+ obj->comment = g_strdup("");
+ obj->mouse_over = g_strdup("");
+ obj->mouse_out = g_strdup("");
+ obj->focus = g_strdup("");
+ obj->blur = g_strdup("");
+ return obj;
+}
+
+static void
+object_destruct(Object_t *obj)
+{
+ if (obj->class->destruct)
+ obj->class->destruct(obj);
+ g_free(obj->url);
+ g_free(obj->target);
+ g_free(obj->comment);
+ g_free(obj->mouse_over);
+ g_free(obj->mouse_out);
+ g_free(obj->focus);
+ g_free(obj->blur);
+ g_free(obj);
+}
+
+Object_t*
+object_ref(Object_t *obj)
+{
+ obj->refcount++;
+ return obj;
+}
+
+void
+object_unref(Object_t *obj)
+{
+ if (!--obj->refcount)
+ object_destruct(obj);
+}
+
+Object_t*
+object_clone(Object_t *obj)
+{
+ Object_t *clone = obj->class->clone(obj);
+ clone->class = obj->class;
+ clone->refcount = 1;
+ clone->selected = obj->selected;
+ clone->locked = FALSE;
+ clone->url = g_strdup(obj->url);
+ clone->target = g_strdup(obj->target);
+ clone->comment = g_strdup(obj->comment);
+ clone->mouse_over = g_strdup(obj->mouse_over);
+ clone->mouse_out = g_strdup(obj->mouse_out);
+ clone->focus = g_strdup(obj->focus);
+ clone->blur = g_strdup(obj->blur);
+ return clone;
+}
+
+static Object_t*
+object_copy(Object_t *src, Object_t *des)
+{
+ des->class = src->class;
+ des->selected = src->selected;
+ des->locked = FALSE;
+ g_strreplace(&des->url, src->url);
+ g_strreplace(&des->target, src->target);
+ g_strreplace(&des->comment, src->comment);
+ g_strreplace(&des->mouse_over, src->mouse_over);
+ g_strreplace(&des->mouse_out, src->mouse_out);
+ g_strreplace(&des->focus, src->focus);
+ g_strreplace(&des->blur, src->blur);
+ return des;
+}
+
+Object_t*
+object_assign(Object_t *obj, Object_t *des)
+{
+ obj->class->assign(obj, des);
+ return object_copy(obj, des);
+}
+
+void
+object_draw(Object_t *obj, cairo_t *cr)
+{
+ PreferencesData_t *preferences = get_preferences();
+ ColorSelData_t *colors = &preferences->colors;
+ GdkColor *fg, *bg;
+ gdouble dash = 4.;
+
+ if (obj->selected & 4) {
+ fg = &colors->interactive_fg;
+ bg = &colors->interactive_bg;
+ obj->selected &= ~4;
+ } else if (obj->selected) {
+ fg = &colors->selected_fg;
+ bg = &colors->selected_bg;
+ } else {
+ fg = &colors->normal_fg;
+ bg = &colors->normal_bg;
+ }
+
+ cairo_save (cr);
+ gdk_cairo_set_source_color (cr, bg);
+ obj->class->draw(obj, cr);
+ gdk_cairo_set_source_color (cr, fg);
+ cairo_set_dash (cr, &dash, 1, 0.);
+ obj->class->draw(obj, cr);
+
+ if (obj->selected && preferences->show_area_handle)
+ obj->class->draw_sashes(obj, cr);
+ cairo_restore (cr);
+}
+
+void
+object_edit(Object_t *obj, gboolean add)
+{
+ if (!obj->class->info_dialog)
+ obj->class->info_dialog = create_edit_area_info_dialog(obj);
+ edit_area_info_dialog_show(obj->class->info_dialog, obj, add);
+}
+
+void
+object_select(Object_t *obj)
+{
+ obj->selected = TRUE;
+ object_list_callback_call(&obj->list->select_cb, obj);
+ object_emit_geometry_signal(obj);
+}
+
+void
+object_unselect(Object_t *obj)
+{
+ obj->selected = FALSE;
+ object_list_callback_call(&obj->list->select_cb, obj);
+ object_emit_geometry_signal(obj);
+}
+
+void
+object_move(Object_t *obj, gint dx, gint dy)
+{
+ obj->class->move(obj, dx, dy);
+ object_emit_geometry_signal(obj);
+}
+
+void
+object_move_sash(Object_t *obj, gint dx, gint dy)
+{
+ gint x, y, width, height;
+ MoveSashFunc_t sash_func;
+
+ obj->class->get_dimensions(obj, &x, &y, &width, &height);
+ if (dx == 0)
+ x += (width / 2);
+ else
+ x += width;
+
+ if (dy == 0)
+ y += (height / 2);
+ else
+ y += height;
+
+ sash_func = obj->class->near_sash(obj, x, y);
+
+ if (sash_func) {
+ sash_func(obj, dx, dy);
+ object_emit_geometry_signal(obj);
+ }
+}
+
+void
+object_remove(Object_t *obj)
+{
+ object_list_remove(obj->list, obj);
+ object_emit_geometry_signal(obj);
+}
+
+void
+object_lock(Object_t *obj)
+{
+ obj->locked = TRUE;
+}
+
+void
+object_unlock(Object_t *obj)
+{
+ obj->locked = FALSE;
+}
+
+void
+object_set_url(Object_t *obj, const gchar *url)
+{
+ g_strreplace(&obj->url, url);
+}
+
+void
+object_set_target(Object_t *obj, const gchar *target)
+{
+ g_strreplace(&obj->target, target);
+}
+
+void
+object_set_comment(Object_t *obj, const gchar *comment)
+{
+ g_strreplace(&obj->comment, comment);
+}
+
+void
+object_set_mouse_over(Object_t *obj, const gchar *mouse_over)
+{
+ g_strreplace(&obj->mouse_over, mouse_over);
+}
+
+void
+object_set_mouse_out(Object_t *obj, const gchar *mouse_out)
+{
+ g_strreplace(&obj->mouse_out, mouse_out);
+}
+
+void
+object_set_focus(Object_t *obj, const gchar *focus)
+{
+ g_strreplace(&obj->focus, focus);
+}
+
+void
+object_set_blur(Object_t *obj, const gchar *blur)
+{
+ g_strreplace(&obj->blur, blur);
+}
+
+gint
+object_get_position_in_list(Object_t *obj)
+{
+ return g_list_index(obj->list->list, (gpointer) obj);
+}
+
+void
+object_emit_changed_signal(Object_t *obj)
+{
+ object_list_callback_call(&obj->list->changed_cb, obj);
+}
+
+void
+object_emit_geometry_signal(Object_t *obj)
+{
+ object_list_callback_call(&obj->list->geometry_cb, obj);
+}
+
+void
+object_emit_update_signal(Object_t *obj)
+{
+ object_list_callback_call(&obj->list->update_cb, obj);
+}
+
+void
+do_object_locked_dialog(void)
+{
+ static DefaultDialog_t *dialog;
+ if (!dialog) {
+ dialog = make_default_dialog("Object locked");
+ default_dialog_hide_cancel_button(dialog);
+ default_dialog_hide_apply_button(dialog);
+ default_dialog_set_label(
+ dialog,
+ "\n You cannot delete the selected object \n"
+ "since it is currently being edited.\n");
+ }
+ default_dialog_show(dialog);
+}
+
+static Object_t*
+object_factory_create_object(ObjectFactory_t *factory, gint x, gint y)
+{
+ return factory->obj = factory->create_object(x, y);
+}
+
+static gboolean
+button_motion(GtkWidget *widget, GdkEventMotion *event,
+ ObjectFactory_t *factory)
+{
+ gint x = get_real_coord((gint) event->x);
+ gint y = get_real_coord((gint) event->y);
+
+ round_to_grid(&x, &y);
+
+ factory->set_xy(factory->obj, event->state, x, y);
+
+ preview_redraw ();
+
+ return FALSE;
+}
+
+gboolean
+object_on_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ static ObjectFactory_t *factory;
+ PreferencesData_t *preferences = get_preferences();
+ gint x = get_real_coord((gint) event->x);
+ gint y = get_real_coord((gint) event->y);
+ static Object_t *obj;
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ return FALSE;
+ round_to_grid(&x, &y);
+
+ if (obj) {
+ if (event->button == 1) {
+ if (!factory->finish || factory->finish(obj, x, y)) {
+ g_signal_handlers_disconnect_by_func(widget,
+ button_motion,
+ factory);
+ if (object_is_valid(obj)) {
+ Command_t *command = create_command_new(get_shapes(), obj);
+ command_execute(command);
+ if (preferences->prompt_for_area_info)
+ object_edit(obj, FALSE);
+ } else {
+ object_unref(obj);
+ }
+ preview_unset_tmp_obj (obj);
+ preview_redraw ();
+ obj = NULL;
+ main_clear_dimension();
+ }
+ } else if (event->button == 3) {
+ if (!factory->cancel || factory->cancel(event, obj)) {
+ g_signal_handlers_disconnect_by_func(widget,
+ button_motion,
+ factory);
+ object_unref(obj);
+ preview_unset_tmp_obj (obj);
+ preview_redraw ();
+ obj = NULL;
+ main_clear_dimension();
+ }
+ return TRUE;
+ }
+ } else {
+ if (event->button == 1) {
+ factory = ((ObjectFactory_t*(*)(guint)) data)(event->state);
+ obj = object_factory_create_object(factory, x, y);
+ preview_set_tmp_obj (obj);
+
+ g_signal_connect(widget, "motion-notify-event",
+ G_CALLBACK(button_motion), factory);
+ }
+ }
+ return FALSE;
+}
+
+ObjectList_t*
+make_object_list(void)
+{
+ return g_new0 (ObjectList_t, 1);
+}
+
+void
+object_list_destruct(ObjectList_t *list)
+{
+ object_list_remove_all(list);
+ g_free(list->list);
+}
+
+ObjectList_t*
+object_list_append_list(ObjectList_t *des, ObjectList_t *src)
+{
+ GList *p;
+ if (!src)
+ return des;
+ for (p = src->list; p; p = p->next)
+ object_list_append(des, object_clone((Object_t*) p->data));
+ object_list_set_changed(des, TRUE);
+ return des;
+}
+
+ObjectList_t*
+object_list_copy(ObjectList_t *des, ObjectList_t *src)
+{
+ if (des)
+ object_list_remove_all(des);
+ else
+ des = make_object_list();
+
+ return object_list_append_list(des, src);
+}
+
+void
+object_list_append(ObjectList_t *list, Object_t *object)
+{
+ object->list = list;
+ list->list = g_list_append(list->list, (gpointer) object);
+ object_list_set_changed(list, TRUE);
+ object_list_callback_call(&list->add_cb, object);
+}
+
+void
+object_list_prepend(ObjectList_t *list, Object_t *object)
+{
+ object->list = list;
+ list->list = g_list_prepend(list->list, (gpointer) object);
+ object_list_set_changed(list, TRUE);
+ object_list_callback_call(&list->add_cb, object);
+}
+
+void
+object_list_insert(ObjectList_t *list, gint position, Object_t *object)
+{
+ object->list = list;
+ list->list = g_list_insert(list->list, (gpointer) object, position);
+ object_list_set_changed(list, TRUE);
+ object_list_callback_call(&list->add_cb, object);
+}
+
+void
+object_list_remove(ObjectList_t *list, Object_t *object)
+{
+ list->list = g_list_remove(list->list, (gpointer) object);
+ object_list_set_changed(list, TRUE);
+ object_list_callback_call(&list->remove_cb, object);
+ object_unref(object);
+}
+
+void
+object_list_remove_link(ObjectList_t *list, GList *link)
+{
+ list->list = g_list_remove_link(list->list, link);
+ object_list_set_changed(list, TRUE);
+ object_list_callback_call(&list->remove_cb, (Object_t*) link->data);
+}
+
+void
+object_list_update(ObjectList_t *list, Object_t *object)
+{
+ object_list_callback_call(&list->update_cb, object);
+}
+
+void
+object_list_draw(ObjectList_t *list, cairo_t *cr)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next)
+ object_draw((Object_t*) p->data, cr);
+}
+
+void
+object_list_draw_selected(ObjectList_t *list, cairo_t *cr)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_draw(obj, cr);
+ }
+}
+
+Object_t*
+object_list_find(ObjectList_t *list, gint x, gint y)
+{
+ Object_t *found = NULL;
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->class->point_is_on(obj, x, y))
+ found = obj;
+ }
+ return found;
+}
+
+Object_t*
+object_list_near_sash(ObjectList_t *list, gint x, gint y,
+ MoveSashFunc_t *sash_func)
+{
+ Object_t *found = NULL;
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected) {
+ MoveSashFunc_t func = obj->class->near_sash(obj, x, y);
+ if (func) {
+ found = obj;
+ *sash_func = func;
+ }
+ }
+ }
+ return found;
+}
+
+void
+object_list_remove_all(ObjectList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ object_list_callback_call(&list->remove_cb, obj);
+ object_unref(obj);
+ }
+ g_list_free(list->list);
+ list->list = NULL;
+ object_list_set_changed(list, TRUE);
+}
+
+void
+clear_paste_buffer(void)
+{
+ if (_paste_buffer)
+ object_list_remove_all(_paste_buffer);
+ else
+ _paste_buffer = make_object_list();
+}
+
+ObjectList_t*
+get_paste_buffer(void)
+{
+ return _paste_buffer;
+}
+
+gint
+object_list_cut(ObjectList_t *list)
+{
+ GList *p, *q;
+ gint count = 0;
+
+ clear_paste_buffer();
+ for (p = list->list; p; p = q) {
+ Object_t *obj = (Object_t*) p->data;
+ q = p->next;
+ if (obj->selected) {
+ if (obj->locked) {
+ do_object_locked_dialog();
+ } else {
+ object_list_append(_paste_buffer, obj);
+ object_list_remove_link(list, p);
+ count++;
+ }
+ }
+ }
+ object_list_set_changed(list, (count) ? TRUE : FALSE);
+ return count;
+}
+
+void
+object_list_copy_to_paste_buffer(ObjectList_t *list)
+{
+ GList *p;
+
+ clear_paste_buffer();
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_list_append(_paste_buffer, object_clone(obj));
+ }
+}
+
+void
+object_list_paste(ObjectList_t *list)
+{
+ object_list_append_list(list, _paste_buffer);
+}
+
+void
+object_list_delete_selected(ObjectList_t *list)
+{
+ GList *p, *q;
+ for (p = list->list; p; p = q) {
+ Object_t *obj = (Object_t*) p->data;
+ q = p->next;
+ if (obj->selected) {
+ if (obj->locked) {
+ do_object_locked_dialog();
+ } else {
+ object_list_remove_link(list, p);
+ object_unref(obj);
+ }
+ }
+ }
+}
+
+void
+object_list_edit_selected(ObjectList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected) {
+ object_edit(obj, TRUE);
+ break;
+ }
+ }
+}
+
+gint
+object_list_select_all(ObjectList_t *list)
+{
+ GList *p;
+ gint count = 0;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (!obj->selected) {
+ object_select(obj);
+ count++;
+ }
+ }
+ return count;
+}
+
+void
+object_list_select_next(ObjectList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected) {
+ object_unselect(obj);
+ p = (p->next) ? p->next : list->list;
+ object_select((Object_t*) p->data);
+ for (p = p->next; p; p = p->next) {
+ obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_unselect(obj);
+ }
+ break;
+ }
+ }
+}
+
+void object_list_select_prev(ObjectList_t *list)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected) {
+ GList *q = (p->prev) ? p->prev : g_list_last(list->list);
+ for (; p; p = p->next) {
+ obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_unselect(obj);
+ }
+ object_select((Object_t*) q->data);
+ break;
+ }
+ }
+}
+
+gint
+object_list_select_region(ObjectList_t *list, gint x, gint y, gint width,
+ gint height)
+{
+ GList *p;
+ gint count = 0;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ gint obj_x, obj_y, obj_width, obj_height;
+
+ object_get_dimensions(obj, &obj_x, &obj_y, &obj_width, &obj_height);
+ if (obj_x >= x && obj_x + obj_width <= x + width &&
+ obj_y >= y && obj_y + obj_height <= y + height) {
+ object_select(obj);
+ count++;
+ }
+ }
+ return count;
+}
+
+gint
+object_list_deselect_all(ObjectList_t *list, Object_t *exception)
+{
+ GList *p;
+ gint count = 0;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected && obj != exception) {
+ object_unselect(obj);
+ count++;
+ }
+ }
+ return count;
+}
+
+gint
+object_list_nr_selected(ObjectList_t *list)
+{
+ GList *p;
+ gint count = 0;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected)
+ count++;
+ }
+ return count;
+}
+
+void
+object_list_resize(ObjectList_t *list, gint percentage_x, gint percentage_y)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ object_resize(obj, percentage_x, percentage_y);
+ }
+}
+
+static void
+object_list_swap_prev(ObjectList_t *list, GList *p)
+{
+ gpointer swap = p->data;
+ p->data = p->prev->data;
+ p->prev->data = swap;
+ object_list_callback_call(&list->move_cb, (Object_t*) p->data);
+ object_list_callback_call(&list->move_cb, (Object_t*) p->prev->data);
+}
+
+static void
+object_list_swap_next(ObjectList_t *list, GList *p)
+{
+ gpointer swap = p->data;
+ p->data = p->next->data;
+ p->next->data = swap;
+ object_list_callback_call(&list->move_cb, (Object_t*) p->data);
+ object_list_callback_call(&list->move_cb, (Object_t*) p->next->data);
+}
+
+void
+object_list_move_selected(ObjectList_t *list, gint dx, gint dy)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_move(obj, dx, dy);
+ }
+}
+
+void
+object_list_move_up(ObjectList_t *list, Object_t *obj)
+{
+ GList *p = g_list_find(list->list, (gpointer) obj);
+ object_list_swap_prev(list, p);
+}
+
+void
+object_list_move_down(ObjectList_t *list, Object_t *obj)
+{
+ GList *p = g_list_find(list->list, (gpointer) obj);
+ object_list_swap_next(list, p);
+}
+
+void
+object_list_move_selected_up(ObjectList_t *list)
+{
+ GList *p;
+
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected && p->prev)
+ object_list_swap_prev(list, p);
+ }
+}
+
+void
+object_list_move_selected_down(ObjectList_t *list)
+{
+ GList *p;
+
+ for (p = g_list_last(list->list); p; p = p->prev) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected && p->next)
+ object_list_swap_next(list, p);
+ }
+}
+
+void
+object_list_move_to_front(ObjectList_t *list)
+{
+ GList *p, *q;
+ guint length = g_list_length(list->list);
+
+ for (p = list->list; length; p = q, length--) {
+ Object_t *obj = (Object_t*) p->data;
+ q = p->next;
+ if (obj->selected) {
+ object_list_remove_link(list, p);
+ object_list_append(list, obj);
+ }
+ }
+}
+
+void
+object_list_send_to_back(ObjectList_t *list)
+{
+ GList *p, *q;
+ guint length = g_list_length(list->list);
+
+ for (p = list->list; length; p = q, length--) {
+ Object_t *obj = (Object_t*) p->data;
+ q = p->next;
+ if (obj->selected) {
+ object_list_remove_link(list, p);
+ object_list_prepend(list, obj);
+ }
+ }
+}
+
+void
+object_list_move_sash_selected(ObjectList_t *list, gint dx, gint dy)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ if (obj->selected)
+ object_move_sash(obj, dx, dy);
+ }
+}
+
+static void
+write_xml_attrib(const gchar *attrib, const gchar *value,
+ const gchar *default_text, gpointer param,
+ OutputFunc_t output)
+{
+ if (*value) {
+ gchar *escaped_value = g_markup_escape_text(value, -1);
+ output(param, " %s=\"%s\"", attrib, escaped_value);
+ g_free(escaped_value);
+ } else if (*default_text) {
+ output(param, " %s", default_text);
+ }
+}
+
+void
+object_list_write_csim(ObjectList_t *list, gpointer param, OutputFunc_t output)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+
+ output(param, "<area shape=");
+ obj->class->write_csim(obj, param, output);
+
+ write_xml_attrib("alt", obj->comment, "", param, output);
+ write_xml_attrib("target", obj->target, "", param, output);
+ write_xml_attrib("onmouseover", obj->mouse_over, "", param, output);
+ write_xml_attrib("onmouseout", obj->mouse_out, "", param, output);
+ write_xml_attrib("onfocus", obj->focus, "", param, output);
+ write_xml_attrib("onblur", obj->blur, "", param, output);
+ write_xml_attrib("href", obj->url, " nohref=\"nohref\"", param, output);
+ output(param," />\n");
+ }
+}
+
+void
+object_list_write_cern(ObjectList_t *list, gpointer param, OutputFunc_t output)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+ obj->class->write_cern(obj, param, output);
+ output(param, " %s\n", obj->url);
+ }
+}
+
+void
+object_list_write_ncsa(ObjectList_t *list, gpointer param, OutputFunc_t output)
+{
+ GList *p;
+ for (p = list->list; p; p = p->next) {
+ Object_t *obj = (Object_t*) p->data;
+
+ if (*obj->comment)
+ output(param, "# %s\n", obj->comment);
+ obj->class->write_ncsa(obj, param, output);
+ output(param, "\n");
+ }
+}
diff --git a/plug-ins/imagemap/imap_object.h b/plug-ins/imagemap/imap_object.h
new file mode 100644
index 0000000..0aaf078
--- /dev/null
+++ b/plug-ins/imagemap/imap_object.h
@@ -0,0 +1,253 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_OBJECT_H
+#define _IMAP_OBJECT_H
+
+typedef struct Object_t Object_t;
+typedef struct ObjectClass_t ObjectClass_t;
+typedef struct ObjectList_t ObjectList_t;
+
+#include "imap_edit_area_info.h"
+#include "imap_menu_funcs.h"
+
+struct Object_t {
+ ObjectClass_t *class;
+ ObjectList_t *list;
+ gint refcount;
+ gboolean selected;
+ gboolean locked;
+ gchar *url;
+ gchar *target;
+ gchar *comment;
+ gchar *mouse_over;
+ gchar *mouse_out;
+ gchar *focus;
+ gchar *blur;
+};
+
+typedef void (*MoveSashFunc_t)(Object_t*, gint, gint);
+typedef void (*OutputFunc_t)(gpointer, const char*, ...) G_GNUC_PRINTF(2,3);
+
+struct AreaInfoDialog_t;
+
+struct ObjectClass_t {
+ const gchar *name;
+ AreaInfoDialog_t *info_dialog;
+
+ gboolean (*is_valid)(Object_t *obj);
+ void (*destruct)(Object_t *obj);
+ Object_t* (*clone)(Object_t *obj);
+ void (*assign)(Object_t *obj, Object_t *des);
+ void (*normalize)(Object_t *obj);
+ void (*draw)(Object_t *obj, cairo_t *cr);
+ void (*draw_sashes)(Object_t *obj, cairo_t *cr);
+ MoveSashFunc_t (*near_sash)(Object_t *obj, gint x, gint y);
+ gboolean (*point_is_on)(Object_t *obj, gint x, gint y);
+ void (*get_dimensions)(Object_t *obj, gint *x, gint *y, gint *width,
+ gint *height);
+ void (*resize)(Object_t *obj, gint percentage_x, gint percentage_y);
+ void (*move)(Object_t *obj, gint dx, gint dy);
+ gpointer (*create_info_widget)(GtkWidget *frame);
+ void (*update_info_widget)(Object_t *obj, gpointer data);
+ void (*fill_info_tab)(Object_t *obj, gpointer data);
+ void (*set_initial_focus)(Object_t *obj, gpointer data);
+ void (*update)(Object_t *obj, gpointer data);
+ void (*write_csim)(Object_t *obj, gpointer param, OutputFunc_t output);
+ void (*write_cern)(Object_t *obj, gpointer param, OutputFunc_t output);
+ void (*write_ncsa)(Object_t *obj, gpointer param, OutputFunc_t output);
+ void (*do_popup)(Object_t *obj, GdkEventButton *event);
+
+ const gchar* (*get_stock_icon_name)(void);
+};
+
+Object_t *object_ref(Object_t *obj);
+void object_unref(Object_t *obj);
+Object_t* object_init(Object_t *obj, ObjectClass_t *class);
+Object_t* object_clone(Object_t *obj);
+Object_t* object_assign(Object_t *src, Object_t *des);
+void object_draw(Object_t *obj, cairo_t *cr);
+void object_edit(Object_t *obj, gboolean add);
+void object_select(Object_t *obj);
+void object_unselect(Object_t *obj);
+void object_move(Object_t *obj, gint dx, gint dy);
+void object_move_sash(Object_t *obj, gint dx, gint dy);
+void object_remove(Object_t *obj);
+void object_lock(Object_t *obj);
+void object_unlock(Object_t *obj);
+void object_set_url(Object_t *obj, const gchar *url);
+void object_set_target(Object_t *obj, const gchar *target);
+void object_set_comment(Object_t *obj, const gchar *comment);
+void object_set_mouse_over(Object_t *obj, const gchar *mouse_over);
+void object_set_mouse_out(Object_t *obj, const gchar *mouse_out);
+void object_set_focus(Object_t *obj, const gchar *focus);
+void object_set_blur(Object_t *obj, const gchar *blur);
+gint object_get_position_in_list(Object_t *obj);
+
+void object_emit_changed_signal(Object_t *obj);
+void object_emit_geometry_signal(Object_t *obj);
+void object_emit_update_signal(Object_t *obj);
+
+#define object_is_valid(obj) \
+ ((obj)->class->is_valid(obj))
+
+#define object_get_dimensions(obj, x, y, width, height) \
+ ((obj)->class->get_dimensions((obj), (x), (y), (width), (height)))
+
+#define object_normalize(obj) \
+ ((obj)->class->normalize(obj))
+
+#define object_resize(obj, per_x, per_y) \
+ ((obj)->class->resize((obj), (per_x), (per_y)))
+
+#define object_update(obj, data) \
+ ((obj)->class->update((obj), (data)))
+
+#define object_update_info_widget(obj, data) \
+ ((obj)->class->update_info_widget((obj), (data)))
+
+#define object_fill_info_tab(obj, data) \
+ ((obj)->class->fill_info_tab((obj), (data)))
+
+#define object_get_stock_icon_name(obj) \
+ ((obj)->class->get_stock_icon_name())
+
+typedef struct {
+ Object_t *obj;
+ gboolean (*finish)(Object_t *obj, gint x, gint y);
+ gboolean (*cancel)(GdkEventButton *event, Object_t *obj);
+ Object_t* (*create_object)(gint x, gint y);
+ void (*set_xy)(Object_t *obj, guint state, gint x, gint y);
+} ObjectFactory_t;
+
+gboolean object_on_button_press(GtkWidget *widget, GdkEventButton *event,
+ gpointer data);
+
+typedef struct {
+ GList *list;
+} ObjectListCallback_t;
+
+struct ObjectList_t {
+ GList *list;
+ gboolean changed;
+ ObjectListCallback_t changed_cb;
+ ObjectListCallback_t update_cb;
+ ObjectListCallback_t add_cb;
+ ObjectListCallback_t remove_cb;
+ ObjectListCallback_t select_cb;
+ ObjectListCallback_t move_cb;
+ ObjectListCallback_t geometry_cb;
+};
+
+ObjectList_t *make_object_list (void);
+void object_list_destruct(ObjectList_t *list);
+ObjectList_t *object_list_copy(ObjectList_t *des, ObjectList_t *src);
+ObjectList_t *object_list_append_list(ObjectList_t *des, ObjectList_t *src);
+
+void object_list_append(ObjectList_t *list, Object_t *object);
+void object_list_prepend(ObjectList_t *list, Object_t *object);
+void object_list_insert(ObjectList_t *list, gint position, Object_t *object);
+void object_list_remove(ObjectList_t *list, Object_t *object);
+void object_list_remove_link(ObjectList_t *list, GList *link);
+void object_list_update(ObjectList_t *list, Object_t *object);
+void object_list_draw(ObjectList_t *list, cairo_t *cr);
+void object_list_draw_selected(ObjectList_t *list, cairo_t *cr);
+Object_t *object_list_find(ObjectList_t *list, gint x, gint y);
+Object_t *object_list_near_sash(ObjectList_t *list, gint x, gint y,
+ MoveSashFunc_t *sash_func);
+
+gint object_list_cut(ObjectList_t *list);
+void object_list_copy_to_paste_buffer(ObjectList_t *list);
+void object_list_paste(ObjectList_t *list);
+
+void object_list_remove_all(ObjectList_t *list);
+void object_list_delete_selected(ObjectList_t *list);
+void object_list_edit_selected(ObjectList_t *list);
+gint object_list_select_all(ObjectList_t *list);
+void object_list_select_next(ObjectList_t *list);
+void object_list_select_prev(ObjectList_t *list);
+gint object_list_select_region(ObjectList_t *list, gint x, gint y, gint width,
+ gint height);
+gint object_list_deselect_all(ObjectList_t *list, Object_t *exception);
+gint object_list_nr_selected(ObjectList_t *list);
+void object_list_resize(ObjectList_t *list, gint percentage_x,
+ gint percentage_y);
+void object_list_move_selected(ObjectList_t *list, gint dx, gint dy);
+void object_list_move_up(ObjectList_t *list, Object_t *obj);
+void object_list_move_down(ObjectList_t *list, Object_t *obj);
+void object_list_move_selected_up(ObjectList_t *list);
+void object_list_move_selected_down(ObjectList_t *list);
+void object_list_move_to_front(ObjectList_t *list);
+void object_list_send_to_back(ObjectList_t *list);
+void object_list_move_sash_selected(ObjectList_t *list, gint dx, gint dy);
+
+void object_list_write_csim(ObjectList_t *list, gpointer param,
+ OutputFunc_t output);
+void object_list_write_cern(ObjectList_t *list, gpointer param,
+ OutputFunc_t output);
+void object_list_write_ncsa(ObjectList_t *list, gpointer param,
+ OutputFunc_t output);
+
+typedef void (*ObjectListCallbackFunc_t)(Object_t*, gpointer);
+
+gpointer object_list_add_changed_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func,
+ gpointer data);
+gpointer object_list_add_update_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func,
+ gpointer data);
+gpointer object_list_add_add_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func, gpointer data);
+gpointer object_list_add_remove_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func,
+ gpointer data);
+gpointer object_list_add_select_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func,
+ gpointer data);
+gpointer object_list_add_move_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func, gpointer data);
+gpointer object_list_add_geometry_cb(ObjectList_t *list,
+ ObjectListCallbackFunc_t func,
+ gpointer data);
+
+void object_list_remove_add_cb(ObjectList_t *list, gpointer id);
+void object_list_remove_select_cb(ObjectList_t *list, gpointer id);
+void object_list_remove_remove_cb(ObjectList_t *list, gpointer id);
+void object_list_remove_move_cb(ObjectList_t *list, gpointer id);
+void object_list_remove_geometry_cb(ObjectList_t *list, gpointer id);
+
+#define object_list_clear_changed(list) ((list)->changed = FALSE)
+#define object_list_set_changed(list, ischanged) \
+ ((list)->changed = (ischanged))
+#define object_list_get_changed(list) ((list)->changed)
+
+void clear_paste_buffer(void);
+gpointer paste_buffer_add_add_cb(ObjectListCallbackFunc_t func, gpointer data);
+gpointer paste_buffer_add_remove_cb(ObjectListCallbackFunc_t func,
+ gpointer data);
+ObjectList_t *get_paste_buffer(void);
+
+void do_object_locked_dialog(void);
+
+#endif /* _IMAP_OBJECT_H */
+
+
diff --git a/plug-ins/imagemap/imap_object_popup.c b/plug-ins/imagemap/imap_object_popup.c
new file mode 100644
index 0000000..63b195f
--- /dev/null
+++ b/plug-ins/imagemap/imap_object_popup.c
@@ -0,0 +1,60 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_commands.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_object_popup.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+void
+object_handle_popup(ObjectPopup_t *popup, Object_t *obj, GdkEventButton *event)
+{
+ /* int position = object_get_position_in_list(obj) + 1; */
+
+#ifdef _TEMP_
+ gtk_widget_set_sensitive(popup->up, (position > 1) ? TRUE : FALSE);
+ gtk_widget_set_sensitive(popup->down,
+ (position < g_list_length(obj->list->list))
+ ? TRUE : FALSE);
+#endif
+ gtk_menu_popup(GTK_MENU(popup->menu), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+}
+
+void
+object_do_popup(Object_t *obj, GdkEventButton *event)
+{
+ static ObjectPopup_t *popup;
+
+ if (!popup)
+ {
+ popup = g_new (ObjectPopup_t, 1);
+ popup->menu = menu_get_widget ("/ObjectPopupMenu");
+ }
+ object_handle_popup (popup, obj, event);
+}
diff --git a/plug-ins/imagemap/imap_object_popup.h b/plug-ins/imagemap/imap_object_popup.h
new file mode 100644
index 0000000..bc7afee
--- /dev/null
+++ b/plug-ins/imagemap/imap_object_popup.h
@@ -0,0 +1,39 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_OBJECT_POPUP_H
+#define _IMAP_OBJECT_POPUP_H
+
+#include "imap_object.h"
+
+typedef struct {
+ GtkWidget *menu;
+ GtkWidget *up;
+ GtkWidget *down;
+ Object_t *obj;
+} ObjectPopup_t;
+
+void object_handle_popup(ObjectPopup_t *popup, Object_t *obj,
+ GdkEventButton *event);
+void object_do_popup(Object_t *obj, GdkEventButton *event);
+
+#endif /* _IMAP_OBJECT_POPUP_H */
diff --git a/plug-ins/imagemap/imap_polygon.c b/plug-ins/imagemap/imap_polygon.c
new file mode 100644
index 0000000..6428cd3
--- /dev/null
+++ b/plug-ins/imagemap/imap_polygon.c
@@ -0,0 +1,854 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_commands.h"
+#include "imap_main.h"
+#include "imap_misc.h"
+#include "imap_menu.h"
+#include "imap_object_popup.h"
+#include "imap_polygon.h"
+#include "imap_stock.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define MAX_POLYGON_POINTS 99
+
+static gboolean polygon_is_valid(Object_t *obj);
+static void polygon_destruct(Object_t *obj);
+static Object_t *polygon_clone(Object_t *obj);
+static void polygon_assign(Object_t *obj, Object_t *des);
+static void polygon_draw(Object_t* obj, cairo_t *cr);
+static void polygon_draw_sashes(Object_t* obj, cairo_t *cr);
+static MoveSashFunc_t polygon_near_sash(Object_t *obj, gint x, gint y);
+static gboolean polygon_point_is_on(Object_t *obj, gint x, gint y);
+static void polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height);
+static void polygon_resize(Object_t *obj, gint percentage_x,
+ gint percentage_y);
+static void polygon_move(Object_t *obj, gint dx, gint dy);
+static gpointer polygon_create_info_widget(GtkWidget *frame);
+static void polygon_update_info_widget(Object_t *obj, gpointer data);
+static void polygon_fill_info_tab(Object_t *obj, gpointer data);
+static void polygon_set_initial_focus(Object_t *obj, gpointer data);
+static void polygon_update(Object_t* obj, gpointer data);
+static void polygon_write_csim(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static void polygon_write_cern(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static void polygon_write_ncsa(Object_t* obj, gpointer param,
+ OutputFunc_t output);
+static void polygon_do_popup(Object_t *obj, GdkEventButton *event);
+static const gchar* polygon_get_stock_icon_name(void);
+
+static ObjectClass_t polygon_class = {
+ N_("_Polygon"),
+ NULL, /* info_dialog */
+
+ polygon_is_valid,
+ polygon_destruct,
+ polygon_clone,
+ polygon_assign,
+ NULL, /* polygon_normalize */
+ polygon_draw,
+ polygon_draw_sashes,
+ polygon_near_sash,
+ polygon_point_is_on,
+ polygon_get_dimensions,
+ polygon_resize,
+ polygon_move,
+ polygon_create_info_widget,
+ polygon_update_info_widget,
+ polygon_fill_info_tab,
+ polygon_set_initial_focus,
+ polygon_update,
+ polygon_write_csim,
+ polygon_write_cern,
+ polygon_write_ncsa,
+ polygon_do_popup,
+ polygon_get_stock_icon_name
+};
+
+Object_t*
+create_polygon(GList *points)
+{
+ Polygon_t *polygon = g_new(Polygon_t, 1);
+ polygon->points = points;
+ return object_init(&polygon->obj, &polygon_class);
+}
+
+static void
+polygon_free_list (Polygon_t *polygon)
+{
+ g_list_free_full (polygon->points, (GDestroyNotify) g_free);
+ polygon->points = NULL;
+}
+
+static void
+polygon_destruct(Object_t *obj)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ polygon_free_list(polygon);
+}
+
+static gboolean
+polygon_is_valid(Object_t *obj)
+{
+ return g_list_length(ObjectToPolygon(obj)->points) > 2;
+}
+
+static Object_t*
+polygon_clone(Object_t *obj)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ Polygon_t *clone = g_new(Polygon_t, 1);
+ GList *p;
+
+ clone->points = NULL;
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ polygon_append_point(clone, point->x, point->y);
+ }
+ return &clone->obj;
+}
+
+static void
+polygon_assign(Object_t *obj, Object_t *des)
+{
+ Polygon_t *src_polygon = ObjectToPolygon(obj);
+ Polygon_t *des_polygon = ObjectToPolygon(des);
+ GList *p;
+
+ polygon_free_list(des_polygon);
+ for (p = src_polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ polygon_append_point(des_polygon, point->x, point->y);
+ }
+}
+
+static void
+polygon_draw(Object_t *obj, cairo_t *cr)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ draw_polygon(cr, polygon->points);
+}
+
+static void
+polygon_draw_sashes(Object_t *obj, cairo_t *cr)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ draw_sash(cr, point->x, point->y);
+ }
+}
+
+static GdkPoint *_sash_point;
+static gint _sash_index;
+
+static void
+move_sash(Object_t *obj, gint dx, gint dy)
+{
+ _sash_point->x += dx;
+ _sash_point->y += dy;
+}
+
+static MoveSashFunc_t
+polygon_near_sash(Object_t *obj, gint x, gint y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+
+ _sash_index = 0;
+ for (p = polygon->points; p; p = p->next, _sash_index++) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ if (near_sash(point->x, point->y, x, y)) {
+ _sash_point = point;
+ return move_sash;
+ }
+ }
+ return NULL;
+}
+
+static gboolean
+right_intersect(GdkPoint *p1, GdkPoint *p2, gint x, gint y)
+{
+ gint dx = p2->x - p1->x;
+ gint dy = p2->y - p1->y;
+
+ if ((dy > 0 && y > p1->y && y < p2->y) ||
+ (dy < 0 && y > p2->y && y < p1->y)) {
+ gint sx = p1->x + (y - p1->y) * dx / dy;
+ return sx > x;
+ }
+ return FALSE;
+}
+
+static gboolean
+polygon_point_is_on(Object_t *obj, gint x, gint y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ int count = 0;
+ GdkPoint *first, *prev;
+
+ p = polygon->points;
+ first = prev = (GdkPoint*) p->data;
+ p = p->next;
+
+ for (; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ if (right_intersect(prev, point, x, y))
+ count++;
+ prev = point;
+ }
+ if (right_intersect(prev, first, x, y))
+ count++;
+
+ return count % 2;
+}
+
+static void
+polygon_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ gint min_x = G_MAXINT, min_y = G_MAXINT;
+ gint max_x = G_MININT, max_y = G_MININT;
+ GList *p;
+
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ if (point->x < min_x)
+ min_x = point->x;
+ if (point->x > max_x)
+ max_x = point->x;
+ if (point->y < min_y)
+ min_y = point->y;
+ if (point->y > max_y)
+ max_y = point->y;
+ }
+ *x = min_x;
+ *y = min_y;
+ *width = max_x - min_x;
+ *height = max_y - min_y;
+}
+
+static void
+polygon_resize(Object_t *obj, gint percentage_x, gint percentage_y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ point->x = point->x * percentage_x / 100;
+ point->y = point->y * percentage_y / 100;
+ }
+}
+
+static void
+polygon_move(Object_t *obj, gint dx, gint dy)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ point->x += dx;
+ point->y += dy;
+ }
+}
+
+typedef struct {
+ Object_t *obj;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkWidget *x;
+ GtkWidget *y;
+ GtkWidget *update;
+ GtkWidget *insert;
+ GtkWidget *append;
+ GtkWidget *remove;
+ gint selected_row;
+ guint timeout;
+} PolygonProperties_t;
+
+static void
+select_row_cb(GtkTreeSelection *selection, PolygonProperties_t *data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ GdkPoint *point;
+
+ gtk_tree_model_get (model, &iter, 0, &point, -1);
+
+ _sash_point = point;
+
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->x), point->x);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->y), point->y);
+ }
+}
+
+static void
+update_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL(data->store);
+
+ if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
+ GdkPoint *point;
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
+
+ gtk_tree_model_get (model, &iter, 0, &point, -1);
+ point->x = x;
+ point->y = y;
+ gtk_list_store_set (data->store, &iter, 0, point, -1);
+ }
+}
+
+static void
+set_buttons_sensitivity(PolygonProperties_t *data)
+{
+ gint rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(data->store),
+ NULL);
+ gtk_widget_set_sensitive(data->insert, rows != MAX_POLYGON_POINTS);
+ gtk_widget_set_sensitive(data->append, rows != MAX_POLYGON_POINTS);
+ gtk_widget_set_sensitive(data->remove, rows > 2);
+}
+
+static void
+insert_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL(data->store);
+
+ if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
+ Polygon_t *polygon = ObjectToPolygon(data->obj);
+ GdkPoint *point;
+ GList *here;
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
+
+ gtk_tree_model_get (model, &iter, 0, &point, -1);
+ here = g_list_find(polygon->points, point);
+ polygon->points = g_list_insert_before(polygon->points, here,
+ new_point(x, y));
+ polygon_fill_info_tab(data->obj, data);
+ }
+}
+
+static void
+append_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
+{
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->x));
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(data->y));
+
+ polygon_append_point(ObjectToPolygon(data->obj), x, y);
+ polygon_fill_info_tab(data->obj, data);
+}
+
+static void
+remove_button_clicked(GtkWidget *widget, PolygonProperties_t *data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL(data->store);
+
+ if (gtk_tree_selection_get_selected (data->selection, &model, &iter)) {
+ Polygon_t *polygon = ObjectToPolygon(data->obj);
+ GdkPoint *point;
+
+ gtk_tree_model_get (model, &iter, 0, &point, -1);
+ polygon->points = g_list_remove(polygon->points, point);
+ g_free(point);
+
+ polygon_fill_info_tab(data->obj, data);
+ }
+}
+
+static void
+x_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((PolygonProperties_t*) data)->obj;
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ _sash_point->x = x;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+y_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((PolygonProperties_t*) data)->obj;
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ _sash_point->y = y;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+render_x(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ GdkPoint *point;
+ gchar scratch[16];
+
+ gtk_tree_model_get(tree_model, iter, 0, &point, -1);
+ sprintf(scratch, "%d", point->x);
+ g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
+}
+
+static void
+render_y(GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ GdkPoint *point;
+ gchar scratch[16];
+
+ gtk_tree_model_get(tree_model, iter, 0, &point, -1);
+ sprintf(scratch, "%d", point->y);
+ g_object_set(cell, "text", scratch, "xalign", 1.0, NULL);
+}
+
+static gpointer
+polygon_create_info_widget(GtkWidget *frame)
+{
+ PolygonProperties_t *props = g_new(PolygonProperties_t, 1);
+ GtkWidget *hbox, *swin, *table, *label;
+ GtkWidget *view;
+ gint max_width = get_image_width();
+ gint max_height = get_image_height();
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+
+ swin = gtk_scrolled_window_new(NULL, NULL);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(swin),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(hbox), swin, FALSE, FALSE, FALSE);
+ gtk_widget_show(swin);
+
+ props->store = gtk_list_store_new (1, G_TYPE_POINTER);
+ view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (props->store));
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
+ g_object_unref (props->store);
+ gtk_widget_show (view);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("x (pixels)"),
+ renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer,
+ render_x, props, NULL);
+ gtk_tree_view_column_set_alignment(column, 0.5);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("y (pixels)"),
+ renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer,
+ render_y, props, NULL);
+ gtk_tree_view_column_set_alignment(column, 0.5);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+ gtk_container_add (GTK_CONTAINER (swin), view);
+
+ table = gtk_table_new(6, 3, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, FALSE);
+ gtk_widget_show(table);
+
+ label = create_label_in_table(table, 0, 0, "_x:");
+ props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
+ max_width - 1);
+ g_signal_connect(props->x, "changed",
+ G_CALLBACK(x_changed_cb), (gpointer) props);
+ gtk_widget_set_size_request(props->x, 64, -1);
+ create_label_in_table(table, 0, 2, _("pixels"));
+
+ label = create_label_in_table(table, 1, 0, "_y:");
+ props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
+ max_height - 1);
+ g_signal_connect(props->y, "changed",
+ G_CALLBACK(y_changed_cb), (gpointer) props);
+ gtk_widget_set_size_request(props->y, 64, -1);
+ create_label_in_table(table, 1, 2, _("pixels"));
+
+ props->update = gtk_button_new_with_mnemonic(_("_Update"));
+ g_signal_connect(props->update, "clicked",
+ G_CALLBACK(update_button_clicked), props);
+ gtk_table_attach_defaults(GTK_TABLE(table), props->update, 1, 2, 2, 3);
+ gtk_widget_show(props->update);
+
+ props->insert = gtk_button_new_with_mnemonic(_("_Insert"));
+ g_signal_connect(props->insert, "clicked",
+ G_CALLBACK(insert_button_clicked), props);
+ gtk_table_attach_defaults(GTK_TABLE(table), props->insert, 1, 2, 3, 4);
+ gtk_widget_show(props->insert);
+
+ props->append = gtk_button_new_with_mnemonic(_("A_ppend"));
+ g_signal_connect(props->append, "clicked",
+ G_CALLBACK(append_button_clicked), props);
+ gtk_table_attach_defaults(GTK_TABLE(table), props->append, 1, 2, 4, 5);
+ gtk_widget_show(props->append);
+
+ props->remove = gtk_button_new_with_mnemonic(_("_Remove"));
+ g_signal_connect(props->remove, "clicked",
+ G_CALLBACK(remove_button_clicked), props);
+ gtk_table_attach_defaults(GTK_TABLE(table), props->remove, 1, 2, 5, 6);
+ gtk_widget_show(props->remove);
+
+ props->timeout = 0;
+
+ props->selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (view));
+ gtk_tree_selection_set_mode(props->selection, GTK_SELECTION_SINGLE);
+ g_signal_connect (props->selection, "changed",
+ G_CALLBACK (select_row_cb), props);
+
+ return props;
+}
+
+static gboolean
+update_timeout(gpointer data)
+{
+ PolygonProperties_t *props = (PolygonProperties_t*) data;
+ polygon_fill_info_tab(props->obj, data);
+ return FALSE;
+}
+
+static void
+polygon_update_info_widget(Object_t *obj, gpointer data)
+{
+ PolygonProperties_t *props = (PolygonProperties_t*) data;
+ GtkTreeIter iter;
+
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), _sash_point->x);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), _sash_point->y);
+
+ if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
+ NULL, _sash_index)) {
+ gtk_tree_selection_select_iter(props->selection, &iter);
+ }
+
+ if (props->timeout)
+ g_source_remove(props->timeout);
+ props->timeout = g_timeout_add(1000, update_timeout, data);
+}
+
+static void
+polygon_fill_info_tab(Object_t *obj, gpointer data)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ PolygonProperties_t *props = (PolygonProperties_t*) data;
+ GtkTreeIter iter;
+ GList *p;
+
+ props->obj = obj;
+
+ gtk_list_store_clear(props->store);
+
+ for (p = polygon->points; p; p = p->next) {
+ gtk_list_store_append(props->store, &iter);
+ gtk_list_store_set(props->store, &iter, 0, p->data, -1);
+ }
+
+ if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(props->store), &iter,
+ NULL, _sash_index)) {
+ gtk_tree_selection_select_iter(props->selection, &iter);
+ }
+ set_buttons_sensitivity(props);
+}
+
+static void
+polygon_set_initial_focus(Object_t *obj, gpointer data)
+{
+ PolygonProperties_t *props = (PolygonProperties_t*) data;
+ gtk_widget_grab_focus(props->x);
+}
+
+static void
+polygon_update(Object_t* obj, gpointer data)
+{
+ /* Nothing to be done! */
+}
+
+static void
+polygon_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+
+ output(param, "\"poly\" coords=\"");
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ output(param, "%d,%d", point->x, point->y);
+ output(param, "%c", (p->next) ? ',' : '"');
+ }
+}
+
+static void
+polygon_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ GdkPoint *first = (GdkPoint*) polygon->points->data;
+
+ output(param, "poly ");
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ output(param, "(%d,%d) ", point->x, point->y);
+ }
+ output(param, "(%d,%d)", first->x, first->y);
+}
+
+static void
+polygon_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p;
+ GdkPoint *first = (GdkPoint*) polygon->points->data;
+
+ output(param, "poly %s", obj->url);
+ for (p = polygon->points; p; p = p->next) {
+ GdkPoint *point = (GdkPoint*) p->data;
+ output(param, " %d,%d", point->x, point->y);
+ }
+ output(param, " %d,%d", first->x, first->y);
+}
+
+static Object_t *_current_obj;
+static gboolean _insert_edge;
+static gint _insert_x;
+static gint _insert_y;
+
+void
+polygon_insert_point(void)
+{
+ Command_t *command = insert_point_command_new (_current_obj, _insert_x,
+ _insert_y, _insert_edge);
+ command_execute (command);
+}
+
+void
+polygon_delete_point(void)
+{
+ Command_t *command = delete_point_command_new(_current_obj, _sash_point);
+ command_execute (command);
+}
+
+static gboolean
+point_near_edge(GdkPoint *first, GdkPoint *second, gint x, gint y)
+{
+ gint den, nom;
+ gdouble u;
+
+ den = (first->x - x) * (first->x - second->x) +
+ (first->y - y) * (first->y - second->y);
+ nom = (second->x - first->x) * (second->x - first->x) +
+ (second->y - first->y) * (second->y - first->y);
+ u = (gdouble) den / nom;
+ if (u >= 0.0 && u <= 1.0) {
+ gint sx = first->x + (gint) (u * (second->x - first->x)) - x;
+ gint sy = first->y + (gint) (u * (second->y - first->y)) - y;
+ return sx * sx + sy * sy <= 25; /* Fix me! */
+ }
+ return FALSE;
+}
+
+static gint
+polygon_near_edge(Object_t *obj, gint x, gint y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *p = polygon->points;
+ GdkPoint *first = (GdkPoint*) p->data;
+ GdkPoint *prev = first;
+ gint n = 1;
+
+ for (p = p->next; p; p = p->next, n++) {
+ GdkPoint *next = (GdkPoint*) p->data;
+ if (point_near_edge(prev, next, x, y))
+ return n;
+ prev = next;
+ }
+ return (point_near_edge(prev, first, x, y)) ? n + 1 : 0;
+}
+
+static void
+polygon_handle_popup (GdkEventButton *event, gboolean near_sash,
+ gboolean near_edge)
+{
+ GtkWidget *popup = menu_get_widget ("/PolygonPopupMenu");
+ GtkWidget *delete = menu_get_widget ("/PolygonPopupMenu/DeletePoint");
+ GtkWidget *insert = menu_get_widget ("/PolygonPopupMenu/InsertPoint");
+
+ gtk_widget_set_sensitive (delete, near_sash);
+ gtk_widget_set_sensitive (insert, near_edge);
+
+ gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
+ event->button, event->time);
+}
+
+static void
+polygon_do_popup(Object_t *obj, GdkEventButton *event)
+{
+ gint x = get_real_coord ((gint) event->x);
+ gint y = get_real_coord ((gint) event->y);
+
+ _current_obj = obj;
+
+ if (polygon_near_sash (obj, x, y))
+ {
+ polygon_handle_popup (event, TRUE, FALSE);
+ }
+ else
+ {
+ _insert_edge = polygon_near_edge (obj, x, y);
+ if (_insert_edge)
+ {
+ _insert_x = x;
+ _insert_y = y;
+
+ polygon_handle_popup (event, FALSE, TRUE);
+ }
+ else {
+ object_do_popup (obj, event);
+ }
+ }
+}
+
+static const gchar*
+polygon_get_stock_icon_name(void)
+{
+ return IMAP_STOCK_POLYGON;
+}
+
+static GList *_prev_link;
+
+static gboolean
+polygon_factory_finish(Object_t *obj, gint x, gint y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GdkPoint *prev_point = (GdkPoint*) _prev_link->data;
+
+ if (x == prev_point->x && y == prev_point->y) {
+ polygon_remove_last_point(polygon);
+ return TRUE;
+ } else {
+ polygon_append_point(polygon, x, y);
+ _prev_link = _prev_link->next;
+ }
+ return FALSE;
+}
+
+static gboolean
+polygon_factory_cancel(GdkEventButton *event, Object_t *obj)
+{
+ if (event->state & GDK_SHIFT_MASK) {
+ return TRUE;
+ } else {
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *link = _prev_link;
+
+ _prev_link = _prev_link->prev;
+ g_free((GdkPoint*) link->data);
+ polygon->points = g_list_remove_link(polygon->points, link);
+ }
+ return _prev_link == NULL;
+}
+
+static Object_t*
+polygon_factory_create_object(gint x, gint y)
+{
+ GList *points;
+
+ points = _prev_link = g_list_append(NULL, new_point(x, y));
+ points = g_list_append(points, new_point(x, y));
+
+ return create_polygon(points);
+}
+
+static void
+polygon_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
+{
+ Polygon_t *polygon = ObjectToPolygon(obj);
+ GList *last = g_list_last(polygon->points);
+ GdkPoint *point = (GdkPoint*) last->data;
+ GdkPoint *prev = (GdkPoint*) last->prev->data;
+
+ point->x = x;
+ point->y = y;
+
+ main_set_dimension(x - prev->x, y - prev->y);
+}
+
+static ObjectFactory_t polygon_factory = {
+ NULL, /* Object pointer */
+ polygon_factory_finish,
+ polygon_factory_cancel,
+ polygon_factory_create_object,
+ polygon_factory_set_xy
+};
+
+ObjectFactory_t*
+get_polygon_factory(guint state)
+{
+ return &polygon_factory;
+}
+
+void
+polygon_remove_last_point(Polygon_t *polygon)
+{
+ GList *last = g_list_last(polygon->points);
+ g_free((GdkPoint*) last->data);
+ polygon->points = g_list_remove_link(polygon->points, last);
+}
+
+GdkPoint*
+new_point(gint x, gint y)
+{
+ GdkPoint *point = g_new(GdkPoint, 1);
+ point->x = x;
+ point->y = y;
+ return point;
+}
+
+void
+polygon_append_point(Polygon_t *polygon, gint x, gint y)
+{
+ polygon->points = g_list_append(polygon->points, new_point(x, y));
+}
diff --git a/plug-ins/imagemap/imap_polygon.h b/plug-ins/imagemap/imap_polygon.h
new file mode 100644
index 0000000..13ce29e
--- /dev/null
+++ b/plug-ins/imagemap/imap_polygon.h
@@ -0,0 +1,45 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_POLYGON_H
+#define _IMAP_POLYGON_H
+
+#include "imap_object.h"
+
+typedef struct {
+ Object_t obj;
+ GList *points;
+} Polygon_t;
+
+#define ObjectToPolygon(obj) ((Polygon_t*) (obj))
+
+Object_t *create_polygon (GList *points);
+ObjectFactory_t *get_polygon_factory (guint state);
+
+void polygon_insert_point (void);
+void polygon_delete_point (void);
+
+void polygon_remove_last_point (Polygon_t *polygon);
+void polygon_append_point (Polygon_t *polygon, gint x, gint y);
+GdkPoint *new_point (gint x, gint y);
+
+#endif /* _IMAP_POLYGON_H */
diff --git a/plug-ins/imagemap/imap_preferences.c b/plug-ins/imagemap/imap_preferences.c
new file mode 100644
index 0000000..218aa46
--- /dev/null
+++ b/plug-ins/imagemap/imap_preferences.c
@@ -0,0 +1,527 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_command.h"
+#include "imap_file.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_misc.h"
+#include "imap_mru.h"
+#include "imap_preferences.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+typedef struct {
+ DefaultDialog_t *dialog;
+ GtkWidget *notebook;
+ GtkWidget *ncsa;
+ GtkWidget *cern;
+ GtkWidget *csim;
+ GtkWidget *prompt_for_area_info;
+ GtkWidget *require_default_url;
+ GtkWidget *show_area_handle;
+ GtkWidget *keep_circles_round;
+ GtkWidget *show_url_tip;
+ GtkWidget *use_doublesized;
+
+ GtkWidget *undo_levels;
+ GtkWidget *mru_size;
+
+ GtkWidget *normal_fg;
+ GtkWidget *normal_bg;
+ GtkWidget *selected_fg;
+ GtkWidget *selected_bg;
+ GtkWidget *interactive_fg;
+ GtkWidget *interactive_bg;
+
+ GtkWidget *threshold;
+ GtkWidget *auto_convert;
+
+ PreferencesData_t *old_data;
+} PreferencesDialog_t;
+
+static void get_button_colors (PreferencesDialog_t *dialog,
+ ColorSelData_t *colors);
+
+static gint
+parse_map_type(void)
+{
+ char *token = strtok(NULL, " )");
+ if (!strcmp(token, "ncsa"))
+ return NCSA;
+ else if (!strcmp(token, "cern"))
+ return CERN;
+ return CSIM;
+}
+
+static gint
+parse_yes_no(void)
+{
+ char *token = strtok(NULL, " )");
+ return (gint) strcmp(token, "no");
+}
+
+static gint
+parse_int(void)
+{
+ char *token = strtok(NULL, " )");
+ return (gint) atoi(token);
+}
+
+static void
+parse_color(GdkColor *gdk_color)
+{
+ gdk_color->red = (guint16) parse_int();
+ gdk_color->green = (guint16) parse_int();
+ gdk_color->blue = (guint16) parse_int();
+}
+
+static void
+parse_mru_entry(void)
+{
+ char *filename = strtok(NULL, " )");
+ mru_add(get_mru(), filename);
+}
+
+static void
+parse_line(PreferencesData_t *data, char *line)
+{
+ char *token;
+ ColorSelData_t *colors = &data->colors;
+
+ line++; /* Skip '(' */
+ token = strtok(line, " ");
+
+ if (!strcmp(token, "default-map-type")) {
+ data->default_map_type = parse_map_type();
+ }else if (!strcmp(token, "prompt-for-area-info")) {
+ data->prompt_for_area_info = parse_yes_no();
+ } else if (!strcmp(token, "require-default-url")) {
+ data->require_default_url = parse_yes_no();
+ } else if (!strcmp(token, "show-area-handle")) {
+ data->show_area_handle = parse_yes_no();
+ } else if (!strcmp(token, "keep-circles-round")) {
+ data->keep_circles_round = parse_yes_no();
+ } else if (!strcmp(token, "show-url-tip")) {
+ data->show_url_tip = parse_yes_no();
+ } else if (!strcmp(token, "use-doublesized")) {
+ data->use_doublesized = parse_yes_no();
+ } else if (!strcmp(token, "mru-size")) {
+ data->mru_size = parse_int();
+ if (data->mru_size < 1)
+ data->mru_size = 1;
+ } else if (!strcmp(token, "undo-levels")) {
+ data->undo_levels = parse_int();
+ if (data->undo_levels < 1)
+ data->undo_levels = 1;
+ } else if (!strcmp(token, "normal-fg-color")) {
+ parse_color(&colors->normal_fg);
+ } else if (!strcmp(token, "normal-bg-color")) {
+ parse_color(&colors->normal_bg);
+ } else if (!strcmp(token, "selected-fg-color")) {
+ parse_color(&colors->selected_fg);
+ } else if (!strcmp(token, "selected-bg-color")) {
+ parse_color(&colors->selected_bg);
+ } else if (!strcmp(token, "interactive-fg-color")) {
+ parse_color(&colors->interactive_fg);
+ } else if (!strcmp(token, "interactive-bg-color")) {
+ parse_color(&colors->interactive_bg);
+ } else if (!strcmp(token, "mru-entry")) {
+ parse_mru_entry();
+ } else {
+ /* Unrecognized, just ignore rest of line */
+ }
+}
+
+gboolean
+preferences_load(PreferencesData_t *data)
+{
+ FILE *in;
+ char buf[256];
+ gchar *filename;
+
+ filename = gimp_personal_rc_file ("imagemaprc");
+
+ in = g_fopen(filename, "rb");
+ g_free(filename);
+ if (in) {
+ while (fgets(buf, sizeof(buf), in)) {
+ if (*buf != '\n' && *buf != '#') {
+ parse_line(data, buf);
+ }
+ }
+ fclose(in);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+preferences_save(PreferencesData_t *data)
+{
+ FILE *out;
+ gchar *filename;
+ ColorSelData_t *colors = &data->colors;
+
+ filename = gimp_personal_rc_file ("imagemaprc");
+
+ out = g_fopen(filename, "wb");
+ if (out) {
+ fprintf(out, "# Image map plug-in resource file\n\n");
+ if (data->default_map_type == NCSA)
+ fprintf(out, "(default-map-type ncsa)\n");
+ else if (data->default_map_type == CERN)
+ fprintf(out, "(default-map-type cern)\n");
+ else
+ fprintf(out, "(default-map-type csim)\n");
+
+ fprintf(out, "(prompt-for-area-info %s)\n",
+ (data->prompt_for_area_info) ? "yes" : "no");
+ fprintf(out, "(require-default-url %s)\n",
+ (data->require_default_url) ? "yes" : "no");
+ fprintf(out, "(show-area-handle %s)\n",
+ (data->show_area_handle) ? "yes" : "no");
+ fprintf(out, "(keep-circles-round %s)\n",
+ (data->keep_circles_round) ? "yes" : "no");
+ fprintf(out, "(show-url-tip %s)\n",
+ (data->show_url_tip) ? "yes" : "no");
+ fprintf(out, "(use-doublesized %s)\n",
+ (data->use_doublesized) ? "yes" : "no");
+
+ fprintf(out, "(undo-levels %d)\n", data->undo_levels);
+ fprintf(out, "(mru-size %d)\n", data->mru_size);
+
+ fprintf(out, "(normal-fg-color %d %d %d)\n",
+ colors->normal_fg.red, colors->normal_fg.green,
+ colors->normal_fg.blue);
+ fprintf(out, "(normal-bg-color %d %d %d)\n",
+ colors->normal_bg.red, colors->normal_bg.green,
+ colors->normal_bg.blue);
+ fprintf(out, "(selected-fg-color %d %d %d)\n",
+ colors->selected_fg.red, colors->selected_fg.green,
+ colors->selected_fg.blue);
+ fprintf(out, "(selected-bg-color %d %d %d)\n",
+ colors->selected_bg.red, colors->selected_bg.green,
+ colors->selected_bg.blue);
+ fprintf(out, "(interactive-fg-color %d %d %d)\n",
+ colors->interactive_fg.red, colors->interactive_fg.green,
+ colors->interactive_fg.blue);
+ fprintf(out, "(interactive-bg-color %d %d %d)\n",
+ colors->interactive_bg.red, colors->interactive_bg.green,
+ colors->interactive_bg.blue);
+
+ mru_write(get_mru(), out);
+
+ fclose(out);
+ } else {
+ do_file_error_dialog( _("Couldn't save resource file:"), filename);
+ }
+ g_free(filename);
+}
+
+static void
+preferences_ok_cb(gpointer data)
+{
+ PreferencesDialog_t *param = (PreferencesDialog_t*) data;
+ PreferencesData_t *old_data = param->old_data;
+ ColorSelData_t *colors = &old_data->colors;
+ MRU_t *mru = get_mru();
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->cern)))
+ old_data->default_map_type = CERN;
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(param->ncsa)))
+ old_data->default_map_type = NCSA;
+ else
+ old_data->default_map_type = CSIM;
+
+ old_data->prompt_for_area_info = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->prompt_for_area_info));
+ old_data->require_default_url = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->require_default_url));
+ old_data->show_area_handle = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->show_area_handle));
+ old_data->keep_circles_round = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->keep_circles_round));
+ old_data->show_url_tip = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->show_url_tip));
+ old_data->use_doublesized = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(param->use_doublesized));
+
+ old_data->mru_size =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->mru_size));
+ old_data->undo_levels =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(param->undo_levels));
+ mru_set_size(mru, old_data->mru_size);
+ menu_build_mru_items(mru);
+ command_list_set_undo_level(old_data->undo_levels);
+
+ get_button_colors (param, colors);
+
+ set_sash_size(old_data->use_doublesized);
+ preview_redraw();
+}
+
+static void
+get_button_color (GtkWidget *button, GdkColor *color)
+{
+ GimpRGB rgb;
+ gimp_color_button_get_color (GIMP_COLOR_BUTTON (button), &rgb);
+ color->red = rgb.r * 0xffff;
+ color->green = rgb.g * 0xffff;
+ color->blue = rgb.b * 0xffff;
+}
+
+static void
+get_button_colors(PreferencesDialog_t *dialog, ColorSelData_t *colors)
+{
+ get_button_color (dialog->normal_fg, &colors->normal_fg);
+ get_button_color (dialog->normal_bg, &colors->normal_bg);
+ get_button_color (dialog->selected_fg, &colors->selected_fg);
+ get_button_color (dialog->selected_bg, &colors->selected_bg);
+ get_button_color (dialog->interactive_fg, &colors->interactive_fg);
+ get_button_color (dialog->interactive_bg, &colors->interactive_bg);
+}
+
+static void
+set_button_color (GtkWidget *button, GdkColor *color)
+{
+ GimpRGB rgb;
+ gimp_rgb_set (&rgb, color->red, color->green, color->blue);
+ gimp_rgb_multiply (&rgb, 1.0 / 0xffff);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (button), &rgb);
+}
+
+static void
+set_button_colors(PreferencesDialog_t *dialog, ColorSelData_t *colors)
+{
+ set_button_color (dialog->normal_fg, &colors->normal_fg);
+ set_button_color (dialog->normal_bg, &colors->normal_bg);
+ set_button_color (dialog->selected_fg, &colors->selected_fg);
+ set_button_color (dialog->selected_bg, &colors->selected_bg);
+ set_button_color (dialog->interactive_fg, &colors->interactive_fg);
+ set_button_color (dialog->interactive_bg, &colors->interactive_bg);
+}
+
+static GtkWidget*
+create_tab(GtkWidget *notebook, const gchar *label, gint rows, gint cols)
+{
+ GtkWidget *table;
+ GtkWidget *vbox;
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
+ gtk_widget_show(vbox);
+
+ table = gtk_table_new(rows, cols, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 12);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_widget_show(table);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox,
+ gtk_label_new_with_mnemonic(label));
+
+ return table;
+}
+
+static void
+create_general_tab(PreferencesDialog_t *data, GtkWidget *notebook)
+{
+ GtkWidget *table = create_tab(notebook, _("General"), 7, 2);
+ GtkWidget *frame;
+ GtkWidget *hbox;
+
+ frame = gimp_frame_new( _("Default Map Type"));
+ gtk_widget_show(frame);
+ gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 0, 1);
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+ data->ncsa = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_NCSA");
+ gtk_box_pack_start(GTK_BOX(hbox), data->ncsa, TRUE, TRUE, 10);
+ gtk_widget_show(data->ncsa);
+ data->cern = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(data->ncsa), "C_ERN");
+ gtk_box_pack_start(GTK_BOX(hbox), data->cern, TRUE, TRUE, 10);
+ gtk_widget_show(data->cern);
+ data->csim = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(data->cern), "C_SIM");
+ gtk_box_pack_start(GTK_BOX(hbox), data->csim, TRUE, TRUE, 10);
+ gtk_widget_show(data->csim);
+
+ data->prompt_for_area_info =
+ create_check_button_in_table(table, 1, 0, _("_Prompt for area info"));
+ data->require_default_url =
+ create_check_button_in_table(table, 2, 0, _("_Require default URL"));
+ data->show_area_handle =
+ create_check_button_in_table(table, 3, 0, _("Show area _handles"));
+ data->keep_circles_round =
+ create_check_button_in_table(table, 4, 0, _("_Keep NCSA circles true"));
+ data->show_url_tip =
+ create_check_button_in_table(table, 5, 0, _("Show area URL _tip"));
+ data->use_doublesized =
+ create_check_button_in_table(table, 6, 0,
+ _("_Use double-sized grab handles"));
+ gtk_widget_show(frame);
+}
+
+static void
+create_menu_tab(PreferencesDialog_t *data, GtkWidget *notebook)
+{
+ GtkWidget *table = create_tab(notebook, _("Menu"), 2, 2);
+ GtkWidget *label;
+
+ label = create_label_in_table(table, 0, 0,
+ _("Number of _undo levels (1 - 99):"));
+ data->undo_levels = create_spin_button_in_table(table, label, 0, 1, 1, 1,
+ 99);
+
+ label = create_label_in_table(table, 1, 0,
+ _("Number of M_RU entries (1 - 16):"));
+ data->mru_size = create_spin_button_in_table(table, label, 1, 1, 1, 1, 16);
+}
+
+static GtkWidget*
+create_color_field(PreferencesDialog_t *data, GtkWidget *table, gint row,
+ gint col)
+{
+ GimpRGB color = {0.0, 0.0, 0.0, 1.0};
+ GtkWidget *area = gimp_color_button_new (_("Select Color"), 16, 8, &color,
+ GIMP_COLOR_AREA_FLAT);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (area), TRUE);
+ gtk_table_attach_defaults (GTK_TABLE (table), area, col, col + 1, row,
+ row + 1);
+ gtk_widget_show (area);
+
+ return area;
+}
+
+static void
+create_colors_tab(PreferencesDialog_t *data, GtkWidget *notebook)
+{
+ GtkWidget *table = create_tab(notebook, _("Colors"), 3, 3);
+
+ create_label_in_table(table, 0, 0, _("Normal:"));
+ data->normal_fg = create_color_field(data, table, 0, 1);
+ data->normal_bg = create_color_field(data, table, 0, 2);
+
+ create_label_in_table(table, 1, 0, _("Selected:"));
+ data->selected_fg = create_color_field(data, table, 1, 1);
+ data->selected_bg = create_color_field(data, table, 1, 2);
+
+ create_label_in_table(table, 2, 0, _("Interaction:"));
+ data->interactive_fg = create_color_field(data, table, 2, 1);
+ data->interactive_bg = create_color_field(data, table, 2, 2);
+}
+
+#ifdef _NOT_READY_YET_
+static void
+create_contiguous_regions_tab(PreferencesDialog_t *data, GtkWidget *notebook)
+{
+ GtkWidget *table = create_tab(notebook, _("Co_ntiguous Region"), 2, 2);
+ GtkWidget *label;
+
+ label = create_label_in_table(table, 0, 0,
+ _("_Threshold:"));
+ data->auto_convert =
+ create_check_button_in_table(table, 1, 0, _("_Automatically convert"));
+}
+#endif
+
+static PreferencesDialog_t*
+create_preferences_dialog(void)
+{
+ PreferencesDialog_t *data = g_new(PreferencesDialog_t, 1);
+ DefaultDialog_t *dialog;
+ GtkWidget *notebook;
+
+ data->dialog = dialog = make_default_dialog( _("General Preferences"));
+ default_dialog_set_ok_cb(dialog, preferences_ok_cb, (gpointer) data);
+
+ data->notebook = notebook = gtk_notebook_new();
+ gtk_box_pack_start (GTK_BOX (data->dialog->vbox), notebook, TRUE, TRUE, 0);
+ create_general_tab(data, notebook);
+ create_menu_tab(data, notebook);
+ create_colors_tab(data, notebook);
+#ifdef _NOT_READY_YET_
+ create_contiguous_regions_tab(data, notebook);
+#endif
+ gtk_widget_show(notebook);
+
+ return data;
+}
+
+void
+do_preferences_dialog(void)
+{
+ static PreferencesDialog_t *dialog;
+ PreferencesData_t *old_data;
+ GtkWidget *map_type;
+
+ if (!dialog) {
+ dialog = create_preferences_dialog();
+ }
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(dialog->notebook), 0);
+ dialog->old_data = old_data = get_preferences();
+
+ if (old_data->default_map_type == CERN)
+ map_type = dialog->cern;
+ else if (old_data->default_map_type == NCSA)
+ map_type = dialog->ncsa;
+ else
+ map_type = dialog->csim;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(map_type), TRUE);
+
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(dialog->prompt_for_area_info),
+ old_data->prompt_for_area_info);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->require_default_url),
+ old_data->require_default_url);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->show_area_handle),
+ old_data->show_area_handle);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->keep_circles_round),
+ old_data->keep_circles_round);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->show_url_tip),
+ old_data->show_url_tip);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->use_doublesized),
+ old_data->use_doublesized);
+
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->undo_levels),
+ old_data->undo_levels);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(dialog->mru_size),
+ old_data->mru_size);
+
+ set_button_colors(dialog, &old_data->colors);
+
+ default_dialog_show(dialog->dialog);
+}
diff --git a/plug-ins/imagemap/imap_preferences.h b/plug-ins/imagemap/imap_preferences.h
new file mode 100644
index 0000000..7376026
--- /dev/null
+++ b/plug-ins/imagemap/imap_preferences.h
@@ -0,0 +1,56 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_PREFERENCES_H
+#define _IMAP_PREFERENCES_H
+
+#include "imap_default_dialog.h"
+
+typedef struct {
+ GdkColor normal_fg;
+ GdkColor normal_bg;
+ GdkColor selected_fg;
+ GdkColor selected_bg;
+ GdkColor interactive_bg;
+ GdkColor interactive_fg;
+} ColorSelData_t;
+
+typedef struct {
+ gint default_map_type;
+ gboolean prompt_for_area_info;
+ gboolean require_default_url;
+ gboolean show_area_handle;
+ gboolean keep_circles_round;
+ gboolean show_url_tip;
+ gboolean use_doublesized;
+ gboolean auto_convert;
+ gdouble threshold;
+ gint undo_levels;
+ gint mru_size;
+ ColorSelData_t colors;
+} PreferencesData_t;
+
+void do_preferences_dialog(void);
+gboolean preferences_load(PreferencesData_t *data);
+void preferences_save(PreferencesData_t *data);
+
+#endif /* _IMAP_PREFERENCES_H */
diff --git a/plug-ins/imagemap/imap_preview.c b/plug-ins/imagemap/imap_preview.c
new file mode 100644
index 0000000..160a88d
--- /dev/null
+++ b/plug-ins/imagemap/imap_preview.c
@@ -0,0 +1,402 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_commands.h"
+#include "imap_grid.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_preview.h"
+
+#define PREVIEW_MASK (GDK_EXPOSURE_MASK | \
+ GDK_POINTER_MOTION_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_BUTTON_RELEASE_MASK | \
+ GDK_BUTTON_MOTION_MASK | \
+ GDK_KEY_PRESS_MASK | \
+ GDK_KEY_RELEASE_MASK | \
+ GDK_ENTER_NOTIFY_MASK | \
+ GDK_LEAVE_NOTIFY_MASK)
+
+#define PREVIEW_SIZE 400
+
+/*======================================================================
+ Preview Rendering Util routine
+=======================================================================*/
+
+#define CHECKWIDTH 4
+#define LIGHTCHECK 192
+#define DARKCHECK 128
+#ifndef OPAQUE
+#define OPAQUE 255
+#endif
+
+static Object_t *_tmp_obj;
+
+static Preview_t*
+preview_user_data(GtkWidget *preview)
+{
+ return (Preview_t*) g_object_get_data (G_OBJECT (preview), "preview");
+}
+
+gint
+preview_get_width(GtkWidget *preview)
+{
+ return preview_user_data(preview)->width;
+}
+
+gint
+preview_get_height(GtkWidget *preview)
+{
+ return preview_user_data(preview)->height;
+}
+
+static void
+render_background(Preview_t *preview_base)
+{
+ GtkWidget *preview = preview_base->preview;
+ GtkStyle *style;
+ const GdkColor *bg_color;
+
+ gtk_widget_ensure_style (preview);
+
+ style = gtk_widget_get_style (preview);
+ bg_color = &style->bg[GTK_STATE_NORMAL];
+
+ gimp_preview_area_fill (GIMP_PREVIEW_AREA (preview),
+ 0, 0, G_MAXINT, G_MAXINT,
+ bg_color->red >> 8,
+ bg_color->green >> 8,
+ bg_color->blue >> 8);
+}
+
+static void
+render_rgb_image (Preview_t *preview_base,
+ gint32 drawable_id)
+{
+ GeglBuffer *buffer;
+ guchar *dest_buffer;
+ gint dwidth, dheight, pwidth, pheight;
+ GtkWidget *preview = preview_base->preview;
+
+ dwidth = gimp_drawable_width (drawable_id);
+ dheight = gimp_drawable_height (drawable_id);
+ pwidth = preview_base->widget_width;
+ pheight = preview_base->widget_height;
+
+ dest_buffer = g_new (guchar, pwidth * pheight * 4);
+
+ buffer = gimp_drawable_get_buffer (drawable_id);
+
+ gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0, pwidth, pheight),
+ MIN ((gdouble) pwidth / (gdouble) dwidth,
+ (gdouble) pheight / (gdouble) dheight),
+ babl_format ("R'G'B'A u8"), dest_buffer,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ g_object_unref (buffer);
+
+ gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
+ 0, 0, pwidth, pheight,
+ GIMP_RGBA_IMAGE,
+ dest_buffer,
+ pwidth * 4);
+
+ g_free (dest_buffer);
+}
+
+static void
+render_preview (Preview_t *preview_base,
+ gint32 drawable_id)
+{
+ render_background (preview_base);
+ render_rgb_image (preview_base, drawable_id);
+}
+
+static gboolean
+arrow_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
+{
+ if (event->button == 1)
+ do_main_popup_menu(event);
+ return TRUE;
+}
+
+static gboolean
+preview_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ cairo_t *cr;
+ gint width = preview_get_width (widget);
+ gint height = preview_get_height (widget);
+
+ cr = gdk_cairo_create (event->window);
+ gdk_cairo_region (cr, event->region);
+ cairo_clip (cr);
+ cairo_set_line_width (cr, 1.);
+ draw_grid (cr, width, height);
+
+ draw_shapes (cr);
+
+ if (_tmp_obj)
+ {
+ /* this is a bit of a hack */
+ gdouble dash = 4.;
+ _tmp_obj->selected |= 4;
+ cairo_set_source_rgb (cr, 1., 0., 1.);
+ cairo_set_dash (cr, &dash, 1, dash);
+ object_draw (_tmp_obj, cr);
+ }
+
+ cairo_destroy (cr);
+ return FALSE;
+}
+
+void
+preview_set_tmp_obj (Object_t *obj)
+{
+ _tmp_obj = obj;
+}
+
+void
+preview_unset_tmp_obj (Object_t *obj)
+{
+ if (_tmp_obj == obj) _tmp_obj = NULL;
+}
+
+void
+preview_zoom(Preview_t *preview, gint zoom_factor)
+{
+ preview->widget_width = preview->width * zoom_factor;
+ preview->widget_height = preview->height * zoom_factor;
+ gtk_widget_set_size_request (preview->preview, preview->widget_width,
+ preview->widget_height);
+ gtk_widget_queue_resize(preview->window);
+ render_preview(preview, preview->drawable_id);
+ preview_redraw();
+}
+
+GdkCursorType
+preview_set_cursor(Preview_t *preview, GdkCursorType cursor_type)
+{
+ GdkCursorType prev_cursor = preview->cursor;
+ GdkDisplay *display = gtk_widget_get_display (preview->window);
+ GdkCursor *cursor = gdk_cursor_new_for_display (display,
+ cursor_type);
+
+ gdk_window_set_cursor(gtk_widget_get_window (preview->window), cursor);
+ gdk_cursor_unref(cursor);
+
+ preview->cursor = cursor_type;
+
+ return prev_cursor;
+}
+
+static const GtkTargetEntry target_table[] =
+{
+ {"STRING", 0, 1 },
+ {"text/plain", 0, 2 }
+};
+
+static void
+handle_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *data, guint info, guint time)
+{
+ gboolean success = FALSE;
+
+ if (gtk_selection_data_get_length (data) >= 0 &&
+ gtk_selection_data_get_format (data) == 8)
+ {
+ ObjectList_t *list = get_shapes();
+ Object_t *obj;
+
+ x = get_real_coord(x);
+ y = get_real_coord(y);
+ obj = object_list_find(list, x, y);
+ if (obj && !obj->locked)
+ {
+ command_list_add(edit_object_command_new(obj));
+ object_set_url(obj, (const gchar *) gtk_selection_data_get_data (data));
+ object_emit_update_signal(obj);
+ success = TRUE;
+ }
+ }
+ gtk_drag_finish(context, success, FALSE, time);
+}
+
+static void
+preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer preview_void)
+{
+ Preview_t *preview = preview_void;
+
+ render_preview (preview, preview->drawable_id);
+}
+
+static void
+scroll_adj_changed (GtkAdjustment *adj,
+ GimpRuler *ruler)
+{
+ gimp_ruler_set_range (ruler,
+ gtk_adjustment_get_value (adj),
+ gtk_adjustment_get_value (adj) +
+ gtk_adjustment_get_page_size (adj),
+ gtk_adjustment_get_upper (adj));
+}
+
+Preview_t *
+make_preview (gint32 drawable_id)
+{
+ Preview_t *data = g_new(Preview_t, 1);
+ GtkAdjustment *hadj;
+ GtkAdjustment *vadj;
+ GtkWidget *preview;
+ GtkWidget *window;
+ GtkWidget *viewport;
+ GtkWidget *button, *arrow;
+ GtkWidget *ruler;
+ GtkWidget *table;
+ GtkWidget *scrollbar;
+ gint width, height;
+
+ data->drawable_id = drawable_id;
+ data->preview = preview = gimp_preview_area_new ();
+
+ g_object_set_data (G_OBJECT (preview), "preview", data);
+ gtk_widget_set_events (GTK_WIDGET (preview), PREVIEW_MASK);
+
+ g_signal_connect_after (preview, "expose-event",
+ G_CALLBACK (preview_expose),
+ data);
+ g_signal_connect (preview, "size-allocate",
+ G_CALLBACK (preview_size_allocate),
+ data);
+
+ /* Handle drop of links in preview widget */
+ gtk_drag_dest_set (preview, GTK_DEST_DEFAULT_ALL, target_table,
+ 2, GDK_ACTION_COPY);
+
+ g_signal_connect (preview, "drag-data-received",
+ G_CALLBACK (handle_drop),
+ NULL);
+
+ data->widget_width = data->width = gimp_drawable_width (drawable_id);
+ data->widget_height = data->height = gimp_drawable_height (drawable_id);
+ gtk_widget_set_size_request (preview, data->widget_width,
+ data->widget_height);
+
+ /* The main table */
+ data->window = table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 1);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 1);
+
+ /* Create button with arrow */
+ button = gtk_button_new ();
+ gtk_widget_set_can_focus (button, FALSE);
+ gtk_table_attach (GTK_TABLE (table), button, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_set_events (button,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "button-press-event",
+ G_CALLBACK (arrow_cb),
+ NULL);
+
+ arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
+ gtk_container_add (GTK_CONTAINER (button), arrow);
+ gtk_widget_show (arrow);
+
+ /* Create horizontal ruler */
+ data->hruler = ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
+ g_signal_connect_swapped (preview, "motion-notify-event",
+ G_CALLBACK (GTK_WIDGET_GET_CLASS (ruler)->motion_notify_event),
+ ruler);
+
+ gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (ruler);
+
+ /* Create vertical ruler */
+ data->vruler = ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
+ g_signal_connect_swapped (preview, "motion-notify-event",
+ G_CALLBACK (GTK_WIDGET_GET_CLASS (ruler)->motion_notify_event),
+ ruler);
+ gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
+ GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (ruler);
+
+ window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ width = (data->width > 600) ? 600 : data->width;
+ height = (data->height > 400) ? 400 : data->height;
+ gtk_widget_set_size_request (window, width, height);
+ gtk_table_attach (GTK_TABLE (table), window, 1, 2, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (window);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_container_add (GTK_CONTAINER (window), viewport);
+ gtk_widget_show (viewport);
+
+ hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (window));
+
+ g_signal_connect (hadj, "changed",
+ G_CALLBACK (scroll_adj_changed),
+ data->hruler);
+ g_signal_connect (hadj, "value-changed",
+ G_CALLBACK (scroll_adj_changed),
+ data->hruler);
+
+ vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
+
+ g_signal_connect (vadj, "changed",
+ G_CALLBACK (scroll_adj_changed),
+ data->vruler);
+ g_signal_connect (vadj, "value-changed",
+ G_CALLBACK (scroll_adj_changed),
+ data->vruler);
+
+ gtk_container_add (GTK_CONTAINER (viewport), preview);
+
+ scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadj);
+ gtk_table_attach(GTK_TABLE(table), scrollbar, 1, 2, 2, 3,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+ gtk_widget_show (scrollbar);
+
+ scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadj);
+ gtk_table_attach (GTK_TABLE (table), scrollbar, 2, 3, 1, 2,
+ GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
+ gtk_widget_show (scrollbar);
+
+ gtk_widget_show (preview);
+
+ render_preview (data, drawable_id);
+
+ gtk_widget_show (table);
+
+ return data;
+}
diff --git a/plug-ins/imagemap/imap_preview.h b/plug-ins/imagemap/imap_preview.h
new file mode 100644
index 0000000..3fe01c4
--- /dev/null
+++ b/plug-ins/imagemap/imap_preview.h
@@ -0,0 +1,55 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_PREVIEW_H
+#define _IMAP_PREVIEW_H
+
+#include <libgimp/gimp.h>
+
+typedef struct {
+ gint32 drawable_id;
+ GtkWidget *window;
+ GtkWidget *preview;
+ GtkWidget *hruler;
+ GtkWidget *vruler;
+ gint width;
+ gint height;
+ gint widget_width;
+ gint widget_height;
+
+ GdkCursorType cursor;
+} Preview_t;
+
+Preview_t *make_preview(gint32 drawable_id);
+void preview_redraw(void);
+
+void preview_unset_tmp_obj (Object_t *obj);
+void preview_set_tmp_obj (Object_t *obj);
+
+gint preview_get_width(GtkWidget *preview);
+gint preview_get_height(GtkWidget *preview);
+
+void preview_zoom(Preview_t *preview, gint zoom_factor);
+GdkCursorType preview_set_cursor(Preview_t *preview,
+ GdkCursorType cursor_type);
+
+#endif /* _IMAP_PREVIEW_H */
diff --git a/plug-ins/imagemap/imap_rectangle.c b/plug-ins/imagemap/imap_rectangle.c
new file mode 100644
index 0000000..fa4c3a9
--- /dev/null
+++ b/plug-ins/imagemap/imap_rectangle.c
@@ -0,0 +1,538 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h> /* abs */
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_main.h"
+#include "imap_misc.h"
+#include "imap_object_popup.h"
+#include "imap_rectangle.h"
+#include "imap_stock.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static gboolean rectangle_is_valid(Object_t *obj);
+static Object_t *rectangle_clone(Object_t *obj);
+static void rectangle_assign(Object_t *obj, Object_t *des);
+static void rectangle_normalize(Object_t *obj);
+static void rectangle_draw(Object_t *obj, cairo_t *cr);
+static void rectangle_draw_sashes(Object_t *obj, cairo_t *cr);
+static MoveSashFunc_t rectangle_near_sash(Object_t *obj, gint x, gint y);
+static gboolean rectangle_point_is_on(Object_t *obj, gint x, gint y);
+static void rectangle_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height);
+static void rectangle_resize(Object_t *obj, gint percentage_x,
+ gint percentage_y);
+static void rectangle_move(Object_t *obj, gint dx, gint dy);
+static gpointer rectangle_create_info_widget(GtkWidget *frame);
+static void rectangle_fill_info_tab(Object_t *obj, gpointer data);
+static void rectangle_set_initial_focus(Object_t *obj, gpointer data);
+static void rectangle_update(Object_t *obj, gpointer data);
+static void rectangle_write_csim(Object_t *obj, gpointer param,
+ OutputFunc_t output);
+static void rectangle_write_cern(Object_t *obj, gpointer param,
+ OutputFunc_t output);
+static void rectangle_write_ncsa(Object_t *obj, gpointer param,
+ OutputFunc_t output);
+static const gchar* rectangle_get_stock_icon_name(void);
+
+static ObjectClass_t rectangle_class = {
+ N_("_Rectangle"),
+ NULL, /* info_dialog */
+
+ rectangle_is_valid,
+ NULL, /* rectangle_destruct */
+ rectangle_clone,
+ rectangle_assign,
+ rectangle_normalize,
+ rectangle_draw,
+ rectangle_draw_sashes,
+ rectangle_near_sash,
+ rectangle_point_is_on,
+ rectangle_get_dimensions,
+ rectangle_resize,
+ rectangle_move,
+ rectangle_create_info_widget,
+ rectangle_fill_info_tab, /* rectangle_update_info_widget */
+ rectangle_fill_info_tab,
+ rectangle_set_initial_focus,
+ rectangle_update,
+ rectangle_write_csim,
+ rectangle_write_cern,
+ rectangle_write_ncsa,
+ object_do_popup,
+ rectangle_get_stock_icon_name
+};
+
+Object_t*
+create_rectangle(gint x, gint y, gint width, gint height)
+{
+ Rectangle_t *rectangle = g_new(Rectangle_t, 1);
+ rectangle->x = x;
+ rectangle->y = y;
+ rectangle->width = width;
+ rectangle->height = height;
+ return object_init(&rectangle->obj, &rectangle_class);
+}
+
+static void
+draw_any_rectangle(cairo_t *cr, gint x, gint y, gint w, gint h)
+{
+ if (w < 0) {
+ x += w;
+ w = -w;
+ }
+ if (h < 0) {
+ y += h;
+ h = -h;
+ }
+ draw_rectangle(cr, FALSE, x, y, w, h);
+}
+
+static gboolean
+rectangle_is_valid(Object_t *obj)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ return rectangle->width && rectangle->height;
+}
+
+static Object_t*
+rectangle_clone(Object_t *obj)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ Rectangle_t *clone = g_new(Rectangle_t, 1);
+
+ clone->x = rectangle->x;
+ clone->y = rectangle->y;
+ clone->width = rectangle->width;
+ clone->height = rectangle->height;
+ return &clone->obj;
+}
+
+static void
+rectangle_assign(Object_t *obj, Object_t *des)
+{
+ Rectangle_t *src_rectangle = ObjectToRectangle(obj);
+ Rectangle_t *des_rectangle = ObjectToRectangle(des);
+ des_rectangle->x = src_rectangle->x;
+ des_rectangle->y = src_rectangle->y;
+ des_rectangle->width = src_rectangle->width;
+ des_rectangle->height = src_rectangle->height;
+}
+
+static void
+rectangle_normalize(Object_t *obj)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ if (rectangle->width < 0) {
+ rectangle->x += rectangle->width;
+ rectangle->width = -rectangle->width;
+ }
+ if (rectangle->height < 0) {
+ rectangle->y += rectangle->height;
+ rectangle->height = -rectangle->height;
+ }
+}
+
+static void
+rectangle_draw(Object_t *obj, cairo_t *cr)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ draw_any_rectangle(cr, rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+}
+
+static void
+rectangle_draw_sashes(Object_t *obj, cairo_t *cr)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ draw_sash(cr, rectangle->x, rectangle->y);
+ draw_sash(cr, rectangle->x + rectangle->width / 2, rectangle->y);
+ draw_sash(cr, rectangle->x + rectangle->width, rectangle->y);
+ draw_sash(cr, rectangle->x, rectangle->y + rectangle->height / 2);
+ draw_sash(cr, rectangle->x + rectangle->width,
+ rectangle->y + rectangle->height / 2);
+ draw_sash(cr, rectangle->x, rectangle->y + rectangle->height);
+ draw_sash(cr, rectangle->x + rectangle->width / 2,
+ rectangle->y + rectangle->height);
+ draw_sash(cr, rectangle->x + rectangle->width,
+ rectangle->y + rectangle->height);
+}
+
+static void
+MoveUpperSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->y += dy;
+ rectangle->height -= dy;
+}
+
+static void
+MoveLeftSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->x += dx;
+ rectangle->width -= dx;
+}
+
+static void
+MoveRightSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->width += dx;
+}
+
+static void
+MoveLowerSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->height += dy;
+}
+
+static void
+MoveUpperLeftSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->x += dx;
+ rectangle->y += dy;
+ rectangle->width -= dx;
+ rectangle->height -= dy;
+}
+
+static void
+MoveUpperRightSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->y += dy;
+ rectangle->width += dx;
+ rectangle->height -= dy;
+}
+
+static void
+MoveLowerLeftSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->x += dx;
+ rectangle->width -= dx;
+ rectangle->height += dy;
+}
+
+static void
+MoveLowerRightSash(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->width += dx;
+ rectangle->height += dy;
+}
+
+static MoveSashFunc_t
+rectangle_near_sash(Object_t *obj, gint x, gint y)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ if (near_sash(rectangle->x, rectangle->y, x, y))
+ return MoveUpperLeftSash;
+ else if (near_sash(rectangle->x + rectangle->width / 2, rectangle->y, x, y))
+ return MoveUpperSash;
+ else if (near_sash(rectangle->x + rectangle->width, rectangle->y, x, y))
+ return MoveUpperRightSash;
+ else if (near_sash(rectangle->x, rectangle->y + rectangle->height / 2,
+ x, y))
+ return MoveLeftSash;
+ else if (near_sash(rectangle->x + rectangle->width,
+ rectangle->y + rectangle->height / 2, x, y))
+ return MoveRightSash;
+ else if (near_sash(rectangle->x, rectangle->y + rectangle->height, x, y))
+ return MoveLowerLeftSash;
+ else if (near_sash(rectangle->x + rectangle->width / 2,
+ rectangle->y + rectangle->height, x, y))
+ return MoveLowerSash;
+ else if (near_sash(rectangle->x + rectangle->width,
+ rectangle->y + rectangle->height, x, y))
+ return MoveLowerRightSash;
+ return NULL;
+}
+
+static gboolean
+rectangle_point_is_on(Object_t *obj, gint x, gint y)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ return x >= rectangle->x && x <= rectangle->x + rectangle->width &&
+ y >= rectangle->y && y <= rectangle->y + rectangle->height;
+}
+
+static void
+rectangle_get_dimensions(Object_t *obj, gint *x, gint *y,
+ gint *width, gint *height)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ *x = rectangle->x;
+ *y = rectangle->y;
+ *width = rectangle->width;
+ *height = rectangle->height;
+}
+
+static void
+rectangle_resize(Object_t *obj, gint percentage_x, gint percentage_y)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->x = rectangle->x * percentage_x / 100;
+ rectangle->y = rectangle->y * percentage_y / 100;
+ rectangle->width = rectangle->width * percentage_x / 100;
+ rectangle->height = rectangle->height * percentage_y / 100;
+}
+
+static void
+rectangle_move(Object_t *obj, gint dx, gint dy)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ rectangle->x += dx;
+ rectangle->y += dy;
+}
+
+typedef struct {
+ Object_t *obj;
+ GtkWidget *x;
+ GtkWidget *y;
+ GtkWidget *width;
+ GtkWidget *height;
+ GtkWidget *chain_button;
+} RectangleProperties_t;
+
+static void
+x_changed_cb(GtkWidget *widget, gpointer data)
+{
+ RectangleProperties_t *props = (RectangleProperties_t*) data;
+ Object_t *obj = props->obj;
+ gint x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+
+ if (gimp_chain_button_get_active(GIMP_CHAIN_BUTTON(props->chain_button)))
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), x);
+
+ ObjectToRectangle(obj)->x = x;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+y_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((RectangleProperties_t*) data)->obj;
+ gint y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToRectangle(obj)->y = y;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+width_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((RectangleProperties_t*) data)->obj;
+ gint width = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToRectangle(obj)->width = width;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static void
+height_changed_cb(GtkWidget *widget, gpointer data)
+{
+ Object_t *obj = ((RectangleProperties_t*) data)->obj;
+ gint height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+ ObjectToRectangle(obj)->height = height;
+ edit_area_info_dialog_emit_geometry_signal(obj->class->info_dialog);
+}
+
+static gpointer
+rectangle_create_info_widget(GtkWidget *frame)
+{
+ RectangleProperties_t *props = g_new(RectangleProperties_t, 1);
+ GtkWidget *table, *label, *chain_button;
+ gint max_width = get_image_width();
+ gint max_height = get_image_height();
+
+ table = gtk_table_new(4, 4, FALSE);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ gtk_table_set_row_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_widget_show(table);
+
+ label = create_label_in_table(table, 0, 0, _("Upper left _x:"));
+ props->x = create_spin_button_in_table(table, label, 0, 1, 1, 0,
+ max_width - 1);
+ g_signal_connect(props->x, "value-changed",
+ G_CALLBACK(x_changed_cb), (gpointer) props);
+ create_label_in_table(table, 0, 3, _("pixels"));
+
+ label = create_label_in_table(table, 1, 0, _("Upper left _y:"));
+ props->y = create_spin_button_in_table(table, label, 1, 1, 1, 0,
+ max_height - 1);
+ g_signal_connect(props->y, "value-changed",
+ G_CALLBACK(y_changed_cb), (gpointer) props);
+ create_label_in_table(table, 1, 3, _("pixels"));
+
+ label = create_label_in_table(table, 2, 0, _("_Width:"));
+ props->width = create_spin_button_in_table(table, label, 2, 1, 1, 1,
+ max_width);
+ g_signal_connect(props->width, "value-changed",
+ G_CALLBACK(width_changed_cb), (gpointer) props);
+ create_label_in_table(table, 2, 3, _("pixels"));
+
+ label = create_label_in_table(table, 3, 0, _("_Height:"));
+ props->height = create_spin_button_in_table(table, label, 3, 1, 1, 1,
+ max_height);
+ g_signal_connect(props->height, "value-changed",
+ G_CALLBACK(height_changed_cb), (gpointer) props);
+ create_label_in_table(table, 3, 3, _("pixels"));
+
+ chain_button = gimp_chain_button_new(GIMP_CHAIN_RIGHT);
+ props->chain_button = chain_button;
+ gtk_table_attach_defaults(GTK_TABLE(table), chain_button, 2, 3, 2, 4);
+ gtk_widget_show(chain_button);
+
+ return props;
+}
+
+static void
+rectangle_fill_info_tab(Object_t *obj, gpointer data)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ RectangleProperties_t *props = (RectangleProperties_t*) data;
+
+ props->obj = obj;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->x), rectangle->x);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->y), rectangle->y);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->width), rectangle->width);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(props->height),
+ rectangle->height);
+}
+
+static void
+rectangle_set_initial_focus(Object_t *obj, gpointer data)
+{
+ RectangleProperties_t *props = (RectangleProperties_t*) data;
+ gtk_widget_grab_focus(props->x);
+}
+
+static void
+rectangle_update(Object_t* obj, gpointer data)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ RectangleProperties_t *props = (RectangleProperties_t*) data;
+
+ rectangle->x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->x));
+ rectangle->y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(props->y));
+ rectangle->width = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(props->width));
+ rectangle->height = gtk_spin_button_get_value_as_int(
+ GTK_SPIN_BUTTON(props->height));
+}
+
+static void
+rectangle_write_csim(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ output(param, "\"rect\" coords=\"%d,%d,%d,%d\"", rectangle->x, rectangle->y,
+ rectangle->x + rectangle->width, rectangle->y + rectangle->height);
+}
+
+static void
+rectangle_write_cern(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ output(param, "rect (%d,%d) (%d,%d)", rectangle->x, rectangle->y,
+ rectangle->x + rectangle->width, rectangle->y + rectangle->height);
+}
+
+static void
+rectangle_write_ncsa(Object_t *obj, gpointer param, OutputFunc_t output)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+ output(param, "rect %s %d,%d %d,%d", obj->url,
+ rectangle->x, rectangle->y,
+ rectangle->x + rectangle->width, rectangle->y + rectangle->height);
+}
+
+static const gchar*
+rectangle_get_stock_icon_name(void)
+{
+ return IMAP_STOCK_RECTANGLE;
+}
+
+static gboolean
+rectangle_factory_finish(Object_t *obj, gint x, gint y)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+
+ rectangle->width = x - rectangle->x;
+ rectangle->height = y - rectangle->y;
+
+ rectangle_normalize(obj);
+
+ return TRUE;
+}
+
+static Object_t*
+rectangle_factory_create_object(gint x, gint y)
+{
+ return create_rectangle(x, y, 0, 0);
+}
+
+static void
+rectangle_factory_set_xy(Object_t *obj, guint state, gint x, gint y)
+{
+ Rectangle_t *rectangle = ObjectToRectangle(obj);
+
+ rectangle->width = x - rectangle->x;
+ rectangle->height = y - rectangle->y;
+
+ if (state & GDK_SHIFT_MASK){
+ gint width = abs(rectangle->width);
+ gint height = abs(rectangle->height);
+ if (width < height)
+ rectangle->height = (rectangle->height < 0) ? -width : width;
+ else
+ rectangle->width = (rectangle->width < 0) ? -height : height;
+ }
+
+ main_set_dimension(rectangle->width, rectangle->height);
+}
+
+static ObjectFactory_t rectangle_factory = {
+ NULL, /* Object pointer */
+ rectangle_factory_finish,
+ NULL, /* Cancel func */
+ rectangle_factory_create_object,
+ rectangle_factory_set_xy
+};
+
+ObjectFactory_t*
+get_rectangle_factory(guint state)
+{
+ return &rectangle_factory;
+}
diff --git a/plug-ins/imagemap/imap_rectangle.h b/plug-ins/imagemap/imap_rectangle.h
new file mode 100644
index 0000000..fe33e31
--- /dev/null
+++ b/plug-ins/imagemap/imap_rectangle.h
@@ -0,0 +1,41 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_RECTANGLE_H
+#define _IMAP_RECTANGLE_H
+
+#include "imap_object.h"
+
+typedef struct {
+ Object_t obj;
+ gint x;
+ gint y;
+ gint width;
+ gint height;
+} Rectangle_t;
+
+#define ObjectToRectangle(obj) ((Rectangle_t*) (obj))
+
+Object_t* create_rectangle(gint x, gint y, gint width, gint height);
+ObjectFactory_t *get_rectangle_factory(guint state);
+
+#endif /* _IMAP_RECTANGLE_H */
diff --git a/plug-ins/imagemap/imap_selection.c b/plug-ins/imagemap/imap_selection.c
new file mode 100644
index 0000000..4713d17
--- /dev/null
+++ b/plug-ins/imagemap/imap_selection.c
@@ -0,0 +1,452 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_commands.h"
+#include "imap_main.h"
+#include "imap_menu.h"
+#include "imap_selection.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static void
+changed_cb(GtkTreeSelection *selection, gpointer param)
+{
+ Selection_t *data = (Selection_t*) param;
+
+ if (data->select_lock)
+ {
+ data->select_lock = FALSE;
+ } else
+ {
+ Command_t *command, *sub_command;
+ GtkTreeModel *model;
+ GList *list, *selected_rows;
+
+ selected_rows = gtk_tree_selection_get_selected_rows (selection,
+ &model);
+
+ command = subcommand_start (NULL);
+ sub_command = unselect_all_command_new (data->object_list, NULL);
+ command_add_subcommand (command, sub_command);
+
+ for (list = selected_rows; list; list = list->next)
+ {
+ Object_t *obj;
+ GtkTreeIter iter;
+ GtkTreePath *path = (GtkTreePath*) list->data;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, 0, &obj, -1);
+
+ sub_command = select_command_new (obj);
+ command_add_subcommand (command, sub_command);
+ }
+
+ command_set_name (command, sub_command->name);
+ subcommand_end ();
+
+ command_execute (command);
+
+ g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
+ }
+}
+
+static gboolean
+button_press_cb(GtkWidget *widget, GdkEventButton *event, Selection_t *data)
+{
+ if (event->button == 1) {
+ if (data->doubleclick) {
+ GtkTreePath *path;
+
+ data->doubleclick = FALSE;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ (gint) event->x, (gint) event->y,
+ &path, NULL, NULL, NULL)) {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (data->store), &iter,
+ path)) {
+ Object_t *obj;
+ gtk_tree_model_get (GTK_TREE_MODEL(data->store), &iter, 0, &obj, -1);
+ object_edit (obj, TRUE);
+ }
+ gtk_tree_path_free (path);
+ }
+ } else {
+ data->doubleclick = TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+button_release_cb(GtkWidget *widget, GdkEventButton *event, Selection_t *data)
+{
+ if (event->button == 1)
+ data->doubleclick = FALSE;
+ return FALSE;
+}
+
+static void
+selection_set_selected(Selection_t *selection, gint row)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (selection->store), &iter,
+ NULL, row)) {
+ Object_t *obj;
+
+ gtk_tree_model_get (GTK_TREE_MODEL(selection->store), &iter, 0, &obj, -1);
+
+ selection->select_lock = TRUE;
+
+ if (obj->selected) {
+ gtk_tree_selection_select_iter (selection->selection, &iter);
+ } else {
+ gtk_tree_selection_unselect_iter (selection->selection, &iter);
+ }
+ }
+}
+
+static void
+object_added_cb(Object_t *obj, gpointer data)
+{
+ Selection_t *selection = (Selection_t*) data;
+ GtkTreeIter iter;
+ gint position = object_get_position_in_list (obj);
+
+ selection->nr_rows++;
+ if (position < selection->nr_rows - 1) {
+ gtk_list_store_insert (selection->store, &iter, position);
+ } else {
+ gtk_list_store_append (selection->store, &iter);
+ }
+ gtk_list_store_set (selection->store, &iter, 0, obj, -1);
+}
+
+static gboolean
+selection_find_object(Selection_t *selection, Object_t *lookup,
+ GtkTreeIter *iter)
+{
+ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (selection->store),
+ iter)) {
+ do {
+ Object_t *obj;
+
+ gtk_tree_model_get (GTK_TREE_MODEL(selection->store), iter, 0,
+ &obj, -1);
+ if (obj == lookup)
+ return TRUE;
+
+ } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (selection->store),
+ iter));
+ }
+ return FALSE;
+}
+
+static void
+object_updated_cb(Object_t *obj, gpointer data)
+{
+ Selection_t *selection = (Selection_t*) data;
+ GtkTreeIter iter;
+
+ if (selection_find_object (selection, obj, &iter)) {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (selection->store), &iter);
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (selection->store), path,
+ &iter);
+ }
+}
+
+static void
+object_removed_cb(Object_t *obj, gpointer data)
+{
+ Selection_t *selection = (Selection_t*) data;
+ GtkTreeIter iter;
+
+ if (selection_find_object (selection, obj, &iter)) {
+ gtk_list_store_remove (GTK_LIST_STORE (selection->store), &iter);
+ }
+}
+
+static void
+object_selected_cb(Object_t *obj, gpointer data)
+{
+ Selection_t *selection = (Selection_t*) data;
+ gint position = object_get_position_in_list (obj);
+ selection_set_selected (selection, position);
+}
+
+static void
+object_moved_cb(Object_t *obj, gpointer data)
+{
+ Selection_t *selection = (Selection_t*) data;
+ selection->select_lock = TRUE;
+}
+
+static const GtkTargetEntry target_table[] =
+{
+ {"STRING", 0, 1 },
+ {"text/plain", 0, 2 }
+};
+
+static Object_t*
+selection_get_object (GtkTreeModel *tree_model, GtkTreeIter *iter)
+{
+ Object_t *obj;
+ gtk_tree_model_get (tree_model, iter, 0, &obj, -1);
+ return obj;
+}
+
+static void
+handle_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *data, guint info, guint time)
+{
+ gboolean success = FALSE;
+
+ if (gtk_selection_data_get_length (data) >= 0 &&
+ gtk_selection_data_get_format (data) == 8)
+ {
+ GtkTreePath *path;
+
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y,
+ &path, NULL, NULL, NULL))
+ {
+ GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ {
+ Object_t *obj = selection_get_object (model, &iter);
+
+ if (!obj->locked)
+ {
+ command_list_add(edit_object_command_new (obj));
+ object_set_url (obj, (const gchar *) gtk_selection_data_get_data (data));
+ object_emit_update_signal (obj);
+ success = TRUE;
+ }
+ }
+ gtk_tree_path_free (path);
+ }
+ }
+ gtk_drag_finish(context, success, FALSE, time);
+}
+
+static void
+render_image (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ Object_t *obj = selection_get_object (tree_model, iter);
+ g_object_set(cell, "stock-id", object_get_stock_icon_name(obj), NULL);
+}
+
+static void
+render_nr (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ Object_t *obj = selection_get_object (tree_model, iter);
+ gchar *scratch;
+
+ scratch = g_strdup_printf ("%d", object_get_position_in_list (obj) + 1);
+ g_object_set (cell, "text", scratch, NULL);
+ g_free (scratch);
+}
+
+static void
+render_url (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ Object_t *obj = selection_get_object (tree_model, iter);
+ g_object_set (cell, "text", obj->url, NULL);
+}
+
+static void
+render_target (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ Object_t *obj = selection_get_object (tree_model, iter);
+ g_object_set (cell, "text", obj->target, NULL);
+}
+
+static void
+render_comment (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
+{
+ Object_t *obj = selection_get_object (tree_model, iter);
+ g_object_set (cell, "text", obj->comment, NULL);
+}
+
+Selection_t*
+make_selection(ObjectList_t *object_list)
+{
+ Selection_t *data = g_new(Selection_t, 1);
+ GtkWidget *swin, *frame, *hbox;
+ GtkWidget *toolbar;
+ GtkWidget *list;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ data->object_list = object_list;
+ data->selected_child = NULL;
+ data->is_visible = TRUE;
+ data->nr_rows = 0;
+ data->select_lock = FALSE;
+ data->doubleclick = FALSE;
+
+ data->container = frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+ gtk_widget_show(frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+
+ toolbar = make_selection_toolbar ();
+ gtk_box_pack_start (GTK_BOX (hbox), toolbar, TRUE, TRUE, 0);
+
+ /* Create selection */
+ frame = gimp_frame_new (_("Selection"));
+ gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ data->store = gtk_list_store_new (1, G_TYPE_POINTER);
+ data->list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (data->store));
+ list = data->list;
+ g_object_unref (data->store);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (N_("#"),
+ renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ render_nr, data, NULL);
+ gtk_tree_view_column_set_min_width (column, 16);
+ gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_alignment (column, 0.5);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_title (column, _("URL"));
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ render_image, data, NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, render_url, data,
+ NULL);
+ gtk_tree_view_column_set_min_width (column, 80);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_alignment (column, 0.5);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("ALT Text"), renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer, render_comment,
+ data, NULL);
+ gtk_tree_view_column_set_min_width (column, 64);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_alignment (column, 0.5);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Target"), renderer,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ render_target, data, NULL);
+ gtk_tree_view_column_set_min_width (column, 64);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_alignment (column, 0.5);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
+
+
+ /* Create scrollable window */
+ swin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (swin, 16 + 80 + 2 * 64 + 16, -1);
+ gtk_container_add (GTK_CONTAINER(frame), swin);
+ gtk_widget_show (swin);
+
+ gtk_container_add (GTK_CONTAINER (swin), list);
+ gtk_widget_show (list);
+
+ /* Drop support */
+ gtk_drag_dest_set (list, GTK_DEST_DEFAULT_ALL, target_table, 2,
+ GDK_ACTION_COPY);
+ g_signal_connect (list, "drag-data-received", G_CALLBACK(handle_drop), NULL);
+
+ /* For handling doubleclick */
+
+ g_signal_connect (list, "button-press-event",
+ G_CALLBACK(button_press_cb), data);
+ g_signal_connect (list, "button-release-event",
+ G_CALLBACK(button_release_cb), data);
+
+ /* Callbacks we are interested in */
+ data->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
+ gtk_tree_selection_set_mode (data->selection, GTK_SELECTION_MULTIPLE);
+ g_signal_connect (data->selection, "changed", G_CALLBACK(changed_cb), data);
+
+ /* Set object list callbacks we're interested in */
+ object_list_add_add_cb (object_list, object_added_cb, data);
+ object_list_add_update_cb (object_list, object_updated_cb, data);
+ object_list_add_remove_cb (object_list, object_removed_cb, data);
+ object_list_add_select_cb (object_list, object_selected_cb, data);
+ object_list_add_move_cb (object_list, object_moved_cb, data);
+
+ return data;
+}
+
+void
+selection_toggle_visibility(Selection_t *selection)
+{
+ /* Toggle */
+ selection->is_visible = ! selection->is_visible;
+
+ /* Adapt to new state */
+ gtk_widget_set_visible (selection->container,
+ selection->is_visible);
+}
+
+void
+selection_freeze(Selection_t *selection)
+{
+}
+
+void
+selection_thaw(Selection_t *selection)
+{
+}
diff --git a/plug-ins/imagemap/imap_selection.h b/plug-ins/imagemap/imap_selection.h
new file mode 100644
index 0000000..cb13afb
--- /dev/null
+++ b/plug-ins/imagemap/imap_selection.h
@@ -0,0 +1,64 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_SELECTION_H
+#define _IMAP_SELECTION_H
+
+#include "imap_command.h"
+#include "imap_object.h"
+
+typedef struct {
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+
+ GtkWidget *container;
+ GtkWidget *list;
+ GtkWidget *selected_child;
+ ObjectList_t *object_list;
+ gint selected_row;
+ gint nr_rows;
+ gboolean is_visible;
+ gboolean select_lock;
+ gboolean doubleclick;
+
+ CommandFactory_t cmd_move_up;
+ CommandFactory_t cmd_move_down;
+ CommandFactory_t cmd_delete;
+ CommandFactory_t cmd_edit;
+} Selection_t;
+
+Selection_t *make_selection(ObjectList_t *list);
+void selection_toggle_visibility(Selection_t *selection);
+void selection_freeze(Selection_t *selection);
+void selection_thaw(Selection_t *selection);
+
+#define selection_set_move_up_command(selection, command) \
+ ((selection)->cmd_move_up = (command))
+#define selection_set_move_down_command(selection, command) \
+ ((selection)->cmd_move_down = (command))
+#define selection_set_delete_command(selection, command) \
+ ((selection)->cmd_delete = (command))
+#define selection_set_edit_command(selection, command) \
+ ((selection)->cmd_edit = (command))
+
+#endif /* _IMAP_SELECTION_H */
+
diff --git a/plug-ins/imagemap/imap_settings.c b/plug-ins/imagemap/imap_settings.c
new file mode 100644
index 0000000..10a4a33
--- /dev/null
+++ b/plug-ins/imagemap/imap_settings.c
@@ -0,0 +1,188 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_browse.h"
+#include "imap_main.h"
+#include "imap_settings.h"
+#include "imap_string.h"
+#include "imap_table.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+typedef struct {
+ DefaultDialog_t *dialog;
+ BrowseWidget_t *imagename;
+ GtkWidget *filename;
+ GtkWidget *title;
+ GtkWidget *author;
+ GtkWidget *default_url;
+ GtkWidget *ncsa;
+ GtkWidget *cern;
+ GtkWidget *csim;
+ GtkTextBuffer *description;
+} SettingsDialog_t;
+
+static MapFormat_t _map_format = CSIM;
+
+static void
+settings_ok_cb(gpointer data)
+{
+ SettingsDialog_t *param = (SettingsDialog_t*) data;
+ MapInfo_t *info = get_map_info();
+ gchar *description;
+ GtkTextIter start, end;
+
+ g_strreplace(&info->image_name, gtk_entry_get_text(
+ GTK_ENTRY(param->imagename->file)));
+ g_strreplace(&info->title, gtk_entry_get_text(GTK_ENTRY(param->title)));
+ g_strreplace(&info->author, gtk_entry_get_text(GTK_ENTRY(param->author)));
+ g_strreplace(&info->default_url,
+ gtk_entry_get_text(GTK_ENTRY(param->default_url)));
+ gtk_text_buffer_get_bounds(param->description, &start, &end);
+ description = gtk_text_buffer_get_text(param->description, &start, &end,
+ FALSE);
+ g_strreplace(&info->description, description);
+ g_free(description);
+
+ info->map_format = _map_format;
+}
+
+static void
+type_toggled_cb(GtkWidget *widget, gpointer data)
+{
+ if (gtk_widget_get_state (widget) & GTK_STATE_SELECTED)
+ _map_format = (MapFormat_t) data;
+}
+
+static SettingsDialog_t*
+create_settings_dialog(void)
+{
+ SettingsDialog_t *data = g_new(SettingsDialog_t, 1);
+ GtkWidget *table, *view, *frame, *hbox, *label, *swin;
+ DefaultDialog_t *dialog;
+
+ dialog = data->dialog = make_default_dialog(_("Settings for this Mapfile"));
+ default_dialog_set_ok_cb(dialog, settings_ok_cb, (gpointer) data);
+ table = default_dialog_add_table(dialog, 9, 2);
+
+ create_label_in_table(table, 0, 0, _("Filename:"));
+ data->filename = create_label_in_table(table, 0, 1, "");
+
+ create_label_in_table(table, 1, 0, _("Image name:"));
+ data->imagename = browse_widget_new(_("Select Image File"));
+ gtk_table_attach_defaults(GTK_TABLE(table), data->imagename->hbox, 1, 2,
+ 1, 2);
+
+ label = create_label_in_table(table, 2, 0, _("_Title:"));
+ data->title = create_entry_in_table(table, label, 2, 1);
+ label = create_label_in_table(table, 3, 0, _("Aut_hor:"));
+ data->author = create_entry_in_table(table, label, 3, 1);
+ label = create_label_in_table(table, 4, 0, _("Default _URL:"));
+ data->default_url = create_entry_in_table(table, label, 4, 1);
+ label = create_label_in_table(table, 5, 0, _("_Description:"));
+
+ data->description = gtk_text_buffer_new(NULL);
+
+ view = gtk_text_view_new_with_buffer(data->description);
+ gtk_widget_set_size_request(view, -1, 128);
+ gtk_widget_show(view);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), view);
+
+ swin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
+ GTK_SHADOW_IN);
+ gtk_table_attach(GTK_TABLE(table), swin, 1, 2, 5, 8,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_show(swin);
+ gtk_container_add(GTK_CONTAINER(swin), view);
+
+ frame = gimp_frame_new(_("Map File Format"));
+ gtk_widget_show(frame);
+ gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 9, 10);
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_widget_show(hbox);
+
+ data->ncsa = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_NCSA");
+ g_signal_connect(data->ncsa, "toggled",
+ G_CALLBACK(type_toggled_cb), (gpointer) NCSA);
+ gtk_box_pack_start(GTK_BOX(hbox), data->ncsa, FALSE, FALSE, 0);
+ gtk_widget_show(data->ncsa);
+
+ data->cern = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(data->ncsa), "C_ERN");
+ g_signal_connect(data->cern, "toggled",
+ G_CALLBACK(type_toggled_cb), (gpointer) CERN);
+ gtk_box_pack_start(GTK_BOX(hbox), data->cern, FALSE, FALSE, 0);
+ gtk_widget_show(data->cern);
+
+ data->csim = gtk_radio_button_new_with_mnemonic_from_widget(
+ GTK_RADIO_BUTTON(data->cern), "C_SIM");
+ g_signal_connect(data->csim, "toggled",
+ G_CALLBACK(type_toggled_cb), (gpointer) CSIM);
+ gtk_box_pack_start(GTK_BOX(hbox), data->csim, FALSE, FALSE, 0);
+ gtk_widget_show(data->csim);
+
+ return data;
+}
+
+void
+do_settings_dialog(void)
+{
+ static SettingsDialog_t *dialog;
+ const char *filename = get_filename();
+ MapInfo_t *info = get_map_info();
+
+ if (!dialog)
+ dialog = create_settings_dialog();
+
+ if (!filename)
+ filename = _("<Untitled>");
+
+ gtk_label_set_text(GTK_LABEL(dialog->filename), filename);
+ browse_widget_set_filename(dialog->imagename, info->image_name);
+ gtk_entry_set_text(GTK_ENTRY(dialog->title), info->title);
+ gtk_entry_set_text(GTK_ENTRY(dialog->author), info->author);
+ gtk_entry_set_text(GTK_ENTRY(dialog->default_url), info->default_url);
+
+ if (info->map_format == NCSA)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->ncsa), TRUE);
+ else if (info->map_format == CERN)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->cern), TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->csim), TRUE);
+
+ gtk_widget_grab_focus(dialog->imagename->file);
+ default_dialog_show(dialog->dialog);
+
+ gtk_text_buffer_set_text (dialog->description, info->description, -1);
+}
diff --git a/plug-ins/imagemap/imap_settings.h b/plug-ins/imagemap/imap_settings.h
new file mode 100644
index 0000000..4b375d6
--- /dev/null
+++ b/plug-ins/imagemap/imap_settings.h
@@ -0,0 +1,28 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_SETTINGS_H
+#define _IMAP_SETTINGS_H
+
+void do_settings_dialog(void);
+
+#endif /* _IMAP_SETTINGS_H */
diff --git a/plug-ins/imagemap/imap_source.c b/plug-ins/imagemap/imap_source.c
new file mode 100644
index 0000000..40ba1b7
--- /dev/null
+++ b/plug-ins/imagemap/imap_source.c
@@ -0,0 +1,91 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "imap_default_dialog.h"
+#include "imap_main.h"
+#include "imap_source.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static void save_to_view (GtkTextBuffer *buffer,
+ const char *format,
+ ...) G_GNUC_PRINTF(2,3);
+
+static void
+save_to_view(GtkTextBuffer *buffer, const char* format, ...)
+{
+ va_list ap;
+ char scratch[1024];
+ GtkTextIter iter;
+
+ va_start(ap, format);
+ vsprintf(scratch, format, ap);
+ va_end(ap);
+
+ gtk_text_buffer_get_end_iter(buffer, &iter);
+ gtk_text_buffer_insert(buffer, &iter, scratch, -1);
+}
+
+void
+do_source_dialog(void)
+{
+ static DefaultDialog_t *dialog;
+ static GtkWidget *text;
+ static GtkTextBuffer *buffer;
+
+ if (!dialog)
+ {
+ GtkWidget *swin;
+
+ dialog = make_default_dialog(_("View Source"));
+ default_dialog_hide_cancel_button(dialog);
+ default_dialog_hide_apply_button(dialog);
+
+ buffer = gtk_text_buffer_new(NULL);
+
+ text = gtk_text_view_new_with_buffer(buffer);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ gtk_widget_show(text);
+
+ swin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request(swin, 400, 300);
+ gtk_box_pack_start(GTK_BOX(dialog->vbox), swin, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_show(swin);
+ gtk_container_add(GTK_CONTAINER(swin), text);
+ }
+ gtk_text_buffer_set_text(buffer, "", -1);
+ dump_output(buffer, (OutputFunc_t) save_to_view);
+
+ default_dialog_show(dialog);
+}
diff --git a/plug-ins/imagemap/imap_source.h b/plug-ins/imagemap/imap_source.h
new file mode 100644
index 0000000..a090515
--- /dev/null
+++ b/plug-ins/imagemap/imap_source.h
@@ -0,0 +1,28 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_SOURCE_H
+#define _IMAP_SOURCE_H
+
+void do_source_dialog(void);
+
+#endif /* _IMAP_SOURCE_H */
diff --git a/plug-ins/imagemap/imap_statusbar.c b/plug-ins/imagemap/imap_statusbar.c
new file mode 100644
index 0000000..ca4bad8
--- /dev/null
+++ b/plug-ins/imagemap/imap_statusbar.c
@@ -0,0 +1,153 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "imap_statusbar.h"
+#include "imap_stock.h"
+
+StatusBar_t*
+make_statusbar(GtkWidget *main_vbox, GtkWidget *window)
+{
+ StatusBar_t *statusbar = g_new(StatusBar_t, 1);
+ GtkWidget *hbox, *iconw;
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+ /* Status info */
+ statusbar->status = gtk_statusbar_new();
+ statusbar->status_id = gtk_statusbar_get_context_id(
+ GTK_STATUSBAR(statusbar->status), "general_status");
+ gtk_box_pack_start(GTK_BOX(hbox), statusbar->status, TRUE, TRUE, 0);
+ gtk_widget_show(statusbar->status);
+
+ /* (x, y) coordinate */
+ iconw = gtk_image_new_from_stock(IMAP_STOCK_COORD,
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+
+ gtk_box_pack_start(GTK_BOX(hbox), iconw, FALSE, FALSE, 10);
+ gtk_widget_show(iconw);
+
+ statusbar->xy = gtk_entry_new();
+ gtk_widget_set_size_request(statusbar->xy, 96, -1);
+ gtk_editable_set_editable(GTK_EDITABLE(statusbar->xy), FALSE);
+ gtk_widget_set_can_focus (statusbar->xy, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), statusbar->xy, FALSE, FALSE, 0);
+ gtk_widget_show(statusbar->xy);
+
+ /* Dimension info */
+ iconw = gtk_image_new_from_stock(IMAP_STOCK_DIMENSION,
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_box_pack_start(GTK_BOX(hbox), iconw, FALSE, FALSE, 10);
+ gtk_widget_show(iconw);
+
+ statusbar->dimension = gtk_entry_new();
+ gtk_widget_set_size_request(statusbar->dimension, 96, -1);
+ gtk_editable_set_editable(GTK_EDITABLE(statusbar->dimension), FALSE);
+ gtk_widget_set_can_focus (statusbar->dimension, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), statusbar->dimension, FALSE, FALSE, 0);
+ gtk_widget_show(statusbar->dimension);
+
+ /* Zoom info */
+ statusbar->zoom = gtk_statusbar_new();
+ gtk_widget_set_size_request(statusbar->zoom, 48, -1);
+ statusbar->zoom_id = gtk_statusbar_get_context_id(
+ GTK_STATUSBAR(statusbar->zoom), "zoom_status");
+ gtk_box_pack_start(GTK_BOX(hbox), statusbar->zoom, FALSE, FALSE, 5);
+ gtk_widget_show(statusbar->zoom);
+
+ gtk_widget_show(hbox);
+
+ return statusbar;
+}
+
+void
+statusbar_set_status(StatusBar_t *statusbar, const gchar *format, ...)
+{
+ va_list ap;
+ char *str;
+
+ va_start(ap, format);
+ str = g_strdup_vprintf (format, ap);
+ va_end(ap);
+
+ statusbar_clear_status(statusbar);
+ statusbar->message_id =
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar->status),
+ statusbar->status_id, str);
+ g_free (str);
+}
+
+void
+statusbar_clear_status(StatusBar_t *statusbar)
+{
+ if (statusbar->message_id)
+ gtk_statusbar_remove(GTK_STATUSBAR(statusbar->status),
+ statusbar->status_id,
+ statusbar->message_id);
+}
+
+void
+statusbar_set_xy(StatusBar_t *statusbar, gint x, gint y)
+{
+ char scratch[16];
+
+ sprintf(scratch, "%d, %d", (int) x, (int) y);
+ gtk_entry_set_text(GTK_ENTRY(statusbar->xy), scratch);
+}
+
+void statusbar_clear_xy(StatusBar_t *statusbar)
+{
+ gtk_entry_set_text(GTK_ENTRY(statusbar->xy), "");
+}
+
+void
+statusbar_set_dimension(StatusBar_t *statusbar, gint w, gint h)
+{
+ gchar scratch[16];
+
+ g_snprintf (scratch, sizeof (scratch), "%d × %d", (gint) w, (gint) h);
+ gtk_entry_set_text(GTK_ENTRY(statusbar->dimension), scratch);
+}
+
+void
+statusbar_clear_dimension(StatusBar_t *statusbar)
+{
+ gtk_entry_set_text(GTK_ENTRY(statusbar->dimension), "");
+}
+
+void
+statusbar_set_zoom(StatusBar_t *statusbar, gint factor)
+{
+ char scratch[16];
+
+ sprintf(scratch, "1:%d", factor);
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar->zoom), statusbar->zoom_id,
+ scratch);
+}
diff --git a/plug-ins/imagemap/imap_statusbar.h b/plug-ins/imagemap/imap_statusbar.h
new file mode 100644
index 0000000..6b1a87b
--- /dev/null
+++ b/plug-ins/imagemap/imap_statusbar.h
@@ -0,0 +1,47 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2003 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_STATUSBAR_H
+#define _IMAP_STATUSBAR_H
+
+typedef struct {
+ GtkWidget *status;
+ GtkWidget *xy;
+ GtkWidget *dimension;
+ GtkWidget *zoom;
+
+ gint status_id;
+ gint message_id;
+
+ gint zoom_id;
+} StatusBar_t;
+
+StatusBar_t *make_statusbar(GtkWidget *main_vbox, GtkWidget *window);
+void statusbar_set_status(StatusBar_t *statusbar, const gchar *format, ...) G_GNUC_PRINTF(2,3);
+void statusbar_clear_status(StatusBar_t *statusbar);
+void statusbar_set_xy(StatusBar_t *statusbar, gint x, gint y);
+void statusbar_clear_xy(StatusBar_t *statusbar);
+void statusbar_set_dimension(StatusBar_t *statusbar, gint w, gint h);
+void statusbar_clear_dimension(StatusBar_t *statusbar);
+void statusbar_set_zoom(StatusBar_t *statusbar, gint factor);
+
+#endif /* _IMAP_STATUSBAR_H */
diff --git a/plug-ins/imagemap/imap_stock.c b/plug-ins/imagemap/imap_stock.c
new file mode 100644
index 0000000..4c3fedd
--- /dev/null
+++ b/plug-ins/imagemap/imap_stock.c
@@ -0,0 +1,89 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "imap_stock.h"
+
+#include "images/imap-stock-pixbufs.h"
+
+static GtkIconFactory *imap_icon_factory = NULL;
+
+static GtkStockItem imap_stock_items[] =
+{
+ { IMAP_STOCK_CIRCLE, NULL, 0, 0, NULL },
+ { IMAP_STOCK_COORD, NULL, 0, 0, NULL },
+ { IMAP_STOCK_DIMENSION, NULL, 0, 0, NULL },
+ { IMAP_STOCK_JAVA, NULL, 0, 0, NULL },
+ { IMAP_STOCK_POLYGON, NULL, 0, 0, NULL },
+ { IMAP_STOCK_RECTANGLE, NULL, 0, 0, NULL },
+ { IMAP_STOCK_TO_BACK, NULL, 0, 0, NULL },
+ { IMAP_STOCK_TO_FRONT, NULL, 0, 0, NULL }
+ };
+
+static void
+add_stock_icon (const gchar *stock_id,
+ const guint8 *inline_data)
+{
+ GtkIconSource *source;
+ GtkIconSet *set;
+ GdkPixbuf *pixbuf;
+
+ source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_size (source, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_icon_source_set_size_wildcarded (source, TRUE);
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
+ gtk_icon_source_set_pixbuf (source, pixbuf);
+ g_object_unref (pixbuf);
+
+ set = gtk_icon_set_new ();
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (imap_icon_factory, stock_id, set);
+
+ gtk_icon_set_unref (set);
+}
+
+void
+init_stock_icons (void)
+{
+ imap_icon_factory = gtk_icon_factory_new ();
+
+ add_stock_icon (IMAP_STOCK_CIRCLE, stock_circle);
+ add_stock_icon (IMAP_STOCK_COORD, stock_coord);
+ add_stock_icon (IMAP_STOCK_DIMENSION, stock_dimension);
+ add_stock_icon (IMAP_STOCK_JAVA, stock_java);
+ add_stock_icon (IMAP_STOCK_POLYGON, stock_polygon);
+ add_stock_icon (IMAP_STOCK_RECTANGLE, stock_rectangle);
+ add_stock_icon (IMAP_STOCK_TO_BACK, stock_to_back);
+ add_stock_icon (IMAP_STOCK_TO_FRONT, stock_to_front);
+
+ gtk_icon_factory_add_default (imap_icon_factory);
+
+ gtk_stock_add_static (imap_stock_items, G_N_ELEMENTS (imap_stock_items));
+}
diff --git a/plug-ins/imagemap/imap_stock.h b/plug-ins/imagemap/imap_stock.h
new file mode 100644
index 0000000..5587b91
--- /dev/null
+++ b/plug-ins/imagemap/imap_stock.h
@@ -0,0 +1,39 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2004 Maurits Rijk m.rijk@chello.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_STOCK_H
+#define _IMAP_STOCK_H
+
+#define IMAP_STOCK_ARROW "imap-arrow"
+#define IMAP_STOCK_CIRCLE "imap-circle"
+#define IMAP_STOCK_COORD "imap-coord"
+#define IMAP_STOCK_DIMENSION "imap-dimension"
+#define IMAP_STOCK_JAVA "imap-java"
+#define IMAP_STOCK_LINK "imap-link"
+#define IMAP_STOCK_POLYGON "imap-polygon"
+#define IMAP_STOCK_RECTANGLE "imap-rectangle"
+#define IMAP_STOCK_TO_BACK "imap-to-back"
+#define IMAP_STOCK_TO_FRONT "imap-to-front"
+
+void init_stock_icons(void);
+
+#endif /* _IMAP_STOCK_H */
diff --git a/plug-ins/imagemap/imap_string.c b/plug-ins/imagemap/imap_string.c
new file mode 100644
index 0000000..5f3b31c
--- /dev/null
+++ b/plug-ins/imagemap/imap_string.c
@@ -0,0 +1,37 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "imap_string.h"
+
+gchar*
+g_strreplace(gchar **old_str, const gchar *new_str)
+{
+ if (*old_str != new_str) {
+ g_free(*old_str);
+ *old_str = g_strdup(new_str);
+ }
+ return *old_str;
+}
diff --git a/plug-ins/imagemap/imap_string.h b/plug-ins/imagemap/imap_string.h
new file mode 100644
index 0000000..c878131
--- /dev/null
+++ b/plug-ins/imagemap/imap_string.h
@@ -0,0 +1,28 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_STRING_H
+#define _IMAP_STRING_H
+
+gchar *g_strreplace(gchar **old_str, const gchar *new_str);
+
+#endif /* _IMAP_STRING_H */
diff --git a/plug-ins/imagemap/imap_table.c b/plug-ins/imagemap/imap_table.c
new file mode 100644
index 0000000..d72a478
--- /dev/null
+++ b/plug-ins/imagemap/imap_table.c
@@ -0,0 +1,81 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "imap_table.h"
+
+static GtkWidget*
+add_widget_to_table(GtkWidget *table, int row, int col, GtkWidget *w)
+{
+ gtk_table_attach_defaults(GTK_TABLE(table), w, col, col + 1, row, row + 1);
+ gtk_widget_show(w);
+ return w;
+}
+
+GtkWidget*
+create_spin_button_in_table(GtkWidget *table, GtkWidget *label,
+ int row, int col, int value, int min, int max)
+{
+ GtkObject *adj = gtk_adjustment_new(value, min, max, 1, 10, 0);
+ GtkWidget *button = gimp_spin_button_new(GTK_ADJUSTMENT(adj), 1, 0);
+ gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE);
+ if (label)
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
+ return add_widget_to_table(table, row, col, button);
+}
+
+GtkWidget*
+create_check_button_in_table(GtkWidget *table, int row, int col,
+ const char *text)
+{
+ GtkWidget *button = gtk_check_button_new_with_mnemonic(text);
+ return add_widget_to_table(table, row, col, button);
+}
+
+GtkWidget*
+create_radio_button_in_table(GtkWidget *table, GSList *group,
+ int row, int col, const char *text)
+{
+ GtkWidget *button = gtk_radio_button_new_with_mnemonic(group, text);
+ return add_widget_to_table(table, row, col, button);
+}
+
+GtkWidget*
+create_label_in_table(GtkWidget *table, int row, int col, const char *text)
+{
+ GtkWidget *label = gtk_label_new_with_mnemonic(text);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ return add_widget_to_table(table, row, col, label);
+}
+
+GtkWidget*
+create_entry_in_table(GtkWidget *table, GtkWidget *label, int row, int col)
+{
+ GtkWidget *entry = gtk_entry_new();
+ if (label)
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ return add_widget_to_table(table, row, col, entry);
+}
diff --git a/plug-ins/imagemap/imap_table.h b/plug-ins/imagemap/imap_table.h
new file mode 100644
index 0000000..5b6a855
--- /dev/null
+++ b/plug-ins/imagemap/imap_table.h
@@ -0,0 +1,39 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-2002 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_TABLE_H
+#define _IMAP_TABLE_H
+
+GtkWidget *create_spin_button_in_table(GtkWidget *table, GtkWidget *label,
+ int row, int col,
+ int value, int min, int max);
+GtkWidget *create_check_button_in_table(GtkWidget *table, int row, int col,
+ const char *text);
+GtkWidget *create_radio_button_in_table(GtkWidget *table, GSList *group,
+ int row, int col, const char *text);
+GtkWidget *create_label_in_table(GtkWidget *table, int row, int col,
+ const char *text);
+GtkWidget *create_entry_in_table(GtkWidget *table, GtkWidget *label, int row,
+ int col);
+
+#endif /* _IMAP_TABLE_H */
+
diff --git a/plug-ins/imagemap/imap_taglist.c b/plug-ins/imagemap/imap_taglist.c
new file mode 100644
index 0000000..9b03c9d
--- /dev/null
+++ b/plug-ins/imagemap/imap_taglist.c
@@ -0,0 +1,126 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "imap_string.h"
+#include "imap_taglist.h"
+
+static Tag_t*
+tag_create(const gchar *name, const gchar *value)
+{
+ Tag_t *tag = g_new(Tag_t, 1);
+ tag->name = g_strdup(name);
+ tag->value = g_strdup(value);
+ return tag;
+}
+
+static Tag_t*
+tag_clone(Tag_t *src)
+{
+ return tag_create(src->name, src->value);
+}
+
+static void
+tag_destruct(Tag_t *tag)
+{
+ g_free(tag->name);
+ g_free(tag->value);
+ g_free(tag);
+}
+
+static void
+tag_write(Tag_t *tag)
+{
+ printf("\"%s\"=\"%s\"\n", tag->name, tag->value);
+}
+
+TagList_t*
+taglist_create(void)
+{
+ TagList_t *tlist = g_new(TagList_t, 1);
+ tlist->list = NULL;
+ return tlist;
+}
+
+TagList_t*
+taglist_clone(TagList_t *src)
+{
+ return taglist_copy(src, taglist_create());
+}
+
+static void
+taglist_remove_all(TagList_t *tlist)
+{
+ GList *p;
+ for (p = tlist->list; p; p = p->next)
+ tag_destruct((Tag_t*) p->data);
+ g_list_free(tlist->list);
+ tlist->list = NULL;
+}
+
+TagList_t*
+taglist_copy(TagList_t *src, TagList_t *des)
+{
+ GList *p;
+
+ taglist_remove_all(des);
+ for (p = src->list; p; p = p->next)
+ des->list = g_list_append(des->list, tag_clone((Tag_t*) p->data));
+ return des;
+}
+
+void
+taglist_destruct(TagList_t *tlist)
+{
+ taglist_remove_all(tlist);
+ g_free(tlist);
+}
+
+void
+taglist_set(TagList_t *tlist, const gchar *name, const gchar *value)
+{
+ GList *p;
+ Tag_t *tag;
+
+ for (p = tlist->list; p; p = p->next) {
+ tag = (Tag_t*) p->data;
+ if (!g_ascii_strcasecmp(tag->name, name)) {
+ g_strreplace(&tag->value, value);
+ return;
+ }
+ }
+ /* Tag not found, add a new tag */
+ tlist->list = g_list_append(tlist->list, tag_create(name, value));
+}
+
+void
+taglist_write(TagList_t *tlist)
+{
+ GList *p;
+ for (p = tlist->list; p; p = p->next)
+ tag_write((Tag_t*) p->data);
+}
diff --git a/plug-ins/imagemap/imap_taglist.h b/plug-ins/imagemap/imap_taglist.h
new file mode 100644
index 0000000..c68ef51
--- /dev/null
+++ b/plug-ins/imagemap/imap_taglist.h
@@ -0,0 +1,44 @@
+/*
+ * This is a plug-in for GIMP.
+ *
+ * Generates clickable image maps.
+ *
+ * Copyright (C) 1998-1999 Maurits Rijk lpeek.mrijk@consunet.nl
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _IMAP_TAGLIST_H
+#define _IMAP_TAGLIST_H
+
+#include <glib.h>
+
+typedef struct {
+ gchar *name;
+ gchar *value;
+} Tag_t;
+
+typedef struct {
+ GList *list;
+} TagList_t;
+
+TagList_t *taglist_create(void);
+TagList_t *taglist_clone(TagList_t *src);
+TagList_t *taglist_copy(TagList_t *src, TagList_t *des);
+void taglist_destruct(TagList_t *tlist);
+void taglist_set(TagList_t *tlist, const gchar *id, const gchar *value);
+void taglist_write(TagList_t *tlist);
+
+#endif /* _IMAP_TAGLIST_H */
diff --git a/plug-ins/lighting/Makefile.am b/plug-ins/lighting/Makefile.am
new file mode 100644
index 0000000..92c3ce3
--- /dev/null
+++ b/plug-ins/lighting/Makefile.am
@@ -0,0 +1,65 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+lighting_RC = lighting.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+SUBDIRS = images
+
+libexecdir = $(gimpplugindir)/plug-ins/lighting
+
+libexec_PROGRAMS = lighting
+
+lighting_SOURCES = \
+ lighting-apply.c \
+ lighting-apply.h \
+ lighting-image.c \
+ lighting-image.h \
+ lighting-main.c \
+ lighting-main.h \
+ lighting-preview.c \
+ lighting-preview.h \
+ lighting-shade.c \
+ lighting-shade.h \
+ lighting-stock.c \
+ lighting-stock.h \
+ lighting-ui.c \
+ lighting-ui.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(lighting_RC)
diff --git a/plug-ins/lighting/Makefile.in b/plug-ins/lighting/Makefile.in
new file mode 100644
index 0000000..96856de
--- /dev/null
+++ b/plug-ins/lighting/Makefile.in
@@ -0,0 +1,1156 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = lighting$(EXEEXT)
+subdir = plug-ins/lighting
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_lighting_OBJECTS = lighting-apply.$(OBJEXT) \
+ lighting-image.$(OBJEXT) lighting-main.$(OBJEXT) \
+ lighting-preview.$(OBJEXT) lighting-shade.$(OBJEXT) \
+ lighting-stock.$(OBJEXT) lighting-ui.$(OBJEXT)
+lighting_OBJECTS = $(am_lighting_OBJECTS)
+lighting_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+lighting_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(lighting_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/lighting-apply.Po \
+ ./$(DEPDIR)/lighting-image.Po ./$(DEPDIR)/lighting-main.Po \
+ ./$(DEPDIR)/lighting-preview.Po ./$(DEPDIR)/lighting-shade.Po \
+ ./$(DEPDIR)/lighting-stock.Po ./$(DEPDIR)/lighting-ui.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(lighting_SOURCES)
+DIST_SOURCES = $(lighting_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README TODO
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/lighting
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@lighting_RC = lighting.rc.o
+AM_LDFLAGS = $(mwindows)
+SUBDIRS = images
+lighting_SOURCES = \
+ lighting-apply.c \
+ lighting-apply.h \
+ lighting-image.c \
+ lighting-image.h \
+ lighting-main.c \
+ lighting-main.h \
+ lighting-preview.c \
+ lighting-preview.h \
+ lighting-shade.c \
+ lighting-shade.h \
+ lighting-stock.c \
+ lighting-stock.h \
+ lighting-ui.c \
+ lighting-ui.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(lighting_RC)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/lighting/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/lighting/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+lighting$(EXEEXT): $(lighting_OBJECTS) $(lighting_DEPENDENCIES) $(EXTRA_lighting_DEPENDENCIES)
+ @rm -f lighting$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(lighting_OBJECTS) $(lighting_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-apply.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-image.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-shade.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-stock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighting-ui.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/lighting-apply.Po
+ -rm -f ./$(DEPDIR)/lighting-image.Po
+ -rm -f ./$(DEPDIR)/lighting-main.Po
+ -rm -f ./$(DEPDIR)/lighting-preview.Po
+ -rm -f ./$(DEPDIR)/lighting-shade.Po
+ -rm -f ./$(DEPDIR)/lighting-stock.Po
+ -rm -f ./$(DEPDIR)/lighting-ui.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/lighting-apply.Po
+ -rm -f ./$(DEPDIR)/lighting-image.Po
+ -rm -f ./$(DEPDIR)/lighting-main.Po
+ -rm -f ./$(DEPDIR)/lighting-preview.Po
+ -rm -f ./$(DEPDIR)/lighting-shade.Po
+ -rm -f ./$(DEPDIR)/lighting-stock.Po
+ -rm -f ./$(DEPDIR)/lighting-ui.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/lighting/README b/plug-ins/lighting/README
new file mode 100644
index 0000000..d723d61
--- /dev/null
+++ b/plug-ins/lighting/README
@@ -0,0 +1,39 @@
+
+Lighting Effects 0.2.2 -- image filter plug-in for GIMP
+===================================================================
+
+Copyright (C) 1996-98 Tom Bech
+Copyright (C) 1996-98 Federico Mena Quintero
+
+You can reach the author(s) via E-mail:
+tomb@gimp.org (Tom) or quartic@gimp.org (Federico).
+
+GIMP was developed by Peter Mattis and Spencer Kimball.
+You can contact them at gimp@xcf.berkeley.edu.
+
+There's more GIMP stuff on our home pages:
+http://www.ii.uib.no/~tomb/gimp.html (Tom's page)
+http://www.nuclecu.unam.mx/~federico/gimp/index.html (Quartic's page)
+
+
+Legal stuff
+===========
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+In other words, you can't sue us for whatever happens while using this ;)
+
+Have fun,
+
+Tom
diff --git a/plug-ins/lighting/TODO b/plug-ins/lighting/TODO
new file mode 100644
index 0000000..06609de
--- /dev/null
+++ b/plug-ins/lighting/TODO
@@ -0,0 +1,15 @@
+
+The lighting plug-in "todo"-list:
+=================================
+
+* Supersampling/antialiasing
+* Bilinear filtering of environment map
+* Refraction back in?
+* Support for any-sized non-gray bump-maps.
+* Support for gray or alpha-channeled env-maps.
+* Zooming and scrolling in preview window
+* Autoconf/automake stuff
+* Put spotlight back in
+* Nicer interactive UI
+* Presets
+* Multiple light sources
diff --git a/plug-ins/lighting/images/Makefile.am b/plug-ins/lighting/images/Makefile.am
new file mode 100644
index 0000000..55dfbde
--- /dev/null
+++ b/plug-ins/lighting/images/Makefile.am
@@ -0,0 +1,29 @@
+## Process this file with automake to produce Makefile.in
+
+STOCK_IMAGES = \
+ stock-intensity-ambient-high.png \
+ stock-intensity-ambient-low.png \
+ stock-intensity-diffuse-high.png \
+ stock-intensity-diffuse-low.png \
+ stock-reflectivity-diffuse-high.png \
+ stock-reflectivity-diffuse-low.png \
+ stock-reflectivity-specular-high.png \
+ stock-reflectivity-specular-low.png \
+ stock-reflectivity-highlight-high.png \
+ stock-reflectivity-highlight-low.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+
+noinst_DATA = stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
diff --git a/plug-ins/lighting/images/Makefile.in b/plug-ins/lighting/images/Makefile.in
new file mode 100644
index 0000000..0f01c96
--- /dev/null
+++ b/plug-ins/lighting/images/Makefile.in
@@ -0,0 +1,775 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/lighting/images
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(noinst_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+STOCK_IMAGES = \
+ stock-intensity-ambient-high.png \
+ stock-intensity-ambient-low.png \
+ stock-intensity-diffuse-high.png \
+ stock-intensity-diffuse-low.png \
+ stock-reflectivity-diffuse-high.png \
+ stock-reflectivity-diffuse-low.png \
+ stock-reflectivity-specular-high.png \
+ stock-reflectivity-specular-low.png \
+ stock-reflectivity-highlight-high.png \
+ stock-reflectivity-highlight-low.png
+
+EXTRA_DIST = $(STOCK_IMAGES)
+noinst_DATA = stock-pixbufs.h
+CLEANFILES = $(noinst_DATA) stock-icons.list
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/lighting/images/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/lighting/images/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+stock-icons.list: $(STOCK_IMAGES) Makefile.am
+ ( rm -f $@; \
+ for image in $(STOCK_IMAGES); do \
+ echo $$image | \
+ sed -e 's|.*/||' -e 's|-|_|g' -e 's|\.png$$||' >> $@; \
+ echo " $(srcdir)/$$image" >> $@; \
+ done )
+
+$(srcdir)/stock-pixbufs.h: stock-icons.list
+ $(GDK_PIXBUF_CSOURCE) --raw --build-list `cat stock-icons.list` > $(@F)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/lighting/images/stock-intensity-ambient-high.png b/plug-ins/lighting/images/stock-intensity-ambient-high.png
new file mode 100644
index 0000000..cb886ec
--- /dev/null
+++ b/plug-ins/lighting/images/stock-intensity-ambient-high.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-intensity-ambient-low.png b/plug-ins/lighting/images/stock-intensity-ambient-low.png
new file mode 100644
index 0000000..fff7b45
--- /dev/null
+++ b/plug-ins/lighting/images/stock-intensity-ambient-low.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-intensity-diffuse-high.png b/plug-ins/lighting/images/stock-intensity-diffuse-high.png
new file mode 100644
index 0000000..628ec4d
--- /dev/null
+++ b/plug-ins/lighting/images/stock-intensity-diffuse-high.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-intensity-diffuse-low.png b/plug-ins/lighting/images/stock-intensity-diffuse-low.png
new file mode 100644
index 0000000..49e02bf
--- /dev/null
+++ b/plug-ins/lighting/images/stock-intensity-diffuse-low.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-diffuse-high.png b/plug-ins/lighting/images/stock-reflectivity-diffuse-high.png
new file mode 100644
index 0000000..88cc4fa
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-diffuse-high.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-diffuse-low.png b/plug-ins/lighting/images/stock-reflectivity-diffuse-low.png
new file mode 100644
index 0000000..6bdb5c4
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-diffuse-low.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-highlight-high.png b/plug-ins/lighting/images/stock-reflectivity-highlight-high.png
new file mode 100644
index 0000000..b43724b
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-highlight-high.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-highlight-low.png b/plug-ins/lighting/images/stock-reflectivity-highlight-low.png
new file mode 100644
index 0000000..88cc4fa
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-highlight-low.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-specular-high.png b/plug-ins/lighting/images/stock-reflectivity-specular-high.png
new file mode 100644
index 0000000..fbd5f54
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-specular-high.png
Binary files differ
diff --git a/plug-ins/lighting/images/stock-reflectivity-specular-low.png b/plug-ins/lighting/images/stock-reflectivity-specular-low.png
new file mode 100644
index 0000000..2d69d41
--- /dev/null
+++ b/plug-ins/lighting/images/stock-reflectivity-specular-low.png
Binary files differ
diff --git a/plug-ins/lighting/lighting-apply.c b/plug-ins/lighting/lighting-apply.c
new file mode 100644
index 0000000..500ed24
--- /dev/null
+++ b/plug-ins/lighting/lighting-apply.c
@@ -0,0 +1,154 @@
+/******************************************************/
+/* Apply mapping and shading on the whole input image */
+/******************************************************/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <libgimp/gimp.h>
+
+#include "lighting-main.h"
+#include "lighting-image.h"
+#include "lighting-shade.h"
+#include "lighting-apply.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*************/
+/* Main loop */
+/*************/
+
+void
+compute_image (void)
+{
+ gint xcount, ycount;
+ GimpRGB color;
+ glong progress_counter = 0;
+ GimpVector3 p;
+ gint32 new_image_id = -1;
+ gint32 new_layer_id = -1;
+ gint32 index;
+ guchar *row = NULL;
+ guchar obpp;
+ gboolean has_alpha;
+ get_ray_func ray_func;
+
+ if (mapvals.create_new_image == TRUE ||
+ (mapvals.transparent_background == TRUE &&
+ ! gimp_drawable_has_alpha (input_drawable_id)))
+ {
+ /* Create a new image */
+ /* ================== */
+
+ new_image_id = gimp_image_new (width, height, GIMP_RGB);
+
+ if (mapvals.transparent_background == TRUE)
+ {
+ /* Add a layer with an alpha channel */
+ /* ================================= */
+
+ new_layer_id = gimp_layer_new (new_image_id, "Background",
+ width, height,
+ GIMP_RGBA_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+ }
+ else
+ {
+ /* Create a "normal" layer */
+ /* ======================= */
+
+ new_layer_id = gimp_layer_new (new_image_id, "Background",
+ width, height,
+ GIMP_RGB_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+ }
+
+ gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
+ output_drawable_id = new_layer_id;
+ }
+
+ if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
+ {
+ bumpmap_setup (mapvals.bumpmap_id);
+ }
+
+ precompute_init (width, height);
+
+ if (! mapvals.env_mapped || mapvals.envmap_id == -1)
+ {
+ ray_func = get_ray_color;
+ }
+ else
+ {
+ envmap_setup (mapvals.envmap_id);
+
+ ray_func = get_ray_color_ref;
+ }
+
+ dest_buffer = gimp_drawable_get_shadow_buffer (output_drawable_id);
+
+ has_alpha = gimp_drawable_has_alpha (output_drawable_id);
+
+ /* FIXME */
+ obpp = has_alpha ? 4 : 3; //gimp_drawable_bpp (output_drawable_id);
+
+ row = g_new (guchar, obpp * width);
+
+ gimp_progress_init (_("Lighting Effects"));
+
+ /* Init the first row */
+ if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1 && height >= 2)
+ interpol_row (0, width, 0);
+
+ for (ycount = 0; ycount < height; ycount++)
+ {
+ if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
+ precompute_normals (0, width, ycount);
+
+ index = 0;
+
+ for (xcount = 0; xcount < width; xcount++)
+ {
+ p = int_to_pos (xcount, ycount);
+ color = (* ray_func) (&p);
+
+ row[index++] = (guchar) (color.r * 255.0);
+ row[index++] = (guchar) (color.g * 255.0);
+ row[index++] = (guchar) (color.b * 255.0);
+
+ if (has_alpha)
+ row[index++] = (guchar) (color.a * 255.0);
+
+ progress_counter++;
+ }
+
+ gimp_progress_update ((gdouble) progress_counter /
+ (gdouble) maxcounter);
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (0, ycount, width, 1), 0,
+ has_alpha ?
+ babl_format ("R'G'B'A u8") : babl_format ("R'G'B' u8"),
+ row,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ gimp_progress_update (1.0);
+
+ g_free (row);
+
+ g_object_unref (dest_buffer);
+
+ gimp_drawable_merge_shadow (output_drawable_id, TRUE);
+ gimp_drawable_update (output_drawable_id, 0, 0, width, height);
+
+ if (new_image_id!=-1)
+ {
+ gimp_display_new (new_image_id);
+ gimp_displays_flush ();
+ }
+
+}
diff --git a/plug-ins/lighting/lighting-apply.h b/plug-ins/lighting/lighting-apply.h
new file mode 100644
index 0000000..304ef4a
--- /dev/null
+++ b/plug-ins/lighting/lighting-apply.h
@@ -0,0 +1,7 @@
+#ifndef __LIGHTING_APPLY_H__
+#define __LIGHTING_APPLY_H__
+
+void init_compute (void);
+void compute_image (void);
+
+#endif /* __LIGHTING_APPLY_H__ */
diff --git a/plug-ins/lighting/lighting-image.c b/plug-ins/lighting/lighting-image.c
new file mode 100644
index 0000000..671176f
--- /dev/null
+++ b/plug-ins/lighting/lighting-image.c
@@ -0,0 +1,410 @@
+/*************************************/
+/* GIMP image manipulation routines. */
+/*************************************/
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "lighting-main.h"
+#include "lighting-image.h"
+#include "lighting-preview.h"
+#include "lighting-ui.h"
+
+
+gint32 input_drawable_id;
+gint32 output_drawable_id;
+GeglBuffer *source_buffer;
+GeglBuffer *dest_buffer;
+
+gint32 bump_drawable_id;
+GeglBuffer *bump_buffer;
+const Babl *bump_format;
+
+gint32 env_drawable_id;
+GeglBuffer *env_buffer;
+
+guchar *preview_rgb_data = NULL;
+gint preview_rgb_stride;
+cairo_surface_t *preview_surface = NULL;
+
+glong maxcounter;
+gint width, height;
+gint env_width, env_height;
+GimpRGB background;
+
+gint border_x1, border_y1, border_x2, border_y2;
+
+guchar sinemap[256], spheremap[256], logmap[256];
+
+/******************/
+/* Implementation */
+/******************/
+
+guchar
+peek_map (GeglBuffer *buffer,
+ const Babl *format,
+ gint x,
+ gint y)
+{
+ guchar data[4];
+ guchar ret_val;
+
+ gegl_buffer_sample (buffer, x, y, NULL, data, format,
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (babl_format_get_bytes_per_pixel (format))
+ {
+ ret_val = data[0];
+ }
+ else
+ {
+ ret_val = (guchar)((float)((data[0] + data[1] + data[2])/3.0));
+ }
+
+ return ret_val;
+}
+
+GimpRGB
+peek (gint x,
+ gint y)
+{
+ GimpRGB color;
+
+ gegl_buffer_sample (source_buffer, x, y, NULL,
+ &color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (! babl_format_has_alpha (gegl_buffer_get_format (source_buffer)))
+ color.a = 1.0;
+
+ return color;
+}
+
+GimpRGB
+peek_env_map (gint x,
+ gint y)
+{
+ GimpRGB color;
+
+ if (x < 0)
+ x = 0;
+ else if (x >= env_width)
+ x = env_width - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= env_height)
+ y = env_height - 1;
+
+ gegl_buffer_sample (env_buffer, x, y, NULL,
+ &color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ color.a = 1.0;
+
+ return color;
+}
+
+void
+poke (gint x,
+ gint y,
+ GimpRGB *color)
+{
+ if (x < 0)
+ x = 0;
+ else if (x >= width)
+ x = width - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= height)
+ y = height - 1;
+
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ babl_format ("R'G'B'A double"), color,
+ GEGL_AUTO_ROWSTRIDE);
+}
+
+gint
+check_bounds (gint x,
+ gint y)
+{
+ if (x < border_x1 ||
+ y < border_y1 ||
+ x >= border_x2 ||
+ y >= border_y2)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+GimpVector3
+int_to_pos (gint x,
+ gint y)
+{
+ GimpVector3 pos;
+
+ if (width >= height)
+ {
+ pos.x = (gdouble) x / (gdouble) width;
+ pos.y = (gdouble) y / (gdouble) width;
+
+ pos.y += 0.5 * (1.0 - (gdouble) height / (gdouble) width);
+ }
+ else
+ {
+ pos.x = (gdouble) x / (gdouble) height;
+ pos.y = (gdouble) y / (gdouble) height;
+
+ pos.x += 0.5 * (1.0 - (gdouble) width / (gdouble) height);
+ }
+
+ pos.z = 0.0;
+ return pos;
+}
+
+GimpVector3
+int_to_posf (gdouble x,
+ gdouble y)
+{
+ GimpVector3 pos;
+
+ if (width >= height)
+ {
+ pos.x = x / (gdouble) width;
+ pos.y = y / (gdouble) width;
+
+ pos.y += 0.5 * (1.0 - (gdouble) height / (gdouble) width);
+ }
+ else
+ {
+ pos.x = x / (gdouble) height;
+ pos.y = y / (gdouble) height;
+
+ pos.x += 0.5 * (1.0 - (gdouble) width / (gdouble) height);
+ }
+
+ pos.z = 0.0;
+ return pos;
+}
+
+void
+pos_to_int (gdouble x,
+ gdouble y,
+ gint *scr_x,
+ gint *scr_y)
+{
+ if (width >= height)
+ {
+ y -= 0.5 * (1.0 - (gdouble) height / (gdouble) width);
+ *scr_x = RINT ((x * (gdouble) width));
+ *scr_y = RINT ((y * (gdouble) width));
+ }
+ else
+ {
+ x -= 0.5 * (1.0 - (gdouble) width / (gdouble) height);
+
+ *scr_x = RINT ((x * (gdouble) height));
+ *scr_y = RINT ((y *(gdouble) height));
+ }
+}
+
+void
+pos_to_float (gdouble x,
+ gdouble y,
+ gdouble *xf,
+ gdouble *yf)
+{
+ if (width >= height)
+ {
+ y -= 0.5 * (1.0 - (gdouble) height / (gdouble) width);
+
+ *xf = x * (gdouble) (width-1);
+ *yf = y * (gdouble) (width-1);
+ }
+ else
+ {
+ x -= 0.5 * (1.0 - (gdouble) width / (gdouble) height);
+
+ *xf = x * (gdouble) (height-1);
+ *yf = y * (gdouble) (height-1);
+ }
+}
+
+/**********************************************/
+/* Compute the image color at pos (u,v) using */
+/* Quartics bilinear interpolation stuff. */
+/**********************************************/
+
+GimpRGB
+get_image_color (gdouble u,
+ gdouble v,
+ gint *inside)
+{
+ gint x1, y1, x2, y2;
+ GimpRGB p[4];
+
+ x1 = RINT (u);
+ y1 = RINT (v);
+
+ if (check_bounds (x1, y1) == FALSE)
+ {
+ *inside = FALSE;
+ return background;
+ }
+
+ x2 = (x1 + 1);
+ y2 = (y1 + 1);
+
+ if (check_bounds (x2, y2) == FALSE)
+ {
+ *inside = TRUE;
+ return peek (x1, y1);
+ }
+
+ *inside = TRUE;
+ p[0] = peek (x1, y1);
+ p[1] = peek (x2, y1);
+ p[2] = peek (x1, y2);
+ p[3] = peek (x2, y2);
+
+ return gimp_bilinear_rgba (u, v, p);
+}
+
+gdouble
+get_map_value (GeglBuffer *buffer,
+ const Babl *format,
+ gdouble u,
+ gdouble v,
+ gint *inside)
+{
+ gint x1, y1, x2, y2;
+ gdouble p[4];
+
+ x1 = RINT (u);
+ y1 = RINT (v);
+
+ x2 = (x1 + 1);
+ y2 = (y1 + 1);
+
+ if (check_bounds (x2, y2) == FALSE)
+ {
+ *inside = TRUE;
+ return (gdouble) peek_map (buffer, format, x1, y1);
+ }
+
+ *inside = TRUE;
+ p[0] = (gdouble) peek_map (buffer, format, x1, y1);
+ p[1] = (gdouble) peek_map (buffer, format, x2, y1);
+ p[2] = (gdouble) peek_map (buffer, format, x1, y2);
+ p[3] = (gdouble) peek_map (buffer, format, x2, y2);
+
+ return gimp_bilinear (u, v, p);
+}
+
+static void
+compute_maps (void)
+{
+ gint x;
+ gdouble val, c, d;
+
+ /* Compute Sine, Log and Spherical transfer function maps */
+ /* ====================================================== */
+
+ c = 1.0 / 255.0;
+ d = 1.15 * 255.0;
+
+ for (x = 0; x < 256; x++)
+ {
+ sinemap[x] = (guchar) (255.0 * (0.5 * (sin ((G_PI * c * (gdouble) x) -
+ 0.5 * G_PI) +
+ 1.0)));
+ spheremap[x] = (guchar) (255.0 * (sqrt (sin (G_PI * (gdouble) x /
+ 512.0))));
+ val = (d * exp (-1.0 / (8.0 * c * ((gdouble) x + 5.0))));
+
+ if (val > 255.0)
+ val = 255.0;
+ logmap[x] = (guchar) val;
+ }
+}
+
+/****************************************/
+/* Allocate memory for temporary images */
+/****************************************/
+
+gint
+image_setup (gint32 drawable_id,
+ gint interactive)
+{
+ gint w, h;
+ gboolean ret;
+
+ compute_maps ();
+
+ /* Get some useful info on the input drawable */
+ /* ========================================== */
+
+ input_drawable_id = drawable_id;
+ output_drawable_id = drawable_id;
+
+ ret = gimp_drawable_mask_intersect (drawable_id,
+ &border_x1, &border_y1, &w, &h);
+
+ border_x2 = border_x1 + w;
+ border_y2 = border_y1 + h;
+
+ if (! ret)
+ return FALSE;
+
+ width = gimp_drawable_width (input_drawable_id);
+ height = gimp_drawable_height (input_drawable_id);
+
+ source_buffer = gimp_drawable_get_buffer (input_drawable_id);
+
+ maxcounter = (glong) width * (glong) height;
+
+ if (interactive)
+ {
+ preview_rgb_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24,
+ PREVIEW_WIDTH);
+ preview_rgb_data = g_new0 (guchar, preview_rgb_stride * PREVIEW_HEIGHT);
+ preview_surface = cairo_image_surface_create_for_data (preview_rgb_data,
+ CAIRO_FORMAT_RGB24,
+ PREVIEW_WIDTH,
+ PREVIEW_HEIGHT,
+ preview_rgb_stride);
+ }
+
+ return TRUE;
+}
+
+void
+bumpmap_setup (gint32 bumpmap_id)
+{
+ if (bumpmap_id != -1)
+ {
+ if (! bump_buffer)
+ {
+ bump_buffer = gimp_drawable_get_buffer (bumpmap_id);
+ }
+
+ if (gimp_drawable_is_rgb (bumpmap_id))
+ bump_format = babl_format ("R'G'B' u8");
+ else
+ bump_format = babl_format ("Y' u8"); /* FIXME */
+ }
+}
+
+void
+envmap_setup (gint32 envmap_id)
+{
+ if (envmap_id != -1 && ! env_buffer)
+ {
+ env_width = gimp_drawable_width (envmap_id);
+ env_height = gimp_drawable_height (envmap_id);
+
+ env_buffer = gimp_drawable_get_buffer (envmap_id);
+ }
+}
diff --git a/plug-ins/lighting/lighting-image.h b/plug-ins/lighting/lighting-image.h
new file mode 100644
index 0000000..817c948
--- /dev/null
+++ b/plug-ins/lighting/lighting-image.h
@@ -0,0 +1,69 @@
+#ifndef __LIGHTING_IMAGE_H__
+#define __LIGHTING_IMAGE_H__
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+extern gint32 input_drawable_id;
+extern gint32 output_drawable_id;
+extern GeglBuffer *source_buffer;
+extern GeglBuffer *dest_buffer;
+
+extern gint32 bump_drawable_id;
+extern GeglBuffer *bump_buffer;
+extern const Babl *bump_format;
+
+extern gint32 env_drawable_id;
+extern GeglBuffer *env_buffer;
+
+extern guchar *preview_rgb_data;
+extern gint preview_rgb_stride;
+extern cairo_surface_t *preview_surface;
+
+extern glong maxcounter;
+extern gint width,height,env_width,env_height;
+extern GimpRGB background;
+
+extern gint border_x1, border_y1, border_x2, border_y2;
+
+extern guchar sinemap[256], spheremap[256], logmap[256];
+
+guchar peek_map (GeglBuffer *buffer,
+ const Babl *format,
+ gint x,
+ gint y);
+GimpRGB peek (gint x,
+ gint y);
+GimpRGB peek_env_map (gint x,
+ gint y);
+void poke (gint x,
+ gint y,
+ GimpRGB *color);
+gint check_bounds (gint x,
+ gint y);
+GimpVector3 int_to_pos (gint x,
+ gint y);
+GimpVector3 int_to_posf (gdouble x,
+ gdouble y);
+void pos_to_int (gdouble x,
+ gdouble y,
+ gint *scr_x,
+ gint *scr_y);
+void pos_to_float (gdouble x,
+ gdouble y,
+ gdouble *xf,
+ gdouble *yf);
+GimpRGB get_image_color (gdouble u,
+ gdouble v,
+ gint *inside);
+gdouble get_map_value (GeglBuffer *buffer,
+ const Babl *format,
+ gdouble u,
+ gdouble v,
+ gint *inside);
+gint image_setup (gint32 drawable_id,
+ gint interactive);
+void bumpmap_setup (gint32 bumpmap_id);
+void envmap_setup (gint32 envmap_id);
+
+#endif /* __LIGHTING_IMAGE_H__ */
diff --git a/plug-ins/lighting/lighting-main.c b/plug-ins/lighting/lighting-main.c
new file mode 100644
index 0000000..f732b3c
--- /dev/null
+++ b/plug-ins/lighting/lighting-main.c
@@ -0,0 +1,330 @@
+/* Lighting Effects 0.2.2 -- image filter plug-in for GIMP
+ *
+ * Copyright (C) 1996-98 Tom Bech
+ * Copyright (C) 1996-98 Federico Mena Quintero
+ *
+ * E-mail: tomb@gimp.org (Tom) or quartic@gimp.org (Federico)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+
+#include "lighting-apply.h"
+#include "lighting-image.h"
+#include "lighting-main.h"
+#include "lighting-preview.h"
+#include "lighting-shade.h"
+#include "lighting-ui.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+LightingValues mapvals;
+
+/******************/
+/* Implementation */
+/******************/
+
+/*************************************/
+/* Set parameters to standard values */
+/*************************************/
+
+static void
+set_default_settings (void)
+{
+ gint k;
+
+ mapvals.update_enabled = TRUE;
+ mapvals.light_selected = 0;
+ mapvals.light_isolated = FALSE;
+
+ gimp_vector3_set (&mapvals.viewpoint, 0.5, 0.5, 0.25);
+ gimp_vector3_set (&mapvals.planenormal, 0.0, 0.0, 1.0);
+
+ gimp_vector3_set (&mapvals.lightsource[0].position, -1.0, -1.0, 1.0);
+ gimp_vector3_set (&mapvals.lightsource[0].direction, -1.0, -1.0, 1.0);
+
+ gimp_rgba_set (&mapvals.lightsource[0].color, 1.0, 1.0, 1.0, 1.0);
+ mapvals.lightsource[0].intensity = 1.0;
+ mapvals.lightsource[0].type = POINT_LIGHT;
+ mapvals.lightsource[0].active = TRUE;
+
+ /* init lights 2 and 3 pos to upper left and below */
+ gimp_vector3_set (&mapvals.lightsource[1].position, 2.0, -1.0, 1.0);
+ gimp_vector3_set (&mapvals.lightsource[1].direction, 1.0, -1.0, 1.0);
+
+ gimp_vector3_set (&mapvals.lightsource[2].position, 1.0, 2.0, 1.0);
+ gimp_vector3_set (&mapvals.lightsource[2].direction, 0.0, 1.0, 1.0);
+
+ /* init any remaining lights to directly overhead */
+ for (k = 3; k < NUM_LIGHTS; k++)
+ {
+ gimp_vector3_set (&mapvals.lightsource[k].position, 0.0, 0.0, 1.0);
+ gimp_vector3_set (&mapvals.lightsource[k].direction, 0.0, 0.0, 1.0);
+ }
+
+ for (k = 1; k < NUM_LIGHTS; k++)
+ {
+ gimp_rgba_set (&mapvals.lightsource[k].color, 1.0, 1.0, 1.0, 1.0);
+ mapvals.lightsource[k].intensity = 1.0;
+ mapvals.lightsource[k].type = NO_LIGHT;
+ mapvals.lightsource[k].active = TRUE;
+ }
+
+ mapvals.material.ambient_int = 0.2;
+ mapvals.material.diffuse_int = 0.5;
+ mapvals.material.diffuse_ref = 0.4;
+ mapvals.material.specular_ref = 0.5;
+ mapvals.material.highlight = 27.0;
+ mapvals.material.metallic = FALSE;
+
+ mapvals.pixel_threshold = 0.25;
+ mapvals.max_depth = 3.0;
+ mapvals.preview_zoom_factor = 1.0;
+
+ mapvals.bumpmaptype = 0;
+ mapvals.bumpmin = 0.0;
+ mapvals.bumpmax = 0.1;
+
+ mapvals.antialiasing = FALSE;
+ mapvals.create_new_image = FALSE;
+ mapvals.transparent_background = FALSE;
+ mapvals.bump_mapped = FALSE;
+ mapvals.env_mapped = FALSE;
+ mapvals.ref_mapped = FALSE;
+ mapvals.previewquality = FALSE;
+ mapvals.interactive_preview = TRUE;
+
+ mapvals.bumpmap_id = -1;
+ mapvals.envmap_id = -1;
+}
+
+static void
+check_drawables (void)
+{
+ if (mapvals.bump_mapped)
+ {
+ if (mapvals.bumpmap_id != -1 &&
+ gimp_item_get_image (mapvals.bumpmap_id) == -1)
+ {
+ mapvals.bump_mapped = FALSE;
+ mapvals.bumpmap_id = -1;
+ }
+
+ if (gimp_drawable_is_indexed (mapvals.bumpmap_id) ||
+ (gimp_drawable_width (mapvals.drawable_id) !=
+ gimp_drawable_width (mapvals.bumpmap_id)) ||
+ (gimp_drawable_height (mapvals.drawable_id) !=
+ gimp_drawable_height (mapvals.bumpmap_id)))
+ {
+ mapvals.bump_mapped = FALSE;
+ mapvals.bumpmap_id = -1;
+ }
+ }
+
+ if (mapvals.env_mapped)
+ {
+ if (mapvals.envmap_id != -1 &&
+ gimp_item_get_image (mapvals.envmap_id) == -1)
+ {
+ mapvals.env_mapped = FALSE;
+ mapvals.envmap_id = -1;
+ }
+
+ if (gimp_drawable_is_gray (mapvals.envmap_id) ||
+ gimp_drawable_has_alpha (mapvals.envmap_id))
+ {
+ mapvals.env_mapped = FALSE;
+ mapvals.envmap_id = -1;
+ }
+ }
+}
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_DRAWABLE, "bumpdrawable", "Bumpmap drawable (set to 0 if disabled)" },
+ { GIMP_PDB_DRAWABLE, "envdrawable", "Environmentmap drawable (set to 0 if disabled)" },
+ { GIMP_PDB_INT32, "dobumpmap", "Enable bumpmapping (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "doenvmap", "Enable envmapping (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "bumpmaptype", "Type of mapping (0=linear,1=log, 2=sinusoidal, 3=spherical)" },
+ { GIMP_PDB_INT32, "lighttype", "Type of lightsource (0=point,1=directional,3=spot,4=none)" },
+ { GIMP_PDB_COLOR, "lightcolor", "Lightsource color (r,g,b)" },
+ { GIMP_PDB_FLOAT, "lightposition-x", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightposition-y", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightposition-z", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightdirection-x", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "lightdirection-y", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "lightdirection-z", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "ambient-intensity", "Material ambient intensity (0..1)" },
+ { GIMP_PDB_FLOAT, "diffuse-intensity", "Material diffuse intensity (0..1)" },
+ { GIMP_PDB_FLOAT, "diffuse-reflectivity", "Material diffuse reflectivity (0..1)" },
+ { GIMP_PDB_FLOAT, "specular-reflectivity", "Material specular reflectivity (0..1)" },
+ { GIMP_PDB_FLOAT, "highlight", "Material highlight (0..->), note: it's exponential" },
+ { GIMP_PDB_INT32, "antialiasing", "Apply antialiasing (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "newimage", "Create a new image (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "transparentbackground", "Make background transparent (TRUE/FALSE)" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Apply various lighting effects to an image"),
+ "No help yet",
+ "Tom Bech & Federico Mena Quintero",
+ "Tom Bech & Federico Mena Quintero",
+ "Version 0.2.0, March 15 1998",
+ N_("_Lighting Effects..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC,
+ "<Image>/Filters/Light and Shadow/Light");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ /* Set default values */
+ /* ================== */
+
+ set_default_settings ();
+
+ /* Possibly retrieve data */
+ /* ====================== */
+
+ gimp_get_data (PLUG_IN_PROC, &mapvals);
+
+ /* Get the specified drawable */
+ /* ========================== */
+
+ run_mode = param[0].data.d_int32;
+ drawable_id = param[2].data.d_drawable;
+
+ mapvals.drawable_id = drawable_id;
+
+ check_drawables ();
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* Make sure that the drawable is RGBA or RGB color */
+ /* ================================================ */
+
+ if (gimp_drawable_is_rgb (drawable_id))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (main_dialog (drawable_id))
+ {
+ compute_image ();
+
+ gimp_set_data (PLUG_IN_PROC,
+ &mapvals, sizeof (LightingValues));
+ gimp_displays_flush ();
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ if (image_setup (drawable_id, FALSE))
+ compute_image ();
+ gimp_displays_flush ();
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 24)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ mapvals.bumpmap_id = param[3].data.d_drawable;
+ mapvals.envmap_id = param[4].data.d_drawable;
+ mapvals.bump_mapped = (gint) param[5].data.d_int32;
+ mapvals.env_mapped = (gint) param[6].data.d_int32;
+ mapvals.bumpmaptype = (gint) param[7].data.d_int32;
+ mapvals.lightsource[0].type = (LightType) param[8].data.d_int32;
+ mapvals.lightsource[0].color = param[9].data.d_color;
+ mapvals.lightsource[0].position.x = param[10].data.d_float;
+ mapvals.lightsource[0].position.y = param[11].data.d_float;
+ mapvals.lightsource[0].position.z = param[12].data.d_float;
+ mapvals.lightsource[0].direction.x = param[13].data.d_float;
+ mapvals.lightsource[0].direction.y = param[14].data.d_float;
+ mapvals.lightsource[0].direction.z = param[15].data.d_float;
+ mapvals.material.ambient_int = param[16].data.d_float;
+ mapvals.material.diffuse_int = param[17].data.d_float;
+ mapvals.material.diffuse_ref = param[18].data.d_float;
+ mapvals.material.specular_ref = param[19].data.d_float;
+ mapvals.material.highlight = param[20].data.d_float;
+ mapvals.antialiasing = (gint) param[21].data.d_int32;
+ mapvals.create_new_image = (gint) param[22].data.d_int32;
+ mapvals.transparent_background = (gint) param[23].data.d_int32;
+
+ check_drawables ();
+ if (image_setup (drawable_id, FALSE))
+ compute_image ();
+ }
+ default:
+ break;
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ values[0].data.d_status = status;
+
+ g_free (xpostab);
+ g_free (ypostab);
+}
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
diff --git a/plug-ins/lighting/lighting-main.h b/plug-ins/lighting/lighting-main.h
new file mode 100644
index 0000000..f547b2d
--- /dev/null
+++ b/plug-ins/lighting/lighting-main.h
@@ -0,0 +1,104 @@
+#ifndef __LIGHTING_MAIN_H__
+#define __LIGHTING_MAIN_H__
+
+/* Defines and stuff */
+/* ================= */
+
+#define PLUG_IN_PROC "plug-in-lighting"
+#define PLUG_IN_BINARY "lighting"
+#define PLUG_IN_ROLE "gimp-lighting"
+
+#define TILE_CACHE_SIZE 16
+#define NUM_LIGHTS 6
+
+/* Typedefs */
+/* ======== */
+
+typedef enum
+{
+ POINT_LIGHT,
+ DIRECTIONAL_LIGHT,
+ SPOT_LIGHT,
+ NO_LIGHT
+} LightType;
+
+enum
+{
+ LINEAR_MAP,
+ LOGARITHMIC_MAP,
+ SINUSOIDAL_MAP,
+ SPHERICAL_MAP
+};
+
+enum
+{
+ IMAGE_BUMP,
+ WAVES_BUMP
+};
+
+typedef struct
+{
+ gdouble ambient_int;
+ gdouble diffuse_int;
+ gdouble diffuse_ref;
+ gdouble specular_ref;
+ gdouble highlight;
+ gboolean metallic;
+ GimpRGB color;
+} MaterialSettings;
+
+typedef struct
+{
+ LightType type;
+ GimpVector3 position;
+ GimpVector3 direction;
+ GimpRGB color;
+ gdouble intensity;
+ gboolean active;
+} LightSettings;
+
+typedef struct
+{
+ gint32 drawable_id;
+ gint32 bumpmap_id;
+ gint32 envmap_id;
+
+ /* Render variables */
+ /* ================ */
+
+ GimpVector3 viewpoint;
+ GimpVector3 planenormal;
+ LightSettings lightsource[NUM_LIGHTS];
+ MaterialSettings material;
+ MaterialSettings ref_material;
+
+ gdouble pixel_threshold;
+ gdouble bumpmax,bumpmin;
+ gint max_depth;
+ gint bumpmaptype;
+
+ /* Flags */
+ gint antialiasing;
+ gint create_new_image;
+ gint transparent_background;
+ gint bump_mapped;
+ gint env_mapped;
+ gint ref_mapped;
+ gint bumpstretch;
+ gint previewquality;
+ gboolean symbols;
+ gboolean interactive_preview;
+
+ /* Misc */
+ gboolean update_enabled;
+ gint light_selected;
+ gboolean light_isolated;
+ gdouble preview_zoom_factor;
+} LightingValues;
+
+/* Externally visible variables */
+/* ============================ */
+
+extern LightingValues mapvals;
+
+#endif /* __LIGHTING_MAIN_H__ */
diff --git a/plug-ins/lighting/lighting-preview.c b/plug-ins/lighting/lighting-preview.c
new file mode 100644
index 0000000..e4bbd26
--- /dev/null
+++ b/plug-ins/lighting/lighting-preview.c
@@ -0,0 +1,496 @@
+/*************************************************/
+/* Compute a preview image and preview wireframe */
+/*************************************************/
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimpmath/gimpmath.h>
+
+#include "lighting-main.h"
+#include "lighting-ui.h"
+#include "lighting-image.h"
+#include "lighting-apply.h"
+#include "lighting-shade.h"
+
+#include "lighting-preview.h"
+
+
+#define LIGHT_SYMBOL_SIZE 8
+
+static gint handle_xpos = 0, handle_ypos = 0;
+
+/* g_free()'ed on exit */
+gdouble *xpostab = NULL;
+gdouble *ypostab = NULL;
+
+static gint xpostab_size = -1; /* if preview size change, do realloc */
+static gint ypostab_size = -1;
+
+static gboolean light_hit = FALSE;
+static gboolean left_button_pressed = FALSE;
+static guint preview_update_timer = 0;
+
+
+/* Protos */
+/* ====== */
+static gboolean
+interactive_preview_timer_callback ( gpointer data );
+
+static void
+compute_preview (gint startx, gint starty, gint w, gint h)
+{
+ gint xcnt, ycnt, f1, f2;
+ guchar r, g, b;
+ gdouble imagex, imagey;
+ gint32 index = 0;
+ GimpRGB color;
+ GimpRGB lightcheck, darkcheck;
+ GimpVector3 pos;
+ get_ray_func ray_func;
+
+ if (xpostab_size != w)
+ {
+ if (xpostab)
+ {
+ g_free (xpostab);
+ xpostab = NULL;
+ }
+ }
+
+ if (!xpostab)
+ {
+ xpostab = g_new (gdouble, w);
+ xpostab_size = w;
+ }
+
+ if (ypostab_size != h)
+ {
+ if (ypostab)
+ {
+ g_free (ypostab);
+ ypostab = NULL;
+ }
+ }
+
+ if (!ypostab)
+ {
+ ypostab = g_new (gdouble, h);
+ ypostab_size = h;
+ }
+
+ for (xcnt = 0; xcnt < w; xcnt++)
+ xpostab[xcnt] = (gdouble) width *((gdouble) xcnt / (gdouble) w);
+ for (ycnt = 0; ycnt < h; ycnt++)
+ ypostab[ycnt] = (gdouble) height *((gdouble) ycnt / (gdouble) h);
+
+ precompute_init (width, height);
+
+ gimp_rgba_set (&lightcheck,
+ GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT,
+ 1.0);
+ gimp_rgba_set (&darkcheck, GIMP_CHECK_DARK, GIMP_CHECK_DARK,
+ GIMP_CHECK_DARK, 1.0);
+
+ if (mapvals.bump_mapped == TRUE && mapvals.bumpmap_id != -1)
+ {
+ bumpmap_setup (mapvals.bumpmap_id);
+ }
+
+ imagey = 0;
+
+ if (mapvals.previewquality)
+ ray_func = get_ray_color;
+ else
+ ray_func = get_ray_color_no_bilinear;
+
+ if (mapvals.env_mapped == TRUE && mapvals.envmap_id != -1)
+ {
+ envmap_setup (mapvals.envmap_id);
+
+ if (mapvals.previewquality)
+ ray_func = get_ray_color_ref;
+ else
+ ray_func = get_ray_color_no_bilinear_ref;
+ }
+
+ cairo_surface_flush (preview_surface);
+
+ for (ycnt = 0; ycnt < PREVIEW_HEIGHT; ycnt++)
+ {
+ index = ycnt * preview_rgb_stride;
+ for (xcnt = 0; xcnt < PREVIEW_WIDTH; xcnt++)
+ {
+ if ((ycnt >= starty && ycnt < (starty + h)) &&
+ (xcnt >= startx && xcnt < (startx + w)))
+ {
+ imagex = xpostab[xcnt - startx];
+ imagey = ypostab[ycnt - starty];
+ pos = int_to_posf (imagex, imagey);
+
+ if (mapvals.bump_mapped == TRUE &&
+ mapvals.bumpmap_id != -1 &&
+ xcnt == startx)
+ {
+ pos_to_float (pos.x, pos.y, &imagex, &imagey);
+ precompute_normals (0, width, RINT (imagey));
+ }
+
+ color = (*ray_func) (&pos);
+
+ if (color.a < 1.0)
+ {
+ f1 = ((xcnt % 32) < 16);
+ f2 = ((ycnt % 32) < 16);
+ f1 = f1 ^ f2;
+
+ if (f1)
+ {
+ if (color.a == 0.0)
+ color = lightcheck;
+ else
+ gimp_rgb_composite (&color,
+ &lightcheck,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+ else
+ {
+ if (color.a == 0.0)
+ color = darkcheck;
+ else
+ gimp_rgb_composite (&color,
+ &darkcheck,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+ }
+
+ gimp_rgb_get_uchar (&color, &r, &g, &b);
+ GIMP_CAIRO_RGB24_SET_PIXEL((preview_rgb_data + index), r, g, b);
+ index += 4;
+ imagex++;
+ }
+ else
+ {
+ preview_rgb_data[index++] = 200;
+ preview_rgb_data[index++] = 200;
+ preview_rgb_data[index++] = 200;
+ index++;
+ }
+ }
+ }
+ cairo_surface_mark_dirty (preview_surface);
+}
+
+static void
+compute_preview_rectangle (gint * xp, gint * yp, gint * wid, gint * heig)
+{
+ gdouble x, y, w, h;
+
+ if (width >= height)
+ {
+ w = (PREVIEW_WIDTH - 50.0);
+ h = (gdouble) height *(w / (gdouble) width);
+
+ x = (PREVIEW_WIDTH - w) / 2.0;
+ y = (PREVIEW_HEIGHT - h) / 2.0;
+ }
+ else
+ {
+ h = (PREVIEW_HEIGHT - 50.0);
+ w = (gdouble) width *(h / (gdouble) height);
+ x = (PREVIEW_WIDTH - w) / 2.0;
+ y = (PREVIEW_HEIGHT - h) / 2.0;
+ }
+ *xp = RINT (x);
+ *yp = RINT (y);
+ *wid = RINT (w);
+ *heig = RINT (h);
+}
+
+/*************************************************/
+/* Check if the given position is within the */
+/* light marker. Return TRUE if so, FALSE if not */
+/*************************************************/
+
+static gboolean
+check_handle_hit (gint xpos, gint ypos)
+{
+ gint dx,dy,r;
+ gint k = mapvals.light_selected;
+
+ dx = handle_xpos - xpos;
+ dy = handle_ypos - ypos;
+
+
+ if (mapvals.lightsource[k].type == POINT_LIGHT ||
+ mapvals.lightsource[k].type == DIRECTIONAL_LIGHT)
+ {
+ r = sqrt (dx * dx + dy * dy) + 0.5;
+ if ((gint) r > 7)
+ {
+ return (FALSE);
+ }
+ else
+ {
+ return (TRUE);
+ }
+ }
+ return FALSE;
+}
+
+/****************************************/
+/* Draw a light symbol */
+/****************************************/
+
+
+static void
+draw_handles (void)
+{
+ gdouble dxpos, dypos;
+ gint startx, starty, pw, ph;
+ GimpVector3 viewpoint;
+ GimpVector3 light_position;
+ gint k = mapvals.light_selected;
+
+ gfloat length;
+ gfloat delta_x = 0.0;
+ gfloat delta_y = 0.0;
+
+ /* calculate handle position */
+ compute_preview_rectangle (&startx, &starty, &pw, &ph);
+ switch (mapvals.lightsource[k].type)
+ {
+ case NO_LIGHT:
+ return;
+ case POINT_LIGHT:
+ case SPOT_LIGHT:
+ /* swap z to reverse light position */
+ viewpoint = mapvals.viewpoint;
+ viewpoint.z = -viewpoint.z;
+ light_position = mapvals.lightsource[k].position;
+ gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
+ &viewpoint, &light_position);
+ handle_xpos = (gint) (dxpos + 0.5);
+ handle_ypos = (gint) (dypos + 0.5);
+ break;
+ case DIRECTIONAL_LIGHT:
+ light_position.x = light_position.y = 0.5;
+ light_position.z = 0;
+ viewpoint.z = -viewpoint.z;
+ gimp_vector_3d_to_2d (startx, starty, pw, ph, &dxpos, &dypos,
+ &viewpoint, &light_position);
+ length = PREVIEW_HEIGHT / 4;
+ delta_x = mapvals.lightsource[k].direction.x * length;
+ delta_y = mapvals.lightsource[k].direction.y * length;
+ handle_xpos = dxpos + delta_x;
+ handle_ypos = dypos + delta_y;
+ break;
+ }
+
+ if (mapvals.lightsource[k].type != NO_LIGHT)
+ {
+ GdkColor color;
+ cairo_t *cr;
+ cr = gdk_cairo_create (gtk_widget_get_window (previewarea));
+
+ cairo_set_line_width (cr, 1.0);
+
+ color.red = 0x0;
+ color.green = 0x4000;
+ color.blue = 0xFFFF;
+ gdk_cairo_set_source_color (cr, &color);
+
+ /* draw circle at light position */
+ switch (mapvals.lightsource[k].type)
+ {
+ case POINT_LIGHT:
+ case SPOT_LIGHT:
+ cairo_arc (cr, handle_xpos, handle_ypos,
+ LIGHT_SYMBOL_SIZE/2, 0, 2 * G_PI);
+ cairo_fill (cr);
+ break;
+ case DIRECTIONAL_LIGHT:
+ cairo_arc (cr, handle_xpos, handle_ypos,
+ LIGHT_SYMBOL_SIZE/2, 0, 2 * G_PI);
+ cairo_fill (cr);
+ cairo_move_to (cr, handle_xpos, handle_ypos);
+ cairo_line_to (cr, startx + pw/2, starty + ph/2);
+ cairo_stroke (cr);
+ break;
+ case NO_LIGHT:
+ break;
+ }
+ cairo_destroy (cr);
+ }
+}
+
+
+/*************************************************/
+/* Update light position given new screen coords */
+/*************************************************/
+
+void
+update_light (gint xpos, gint ypos)
+{
+ gint startx, starty, pw, ph;
+ GimpVector3 vp;
+ gint k = mapvals.light_selected;
+
+ compute_preview_rectangle (&startx, &starty, &pw, &ph);
+
+ vp = mapvals.viewpoint;
+ vp.z = -vp.z;
+
+ switch (mapvals.lightsource[k].type)
+ {
+ case NO_LIGHT:
+ break;
+ case POINT_LIGHT:
+ case SPOT_LIGHT:
+ gimp_vector_2d_to_3d (startx,
+ starty,
+ pw,
+ ph,
+ xpos, ypos, &vp, &mapvals.lightsource[k].position);
+ break;
+ case DIRECTIONAL_LIGHT:
+ gimp_vector_2d_to_3d (startx,
+ starty,
+ pw,
+ ph,
+ xpos, ypos, &vp, &mapvals.lightsource[k].direction);
+ break;
+ }
+}
+
+
+/******************************************************************/
+/* Draw preview image. if DoCompute is TRUE then recompute image. */
+/******************************************************************/
+
+void
+preview_compute (void)
+{
+ GdkDisplay *display = gtk_widget_get_display (previewarea);
+ GdkCursor *cursor;
+ gint startx, starty, pw, ph;
+
+ compute_preview_rectangle (&startx, &starty, &pw, &ph);
+
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+ gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+
+ compute_preview (startx, starty, pw, ph);
+ cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+ gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+ gdk_flush ();
+}
+
+
+/******************************/
+/* Preview area event handler */
+/******************************/
+
+gboolean
+preview_events (GtkWidget *area,
+ GdkEvent *event)
+{
+ switch (event->type)
+ {
+ case GDK_ENTER_NOTIFY:
+ break;
+ case GDK_LEAVE_NOTIFY:
+ break;
+ case GDK_BUTTON_PRESS:
+ light_hit = check_handle_hit (event->button.x, event->button.y);
+ left_button_pressed = TRUE;
+ break;
+ case GDK_BUTTON_RELEASE:
+ left_button_pressed = FALSE;
+ break;
+ case GDK_MOTION_NOTIFY:
+ if (left_button_pressed == TRUE &&
+ light_hit == TRUE &&
+ mapvals.interactive_preview == TRUE )
+ {
+ gtk_widget_queue_draw (previewarea);
+ interactive_preview_callback(NULL);
+ update_light (event->motion.x, event->motion.y);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+gboolean
+preview_expose (GtkWidget *area,
+ GdkEventExpose *eevent)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (eevent->window);
+
+ cairo_set_source_surface (cr, preview_surface, 0.0, 0.0);
+
+ cairo_paint (cr);
+
+ /* draw symbols if enabled in UI */
+ if (mapvals.interactive_preview)
+ {
+ draw_handles ();
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+void
+interactive_preview_callback (GtkWidget *widget)
+{
+ if (preview_update_timer != 0)
+ g_source_remove (preview_update_timer);
+
+ preview_update_timer = g_timeout_add (100,
+ interactive_preview_timer_callback,
+ NULL);
+}
+
+static gboolean
+interactive_preview_timer_callback (gpointer data)
+{
+ gint k = mapvals.light_selected;
+
+ mapvals.update_enabled = FALSE; /* disable apply_settings() */
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_x),
+ mapvals.lightsource[k].position.x);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_y),
+ mapvals.lightsource[k].position.y);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_z),
+ mapvals.lightsource[k].position.z);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_x),
+ mapvals.lightsource[k].direction.x);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_y),
+ mapvals.lightsource[k].direction.y);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_z),
+ mapvals.lightsource[k].direction.z);
+
+ mapvals.update_enabled = TRUE;
+
+ preview_compute ();
+
+ gtk_widget_queue_draw (previewarea);
+
+ preview_update_timer = 0;
+
+ return FALSE;
+}
diff --git a/plug-ins/lighting/lighting-preview.h b/plug-ins/lighting/lighting-preview.h
new file mode 100644
index 0000000..881dacd
--- /dev/null
+++ b/plug-ins/lighting/lighting-preview.h
@@ -0,0 +1,38 @@
+/* Lighting Effects - A plug-in for GIMP
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIGHTING_PREVIEW_H__
+#define __LIGHTING_PREVIEW_H__
+
+#define PREVIEW_WIDTH 200
+#define PREVIEW_HEIGHT 200
+
+/* Externally visible variables */
+
+extern gdouble *xpostab, *ypostab;
+
+/* Externally visible functions */
+
+void preview_compute (void);
+void interactive_preview_callback (GtkWidget *widget);
+gboolean preview_events (GtkWidget *area,
+ GdkEvent *event);
+gboolean preview_expose (GtkWidget *area,
+ GdkEventExpose *eevent);
+void update_light (gint xpos,
+ gint ypos);
+
+#endif /* __LIGHTING_PREVIEW_H__ */
diff --git a/plug-ins/lighting/lighting-shade.c b/plug-ins/lighting/lighting-shade.c
new file mode 100644
index 0000000..70443d9
--- /dev/null
+++ b/plug-ins/lighting/lighting-shade.c
@@ -0,0 +1,838 @@
+/*****************/
+/* Shading stuff */
+/*****************/
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "lighting-main.h"
+#include "lighting-image.h"
+#include "lighting-shade.h"
+
+
+static GimpVector3 *triangle_normals[2] = { NULL, NULL };
+static GimpVector3 *vertex_normals[3] = { NULL, NULL, NULL };
+static gdouble *heights[3] = { NULL, NULL, NULL };
+static gdouble xstep, ystep;
+static guchar *bumprow = NULL;
+
+static gint pre_w = -1;
+static gint pre_h = -1;
+
+/*****************/
+/* Phong shading */
+/*****************/
+
+static GimpRGB
+phong_shade (GimpVector3 *position,
+ GimpVector3 *viewpoint,
+ GimpVector3 *normal,
+ GimpVector3 *lightposition,
+ GimpRGB *diff_col,
+ GimpRGB *light_col,
+ LightType light_type)
+{
+ GimpRGB diffuse_color, specular_color;
+ gdouble nl, rv, dist;
+ GimpVector3 l, v, n, lnormal, h;
+
+ /* Compute ambient intensity */
+ /* ========================= */
+
+ n = *normal;
+
+ /* Compute (N*L) term of Phong's equation */
+ /* ====================================== */
+
+ if (light_type == POINT_LIGHT)
+ gimp_vector3_sub (&l, lightposition, position);
+ else
+ {
+ l = *lightposition;
+ gimp_vector3_normalize (&l);
+ }
+
+ dist = gimp_vector3_length (&l);
+
+ if (dist != 0.0)
+ gimp_vector3_mul (&l, 1.0 / dist);
+
+ nl = MAX (0., 2.0 * gimp_vector3_inner_product (&n, &l));
+
+ lnormal = l;
+ gimp_vector3_normalize (&lnormal);
+
+ if (nl >= 0.0)
+ {
+ /* Compute (R*V)^alpha term of Phong's equation */
+ /* ============================================ */
+
+ gimp_vector3_sub (&v, viewpoint, position);
+ gimp_vector3_normalize (&v);
+
+ gimp_vector3_add (&h, &lnormal, &v);
+ gimp_vector3_normalize (&h);
+
+ rv = MAX (0.01, gimp_vector3_inner_product (&n, &h));
+ rv = pow (rv, mapvals.material.highlight);
+ rv *= nl;
+
+ /* Compute diffuse and specular intensity contribution */
+ /* =================================================== */
+
+ diffuse_color = *light_col;
+ gimp_rgb_multiply (&diffuse_color, mapvals.material.diffuse_int);
+ diffuse_color.r *= diff_col->r;
+ diffuse_color.g *= diff_col->g;
+ diffuse_color.b *= diff_col->b;
+ gimp_rgb_multiply (&diffuse_color, nl);
+
+ specular_color = *light_col;
+ if (mapvals.material.metallic) /* for metals, specular color = diffuse color */
+ {
+ specular_color.r *= diff_col->r;
+ specular_color.g *= diff_col->g;
+ specular_color.b *= diff_col->b;
+ }
+ gimp_rgb_multiply (&specular_color, mapvals.material.specular_ref);
+ gimp_rgb_multiply (&specular_color, rv);
+
+ gimp_rgb_add (&diffuse_color, &specular_color);
+ gimp_rgb_clamp (&diffuse_color);
+ }
+
+ gimp_rgb_clamp (&diffuse_color);
+
+ return diffuse_color;
+}
+
+void
+precompute_init (gint w,
+ gint h)
+{
+ gint n;
+ gint bpp=1;
+
+ xstep = 1.0 / (gdouble) width;
+ ystep = 1.0 / (gdouble) height;
+
+ pre_w = w;
+ pre_h = h;
+
+ for (n = 0; n < 3; n++)
+ {
+ if (vertex_normals[n] != NULL)
+ g_free (vertex_normals[n]);
+
+ if (heights[n] != NULL)
+ g_free (heights[n]);
+
+ heights[n] = g_new (gdouble, w);
+ vertex_normals[n] = g_new (GimpVector3, w);
+ }
+
+ for (n = 0; n < 2; n++)
+ if (triangle_normals[n] != NULL)
+ g_free (triangle_normals[n]);
+
+ g_clear_pointer (&bumprow, g_free);
+
+ if (mapvals.bumpmap_id != -1)
+ {
+ bpp = gimp_drawable_bpp(mapvals.bumpmap_id);
+ }
+
+ bumprow = g_new (guchar, w * bpp);
+
+ triangle_normals[0] = g_new (GimpVector3, (w << 1) + 2);
+ triangle_normals[1] = g_new (GimpVector3, (w << 1) + 2);
+
+ for (n = 0; n < (w << 1) + 1; n++)
+ {
+ gimp_vector3_set (&triangle_normals[0][n], 0.0, 0.0, 1.0);
+ gimp_vector3_set (&triangle_normals[1][n], 0.0, 0.0, 1.0);
+ }
+
+ for (n = 0; n < w; n++)
+ {
+ gimp_vector3_set (&vertex_normals[0][n], 0.0, 0.0, 1.0);
+ gimp_vector3_set (&vertex_normals[1][n], 0.0, 0.0, 1.0);
+ gimp_vector3_set (&vertex_normals[2][n], 0.0, 0.0, 1.0);
+ heights[0][n] = 0.0;
+ heights[1][n] = 0.0;
+ heights[2][n] = 0.0;
+ }
+}
+
+
+/* Interpol linearly height[2] and triangle_normals[1]
+ * using the next row
+ */
+void
+interpol_row (gint x1,
+ gint x2,
+ gint y)
+{
+ GimpVector3 p1, p2, p3;
+ gint n, i;
+ guchar *map = NULL;
+ gint bpp = 1;
+ guchar *bumprow1 = NULL;
+ guchar *bumprow2 = NULL;
+
+ if (mapvals.bumpmap_id != -1)
+ {
+ bumpmap_setup (mapvals.bumpmap_id);
+
+ bpp = babl_format_get_bytes_per_pixel (bump_format);
+ }
+
+ bumprow1 = g_new0 (guchar, pre_w * bpp);
+ bumprow2 = g_new0 (guchar, pre_w * bpp);
+
+ gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y, x2 - x1, 1), 1.0,
+ bump_format, bumprow1,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y - 1, x2 - x1, 1), 1.0,
+ bump_format, bumprow2,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (mapvals.bumpmaptype > 0)
+ {
+ switch (mapvals.bumpmaptype)
+ {
+ case 1:
+ map = logmap;
+ break;
+ case 2:
+ map = sinemap;
+ break;
+ default:
+ map = spheremap;
+ break;
+ }
+ }
+
+ for (n = 0; n < (x2 - x1); n++)
+ {
+ gdouble diff;
+ guchar mapval;
+ guchar mapval1, mapval2;
+
+ if (bpp>1)
+ {
+ mapval1 = (guchar)((float)((bumprow1[n * bpp] +bumprow1[n * bpp +1] + bumprow1[n * bpp + 2])/3.0 )) ;
+ mapval2 = (guchar)((float)((bumprow2[n * bpp] +bumprow2[n * bpp +1] + bumprow2[n * bpp + 2])/3.0 )) ;
+ }
+ else
+ {
+ mapval1 = bumprow1[n * bpp];
+ mapval2 = bumprow2[n * bpp];
+ }
+
+ diff = mapval1 - mapval2;
+ mapval = (guchar) CLAMP (mapval1 + diff, 0.0, 255.0);
+
+ if (mapvals.bumpmaptype > 0)
+ {
+ heights[1][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval1] / 255.0;
+ heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval] / 255.0;
+ }
+ else
+ {
+ heights[1][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval1 / 255.0;
+ heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval / 255.0;
+ }
+ }
+
+ i = 0;
+ for (n = 0; n < (x2 - x1 - 1); n++)
+ {
+ /* heights rows 1 and 2 are inverted */
+ p1.x = 0.0;
+ p1.y = ystep;
+ p1.z = heights[1][n] - heights[2][n];
+
+ p2.x = xstep;
+ p2.y = ystep;
+ p2.z = heights[1][n+1] - heights[2][n];
+
+ p3.x = xstep;
+ p3.y = 0.0;
+ p3.z = heights[2][n+1] - heights[2][n];
+
+ triangle_normals[1][i] = gimp_vector3_cross_product (&p2, &p1);
+ triangle_normals[1][i+1] = gimp_vector3_cross_product (&p3, &p2);
+
+ gimp_vector3_normalize (&triangle_normals[1][i]);
+ gimp_vector3_normalize (&triangle_normals[1][i+1]);
+
+ i += 2;
+ }
+
+ g_free (bumprow1);
+ g_free (bumprow2);
+}
+
+/********************************************/
+/* Compute triangle and then vertex normals */
+/********************************************/
+
+
+void
+precompute_normals (gint x1,
+ gint x2,
+ gint y)
+{
+ GimpVector3 *tmpv, p1, p2, p3, normal;
+ gdouble *tmpd;
+ gint n, i, nv;
+ guchar *map = NULL;
+ gint bpp = 1;
+ guchar mapval;
+
+
+ /* First, compute the heights */
+ /* ========================== */
+
+ tmpv = triangle_normals[0];
+ triangle_normals[0] = triangle_normals[1];
+ triangle_normals[1] = tmpv;
+
+ tmpv = vertex_normals[0];
+ vertex_normals[0] = vertex_normals[1];
+ vertex_normals[1] = vertex_normals[2];
+ vertex_normals[2] = tmpv;
+
+ tmpd = heights[0];
+ heights[0] = heights[1];
+ heights[1] = heights[2];
+ heights[2] = tmpd;
+
+ if (mapvals.bumpmap_id != -1)
+ {
+ bumpmap_setup (mapvals.bumpmap_id);
+
+ bpp = babl_format_get_bytes_per_pixel (bump_format);
+ }
+
+ gegl_buffer_get (bump_buffer, GEGL_RECTANGLE (x1, y, x2 - x1, 1), 1.0,
+ bump_format, bumprow,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ if (mapvals.bumpmaptype > 0)
+ {
+ switch (mapvals.bumpmaptype)
+ {
+ case 1:
+ map = logmap;
+ break;
+ case 2:
+ map = sinemap;
+ break;
+ default:
+ map = spheremap;
+ break;
+ }
+
+ for (n = 0; n < (x2 - x1); n++)
+ {
+ if (bpp > 1)
+ {
+ mapval = (guchar)((float)((bumprow[n * bpp + 0] +
+ bumprow[n * bpp + 1] +
+ bumprow[n * bpp + 2]) /3.0));
+ }
+ else
+ {
+ mapval = bumprow[n * bpp];
+ }
+
+ heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) map[mapval] / 255.0;
+ }
+ }
+ else
+ {
+ for (n = 0; n < (x2 - x1); n++)
+ {
+ if (bpp>1)
+ {
+ mapval = (guchar)((float)((bumprow[n * bpp + 0] +
+ bumprow[n * bpp + 1] +
+ bumprow[n * bpp + 2]) / 3.0));
+ }
+ else
+ {
+ mapval = bumprow[n * bpp];
+ }
+
+ heights[2][n] = (gdouble) mapvals.bumpmax * (gdouble) mapval / 255.0;
+ }
+ }
+
+ /* Compute triangle normals */
+ /* ======================== */
+
+ i = 0;
+ for (n = 0; n < (x2 - x1 - 1); n++)
+ {
+ p1.x = 0.0;
+ p1.y = ystep;
+ p1.z = heights[2][n] - heights[1][n];
+
+ p2.x = xstep;
+ p2.y = ystep;
+ p2.z = heights[2][n+1] - heights[1][n];
+
+ p3.x = xstep;
+ p3.y = 0.0;
+ p3.z = heights[1][n+1] - heights[1][n];
+
+ triangle_normals[1][i] = gimp_vector3_cross_product (&p2, &p1);
+ triangle_normals[1][i+1] = gimp_vector3_cross_product (&p3, &p2);
+
+ gimp_vector3_normalize (&triangle_normals[1][i]);
+ gimp_vector3_normalize (&triangle_normals[1][i+1]);
+
+ i += 2;
+ }
+
+ /* Compute vertex normals */
+ /* ====================== */
+
+ i = 0;
+ gimp_vector3_set (&normal, 0.0, 0.0, 0.0);
+
+ for (n = 0; n < (x2 - x1 - 1); n++)
+ {
+ nv = 0;
+
+ if (n > 0)
+ {
+ if (y > 0)
+ {
+ gimp_vector3_add (&normal, &normal, &triangle_normals[0][i-1]);
+ gimp_vector3_add (&normal, &normal, &triangle_normals[0][i-2]);
+ nv += 2;
+ }
+
+ if (y < pre_h)
+ {
+ gimp_vector3_add (&normal, &normal, &triangle_normals[1][i-1]);
+ nv++;
+ }
+ }
+
+ if (n <pre_w)
+ {
+ if (y > 0)
+ {
+ gimp_vector3_add (&normal, &normal, &triangle_normals[0][i]);
+ gimp_vector3_add (&normal, &normal, &triangle_normals[0][i+1]);
+ nv += 2;
+ }
+
+ if (y < pre_h)
+ {
+ gimp_vector3_add (&normal, &normal, &triangle_normals[1][i]);
+ gimp_vector3_add (&normal, &normal, &triangle_normals[1][i+1]);
+ nv += 2;
+ }
+ }
+
+ gimp_vector3_mul (&normal, 1.0 / (gdouble) nv);
+ gimp_vector3_normalize (&normal);
+ vertex_normals[1][n] = normal;
+
+ i += 2;
+ }
+}
+
+/***********************************************************************/
+/* Compute the reflected ray given the normalized normal and ins. vec. */
+/***********************************************************************/
+
+static GimpVector3
+compute_reflected_ray (GimpVector3 *normal,
+ GimpVector3 *view)
+{
+ GimpVector3 ref;
+ gdouble nl;
+
+ nl = 2.0 * gimp_vector3_inner_product (normal, view);
+
+ ref = *normal;
+
+ gimp_vector3_mul (&ref, nl);
+ gimp_vector3_sub (&ref, &ref, view);
+
+ return ref;
+}
+
+/************************************************************************/
+/* Given the NorthPole, Equator and a third vector (normal) compute */
+/* the conversion from spherical coordinates to image space coordinates */
+/************************************************************************/
+
+static void
+sphere_to_image (GimpVector3 *normal,
+ gdouble *u,
+ gdouble *v)
+{
+ static gdouble alpha, fac;
+ static GimpVector3 cross_prod;
+ static GimpVector3 firstaxis = { 1.0, 0.0, 0.0 };
+ static GimpVector3 secondaxis = { 0.0, 1.0, 0.0 };
+
+ alpha = acos (-gimp_vector3_inner_product (&secondaxis, normal));
+
+ *v = alpha / G_PI;
+
+ if (*v==0.0 || *v==1.0)
+ {
+ *u = 0.0;
+ }
+ else
+ {
+ fac = gimp_vector3_inner_product (&firstaxis, normal) / sin (alpha);
+
+ /* Make sure that we map to -1.0..1.0 (take care of rounding errors) */
+ /* ================================================================= */
+
+ if (fac>1.0)
+ fac = 1.0;
+ else if (fac<-1.0)
+ fac = -1.0;
+
+ *u = acos (fac) / (2.0 * G_PI);
+
+ cross_prod = gimp_vector3_cross_product (&secondaxis, &firstaxis);
+
+ if (gimp_vector3_inner_product (&cross_prod, normal) < 0.0)
+ *u = 1.0 - *u;
+ }
+}
+
+/*********************************************************************/
+/* These routines computes the color of the surface at a given point */
+/*********************************************************************/
+
+GimpRGB
+get_ray_color (GimpVector3 *position)
+{
+ GimpRGB color;
+ GimpRGB color_int;
+ GimpRGB color_sum;
+ GimpRGB light_color;
+ gint x, f;
+ gdouble xf, yf;
+ GimpVector3 normal, *p;
+ gint k;
+
+ pos_to_float (position->x, position->y, &xf, &yf);
+
+ x = RINT (xf);
+
+ if (mapvals.transparent_background && heights[1][x] == 0)
+ {
+ gimp_rgb_set_alpha (&color_sum, 0.0);
+ }
+ else
+ {
+ color = get_image_color (xf, yf, &f);
+
+ color_sum = color;
+ gimp_rgb_multiply (&color_sum, mapvals.material.ambient_int);
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ {
+ if (!mapvals.lightsource[k].active
+ || mapvals.lightsource[k].type == NO_LIGHT)
+ continue;
+ else if (mapvals.lightsource[k].type == POINT_LIGHT)
+ p = &mapvals.lightsource[k].position;
+ else
+ p = &mapvals.lightsource[k].direction;
+
+ color_int = mapvals.lightsource[k].color;
+ gimp_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
+
+ if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
+ {
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &mapvals.planenormal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[k].type);
+ }
+ else
+ {
+ normal = vertex_normals[1][(gint) RINT (xf)];
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[k].type);
+ }
+
+ gimp_rgb_add (&color_sum, &light_color);
+ }
+ }
+
+ gimp_rgb_clamp (&color_sum);
+ return color_sum;
+}
+
+GimpRGB
+get_ray_color_ref (GimpVector3 *position)
+{
+ GimpRGB color_sum;
+ GimpRGB color_int;
+ GimpRGB light_color;
+ GimpRGB color, env_color;
+ gint x, f;
+ gdouble xf, yf;
+ GimpVector3 normal, *p, v, r;
+ gint k;
+ gdouble tmpval;
+
+ pos_to_float (position->x, position->y, &xf, &yf);
+
+ x = RINT (xf);
+
+ if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
+ normal = mapvals.planenormal;
+ else
+ normal = vertex_normals[1][(gint) RINT (xf)];
+ gimp_vector3_normalize (&normal);
+
+ if (mapvals.transparent_background && heights[1][x] == 0)
+ {
+ gimp_rgb_set_alpha (&color_sum, 0.0);
+ }
+ else
+ {
+ color = get_image_color (xf, yf, &f);
+ color_sum = color;
+ gimp_rgb_multiply (&color_sum, mapvals.material.ambient_int);
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ {
+ p = &mapvals.lightsource[k].direction;
+
+ if (!mapvals.lightsource[k].active
+ || mapvals.lightsource[k].type == NO_LIGHT)
+ continue;
+ else if (mapvals.lightsource[k].type == POINT_LIGHT)
+ p = &mapvals.lightsource[k].position;
+
+ color_int = mapvals.lightsource[k].color;
+ gimp_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[0].type);
+ }
+
+ gimp_vector3_sub (&v, &mapvals.viewpoint, position);
+ gimp_vector3_normalize (&v);
+
+ r = compute_reflected_ray (&normal, &v);
+
+ /* Get color in the direction of r */
+ /* =============================== */
+
+ sphere_to_image (&r, &xf, &yf);
+ env_color = peek_env_map (RINT (env_width * xf),
+ RINT (env_height * yf));
+
+ tmpval = mapvals.material.diffuse_int;
+ mapvals.material.diffuse_int = 0.;
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ &r,
+ &color,
+ &env_color,
+ DIRECTIONAL_LIGHT);
+
+ mapvals.material.diffuse_int = tmpval;
+
+ gimp_rgb_add (&color_sum, &light_color);
+ }
+
+ gimp_rgb_clamp (&color_sum);
+ return color_sum;
+}
+
+GimpRGB
+get_ray_color_no_bilinear (GimpVector3 *position)
+{
+ GimpRGB color;
+ GimpRGB color_int;
+ GimpRGB color_sum;
+ GimpRGB light_color;
+ gint x;
+ gdouble xf, yf;
+ GimpVector3 normal, *p;
+ gint k;
+
+
+ pos_to_float (position->x, position->y, &xf, &yf);
+
+ x = RINT (xf);
+
+ if (mapvals.transparent_background && heights[1][x] == 0)
+ {
+ gimp_rgb_set_alpha (&color_sum, 0.0);
+ }
+ else
+ {
+ color = peek (x, RINT (yf));
+
+ color_sum = color;
+ gimp_rgb_multiply (&color_sum, mapvals.material.ambient_int);
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ {
+ p = &mapvals.lightsource[k].direction;
+
+ if (!mapvals.lightsource[k].active
+ || mapvals.lightsource[k].type == NO_LIGHT)
+ continue;
+ else if (mapvals.lightsource[k].type == POINT_LIGHT)
+ p = &mapvals.lightsource[k].position;
+
+ color_int = mapvals.lightsource[k].color;
+ gimp_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
+
+ if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
+ {
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &mapvals.planenormal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[k].type);
+ }
+ else
+ {
+ normal = vertex_normals[1][x];
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[k].type);
+ }
+
+ gimp_rgb_add (&color_sum, &light_color);
+ }
+ }
+
+ gimp_rgb_clamp (&color_sum);
+ return color_sum;
+}
+
+GimpRGB
+get_ray_color_no_bilinear_ref (GimpVector3 *position)
+{
+ GimpRGB color_sum;
+ GimpRGB color_int;
+ GimpRGB light_color;
+ GimpRGB color, env_color;
+ gint x;
+ gdouble xf, yf;
+ GimpVector3 normal, *p, v, r;
+ gint k;
+ gdouble tmpval;
+
+ pos_to_float (position->x, position->y, &xf, &yf);
+
+ x = RINT (xf);
+
+ if (mapvals.bump_mapped == FALSE || mapvals.bumpmap_id == -1)
+ normal = mapvals.planenormal;
+ else
+ normal = vertex_normals[1][(gint) RINT (xf)];
+ gimp_vector3_normalize (&normal);
+
+ if (mapvals.transparent_background && heights[1][x] == 0)
+ {
+ gimp_rgb_set_alpha (&color_sum, 0.0);
+ }
+ else
+ {
+ color = peek (RINT (xf), RINT (yf));
+ color_sum = color;
+ gimp_rgb_multiply (&color_sum, mapvals.material.ambient_int);
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ {
+ p = &mapvals.lightsource[k].direction;
+
+ if (!mapvals.lightsource[k].active
+ || mapvals.lightsource[k].type == NO_LIGHT)
+ continue;
+ else if (mapvals.lightsource[k].type == POINT_LIGHT)
+ p = &mapvals.lightsource[k].position;
+
+ color_int = mapvals.lightsource[k].color;
+ gimp_rgb_multiply (&color_int, mapvals.lightsource[k].intensity);
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ p,
+ &color,
+ &color_int,
+ mapvals.lightsource[0].type);
+ }
+
+ gimp_vector3_sub (&v, &mapvals.viewpoint, position);
+ gimp_vector3_normalize (&v);
+
+ r = compute_reflected_ray (&normal, &v);
+
+ /* Get color in the direction of r */
+ /* =============================== */
+
+ sphere_to_image (&r, &xf, &yf);
+ env_color = peek_env_map (RINT (env_width * xf),
+ RINT (env_height * yf));
+
+ tmpval = mapvals.material.diffuse_int;
+ mapvals.material.diffuse_int = 0.;
+
+ light_color = phong_shade (position,
+ &mapvals.viewpoint,
+ &normal,
+ &r,
+ &color,
+ &env_color,
+ DIRECTIONAL_LIGHT);
+
+ mapvals.material.diffuse_int = tmpval;
+
+ gimp_rgb_add (&color_sum, &light_color);
+ }
+
+ gimp_rgb_clamp (&color_sum);
+
+ return color_sum;
+}
diff --git a/plug-ins/lighting/lighting-shade.h b/plug-ins/lighting/lighting-shade.h
new file mode 100644
index 0000000..536df95
--- /dev/null
+++ b/plug-ins/lighting/lighting-shade.h
@@ -0,0 +1,20 @@
+#ifndef __LIGHTING_SHADE_H__
+#define __LIGHTING_SHADE_H__
+
+typedef GimpRGB (* get_ray_func) (GimpVector3 *vector);
+
+GimpRGB get_ray_color (GimpVector3 *position);
+GimpRGB get_ray_color_no_bilinear (GimpVector3 *position);
+GimpRGB get_ray_color_ref (GimpVector3 *position);
+GimpRGB get_ray_color_no_bilinear_ref (GimpVector3 *position);
+
+void precompute_init (gint w,
+ gint h);
+void precompute_normals (gint x1,
+ gint x2,
+ gint y);
+void interpol_row (gint x1,
+ gint x2,
+ gint y);
+
+#endif /* __LIGHTING_SHADE_H__ */
diff --git a/plug-ins/lighting/lighting-stock.c b/plug-ins/lighting/lighting-stock.c
new file mode 100644
index 0000000..0cc99b6
--- /dev/null
+++ b/plug-ins/lighting/lighting-stock.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "lighting-stock.h"
+
+#include "images/stock-pixbufs.h"
+
+
+static GtkIconFactory *lighting_icon_factory = NULL;
+
+static GtkStockItem lighting_stock_items[] =
+{
+ { STOCK_INTENSITY_AMBIENT_LOW, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_AMBIENT_HIGH, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_DIFFUSE_LOW, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_DIFFUSE_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_DIFFUSE_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_DIFFUSE_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_SPECULAR_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_SPECULAR_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_HIGHLIGHT_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_HIGHLIGHT_HIGH, NULL, 0, 0, NULL }
+};
+
+
+static void
+add_stock_icon (const gchar *stock_id,
+ GtkIconSize size,
+ const guint8 *inline_data)
+{
+ GtkIconSource *source;
+ GtkIconSet *set;
+ GdkPixbuf *pixbuf;
+
+ source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_size (source, size);
+ gtk_icon_source_set_size_wildcarded (source, FALSE);
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
+
+ gtk_icon_source_set_pixbuf (source, pixbuf);
+ g_object_unref (pixbuf);
+
+ set = gtk_icon_set_new ();
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (lighting_icon_factory, stock_id, set);
+
+ gtk_icon_set_unref (set);
+}
+
+void
+lighting_stock_init (void)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ lighting_icon_factory = gtk_icon_factory_new ();
+
+ add_stock_icon (STOCK_INTENSITY_AMBIENT_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_ambient_low);
+ add_stock_icon (STOCK_INTENSITY_AMBIENT_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_ambient_high);
+ add_stock_icon (STOCK_INTENSITY_DIFFUSE_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_diffuse_low);
+ add_stock_icon (STOCK_INTENSITY_DIFFUSE_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_diffuse_high);
+ add_stock_icon (STOCK_REFLECTIVITY_DIFFUSE_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_diffuse_low);
+ add_stock_icon (STOCK_REFLECTIVITY_DIFFUSE_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_diffuse_high);
+ add_stock_icon (STOCK_REFLECTIVITY_SPECULAR_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_specular_low);
+ add_stock_icon (STOCK_REFLECTIVITY_SPECULAR_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_specular_high);
+ add_stock_icon (STOCK_REFLECTIVITY_HIGHLIGHT_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_highlight_low);
+ add_stock_icon (STOCK_REFLECTIVITY_HIGHLIGHT_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_highlight_high);
+
+ gtk_icon_factory_add_default (lighting_icon_factory);
+
+ gtk_stock_add_static (lighting_stock_items,
+ G_N_ELEMENTS (lighting_stock_items));
+
+ initialized = TRUE;
+}
diff --git a/plug-ins/lighting/lighting-stock.h b/plug-ins/lighting/lighting-stock.h
new file mode 100644
index 0000000..6447526
--- /dev/null
+++ b/plug-ins/lighting/lighting-stock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIGHTING_STOCK_H__
+#define __LIGHTING_STOCK_H__
+
+
+#define STOCK_INTENSITY_AMBIENT_LOW "intensity-ambient-low"
+#define STOCK_INTENSITY_AMBIENT_HIGH "intensity-ambient-high"
+#define STOCK_INTENSITY_DIFFUSE_LOW "intensity-diffuse-low"
+#define STOCK_INTENSITY_DIFFUSE_HIGH "intensity-diffuse-high"
+#define STOCK_REFLECTIVITY_DIFFUSE_LOW "reflectivity-diffuse-low"
+#define STOCK_REFLECTIVITY_DIFFUSE_HIGH "reflectivity-diffuse-high"
+#define STOCK_REFLECTIVITY_SPECULAR_LOW "reflectivity-specular-low"
+#define STOCK_REFLECTIVITY_SPECULAR_HIGH "reflectivity-specular-high"
+#define STOCK_REFLECTIVITY_HIGHLIGHT_LOW "reflectivity-highlight-low"
+#define STOCK_REFLECTIVITY_HIGHLIGHT_HIGH "reflectivity-highlight-high"
+
+
+void lighting_stock_init (void);
+
+
+#endif /* __LIGHTING_STOCK_H__ */
diff --git a/plug-ins/lighting/lighting-ui.c b/plug-ins/lighting/lighting-ui.c
new file mode 100644
index 0000000..86b4e12
--- /dev/null
+++ b/plug-ins/lighting/lighting-ui.c
@@ -0,0 +1,1557 @@
+/* Lighting Effects - A plug-in for GIMP
+ *
+ * Dialog creation and updaters, callbacks and event-handlers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "lighting-ui.h"
+#include "lighting-main.h"
+#include "lighting-image.h"
+#include "lighting-apply.h"
+#include "lighting-preview.h"
+#include "lighting-stock.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+extern LightingValues mapvals;
+
+static GtkWidget *appwin = NULL;
+static GtkNotebook *options_note_book = NULL;
+
+GtkWidget *previewarea = NULL;
+
+GtkWidget *spin_pos_x = NULL;
+GtkWidget *spin_pos_y = NULL;
+GtkWidget *spin_pos_z = NULL;
+GtkWidget *spin_dir_x = NULL;
+GtkWidget *spin_dir_y = NULL;
+GtkWidget *spin_dir_z = NULL;
+
+static GtkWidget *colorbutton;
+static GtkWidget *light_type_combo;
+static GtkWidget *lightselect_combo;
+static GtkWidget *spin_intensity;
+static GtkWidget *isolate_button;
+static gchar *lighting_effects_path = NULL;
+
+static void create_main_notebook (GtkWidget *container);
+
+static void toggle_update (GtkWidget *widget,
+ gpointer data);
+
+static void distance_update (GtkAdjustment *adj,
+ gpointer data);
+
+static gboolean bumpmap_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+static gboolean envmap_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+static void envmap_combo_callback (GtkWidget *widget,
+ gpointer data);
+static void save_lighting_preset (GtkWidget *widget,
+ gpointer data);
+static void save_preset_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data);
+static void load_lighting_preset (GtkWidget *widget,
+ gpointer data);
+static void load_preset_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data);
+static void lightselect_callback (GimpIntComboBox *combo,
+ gpointer data);
+static void apply_settings (GtkWidget *widget,
+ gpointer data);
+static void isolate_selected_light (GtkWidget *widget,
+ gpointer data);
+
+static GtkWidget * spin_button_new (GtkAdjustment **adjustment, /* return value */
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits);
+
+/**********************/
+/* Std. toggle update */
+/**********************/
+
+static void
+toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ preview_compute ();
+ gtk_widget_queue_draw (previewarea);
+}
+
+
+static void
+distance_update (GtkAdjustment *adj,
+ gpointer data)
+{
+ mapvals.viewpoint.z = gtk_adjustment_get_value (adj);
+
+ preview_compute ();
+ gtk_widget_queue_draw (previewarea);
+}
+
+
+/*****************************************/
+/* Main window light type menu callback. */
+/*****************************************/
+
+static void
+apply_settings (GtkWidget *widget,
+ gpointer data)
+{
+ gint valid;
+ gint type;
+ gint k = mapvals.light_selected;
+
+ if (mapvals.update_enabled)
+ {
+ valid = gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (light_type_combo),
+ &type);
+ if (valid)
+ mapvals.lightsource[k].type = type;
+
+ gimp_color_button_get_color (GIMP_COLOR_BUTTON (colorbutton),
+ &mapvals.lightsource[k].color);
+
+ mapvals.lightsource[k].position.x
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_pos_x));
+ mapvals.lightsource[k].position.y
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_pos_y));
+ mapvals.lightsource[k].position.z
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_pos_z));
+
+ mapvals.lightsource[k].direction.x
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_dir_x));
+ mapvals.lightsource[k].direction.y
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_dir_y));
+ mapvals.lightsource[k].direction.z
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_dir_z));
+
+ mapvals.lightsource[k].intensity
+ = gtk_spin_button_get_value (GTK_SPIN_BUTTON(spin_intensity));
+
+ interactive_preview_callback(NULL);
+ }
+
+ if (widget == light_type_combo)
+ {
+ switch (mapvals.lightsource[k].type)
+ {
+ case NO_LIGHT:
+ gtk_widget_set_sensitive (spin_pos_x, FALSE);
+ gtk_widget_set_sensitive (spin_pos_y, FALSE);
+ gtk_widget_set_sensitive (spin_pos_z, FALSE);
+ gtk_widget_set_sensitive (spin_dir_x, FALSE);
+ gtk_widget_set_sensitive (spin_dir_y, FALSE);
+ gtk_widget_set_sensitive (spin_dir_z, FALSE);
+ break;
+ case POINT_LIGHT:
+ gtk_widget_set_sensitive (spin_pos_x, TRUE);
+ gtk_widget_set_sensitive (spin_pos_y, TRUE);
+ gtk_widget_set_sensitive (spin_pos_z, TRUE);
+ gtk_widget_set_sensitive (spin_dir_x, FALSE);
+ gtk_widget_set_sensitive (spin_dir_y, FALSE);
+ gtk_widget_set_sensitive (spin_dir_z, FALSE);
+ break;
+ case DIRECTIONAL_LIGHT:
+ gtk_widget_set_sensitive (spin_pos_x, FALSE);
+ gtk_widget_set_sensitive (spin_pos_y, FALSE);
+ gtk_widget_set_sensitive (spin_pos_z, FALSE);
+ gtk_widget_set_sensitive (spin_dir_x, TRUE);
+ gtk_widget_set_sensitive (spin_dir_y, TRUE);
+ gtk_widget_set_sensitive (spin_dir_z, TRUE);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+mapmenu2_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), (gint *) data);
+
+ preview_compute ();
+ gtk_widget_queue_draw (previewarea);
+}
+
+/******************************************/
+/* Main window "Preview!" button callback */
+/******************************************/
+
+static void
+preview_callback (GtkWidget *widget)
+{
+ preview_compute ();
+ gtk_widget_queue_draw (previewarea);
+}
+
+
+
+
+/*********************************************/
+/* Main window "-" (zoom in) button callback */
+/*********************************************/
+/*
+static void
+zoomout_callback (GtkWidget *widget)
+{
+ mapvals.preview_zoom_factor *= 0.5;
+ draw_preview_image (TRUE);
+}
+*/
+/*********************************************/
+/* Main window "+" (zoom out) button callback */
+/*********************************************/
+/*
+static void
+zoomin_callback (GtkWidget *widget)
+{
+ mapvals.preview_zoom_factor *= 2.0;
+ draw_preview_image (TRUE);
+}
+*/
+/**********************************************/
+/* Main window "Apply" button callback. */
+/* Render to GIMP image, close down and exit. */
+/**********************************************/
+
+static gint
+bumpmap_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ return ((gimp_drawable_width (drawable_id) ==
+ gimp_drawable_width (mapvals.drawable_id)) &&
+ (gimp_drawable_height (drawable_id) ==
+ gimp_drawable_height (mapvals.drawable_id)));
+}
+
+static gint
+envmap_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ return (!gimp_drawable_is_gray (drawable_id) &&
+ !gimp_drawable_has_alpha (drawable_id));
+}
+
+static void
+envmap_combo_callback (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &mapvals.envmap_id);
+
+ env_width = gimp_drawable_width (mapvals.envmap_id);
+ env_height = gimp_drawable_height (mapvals.envmap_id);
+}
+
+/***********************/
+/* Dialog constructors */
+/***********************/
+
+static GtkWidget *
+create_options_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *toggle;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ /* General options */
+
+ frame = gimp_frame_new (_("General Options"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("T_ransparent background"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.transparent_background);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.transparent_background);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Make destination image transparent where bump "
+ "height is zero"),NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Cre_ate new image"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.create_new_image);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.create_new_image);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Create a new image when applying filter"), NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("High _quality preview"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.previewquality);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.previewquality);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Enable/disable high quality preview"), NULL);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 12);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Distance:"), 100, 6,
+ mapvals.viewpoint.z,
+ 0.0, 2.0, 0.01, 0.05,
+ 3, TRUE, 0.0, 0.0,
+ "Distance of observer from surface",
+ "plug-in-lighting");
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (distance_update),
+ NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/******************************/
+/* Create light settings page */
+/******************************/
+
+static GtkWidget *
+create_light_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkAdjustment *adj;
+ GtkWidget *label;
+ gint k = mapvals.light_selected;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Light Settings"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (8, 8, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 3, 12);
+
+ lightselect_combo = gimp_int_combo_box_new (_("Light 1"), 0,
+ _("Light 2"), 1,
+ _("Light 3"), 2,
+ _("Light 4"), 3,
+ _("Light 5"), 4,
+ _("Light 6"), 5,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (lightselect_combo), k);
+ gtk_table_attach_defaults (GTK_TABLE (table), lightselect_combo, 0, 2, 0, 1);
+ g_signal_connect (lightselect_combo, "changed",
+ G_CALLBACK (lightselect_callback), NULL);
+ gtk_widget_show (lightselect_combo);
+
+ /* row labels */
+ label = gtk_label_new (_("Type:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("Color:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 2, 3);
+ gtk_widget_show (label);
+
+ light_type_combo =
+ gimp_int_combo_box_new (C_("light-source", "None"), NO_LIGHT,
+ _("Directional"), DIRECTIONAL_LIGHT,
+ _("Point"), POINT_LIGHT,
+ /* _("Spot"), SPOT_LIGHT, */
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (light_type_combo),
+ mapvals.lightsource[k].type);
+ gtk_table_attach_defaults (GTK_TABLE (table), light_type_combo,
+ 1, 2, 1, 2);
+ gtk_widget_show (light_type_combo);
+
+ g_signal_connect (light_type_combo, "changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (light_type_combo,
+ _("Type of light source to apply"), NULL);
+
+ colorbutton = gimp_color_button_new (_("Select lightsource color"),
+ 64, 16,
+ &mapvals.lightsource[k].color,
+ GIMP_COLOR_AREA_FLAT);
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (colorbutton), TRUE);
+ gtk_widget_show (colorbutton);
+ gtk_table_attach_defaults (GTK_TABLE (table),
+ colorbutton, 1, 2, 2, 3);
+
+ g_signal_connect (colorbutton, "color-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (colorbutton,
+ _("Set light source color"), NULL);
+
+
+ spin_intensity = spin_button_new (&adj,
+ mapvals.lightsource[k].intensity,
+ 0.0, 100.0,
+ 0.01, 0.1, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Intensity:"), 0.0, 0.5,
+ spin_intensity, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_intensity,
+ _("Light intensity"), NULL);
+
+
+ label = gtk_label_new (_("Position"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 3, 4, 0, 1);
+ gtk_widget_show (label);
+
+ spin_pos_x = spin_button_new (&adj,
+ mapvals.lightsource[k].position.x,
+ -100.0, 100.0,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 2, 1,
+ _("_X:"), 0.0, 0.5,
+ spin_pos_x, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_pos_x,
+ _("Light source X position in XYZ space"), NULL);
+
+ spin_pos_y = spin_button_new (&adj,
+ mapvals.lightsource[k].position.y,
+ -100.0, 100.0,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 2, 2,
+ _("_Y:"), 0.0, 0.5,
+ spin_pos_y, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_pos_y,
+ _("Light source Y position in XYZ space"), NULL);
+
+ spin_pos_z = spin_button_new (&adj,
+ mapvals.lightsource[k].position.z,
+ -100.0, 100.0,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 2, 3,
+ _("_Z:"), 0.0, 0.5,
+ spin_pos_z, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_pos_z,
+ _("Light source Z position in XYZ space"), NULL);
+
+
+ label = gtk_label_new (_("Direction"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 5, 6, 0, 1);
+ gtk_widget_show (label);
+
+ spin_dir_x = spin_button_new (&adj,
+ mapvals.lightsource[k].direction.x,
+ -100.0, 100.0, 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 4, 1,
+ _("X:"), 0.0, 0.5,
+ spin_dir_x, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_dir_x,
+ _("Light source X direction in XYZ space"), NULL);
+
+ spin_dir_y = spin_button_new (&adj,
+ mapvals.lightsource[k].direction.y,
+ -100.0, 100.0, 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 4, 2,
+ _("Y:"), 0.0, 0.5,
+ spin_dir_y, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_dir_y,
+ _("Light source Y direction in XYZ space"), NULL);
+
+ spin_dir_z = spin_button_new (&adj,
+ mapvals.lightsource[k].direction.z,
+ -100.0, 100.0, 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 4, 3,
+ _("Z:"), 0.0, 0.5,
+ spin_dir_z, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (apply_settings),
+ NULL);
+
+ gimp_help_set_help_data (spin_dir_z,
+ _("Light source Z direction in XYZ space"),
+ NULL);
+
+ isolate_button = gtk_check_button_new_with_mnemonic (_("I_solate"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (isolate_button),
+ mapvals.light_isolated);
+ g_signal_connect (isolate_button, "toggled",
+ G_CALLBACK (isolate_selected_light),
+ NULL);
+ gtk_table_attach_defaults (GTK_TABLE (table), isolate_button, 0, 1, 5, 6);
+ gtk_widget_show (isolate_button);
+
+ label = gtk_label_new (_("Lighting preset:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 1.0);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 5, 12);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 2, 6, 7);
+ gtk_widget_show (label);
+
+ button = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 2, 4, 6, 7);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (save_lighting_preset),
+ NULL);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_mnemonic (_("_Open"));
+ gtk_table_attach_defaults (GTK_TABLE (table), button, 4, 6, 6, 7);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (load_lighting_preset),
+ NULL);
+ gtk_widget_show (button);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/*********************************/
+/* Create material settings page */
+/*********************************/
+
+static GtkWidget *
+create_material_page (void)
+{
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ GtkWidget *spinbutton;
+ GtkWidget *image;
+ GtkWidget *button;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Material Properties"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ table = gtk_table_new (5, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Ambient intensity */
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_AMBIENT_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("_Glowing:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.ambient_int,
+ 0, G_MAXFLOAT, 0.01, 0.1, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.material.ambient_int);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gimp_help_set_help_data (spinbutton,
+ _("Amount of original color to show where no "
+ "direct light falls"), NULL);
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_AMBIENT_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Diffuse intensity */
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_DIFFUSE_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Bright:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.diffuse_int,
+ 0, G_MAXFLOAT, 0.01, 0.1, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.material.diffuse_int);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gimp_help_set_help_data (spinbutton,
+ _("Intensity of original color when lit by a light "
+ "source"), NULL);
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_DIFFUSE_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Specular reflection */
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_SPECULAR_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("_Shiny:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.specular_ref,
+ 0, G_MAXFLOAT, 0.01, 0.1, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.material.specular_ref);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gimp_help_set_help_data (spinbutton,
+ _("Controls how intense the highlights will be"),
+ NULL);
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_SPECULAR_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Highlight */
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_HIGHLIGHT_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
+ _("_Polished:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.highlight,
+ 0, G_MAXFLOAT, 0.01, 0.1, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 3, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.material.highlight);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinbutton);
+ gimp_help_set_help_data (spinbutton,
+ _("Higher values makes the highlights more focused"),
+ NULL);
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_HIGHLIGHT_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 3, 4,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Metallic */
+ button = gtk_check_button_new_with_mnemonic (_("_Metallic"));
+ gtk_table_attach (GTK_TABLE (table), button, 0, 3, 4, 5,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.material.metallic);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/* Create Bump mapping page */
+
+static GtkWidget *
+create_bump_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *toggle;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("E_nable bump mapping"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.bump_mapped);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.bump_mapped);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gimp_help_set_help_data (toggle,
+ _("Enable/disable bump-mapping (image depth)"),
+ NULL);
+
+ table = gtk_table_new (6, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ combo = gimp_drawable_combo_box_new (bumpmap_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), mapvals.bumpmap_id,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &mapvals.bumpmap_id);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (mapmenu2_callback),
+ &mapvals.bumpmap_id);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Bumpm_ap image:"), 0.0, 0.5,
+ combo, 1, FALSE);
+
+ combo = gimp_int_combo_box_new (_("Linear"), LINEAR_MAP,
+ _("Logarithmic"), LOGARITHMIC_MAP,
+ _("Sinusoidal"), SINUSOIDAL_MAP,
+ _("Spherical"), SPHERICAL_MAP,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ mapvals.bumpmaptype);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (mapmenu2_callback),
+ &mapvals.bumpmaptype);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Cu_rve:"), 0.0, 0.5, combo, 1, FALSE);
+
+ spinbutton = spin_button_new (&adj, mapvals.bumpmax,
+ 0, G_MAXFLOAT, 0.01, 0.1, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Ma_ximum height:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.bumpmax);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Maximum height for bumps"),
+ NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+static GtkWidget *
+create_environment_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *toggle;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *combo;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("E_nable environment mapping"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.env_mapped);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.env_mapped);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gimp_help_set_help_data (toggle,
+ _("Enable/disable environment-mapping (reflection)"),
+ NULL);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ combo = gimp_drawable_combo_box_new (envmap_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), mapvals.envmap_id,
+ G_CALLBACK (envmap_combo_callback),
+ NULL);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("En_vironment image:"), 0.0, 0.5,
+ combo, 1, FALSE);
+
+ gimp_help_set_help_data (combo, _("Environment image to use"), NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/*****************************/
+/* Create notebook and pages */
+/*****************************/
+
+static void
+create_main_notebook (GtkWidget *container)
+{
+ GtkWidget *page;
+
+ options_note_book = GTK_NOTEBOOK (gtk_notebook_new ());
+ gtk_container_add (GTK_CONTAINER (container),
+ GTK_WIDGET (options_note_book));
+
+ page = create_options_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("Op_tions")));
+
+ page = create_light_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Light")));
+
+ page = create_material_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Material")));
+
+ page = create_bump_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Bump Map")));
+
+ page = create_environment_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Environment Map")));
+
+ /*
+ if (mapvals.bump_mapped == TRUE)
+ {
+ bump_page = create_bump_page ();
+ bump_page_pos = g_list_length (options_note_book->children);
+ gtk_notebook_append_page (options_note_book, bump_page,
+ gtk_label_new (_("Bumpmap")));
+ }
+
+ if (mapvals.env_mapped == TRUE)
+ {
+ env_page = create_environment_page ();
+ env_page_pos = g_list_length (options_note_book->children);
+ gtk_notebook_append_page (options_note_book, env_page,
+ gtk_label_new (_("Environment")));
+ }
+ */
+ gtk_widget_show (GTK_WIDGET (options_note_book));
+}
+
+/********************************/
+/* Create and show main dialog. */
+/********************************/
+
+gboolean
+main_dialog (gint32 drawable_id)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *toggle;
+ gchar *path;
+ gboolean run = FALSE;
+
+ /*
+ GtkWidget *image;
+ */
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ path = gimp_gimprc_query ("lighting-effects-path");
+ if (path)
+ {
+ lighting_effects_path = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);
+ g_free (path);
+ }
+
+ lighting_stock_init ();
+
+ appwin = gimp_dialog_new (_("Lighting Effects"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (appwin),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (appwin));
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (appwin))),
+ main_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_hbox);
+
+ /* Create the Preview */
+ /* ================== */
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* Add preview widget and various buttons to the first part */
+ /* ======================================================== */
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_realize (appwin);
+
+ previewarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (previewarea, PREVIEW_WIDTH, PREVIEW_HEIGHT);
+ gtk_widget_set_events (previewarea, (GDK_EXPOSURE_MASK |
+ GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK));
+ g_signal_connect (previewarea, "event",
+ G_CALLBACK (preview_events),
+ previewarea);
+ g_signal_connect (previewarea, "expose-event",
+ G_CALLBACK (preview_expose),
+ previewarea);
+ gtk_container_add (GTK_CONTAINER (frame), previewarea);
+ gtk_widget_show (previewarea);
+
+ /* create preview options, frame and vbox */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Update"));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (preview_callback),
+ NULL);
+ gtk_widget_show (button);
+
+ gimp_help_set_help_data (button, _("Recompute preview image"), NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("I_nteractive"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.interactive_preview);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.interactive_preview);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (interactive_preview_callback),
+ NULL);
+
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("Enable/disable real time preview of changes"),
+ NULL);
+
+ create_main_notebook (main_hbox);
+
+ gtk_widget_show (appwin);
+
+ {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (previewarea),
+ GDK_HAND2);
+ gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+ }
+
+ if (image_setup (drawable_id, TRUE))
+ preview_compute ();
+
+ if (gimp_dialog_run (GIMP_DIALOG (appwin)) == GTK_RESPONSE_OK)
+ run = TRUE;
+
+ if (preview_rgb_data != NULL)
+ g_free (preview_rgb_data);
+
+ if (preview_surface != NULL)
+ cairo_surface_destroy (preview_surface);
+
+ gtk_widget_destroy (appwin);
+
+ return run;
+}
+
+
+static void
+save_lighting_preset (GtkWidget *widget,
+ gpointer data)
+{
+ static GtkWidget *window = NULL;
+
+ if (! window)
+ {
+ window =
+ gtk_file_chooser_dialog_new (_("Save Lighting Preset"),
+ GTK_WINDOW (appwin),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (window),
+ TRUE);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window);
+ g_signal_connect (window, "response",
+ G_CALLBACK (save_preset_response),
+ NULL);
+ }
+
+ if (lighting_effects_path)
+ {
+ GList *list;
+ gchar *dir;
+
+ list = gimp_path_parse (lighting_effects_path, 256, FALSE, NULL);
+ dir = gimp_path_get_user_writable_dir (list);
+ gimp_path_free (list);
+
+ if (! dir)
+ dir = g_strdup (gimp_directory ());
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window), dir);
+
+ g_free (dir);
+ }
+ else
+ {
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window),
+ g_get_tmp_dir ());
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+
+static void
+save_preset_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ FILE *fp;
+ gint num_lights = 0;
+ gint k;
+ LightSettings *source;
+ gchar buffer1[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer2[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer3[G_ASCII_DTOSTR_BUF_SIZE];
+ gint blen = G_ASCII_DTOSTR_BUF_SIZE;
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+
+ fp = g_fopen (filename, "wb");
+
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ filename, g_strerror (errno));
+ }
+ else
+ {
+ for (k = 0; k < NUM_LIGHTS; k++)
+ if (mapvals.lightsource[k].type != NO_LIGHT)
+ ++num_lights;
+
+ fprintf (fp, "Number of lights: %d\n", num_lights);
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ if (mapvals.lightsource[k].type != NO_LIGHT)
+ {
+ source = &mapvals.lightsource[k];
+
+ switch (source->type)
+ {
+ case POINT_LIGHT:
+ fprintf (fp, "Type: Point\n");
+ break;
+ case DIRECTIONAL_LIGHT:
+ fprintf (fp, "Type: Directional\n");
+ break;
+ case SPOT_LIGHT:
+ fprintf (fp, "Type: Spot\n");
+ break;
+ default:
+ g_warning ("Unknown light type: %d",
+ mapvals.lightsource[k].type);
+ continue;
+ }
+
+ fprintf (fp, "Position: %s %s %s\n",
+ g_ascii_dtostr (buffer1, blen, source->position.x),
+ g_ascii_dtostr (buffer2, blen, source->position.y),
+ g_ascii_dtostr (buffer3, blen, source->position.z));
+
+ fprintf (fp, "Direction: %s %s %s\n",
+ g_ascii_dtostr (buffer1, blen, source->direction.x),
+ g_ascii_dtostr (buffer2, blen, source->direction.y),
+ g_ascii_dtostr (buffer3, blen, source->direction.z));
+
+ fprintf (fp, "Color: %s %s %s\n",
+ g_ascii_dtostr (buffer1, blen, source->color.r),
+ g_ascii_dtostr (buffer2, blen, source->color.g),
+ g_ascii_dtostr (buffer3, blen, source->color.b));
+
+ fprintf (fp, "Intensity: %s\n",
+ g_ascii_dtostr (buffer1, blen, source->intensity));
+ }
+
+ fclose (fp);
+ }
+
+ g_free (filename);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+}
+
+static void
+load_lighting_preset (GtkWidget *widget,
+ gpointer data)
+{
+ static GtkWidget *window = NULL;
+
+ if (! window)
+ {
+ window =
+ gtk_file_chooser_dialog_new (_("Load Lighting Preset"),
+ GTK_WINDOW (appwin),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (window), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed),
+ &window);
+ g_signal_connect (window, "response",
+ G_CALLBACK (load_preset_response),
+ NULL);
+ }
+
+ if (lighting_effects_path)
+ {
+ GList *list;
+ gchar *dir;
+
+ list = gimp_path_parse (lighting_effects_path, 256, FALSE, NULL);
+ dir = gimp_path_get_user_writable_dir (list);
+ gimp_path_free (list);
+
+ if (! dir)
+ dir = g_strdup (gimp_directory ());
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window), dir);
+
+ g_free (dir);
+ }
+ else
+ {
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (window),
+ g_get_tmp_dir ());
+ }
+
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+
+static void
+load_preset_response (GtkFileChooser *chooser,
+ gint response_id,
+ gpointer data)
+{
+ FILE *fp;
+ gint num_lights;
+ gint k;
+ LightSettings *source;
+ gchar buffer1[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer2[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar buffer3[G_ASCII_DTOSTR_BUF_SIZE];
+ gchar type_label[21];
+ gchar *endptr;
+ gchar fmt_str[32];
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+
+ fp = g_fopen (filename, "rb");
+
+ if (!fp)
+ {
+ g_message (_("Could not open '%s' for reading: %s"),
+ filename, g_strerror (errno));
+ }
+ else
+ {
+ fscanf (fp, "Number of lights: %d", &num_lights);
+
+ /* initialize lights to off */
+ for (k = 0; k < NUM_LIGHTS; k++)
+ mapvals.lightsource[k].type = NO_LIGHT;
+
+ for (k = 0; k < num_lights; k++)
+ {
+ source = &mapvals.lightsource[k];
+
+ fscanf (fp, " Type: %20s", type_label);
+
+ if (!strcmp (type_label, "Point"))
+ source->type = POINT_LIGHT;
+ else if (!strcmp (type_label, "Directional"))
+ source->type = DIRECTIONAL_LIGHT;
+ else if (!strcmp (type_label, "Spot"))
+ source->type = SPOT_LIGHT;
+ else
+ {
+ g_warning ("Unknown light type: %s", type_label);
+ fclose (fp);
+ return;
+ }
+
+ snprintf (fmt_str, sizeof (fmt_str),
+ " Position: %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s",
+ sizeof (buffer1) - 1,
+ sizeof (buffer2) - 1,
+ sizeof (buffer3) - 1);
+ fscanf (fp, fmt_str, buffer1, buffer2, buffer3);
+ source->position.x = g_ascii_strtod (buffer1, &endptr);
+ source->position.y = g_ascii_strtod (buffer2, &endptr);
+ source->position.z = g_ascii_strtod (buffer3, &endptr);
+
+ snprintf (fmt_str, sizeof (fmt_str),
+ " Direction: %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s",
+ sizeof (buffer1) - 1,
+ sizeof (buffer2) - 1,
+ sizeof (buffer3) - 1);
+ fscanf (fp, fmt_str, buffer1, buffer2, buffer3);
+ source->direction.x = g_ascii_strtod (buffer1, &endptr);
+ source->direction.y = g_ascii_strtod (buffer2, &endptr);
+ source->direction.z = g_ascii_strtod (buffer3, &endptr);
+
+ snprintf (fmt_str, sizeof (fmt_str),
+ " Color: %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s %%%" G_GSIZE_FORMAT "s",
+ sizeof (buffer1) - 1,
+ sizeof (buffer2) - 1,
+ sizeof (buffer3) - 1);
+ fscanf (fp, fmt_str, buffer1, buffer2, buffer3);
+ source->color.r = g_ascii_strtod (buffer1, &endptr);
+ source->color.g = g_ascii_strtod (buffer2, &endptr);
+ source->color.b = g_ascii_strtod (buffer3, &endptr);
+ source->color.a = 1.0;
+
+ snprintf (fmt_str, sizeof (fmt_str),
+ " Intensity: %%%" G_GSIZE_FORMAT "s",
+ sizeof (buffer1) - 1);
+ fscanf (fp, fmt_str, buffer1);
+ source->intensity = g_ascii_strtod (buffer1, &endptr);
+
+ }
+
+ fclose (fp);
+ }
+
+ g_free (filename);
+
+ lightselect_callback (GIMP_INT_COMBO_BOX (lightselect_combo), NULL);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (chooser));
+ interactive_preview_callback (GTK_WIDGET (chooser));
+}
+
+
+static void
+lightselect_callback (GimpIntComboBox *combo,
+ gpointer data)
+{
+ gint valid;
+ gint j, k;
+
+ valid = gimp_int_combo_box_get_active (combo, &k);
+
+ if (valid)
+ {
+ mapvals.update_enabled = FALSE; /* prevent apply_settings() */
+
+ mapvals.light_selected = k;
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (light_type_combo),
+ mapvals.lightsource[k].type);
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (colorbutton),
+ &mapvals.lightsource[k].color);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_x),
+ mapvals.lightsource[k].position.x);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_y),
+ mapvals.lightsource[k].position.y);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_pos_z),
+ mapvals.lightsource[k].position.z);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_x),
+ mapvals.lightsource[k].direction.x);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_y),
+ mapvals.lightsource[k].direction.y);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_dir_z),
+ mapvals.lightsource[k].direction.z);
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON(spin_intensity),
+ mapvals.lightsource[k].intensity);
+
+ mapvals.update_enabled = TRUE;
+
+ /* if we are isolating a light, need to switch */
+ if (mapvals.light_isolated)
+ {
+ for (j = 0; j < NUM_LIGHTS; j++)
+ if (j == mapvals.light_selected)
+ mapvals.lightsource[j].active = TRUE;
+ else
+ mapvals.lightsource[j].active = FALSE;
+ }
+
+ interactive_preview_callback (NULL);
+ }
+}
+
+static void
+isolate_selected_light (GtkWidget *widget,
+ gpointer data)
+{
+ gint k;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
+ {
+ mapvals.light_isolated = TRUE;
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ if (k == mapvals.light_selected)
+ mapvals.lightsource[k].active = TRUE;
+ else
+ mapvals.lightsource[k].active = FALSE;
+ }
+ else
+ {
+ mapvals.light_isolated = FALSE;
+
+ for (k = 0; k < NUM_LIGHTS; k++)
+ mapvals.lightsource[k].active = TRUE;
+ }
+
+ interactive_preview_callback (NULL);
+}
+
+static GtkWidget *
+spin_button_new (GtkAdjustment **adjustment, /* return value */
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits)
+{
+ GtkWidget *spinbutton;
+
+ *adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0);
+
+ spinbutton = gimp_spin_button_new (GTK_ADJUSTMENT (*adjustment),
+ climb_rate, digits);
+
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ return spinbutton;
+}
diff --git a/plug-ins/lighting/lighting-ui.h b/plug-ins/lighting/lighting-ui.h
new file mode 100644
index 0000000..ad0ffba
--- /dev/null
+++ b/plug-ins/lighting/lighting-ui.h
@@ -0,0 +1,21 @@
+#ifndef __LIGHTING_UI_H__
+#define __LIGHTING_UI_H__
+
+/* Externally visible variables */
+/* ============================ */
+
+extern GtkWidget *previewarea;
+
+extern GtkWidget *spin_pos_x;
+extern GtkWidget *spin_pos_y;
+extern GtkWidget *spin_pos_z;
+extern GtkWidget *spin_dir_x;
+extern GtkWidget *spin_dir_y;
+extern GtkWidget *spin_dir_z;
+
+/* Externally visible functions */
+/* ============================ */
+
+gboolean main_dialog (gint32 drawable_id);
+
+#endif /* __LIGHTING_UI_H__ */
diff --git a/plug-ins/map-object/Makefile.am b/plug-ins/map-object/Makefile.am
new file mode 100644
index 0000000..b78b18b
--- /dev/null
+++ b/plug-ins/map-object/Makefile.am
@@ -0,0 +1,65 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+map_object_RC = map-object.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/map-object
+
+libexec_PROGRAMS = map-object
+
+map_object_SOURCES = \
+ arcball.c \
+ arcball.h \
+ map-object-apply.c \
+ map-object-apply.h \
+ map-object-image.c \
+ map-object-image.h \
+ map-object-main.c \
+ map-object-main.h \
+ map-object-preview.c \
+ map-object-preview.h \
+ map-object-shade.c \
+ map-object-shade.h \
+ map-object-stock.c \
+ map-object-stock.h \
+ map-object-ui.c \
+ map-object-ui.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(map_object_RC)
diff --git a/plug-ins/map-object/Makefile.in b/plug-ins/map-object/Makefile.in
new file mode 100644
index 0000000..b006121
--- /dev/null
+++ b/plug-ins/map-object/Makefile.in
@@ -0,0 +1,1046 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = map-object$(EXEEXT)
+subdir = plug-ins/map-object
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_map_object_OBJECTS = arcball.$(OBJEXT) map-object-apply.$(OBJEXT) \
+ map-object-image.$(OBJEXT) map-object-main.$(OBJEXT) \
+ map-object-preview.$(OBJEXT) map-object-shade.$(OBJEXT) \
+ map-object-stock.$(OBJEXT) map-object-ui.$(OBJEXT)
+map_object_OBJECTS = $(am_map_object_OBJECTS)
+map_object_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+map_object_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(map_object_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/arcball.Po \
+ ./$(DEPDIR)/map-object-apply.Po \
+ ./$(DEPDIR)/map-object-image.Po ./$(DEPDIR)/map-object-main.Po \
+ ./$(DEPDIR)/map-object-preview.Po \
+ ./$(DEPDIR)/map-object-shade.Po \
+ ./$(DEPDIR)/map-object-stock.Po ./$(DEPDIR)/map-object-ui.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(map_object_SOURCES)
+DIST_SOURCES = $(map_object_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README TODO
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/map-object
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@map_object_RC = map-object.rc.o
+AM_LDFLAGS = $(mwindows)
+map_object_SOURCES = \
+ arcball.c \
+ arcball.h \
+ map-object-apply.c \
+ map-object-apply.h \
+ map-object-image.c \
+ map-object-image.h \
+ map-object-main.c \
+ map-object-main.h \
+ map-object-preview.c \
+ map-object-preview.h \
+ map-object-shade.c \
+ map-object-shade.h \
+ map-object-stock.c \
+ map-object-stock.h \
+ map-object-ui.c \
+ map-object-ui.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(map_object_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/map-object/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/map-object/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+map-object$(EXEEXT): $(map_object_OBJECTS) $(map_object_DEPENDENCIES) $(EXTRA_map_object_DEPENDENCIES)
+ @rm -f map-object$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(map_object_OBJECTS) $(map_object_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arcball.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-apply.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-image.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-shade.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-stock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map-object-ui.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/arcball.Po
+ -rm -f ./$(DEPDIR)/map-object-apply.Po
+ -rm -f ./$(DEPDIR)/map-object-image.Po
+ -rm -f ./$(DEPDIR)/map-object-main.Po
+ -rm -f ./$(DEPDIR)/map-object-preview.Po
+ -rm -f ./$(DEPDIR)/map-object-shade.Po
+ -rm -f ./$(DEPDIR)/map-object-stock.Po
+ -rm -f ./$(DEPDIR)/map-object-ui.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/arcball.Po
+ -rm -f ./$(DEPDIR)/map-object-apply.Po
+ -rm -f ./$(DEPDIR)/map-object-image.Po
+ -rm -f ./$(DEPDIR)/map-object-main.Po
+ -rm -f ./$(DEPDIR)/map-object-preview.Po
+ -rm -f ./$(DEPDIR)/map-object-shade.Po
+ -rm -f ./$(DEPDIR)/map-object-stock.Po
+ -rm -f ./$(DEPDIR)/map-object-ui.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/map-object/README b/plug-ins/map-object/README
new file mode 100644
index 0000000..6c6e228
--- /dev/null
+++ b/plug-ins/map-object/README
@@ -0,0 +1,61 @@
+
+MapObject 1.2.0 -- image filter plug-in for GIMP
+===========================================================
+
+Copyright (C) 1996-98 Tom Bech
+Copyright (C) 1996-98 Federico Mena Quintero
+
+Released 16th of July, 1998
+
+You can reach the author(s) via E-mail:
+tomb@gimp.org (Tom) or quartic@gimp.org (Federico).
+
+GIMP was developed by Peter Mattis and Spencer Kimball.
+You can contact them at gimp@xcf.berkeley.edu.
+
+There's more GIMP stuff on our home pages:
+http://www.ii.uib.no/~tomb/gimp.html (Tom's page)
+http://www.nuclecu.unam.mx/~federico/gimp/index.html (Quartic's page)
+
+Legal stuff
+===========
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+In other words, you can't sue us for whatever happens while using this ;)
+
+Compiling
+=========
+
+To compile you'll need GIMP 1.0 and GTK+ 1.0.4 or later.
+You'll also need GCK 1.00 (http://www.ii.uib.no/~tomb/gck.html)
+
+1) Edit the Makefile to reflect your system setup.
+
+2) Type "make" and then "make install"
+
+You should now be ready to run. "make install" puts the executable "MapObject"
+in the standard plug-in directory.
+
+Documentation
+=============
+
+Ahem.. right.. ;) ..I'll get around to it eventually.
+
+Please send me a mail if you find any bugs.
+
+Have fun,
+
+Tom
+
diff --git a/plug-ins/map-object/TODO b/plug-ins/map-object/TODO
new file mode 100644
index 0000000..fc9b41c
--- /dev/null
+++ b/plug-ins/map-object/TODO
@@ -0,0 +1,18 @@
+
+The MapObject plug-in "todo"-list:
+=================================
+
+* Interactive positioning of directional light
+* Rotation by mouse (doesn't work correctly yet and is disabled).
+* Faster mapping code
+* Multiple light-sources
+* More objects?
+* Presets (including save/load)
+* Gray-scale/channels support
+* Documentation
+* Autoconf/automake stuff?
+
+If there's anything you would like to add, feel free
+to send me any suggestions for new stuff or improvements.
+
+Tom
diff --git a/plug-ins/map-object/arcball.c b/plug-ins/map-object/arcball.c
new file mode 100644
index 0000000..1f509d4
--- /dev/null
+++ b/plug-ins/map-object/arcball.c
@@ -0,0 +1,515 @@
+/************************************/
+/* ArcBall.c (c) Ken Shoemake, 1993 */
+/* Modified by Tom Bech, 1996 */
+/************************************/
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "arcball.h"
+
+/* Global variables */
+/* ================ */
+
+Quat qOne = { 0, 0, 0, 1 };
+
+static HVect center;
+static double radius;
+static Quat qNow, qDown, qDrag;
+static HVect vNow, vDown, vFrom, vTo, vrFrom, vrTo;
+static HMatrix mNow, mDown;
+static unsigned int showResult, dragging;
+static ConstraintSet sets[NSets];
+static int setSizes[NSets];
+static AxisSet axisSet;
+static int axisIndex;
+
+static HMatrix mId =
+{
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+};
+
+static double otherAxis[][4] =
+{
+ {-0.48, 0.80, 0.36, 1}
+};
+
+/* Internal methods */
+/* ================ */
+
+static void Qt_ToMatrix(Quat q,HMatrix out);
+static Quat Qt_Conj(Quat q);
+static Quat Qt_Mul(Quat qL, Quat qR);
+static Quat Qt_FromBallPoints(HVect from, HVect to);
+static void Qt_ToBallPoints(Quat q, HVect *arcFrom, HVect *arcTo);
+
+static HVect V3_(double x, double y, double z);
+static double V3_Norm(HVect v);
+static HVect V3_Unit(HVect v);
+static HVect V3_Scale(HVect v, double s);
+static HVect V3_Negate(HVect v);
+/*
+static HVect V3_Add(HVect v1, HVect v2);
+*/
+static HVect V3_Sub(HVect v1, HVect v2);
+static double V3_Dot(HVect v1, HVect v2);
+/*
+static HVect V3_Cross(HVect v1, HVect v2);
+static HVect V3_Bisect(HVect v0, HVect v1);
+*/
+
+static HVect MouseOnSphere(HVect mouse, HVect ballCenter, double ballRadius);
+static HVect ConstrainToAxis(HVect loose, HVect axis);
+static int NearestConstraintAxis(HVect loose, HVect *axes, int nAxes);
+
+/* Establish reasonable initial values for controller. */
+/* =================================================== */
+
+void
+ArcBall_Init (void)
+{
+ int i;
+
+ center = qOne;
+ radius = 1.0;
+ vDown = vNow = qOne;
+ qDown = qNow = qOne;
+ for (i=15; i>=0; i--)
+ ((double *)mNow)[i] = ((double *)mDown)[i] = ((double *)mId)[i];
+
+ showResult = dragging = FALSE;
+ axisSet = NoAxes;
+ sets[CameraAxes] = mId[X];
+ setSizes[CameraAxes] = 3;
+ sets[BodyAxes] = mDown[X];
+ setSizes[BodyAxes] = 3;
+ sets[OtherAxes] = otherAxis[X];
+ setSizes[OtherAxes] = 1;
+}
+
+/* Set the center and size of the controller. */
+/* ========================================== */
+
+void
+ArcBall_Place (HVect Center,
+ double Radius)
+{
+ center = Center;
+ radius = Radius;
+}
+
+/* Incorporate new mouse position. */
+/* =============================== */
+
+void
+ArcBall_Mouse (HVect v_Now)
+{
+ vNow = v_Now;
+}
+
+/* Choose a constraint set, or none. */
+/* ================================= */
+
+void
+ArcBall_UseSet (AxisSet axis_Set)
+{
+ if (!dragging) axisSet = axis_Set;
+}
+
+/* Using vDown, vNow, dragging, and axisSet, compute rotation etc. */
+/* =============================================================== */
+
+void
+ArcBall_Update (void)
+{
+ int setSize = setSizes[axisSet];
+ HVect *set = (HVect *)(sets[axisSet]);
+
+ vFrom = MouseOnSphere(vDown, center, radius);
+ vTo = MouseOnSphere(vNow, center, radius);
+ if (dragging)
+ {
+ if (axisSet!=NoAxes)
+ {
+ vFrom = ConstrainToAxis(vFrom, set[axisIndex]);
+ vTo = ConstrainToAxis(vTo, set[axisIndex]);
+ }
+ qDrag = Qt_FromBallPoints(vFrom, vTo);
+ qNow = Qt_Mul(qDrag, qDown);
+ }
+ else
+ {
+ if (axisSet!=NoAxes) axisIndex = NearestConstraintAxis(vTo, set, setSize);
+ }
+ Qt_ToBallPoints(qDown, &vrFrom, &vrTo);
+ Qt_ToMatrix(Qt_Conj(qNow), mNow); /* Gives transpose for GL. */
+}
+
+/* Return rotation matrix defined by controller use. */
+/* ================================================= */
+
+void
+ArcBall_Value (HMatrix m_Now)
+{
+ ArcBall_CopyMat (mNow, m_Now);
+}
+
+/* Extract rotation angles from matrix */
+/* =================================== */
+
+void
+ArcBall_Values (double *alpha,
+ double *beta,
+ double *gamma)
+{
+ if ((*beta=asin(-mNow[0][2]))!=0.0)
+ {
+ *gamma=atan2(mNow[1][2],mNow[2][2]);
+ *alpha=atan2(mNow[0][1],mNow[0][0]);
+ }
+ else
+ {
+ *gamma=atan2(mNow[1][0],mNow[1][1]);
+ *alpha=0.0;
+ }
+}
+
+/* Begin drag sequence. */
+/* ==================== */
+
+void
+ArcBall_BeginDrag (void)
+{
+ dragging = TRUE;
+ vDown = vNow;
+}
+
+/* Stop drag sequence. */
+/* =================== */
+
+void
+ArcBall_EndDrag (void)
+{
+ dragging = FALSE;
+ qDown = qNow;
+
+ ArcBall_CopyMat (mNow, mDown);
+}
+
+/*===================*/
+/***** BallAux.c *****/
+/*===================*/
+
+/* Return quaternion product qL * qR. Note: order is important! */
+/* To combine rotations, use the product Mul(qSecond, qFirst), */
+/* which gives the effect of rotating by qFirst then qSecond. */
+/* ============================================================= */
+
+static Quat
+Qt_Mul (Quat qL,
+ Quat qR)
+{
+ Quat qq;
+ qq.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;
+ qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
+ qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;
+ qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;
+ return (qq);
+}
+
+/* Construct rotation matrix from (possibly non-unit) quaternion. */
+/* Assumes matrix is used to multiply column vector on the left: */
+/* vnew = mat vold. Works correctly for right-handed coordinate */
+/* system and right-handed rotations. */
+/* ============================================================== */
+
+static void
+Qt_ToMatrix (Quat q,
+ HMatrix out)
+{
+ double Nq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
+ double xs = q.x*s, ys = q.y*s, zs = q.z*s;
+ double wx = q.w*xs, wy = q.w*ys, wz = q.w*zs;
+ double xx = q.x*xs, xy = q.x*ys, xz = q.x*zs;
+ double yy = q.y*ys, yz = q.y*zs, zz = q.z*zs;
+ out[X][X] = 1.0 - (yy + zz); out[Y][X] = xy + wz; out[Z][X] = xz - wy;
+ out[X][Y] = xy - wz; out[Y][Y] = 1.0 - (xx + zz); out[Z][Y] = yz + wx;
+ out[X][Z] = xz + wy; out[Y][Z] = yz - wx; out[Z][Z] = 1.0 - (xx + yy);
+ out[X][W] = out[Y][W] = out[Z][W] = out[W][X] = out[W][Y] = out[W][Z] = 0.0;
+ out[W][W] = 1.0;
+}
+
+/* Return conjugate of quaternion. */
+/* =============================== */
+
+static Quat
+Qt_Conj (Quat q)
+{
+ Quat qq;
+ qq.x = -q.x; qq.y = -q.y; qq.z = -q.z; qq.w = q.w;
+ return (qq);
+}
+
+/* Return vector formed from components */
+/* ==================================== */
+
+static HVect
+V3_ (double x,
+ double y,
+ double z)
+{
+ HVect v;
+ v.x = x; v.y = y; v.z = z; v.w = 0;
+ return (v);
+}
+
+/* Return norm of v, defined as sum of squares of components */
+/* ========================================================= */
+
+static double
+V3_Norm (HVect v)
+{
+ return ( v.x*v.x + v.y*v.y + v.z*v.z );
+}
+
+/* Return unit magnitude vector in direction of v */
+/* ============================================== */
+
+static HVect
+V3_Unit (HVect v)
+{
+ static HVect u = {0, 0, 0, 0};
+ double vlen = sqrt(V3_Norm(v));
+
+ if (vlen != 0.0)
+ {
+ u.x = v.x/vlen;
+ u.y = v.y/vlen;
+ u.z = v.z/vlen;
+ }
+ return (u);
+}
+
+/* Return version of v scaled by s */
+/* =============================== */
+
+static HVect
+V3_Scale (HVect v,
+ double s)
+{
+ HVect u;
+ u.x = s*v.x; u.y = s*v.y; u.z = s*v.z; u.w = v.w;
+ return (u);
+}
+
+/* Return negative of v */
+/* ==================== */
+
+static HVect
+V3_Negate (HVect v)
+{
+ static HVect u = {0, 0, 0, 0};
+ u.x = -v.x; u.y = -v.y; u.z = -v.z;
+ return (u);
+}
+
+/* Return sum of v1 and v2 */
+/* ======================= */
+/*
+static HVect
+V3_Add (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.x+v2.x; v.y = v1.y+v2.y; v.z = v1.z+v2.z;
+ return (v);
+}
+*/
+/* Return difference of v1 minus v2 */
+/* ================================ */
+
+static HVect
+V3_Sub (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.x-v2.x; v.y = v1.y-v2.y; v.z = v1.z-v2.z;
+ return (v);
+}
+
+/* Halve arc between unit vectors v0 and v1. */
+/* ========================================= */
+/*
+static HVect
+V3_Bisect (HVect v0,
+ HVect v1)
+{
+ HVect v = {0, 0, 0, 0};
+ double Nv;
+
+ v = V3_Add(v0, v1);
+ Nv = V3_Norm(v);
+ if (Nv < 1.0e-5) v = V3_(0, 0, 1);
+ else v = V3_Scale(v, 1/sqrt(Nv));
+ return (v);
+}
+*/
+
+/* Return dot product of v1 and v2 */
+/* =============================== */
+
+static double
+V3_Dot (HVect v1,
+ HVect v2)
+{
+ return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
+}
+
+
+/* Return cross product, v1 x v2 */
+/* ============================= */
+/*
+static HVect
+V3_Cross (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.y*v2.z-v1.z*v2.y;
+ v.y = v1.z*v2.x-v1.x*v2.z;
+ v.z = v1.x*v2.y-v1.y*v2.x;
+ return (v);
+}
+*/
+
+void
+ArcBall_CopyMat (HMatrix inm,
+ HMatrix outm)
+{
+ int x=0,y=0;
+
+ for (x=0;x<4;x++)
+ {
+ for (y=0;y<4;y++)
+ {
+ outm[y][x]=inm[y][x];
+ }
+ }
+}
+
+/*=====================================================*/
+/**** BallMath.c - Essential routines for ArcBall. ****/
+/*=====================================================*/
+
+/* Convert window coordinates to sphere coordinates. */
+/* ================================================= */
+
+static HVect
+MouseOnSphere (HVect mouse,
+ HVect ballCenter,
+ double ballRadius)
+{
+ HVect ballMouse;
+ register double mag;
+
+ ballMouse.x = (mouse.x - ballCenter.x) / ballRadius;
+ ballMouse.y = (mouse.y - ballCenter.y) / ballRadius;
+ mag = ballMouse.x*ballMouse.x + ballMouse.y*ballMouse.y;
+ if (mag > 1.0)
+ {
+ register double scale = 1.0/sqrt(mag);
+ ballMouse.x *= scale; ballMouse.y *= scale;
+ ballMouse.z = 0.0;
+ }
+ else ballMouse.z = sqrt(1 - mag);
+ ballMouse.w = 0.0;
+ return (ballMouse);
+}
+
+/* Construct a unit quaternion from two points on unit sphere */
+/* ========================================================== */
+
+static Quat
+Qt_FromBallPoints (HVect from,
+ HVect to)
+{
+ Quat qu;
+ qu.x = from.y*to.z - from.z*to.y;
+ qu.y = from.z*to.x - from.x*to.z;
+ qu.z = from.x*to.y - from.y*to.x;
+ qu.w = from.x*to.x + from.y*to.y + from.z*to.z;
+ return (qu);
+}
+
+/* Convert a unit quaternion to two points on unit sphere */
+/* ====================================================== */
+
+static void
+Qt_ToBallPoints (Quat q,
+ HVect *arcFrom,
+ HVect *arcTo)
+{
+ double s;
+
+ s = sqrt(q.x*q.x + q.y*q.y);
+ if (s == 0.0) *arcFrom = V3_(0.0, 1.0, 0.0);
+ else *arcFrom = V3_(-q.y/s, q.x/s, 0.0);
+ arcTo->x = q.w*arcFrom->x - q.z*arcFrom->y;
+ arcTo->y = q.w*arcFrom->y + q.z*arcFrom->x;
+ arcTo->z = q.x*arcFrom->y - q.y*arcFrom->x;
+ if (q.w < 0.0) *arcFrom = V3_(-arcFrom->x, -arcFrom->y, 0.0);
+}
+
+/* Force sphere point to be perpendicular to axis. */
+/* =============================================== */
+
+static HVect
+ConstrainToAxis (HVect loose,
+ HVect axis)
+{
+ HVect onPlane;
+ register double norm;
+
+ onPlane = V3_Sub(loose, V3_Scale(axis, V3_Dot(axis, loose)));
+ norm = V3_Norm(onPlane);
+ if (norm > 0.0)
+ {
+ if (onPlane.z < 0.0) onPlane = V3_Negate(onPlane);
+ return ( V3_Scale(onPlane, 1/sqrt(norm)) );
+ }
+ /* else drop through */
+ /* ================= */
+
+ if (axis.z == 1) onPlane = V3_(1.0, 0.0, 0.0);
+ else onPlane = V3_Unit(V3_(-axis.y, axis.x, 0.0));
+ return (onPlane);
+}
+
+/* Find the index of nearest arc of axis set. */
+/* ========================================== */
+
+static int
+NearestConstraintAxis (HVect loose,
+ HVect *axes,
+ int nAxes)
+{
+ HVect onPlane;
+ register double max, dot;
+ register int i, nearest;
+ max = -1; nearest = 0;
+
+ for (i=0; i<nAxes; i++)
+ {
+ onPlane = ConstrainToAxis(loose, axes[i]);
+ dot = V3_Dot(onPlane, loose);
+ if (dot>max)
+ {
+ max = dot; nearest = i;
+ }
+ }
+ return (nearest);
+}
diff --git a/plug-ins/map-object/arcball.h b/plug-ins/map-object/arcball.h
new file mode 100644
index 0000000..f0b56ce
--- /dev/null
+++ b/plug-ins/map-object/arcball.h
@@ -0,0 +1,50 @@
+#ifndef __ARCBALL_H__
+#define __ARCBALL_H__
+
+typedef struct
+{
+ double x, y, z, w;
+} Quat;
+
+enum QuatPart
+{
+ X,
+ Y,
+ Z,
+ W,
+ QuatLen
+};
+
+typedef Quat HVect;
+
+typedef double HMatrix[QuatLen][QuatLen];
+
+typedef enum AxisSet
+{
+ NoAxes,
+ CameraAxes,
+ BodyAxes,
+ OtherAxes,
+ NSets
+} AxisSet;
+
+typedef double *ConstraintSet;
+
+extern Quat qOne;
+
+void ArcBall_Init (void);
+void ArcBall_Place (HVect Center,
+ double Radius);
+void ArcBall_UseSet (AxisSet axis_Set);
+void ArcBall_Update (void);
+void ArcBall_Value (HMatrix m_Now);
+void ArcBall_Values (double *alpha,
+ double *beta,
+ double *gamma);
+void ArcBall_BeginDrag (void);
+void ArcBall_EndDrag (void);
+void ArcBall_Mouse (HVect v_Now);
+void ArcBall_CopyMat (HMatrix inm,
+ HMatrix outm);
+
+#endif /* __ARCBALL_H__ */
diff --git a/plug-ins/map-object/map-object-apply.c b/plug-ins/map-object/map-object-apply.c
new file mode 100644
index 0000000..0a2fb9b
--- /dev/null
+++ b/plug-ins/map-object/map-object-apply.c
@@ -0,0 +1,328 @@
+/******************************************************/
+/* Apply mapping and shading on the whole input image */
+/******************************************************/
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "map-object-main.h"
+#include "map-object-image.h"
+#include "map-object-shade.h"
+#include "map-object-apply.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/*************/
+/* Main loop */
+/*************/
+
+gdouble imat[4][4];
+gfloat rotmat[16];
+static gfloat a[16], b[16];
+
+void
+init_compute (void)
+{
+ gint i;
+
+ switch (mapvals.maptype)
+ {
+ case MAP_SPHERE:
+
+ /* Rotate the equator/northpole axis */
+ /* ================================= */
+
+ gimp_vector3_set (&mapvals.firstaxis, 0.0, 0.0, -1.0);
+ gimp_vector3_set (&mapvals.secondaxis, 0.0, 1.0, 0.0);
+
+ gimp_vector3_rotate (&mapvals.firstaxis,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ gimp_vector3_rotate (&mapvals.secondaxis,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+
+ /* Compute the 2D bounding box of the sphere spanned by the axis */
+ /* ============================================================= */
+
+ compute_bounding_box ();
+
+ get_ray_color = get_ray_color_sphere;
+
+ break;
+
+ case MAP_PLANE:
+
+ /* Rotate the plane axis */
+ /* ===================== */
+
+ gimp_vector3_set (&mapvals.firstaxis, 1.0, 0.0, 0.0);
+ gimp_vector3_set (&mapvals.secondaxis, 0.0, 1.0, 0.0);
+ gimp_vector3_set (&mapvals.normal, 0.0, 0.0, 1.0);
+
+ gimp_vector3_rotate (&mapvals.firstaxis,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ gimp_vector3_rotate (&mapvals.secondaxis,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+
+ mapvals.normal = gimp_vector3_cross_product (&mapvals.firstaxis,
+ &mapvals.secondaxis);
+
+ if (mapvals.normal.z < 0.0)
+ gimp_vector3_mul (&mapvals.normal, -1.0);
+
+ /* Initialize intersection matrix */
+ /* ============================== */
+
+ imat[0][1] = -mapvals.firstaxis.x;
+ imat[1][1] = -mapvals.firstaxis.y;
+ imat[2][1] = -mapvals.firstaxis.z;
+
+ imat[0][2] = -mapvals.secondaxis.x;
+ imat[1][2] = -mapvals.secondaxis.y;
+ imat[2][2] = -mapvals.secondaxis.z;
+
+ imat[0][3] = mapvals.position.x - mapvals.viewpoint.x;
+ imat[1][3] = mapvals.position.y - mapvals.viewpoint.y;
+ imat[2][3] = mapvals.position.z - mapvals.viewpoint.z;
+
+ get_ray_color = get_ray_color_plane;
+
+ break;
+
+ case MAP_BOX:
+ get_ray_color = get_ray_color_box;
+
+ gimp_vector3_set (&mapvals.firstaxis, 1.0, 0.0, 0.0);
+ gimp_vector3_set (&mapvals.secondaxis, 0.0, 1.0, 0.0);
+ gimp_vector3_set (&mapvals.normal, 0.0, 0.0, 1.0);
+
+ ident_mat (rotmat);
+
+ rotatemat (mapvals.alpha, &mapvals.firstaxis, a);
+
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ rotatemat (mapvals.beta, &mapvals.secondaxis, a);
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ rotatemat (mapvals.gamma, &mapvals.normal, a);
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ /* Set up pixel regions for the box face images */
+ /* ============================================ */
+
+ for (i = 0; i < 6; i++)
+ {
+ box_drawable_ids[i] = mapvals.boxmap_id[i];
+
+ box_buffers[i] = gimp_drawable_get_buffer (box_drawable_ids[i]);
+ }
+
+ break;
+
+ case MAP_CYLINDER:
+ get_ray_color = get_ray_color_cylinder;
+
+ gimp_vector3_set (&mapvals.firstaxis, 1.0, 0.0, 0.0);
+ gimp_vector3_set (&mapvals.secondaxis, 0.0, 1.0, 0.0);
+ gimp_vector3_set (&mapvals.normal, 0.0, 0.0, 1.0);
+
+ ident_mat (rotmat);
+
+ rotatemat (mapvals.alpha, &mapvals.firstaxis, a);
+
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ rotatemat (mapvals.beta, &mapvals.secondaxis, a);
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ rotatemat (mapvals.gamma, &mapvals.normal, a);
+ matmul (a, rotmat, b);
+
+ memcpy (rotmat, b, sizeof (gfloat) * 16);
+
+ /* Set up pixel regions for the cylinder cap images */
+ /* ================================================ */
+
+ for (i = 0; i < 2; i++)
+ {
+ cylinder_drawable_ids[i] = mapvals.cylindermap_id[i];
+
+ cylinder_buffers[i] = gimp_drawable_get_buffer (cylinder_drawable_ids[i]);
+ }
+ break;
+ }
+
+ max_depth = (gint) mapvals.maxdepth;
+}
+
+static void
+render (gdouble x,
+ gdouble y,
+ GimpRGB *col,
+ gpointer data)
+{
+ GimpVector3 pos;
+
+ pos.x = x / (gdouble) width;
+ pos.y = y / (gdouble) height;
+ pos.z = 0.0;
+
+ *col = get_ray_color (&pos);
+}
+
+static void
+show_progress (gint min,
+ gint max,
+ gint curr,
+ gpointer data)
+{
+ gimp_progress_update ((gdouble) curr / (gdouble) max);
+}
+
+/**************************************************/
+/* Performs map-to-sphere on the whole input image */
+/* and updates or creates a new GIMP image. */
+/**************************************************/
+
+void
+compute_image (void)
+{
+ gint xcount, ycount;
+ GimpRGB color;
+ glong progress_counter = 0;
+ GimpVector3 p;
+ gint32 new_image_id = -1;
+ gint32 new_layer_id = -1;
+ gboolean insert_layer = FALSE;
+
+ init_compute ();
+
+ if (mapvals.create_new_image)
+ {
+ new_image_id = gimp_image_new (width, height, GIMP_RGB);
+ }
+ else
+ {
+ new_image_id = image_id;
+ }
+
+ gimp_image_undo_group_start (new_image_id);
+
+ if (mapvals.create_new_image ||
+ mapvals.create_new_layer ||
+ (mapvals.transparent_background &&
+ ! gimp_drawable_has_alpha (output_drawable_id)))
+ {
+ gchar *layername[] = {_("Map to plane"),
+ _("Map to sphere"),
+ _("Map to box"),
+ _("Map to cylinder"),
+ _("Background")};
+
+ new_layer_id = gimp_layer_new (new_image_id,
+ layername[mapvals.create_new_image ? 4 :
+ mapvals.maptype],
+ width, height,
+ mapvals.transparent_background ?
+ GIMP_RGBA_IMAGE :
+ GIMP_RGB_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+
+ insert_layer = TRUE;
+ output_drawable_id = new_layer_id;
+ }
+
+ dest_buffer = gimp_drawable_get_shadow_buffer (output_drawable_id);
+
+ switch (mapvals.maptype)
+ {
+ case MAP_PLANE:
+ gimp_progress_init (_("Map to plane"));
+ break;
+ case MAP_SPHERE:
+ gimp_progress_init (_("Map to sphere"));
+ break;
+ case MAP_BOX:
+ gimp_progress_init (_("Map to box"));
+ break;
+ case MAP_CYLINDER:
+ gimp_progress_init (_("Map to cylinder"));
+ break;
+ }
+
+ if (! mapvals.antialiasing)
+ {
+ for (ycount = 0; ycount < height; ycount++)
+ {
+ for (xcount = 0; xcount < width; xcount++)
+ {
+ p = int_to_pos (xcount, ycount);
+ color = (* get_ray_color) (&p);
+ poke (xcount, ycount, &color, NULL);
+
+ progress_counter++;
+ }
+
+ gimp_progress_update ((gdouble) progress_counter /
+ (gdouble) maxcounter);
+ }
+ }
+ else
+ {
+ gimp_adaptive_supersample_area (0, 0,
+ width - 1, height - 1,
+ max_depth,
+ mapvals.pixelthreshold,
+ render,
+ NULL,
+ poke,
+ NULL,
+ show_progress,
+ NULL);
+ }
+
+ gimp_progress_update (1.0);
+
+ g_object_unref (source_buffer);
+ g_object_unref (dest_buffer);
+
+ if (insert_layer)
+ gimp_image_insert_layer (new_image_id, new_layer_id, -1, 0);
+
+ gimp_drawable_merge_shadow (output_drawable_id, TRUE);
+ gimp_drawable_update (output_drawable_id, 0, 0, width, height);
+
+ if (new_image_id != image_id)
+ {
+ gimp_display_new (new_image_id);
+ gimp_displays_flush ();
+ }
+
+ gimp_image_undo_group_end (new_image_id);
+}
diff --git a/plug-ins/map-object/map-object-apply.h b/plug-ins/map-object/map-object-apply.h
new file mode 100644
index 0000000..7b06eda
--- /dev/null
+++ b/plug-ins/map-object/map-object-apply.h
@@ -0,0 +1,10 @@
+#ifndef __MAPOBJECT_APPLY_H__
+#define __MAPOBJECT_APPLY_H__
+
+extern gdouble imat[4][4];
+extern gfloat rotmat[16];
+
+void init_compute (void);
+void compute_image (void);
+
+#endif /* __MAPOBJECT_APPLY_H__ */
diff --git a/plug-ins/map-object/map-object-image.c b/plug-ins/map-object/map-object-image.c
new file mode 100644
index 0000000..1f16279
--- /dev/null
+++ b/plug-ins/map-object/map-object-image.c
@@ -0,0 +1,352 @@
+/*********************************************************/
+/* Image manipulation routines. Calls mapobject_shade.c */
+/* functions to compute the shading of the image at each */
+/* pixel. These routines are used by the functions in */
+/* mapobject_preview.c and mapobject_apply.c */
+/*********************************************************/
+
+#include "config.h"
+
+#include <string.h>
+
+#include <sys/types.h>
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "map-object-main.h"
+#include "map-object-preview.h"
+#include "map-object-shade.h"
+#include "map-object-ui.h"
+#include "map-object-image.h"
+
+
+gint32 input_drawable_id;
+gint32 output_drawable_id;
+GeglBuffer *source_buffer;
+GeglBuffer *dest_buffer;
+
+gint32 box_drawable_ids[6];
+GeglBuffer *box_buffers[6];
+
+gint32 cylinder_drawable_ids[2];
+GeglBuffer *cylinder_buffers[2];
+
+guchar *preview_rgb_data = NULL;
+gint preview_rgb_stride;
+cairo_surface_t *preview_surface = NULL;
+
+glong maxcounter, old_depth, max_depth;
+gint width, height, image_id;
+GimpRGB background;
+
+gint border_x, border_y, border_w, border_h;
+
+/******************/
+/* Implementation */
+/******************/
+
+GimpRGB
+peek (gint x,
+ gint y)
+{
+ GimpRGB color;
+
+ gegl_buffer_sample (source_buffer, x, y, NULL,
+ &color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (! babl_format_has_alpha (gegl_buffer_get_format (source_buffer)))
+ color.a = 1.0;
+
+ return color;
+}
+
+static GimpRGB
+peek_box_image (gint image,
+ gint x,
+ gint y)
+{
+ GimpRGB color;
+
+ gegl_buffer_sample (box_buffers[image], x, y, NULL,
+ &color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (! babl_format_has_alpha (gegl_buffer_get_format (box_buffers[image])))
+ color.a = 1.0;
+
+ return color;
+}
+
+static GimpRGB
+peek_cylinder_image (gint image,
+ gint x,
+ gint y)
+{
+ GimpRGB color;
+
+ gegl_buffer_sample (cylinder_buffers[image], x, y, NULL,
+ &color, babl_format ("R'G'B'A double"),
+ GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+
+ if (! babl_format_has_alpha (gegl_buffer_get_format (cylinder_buffers[image])))
+ color.a = 1.0;
+
+ return color;
+}
+
+void
+poke (gint x,
+ gint y,
+ GimpRGB *color,
+ gpointer user_data)
+{
+ gegl_buffer_set (dest_buffer, GEGL_RECTANGLE (x, y, 1, 1), 0,
+ babl_format ("R'G'B'A double"), color,
+ GEGL_AUTO_ROWSTRIDE);
+}
+
+gint
+checkbounds (gint x,
+ gint y)
+{
+ if (x < border_x ||
+ y < border_y ||
+ x >= border_x + border_w ||
+ y >= border_y + border_h)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static gint
+checkbounds_box_image (gint image,
+ gint x,
+ gint y)
+{
+ gint w, h;
+
+ w = gegl_buffer_get_width (box_buffers[image]);
+ h = gegl_buffer_get_height (box_buffers[image]);
+
+ if (x < 0 || y < 0 || x >= w || y >= h)
+ return FALSE ;
+ else
+ return TRUE ;
+}
+
+static gint
+checkbounds_cylinder_image (gint image,
+ gint x,
+ gint y)
+{
+ gint w, h;
+
+ w = gegl_buffer_get_width (cylinder_buffers[image]);
+ h = gegl_buffer_get_height (cylinder_buffers[image]);
+
+ if (x < 0 || y < 0 || x >= w || y >= h)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+GimpVector3
+int_to_pos (gint x,
+ gint y)
+{
+ GimpVector3 pos;
+
+ pos.x = (gdouble) x / (gdouble) width;
+ pos.y = (gdouble) y / (gdouble) height;
+ pos.z = 0.0;
+
+ return pos;
+}
+
+void
+pos_to_int (gdouble x,
+ gdouble y,
+ gint *scr_x,
+ gint *scr_y)
+{
+ *scr_x = (gint) ((x * (gdouble) width));
+ *scr_y = (gint) ((y * (gdouble) height));
+}
+
+/**********************************************/
+/* Compute the image color at pos (u,v) using */
+/* Quartics bilinear interpolation stuff. */
+/**********************************************/
+
+GimpRGB
+get_image_color (gdouble u,
+ gdouble v,
+ gint *inside)
+{
+ gint x1, y1, x2, y2;
+ GimpRGB p[4];
+
+ pos_to_int (u, v, &x1, &y1);
+
+ if (mapvals.tiled == TRUE)
+ {
+ *inside = TRUE;
+
+ if (x1 < 0) x1 = (width-1) - (-x1 % width);
+ else x1 = x1 % width;
+
+ if (y1 < 0) y1 = (height-1) - (-y1 % height);
+ else y1 = y1 % height;
+
+ x2 = (x1 + 1) % width;
+ y2 = (y1 + 1) % height;
+
+ p[0] = peek (x1, y1);
+ p[1] = peek (x2, y1);
+ p[2] = peek (x1, y2);
+ p[3] = peek (x2, y2);
+
+ return gimp_bilinear_rgba (u * width, v * height, p);
+ }
+
+ if (checkbounds (x1, y1) == FALSE)
+ {
+ *inside =FALSE;
+
+ return background;
+ }
+
+ x2 = (x1 + 1);
+ y2 = (y1 + 1);
+
+ if (checkbounds (x2, y2) == FALSE)
+ {
+ *inside = TRUE;
+
+ return peek (x1, y1);
+ }
+
+ *inside=TRUE;
+
+ p[0] = peek (x1, y1);
+ p[1] = peek (x2, y1);
+ p[2] = peek (x1, y2);
+ p[3] = peek (x2, y2);
+
+ return gimp_bilinear_rgba (u * width, v * height, p);
+}
+
+GimpRGB
+get_box_image_color (gint image,
+ gdouble u,
+ gdouble v)
+{
+ gint w, h;
+ gint x1, y1, x2, y2;
+ GimpRGB p[4];
+
+ w = gegl_buffer_get_width (box_buffers[image]);
+ h = gegl_buffer_get_height (box_buffers[image]);
+
+ x1 = (gint) ((u * (gdouble) w));
+ y1 = (gint) ((v * (gdouble) h));
+
+ if (checkbounds_box_image (image, x1, y1) == FALSE)
+ return background;
+
+ x2 = (x1 + 1);
+ y2 = (y1 + 1);
+
+ if (checkbounds_box_image (image, x2, y2) == FALSE)
+ return peek_box_image (image, x1,y1);
+
+ p[0] = peek_box_image (image, x1, y1);
+ p[1] = peek_box_image (image, x2, y1);
+ p[2] = peek_box_image (image, x1, y2);
+ p[3] = peek_box_image (image, x2, y2);
+
+ return gimp_bilinear_rgba (u * w, v * h, p);
+}
+
+GimpRGB
+get_cylinder_image_color (gint image,
+ gdouble u,
+ gdouble v)
+{
+ gint w, h;
+ gint x1, y1, x2, y2;
+ GimpRGB p[4];
+
+ w = gegl_buffer_get_width (cylinder_buffers[image]);
+ h = gegl_buffer_get_height (cylinder_buffers[image]);
+
+ x1 = (gint) ((u * (gdouble) w));
+ y1 = (gint) ((v * (gdouble) h));
+
+ if (checkbounds_cylinder_image (image, x1, y1) == FALSE)
+ return background;
+
+ x2 = (x1 + 1);
+ y2 = (y1 + 1);
+
+ if (checkbounds_cylinder_image (image, x2, y2) == FALSE)
+ return peek_cylinder_image (image, x1,y1);
+
+ p[0] = peek_cylinder_image (image, x1, y1);
+ p[1] = peek_cylinder_image (image, x2, y1);
+ p[2] = peek_cylinder_image (image, x1, y2);
+ p[3] = peek_cylinder_image (image, x2, y2);
+
+ return gimp_bilinear_rgba (u * w, v * h, p);
+}
+
+/****************************************/
+/* Allocate memory for temporary images */
+/****************************************/
+
+gint
+image_setup (gint32 drawable_id,
+ gint interactive)
+{
+ input_drawable_id = drawable_id;
+ output_drawable_id = drawable_id;
+
+ if (! gimp_drawable_mask_intersect (drawable_id, &border_x, &border_y,
+ &border_w, &border_h))
+ return FALSE;
+
+ width = gimp_drawable_width (input_drawable_id);
+ height = gimp_drawable_height (input_drawable_id);
+
+ source_buffer = gimp_drawable_get_buffer (input_drawable_id);
+
+ maxcounter = (glong) width * (glong) height;
+
+ if (mapvals.transparent_background == TRUE)
+ {
+ gimp_rgba_set (&background, 0.0, 0.0, 0.0, 0.0);
+ }
+ else
+ {
+ gimp_context_get_background (&background);
+ gimp_rgb_set_alpha (&background, 1.0);
+ }
+
+ if (interactive == TRUE)
+ {
+ preview_rgb_stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24,
+ PREVIEW_WIDTH);
+ preview_rgb_data = g_new0 (guchar, preview_rgb_stride * PREVIEW_HEIGHT);
+ preview_surface = cairo_image_surface_create_for_data (preview_rgb_data,
+ CAIRO_FORMAT_RGB24,
+ PREVIEW_WIDTH,
+ PREVIEW_HEIGHT,
+ preview_rgb_stride);
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/map-object/map-object-image.h b/plug-ins/map-object/map-object-image.h
new file mode 100644
index 0000000..189204b
--- /dev/null
+++ b/plug-ins/map-object/map-object-image.h
@@ -0,0 +1,62 @@
+#ifndef __MAPOBJECT_IMAGE_H__
+#define __MAPOBJECT_IMAGE_H__
+
+/* Externally visible variables */
+/* ============================ */
+
+extern gint32 input_drawable_id;
+extern gint32 output_drawable_id;
+extern GeglBuffer *source_buffer;
+extern GeglBuffer *dest_buffer;
+
+extern gint32 box_drawable_ids[6];
+extern GeglBuffer *box_buffers[6];
+
+extern gint32 cylinder_drawable_ids[2];
+extern GeglBuffer *cylinder_buffers[2];
+
+extern guchar *preview_rgb_data;
+extern gint preview_rgb_stride;
+extern cairo_surface_t *preview_surface;
+
+extern glong maxcounter, old_depth, max_depth;
+extern gint width, height, image_id;
+extern GimpRGB background;
+
+extern gint border_x1, border_y1, border_x2, border_y2;
+
+/* Externally visible functions */
+/* ============================ */
+
+extern gint image_setup (gint32 drawable_id,
+ gint interactive);
+extern glong in_xy_to_index (gint x,
+ gint y);
+extern glong out_xy_to_index (gint x,
+ gint y);
+extern gint checkbounds (gint x,
+ gint y);
+extern GimpRGB peek (gint x,
+ gint y);
+extern void poke (gint x,
+ gint y,
+ GimpRGB *color,
+ gpointer user_data);
+extern GimpVector3 int_to_pos (gint x,
+ gint y);
+extern void pos_to_int (gdouble x,
+ gdouble y,
+ gint *scr_x,
+ gint *scr_y);
+
+extern GimpRGB get_image_color (gdouble u,
+ gdouble v,
+ gint *inside);
+extern GimpRGB get_box_image_color (gint image,
+ gdouble u,
+ gdouble v);
+extern GimpRGB get_cylinder_image_color (gint image,
+ gdouble u,
+ gdouble v);
+
+#endif /* __MAPOBJECT_IMAGE_H__ */
diff --git a/plug-ins/map-object/map-object-main.c b/plug-ins/map-object/map-object-main.c
new file mode 100644
index 0000000..c2f6a82
--- /dev/null
+++ b/plug-ins/map-object/map-object-main.c
@@ -0,0 +1,334 @@
+/* MapObject 1.2.0 -- image filter plug-in for GIMP
+ *
+ * Copyright (C) 1996-98 Tom Bech
+ * Copyright (C) 1996-98 Federico Mena Quintero
+ *
+ * E-mail: tomb@gimp.org (Tom) or quartic@gimp.org (Federico)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "map-object-ui.h"
+#include "map-object-image.h"
+#include "map-object-apply.h"
+#include "map-object-preview.h"
+#include "map-object-main.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Global variables */
+/* ================ */
+
+MapObjectValues mapvals;
+
+/******************/
+/* Implementation */
+/******************/
+
+/*************************************/
+/* Set parameters to standard values */
+/*************************************/
+
+static void
+set_default_settings (void)
+{
+ gint i;
+
+ gimp_vector3_set (&mapvals.viewpoint, 0.5, 0.5, 2.0);
+ gimp_vector3_set (&mapvals.firstaxis, 1.0, 0.0, 0.0);
+ gimp_vector3_set (&mapvals.secondaxis, 0.0, 1.0, 0.0);
+ gimp_vector3_set (&mapvals.normal, 0.0, 0.0, 1.0);
+ gimp_vector3_set (&mapvals.position, 0.5, 0.5, 0.0);
+ gimp_vector3_set (&mapvals.lightsource.position, -0.5, -0.5, 2.0);
+ gimp_vector3_set (&mapvals.lightsource.direction, -1.0, -1.0, 1.0);
+ gimp_vector3_set (&mapvals.scale, 0.5, 0.5, 0.5);
+
+ mapvals.maptype = MAP_PLANE;
+
+ mapvals.pixelthreshold = 0.25;
+ mapvals.alpha = 0.0;
+ mapvals.beta = 0.0;
+ mapvals.gamma = 0.0;
+ mapvals.maxdepth = 3.0;
+ mapvals.radius = 0.25;
+ mapvals.cylinder_radius = 0.25;
+ mapvals.cylinder_length = 1.0;
+
+ mapvals.zoom = 1.0;
+ mapvals.lightsource.type = POINT_LIGHT;
+
+ mapvals.antialiasing = TRUE;
+ mapvals.create_new_image = FALSE;
+ mapvals.create_new_layer = FALSE;
+ mapvals.transparent_background = FALSE;
+ mapvals.tiled = FALSE;
+ mapvals.livepreview = FALSE;
+ mapvals.showgrid = TRUE;
+
+ mapvals.lightsource.intensity = 1.0;
+ gimp_rgba_set (&mapvals.lightsource.color, 1.0, 1.0, 1.0, 1.0);
+
+ mapvals.material.ambient_int = 0.3;
+ mapvals.material.diffuse_int = 1.0;
+ mapvals.material.diffuse_ref = 0.5;
+ mapvals.material.specular_ref = 0.5;
+ mapvals.material.highlight = 27.0;
+
+ for (i = 0; i < 6; i++)
+ mapvals.boxmap_id[i] = -1;
+
+ for (i = 0; i < 2; i++)
+ mapvals.cylindermap_id[i] = -1;
+}
+
+static void
+check_drawables (gint32 drawable_id)
+{
+ gint i;
+
+ /* Check that boxmap images are valid */
+ /* ================================== */
+
+ for (i = 0; i < 6; i++)
+ {
+ if (mapvals.boxmap_id[i] == -1 ||
+ !gimp_item_is_valid (mapvals.boxmap_id[i]) ||
+ gimp_drawable_is_gray (mapvals.boxmap_id[i]))
+ mapvals.boxmap_id[i] = drawable_id;
+ }
+
+ /* Check that cylindermap images are valid */
+ /* ======================================= */
+
+ for (i = 0; i < 2; i++)
+ {
+ if (mapvals.cylindermap_id[i] == -1 ||
+ !gimp_item_is_valid (mapvals.cylindermap_id[i]) ||
+ gimp_drawable_is_gray (mapvals.cylindermap_id[i]))
+ mapvals.cylindermap_id[i] = drawable_id;
+ }
+}
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "maptype", "Type of mapping (0=plane,1=sphere,2=box,3=cylinder)" },
+ { GIMP_PDB_FLOAT, "viewpoint-x", "Position of viewpoint (x,y,z)" },
+ { GIMP_PDB_FLOAT, "viewpoint-y", "Position of viewpoint (x,y,z)" },
+ { GIMP_PDB_FLOAT, "viewpoint-z", "Position of viewpoint (x,y,z)" },
+ { GIMP_PDB_FLOAT, "position-x", "Object position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "position-y", "Object position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "position-z", "Object position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "firstaxis-x", "First axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "firstaxis-y", "First axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "firstaxis-z", "First axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "secondaxis-x", "Second axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "secondaxis-y", "Second axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "secondaxis-z", "Second axis of object [x,y,z]" },
+ { GIMP_PDB_FLOAT, "rotationangle-x", "Rotation about X axis in degrees" },
+ { GIMP_PDB_FLOAT, "rotationangle-y", "Rotation about Y axis in degrees" },
+ { GIMP_PDB_FLOAT, "rotationangle-z", "Rotation about Z axis in degrees" },
+ { GIMP_PDB_INT32, "lighttype", "Type of lightsource (0=point,1=directional,2=none)" },
+ { GIMP_PDB_COLOR, "lightcolor", "Lightsource color (r,g,b)" },
+ { GIMP_PDB_FLOAT, "lightposition-x", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightposition-y", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightposition-z", "Lightsource position (x,y,z)" },
+ { GIMP_PDB_FLOAT, "lightdirection-x", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "lightdirection-y", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "lightdirection-z", "Lightsource direction [x,y,z]" },
+ { GIMP_PDB_FLOAT, "ambient_intensity", "Material ambient intensity (0..1)" },
+ { GIMP_PDB_FLOAT, "diffuse_intensity", "Material diffuse intensity (0..1)" },
+ { GIMP_PDB_FLOAT, "diffuse_reflectivity", "Material diffuse reflectivity (0..1)" },
+ { GIMP_PDB_FLOAT, "specular_reflectivity", "Material specular reflectivity (0..1)" },
+ { GIMP_PDB_FLOAT, "highlight", "Material highlight (0..->), note: it's exponential" },
+ { GIMP_PDB_INT32, "antialiasing", "Apply antialiasing (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "tiled", "Tile source image (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "newimage", "Create a new image (TRUE/FALSE)" },
+ { GIMP_PDB_INT32, "transparentbackground", "Make background transparent (TRUE/FALSE)" },
+ { GIMP_PDB_FLOAT, "radius", "Sphere/cylinder radius (only used when maptype=1 or 3)" },
+ { GIMP_PDB_FLOAT, "x-scale", "Box x size (0..->)" },
+ { GIMP_PDB_FLOAT, "y-scale", "Box y size (0..->)" },
+ { GIMP_PDB_FLOAT, "z-scale", "Box z size (0..->)"},
+ { GIMP_PDB_FLOAT, "cylinder-length", "Cylinder length (0..->)"},
+ { GIMP_PDB_DRAWABLE, "box-front-drawable", "Box front face (set these to -1 if not used)" },
+ { GIMP_PDB_DRAWABLE, "box-back-drawable", "Box back face" },
+ { GIMP_PDB_DRAWABLE, "box-top-drawable", "Box top face" },
+ { GIMP_PDB_DRAWABLE, "box-bottom-drawable", "Box bottom face" },
+ { GIMP_PDB_DRAWABLE, "box-left-drawable", "Box left face" },
+ { GIMP_PDB_DRAWABLE, "box-right-drawable", "Box right face" },
+ { GIMP_PDB_DRAWABLE, "cyl-top-drawable", "Cylinder top face (set these to -1 if not used)" },
+ { GIMP_PDB_DRAWABLE, "cyl-bottom-drawable", "Cylinder bottom face" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Map the image to an object (plane, sphere, box or cylinder)"),
+ "No help yet",
+ "Tom Bech & Federico Mena Quintero",
+ "Tom Bech & Federico Mena Quintero",
+ "Version 1.2.0, July 16 1998",
+ N_("Map _Object..."),
+ "RGB*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Map");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpRunMode run_mode;
+ gint32 drawable_id;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint i;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ /* Set default values */
+ /* ================== */
+
+ set_default_settings ();
+
+ /* Get the specified drawable */
+ /* ========================== */
+
+ run_mode = param[0].data.d_int32;
+ image_id = param[1].data.d_int32;
+ drawable_id = param[2].data.d_int32;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+
+ /* Possibly retrieve data */
+ /* ====================== */
+
+ gimp_get_data (PLUG_IN_PROC, &mapvals);
+ check_drawables (drawable_id);
+ if (main_dialog (drawable_id))
+ {
+ compute_image ();
+
+ gimp_set_data (PLUG_IN_PROC, &mapvals, sizeof (MapObjectValues));
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_get_data (PLUG_IN_PROC, &mapvals);
+ check_drawables (drawable_id);
+ if (image_setup (drawable_id, FALSE))
+ compute_image ();
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 49)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ mapvals.maptype = (MapType) param[3].data.d_int32;
+ mapvals.viewpoint.x = param[4].data.d_float;
+ mapvals.viewpoint.y = param[5].data.d_float;
+ mapvals.viewpoint.z = param[6].data.d_float;
+ mapvals.position.x = param[7].data.d_float;
+ mapvals.position.y = param[8].data.d_float;
+ mapvals.position.z = param[9].data.d_float;
+ mapvals.firstaxis.x = param[10].data.d_float;
+ mapvals.firstaxis.y = param[11].data.d_float;
+ mapvals.firstaxis.z = param[12].data.d_float;
+ mapvals.secondaxis.x = param[13].data.d_float;
+ mapvals.secondaxis.y = param[14].data.d_float;
+ mapvals.secondaxis.z = param[15].data.d_float;
+ mapvals.alpha = param[16].data.d_float;
+ mapvals.beta = param[17].data.d_float;
+ mapvals.gamma = param[18].data.d_float;
+ mapvals.lightsource.type = (LightType) param[19].data.d_int32;
+ mapvals.lightsource.color = param[20].data.d_color;
+ mapvals.lightsource.position.x = param[21].data.d_float;
+ mapvals.lightsource.position.y = param[22].data.d_float;
+ mapvals.lightsource.position.z = param[23].data.d_float;
+ mapvals.lightsource.direction.x = param[24].data.d_float;
+ mapvals.lightsource.direction.y = param[25].data.d_float;
+ mapvals.lightsource.direction.z = param[26].data.d_float;
+ mapvals.material.ambient_int = param[27].data.d_float;
+ mapvals.material.diffuse_int = param[28].data.d_float;
+ mapvals.material.diffuse_ref = param[29].data.d_float;
+ mapvals.material.specular_ref = param[30].data.d_float;
+ mapvals.material.highlight = param[31].data.d_float;
+ mapvals.antialiasing = (gint) param[32].data.d_int32;
+ mapvals.tiled = (gint) param[33].data.d_int32;
+ mapvals.create_new_image = (gint) param[34].data.d_int32;
+ mapvals.transparent_background = (gint) param[35].data.d_int32;
+ mapvals.radius = param[36].data.d_float;
+ mapvals.cylinder_radius = param[36].data.d_float;
+ mapvals.scale.x = param[37].data.d_float;
+ mapvals.scale.y = param[38].data.d_float;
+ mapvals.scale.z = param[39].data.d_float;
+ mapvals.cylinder_length = param[40].data.d_float;
+
+ for (i = 0; i < 6; i++)
+ mapvals.boxmap_id[i] = param[41+i].data.d_drawable;
+
+ for (i = 0; i < 2; i++)
+ mapvals.cylindermap_id[i] = param[47+i].data.d_drawable;
+
+ check_drawables (drawable_id);
+ if (image_setup (drawable_id, FALSE))
+ compute_image ();
+ }
+ break;
+ }
+
+ values[0].data.d_status = status;
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+}
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
diff --git a/plug-ins/map-object/map-object-main.h b/plug-ins/map-object/map-object-main.h
new file mode 100644
index 0000000..5958c7c
--- /dev/null
+++ b/plug-ins/map-object/map-object-main.h
@@ -0,0 +1,90 @@
+#ifndef __MAPOBJECT_MAIN_H__
+#define __MAPOBJECT_MAIN_H__
+
+/* Defines and stuff */
+/* ================= */
+
+#define PLUG_IN_PROC "plug-in-map-object"
+#define PLUG_IN_BINARY "map-object"
+#define PLUG_IN_ROLE "gimp-map-object"
+
+#define TILE_CACHE_SIZE 16
+
+/* Typedefs */
+/* ======== */
+
+typedef enum
+{
+ POINT_LIGHT,
+ DIRECTIONAL_LIGHT,
+ NO_LIGHT
+} LightType;
+
+typedef enum
+{
+ MAP_PLANE,
+ MAP_SPHERE,
+ MAP_BOX,
+ MAP_CYLINDER
+} MapType;
+
+/* Typedefs */
+/* ======== */
+
+typedef struct
+{
+ gdouble ambient_int;
+ gdouble diffuse_int;
+ gdouble diffuse_ref;
+ gdouble specular_ref;
+ gdouble highlight;
+ GimpRGB color;
+} MaterialSettings;
+
+typedef struct
+{
+ LightType type;
+ GimpVector3 position;
+ GimpVector3 direction;
+ GimpRGB color;
+ gdouble intensity;
+} LightSettings;
+
+typedef struct
+{
+ GimpVector3 viewpoint,firstaxis,secondaxis,normal,position,scale;
+ LightSettings lightsource;
+
+ MaterialSettings material;
+ MaterialSettings refmaterial;
+
+ MapType maptype;
+
+ gint antialiasing;
+ gint create_new_image;
+ gint create_new_layer;
+ gint transparent_background;
+ gint tiled;
+ gint livepreview;
+ gint showgrid;
+ gint showcaps;
+
+ gdouble zoom;
+ gdouble alpha,beta,gamma;
+ gdouble maxdepth;
+ gdouble pixelthreshold;
+ gdouble radius;
+ gdouble cylinder_radius;
+ gdouble cylinder_length;
+
+ gint32 boxmap_id[6];
+ gint32 cylindermap_id[2];
+
+} MapObjectValues;
+
+/* Externally visible variables */
+/* ============================ */
+
+extern MapObjectValues mapvals;
+
+#endif /* __MAPOBJECT_MAIN_H__ */
diff --git a/plug-ins/map-object/map-object-preview.c b/plug-ins/map-object/map-object-preview.c
new file mode 100644
index 0000000..afc29b4
--- /dev/null
+++ b/plug-ins/map-object/map-object-preview.c
@@ -0,0 +1,745 @@
+/*************************************************/
+/* Compute a preview image and preview wireframe */
+/*************************************************/
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "map-object-main.h"
+#include "map-object-ui.h"
+#include "map-object-image.h"
+#include "map-object-apply.h"
+#include "map-object-shade.h"
+#include "map-object-preview.h"
+
+
+gdouble mat[3][4];
+gint lightx, lighty;
+
+/* Protos */
+/* ====== */
+
+static void compute_preview (gint x,
+ gint y,
+ gint w,
+ gint h,
+ gint pw,
+ gint ph);
+static void draw_light_marker (cairo_t *cr,
+ gint xpos,
+ gint ypos);
+static void draw_line (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph,
+ gdouble cx1,
+ gdouble cy1,
+ gdouble cx2,
+ gdouble cy2,
+ GimpVector3 a,
+ GimpVector3 b);
+static void draw_wireframe (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph);
+static void draw_preview_wireframe (cairo_t *cr);
+static void draw_wireframe_plane (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph);
+static void draw_wireframe_sphere (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph);
+static void draw_wireframe_box (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph);
+static void draw_wireframe_cylinder (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph);
+
+/**************************************************************/
+/* Computes a preview of the rectangle starting at (x,y) with */
+/* dimensions (w,h), placing the result in preview_RGB_data. */
+/**************************************************************/
+
+static void
+compute_preview (gint x,
+ gint y,
+ gint w,
+ gint h,
+ gint pw,
+ gint ph)
+{
+ gdouble xpostab[PREVIEW_WIDTH];
+ gdouble ypostab[PREVIEW_HEIGHT];
+ gdouble realw;
+ gdouble realh;
+ GimpVector3 p1, p2;
+ GimpRGB color;
+ GimpRGB lightcheck, darkcheck;
+ gint xcnt, ycnt, f1, f2;
+ guchar r, g, b;
+ glong index = 0;
+
+ init_compute ();
+
+ if (! preview_surface)
+ return;
+
+ p1 = int_to_pos (x, y);
+ p2 = int_to_pos (x + w, y + h);
+
+ /* First, compute the linear mapping (x,y,x+w,y+h) to (0,0,pw,ph) */
+ /* ============================================================== */
+
+ realw = (p2.x - p1.x);
+ realh = (p2.y - p1.y);
+
+ for (xcnt = 0; xcnt < pw; xcnt++)
+ xpostab[xcnt] = p1.x + realw * ((gdouble) xcnt / (gdouble) pw);
+
+ for (ycnt = 0; ycnt < ph; ycnt++)
+ ypostab[ycnt] = p1.y + realh * ((gdouble) ycnt / (gdouble) ph);
+
+ /* Compute preview using the offset tables */
+ /* ======================================= */
+
+ if (mapvals.transparent_background == TRUE)
+ {
+ gimp_rgba_set (&background, 0.0, 0.0, 0.0, 0.0);
+ }
+ else
+ {
+ gimp_context_get_background (&background);
+ gimp_rgb_set_alpha (&background, 1.0);
+ }
+
+ gimp_rgba_set (&lightcheck,
+ GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, GIMP_CHECK_LIGHT, 1.0);
+ gimp_rgba_set (&darkcheck,
+ GIMP_CHECK_DARK, GIMP_CHECK_DARK, GIMP_CHECK_DARK, 1.0);
+ gimp_vector3_set (&p2, -1.0, -1.0, 0.0);
+
+ cairo_surface_flush (preview_surface);
+
+ for (ycnt = 0; ycnt < ph; ycnt++)
+ {
+ index = ycnt * preview_rgb_stride;
+ for (xcnt = 0; xcnt < pw; xcnt++)
+ {
+ p1.x = xpostab[xcnt];
+ p1.y = ypostab[ycnt];
+
+ p2 = p1;
+ color = (* get_ray_color) (&p1);
+
+ if (color.a < 1.0)
+ {
+ f1 = ((xcnt % 32) < 16);
+ f2 = ((ycnt % 32) < 16);
+ f1 = f1 ^ f2;
+
+ if (f1)
+ {
+ if (color.a == 0.0)
+ color = lightcheck;
+ else
+ gimp_rgb_composite (&color, &lightcheck,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+ else
+ {
+ if (color.a == 0.0)
+ color = darkcheck;
+ else
+ gimp_rgb_composite (&color, &darkcheck,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+ }
+
+ gimp_rgb_get_uchar (&color, &r, &g, &b);
+ GIMP_CAIRO_RGB24_SET_PIXEL((preview_rgb_data + index), r, g, b);
+ index += 4;
+ }
+ }
+ cairo_surface_mark_dirty (preview_surface);
+}
+
+/*************************************************/
+/* Check if the given position is within the */
+/* light marker. Return TRUE if so, FALSE if not */
+/*************************************************/
+
+gint
+check_light_hit (gint xpos,
+ gint ypos)
+{
+ gdouble dx, dy, r;
+
+ if (mapvals.lightsource.type == POINT_LIGHT)
+ {
+ dx = (gdouble) lightx - xpos;
+ dy = (gdouble) lighty - ypos;
+ r = sqrt (dx * dx + dy * dy) + 0.5;
+
+ if ((gint) r > 7)
+ return FALSE;
+ else
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/****************************************/
+/* Draw a marker to show light position */
+/****************************************/
+
+static void
+draw_light_marker (cairo_t *cr,
+ gint xpos,
+ gint ypos)
+{
+ GdkColor color;
+
+ if (mapvals.lightsource.type != POINT_LIGHT)
+ return;
+
+ cairo_set_line_width (cr, 1.0);
+
+ color.red = 0x0;
+ color.green = 0x4000;
+ color.blue = 0xFFFF;
+ gdk_cairo_set_source_color (cr, &color);
+
+ lightx = xpos;
+ lighty = ypos;
+
+ cairo_arc (cr, lightx, lighty, 7, 0, 2 * G_PI);
+ cairo_fill (cr);
+}
+
+static void
+draw_lights (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ gdouble dxpos, dypos;
+ gint xpos, ypos;
+
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &dxpos, &dypos, &mapvals.viewpoint,
+ &mapvals.lightsource.position);
+ xpos = RINT (dxpos);
+ ypos = RINT (dypos);
+
+ if (xpos >= 0 && xpos <= PREVIEW_WIDTH &&
+ ypos >= 0 && ypos <= PREVIEW_HEIGHT)
+ {
+ draw_light_marker (cr, xpos, ypos);
+ }
+}
+
+/*************************************************/
+/* Update light position given new screen coords */
+/*************************************************/
+
+void
+update_light (gint xpos,
+ gint ypos)
+{
+ gint startx, starty, pw, ph;
+
+ pw = PREVIEW_WIDTH * mapvals.zoom;
+ ph = PREVIEW_HEIGHT * mapvals.zoom;
+ startx = (PREVIEW_WIDTH - pw) / 2;
+ starty = (PREVIEW_HEIGHT - ph) / 2;
+
+ gimp_vector_2d_to_3d (startx, starty, pw, ph, xpos, ypos,
+ &mapvals.viewpoint, &mapvals.lightsource.position);
+
+ gtk_widget_queue_draw (previewarea);
+}
+
+/**************************/
+/* Compute preview image. */
+/**************************/
+
+void
+compute_preview_image (void)
+{
+ GdkDisplay *display = gtk_widget_get_display (previewarea);
+ GdkCursor *cursor;
+ gint pw, ph;
+
+ pw = PREVIEW_WIDTH * mapvals.zoom;
+ ph = PREVIEW_HEIGHT * mapvals.zoom;
+
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+ gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+
+ compute_preview (0, 0, width - 1, height - 1, pw, ph);
+
+ cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
+ gdk_window_set_cursor(gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+}
+
+gboolean
+preview_expose (GtkWidget *widget,
+ GdkEventExpose *eevent)
+{
+ gint startx, starty, pw, ph;
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (eevent->window);
+
+ pw = PREVIEW_WIDTH * mapvals.zoom;
+ ph = PREVIEW_HEIGHT * mapvals.zoom;
+ startx = (PREVIEW_WIDTH - pw) / 2;
+ starty = (PREVIEW_HEIGHT - ph) / 2;
+
+ cairo_set_source_surface (cr, preview_surface, startx, starty);
+ cairo_rectangle (cr, startx, starty, pw, ph);
+ cairo_clip (cr);
+
+ cairo_paint (cr);
+
+ cairo_reset_clip (cr);
+
+ if (mapvals.showgrid)
+ draw_preview_wireframe (cr);
+
+ cairo_reset_clip (cr);
+ draw_lights (cr, startx, starty, pw, ph);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/**************************/
+/* Draw preview wireframe */
+/**************************/
+
+void
+draw_preview_wireframe (cairo_t *cr)
+{
+ gint startx, starty, pw, ph;
+
+ pw = PREVIEW_WIDTH * mapvals.zoom;
+ ph = PREVIEW_HEIGHT * mapvals.zoom;
+ startx = (PREVIEW_WIDTH - pw) / 2;
+ starty = (PREVIEW_HEIGHT - ph) / 2;
+
+ draw_wireframe (cr, startx, starty, pw, ph);
+}
+
+/****************************/
+/* Draw a wireframe preview */
+/****************************/
+
+void
+draw_wireframe (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ switch (mapvals.maptype)
+ {
+ case MAP_PLANE:
+ draw_wireframe_plane (cr, startx, starty, pw, ph);
+ break;
+ case MAP_SPHERE:
+ draw_wireframe_sphere (cr, startx, starty, pw, ph);
+ break;
+ case MAP_BOX:
+ draw_wireframe_box (cr, startx, starty, pw, ph);
+ break;
+ case MAP_CYLINDER:
+ draw_wireframe_cylinder (cr, startx, starty, pw, ph);
+ break;
+ }
+}
+
+static void
+draw_wireframe_plane (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ GimpVector3 v1, v2, a, b, c, d, dir1, dir2;
+ gint cnt;
+ gdouble x1, y1, x2, y2, fac;
+
+ cairo_rectangle (cr, startx, starty, pw, ph);
+ cairo_clip (cr);
+
+ /* Find rotated box corners */
+ /* ======================== */
+
+ gimp_vector3_set (&v1, 0.5, 0.0, 0.0);
+ gimp_vector3_set (&v2, 0.0, 0.5, 0.0);
+
+ gimp_vector3_rotate (&v1,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+
+ gimp_vector3_rotate (&v2,
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+
+ dir1 = v1; gimp_vector3_normalize (&dir1);
+ dir2 = v2; gimp_vector3_normalize (&dir2);
+
+ fac = 1.0 / (gdouble) WIRESIZE;
+
+ gimp_vector3_mul (&dir1, fac);
+ gimp_vector3_mul (&dir2, fac);
+
+ gimp_vector3_add (&a, &mapvals.position, &v1);
+ gimp_vector3_sub (&b, &a, &v2);
+ gimp_vector3_add (&a, &a, &v2);
+ gimp_vector3_sub (&d, &mapvals.position, &v1);
+ gimp_vector3_sub (&d, &d, &v2);
+
+ c = b;
+
+ for (cnt = 0; cnt <= WIRESIZE; cnt++)
+ {
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x1, &y1, &mapvals.viewpoint, &a);
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x2, &y2, &mapvals.viewpoint, &b);
+
+ cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5);
+ cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5);
+
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x1, &y1, &mapvals.viewpoint, &c);
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x2, &y2, &mapvals.viewpoint, &d);
+
+ cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5);
+ cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5);
+
+ gimp_vector3_sub (&a, &a, &dir1);
+ gimp_vector3_sub (&b, &b, &dir1);
+ gimp_vector3_add (&c, &c, &dir2);
+ gimp_vector3_add (&d, &d, &dir2);
+ }
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_stroke_preserve (cr);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_stroke (cr);
+}
+
+static void
+draw_wireframe_sphere (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ GimpVector3 p[2 * (WIRESIZE + 5)];
+ gint cnt, cnt2;
+ gdouble x1, y1, x2, y2, twopifac;
+
+ cairo_rectangle (cr, startx, starty, pw, ph);
+ cairo_clip (cr);
+
+ /* Compute wireframe points */
+ /* ======================== */
+
+ twopifac = (2.0 * G_PI) / WIRESIZE;
+
+ for (cnt = 0; cnt < WIRESIZE; cnt++)
+ {
+ p[cnt].x = mapvals.radius * cos ((gdouble) cnt * twopifac);
+ p[cnt].y = 0.0;
+ p[cnt].z = mapvals.radius * sin ((gdouble) cnt * twopifac);
+ gimp_vector3_rotate (&p[cnt],
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position);
+ }
+
+ p[cnt] = p[0];
+
+ for (cnt = WIRESIZE + 1; cnt < 2 * WIRESIZE + 1; cnt++)
+ {
+ p[cnt].x = mapvals.radius * cos ((gdouble) (cnt-(WIRESIZE+1))*twopifac);
+ p[cnt].y = mapvals.radius * sin ((gdouble) (cnt-(WIRESIZE+1))*twopifac);
+ p[cnt].z = 0.0;
+ gimp_vector3_rotate (&p[cnt],
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position);
+ }
+
+ p[cnt] = p[WIRESIZE+1];
+ cnt++;
+ cnt2 = cnt;
+
+ /* Find rotated axis */
+ /* ================= */
+
+ gimp_vector3_set (&p[cnt], 0.0, -0.35, 0.0);
+ gimp_vector3_rotate (&p[cnt],
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ p[cnt+1] = mapvals.position;
+
+ gimp_vector3_set (&p[cnt+2], 0.0, 0.0, -0.35);
+ gimp_vector3_rotate (&p[cnt+2],
+ gimp_deg_to_rad (mapvals.alpha),
+ gimp_deg_to_rad (mapvals.beta),
+ gimp_deg_to_rad (mapvals.gamma));
+ p[cnt+3] = mapvals.position;
+
+ p[cnt + 4] = p[cnt];
+ gimp_vector3_mul (&p[cnt + 4], -1.0);
+ p[cnt + 5] = p[cnt + 1];
+
+ gimp_vector3_add (&p[cnt], &p[cnt], &mapvals.position);
+ gimp_vector3_add (&p[cnt + 2], &p[cnt + 2], &mapvals.position);
+ gimp_vector3_add (&p[cnt + 4], &p[cnt + 4], &mapvals.position);
+
+ /* Draw the circles (equator and zero meridian) */
+ /* ============================================ */
+
+ for (cnt = 0; cnt < cnt2 - 1; cnt++)
+ {
+ if (p[cnt].z > mapvals.position.z && p[cnt + 1].z > mapvals.position.z)
+ {
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x1, &y1, &mapvals.viewpoint, &p[cnt]);
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x2, &y2, &mapvals.viewpoint, &p[cnt + 1]);
+
+ cairo_move_to (cr, (gint) (x1 + 0.5) + 0.5, (gint) (y1 + 0.5) + 0.5);
+ cairo_line_to (cr, (gint) (x2 + 0.5) + 0.5, (gint) (y2 + 0.5) + 0.5);
+ }
+ }
+
+ /* Draw the axis (pole to pole and center to zero meridian) */
+ /* ======================================================== */
+
+ for (cnt = 0; cnt < 3; cnt++)
+ {
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x1, &y1, &mapvals.viewpoint, &p[cnt2]);
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x2, &y2, &mapvals.viewpoint, &p[cnt2 + 1]);
+
+ cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5);
+ cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5);
+
+ cnt2 += 2;
+ }
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_stroke_preserve (cr);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_stroke (cr);
+}
+
+static void
+draw_line (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph,
+ gdouble cx1,
+ gdouble cy1,
+ gdouble cx2,
+ gdouble cy2,
+ GimpVector3 a,
+ GimpVector3 b)
+{
+ gdouble x1, y1, x2, y2;
+
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x1, &y1, &mapvals.viewpoint, &a);
+ gimp_vector_3d_to_2d (startx, starty, pw, ph,
+ &x2, &y2, &mapvals.viewpoint, &b);
+
+ cairo_move_to (cr, RINT (x1) + 0.5, RINT (y1) + 0.5);
+ cairo_line_to (cr, RINT (x2) + 0.5, RINT (y2) + 0.5);
+}
+
+static void
+draw_wireframe_box (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ GimpVector3 p[8], tmp, scale;
+ gint i;
+ gdouble cx1, cy1, cx2, cy2;
+
+ cairo_rectangle (cr, startx, starty, pw, ph);
+ cairo_clip (cr);
+
+ /* Compute wireframe points */
+ /* ======================== */
+
+ init_compute ();
+
+ scale = mapvals.scale;
+ gimp_vector3_mul (&scale, 0.5);
+
+ gimp_vector3_set (&p[0], -scale.x, -scale.y, scale.z);
+ gimp_vector3_set (&p[1], scale.x, -scale.y, scale.z);
+ gimp_vector3_set (&p[2], scale.x, scale.y, scale.z);
+ gimp_vector3_set (&p[3], -scale.x, scale.y, scale.z);
+
+ gimp_vector3_set (&p[4], -scale.x, -scale.y, -scale.z);
+ gimp_vector3_set (&p[5], scale.x, -scale.y, -scale.z);
+ gimp_vector3_set (&p[6], scale.x, scale.y, -scale.z);
+ gimp_vector3_set (&p[7], -scale.x, scale.y, -scale.z);
+
+ /* Rotate and translate points */
+ /* =========================== */
+
+ for (i = 0; i < 8; i++)
+ {
+ vecmulmat (&tmp, &p[i], rotmat);
+ gimp_vector3_add (&p[i], &tmp, &mapvals.position);
+ }
+
+ /* Draw the box */
+ /* ============ */
+
+ cx1 = (gdouble) startx;
+ cy1 = (gdouble) starty;
+ cx2 = cx1 + (gdouble) pw;
+ cy2 = cy1 + (gdouble) ph;
+
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[1]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[2]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[3]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[0]);
+
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[4],p[5]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[5],p[6]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[6],p[7]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[4]);
+
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[0],p[4]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[1],p[5]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[2],p[6]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[3],p[7]);
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_stroke_preserve (cr);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_stroke (cr);
+}
+
+static void
+draw_wireframe_cylinder (cairo_t *cr,
+ gint startx,
+ gint starty,
+ gint pw,
+ gint ph)
+{
+ GimpVector3 p[2*8], a, axis, scale;
+ gint i;
+ gdouble cx1, cy1, cx2, cy2;
+ gfloat m[16], l, angle;
+
+ cairo_rectangle (cr, startx, starty, pw, ph);
+ cairo_clip (cr);
+
+ /* Compute wireframe points */
+ /* ======================== */
+
+ init_compute ();
+
+ scale = mapvals.scale;
+ gimp_vector3_mul (&scale, 0.5);
+
+ l = mapvals.cylinder_length / 2.0;
+ angle = 0;
+
+ gimp_vector3_set (&axis, 0.0, 1.0, 0.0);
+
+ for (i = 0; i < 8; i++)
+ {
+ rotatemat (angle, &axis, m);
+
+ gimp_vector3_set (&a, mapvals.cylinder_radius, 0.0, 0.0);
+
+ vecmulmat (&p[i], &a, m);
+
+ p[i+8] = p[i];
+
+ p[i].y += l;
+ p[i+8].y -= l;
+
+ angle += 360.0 / 8.0;
+ }
+
+ /* Rotate and translate points */
+ /* =========================== */
+
+ for (i = 0; i < 16; i++)
+ {
+ vecmulmat (&a, &p[i], rotmat);
+ gimp_vector3_add (&p[i], &a, &mapvals.position);
+ }
+
+ /* Draw the box */
+ /* ============ */
+
+ cx1 = (gdouble) startx;
+ cy1 = (gdouble) starty;
+ cx2 = cx1 + (gdouble) pw;
+ cy2 = cy1 + (gdouble) ph;
+
+ for (i = 0; i < 7; i++)
+ {
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+1]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i+8],p[i+9]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[i],p[i+8]);
+ }
+
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[7],p[0]);
+ draw_line (cr, startx,starty,pw,ph, cx1,cy1,cx2,cy2, p[15],p[8]);
+
+ cairo_set_line_width (cr, 3.0);
+ cairo_stroke_preserve (cr);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ cairo_stroke (cr);
+}
diff --git a/plug-ins/map-object/map-object-preview.h b/plug-ins/map-object/map-object-preview.h
new file mode 100644
index 0000000..400b767
--- /dev/null
+++ b/plug-ins/map-object/map-object-preview.h
@@ -0,0 +1,26 @@
+#ifndef __MAPOBJECT_PREVIEW_H__
+#define __MAPOBJECT_PREVIEW_H__
+
+#define PREVIEW_WIDTH 200
+#define PREVIEW_HEIGHT 200
+
+#define WIRESIZE 16
+
+/* Externally visible variables */
+/* ============================ */
+
+extern gdouble mat[3][4];
+extern gint lightx,lighty;
+
+/* Externally visible functions */
+/* ============================ */
+
+void compute_preview_image (void);
+gboolean preview_expose (GtkWidget *widget,
+ GdkEventExpose *eevent);
+gint check_light_hit (gint xpos,
+ gint ypos);
+void update_light (gint xpos,
+ gint ypos);
+
+#endif /* __MAPOBJECT_PREVIEW_H__ */
diff --git a/plug-ins/map-object/map-object-shade.c b/plug-ins/map-object/map-object-shade.c
new file mode 100644
index 0000000..f91345b
--- /dev/null
+++ b/plug-ins/map-object/map-object-shade.c
@@ -0,0 +1,1254 @@
+/*****************/
+/* Shading stuff */
+/*****************/
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "map-object-apply.h"
+#include "map-object-main.h"
+#include "map-object-image.h"
+#include "map-object-shade.h"
+
+
+static gdouble bx1, by1, bx2, by2;
+get_ray_color_func get_ray_color;
+
+typedef struct
+{
+ gdouble u, v;
+ gdouble t;
+ GimpVector3 s;
+ GimpVector3 n;
+ gint face;
+} FaceIntersectInfo;
+
+/*****************/
+/* Phong shading */
+/*****************/
+
+static GimpRGB
+phong_shade (GimpVector3 *pos,
+ GimpVector3 *viewpoint,
+ GimpVector3 *normal,
+ GimpRGB *diff_col,
+ GimpRGB *spec_col,
+ LightType type)
+{
+ GimpRGB ambientcolor, diffusecolor, specularcolor;
+ gdouble NL, RV, dist;
+ GimpVector3 L, NN, V, N;
+ GimpVector3 *light;
+
+ light = mapvals.lightsource.type == DIRECTIONAL_LIGHT
+ ? &mapvals.lightsource.direction
+ : &mapvals.lightsource.position,
+
+ /* Compute ambient intensity */
+ /* ========================= */
+
+ N = *normal;
+ ambientcolor = *diff_col;
+ gimp_rgb_multiply (&ambientcolor, mapvals.material.ambient_int);
+
+ /* Compute (N*L) term of Phong's equation */
+ /* ====================================== */
+
+ if (type == POINT_LIGHT)
+ gimp_vector3_sub (&L, light, pos);
+ else
+ L = *light;
+
+ dist = gimp_vector3_length (&L);
+
+ if (dist != 0.0)
+ gimp_vector3_mul (&L, 1.0 / dist);
+
+ NL = 2.0 * gimp_vector3_inner_product (&N, &L);
+
+ if (NL >= 0.0)
+ {
+ /* Compute (R*V)^alpha term of Phong's equation */
+ /* ============================================ */
+
+ gimp_vector3_sub (&V, viewpoint, pos);
+ gimp_vector3_normalize (&V);
+
+ gimp_vector3_mul (&N, NL);
+ gimp_vector3_sub (&NN, &N, &L);
+ RV = gimp_vector3_inner_product (&NN, &V);
+ RV = 0.0 < RV ? pow (RV, mapvals.material.highlight) : 0.0;
+
+ /* Compute diffuse and specular intensity contribution */
+ /* =================================================== */
+
+ diffusecolor = *diff_col;
+ gimp_rgb_multiply (&diffusecolor, mapvals.material.diffuse_ref);
+ gimp_rgb_multiply (&diffusecolor, NL);
+
+ specularcolor = *spec_col;
+ gimp_rgb_multiply (&specularcolor, mapvals.material.specular_ref);
+ gimp_rgb_multiply (&specularcolor, RV);
+
+ gimp_rgb_add (&diffusecolor, &specularcolor);
+ gimp_rgb_multiply (&diffusecolor, mapvals.material.diffuse_int);
+ gimp_rgb_clamp (&diffusecolor);
+
+ gimp_rgb_add (&ambientcolor, &diffusecolor);
+ }
+
+ return ambientcolor;
+}
+
+static gint
+plane_intersect (GimpVector3 *dir,
+ GimpVector3 *viewp,
+ GimpVector3 *ipos,
+ gdouble *u,
+ gdouble *v)
+{
+ static gdouble det, det1, det2, det3, t;
+
+ imat[0][0] = dir->x;
+ imat[1][0] = dir->y;
+ imat[2][0] = dir->z;
+
+ /* Compute determinant of the first 3x3 sub matrix (denominator) */
+ /* ============================================================= */
+
+ det = (imat[0][0] * imat[1][1] * imat[2][2] +
+ imat[0][1] * imat[1][2] * imat[2][0] +
+ imat[0][2] * imat[1][0] * imat[2][1] -
+ imat[0][2] * imat[1][1] * imat[2][0] -
+ imat[0][0] * imat[1][2] * imat[2][1] -
+ imat[2][2] * imat[0][1] * imat[1][0]);
+
+ /* If the determinant is non-zero, a intersection point exists */
+ /* =========================================================== */
+
+ if (det != 0.0)
+ {
+ /* Now, lets compute the numerator determinants (wow ;) */
+ /* ==================================================== */
+
+ det1 = (imat[0][3] * imat[1][1] * imat[2][2] +
+ imat[0][1] * imat[1][2] * imat[2][3] +
+ imat[0][2] * imat[1][3] * imat[2][1] -
+ imat[0][2] * imat[1][1] * imat[2][3] -
+ imat[1][2] * imat[2][1] * imat[0][3] -
+ imat[2][2] * imat[0][1] * imat[1][3]);
+
+ det2 = (imat[0][0] * imat[1][3] * imat[2][2] +
+ imat[0][3] * imat[1][2] * imat[2][0] +
+ imat[0][2] * imat[1][0] * imat[2][3] -
+ imat[0][2] * imat[1][3] * imat[2][0] -
+ imat[1][2] * imat[2][3] * imat[0][0] -
+ imat[2][2] * imat[0][3] * imat[1][0]);
+
+ det3 = (imat[0][0] * imat[1][1] * imat[2][3] +
+ imat[0][1] * imat[1][3] * imat[2][0] +
+ imat[0][3] * imat[1][0] * imat[2][1] -
+ imat[0][3] * imat[1][1] * imat[2][0] -
+ imat[1][3] * imat[2][1] * imat[0][0] -
+ imat[2][3] * imat[0][1] * imat[1][0]);
+
+ /* Now we have the simultaneous solutions. Lets compute the unknowns */
+ /* (skip u&v if t is <0, this means the intersection is behind us) */
+ /* ================================================================ */
+
+ t = det1 / det;
+
+ if (t > 0.0)
+ {
+ *u = 1.0 + ((det2 / det) - 0.5);
+ *v = 1.0 + ((det3 / det) - 0.5);
+
+ ipos->x = viewp->x + t * dir->x;
+ ipos->y = viewp->y + t * dir->y;
+ ipos->z = viewp->z + t * dir->z;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*****************************************************************************
+ * These routines computes the color of the surface
+ * of the plane at a given point
+ *****************************************************************************/
+
+GimpRGB
+get_ray_color_plane (GimpVector3 *pos)
+{
+ GimpRGB color = background;
+
+ static gint inside = FALSE;
+ static GimpVector3 ray, spos;
+ static gdouble vx, vy;
+
+ /* Construct a line from our VP to the point */
+ /* ========================================= */
+
+ gimp_vector3_sub (&ray, pos, &mapvals.viewpoint);
+ gimp_vector3_normalize (&ray);
+
+ /* Check for intersection. This is a quasi ray-tracer. */
+ /* =================================================== */
+
+ if (plane_intersect (&ray, &mapvals.viewpoint, &spos, &vx, &vy) == TRUE)
+ {
+ color = get_image_color (vx, vy, &inside);
+
+ if (color.a != 0.0 && inside == TRUE &&
+ mapvals.lightsource.type != NO_LIGHT)
+ {
+ /* Compute shading at this point */
+ /* ============================= */
+
+ color = phong_shade (&spos,
+ &mapvals.viewpoint,
+ &mapvals.normal,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+ }
+ }
+
+ if (mapvals.transparent_background == FALSE && color.a < 1.0)
+ {
+ gimp_rgb_composite (&color, &background,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+
+ return color;
+}
+
+/***********************************************************************/
+/* Given the NorthPole, Equator and a third vector (normal) compute */
+/* the conversion from spherical oordinates to image space coordinates */
+/***********************************************************************/
+
+static void
+sphere_to_image (GimpVector3 *normal,
+ gdouble *u,
+ gdouble *v)
+{
+ static gdouble alpha, fac;
+ static GimpVector3 cross_prod;
+
+ alpha = acos (-gimp_vector3_inner_product (&mapvals.secondaxis, normal));
+
+ *v = alpha / G_PI;
+
+ if (*v == 0.0 || *v == 1.0)
+ {
+ *u = 0.0;
+ }
+ else
+ {
+ fac = (gimp_vector3_inner_product (&mapvals.firstaxis, normal) /
+ sin (alpha));
+
+ /* Make sure that we map to -1.0..1.0 (take care of rounding errors) */
+ /* ================================================================= */
+
+ fac = CLAMP (fac, -1.0, 1.0);
+
+ *u = acos (fac) / (2.0 * G_PI);
+
+ cross_prod = gimp_vector3_cross_product (&mapvals.secondaxis,
+ &mapvals.firstaxis);
+
+ if (gimp_vector3_inner_product (&cross_prod, normal) < 0.0)
+ *u = 1.0 - *u;
+ }
+}
+
+/***************************************************/
+/* Compute intersection point with sphere (if any) */
+/***************************************************/
+
+static gint
+sphere_intersect (GimpVector3 *dir,
+ GimpVector3 *viewp,
+ GimpVector3 *spos1,
+ GimpVector3 *spos2)
+{
+ static gdouble alpha, beta, tau, s1, s2, tmp;
+ static GimpVector3 t;
+
+ gimp_vector3_sub (&t, &mapvals.position, viewp);
+
+ alpha = gimp_vector3_inner_product (dir, &t);
+ beta = gimp_vector3_inner_product (&t, &t);
+
+ tau = alpha * alpha - beta + mapvals.radius * mapvals.radius;
+
+ if (tau >= 0.0)
+ {
+ tau = sqrt (tau);
+ s1 = alpha + tau;
+ s2 = alpha - tau;
+
+ if (s2 < s1)
+ {
+ tmp = s1;
+ s1 = s2;
+ s2 = tmp;
+ }
+
+ spos1->x = viewp->x + s1 * dir->x;
+ spos1->y = viewp->y + s1 * dir->y;
+ spos1->z = viewp->z + s1 * dir->z;
+ spos2->x = viewp->x + s2 * dir->x;
+ spos2->y = viewp->y + s2 * dir->y;
+ spos2->z = viewp->z + s2 * dir->z;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*****************************************************************************
+ * These routines computes the color of the surface
+ * of the sphere at a given point
+ *****************************************************************************/
+
+GimpRGB
+get_ray_color_sphere (GimpVector3 *pos)
+{
+ GimpRGB color = background;
+
+ static GimpRGB color2;
+ static gint inside = FALSE;
+ static GimpVector3 normal, ray, spos1, spos2;
+ static gdouble vx, vy;
+
+ /* Check if ray is within the bounding box */
+ /* ======================================= */
+
+ if (pos->x<bx1 || pos->x>bx2 || pos->y<by1 || pos->y>by2)
+ return color;
+
+ /* Construct a line from our VP to the point */
+ /* ========================================= */
+
+ gimp_vector3_sub (&ray, pos, &mapvals.viewpoint);
+ gimp_vector3_normalize (&ray);
+
+ /* Check for intersection. This is a quasi ray-tracer. */
+ /* =================================================== */
+
+ if (sphere_intersect (&ray, &mapvals.viewpoint, &spos1, &spos2) == TRUE)
+ {
+ /* Compute spherical to rectangular mapping */
+ /* ======================================== */
+
+ gimp_vector3_sub (&normal, &spos1, &mapvals.position);
+ gimp_vector3_normalize (&normal);
+ sphere_to_image (&normal, &vx, &vy);
+ color = get_image_color (vx, vy, &inside);
+
+ /* Check for total transparency... */
+ /* =============================== */
+
+ if (color.a < 1.0)
+ {
+ /* Hey, we can see through here! */
+ /* Lets see what's on the other side.. */
+ /* =================================== */
+
+ color = phong_shade (&spos1,
+ &mapvals.viewpoint,
+ &normal,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+
+ gimp_vector3_sub (&normal, &spos2, &mapvals.position);
+ gimp_vector3_normalize (&normal);
+ sphere_to_image (&normal, &vx, &vy);
+ color2 = get_image_color (vx, vy, &inside);
+
+ /* Make the normal point inwards */
+ /* ============================= */
+
+ gimp_vector3_mul (&normal, -1.0);
+
+ color2 = phong_shade (&spos2,
+ &mapvals.viewpoint,
+ &normal,
+ &color2,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color2);
+
+ /* Compute a mix of the first and second colors */
+ /* ============================================ */
+
+ gimp_rgb_composite (&color, &color2, GIMP_RGB_COMPOSITE_NORMAL);
+ gimp_rgb_clamp (&color);
+ }
+ else if (color.a != 0.0 &&
+ inside == TRUE &&
+ mapvals.lightsource.type != NO_LIGHT)
+ {
+ /* Compute shading at this point */
+ /* ============================= */
+
+ color = phong_shade (&spos1,
+ &mapvals.viewpoint,
+ &normal,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+ }
+ }
+
+ if (mapvals.transparent_background == FALSE && color.a < 1.0)
+ {
+ gimp_rgb_composite (&color, &background,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+
+ return color;
+}
+
+/***************************************************/
+/* Transform the corners of the bounding box to 2D */
+/***************************************************/
+
+void
+compute_bounding_box (void)
+{
+ GimpVector3 p1, p2;
+ gdouble t;
+ GimpVector3 dir;
+
+ p1 = mapvals.position;
+ p1.x -= (mapvals.radius + 0.01);
+ p1.y -= (mapvals.radius + 0.01);
+
+ p2 = mapvals.position;
+ p2.x += (mapvals.radius + 0.01);
+ p2.y += (mapvals.radius + 0.01);
+
+ gimp_vector3_sub (&dir, &p1, &mapvals.viewpoint);
+ gimp_vector3_normalize (&dir);
+
+ if (dir.z != 0.0)
+ {
+ t = (-1.0 * mapvals.viewpoint.z) / dir.z;
+ p1.x = (mapvals.viewpoint.x + t * dir.x);
+ p1.y = (mapvals.viewpoint.y + t * dir.y);
+ }
+
+ gimp_vector3_sub (&dir, &p2, &mapvals.viewpoint);
+ gimp_vector3_normalize (&dir);
+
+ if (dir.z != 0.0)
+ {
+ t = (-1.0 * mapvals.viewpoint.z) / dir.z;
+ p2.x = (mapvals.viewpoint.x + t * dir.x);
+ p2.y = (mapvals.viewpoint.y + t * dir.y);
+ }
+
+ bx1 = p1.x;
+ by1 = p1.y;
+ bx2 = p2.x;
+ by2 = p2.y;
+}
+
+/* These two were taken from the Mesa source. Mesa is written */
+/* and is (C) by Brian Paul. vecmulmat() performs a post-mul by */
+/* a 4x4 matrix to a 1x4(3) vector. rotmat() creates a matrix */
+/* that by post-mul will rotate a 1x4(3) vector the given angle */
+/* about the given axis. */
+/* ============================================================ */
+
+void
+vecmulmat (GimpVector3 *u,
+ GimpVector3 *v,
+ gfloat m[16])
+{
+ gfloat v0=v->x, v1=v->y, v2=v->z;
+#define M(row,col) m[col*4+row]
+ u->x = v0 * M(0,0) + v1 * M(1,0) + v2 * M(2,0) + M(3,0);
+ u->y = v0 * M(0,1) + v1 * M(1,1) + v2 * M(2,1) + M(3,1);
+ u->z = v0 * M(0,2) + v1 * M(1,2) + v2 * M(2,2) + M(3,2);
+#undef M
+}
+
+void
+rotatemat (gfloat angle,
+ GimpVector3 *v,
+ gfloat m[16])
+{
+ /* This function contributed by Erich Boleyn (erich@uruk.org) */
+ gfloat mag, s, c;
+ gfloat xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
+ gfloat IdentityMat[16];
+ gint cnt;
+
+ s = sin (angle * (G_PI / 180.0));
+ c = cos (angle * (G_PI / 180.0));
+
+ mag = sqrt (v->x*v->x + v->y*v->y + v->z*v->z);
+
+ if (mag == 0.0)
+ {
+ /* generate an identity matrix and return */
+
+ for (cnt = 0; cnt < 16; cnt++)
+ IdentityMat[cnt] = 0.0;
+
+ IdentityMat[0] = 1.0;
+ IdentityMat[5] = 1.0;
+ IdentityMat[10] = 1.0;
+ IdentityMat[15] = 1.0;
+
+ memcpy (m, IdentityMat, sizeof (gfloat) * 16);
+ return;
+ }
+
+ v->x /= mag;
+ v->y /= mag;
+ v->z /= mag;
+
+#define M(row,col) m[col*4+row]
+
+ xx = v->x * v->x;
+ yy = v->y * v->y;
+ zz = v->z * v->z;
+ xy = v->x * v->y;
+ yz = v->y * v->z;
+ zx = v->z * v->x;
+ xs = v->x * s;
+ ys = v->y * s;
+ zs = v->z * s;
+ one_c = 1.0F - c;
+
+ M(0,0) = (one_c * xx) + c;
+ M(0,1) = (one_c * xy) - zs;
+ M(0,2) = (one_c * zx) + ys;
+ M(0,3) = 0.0F;
+
+ M(1,0) = (one_c * xy) + zs;
+ M(1,1) = (one_c * yy) + c;
+ M(1,2) = (one_c * yz) - xs;
+ M(1,3) = 0.0F;
+
+ M(2,0) = (one_c * zx) - ys;
+ M(2,1) = (one_c * yz) + xs;
+ M(2,2) = (one_c * zz) + c;
+ M(2,3) = 0.0F;
+
+ M(3,0) = 0.0F;
+ M(3,1) = 0.0F;
+ M(3,2) = 0.0F;
+ M(3,3) = 1.0F;
+
+#undef M
+}
+
+/* Transpose the matrix m. If m is orthogonal (like a rotation matrix), */
+/* this is equal to the inverse of the matrix. */
+/* ==================================================================== */
+
+void
+transpose_mat (gfloat m[16])
+{
+ gint i, j;
+ gfloat t;
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < i; j++)
+ {
+ t = m[j*4+i];
+ m[j*4+i] = m[i*4+j];
+ m[i*4+j] = t;
+ }
+ }
+}
+
+/* Compute the matrix product c=a*b */
+/* ================================ */
+
+void
+matmul (gfloat a[16],
+ gfloat b[16],
+ gfloat c[16])
+{
+ gint i, j, k;
+ gfloat value;
+
+#define A(row,col) a[col*4+row]
+#define B(row,col) b[col*4+row]
+#define C(row,col) c[col*4+row]
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ value = 0.0;
+
+ for (k = 0; k < 4; k++)
+ value += A(i,k) * B(k,j);
+
+ C(i,j) = value;
+ }
+ }
+
+#undef A
+#undef B
+#undef C
+}
+
+void
+ident_mat (gfloat m[16])
+{
+ gint i, j;
+
+#define M(row,col) m[col*4+row]
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < 4; j++)
+ {
+ if (i == j)
+ M(i,j) = 1.0;
+ else
+ M(i,j) = 0.0;
+ }
+ }
+
+#undef M
+}
+
+static gboolean
+intersect_rect (gdouble u,
+ gdouble v,
+ gdouble w,
+ GimpVector3 viewp,
+ GimpVector3 dir,
+ FaceIntersectInfo *face_info)
+{
+ gboolean result = FALSE;
+ gdouble u2, v2;
+
+ if (dir.z!=0.0)
+ {
+ u2 = u / 2.0;
+ v2 = v / 2.0;
+
+ face_info->t = (w-viewp.z) / dir.z;
+ face_info->s.x = viewp.x + face_info->t * dir.x;
+ face_info->s.y = viewp.y + face_info->t * dir.y;
+ face_info->s.z = w;
+
+ if (face_info->s.x >= -u2 && face_info->s.x <= u2 &&
+ face_info->s.y >= -v2 && face_info->s.y <= v2)
+ {
+ face_info->u = (face_info->s.x + u2) / u;
+ face_info->v = (face_info->s.y + v2) / v;
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+static gboolean
+intersect_box (GimpVector3 scale,
+ GimpVector3 viewp,
+ GimpVector3 dir,
+ FaceIntersectInfo *face_intersect)
+{
+ GimpVector3 v, d, tmp, axis[3];
+ FaceIntersectInfo face_tmp;
+ gboolean result = FALSE;
+ gfloat m[16];
+ gint i = 0;
+
+ gimp_vector3_set (&axis[0], 1.0, 0.0, 0.0);
+ gimp_vector3_set (&axis[1], 0.0, 1.0, 0.0);
+ gimp_vector3_set (&axis[2], 0.0, 0.0, 1.0);
+
+ /* Front side */
+ /* ========== */
+
+ if (intersect_rect (scale.x, scale.y, scale.z / 2.0,
+ viewp, dir, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 0;
+ gimp_vector3_set (&face_intersect[i++].n, 0.0, 0.0, 1.0);
+ result = TRUE;
+ }
+
+ /* Back side */
+ /* ========= */
+
+ if (intersect_rect (scale.x, scale.y, -scale.z / 2.0,
+ viewp, dir, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 1;
+ face_intersect[i].u = 1.0 - face_intersect[i].u;
+ gimp_vector3_set (&face_intersect[i++].n, 0.0, 0.0, -1.0);
+ result = TRUE;
+ }
+
+ /* Check if we've found the two possible intersection points */
+ /* ========================================================= */
+
+ if (i < 2)
+ {
+ /* Top: Rotate viewpoint and direction into rectangle's local coordinate system */
+ /* ============================================================================ */
+
+ rotatemat (90, &axis[0], m);
+ vecmulmat (&v, &viewp, m);
+ vecmulmat (&d, &dir, m);
+
+ if (intersect_rect (scale.x, scale.z, scale.y / 2.0,
+ v, d, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 2;
+
+ transpose_mat (m);
+ vecmulmat(&tmp, &face_intersect[i].s, m);
+ face_intersect[i].s = tmp;
+
+ gimp_vector3_set (&face_intersect[i++].n, 0.0, -1.0, 0.0);
+ result = TRUE;
+ }
+ }
+
+ /* Check if we've found the two possible intersection points */
+ /* ========================================================= */
+
+ if (i < 2)
+ {
+ /* Bottom: Rotate viewpoint and direction into rectangle's local coordinate system */
+ /* =============================================================================== */
+
+ rotatemat (90, &axis[0], m);
+ vecmulmat (&v, &viewp, m);
+ vecmulmat (&d, &dir, m);
+
+ if (intersect_rect (scale.x, scale.z, -scale.y / 2.0,
+ v, d, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 3;
+
+ transpose_mat (m);
+
+ vecmulmat (&tmp, &face_intersect[i].s, m);
+ face_intersect[i].s = tmp;
+
+ face_intersect[i].v = 1.0 - face_intersect[i].v;
+
+ gimp_vector3_set (&face_intersect[i++].n, 0.0, 1.0, 0.0);
+
+ result = TRUE;
+ }
+ }
+
+ /* Check if we've found the two possible intersection points */
+ /* ========================================================= */
+
+ if (i < 2)
+ {
+ /* Left side: Rotate viewpoint and direction into rectangle's local coordinate system */
+ /* ================================================================================== */
+
+ rotatemat (90, &axis[1], m);
+ vecmulmat (&v, &viewp, m);
+ vecmulmat (&d, &dir, m);
+
+ if (intersect_rect (scale.z, scale.y, scale.x / 2.0,
+ v, d, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 4;
+
+ transpose_mat (m);
+ vecmulmat (&tmp, &face_intersect[i].s, m);
+ face_intersect[i].s = tmp;
+
+ gimp_vector3_set (&face_intersect[i++].n, 1.0, 0.0, 0.0);
+ result = TRUE;
+ }
+ }
+
+ /* Check if we've found the two possible intersection points */
+ /* ========================================================= */
+
+ if (i < 2)
+ {
+ /* Right side: Rotate viewpoint and direction into rectangle's local coordinate system */
+ /* =================================================================================== */
+
+ rotatemat (90, &axis[1], m);
+ vecmulmat (&v, &viewp, m);
+ vecmulmat (&d, &dir, m);
+
+ if (intersect_rect (scale.z, scale.y, -scale.x / 2.0,
+ v, d, &face_intersect[i]) == TRUE)
+ {
+ face_intersect[i].face = 5;
+
+ transpose_mat (m);
+ vecmulmat (&tmp, &face_intersect[i].s, m);
+
+ face_intersect[i].u = 1.0 - face_intersect[i].u;
+
+ gimp_vector3_set (&face_intersect[i++].n, -1.0, 0.0, 0.0);
+ result = TRUE;
+ }
+ }
+
+ /* Sort intersection points */
+ /* ======================== */
+
+ if (face_intersect[0].t > face_intersect[1].t)
+ {
+ face_tmp = face_intersect[0];
+ face_intersect[0] = face_intersect[1];
+ face_intersect[1] = face_tmp;
+ }
+
+ return result;
+}
+
+GimpRGB
+get_ray_color_box (GimpVector3 *pos)
+{
+ GimpVector3 lvp, ldir, vp, p, dir, ns, nn;
+ GimpRGB color, color2;
+ gfloat m[16];
+ gint i;
+ FaceIntersectInfo face_intersect[2];
+
+ color = background;
+ vp = mapvals.viewpoint;
+ p = *pos;
+
+ /* Translate viewpoint so that the box has its origin */
+ /* at its lower left corner. */
+ /* ================================================== */
+
+ vp.x = vp.x - mapvals.position.x;
+ vp.y = vp.y - mapvals.position.y;
+ vp.z = vp.z - mapvals.position.z;
+
+ p.x = p.x - mapvals.position.x;
+ p.y = p.y - mapvals.position.y;
+ p.z = p.z - mapvals.position.z;
+
+ /* Compute direction */
+ /* ================= */
+
+ gimp_vector3_sub (&dir, &p, &vp);
+ gimp_vector3_normalize (&dir);
+
+ /* Compute inverse of rotation matrix and apply it to */
+ /* the viewpoint and direction. This transforms the */
+ /* observer into the local coordinate system of the box */
+ /* ==================================================== */
+
+ memcpy (m, rotmat, sizeof (gfloat) * 16);
+
+ transpose_mat (m);
+
+ vecmulmat (&lvp, &vp, m);
+ vecmulmat (&ldir, &dir, m);
+
+ /* Ok. Now the observer is in the space where the box is located */
+ /* with its lower left corner at the origin and its axis aligned */
+ /* to the cartesian basis. Check if the transformed ray hits it. */
+ /* ============================================================= */
+
+ face_intersect[0].t = 1000000.0;
+ face_intersect[1].t = 1000000.0;
+
+ if (intersect_box (mapvals.scale, lvp, ldir, face_intersect) == TRUE)
+ {
+ /* We've hit the box. Transform the hit points and */
+ /* normals back into the world coordinate system */
+ /* =============================================== */
+
+ for (i = 0; i < 2; i++)
+ {
+ vecmulmat (&ns, &face_intersect[i].s, rotmat);
+ vecmulmat (&nn, &face_intersect[i].n, rotmat);
+
+ ns.x = ns.x + mapvals.position.x;
+ ns.y = ns.y + mapvals.position.y;
+ ns.z = ns.z + mapvals.position.z;
+
+ face_intersect[i].s = ns;
+ face_intersect[i].n = nn;
+ }
+
+ color = get_box_image_color (face_intersect[0].face,
+ face_intersect[0].u,
+ face_intersect[0].v);
+
+ /* Check for total transparency... */
+ /* =============================== */
+
+ if (color.a < 1.0)
+ {
+ /* Hey, we can see through here! */
+ /* Lets see what's on the other side.. */
+ /* =================================== */
+
+ color = phong_shade (&face_intersect[0].s,
+ &mapvals.viewpoint,
+ &face_intersect[0].n,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+
+ color2 = get_box_image_color (face_intersect[1].face,
+ face_intersect[1].u,
+ face_intersect[1].v);
+
+ /* Make the normal point inwards */
+ /* ============================= */
+
+ gimp_vector3_mul (&face_intersect[1].n, -1.0);
+
+ color2 = phong_shade (&face_intersect[1].s,
+ &mapvals.viewpoint,
+ &face_intersect[1].n,
+ &color2,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color2);
+
+ if (mapvals.transparent_background == FALSE && color2.a < 1.0)
+ {
+ gimp_rgb_composite (&color2, &background,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+
+ /* Compute a mix of the first and second colors */
+ /* ============================================ */
+
+ gimp_rgb_composite (&color, &color2, GIMP_RGB_COMPOSITE_NORMAL);
+ gimp_rgb_clamp (&color);
+ }
+ else if (color.a != 0.0 && mapvals.lightsource.type != NO_LIGHT)
+ {
+ color = phong_shade (&face_intersect[0].s,
+ &mapvals.viewpoint,
+ &face_intersect[0].n,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+ }
+ }
+ else
+ {
+ if (mapvals.transparent_background == TRUE)
+ gimp_rgb_set_alpha (&color, 0.0);
+ }
+
+ return color;
+}
+
+static gboolean
+intersect_circle (GimpVector3 vp,
+ GimpVector3 dir,
+ gdouble w,
+ FaceIntersectInfo *face_info)
+{
+ gboolean result = FALSE;
+ gdouble r, d;
+
+#define sqr(a) (a*a)
+
+ if (dir.y != 0.0)
+ {
+ face_info->t = (w-vp.y)/dir.y;
+ face_info->s.x = vp.x + face_info->t*dir.x;
+ face_info->s.y = w;
+ face_info->s.z = vp.z + face_info->t*dir.z;
+
+ r = sqrt (sqr (face_info->s.x) + sqr (face_info->s.z));
+
+ if (r <= mapvals.cylinder_radius)
+ {
+ d = 2.0 * mapvals.cylinder_radius;
+ face_info->u = (face_info->s.x + mapvals.cylinder_radius) / d;
+ face_info->v = (face_info->s.z + mapvals.cylinder_radius) / d;
+ result = TRUE;
+ }
+ }
+
+#undef sqr
+
+ return result;
+}
+
+static gboolean
+intersect_cylinder (GimpVector3 vp,
+ GimpVector3 dir,
+ FaceIntersectInfo *face_intersect)
+{
+ gdouble a, b, c, d, e, f, tmp, l;
+ gboolean result = FALSE;
+ gint i;
+
+#define sqr(a) (a*a)
+
+ a = sqr (dir.x) + sqr (dir.z);
+ b = 2.0 * (vp.x * dir.x + vp.z * dir.z);
+ c = sqr (vp.x) + sqr (vp.z) - sqr (mapvals.cylinder_radius);
+
+ d = sqr (b) - 4.0 * a * c;
+
+ if (d >= 0.0)
+ {
+ e = sqrt (d);
+ f = 2.0 * a;
+
+ if (f != 0.0)
+ {
+ result = TRUE;
+
+ face_intersect[0].t = (-b+e)/f;
+ face_intersect[1].t = (-b-e)/f;
+
+ if (face_intersect[0].t>face_intersect[1].t)
+ {
+ tmp = face_intersect[0].t;
+ face_intersect[0].t = face_intersect[1].t;
+ face_intersect[1].t = tmp;
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ face_intersect[i].s.x = vp.x + face_intersect[i].t * dir.x;
+ face_intersect[i].s.y = vp.y + face_intersect[i].t * dir.y;
+ face_intersect[i].s.z = vp.z + face_intersect[i].t * dir.z;
+
+ face_intersect[i].n = face_intersect[i].s;
+ face_intersect[i].n.y = 0.0;
+ gimp_vector3_normalize(&face_intersect[i].n);
+
+ l = mapvals.cylinder_length/2.0;
+
+ face_intersect[i].u = (atan2(face_intersect[i].s.x,face_intersect[i].s.z)+G_PI)/(2.0*G_PI);
+ face_intersect[i].v = (face_intersect[i].s.y+l)/mapvals.cylinder_length;
+
+ /* Mark hitpoint as on the cylinder hull */
+ /* ===================================== */
+
+ face_intersect[i].face = 0;
+
+ /* Check if we're completely off the cylinder axis */
+ /* =============================================== */
+
+ if (face_intersect[i].s.y>l || face_intersect[i].s.y<-l)
+ {
+ /* Check if we've hit a cap */
+ /* ======================== */
+
+ if (face_intersect[i].s.y>l)
+ {
+ if (intersect_circle(vp,dir,l,&face_intersect[i])==FALSE)
+ result = FALSE;
+ else
+ {
+ face_intersect[i].face = 2;
+ face_intersect[i].v = 1 - face_intersect[i].v;
+ gimp_vector3_set(&face_intersect[i].n, 0.0, 1.0, 0.0);
+ }
+ }
+ else
+ {
+ if (intersect_circle(vp,dir,-l,&face_intersect[i])==FALSE)
+ result = FALSE;
+ else
+ {
+ face_intersect[i].face = 1;
+ gimp_vector3_set(&face_intersect[i].n, 0.0, -1.0, 0.0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+#undef sqr
+
+ return result;
+}
+
+static GimpRGB
+get_cylinder_color (gint face,
+ gdouble u,
+ gdouble v)
+{
+ GimpRGB color;
+ gint inside;
+
+ if (face == 0)
+ color = get_image_color (u, v, &inside);
+ else
+ color = get_cylinder_image_color (face - 1, u, v);
+
+ return color;
+}
+
+GimpRGB
+get_ray_color_cylinder (GimpVector3 *pos)
+{
+ GimpVector3 lvp, ldir, vp, p, dir, ns, nn;
+ GimpRGB color, color2;
+ gfloat m[16];
+ gint i;
+ FaceIntersectInfo face_intersect[2];
+
+ color = background;
+ vp = mapvals.viewpoint;
+ p = *pos;
+
+ vp.x = vp.x - mapvals.position.x;
+ vp.y = vp.y - mapvals.position.y;
+ vp.z = vp.z - mapvals.position.z;
+
+ p.x = p.x - mapvals.position.x;
+ p.y = p.y - mapvals.position.y;
+ p.z = p.z - mapvals.position.z;
+
+ /* Compute direction */
+ /* ================= */
+
+ gimp_vector3_sub (&dir, &p, &vp);
+ gimp_vector3_normalize (&dir);
+
+ /* Compute inverse of rotation matrix and apply it to */
+ /* the viewpoint and direction. This transforms the */
+ /* observer into the local coordinate system of the box */
+ /* ==================================================== */
+
+ memcpy (m, rotmat, sizeof (gfloat) * 16);
+
+ transpose_mat (m);
+
+ vecmulmat (&lvp, &vp, m);
+ vecmulmat (&ldir, &dir, m);
+
+ if (intersect_cylinder (lvp, ldir, face_intersect) == TRUE)
+ {
+ /* We've hit the cylinder. Transform the hit points and */
+ /* normals back into the world coordinate system */
+ /* ==================================================== */
+
+ for (i = 0; i < 2; i++)
+ {
+ vecmulmat (&ns, &face_intersect[i].s, rotmat);
+ vecmulmat (&nn, &face_intersect[i].n, rotmat);
+
+ ns.x = ns.x + mapvals.position.x;
+ ns.y = ns.y + mapvals.position.y;
+ ns.z = ns.z + mapvals.position.z;
+
+ face_intersect[i].s = ns;
+ face_intersect[i].n = nn;
+ }
+
+ color = get_cylinder_color (face_intersect[0].face,
+ face_intersect[0].u,
+ face_intersect[0].v);
+
+ /* Check for transparency... */
+ /* ========================= */
+
+ if (color.a < 1.0)
+ {
+ /* Hey, we can see through here! */
+ /* Lets see what's on the other side.. */
+ /* =================================== */
+
+ color = phong_shade (&face_intersect[0].s,
+ &mapvals.viewpoint,
+ &face_intersect[0].n,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+
+ color2 = get_cylinder_color (face_intersect[1].face,
+ face_intersect[1].u,
+ face_intersect[1].v);
+
+ /* Make the normal point inwards */
+ /* ============================= */
+
+ gimp_vector3_mul (&face_intersect[1].n, -1.0);
+
+ color2 = phong_shade (&face_intersect[1].s,
+ &mapvals.viewpoint,
+ &face_intersect[1].n,
+ &color2,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color2);
+
+ if (mapvals.transparent_background == FALSE && color2.a < 1.0)
+ {
+ gimp_rgb_composite (&color2, &background,
+ GIMP_RGB_COMPOSITE_BEHIND);
+ }
+
+ /* Compute a mix of the first and second colors */
+ /* ============================================ */
+
+ gimp_rgb_composite (&color, &color2, GIMP_RGB_COMPOSITE_NORMAL);
+ gimp_rgb_clamp (&color);
+ }
+ else if (color.a != 0.0 && mapvals.lightsource.type != NO_LIGHT)
+ {
+ color = phong_shade (&face_intersect[0].s,
+ &mapvals.viewpoint,
+ &face_intersect[0].n,
+ &color,
+ &mapvals.lightsource.color,
+ mapvals.lightsource.type);
+
+ gimp_rgb_clamp (&color);
+ }
+ }
+ else
+ {
+ if (mapvals.transparent_background == TRUE)
+ gimp_rgb_set_alpha (&color, 0.0);
+ }
+
+ return color;
+}
diff --git a/plug-ins/map-object/map-object-shade.h b/plug-ins/map-object/map-object-shade.h
new file mode 100644
index 0000000..259e5eb
--- /dev/null
+++ b/plug-ins/map-object/map-object-shade.h
@@ -0,0 +1,26 @@
+#ifndef __MAPOBJECT_SHADE_H__
+#define __MAPOBJECT_SHADE_H__
+
+typedef GimpRGB (* get_ray_color_func) (GimpVector3 *pos);
+
+extern get_ray_color_func get_ray_color;
+
+GimpRGB get_ray_color_plane (GimpVector3 *pos);
+GimpRGB get_ray_color_sphere (GimpVector3 *pos);
+GimpRGB get_ray_color_box (GimpVector3 *pos);
+GimpRGB get_ray_color_cylinder (GimpVector3 *pos);
+void compute_bounding_box (void);
+
+void vecmulmat (GimpVector3 *u,
+ GimpVector3 *v,
+ gfloat m[16]);
+void rotatemat (gfloat angle,
+ GimpVector3 *v,
+ gfloat m[16]);
+void transpose_mat (gfloat m[16]);
+void matmul (gfloat a[16],
+ gfloat b[16],
+ gfloat c[16]);
+void ident_mat (gfloat m[16]);
+
+#endif /* __MAPOBJECT_SHADE_H__ */
diff --git a/plug-ins/map-object/map-object-stock.c b/plug-ins/map-object/map-object-stock.c
new file mode 100644
index 0000000..fc3c153
--- /dev/null
+++ b/plug-ins/map-object/map-object-stock.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "map-object-stock.h"
+
+#include "../lighting/images/stock-pixbufs.h"
+
+
+static GtkIconFactory *mapobject_icon_factory = NULL;
+
+static GtkStockItem mapobject_stock_items[] =
+{
+ { STOCK_INTENSITY_AMBIENT_LOW, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_AMBIENT_HIGH, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_DIFFUSE_LOW, NULL, 0, 0, NULL },
+ { STOCK_INTENSITY_DIFFUSE_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_DIFFUSE_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_DIFFUSE_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_SPECULAR_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_SPECULAR_HIGH, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_HIGHLIGHT_LOW, NULL, 0, 0, NULL },
+ { STOCK_REFLECTIVITY_HIGHLIGHT_HIGH, NULL, 0, 0, NULL }
+};
+
+
+static void
+add_stock_icon (const gchar *stock_id,
+ GtkIconSize size,
+ const guint8 *inline_data)
+{
+ GtkIconSource *source;
+ GtkIconSet *set;
+ GdkPixbuf *pixbuf;
+
+ source = gtk_icon_source_new ();
+
+ gtk_icon_source_set_size (source, size);
+ gtk_icon_source_set_size_wildcarded (source, FALSE);
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);
+
+ gtk_icon_source_set_pixbuf (source, pixbuf);
+ g_object_unref (pixbuf);
+
+ set = gtk_icon_set_new ();
+
+ gtk_icon_set_add_source (set, source);
+ gtk_icon_source_free (source);
+
+ gtk_icon_factory_add (mapobject_icon_factory, stock_id, set);
+
+ gtk_icon_set_unref (set);
+}
+
+void
+mapobject_stock_init (void)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ mapobject_icon_factory = gtk_icon_factory_new ();
+
+ add_stock_icon (STOCK_INTENSITY_AMBIENT_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_ambient_low);
+ add_stock_icon (STOCK_INTENSITY_AMBIENT_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_ambient_high);
+ add_stock_icon (STOCK_INTENSITY_DIFFUSE_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_diffuse_low);
+ add_stock_icon (STOCK_INTENSITY_DIFFUSE_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_intensity_diffuse_high);
+ add_stock_icon (STOCK_REFLECTIVITY_DIFFUSE_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_diffuse_low);
+ add_stock_icon (STOCK_REFLECTIVITY_DIFFUSE_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_diffuse_high);
+ add_stock_icon (STOCK_REFLECTIVITY_SPECULAR_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_specular_low);
+ add_stock_icon (STOCK_REFLECTIVITY_SPECULAR_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_specular_high);
+ add_stock_icon (STOCK_REFLECTIVITY_HIGHLIGHT_LOW, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_highlight_low);
+ add_stock_icon (STOCK_REFLECTIVITY_HIGHLIGHT_HIGH, GTK_ICON_SIZE_BUTTON,
+ stock_reflectivity_highlight_high);
+
+ gtk_icon_factory_add_default (mapobject_icon_factory);
+
+ gtk_stock_add_static (mapobject_stock_items,
+ G_N_ELEMENTS (mapobject_stock_items));
+
+ initialized = TRUE;
+}
diff --git a/plug-ins/map-object/map-object-stock.h b/plug-ins/map-object/map-object-stock.h
new file mode 100644
index 0000000..9226383
--- /dev/null
+++ b/plug-ins/map-object/map-object-stock.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MAPOBJECT_STOCK_H__
+#define __MAPOBJECT_STOCK_H__
+
+
+#define STOCK_INTENSITY_AMBIENT_LOW "intensity-ambient-low"
+#define STOCK_INTENSITY_AMBIENT_HIGH "intensity-ambient-high"
+#define STOCK_INTENSITY_DIFFUSE_LOW "intensity-diffuse-low"
+#define STOCK_INTENSITY_DIFFUSE_HIGH "intensity-diffuse-high"
+#define STOCK_REFLECTIVITY_DIFFUSE_LOW "reflectivity-diffuse-low"
+#define STOCK_REFLECTIVITY_DIFFUSE_HIGH "reflectivity-diffuse-high"
+#define STOCK_REFLECTIVITY_SPECULAR_LOW "reflectivity-specular-low"
+#define STOCK_REFLECTIVITY_SPECULAR_HIGH "reflectivity-specular-high"
+#define STOCK_REFLECTIVITY_HIGHLIGHT_LOW "reflectivity-highlight-low"
+#define STOCK_REFLECTIVITY_HIGHLIGHT_HIGH "reflectivity-highlight-high"
+
+
+void mapobject_stock_init (void);
+
+
+#endif /* __MAPOBJECT_STOCK_H__ */
diff --git a/plug-ins/map-object/map-object-ui.c b/plug-ins/map-object/map-object-ui.c
new file mode 100644
index 0000000..7f1d96d
--- /dev/null
+++ b/plug-ins/map-object/map-object-ui.c
@@ -0,0 +1,1462 @@
+/**************************************************************/
+/* Dialog creation and updaters, callbacks and event-handlers */
+/**************************************************************/
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "arcball.h"
+#include "map-object-ui.h"
+#include "map-object-image.h"
+#include "map-object-apply.h"
+#include "map-object-preview.h"
+#include "map-object-main.h"
+#include "map-object-stock.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+GtkWidget *previewarea = NULL;
+
+static GtkWidget *appwin = NULL;
+static GtkNotebook *options_note_book = NULL;
+
+static GtkWidget *pointlightwid;
+static GtkWidget *dirlightwid;
+
+static GtkAdjustment *xadj, *yadj, *zadj;
+
+static GtkWidget *box_page = NULL;
+static GtkWidget *cylinder_page = NULL;
+
+static guint left_button_pressed = FALSE;
+static guint light_hit = FALSE;
+
+
+static void create_main_notebook (GtkWidget *container);
+
+static gint preview_events (GtkWidget *area,
+ GdkEvent *event);
+
+static void update_light_pos_entries (void);
+
+static void double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data);
+
+static void toggle_update (GtkWidget *widget,
+ gpointer data);
+
+static void lightmenu_callback (GtkWidget *widget,
+ gpointer data);
+
+static void preview_callback (GtkWidget *widget,
+ gpointer data);
+
+static gint box_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+static gint cylinder_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data);
+
+static GtkWidget * create_options_page (void);
+static GtkWidget * create_light_page (void);
+static GtkWidget * create_material_page (void);
+static GtkWidget * create_orientation_page (void);
+static GtkWidget * create_box_page (void);
+static GtkWidget * create_cylinder_page (void);
+
+
+/******************************************************/
+/* Update angle & position (redraw grid if necessary) */
+/******************************************************/
+
+static void
+double_adjustment_update (GtkAdjustment *adjustment,
+ gpointer data)
+{
+ gimp_double_adjustment_update (adjustment, data);
+
+ if (mapvals.livepreview)
+ compute_preview_image ();
+
+ gtk_widget_queue_draw (previewarea);
+}
+
+static void
+update_light_pos_entries (void)
+{
+ g_signal_handlers_block_by_func (xadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.x);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (xadj),
+ mapvals.lightsource.position.x);
+ g_signal_handlers_unblock_by_func (xadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.x);
+
+ g_signal_handlers_block_by_func (yadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.y);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (yadj),
+ mapvals.lightsource.position.y);
+ g_signal_handlers_unblock_by_func (yadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.y);
+
+ g_signal_handlers_block_by_func (zadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.z);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (zadj),
+ mapvals.lightsource.position.z);
+ g_signal_handlers_unblock_by_func (zadj,
+ double_adjustment_update,
+ &mapvals.lightsource.position.z);
+}
+
+/**********************/
+/* Std. toggle update */
+/**********************/
+
+static void
+toggle_update (GtkWidget *widget,
+ gpointer data)
+{
+ gimp_toggle_button_update (widget, data);
+
+ compute_preview_image ();
+ gtk_widget_queue_draw (previewarea);
+}
+
+/*****************************************/
+/* Main window light type menu callback. */
+/*****************************************/
+
+static void
+lightmenu_callback (GtkWidget *widget,
+ gpointer data)
+{
+ int active;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &active);
+
+ mapvals.lightsource.type = active;
+
+ if (mapvals.lightsource.type == POINT_LIGHT)
+ {
+ gtk_widget_hide (dirlightwid);
+ gtk_widget_show (pointlightwid);
+ }
+ else if (mapvals.lightsource.type == DIRECTIONAL_LIGHT)
+ {
+ gtk_widget_hide (pointlightwid);
+ gtk_widget_show (dirlightwid);
+ }
+ else
+ {
+ gtk_widget_hide (pointlightwid);
+ gtk_widget_hide (dirlightwid);
+ }
+
+ if (mapvals.livepreview)
+ {
+ compute_preview_image ();
+ gtk_widget_queue_draw (previewarea);
+ }
+}
+
+/***************************************/
+/* Main window map type menu callback. */
+/***************************************/
+
+static void
+mapmenu_callback (GtkWidget *widget,
+ gpointer data)
+{
+ int active;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget),
+ &active);
+
+ mapvals.maptype = active;
+
+ if (mapvals.livepreview)
+ {
+ compute_preview_image ();
+ gtk_widget_queue_draw (previewarea);
+ }
+
+ if (mapvals.maptype == MAP_BOX)
+ {
+ if (cylinder_page && gtk_widget_get_parent (GTK_WIDGET (cylinder_page)) ==
+ GTK_WIDGET (options_note_book))
+ {
+ gtk_container_remove (GTK_CONTAINER (options_note_book), cylinder_page);
+ }
+
+ if (!box_page)
+ {
+ box_page = create_box_page ();
+ g_object_ref (box_page);
+ }
+ gtk_notebook_append_page (options_note_book,
+ box_page,
+ gtk_label_new_with_mnemonic (_("_Box")));
+ }
+ else if (mapvals.maptype == MAP_CYLINDER)
+ {
+ if (box_page && gtk_widget_get_parent (GTK_WIDGET (box_page)) ==
+ GTK_WIDGET (options_note_book))
+ {
+ gtk_container_remove (GTK_CONTAINER (options_note_book), box_page);
+ }
+
+ if (!cylinder_page)
+ {
+ cylinder_page = create_cylinder_page ();
+ g_object_ref (cylinder_page);
+ }
+ gtk_notebook_append_page (options_note_book,
+ cylinder_page,
+ gtk_label_new_with_mnemonic (_("C_ylinder")));
+ }
+ else
+ {
+ if (box_page && gtk_widget_get_parent (GTK_WIDGET (box_page)) ==
+ GTK_WIDGET (options_note_book))
+ {
+ gtk_container_remove (GTK_CONTAINER (options_note_book), box_page);
+ }
+
+ if (cylinder_page && gtk_widget_get_parent (GTK_WIDGET (cylinder_page)) ==
+ GTK_WIDGET (options_note_book))
+ {
+ gtk_container_remove (GTK_CONTAINER (options_note_book), cylinder_page);
+ }
+ }
+}
+
+/******************************************/
+/* Main window "Preview!" button callback */
+/******************************************/
+
+static void
+preview_callback (GtkWidget *widget,
+ gpointer data)
+{
+ compute_preview_image ();
+
+ gtk_widget_queue_draw (previewarea);
+}
+
+static void
+zoomed_callback (GimpZoomModel *model)
+{
+ mapvals.zoom = gimp_zoom_model_get_factor (model);
+
+ compute_preview_image ();
+
+ gtk_widget_queue_draw (previewarea);
+}
+
+/**********************************************/
+/* Main window "Apply" button callback. */
+/* Render to GIMP image, close down and exit. */
+/**********************************************/
+
+static gint
+box_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ if (drawable_id == -1)
+ return TRUE;
+
+ return (gimp_drawable_is_rgb (drawable_id) &&
+ !gimp_drawable_is_indexed (drawable_id));
+}
+
+static gint
+cylinder_constrain (gint32 image_id,
+ gint32 drawable_id,
+ gpointer data)
+{
+ if (drawable_id == -1)
+ return TRUE;
+
+ return (gimp_drawable_is_rgb (drawable_id) &&
+ !gimp_drawable_is_indexed (drawable_id));
+}
+
+/******************************/
+/* Preview area event handler */
+/******************************/
+
+static gint
+preview_events (GtkWidget *area,
+ GdkEvent *event)
+{
+ HVect __attribute__((unused))pos;
+/* HMatrix RotMat;
+ gdouble a,b,c; */
+
+ switch (event->type)
+ {
+ case GDK_ENTER_NOTIFY:
+ break;
+
+ case GDK_LEAVE_NOTIFY:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ light_hit = check_light_hit (event->button.x, event->button.y);
+ if (light_hit == FALSE)
+ {
+ pos.x = -(2.0 * (gdouble) event->button.x /
+ (gdouble) PREVIEW_WIDTH - 1.0);
+ pos.y = (2.0 * (gdouble) event->button.y /
+ (gdouble) PREVIEW_HEIGHT - 1.0);
+ /*ArcBall_Mouse(pos);
+ ArcBall_BeginDrag(); */
+ }
+ left_button_pressed = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (light_hit == TRUE)
+ {
+ compute_preview_image ();
+
+ gtk_widget_queue_draw (previewarea);
+ }
+ else
+ {
+ pos.x = -(2.0 * (gdouble) event->button.x /
+ (gdouble) PREVIEW_WIDTH - 1.0);
+ pos.y = (2.0 * (gdouble) event->button.y /
+ (gdouble) PREVIEW_HEIGHT - 1.0);
+ /*ArcBall_Mouse(pos);
+ ArcBall_EndDrag(); */
+ }
+ left_button_pressed = FALSE;
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (left_button_pressed == TRUE)
+ {
+ if (light_hit == TRUE)
+ {
+ gint live = mapvals.livepreview;
+
+ mapvals.livepreview = FALSE;
+ update_light (event->motion.x, event->motion.y);
+ update_light_pos_entries ();
+ mapvals.livepreview = live;
+ }
+ else
+ {
+ pos.x = -(2.0 * (gdouble) event->motion.x /
+ (gdouble) PREVIEW_WIDTH - 1.0);
+ pos.y = (2.0 * (gdouble) event->motion.y /
+ (gdouble) PREVIEW_HEIGHT - 1.0);
+/* ArcBall_Mouse(pos);
+ ArcBall_Update();
+ ArcBall_Values(&a,&b,&c);
+ Alpha+=RadToDeg(-a);
+ Beta+RadToDeg(-b);
+ Gamma+=RadToDeg(-c);
+ if (Alpha>180) Alpha-=360;
+ if (Alpha<-180) Alpha+=360;
+ if (Beta>180) Beta-=360;
+ if (Beta<-180) Beta+=360;
+ if (Gamma>180) Gamma-=360;
+ if (Gamma<-180) Gamma+=360;
+ UpdateAngleSliders(); */
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+static GtkWidget *
+spin_button_new (GtkAdjustment **adjustment, /* return value */
+ gdouble value,
+ gdouble lower,
+ gdouble upper,
+ gdouble step_increment,
+ gdouble page_increment,
+ gdouble page_size,
+ gdouble climb_rate,
+ guint digits)
+{
+ GtkWidget *spinbutton;
+
+ *adjustment = (GtkAdjustment *)
+ gtk_adjustment_new (value, lower, upper,
+ step_increment, page_increment, 0);
+
+ spinbutton = gimp_spin_button_new (GTK_ADJUSTMENT (*adjustment),
+ climb_rate, digits);
+
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+
+ return spinbutton;
+}
+
+/*******************************/
+/* Create general options page */
+/*******************************/
+
+static GtkWidget *
+create_options_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *toggle;
+ GtkWidget *table;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ /* General options */
+
+ frame = gimp_frame_new (_("General Options"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new (_("Map to:"));
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ combo = gimp_int_combo_box_new (_("Plane"), MAP_PLANE,
+ _("Sphere"), MAP_SPHERE,
+ _("Box"), MAP_BOX,
+ _("Cylinder"), MAP_CYLINDER,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), mapvals.maptype);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (mapmenu_callback),
+ &mapvals.maptype);
+
+ gimp_help_set_help_data (combo, _("Type of object to map to"), NULL);
+
+ toggle = gtk_check_button_new_with_label (_("Transparent background"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.transparent_background);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.transparent_background);
+
+ gimp_help_set_help_data (toggle,
+ _("Make image transparent outside object"), NULL);
+
+ toggle = gtk_check_button_new_with_label (_("Tile source image"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.tiled);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.tiled);
+
+ gimp_help_set_help_data (toggle,
+ _("Tile source image: useful for infinite planes"),
+ NULL);
+
+ toggle = gtk_check_button_new_with_label (_("Create new image"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.create_new_image);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.create_new_image);
+
+ gimp_help_set_help_data (toggle,
+ _("Create a new image when applying filter"), NULL);
+
+ toggle = gtk_check_button_new_with_label (_("Create new layer"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.create_new_layer);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.create_new_layer);
+
+ gimp_help_set_help_data (toggle,
+ _("Create a new layer when applying filter"), NULL);
+
+ /* Antialiasing options */
+
+ frame = gimp_frame_new (NULL);
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Enable _antialiasing"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ mapvals.antialiasing);
+ gtk_frame_set_label_widget (GTK_FRAME (frame), toggle);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &mapvals.antialiasing);
+
+ gimp_help_set_help_data (toggle,
+ _("Enable/disable jagged edges removal "
+ "(antialiasing)"), NULL);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ g_object_bind_property (toggle, "active",
+ table, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Depth:"), 0, 0,
+ mapvals.maxdepth, 1.0, 5.0, 0.1, 1.0,
+ 1, TRUE, 0, 0,
+ _("Antialiasing quality. Higher is better, "
+ "but slower"), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &mapvals.maxdepth);
+
+ spinbutton = spin_button_new (&adj, mapvals.pixelthreshold,
+ 0.001, 1000, 0.1, 1, 0, 0, 3);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("_Threshold:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.pixelthreshold);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Stop when pixel differences are smaller than "
+ "this value"), NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/******************************/
+/* Create light settings page */
+/******************************/
+
+static GtkWidget *
+create_light_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *combo;
+ GtkWidget *colorbutton;
+ GtkWidget *spinbutton;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Light Settings"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table); gtk_widget_show (table);
+
+ combo = gimp_int_combo_box_new (_("Point light"), POINT_LIGHT,
+ _("Directional light"), DIRECTIONAL_LIGHT,
+ _("No light"), NO_LIGHT,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ mapvals.lightsource.type);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Lightsource type:"), 0.0, 0.5,
+ combo, 1, FALSE);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (lightmenu_callback),
+ &mapvals.lightsource.type);
+
+ gimp_help_set_help_data (combo, _("Type of light source to apply"), NULL);
+
+ colorbutton = gimp_color_button_new (_("Select lightsource color"),
+ 64, 16,
+ &mapvals.lightsource.color,
+ GIMP_COLOR_AREA_FLAT);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Lightsource color:"), 0.0, 0.5,
+ colorbutton, 1, FALSE);
+
+ g_signal_connect (colorbutton, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &mapvals.lightsource.color);
+
+ gimp_help_set_help_data (colorbutton,
+ _("Set light source color"), NULL);
+
+ pointlightwid = gimp_frame_new (_("Position"));
+ gtk_box_pack_start (GTK_BOX (page), pointlightwid, FALSE, FALSE, 0);
+
+ if (mapvals.lightsource.type == POINT_LIGHT)
+ gtk_widget_show (pointlightwid);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (pointlightwid), table);
+ gtk_widget_show (table);
+
+ spinbutton = spin_button_new (&xadj, mapvals.lightsource.position.x,
+ -G_MAXFLOAT, G_MAXFLOAT,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("X:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (xadj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.position.x);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source X position in XYZ space"), NULL);
+
+ spinbutton = spin_button_new (&yadj, mapvals.lightsource.position.y,
+ -G_MAXFLOAT, G_MAXFLOAT,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Y:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (yadj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.position.y);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source Y position in XYZ space"), NULL);
+
+ spinbutton = spin_button_new (&zadj, mapvals.lightsource.position.z,
+ -G_MAXFLOAT, G_MAXFLOAT,
+ 0.1, 1.0, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Z:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (zadj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.position.z);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source Z position in XYZ space"), NULL);
+
+
+ dirlightwid = gimp_frame_new (_("Direction Vector"));
+ gtk_box_pack_start (GTK_BOX (page), dirlightwid, FALSE, FALSE, 0);
+
+ if (mapvals.lightsource.type == DIRECTIONAL_LIGHT)
+ gtk_widget_show (dirlightwid);
+
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (dirlightwid), table);
+ gtk_widget_show (table);
+
+ spinbutton = spin_button_new (&adj, mapvals.lightsource.direction.x,
+ -1.0, 1.0, 0.01, 0.1, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("X:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.direction.x);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source X direction in XYZ space"), NULL);
+
+ spinbutton = spin_button_new (&adj, mapvals.lightsource.direction.y,
+ -1.0, 1.0, 0.01, 0.1, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Y:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.direction.y);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source Y direction in XYZ space"), NULL);
+
+ spinbutton = spin_button_new (&adj, mapvals.lightsource.direction.z,
+ -1.0, 1.0, 0.01, 0.1, 0.0, 0.0, 2);
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Z:"), 0.0, 0.5,
+ spinbutton, 1, TRUE);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.lightsource.direction.z);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Light source Z direction in XYZ space"), NULL);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+/*********************************/
+/* Create material settings page */
+/*********************************/
+
+static GtkWidget *
+create_material_page (void)
+{
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *hbox;
+ GtkWidget *spinbutton;
+ GtkWidget *image;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Intensity Levels"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ table = gtk_table_new (2, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Ambient intensity */
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_AMBIENT_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Ambient:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.ambient_int,
+ 0, G_MAXFLOAT, 0.1, 1.0, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.material.ambient_int);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Amount of original color to show where no "
+ "direct light falls"), NULL);
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_AMBIENT_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Diffuse intensity */
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_DIFFUSE_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Diffuse:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.diffuse_int,
+ 0, G_MAXFLOAT, 0.1, 1.0, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.material.diffuse_int);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Intensity of original color when lit by a light "
+ "source"), NULL);
+
+ image = gtk_image_new_from_stock (STOCK_INTENSITY_DIFFUSE_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ frame = gimp_frame_new (_("Reflectivity"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ table = gtk_table_new (3, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* Diffuse reflection */
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_DIFFUSE_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Diffuse:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.diffuse_ref,
+ 0, G_MAXFLOAT, 0.1, 1.0, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.material.diffuse_ref);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Higher values makes the object reflect more "
+ "light (appear lighter)"), NULL);
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_DIFFUSE_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 0, 1,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Specular reflection */
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_SPECULAR_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Specular:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.specular_ref,
+ 0, G_MAXFLOAT, 0.1, 1.0, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.material.specular_ref);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Controls how intense the highlights will be"),
+ NULL);
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_SPECULAR_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 1, 2,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ /* Highlight */
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_HIGHLIGHT_LOW,
+ GTK_ICON_SIZE_BUTTON);
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Highlight:"), 0.0, 0.5,
+ image, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+
+ spinbutton = spin_button_new (&adj, mapvals.material.highlight,
+ 0, G_MAXFLOAT, 0.1, 1.0, 0.0, 0.0, 2);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 2, 3, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.material.highlight);
+
+ gimp_help_set_help_data (spinbutton,
+ _("Higher values makes the highlights more focused"),
+ NULL);
+
+ image = gtk_image_new_from_stock (STOCK_REFLECTIVITY_HIGHLIGHT_HIGH,
+ GTK_ICON_SIZE_BUTTON);
+ gtk_table_attach (GTK_TABLE (table), image, 3, 4, 2, 3,
+ GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (image);
+
+ gtk_widget_show (page);
+
+ g_object_unref (group);
+
+ return page;
+}
+
+/****************************************/
+/* Create orientation and position page */
+/****************************************/
+
+static GtkWidget *
+create_orientation_page (void)
+{
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Position"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("X:"), 0, 0,
+ mapvals.position.x, -1.0, 2.0, 0.01, 0.1, 5,
+ TRUE, 0, 0,
+ _("Object X position in XYZ space"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.01, 5);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.position.x);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Y:"), 0, 0,
+ mapvals.position.y, -1.0, 2.0, 0.01, 0.1, 5,
+ TRUE, 0, 0,
+ _("Object Y position in XYZ space"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.01, 5);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.position.y);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Z:"), 0, 0,
+ mapvals.position.z, -1.0, 2.0, 0.01, 0.1, 5,
+ TRUE, 0, 0,
+ _("Object Z position in XYZ space"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.01, 5);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.position.z);
+
+ frame = gimp_frame_new (_("Rotation"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("X:"), 0, 0,
+ mapvals.alpha, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ _("Rotation angle about X axis"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.alpha);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Y:"), 0, 0,
+ mapvals.beta, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ _("Rotation angle about Y axis"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.beta);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Z:"), 0, 0,
+ mapvals.gamma, -180.0, 180.0, 1.0, 15.0, 1,
+ TRUE, 0, 0,
+ _("Rotation angle about Z axis"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_SPINBUTTON (adj));
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.gamma);
+
+ gtk_widget_show (page);
+
+ g_object_unref (group);
+
+ return page;
+}
+
+static GtkWidget *
+create_box_page (void)
+{
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+ gint i;
+
+ static gchar *labels[] =
+ {
+ N_("Front:"), N_("Back:"),
+ N_("Top:"), N_("Bottom:"),
+ N_("Left:"), N_("Right:")
+ };
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Map Images to Box Faces"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ table = gtk_table_new (6, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE(table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 5);
+ gtk_widget_show (table);
+
+ for (i = 0; i < 6; i++)
+ {
+ GtkWidget *combo;
+
+ combo = gimp_drawable_combo_box_new (box_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ mapvals.boxmap_id[i],
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &mapvals.boxmap_id[i]);
+
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, i,
+ gettext (labels[i]), 0.0, 0.5,
+ combo, 1, FALSE);
+ }
+
+ /* Scale scales */
+
+ table = gtk_table_new (3, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE(table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE(table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("Scale X:"), 0, 0,
+ mapvals.scale.x, 0.0, 5.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("X scale (size)"), NULL);
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.1, 2);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.scale.x);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("Y:"), 0, 0,
+ mapvals.scale.y, 0.0, 5.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Y scale (size)"), NULL);
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.1, 2);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.scale.y);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 2,
+ _("Z:"), 0, 0,
+ mapvals.scale.z, 0.0, 5.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Z scale (size)"), NULL);
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.1, 2);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.scale.z);
+
+ gtk_widget_show (page);
+
+ return page;
+}
+
+static GtkWidget *
+create_cylinder_page (void)
+{
+ GtkSizeGroup *group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ GtkWidget *page;
+ GtkWidget *frame;
+ GtkWidget *table;
+ GtkAdjustment *adj;
+ gint i;
+
+ static const gchar *labels[] = { N_("_Top:"), N_("_Bottom:") };
+
+ page = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (page), 12);
+
+ frame = gimp_frame_new (_("Images for the Cap Faces"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ /* Option menus */
+
+ for (i = 0; i < 2; i++)
+ {
+ GtkWidget *combo;
+ GtkWidget *label;
+
+ combo = gimp_drawable_combo_box_new (cylinder_constrain, NULL);
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ mapvals.cylindermap_id[i],
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &mapvals.cylindermap_id[i]);
+
+ label = gimp_table_attach_aligned (GTK_TABLE (table), 0, i,
+ gettext (labels[i]), 0.0, 0.5,
+ combo, 1, FALSE);
+ gtk_size_group_add_widget (group, label);
+ }
+
+ frame = gimp_frame_new (_("Size"));
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (2, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("R_adius:"), 0, 0,
+ mapvals.cylinder_radius,
+ 0.0, 2.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Cylinder radius"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.1, 2);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.cylinder_radius);
+
+ adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
+ _("L_ength:"), 0, 0,
+ mapvals.cylinder_length,
+ 0.0, 2.0, 0.01, 0.1, 2,
+ TRUE, 0, 0,
+ _("Cylinder length"), NULL);
+ gtk_size_group_add_widget (group, GIMP_SCALE_ENTRY_LABEL (adj));
+ gtk_spin_button_configure (GIMP_SCALE_ENTRY_SPINBUTTON (adj),
+ GIMP_SCALE_ENTRY_SPINBUTTON_ADJ (adj), 0.1, 2);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (double_adjustment_update),
+ &mapvals.cylinder_length);
+
+ gtk_widget_show (page);
+
+ g_object_unref (group);
+
+ return page;
+}
+
+/****************************/
+/* Create notbook and pages */
+/****************************/
+
+static void
+create_main_notebook (GtkWidget *container)
+{
+ GtkWidget *page;
+
+ options_note_book = GTK_NOTEBOOK (gtk_notebook_new ());
+ gtk_container_add (GTK_CONTAINER (container),
+ GTK_WIDGET (options_note_book));
+
+ page = create_options_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("O_ptions")));
+
+ page = create_light_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Light")));
+
+ page = create_material_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("_Material")));
+
+ page = create_orientation_page ();
+ gtk_notebook_append_page (options_note_book, page,
+ gtk_label_new_with_mnemonic (_("O_rientation")));
+
+ if (mapvals.maptype == MAP_BOX)
+ {
+ box_page = create_box_page ();
+ g_object_ref (box_page);
+ gtk_notebook_append_page (options_note_book, box_page,
+ gtk_label_new_with_mnemonic (_("_Box")));
+ }
+ else if (mapvals.maptype == MAP_CYLINDER)
+ {
+ cylinder_page = create_cylinder_page ();
+ g_object_ref (cylinder_page);
+ gtk_notebook_append_page (options_note_book, cylinder_page,
+ gtk_label_new_with_mnemonic (_("C_ylinder")));
+ }
+
+ gtk_widget_show (GTK_WIDGET (options_note_book));
+}
+
+/********************************/
+/* Create and show main dialog. */
+/********************************/
+
+gboolean
+main_dialog (gint32 drawable_id)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *toggle;
+ GimpZoomModel *model;
+ gboolean run = FALSE;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ appwin = gimp_dialog_new (_("Map to Object"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (appwin),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (appwin));
+
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (appwin))),
+ main_hbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_hbox);
+
+ /* Create the Preview */
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* Add preview widget and various buttons to the first part */
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ gtk_widget_realize (appwin);
+ previewarea = gtk_drawing_area_new ();
+ gtk_widget_set_size_request (previewarea, PREVIEW_WIDTH, PREVIEW_HEIGHT);
+ gtk_widget_set_events (previewarea, (GDK_EXPOSURE_MASK |
+ GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK));
+ gtk_container_add (GTK_CONTAINER (frame), previewarea);
+ gtk_widget_show (previewarea);
+
+ g_signal_connect (previewarea, "event",
+ G_CALLBACK (preview_events),
+ previewarea);
+
+ g_signal_connect (previewarea, "expose-event",
+ G_CALLBACK (preview_expose),
+ previewarea);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ button = gtk_button_new_with_mnemonic (_("_Preview!"));
+ gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (preview_callback),
+ NULL);
+
+ gimp_help_set_help_data (button, _("Recompute preview image"), NULL);
+
+ model = gimp_zoom_model_new ();
+ gimp_zoom_model_set_range (model, 0.25, 1.0);
+ gimp_zoom_model_zoom (model, GIMP_ZOOM_TO, mapvals.zoom);
+
+ button = gimp_zoom_button_new (model, GIMP_ZOOM_IN, GTK_ICON_SIZE_MENU);
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ button = gimp_zoom_button_new (model, GIMP_ZOOM_OUT, GTK_ICON_SIZE_MENU);
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (model, "zoomed",
+ G_CALLBACK (zoomed_callback),
+ NULL);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Show _wireframe"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mapvals.showgrid);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.showgrid);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Update preview _live"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mapvals.livepreview);
+ gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_update),
+ &mapvals.livepreview);
+
+ create_main_notebook (main_hbox);
+
+ gtk_widget_show (appwin);
+
+ {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (previewarea),
+ GDK_HAND2);
+ gdk_window_set_cursor (gtk_widget_get_window (previewarea), cursor);
+ gdk_cursor_unref (cursor);
+ }
+
+ image_setup (drawable_id, TRUE);
+
+ compute_preview_image ();
+
+ if (gimp_dialog_run (GIMP_DIALOG (appwin)) == GTK_RESPONSE_OK)
+ run = TRUE;
+
+ gtk_widget_destroy (appwin);
+ if (preview_rgb_data)
+ g_free (preview_rgb_data);
+ if (preview_surface)
+ cairo_surface_destroy (preview_surface);
+ if (box_page)
+ g_object_unref (box_page);
+ if (cylinder_page)
+ g_object_unref (cylinder_page);
+
+ return run;
+}
diff --git a/plug-ins/map-object/map-object-ui.h b/plug-ins/map-object/map-object-ui.h
new file mode 100644
index 0000000..69f6065
--- /dev/null
+++ b/plug-ins/map-object/map-object-ui.h
@@ -0,0 +1,14 @@
+#ifndef __MAPOBJECT_UI_H__
+#define __MAPOBJECT_UI_H__
+
+/* Externally visible variables */
+/* ============================ */
+
+extern GtkWidget *previewarea;
+
+/* Externally visible functions */
+/* ============================ */
+
+gboolean main_dialog (gint32 drawable_id);
+
+#endif /* __MAPOBJECT_UI_H__ */
diff --git a/plug-ins/metadata/Makefile.am b/plug-ins/metadata/Makefile.am
new file mode 100644
index 0000000..23e00f2
--- /dev/null
+++ b/plug-ins/metadata/Makefile.am
@@ -0,0 +1,82 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+metadata_editor_RC = metadata-editor.rc.o
+metadata_viewer_RC = metadata-viewer.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+me_libexecdir = $(gimpplugindir)/plug-ins/metadata-editor
+mv_libexecdir = $(gimpplugindir)/plug-ins/metadata-viewer
+
+me_libexec_PROGRAMS = metadata-editor
+mv_libexec_PROGRAMS = metadata-viewer
+
+metadata_editor_SOURCES = \
+ metadata-editor.c \
+ metadata-editor.h \
+ metadata-impexp.c \
+ metadata-impexp.h \
+ metadata-misc.h \
+ metadata-tags.c \
+ metadata-tags.h \
+ metadata-xml.c \
+ metadata-xml.h
+
+metadata_viewer_SOURCES = \
+ metadata-viewer.c \
+ metadata-tags.c \
+ metadata-tags.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+metadata_viewer_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_viewer_RC)
+
+metadata_editor_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_editor_RC)
+
diff --git a/plug-ins/metadata/Makefile.in b/plug-ins/metadata/Makefile.in
new file mode 100644
index 0000000..8d8e2e6
--- /dev/null
+++ b/plug-ins/metadata/Makefile.in
@@ -0,0 +1,1114 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+me_libexec_PROGRAMS = metadata-editor$(EXEEXT)
+mv_libexec_PROGRAMS = metadata-viewer$(EXEEXT)
+subdir = plug-ins/metadata
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(me_libexecdir)" \
+ "$(DESTDIR)$(mv_libexecdir)"
+PROGRAMS = $(me_libexec_PROGRAMS) $(mv_libexec_PROGRAMS)
+am_metadata_editor_OBJECTS = metadata-editor.$(OBJEXT) \
+ metadata-impexp.$(OBJEXT) metadata-tags.$(OBJEXT) \
+ metadata-xml.$(OBJEXT)
+metadata_editor_OBJECTS = $(am_metadata_editor_OBJECTS)
+am__DEPENDENCIES_1 =
+metadata_editor_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(metadata_editor_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_metadata_viewer_OBJECTS = metadata-viewer.$(OBJEXT) \
+ metadata-tags.$(OBJEXT)
+metadata_viewer_OBJECTS = $(am_metadata_viewer_OBJECTS)
+metadata_viewer_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(metadata_viewer_RC)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/metadata-editor.Po \
+ ./$(DEPDIR)/metadata-impexp.Po ./$(DEPDIR)/metadata-tags.Po \
+ ./$(DEPDIR)/metadata-viewer.Po ./$(DEPDIR)/metadata-xml.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(metadata_editor_SOURCES) $(metadata_viewer_SOURCES)
+DIST_SOURCES = $(metadata_editor_SOURCES) $(metadata_viewer_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@metadata_editor_RC = metadata-editor.rc.o
+@HAVE_WINDRES_TRUE@metadata_viewer_RC = metadata-viewer.rc.o
+AM_LDFLAGS = $(mwindows)
+me_libexecdir = $(gimpplugindir)/plug-ins/metadata-editor
+mv_libexecdir = $(gimpplugindir)/plug-ins/metadata-viewer
+metadata_editor_SOURCES = \
+ metadata-editor.c \
+ metadata-editor.h \
+ metadata-impexp.c \
+ metadata-impexp.h \
+ metadata-misc.h \
+ metadata-tags.c \
+ metadata-tags.h \
+ metadata-xml.c \
+ metadata-xml.h
+
+metadata_viewer_SOURCES = \
+ metadata-viewer.c \
+ metadata-tags.c \
+ metadata-tags.h
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+metadata_viewer_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_viewer_RC)
+
+metadata_editor_LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(metadata_editor_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/metadata/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/metadata/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-me_libexecPROGRAMS: $(me_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(me_libexec_PROGRAMS)'; test -n "$(me_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(me_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(me_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(me_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(me_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-me_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(me_libexec_PROGRAMS)'; test -n "$(me_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(me_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(me_libexecdir)" && rm -f $$files
+
+clean-me_libexecPROGRAMS:
+ @list='$(me_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+install-mv_libexecPROGRAMS: $(mv_libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$(mv_libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(mv_libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(mv_libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(mv_libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(mv_libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-mv_libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$(mv_libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(mv_libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(mv_libexecdir)" && rm -f $$files
+
+clean-mv_libexecPROGRAMS:
+ @list='$(mv_libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+metadata-editor$(EXEEXT): $(metadata_editor_OBJECTS) $(metadata_editor_DEPENDENCIES) $(EXTRA_metadata_editor_DEPENDENCIES)
+ @rm -f metadata-editor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(metadata_editor_OBJECTS) $(metadata_editor_LDADD) $(LIBS)
+
+metadata-viewer$(EXEEXT): $(metadata_viewer_OBJECTS) $(metadata_viewer_DEPENDENCIES) $(EXTRA_metadata_viewer_DEPENDENCIES)
+ @rm -f metadata-viewer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(metadata_viewer_OBJECTS) $(metadata_viewer_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-editor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-impexp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-tags.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-viewer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metadata-xml.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(me_libexecdir)" "$(DESTDIR)$(mv_libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-me_libexecPROGRAMS \
+ clean-mv_libexecPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/metadata-editor.Po
+ -rm -f ./$(DEPDIR)/metadata-impexp.Po
+ -rm -f ./$(DEPDIR)/metadata-tags.Po
+ -rm -f ./$(DEPDIR)/metadata-viewer.Po
+ -rm -f ./$(DEPDIR)/metadata-xml.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-me_libexecPROGRAMS install-mv_libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/metadata-editor.Po
+ -rm -f ./$(DEPDIR)/metadata-impexp.Po
+ -rm -f ./$(DEPDIR)/metadata-tags.Po
+ -rm -f ./$(DEPDIR)/metadata-viewer.Po
+ -rm -f ./$(DEPDIR)/metadata-xml.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-me_libexecPROGRAMS \
+ uninstall-mv_libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-me_libexecPROGRAMS \
+ clean-mv_libexecPROGRAMS cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-me_libexecPROGRAMS \
+ install-mv_libexecPROGRAMS install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-me_libexecPROGRAMS uninstall-mv_libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/metadata/metadata-editor.c b/plug-ins/metadata/metadata-editor.c
new file mode 100644
index 0000000..87a2e87
--- /dev/null
+++ b/plug-ins/metadata/metadata-editor.c
@@ -0,0 +1,4757 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata-editor.c
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-tags.h"
+#include "metadata-xml.h"
+#include "metadata-impexp.h"
+#include "metadata-misc.h"
+
+#define PLUG_IN_PROC "plug-in-metadata-editor"
+#define PLUG_IN_BINARY "metadata-editor"
+#define PLUG_IN_ROLE "gimp-metadata"
+
+#define DEFAULT_TEMPLATE_FILE "gimp_metadata_template.xml"
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean metadata_editor_dialog (gint32 image_id,
+ GimpMetadata *metadata,
+ GError **error);
+
+static void metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder);
+
+void metadata_editor_write_callback (GtkWidget *dialog,
+ GtkBuilder *builder,
+ gint32 image_id);
+
+static void impex_combo_callback (GtkComboBoxText *combo,
+ gpointer data);
+
+static void gpsaltsys_combo_callback (GtkComboBoxText *combo,
+ gpointer data);
+
+static void remove_substring (const gchar *string,
+ const gchar *substring);
+
+static gchar * clean_xmp_string (const gchar *value);
+static gchar ** split_metadata_string (gchar *value);
+static void add_to_store (gchar *value,
+ GtkListStore *liststore,
+ gint store_column);
+
+static void set_tag_string (GimpMetadata *metadata,
+ const gchar *name,
+ const gchar *value);
+
+static gchar * get_phonetype (gchar *cur_value);
+
+static void write_metadata_tag (GtkBuilder *builder,
+ GimpMetadata *metadata,
+ gchar *tag,
+ gint data_column);
+
+static void write_metadata_tag_multiple (GtkBuilder *builder,
+ GimpMetadata *metadata,
+ GExiv2StructureType type,
+ const gchar *header_tag,
+ gint n_columns,
+ const gchar **column_tags,
+ const gint special_handling[]);
+
+gboolean hasCreatorTagData (GtkBuilder *builder);
+gboolean hasLocationCreationTagData (GtkBuilder *builder);
+gboolean hasImageSupplierTagData (GtkBuilder *builder);
+
+void on_date_button_clicked (GtkButton *widget,
+ GtkWidget *entry_widget,
+ gchar *tag);
+
+void on_create_date_button_clicked (GtkButton *widget,
+ gpointer data);
+
+void on_patient_dob_date_button_clicked (GtkButton *widget,
+ gpointer data);
+
+void on_study_date_button_clicked (GtkButton *widget,
+ gpointer data);
+
+void on_series_date_button_clicked (GtkButton *widget,
+ gpointer data);
+
+
+static void
+property_release_id_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+property_release_id_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+model_release_id_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+model_release_id_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+shown_location_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+shown_location_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+feat_org_name_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+feat_org_name_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+feat_org_code_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+feat_org_code_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+artwork_object_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+artwork_object_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+reg_entry_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+reg_entry_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+image_creator_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+image_creator_remove_callback (GtkWidget *widget,
+ gpointer data);
+
+static void
+copyright_own_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+copyright_own_remove_callback (GtkWidget *widget,
+ gpointer data);
+static void
+licensor_add_callback (GtkWidget *widget,
+ gpointer data);
+static void
+licensor_remove_callback (GtkWidget *widget,
+ gpointer data);
+
+static void
+list_row_remove_callback (GtkWidget *widget,
+ gpointer data,
+ gchar *tag);
+
+static void
+list_row_add_callback (GtkWidget *widget,
+ gpointer data,
+ gchar *tag);
+
+static gint
+count_tags (GExiv2Metadata *metadata,
+ const gchar *header,
+ const gchar **tags,
+ int items);
+
+static gchar **
+get_tags (GExiv2Metadata *metadata,
+ const gchar *header,
+ const gchar **tags,
+ const int items,
+ const int count);
+
+static void
+free_tagdata (gchar **tagdata,
+ gint rows,
+ gint cols);
+
+gboolean
+hasModelReleaseTagData (GtkBuilder *builder);
+
+gboolean
+hasPropertyReleaseTagData (GtkBuilder *builder);
+
+static void
+organisation_image_code_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+organisation_image_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+prop_rel_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+loc_sho_sub_loc_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+loc_sho_city_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+loc_sho_state_prov_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+loc_sho_cntry_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+loc_sho_cntry_iso_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+reg_org_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+reg_item_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_title_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_copyright_notice_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_source_inv_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_source_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_creator_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+aoo_date_creat_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+cr_owner_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+cr_owner_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_phone1_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_phone_type1_cell_edited_callback (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_phone2_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_phone_type2_cell_edited_callback (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_email_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+static void
+licensor_web_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data);
+
+void
+cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data,
+ int index);
+
+void
+cell_edited_callback_combo (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data,
+ int index);
+
+
+/* local variables */
+
+static int last_gpsaltsys_sel;
+
+gboolean gimpmetadata;
+gboolean force_write;
+
+static const gchar *lang_default = "lang=\"x-default\"";
+static const gchar *seq_default = "type=\"Seq\"";
+static const gchar *bag_default = "type=\"Bag\"";
+
+metadata_editor meta_args;
+
+#define ME_LOG_DOMAIN "metadata-editor"
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* ============================================================================
+ * ==[ ]=============================================================
+ * ==[ FUNCTIONS ]=============================================================
+ * ==[ ]=============================================================
+ * ============================================================================
+ */
+
+
+MAIN ()
+
+/* ============================================================================
+ * ==[ QUERY ]=================================================================
+ * ============================================================================
+ */
+
+static void
+query (void)
+{
+ static const GimpParamDef metadata_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Edit metadata (IPTC, EXIF, XMP)"),
+ "Edit metadata information attached to the "
+ "current image. Some or all of this metadata "
+ "will be saved in the file, depending on the output "
+ "file format.",
+ "Ben Touchette",
+ "Ben Touchette",
+ "2017",
+ N_("_Edit Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (metadata_args), 0,
+ metadata_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Metadata");
+}
+
+/* ============================================================================
+ * ==[ RUN ]===================================================================
+ * ============================================================================
+ */
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ force_write = FALSE;
+
+ INIT_I18N ();
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ if (! strcmp (name, PLUG_IN_PROC))
+ {
+ GimpMetadata *metadata;
+ gint32 image_ID = param[1].data.d_image;
+
+ metadata = gimp_image_get_metadata (image_ID);
+
+ /* Always show metadata dialog so we can add
+ appropriate iptc data as needed. Sometimes
+ license data needs to be added after the
+ fact and the image may not contain metadata
+ but should have it added as needed. */
+
+ if (!metadata)
+ {
+ metadata = gimp_metadata_new ();
+ gimp_image_set_metadata (image_ID, metadata);
+ }
+
+ if (metadata_editor_dialog (image_ID, metadata, &error))
+ status = GIMP_PDB_SUCCESS;
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* ============================================================================
+ * ==[ EDITOR DIALOG UI ]======================================================
+ * ============================================================================
+ */
+
+static GtkWidget *
+builder_get_widget (GtkBuilder *builder,
+ const gchar *name)
+{
+ GObject *object = gtk_builder_get_object (builder, name);
+
+ return GTK_WIDGET (object);
+}
+
+static gboolean
+metadata_editor_dialog (gint32 image_id,
+ GimpMetadata *g_metadata,
+ GError **error)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *metadata_vbox;
+ GtkWidget *impex_combo;
+ GtkWidget *content_area;
+ GExiv2Metadata *metadata;
+ gchar *ui_file;
+ gchar *title;
+ gchar *name;
+ GError *local_error = NULL;
+ gboolean run;
+
+ metadata = GEXIV2_METADATA (g_metadata);
+
+ builder = gtk_builder_new ();
+
+ meta_args.image_id = image_id;
+ meta_args.builder = builder;
+ meta_args.metadata = metadata;
+ meta_args.filename = g_strconcat (g_get_home_dir (), "/", DEFAULT_TEMPLATE_FILE,
+ NULL);
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-metadata-editor.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &local_error))
+ {
+ if (! local_error)
+ local_error = g_error_new_literal (G_FILE_ERROR, 0,
+ _("Error loading metadata-editor dialog."));
+ g_propagate_error (error, local_error);
+
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+
+ name = gimp_image_get_name (image_id);
+ title = g_strdup_printf (_("Metadata Editor: %s"), name);
+ if (name)
+ g_free (name);
+
+ dialog = gimp_dialog_new (title,
+ "gimp-metadata-editor-dialog",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Write Metadata"), GTK_RESPONSE_OK,
+ NULL);
+
+ meta_args.dialog = dialog;
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ if (title)
+ g_free (title);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ metadata_vbox = builder_get_widget (builder, "metadata-vbox");
+ impex_combo = builder_get_widget (builder, "impex_combo");
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
+ _("Select:"));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
+ _("Import metadata"));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impex_combo),
+ _("Export metadata"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (impex_combo), 0);
+
+ g_signal_connect (G_OBJECT (impex_combo),
+ "changed", G_CALLBACK (impex_combo_callback), &meta_args);
+
+ gtk_container_set_border_width (GTK_CONTAINER (metadata_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), metadata_vbox, TRUE, TRUE, 0);
+
+ metadata_dialog_editor_set_metadata (metadata, builder);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+ if (run)
+ {
+ metadata_editor_write_callback (dialog, builder, image_id);
+ }
+
+ if (meta_args.filename)
+ {
+ g_free (meta_args.filename);
+ }
+
+ return TRUE;
+}
+
+/* ============================================================================
+ * ==[ ]=====================================================
+ * ==[ PRIVATE FUNCTIONS ]=====================================================
+ * ==[ ]=====================================================
+ * ============================================================================
+ */
+static void
+remove_substring (const gchar *string,
+ const gchar *substring)
+{
+ if (string != NULL && substring != NULL && substring[0] != '\0')
+ {
+ gchar *p = strstr (string, substring);
+ if (p)
+ {
+ memmove (p, p + strlen (substring), strlen (p + strlen (substring)) + 1);
+ }
+ }
+}
+
+static gchar *
+clean_xmp_string (const gchar *value)
+{
+ gchar *value_clean;
+ gchar *value_utf8;
+
+ value_clean = g_strdup (value);
+
+ if (strstr (value_clean, lang_default) != NULL)
+ {
+ remove_substring (value_clean, lang_default);
+ if (strstr (value_clean, " ") != NULL)
+ {
+ remove_substring (value_clean, " ");
+ }
+ }
+
+ if (strstr (value_clean, bag_default) != NULL)
+ {
+ remove_substring (value_clean, bag_default);
+ if (strstr (value_clean, " ") != NULL)
+ {
+ remove_substring (value_clean, " ");
+ }
+ }
+
+ if (strstr (value_clean, seq_default) != NULL)
+ {
+ remove_substring (value_clean, seq_default);
+ if (strstr (value_clean, " ") != NULL)
+ {
+ remove_substring (value_clean, " ");
+ }
+ }
+
+ if (! g_utf8_validate (value_clean, -1, NULL))
+ {
+ value_utf8 = g_locale_to_utf8 (value_clean, -1, NULL, NULL, NULL);
+ }
+ else
+ {
+ value_utf8 = g_strdup (value_clean);
+ }
+
+ g_free (value_clean);
+
+ return value_utf8;
+}
+
+/* We split a string and accept "," and ";" as delimiters.
+ * The result needs to be freed with g_strfreev.
+ */
+static gchar **
+split_metadata_string (gchar *value)
+{
+ gchar **split;
+ gint item;
+
+ /* Can't use g_strsplit_set since we work with utf-8 here. */
+ split = g_strsplit (g_strdelimit (value, ";", ','), ",", 0);
+
+ for (item = 0; split[item]; item++)
+ {
+ split[item] = g_strstrip(split[item]);
+ }
+
+ return split;
+}
+
+static void
+add_to_store (gchar *value, GtkListStore *liststore, gint store_column)
+{
+ gchar **strings;
+ gint cnt = 0;
+ gint item;
+ GtkTreeIter iter;
+
+ if (value)
+ {
+ strings = split_metadata_string (value);
+ if (strings)
+ {
+ for (item = 0; strings[item]; item++)
+ {
+ if (strings[item][0] != '\0')
+ {
+ cnt++;
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ store_column, strings[item],
+ -1);
+ }
+ }
+ g_strfreev(strings);
+ }
+ }
+
+ /* If there are less than two rows, add empty ones. */
+ for (item = cnt; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ store_column, NULL,
+ -1);
+ }
+}
+
+static gint
+count_tags (GExiv2Metadata *metadata,
+ const gchar *header,
+ const gchar **tags,
+ gint items)
+{
+ int tagcount;
+ gchar tag[256];
+ int row, col;
+
+ tagcount = 0;
+ for (row = 1; row < 256; row++)
+ {
+ for (col = 0; col < items; col++)
+ {
+ g_snprintf ((gchar *) &tag, 256, "%s[%d]", header, row);
+ g_snprintf ((gchar *) &tag, 256, "%s%s",
+ (gchar *) &tag, (gchar *) tags[col]);
+ if (gexiv2_metadata_has_tag (metadata, (gchar *) &tag))
+ {
+ tagcount++;
+ break;
+ }
+ }
+ }
+ return tagcount;
+}
+
+static gchar **
+get_tags (GExiv2Metadata *metadata,
+ const gchar *header,
+ const gchar **tags,
+ const gint items,
+ const gint count)
+{
+ gchar **tagdata;
+ gchar **_datarow;
+ gchar tag[256];
+ int row, col;
+
+ g_return_val_if_fail (header != NULL && tags != NULL, NULL);
+ g_return_val_if_fail (items > 0, NULL);
+
+ if (count <= 0)
+ return NULL;
+ tagdata = g_new0 (gchar *, count);
+ if (! tagdata)
+ return NULL;
+
+ for (row = 1; row < count + 1; row++)
+ {
+ tagdata[row-1] = g_malloc0 (sizeof (gchar *) * items);
+ for (col = 0; col < items; col++)
+ {
+ gchar *value;
+
+ g_snprintf ((gchar *) &tag, 256, "%s[%d]", header, row);
+ g_snprintf ((gchar *) &tag, 256, "%s%s",
+ (gchar *) &tag, (gchar *) tags[col]);
+
+ value = gexiv2_metadata_get_tag_string (metadata, (gchar *) &tag);
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "get_tags tag: %s, value: %s", (gchar *) &tag, value);
+
+ _datarow = (gchar **) tagdata[row-1];
+ if (_datarow)
+ _datarow[col] = strdup (value);
+ }
+ }
+ return tagdata;
+}
+
+static void
+free_tagdata(gchar **tagdata, gint rows, gint cols)
+{
+ gint row, col;
+ gchar **tagdatarow;
+
+ for (row = 0; row < rows; row++)
+ {
+ tagdatarow = (gpointer) tagdata[row];
+
+ for (col = 0; col < cols; col++)
+ {
+ g_free (tagdatarow[col]);
+ }
+ g_free (tagdatarow);
+ }
+ g_free (tagdata);
+}
+
+/* ============================================================================
+ * ==[ DATE CALLBACKS ]========================================================
+ * ============================================================================
+ */
+void
+on_create_date_button_clicked (GtkButton *widget,
+ gpointer data)
+{
+ on_date_button_clicked (widget, (GtkWidget*)data,
+ "Xmp.photoshop.DateCreated");
+}
+
+void
+on_patient_dob_date_button_clicked (GtkButton *widget,
+ gpointer data)
+{
+ on_date_button_clicked (widget, (GtkWidget*)data,
+ "Xmp.DICOM.PatientDOB");
+}
+
+void
+on_study_date_button_clicked (GtkButton *widget,
+ gpointer data)
+{
+ on_date_button_clicked (widget, (GtkWidget*)data,
+ "Xmp.DICOM.StudyDateTime");
+}
+
+void
+on_series_date_button_clicked (GtkButton *widget,
+ gpointer data)
+{
+ on_date_button_clicked (widget, (GtkWidget*)data,
+ "Xmp.DICOM.SeriesDateTime");
+}
+
+void
+on_date_button_clicked (GtkButton *widget,
+ GtkWidget *entry_widget,
+ gchar *tag)
+{
+ GtkBuilder *builder;
+ GtkWidget *calendar_dialog;
+ GtkWidget *calendar_content_area;
+ GtkWidget *calendar_vbox;
+ GtkWidget *calendar;
+ const gchar *date_text;
+ gchar *ui_file;
+ GError *error = NULL;
+ GDateTime *current_datetime;
+ guint year, month, day;
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins",
+ "plug-in-metadata-editor-calendar.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &error))
+ {
+ g_log ("", G_LOG_LEVEL_MESSAGE,
+ _("Error loading calendar. %s"),
+ error ? error->message : "");
+ g_clear_error (&error);
+
+ if (ui_file)
+ g_free (ui_file);
+ g_object_unref (builder);
+ return;
+ }
+
+ if (ui_file)
+ g_free (ui_file);
+
+ date_text = gtk_entry_get_text (GTK_ENTRY (entry_widget));
+ if (date_text && date_text[0] != '\0')
+ {
+ sscanf (date_text, "%d-%d-%d;", &year, &month, &day);
+ month--;
+ }
+ else
+ {
+ current_datetime = g_date_time_new_now_local ();
+ year = g_date_time_get_year (current_datetime);
+ month = g_date_time_get_month (current_datetime) - 1;
+ day = g_date_time_get_day_of_month (current_datetime);
+ }
+
+ calendar_dialog =
+ gtk_dialog_new_with_buttons (_("Calendar Date:"),
+ NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("Set Date"), GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (calendar_dialog),
+ GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (calendar_dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (calendar_dialog));
+
+ calendar_content_area = gtk_dialog_get_content_area (GTK_DIALOG (
+ calendar_dialog));
+
+ calendar_vbox = builder_get_widget (builder, "calendar-vbox");
+
+ gtk_container_set_border_width (GTK_CONTAINER (calendar_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (calendar_content_area), calendar_vbox, TRUE, TRUE,
+ 0);
+
+ calendar = builder_get_widget (builder, "calendar");
+
+ gtk_calendar_select_month (GTK_CALENDAR (calendar), month, year);
+ gtk_calendar_select_day (GTK_CALENDAR (calendar), day);
+ gtk_calendar_mark_day (GTK_CALENDAR (calendar), day);
+
+ if (gtk_dialog_run (GTK_DIALOG (calendar_dialog)) == GTK_RESPONSE_OK)
+ {
+ gchar date[25];
+ gtk_calendar_get_date (GTK_CALENDAR (calendar), &year, &month, &day);
+ g_sprintf ((gchar*) &date, "%d-%02d-%02d", year, month+1, day);
+ gtk_entry_set_text (GTK_ENTRY (entry_widget), date);
+ }
+
+ gtk_widget_destroy (calendar_dialog);
+}
+
+/* ============================================================================
+ * ==[ SPECIAL TAGS HANDLERS ]=================================================
+ * ============================================================================
+ */
+
+gboolean
+hasImageSupplierTagData (GtkBuilder *builder)
+{
+ gint loop;
+
+ for (loop = 0; loop < imageSupplierInfoHeader.size; loop++)
+ {
+ GObject *object;
+ const gchar *text;
+
+ object = gtk_builder_get_object (builder, imageSupplierInfoTags[loop].id);
+
+ if (! strcmp (imageSupplierInfoTags[loop].mode, "single"))
+ {
+ text = gtk_entry_get_text (GTK_ENTRY (object));
+
+ if (text && *text)
+ return TRUE;
+ }
+ else if (! strcmp (imageSupplierInfoTags[loop].mode, "multi"))
+ {
+ text = gtk_entry_get_text (GTK_ENTRY (object));
+
+ if (text && *text)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+hasLocationCreationTagData (GtkBuilder *builder)
+{
+ gint loop;
+
+ for (loop = 0; loop < locationCreationInfoHeader.size; loop++)
+ {
+ GObject *object;
+ const gchar *text;
+
+ object = gtk_builder_get_object (builder, locationCreationInfoTags[loop].id);
+
+ if (! strcmp (locationCreationInfoTags[loop].mode, "single"))
+ {
+ text = gtk_entry_get_text (GTK_ENTRY (object));
+
+ if (text && *text)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+hasModelReleaseTagData (GtkBuilder *builder)
+{
+ return FALSE;
+}
+
+gboolean
+hasPropertyReleaseTagData (GtkBuilder *builder)
+{
+ return FALSE;
+}
+
+
+gboolean
+hasCreatorTagData (GtkBuilder *builder)
+{
+ gboolean has_data = FALSE;
+ gint loop;
+
+ for (loop = 0; loop < creatorContactInfoHeader.size; loop++)
+ {
+ GObject *object;
+
+ object = gtk_builder_get_object (builder, creatorContactInfoTags[loop].id);
+
+ if (GTK_IS_ENTRY (object))
+ {
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (object));
+
+ if (text && *text)
+ has_data = TRUE;
+ }
+ else if (GTK_IS_TEXT_VIEW (object))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *text;
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+
+ if (text && *text)
+ has_data = TRUE;
+
+ if (text)
+ g_free (text);
+ }
+ }
+
+ return has_data;
+}
+
+/* ============================================================================
+ * ==[ SET DIALOG METADATA ]===================================================
+ * ============================================================================
+ */
+
+/* CELL EDITED */
+
+void
+cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data,
+ int index)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ model = (GtkTreeModel *)data;
+ path = gtk_tree_path_new_from_string (path_string);
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, index,
+ new_text, -1);
+}
+
+void
+cell_edited_callback_combo (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data,
+ int column)
+{
+ GtkWidget *widget;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeSelection *selection;
+
+ widget = GTK_WIDGET (data);
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+ if (gtk_tree_selection_get_selected (GTK_TREE_SELECTION (selection),
+ NULL, &iter))
+ {
+ path = gtk_tree_model_get_path (treemodel, &iter);
+ gtk_tree_path_free (path);
+ gtk_list_store_set (liststore, &iter, column, new_text, -1);
+ }
+}
+
+static void
+licensor_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+licensor_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+licensor_phone1_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 2);
+}
+
+static void
+licensor_phone_type1_cell_edited_callback (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback_combo (cell, path_string, new_text, data, 3);
+}
+
+static void
+licensor_phone2_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 4);
+}
+
+static void
+licensor_phone_type2_cell_edited_callback (GtkCellRendererCombo *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback_combo (cell, path_string, new_text, data, 5);
+}
+
+static void
+licensor_email_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 6);
+}
+
+static void
+licensor_web_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 7);
+}
+
+static void
+cr_owner_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+cr_owner_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+img_cr8_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+img_cr8_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+aoo_copyright_notice_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 5);
+}
+
+static void
+aoo_source_inv_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 4);
+}
+
+static void
+aoo_source_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 3);
+}
+
+static void
+aoo_creator_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 2);
+}
+
+static void
+aoo_date_creat_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+aoo_title_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+reg_org_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+reg_item_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+loc_sho_sub_loc_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 0);
+}
+
+static void
+loc_sho_city_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 1);
+}
+
+static void
+loc_sho_state_prov_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 2);
+}
+
+static void
+loc_sho_cntry_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 3);
+}
+
+static void
+loc_sho_cntry_iso_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 4);
+}
+
+static void
+loc_sho_wrld_reg_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ cell_edited_callback (cell, path_string, new_text, data, 5);
+}
+
+static void
+prop_rel_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint column;
+ model = (GtkTreeModel *)data;
+ path = gtk_tree_path_new_from_string (path_string);
+
+ column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+ new_text, -1);
+}
+
+static void
+mod_rel_id_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint column;
+
+ model = (GtkTreeModel *)data;
+ path = gtk_tree_path_new_from_string (path_string);
+
+ column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+ new_text, -1);
+}
+
+static void
+organisation_image_name_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint column;
+
+ model = (GtkTreeModel *)data;
+ path = gtk_tree_path_new_from_string (path_string);
+
+ column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+ new_text, -1);
+}
+
+static void
+organisation_image_code_cell_edited_callback (GtkCellRendererText *cell,
+ const gchar *path_string,
+ const gchar *new_text,
+ gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint column;
+
+ model = (GtkTreeModel *)data;
+ path = gtk_tree_path_new_from_string (path_string);
+
+ column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column"));
+
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, column,
+ new_text, -1);
+}
+
+
+/* CELL / ROW REMOVE */
+
+static void
+list_row_remove_callback (GtkWidget *widget,
+ gpointer data,
+ gchar *tag)
+{
+ GtkBuilder *builder = data;
+ GtkWidget *list_widget;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GtkTreeModel *treemodel;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ list_widget = builder_get_widget (builder, tag);
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ selection = gtk_tree_view_get_selection ((GtkTreeView *)list_widget);
+
+ if (gtk_tree_selection_get_selected (selection, NULL, &iter))
+ {
+ gint number_of_rows;
+
+ path = gtk_tree_model_get_path (treemodel, &iter);
+ gtk_list_store_remove (liststore, &iter);
+ gtk_tree_path_free (path);
+
+ number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);
+ /* Make sur that two rows are always showing, else it looks ugly. */
+ if (number_of_rows < 2)
+ {
+ gtk_list_store_append (liststore, &iter);
+ }
+ }
+}
+
+static void
+property_release_id_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.plus.PropertyReleaseID");
+}
+
+static void
+model_release_id_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.plus.ModelReleaseID");
+}
+
+static void
+shown_location_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.iptcExt.LocationShown");
+}
+
+static void
+feat_org_name_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.iptcExt.OrganisationInImageName");
+}
+
+static void
+feat_org_code_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.iptcExt.OrganisationInImageCode");
+}
+
+static void
+artwork_object_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.iptcExt.ArtworkOrObject");
+}
+
+static void
+reg_entry_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.iptcExt.RegistryId");
+}
+
+static void
+image_creator_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.plus.ImageCreator");
+}
+
+static void
+copyright_own_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.plus.CopyrightOwner");
+}
+
+static void
+licensor_remove_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_remove_callback (widget, data, "Xmp.plus.Licensor");
+}
+
+
+/* CELL / ROW ADD */
+
+static void
+list_row_add_callback (GtkWidget *widget,
+ gpointer data,
+ gchar *tag)
+{
+ GtkBuilder *builder = data;
+ GtkWidget *list_widget;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+
+ list_widget = builder_get_widget (builder, tag);
+
+ liststore = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget)));
+
+ gtk_list_store_append (liststore, &iter);
+}
+
+static void
+property_release_id_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.plus.PropertyReleaseID");
+}
+
+static void
+model_release_id_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.plus.ModelReleaseID");
+}
+
+static void
+shown_location_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.iptcExt.LocationShown");
+}
+
+static void
+feat_org_name_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.iptcExt.OrganisationInImageName");
+}
+
+static void
+feat_org_code_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.iptcExt.OrganisationInImageCode");
+}
+
+static void
+artwork_object_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.iptcExt.ArtworkOrObject");
+}
+
+static void
+reg_entry_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.iptcExt.RegistryId");
+}
+
+static void
+image_creator_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.plus.ImageCreator");
+}
+
+static void
+copyright_own_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.plus.CopyrightOwner");
+}
+
+static void
+licensor_add_callback (GtkWidget *widget,
+ gpointer data)
+{
+ list_row_add_callback (widget, data, "Xmp.plus.Licensor");
+}
+
+const gchar *gpstooltips[] =
+{
+ N_ ("Enter or edit GPS value here.\n"
+ "Valid values consist of 1, 2 or 3 numbers "
+ "(degrees, minutes, seconds), see the following examples:\n"
+ "10deg 15' 20\", or 10\u00b0 15' 20\", or 10:15:20.45, or "
+ "10 15 20, or 10 15.30, or 10.45\n"
+ "Delete all text to remove the current value."),
+ N_ ("Enter or edit GPS altitude value here.\n"
+ "A valid value consists of one number:\n"
+ "e.g. 100, or 12.24\n"
+ "Depending on the selected measurement type "
+ "the value should be entered in meter (m) "
+ "or feet (ft)\n"
+ "Delete all text to remove the current value.")
+};
+
+enum
+{
+ GPS_LONG_LAT_TOOLTIP,
+ GPS_ALTITUDE_TOOLTIP,
+};
+
+/* Set dialog display settings and data */
+
+static void
+metadata_dialog_editor_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder)
+{
+ GtkWidget *combo_widget;
+ GtkWidget *entry_widget;
+ GtkWidget *text_widget;
+ GtkWidget *button_widget;
+ gint width, height;
+ gchar *value;
+ gint i;
+
+ gint32 numele = n_default_metadata_tags;
+
+ /* Setup Buttons */
+ button_widget = builder_get_widget (builder, "add_licensor_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (licensor_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_licensor_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (licensor_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_copyright_own_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (copyright_own_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_copyright_own_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (copyright_own_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_image_creator_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (image_creator_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_image_creator_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (image_creator_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_reg_entry_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (reg_entry_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_reg_entry_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (reg_entry_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_artwork_object_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (artwork_object_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_artwork_object_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (artwork_object_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_feat_org_code_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (feat_org_code_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_feat_org_code_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (feat_org_code_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_feat_org_name_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (feat_org_name_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_feat_org_name_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (feat_org_name_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_shown_location_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (shown_location_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_shown_location_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (shown_location_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_model_rel_id_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (model_release_id_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_model_rel_id_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (model_release_id_remove_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "add_prop_rel_id_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (property_release_id_add_callback),
+ builder);
+
+ button_widget = builder_get_widget (builder, "rem_prop_rel_id_button");
+ g_signal_connect (G_OBJECT (button_widget), "clicked",
+ G_CALLBACK (property_release_id_remove_callback),
+ builder);
+
+
+ /* Setup Comboboxes */
+ combo_widget = builder_get_widget (builder, "Xmp.xmp.Rating");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ _("Unrated"));
+ for (i = 1; i < 6; i++)
+ {
+ gchar *display = g_strdup_printf ("%d", i);
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ display);
+ g_free (display);
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Xmp.xmpRights.Marked");
+ for (i = 0; i < 3; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (marked[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Xmp.photoshop.Urgency");
+ for (i = 0; i < 9; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (urgency[i]));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Xmp.plus.MinorModelAgeDisclosure");
+ for (i = 0; i < 13; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (minormodelagedisclosure[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Xmp.plus.ModelReleaseStatus");
+ for (i = 0; i < n_modelreleasestatus; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (modelreleasestatus[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+ gtk_widget_get_size_request (combo_widget, &width, &height);
+ gtk_widget_set_size_request (combo_widget, 180, height);
+
+ combo_widget = builder_get_widget (builder, "Xmp.iptcExt.DigitalSourceType");
+ for (i = 0; i < n_digitalsourcetype; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (digitalsourcetype[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Xmp.plus.PropertyReleaseStatus");
+ for (i = 0; i < 4; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (propertyreleasestatus[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+ gtk_widget_get_size_request (combo_widget, &width, &height);
+ gtk_widget_set_size_request (combo_widget, 180, height);
+
+ combo_widget = builder_get_widget (builder, "Xmp.DICOM.PatientSex");
+ for (i = 0; i < 4; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (dicom[i].display));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSLatitudeRef");
+ for (i = 0; i < 3; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (gpslatref[i]));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSLongitudeRef");
+ for (i = 0; i < 3; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (gpslngref[i]));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "Exif.GPSInfo.GPSAltitudeRef");
+ for (i = 0; i < 3; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (gpsaltref[i]));
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ combo_widget = builder_get_widget (builder, "GPSAltitudeSystem");
+ for (i = 0; i < 2; i++)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_widget),
+ gettext (gpsaltsys[i]));
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo_widget), 0);
+
+ g_signal_connect (G_OBJECT (combo_widget), "changed",
+ G_CALLBACK (gpsaltsys_combo_callback),
+ builder);
+
+ /* Set up text view heights */
+ for (i = 0; i < numele; i++)
+ {
+ if (! strcmp ("multi", default_metadata_tags[i].mode))
+ {
+ text_widget = builder_get_widget (builder,
+ default_metadata_tags[i].tag);
+ gtk_widget_get_size_request (text_widget, &width, &height);
+ gtk_widget_set_size_request (text_widget, width, height + 60);
+ }
+ }
+
+ for (i = 0; i < creatorContactInfoHeader.size; i++)
+ {
+ if (! strcmp ("multi", creatorContactInfoTags[i].mode))
+ {
+ text_widget = builder_get_widget (builder,
+ creatorContactInfoTags[i].id);
+ gtk_widget_get_size_request (text_widget, &width, &height);
+ gtk_widget_set_size_request (text_widget, width, height + 60);
+ }
+ }
+
+ /* Set up lists */
+ for (i = 0; i < imageSupplierInfoHeader.size; i++)
+ {
+ GtkWidget *widget;
+
+ widget = builder_get_widget (builder,
+ imageSupplierInfoTags[i].id);
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ imageSupplierInfoTags[i].tag);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = clean_xmp_string (value);
+ g_free (value);
+
+ if (! strcmp ("single", imageSupplierInfoTags[i].mode))
+ {
+ gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
+ }
+ else if (! strcmp ("multi", imageSupplierInfoTags[i].mode))
+ {
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
+ gtk_text_buffer_set_text (buffer, value_utf, -1);
+ }
+ g_free (value_utf);
+ }
+ }
+
+ for (i = 0; i < locationCreationInfoHeader.size; i++)
+ {
+ GtkWidget *widget;
+
+ widget = builder_get_widget (builder,
+ locationCreationInfoTags[i].id);
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ locationCreationInfoTags[i].tag);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = clean_xmp_string (value);
+ g_free (value);
+
+ if (! strcmp ("single", locationCreationInfoTags[i].mode))
+ {
+ gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
+ }
+ g_free (value_utf);
+ }
+ }
+
+ /* Set up tag data */
+
+ for (i = 0; i < numele; i++)
+ {
+ GtkWidget *widget;
+ gint index;
+
+ widget = builder_get_widget (builder, default_metadata_tags[i].tag);
+
+ if (! strcmp ("Exif.GPSInfo.GPSLongitude",
+ default_metadata_tags[i].tag))
+ {
+ gdouble gps_value;
+ gchar *str;
+
+ if (gexiv2_metadata_get_gps_longitude (metadata, &gps_value))
+ {
+ str = metadata_format_gps_longitude_latitude (gps_value);
+ gtk_entry_set_text (GTK_ENTRY (widget), str);
+ g_free (str);
+ }
+ gtk_widget_set_tooltip_text (widget,
+ gettext (gpstooltips[GPS_LONG_LAT_TOOLTIP]));
+ continue;
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLatitude",
+ default_metadata_tags[i].tag))
+ {
+ gdouble gps_value;
+ gchar *str;
+
+ if (gexiv2_metadata_get_gps_latitude (metadata, &gps_value))
+ {
+ str = metadata_format_gps_longitude_latitude (gps_value);
+ gtk_entry_set_text (GTK_ENTRY (widget), str);
+ g_free (str);
+ }
+ gtk_widget_set_tooltip_text (widget,
+ gettext (gpstooltips[GPS_LONG_LAT_TOOLTIP]));
+ continue;
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSAltitude",
+ default_metadata_tags[i].tag))
+ {
+ gdouble gps_value;
+ gchar *str;
+
+ if (gexiv2_metadata_get_gps_altitude (metadata, &gps_value))
+ {
+ str = metadata_format_gps_altitude (gps_value, TRUE, "");
+ gtk_entry_set_text (GTK_ENTRY (widget), str);
+ g_free (str);
+ }
+ gtk_widget_set_tooltip_text (widget,
+ gettext (gpstooltips[GPS_ALTITUDE_TOOLTIP]));
+ continue;
+ }
+
+ index = default_metadata_tags[i].other_tag_index;
+
+ if (default_metadata_tags[i].xmp_type == GIMP_XMP_BAG ||
+ default_metadata_tags[i].xmp_type == GIMP_XMP_SEQ)
+ {
+ gchar **values;
+
+ value = NULL;
+ values = gexiv2_metadata_get_tag_multiple (metadata,
+ default_metadata_tags[i].tag);
+
+ if (values)
+ {
+ gint vi;
+
+ for (vi = 0; values[vi] != NULL; vi++)
+ {
+ gchar *value_clean;
+
+ value_clean = clean_xmp_string (values[vi]);
+
+ if (value_clean != NULL && value_clean[0] != '\0')
+ {
+ if (! value)
+ {
+ value = g_strdup (value_clean);
+ }
+ else
+ {
+ gchar *tmpvalue;
+
+ tmpvalue = value;
+ value = g_strconcat (value, "\n", value_clean, NULL);
+ g_free (tmpvalue);
+ }
+ }
+ g_free (value_clean);
+ }
+ }
+
+ if (index > -1)
+ {
+ gchar **equiv_values;
+
+ /* These are all IPTC tags some of which can appear multiple times so
+ * we will use get_tag_multiple. Also IPTC most commonly uses UTF-8
+ * not current locale so get_tag_interpreted was wrong anyway.
+ * FIXME For now lets interpret as UTF-8 and in the future read
+ * and interpret based on the CharacterSet tag.
+ */
+ equiv_values = gexiv2_metadata_get_tag_multiple (metadata,
+ equivalent_metadata_tags[index].tag);
+
+ if (equiv_values)
+ {
+ gint evi;
+
+ for (evi = 0; equiv_values[evi] != NULL; evi++)
+ {
+ if (equiv_values[evi][0] != '\0')
+ {
+ if (! value)
+ {
+ value = g_strdup (equiv_values[evi]);
+ }
+ else
+ {
+ if (! g_strv_contains (values, equiv_values[evi]))
+ {
+ gchar *tmpvalue;
+
+ tmpvalue = value;
+ value = g_strconcat (value, "\n", equiv_values[evi], NULL);
+ g_free (tmpvalue);
+ }
+ }
+ }
+ }
+ }
+ }
+ g_strfreev (values);
+ }
+ else
+ {
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ default_metadata_tags[i].tag);
+
+ if (value)
+ {
+ gchar *value_utf8 = clean_xmp_string (value);
+
+ g_free (value);
+
+ if (value_utf8 && value_utf8[0] != '\0')
+ {
+ value = g_strdup (value_utf8);
+ }
+ else
+ {
+ value = NULL;
+ }
+
+ g_free (value_utf8);
+ }
+
+ if (index > -1)
+ {
+ gchar **values;
+
+ /* It's not very likely we will have an XMP tag that can only
+ * have a single value instead of an array, which corresponds to
+ * an IPTC tag that can have multiple values, but since we
+ * already have this code it can't hurt to keep testing for it.
+ * FIXME For now lets interpret as UTF-8 and in the future read
+ * and interpret based on the CharacterSet tag.
+ */
+ values = gexiv2_metadata_get_tag_multiple (metadata,
+ equivalent_metadata_tags[index].tag);
+
+ if (values)
+ {
+ gint i;
+ GString *str = NULL;
+
+ for (i = 0; values[i] != NULL; i++)
+ {
+ if (values[i][0] != '\0')
+ {
+ if (! str)
+ {
+ str = g_string_new (values[i]);
+ }
+ else
+ {
+ if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
+ {
+ g_string_append (str, "\n");
+ }
+ else
+ {
+ g_string_append (str, ", ");
+ }
+ g_string_append (str, values[i]);
+ }
+ }
+ }
+
+ if (str)
+ {
+ /* If we got values from both Xmp and Iptc then compare those
+ * values and if they are different concatenate them. Usually they
+ * should be the same in which case we won't duplicate the string.
+ */
+ if (value && strcmp (value, str->str))
+ {
+ if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
+ {
+ g_string_prepend (str, "\n");
+ }
+ else
+ {
+ g_string_prepend (str, ", ");
+ }
+ g_string_prepend (str, value);
+ g_free (value);
+ }
+ value = g_string_free (str, FALSE);
+ }
+ }
+ }
+ }
+
+ if (!strcmp ("list", default_metadata_tags[i].mode))
+ {
+ /* Tab: IPTC Extension, Label: Location Shown */
+ if (! strcmp ("Xmp.iptcExt.LocationShown",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint counter;
+ gchar **tagdata;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* LOCATION SHOWN - SUB LOCATION */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_SUB_LOC);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_sub_loc_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_SUB_LOC));
+ }
+
+ /* LOCATION SHOWN - CITY */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_CITY);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_city_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_CITY));
+ }
+
+ /* LOCATION SHOWN - STATE PROVINCE */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_STATE_PROV);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_state_prov_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_STATE_PROV));
+ }
+
+ /* LOCATION SHOWN - COUNTRY */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_CNTRY);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_cntry_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_CNTRY));
+ }
+
+ /* LOCATION SHOWN - COUNTRY ISO */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_CNTRY_ISO);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_cntry_iso_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_CNTRY_ISO));
+ }
+
+ /* LOCATION SHOWN - WORLD REGION */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LOC_SHO_CNTRY_WRLD_REG);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (loc_sho_wrld_reg_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LOC_SHO_CNTRY_WRLD_REG));
+ }
+
+ /* Favor the most common form: /Iptc4xmpExt:* */
+ counter = count_tags (metadata, LOCATIONSHOWN_HEADER,
+ locationshown,
+ n_locationshown);
+
+ tagdata = get_tags (metadata, LOCATIONSHOWN_HEADER,
+ locationshown,
+ n_locationshown, counter);
+
+ if (counter == 0 || ! tagdata)
+ {
+ /* Alternatively try: /iptcExt:* */
+ counter = count_tags (metadata, LOCATIONSHOWN_HEADER,
+ locationshown_alternative,
+ n_locationshown);
+
+ tagdata = get_tags (metadata, LOCATIONSHOWN_HEADER,
+ locationshown_alternative,
+ n_locationshown, counter);
+ }
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, tagdatarow[0],
+ COL_LOC_SHO_CITY, tagdatarow[1],
+ COL_LOC_SHO_STATE_PROV, tagdatarow[2],
+ COL_LOC_SHO_CNTRY, tagdatarow[3],
+ COL_LOC_SHO_CNTRY_ISO, tagdatarow[4],
+ COL_LOC_SHO_CNTRY_WRLD_REG, tagdatarow[5],
+ -1);
+ }
+ free_tagdata(tagdata, counter, n_locationshown);
+
+ if (counter == 1)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, NULL,
+ COL_LOC_SHO_CITY, NULL,
+ COL_LOC_SHO_STATE_PROV, NULL,
+ COL_LOC_SHO_CNTRY, NULL,
+ COL_LOC_SHO_CNTRY_ISO, NULL,
+ COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
+ -1);
+ }
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, NULL,
+ COL_LOC_SHO_CITY, NULL,
+ COL_LOC_SHO_STATE_PROV, NULL,
+ COL_LOC_SHO_CNTRY, NULL,
+ COL_LOC_SHO_CNTRY_ISO, NULL,
+ COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Featured Organization - Name */
+ else if (! strcmp ("Xmp.iptcExt.OrganisationInImageName",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
+ GTK_SELECTION_SINGLE);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (organisation_image_name_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_ORG_IMG_NAME));
+ }
+
+ add_to_store (value, liststore, COL_ORG_IMG_NAME);
+ }
+ /* Tab: IPTC Extension, Label: Featured Organization - Code */
+ else if (! strcmp ("Xmp.iptcExt.OrganisationInImageCode",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
+ GTK_SELECTION_SINGLE);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (organisation_image_code_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_ORG_IMG_CODE));
+ }
+
+ add_to_store (value, liststore, COL_ORG_IMG_CODE);
+ }
+ /* Tab: IPTC Extension, Label: Artwork or Object */
+ else if (! strcmp ("Xmp.iptcExt.ArtworkOrObject",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint counter;
+ gchar **tagdata;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* ARTWORK OR OBJECT - TITLE */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_TITLE);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_title_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_TITLE));
+ }
+
+ /* ARTWORK OR OBJECT - DATE CREATED */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_DATE_CREAT);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r != NULL; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_date_creat_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_DATE_CREAT));
+ }
+
+ /* ARTWORK OR OBJECT - CREATOR */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_CREATOR);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r != NULL; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_creator_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_CREATOR));
+ }
+
+ /* ARTWORK OR OBJECT - SOURCE */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_SOURCE);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_source_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_SOURCE));
+ }
+
+ /* ARTWORK OR OBJECT - SOURCE INVENTORY ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_SRC_INV_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_source_inv_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_SRC_INV_ID));
+ }
+
+ /* ARTWORK OR OBJECT - COPYRIGHT NOTICE */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_AOO_CR_NOT);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (aoo_copyright_notice_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_AOO_CR_NOT));
+ }
+
+ counter = count_tags (metadata, ARTWORKOROBJECT_HEADER,
+ artworkorobject,
+ n_artworkorobject);
+
+ tagdata = get_tags (metadata, ARTWORKOROBJECT_HEADER,
+ artworkorobject,
+ n_artworkorobject, counter);
+
+ if (counter == 0 || ! tagdata)
+ {
+ /* Alternatively try: /iptcExt:* */
+ counter = count_tags (metadata, ARTWORKOROBJECT_HEADER,
+ artworkorobject_alternative,
+ n_artworkorobject);
+
+ tagdata = get_tags (metadata, ARTWORKOROBJECT_HEADER,
+ artworkorobject_alternative,
+ n_artworkorobject, counter);
+ }
+
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+
+ /* remove substring for language id in title field */
+ remove_substring (tagdatarow[COL_AOO_TITLE], lang_default);
+ if (strstr (tagdatarow[COL_AOO_TITLE], " "))
+ {
+ remove_substring (tagdatarow[COL_AOO_TITLE], " ");
+ }
+
+ remove_substring (tagdatarow[COL_AOO_TITLE], bag_default);
+ if (strstr (tagdatarow[COL_AOO_TITLE], " "))
+ {
+ remove_substring (tagdatarow[COL_AOO_TITLE], " ");
+ }
+
+ remove_substring (tagdatarow[COL_AOO_TITLE], seq_default);
+ if (strstr (tagdatarow[COL_AOO_TITLE], " "))
+ {
+ remove_substring (tagdatarow[COL_AOO_TITLE], " ");
+ }
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_AOO_TITLE, tagdatarow[0],
+ COL_AOO_DATE_CREAT, tagdatarow[1],
+ COL_AOO_CREATOR, tagdatarow[2],
+ COL_AOO_SOURCE, tagdatarow[3],
+ COL_AOO_SRC_INV_ID, tagdatarow[4],
+ COL_AOO_CR_NOT, tagdatarow[5],
+ -1);
+ }
+ free_tagdata(tagdata, counter, n_artworkorobject);
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_AOO_TITLE, NULL,
+ COL_AOO_DATE_CREAT, NULL,
+ COL_AOO_CREATOR, NULL,
+ COL_AOO_SOURCE, NULL,
+ COL_AOO_SRC_INV_ID, NULL,
+ COL_AOO_CR_NOT, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Model Release Identifier */
+ else if (! strcmp ("Xmp.plus.ModelReleaseID",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
+ GTK_SELECTION_SINGLE);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (mod_rel_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_PROP_REL_ID));
+ }
+
+ add_to_store (value, liststore, COL_MOD_REL_ID);
+ }
+ /* Tab: IPTC Extension, Label: Registry Entry */
+ else if (! strcmp ("Xmp.iptcExt.RegistryId",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint counter;
+ gchar **tagdata;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* REGISTRY - ORGANIZATION ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_REGISTRY_ORG_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r != NULL; r = r->next)
+ {
+ renderer = (GtkCellRenderer*) r->data;
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (reg_org_id_cell_edited_callback),
+ treemodel);
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_REGISTRY_ORG_ID));
+ }
+
+ /* REGISTRY - ITEM ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_REGISTRY_ITEM_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (reg_item_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_REGISTRY_ITEM_ID));
+ }
+
+ counter = count_tags (metadata, REGISTRYID_HEADER,
+ registryid,
+ n_registryid);
+
+ tagdata = get_tags (metadata, REGISTRYID_HEADER,
+ registryid,
+ n_registryid, counter);
+
+ if (counter == 0 || ! tagdata)
+ {
+ /* Alternatively try: /iptcExt:* */
+ counter = count_tags (metadata, REGISTRYID_HEADER,
+ registryid_alternative,
+ n_registryid);
+
+ tagdata = get_tags (metadata, REGISTRYID_HEADER,
+ registryid_alternative,
+ n_registryid, counter);
+ }
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, tagdatarow[0],
+ COL_REGISTRY_ITEM_ID, tagdatarow[1],
+ -1);
+ }
+ free_tagdata(tagdata, counter, n_registryid);
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, NULL,
+ COL_REGISTRY_ITEM_ID, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Image Creator */
+ else if (! strcmp ("Xmp.plus.ImageCreator",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint counter;
+ gchar **tagdata;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* IMAGE CREATOR - NAME */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_IMG_CR8_NAME);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (img_cr8_name_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_IMG_CR8_NAME));
+ }
+
+ /* IMAGE CREATOR - ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_IMG_CR8_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (img_cr8_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_IMG_CR8_ID));
+ }
+
+ counter = count_tags (metadata, IMAGECREATOR_HEADER,
+ imagecreator,
+ n_imagecreator);
+
+ tagdata = get_tags (metadata, IMAGECREATOR_HEADER,
+ imagecreator,
+ n_imagecreator, counter);
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, tagdatarow[0],
+ COL_IMG_CR8_ID, tagdatarow[1],
+ -1);
+ }
+ free_tagdata(tagdata, counter, n_imagecreator);
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, NULL,
+ COL_IMG_CR8_ID, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Copyright Owner */
+ else if (! strcmp ("Xmp.plus.CopyrightOwner",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint counter;
+ gchar **tagdata;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* COPYRIGHT OWNER - NAME */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_CR_OWNER_NAME);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (cr_owner_name_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_CR_OWNER_NAME));
+ }
+
+ /* COPYRIGHT OWNER - ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_CR_OWNER_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (cr_owner_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_CR_OWNER_ID));
+ }
+
+ counter = count_tags (metadata, COPYRIGHTOWNER_HEADER,
+ copyrightowner,
+ n_copyrightowner);
+
+ tagdata = get_tags (metadata, COPYRIGHTOWNER_HEADER,
+ copyrightowner,
+ n_copyrightowner, counter);
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, tagdatarow[0],
+ COL_CR_OWNER_ID, tagdatarow[1],
+ -1);
+ }
+ free_tagdata(tagdata, counter, n_copyrightowner);
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, NULL,
+ COL_CR_OWNER_ID, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Licensor */
+ else if (! strcmp ("Xmp.plus.Licensor",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkTreeModel *phonemodel;
+ GtkListStore *liststore;
+ GtkListStore *phonestore;
+ GtkTreeIter iter;
+ GtkTreeIter phoneiter;
+ gint counter;
+ gint j;
+ gchar **tagdata;
+
+ phonestore = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_list_store_append (phonestore, &phoneiter);
+ gtk_list_store_set (phonestore, &phoneiter, 0, "Unknown", -1);
+ for (j=1; j < 6; j++)
+ {
+ gtk_list_store_append (phonestore, &phoneiter);
+ gtk_list_store_set (phonestore, &phoneiter,
+ 0, gettext (phone_types[j].display),
+ -1);
+ }
+ phonemodel = GTK_TREE_MODEL (phonestore);
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ /* LICENSOR - NAME */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_NAME);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_name_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_NAME));
+ }
+
+ /* LICENSOR - ID */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_ID);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_ID));
+ }
+
+ /* LICENSOR - PHONE NUMBER 1 */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_PHONE1);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_phone1_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_PHONE1));
+ }
+
+ /* LICENSOR - PHONE TYPE 1 */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_PHONE_TYPE1);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ "text-column", 0,
+ "has-entry", FALSE,
+ "model", phonemodel,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_phone_type1_cell_edited_callback),
+ widget);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_PHONE_TYPE1));
+ }
+
+ /* LICENSOR - PHONE NUMBER 2 */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_PHONE2);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_phone2_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_PHONE2));
+ }
+
+ /* LICENSOR - PHONE TYPE 2 */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_PHONE_TYPE2);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ "text-column", 0,
+ "has-entry", FALSE,
+ "model", phonemodel,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_phone_type2_cell_edited_callback),
+ widget);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_PHONE_TYPE2));
+ }
+
+ /* LICENSOR - EMAIL */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_EMAIL);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_email_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_EMAIL));
+ }
+
+ /* LICENSOR - WEB ADDRESS */
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget),
+ COL_LICENSOR_WEB);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (licensor_web_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_LICENSOR_WEB));
+ }
+
+ counter = count_tags (metadata, LICENSOR_HEADER,
+ licensor,
+ n_licensor);
+
+ tagdata = get_tags (metadata, LICENSOR_HEADER,
+ licensor,
+ n_licensor, counter);
+
+ if (counter > 0 && tagdata)
+ {
+ gint item;
+
+ for (item = 0; item < counter; item++)
+ {
+ gchar **tagdatarow = (gchar **) tagdata[item];
+ gchar *type1;
+ gchar *type2;
+ gint types;
+
+ type1 = g_strdup (gettext (phone_types[0].display));
+ type2 = g_strdup (gettext (phone_types[0].display));
+
+ for (types = 0; types < 6; types++)
+ {
+ /* phone type 1 */
+ if (tagdatarow[3] &&
+ ! strcmp (tagdatarow[3],
+ phone_types[types].data))
+ {
+ g_free (type1);
+ type1 = g_strdup (gettext (phone_types[types].display));
+ }
+
+ /* phone type 2 */
+ if (tagdatarow[5] &&
+ ! strcmp (tagdatarow[5],
+ phone_types[types].data))
+ {
+ g_free (type2);
+ type2 = g_strdup (gettext (phone_types[types].display));
+ }
+ }
+
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LICENSOR_NAME, tagdatarow[0],
+ COL_LICENSOR_ID, tagdatarow[1],
+ COL_LICENSOR_PHONE1, tagdatarow[2],
+ COL_LICENSOR_PHONE_TYPE1, type1,
+ COL_LICENSOR_PHONE2, tagdatarow[4],
+ COL_LICENSOR_PHONE_TYPE2, type2,
+ COL_LICENSOR_EMAIL, tagdatarow[6],
+ COL_LICENSOR_WEB, tagdatarow[7],
+ -1);
+ g_free (type1);
+ g_free (type2);
+ }
+ free_tagdata(tagdata, counter, n_licensor);
+ }
+ else
+ {
+ gint item;
+
+ for (item = 0; item < 2; item++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LICENSOR_NAME, NULL,
+ COL_LICENSOR_ID, NULL,
+ COL_LICENSOR_PHONE1, NULL,
+ COL_LICENSOR_PHONE_TYPE1, gettext (phone_types[0].display),
+ COL_LICENSOR_PHONE2, NULL,
+ COL_LICENSOR_PHONE_TYPE1, gettext (phone_types[0].display),
+ COL_LICENSOR_EMAIL, NULL,
+ COL_LICENSOR_WEB, NULL,
+ -1);
+ }
+ }
+ }
+ /* Tab: IPTC Extension, Label: Property Release Identifier */
+ else if (! strcmp ("Xmp.plus.PropertyReleaseID",
+ default_metadata_tags[i].tag))
+ {
+ GList *rlist;
+ GList *r;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ liststore = GTK_LIST_STORE (treemodel);
+
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)),
+ GTK_SELECTION_SINGLE);
+
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW (widget), 0);
+ rlist = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
+ for (r = rlist; r; r = r->next)
+ {
+ renderer = r->data;
+
+ g_object_set (renderer,
+ "editable", TRUE,
+ NULL);
+
+ g_signal_connect (renderer, "edited",
+ G_CALLBACK (prop_rel_id_cell_edited_callback),
+ treemodel);
+
+ g_object_set_data (G_OBJECT (renderer),
+ "column",
+ GINT_TO_POINTER (COL_PROP_REL_ID));
+ }
+
+ add_to_store (value, liststore, COL_PROP_REL_ID);
+ }
+ }
+
+ if (value)
+ {
+ if (! strcmp ("single", default_metadata_tags[i].mode))
+ {
+ gtk_entry_set_text (GTK_ENTRY (widget), value);
+ }
+ else if (! strcmp ("multi", default_metadata_tags[i].mode))
+ {
+ GtkTextBuffer *buffer;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
+ gtk_text_buffer_set_text (buffer, value, -1);
+ }
+ else if (! strcmp ("combo", default_metadata_tags[i].mode))
+ {
+ gint32 data = 0;
+
+ if (! strcmp ("Exif.GPSInfo.GPSLatitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ if (! strncmp ("N", value, 1))
+ {
+ data = 1;
+ }
+ else if (! strncmp ("S", value, 1))
+ {
+ data = 2;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLongitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ if (! strncmp ("E", value, 1))
+ {
+ data = 1;
+ }
+ else if (! strncmp ("W", value, 1))
+ {
+ data = 2;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSAltitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ if (! strncmp ("A", value, 1))
+ {
+ data = 1;
+ }
+ else if (! strncmp ("B", value, 1))
+ {
+ data = 2;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Xmp.xmp.Rating", default_metadata_tags[i].tag))
+ {
+ if (! strcmp ("1", value))
+ {
+ data = 1;
+ }
+ else if (! strcmp ("2", value))
+ {
+ data = 2;
+ }
+ else if (! strcmp ("3", value))
+ {
+ data = 3;
+ }
+ else if (! strcmp ("4", value))
+ {
+ data = 4;
+ }
+ else if (! strcmp ("5", value))
+ {
+ data = 5;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Xmp.xmpRights.Marked",
+ default_metadata_tags[i].tag))
+ {
+ if (! strcmp ("True", value))
+ {
+ data = 1;
+ }
+ else if (! strcmp ("False", value))
+ {
+ data = 2;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Xmp.photoshop.Urgency",
+ default_metadata_tags[i].tag))
+ {
+ if (! strcmp ("1", value))
+ {
+ data = 1;
+ }
+ else if (! strcmp ("2", value))
+ {
+ data = 2;
+ }
+ else if (! strcmp ("3", value))
+ {
+ data = 3;
+ }
+ else if (! strcmp ("4", value))
+ {
+ data = 4;
+ }
+ else if (! strcmp ("5", value))
+ {
+ data = 5;
+ }
+ else if (! strcmp ("6", value))
+ {
+ data = 6;
+ }
+ else if (! strcmp ("7", value))
+ {
+ data = 7;
+ }
+ else if (! strcmp ("8", value))
+ {
+ data = 8;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Xmp.plus.MinorModelAgeDisclosure",
+ default_metadata_tags[i].tag))
+ {
+ if (! strcmp ("Age Unknown", value))
+ {
+ data = 0;
+ }
+ else if (! strcmp ("Age 25 or Over", value))
+ {
+ data = 1;
+ }
+ else if (! strcmp ("Age 24", value))
+ {
+ data = 2;
+ }
+ else if (! strcmp ("Age 23", value))
+ {
+ data = 3;
+ }
+ else if (! strcmp ("Age 22", value))
+ {
+ data = 4;
+ }
+ else if (! strcmp ("Age 21", value))
+ {
+ data = 5;
+ }
+ else if (! strcmp ("Age 20", value))
+ {
+ data = 6;
+ }
+ else if (! strcmp ("Age 19", value))
+ {
+ data = 7;
+ }
+ else if (! strcmp ("Age 18", value))
+ {
+ data = 8;
+ }
+ else if (! strcmp ("Age 17", value))
+ {
+ data = 9;
+ }
+ else if (! strcmp ("Age 16", value))
+ {
+ data = 10;
+ }
+ else if (! strcmp ("Age 15", value))
+ {
+ data = 11;
+ }
+ else if (! strcmp ("Age 14 or Under", value))
+ {
+ data = 12;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ else if (! strcmp ("Xmp.plus.ModelReleaseStatus",
+ default_metadata_tags[i].tag))
+ {
+ gint loop;
+
+ for (loop = 0; loop < n_modelreleasestatus; loop++)
+ {
+ if (! strcmp (modelreleasestatus[loop].data, value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+
+ if (! strcmp (gettext (modelreleasestatus[loop].display),
+ value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+ }
+ }
+ else if (! strcmp ("Xmp.iptcExt.DigitalSourceType",
+ default_metadata_tags[i].tag))
+ {
+ gint loop;
+
+ for (loop = 0; loop < n_digitalsourcetype; loop++)
+ {
+ if (! strcmp (digitalsourcetype[loop].data, value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+
+ if (! strcmp (gettext (digitalsourcetype[loop].display),
+ value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+ }
+ }
+ else if (! strcmp ("Xmp.plus.PropertyReleaseStatus",
+ default_metadata_tags[i].tag))
+ {
+ gint loop;
+
+ for (loop = 0; loop < 4; loop++)
+ {
+ if (! strcmp (propertyreleasestatus[loop].data, value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+
+ if (! strcmp (gettext (propertyreleasestatus[loop].display),
+ value))
+ {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), loop);
+ break;
+ }
+ }
+ }
+ else if (! strcmp ("Xmp.DICOM.PatientSex",
+ default_metadata_tags[i].tag))
+ {
+ if (! strcmp ("male", value))
+ {
+ data = 1;
+ }
+ else if (! strcmp ("female", value))
+ {
+ data = 2;
+ }
+ else if (! strcmp ("other", value))
+ {
+ data = 3;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget), data);
+ }
+ }
+ g_free (value);
+ }
+ }
+
+ /* Set Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:* last since the short form
+ * Xmp.iptc.Ci* could have been used to set this information too. Because
+ * the first (longer) form is the most common let that override the shorter
+ * form in the (unlikely) case that both are present and also have
+ * different values. Due to a bug in the metadata-editor previously only
+ * the short form was saved.
+ */
+ for (i = 0; i < creatorContactInfoHeader.size; i++)
+ {
+ GtkWidget *widget;
+
+ widget = builder_get_widget (builder, creatorContactInfoTags[i].id);
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata,
+ creatorContactInfoTags[i].tag);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = clean_xmp_string (value);
+ g_free (value);
+
+ if (! strcmp ("single", creatorContactInfoTags[i].mode))
+ {
+ gtk_entry_set_text (GTK_ENTRY (widget), value_utf);
+ }
+ else if (! strcmp ("multi", creatorContactInfoTags[i].mode))
+ {
+ GtkTextBuffer *buffer;
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
+ gtk_text_buffer_set_text (buffer, value_utf, -1);
+ }
+ g_free (value_utf);
+ }
+ }
+
+ /* Set creation date */
+ entry_widget = builder_get_widget (builder, "create_date_button");
+ g_signal_connect (entry_widget, "clicked",
+ G_CALLBACK (on_create_date_button_clicked),
+ builder_get_widget (builder,
+ "Xmp.photoshop.DateCreated"));
+
+ /* Set patient dob date */
+ entry_widget = builder_get_widget (builder, "dob_date_button");
+ g_signal_connect (entry_widget, "clicked",
+ G_CALLBACK (on_patient_dob_date_button_clicked),
+ builder_get_widget (builder,
+ "Xmp.DICOM.PatientDOB"));
+
+ /* Set study date */
+ entry_widget = builder_get_widget (builder, "study_date_button");
+ g_signal_connect (entry_widget, "clicked",
+ G_CALLBACK (on_study_date_button_clicked),
+ builder_get_widget (builder,
+ "Xmp.DICOM.StudyDateTime"));
+
+ /* Set series date */
+ entry_widget = builder_get_widget (builder, "series_date_button");
+ g_signal_connect (entry_widget, "clicked",
+ G_CALLBACK (on_series_date_button_clicked),
+ builder_get_widget (builder,
+ "Xmp.DICOM.SeriesDateTime"));
+}
+
+
+/* ============================================================================
+ * ==[ WRITE METADATA ]========================================================
+ * ============================================================================
+ */
+
+static void
+set_tag_failed (const gchar *tag)
+{
+ g_log ("", G_LOG_LEVEL_MESSAGE,
+ _("Failed to set metadata tag %s"), tag);
+}
+
+static void
+set_tag_string (GimpMetadata *metadata,
+ const gchar *name,
+ const gchar *value)
+{
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), name);
+
+ if (metadata == NULL) return;
+ if (name == NULL) return;
+ if (value == NULL) return;
+
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ name, value))
+ {
+ set_tag_failed (name);
+ }
+}
+
+static gchar *
+get_phonetype (gchar *cur_value)
+{
+ gchar *phone_type_value = NULL;
+ gint types;
+
+ if (cur_value != NULL)
+ {
+ for (types = 0; types < 6; types++)
+ {
+ if (! strcmp (cur_value, gettext (phone_types[types].display)))
+ {
+ phone_type_value = strdup (phone_types[types].data);
+ break;
+ }
+ }
+ g_free (cur_value);
+ }
+ if (! phone_type_value)
+ phone_type_value = strdup (phone_types[0].data);
+ cur_value = phone_type_value;
+
+ return phone_type_value;
+}
+
+static void
+write_metadata_tag (GtkBuilder *builder, GimpMetadata *metadata, gchar * tag, gint data_column)
+{
+ GtkWidget *list_widget;
+ GtkTreeModel *treemodel;
+ gint row;
+ gint number_of_rows;
+ gchar *rc_data;
+ GString *data;
+
+ list_widget = builder_get_widget (builder, tag);
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));
+
+ number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);
+
+ if (number_of_rows <= 0)
+ return;
+
+ data = g_string_sized_new (256);
+
+ for (row = 0; row < number_of_rows; row++)
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_iter_nth_child (treemodel, &iter, NULL, row))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ data_column, &rc_data,
+ -1);
+ if (rc_data && rc_data[0] != '\0')
+ {
+ if (row > 0)
+ g_string_append (data, ", ");
+
+ g_string_append (data, rc_data);
+ }
+ g_free (rc_data);
+ }
+ }
+
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ "write_metadata_tag tag: %s, value: %s",
+ tag, data->str);
+
+ set_tag_string (metadata, tag, data->str);
+ g_string_free (data, TRUE);
+}
+
+static void
+write_metadata_tag_multiple (GtkBuilder *builder, GimpMetadata *metadata,
+ GExiv2StructureType type, const gchar * header_tag,
+ gint n_columns, const gchar **column_tags,
+ const gint special_handling[])
+{
+ GtkWidget *list_widget;
+ GtkTreeModel *treemodel;
+ gint row;
+ gint number_of_rows;
+ gint counter;
+ gchar temp_tag[1024];
+
+ /* Clear old tag data first */
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), header_tag);
+
+ for (row = 0; row < 256; row++)
+ {
+ gint item;
+
+ for (item = 0; item < n_columns; item++)
+ {
+ g_snprintf (temp_tag, sizeof (temp_tag), "%s[%d]%s",
+ header_tag, row, locationshown[item]);
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), temp_tag);
+ }
+ }
+
+ list_widget = builder_get_widget (builder, header_tag);
+ treemodel = gtk_tree_view_get_model (GTK_TREE_VIEW (list_widget));
+
+ number_of_rows = gtk_tree_model_iter_n_children (treemodel, NULL);
+
+ if (number_of_rows <= 0)
+ return;
+
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (metadata),
+ header_tag,
+ GEXIV2_STRUCTURE_XA_BAG);
+
+ /* We need a separate counter because an empty row will not be written */
+ counter = 1;
+ for (row = 0; row < number_of_rows; row++)
+ {
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_iter_nth_child (treemodel, &iter, NULL, row))
+ {
+ gint col;
+
+ for (col = 0; col < n_columns; col++)
+ {
+ gchar *tag_data;
+
+ gtk_tree_model_get (treemodel, &iter,
+ col, &tag_data,
+ -1);
+
+ g_snprintf (temp_tag, sizeof (temp_tag), "%s[%d]%s",
+ header_tag, counter, column_tags[col]);
+
+ if (special_handling)
+ switch(special_handling[col])
+ {
+ case METADATA_PHONETYPE:
+ tag_data = get_phonetype (tag_data);
+ break;
+ }
+
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+ "write_metadata_tag_multiple tag: %s, value: %s, col: %d",
+ temp_tag, tag_data, col);
+
+ set_tag_string (metadata, temp_tag, tag_data);
+ g_free (tag_data);
+ }
+ counter++;
+ }
+ }
+}
+
+static void
+set_gps_longitude_latitude (GimpMetadata *metadata,
+ const gchar *tag,
+ const gchar *value)
+{
+ /* \u00b0 - degree symbol */
+ const gchar delimiters_dms[] = " deg'\":\u00b0";
+ gchar lng_lat[256];
+ gchar *s = g_strdup (value);
+ gchar *str1 = NULL;
+ gchar *str2 = NULL;
+ gchar *str3 = NULL;
+ gdouble val = 0.f;
+ gint degrees, minutes;
+ gdouble seconds;
+ gboolean remove_val = FALSE;
+
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "set_gps_longitude_latitude - Tag %s, Input value: %s", tag, value);
+
+ if (s && s[0] != '\0')
+ {
+ str1 = strtok (s, delimiters_dms);
+ str2 = strtok (NULL, delimiters_dms);
+ str3 = strtok (NULL, delimiters_dms);
+
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "String split into: %s - %s - %s", str1, str2, str3);
+ }
+
+ g_free (s);
+
+ if (str1 && str2 && str3)
+ {
+ /* Assuming degrees, minutes, seconds */
+ degrees = g_ascii_strtoll (str1, NULL, 10);
+ minutes = g_ascii_strtoll (str2, NULL, 10);
+ seconds = g_ascii_strtod (str3, NULL);
+ }
+ else if (str1 && str2)
+ {
+ /* Assuming degrees, minutes */
+ gdouble min;
+
+ degrees = g_ascii_strtoll (str1, NULL, 10);
+ min = g_ascii_strtod (str2, NULL);
+ minutes = (gint) min;
+ seconds = (min - (gdouble) minutes) * 60.f;
+ }
+ else if (str1)
+ {
+ /* Assuming degrees only */
+ val = g_ascii_strtod (str1, NULL);
+ degrees = (gint) val;
+ minutes = (gint) ((val - (gdouble) degrees) * 60.f);
+ seconds = ((val - (gdouble) degrees - (gdouble) (minutes / 60.f)) * 60.f * 60.f);
+ }
+ else
+ remove_val = TRUE;
+
+ if (!remove_val)
+ {
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Converted values: %d - %d - %f", degrees, minutes, seconds);
+ g_snprintf (lng_lat, sizeof (lng_lat),
+ "%d/1 %d/1 %d/1000",
+ abs (degrees), abs (minutes), abs ((gint) (seconds * 1000.f)));
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Tag: %s, output string: %s", tag, lng_lat);
+
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (metadata),
+ tag, lng_lat))
+ {
+ set_tag_failed (tag);
+ }
+ }
+ else
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), tag);
+ g_log (ME_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Removed tag %s (no value).", tag);
+ }
+}
+
+void
+metadata_editor_write_callback (GtkWidget *dialog,
+ GtkBuilder *builder,
+ gint32 image_id)
+{
+ GimpMetadata *g_metadata;
+ gint max_elements;
+ gint i = 0;
+
+ g_metadata = gimp_image_get_metadata (image_id);
+
+ gimp_metadata_add_xmp_history (g_metadata, "metadata");
+
+ write_metadata_tag (builder, g_metadata,
+ "Xmp.iptcExt.OrganisationInImageName",
+ COL_ORG_IMG_NAME);
+
+ write_metadata_tag (builder, g_metadata,
+ "Xmp.iptcExt.OrganisationInImageCode",
+ COL_ORG_IMG_CODE);
+
+ write_metadata_tag (builder, g_metadata,
+ "Xmp.plus.ModelReleaseID",
+ COL_MOD_REL_ID);
+
+ write_metadata_tag (builder, g_metadata,
+ "Xmp.plus.PropertyReleaseID",
+ COL_PROP_REL_ID);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
+ "Xmp.iptcExt.LocationShown",
+ n_locationshown, locationshown_alternative, NULL);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
+ "Xmp.iptcExt.ArtworkOrObject",
+ n_artworkorobject, artworkorobject_alternative, NULL);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_BAG,
+ "Xmp.iptcExt.RegistryId",
+ n_registryid, registryid_alternative, NULL);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
+ "Xmp.plus.ImageCreator",
+ n_imagecreator, imagecreator, NULL);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
+ "Xmp.plus.CopyrightOwner",
+ n_copyrightowner, copyrightowner, NULL);
+
+ write_metadata_tag_multiple (builder, g_metadata, GEXIV2_STRUCTURE_XA_SEQ,
+ "Xmp.plus.Licensor",
+ n_licensor, licensor,
+ licensor_special_handling);
+
+ /* DO CREATOR TAGS */
+
+ if (hasCreatorTagData (builder))
+ {
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ creatorContactInfoHeader.header,
+ "type=\"Struct\""))
+ {
+ set_tag_failed (creatorContactInfoTags[i].tag);
+ }
+
+ for (i = 0; i < creatorContactInfoHeader.size; i++)
+ {
+ GObject *object = gtk_builder_get_object (builder,
+ creatorContactInfoTags[i].id);
+
+ if (! strcmp ("single", creatorContactInfoTags[i].mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ creatorContactInfoTags[i].tag,
+ gtk_entry_get_text (entry)))
+ {
+ set_tag_failed (creatorContactInfoTags[i].tag);
+ }
+ }
+ else if (! strcmp ("multi", creatorContactInfoTags[i].mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *text;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ creatorContactInfoTags[i].tag,
+ text))
+ {
+ set_tag_failed (creatorContactInfoTags[i].tag);
+ }
+
+ g_free (text);
+ }
+ }
+ }
+
+ /* DO SINGLE, MULTI AND COMBO TAGS */
+
+ else
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ creatorContactInfoHeader.header);
+
+ for (i = 0; i < creatorContactInfoHeader.size; i++)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ creatorContactInfoTags[i].tag);
+ }
+ }
+
+ max_elements = n_default_metadata_tags;
+
+ for (i = 0; i < max_elements; i++)
+ {
+ GObject *object = gtk_builder_get_object (builder,
+ default_metadata_tags[i].tag);
+
+ /* SINGLE TAGS */
+
+ if (! strcmp ("single", default_metadata_tags[i].mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+ gchar *value_entry = g_strdup (gtk_entry_get_text (entry));
+
+ if (! strcmp ("Exif.GPSInfo.GPSLongitude",
+ default_metadata_tags[i].tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLatitude",
+ default_metadata_tags[i].tag))
+ {
+ set_gps_longitude_latitude (g_metadata,
+ default_metadata_tags[i].tag,
+ value_entry);
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSAltitude",
+ default_metadata_tags[i].tag))
+ {
+ GtkWidget *combo_widget;
+ gchar alt_str[256];
+ gdouble alt_d;
+ gint msr;
+
+ combo_widget = builder_get_widget (builder,
+ "GPSAltitudeSystem");
+ msr = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_widget));
+
+ alt_d = atof (gtk_entry_get_text (entry));
+ if (msr == 1)
+ alt_d = (alt_d * 12 * 2.54);
+ else
+ alt_d *= 100.f;
+
+ g_snprintf (alt_str, sizeof (alt_str), "%d/100", (gint) alt_d);
+
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ alt_str))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ }
+ else
+ {
+ gint index;
+ const gchar *text_value = gtk_entry_get_text (entry);
+
+ if (default_metadata_tags[i].xmp_type == GIMP_XMP_TEXT ||
+ default_metadata_tags[i].xmp_type == GIMP_XMP_NONE)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ GEXIV2_STRUCTURE_XA_NONE);
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ text_value))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ }
+ else
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ text_value))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ }
+
+ index = default_metadata_tags[i].other_tag_index;
+ if (index > -1)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ equivalent_metadata_tags[index].tag);
+ if (*text_value &&
+ ! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ equivalent_metadata_tags[index].tag,
+ text_value))
+ {
+ set_tag_failed (equivalent_metadata_tags[index].tag);
+ }
+ }
+ }
+ }
+
+ /* MULTI TAGS */
+
+ else if (! strcmp ("multi", default_metadata_tags[i].mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+ gchar *text;
+ gint index;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+
+ if (text && *text)
+ {
+ if (default_metadata_tags[i].xmp_type == GIMP_XMP_TEXT ||
+ default_metadata_tags[i].xmp_type == GIMP_XMP_NONE)
+ {
+ gexiv2_metadata_set_xmp_tag_struct (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ GEXIV2_STRUCTURE_XA_NONE);
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ text))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ }
+ else
+ {
+ gchar **multi;
+
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+
+ /* We have one value per line. */
+ multi = g_strsplit (text, "\n", 0);
+
+ if (! gexiv2_metadata_set_tag_multiple (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ (const gchar **) multi))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ g_strfreev (multi);
+ }
+ }
+
+ index = default_metadata_tags[i].other_tag_index;
+ if (index > -1)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ equivalent_metadata_tags[index].tag);
+
+ if (text && *text)
+ {
+ if (! strcmp ("multi", equivalent_metadata_tags[index].mode))
+ {
+ gchar **multi;
+
+ multi = g_strsplit (text, "\n", 0);
+
+ if (! gexiv2_metadata_set_tag_multiple (GEXIV2_METADATA (g_metadata),
+ equivalent_metadata_tags[index].tag,
+ (const gchar **) multi))
+ {
+ set_tag_failed (equivalent_metadata_tags[index].tag);
+ }
+
+ g_strfreev (multi);
+ }
+ else if (! strcmp ("single", equivalent_metadata_tags[index].mode))
+ {
+ /* Convert from multiline to single line: keep the \n and just add the whole text. */
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ equivalent_metadata_tags[index].tag,
+ text))
+ {
+ set_tag_failed (equivalent_metadata_tags[index].tag);
+ }
+ }
+ else
+ {
+ g_warning ("Copying from multiline tag %s to %s tag %s not implemented!",
+ default_metadata_tags[i].tag,
+ equivalent_metadata_tags[index].mode,
+ equivalent_metadata_tags[index].tag);
+ }
+ }
+ }
+
+ if (text)
+ g_free (text);
+ }
+ else if (! strcmp ("list", default_metadata_tags[i].mode))
+ {
+ /* MIGHT DO SOMETHING HERE */
+ }
+
+ /* COMBO TAGS */
+
+ else if (! strcmp ("combo", default_metadata_tags[i].mode))
+ {
+ GtkComboBoxText *combo;
+ gint32 value;
+
+ combo = GTK_COMBO_BOX_TEXT (object);
+ value = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ if (! strcmp ("Xmp.photoshop.Urgency", default_metadata_tags[i].tag))
+ {
+ /* IPTC tab - Urgency */
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ "Iptc.Application2.Urgency");
+ }
+ else
+ {
+ gchar *save;
+
+ save = g_strdup_printf ("%d", value);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ save);
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ "Iptc.Application2.Urgency",
+ save);
+ g_free (save);
+ }
+ }
+ else if (! strcmp ("Xmp.xmpRights.Marked",
+ default_metadata_tags[i].tag))
+ {
+ /* Description tab - Copyright Status */
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gchar *save_value;
+
+ if (value == 1)
+ save_value = g_strdup_printf ("%s", "True");
+ else /* (value == 2) */
+ save_value = g_strdup_printf ("%s", "False");
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ save_value);
+ g_free (save_value);
+ }
+ }
+ else if (! strcmp ("Xmp.xmp.Rating", default_metadata_tags[i].tag))
+ {
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gchar *save;
+
+ save = g_strdup_printf ("%d", value);
+
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ save);
+ g_free (save);
+ }
+ }
+ else if (! strcmp ("Xmp.DICOM.PatientSex",
+ default_metadata_tags[i].tag))
+ {
+ switch (value)
+ {
+ case 0:
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ break;
+
+ case 1:
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "male"))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ break;
+
+ case 2:
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "female"))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ break;
+
+ case 3:
+ if (! gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "other"))
+ {
+ set_tag_failed (default_metadata_tags[i].tag);
+ }
+ break;
+ }
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLongitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ switch (value)
+ {
+ case 0:
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ break;
+
+ case 1:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "E");
+ break;
+
+ case 2:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "W");
+ break;
+ }
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLatitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ switch (value)
+ {
+ case 0:
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ break;
+
+ case 1:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "N");
+ break;
+
+ case 2:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "S");
+ break;
+ }
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSAltitudeRef",
+ default_metadata_tags[i].tag))
+ {
+ switch (value)
+ {
+ case 0:
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ break;
+
+ case 1:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "0");
+ break;
+
+ case 2:
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ "1");
+ break;
+ }
+ }
+ else if (! strcmp ("Xmp.plus.ModelReleaseStatus",
+ default_metadata_tags[i].tag))
+ {
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ modelreleasestatus[value].data);
+ }
+ }
+ else if (! strcmp ("Xmp.plus.PropertyReleaseStatus",
+ default_metadata_tags[i].tag))
+ {
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ propertyreleasestatus[value].data);
+ }
+ }
+ else if (! strcmp ("Xmp.plus.MinorModelAgeDisclosure",
+ default_metadata_tags[i].tag))
+ {
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ minormodelagedisclosure[value].data);
+ }
+ }
+ else if (! strcmp ("Xmp.iptcExt.DigitalSourceType",
+ default_metadata_tags[i].tag))
+ {
+ if (value == 0)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag);
+ }
+ else
+ {
+ gexiv2_metadata_set_tag_string (GEXIV2_METADATA (g_metadata),
+ default_metadata_tags[i].tag,
+ digitalsourcetype[value].data);
+ }
+ }
+ }
+ }
+
+ gimp_image_set_metadata (image_id, g_metadata);
+}
+
+/* ============================================================================
+ * ==[ METADATA IMPORT / EXPORT FILE DIALOG UI ]===============================
+ * ============================================================================
+ */
+
+static void
+import_dialog_metadata (metadata_editor *args)
+{
+ GtkWidget *file_dialog;
+ gchar *filename;
+
+ file_dialog = gtk_file_chooser_dialog_new (_("Import Metadata File"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_dialog),
+ args->filename);
+
+ if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_dialog));
+
+ if (filename)
+ {
+ if (args->filename)
+ {
+ g_free (args->filename);
+ }
+
+ args->filename = g_strdup (filename);
+ import_file_metadata (args);
+ }
+ }
+
+ gtk_widget_destroy (file_dialog);
+}
+
+static void
+export_dialog_metadata (metadata_editor *args)
+{
+ GtkWidget *file_dialog;
+ gchar *filename;
+
+ file_dialog = gtk_file_chooser_dialog_new (_("Export Metadata File"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Export"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_dialog),
+ TRUE);
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (file_dialog),
+ args->filename);
+
+ if (gtk_dialog_run (GTK_DIALOG (file_dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_dialog));
+
+ if (filename)
+ {
+ if (args->filename)
+ {
+ g_free (args->filename);
+ }
+
+ args->filename = g_strdup (filename);
+ export_file_metadata (args);
+ }
+ }
+
+ gtk_widget_destroy (file_dialog);
+}
+
+static void
+impex_combo_callback (GtkComboBoxText *combo,
+ gpointer data)
+{
+ metadata_editor *args;
+ gint32 selection;
+
+ args = data;
+ selection = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ switch (selection)
+ {
+ case 1: /* Import */
+ import_dialog_metadata (args);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+ break;
+
+ case 2: /* Export */
+ export_dialog_metadata (args);
+ gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+ break;
+ }
+}
+
+
+static void
+gpsaltsys_combo_callback (GtkComboBoxText *combo,
+ gpointer data)
+{
+ GtkWidget *entry;
+ GtkBuilder *builder;
+ gint32 selection;
+ gchar alt_str[256];
+ double alt_d;
+
+ builder = data;
+ selection = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+ entry = builder_get_widget (builder, "Exif.GPSInfo.GPSAltitude");
+
+ switch (selection)
+ {
+ case 0: /* Meters */
+ if (last_gpsaltsys_sel != 0)
+ {
+ alt_d = atof (gtk_entry_get_text (GTK_ENTRY (entry)));
+ alt_d = (alt_d * (12 * 2.54)) / 100;
+
+ g_snprintf (alt_str, sizeof (alt_str), "%.2f", (float)alt_d);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), alt_str);
+ }
+ break;
+
+ case 1: /* Feet */
+ if (last_gpsaltsys_sel != 1)
+ {
+ alt_d = atof (gtk_entry_get_text (GTK_ENTRY (entry)));
+ alt_d = alt_d * 3.28;
+
+ g_snprintf (alt_str, sizeof (alt_str), "%.2f", (float)alt_d);
+
+ gtk_entry_set_text (GTK_ENTRY (entry), alt_str);
+ }
+ break;
+ }
+
+ last_gpsaltsys_sel = selection;
+}
diff --git a/plug-ins/metadata/metadata-editor.h b/plug-ins/metadata/metadata-editor.h
new file mode 100644
index 0000000..c3fe93c
--- /dev/null
+++ b/plug-ins/metadata/metadata-editor.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METADATA_EDITOR_H__
+#define __METADATA_EDITOR_H__
+
+extern void metadata_editor_write_callback (GtkWidget *dialog,
+ GtkBuilder *builder,
+ gint32 image_id);
+
+#endif /* __METADATA_EDITOR_H__ */
diff --git a/plug-ins/metadata/metadata-impexp.c b/plug-ins/metadata/metadata-impexp.c
new file mode 100644
index 0000000..c7bc176
--- /dev/null
+++ b/plug-ins/metadata/metadata-impexp.c
@@ -0,0 +1,244 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata-editor.c
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gexiv2/gexiv2.h>
+
+#include <glib/gstdio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-xml.h"
+#include "metadata-misc.h"
+#include "metadata-tags.h"
+#include "metadata-impexp.h"
+#include "metadata-editor.h"
+
+extern gboolean gimpmetadata;
+extern gboolean xmptag;
+extern gboolean iptctag;
+extern gboolean tagvalue;
+extern gboolean tagname;
+extern gboolean force_write;
+extern gchar *str_tag_value;
+extern gchar *str_tag_name;
+
+const GMarkupParser xml_markup_parser =
+{
+ xml_parser_start_element,
+ xml_parser_end_element,
+ xml_parser_data,
+ NULL, /* passthrough */
+ NULL /* error */
+};
+
+
+/* ============================================================================
+ * ==[ METADATA IMPORT TEMPLATE ]==============================================
+ * ============================================================================
+ */
+void
+import_file_metadata(metadata_editor *args)
+{
+ GimpXmlParser *xml_parser;
+ GError *error = NULL;
+ FILE *file;
+
+ gimpmetadata = FALSE;
+ xmptag = FALSE;
+ iptctag = FALSE;
+ tagvalue = FALSE;
+ tagname = FALSE;
+
+ file = g_fopen (args->filename, "r");
+ if (file != NULL)
+ {
+ /* parse xml data fetched from file */
+ xml_parser = xml_parser_new (&xml_markup_parser, args);
+ if (! xml_parser_parse_file (xml_parser, args->filename, &error))
+ {
+ g_warning ("Error parsing xml: %s.", error? error->message: "");
+ g_clear_error (&error);
+ }
+ xml_parser_free (xml_parser);
+
+ fclose (file);
+ }
+}
+
+/* ============================================================================
+ * ==[ METADATA EXPORT TEMPLATE ]==============================================
+ * ============================================================================
+ */
+void
+export_file_metadata (metadata_editor *args)
+{
+ FILE *file;
+ GString *xmldata;
+ gint i, size;
+
+ if (force_write == TRUE)
+ {
+ /* Save fields in case of updates */
+ metadata_editor_write_callback (args->dialog, args->builder, args->image_id);
+ /* Fetch a fresh copy of the metadata */
+ args->metadata = GEXIV2_METADATA (gimp_image_get_metadata (args->image_id));
+ }
+
+ xmldata = g_string_new ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<gimp-metadata>\n");
+
+ /* HANDLE IPTC */
+ for (i = 0; i < n_equivalent_metadata_tags; i++)
+ {
+ int index = equivalent_metadata_tags[i].other_tag_index;
+ g_string_append (xmldata, "\t<iptc-tag>\n");
+ g_string_append (xmldata, "\t\t<tag-name>");
+ g_string_append (xmldata, equivalent_metadata_tags[i].tag);
+ g_string_append (xmldata, "</tag-name>\n");
+ g_string_append (xmldata, "\t\t<tag-mode>");
+ g_string_append (xmldata, equivalent_metadata_tags[i].mode);
+ g_string_append (xmldata, "</tag-mode>\n");
+ g_string_append (xmldata, "\t\t<tag-value>");
+
+ if (!strcmp("single", default_metadata_tags[index].mode) ||
+ !strcmp("multi", default_metadata_tags[index].mode))
+ {
+ const gchar *value;
+
+ value = get_tag_ui_text (args, default_metadata_tags[index].tag,
+ default_metadata_tags[index].mode);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ g_string_append (xmldata, value_utf);
+ g_free (value_utf);
+ }
+ }
+ else if (!strcmp("combo", default_metadata_tags[index].mode))
+ {
+ gint data = get_tag_ui_combo (args, default_metadata_tags[index].tag,
+ default_metadata_tags[index].mode);
+ g_string_append_printf (xmldata, "%d", data);
+ }
+ else if (!strcmp("list", default_metadata_tags[i].mode))
+ {
+ /* No IPTC lists elements at this point */
+ }
+
+ g_string_append (xmldata, "</tag-value>\n");
+ g_string_append (xmldata, "\t</iptc-tag>\n");
+ }
+
+ /* HANDLE XMP */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ g_string_append (xmldata, "\t<xmp-tag>\n");
+ g_string_append (xmldata, "\t\t<tag-name>");
+ g_string_append (xmldata, default_metadata_tags[i].tag);
+ g_string_append (xmldata, "</tag-name>\n");
+ g_string_append (xmldata, "\t\t<tag-mode>");
+ g_string_append (xmldata, default_metadata_tags[i].mode);
+ g_string_append (xmldata, "</tag-mode>\n");
+
+ if (!strcmp("single", default_metadata_tags[i].mode) ||
+ !strcmp("multi", default_metadata_tags[i].mode))
+ {
+ const gchar *value;
+
+ g_string_append (xmldata, "\t\t<tag-value>");
+ value = get_tag_ui_text (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+
+ if (value)
+ {
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ g_string_append (xmldata, value_utf);
+ g_free (value_utf);
+ }
+
+ g_string_append (xmldata, "</tag-value>\n");
+ }
+ else if (!strcmp("combo", default_metadata_tags[i].mode))
+ {
+ gint data;
+
+ g_string_append (xmldata, "\t\t<tag-value>");
+
+ data = get_tag_ui_combo (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+ g_string_append_printf (xmldata, "%d", data);
+
+ g_string_append (xmldata, "</tag-value>\n");
+ }
+ else if (!strcmp("list", default_metadata_tags[i].mode))
+ {
+ gchar *data;
+
+ g_string_append (xmldata, "\t\t<tag-list-value>\n");
+
+ data = get_tag_ui_list (args, default_metadata_tags[i].tag,
+ default_metadata_tags[i].mode);
+
+ if (data)
+ {
+ g_string_append (xmldata, data);
+ g_free(data);
+ }
+
+ g_string_append (xmldata, "\t\t</tag-list-value>\n");
+ }
+
+ g_string_append (xmldata, "\t</xmp-tag>\n");
+
+ }
+
+ g_string_append (xmldata, "</gimp-metadata>\n");
+
+
+ size = strlen (xmldata->str);
+ file = g_fopen (args->filename, "w");
+ if (file != NULL)
+ {
+ GError *error = NULL;
+
+ if (! g_file_set_contents (args->filename, xmldata->str, size, &error))
+ {
+ g_warning ("Error saving file: %s.", error? error->message: "");
+ g_clear_error (&error);
+ }
+ fclose (file);
+ }
+
+ if (xmldata)
+ {
+ g_string_free(xmldata, TRUE);
+ }
+}
+
diff --git a/plug-ins/metadata/metadata-impexp.h b/plug-ins/metadata/metadata-impexp.h
new file mode 100644
index 0000000..0229751
--- /dev/null
+++ b/plug-ins/metadata/metadata-impexp.h
@@ -0,0 +1,30 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METADATA_IMPEXP_H__
+#define __METADATA_IMPEXP_H__
+
+void
+import_file_metadata (metadata_editor *args);
+
+void
+export_file_metadata (metadata_editor *args);
+
+#endif /* __METADATA_IMPEXP_H__ */
+
diff --git a/plug-ins/metadata/metadata-misc.h b/plug-ins/metadata/metadata-misc.h
new file mode 100644
index 0000000..4ba27d5
--- /dev/null
+++ b/plug-ins/metadata/metadata-misc.h
@@ -0,0 +1,70 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METADATA_MISC_H__
+#define __METADATA_MISC_H__
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkBuilder *builder;
+ GExiv2Metadata *metadata;
+ gint32 image_id;
+ gchar *filename;
+} metadata_editor;
+
+typedef struct
+{
+ gchar *tag;
+ gchar *mode;
+ gint32 other_tag_index;
+ gint32 tag_type;
+ gint32 xmp_type;
+} metadata_tag;
+
+typedef struct
+{
+ gchar *data;
+ gchar *display;
+} combobox_str_tag;
+
+typedef struct
+{
+ gint32 data;
+ gchar *display;
+} combobox_int_tag;
+
+typedef struct
+{
+ gchar *header;
+ gchar *type;
+ gint32 size;
+} TranslateHeaderTag;
+
+typedef struct
+{
+ gchar *id;
+ gchar *tag;
+ gchar *mode;
+ gint32 other_tag_index;
+ gint32 tag_type;
+} TranslateTag;
+
+#endif /* __METADATA_MISC_H__ */
+
diff --git a/plug-ins/metadata/metadata-tags.c b/plug-ins/metadata/metadata-tags.c
new file mode 100644
index 0000000..601751f
--- /dev/null
+++ b/plug-ins/metadata/metadata-tags.c
@@ -0,0 +1,506 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gexiv2/gexiv2.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <libgimp/gimp.h>
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-tags.h"
+
+
+/* The meaning of "single" and "multi" here denotes whether it is used in a
+ * single line or a multi line edit field.
+ * Depending on it's xmp type multi line can be saved as either:
+ * - one tag of type text, possibly including newlines
+ * - an array of tags of the same type for seq and bag, where each line in
+ * the multi line edit will be one item in the array
+ */
+const metadata_tag default_metadata_tags[] =
+{
+ /* Description */
+ { "Xmp.dc.title", "single", 16, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 0
+ { "Xmp.dc.creator", "single", 13, TAG_TYPE_XMP, GIMP_XMP_SEQ }, // 1
+ { "Xmp.dc.description", "multi", 14, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 2
+ { "Xmp.dc.subject", "multi", 15, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 3
+ { "Xmp.dc.rights", "single", 17, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 4
+ { "Xmp.photoshop.AuthorsPosition", "single", 19, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 5
+ { "Xmp.photoshop.CaptionWriter", "single", 21, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 6
+ { "Xmp.xmp.Rating", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 7
+ { "Xmp.xmpRights.Marked", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 8
+ { "Xmp.xmpRights.WebStatement", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 9
+
+ /* IPTC */
+ { "Xmp.photoshop.DateCreated", "single", 0, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 10
+ { "Xmp.photoshop.Headline", "multi", 3, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 11
+ { "Xmp.photoshop.TransmissionReference", "single", 1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 12
+ { "Xmp.photoshop.Instructions", "multi", 2, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 13
+ { "Xmp.iptc.IntellectualGenre", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 14
+ { "Xmp.iptc.Scene", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 15
+ { "Xmp.iptc.Location", "single", 18, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 16
+ { "Xmp.iptc.CountryCode", "single", 20, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 17
+ { "Xmp.iptc.SubjectCode", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 18
+ { "Xmp.xmpRights.UsageTerms", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 19
+ { "Xmp.photoshop.City", "single", 5, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 20
+ { "Xmp.photoshop.State", "single", 6, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 21
+ { "Xmp.photoshop.Country", "single", 7, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 22
+ /* Xmp.photoshop.CaptionWriter here is a duplicate of #6 above. We keep it here to not have
+ * to renumber the tag references. It seems it is not used on the IPTC tab. */
+ { "Xmp.photoshop.CaptionWriter", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 23
+ { "Xmp.photoshop.Credit", "single", 8, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 24
+ { "Xmp.photoshop.Source", "single", 9, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 25
+ { "Xmp.photoshop.Urgency", "combo", 11, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 26
+
+ /* IPTC Extension */
+ { "Xmp.iptcExt.PersonInImage", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 27
+ { "Xmp.iptcExt.Sublocation", "single", 12, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 28
+ { "Xmp.iptcExt.City", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 29
+ { "Xmp.iptcExt.ProvinceState", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 30
+ { "Xmp.iptcExt.CountryName", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 31
+ { "Xmp.iptcExt.CountryCode", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 32
+ { "Xmp.iptcExt.WorldRegion", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 33
+ { "Xmp.iptcExt.LocationShown", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 34
+ { "Xmp.iptcExt.OrganisationInImageName", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 35
+ { "Xmp.iptcExt.OrganisationInImageCode", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 36
+ { "Xmp.iptcExt.Event", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 37
+ { "Xmp.iptcExt.RegistryId", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 38
+ { "Xmp.iptcExt.ArtworkOrObject", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 39
+ { "Xmp.iptcExt.AddlModelInfo", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 40
+ { "Xmp.iptcExt.ModelAge", "single", -1, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 41
+ { "Xmp.iptcExt.MaxAvailWidth", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 42
+ { "Xmp.iptcExt.MaxAvailHeight", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 43
+ { "Xmp.iptcExt.DigitalSourceType", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 44
+ { "Xmp.plus.MinorModelAgeDisclosure", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 45
+ { "Xmp.plus.ModelReleaseStatus", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 46
+ { "Xmp.plus.ModelReleaseID", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 47
+ { "Xmp.plus.ImageSupplierName", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 48
+ { "Xmp.plus.ImageSupplierID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 49
+ { "Xmp.plus.ImageSupplierImageID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 50
+ { "Xmp.plus.ImageCreator", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 51
+ { "Xmp.plus.CopyrightOwner", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 52
+ { "Xmp.plus.Licensor", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 53
+ { "Xmp.plus.PropertyReleaseStatus", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 54
+ { "Xmp.plus.PropertyReleaseID", "list", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 55
+
+ /* Categories */
+ { "Xmp.photoshop.Category", "single", 4, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 56
+ { "Xmp.photoshop.SupplementalCategories", "multi", 10, TAG_TYPE_XMP, GIMP_XMP_BAG }, // 57
+
+ /* GPS */
+ { "Exif.GPSInfo.GPSLongitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 58
+ { "Exif.GPSInfo.GPSLongitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 59
+ { "Exif.GPSInfo.GPSLatitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 60
+ { "Exif.GPSInfo.GPSLatitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 61
+ { "Exif.GPSInfo.GPSAltitude", "single", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 62
+ { "Exif.GPSInfo.GPSAltitudeRef", "combo", -1, TAG_TYPE_EXIF, GIMP_XMP_NONE }, // 63
+
+ /* DICOM */
+ { "Xmp.DICOM.PatientName", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 64
+ { "Xmp.DICOM.PatientID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 65
+ { "Xmp.DICOM.PatientDOB", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 66
+ { "Xmp.DICOM.PatientSex", "combo", -1, TAG_TYPE_XMP, GIMP_XMP_NONE }, // 67
+ { "Xmp.DICOM.StudyID", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 68
+ { "Xmp.DICOM.StudyPhysician", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 69
+ { "Xmp.DICOM.StudyDateTime", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 70
+ { "Xmp.DICOM.StudyDescription", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 71
+ { "Xmp.DICOM.SeriesNumber", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 72
+ { "Xmp.DICOM.SeriesModality", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 73
+ { "Xmp.DICOM.SeriesDateTime", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 74
+ { "Xmp.DICOM.SeriesDescription", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 75
+ { "Xmp.DICOM.EquipmentInstitution", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 76
+ { "Xmp.DICOM.EquipmentManufacturer", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 77
+
+ /* IPTC */
+ { "Xmp.iptc.CiAdrExtadr", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 78
+ { "Xmp.iptc.CiAdrCity", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 79
+ { "Xmp.iptc.CiAdrRegion", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 80
+ { "Xmp.iptc.CiAdrPcode", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 81
+ { "Xmp.iptc.CiAdrCtry", "single", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 82
+ { "Xmp.iptc.CiTelWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 83
+ { "Xmp.iptc.CiEmailWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT }, // 84
+ { "Xmp.iptc.CiUrlWork", "multi", -1, TAG_TYPE_XMP, GIMP_XMP_TEXT } // 85
+
+};
+const gint n_default_metadata_tags = G_N_ELEMENTS (default_metadata_tags);
+
+/* Then meaning of "single" and "multi" below is a little different than above.
+ * "single" - for iptc tags that can appear only once,
+ * "multi" - for iptc tags that are repeatable, i.e. can appear multiple times.
+ */
+const metadata_tag equivalent_metadata_tags[] =
+{
+ { "Iptc.Application2.DateCreated", "single", 10, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 0
+ { "Iptc.Application2.TransmissionReference", "single", 12, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 1
+ { "Iptc.Application2.SpecialInstructions", "single", 13, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 2
+ { "Iptc.Application2.Headline", "single", 11, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 3
+ { "Iptc.Application2.Category", "single", 56, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 4
+ { "Iptc.Application2.City", "single", 20, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 5
+ { "Iptc.Application2.ProvinceState", "single", 21, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 6
+ { "Iptc.Application2.CountryName", "single", 22, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 7
+ { "Iptc.Application2.Credit", "single", 24, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 8
+ { "Iptc.Application2.Source", "single", 25, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 9
+ { "Iptc.Application2.SuppCategory", "multi", 57, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 10
+ { "Iptc.Application2.Urgency", "combo", 26, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 11
+ { "Iptc.Application2.SubLocation", "single", 28, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 12
+ { "Iptc.Application2.Byline", "single", 1, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 13
+ { "Iptc.Application2.Caption", "single", 2, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 14
+ { "Iptc.Application2.Keywords", "multi", 3, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 15
+ { "Iptc.Application2.ObjectName", "single", 0, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 16
+ { "Iptc.Application2.Copyright", "single", 4, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 17
+ { "Iptc.Application2.LocationName", "multi", 16, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 18
+ { "Iptc.Application2.BylineTitle", "multi", 5, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 19
+ { "Iptc.Application2.CountryCode", "single", 17, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 20
+ { "Iptc.Application2.Writer", "multi", 6, TAG_TYPE_IPTC, GIMP_XMP_NONE }, // 21
+};
+const gint n_equivalent_metadata_tags = G_N_ELEMENTS (equivalent_metadata_tags);
+
+/* Digital Source Type Combobox Items
+ * http://cv.iptc.org/newscodes/digitalsourcetype/
+ */
+const combobox_str_tag digitalsourcetype[] =
+{
+ { "", N_("Select a value") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture", N_("Original digital capture of a real life scene") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/negativeFilm", N_("Digitized from a negative on film") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/positiveFilm", N_("Digitized from a positive on film") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/print", N_("Digitized from a print on non-transparent medium") },
+ { "http://cv.iptc.org/newscodes/digitalsourcetype/softwareImage", N_("Created by software") }
+};
+const gint n_digitalsourcetype = G_N_ELEMENTS (digitalsourcetype);
+
+/* Model Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#ModelReleaseStatus
+ */
+const combobox_str_tag modelreleasestatus[] =
+{
+ { "", N_("Select a value") },
+ { "http://ns.useplus.org/ldf/vocab/MR-NON", N_("None") },
+ { "http://ns.useplus.org/ldf/vocab/MR-NAP", N_("Not Applicable") },
+ { "http://ns.useplus.org/ldf/vocab/MR-UMR", N_("Unlimited Model Releases") },
+ { "http://ns.useplus.org/ldf/vocab/MR-LMR", N_("Limited or Incomplete Model Releases") }
+};
+const gint n_modelreleasestatus = G_N_ELEMENTS (modelreleasestatus);
+
+/* Property Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#PropertyReleaseStatus
+ */
+const combobox_str_tag propertyreleasestatus[] =
+{
+ { "http://ns.useplus.org/ldf/vocab/PR-NON", N_("None") },
+ { "http://ns.useplus.org/ldf/vocab/PR-NAP", N_("Not Applicable") },
+ { "http://ns.useplus.org/ldf/vocab/PR-UPR", N_("Unlimited Property Releases") },
+ { "http://ns.useplus.org/ldf/vocab/PR-LPR", N_("Limited or Incomplete Property Releases") }
+};
+const gint n_propertyreleasestatus = G_N_ELEMENTS (propertyreleasestatus);
+
+/* Minor Model Age Disclosure Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#MinorModelAgeDisclosure
+ */
+const combobox_str_tag minormodelagedisclosure[] =
+{
+ { "http://ns.useplus.org/ldf/vocab/AG-UNK", N_("Age Unknown") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A25", N_("Age 25 or Over") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A24", N_("Age 24") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A23", N_("Age 23") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A22", N_("Age 22") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A21", N_("Age 21") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A20", N_("Age 20") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A19", N_("Age 19") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A18", N_("Age 18") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A17", N_("Age 17") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A16", N_("Age 16") },
+ { "http://ns.useplus.org/ldf/vocab/AG-A15", N_("Age 15") },
+ { "http://ns.useplus.org/ldf/vocab/AG-U14", N_("Age 14 or Under") }
+};
+const gint n_minormodelagedisclosure = G_N_ELEMENTS (minormodelagedisclosure);
+
+/* Urgency */
+const gchar *urgency[] =
+{
+ N_("None"), N_("High"), N_("2"), N_("3"), N_("4"), N_("Normal"), N_("6"), N_("7"), N_("Low")
+};
+const gint n_urgency = G_N_ELEMENTS (urgency);
+
+/* Marked */
+const combobox_int_tag marked[] =
+{
+ { -1, N_("Unknown") }, // DO NOT SAVE
+ { TRUE, N_("Copyrighted") }, // TRUE
+ { FALSE, N_("Public Domain") }, // FALSE
+};
+const gint n_marked = G_N_ELEMENTS (marked);
+
+/* Phone Types */
+const combobox_str_tag phone_types[] =
+{
+ { "", N_("Select a value") },
+ { "http://ns.useplus.org/ldf/vocab/work", N_("Work") },
+ { "http://ns.useplus.org/ldf/vocab/cell", N_("Cell") },
+ { "http://ns.useplus.org/ldf/vocab/fax", N_("Fax") },
+ { "http://ns.useplus.org/ldf/vocab/home", N_("Home") },
+ { "http://ns.useplus.org/ldf/vocab/pager", N_("Pager") }
+};
+const gint n_phone_types = G_N_ELEMENTS (phone_types);
+
+/* DICOM Patient Sex
+ * http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.7-1
+ * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/XMP.html#DICOM
+ * https://dicomiseasy.blogspot.ca/2011/11/introduction-to-dicom-chapter-iii-dicom.html
+ * http://dicom.nema.org/standard.html
+ */
+const combobox_str_tag dicom[] =
+{
+ { "", N_("Select a value") },
+ { "male", N_("Male") },
+ { "female", N_("Female") },
+ { "other", N_("Other") },
+};
+const gint n_dicom = G_N_ELEMENTS (dicom);
+
+/* GPS Altitude Ref */
+const gchar *gpsaltref[] =
+{
+ N_("Unknown"), N_("Above sea level"), N_("Below sea level")
+};
+const gint n_gpsaltref = G_N_ELEMENTS (gpsaltref);
+
+/* GPS Latitude Ref */
+const gchar *gpslatref[] =
+{
+ N_("Unknown"), N_("North"), N_("South")
+};
+const gint n_gpslatref = G_N_ELEMENTS (gpslatref);
+
+/* GPS Longitude Ref */
+const gchar *gpslngref[] =
+{
+ N_("Unknown"), N_("East"), N_("West")
+};
+const gint n_gpslngref = G_N_ELEMENTS (gpslngref);
+
+/* GPS Measurement System */
+const gchar *gpsaltsys[] =
+{
+ "m", "ft"
+};
+const gint n_gpsaltsys = G_N_ELEMENTS (gpsaltsys);
+
+const TranslateHeaderTag creatorContactInfoHeader =
+{
+ "Xmp.iptc.CreatorContactInfo", "type=\"Struct\"", 8
+};
+
+const TranslateTag creatorContactInfoTags[] =
+{
+ { "Xmp.iptc.CiAdrExtadr", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrExtadr", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrCity", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrRegion", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrRegion", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrPcode", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrPcode", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiAdrCtry", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiTelWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiTelWork", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiEmailWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiEmailWork", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.iptc.CiUrlWork", "Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork", "multi", -1, TAG_TYPE_XMP }
+};
+
+const TranslateHeaderTag locationCreationInfoHeader =
+{
+ "Xmp.iptcExt.LocationCreated", "type=\"Bag\"", 6
+};
+
+const TranslateTag locationCreationInfoTags[] =
+{
+ { "Xmp.iptcExt.Sublocation", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:Sublocation", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.City", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:City", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.ProvinceState", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:ProvinceState", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.CountryName", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryName", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.CountryCode", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:CountryCode", "single", -1, TAG_TYPE_XMP },
+ { "Xmp.iptcExt.WorldRegion", "Xmp.iptcExt.LocationCreated[1]/Iptc4xmpExt:WorldRegion", "single", -1, TAG_TYPE_XMP }
+};
+
+const TranslateHeaderTag imageSupplierInfoHeader =
+{
+ "Xmp.plus.ImageSupplier", "type=\"Seq\"", 2
+};
+
+const TranslateTag imageSupplierInfoTags[] =
+{
+ { "Xmp.plus.ImageSupplierName", "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierName", "multi", -1, TAG_TYPE_XMP },
+ { "Xmp.plus.ImageSupplierID", "Xmp.plus.ImageSupplier[1]/plus:ImageSupplierID", "single", -1, TAG_TYPE_XMP }
+};
+
+/* Plus and IPTC extension tags */
+
+const gchar *licensor[] =
+{
+ "/plus:LicensorName",
+ "/plus:LicensorID",
+ "/plus:LicensorTelephone1",
+ "/plus:LicensorTelephoneType1",
+ "/plus:LicensorTelephone2",
+ "/plus:LicensorTelephoneType2",
+ "/plus:LicensorEmail",
+ "/plus:LicensorURL"
+};
+const gint n_licensor = G_N_ELEMENTS (licensor);
+
+const gint licensor_special_handling[] =
+{
+ METADATA_NONE,
+ METADATA_NONE,
+ METADATA_NONE,
+ METADATA_PHONETYPE,
+ METADATA_NONE,
+ METADATA_PHONETYPE,
+ METADATA_NONE,
+ METADATA_NONE
+};
+
+#ifdef USE_TAGS
+const gchar *imagesupplier[] =
+{
+ "/plus:ImageSupplierName",
+ "/plus:ImageSupplierID"
+};
+const gint n_imagesupplier = G_N_ELEMENTS (imagesupplier);
+#endif
+
+const gchar *imagecreator[] =
+{
+ "/plus:ImageCreatorName",
+ "/plus:ImageCreatorID"
+};
+const gint n_imagecreator = G_N_ELEMENTS (imagecreator);
+
+const gchar *copyrightowner[] =
+{
+ "/plus:CopyrightOwnerName",
+ "/plus:CopyrightOwnerID"
+};
+const gint n_copyrightowner = G_N_ELEMENTS (copyrightowner);
+
+const gchar *registryid[] =
+{
+ "/Iptc4xmpExt:RegOrgId",
+ "/Iptc4xmpExt:RegItemId"
+};
+const gint n_registryid = G_N_ELEMENTS (registryid);
+
+const gchar *registryid_alternative[] =
+{
+ "/iptcExt:RegOrgId",
+ "/iptcExt:RegItemId"
+};
+
+const gchar *artworkorobject[] =
+{
+ "/Iptc4xmpExt:AOTitle",
+ "/Iptc4xmpExt:AODateCreated",
+ "/Iptc4xmpExt:AOCreator",
+ "/Iptc4xmpExt:AOSource",
+ "/Iptc4xmpExt:AOSourceInvNo",
+ "/Iptc4xmpExt:AOCopyrightNotice",
+};
+const gint n_artworkorobject = G_N_ELEMENTS (artworkorobject);
+
+const gchar *artworkorobject_alternative[] =
+{
+ "/iptcExt:AOTitle",
+ "/iptcExt:AODateCreated",
+ "/iptcExt:AOCreator",
+ "/iptcExt:AOSource",
+ "/iptcExt:AOSourceInvNo",
+ "/iptcExt:AOCopyrightNotice",
+};
+
+const gchar *locationshown[] =
+{
+ "/Iptc4xmpExt:Sublocation",
+ "/Iptc4xmpExt:City",
+ "/Iptc4xmpExt:ProvinceState",
+ "/Iptc4xmpExt:CountryName",
+ "/Iptc4xmpExt:CountryCode",
+ "/Iptc4xmpExt:WorldRegion"
+};
+const gint n_locationshown = G_N_ELEMENTS (locationshown);
+
+const gchar *locationshown_alternative[] =
+{
+ "/iptcExt:Sublocation",
+ "/iptcExt:City",
+ "/iptcExt:ProvinceState",
+ "/iptcExt:CountryName",
+ "/iptcExt:CountryCode",
+ "/iptcExt:WorldRegion"
+};
+
+
+#ifdef USE_TAGS
+const gchar *locationcreated[] =
+{
+ "/Iptc4xmpExt:Sublocation",
+ "/Iptc4xmpExt:City",
+ "/Iptc4xmpExt:ProvinceState",
+ "/Iptc4xmpExt:CountryName",
+ "/Iptc4xmpExt:CountryCode",
+ "/Iptc4xmpExt:WorldRegion"
+};
+const gint n_locationcreated = G_N_ELEMENTS (locationcreated);
+#endif
+
+
+gchar *
+metadata_format_gps_longitude_latitude (const gdouble value)
+{
+ gint deg, min;
+ gdouble sec;
+ gdouble gps_value = value;
+
+ if (gps_value < 0.f)
+ gps_value *= -1.f;
+
+ deg = (gint) gps_value;
+ min = (gint) ((gps_value - (gdouble) deg) * 60.f);
+ sec = ((gps_value - (gdouble) deg - (gdouble) (min / 60.f)) * 60.f * 60.f);
+
+ return g_strdup_printf ("%ddeg %d' %.3f\"", deg, min, sec);
+}
+
+/*
+ * use_meter: True return meters, False return feet
+ * measurement_symbol: Should be "m", "ft", or empty string (not NULL)
+ */
+gchar *
+metadata_format_gps_altitude (const gdouble value,
+ gboolean use_meter,
+ gchar *measurement_symbol)
+{
+ gdouble gps_value = value;
+
+ if (gps_value < 0.f)
+ gps_value *= -1.f;
+
+ if (! use_meter)
+ {
+ gps_value *= 3.28;
+ }
+
+ return g_strdup_printf ("%.2f%s", gps_value, measurement_symbol);
+}
diff --git a/plug-ins/metadata/metadata-tags.h b/plug-ins/metadata/metadata-tags.h
new file mode 100644
index 0000000..81209d5
--- /dev/null
+++ b/plug-ins/metadata/metadata-tags.h
@@ -0,0 +1,253 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METADATA_TAGS_H__
+#define __METADATA_TAGS_H__
+
+#include "metadata-misc.h"
+
+#define TAG_TYPE_XMP 1
+#define TAG_TYPE_EXIF 2
+#define TAG_TYPE_IPTC 3
+
+enum
+{
+ GIMP_XMP_NONE = 0,
+ GIMP_XMP_TEXT,
+ GIMP_XMP_BAG,
+ GIMP_XMP_SEQ,
+ GIMP_XMP_LANG,
+ GIMP_XMP_ALT
+};
+
+enum
+{
+ COL_LICENSOR_NAME = 0,
+ COL_LICENSOR_ID,
+ COL_LICENSOR_PHONE1,
+ COL_LICENSOR_PHONE_TYPE1,
+ COL_LICENSOR_PHONE2,
+ COL_LICENSOR_PHONE_TYPE2,
+ COL_LICENSOR_EMAIL,
+ COL_LICENSOR_WEB,
+ COL_LICENSOR_NUM_COLS
+};
+
+enum
+{
+ COL_CR_OWNER_NAME = 0,
+ COL_CR_OWNER_ID,
+ COL_CR_OWNER_NUM_COLS
+};
+
+enum
+{
+ COL_IMG_CR8_NAME = 0,
+ COL_IMG_CR8_ID,
+ COL_IMG_CR8_NUM_COLS
+};
+
+enum
+{
+ COL_AOO_TITLE = 0,
+ COL_AOO_DATE_CREAT,
+ COL_AOO_CREATOR,
+ COL_AOO_SOURCE,
+ COL_AOO_SRC_INV_ID,
+ COL_AOO_CR_NOT,
+ COL_AOO_NUM_COLS
+};
+
+enum
+{
+ COL_REGISTRY_ORG_ID = 0,
+ COL_REGISTRY_ITEM_ID,
+ COL_REGISTRY_NUM_COLS
+};
+
+enum
+{
+ COL_LOC_SHO_SUB_LOC = 0,
+ COL_LOC_SHO_CITY,
+ COL_LOC_SHO_STATE_PROV,
+ COL_LOC_SHO_CNTRY,
+ COL_LOC_SHO_CNTRY_ISO,
+ COL_LOC_SHO_CNTRY_WRLD_REG,
+ COL_LOC_SHO_NUM_COLS
+};
+
+enum
+{
+ COL_ORG_IMG_CODE = 0,
+ ORG_IMG_CODE_REL_NUM_COLS
+};
+
+enum
+{
+ COL_ORG_IMG_NAME = 0,
+ ORG_IMG_NAME_REL_NUM_COLS
+};
+
+enum
+{
+ COL_MOD_REL_ID = 0,
+ MOD_REL_NUM_COLS
+};
+
+enum
+{
+ COL_PROP_REL_ID = 0,
+ PROP_REL_NUM_COLS
+};
+
+enum METADATA_SPECIAL_PROCESSING
+{
+ METADATA_NONE = 0,
+ METADATA_PHONETYPE,
+ METADATA_PREPROCESS_TEXT
+};
+
+extern const metadata_tag default_metadata_tags[];
+extern const gint n_default_metadata_tags;
+
+extern const metadata_tag equivalent_metadata_tags[];
+extern const gint n_equivalent_metadata_tags;
+
+/* Digital Source Type Combobox Items
+ * http://cv.iptc.org/newscodes/digitalsourcetype/
+ */
+extern const combobox_str_tag digitalsourcetype[];
+extern const gint n_digitalsourcetype;
+
+/* Model Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#ModelReleaseStatus
+ */
+extern const combobox_str_tag modelreleasestatus[];
+extern const gint n_modelreleasestatus;
+
+/* Property Release Status Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#PropertyReleaseStatus
+ */
+extern const combobox_str_tag propertyreleasestatus[];
+extern const gint n_propertyreleasestatus;
+
+/* Minor Model Age Disclosure Combobox Items
+ * http://ns.useplus.org/LDF/ldf-XMPSpecification#MinorModelAgeDisclosure
+ */
+extern const combobox_str_tag minormodelagedisclosure[];
+extern const gint n_minormodelagedisclosure;
+
+/* Urgency */
+extern const gchar *urgency[];
+extern const gint n_urgency;
+
+/* Marked */
+extern const combobox_int_tag marked[];
+extern const gint n_marked;
+
+/* Phone Types */
+extern const combobox_str_tag phone_types[];
+extern const gint n_phone_types;
+
+/* DICOM Patient Sex
+ * http://dicomlookup.com/lookup.asp?sw=Ttable&q=C.7-1
+ * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/XMP.html#DICOM
+ * https://dicomiseasy.blogspot.ca/2011/11/introduction-to-dicom-chapter-iii-dicom.html
+ * http://dicom.nema.org/standard.html
+ */
+extern const combobox_str_tag dicom[];
+extern const gint n_dicom;
+
+/* GPS Altitude Ref */
+extern const gchar *gpsaltref[];
+extern const gint n_gpsaltref;
+
+/* GPS Latitude Ref */
+extern const gchar *gpslatref[];
+extern const gint n_gpslatref;
+
+/* GPS Longitude Ref */
+extern const gchar *gpslngref[];
+extern const gint n_gpslngref;
+
+/* GPS Measurement System */
+extern const gchar *gpsaltsys[];
+extern const gint n_gpsaltsys;
+
+extern const TranslateHeaderTag creatorContactInfoHeader;
+
+extern const TranslateTag creatorContactInfoTags[];
+
+extern const TranslateHeaderTag locationCreationInfoHeader;
+
+extern const TranslateTag locationCreationInfoTags[];
+
+extern const TranslateHeaderTag imageSupplierInfoHeader;
+
+extern const TranslateTag imageSupplierInfoTags[];
+
+/* Plus and IPTC extension tags */
+
+#define LICENSOR_HEADER "Xmp.plus.Licensor"
+extern const gchar *licensor[];
+extern const gint n_licensor;
+extern const gint licensor_special_handling[];
+
+#ifdef USE_TAGS
+#define IMAGESUPPLIER_HEADER "Xmp.plus.ImageSupplier"
+extern const gchar *imagesupplier[];
+extern const gint n_imagesupplier;
+#endif
+
+#define IMAGECREATOR_HEADER "Xmp.plus.ImageCreator"
+extern const gchar *imagecreator[];
+extern const gint n_imagecreator;
+
+#define COPYRIGHTOWNER_HEADER "Xmp.plus.CopyrightOwner"
+extern const gchar *copyrightowner[];
+extern const gint n_copyrightowner;
+
+#define REGISTRYID_HEADER "Xmp.iptcExt.RegistryId"
+extern const gchar *registryid[];
+extern const gchar *registryid_alternative[];
+extern const gint n_registryid;
+
+#define ARTWORKOROBJECT_HEADER "Xmp.iptcExt.ArtworkOrObject"
+extern const gchar *artworkorobject[];
+extern const gchar *artworkorobject_alternative[];
+extern const gint n_artworkorobject;
+
+#define LOCATIONSHOWN_HEADER "Xmp.iptcExt.LocationShown"
+extern const gchar *locationshown[];
+extern const gchar *locationshown_alternative[];
+extern const gint n_locationshown;
+
+#ifdef USE_TAGS
+#define LOCATIONCREATED_HEADER "Xmp.iptcExt.LocationCreated"
+extern const gchar *locationcreated[];
+extern const gint n_locationcreated;
+#endif
+
+
+gchar * metadata_format_gps_longitude_latitude (const gdouble value);
+gchar * metadata_format_gps_altitude (const gdouble value,
+ gboolean use_meter,
+ gchar *measurement_symbol);
+
+#endif /* __METADATA_TAGS_H__ */
diff --git a/plug-ins/metadata/metadata-viewer.c b/plug-ins/metadata/metadata-viewer.c
new file mode 100644
index 0000000..59719aa
--- /dev/null
+++ b/plug-ins/metadata/metadata-viewer.c
@@ -0,0 +1,683 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata.c
+ * Copyright (C) 2013 Hartmut Kuhse
+ * Copyright (C) 2016 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gegl.h>
+#include <gtk/gtk.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-tags.h"
+
+#define PLUG_IN_PROC "plug-in-metadata-viewer"
+#define PLUG_IN_BINARY "metadata-viewer"
+#define PLUG_IN_ROLE "gimp-metadata"
+
+#define EXIF_PREFIX "Exif."
+#define IPTC_PREFIX "Iptc."
+#define XMP_PREFIX "Xmp."
+
+/* The length at which to truncate tag values, in characters. */
+#define TAG_VALUE_MAX_SIZE 1024
+
+/* The length at which to truncate raw data (i.e., tag values
+ * of type "Byte" or "Undefined"), in bytes.
+ */
+#define RAW_DATA_MAX_SIZE 16
+
+
+enum
+{
+ C_XMP_TAG = 0,
+ C_XMP_VALUE,
+ NUM_XMP_COLS
+};
+
+enum
+{
+ C_EXIF_TAG = 0,
+ C_EXIF_VALUE,
+ NUM_EXIF_COLS
+};
+
+enum
+{
+ C_IPTC_TAG = 0,
+ C_IPTC_VALUE,
+ NUM_IPTC_COLS
+};
+
+
+/* local function prototypes */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gboolean metadata_viewer_dialog (gint32 image_id,
+ GimpMetadata *g_metadata,
+ GError **error);
+static void metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder);
+static void metadata_dialog_add_multiple_values (GExiv2Metadata *metadata,
+ const gchar *tag,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column);
+static void metadata_dialog_append_tags (GExiv2Metadata *metadata,
+ gchar **tags,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column,
+ gboolean load_iptc);
+static void metadata_dialog_add_tag (GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag,
+ const gchar *value);
+static void metadata_dialog_add_translated_tag (GExiv2Metadata *metadata,
+ GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag);
+static gchar * metadata_dialog_format_tag_value (GExiv2Metadata *metadata,
+ const gchar *tag,
+ gboolean truncate);
+
+
+/* local variables */
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+/* functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef metadata_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "Run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("View metadata (Exif, IPTC, XMP)"),
+ "View metadata information attached to the "
+ "current image. This can include Exif, IPTC and/or "
+ "XMP information.",
+ "Hartmut Kuhse, Michael Natterer, Ben Touchette",
+ "Hartmut Kuhse, Michael Natterer, Ben Touchette",
+ "2013, 2017",
+ N_("_View Metadata"),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (metadata_args), 0,
+ metadata_args, NULL);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Image/Metadata");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ INIT_I18N();
+ gimp_ui_init (PLUG_IN_BINARY, TRUE);
+
+ if (! strcmp (name, PLUG_IN_PROC))
+ {
+ GimpMetadata *metadata;
+ gint32 image_ID = param[1].data.d_image;
+
+ metadata = gimp_image_get_metadata (image_ID);
+
+ /* Always show metadata dialog so we can add
+ appropriate iptc data as needed. Sometimes
+ license data needs to be added after the
+ fact and the image may not contain metadata
+ but should have it added as needed. */
+
+ if (!metadata)
+ {
+ metadata = gimp_metadata_new();
+ gimp_image_set_metadata (image_ID, metadata);
+ }
+
+ if (metadata_viewer_dialog (image_ID, metadata, &error))
+ status = GIMP_PDB_SUCCESS;
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ if (error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+metadata_viewer_dialog (gint32 image_id,
+ GimpMetadata *g_metadata,
+ GError **error)
+{
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *metadata_vbox;
+ GtkWidget *content_area;
+ gchar *ui_file;
+ gchar *title;
+ gchar *name;
+ GError *local_error = NULL;
+ GExiv2Metadata *metadata;
+
+ metadata = GEXIV2_METADATA(g_metadata);
+
+ builder = gtk_builder_new ();
+
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-metadata-viewer.ui", NULL);
+
+ if (! gtk_builder_add_from_file (builder, ui_file, &local_error))
+ {
+ if (! local_error)
+ local_error = g_error_new_literal (G_FILE_ERROR, 0,
+ _("Error loading metadata-viewer dialog."));
+ g_propagate_error (error, local_error);
+
+ g_free (ui_file);
+ g_object_unref (builder);
+ return FALSE;
+ }
+
+ g_free (ui_file);
+
+ name = gimp_image_get_name (image_id);
+ title = g_strdup_printf (_("Metadata Viewer: %s"), name);
+ g_free (name);
+
+ dialog = gimp_dialog_new (title,
+ "gimp-metadata-viewer-dialog",
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+ NULL);
+
+ gtk_widget_set_size_request(dialog, 650, 500);
+
+ g_free (title);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_CLOSE,
+ -1);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ metadata_vbox = GTK_WIDGET (gtk_builder_get_object (builder,
+ "metadata-vbox"));
+ gtk_container_set_border_width (GTK_CONTAINER (metadata_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), metadata_vbox, TRUE, TRUE, 0);
+
+ metadata_dialog_set_metadata (metadata, builder);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ return TRUE;
+}
+
+
+/* private functions */
+
+static void
+metadata_dialog_set_metadata (GExiv2Metadata *metadata,
+ GtkBuilder *builder)
+{
+ gchar **tags;
+ GtkListStore *store;
+
+ /* load exif tags */
+ tags = gexiv2_metadata_get_exif_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "exif-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_EXIF_TAG, C_EXIF_VALUE, FALSE);
+
+ g_strfreev (tags);
+
+ /* load xmp tags */
+ tags = gexiv2_metadata_get_xmp_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "xmp-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_XMP_TAG, C_XMP_VALUE, FALSE);
+
+ g_strfreev (tags);
+
+ /* load iptc tags */
+ tags = gexiv2_metadata_get_iptc_tags (metadata);
+ store = GTK_LIST_STORE (gtk_builder_get_object (builder, "iptc-liststore"));
+
+ metadata_dialog_append_tags (metadata, tags, store, C_IPTC_TAG, C_IPTC_VALUE, TRUE);
+
+ g_strfreev (tags);
+}
+
+static gchar *
+metadata_format_string_value (const gchar *value,
+ gboolean truncate)
+{
+ glong size;
+ gchar *result;
+
+ size = g_utf8_strlen (value, -1);
+
+ if (! truncate || size <= TAG_VALUE_MAX_SIZE)
+ {
+ result = g_strdup(value);
+ }
+ else
+ {
+ gchar *value_utf8_trunc;
+ GString *str;
+
+ value_utf8_trunc = g_utf8_substring (value, 0, TAG_VALUE_MAX_SIZE);
+ str = g_string_new (value_utf8_trunc);
+
+ g_free (value_utf8_trunc);
+
+ g_string_append (str, "... ");
+ g_string_append_printf (str,
+ _("(%lu more character(s))"),
+ size - TAG_VALUE_MAX_SIZE);
+
+ result = g_string_free (str, FALSE);
+ }
+ return result;
+}
+
+static inline gboolean
+metadata_tag_is_string (const gchar *tag)
+{
+ const gchar *tag_type;
+
+ tag_type = gexiv2_metadata_get_tag_type (tag);
+
+ return (g_strcmp0 (tag_type, "Byte") != 0 &&
+ g_strcmp0 (tag_type, "Undefined") != 0 &&
+ g_strcmp0 (tag_type, NULL) != 0);
+}
+
+static void
+metadata_dialog_add_tag (GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag,
+ const gchar *value)
+{
+ if (value)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ tag_column, tag,
+ value_column, value,
+ -1);
+ }
+}
+
+static void
+metadata_dialog_add_translated_tag (GExiv2Metadata *metadata,
+ GtkListStore *store,
+ GtkTreeIter iter,
+ gint tag_column,
+ gint value_column,
+ const gchar *tag)
+{
+ gchar *value;
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ metadata_dialog_add_tag (store, iter, tag_column, value_column,
+ tag, gettext (value));
+ g_free (value);
+}
+
+static gchar *
+metadata_interpret_user_comment (gchar *comment)
+{
+ /* Exiv2 can return unwanted text at the start of a comment
+ * taken from Exif.Photo.UserComment since 0.27.3.
+ * Let's remove that part and replace with an empty string
+ * if there is nothing else left as comment. */
+
+ if (comment && g_str_has_prefix (comment, "charset=Ascii "))
+ {
+ gchar *real_comment;
+
+ /* Skip "charset=Ascii " (length 14) to find the real comment */
+ real_comment = comment + 14;
+ if (real_comment[0] == '\0' ||
+ ! g_strcmp0 (real_comment, "binary comment"))
+ {
+ g_free (comment);
+ /* Make empty comment instead of returning NULL or else
+ * the exif value will not be shown at all. */
+ comment = g_strdup ("");
+ }
+ else
+ {
+ real_comment = g_strdup (real_comment);
+ g_free (comment);
+ return real_comment;
+ }
+ }
+
+ return comment;
+}
+
+static void
+metadata_dialog_add_multiple_values (GExiv2Metadata *metadata,
+ const gchar *tag,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column)
+{
+ gchar **values;
+
+ values = gexiv2_metadata_get_tag_multiple (GEXIV2_METADATA (metadata), tag);
+
+ if (values)
+ {
+ gint i;
+
+ for (i = 0; values[i] != NULL; i++)
+ {
+ gchar *value;
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+
+ value = metadata_format_string_value (values[i], /* truncate = */ TRUE);
+
+ gtk_list_store_set (store, &iter,
+ tag_column, tag,
+ value_column, value,
+ -1);
+
+ g_free (value);
+ }
+
+ g_strfreev (values);
+ }
+}
+
+static void
+metadata_dialog_append_tags (GExiv2Metadata *metadata,
+ gchar **tags,
+ GtkListStore *store,
+ gint tag_column,
+ gint value_column,
+ gboolean load_iptc)
+{
+ GtkTreeIter iter;
+ const gchar *tag;
+ const gchar *last_tag = NULL;
+ gboolean gps_done = FALSE;
+
+ while ((tag = *tags++))
+ {
+ gchar *value;
+ gchar **values;
+
+ /* We need special handling for iptc tags like "Keywords" which
+ * can appear multiple times. For now assuming that this can
+ * only happen for iptc tags of String and related types.
+ * See also: https://exiv2.org/iptc.html which only lists
+ * one Date type as repeatable (Iptc.Application2.ReferenceDate),
+ * and Date is handled here as string.
+ */
+ if (load_iptc && metadata_tag_is_string (tag))
+ {
+ if (last_tag && ! strcmp (tag, last_tag))
+ {
+ continue;
+ }
+ last_tag = tag;
+
+ metadata_dialog_add_multiple_values (GEXIV2_METADATA (metadata),
+ tag, store,
+ tag_column,
+ value_column);
+ }
+ else if (! strcmp ("Exif.GPSInfo.GPSLongitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLongitudeRef", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLatitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSLatitudeRef", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSAltitude", tag) ||
+ ! strcmp ("Exif.GPSInfo.GPSAltitudeRef", tag))
+ {
+ /* We need special handling for some of the GPS tags to
+ * be able to show better values than the default. */
+ if (! gps_done)
+ {
+ gdouble lng, lat, alt;
+ gchar *str;
+ gchar *value;
+
+ if (gexiv2_metadata_get_gps_longitude (GEXIV2_METADATA (metadata),
+ &lng))
+ {
+ str = metadata_format_gps_longitude_latitude (lng);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLongitude",
+ str);
+ g_free (str);
+ }
+ metadata_dialog_add_translated_tag (metadata, store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLongitudeRef");
+
+ if (gexiv2_metadata_get_gps_latitude (GEXIV2_METADATA (metadata),
+ &lat))
+ {
+ str = metadata_format_gps_longitude_latitude (lat);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLatitude",
+ str);
+ g_free (str);
+ }
+ metadata_dialog_add_translated_tag (metadata, store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSLatitudeRef");
+
+ if (gexiv2_metadata_get_gps_altitude (GEXIV2_METADATA (metadata),
+ &alt))
+ {
+ gchar *str2, *str3;
+
+ str = metadata_format_gps_altitude (alt, TRUE, _(" meter"));
+ str2 = metadata_format_gps_altitude (alt, FALSE, _(" feet"));
+ str3 = g_strdup_printf ("%s (%s)", str, str2);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSAltitude",
+ str3);
+ g_free (str);
+ g_free (str2);
+ g_free (str3);
+ value = gexiv2_metadata_get_tag_string (metadata,
+ "Exif.GPSInfo.GPSAltitudeRef");
+
+ if (value)
+ {
+ gint index;
+
+ if (value[0] == '0')
+ index = 1;
+ else if (value[0] == '1')
+ index = 2;
+ else
+ index = 0;
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ "Exif.GPSInfo.GPSAltitudeRef",
+ gettext (gpsaltref[index]));
+ g_free (value);
+ }
+ }
+ gps_done = TRUE;
+ }
+ }
+ else if (! strcmp ("Exif.Photo.UserComment", tag))
+ {
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ /* Can start with charset. Remove part that is not relevant. */
+ value = metadata_interpret_user_comment (value);
+
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ tag, value);
+ g_free (value);
+ }
+ else
+ {
+ if (g_str_has_prefix (tag, "Xmp.") &&
+ g_strcmp0 (gexiv2_metadata_get_tag_type (tag), "XmpText") != 0)
+ {
+ metadata_dialog_add_multiple_values (GEXIV2_METADATA (metadata),
+ tag, store,
+ tag_column,
+ value_column);
+ }
+ else
+ {
+ value = metadata_dialog_format_tag_value (metadata, tag,
+ /* truncate = */ TRUE);
+ metadata_dialog_add_tag (store, iter,
+ tag_column, value_column,
+ tag, value);
+ g_free (value);
+ }
+ }
+ }
+}
+
+static gchar *
+metadata_dialog_format_tag_value (GExiv2Metadata *metadata,
+ const gchar *tag,
+ gboolean truncate)
+{
+ gchar *result;
+
+ if (metadata_tag_is_string(tag))
+ {
+ gchar *value;
+ gchar *value_utf8;
+
+ value = gexiv2_metadata_get_tag_interpreted_string (metadata, tag);
+ if (! g_utf8_validate (value, -1, NULL))
+ {
+ value_utf8 = g_locale_to_utf8 (value, -1, NULL, NULL, NULL);
+ }
+ else
+ {
+ value_utf8 = g_strdup (value);
+ }
+
+ g_free (value);
+
+ result = metadata_format_string_value (value_utf8, truncate);
+ }
+ else
+ {
+ GBytes *bytes;
+ const guchar *data;
+ gsize size;
+ gsize display_size;
+ GString *str;
+ gint i;
+
+ bytes = gexiv2_metadata_get_tag_raw (metadata, tag);
+ data = g_bytes_get_data (bytes, &size);
+
+ if (! truncate)
+ display_size = size;
+ else
+ display_size = MIN (size, RAW_DATA_MAX_SIZE);
+
+ str = g_string_sized_new (3 * display_size);
+
+ for (i = 0; i < display_size; i++)
+ g_string_append_printf (str, i == 0 ? "%02x" : " %02x", data[i]);
+
+ if (display_size < size)
+ {
+ g_string_append (str, " ... ");
+ g_string_append_printf (str,
+ _("(%llu more byte(s))"),
+ (unsigned long long) (size - display_size));
+ }
+
+ result = g_string_free (str, FALSE);
+
+ g_bytes_unref (bytes);
+ }
+
+ return result;
+}
diff --git a/plug-ins/metadata/metadata-xml.c b/plug-ins/metadata/metadata-xml.c
new file mode 100644
index 0000000..62c70b4
--- /dev/null
+++ b/plug-ins/metadata/metadata-xml.c
@@ -0,0 +1,1147 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * metadata-editor.c
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "metadata-misc.h"
+#include "metadata-xml.h"
+#include "metadata-tags.h"
+
+extern gboolean gimpmetadata;
+extern gboolean force_write;
+
+gboolean xmptag;
+gboolean iptctag;
+gboolean tagvalue;
+gboolean taglistvalue;
+gboolean tagname;
+gboolean tagmode;
+gboolean listelement;
+gboolean element;
+gchar *str_tag_value;
+gchar *str_tag_name;
+gchar *str_tag_mode;
+gchar *str_element;
+gchar *list_tag_data[256][256];
+gint row_count = 0;
+gint item_count = 0;
+
+
+static void get_list_elements (GString *xmldata,
+ int element_count,
+ gchar **rowtagdata);
+
+
+void
+xml_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ if (strcmp (element_name, "gimp-metadata") == 0)
+ {
+ gimpmetadata = TRUE;
+ }
+ else if (strcmp (element_name, "iptc-tag") == 0)
+ {
+ item_count = 0;
+ row_count = 0;
+ iptctag = TRUE;
+ }
+ else if (strcmp (element_name, "xmp-tag") == 0)
+ {
+ item_count = 0;
+ row_count = 0;
+ xmptag = TRUE;
+ }
+ else if (strcmp (element_name, "tag-value") == 0)
+ {
+ tagvalue = TRUE;
+ }
+ else if (strcmp (element_name, "tag-list-value") == 0)
+ {
+ taglistvalue = TRUE;
+ }
+ else if (strcmp (element_name, "tag-name") == 0)
+ {
+ tagname = TRUE;
+ }
+ else if (strcmp (element_name, "tag-mode") == 0)
+ {
+ tagmode = TRUE;
+ }
+ else if (strcmp (element_name, "list-element") == 0)
+ {
+ listelement = TRUE;
+ row_count += 1;
+ }
+ else if (strcmp (element_name, "element") == 0)
+ {
+ element = TRUE;
+ item_count += 1;
+ }
+}
+
+void
+xml_parser_data (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ if (tagvalue)
+ {
+ if (str_tag_value)
+ g_free(str_tag_value);
+
+ if (text)
+ str_tag_value = g_strdup(text);
+ else
+ str_tag_value = g_strconcat("", NULL);
+ }
+ else if (tagname)
+ {
+ if (str_tag_name)
+ g_free(str_tag_name);
+
+ if (text)
+ str_tag_name = g_strdup(text);
+ else
+ str_tag_name = g_strconcat("", NULL);
+ }
+ else if (tagmode)
+ {
+ if (str_tag_mode)
+ g_free(str_tag_mode);
+
+ if (text)
+ str_tag_mode = g_strdup(text);
+ else
+ str_tag_mode = g_strconcat("", NULL);
+ }
+ else if (element)
+ {
+ if (str_element)
+ g_free(str_element);
+
+ if (text)
+ str_element = g_strdup(text);
+ else
+ str_element = g_strconcat("", NULL);
+ }
+}
+
+void
+set_tag_ui (metadata_editor *args,
+ gint index,
+ gchar *name,
+ gchar *value,
+ gchar* mode)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (args->builder, str_tag_name));
+
+ if (!strcmp ("single", mode))
+ {
+ GtkEntry *entry_widget;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ entry_widget = GTK_ENTRY (widget);
+ gtk_entry_set_text (entry_widget, value_utf);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("multi", mode))
+ {
+ GtkTextView *text_view;
+ GtkTextBuffer *buffer;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ text_view = GTK_TEXT_VIEW (widget);
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_set_text (buffer, value_utf, -1);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("combo", mode))
+ {
+ gint32 value;
+ gchar *value_utf;
+
+ value_utf = g_locale_to_utf8 (str_tag_value, -1, NULL, NULL, NULL);
+ value = atoi(value_utf);
+ gtk_combo_box_set_active (GTK_COMBO_BOX(widget), value);
+ g_free (value_utf);
+ }
+ else if (!strcmp ("list", mode))
+ {
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ gint number_of_rows;
+ gint row;
+ gint item;
+
+ liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
+ treemodel = GTK_TREE_MODEL (liststore);
+ number_of_rows =
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);
+
+ /* Clear all current values */
+ for (row = number_of_rows; row > -1; row--)
+ {
+ if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
+ {
+ gtk_list_store_remove(liststore, &iter);
+ }
+ }
+ /* Add new values values */
+ if (!strcmp (LICENSOR_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LICENSOR_NAME, list_tag_data[row][1],
+ COL_LICENSOR_ID, list_tag_data[row][2],
+ COL_LICENSOR_PHONE1, list_tag_data[row][3],
+ COL_LICENSOR_PHONE_TYPE1, list_tag_data[row][4],
+ COL_LICENSOR_PHONE2, list_tag_data[row][5],
+ COL_LICENSOR_PHONE_TYPE2, list_tag_data[row][6],
+ COL_LICENSOR_EMAIL, list_tag_data[row][7],
+ COL_LICENSOR_WEB, list_tag_data[row][8],
+ -1);
+ for (item = 1; item < n_licensor + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LICENSOR_NAME, NULL,
+ COL_LICENSOR_ID, NULL,
+ COL_LICENSOR_PHONE1, NULL,
+ COL_LICENSOR_PHONE_TYPE1, NULL,
+ COL_LICENSOR_PHONE2, NULL,
+ COL_LICENSOR_PHONE_TYPE2, NULL,
+ COL_LICENSOR_EMAIL, NULL,
+ COL_LICENSOR_WEB, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (IMAGECREATOR_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, list_tag_data[row][1],
+ COL_IMG_CR8_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_imagecreator + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_IMG_CR8_NAME, NULL,
+ COL_IMG_CR8_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (ARTWORKOROBJECT_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_AOO_TITLE, list_tag_data[row][1],
+ COL_AOO_DATE_CREAT, list_tag_data[row][2],
+ COL_AOO_CREATOR, list_tag_data[row][3],
+ COL_AOO_SOURCE, list_tag_data[row][4],
+ COL_AOO_SRC_INV_ID, list_tag_data[row][5],
+ COL_AOO_CR_NOT, list_tag_data[row][6],
+ -1);
+ for (item = 1; item < n_artworkorobject + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_AOO_TITLE, NULL,
+ COL_AOO_DATE_CREAT, NULL,
+ COL_AOO_CREATOR, NULL,
+ COL_AOO_SOURCE, NULL,
+ COL_AOO_SRC_INV_ID, NULL,
+ COL_AOO_CR_NOT, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (REGISTRYID_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, list_tag_data[row][1],
+ COL_REGISTRY_ITEM_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_registryid + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_REGISTRY_ORG_ID, NULL,
+ COL_REGISTRY_ITEM_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (COPYRIGHTOWNER_HEADER, name))
+ {
+ if (row_count > 0)
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, list_tag_data[row][1],
+ COL_CR_OWNER_ID, list_tag_data[row][2],
+ -1);
+ for (item = 1; item < n_copyrightowner + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_CR_OWNER_NAME, NULL,
+ COL_CR_OWNER_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp (LOCATIONSHOWN_HEADER, name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, list_tag_data[row][1],
+ COL_LOC_SHO_CITY, list_tag_data[row][2],
+ COL_LOC_SHO_STATE_PROV, list_tag_data[row][3],
+ COL_LOC_SHO_CNTRY, list_tag_data[row][4],
+ COL_LOC_SHO_CNTRY_ISO, list_tag_data[row][5],
+ COL_LOC_SHO_CNTRY_WRLD_REG, list_tag_data[row][6],
+ -1);
+ for (item = 1; item < n_locationshown + 1; item++)
+ {
+ if (list_tag_data[row][item])
+ {
+ if (list_tag_data[row][item])
+ g_free(list_tag_data[row][item]);
+ }
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_LOC_SHO_SUB_LOC, NULL,
+ COL_LOC_SHO_CITY, NULL,
+ COL_LOC_SHO_STATE_PROV, NULL,
+ COL_LOC_SHO_CNTRY, NULL,
+ COL_LOC_SHO_CNTRY_ISO, NULL,
+ COL_LOC_SHO_CNTRY_WRLD_REG, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_NAME, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_NAME, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_CODE, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_ORG_IMG_CODE, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_PROP_REL_ID, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_PROP_REL_ID, NULL,
+ -1);
+ }
+ }
+ }
+ else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
+ {
+ for (row = 1; row < row_count+1; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_MOD_REL_ID, list_tag_data[row][1],
+ -1);
+ if (list_tag_data[row][1])
+ {
+ if (list_tag_data[row][1])
+ g_free(list_tag_data[row][1]);
+ }
+ }
+
+ if (row_count < 2)
+ {
+ for (row = 0; row < 2 - row_count; row++)
+ {
+ gtk_list_store_append (liststore, &iter);
+ gtk_list_store_set (liststore, &iter,
+ COL_MOD_REL_ID, NULL,
+ -1);
+ }
+ }
+ }
+ }
+}
+
+const gchar *
+get_tag_ui_text (metadata_editor *args,
+ gchar *name,
+ gchar *mode)
+{
+ GObject *object;
+
+ object = gtk_builder_get_object (args->builder, name);
+
+ if (! strcmp ("single", mode))
+ {
+ GtkEntry *entry = GTK_ENTRY (object);
+ return gtk_entry_get_text (entry);
+ }
+ else if (!strcmp ("multi", mode))
+ {
+ GtkTextView *text_view = GTK_TEXT_VIEW (object);
+ GtkTextBuffer *buffer;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ return gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+ }
+
+ return NULL;
+}
+
+static void
+get_list_elements (GString *xmldata, int element_count, gchar **rowtagdata)
+{
+ gint list_idx;
+
+ g_string_append (xmldata, "\t\t\t<list-element>\n");
+
+ for (list_idx = 0; list_idx < element_count; list_idx++)
+ {
+ g_string_append (xmldata, "\t\t\t\t<element>");
+
+ if (rowtagdata && rowtagdata[list_idx] && strlen(rowtagdata[list_idx]) > 0)
+ {
+ g_string_append (xmldata, rowtagdata[list_idx]);
+ }
+
+ g_string_append (xmldata, "</element>\n");
+ }
+ g_string_append (xmldata, "\t\t\t</list-element>\n");
+}
+
+gchar *
+get_tag_ui_list (metadata_editor *args, gchar *name, gchar *mode)
+{
+ GObject *object;
+ GtkWidget *widget;
+ GtkTreeModel *treemodel;
+ GtkListStore *liststore;
+ GtkTreeIter iter;
+ GString *xmldata;
+ gint number_of_rows;
+ gint row;
+ gint has_data;
+ gchar *tagdata[256][256];
+
+ has_data = FALSE;
+ xmldata = g_string_new ("");
+
+ object = gtk_builder_get_object (args->builder, name);
+ widget = GTK_WIDGET(object);
+
+ liststore = GTK_LIST_STORE(gtk_tree_view_get_model((GtkTreeView *)widget));
+ treemodel = GTK_TREE_MODEL (liststore);
+ number_of_rows =
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL);
+
+ for (row = 0; row < number_of_rows; row++)
+ {
+ if (gtk_tree_model_iter_nth_child(treemodel, &iter, NULL, row))
+ {
+ if (!strcmp (LICENSOR_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_LICENSOR_NAME, &tagdata[row][0],
+ COL_LICENSOR_ID, &tagdata[row][1],
+ COL_LICENSOR_PHONE1, &tagdata[row][2],
+ COL_LICENSOR_PHONE_TYPE1, &tagdata[row][3],
+ COL_LICENSOR_PHONE2, &tagdata[row][4],
+ COL_LICENSOR_PHONE_TYPE2, &tagdata[row][5],
+ COL_LICENSOR_EMAIL, &tagdata[row][6],
+ COL_LICENSOR_WEB, &tagdata[row][7],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0) ||
+ (tagdata[row][6] != NULL && strlen(tagdata[row][6]) > 0) ||
+ (tagdata[row][7] != NULL && strlen(tagdata[row][7]) > 0))
+ {
+
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 8, tagdata[row]);
+ }
+ }
+ else if (!strcmp (COPYRIGHTOWNER_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_CR_OWNER_NAME, &tagdata[row][0],
+ COL_CR_OWNER_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ g_string_append (xmldata, "\t\t\t<list-element>\n");
+ g_string_append (xmldata, "\t\t\t\t<element>");
+ g_string_append (xmldata, tagdata[row][0]);
+ g_string_append (xmldata, "</element>\n");
+ g_string_append (xmldata, "\t\t\t\t<element>");
+ g_string_append (xmldata, tagdata[row][1]);
+ g_string_append (xmldata, "</element>\n");
+ g_string_append (xmldata, "\t\t\t</list-element>\n");
+ }
+ }
+ else if (!strcmp (IMAGECREATOR_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_IMG_CR8_NAME, &tagdata[row][0],
+ COL_IMG_CR8_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 2, tagdata[row]);
+ }
+ }
+ else if (!strcmp (ARTWORKOROBJECT_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_AOO_TITLE, &tagdata[row][0],
+ COL_AOO_DATE_CREAT, &tagdata[row][1],
+ COL_AOO_CREATOR, &tagdata[row][2],
+ COL_AOO_SOURCE, &tagdata[row][3],
+ COL_AOO_SRC_INV_ID, &tagdata[row][4],
+ COL_AOO_CR_NOT, &tagdata[row][5],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 6, tagdata[row]);
+ }
+ }
+ else if (!strcmp (REGISTRYID_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_REGISTRY_ORG_ID, &tagdata[row][0],
+ COL_REGISTRY_ITEM_ID, &tagdata[row][1],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 2, tagdata[row]);
+ }
+ }
+ else if (!strcmp (LOCATIONSHOWN_HEADER, name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_LOC_SHO_SUB_LOC, &tagdata[row][0],
+ COL_LOC_SHO_CITY, &tagdata[row][1],
+ COL_LOC_SHO_STATE_PROV, &tagdata[row][2],
+ COL_LOC_SHO_CNTRY, &tagdata[row][3],
+ COL_LOC_SHO_CNTRY_ISO, &tagdata[row][4],
+ COL_LOC_SHO_CNTRY_WRLD_REG, &tagdata[row][5],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0) ||
+ (tagdata[row][1] != NULL && strlen(tagdata[row][1]) > 0) ||
+ (tagdata[row][2] != NULL && strlen(tagdata[row][2]) > 0) ||
+ (tagdata[row][3] != NULL && strlen(tagdata[row][3]) > 0) ||
+ (tagdata[row][4] != NULL && strlen(tagdata[row][4]) > 0) ||
+ (tagdata[row][5] != NULL && strlen(tagdata[row][5]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 6, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageName", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_ORG_IMG_NAME, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.iptcExt.OrganisationInImageCode", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_ORG_IMG_CODE, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.plus.PropertyReleaseID", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_PROP_REL_ID, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ else if (!strcmp ("Xmp.plus.ModelReleaseID", name))
+ {
+ gtk_tree_model_get (treemodel, &iter,
+ COL_MOD_REL_ID, &tagdata[row][0],
+ -1);
+
+ if ((tagdata[row][0] != NULL && strlen(tagdata[row][0]) > 0))
+ {
+ has_data = TRUE;
+
+ get_list_elements (xmldata, 1, tagdata[row]);
+ }
+ }
+ }
+ }
+
+ if (has_data == TRUE)
+ {
+ gchar *xml;
+
+ xml = g_strdup (xmldata->str);
+ g_string_free(xmldata, TRUE);
+ return xml;
+ }
+
+ g_string_free(xmldata, TRUE);
+
+ return NULL;
+}
+
+gint
+get_tag_ui_combo (metadata_editor *args, gchar *name, gchar *mode)
+{
+ GObject *object;
+ GtkComboBoxText *combo;
+ gint value;
+
+ object = gtk_builder_get_object (args->builder, name);
+
+ combo = GTK_COMBO_BOX_TEXT (object);
+ value = gtk_combo_box_get_active (GTK_COMBO_BOX(combo));
+
+ return value;
+}
+
+void
+xml_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ metadata_editor *args;
+ int i;
+
+ args = user_data;
+
+ if (strcmp (element_name, "gimp-metadata") == 0)
+ {
+ gimpmetadata = FALSE;
+ }
+ else if (strcmp (element_name, "iptc-tag") == 0)
+ {
+ iptctag = FALSE;
+#ifdef _ENABLE_IPTC_TAG_
+ if (str_tag_name && str_tag_value)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_equivalent_metadata_tags; i++)
+ {
+ if (strcmp(equivalent_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+#ifdef _SET_IPTC_TAG_
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ equivalent_metadata_tags[i].mode);
+#endif
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ else if (strcmp (element_name, "xmp-tag") == 0)
+ {
+ xmptag = FALSE;
+ if (strcmp (str_tag_mode, "list") != 0)
+ {
+ if (str_tag_name && str_tag_value)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ default_metadata_tags[i].mode);
+#ifdef _ENABLE_FORCE_WRITE_
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+#endif
+ break;
+ }
+ }
+ }
+
+ }
+ else if (strcmp (str_tag_mode, "list") == 0)
+ {
+ if (row_count > 0)
+ {
+ /* make sure to only allow supported tags */
+ for (i = 0; i < n_default_metadata_tags; i++)
+ {
+ if (strcmp(default_metadata_tags[i].tag, str_tag_name) == 0)
+ {
+ set_tag_ui (args, i, str_tag_name, str_tag_value,
+ default_metadata_tags[i].mode);
+#ifdef _ENABLE_FORCE_WRITE_
+ if (force_write == TRUE)
+ gexiv2_metadata_set_tag_string (args->metadata,
+ str_tag_name,
+ str_tag_value);
+#endif
+ break;
+ }
+ }
+ }
+ row_count = 0;
+ item_count = 0;
+ }
+ }
+ else if (strcmp (element_name, "tag-value") == 0)
+ {
+ tagvalue = FALSE;
+ }
+ else if (strcmp (element_name, "tag-list-value") == 0)
+ {
+ taglistvalue = FALSE;
+ }
+ else if (strcmp (element_name, "tag-name") == 0)
+ {
+ tagname = FALSE;
+ }
+ else if (strcmp (element_name, "tag-mode") == 0)
+ {
+ tagmode = FALSE;
+ }
+ else if (strcmp (element_name, "list-element") == 0)
+ {
+ listelement = FALSE;
+ item_count = 0;
+ }
+ else if (strcmp (element_name, "element") == 0)
+ {
+ element = FALSE;
+ list_tag_data[row_count][item_count] = g_strdup(str_element);
+ }
+}
+
+gboolean
+xml_parser_parse_file (GimpXmlParser *parser,
+ const gchar *filename,
+ GError **error)
+{
+ GIOChannel *io;
+ gboolean success;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ io = g_io_channel_new_file (filename, "r", error);
+ if (!io)
+ return FALSE;
+
+ success = xml_parser_parse_io_channel (parser, io, error);
+
+ g_io_channel_unref (io);
+
+ return success;
+}
+
+GimpXmlParser *
+xml_parser_new (const GMarkupParser *markup_parser,
+ gpointer user_data)
+{
+ GimpXmlParser *parser;
+
+ g_return_val_if_fail (markup_parser != NULL, NULL);
+
+ parser = g_slice_new (GimpXmlParser);
+
+ parser->context = g_markup_parse_context_new (markup_parser,
+ 0, user_data, NULL);
+
+ return parser;
+}
+
+void
+xml_parser_free (GimpXmlParser *parser)
+{
+ g_return_if_fail (parser != NULL);
+
+ g_markup_parse_context_free (parser->context);
+ g_slice_free (GimpXmlParser, parser);
+}
+
+gboolean
+parse_encoding (const gchar *text,
+ gint text_len,
+ gchar **encoding)
+{
+ const gchar *start;
+ const gchar *end;
+ gint i;
+
+ g_return_val_if_fail (text, FALSE);
+
+ if (text_len < 20)
+ return FALSE;
+
+ start = g_strstr_len (text, text_len, "<?xml");
+ if (!start)
+ return FALSE;
+
+ end = g_strstr_len (start, text_len - (start - text), "?>");
+ if (!end)
+ return FALSE;
+
+ *encoding = NULL;
+
+ text_len = end - start;
+ if (text_len < 12)
+ return TRUE;
+
+ start = g_strstr_len (start + 1, text_len - 1, "encoding");
+ if (!start)
+ return TRUE;
+
+ start += 8;
+
+ while (start < end && *start == ' ')
+ start++;
+
+ if (*start != '=')
+ return TRUE;
+
+ start++;
+
+ while (start < end && *start == ' ')
+ start++;
+
+ if (*start != '\"' && *start != '\'')
+ return TRUE;
+
+ text_len = end - start;
+ if (text_len < 1)
+ return TRUE;
+
+ for (i = 1; i < text_len; i++)
+ if (start[i] == start[0])
+ break;
+
+ if (i == text_len || i < 3)
+ return TRUE;
+
+ *encoding = g_strndup (start + 1, i - 1);
+
+ return TRUE;
+}
+
+gboolean
+xml_parser_parse_io_channel (GimpXmlParser *parser,
+ GIOChannel *io,
+ GError **error)
+{
+ GIOStatus status;
+ gchar buffer[4096];
+ gsize len = 0;
+ gsize bytes;
+ const gchar *io_encoding;
+ gchar *encoding = NULL;
+
+ g_return_val_if_fail (parser != NULL, FALSE);
+ g_return_val_if_fail (io != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ io_encoding = g_io_channel_get_encoding (io);
+ if (g_strcmp0 (io_encoding, "UTF-8"))
+ {
+ g_warning ("xml_parser_parse_io_channel():\n"
+ "The encoding has already been set on this GIOChannel!");
+ return FALSE;
+ }
+
+ /* try to determine the encoding */
+
+ g_io_channel_set_encoding (io, NULL, NULL);
+
+ while (len < sizeof (buffer))
+ {
+ status = g_io_channel_read_chars (io, buffer + len, 1, &bytes, error);
+ len += bytes;
+
+ if (status == G_IO_STATUS_ERROR)
+ return FALSE;
+ if (status == G_IO_STATUS_EOF)
+ break;
+
+ if (parse_encoding (buffer, len, &encoding))
+ break;
+ }
+
+ if (encoding)
+ {
+ if (! g_io_channel_set_encoding (io, encoding, error))
+ return FALSE;
+
+ if (encoding)
+ g_free (encoding);
+ }
+ else
+ {
+ g_io_channel_set_encoding (io, "UTF-8", NULL);
+ }
+
+ while (TRUE)
+ {
+ if (!g_markup_parse_context_parse (parser->context, buffer, len, error))
+ return FALSE;
+
+ status = g_io_channel_read_chars (io,
+ buffer, sizeof (buffer), &len, error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_ERROR:
+ return FALSE;
+ case G_IO_STATUS_EOF:
+ return g_markup_parse_context_end_parse (parser->context, error);
+ case G_IO_STATUS_NORMAL:
+ case G_IO_STATUS_AGAIN:
+ break;
+ }
+ }
+}
+
diff --git a/plug-ins/metadata/metadata-xml.h b/plug-ins/metadata/metadata-xml.h
new file mode 100644
index 0000000..12ac77b
--- /dev/null
+++ b/plug-ins/metadata/metadata-xml.h
@@ -0,0 +1,98 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Copyright (C) 2016, 2017 Ben Touchette
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __METADATA_XML_H__
+#define __METADATA_XML_H__
+
+#include "metadata-misc.h"
+
+struct _GimpXmlParser
+{
+ GMarkupParseContext *context;
+};
+
+typedef struct _GimpXmlParser GimpXmlParser;
+
+void
+xml_parser_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+
+void
+xml_parser_data (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+void
+set_tag_ui (metadata_editor *args,
+ int index,
+ gchar *name,
+ gchar *value,
+ gchar *mode);
+
+const gchar *
+get_tag_ui_text (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+gchar *
+get_tag_ui_list (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+gint
+get_tag_ui_combo (metadata_editor *args,
+ gchar *name,
+ gchar *mode);
+
+void
+xml_parser_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+
+gboolean
+xml_parser_parse_file (GimpXmlParser *parser,
+ const gchar *filename,
+ GError **error);
+
+void
+xml_parser_free (GimpXmlParser *parser);
+
+gboolean
+parse_encoding (const gchar *text,
+ gint text_len,
+ gchar **encoding);
+
+gboolean
+xml_parser_parse_io_channel (GimpXmlParser *parser,
+ GIOChannel *io,
+ GError **error);
+
+GimpXmlParser *
+xml_parser_new (const GMarkupParser *markup_parser,
+ gpointer user_data);
+
+#endif /* __METADATA_XML_H__ */
+
diff --git a/plug-ins/pagecurl/Makefile.am b/plug-ins/pagecurl/Makefile.am
new file mode 100644
index 0000000..4640fe0
--- /dev/null
+++ b/plug-ins/pagecurl/Makefile.am
@@ -0,0 +1,82 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+pagecurl_RC = pagecurl.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/pagecurl
+
+libexec_PROGRAMS = pagecurl
+
+pagecurl_SOURCES = pagecurl.c
+
+BUILT_SOURCES = pagecurl-icons.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(pagecurl_RC)
+
+
+PAGECURL_IMAGES = \
+ curl0.png \
+ curl1.png \
+ curl2.png \
+ curl3.png \
+ curl4.png \
+ curl5.png \
+ curl6.png \
+ curl7.png
+
+EXTRA_DIST = $(PAGECURL_IMAGES)
+
+CLEANFILES = $(BUILT_SOURCES) pagecurl-icons.gresource.xml
+
+pagecurl-icons.gresource.xml: $(PAGECURL_IMAGES) Makefile.am
+ $(AM_V_GEN) ( rm -f $@; \
+ echo '<?xml version="1.0" encoding="UTF-8"?>' > $@; \
+ echo '<gresources>' >> $@; \
+ echo ' <gresource prefix="/org/gimp/pagecurl-icons">' >> $@; \
+ for image in $(PAGECURL_IMAGES); do \
+ echo " <file preprocess=\"to-pixdata\">$$image</file>" >> $@; \
+ done; \
+ echo ' </gresource>' >> $@; \
+ echo '</gresources>' >> $@ )
+
+pagecurl-icons.c: pagecurl-icons.gresource.xml
+ $(AM_V_GEN) $(HOST_GLIB_COMPILE_RESOURCES) \
+ --sourcedir=$(srcdir) --generate-source \
+ --target=$@ pagecurl-icons.gresource.xml
diff --git a/plug-ins/pagecurl/Makefile.in b/plug-ins/pagecurl/Makefile.in
new file mode 100644
index 0000000..ff7ffee
--- /dev/null
+++ b/plug-ins/pagecurl/Makefile.in
@@ -0,0 +1,1035 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = pagecurl$(EXEEXT)
+subdir = plug-ins/pagecurl
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_pagecurl_OBJECTS = pagecurl.$(OBJEXT)
+pagecurl_OBJECTS = $(am_pagecurl_OBJECTS)
+pagecurl_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+pagecurl_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimp) $(libgimpcolor) \
+ $(libgimpmath) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(pagecurl_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/pagecurl.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(pagecurl_SOURCES)
+DIST_SOURCES = $(pagecurl_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/pagecurl
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@pagecurl_RC = pagecurl.rc.o
+AM_LDFLAGS = $(mwindows)
+pagecurl_SOURCES = pagecurl.c
+BUILT_SOURCES = pagecurl-icons.c
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(pagecurl_RC)
+
+PAGECURL_IMAGES = \
+ curl0.png \
+ curl1.png \
+ curl2.png \
+ curl3.png \
+ curl4.png \
+ curl5.png \
+ curl6.png \
+ curl7.png
+
+EXTRA_DIST = $(PAGECURL_IMAGES)
+CLEANFILES = $(BUILT_SOURCES) pagecurl-icons.gresource.xml
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/pagecurl/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/pagecurl/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+pagecurl$(EXEEXT): $(pagecurl_OBJECTS) $(pagecurl_DEPENDENCIES) $(EXTRA_pagecurl_DEPENDENCIES)
+ @rm -f pagecurl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(pagecurl_OBJECTS) $(pagecurl_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pagecurl.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/pagecurl.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/pagecurl.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: all check install install-am install-exec install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+pagecurl-icons.gresource.xml: $(PAGECURL_IMAGES) Makefile.am
+ $(AM_V_GEN) ( rm -f $@; \
+ echo '<?xml version="1.0" encoding="UTF-8"?>' > $@; \
+ echo '<gresources>' >> $@; \
+ echo ' <gresource prefix="/org/gimp/pagecurl-icons">' >> $@; \
+ for image in $(PAGECURL_IMAGES); do \
+ echo " <file preprocess=\"to-pixdata\">$$image</file>" >> $@; \
+ done; \
+ echo ' </gresource>' >> $@; \
+ echo '</gresources>' >> $@ )
+
+pagecurl-icons.c: pagecurl-icons.gresource.xml
+ $(AM_V_GEN) $(HOST_GLIB_COMPILE_RESOURCES) \
+ --sourcedir=$(srcdir) --generate-source \
+ --target=$@ pagecurl-icons.gresource.xml
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/pagecurl/curl0.png b/plug-ins/pagecurl/curl0.png
new file mode 100644
index 0000000..f136c61
--- /dev/null
+++ b/plug-ins/pagecurl/curl0.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl1.png b/plug-ins/pagecurl/curl1.png
new file mode 100644
index 0000000..d54f7e6
--- /dev/null
+++ b/plug-ins/pagecurl/curl1.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl2.png b/plug-ins/pagecurl/curl2.png
new file mode 100644
index 0000000..0553acb
--- /dev/null
+++ b/plug-ins/pagecurl/curl2.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl3.png b/plug-ins/pagecurl/curl3.png
new file mode 100644
index 0000000..f4d94a7
--- /dev/null
+++ b/plug-ins/pagecurl/curl3.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl4.png b/plug-ins/pagecurl/curl4.png
new file mode 100644
index 0000000..0a5413c
--- /dev/null
+++ b/plug-ins/pagecurl/curl4.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl5.png b/plug-ins/pagecurl/curl5.png
new file mode 100644
index 0000000..30d3232
--- /dev/null
+++ b/plug-ins/pagecurl/curl5.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl6.png b/plug-ins/pagecurl/curl6.png
new file mode 100644
index 0000000..bed61cb
--- /dev/null
+++ b/plug-ins/pagecurl/curl6.png
Binary files differ
diff --git a/plug-ins/pagecurl/curl7.png b/plug-ins/pagecurl/curl7.png
new file mode 100644
index 0000000..12dae05
--- /dev/null
+++ b/plug-ins/pagecurl/curl7.png
Binary files differ
diff --git a/plug-ins/pagecurl/pagecurl.c b/plug-ins/pagecurl/pagecurl.c
new file mode 100644
index 0000000..c82dc8a
--- /dev/null
+++ b/plug-ins/pagecurl/pagecurl.c
@@ -0,0 +1,1028 @@
+/* Page Curl 0.9 --- image filter plug-in for GIMP
+ * Copyright (C) 1996 Federico Mena Quintero
+ * Ported to Gimp 1.0 1998 by Simon Budig <Simon.Budig@unix-ag.org>
+ *
+ * You can contact me at quartic@polloux.fciencias.unam.mx
+ * You can contact the original GIMP authors at gimp@xcf.berkeley.edu
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * Ported to the 0.99.x architecture by Simon Budig, Simon.Budig@unix-ag.org
+ */
+
+/*
+ * Version History
+ * 0.5: (1996) Version for Gimp 0.54 by Federico Mena Quintero
+ * 0.6: (Feb '98) First Version for Gimp 0.99.x, very buggy.
+ * 0.8: (Mar '98) First "stable" version
+ * 0.9: (May '98)
+ * - Added support for Gradients. It is now possible to map
+ * a gradient to the back of the curl.
+ * - This implies a changed PDB-Interface: New "mode" parameter.
+ * - Pagecurl now returns the ID of the new layer.
+ * - Exchanged the meaning of FG/BG Color, because mostly the FG
+ * color is darker.
+ * 1.0: (July '04)
+ * - Code cleanup, added reverse gradient option.
+ */
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "pagecurl-icons.c"
+
+
+#define PLUG_IN_PROC "plug-in-pagecurl"
+#define PLUG_IN_BINARY "pagecurl"
+#define PLUG_IN_ROLE "gimp-pagecurl"
+#define PLUG_IN_VERSION "July 2004, 1.0"
+#define NGRADSAMPLES 256
+
+
+typedef enum
+{
+ CURL_COLORS_FG_BG,
+ CURL_COLORS_GRADIENT,
+ CURL_COLORS_GRADIENT_REVERSE,
+ CURL_COLORS_LAST = CURL_COLORS_GRADIENT_REVERSE
+} CurlColors;
+
+typedef enum
+{
+ CURL_ORIENTATION_VERTICAL,
+ CURL_ORIENTATION_HORIZONTAL,
+ CURL_ORIENTATION_LAST = CURL_ORIENTATION_HORIZONTAL
+} CurlOrientation;
+
+typedef enum
+{
+ CURL_EDGE_LOWER_RIGHT = 1,
+ CURL_EDGE_LOWER_LEFT = 2,
+ CURL_EDGE_UPPER_LEFT = 3,
+ CURL_EDGE_UPPER_RIGHT = 4,
+ CURL_EDGE_FIRST = CURL_EDGE_LOWER_RIGHT,
+ CURL_EDGE_LAST = CURL_EDGE_UPPER_RIGHT
+} CurlEdge;
+
+
+#define CURL_EDGE_LEFT(e) ((e) == CURL_EDGE_LOWER_LEFT || \
+ (e) == CURL_EDGE_UPPER_LEFT)
+#define CURL_EDGE_RIGHT(e) ((e) == CURL_EDGE_LOWER_RIGHT || \
+ (e) == CURL_EDGE_UPPER_RIGHT)
+#define CURL_EDGE_LOWER(e) ((e) == CURL_EDGE_LOWER_RIGHT || \
+ (e) == CURL_EDGE_LOWER_LEFT)
+#define CURL_EDGE_UPPER(e) ((e) == CURL_EDGE_UPPER_LEFT || \
+ (e) == CURL_EDGE_UPPER_RIGHT)
+
+
+/***** Types *****/
+
+typedef struct
+{
+ CurlColors colors;
+ gdouble opacity;
+ gboolean shade;
+ CurlEdge edge;
+ CurlOrientation orientation;
+} CurlParams;
+
+
+/***** Prototypes *****/
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static void set_default_params (void);
+
+static void dialog_scale_update (GtkAdjustment *adjustment,
+ gdouble *value);
+
+static gboolean dialog (void);
+
+static void init_calculation (gint32 drawable_id);
+static gint32 do_curl_effect (gint32 drawable_id);
+static void clear_curled_region (gint32 drawable_id);
+static gint32 page_curl (gint32 drawable_id);
+static GimpRGB * get_gradient_samples (gint32 drawable_id,
+ gboolean reverse);
+
+
+/***** Variables *****/
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+static CurlParams curl;
+
+/* Image parameters */
+
+static gint32 image_id;
+
+static GtkWidget *curl_image = NULL;
+
+static gint sel_x, sel_y;
+static gint true_sel_width, true_sel_height;
+static gint sel_width, sel_height;
+static gint drawable_position;
+
+/* Center and radius of circle */
+
+static GimpVector2 center;
+static double radius;
+
+/* Useful points to keep around */
+
+static GimpVector2 left_tangent;
+static GimpVector2 right_tangent;
+
+/* Slopes --- these are *not* in the usual geometric sense! */
+
+static gdouble diagl_slope;
+static gdouble diagr_slope;
+static gdouble diagb_slope;
+static gdouble diagm_slope;
+
+/* User-configured parameters */
+
+static GimpRGB fg_color;
+static GimpRGB bg_color;
+
+
+/***** Functions *****/
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
+ { GIMP_PDB_INT32, "colors", "FG- and BG-Color (0), Current gradient (1), Current gradient reversed (2)" },
+ { GIMP_PDB_INT32, "edge",
+ "Edge to curl (1-4, clockwise, starting in the lower right edge)" },
+ { GIMP_PDB_INT32, "orientation", "Vertical (0), Horizontal (1)" },
+ { GIMP_PDB_INT32, "shade",
+ "Shade the region under the curl (1) or not (0)" },
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_LAYER, "curl-layer", "The new layer with the curl." }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Curl up one of the image corners"),
+ "This plug-in creates a pagecurl-effect.",
+ "Federico Mena Quintero and Simon Budig",
+ "Federico Mena Quintero and Simon Budig",
+ PLUG_IN_VERSION,
+ N_("_Pagecurl..."),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Distorts");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 drawable_id;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ set_default_params ();
+
+ /* Possibly retrieve data */
+ gimp_get_data (PLUG_IN_PROC, &curl);
+
+ *nreturn_vals = 2;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+ values[1].type = GIMP_PDB_LAYER;
+ values[1].data.d_layer = -1;
+
+ /* Get the specified drawable */
+ drawable_id = param[2].data.d_drawable;
+ image_id = param[1].data.d_image;
+
+ if ((gimp_drawable_is_rgb (drawable_id) ||
+ gimp_drawable_is_gray (drawable_id)) &&
+ gimp_drawable_mask_intersect (drawable_id, &sel_x, &sel_y,
+ &true_sel_width, &true_sel_height))
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* First acquire information with a dialog */
+ if (! dialog ())
+ return;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams != 7)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ curl.colors = CLAMP (param[3].data.d_int32,
+ 0, CURL_COLORS_LAST);
+ curl.edge = CLAMP (param[4].data.d_int32,
+ CURL_EDGE_FIRST, CURL_EDGE_LAST);
+ curl.orientation = CLAMP (param[5].data.d_int32,
+ 0, CURL_ORIENTATION_LAST);
+ curl.shade = param[6].data.d_int32 ? TRUE : FALSE;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ values[1].data.d_layer = page_curl (drawable_id);
+
+ if (run_mode != GIMP_RUN_NONINTERACTIVE)
+ gimp_displays_flush ();
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_set_data (PLUG_IN_PROC, &curl, sizeof (CurlParams));
+ }
+ }
+ else
+ /* Sorry - no indexed/noalpha images */
+ status = GIMP_PDB_EXECUTION_ERROR;
+
+ values[0].data.d_status = status;
+}
+
+/*****************************************************
+ * Functions to locate the current point in the curl.
+ * The following functions assume an curl in the
+ * lower right corner.
+ * diagb crosses the two tangential points from the
+ * circle with diagl and diagr.
+ *
+ * +------------------------------------------------+
+ * | __-- /|
+ * | __-- _/ |
+ * | __-- / |
+ * | __-- _/ |
+ * | __-- / |
+ * | __--____ _/ |
+ * | __----~~ \ _/ |
+ * | __-- | _/ |
+ * | diagl __-- _/| _/diagr |
+ * | __-- diagm_/ |/ |
+ * | __-- /___/ |
+ * +-------------------------+----------------------+
+ */
+
+static inline gboolean
+left_of_diagl (gdouble x,
+ gdouble y)
+{
+ return (x < (sel_width + (y - sel_height) * diagl_slope));
+}
+
+static inline gboolean
+right_of_diagr (gdouble x,
+ gdouble y)
+{
+ return (x > (sel_width + (y - sel_height) * diagr_slope));
+}
+
+static inline gboolean
+below_diagb (gdouble x,
+ gdouble y)
+{
+ return (y < (right_tangent.y + (x - right_tangent.x) * diagb_slope));
+}
+
+static inline gboolean
+right_of_diagm (gdouble x,
+ gdouble y)
+{
+ return (x > (sel_width + (y - sel_height) * diagm_slope));
+}
+
+static inline gboolean
+inside_circle (gdouble x,
+ gdouble y)
+{
+ x -= center.x;
+ y -= center.y;
+
+ return x * x + y * y <= radius * radius;
+}
+
+static void
+set_default_params (void)
+{
+ curl.colors = CURL_COLORS_FG_BG;
+ curl.opacity = 1.0;
+ curl.shade = TRUE;
+ curl.edge = CURL_EDGE_LOWER_RIGHT;
+ curl.orientation = CURL_ORIENTATION_VERTICAL;
+}
+
+/********************************************************************/
+/* dialog callbacks */
+/********************************************************************/
+
+static void
+dialog_scale_update (GtkAdjustment *adjustment,
+ gdouble *value)
+{
+ *value = ((gdouble) gtk_adjustment_get_value (adjustment)) / 100.0;
+}
+
+static void
+curl_pixbuf_update (void)
+{
+ GdkPixbuf *pixbuf;
+ gint index;
+ gchar *resource;
+
+ switch (curl.edge)
+ {
+ case CURL_EDGE_LOWER_RIGHT: index = 0; break;
+ case CURL_EDGE_LOWER_LEFT: index = 1; break;
+ case CURL_EDGE_UPPER_RIGHT: index = 2; break;
+ case CURL_EDGE_UPPER_LEFT: index = 3; break;
+ default:
+ return;
+ }
+
+ index += curl.orientation * 4;
+
+ resource = g_strdup_printf ("/org/gimp/pagecurl-icons/curl%c.png",
+ '0' + index);
+ pixbuf = gdk_pixbuf_new_from_resource (resource, NULL);
+ g_free (resource);
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (curl_image), pixbuf);
+ g_object_unref (pixbuf);
+}
+
+static gboolean
+dialog (void)
+{
+ /* Missing options: Color-dialogs? / own curl layer ? / transparency
+ to original drawable / Warp-curl (unsupported yet) */
+
+ GtkWidget *dialog;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *combo;
+ GtkObject *adjustment;
+ gboolean run;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Pagecurl Effect"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new (_("Curl Location"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ table = gtk_table_new (3, 2, TRUE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+
+ curl_image = gtk_image_new ();
+
+ gtk_table_attach (GTK_TABLE (table), curl_image, 0, 2, 1, 2,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (curl_image);
+
+ curl_pixbuf_update ();
+
+ {
+ static const gchar *name[] =
+ {
+ N_("Lower right"),
+ N_("Lower left"),
+ N_("Upper left"),
+ N_("Upper right")
+ };
+ gint i;
+
+ button = NULL;
+ for (i = CURL_EDGE_FIRST; i <= CURL_EDGE_LAST; i++)
+ {
+ button =
+ gtk_radio_button_new_with_label (button == NULL ?
+ NULL :
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)),
+ gettext (name[i - CURL_EDGE_FIRST]));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ curl.edge == i);
+
+ g_object_set_data (G_OBJECT (button),
+ "gimp-item-data", GINT_TO_POINTER (i));
+
+ gtk_table_attach (GTK_TABLE (table), button,
+ CURL_EDGE_LEFT (i) ? 0 : 1,
+ CURL_EDGE_LEFT (i) ? 1 : 2,
+ CURL_EDGE_UPPER (i) ? 0 : 2,
+ CURL_EDGE_UPPER (i) ? 1 : 3,
+ GTK_FILL | GTK_EXPAND, GTK_SHRINK, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &curl.edge);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (curl_pixbuf_update),
+ NULL);
+ }
+ }
+
+ gtk_widget_show (table);
+ gtk_widget_show (frame);
+
+ frame = gimp_frame_new (_("Curl Orientation"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ {
+ static const gchar *name[] =
+ {
+ N_("_Vertical"),
+ N_("_Horizontal")
+ };
+ gint i;
+
+ button = NULL;
+ for (i = 0; i <= CURL_ORIENTATION_LAST; i++)
+ {
+ button = gtk_radio_button_new_with_mnemonic (button == NULL ?
+ NULL :
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (button)),
+ gettext (name[i]));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ curl.orientation == i);
+
+ g_object_set_data (G_OBJECT (button),
+ "gimp-item-data", GINT_TO_POINTER (i));
+
+ gtk_box_pack_end (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_radio_button_update),
+ &curl.orientation);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (curl_pixbuf_update),
+ NULL);
+ }
+ }
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (frame);
+
+ button = gtk_check_button_new_with_mnemonic (_("_Shade under curl"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), curl.shade);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &curl.shade);
+
+ combo = g_object_new (GIMP_TYPE_INT_COMBO_BOX, NULL);
+
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, CURL_COLORS_GRADIENT_REVERSE,
+ GIMP_INT_STORE_LABEL, _("Current gradient (reversed)"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, CURL_COLORS_GRADIENT,
+ GIMP_INT_STORE_LABEL, _("Current gradient"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_GRADIENT,
+ -1);
+ gimp_int_combo_box_prepend (GIMP_INT_COMBO_BOX (combo),
+ GIMP_INT_STORE_VALUE, CURL_COLORS_FG_BG,
+ GIMP_INT_STORE_LABEL, _("Foreground / background colors"),
+ GIMP_INT_STORE_ICON_NAME, GIMP_ICON_COLORS_DEFAULT,
+ -1);
+
+ gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0);
+ gtk_widget_show (combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ curl.colors,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &curl.colors);
+
+ gtk_widget_show (dialog);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ adjustment = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Opacity:"), 100, 0,
+ curl.opacity * 100.0, 0.0, 100.0,
+ 1.0, 1.0, 0.0,
+ TRUE, 0, 0,
+ NULL, NULL);
+ g_signal_connect (adjustment, "value-changed",
+ G_CALLBACK (dialog_scale_update),
+ &curl.opacity);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+init_calculation (gint32 drawable_id)
+{
+ gdouble k;
+ gdouble alpha, beta;
+ gdouble angle;
+ GimpVector2 v1, v2;
+ gint32 *image_layers;
+ gint32 nlayers;
+
+ gimp_layer_add_alpha (drawable_id);
+
+ /* Image parameters */
+
+ /* Determine Position of original Layer in the Layer stack. */
+
+ image_layers = gimp_image_get_layers (image_id, &nlayers);
+ drawable_position = 0;
+ while (drawable_position < nlayers &&
+ image_layers[drawable_position] != drawable_id)
+ drawable_position++;
+
+ switch (curl.orientation)
+ {
+ case CURL_ORIENTATION_VERTICAL:
+ sel_width = true_sel_width;
+ sel_height = true_sel_height;
+ break;
+
+ case CURL_ORIENTATION_HORIZONTAL:
+ sel_width = true_sel_height;
+ sel_height = true_sel_width;
+ break;
+ }
+
+ /* Circle parameters */
+
+ alpha = atan ((double) sel_height / sel_width);
+ beta = alpha / 2.0;
+ k = sel_width / ((G_PI + alpha) * sin (beta) + cos (beta));
+ gimp_vector2_set (&center, k * cos (beta), k * sin (beta));
+ radius = center.y;
+
+ /* left_tangent */
+
+ gimp_vector2_set (&left_tangent, radius * -sin (alpha), radius * cos (alpha));
+ gimp_vector2_add (&left_tangent, &left_tangent, &center);
+
+ /* right_tangent */
+
+ gimp_vector2_sub (&v1, &left_tangent, &center);
+ gimp_vector2_set (&v2, sel_width - center.x, sel_height - center.y);
+ angle = -2.0 * acos (gimp_vector2_inner_product (&v1, &v2) /
+ (gimp_vector2_length (&v1) *
+ gimp_vector2_length (&v2)));
+ gimp_vector2_set (&right_tangent,
+ v1.x * cos (angle) + v1.y * -sin (angle),
+ v1.x * sin (angle) + v1.y * cos (angle));
+ gimp_vector2_add (&right_tangent, &right_tangent, &center);
+
+ /* Slopes */
+
+ diagl_slope = (double) sel_width / sel_height;
+ diagr_slope = (sel_width - right_tangent.x) / (sel_height - right_tangent.y);
+ diagb_slope = ((right_tangent.y - left_tangent.y) /
+ (right_tangent.x - left_tangent.x));
+ diagm_slope = (sel_width - center.x) / sel_height;
+
+ /* Colors */
+
+ gimp_context_get_foreground (&fg_color);
+ gimp_context_get_background (&bg_color);
+}
+
+static gint32
+do_curl_effect (gint32 drawable_id)
+{
+ gint x = 0;
+ gint y = 0;
+ gboolean color_image;
+ gint x1, y1;
+ guint progress, max_progress;
+ gdouble intensity, alpha;
+ GimpVector2 v, dl, dr;
+ gdouble dl_mag, dr_mag, angle, factor;
+ GeglBuffer *curl_buffer;
+ gint32 curl_layer_id;
+ GimpRGB *grad_samples = NULL;
+ gint width, height, n_ch;
+ GeglRectangle *roi;
+ GeglBufferIterator *iter;
+ const Babl *format;
+
+ color_image = gimp_drawable_is_rgb (drawable_id);
+
+ curl_layer_id = gimp_layer_new (image_id,
+ _("Curl Layer"),
+ true_sel_width,
+ true_sel_height,
+ color_image ?
+ GIMP_RGBA_IMAGE : GIMP_GRAYA_IMAGE,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_id));
+
+ gimp_image_insert_layer (image_id, curl_layer_id,
+ gimp_item_get_parent (drawable_id),
+ drawable_position);
+ gimp_drawable_fill (curl_layer_id, GIMP_FILL_TRANSPARENT);
+
+ gimp_drawable_offsets (drawable_id, &x1, &y1);
+ gimp_layer_set_offsets (curl_layer_id, sel_x + x1, sel_y + y1);
+
+ curl_buffer = gimp_drawable_get_shadow_buffer (curl_layer_id);
+
+ width = gegl_buffer_get_width (curl_buffer);
+ height = gegl_buffer_get_height (curl_buffer);
+ format = babl_format ("R'G'B'A float");
+ n_ch = babl_format_get_n_components (format);
+
+ iter = gegl_buffer_iterator_new (curl_buffer,
+ GEGL_RECTANGLE (0, 0, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+
+ /* Init shade_under */
+ gimp_vector2_set (&dl, -sel_width, -sel_height);
+ dl_mag = gimp_vector2_length (&dl);
+ gimp_vector2_set (&dr,
+ -(sel_width - right_tangent.x),
+ -(sel_height - right_tangent.y));
+ dr_mag = gimp_vector2_length (&dr);
+ alpha = acos (gimp_vector2_inner_product (&dl, &dr) / (dl_mag * dr_mag));
+
+ /* Gradient Samples */
+ switch (curl.colors)
+ {
+ case CURL_COLORS_FG_BG:
+ break;
+ case CURL_COLORS_GRADIENT:
+ grad_samples = get_gradient_samples (curl_layer_id, FALSE);
+ break;
+ case CURL_COLORS_GRADIENT_REVERSE:
+ grad_samples = get_gradient_samples (curl_layer_id, TRUE);
+ break;
+ }
+
+ max_progress = 2 * sel_width * sel_height;
+ progress = 0;
+
+ /* Main loop */
+ while (gegl_buffer_iterator_next (iter))
+ {
+ gfloat *dest;
+
+ roi = &iter->items[0].roi;
+ dest = (gfloat *) iter->items[0].data;
+
+ for (y1 = roi->y; y1 < roi->y + roi->height; y1++)
+ {
+ GimpRGB color;
+
+ for (x1 = roi->x; x1 < roi->x + roi->width; x1++)
+ {
+ /* Map coordinates to get the curl correct... */
+ switch (curl.orientation)
+ {
+ case CURL_ORIENTATION_VERTICAL:
+ x = CURL_EDGE_RIGHT (curl.edge) ? x1 : sel_width - 1 - x1;
+ y = CURL_EDGE_UPPER (curl.edge) ? y1 : sel_height - 1 - y1;
+ break;
+
+ case CURL_ORIENTATION_HORIZONTAL:
+ x = CURL_EDGE_LOWER (curl.edge) ? y1 : sel_width - 1 - y1;
+ y = CURL_EDGE_LEFT (curl.edge) ? x1 : sel_height - 1 - x1;
+ break;
+ }
+
+ if (left_of_diagl (x, y))
+ { /* uncurled region: transparent black */
+ gimp_rgba_set (&color, 0, 0, 0, 0);
+ }
+ else if (right_of_diagr (x, y) ||
+ (right_of_diagm (x, y) &&
+ below_diagb (x, y) &&
+ !inside_circle (x, y)))
+ {
+ /* curled region: transparent black */
+ gimp_rgba_set (&color, 0, 0, 0, 0);
+ }
+ else
+ {
+ v.x = -(sel_width - x);
+ v.y = -(sel_height - y);
+ angle = acos (gimp_vector2_inner_product (&v, &dl) /
+ (gimp_vector2_length (&v) * dl_mag));
+
+ if (inside_circle (x, y) || below_diagb (x, y))
+ {
+ /* Below the curl. */
+ factor = angle / alpha;
+ gimp_rgba_set (&color, 0, 0, 0, curl.shade ? factor : 0);
+ }
+ else
+ {
+ GimpRGB *gradrgb;
+
+ /* On the curl */
+ switch (curl.colors)
+ {
+ case CURL_COLORS_FG_BG:
+ intensity = pow (sin (G_PI * angle / alpha), 1.5);
+ gimp_rgba_set (&color,
+ intensity * bg_color.r + (1.0 - intensity) * fg_color.r,
+ intensity * bg_color.g + (1.0 - intensity) * fg_color.g,
+ intensity * bg_color.b + (1.0 - intensity) * fg_color.b,
+ (1.0 - intensity * (1.0 - curl.opacity)));
+ break;
+
+ case CURL_COLORS_GRADIENT:
+ case CURL_COLORS_GRADIENT_REVERSE:
+ /* Calculate position in Gradient */
+ intensity = (angle/alpha) + sin (G_PI*2 * angle/alpha) * 0.075;
+
+ gradrgb = grad_samples + ((guint) (intensity * NGRADSAMPLES));
+
+ /* Check boundaries */
+ intensity = CLAMP (intensity, 0.0, 1.0);
+ color = *gradrgb;
+ color.a = gradrgb->a * (1.0 - intensity * (1.0 - curl.opacity));
+ break;
+ }
+ }
+ }
+
+ dest[0] = color.r;
+ dest[1] = color.g;
+ dest[2] = color.b;
+ dest[3] = color.a;
+
+ dest += n_ch;
+ }
+ }
+ progress += roi->width * roi->height;
+ gimp_progress_update ((double) progress / (double) max_progress);
+ }
+
+ gimp_progress_update (0.5);
+ gegl_buffer_flush (curl_buffer);
+ gimp_drawable_merge_shadow (curl_layer_id, FALSE);
+ gimp_drawable_update (curl_layer_id, 0, 0, width, height);
+
+ g_free (grad_samples);
+
+ return curl_layer_id;
+}
+
+/************************************************/
+
+static void
+clear_curled_region (gint32 drawable_id)
+{
+ gint x = 0;
+ gint y = 0;
+ guint x1, y1;
+ gfloat *dest, *src;
+ guint progress, max_progress;
+ GeglBuffer *buf;
+ GeglBuffer *shadow_buf;
+ GeglRectangle *roi;
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint width, height, bpp, n_ch;
+ gint buf_index;
+
+ max_progress = 2 * sel_width * sel_height;
+ progress = max_progress / 2;
+
+ buf = gimp_drawable_get_buffer (drawable_id);
+ shadow_buf = gimp_drawable_get_shadow_buffer (drawable_id);
+ width = gegl_buffer_get_width (buf);
+ height = gegl_buffer_get_height (buf);
+
+ format = babl_format ("R'G'B'A float");
+ bpp = babl_format_get_bytes_per_pixel (format);
+ n_ch = babl_format_get_n_components (format);
+
+ iter = gegl_buffer_iterator_new (shadow_buf,
+ GEGL_RECTANGLE (0, 0, width, height), 0,
+ format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 2);
+ buf_index = gegl_buffer_iterator_add (iter, buf, NULL, 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ roi = &iter->items[0].roi;
+ dest = iter->items[0].data;
+ src = iter->items[buf_index].data;
+
+ memcpy (dest, src, roi->width * roi->height * bpp);
+
+ for (y1 = roi->y; y1 < roi->y + roi->height; y1++)
+ {
+ for (x1 = roi->x; x1 < roi->x + roi->width; x1++)
+ {
+ /* Map coordinates to get the curl correct... */
+ switch (curl.orientation)
+ {
+ case CURL_ORIENTATION_VERTICAL:
+ x = (CURL_EDGE_RIGHT (curl.edge) ?
+ x1 - sel_x : sel_width - 1 - (x1 - sel_x));
+ y = (CURL_EDGE_UPPER (curl.edge) ?
+ y1 - sel_y : sel_height - 1 - (y1 - sel_y));
+ break;
+
+ case CURL_ORIENTATION_HORIZONTAL:
+ x = (CURL_EDGE_LOWER (curl.edge) ?
+ y1 - sel_y : sel_width - 1 - (y1 - sel_y));
+ y = (CURL_EDGE_LEFT (curl.edge) ?
+ x1 - sel_x : sel_height - 1 - (x1 - sel_x));
+ break;
+ }
+
+ if (right_of_diagr (x, y) ||
+ (right_of_diagm (x, y) &&
+ below_diagb (x, y) &&
+ !inside_circle (x, y)))
+ {
+ /* Right of the curl: Alpha = 0 */
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = 0.0;
+ }
+
+ dest += n_ch;
+ src += n_ch;
+ }
+ }
+
+ progress += roi->width * roi->height;
+ gimp_progress_update ((double) progress / (double) max_progress);
+ }
+
+ gimp_progress_update (1.0);
+ gegl_buffer_flush (shadow_buf);
+ gimp_drawable_merge_shadow (drawable_id, TRUE);
+ gimp_drawable_update (drawable_id,
+ sel_x, sel_y,
+ true_sel_width, true_sel_height);
+}
+
+static gint32
+page_curl (gint32 drawable_id)
+{
+ gint curl_layer_id;
+
+ gimp_image_undo_group_start (image_id);
+
+ gimp_progress_init (_("Page Curl"));
+
+ init_calculation (drawable_id);
+
+ curl_layer_id = do_curl_effect (drawable_id);
+
+ clear_curled_region (drawable_id);
+
+ gimp_image_undo_group_end (image_id);
+
+ return curl_layer_id;
+}
+
+/*
+ Returns NGRADSAMPLES samples of active gradient.
+ Each sample has (gimp_drawable_bpp (drawable_id)) bytes.
+ "ripped" from gradmap.c.
+ */
+static GimpRGB *
+get_gradient_samples (gint32 drawable_id,
+ gboolean reverse)
+{
+ gchar *gradient_name;
+ gint n_d_samples;
+ gdouble *d_samples = NULL;
+ GimpRGB *rgba;
+ gint i;
+
+ gradient_name = gimp_context_get_gradient ();
+
+ gimp_gradient_get_uniform_samples (gradient_name, NGRADSAMPLES, reverse,
+ &n_d_samples, &d_samples);
+
+ rgba = g_new0 (GimpRGB, NGRADSAMPLES);
+ for (i = 0; i < NGRADSAMPLES; i++)
+ {
+ gimp_rgba_set (rgba + i,
+ d_samples[i*4 + 0],
+ d_samples[i*4 + 1],
+ d_samples[i*4 + 2],
+ d_samples[i*4 + 3]);
+ }
+
+ g_free (gradient_name);
+
+ return rgba;
+}
diff --git a/plug-ins/print/Makefile.am b/plug-ins/print/Makefile.am
new file mode 100644
index 0000000..fed7f8e
--- /dev/null
+++ b/plug-ins/print/Makefile.am
@@ -0,0 +1,62 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+print_RC = print.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(print_RC)
+
+libexecdir = $(gimpplugindir)/plug-ins/print
+
+libexec_PROGRAMS = print
+
+EXTRA_PROGRAMS = print
+
+print_SOURCES = \
+ print.c \
+ print.h \
+ print-draw-page.c \
+ print-draw-page.h \
+ print-page-layout.c \
+ print-page-layout.h \
+ print-page-setup.h \
+ print-page-setup.c \
+ print-preview.c \
+ print-preview.h \
+ print-settings.c \
+ print-settings.h \
+ print-utils.c \
+ print-utils.h
diff --git a/plug-ins/print/Makefile.in b/plug-ins/print/Makefile.in
new file mode 100644
index 0000000..bb9bf5f
--- /dev/null
+++ b/plug-ins/print/Makefile.in
@@ -0,0 +1,1038 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = print$(EXEEXT)
+EXTRA_PROGRAMS = print$(EXEEXT)
+subdir = plug-ins/print
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_print_OBJECTS = print.$(OBJEXT) print-draw-page.$(OBJEXT) \
+ print-page-layout.$(OBJEXT) print-page-setup.$(OBJEXT) \
+ print-preview.$(OBJEXT) print-settings.$(OBJEXT) \
+ print-utils.$(OBJEXT)
+print_OBJECTS = $(am_print_OBJECTS)
+print_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+print_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) $(libgimpconfig) \
+ $(libgimp) $(libgimpcolor) $(libgimpmath) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(print_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/print-draw-page.Po \
+ ./$(DEPDIR)/print-page-layout.Po \
+ ./$(DEPDIR)/print-page-setup.Po ./$(DEPDIR)/print-preview.Po \
+ ./$(DEPDIR)/print-settings.Po ./$(DEPDIR)/print-utils.Po \
+ ./$(DEPDIR)/print.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(print_SOURCES)
+DIST_SOURCES = $(print_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/print
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@print_RC = print.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(print_RC)
+
+print_SOURCES = \
+ print.c \
+ print.h \
+ print-draw-page.c \
+ print-draw-page.h \
+ print-page-layout.c \
+ print-page-layout.h \
+ print-page-setup.h \
+ print-page-setup.c \
+ print-preview.c \
+ print-preview.h \
+ print-settings.c \
+ print-settings.h \
+ print-utils.c \
+ print-utils.h
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/print/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/print/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+print$(EXEEXT): $(print_OBJECTS) $(print_DEPENDENCIES) $(EXTRA_print_DEPENDENCIES)
+ @rm -f print$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(print_OBJECTS) $(print_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-draw-page.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-page-layout.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-page-setup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-preview.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/print-draw-page.Po
+ -rm -f ./$(DEPDIR)/print-page-layout.Po
+ -rm -f ./$(DEPDIR)/print-page-setup.Po
+ -rm -f ./$(DEPDIR)/print-preview.Po
+ -rm -f ./$(DEPDIR)/print-settings.Po
+ -rm -f ./$(DEPDIR)/print-utils.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/print-draw-page.Po
+ -rm -f ./$(DEPDIR)/print-page-layout.Po
+ -rm -f ./$(DEPDIR)/print-page-setup.Po
+ -rm -f ./$(DEPDIR)/print-preview.Po
+ -rm -f ./$(DEPDIR)/print-settings.Po
+ -rm -f ./$(DEPDIR)/print-utils.Po
+ -rm -f ./$(DEPDIR)/print.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/print/print-draw-page.c b/plug-ins/print/print-draw-page.c
new file mode 100644
index 0000000..588cbde
--- /dev/null
+++ b/plug-ins/print/print-draw-page.c
@@ -0,0 +1,222 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print.h"
+#include "print-draw-page.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static cairo_surface_t * print_surface_from_drawable (gint32 drawable_ID,
+ GError **error);
+
+static void print_draw_crop_marks (GtkPrintContext *context,
+ gdouble x,
+ gdouble y,
+ gdouble w,
+ gdouble h);
+
+gboolean
+print_draw_page (GtkPrintContext *context,
+ PrintData *data,
+ GError **error)
+{
+ cairo_t *cr = gtk_print_context_get_cairo_context (context);
+ cairo_surface_t *surface;
+
+ surface = print_surface_from_drawable (data->drawable_id, error);
+
+ if (surface)
+ {
+ gint width;
+ gint height;
+ gdouble scale_x;
+ gdouble scale_y;
+
+ /* create a white rectangle covering the entire page, just
+ * to be safe; see bug #777233.
+ */
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+ cairo_paint (cr);
+
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+
+ scale_x = gtk_print_context_get_dpi_x (context) / data->xres;
+ scale_y = gtk_print_context_get_dpi_y (context) / data->yres;
+
+ cairo_translate (cr,
+ data->offset_x / 72.0 * gtk_print_context_get_dpi_x (context),
+ data->offset_y / 72.0 * gtk_print_context_get_dpi_y (context));
+
+ if (data->draw_crop_marks)
+ print_draw_crop_marks (context,
+ 0, 0, width * scale_x, height * scale_y);
+
+ cairo_scale (cr, scale_x, scale_y);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_fill (cr);
+
+ cairo_surface_destroy (surface);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static cairo_surface_t *
+print_surface_from_drawable (gint32 drawable_ID,
+ GError **error)
+{
+ GeglBuffer *buffer = gimp_drawable_get_buffer (drawable_ID);
+ const Babl *format;
+ cairo_surface_t *surface;
+ cairo_status_t status;
+ const gint width = gimp_drawable_width (drawable_ID);
+ const gint height = gimp_drawable_height (drawable_ID);
+ GeglBufferIterator *iter;
+ guchar *pixels;
+ gint stride;
+ guint count = 0;
+ guint64 done = 0;
+
+ if (gimp_drawable_has_alpha (drawable_ID))
+ format = babl_format ("cairo-ARGB32");
+ else
+ format = babl_format ("cairo-RGB24");
+
+ surface = cairo_image_surface_create (gimp_drawable_has_alpha (drawable_ID) ?
+ CAIRO_FORMAT_ARGB32 :
+ CAIRO_FORMAT_RGB24,
+ width, height);
+
+ status = cairo_surface_status (surface);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ switch (status)
+ {
+ case CAIRO_STATUS_INVALID_SIZE:
+ g_set_error_literal (error,
+ GIMP_PLUGIN_PRINT_ERROR,
+ GIMP_PLUGIN_PRINT_ERROR_FAILED,
+ _("Cannot handle the size (either width or height) of the image."));
+ break;
+ default:
+ g_set_error (error,
+ GIMP_PLUGIN_PRINT_ERROR,
+ GIMP_PLUGIN_PRINT_ERROR_FAILED,
+ "Cairo error: %s",
+ cairo_status_to_string (status));
+ break;
+ }
+
+ return NULL;
+ }
+
+ pixels = cairo_image_surface_get_data (surface);
+ stride = cairo_image_surface_get_stride (surface);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, 0, width, height), 0,
+ format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->items[0].data;
+ guchar *dest = pixels + (guint64) iter->items[0].roi.y * stride + iter->items[0].roi.x * 4;
+ gint y;
+
+ for (y = 0; y < iter->items[0].roi.height; y++)
+ {
+ memcpy (dest, src, iter->items[0].roi.width * 4);
+
+ src += iter->items[0].roi.width * 4;
+ dest += stride;
+ }
+
+ done += (guint64) iter->items[0].roi.height * iter->items[0].roi.width;
+
+ if (count++ % 16 == 0)
+ gimp_progress_update ((gdouble) done / ((gdouble) width * height));
+ }
+
+ g_object_unref (buffer);
+
+ cairo_surface_mark_dirty (surface);
+
+ gimp_progress_update (1.0);
+
+ return surface;
+}
+
+static void
+print_draw_crop_marks (GtkPrintContext *context,
+ gdouble x,
+ gdouble y,
+ gdouble w,
+ gdouble h)
+{
+ cairo_t *cr = gtk_print_context_get_cairo_context (context);
+ gdouble len = MIN (gtk_print_context_get_width (context),
+ gtk_print_context_get_height (context)) / 20.0;
+
+ /* upper left */
+
+ cairo_move_to (cr, x - len, y);
+ cairo_line_to (cr, x - len / 5, y);
+
+ cairo_move_to (cr, x, y - len);
+ cairo_line_to (cr, x, y - len / 5);
+
+ /* upper right */
+
+ cairo_move_to (cr, x + w + len / 5, y);
+ cairo_line_to (cr, x + w + len, y);
+
+ cairo_move_to (cr, x + w, y - len);
+ cairo_line_to (cr, x + w, y - len / 5);
+
+ /* lower left */
+
+ cairo_move_to (cr, x - len, y + h);
+ cairo_line_to (cr, x - len / 5, y + h);
+
+ cairo_move_to (cr, x, y + h + len);
+ cairo_line_to (cr, x, y + h + len / 5);
+
+ /* lower right */
+
+ cairo_move_to (cr, x + w + len / 5, y + h);
+ cairo_line_to (cr, x + w + len, y + h);
+
+ cairo_move_to (cr, x + w, y + h + len);
+ cairo_line_to (cr, x + w, y + h + len / 5);
+
+ cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
+ cairo_set_line_width (cr, 2);
+ cairo_stroke (cr);
+}
diff --git a/plug-ins/print/print-draw-page.h b/plug-ins/print/print-draw-page.h
new file mode 100644
index 0000000..88f3968
--- /dev/null
+++ b/plug-ins/print/print-draw-page.h
@@ -0,0 +1,20 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+gboolean print_draw_page (GtkPrintContext *context,
+ PrintData *data,
+ GError **error);
diff --git a/plug-ins/print/print-page-layout.c b/plug-ins/print/print-page-layout.c
new file mode 100644
index 0000000..1fb91de
--- /dev/null
+++ b/plug-ins/print/print-page-layout.c
@@ -0,0 +1,948 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print.h"
+#include "print-page-layout.h"
+#include "print-preview.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+typedef struct
+{
+ PrintData *data;
+ gint image_width;
+ gint image_height;
+ GimpSizeEntry *size_entry;
+ GimpSizeEntry *resolution_entry;
+ GimpChainButton *chain;
+ GtkWidget *center_combo;
+ GtkWidget *area_label;
+ GtkWidget *preview;
+ GtkAdjustment *left_adj;
+ GtkAdjustment *right_adj;
+ GtkAdjustment *top_adj;
+ GtkAdjustment *bottom_adj;
+} PrintSizeInfo;
+
+enum
+{
+ BOTTOM,
+ TOP,
+ RIGHT,
+ LEFT,
+ WIDTH,
+ HEIGHT
+};
+
+
+static void print_page_setup_notify (GtkPrintOperation *operation);
+static void update_custom_widget (GtkPrintOperation *operation,
+ GtkWidget *custom_widget,
+ GtkPageSetup *page_setup,
+ GtkPrintSettings *print_settings);
+
+static GtkWidget * print_size_frame (PrintData *data,
+ GtkSizeGroup *label_group,
+ GtkSizeGroup *entry_group);
+static GtkWidget * print_offset_frame (PrintData *data,
+ GtkSizeGroup *label_group,
+ GtkSizeGroup *entry_group);
+
+static void print_size_info_update_offsets (void);
+static void print_size_info_size_changed (GtkWidget *widget);
+static void print_size_info_offset_max_changed (GtkAdjustment *adj,
+ gpointer data);
+static void print_size_info_resolution_changed (GtkWidget *widget);
+static void print_size_info_unit_changed (GtkWidget *widget);
+static void print_size_info_preview_offset_changed
+ (GtkWidget *widget,
+ gdouble offset_x,
+ gdouble offset_y);
+static void print_size_info_center_changed (GtkWidget *widget);
+static void print_size_info_center_none (void);
+static void print_size_info_use_full_page_toggled
+ (GtkWidget *widget);
+
+static void print_size_info_set_resolution (PrintSizeInfo *info,
+ gdouble xres,
+ gdouble yres);
+
+
+static void print_size_info_set_page_setup (PrintSizeInfo *info);
+
+static void print_draw_crop_marks_toggled (GtkWidget *widget);
+
+static void print_resolution_load_defaults (PrintSizeInfo *info);
+
+static PrintSizeInfo info;
+
+
+GtkWidget *
+print_page_layout_gui (PrintData *data,
+ const gchar *help_id)
+{
+ GtkWidget *main_hbox;
+ GtkWidget *main_vbox;
+ GtkWidget *button;
+ GtkWidget *frame;
+ GtkPageSetup *setup;
+ GtkSizeGroup *label_group;
+ GtkSizeGroup *entry_group;
+
+ memset (&info, 0, sizeof (PrintSizeInfo));
+
+ info.data = data;
+ info.image_width = gimp_drawable_width (data->drawable_id);
+ info.image_height = gimp_drawable_height (data->drawable_id);
+
+ setup = gtk_print_operation_get_default_page_setup (data->operation);
+ if (! setup)
+ {
+ setup = gtk_page_setup_new ();
+ gtk_print_operation_set_default_page_setup (data->operation, setup);
+ }
+
+ /* main hbox */
+ main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
+
+ /* main vbox */
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_box_pack_start (GTK_BOX (main_hbox), main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+ label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ entry_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ /* size entry area for the image's print size */
+
+ frame = print_size_frame (data, label_group, entry_group);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ /* offset entry area for the image's offset position */
+
+ frame = print_offset_frame (data, label_group, entry_group);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ g_object_unref (label_group);
+ g_object_unref (entry_group);
+
+ button = gtk_check_button_new_with_mnemonic (_("Ignore Page _Margins"));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ data->use_full_page);
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (print_size_info_use_full_page_toggled),
+ NULL);
+ gtk_widget_show (button);
+
+ /* crop marks toggle */
+ button = gtk_check_button_new_with_mnemonic (_("_Draw Crop Marks"));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ data->draw_crop_marks);
+ gtk_box_pack_start (GTK_BOX (main_vbox), button, FALSE, FALSE, 0);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (print_draw_crop_marks_toggled),
+ NULL);
+ gtk_widget_show (button);
+
+ /* preview */
+ frame = gimp_frame_new (_("Preview"));
+ gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ info.preview = print_preview_new (setup, data->drawable_id);
+ print_preview_set_use_full_page (PRINT_PREVIEW (info.preview),
+ data->use_full_page);
+ gtk_container_add (GTK_CONTAINER (frame), info.preview);
+ gtk_widget_show (info.preview);
+
+ g_signal_connect (info.preview, "offsets-changed",
+ G_CALLBACK (print_size_info_preview_offset_changed),
+ NULL);
+
+ print_size_info_set_page_setup (&info);
+
+ g_signal_connect_object (data->operation, "notify::default-page-setup",
+ G_CALLBACK (print_page_setup_notify),
+ main_hbox, 0);
+ g_signal_connect_object (data->operation, "update-custom-widget",
+ G_CALLBACK (update_custom_widget),
+ main_hbox, 0);
+
+ gimp_help_connect (main_hbox, gimp_standard_help_func, help_id, NULL);
+
+ return main_hbox;
+}
+
+static void
+print_page_setup_notify (GtkPrintOperation *operation)
+{
+ GtkPageSetup *setup;
+
+ setup = gtk_print_operation_get_default_page_setup (operation);
+
+ print_size_info_set_page_setup (&info);
+ print_preview_set_page_setup (PRINT_PREVIEW (info.preview), setup);
+}
+
+static void
+update_custom_widget (GtkPrintOperation *operation,
+ GtkWidget *custom_widget,
+ GtkPageSetup *page_setup,
+ GtkPrintSettings *print_settings)
+{
+ gtk_print_operation_set_default_page_setup (operation, page_setup);
+}
+
+
+#define SB_WIDTH 8
+
+static GtkWidget *
+print_size_frame (PrintData *data,
+ GtkSizeGroup *label_group,
+ GtkSizeGroup *entry_group)
+{
+ GtkWidget *entry;
+ GtkWidget *height;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *chain;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *button;
+ GtkAdjustment *adj;
+ gdouble image_width;
+ gdouble image_height;
+
+ image_width = (info.image_width *
+ gimp_unit_get_factor (data->unit) / data->xres);
+ image_height = (info.image_height *
+ gimp_unit_get_factor (data->unit) / data->yres);
+
+ frame = gimp_frame_new (_("Size"));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* the print size entry */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ entry = gimp_size_entry_new (1, data->unit, "%p",
+ FALSE, FALSE, FALSE, SB_WIDTH,
+ GIMP_SIZE_ENTRY_UPDATE_SIZE);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show (entry);
+
+ info.size_entry = GIMP_SIZE_ENTRY (entry);
+
+ gtk_table_set_row_spacings (GTK_TABLE (entry), 2);
+ gtk_table_set_col_spacing (GTK_TABLE (entry), 0, 6);
+ gtk_table_set_col_spacing (GTK_TABLE (entry), 2, 6);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ height = gimp_spin_button_new (adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (height), TRUE);
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (height), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (entry), height, 1, 2, 0, 1);
+ gtk_widget_show (height);
+
+ gtk_size_group_add_widget (entry_group, height);
+
+ gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry),
+ _("_Width:"), 0, 0, 0.0);
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry),
+ _("_Height:"), 1, 0, 0.0);
+
+ gtk_size_group_add_widget (label_group, label);
+
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 0,
+ data->xres, FALSE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), 1,
+ data->yres, FALSE);
+
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (entry), 0, image_width);
+ gimp_size_entry_set_value (GIMP_SIZE_ENTRY (entry), 1, image_height);
+
+ /* the resolution entry */
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ entry = gimp_size_entry_new (1, data->image_unit,
+ _("pixels/%a"),
+ FALSE, FALSE, FALSE, SB_WIDTH,
+ GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
+ gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show (entry);
+
+ info.resolution_entry = GIMP_SIZE_ENTRY (entry);
+
+ gtk_table_set_row_spacings (GTK_TABLE (entry), 2);
+ gtk_table_set_col_spacing (GTK_TABLE (entry), 0, 6);
+ gtk_table_set_col_spacing (GTK_TABLE (entry), 2, 6);
+
+ adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ height = gimp_spin_button_new (adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (height), TRUE);
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (height), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (entry), height, 1, 2, 0, 1);
+ gtk_widget_show (height);
+
+ gtk_size_group_add_widget (entry_group, height);
+
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry),
+ _("_X resolution:"), 0, 0, 0.0);
+ gtk_size_group_add_widget (label_group, label);
+
+ label = gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (entry),
+ _("_Y resolution:"), 1, 0, 0.0);
+ gtk_size_group_add_widget (label_group, label);
+
+ button = gtk_button_new_with_mnemonic (_("_Load Defaults"));
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (print_resolution_load_defaults),
+ &info);
+ gtk_widget_show (button);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 0,
+ GIMP_MIN_RESOLUTION,
+ GIMP_MAX_RESOLUTION);
+ gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (entry), 1,
+ GIMP_MIN_RESOLUTION,
+ GIMP_MAX_RESOLUTION);
+
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 0, data->xres);
+ gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (entry), 1, data->yres);
+
+ chain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
+ if (ABS (data->xres - data->yres) < GIMP_MIN_RESOLUTION)
+ gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (chain), TRUE);
+ gtk_table_attach (GTK_TABLE (entry), chain, 2, 3, 0, 2,
+ GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (chain);
+
+ info.chain = GIMP_CHAIN_BUTTON (chain);
+
+ g_signal_connect (info.resolution_entry, "value-changed",
+ G_CALLBACK (print_size_info_resolution_changed),
+ NULL);
+ g_signal_connect (info.size_entry, "unit-changed",
+ G_CALLBACK (print_size_info_unit_changed),
+ NULL);
+
+ return frame;
+}
+
+static GtkWidget *
+print_offset_frame (PrintData *data,
+ GtkSizeGroup *label_group,
+ GtkSizeGroup *entry_group)
+{
+ GtkWidget *entry;
+ GtkWidget *spinner;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *frame;
+ GtkWidget *label;
+ GtkWidget *combo;
+
+ frame = gimp_frame_new (_("Position"));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* the offset entry */
+
+ entry = GTK_WIDGET (info.size_entry);
+
+ table = gtk_table_new (4, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 0);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* left */
+ info.left_adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinner = gimp_spin_button_new (info.left_adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (spinner), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (table), spinner, 1, 2, 0, 1);
+ gtk_widget_show (spinner);
+
+ label = gtk_label_new_with_mnemonic (_("_Left:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 0, 1);
+ gtk_size_group_add_widget (label_group, label);
+ gtk_widget_show (label);
+
+ /* right */
+ info.right_adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinner = gimp_spin_button_new (info.right_adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+
+ g_signal_connect (info.right_adj, "value-changed",
+ G_CALLBACK (print_size_info_offset_max_changed),
+ GINT_TO_POINTER (LEFT));
+
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (spinner), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (table), spinner, 3, 4, 0, 1);
+ gtk_widget_show (spinner);
+
+ label = gtk_label_new_with_mnemonic (_("_Right:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 2, 3, 0, 1);
+ gtk_widget_show (label);
+
+ /* top */
+ info.top_adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinner = gimp_spin_button_new (info.top_adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (spinner), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (table), spinner, 1, 2, 1, 2);
+ gtk_widget_show (spinner);
+
+ label = gtk_label_new_with_mnemonic (_("_Top:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 1, 2);
+ gtk_size_group_add_widget (label_group, label);
+ gtk_widget_show (label);
+
+ /* bottom */
+ info.bottom_adj = (GtkAdjustment *) gtk_adjustment_new (1, 1, 1, 1, 10, 0);
+ spinner = gimp_spin_button_new (info.bottom_adj, 1, 2);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+
+ g_signal_connect (info.bottom_adj, "value-changed",
+ G_CALLBACK (print_size_info_offset_max_changed),
+ GINT_TO_POINTER (TOP));
+
+ gimp_size_entry_add_field (GIMP_SIZE_ENTRY (entry),
+ GTK_SPIN_BUTTON (spinner), NULL);
+ gtk_table_attach_defaults (GTK_TABLE (table), spinner, 3, 4, 1, 2);
+ gtk_widget_show (spinner);
+
+ label = gtk_label_new_with_mnemonic (_("_Bottom:"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label, 2, 3, 1, 2);
+ gtk_widget_show (label);
+
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), LEFT, 72.0, FALSE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), RIGHT, 72.0, FALSE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), TOP, 72.0, FALSE);
+ gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (entry), BOTTOM, 72.0, FALSE);
+
+ print_size_info_update_offsets ();
+
+ g_signal_connect (info.size_entry, "value-changed",
+ G_CALLBACK (print_size_info_size_changed),
+ NULL);
+
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new_with_mnemonic (_("C_enter:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_size_group_add_widget (label_group, label);
+ gtk_widget_show (label);
+
+ /* if and how to center the image on the page */
+ combo = gimp_int_combo_box_new (C_("center-mode", "None"), CENTER_NONE,
+ _("Horizontally"), CENTER_HORIZONTALLY,
+ _("Vertically"), CENTER_VERTICALLY,
+ _("Both"), CENTER_BOTH,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ data->center,
+ G_CALLBACK (print_size_info_center_changed),
+ NULL);
+
+ info.center_combo = combo;
+
+ g_signal_connect (info.left_adj, "value-changed",
+ G_CALLBACK (print_size_info_center_none),
+ NULL);
+ g_signal_connect (info.right_adj, "value-changed",
+ G_CALLBACK (print_size_info_center_none),
+ NULL);
+ g_signal_connect (info.top_adj, "value-changed",
+ G_CALLBACK (print_size_info_center_none),
+ NULL);
+ g_signal_connect (info.bottom_adj, "value-changed",
+ G_CALLBACK (print_size_info_center_none),
+ NULL);
+
+ return frame;
+}
+
+static void
+print_size_info_get_page_dimensions (PrintSizeInfo *info,
+ gdouble *page_width,
+ gdouble *page_height,
+ GtkUnit unit)
+{
+ GtkPageSetup *setup;
+
+ setup = gtk_print_operation_get_default_page_setup (info->data->operation);
+
+ if (info->data->use_full_page)
+ {
+ *page_width = gtk_page_setup_get_paper_width (setup, unit);
+ *page_height = gtk_page_setup_get_paper_height (setup, unit);
+ }
+ else
+ {
+ *page_width = gtk_page_setup_get_page_width (setup, unit);
+ *page_height = gtk_page_setup_get_page_height (setup, unit);
+ }
+
+}
+
+static void
+gimp_size_info_get_max_offsets (gdouble *offset_x_max,
+ gdouble *offset_y_max)
+{
+ gdouble width;
+ gdouble height;
+
+ print_size_info_get_page_dimensions (&info, &width, &height, GTK_UNIT_POINTS);
+
+ *offset_x_max = width - 72.0 * info.image_width / info.data->xres;
+ *offset_x_max = MAX (0, *offset_x_max);
+
+ *offset_y_max = height - 72.0 * info.image_height / info.data->yres;
+ *offset_y_max = MAX (0, *offset_y_max);
+}
+
+static void
+print_size_info_center_none_block (void)
+{
+ g_signal_handlers_block_by_func (info.left_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_block_by_func (info.right_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_block_by_func (info.top_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_block_by_func (info.bottom_adj,
+ print_size_info_center_none, NULL);
+}
+
+static void
+print_size_info_center_none_unblock (void)
+{
+ g_signal_handlers_unblock_by_func (info.left_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_unblock_by_func (info.right_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_unblock_by_func (info.top_adj,
+ print_size_info_center_none, NULL);
+ g_signal_handlers_unblock_by_func (info.bottom_adj,
+ print_size_info_center_none, NULL);
+}
+
+static void
+print_size_info_update_offsets (void)
+{
+ PrintData *data = info.data;
+ gdouble offset_x_max;
+ gdouble offset_y_max;
+
+ gimp_size_info_get_max_offsets (&offset_x_max, &offset_y_max);
+
+ g_signal_handlers_block_by_func (info.size_entry,
+ print_size_info_size_changed, NULL);
+
+ print_size_info_center_none_block ();
+
+ gimp_size_entry_set_refval_boundaries (info.size_entry, LEFT,
+ 0, offset_x_max);
+ gimp_size_entry_set_refval_boundaries (info.size_entry, RIGHT,
+ 0, offset_x_max);
+ gimp_size_entry_set_refval_boundaries (info.size_entry, TOP,
+ 0, offset_y_max);
+ gimp_size_entry_set_refval_boundaries (info.size_entry, BOTTOM,
+ 0, offset_y_max);
+
+ switch (data->center)
+ {
+ case CENTER_NONE:
+ break;
+
+ case CENTER_HORIZONTALLY:
+ data->offset_x = offset_x_max / 2.0;
+ break;
+
+ case CENTER_VERTICALLY:
+ data->offset_y = offset_y_max / 2.0;
+ break;
+
+ case CENTER_BOTH:
+ data->offset_x = offset_x_max / 2.0;
+ data->offset_y = offset_y_max / 2.0;
+ break;
+ }
+
+ gimp_size_entry_set_refval (info.size_entry, LEFT,
+ data->offset_x);
+ gimp_size_entry_set_refval (info.size_entry, RIGHT,
+ offset_x_max - data->offset_x);
+ gimp_size_entry_set_refval (info.size_entry, TOP,
+ info.data->offset_y);
+ gimp_size_entry_set_refval (info.size_entry, BOTTOM,
+ offset_y_max - data->offset_y);
+
+ print_size_info_center_none_unblock ();
+
+ g_signal_handlers_unblock_by_func (info.size_entry,
+ print_size_info_size_changed, NULL);
+}
+
+static void
+print_size_info_center_changed (GtkWidget *combo)
+{
+ gint value;
+
+ if (gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value))
+ {
+ info.data->center = value;
+
+ print_size_info_update_offsets ();
+
+ if (info.preview)
+ print_preview_set_image_offsets (PRINT_PREVIEW (info.preview),
+ info.data->offset_x,
+ info.data->offset_y);
+ }
+}
+
+static void
+print_size_info_center_none (void)
+{
+ /* return early if we are called from a unit change */
+ if (gimp_size_entry_get_unit (info.size_entry) != info.data->unit)
+ return;
+
+ info.data->center = CENTER_NONE;
+
+ if (info.center_combo)
+ {
+ g_signal_handlers_block_by_func (info.center_combo,
+ print_size_info_center_changed, NULL);
+
+ info.data->center = CENTER_NONE;
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (info.center_combo),
+ info.data->center);
+
+ g_signal_handlers_unblock_by_func (info.center_combo,
+ print_size_info_center_changed, NULL);
+ }
+}
+
+static void
+print_size_info_preview_offset_changed (GtkWidget *widget,
+ gdouble offset_x,
+ gdouble offset_y)
+{
+ print_size_info_center_none ();
+
+ info.data->offset_x = offset_x;
+ info.data->offset_y = offset_y;
+
+ print_size_info_update_offsets ();
+}
+
+static void
+print_size_info_size_changed (GtkWidget *widget)
+{
+ gdouble width;
+ gdouble height;
+ gdouble xres;
+ gdouble yres;
+ gdouble scale;
+
+ scale = gimp_unit_get_factor (gimp_size_entry_get_unit (info.size_entry));
+
+ width = gimp_size_entry_get_value (info.size_entry, WIDTH);
+ height = gimp_size_entry_get_value (info.size_entry, HEIGHT);
+
+ xres = scale * info.image_width / MAX (0.0001, width);
+ yres = scale * info.image_height / MAX (0.0001, height);
+
+ print_size_info_set_resolution (&info, xres, yres);
+
+ info.data->offset_x = gimp_size_entry_get_refval (info.size_entry, LEFT);
+ info.data->offset_y = gimp_size_entry_get_refval (info.size_entry, TOP);
+
+ print_preview_set_image_offsets (PRINT_PREVIEW (info.preview),
+ info.data->offset_x,
+ info.data->offset_y);
+}
+
+static void
+print_size_info_offset_max_changed (GtkAdjustment *adj,
+ gpointer data)
+{
+ guint index = GPOINTER_TO_INT (data);
+
+ /* return early if we are called from a unit change */
+ if (gimp_size_entry_get_unit (info.size_entry) != info.data->unit)
+ return;
+
+ g_signal_handlers_block_by_func (info.size_entry,
+ print_size_info_size_changed, NULL);
+
+ gimp_size_entry_set_value (info.size_entry, index,
+ gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_value (adj));
+
+ g_signal_handlers_unblock_by_func (info.size_entry,
+ print_size_info_size_changed, NULL);
+}
+
+static void
+print_size_info_resolution_changed (GtkWidget *widget)
+{
+ GimpSizeEntry *entry = info.resolution_entry;
+ gdouble xres = gimp_size_entry_get_refval (entry, 0);
+ gdouble yres = gimp_size_entry_get_refval (entry, 1);
+
+ print_size_info_set_resolution (&info, xres, yres);
+}
+
+static void
+print_size_info_use_full_page_toggled (GtkWidget *widget)
+{
+ gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ info.data->use_full_page = active;
+
+ print_size_info_set_page_setup (&info);
+
+ print_preview_set_use_full_page (PRINT_PREVIEW(info.preview), active);
+}
+
+static void
+print_size_info_unit_changed (GtkWidget *widget)
+{
+ info.data->unit = gimp_size_entry_get_unit (GIMP_SIZE_ENTRY (widget));
+
+ print_size_info_set_page_setup (&info);
+}
+
+static void
+print_size_info_set_resolution (PrintSizeInfo *info,
+ gdouble xres,
+ gdouble yres)
+{
+ PrintData *data = info->data;
+ gdouble offset_x;
+ gdouble offset_y;
+ gdouble offset_x_max;
+ gdouble offset_y_max;
+
+ if (info->chain && gimp_chain_button_get_active (info->chain))
+ {
+ if (xres != data->xres)
+ yres = xres;
+ else
+ xres = yres;
+ }
+
+ data->xres = xres;
+ data->yres = yres;
+
+ g_signal_handlers_block_by_func (info->resolution_entry,
+ print_size_info_resolution_changed,
+ NULL);
+
+ gimp_size_entry_set_refval (info->resolution_entry, 0, xres);
+ gimp_size_entry_set_refval (info->resolution_entry, 1, yres);
+
+ g_signal_handlers_unblock_by_func (info->resolution_entry,
+ print_size_info_resolution_changed,
+ NULL);
+
+ g_signal_handlers_block_by_func (info->size_entry,
+ print_size_info_size_changed,
+ NULL);
+
+ gimp_size_entry_set_value (info->size_entry, WIDTH,
+ info->image_width *
+ gimp_unit_get_factor (data->unit) / xres);
+ gimp_size_entry_set_value (info->size_entry, HEIGHT,
+ info->image_height *
+ gimp_unit_get_factor (data->unit) / yres);
+
+ g_signal_handlers_unblock_by_func (info->size_entry,
+ print_size_info_size_changed,
+ NULL);
+
+ gimp_size_info_get_max_offsets (&offset_x_max, &offset_y_max);
+
+ offset_x = gimp_size_entry_get_refval (info->size_entry, LEFT);
+ offset_y = gimp_size_entry_get_refval (info->size_entry, TOP);
+
+ offset_x = CLAMP (offset_x, 0, offset_x_max);
+ offset_y = CLAMP (offset_y, 0, offset_y_max);
+
+ data->offset_x = offset_x;
+ data->offset_y = offset_y;
+
+ print_size_info_update_offsets ();
+
+ print_preview_set_image_dpi (PRINT_PREVIEW (info->preview),
+ data->xres, data->yres);
+ print_preview_set_image_offsets (PRINT_PREVIEW (info->preview),
+ data->offset_x, data->offset_y);
+ print_preview_set_image_offsets_max (PRINT_PREVIEW (info->preview),
+ offset_x_max, offset_y_max);
+}
+
+static void
+print_size_info_set_page_setup (PrintSizeInfo *info)
+{
+ PrintData *data = info->data;
+ gdouble page_width;
+ gdouble page_height;
+ gdouble x;
+ gdouble y;
+
+ print_size_info_get_page_dimensions (info,
+ &page_width, &page_height,
+ GTK_UNIT_INCH);
+
+ page_width *= gimp_unit_get_factor (data->unit);
+ page_height *= gimp_unit_get_factor (data->unit);
+
+ if (info->area_label)
+ {
+ gchar *format;
+ gchar *text;
+
+ format = g_strdup_printf ("%%.%df x %%.%df %s",
+ gimp_unit_get_digits (data->unit),
+ gimp_unit_get_digits (data->unit),
+ gimp_unit_get_plural (data->unit));
+ text = g_strdup_printf (format, page_width, page_height);
+ g_free (format);
+
+ gtk_label_set_text (GTK_LABEL (info->area_label), text);
+ g_free (text);
+ }
+
+ x = page_width;
+ y = page_height;
+
+ if (info->chain && gimp_chain_button_get_active (info->chain))
+ {
+ gdouble ratio_x = page_width / (gdouble) info->image_width;
+ gdouble ratio_y = page_height / (gdouble) info->image_height;
+
+ if (ratio_x < ratio_y)
+ y = (gdouble) info->image_height * ratio_x;
+ else
+ x = (gdouble) info->image_width * ratio_y;
+ }
+
+ gimp_size_entry_set_value_boundaries (info->size_entry, WIDTH,
+ page_width / 100.0, x);
+ gimp_size_entry_set_value_boundaries (info->size_entry, HEIGHT,
+ page_height / 100.0, y);
+
+ print_size_info_get_page_dimensions (info,
+ &page_width, &page_height,
+ GTK_UNIT_POINTS);
+
+ x = (gdouble) info->image_width / page_width * 72.0;
+ y = (gdouble) info->image_height / page_height * 72.0;
+
+ if (info->chain && gimp_chain_button_get_active (info->chain))
+ {
+ gdouble max = MAX (x, y);
+
+ x = y = max;
+ }
+
+ data->min_xres = x;
+ data->min_yres = y;
+ gimp_size_entry_set_refval_boundaries (info->resolution_entry, 0,
+ x, GIMP_MAX_RESOLUTION);
+ gimp_size_entry_set_refval_boundaries (info->resolution_entry, 1,
+ y, GIMP_MAX_RESOLUTION);
+}
+
+static void
+print_draw_crop_marks_toggled (GtkWidget *widget)
+{
+ gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ info.data->draw_crop_marks = active;
+}
+
+static void
+print_resolution_load_defaults (PrintSizeInfo *info)
+{
+ gdouble xres;
+ gdouble yres;
+
+ gimp_image_get_resolution (info->data->image_id, &xres, &yres);
+
+ gimp_size_entry_set_refval (info->resolution_entry, 0, xres);
+ gimp_size_entry_set_refval (info->resolution_entry, 1, yres);
+ print_size_info_resolution_changed (GTK_WIDGET (info->resolution_entry));
+}
diff --git a/plug-ins/print/print-page-layout.h b/plug-ins/print/print-page-layout.h
new file mode 100644
index 0000000..d01f777
--- /dev/null
+++ b/plug-ins/print/print-page-layout.h
@@ -0,0 +1,20 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+GtkWidget * print_page_layout_gui (PrintData *data,
+ const gchar *help_id);
+
diff --git a/plug-ins/print/print-page-setup.c b/plug-ins/print/print-page-setup.c
new file mode 100644
index 0000000..f80e014
--- /dev/null
+++ b/plug-ins/print/print-page-setup.c
@@ -0,0 +1,101 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print.h"
+#include "print-page-setup.h"
+#include "print-utils.h"
+
+#define PRINT_PAGE_SETUP_NAME "print-page-setup"
+
+
+#ifndef EMBED_PAGE_SETUP
+void
+print_page_setup_dialog (GtkPrintOperation *operation)
+{
+ GtkPrintSettings *settings;
+ GtkPageSetup *setup;
+
+ g_return_if_fail (GTK_IS_PRINT_OPERATION (operation));
+
+ setup = gtk_print_operation_get_default_page_setup (operation);
+
+ settings = gtk_print_settings_new ();
+ setup = gtk_print_run_page_setup_dialog (NULL, setup, settings);
+ g_object_unref (settings);
+
+ gtk_print_operation_set_default_page_setup (operation, setup);
+}
+#endif
+
+void
+print_page_setup_load (GtkPrintOperation *operation,
+ gint32 image_ID)
+{
+ GKeyFile *key_file;
+
+ g_return_if_fail (GTK_IS_PRINT_OPERATION (operation));
+
+ key_file = print_utils_key_file_load_from_parasite (image_ID,
+ PRINT_PAGE_SETUP_NAME);
+
+ if (! key_file)
+ key_file = print_utils_key_file_load_from_rcfile (PRINT_PAGE_SETUP_NAME);
+
+ if (key_file)
+ {
+ GtkPageSetup *setup;
+
+ setup = gtk_page_setup_new_from_key_file (key_file,
+ PRINT_PAGE_SETUP_NAME, NULL);
+
+ if (setup)
+ {
+ gtk_print_operation_set_default_page_setup (operation, setup);
+ g_object_unref (setup);
+ }
+
+ g_key_file_free (key_file);
+ }
+}
+
+void
+print_page_setup_save (GtkPrintOperation *operation,
+ gint32 image_ID)
+{
+ GtkPageSetup *setup;
+ GKeyFile *key_file;
+
+ g_return_if_fail (GTK_IS_PRINT_OPERATION (operation));
+
+ key_file = g_key_file_new ();
+
+ setup = gtk_print_operation_get_default_page_setup (operation);
+
+ gtk_page_setup_to_key_file (setup, key_file, PRINT_PAGE_SETUP_NAME);
+
+ print_utils_key_file_save_as_parasite (key_file,
+ image_ID, PRINT_PAGE_SETUP_NAME);
+ print_utils_key_file_save_as_rcfile (key_file,
+ PRINT_PAGE_SETUP_NAME);
+
+ g_key_file_free (key_file);
+}
diff --git a/plug-ins/print/print-page-setup.h b/plug-ins/print/print-page-setup.h
new file mode 100644
index 0000000..8440e10
--- /dev/null
+++ b/plug-ins/print/print-page-setup.h
@@ -0,0 +1,27 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef EMBED_PAGE_SETUP
+void print_page_setup_dialog (GtkPrintOperation *operation);
+#endif
+void print_page_setup_load (GtkPrintOperation *operation,
+ gint32 image_ID);
+void print_page_setup_save (GtkPrintOperation *operation,
+ gint32 image_ID);
+
+
diff --git a/plug-ins/print/print-preview.c b/plug-ins/print/print-preview.c
new file mode 100644
index 0000000..366a4c4
--- /dev/null
+++ b/plug-ins/print/print-preview.c
@@ -0,0 +1,880 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print-preview.h"
+
+
+enum
+{
+ OFFSETS_CHANGED,
+ LAST_SIGNAL
+};
+
+
+#define SIZE_REQUEST 200
+
+
+struct _PrintPreview
+{
+ GtkEventBox parent_instance;
+
+ GdkCursor *cursor;
+
+ GtkPageSetup *page;
+ cairo_surface_t *thumbnail;
+ gboolean dragging;
+ gboolean inside;
+
+ gint32 drawable_id;
+
+ gdouble image_offset_x;
+ gdouble image_offset_y;
+ gdouble image_offset_x_max;
+ gdouble image_offset_y_max;
+ gdouble image_width;
+ gdouble image_height;
+
+ gboolean use_full_page;
+
+ /* for mouse drags */
+ gdouble orig_offset_x;
+ gdouble orig_offset_y;
+ gint start_x;
+ gint start_y;
+};
+
+struct _PrintPreviewClass
+{
+ GtkEventBoxClass parent_class;
+
+ void (* offsets_changed) (PrintPreview *print_preview,
+ gint offset_x,
+ gint offset_y);
+};
+
+
+static void print_preview_finalize (GObject *object);
+
+static void print_preview_realize (GtkWidget *widget);
+static void print_preview_unrealize (GtkWidget *widget);
+static void print_preview_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void print_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static gboolean print_preview_expose_event (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean print_preview_button_press_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean print_preview_button_release_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean print_preview_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean print_preview_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event);
+
+static gboolean print_preview_is_inside (PrintPreview *preview,
+ gdouble x,
+ gdouble y);
+static void print_preview_set_inside (PrintPreview *preview,
+ gboolean inside);
+
+static gdouble print_preview_get_scale (PrintPreview *preview);
+
+static void print_preview_get_page_size (PrintPreview *preview,
+ gdouble *paper_width,
+ gdouble *paper_height);
+static void print_preview_get_page_margins (PrintPreview *preview,
+ gdouble *left_margin,
+ gdouble *right_margin,
+ gdouble *top_margin,
+ gdouble *bottom_margin);
+static cairo_surface_t * print_preview_get_thumbnail (gint32 drawable_id,
+ gint width,
+ gint height);
+
+
+G_DEFINE_TYPE (PrintPreview, print_preview, GTK_TYPE_EVENT_BOX)
+
+#define parent_class print_preview_parent_class
+
+static guint print_preview_signals[LAST_SIGNAL] = { 0 };
+
+
+#define g_marshal_value_peek_double(v) (v)->data[0].v_double
+
+static void
+marshal_VOID__DOUBLE_DOUBLE (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ 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);
+}
+
+static void
+print_preview_class_init (PrintPreviewClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ print_preview_signals[OFFSETS_CHANGED] =
+ g_signal_new ("offsets-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (PrintPreviewClass, offsets_changed),
+ NULL, NULL,
+ marshal_VOID__DOUBLE_DOUBLE,
+ G_TYPE_NONE, 2,
+ G_TYPE_DOUBLE,
+ G_TYPE_DOUBLE);
+
+ object_class->finalize = print_preview_finalize;
+
+ widget_class->realize = print_preview_realize;
+ widget_class->unrealize = print_preview_unrealize;
+ widget_class->size_request = print_preview_size_request;
+ widget_class->size_allocate = print_preview_size_allocate;
+ widget_class->expose_event = print_preview_expose_event;
+ widget_class->button_press_event = print_preview_button_press_event;
+ widget_class->button_release_event = print_preview_button_release_event;
+ widget_class->motion_notify_event = print_preview_motion_notify_event;
+ widget_class->leave_notify_event = print_preview_leave_notify_event;
+
+ klass->offsets_changed = NULL;
+}
+
+static void
+print_preview_init (PrintPreview *preview)
+{
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (preview), FALSE);
+
+ gtk_widget_add_events (GTK_WIDGET (preview),
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK);
+}
+
+
+static void
+print_preview_finalize (GObject *object)
+{
+ PrintPreview *preview = PRINT_PREVIEW (object);
+
+ if (preview->thumbnail)
+ {
+ cairo_surface_destroy (preview->thumbnail);
+ preview->thumbnail = NULL;
+ }
+
+ if (preview->page)
+ {
+ g_object_unref (preview->page);
+ preview->page = NULL;
+ }
+
+ G_OBJECT_CLASS (print_preview_parent_class)->finalize (object);
+}
+
+static void
+print_preview_realize (GtkWidget *widget)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->realize (widget);
+
+ preview->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_HAND1);
+}
+
+static void
+print_preview_unrealize (GtkWidget *widget)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->cursor)
+ gdk_cursor_unref (preview->cursor);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->unrealize (widget);
+}
+
+static void
+print_preview_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+ gdouble paper_width;
+ gdouble paper_height;
+ gint border;
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ if (paper_width > paper_height)
+ {
+ requisition->height = SIZE_REQUEST;
+ requisition->width = paper_width * SIZE_REQUEST / paper_height;
+ requisition->width = MIN (requisition->width, 2 * SIZE_REQUEST);
+ }
+ else
+ {
+ requisition->width = SIZE_REQUEST;
+ requisition->height = paper_height * SIZE_REQUEST / paper_width;
+ requisition->height = MIN (requisition->height, 2 * SIZE_REQUEST);
+ }
+
+ requisition->width += 2 * border;
+ requisition->height += 2 * border;
+}
+
+static void
+print_preview_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ GTK_WIDGET_CLASS (print_preview_parent_class)->size_allocate (widget,
+ allocation);
+
+ if (preview->thumbnail)
+ {
+ cairo_surface_destroy (preview->thumbnail);
+ preview->thumbnail = NULL;
+ }
+}
+
+static gboolean
+print_preview_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1 && preview->inside)
+ {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
+ GDK_FLEUR);
+
+ if (gdk_pointer_grab (event->window, FALSE,
+ (GDK_BUTTON1_MOTION_MASK |
+ GDK_BUTTON_RELEASE_MASK),
+ NULL, cursor, event->time) == GDK_GRAB_SUCCESS)
+ {
+ preview->orig_offset_x = preview->image_offset_x;
+ preview->orig_offset_y = preview->image_offset_y;
+
+ preview->start_x = event->x;
+ preview->start_y = event->y;
+
+ preview->dragging = TRUE;
+ }
+
+ gdk_cursor_unref (cursor);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->dragging)
+ {
+ gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+ event->time);
+ preview->dragging = FALSE;
+
+ print_preview_set_inside (preview,
+ print_preview_is_inside (preview,
+ event->x, event->y));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (preview->dragging)
+ {
+ gdouble scale = print_preview_get_scale (preview);
+ gdouble offset_x;
+ gdouble offset_y;
+
+ offset_x = (preview->orig_offset_x +
+ (event->x - preview->start_x) / scale);
+ offset_y = (preview->orig_offset_y +
+ (event->y - preview->start_y) / scale);
+
+ offset_x = CLAMP (offset_x, 0, preview->image_offset_x_max);
+ offset_y = CLAMP (offset_y, 0, preview->image_offset_y_max);
+
+ if (preview->image_offset_x != offset_x ||
+ preview->image_offset_y != offset_y)
+ {
+ print_preview_set_image_offsets (preview, offset_x, offset_y);
+
+ g_signal_emit (preview,
+ print_preview_signals[OFFSETS_CHANGED], 0,
+ preview->image_offset_x,
+ preview->image_offset_y);
+ }
+ }
+ else
+ {
+ print_preview_set_inside (preview,
+ print_preview_is_inside (preview,
+ event->x, event->y));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_leave_notify_event (GtkWidget *widget,
+ GdkEventCrossing *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+
+ if (event->mode == GDK_CROSSING_NORMAL)
+ print_preview_set_inside (preview, FALSE);
+
+ return FALSE;
+}
+
+static gboolean
+print_preview_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ PrintPreview *preview = PRINT_PREVIEW (widget);
+ GtkStyle *style = gtk_widget_get_style (widget);
+ GtkAllocation allocation;
+ cairo_t *cr;
+ gdouble paper_width;
+ gdouble paper_height;
+ gdouble left_margin;
+ gdouble right_margin;
+ gdouble top_margin;
+ gdouble bottom_margin;
+ gdouble scale;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+ print_preview_get_page_margins (preview,
+ &left_margin, &right_margin,
+ &top_margin, &bottom_margin);
+
+ scale = print_preview_get_scale (preview);
+
+ cr = gdk_cairo_create (gtk_widget_get_window (widget));
+
+ cairo_translate (cr,
+ allocation.x + border,
+ allocation.y + border);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ gint width = allocation.width - 2 * border;
+
+ cairo_translate (cr, width - scale * paper_width, 0);
+ }
+
+ cairo_set_line_width (cr, 1.0);
+
+ /* draw page background */
+ cairo_rectangle (cr, 0, 0, scale * paper_width, scale * paper_height);
+
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke_preserve (cr);
+
+ gdk_cairo_set_source_color (cr, &style->white);
+ cairo_fill (cr);
+
+ /* draw page_margins */
+ cairo_rectangle (cr,
+ scale * left_margin,
+ scale * top_margin,
+ scale * (paper_width - left_margin - right_margin),
+ scale * (paper_height - top_margin - bottom_margin));
+
+ gdk_cairo_set_source_color (cr, &style->mid[gtk_widget_get_state (widget)]);
+ cairo_stroke (cr);
+
+ cairo_translate (cr,
+ scale * (left_margin + preview->image_offset_x),
+ scale * (top_margin + preview->image_offset_y));
+
+ if (preview->dragging || preview->inside)
+ {
+ cairo_rectangle (cr,
+ 0, 0,
+ scale * preview->image_width,
+ scale * preview->image_height);
+
+ gdk_cairo_set_source_color (cr, &style->black);
+ cairo_stroke (cr);
+ }
+
+ if (preview->thumbnail == NULL &&
+ gimp_item_is_valid (preview->drawable_id))
+ {
+ preview->thumbnail =
+ print_preview_get_thumbnail (preview->drawable_id,
+ MIN (allocation.width, 1024),
+ MIN (allocation.height, 1024));
+ }
+
+ if (preview->thumbnail != NULL)
+ {
+ gdouble scale_x;
+ gdouble scale_y;
+
+ scale_x = (preview->image_width /
+ cairo_image_surface_get_width (preview->thumbnail));
+ scale_y = (preview->image_height /
+ cairo_image_surface_get_height (preview->thumbnail));
+
+ cairo_rectangle (cr, 0, 0, preview->image_width, preview->image_height);
+
+ cairo_scale (cr, scale_x * scale, scale_y * scale);
+
+ cairo_set_source_surface (cr, preview->thumbnail, 0, 0);
+ cairo_fill (cr);
+ }
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+/**
+ * print_preview_new:
+ * @page: page setup
+ * @drawable_id: the drawable to print
+ *
+ * Creates a new #PrintPreview widget.
+ *
+ * Return value: the new #PrintPreview widget.
+ **/
+GtkWidget *
+print_preview_new (GtkPageSetup *page,
+ gint32 drawable_id)
+{
+ PrintPreview *preview;
+
+ g_return_val_if_fail (GTK_IS_PAGE_SETUP (page), NULL);
+
+ preview = g_object_new (PRINT_TYPE_PREVIEW, NULL);
+
+ preview->drawable_id = drawable_id;
+
+ print_preview_set_page_setup (preview, page);
+
+ return GTK_WIDGET (preview);
+}
+
+/**
+ * print_preview_set_image_dpi:
+ * @preview: a #PrintPreview.
+ * @xres: the X resolution
+ * @yres: the Y resolution
+ *
+ * Sets the resolution of the image/drawable displayed by the
+ * #PrintPreview.
+ **/
+void
+print_preview_set_image_dpi (PrintPreview *preview,
+ gdouble xres,
+ gdouble yres)
+{
+ gdouble width;
+ gdouble height;
+
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+ g_return_if_fail (xres > 0.0 && yres > 0.0);
+
+ width = gimp_drawable_width (preview->drawable_id) * 72.0 / xres;
+ height = gimp_drawable_height (preview->drawable_id) * 72.0 / yres;
+
+ if (width != preview->image_width || height != preview->image_height)
+ {
+ preview->image_width = width;
+ preview->image_height = height;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+ }
+}
+
+/**
+ * print_preview_set_page_setup:
+ * @preview: a #PrintPreview.
+ * @page: the page setup to use
+ *
+ * Sets the page setup to use by the #PrintPreview.
+ **/
+void
+print_preview_set_page_setup (PrintPreview *preview,
+ GtkPageSetup *page)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+ g_return_if_fail (GTK_IS_PAGE_SETUP (page));
+
+ if (preview->page)
+ g_object_unref (preview->page);
+
+ preview->page = gtk_page_setup_copy (page);
+
+ gtk_widget_queue_resize (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_image_offsets:
+ * @preview: a #PrintPreview.
+ * @offset_x: the X offset
+ * @offset_y: the Y offset
+ *
+ * Sets the offsets of the image/drawable displayed by the #PrintPreview.
+ * It does not emit the "offsets-changed" signal.
+ **/
+void
+print_preview_set_image_offsets (PrintPreview *preview,
+ gdouble offset_x,
+ gdouble offset_y)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->image_offset_x = offset_x;
+ preview->image_offset_y = offset_y;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_image_offsets_max:
+ * @preview: a #PrintPreview.
+ * @offset_x_max: the maximum X offset allowed
+ * @offset_y_max: the maximum Y offset allowed
+ *
+ * Sets the maximum offsets of the image/drawable displayed by the
+ * #PrintPreview. It does not emit the "offsets-changed" signal.
+ **/
+void
+print_preview_set_image_offsets_max (PrintPreview *preview,
+ gdouble offset_x_max,
+ gdouble offset_y_max)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->image_offset_x_max = offset_x_max;
+ preview->image_offset_y_max = offset_y_max;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+/**
+ * print_preview_set_use_full_page:
+ * @preview: a #PrintPreview.
+ * @full_page: TRUE to ignore the page margins
+ *
+ * If @full_page is TRUE, the page margins are ignored and the full page
+ * can be used to setup printing.
+ **/
+void
+print_preview_set_use_full_page (PrintPreview *preview,
+ gboolean full_page)
+{
+ g_return_if_fail (PRINT_IS_PREVIEW (preview));
+
+ preview->use_full_page = full_page;
+
+ gtk_widget_queue_draw (GTK_WIDGET (preview));
+}
+
+static gboolean
+print_preview_is_inside (PrintPreview *preview,
+ gdouble x,
+ gdouble y)
+{
+ GtkWidget *widget = GTK_WIDGET (preview);
+ GtkAllocation allocation;
+ gdouble left_margin;
+ gdouble right_margin;
+ gdouble top_margin;
+ gdouble bottom_margin;
+ gdouble scale;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ x -= border;
+
+ scale = print_preview_get_scale (preview);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ {
+ gdouble paper_width;
+ gdouble paper_height;
+ gint width = allocation.width - 2 * border;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ x -= width - scale * paper_width;
+ }
+
+ print_preview_get_page_margins (preview,
+ &left_margin, &right_margin,
+ &top_margin, &bottom_margin);
+
+ x = x / scale - left_margin;
+ y = y / scale - top_margin;
+
+ return (x > preview->image_offset_x &&
+ x < preview->image_offset_x + preview->image_width &&
+ y > preview->image_offset_y &&
+ y < preview->image_offset_y + preview->image_height);
+}
+
+static void
+print_preview_set_inside (PrintPreview *preview,
+ gboolean inside)
+{
+ if (inside != preview->inside)
+ {
+ GtkWidget *widget = GTK_WIDGET (preview);
+
+ preview->inside = inside;
+
+ if (gtk_widget_is_drawable (widget))
+ gdk_window_set_cursor (gtk_widget_get_window (widget),
+ inside ? preview->cursor : NULL);
+
+ gtk_widget_queue_draw (widget);
+ }
+}
+
+static gdouble
+print_preview_get_scale (PrintPreview *preview)
+{
+ GtkWidget *widget = GTK_WIDGET (preview);
+ GtkAllocation allocation;
+ gdouble paper_width;
+ gdouble paper_height;
+ gdouble scale_x;
+ gdouble scale_y;
+ gint border;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ border = gtk_container_get_border_width (GTK_CONTAINER (widget)) + 1;
+
+ print_preview_get_page_size (preview, &paper_width, &paper_height);
+
+ scale_x = (gdouble) (allocation.width - 2 * border) / paper_width;
+ scale_y = (gdouble) (allocation.height - 2 * border) / paper_height;
+
+ return MIN (scale_x, scale_y);
+}
+
+static void
+print_preview_get_page_size (PrintPreview *preview,
+ gdouble *paper_width,
+ gdouble *paper_height)
+{
+ *paper_width = gtk_page_setup_get_paper_width (preview->page,
+ GTK_UNIT_POINTS);
+ *paper_height = gtk_page_setup_get_paper_height (preview->page,
+ GTK_UNIT_POINTS);
+}
+
+static void
+print_preview_get_page_margins (PrintPreview *preview,
+ gdouble *left_margin,
+ gdouble *right_margin,
+ gdouble *top_margin,
+ gdouble *bottom_margin)
+{
+ if (preview->use_full_page)
+ {
+ *left_margin = 0.0;
+ *right_margin = 0.0;
+ *top_margin = 0.0;
+ *bottom_margin = 0.0;
+ }
+ else
+ {
+ *left_margin = gtk_page_setup_get_left_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *right_margin = gtk_page_setup_get_right_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *top_margin = gtk_page_setup_get_top_margin (preview->page,
+ GTK_UNIT_POINTS);
+ *bottom_margin = gtk_page_setup_get_bottom_margin (preview->page,
+ GTK_UNIT_POINTS);
+ }
+}
+
+
+/* This thumbnail code should eventually end up in libgimpui. */
+
+static cairo_surface_t *
+print_preview_get_thumbnail (gint32 drawable_id,
+ gint width,
+ gint height)
+{
+ cairo_surface_t *surface;
+ cairo_format_t format;
+ guchar *data;
+ guchar *dest;
+ const guchar *src;
+ gint src_stride;
+ gint dest_stride;
+ gint y;
+ gint bpp;
+
+ g_return_val_if_fail (width > 0 && width <= 1024, NULL);
+ g_return_val_if_fail (height > 0 && height <= 1024, NULL);
+
+ data = gimp_drawable_get_thumbnail_data (drawable_id,
+ &width, &height, &bpp);
+
+ switch (bpp)
+ {
+ case 1:
+ case 3:
+ format = CAIRO_FORMAT_RGB24;
+ break;
+
+ case 2:
+ case 4:
+ format = CAIRO_FORMAT_ARGB32;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ surface = cairo_image_surface_create (format, width, height);
+
+ src = data;
+ src_stride = width * bpp;
+
+ dest = cairo_image_surface_get_data (surface);
+ dest_stride = cairo_image_surface_get_stride (surface);
+
+ for (y = 0; y < height; y++)
+ {
+ const guchar *s = src;
+ guchar *d = dest;
+ gint w = width;
+
+ switch (bpp)
+ {
+ case 1:
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[0], s[0]);
+ s += 1;
+ d += 4;
+ }
+ break;
+
+ case 2:
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[0], s[0], s[1]);
+ s += 2;
+ d += 4;
+ }
+ break;
+
+ case 3:
+ while (w--)
+ {
+ GIMP_CAIRO_RGB24_SET_PIXEL (d, s[0], s[1], s[2]);
+ s += 3;
+ d += 4;
+ }
+ break;
+
+ case 4:
+ while (w--)
+ {
+ GIMP_CAIRO_ARGB32_SET_PIXEL (d, s[0], s[1], s[2], s[3]);
+ s += 4;
+ d += 4;
+ }
+ break;
+ }
+
+ src += src_stride;
+ dest += dest_stride;
+ }
+
+ g_free (data);
+
+ cairo_surface_mark_dirty (surface);
+
+ return surface;
+}
diff --git a/plug-ins/print/print-preview.h b/plug-ins/print/print-preview.h
new file mode 100644
index 0000000..dc4e172
--- /dev/null
+++ b/plug-ins/print/print-preview.h
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PRINT_PREVIEW_H__
+#define __PRINT_PREVIEW_H__
+
+G_BEGIN_DECLS
+
+
+#define PRINT_TYPE_PREVIEW (print_preview_get_type ())
+#define PRINT_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PRINT_TYPE_PREVIEW, PrintPreview))
+#define PRINT_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PRINT_TYPE_PREVIEW, PrintPreviewClass))
+#define PRINT_IS_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PRINT_TYPE_PREVIEW))
+#define PRINT_IS_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PRINT_TYPE_PREVIEW))
+#define PRINT_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PRINT_TYPE_PREVIEW, PrintPreviewClass))
+
+typedef struct _PrintPreview PrintPreview;
+typedef struct _PrintPreviewClass PrintPreviewClass;
+
+
+GType print_preview_get_type (void) G_GNUC_CONST;
+
+GtkWidget * print_preview_new (GtkPageSetup *page,
+ gint32 drawable_id);
+
+void print_preview_set_image_dpi (PrintPreview *preview,
+ gdouble xres,
+ gdouble yres);
+
+void print_preview_set_page_setup (PrintPreview *preview,
+ GtkPageSetup *page);
+
+void print_preview_set_image_offsets (PrintPreview *preview,
+ gdouble offset_x,
+ gdouble offset_y);
+
+void print_preview_set_image_offsets_max (PrintPreview *preview,
+ gdouble offset_x_max,
+ gdouble offset_y_max);
+
+void print_preview_set_use_full_page (PrintPreview *preview,
+ gboolean full_page);
+
+G_END_DECLS
+
+#endif /* __PRINT_PREVIEW_H__ */
diff --git a/plug-ins/print/print-settings.c b/plug-ins/print/print-settings.c
new file mode 100644
index 0000000..617282f
--- /dev/null
+++ b/plug-ins/print/print-settings.c
@@ -0,0 +1,320 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print.h"
+#include "print-settings.h"
+#include "print-utils.h"
+
+
+#define PRINT_SETTINGS_MAJOR_VERSION 0
+#define PRINT_SETTINGS_MINOR_VERSION 4
+
+#define PRINT_SETTINGS_NAME "print-settings"
+
+
+static GKeyFile * print_settings_key_file_from_settings (PrintData *data);
+
+static void print_settings_add_to_key_file (const gchar *key,
+ const gchar *value,
+ gpointer data);
+
+static GKeyFile * print_settings_key_file_from_resource_file (void);
+
+static GKeyFile * print_settings_key_file_from_parasite (gint32 image_ID);
+
+static gboolean print_settings_load_from_key_file (PrintData *data,
+ GKeyFile *key_file);
+
+static gboolean print_settings_check_version (GKeyFile *key_file);
+
+/*
+ * set GtkPrintSettings from the contents of a "print-settings"
+ * image parasite, or, if none exists, from a resource
+ * file of the same name
+ */
+gboolean
+print_settings_load (PrintData *data)
+{
+ GKeyFile *key_file = print_settings_key_file_from_parasite (data->image_id);
+
+ if (! key_file)
+ key_file = print_settings_key_file_from_resource_file ();
+
+ if (key_file)
+ {
+ print_settings_load_from_key_file (data, key_file);
+ g_key_file_free (key_file);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * save all settings as a resource file "print-settings"
+ * and as an image parasite
+ */
+void
+print_settings_save (PrintData *data)
+{
+ GKeyFile *key_file = print_settings_key_file_from_settings (data);
+
+ /* image setup */
+ if (gimp_image_is_valid (data->image_id))
+ {
+ gdouble xres;
+ gdouble yres;
+
+ gimp_image_get_resolution (data->image_id, &xres, &yres);
+
+ g_key_file_set_integer (key_file, "image-setup",
+ "unit", data->unit);
+ /* Do not save the print resolution when it is the expected image
+ * resolution so that changing it (i.e. in "print size" dialog)
+ * is not overridden by any previous prints.
+ */
+ if ((data->min_xres <= xres && ABS (xres - data->xres) > 0.1) ||
+ (data->min_yres <= yres && ABS (yres - data->yres) > 0.1) ||
+ (data->min_xres > xres && ABS (data->min_xres - data->xres) > 0.1) ||
+ (data->min_yres > yres && ABS (data->min_yres - data->yres) > 0.1))
+ {
+ g_key_file_set_double (key_file, "image-setup",
+ "x-resolution", data->xres);
+ g_key_file_set_double (key_file, "image-setup",
+ "y-resolution", data->yres);
+ }
+ g_key_file_set_double (key_file, "image-setup",
+ "x-offset", data->offset_x);
+ g_key_file_set_double (key_file, "image-setup",
+ "y-offset", data->offset_y);
+ g_key_file_set_integer (key_file, "image-setup",
+ "center-mode", data->center);
+ g_key_file_set_boolean (key_file, "image-setup",
+ "use-full-page", data->use_full_page);
+ g_key_file_set_boolean (key_file, "image-setup",
+ "crop-marks", data->draw_crop_marks);
+
+ print_utils_key_file_save_as_parasite (key_file,
+ data->image_id,
+ PRINT_SETTINGS_NAME);
+ }
+
+ /* some settings shouldn't be made persistent on a global level,
+ * so they are only stored in the image, not in the rcfile
+ */
+
+ g_key_file_remove_key (key_file, "image-setup", "x-resolution", NULL);
+ g_key_file_remove_key (key_file, "image-setup", "y-resolution", NULL);
+ g_key_file_remove_key (key_file, "image-setup", "x-offset", NULL);
+ g_key_file_remove_key (key_file, "image-setup", "y-offset", NULL);
+
+ g_key_file_remove_key (key_file, PRINT_SETTINGS_NAME, "n-copies", NULL);
+
+ print_utils_key_file_save_as_rcfile (key_file, PRINT_SETTINGS_NAME);
+
+ g_key_file_free (key_file);
+}
+
+/*
+ * serialize print settings into a GKeyFile
+ */
+static GKeyFile *
+print_settings_key_file_from_settings (PrintData *data)
+{
+ GtkPrintOperation *operation = data->operation;
+ GtkPrintSettings *settings;
+ GKeyFile *key_file = g_key_file_new ();
+
+ /* put version information into the file */
+ g_key_file_set_integer (key_file, "meta", "major-version",
+ PRINT_SETTINGS_MAJOR_VERSION);
+ g_key_file_set_integer (key_file, "meta", "minor-version",
+ PRINT_SETTINGS_MINOR_VERSION);
+
+ /* save the contents of the GtkPrintSettings for the operation */
+ settings = gtk_print_operation_get_print_settings (operation);
+
+ if (settings)
+ gtk_print_settings_foreach (settings,
+ print_settings_add_to_key_file, key_file);
+
+ return key_file;
+}
+
+/*
+ * callback used in gtk_print_settings_foreach loop
+ */
+static void
+print_settings_add_to_key_file (const gchar *key,
+ const gchar *value,
+ gpointer data)
+{
+ GKeyFile *key_file = data;
+
+ g_key_file_set_value (key_file, PRINT_SETTINGS_NAME, key, value);
+}
+
+/*
+ * deserialize a "print-settings" resource file into a GKeyFile
+ */
+static GKeyFile *
+print_settings_key_file_from_resource_file (void)
+{
+ GKeyFile *key_file;
+
+ key_file = print_utils_key_file_load_from_rcfile (PRINT_SETTINGS_NAME);
+
+ if (key_file && ! print_settings_check_version (key_file))
+ {
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ return key_file;
+}
+
+/* load information from an image parasite called "print-settings"
+ * return a GKeyFile containing the information if a valid parasite is found,
+ * NULL otherwise
+ */
+static GKeyFile *
+print_settings_key_file_from_parasite (gint32 image_ID)
+{
+ GKeyFile *key_file;
+
+ key_file = print_utils_key_file_load_from_parasite (image_ID,
+ PRINT_SETTINGS_NAME);
+
+ if (key_file && ! print_settings_check_version (key_file))
+ {
+ g_key_file_free (key_file);
+ return NULL;
+ }
+
+ return key_file;
+}
+
+static gboolean
+print_settings_load_from_key_file (PrintData *data,
+ GKeyFile *key_file)
+{
+ GtkPrintOperation *operation = data->operation;
+ GtkPrintSettings *settings;
+ gchar **keys;
+ gsize n_keys;
+ gint i;
+
+ settings = gtk_print_operation_get_print_settings (operation);
+ if (! settings)
+ settings = gtk_print_settings_new ();
+
+ keys = g_key_file_get_keys (key_file, PRINT_SETTINGS_NAME, &n_keys, NULL);
+
+ if (! keys)
+ return FALSE;
+
+ for (i = 0; i < n_keys; i++)
+ {
+ gchar *value;
+
+ value = g_key_file_get_value (key_file,
+ PRINT_SETTINGS_NAME, keys[i], NULL);
+
+ if (value)
+ {
+ gtk_print_settings_set (settings, keys[i], value);
+ g_free (value);
+ }
+ }
+
+ g_strfreev (keys);
+
+ if (g_key_file_has_key (key_file, "image-setup", "unit", NULL))
+ {
+ data->unit = g_key_file_get_integer (key_file, "image-setup",
+ "unit", NULL);
+ }
+
+ if (g_key_file_has_key (key_file, "image-setup", "x-resolution", NULL) &&
+ g_key_file_has_key (key_file, "image-setup", "y-resolution", NULL))
+ {
+ data->xres = g_key_file_get_double (key_file, "image-setup",
+ "x-resolution", NULL);
+ data->yres = g_key_file_get_double (key_file, "image-setup",
+ "y-resolution", NULL);
+ }
+
+ if (g_key_file_has_key (key_file, "image-setup", "x-offset", NULL) &&
+ g_key_file_has_key (key_file, "image-setup", "y-offset", NULL))
+ {
+ data->offset_x = g_key_file_get_double (key_file, "image-setup",
+ "x-offset", NULL);
+ data->offset_y = g_key_file_get_double (key_file, "image-setup",
+ "y-offset", NULL);
+ }
+
+ if (g_key_file_has_key (key_file, "image-setup", "center-mode", NULL))
+ {
+ data->center = g_key_file_get_integer (key_file, "image-setup",
+ "center-mode", NULL);
+ }
+
+ if (g_key_file_has_key (key_file, "image-setup", "use-full-page", NULL))
+ {
+ data->use_full_page = g_key_file_get_boolean (key_file, "image-setup",
+ "use-full-page", NULL);
+ }
+
+ if (g_key_file_has_key (key_file, "image-setup", "crop-marks", NULL))
+ {
+ data->draw_crop_marks = g_key_file_get_boolean (key_file, "image-setup",
+ "crop-marks", NULL);
+ }
+
+ gtk_print_operation_set_print_settings (operation, settings);
+
+ return TRUE;
+}
+
+static gboolean
+print_settings_check_version (GKeyFile *key_file)
+{
+ gint major_version;
+ gint minor_version;
+
+ if (! g_key_file_has_group (key_file, "meta"))
+ return FALSE;
+
+ major_version = g_key_file_get_integer (key_file,
+ "meta", "major-version", NULL);
+
+ if (major_version != PRINT_SETTINGS_MAJOR_VERSION)
+ return FALSE;
+
+ minor_version = g_key_file_get_integer (key_file,
+ "meta", "minor-version", NULL);
+
+ if (minor_version != PRINT_SETTINGS_MINOR_VERSION)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/plug-ins/print/print-settings.h b/plug-ins/print/print-settings.h
new file mode 100644
index 0000000..0120f07
--- /dev/null
+++ b/plug-ins/print/print-settings.h
@@ -0,0 +1,19 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+gboolean print_settings_load (PrintData *data);
+void print_settings_save (PrintData *data);
diff --git a/plug-ins/print/print-utils.c b/plug-ins/print/print-utils.c
new file mode 100644
index 0000000..a1b057b
--- /dev/null
+++ b/plug-ins/print/print-utils.c
@@ -0,0 +1,145 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "print-utils.h"
+
+
+GKeyFile *
+print_utils_key_file_load_from_rcfile (const gchar *basename)
+{
+ GKeyFile *key_file;
+ gchar *filename;
+
+ g_return_val_if_fail (basename != NULL, NULL);
+
+ filename = g_build_filename (gimp_directory (), basename, NULL);
+
+ key_file = g_key_file_new ();
+
+ if (! g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL))
+ {
+ g_key_file_free (key_file);
+ key_file = NULL;
+ }
+
+ g_free (filename);
+
+ return key_file;
+}
+
+GKeyFile *
+print_utils_key_file_load_from_parasite (gint32 image_ID,
+ const gchar *parasite_name)
+{
+ GimpParasite *parasite;
+ GKeyFile *key_file;
+ GError *error = NULL;
+
+ g_return_val_if_fail (parasite_name != NULL, NULL);
+
+ parasite = gimp_image_get_parasite (image_ID, parasite_name);
+
+ if (! parasite)
+ return NULL;
+
+ key_file = g_key_file_new ();
+
+ if (! g_key_file_load_from_data (key_file,
+ gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite),
+ G_KEY_FILE_NONE, &error))
+ {
+ g_key_file_free (key_file);
+ gimp_parasite_free (parasite);
+
+ g_warning ("Unable to create key file from image parasite '%s': %s",
+ parasite_name, error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ gimp_parasite_free (parasite);
+
+ return key_file;
+}
+
+void
+print_utils_key_file_save_as_rcfile (GKeyFile *key_file,
+ const gchar *basename)
+{
+ gchar *filename;
+ gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ g_return_if_fail (basename != NULL);
+
+ contents = g_key_file_to_data (key_file, &length, &error);
+
+ if (! contents)
+ {
+ g_warning ("Unable to get contents of key file for '%s': %s",
+ basename, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ filename = g_build_filename (gimp_directory (), basename, NULL);
+
+ if (! g_file_set_contents (filename, contents, length, &error))
+ {
+ g_warning ("Unable to write settings to '%s': %s",
+ gimp_filename_to_utf8 (filename), error->message);
+ g_error_free (error);
+ }
+
+ g_free (filename);
+ g_free (contents);
+}
+
+void
+print_utils_key_file_save_as_parasite (GKeyFile *key_file,
+ gint32 image_ID,
+ const gchar *parasite_name)
+{
+ GimpParasite *parasite;
+ gchar *contents;
+ gsize length;
+ GError *error = NULL;
+
+ g_return_if_fail (parasite_name != NULL);
+
+ contents = g_key_file_to_data (key_file, &length, &error);
+
+ if (! contents)
+ {
+ g_warning ("Unable to get contents of key file for parasite '%s': %s",
+ parasite_name, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ parasite = gimp_parasite_new (parasite_name, 0, length, contents);
+ g_free (contents);
+
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+}
diff --git a/plug-ins/print/print-utils.h b/plug-ins/print/print-utils.h
new file mode 100644
index 0000000..78aa12a
--- /dev/null
+++ b/plug-ins/print/print-utils.h
@@ -0,0 +1,26 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+GKeyFile * print_utils_key_file_load_from_rcfile (const gchar *basename);
+GKeyFile * print_utils_key_file_load_from_parasite (gint32 image_ID,
+ const gchar *parasite_name);
+
+void print_utils_key_file_save_as_rcfile (GKeyFile *key_file,
+ const gchar *basename);
+void print_utils_key_file_save_as_parasite (GKeyFile *key_file,
+ gint32 image_ID,
+ const gchar *parasite_name);
diff --git a/plug-ins/print/print.c b/plug-ins/print/print.c
new file mode 100644
index 0000000..57b83ce
--- /dev/null
+++ b/plug-ins/print/print.c
@@ -0,0 +1,503 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "print.h"
+#include "print-settings.h"
+#include "print-page-layout.h"
+#include "print-page-setup.h"
+#include "print-draw-page.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_BINARY "print"
+#define PLUG_IN_ROLE "gimp-print"
+#define PRINT_PROC_NAME "file-print-gtk"
+
+#ifndef EMBED_PAGE_SETUP
+#define PAGE_SETUP_PROC_NAME "file-print-gtk-page-setup"
+#define PRINT_TEMP_PROC_NAME "file-print-gtk-page-setup-notify-temp"
+#endif
+
+G_DEFINE_QUARK (gimp-plugin-print-error-quark, gimp_plugin_print_error)
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType print_image (gint32 image_ID,
+ gboolean interactive,
+ GError **error);
+#ifndef EMBED_PAGE_SETUP
+static GimpPDBStatusType page_setup (gint32 image_ID);
+#endif
+
+static void print_show_error (const gchar *message);
+static void print_operation_set_name (GtkPrintOperation *operation,
+ gint image_ID);
+
+static void begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ PrintData *data);
+static void end_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint32 *layer_ID);
+static void draw_page (GtkPrintOperation *print,
+ GtkPrintContext *context,
+ gint page_nr,
+ PrintData *data);
+
+static GtkWidget * create_custom_widget (GtkPrintOperation *operation,
+ PrintData *data);
+
+#ifndef EMBED_PAGE_SETUP
+static gchar * print_temp_proc_name (gint32 image_ID);
+static gchar * print_temp_proc_install (gint32 image_ID);
+
+/* Keep a reference to the current GtkPrintOperation
+ * for access by the temporary procedure.
+ */
+static GtkPrintOperation *print_operation = NULL;
+#endif
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef print_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" },
+ { GIMP_PDB_IMAGE, "image", "Image to print" }
+ };
+
+ gimp_install_procedure (PRINT_PROC_NAME,
+ N_("Print the image"),
+ "Print the image using the GTK+ Print API.",
+ "Bill Skaggs, Sven Neumann, Stefan Röllin",
+ "Bill Skaggs <weskaggs@primate.ucdavis.edu>",
+ "2006 - 2008",
+ N_("_Print..."),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (print_args), 0,
+ print_args, NULL);
+
+ gimp_plugin_menu_register (PRINT_PROC_NAME, "<Image>/File/Send");
+ gimp_plugin_icon_register (PRINT_PROC_NAME, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_DOCUMENT_PRINT);
+
+#ifndef EMBED_PAGE_SETUP
+ gimp_install_procedure (PAGE_SETUP_PROC_NAME,
+ N_("Adjust page size and orientation for printing"),
+ "Adjust page size and orientation for printing the "
+ "image using the GTK+ Print API.",
+ "Bill Skaggs, Sven Neumann, Stefan Röllin",
+ "Sven Neumann <sven@gimp.org>",
+ "2008",
+ N_("Page Set_up..."),
+ "*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (print_args), 0,
+ print_args, NULL);
+
+ gimp_plugin_menu_register (PAGE_SETUP_PROC_NAME, "<Image>/File/Send");
+ gimp_plugin_icon_register (PAGE_SETUP_PROC_NAME, GIMP_ICON_TYPE_ICON_NAME,
+ (const guint8 *) GIMP_ICON_DOCUMENT_PAGE_SETUP);
+#endif
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status;
+ gint32 image_ID;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ image_ID = param[1].data.d_int32;
+
+ if (strcmp (name, PRINT_PROC_NAME) == 0)
+ {
+ status = print_image (image_ID, run_mode == GIMP_RUN_INTERACTIVE, &error);
+
+ if (error && run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ print_show_error (error->message);
+ }
+ }
+#ifndef EMBED_PAGE_SETUP
+ else if (strcmp (name, PAGE_SETUP_PROC_NAME) == 0)
+ {
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ status = page_setup (image_ID);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+#endif
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static GimpPDBStatusType
+print_image (gint32 image_ID,
+ gboolean interactive,
+ GError **error)
+{
+ GtkPrintOperation *operation;
+ GtkPrintOperationResult result;
+ gint32 layer;
+ PrintData data;
+#ifndef EMBED_PAGE_SETUP
+ gchar *temp_proc;
+#endif
+
+ /* create a print layer from the projection */
+ layer = gimp_layer_new_from_visible (image_ID, image_ID, PRINT_PROC_NAME);
+
+ operation = gtk_print_operation_new ();
+
+ gtk_print_operation_set_n_pages (operation, 1);
+ print_operation_set_name (operation, image_ID);
+
+ print_page_setup_load (operation, image_ID);
+
+ /* fill in the PrintData struct */
+ data.image_id = image_ID;
+ data.drawable_id = layer;
+ data.unit = gimp_get_default_unit ();
+ data.image_unit = gimp_image_get_unit (image_ID);
+ data.offset_x = 0;
+ data.offset_y = 0;
+ data.center = CENTER_BOTH;
+ data.use_full_page = FALSE;
+ data.draw_crop_marks = FALSE;
+ data.operation = operation;
+
+ gimp_image_get_resolution (image_ID, &data.xres, &data.yres);
+
+ print_settings_load (&data);
+
+ gtk_print_operation_set_unit (operation, GTK_UNIT_PIXEL);
+
+ g_signal_connect (operation, "begin-print",
+ G_CALLBACK (begin_print),
+ &data);
+ g_signal_connect (operation, "draw-page",
+ G_CALLBACK (draw_page),
+ &data);
+ g_signal_connect (operation, "end-print",
+ G_CALLBACK (end_print),
+ &layer);
+
+#ifndef EMBED_PAGE_SETUP
+ print_operation = operation;
+ temp_proc = print_temp_proc_install (image_ID);
+ gimp_extension_enable ();
+#endif
+
+ if (interactive)
+ {
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ g_signal_connect_swapped (operation, "end-print",
+ G_CALLBACK (print_settings_save),
+ &data);
+
+ g_signal_connect (operation, "create-custom-widget",
+ G_CALLBACK (create_custom_widget),
+ &data);
+
+ gtk_print_operation_set_custom_tab_label (operation, _("Image Settings"));
+
+#ifdef EMBED_PAGE_SETUP
+ gtk_print_operation_set_embed_page_setup (operation, TRUE);
+#endif
+
+ result = gtk_print_operation_run (operation,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ NULL, error);
+
+ if (result == GTK_PRINT_OPERATION_RESULT_APPLY ||
+ result == GTK_PRINT_OPERATION_RESULT_IN_PROGRESS)
+ {
+ print_page_setup_save (operation, image_ID);
+ }
+ }
+ else
+ {
+ result = gtk_print_operation_run (operation,
+ GTK_PRINT_OPERATION_ACTION_PRINT,
+ NULL, error);
+ }
+
+#ifndef EMBED_PAGE_SETUP
+ gimp_uninstall_temp_proc (temp_proc);
+ g_free (temp_proc);
+ print_operation = NULL;
+#endif
+
+ g_object_unref (operation);
+
+ if (gimp_item_is_valid (layer))
+ gimp_item_delete (layer);
+
+ switch (result)
+ {
+ case GTK_PRINT_OPERATION_RESULT_APPLY:
+ case GTK_PRINT_OPERATION_RESULT_IN_PROGRESS:
+ return GIMP_PDB_SUCCESS;
+
+ case GTK_PRINT_OPERATION_RESULT_CANCEL:
+ return GIMP_PDB_CANCEL;
+
+ case GTK_PRINT_OPERATION_RESULT_ERROR:
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ return GIMP_PDB_EXECUTION_ERROR;
+}
+
+#ifndef EMBED_PAGE_SETUP
+static GimpPDBStatusType
+page_setup (gint32 image_ID)
+{
+ GtkPrintOperation *operation;
+ GimpParam *return_vals;
+ gchar *name;
+ gint n_return_vals;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ operation = gtk_print_operation_new ();
+
+ print_page_setup_load (operation, image_ID);
+ print_page_setup_dialog (operation);
+ print_page_setup_save (operation, image_ID);
+
+ g_object_unref (operation);
+
+ /* now notify a running print procedure about this change */
+ name = print_temp_proc_name (image_ID);
+
+ /* we don't want the core to show an error message if the
+ * temporary procedure does not exist
+ */
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ return_vals = gimp_run_procedure (name,
+ &n_return_vals,
+ GIMP_PDB_IMAGE, image_ID,
+ GIMP_PDB_END);
+ gimp_destroy_params (return_vals, n_return_vals);
+
+ g_free (name);
+
+ return GIMP_PDB_SUCCESS;
+}
+#endif
+
+static void
+print_show_error (const gchar *message)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ _("An error occurred while trying to print:"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+print_operation_set_name (GtkPrintOperation *operation,
+ gint image_ID)
+{
+ gchar *name = gimp_image_get_name (image_ID);
+
+ gtk_print_operation_set_job_name (operation, name);
+
+ g_free (name);
+}
+
+static void
+begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ PrintData *data)
+{
+ gtk_print_operation_set_use_full_page (operation, data->use_full_page);
+
+ gimp_progress_init (_("Printing"));
+}
+
+static void
+end_print (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint32 *layer_ID)
+{
+ /* we don't need the print layer any longer, delete it */
+ if (gimp_item_is_valid (*layer_ID))
+ {
+ gimp_item_delete (*layer_ID);
+ *layer_ID = -1;
+ }
+
+ gimp_progress_end ();
+
+ /* generate events to solve the problems described in bug #466928 */
+ g_timeout_add_seconds (1, (GSourceFunc) gtk_true, NULL);
+}
+
+static void
+draw_page (GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ PrintData *data)
+{
+ GError *error = NULL;
+
+ if (print_draw_page (context, data, &error))
+ {
+ gimp_progress_update (1.0);
+ }
+ else
+ {
+ print_show_error (error->message);
+ g_error_free (error);
+ }
+}
+
+
+/*
+ * This callback creates a "custom" widget that gets inserted into the
+ * print operation dialog.
+ */
+static GtkWidget *
+create_custom_widget (GtkPrintOperation *operation,
+ PrintData *data)
+{
+ return print_page_layout_gui (data, PRINT_PROC_NAME);
+}
+
+#ifndef EMBED_PAGE_SETUP
+static void
+print_temp_proc_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ if (print_operation && nparams == 1)
+ print_page_setup_load (print_operation, param[0].data.d_int32);
+}
+
+static gchar *
+print_temp_proc_name (gint32 image_ID)
+{
+ return g_strdup_printf (PRINT_TEMP_PROC_NAME "-%d", image_ID);
+}
+
+static gchar *
+print_temp_proc_install (gint32 image_ID)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Image to print" }
+ };
+
+ gchar *name = print_temp_proc_name (image_ID);
+
+ gimp_install_temp_proc (name,
+ "DON'T USE THIS ONE",
+ "Temporary procedure to notify the Print plug-in "
+ "about changes to the Page Setup.",
+ "Sven Neumann",
+ "Sven Neumann",
+ "2008",
+ NULL,
+ "",
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0, args, NULL,
+ print_temp_proc_run);
+
+ return name;
+}
+#endif
diff --git a/plug-ins/print/print.h b/plug-ins/print/print.h
new file mode 100644
index 0000000..7a863e4
--- /dev/null
+++ b/plug-ins/print/print.h
@@ -0,0 +1,56 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef G_OS_WIN32
+#define EMBED_PAGE_SETUP 1
+#endif
+
+#define GIMP_PLUGIN_PRINT_ERROR gimp_plugin_print_error_quark ()
+
+typedef enum
+{
+ GIMP_PLUGIN_PRINT_ERROR_FAILED
+} GimpPluginPrintError;
+
+GQuark gimp_plugin_print_error_quark (void);
+
+typedef enum
+{
+ CENTER_NONE = 0,
+ CENTER_HORIZONTALLY = 1,
+ CENTER_VERTICALLY = 2,
+ CENTER_BOTH = 3
+} PrintCenterMode;
+
+typedef struct
+{
+ gint32 image_id;
+ gint32 drawable_id;
+ GimpUnit unit;
+ gdouble xres;
+ gdouble yres;
+ gdouble min_xres;
+ gdouble min_yres;
+ GimpUnit image_unit;
+ gdouble offset_x;
+ gdouble offset_y;
+ PrintCenterMode center;
+ gboolean use_full_page;
+ gboolean draw_crop_marks;
+ GtkPrintOperation *operation;
+} PrintData;
diff --git a/plug-ins/pygimp/Makefile.am b/plug-ins/pygimp/Makefile.am
new file mode 100644
index 0000000..801b78a
--- /dev/null
+++ b/plug-ins/pygimp/Makefile.am
@@ -0,0 +1,188 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la
+
+SUBDIRS = plug-ins
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+shrext = -shrext .pyd
+path_separator = ;
+else
+path_separator = :
+endif
+
+AM_CFLAGS = $(PYGIMP_EXTRA_CFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(PYTHON_INCLUDES) \
+ $(PYGTK_CFLAGS) \
+ $(PYCAIRO_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS)
+
+pygimpbase = python
+
+pygimpdir = $(gimpplugindir)/$(pygimpbase)
+
+pygimp_LTLIBRARIES = gimp.la _gimpenums.la gimpcolor.la _gimpui.la \
+ gimpthumb.la
+
+gimp_la_SOURCES = \
+ gimpmodule.c \
+ pygimp-item.c \
+ pygimp-display.c \
+ pygimp-drawable.c \
+ pygimp-image.c \
+ pygimp-parasite.c \
+ pygimp-pdb.c \
+ pygimp-tile.c \
+ pygimp-vectors.c \
+ pygimp.h \
+ pygimp-api.h \
+ pygimp-intl.h \
+ pygimp-util.h
+
+gimp_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimp
+
+gimp_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpui) \
+ $(GLIB_LIBS) \
+ $(GEGL_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS)
+
+_gimpenums_la_SOURCES = \
+ gimpenumsmodule.c
+
+_gimpenums_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex init_gimpenums
+
+_gimpenums_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS)
+
+gimpcolor_la_SOURCES = \
+ gimpcolormodule.c \
+ pygimp-colors.c \
+ pygimpcolor.h \
+ pygimpcolor-api.h
+
+gimpcolor_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimpcolor
+
+gimpcolor_la_LIBADD = \
+ $(libgimpcolor) \
+ $(GLIB_LIBS) \
+ $(PYLINK_LIBS)
+
+_gimpui_la_SOURCES = \
+ gimpuimodule.c \
+ gimpui.c
+
+_gimpui_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex init_gimpui
+
+_gimpui_la_LIBADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
+
+gimpthumb_la_SOURCES = \
+ gimpthumbmodule.c \
+ gimpthumb.c
+
+gimpthumb_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimpthumb
+
+gimpthumb_la_LIBADD = \
+ $(libgimpthumb) \
+ $(GDK_PIXBUF_LIBS) \
+ $(PYLINK_LIBS)
+
+pygimp_PYTHON = \
+ gimpenums.py \
+ gimpfu.py \
+ gimpplugin.py \
+ gimpshelf.py \
+ gimpui.py
+
+pygimp_DATA = pygimp-logo.png
+
+codegen_files = \
+ gimp-types.defs \
+ gimpcolor-types.defs \
+ gimpenums-types.defs \
+ gimpthumb.defs \
+ gimpthumb.override \
+ gimpui.defs \
+ gimpui.override
+
+pyenvdir = $(gimpplugindir)/environ
+pyinterpdir = $(gimpplugindir)/interpreters
+
+pyenvfile = $(pyenvdir)/pygimp.env
+pyinterpfile = $(pyinterpdir)/pygimp.interp
+
+install-env-file:
+ $(mkinstalldirs) '$(DESTDIR)$(pyenvdir)'
+ echo '$(path_separator) PYTHONPATH=$${gimp_plug_in_dir}/$(pygimpbase)' > '$(DESTDIR)$(pyenvfile)'
+
+install-interp-file:
+ $(mkinstalldirs) '$(DESTDIR)$(pyinterpdir)'
+ echo 'python=$(PYBIN_PATH)' > '$(DESTDIR)$(pyinterpfile)'
+ echo 'python2=$(PYBIN_PATH)' >> '$(DESTDIR)$(pyinterpfile)'
+ echo '/usr/bin/python=$(PYBIN_PATH)' >> '$(DESTDIR)$(pyinterpfile)'
+ echo ":Python:E::py::`basename $(PYTHON)`:" >> '$(DESTDIR)$(pyinterpfile)'
+
+install-data-local: install-env-file install-interp-file
+
+uninstall-local:
+ rm -f '$(DESTDIR)$(pyenvfile)' '$(DESTDIR)$(pyinterpfile)'
+
+EXTRA_DIST = \
+ $(pygimp_DATA) \
+ $(codegen_files)
+
+gimpui.c: gimpui.defs gimpui.override gimp-types.defs gimpcolor-types.defs gimpenums-types.defs
+
+gimpthumb.c: gimpthumb.defs gimpthumb.override
+
+CLEANFILES = gimpui.c gimpthumb.c
+
+.defs.c:
+ (cd $(srcdir) \
+ && $(PYGTK_CODEGEN) \
+ --override $*.override \
+ --register $(PYGTK_DEFSDIR)/gdk-types.defs \
+ --register $(PYGTK_DEFSDIR)/gtk-types.defs \
+ --register gimp-types.defs \
+ --register gimpcolor-types.defs \
+ --register gimpenums-types.defs \
+ --prefix $* $*.defs) > gen-$*.c \
+ && cp gen-$*.c $*.c \
+ && rm -f gen-$*.c
diff --git a/plug-ins/pygimp/Makefile.in b/plug-ins/pygimp/Makefile.in
new file mode 100644
index 0000000..7055e26
--- /dev/null
+++ b/plug-ins/pygimp/Makefile.in
@@ -0,0 +1,1431 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/pygimp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(pygimp_PYTHON) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(pygimpdir)" "$(DESTDIR)$(pygimpdir)" \
+ "$(DESTDIR)$(pygimpdir)"
+LTLIBRARIES = $(pygimp_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+_gimpenums_la_DEPENDENCIES = $(libgimp) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__gimpenums_la_OBJECTS = gimpenumsmodule.lo
+_gimpenums_la_OBJECTS = $(am__gimpenums_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+_gimpenums_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(_gimpenums_la_LDFLAGS) $(LDFLAGS) -o $@
+_gimpui_la_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimpmath) $(libgimp) $(libgimpcolor) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am__gimpui_la_OBJECTS = gimpuimodule.lo gimpui.lo
+_gimpui_la_OBJECTS = $(am__gimpui_la_OBJECTS)
+_gimpui_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(_gimpui_la_LDFLAGS) $(LDFLAGS) -o $@
+gimp_la_DEPENDENCIES = $(libgimp) $(libgimpcolor) $(libgimpbase) \
+ $(libgimpui) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_gimp_la_OBJECTS = gimpmodule.lo pygimp-item.lo pygimp-display.lo \
+ pygimp-drawable.lo pygimp-image.lo pygimp-parasite.lo \
+ pygimp-pdb.lo pygimp-tile.lo pygimp-vectors.lo
+gimp_la_OBJECTS = $(am_gimp_la_OBJECTS)
+gimp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(gimp_la_LDFLAGS) $(LDFLAGS) -o $@
+gimpcolor_la_DEPENDENCIES = $(libgimpcolor) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_gimpcolor_la_OBJECTS = gimpcolormodule.lo pygimp-colors.lo
+gimpcolor_la_OBJECTS = $(am_gimpcolor_la_OBJECTS)
+gimpcolor_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(gimpcolor_la_LDFLAGS) $(LDFLAGS) -o $@
+gimpthumb_la_DEPENDENCIES = $(libgimpthumb) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_gimpthumb_la_OBJECTS = gimpthumbmodule.lo gimpthumb.lo
+gimpthumb_la_OBJECTS = $(am_gimpthumb_la_OBJECTS)
+gimpthumb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(gimpthumb_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/gimpcolormodule.Plo \
+ ./$(DEPDIR)/gimpenumsmodule.Plo ./$(DEPDIR)/gimpmodule.Plo \
+ ./$(DEPDIR)/gimpthumb.Plo ./$(DEPDIR)/gimpthumbmodule.Plo \
+ ./$(DEPDIR)/gimpui.Plo ./$(DEPDIR)/gimpuimodule.Plo \
+ ./$(DEPDIR)/pygimp-colors.Plo ./$(DEPDIR)/pygimp-display.Plo \
+ ./$(DEPDIR)/pygimp-drawable.Plo ./$(DEPDIR)/pygimp-image.Plo \
+ ./$(DEPDIR)/pygimp-item.Plo ./$(DEPDIR)/pygimp-parasite.Plo \
+ ./$(DEPDIR)/pygimp-pdb.Plo ./$(DEPDIR)/pygimp-tile.Plo \
+ ./$(DEPDIR)/pygimp-vectors.Plo
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(_gimpenums_la_SOURCES) $(_gimpui_la_SOURCES) \
+ $(gimp_la_SOURCES) $(gimpcolor_la_SOURCES) \
+ $(gimpthumb_la_SOURCES)
+DIST_SOURCES = $(_gimpenums_la_SOURCES) $(_gimpui_la_SOURCES) \
+ $(gimp_la_SOURCES) $(gimpcolor_la_SOURCES) \
+ $(gimpthumb_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__py_compile = PYTHON=$(PYTHON) $(SHELL) $(py_compile)
+am__pep3147_tweak = \
+ sed -e 's|\.py$$||' -e 's|[^/]*$$|__pycache__/&.*.pyc __pycache__/&.*.pyo|'
+py_compile = $(top_srcdir)/py-compile
+DATA = $(pygimp_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \
+ $(top_srcdir)/py-compile py-compile
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+libgimpthumb = $(top_builddir)/libgimpthumb/libgimpthumb-$(GIMP_API_VERSION).la
+SUBDIRS = plug-ins
+@PLATFORM_WIN32_TRUE@no_undefined = -no-undefined
+@PLATFORM_WIN32_TRUE@shrext = -shrext .pyd
+@PLATFORM_WIN32_FALSE@path_separator = :
+@PLATFORM_WIN32_TRUE@path_separator = ;
+AM_CFLAGS = $(PYGIMP_EXTRA_CFLAGS)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(PYTHON_INCLUDES) \
+ $(PYGTK_CFLAGS) \
+ $(PYCAIRO_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS)
+
+pygimpbase = python
+pygimpdir = $(gimpplugindir)/$(pygimpbase)
+pygimp_LTLIBRARIES = gimp.la _gimpenums.la gimpcolor.la _gimpui.la \
+ gimpthumb.la
+
+gimp_la_SOURCES = \
+ gimpmodule.c \
+ pygimp-item.c \
+ pygimp-display.c \
+ pygimp-drawable.c \
+ pygimp-image.c \
+ pygimp-parasite.c \
+ pygimp-pdb.c \
+ pygimp-tile.c \
+ pygimp-vectors.c \
+ pygimp.h \
+ pygimp-api.h \
+ pygimp-intl.h \
+ pygimp-util.h
+
+gimp_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimp
+
+gimp_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libgimpui) \
+ $(GLIB_LIBS) \
+ $(GEGL_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS)
+
+_gimpenums_la_SOURCES = \
+ gimpenumsmodule.c
+
+_gimpenums_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex init_gimpenums
+
+_gimpenums_la_LIBADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS)
+
+gimpcolor_la_SOURCES = \
+ gimpcolormodule.c \
+ pygimp-colors.c \
+ pygimpcolor.h \
+ pygimpcolor-api.h
+
+gimpcolor_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimpcolor
+
+gimpcolor_la_LIBADD = \
+ $(libgimpcolor) \
+ $(GLIB_LIBS) \
+ $(PYLINK_LIBS)
+
+_gimpui_la_SOURCES = \
+ gimpuimodule.c \
+ gimpui.c
+
+_gimpui_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex init_gimpui
+
+_gimpui_la_LIBADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(PYLINK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
+
+gimpthumb_la_SOURCES = \
+ gimpthumbmodule.c \
+ gimpthumb.c
+
+gimpthumb_la_LDFLAGS = -module -avoid-version $(no_undefined) $(shrext) \
+ -export-symbols-regex initgimpthumb
+
+gimpthumb_la_LIBADD = \
+ $(libgimpthumb) \
+ $(GDK_PIXBUF_LIBS) \
+ $(PYLINK_LIBS)
+
+pygimp_PYTHON = \
+ gimpenums.py \
+ gimpfu.py \
+ gimpplugin.py \
+ gimpshelf.py \
+ gimpui.py
+
+pygimp_DATA = pygimp-logo.png
+codegen_files = \
+ gimp-types.defs \
+ gimpcolor-types.defs \
+ gimpenums-types.defs \
+ gimpthumb.defs \
+ gimpthumb.override \
+ gimpui.defs \
+ gimpui.override
+
+pyenvdir = $(gimpplugindir)/environ
+pyinterpdir = $(gimpplugindir)/interpreters
+pyenvfile = $(pyenvdir)/pygimp.env
+pyinterpfile = $(pyinterpdir)/pygimp.interp
+EXTRA_DIST = \
+ $(pygimp_DATA) \
+ $(codegen_files)
+
+CLEANFILES = gimpui.c gimpthumb.c
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .defs .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/pygimp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/pygimp/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-pygimpLTLIBRARIES: $(pygimp_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(pygimp_LTLIBRARIES)'; test -n "$(pygimpdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pygimpdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pygimpdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pygimpdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pygimpdir)"; \
+ }
+
+uninstall-pygimpLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pygimp_LTLIBRARIES)'; test -n "$(pygimpdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pygimpdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pygimpdir)/$$f"; \
+ done
+
+clean-pygimpLTLIBRARIES:
+ -test -z "$(pygimp_LTLIBRARIES)" || rm -f $(pygimp_LTLIBRARIES)
+ @list='$(pygimp_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+_gimpenums.la: $(_gimpenums_la_OBJECTS) $(_gimpenums_la_DEPENDENCIES) $(EXTRA__gimpenums_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(_gimpenums_la_LINK) -rpath $(pygimpdir) $(_gimpenums_la_OBJECTS) $(_gimpenums_la_LIBADD) $(LIBS)
+
+_gimpui.la: $(_gimpui_la_OBJECTS) $(_gimpui_la_DEPENDENCIES) $(EXTRA__gimpui_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(_gimpui_la_LINK) -rpath $(pygimpdir) $(_gimpui_la_OBJECTS) $(_gimpui_la_LIBADD) $(LIBS)
+
+gimp.la: $(gimp_la_OBJECTS) $(gimp_la_DEPENDENCIES) $(EXTRA_gimp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(gimp_la_LINK) -rpath $(pygimpdir) $(gimp_la_OBJECTS) $(gimp_la_LIBADD) $(LIBS)
+
+gimpcolor.la: $(gimpcolor_la_OBJECTS) $(gimpcolor_la_DEPENDENCIES) $(EXTRA_gimpcolor_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(gimpcolor_la_LINK) -rpath $(pygimpdir) $(gimpcolor_la_OBJECTS) $(gimpcolor_la_LIBADD) $(LIBS)
+
+gimpthumb.la: $(gimpthumb_la_OBJECTS) $(gimpthumb_la_DEPENDENCIES) $(EXTRA_gimpthumb_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(gimpthumb_la_LINK) -rpath $(pygimpdir) $(gimpthumb_la_OBJECTS) $(gimpthumb_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpcolormodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpenumsmodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpmodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpthumbmodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpui.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpuimodule.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-colors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-display.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-drawable.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-image.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-item.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-parasite.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-pdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-tile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pygimp-vectors.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pygimpPYTHON: $(pygimp_PYTHON)
+ @$(NORMAL_INSTALL)
+ @list='$(pygimp_PYTHON)'; dlist=; list2=; test -n "$(pygimpdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pygimpdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pygimpdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then b=; else b="$(srcdir)/"; fi; \
+ if test -f $$b$$p; then \
+ $(am__strip_dir) \
+ dlist="$$dlist $$f"; \
+ list2="$$list2 $$b$$p"; \
+ else :; fi; \
+ done; \
+ for file in $$list2; do echo $$file; done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pygimpdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pygimpdir)" || exit $$?; \
+ done || exit $$?; \
+ if test -n "$$dlist"; then \
+ $(am__py_compile) --destdir "$(DESTDIR)" \
+ --basedir "$(pygimpdir)" $$dlist; \
+ else :; fi
+
+uninstall-pygimpPYTHON:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pygimp_PYTHON)'; test -n "$(pygimpdir)" || list=; \
+ py_files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$py_files" || exit 0; \
+ dir='$(DESTDIR)$(pygimpdir)'; \
+ pyc_files=`echo "$$py_files" | sed 's|$$|c|'`; \
+ pyo_files=`echo "$$py_files" | sed 's|$$|o|'`; \
+ st=0; \
+ for files in "$$py_files" "$$pyc_files" "$$pyo_files"; do \
+ $(am__uninstall_files_from_dir) || st=$$?; \
+ done; \
+ dir='$(DESTDIR)$(pygimpdir)'; \
+ echo "$$py_files" | $(am__pep3147_tweak) | $(am__base_list) | \
+ while read files; do \
+ $(am__uninstall_files_from_dir) || st=$$?; \
+ done || exit $$?; \
+ exit $$st
+install-pygimpDATA: $(pygimp_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pygimp_DATA)'; test -n "$(pygimpdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pygimpdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pygimpdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pygimpdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pygimpdir)" || exit $$?; \
+ done
+
+uninstall-pygimpDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pygimp_DATA)'; test -n "$(pygimpdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pygimpdir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(pygimpdir)" "$(DESTDIR)$(pygimpdir)" "$(DESTDIR)$(pygimpdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-pygimpLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/gimpcolormodule.Plo
+ -rm -f ./$(DEPDIR)/gimpenumsmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb.Plo
+ -rm -f ./$(DEPDIR)/gimpthumbmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpui.Plo
+ -rm -f ./$(DEPDIR)/gimpuimodule.Plo
+ -rm -f ./$(DEPDIR)/pygimp-colors.Plo
+ -rm -f ./$(DEPDIR)/pygimp-display.Plo
+ -rm -f ./$(DEPDIR)/pygimp-drawable.Plo
+ -rm -f ./$(DEPDIR)/pygimp-image.Plo
+ -rm -f ./$(DEPDIR)/pygimp-item.Plo
+ -rm -f ./$(DEPDIR)/pygimp-parasite.Plo
+ -rm -f ./$(DEPDIR)/pygimp-pdb.Plo
+ -rm -f ./$(DEPDIR)/pygimp-tile.Plo
+ -rm -f ./$(DEPDIR)/pygimp-vectors.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-data-local install-pygimpDATA \
+ install-pygimpLTLIBRARIES install-pygimpPYTHON
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/gimpcolormodule.Plo
+ -rm -f ./$(DEPDIR)/gimpenumsmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpthumb.Plo
+ -rm -f ./$(DEPDIR)/gimpthumbmodule.Plo
+ -rm -f ./$(DEPDIR)/gimpui.Plo
+ -rm -f ./$(DEPDIR)/gimpuimodule.Plo
+ -rm -f ./$(DEPDIR)/pygimp-colors.Plo
+ -rm -f ./$(DEPDIR)/pygimp-display.Plo
+ -rm -f ./$(DEPDIR)/pygimp-drawable.Plo
+ -rm -f ./$(DEPDIR)/pygimp-image.Plo
+ -rm -f ./$(DEPDIR)/pygimp-item.Plo
+ -rm -f ./$(DEPDIR)/pygimp-parasite.Plo
+ -rm -f ./$(DEPDIR)/pygimp-pdb.Plo
+ -rm -f ./$(DEPDIR)/pygimp-tile.Plo
+ -rm -f ./$(DEPDIR)/pygimp-vectors.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-local uninstall-pygimpDATA \
+ uninstall-pygimpLTLIBRARIES uninstall-pygimpPYTHON
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic clean-libtool \
+ clean-pygimpLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-pygimpDATA \
+ install-pygimpLTLIBRARIES install-pygimpPYTHON install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-local uninstall-pygimpDATA \
+ uninstall-pygimpLTLIBRARIES uninstall-pygimpPYTHON
+
+.PRECIOUS: Makefile
+
+
+install-env-file:
+ $(mkinstalldirs) '$(DESTDIR)$(pyenvdir)'
+ echo '$(path_separator) PYTHONPATH=$${gimp_plug_in_dir}/$(pygimpbase)' > '$(DESTDIR)$(pyenvfile)'
+
+install-interp-file:
+ $(mkinstalldirs) '$(DESTDIR)$(pyinterpdir)'
+ echo 'python=$(PYBIN_PATH)' > '$(DESTDIR)$(pyinterpfile)'
+ echo 'python2=$(PYBIN_PATH)' >> '$(DESTDIR)$(pyinterpfile)'
+ echo '/usr/bin/python=$(PYBIN_PATH)' >> '$(DESTDIR)$(pyinterpfile)'
+ echo ":Python:E::py::`basename $(PYTHON)`:" >> '$(DESTDIR)$(pyinterpfile)'
+
+install-data-local: install-env-file install-interp-file
+
+uninstall-local:
+ rm -f '$(DESTDIR)$(pyenvfile)' '$(DESTDIR)$(pyinterpfile)'
+
+gimpui.c: gimpui.defs gimpui.override gimp-types.defs gimpcolor-types.defs gimpenums-types.defs
+
+gimpthumb.c: gimpthumb.defs gimpthumb.override
+
+.defs.c:
+ (cd $(srcdir) \
+ && $(PYGTK_CODEGEN) \
+ --override $*.override \
+ --register $(PYGTK_DEFSDIR)/gdk-types.defs \
+ --register $(PYGTK_DEFSDIR)/gtk-types.defs \
+ --register gimp-types.defs \
+ --register gimpcolor-types.defs \
+ --register gimpenums-types.defs \
+ --prefix $* $*.defs) > gen-$*.c \
+ && cp gen-$*.c $*.c \
+ && rm -f gen-$*.c
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/pygimp/gimp-types.defs b/plug-ins/pygimp/gimp-types.defs
new file mode 100644
index 0000000..0158ede
--- /dev/null
+++ b/plug-ins/pygimp/gimp-types.defs
@@ -0,0 +1,9 @@
+;; -*- scheme -*-
+
+;; from libgimpbase, implemented manually in pygimp-parasite.c
+
+(define-boxed GimpParasite
+ (in-module "Gimp")
+ (c-name "GimpParasite")
+ (gtype-id "GIMP_TYPE_PARASITE")
+)
diff --git a/plug-ins/pygimp/gimpcolor-types.defs b/plug-ins/pygimp/gimpcolor-types.defs
new file mode 100644
index 0000000..460ac92
--- /dev/null
+++ b/plug-ins/pygimp/gimpcolor-types.defs
@@ -0,0 +1,25 @@
+;; -*- scheme -*-
+
+(define-boxed GimpRGB
+ (in-module "Gimp")
+ (c-name "GimpRGB")
+ (gtype-id "GIMP_TYPE_RGB")
+)
+
+(define-boxed GimpHSV
+ (in-module "Gimp")
+ (c-name "GimpHSV")
+ (gtype-id "GIMP_TYPE_HSV")
+)
+
+(define-boxed GimpHSL
+ (in-module "Gimp")
+ (c-name "GimpHSL")
+ (gtype-id "GIMP_TYPE_HSL")
+)
+
+(define-boxed GimpCMYK
+ (in-module "Gimp")
+ (c-name "GimpCMYK")
+ (gtype-id "GIMP_TYPE_CMYK")
+)
diff --git a/plug-ins/pygimp/gimpcolormodule.c b/plug-ins/pygimp/gimpcolormodule.c
new file mode 100644
index 0000000..8b4a478
--- /dev/null
+++ b/plug-ins/pygimp/gimpcolormodule.c
@@ -0,0 +1,433 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005-2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "pygimpcolor.h"
+
+#define _INSIDE_PYGIMPCOLOR_
+#include "pygimpcolor-api.h"
+
+#include "pygimp-util.h"
+
+
+static PyObject *
+pygimp_rgb_parse_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int len;
+ GimpRGB rgb;
+ gboolean success;
+ static char *kwlist[] = { "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:rgb_parse_name", kwlist,
+ &name, &len))
+ return NULL;
+
+ rgb.a = 1.0;
+ success = gimp_rgb_parse_name(&rgb, name, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse color name");
+ return NULL;
+ }
+
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_rgb_parse_hex(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *hex;
+ int len;
+ GimpRGB rgb;
+ gboolean success;
+ static char *kwlist[] = { "hex", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:rgb_parse_hex", kwlist,
+ &hex, &len))
+ return NULL;
+
+ rgb.a = 1.0;
+ success = gimp_rgb_parse_hex(&rgb, hex, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse hex value");
+ return NULL;
+ }
+
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_rgb_parse_css(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *css;
+ int len;
+ GimpRGB rgb;
+ gboolean success, with_alpha = FALSE;
+ static char *kwlist[] = { "css", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s#|i:rgb_parse_css", kwlist,
+ &css, &len, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ success = gimp_rgba_parse_css(&rgb, css, len);
+ else {
+ rgb.a = 1.0;
+ success = gimp_rgb_parse_css(&rgb, css, len);
+ }
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse CSS color");
+ return NULL;
+ }
+
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_rgb_list_names(PyObject *self)
+{
+ int num_names, i;
+ const char **names;
+ GimpRGB *colors;
+ PyObject *dict, *color;
+
+ num_names = gimp_rgb_list_names(&names, &colors);
+
+ dict = PyDict_New();
+ if (!dict)
+ goto cleanup;
+
+ for (i = 0; i < num_names; i++) {
+ color = pygimp_rgb_new(&colors[i]);
+
+ if (!color)
+ goto bail;
+
+ if (PyDict_SetItemString(dict, names[i], color) < 0) {
+ Py_DECREF(color);
+ goto bail;
+ }
+
+ Py_DECREF(color);
+ }
+
+ goto cleanup;
+
+bail:
+ Py_DECREF(dict);
+ dict = NULL;
+
+cleanup:
+ g_free(names);
+ g_free(colors);
+
+ return dict;
+}
+
+static PyObject *
+pygimp_bilinear(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ gdouble x, y;
+ gdouble values[4];
+ PyObject *py_values;
+ static char *kwlist[] = { "x", "y", "values", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddO:bilinear", kwlist,
+ &x, &y, &py_values))
+ return NULL;
+
+ if (PyString_Check(py_values)) {
+ if (PyString_Size(py_values) == 4) {
+ guchar ret;
+ ret = gimp_bilinear_8(x, y, (guchar *)PyString_AsString(py_values));
+ return PyString_FromStringAndSize((char *)&ret, 1);
+ }
+ } else if (PySequence_Check(py_values)) {
+ if (PySequence_Size(py_values) == 4) {
+ int i;
+ for (i = 0; i < 4; i++) {
+ PyObject *v;
+ v = PySequence_GetItem(py_values, i);
+ values[i] = PyFloat_AsDouble(v);
+ Py_DECREF(v);
+ }
+ return PyFloat_FromDouble(gimp_bilinear(x, y, values));
+ }
+ }
+
+ PyErr_SetString(PyExc_TypeError, "values is not a sequence of 4 items");
+ return NULL;
+}
+
+static PyObject *
+pygimp_bilinear_color(PyObject *self, PyObject *args, PyObject *kwargs, gboolean with_alpha)
+{
+ gdouble x, y;
+ GimpRGB values[4];
+ GimpRGB rgb;
+ PyObject *py_values, *v;
+ int i, success;
+ static char *kwlist[] = { "x", "y", "values", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ with_alpha ? "ddO:bilinear_rgba"
+ : "ddO:bilinear_rgb",
+ kwlist,
+ &x, &y, &py_values))
+ return NULL;
+
+ if (!PySequence_Check(py_values) || PySequence_Size(py_values) != 4) {
+ PyErr_SetString(PyExc_TypeError, "values is not a sequence of 4 items");
+ return NULL;
+ }
+
+ for (i = 0; i < 4; i++) {
+ v = PySequence_GetItem(py_values, i);
+ success = pygimp_rgb_from_pyobject(v, &values[i]);
+ Py_DECREF(v);
+ if (!success) {
+ PyErr_Format(PyExc_TypeError, "values[%d] is not a GimpRGB", i);
+ return NULL;
+ }
+ }
+
+ if (with_alpha)
+ rgb = gimp_bilinear_rgba(x, y, values);
+ else
+ rgb = gimp_bilinear_rgb(x, y, values);
+
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_bilinear_rgb(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ return pygimp_bilinear_color(self, args, kwargs, FALSE);
+}
+
+static PyObject *
+pygimp_bilinear_rgba(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ return pygimp_bilinear_color(self, args, kwargs, TRUE);
+}
+
+#if 0
+static PyObject *
+pygimp_bilinear_pixels_8(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+typedef struct
+{
+ PyObject *func;
+ PyObject *data;
+} ProxyData;
+
+static void
+proxy_render(gdouble x, gdouble y, GimpRGB *color, gpointer pdata)
+{
+ ProxyData *data = pdata;
+
+ if (data->data)
+ PyObject_CallFunction(data->func, "ddO&O", x, y, pygimp_rgb_new, color,
+ data->data);
+ else
+ PyObject_CallFunction(data->func, "ddO&", x, y, pygimp_rgb_new, color);
+}
+
+static void
+proxy_put_pixel(gint x, gint y, GimpRGB *color, gpointer pdata)
+{
+ ProxyData *data = pdata;
+
+ if (data->data)
+ PyObject_CallFunction(data->func, "iiO&O", x, y, pygimp_rgb_new, color,
+ data->data);
+ else
+ PyObject_CallFunction(data->func, "iiO&", x, y, pygimp_rgb_new, color);
+}
+
+static void
+proxy_progress(gint min, gint max, gint current, gpointer pdata)
+{
+ ProxyData *data = pdata;
+
+ if (data->data)
+ PyObject_CallFunction(data->func, "iiiO", min, max, current,
+ data->data);
+ else
+ PyObject_CallFunction(data->func, "iii", min, max, current);
+}
+
+static PyObject *
+pygimp_adaptive_supersample_area(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ gulong r;
+
+ gint x1, y1, x2, y2, max_depth;
+ gdouble threshold;
+ PyObject *py_func_render = NULL, *py_data_render = NULL;
+ PyObject *py_func_put_pixel = NULL, *py_data_put_pixel = NULL;
+ PyObject *py_func_progress = NULL, *py_data_progress = NULL;
+
+ GimpRenderFunc proxy_func_render = NULL;
+ GimpPutPixelFunc proxy_func_put_pixel = NULL;
+ GimpProgressFunc proxy_func_progress = NULL;
+
+ ProxyData proxy_data_render, proxy_data_put_pixel, proxy_data_progress;
+
+ static char *kwlist[] = {
+ "x1", "y1", "x2", "y2", "max_depth", "threshold",
+ "render_func", "render_data",
+ "put_pixel_func", "put_pixel_data",
+ "progress_func", "progress_data",
+ NULL
+ };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "iiiiid|OOOOOO"
+ ":adaptive_supersample_area",
+ kwlist,
+ &x1, &y1, &x2, &y2, &max_depth, &threshold,
+ &py_func_render, &py_data_render,
+ &py_func_put_pixel, &py_data_put_pixel,
+ &py_func_progress, &py_data_progress))
+ return NULL;
+
+#define PROCESS_FUNC(n) G_STMT_START { \
+ if (py_func_##n != NULL) { \
+ if (!PyCallable_Check(py_func_##n)) { \
+ PyErr_SetString(PyExc_TypeError, #n "_func " \
+ "must be callable"); \
+ return NULL; \
+ } \
+ \
+ proxy_func_##n = proxy_##n; \
+ \
+ proxy_data_##n.func = py_func_##n; \
+ proxy_data_##n.data = py_data_##n; \
+ } \
+} G_STMT_END
+
+ PROCESS_FUNC(render);
+ PROCESS_FUNC(put_pixel);
+ PROCESS_FUNC(progress);
+
+#undef PROCESS_FUNC
+
+#define PASS_FUNC(n) proxy_func_##n, &proxy_data_##n
+
+ r = gimp_adaptive_supersample_area (x1, y1, x2, y2, max_depth, threshold,
+ PASS_FUNC(render),
+ PASS_FUNC(put_pixel),
+ PASS_FUNC(progress));
+
+#undef PASS_FUNC
+
+ return PyInt_FromLong(r);
+}
+#endif
+
+/* List of methods defined in the module */
+
+static struct PyMethodDef gimpcolor_methods[] = {
+ {"rgb_parse_name", (PyCFunction)pygimp_rgb_parse_name, METH_VARARGS | METH_KEYWORDS},
+ {"rgb_parse_hex", (PyCFunction)pygimp_rgb_parse_hex, METH_VARARGS | METH_KEYWORDS},
+ {"rgb_parse_css", (PyCFunction)pygimp_rgb_parse_css, METH_VARARGS | METH_KEYWORDS},
+ {"rgb_names", (PyCFunction)pygimp_rgb_list_names, METH_NOARGS},
+ {"bilinear", (PyCFunction)pygimp_bilinear, METH_VARARGS | METH_KEYWORDS},
+ {"bilinear_rgb", (PyCFunction)pygimp_bilinear_rgb, METH_VARARGS | METH_KEYWORDS},
+ {"bilinear_rgba", (PyCFunction)pygimp_bilinear_rgba, METH_VARARGS | METH_KEYWORDS},
+#if 0
+ {"bilinear_pixels_8", (PyCFunction)pygimp_bilinear_pixels_8, METH_VARARGS | METH_KEYWORDS},
+ {"adaptive_supersample_area", (PyCFunction)pygimp_adaptive_supersample_area, METH_VARARGS | METH_KEYWORDS},
+#endif
+ {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
+};
+
+
+static struct _PyGimpColor_Functions pygimpcolor_api_functions = {
+ &PyGimpRGB_Type,
+ pygimp_rgb_new,
+ &PyGimpHSV_Type,
+ pygimp_hsv_new,
+ &PyGimpHSL_Type,
+ pygimp_hsl_new,
+ &PyGimpCMYK_Type,
+ pygimp_cmyk_new,
+ pygimp_rgb_from_pyobject
+};
+
+
+/* Initialization function for the module (*must* be called initgimpcolor) */
+
+static char gimpcolor_doc[] =
+"This module provides interfaces to allow you to write gimp plug-ins"
+;
+
+void initgimpcolor(void);
+
+PyMODINIT_FUNC
+initgimpcolor(void)
+{
+ PyObject *m, *d;
+
+ pygimp_init_pygobject();
+
+ /* Create the module and add the functions */
+ m = Py_InitModule3("gimpcolor", gimpcolor_methods, gimpcolor_doc);
+
+ d = PyModule_GetDict(m);
+
+ pyg_register_boxed(d, "RGB", GIMP_TYPE_RGB, &PyGimpRGB_Type);
+ pyg_register_boxed(d, "HSV", GIMP_TYPE_HSV, &PyGimpHSV_Type);
+ pyg_register_boxed(d, "HSL", GIMP_TYPE_HSL, &PyGimpHSL_Type);
+ pyg_register_boxed(d, "CMYK", GIMP_TYPE_CMYK, &PyGimpCMYK_Type);
+
+ PyModule_AddObject(m, "RGB_COMPOSITE_NONE",
+ PyInt_FromLong(GIMP_RGB_COMPOSITE_NONE));
+ PyModule_AddObject(m, "RGB_COMPOSITE_NORMAL",
+ PyInt_FromLong(GIMP_RGB_COMPOSITE_NORMAL));
+ PyModule_AddObject(m, "RGB_COMPOSITE_BEHIND",
+ PyInt_FromLong(GIMP_RGB_COMPOSITE_BEHIND));
+
+ PyModule_AddObject(m, "RGB_LUMINANCE_RED",
+ PyFloat_FromDouble(GIMP_RGB_LUMINANCE_RED));
+ PyModule_AddObject(m, "RGB_LUMINANCE_GREEN",
+ PyFloat_FromDouble(GIMP_RGB_LUMINANCE_GREEN));
+ PyModule_AddObject(m, "RGB_LUMINANCE_BLUE",
+ PyFloat_FromDouble(GIMP_RGB_LUMINANCE_BLUE));
+
+ /* for other modules */
+ PyModule_AddObject(m, "_PyGimpColor_API",
+ PyCObject_FromVoidPtr(&pygimpcolor_api_functions, NULL));
+
+ /* Check for errors */
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module gimpcolor");
+}
diff --git a/plug-ins/pygimp/gimpenums-types.defs b/plug-ins/pygimp/gimpenums-types.defs
new file mode 100644
index 0000000..b919c93
--- /dev/null
+++ b/plug-ins/pygimp/gimpenums-types.defs
@@ -0,0 +1,338 @@
+;; -*- scheme -*-
+; object definitions ...
+;; Enumerations and flags ...
+
+(define-enum BrushApplicationMode
+ (in-module "Gimp")
+ (c-name "GimpBrushApplicationMode")
+ (gtype-id "GIMP_TYPE_BRUSH_APPLICATION_MODE")
+ (values
+ '("hard" "GIMP_BRUSH_HARD")
+ '("soft" "GIMP_BRUSH_SOFT")
+ )
+)
+
+(define-enum BrushGeneratedShape
+ (in-module "Gimp")
+ (c-name "GimpBrushGeneratedShape")
+ (gtype-id "GIMP_TYPE_BRUSH_GENERATED_SHAPE")
+ (values
+ '("circle" "GIMP_BRUSH_GENERATED_CIRCLE")
+ '("square" "GIMP_BRUSH_GENERATED_SQUARE")
+ '("diamond" "GIMP_BRUSH_GENERATED_DIAMOND")
+ )
+)
+
+(define-enum ConvertDitherType
+ (in-module "Gimp")
+ (c-name "GimpConvertDitherType")
+ (gtype-id "GIMP_TYPE_CONVERT_DITHER_TYPE")
+ (values
+ '("no-dither" "GIMP_NO_DITHER")
+ '("fs-dither" "GIMP_FS_DITHER")
+ '("fslowbleed-dither" "GIMP_FSLOWBLEED_DITHER")
+ '("fixed-dither" "GIMP_FIXED_DITHER")
+ )
+)
+
+(define-enum ConvertPaletteType
+ (in-module "Gimp")
+ (c-name "GimpConvertPaletteType")
+ (gtype-id "GIMP_TYPE_CONVERT_PALETTE_TYPE")
+ (values
+ '("make-palette" "GIMP_MAKE_PALETTE")
+ '("reuse-palette" "GIMP_REUSE_PALETTE")
+ '("web-palette" "GIMP_WEB_PALETTE")
+ '("mono-palette" "GIMP_MONO_PALETTE")
+ '("custom-palette" "GIMP_CUSTOM_PALETTE")
+ )
+)
+
+(define-enum ConvolutionType
+ (in-module "Gimp")
+ (c-name "GimpConvolutionType")
+ (gtype-id "GIMP_TYPE_CONVOLUTION_TYPE")
+ (values
+ '("normal-convol" "GIMP_NORMAL_CONVOL")
+ '("absolute-convol" "GIMP_ABSOLUTE_CONVOL")
+ '("negative-convol" "GIMP_NEGATIVE_CONVOL")
+ )
+)
+
+(define-enum ConvolveType
+ (in-module "Gimp")
+ (c-name "GimpConvolveType")
+ (gtype-id "GIMP_TYPE_CONVOLVE_TYPE")
+ (values
+ '("blur-convolve" "GIMP_BLUR_CONVOLVE")
+ '("sharpen-convolve" "GIMP_SHARPEN_CONVOLVE")
+ )
+)
+
+(define-enum FillType
+ (in-module "Gimp")
+ (c-name "GimpFillType")
+ (gtype-id "GIMP_TYPE_FILL_TYPE")
+ (values
+ '("foreground-fill" "GIMP_FOREGROUND_FILL")
+ '("background-fill" "GIMP_BACKGROUND_FILL")
+ '("white-fill" "GIMP_WHITE_FILL")
+ '("transparent-fill" "GIMP_TRANSPARENT_FILL")
+ '("pattern-fill" "GIMP_PATTERN_FILL")
+ )
+)
+
+(define-enum GradientSegmentColor
+ (in-module "Gimp")
+ (c-name "GimpGradientSegmentColor")
+ (gtype-id "GIMP_TYPE_GRADIENT_SEGMENT_COLOR")
+ (values
+ '("rgb" "GIMP_GRADIENT_SEGMENT_RGB")
+ '("hsv-ccw" "GIMP_GRADIENT_SEGMENT_HSV_CCW")
+ '("hsv-cw" "GIMP_GRADIENT_SEGMENT_HSV_CW")
+ )
+)
+
+(define-enum GradientSegmentType
+ (in-module "Gimp")
+ (c-name "GimpGradientSegmentType")
+ (gtype-id "GIMP_TYPE_GRADIENT_SEGMENT_TYPE")
+ (values
+ '("linear" "GIMP_GRADIENT_SEGMENT_LINEAR")
+ '("curved" "GIMP_GRADIENT_SEGMENT_CURVED")
+ '("sine" "GIMP_GRADIENT_SEGMENT_SINE")
+ '("sphere-increasing" "GIMP_GRADIENT_SEGMENT_SPHERE_INCREASING")
+ '("sphere-decreasing" "GIMP_GRADIENT_SEGMENT_SPHERE_DECREASING")
+ )
+)
+
+(define-enum HistogramChannel
+ (in-module "Gimp")
+ (c-name "GimpHistogramChannel")
+ (gtype-id "GIMP_TYPE_HISTOGRAM_CHANNEL")
+ (values
+ '("value" "GIMP_HISTOGRAM_VALUE")
+ '("red" "GIMP_HISTOGRAM_RED")
+ '("green" "GIMP_HISTOGRAM_GREEN")
+ '("blue" "GIMP_HISTOGRAM_BLUE")
+ '("alpha" "GIMP_HISTOGRAM_ALPHA")
+ )
+)
+
+(define-enum HueRange
+ (in-module "Gimp")
+ (c-name "GimpHueRange")
+ (gtype-id "GIMP_TYPE_HUE_RANGE")
+ (values
+ '("all-hues" "GIMP_ALL_HUES")
+ '("red-hues" "GIMP_RED_HUES")
+ '("yellow-hues" "GIMP_YELLOW_HUES")
+ '("green-hues" "GIMP_GREEN_HUES")
+ '("cyan-hues" "GIMP_CYAN_HUES")
+ '("blue-hues" "GIMP_BLUE_HUES")
+ '("magenta-hues" "GIMP_MAGENTA_HUES")
+ )
+)
+
+(define-enum ImageType
+ (in-module "Gimp")
+ (c-name "GimpImageType")
+ (gtype-id "GIMP_TYPE_IMAGE_TYPE")
+ (values
+ '("rgb" "GIMP_RGB_IMAGE")
+ '("rgba" "GIMP_RGBA_IMAGE")
+ '("gray" "GIMP_GRAY_IMAGE")
+ '("graya" "GIMP_GRAYA_IMAGE")
+ '("indexed" "GIMP_INDEXED_IMAGE")
+ '("indexeda" "GIMP_INDEXEDA_IMAGE")
+ )
+)
+
+(define-enum LayerModeEffects
+ (in-module "Gimp")
+ (c-name "GimpLayerModeEffects")
+ (gtype-id "GIMP_TYPE_LAYER_MODE_EFFECTS")
+ (values
+ '("normal-mode" "GIMP_NORMAL_MODE")
+ '("dissolve-mode" "GIMP_DISSOLVE_MODE")
+ '("behind-mode" "GIMP_BEHIND_MODE")
+ '("multiply-mode" "GIMP_MULTIPLY_MODE")
+ '("screen-mode" "GIMP_SCREEN_MODE")
+ '("overlay-mode" "GIMP_OVERLAY_MODE")
+ '("difference-mode" "GIMP_DIFFERENCE_MODE")
+ '("addition-mode" "GIMP_ADDITION_MODE")
+ '("subtract-mode" "GIMP_SUBTRACT_MODE")
+ '("darken-only-mode" "GIMP_DARKEN_ONLY_MODE")
+ '("lighten-only-mode" "GIMP_LIGHTEN_ONLY_MODE")
+ '("hue-mode" "GIMP_HUE_MODE")
+ '("saturation-mode" "GIMP_SATURATION_MODE")
+ '("color-mode" "GIMP_COLOR_MODE")
+ '("value-mode" "GIMP_VALUE_MODE")
+ '("divide-mode" "GIMP_DIVIDE_MODE")
+ '("dodge-mode" "GIMP_DODGE_MODE")
+ '("burn-mode" "GIMP_BURN_MODE")
+ '("hardlight-mode" "GIMP_HARDLIGHT_MODE")
+ '("softlight-mode" "GIMP_SOFTLIGHT_MODE")
+ '("grain-extract-mode" "GIMP_GRAIN_EXTRACT_MODE")
+ '("grain-merge-mode" "GIMP_GRAIN_MERGE_MODE")
+ '("color-erase-mode" "GIMP_COLOR_ERASE_MODE")
+ '("new-overlay-mode" "GIMP_NEW_OVERLAY_MODE")
+ )
+)
+
+(define-enum MaskApplyMode
+ (in-module "Gimp")
+ (c-name "GimpMaskApplyMode")
+ (gtype-id "GIMP_TYPE_MASK_APPLY_MODE")
+ (values
+ '("apply" "GIMP_MASK_APPLY")
+ '("discard" "GIMP_MASK_DISCARD")
+ )
+)
+
+(define-enum MergeType
+ (in-module "Gimp")
+ (c-name "GimpMergeType")
+ (gtype-id "GIMP_TYPE_MERGE_TYPE")
+ (values
+ '("expand-as-necessary" "GIMP_EXPAND_AS_NECESSARY")
+ '("clip-to-image" "GIMP_CLIP_TO_IMAGE")
+ '("clip-to-bottom-layer" "GIMP_CLIP_TO_BOTTOM_LAYER")
+ '("flatten-image" "GIMP_FLATTEN_IMAGE")
+ )
+)
+
+(define-enum OffsetType
+ (in-module "Gimp")
+ (c-name "GimpOffsetType")
+ (gtype-id "GIMP_TYPE_OFFSET_TYPE")
+ (values
+ '("background" "GIMP_OFFSET_BACKGROUND")
+ '("transparent" "GIMP_OFFSET_TRANSPARENT")
+ )
+)
+
+(define-enum OrientationType
+ (in-module "Gimp")
+ (c-name "GimpOrientationType")
+ (gtype-id "GIMP_TYPE_ORIENTATION_TYPE")
+ (values
+ '("horizontal" "GIMP_ORIENTATION_HORIZONTAL")
+ '("vertical" "GIMP_ORIENTATION_VERTICAL")
+ '("unknown" "GIMP_ORIENTATION_UNKNOWN")
+ )
+)
+
+(define-enum RotationType
+ (in-module "Gimp")
+ (c-name "GimpRotationType")
+ (gtype-id "GIMP_TYPE_ROTATION_TYPE")
+ (values
+ '("90" "GIMP_ROTATE_90")
+ '("180" "GIMP_ROTATE_180")
+ '("270" "GIMP_ROTATE_270")
+ )
+)
+
+
+;; From ../../libgimp/gimpenums.h
+
+(define-function gimp_brush_application_mode_get_type
+ (c-name "gimp_brush_application_mode_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_brush_generated_shape_get_type
+ (c-name "gimp_brush_generated_shape_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_convert_dither_type_get_type
+ (c-name "gimp_convert_dither_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_convert_palette_type_get_type
+ (c-name "gimp_convert_palette_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_convolution_type_get_type
+ (c-name "gimp_convolution_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_convolve_type_get_type
+ (c-name "gimp_convolve_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_fill_type_get_type
+ (c-name "gimp_fill_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_gradient_segment_color_get_type
+ (c-name "gimp_gradient_segment_color_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_gradient_segment_type_get_type
+ (c-name "gimp_gradient_segment_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_histogram_channel_get_type
+ (c-name "gimp_histogram_channel_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_hue_range_get_type
+ (c-name "gimp_hue_range_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_layer_mode_effects_get_type
+ (c-name "gimp_layer_mode_effects_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_mask_apply_mode_get_type
+ (c-name "gimp_mask_apply_mode_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_merge_type_get_type
+ (c-name "gimp_merge_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_offset_type_get_type
+ (c-name "gimp_offset_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_orientation_type_get_type
+ (c-name "gimp_orientation_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_rotation_type_get_type
+ (c-name "gimp_rotation_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_enums_init
+ (c-name "gimp_enums_init")
+ (return-type "none")
+)
+
+(define-function gimp_enums_get_type_names
+ (c-name "gimp_enums_get_type_names")
+ (return-type "const-gchar**")
+ (parameters
+ '("gint*" "n_type_names")
+ )
+)
+
+
diff --git a/plug-ins/pygimp/gimpenums.py b/plug-ins/pygimp/gimpenums.py
new file mode 100644
index 0000000..9e23ded
--- /dev/null
+++ b/plug-ins/pygimp/gimpenums.py
@@ -0,0 +1,50 @@
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 2005 Manish Singh <yosh@gimp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# gimpenums.py -- constants for use with the gimp module
+#
+# this file pulls in constants that are useful for use in
+# gimp plugins. Just add 'from gimpenums import *' to the top
+# of the script
+
+from _gimpenums import *
+
+# This is from pygtk/gtk/__init__.py
+# Copyright (C) 1998-2003 James Henstridge
+
+class _DeprecatedConstant:
+ def __init__(self, value, name, suggestion):
+ self._v = value
+ self._name = name
+ self._suggestion = suggestion
+
+ def _deprecated(self, value):
+ import warnings
+ message = '%s is deprecated, use %s instead' % (self._name,
+ self._suggestion)
+ warnings.warn(message, DeprecationWarning, 3)
+ return value
+
+ __nonzero__ = lambda self: self._deprecated(self._v == True)
+ __int__ = lambda self: self._deprecated(int(self._v))
+ __str__ = lambda self: self._deprecated(str(self._v))
+ __repr__ = lambda self: self._deprecated(repr(self._v))
+ __cmp__ = lambda self, other: self._deprecated(cmp(self._v, other))
+
+TRUE = _DeprecatedConstant(True, 'gimpenums.TRUE', 'True')
+FALSE = _DeprecatedConstant(False, 'gimpenums.FALSE', 'False')
+
+del _DeprecatedConstant
diff --git a/plug-ins/pygimp/gimpenumsmodule.c b/plug-ins/pygimp/gimpenumsmodule.c
new file mode 100644
index 0000000..2a1194c
--- /dev/null
+++ b/plug-ins/pygimp/gimpenumsmodule.c
@@ -0,0 +1,174 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Python.h>
+
+#include <glib-object.h>
+
+#include <pygobject.h>
+
+#include "pygimp-api.h"
+#include "pygimp-util.h"
+#include "libgimp/gimpui.h"
+
+static void
+add_misc_enums(PyObject *m)
+{
+ PyModule_AddIntConstant(m, "PARASITE_PERSISTENT",
+ GIMP_PARASITE_PERSISTENT);
+ PyModule_AddIntConstant(m, "PARASITE_UNDOABLE",
+ GIMP_PARASITE_UNDOABLE);
+ PyModule_AddIntConstant(m, "PARASITE_ATTACH_PARENT",
+ GIMP_PARASITE_ATTACH_PARENT);
+ PyModule_AddIntConstant(m, "PARASITE_PARENT_PERSISTENT",
+ GIMP_PARASITE_PARENT_PERSISTENT);
+ PyModule_AddIntConstant(m, "PARASITE_PARENT_UNDOABLE",
+ GIMP_PARASITE_PARENT_UNDOABLE);
+ PyModule_AddIntConstant(m, "PARASITE_ATTACH_GRANDPARENT",
+ GIMP_PARASITE_ATTACH_GRANDPARENT);
+ PyModule_AddIntConstant(m, "PARASITE_GRANDPARENT_PERSISTENT",
+ GIMP_PARASITE_GRANDPARENT_PERSISTENT);
+ PyModule_AddIntConstant(m, "PARASITE_GRANDPARENT_UNDOABLE",
+ GIMP_PARASITE_GRANDPARENT_UNDOABLE);
+
+ PyModule_AddIntConstant(m, "UNIT_PIXEL",
+ GIMP_UNIT_PIXEL);
+ PyModule_AddIntConstant(m, "UNIT_INCH",
+ GIMP_UNIT_INCH);
+ PyModule_AddIntConstant(m, "UNIT_MM",
+ GIMP_UNIT_MM);
+ PyModule_AddIntConstant(m, "UNIT_POINT",
+ GIMP_UNIT_POINT);
+ PyModule_AddIntConstant(m, "UNIT_PICA",
+ GIMP_UNIT_PICA);
+
+ PyModule_AddIntConstant(m, "MIN_IMAGE_SIZE",
+ GIMP_MIN_IMAGE_SIZE);
+ PyModule_AddIntConstant(m, "MAX_IMAGE_SIZE",
+ GIMP_MAX_IMAGE_SIZE);
+
+ PyModule_AddObject(m, "MIN_RESOLUTION",
+ PyFloat_FromDouble(GIMP_MIN_RESOLUTION));
+ PyModule_AddObject(m, "MAX_RESOLUTION",
+ PyFloat_FromDouble(GIMP_MAX_RESOLUTION));
+
+ PyModule_AddObject(m, "MAX_MEMSIZE",
+ PyLong_FromUnsignedLongLong(GIMP_MAX_MEMSIZE));
+
+ PyModule_AddIntConstant(m, "PIXEL_FETCHER_EDGE_NONE",
+ GIMP_PIXEL_FETCHER_EDGE_NONE);
+ PyModule_AddIntConstant(m, "PIXEL_FETCHER_EDGE_WRAP",
+ GIMP_PIXEL_FETCHER_EDGE_WRAP);
+ PyModule_AddIntConstant(m, "PIXEL_FETCHER_EDGE_SMEAR",
+ GIMP_PIXEL_FETCHER_EDGE_SMEAR);
+ PyModule_AddIntConstant(m, "PIXEL_FETCHER_EDGE_BLACK",
+ GIMP_PIXEL_FETCHER_EDGE_BLACK);
+ PyModule_AddIntConstant(m, "PIXEL_FETCHER_EDGE_BACKGROUND",
+ GIMP_PIXEL_FETCHER_EDGE_BACKGROUND);
+}
+
+static void
+add_compat_enums(PyObject *m)
+{
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_RGB",
+ GIMP_EXPORT_CAN_HANDLE_RGB);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_GRAY",
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_INDEXED",
+ GIMP_EXPORT_CAN_HANDLE_INDEXED);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_BITMAP",
+ GIMP_EXPORT_CAN_HANDLE_BITMAP);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_ALPHA",
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_LAYERS",
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION",
+ GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION);
+ PyModule_AddIntConstant(m, "EXPORT_CAN_HANDLE_LAYER_MASKS",
+ GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS);
+ PyModule_AddIntConstant(m, "EXPORT_NEEDS_ALPHA",
+ GIMP_EXPORT_NEEDS_ALPHA);
+
+ PyModule_AddIntConstant(m, "EXPORT_CANCEL",
+ GIMP_EXPORT_CANCEL);
+ PyModule_AddIntConstant(m, "EXPORT_IGNORE",
+ GIMP_EXPORT_IGNORE);
+ PyModule_AddIntConstant(m, "EXPORT_EXPORT",
+ GIMP_EXPORT_EXPORT);
+}
+
+static void
+add_registered_enums(PyObject *m)
+{
+ gint num_names, i;
+ const gchar **names;
+ GQuark quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ names = gimp_enums_get_type_names (&num_names);
+
+ pyg_enum_add_constants (m, GIMP_TYPE_CHECK_SIZE, "GIMP_");
+ pyg_enum_add_constants (m, GIMP_TYPE_CHECK_TYPE, "GIMP_");
+
+ for (i = 0; i < num_names; i++)
+ {
+ GType enum_type = g_type_from_name (names[i]);
+
+ pyg_enum_add_constants (m, enum_type, "GIMP_");
+
+ enum_type = (GType) g_type_get_qdata (enum_type, quark);
+
+ if (enum_type)
+ pyg_enum_add_constants (m, enum_type, "GIMP_");
+ }
+}
+
+
+/* Initialization function for the module (*must* be called initgimpenums) */
+
+static char gimpenums_doc[] =
+"This module provides interfaces to allow you to write gimp plug-ins"
+;
+
+void init_gimpenums(void);
+
+PyMODINIT_FUNC
+init_gimpenums(void)
+{
+ PyObject *m;
+
+ pygimp_init_pygobject();
+
+ init_pygimp();
+
+ gimp_enums_init();
+
+ /* Create the module and add the functions */
+ m = Py_InitModule3("_gimpenums", NULL, gimpenums_doc);
+
+ add_misc_enums(m);
+ add_compat_enums(m);
+ add_registered_enums(m);
+
+ /* Check for errors */
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module _gimpenums");
+}
diff --git a/plug-ins/pygimp/gimpfu.py b/plug-ins/pygimp/gimpfu.py
new file mode 100644
index 0000000..266d5ae
--- /dev/null
+++ b/plug-ins/pygimp/gimpfu.py
@@ -0,0 +1,876 @@
+# Gimp-Python - allows the writing of GIMP plug-ins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""Simple interface for writing GIMP plug-ins in Python.
+
+Instead of worrying about all the user interaction, saving last used
+values and everything, the gimpfu module can take care of it for you.
+It provides a simple register() function that will register your
+plug-in if needed, and cause your plug-in function to be called when
+needed.
+
+Gimpfu will also handle showing a user interface for editing plug-in
+parameters if the plug-in is called interactively, and will also save
+the last used parameters, so the RUN_WITH_LAST_VALUES run_type will
+work correctly. It will also make sure that the displays are flushed
+on completion if the plug-in was run interactively.
+
+When registering the plug-in, you do not need to worry about
+specifying the run_type parameter.
+
+A typical gimpfu plug-in would look like this:
+ from gimpfu import *
+
+ def plugin_func(image, drawable, args):
+ # do what plugins do best
+ register(
+ "plugin_func",
+ "blurb",
+ "help message",
+ "author",
+ "copyright",
+ "year",
+ "My plug-in",
+ "*",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_STRING, "arg", "The argument", "default-value")
+ ],
+ [],
+ plugin_func, menu="<Image>/Somewhere")
+ main()
+
+The call to "from gimpfu import *" will import all the gimp constants
+into the plug-in namespace, and also import the symbols gimp, pdb,
+register and main. This should be just about all any plug-in needs.
+
+You can use any of the PF_* constants below as parameter types, and an
+appropriate user interface element will be displayed when the plug-in
+is run in interactive mode. Note that the the PF_SPINNER and
+PF_SLIDER types expect a fifth element in their description tuple -- a
+3-tuple of the form (lower,upper,step), which defines the limits for
+the slider or spinner.
+
+If want to localize your plug-in, add an optional domain parameter to
+the register call. It can be the name of the translation domain or a
+tuple that consists of the translation domain and the directory where
+the translations are installed.
+"""
+
+import string as _string
+import math
+import gimp
+import gimpcolor
+from gimpenums import *
+pdb = gimp.pdb
+
+import gettext
+t = gettext.translation("gimp20-python", gimp.locale_directory, fallback=True)
+_ = t.ugettext
+
+class error(RuntimeError): pass
+class CancelError(RuntimeError): pass
+
+PF_INT8 = PDB_INT8
+PF_INT16 = PDB_INT16
+PF_INT32 = PDB_INT32
+PF_INT = PF_INT32
+PF_FLOAT = PDB_FLOAT
+PF_STRING = PDB_STRING
+PF_VALUE = PF_STRING
+#PF_INT8ARRAY = PDB_INT8ARRAY
+#PF_INT16ARRAY = PDB_INT16ARRAY
+#PF_INT32ARRAY = PDB_INT32ARRAY
+#PF_INTARRAY = PF_INT32ARRAY
+#PF_FLOATARRAY = PDB_FLOATARRAY
+#PF_STRINGARRAY = PDB_STRINGARRAY
+PF_COLOR = PDB_COLOR
+PF_COLOUR = PF_COLOR
+PF_ITEM = PDB_ITEM
+PF_DISPLAY = PDB_DISPLAY
+PF_IMAGE = PDB_IMAGE
+PF_LAYER = PDB_LAYER
+PF_CHANNEL = PDB_CHANNEL
+PF_DRAWABLE = PDB_DRAWABLE
+PF_VECTORS = PDB_VECTORS
+#PF_SELECTION = PDB_SELECTION
+#PF_BOUNDARY = PDB_BOUNDARY
+#PF_PATH = PDB_PATH
+#PF_STATUS = PDB_STATUS
+
+PF_TOGGLE = 1000
+PF_BOOL = PF_TOGGLE
+PF_SLIDER = 1001
+PF_SPINNER = 1002
+PF_ADJUSTMENT = PF_SPINNER
+
+PF_FONT = 1003
+PF_FILE = 1004
+PF_BRUSH = 1005
+PF_PATTERN = 1006
+PF_GRADIENT = 1007
+PF_RADIO = 1008
+PF_TEXT = 1009
+PF_PALETTE = 1010
+PF_FILENAME = 1011
+PF_DIRNAME = 1012
+PF_OPTION = 1013
+
+_type_mapping = {
+ PF_INT8 : PDB_INT8,
+ PF_INT16 : PDB_INT16,
+ PF_INT32 : PDB_INT32,
+ PF_FLOAT : PDB_FLOAT,
+ PF_STRING : PDB_STRING,
+ #PF_INT8ARRAY : PDB_INT8ARRAY,
+ #PF_INT16ARRAY : PDB_INT16ARRAY,
+ #PF_INT32ARRAY : PDB_INT32ARRAY,
+ #PF_FLOATARRAY : PDB_FLOATARRAY,
+ #PF_STRINGARRAY : PDB_STRINGARRAY,
+ PF_COLOR : PDB_COLOR,
+ PF_ITEM : PDB_ITEM,
+ PF_DISPLAY : PDB_DISPLAY,
+ PF_IMAGE : PDB_IMAGE,
+ PF_LAYER : PDB_LAYER,
+ PF_CHANNEL : PDB_CHANNEL,
+ PF_DRAWABLE : PDB_DRAWABLE,
+ PF_VECTORS : PDB_VECTORS,
+
+ PF_TOGGLE : PDB_INT32,
+ PF_SLIDER : PDB_FLOAT,
+ PF_SPINNER : PDB_INT32,
+
+ PF_FONT : PDB_STRING,
+ PF_FILE : PDB_STRING,
+ PF_BRUSH : PDB_STRING,
+ PF_PATTERN : PDB_STRING,
+ PF_GRADIENT : PDB_STRING,
+ PF_RADIO : PDB_STRING,
+ PF_TEXT : PDB_STRING,
+ PF_PALETTE : PDB_STRING,
+ PF_FILENAME : PDB_STRING,
+ PF_DIRNAME : PDB_STRING,
+ PF_OPTION : PDB_INT32,
+}
+
+_obj_mapping = {
+ PF_INT8 : int,
+ PF_INT16 : int,
+ PF_INT32 : int,
+ PF_FLOAT : float,
+ PF_STRING : str,
+ #PF_INT8ARRAY : list,
+ #PF_INT16ARRAY : list,
+ #PF_INT32ARRAY : list,
+ #PF_FLOATARRAY : list,
+ #PF_STRINGARRAY : list,
+ PF_COLOR : gimpcolor.RGB,
+ PF_ITEM : int,
+ PF_DISPLAY : gimp.Display,
+ PF_IMAGE : gimp.Image,
+ PF_LAYER : gimp.Layer,
+ PF_CHANNEL : gimp.Channel,
+ PF_DRAWABLE : gimp.Drawable,
+ PF_VECTORS : gimp.Vectors,
+
+ PF_TOGGLE : bool,
+ PF_SLIDER : float,
+ PF_SPINNER : int,
+
+ PF_FONT : str,
+ PF_FILE : str,
+ PF_BRUSH : str,
+ PF_PATTERN : str,
+ PF_GRADIENT : str,
+ PF_RADIO : str,
+ PF_TEXT : str,
+ PF_PALETTE : str,
+ PF_FILENAME : str,
+ PF_DIRNAME : str,
+ PF_OPTION : int,
+}
+
+_registered_plugins_ = {}
+
+def register(proc_name, blurb, help, author, copyright, date, label,
+ imagetypes, params, results, function,
+ menu=None, domain=None, on_query=None, on_run=None, run_mode_param=True):
+ """This is called to register a new plug-in."""
+
+ # First perform some sanity checks on the data
+ def letterCheck(str):
+ allowed = _string.letters + _string.digits + "_" + "-"
+ for ch in str:
+ if not ch in allowed:
+ return 0
+ else:
+ return 1
+
+ if not letterCheck(proc_name):
+ raise error, "procedure name contains illegal characters"
+
+ for ent in params:
+ if len(ent) < 4:
+ raise error, ("parameter definition must contain at least 4 "
+ "elements (%s given: %s)" % (len(ent), ent))
+
+ if type(ent[0]) != int:
+ raise error, "parameter types must be integers"
+
+ if not letterCheck(ent[1]):
+ raise error, "parameter name contains illegal characters"
+
+ for ent in results:
+ if len(ent) < 3:
+ raise error, ("result definition must contain at least 3 elements "
+ "(%s given: %s)" % (len(ent), ent))
+
+ if type(ent[0]) != type(42):
+ raise error, "result types must be integers"
+
+ if not letterCheck(ent[1]):
+ raise error, "result name contains illegal characters"
+
+ plugin_type = PLUGIN
+
+ if (not proc_name.startswith("python-") and
+ not proc_name.startswith("python_") and
+ not proc_name.startswith("extension-") and
+ not proc_name.startswith("extension_") and
+ not proc_name.startswith("plug-in-") and
+ not proc_name.startswith("plug_in_") and
+ not proc_name.startswith("file-") and
+ not proc_name.startswith("file_")):
+ proc_name = "python-fu-" + proc_name
+
+ # if menu is not given, derive it from label
+ need_compat_params = False
+ if menu is None and label:
+ fields = label.split("/")
+ if fields:
+ label = fields.pop()
+ menu = "/".join(fields)
+ need_compat_params = True
+
+ import warnings
+ message = ("%s: passing the full menu path for the menu label is "
+ "deprecated, use the 'menu' parameter instead"
+ % (proc_name))
+ warnings.warn(message, DeprecationWarning, 3)
+
+ if need_compat_params and plugin_type == PLUGIN:
+ file_params = [(PDB_STRING, "filename", "The name of the file", ""),
+ (PDB_STRING, "raw-filename", "The name of the file", "")]
+
+ if menu is None:
+ pass
+ elif menu.startswith("<Load>"):
+ params[0:0] = file_params
+ elif menu.startswith("<Image>") or menu.startswith("<Save>"):
+ params.insert(0, (PDB_IMAGE, "image", "Input image", None))
+ params.insert(1, (PDB_DRAWABLE, "drawable", "Input drawable", None))
+ if menu.startswith("<Save>"):
+ params[2:2] = file_params
+
+ _registered_plugins_[proc_name] = (blurb, help, author, copyright,
+ date, label, imagetypes,
+ plugin_type, params, results,
+ function, menu, domain,
+ on_query, on_run, run_mode_param)
+
+def _query():
+ for plugin in _registered_plugins_.keys():
+ (blurb, help, author, copyright, date,
+ label, imagetypes, plugin_type,
+ params, results, function, menu, domain,
+ on_query, on_run, has_param_run_mode) = _registered_plugins_[plugin]
+
+ def make_params(params):
+ return [(_type_mapping[x[0]],
+ x[1],
+ _string.replace(x[2], "_", "")) for x in params]
+
+ params = make_params(params)
+ # add the run mode argument ...
+ if has_param_run_mode:
+ params.insert(0, (PDB_INT32, "run-mode",
+ "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"))
+
+ results = make_params(results)
+
+ if domain:
+ try:
+ (domain, locale_dir) = domain
+ gimp.domain_register(domain, locale_dir)
+ except ValueError:
+ gimp.domain_register(domain)
+
+ gimp.install_procedure(plugin, blurb, help, author, copyright,
+ date, label, imagetypes, plugin_type,
+ params, results)
+
+ if menu:
+ gimp.menu_register(plugin, menu)
+ if on_query:
+ on_query()
+
+def _get_defaults(proc_name):
+ import gimpshelf
+
+ (blurb, help, author, copyright, date,
+ label, imagetypes, plugin_type,
+ params, results, function, menu, domain,
+ on_query, on_run, has_run_mode) = _registered_plugins_[proc_name]
+
+ key = "python-fu-save--" + proc_name
+
+ if gimpshelf.shelf.has_key(key):
+ return gimpshelf.shelf[key]
+ else:
+ # return the default values
+ return [x[3] for x in params]
+
+def _set_defaults(proc_name, defaults):
+ import gimpshelf
+
+ key = "python-fu-save--" + proc_name
+ gimpshelf.shelf[key] = defaults
+
+def _interact(proc_name, start_params):
+ (blurb, help, author, copyright, date,
+ label, imagetypes, plugin_type,
+ params, results, function, menu, domain,
+ on_query, on_run, has_run_mode) = _registered_plugins_[proc_name]
+
+ def run_script(run_params):
+ params = start_params + tuple(run_params)
+ _set_defaults(proc_name, params)
+ return apply(function, params)
+
+ params = params[len(start_params):]
+
+ # short circuit for no parameters ...
+ if len(params) == 0:
+ return run_script([])
+
+ import pygtk
+ pygtk.require('2.0')
+
+ import gimpui
+ import gtk
+# import pango
+ gimpui.gimp_ui_init ()
+
+ defaults = _get_defaults(proc_name)
+ defaults = defaults[len(start_params):]
+
+ class EntryValueError(Exception):
+ pass
+
+ def warning_dialog(parent, primary, secondary=None):
+ dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE,
+ primary)
+ if secondary:
+ dlg.format_secondary_text(secondary)
+ dlg.run()
+ dlg.destroy()
+
+ def error_dialog(parent, proc_name):
+ import sys, traceback
+
+ exc_str = exc_only_str = _("Missing exception information")
+
+ try:
+ etype, value, tb = sys.exc_info()
+ exc_str = "".join(traceback.format_exception(etype, value, tb))
+ exc_only_str = "".join(traceback.format_exception_only(etype, value))
+ finally:
+ etype = value = tb = None
+
+ title = _("An error occurred running %s") % proc_name
+ dlg = gtk.MessageDialog(parent, gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
+ title)
+ dlg.format_secondary_text(exc_only_str)
+
+ alignment = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
+ alignment.set_padding(0, 0, 12, 12)
+ dlg.vbox.pack_start(alignment)
+ alignment.show()
+
+ expander = gtk.Expander(_("_More Information"));
+ expander.set_use_underline(True)
+ expander.set_spacing(6)
+ alignment.add(expander)
+ expander.show()
+
+ scrolled = gtk.ScrolledWindow()
+ scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled.set_size_request(-1, 200)
+ expander.add(scrolled)
+ scrolled.show()
+
+
+ label = gtk.Label(exc_str)
+ label.set_alignment(0.0, 0.0)
+ label.set_padding(6, 6)
+ label.set_selectable(True)
+ scrolled.add_with_viewport(label)
+ label.show()
+
+ def response(widget, id):
+ widget.destroy()
+
+ dlg.connect("response", response)
+ dlg.set_resizable(True)
+ dlg.show()
+
+ # define a mapping of param types to edit objects ...
+ class StringEntry(gtk.Entry):
+ def __init__(self, default=""):
+ gtk.Entry.__init__(self)
+ self.set_text(str(default))
+ self.set_activates_default(True)
+
+ def get_value(self):
+ return self.get_text()
+
+ class TextEntry(gtk.ScrolledWindow):
+ def __init__ (self, default=""):
+ gtk.ScrolledWindow.__init__(self)
+ self.set_shadow_type(gtk.SHADOW_IN)
+
+ self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.set_size_request(100, -1)
+
+ self.view = gtk.TextView()
+ self.add(self.view)
+ self.view.show()
+
+ self.buffer = self.view.get_buffer()
+
+ self.set_value(str(default))
+
+ def set_value(self, text):
+ self.buffer.set_text(text)
+
+ def get_value(self):
+ return self.buffer.get_text(self.buffer.get_start_iter(),
+ self.buffer.get_end_iter())
+
+ class IntEntry(StringEntry):
+ def get_value(self):
+ try:
+ return int(self.get_text())
+ except ValueError, e:
+ raise EntryValueError, e.args
+
+ class FloatEntry(StringEntry):
+ def get_value(self):
+ try:
+ return float(self.get_text())
+ except ValueError, e:
+ raise EntryValueError, e.args
+
+# class ArrayEntry(StringEntry):
+# def get_value(self):
+# return eval(self.get_text(), {}, {})
+
+
+ def precision(step):
+ # calculate a reasonable precision from a given step size
+ if math.fabs(step) >= 1.0 or step == 0.0:
+ digits = 0
+ else:
+ digits = abs(math.floor(math.log10(math.fabs(step))));
+ if digits > 20:
+ digits = 20
+ return int(digits)
+
+ class SliderEntry(gtk.HScale):
+ # bounds is (upper, lower, step)
+ def __init__(self, default=0, bounds=(0, 100, 5)):
+ step = bounds[2]
+ self.adj = gtk.Adjustment(default, bounds[0], bounds[1],
+ step, 10 * step, 0)
+ gtk.HScale.__init__(self, self.adj)
+ self.set_digits(precision(step))
+
+ def get_value(self):
+ return self.adj.value
+
+ class SpinnerEntry(gtk.SpinButton):
+ # bounds is (upper, lower, step)
+ def __init__(self, default=0, bounds=(0, 100, 5)):
+ step = bounds[2]
+ self.adj = gtk.Adjustment(default, bounds[0], bounds[1],
+ step, 10 * step, 0)
+ gtk.SpinButton.__init__(self, self.adj, step, precision(step))
+
+ class ToggleEntry(gtk.ToggleButton):
+ def __init__(self, default=0):
+ gtk.ToggleButton.__init__(self)
+
+ self.label = gtk.Label(_("No"))
+ self.add(self.label)
+ self.label.show()
+
+ self.connect("toggled", self.changed)
+
+ self.set_active(default)
+
+ def changed(self, tog):
+ if tog.get_active():
+ self.label.set_text(_("Yes"))
+ else:
+ self.label.set_text(_("No"))
+
+ def get_value(self):
+ return self.get_active()
+
+ class RadioEntry(gtk.VBox):
+ def __init__(self, default=0, items=((_("Yes"), 1), (_("No"), 0))):
+ gtk.VBox.__init__(self, homogeneous=False, spacing=2)
+
+ button = None
+
+ for (label, value) in items:
+ button = gtk.RadioButton(button, label)
+ self.pack_start(button)
+ button.show()
+
+ button.connect("toggled", self.changed, value)
+
+ if value == default:
+ button.set_active(True)
+ self.active_value = value
+
+ def changed(self, radio, value):
+ if radio.get_active():
+ self.active_value = value
+
+ def get_value(self):
+ return self.active_value
+
+ class ComboEntry(gtk.ComboBox):
+ def __init__(self, default=0, items=()):
+ store = gtk.ListStore(str)
+ for item in items:
+ store.append([item])
+
+ gtk.ComboBox.__init__(self, model=store)
+
+ cell = gtk.CellRendererText()
+ self.pack_start(cell)
+ self.set_attributes(cell, text=0)
+
+ self.set_active(default)
+
+ def get_value(self):
+ return self.get_active()
+
+ def FileSelector(default="", title=None):
+ # FIXME: should this be os.path.separator? If not, perhaps explain why?
+ if default and default.endswith("/"):
+ if default == "/": default = ""
+ return DirnameSelector(default)
+ else:
+ return FilenameSelector(default, title=title, save_mode=False)
+
+ class FilenameSelector(gtk.HBox):
+ #gimpfu.FileChooserButton
+ def __init__(self, default, save_mode=True, title=None):
+ super(FilenameSelector, self).__init__()
+ if not title:
+ self.title = _("Python-Fu File Selection")
+ else:
+ self.title = title
+ self.save_mode = save_mode
+ box = self
+ self.entry = gtk.Entry()
+ image = gtk.Image()
+ image.set_from_stock(gtk.STOCK_FILE, gtk.ICON_SIZE_BUTTON)
+ self.button = gtk.Button()
+ self.button.set_image(image)
+ box.pack_start(self.entry)
+ box.pack_start(self.button, expand=False)
+ self.button.connect("clicked", self.pick_file)
+ if default:
+ self.entry.set_text(default)
+
+ def show(self):
+ super(FilenameSelector, self).show()
+ self.button.show()
+ self.entry.show()
+
+ def pick_file(self, widget):
+ entry = self.entry
+ dialog = gtk.FileChooserDialog(
+ title=self.title,
+ action=(gtk.FILE_CHOOSER_ACTION_SAVE
+ if self.save_mode else
+ gtk.FILE_CHOOSER_ACTION_OPEN),
+ buttons=(gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL,
+ gtk.STOCK_SAVE
+ if self.save_mode else
+ gtk.STOCK_OPEN,
+ gtk.RESPONSE_OK)
+ )
+ dialog.set_alternative_button_order ((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL))
+ dialog.show_all()
+ response = dialog.run()
+ if response == gtk.RESPONSE_OK:
+ entry.set_text(dialog.get_filename())
+ dialog.destroy()
+
+ def get_value(self):
+ return self.entry.get_text()
+
+
+ class DirnameSelector(gtk.FileChooserButton):
+ def __init__(self, default=""):
+ gtk.FileChooserButton.__init__(self,
+ _("Python-Fu Folder Selection"))
+ self.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ if default:
+ self.set_filename(default)
+
+ def get_value(self):
+ return self.get_filename()
+
+ _edit_mapping = {
+ PF_INT8 : IntEntry,
+ PF_INT16 : IntEntry,
+ PF_INT32 : IntEntry,
+ PF_FLOAT : FloatEntry,
+ PF_STRING : StringEntry,
+ #PF_INT8ARRAY : ArrayEntry,
+ #PF_INT16ARRAY : ArrayEntry,
+ #PF_INT32ARRAY : ArrayEntry,
+ #PF_FLOATARRAY : ArrayEntry,
+ #PF_STRINGARRAY : ArrayEntry,
+ PF_COLOR : gimpui.ColorSelector,
+ PF_ITEM : IntEntry, # should handle differently ...
+ PF_IMAGE : gimpui.ImageSelector,
+ PF_LAYER : gimpui.LayerSelector,
+ PF_CHANNEL : gimpui.ChannelSelector,
+ PF_DRAWABLE : gimpui.DrawableSelector,
+ PF_VECTORS : gimpui.VectorsSelector,
+
+ PF_TOGGLE : ToggleEntry,
+ PF_SLIDER : SliderEntry,
+ PF_SPINNER : SpinnerEntry,
+ PF_RADIO : RadioEntry,
+ PF_OPTION : ComboEntry,
+
+ PF_FONT : gimpui.FontSelector,
+ PF_FILE : FileSelector,
+ PF_FILENAME : FilenameSelector,
+ PF_DIRNAME : DirnameSelector,
+ PF_BRUSH : gimpui.BrushSelector,
+ PF_PATTERN : gimpui.PatternSelector,
+ PF_GRADIENT : gimpui.GradientSelector,
+ PF_PALETTE : gimpui.PaletteSelector,
+ PF_TEXT : TextEntry
+ }
+
+ if on_run:
+ on_run()
+
+ dialog = gimpui.Dialog(proc_name, "python-fu", None, 0, None, proc_name,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK))
+
+ dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL))
+
+ dialog.set_transient()
+
+ vbox = gtk.VBox(False, 12)
+ vbox.set_border_width(12)
+ dialog.vbox.pack_start(vbox)
+ vbox.show()
+
+ if blurb:
+ if domain:
+ try:
+ (domain, locale_dir) = domain
+ trans = gettext.translation(domain, locale_dir, fallback=True)
+ except ValueError:
+ trans = gettext.translation(domain, fallback=True)
+ blurb = trans.ugettext(blurb)
+ box = gimpui.HintBox(blurb)
+ vbox.pack_start(box, expand=False)
+ box.show()
+
+ table = gtk.Table(len(params), 2, False)
+ table.set_row_spacings(6)
+ table.set_col_spacings(6)
+ vbox.pack_start(table, expand=False)
+ table.show()
+
+ def response(dlg, id):
+ if id == gtk.RESPONSE_OK:
+ dlg.set_response_sensitive(gtk.RESPONSE_OK, False)
+ dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
+
+ params = []
+
+ try:
+ for wid in edit_wids:
+ params.append(wid.get_value())
+ except EntryValueError:
+ warning_dialog(dialog, _("Invalid input for '%s'") % wid.desc)
+ else:
+ try:
+ dialog.res = run_script(params)
+ except CancelError:
+ pass
+ except Exception:
+ dlg.set_response_sensitive(gtk.RESPONSE_CANCEL, True)
+ error_dialog(dialog, proc_name)
+ raise
+
+ gtk.main_quit()
+
+ dialog.connect("response", response)
+
+ edit_wids = []
+ for i in range(len(params)):
+ pf_type = params[i][0]
+ name = params[i][1]
+ desc = params[i][2]
+ def_val = defaults[i]
+
+ label = gtk.Label(desc)
+ label.set_use_underline(True)
+ label.set_alignment(0.0, 0.5)
+ table.attach(label, 1, 2, i, i+1, xoptions=gtk.FILL)
+ label.show()
+
+ # Remove accelerator markers from tooltips
+ tooltip_text = desc.replace("_", "")
+
+ if pf_type in (PF_SPINNER, PF_SLIDER, PF_RADIO, PF_OPTION):
+ wid = _edit_mapping[pf_type](def_val, params[i][4])
+ elif pf_type in (PF_FILE, PF_FILENAME):
+ wid = _edit_mapping[pf_type](def_val, title= "%s - %s" %
+ (proc_name, tooltip_text))
+ else:
+ wid = _edit_mapping[pf_type](def_val)
+
+
+ label.set_mnemonic_widget(wid)
+
+ table.attach(wid, 2,3, i,i+1, yoptions=0)
+
+ if pf_type != PF_TEXT:
+ wid.set_tooltip_text(tooltip_text)
+ else:
+ # Attach tip to TextView, not to ScrolledWindow
+ wid.view.set_tooltip_text(tooltip_text)
+ wid.show()
+
+ wid.desc = desc
+ edit_wids.append(wid)
+
+ progress_vbox = gtk.VBox(False, 6)
+ vbox.pack_end(progress_vbox, expand=False)
+ progress_vbox.show()
+
+ progress = gimpui.ProgressBar()
+ progress_vbox.pack_start(progress)
+ progress.show()
+
+# progress_label = gtk.Label()
+# progress_label.set_alignment(0.0, 0.5)
+# progress_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+
+# attrs = pango.AttrList()
+# attrs.insert(pango.AttrStyle(pango.STYLE_ITALIC, 0, -1))
+# progress_label.set_attributes(attrs)
+
+# progress_vbox.pack_start(progress_label)
+# progress_label.show()
+
+ dialog.show()
+
+ gtk.main()
+
+ if hasattr(dialog, "res"):
+ res = dialog.res
+ dialog.destroy()
+ return res
+ else:
+ dialog.destroy()
+ raise CancelError
+
+def _run(proc_name, params):
+ run_mode = params[0]
+ func = _registered_plugins_[proc_name][10]
+
+ if run_mode == RUN_NONINTERACTIVE:
+ return apply(func, params[1:])
+
+ script_params = _registered_plugins_[proc_name][8]
+ has_param_run_mode = _registered_plugins_[proc_name][15]
+
+ min_args = 0
+ start_param_idx = 1 if has_param_run_mode else 0
+ if len(params) > start_param_idx:
+ for i in range(start_param_idx, len(params)):
+ param_type = _obj_mapping[script_params[i - start_param_idx][0]]
+ if not isinstance(params[i], param_type):
+ break
+
+ min_args = i
+
+ if len(script_params) > min_args:
+ start_params = params[:min_args + 1]
+
+ if run_mode == RUN_WITH_LAST_VALS:
+ default_params = _get_defaults(proc_name)
+ params = start_params + default_params[min_args:]
+ else:
+ params = start_params
+ else:
+ run_mode = RUN_NONINTERACTIVE
+
+ if run_mode == RUN_INTERACTIVE:
+ try:
+ res = _interact(proc_name, params[start_param_idx:])
+ except CancelError:
+ return
+ else:
+ res = apply(func, params[start_param_idx:])
+
+ gimp.displays_flush()
+
+ return res
+
+def main():
+ """This should be called after registering the plug-in."""
+ gimp.main(None, None, _query, _run)
+
+def fail(msg):
+ """Display an error message and quit"""
+ gimp.message(msg)
+ raise error, msg
+
+def N_(message):
+ return message
diff --git a/plug-ins/pygimp/gimpmodule.c b/plug-ins/pygimp/gimpmodule.c
new file mode 100644
index 0000000..36a1b88
--- /dev/null
+++ b/plug-ins/pygimp/gimpmodule.c
@@ -0,0 +1,2071 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#undef GIMP_DISABLE_DEPRECATED
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+#include "pygimp.h"
+
+#include "pygimpcolor-api.h"
+
+#include <sysmodule.h>
+
+#include <glib-object.h>
+
+#include <pygobject.h>
+
+#include "pygimp-util.h"
+
+#include "pygimp-intl.h"
+
+#include "libgimp/gimpui.h"
+
+#include <gtk/gtk.h>
+
+#include <gegl.h>
+
+
+PyObject *pygimp_error;
+
+#ifndef PG_DEBUG
+# define PG_DEBUG 0
+#endif
+
+
+/* End of code for pdbFunc objects */
+/* -------------------------------------------------------- */
+
+GimpPlugInInfo PLUG_IN_INFO = {
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ NULL, /* query_proc */
+ NULL /* run_proc */
+};
+
+static PyObject *callbacks[] = {
+ NULL, NULL, NULL, NULL
+};
+
+typedef struct _ProgressData ProgressData;
+
+struct _ProgressData
+{
+ PyObject *start, *end, *text, *value;
+ PyObject *user_data;
+};
+
+
+static void
+pygimp_init_proc(void)
+{
+ PyObject *r;
+
+ r = PyObject_CallFunction(callbacks[0], "()");
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_quit_proc(void)
+{
+ PyObject *r;
+
+ r = PyObject_CallFunction(callbacks[1], "()");
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_query_proc(void)
+{
+ PyObject *r;
+
+ r = PyObject_CallFunction(callbacks[2], "()");
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_run_proc(const char *name, int nparams, const GimpParam *params,
+ int *nreturn_vals, GimpParam **return_vals)
+{
+ PyObject *args, *ret;
+ GimpParamDef *pd, *rv;
+ GimpPDBProcType t;
+ char *b, *h, *a, *c, *d;
+ int np, nrv;
+
+ gimp_procedural_db_proc_info(name, &b, &h, &a, &c, &d, &t, &np, &nrv,
+ &pd, &rv);
+ g_free(b); g_free(h); g_free(a); g_free(c); g_free(d); g_free(pd);
+
+#if PG_DEBUG > 0
+ g_printerr("Params for %s:", name);
+ print_GParam(nparams, params);
+#endif
+
+ args = pygimp_param_to_tuple(nparams, params);
+
+ if (args == NULL) {
+ PyErr_Clear();
+
+ *nreturn_vals = 1;
+ *return_vals = g_new(GimpParam, 1);
+ (*return_vals)[0].type = GIMP_PDB_STATUS;
+ (*return_vals)[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ return;
+ }
+
+ ret = PyObject_CallFunction(callbacks[3], "(sO)", name, args);
+ Py_DECREF(args);
+
+ if (ret == NULL) {
+ PyErr_Print();
+ PyErr_Clear();
+
+ *nreturn_vals = 1;
+ *return_vals = g_new(GimpParam, 1);
+ (*return_vals)[0].type = GIMP_PDB_STATUS;
+ (*return_vals)[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ return;
+ }
+
+ *return_vals = pygimp_param_from_tuple(ret, rv, nrv);
+ g_free(rv);
+
+ if (*return_vals == NULL) {
+ PyErr_Clear();
+
+ *nreturn_vals = 1;
+ *return_vals = g_new(GimpParam, 1);
+ (*return_vals)[0].type = GIMP_PDB_STATUS;
+ (*return_vals)[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ return;
+ }
+
+ Py_DECREF(ret);
+
+ *nreturn_vals = nrv + 1;
+ (*return_vals)[0].type = GIMP_PDB_STATUS;
+ (*return_vals)[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static PyObject *
+pygimp_main(PyObject *self, PyObject *args)
+{
+ PyObject *av;
+ int argc, i;
+ char **argv;
+ PyObject *ip; // init proc
+ PyObject *qp; // quit proc
+ PyObject *query; // query proc
+ PyObject *rp; // run proc
+
+ if (!PyArg_ParseTuple(args, "OOOO:main", &ip, &qp, &query, &rp))
+ return NULL;
+
+#define Arg_Check(v) (PyCallable_Check(v) || (v) == Py_None)
+
+ if (!Arg_Check(ip) || !Arg_Check(qp) || !Arg_Check(query) ||
+ !Arg_Check(rp)) {
+ PyErr_SetString(pygimp_error, "arguments must be callable");
+ return NULL;
+ }
+
+#undef Arg_Check
+
+ if (query == Py_None) {
+ PyErr_SetString(pygimp_error, "a query procedure must be provided");
+ return NULL;
+ }
+
+ if (ip != Py_None) {
+ callbacks[0] = ip;
+ PLUG_IN_INFO.init_proc = pygimp_init_proc;
+ }
+
+ if (qp != Py_None) {
+ callbacks[1] = qp;
+ PLUG_IN_INFO.quit_proc = pygimp_quit_proc;
+ }
+
+ if (query != Py_None) {
+ callbacks[2] = query;
+ PLUG_IN_INFO.query_proc = pygimp_query_proc;
+ }
+
+ if (rp != Py_None) {
+ callbacks[3] = rp;
+ PLUG_IN_INFO.run_proc = pygimp_run_proc;
+ }
+
+ av = PySys_GetObject("argv");
+
+ argc = PyList_Size(av);
+ argv = g_new(char *, argc);
+
+ for (i = 0; i < argc; i++)
+ argv[i] = g_strdup(PyString_AsString(PyList_GetItem(av, i)));
+
+ gimp_main(&PLUG_IN_INFO, argc, argv);
+
+ if (argv != NULL) {
+ for (i = 0; i < argc; i++)
+ if (argv[i] != NULL)
+ g_free(argv[i]);
+
+ g_free(argv);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_quit(PyObject *self)
+{
+ gimp_quit();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_message(PyObject *self, PyObject *args)
+{
+ char *msg;
+
+ if (!PyArg_ParseTuple(args, "s:message", &msg))
+ return NULL;
+
+ gimp_message(msg);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_exit(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ gboolean force = FALSE;
+ int nreturn_vals;
+ GimpParam *return_vals;
+
+ static char *kwlist[] = { "force", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:exit", kwlist, &force))
+ return NULL;
+
+ return_vals = gimp_run_procedure("gimp-quit",
+ &nreturn_vals,
+ GIMP_PDB_INT32, force,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) {
+ PyErr_SetString(pygimp_error, "error while exiting");
+ return NULL;
+ }
+
+ gimp_destroy_params(return_vals, nreturn_vals);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_set_data(PyObject *self, PyObject *args)
+{
+ char *id, *data;
+ int bytes, nreturn_vals;
+ GimpParam *return_vals;
+
+ if (!PyArg_ParseTuple(args, "ss#:set_data", &id, &data, &bytes))
+ return NULL;
+
+ return_vals = gimp_run_procedure("gimp-procedural-db-set-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, id,
+ GIMP_PDB_INT32, bytes,
+ GIMP_PDB_INT8ARRAY, data,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) {
+ PyErr_SetString(pygimp_error, "error occurred while storing");
+ return NULL;
+ }
+
+ gimp_destroy_params(return_vals, nreturn_vals);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_get_data(PyObject *self, PyObject *args)
+{
+ char *id;
+ int nreturn_vals;
+ GimpParam *return_vals;
+ PyObject *s;
+
+ if (!PyArg_ParseTuple(args, "s:get_data", &id))
+ return NULL;
+
+ return_vals = gimp_run_procedure("gimp-procedural-db-get-data",
+ &nreturn_vals,
+ GIMP_PDB_STRING, id,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status != GIMP_PDB_SUCCESS) {
+ PyErr_SetString(pygimp_error, "no data for id");
+ return NULL;
+ }
+
+ s = PyString_FromStringAndSize((char *)return_vals[2].data.d_int8array,
+ return_vals[1].data.d_int32);
+ gimp_destroy_params(return_vals, nreturn_vals);
+
+ return s;
+}
+
+static PyObject *
+pygimp_progress_init(PyObject *self, PyObject *args)
+{
+ char *msg = NULL;
+
+ if (!PyArg_ParseTuple(args, "|s:progress_init", &msg))
+ return NULL;
+
+ gimp_progress_init(msg);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_progress_update(PyObject *self, PyObject *args)
+{
+ double p;
+
+ if (!PyArg_ParseTuple(args, "d:progress_update", &p))
+ return NULL;
+
+ gimp_progress_update(p);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static void
+pygimp_progress_start(const gchar *message, gboolean cancelable, gpointer data)
+{
+ ProgressData *pdata = data;
+ PyObject *r;
+
+ if (pdata->user_data) {
+ r = PyObject_CallFunction(pdata->start, "siO", message, cancelable,
+ pdata->user_data);
+ Py_DECREF(pdata->user_data);
+ } else
+ r = PyObject_CallFunction(pdata->start, "si", message, cancelable);
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_progress_end(gpointer data)
+{
+ ProgressData *pdata = data;
+ PyObject *r;
+
+ if (pdata->user_data) {
+ r = PyObject_CallFunction(pdata->end, "O", pdata->user_data);
+ Py_DECREF(pdata->user_data);
+ } else
+ r = PyObject_CallFunction(pdata->end, NULL);
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_progress_text(const gchar *message, gpointer data)
+{
+ ProgressData *pdata = data;
+ PyObject *r;
+
+ if (pdata->user_data) {
+ r = PyObject_CallFunction(pdata->text, "sO", message, pdata->user_data);
+ Py_DECREF(pdata->user_data);
+ } else
+ r = PyObject_CallFunction(pdata->text, "s", message);
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static void
+pygimp_progress_value(gdouble percentage, gpointer data)
+{
+ ProgressData *pdata = data;
+ PyObject *r;
+
+ if (pdata->user_data) {
+ r = PyObject_CallFunction(pdata->value, "dO", percentage,
+ pdata->user_data);
+ Py_DECREF(pdata->user_data);
+ } else
+ r = PyObject_CallFunction(pdata->value, "d", percentage);
+
+ if (!r) {
+ PyErr_Print();
+ PyErr_Clear();
+ return;
+ }
+
+ Py_DECREF(r);
+}
+
+static PyObject *
+pygimp_progress_install(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ GimpProgressVtable vtable = { 0, };
+ const gchar *ret;
+ ProgressData *pdata;
+ static char *kwlist[] = { "start", "end", "text", "value", "data", NULL };
+
+ pdata = g_new0(ProgressData, 1);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO|O:progress_install",
+ kwlist,
+ &pdata->start, &pdata->end,
+ &pdata->text, &pdata->value,
+ &pdata->user_data))
+ goto cleanup;
+
+#define PROCESS_FUNC(n) G_STMT_START { \
+ if (!PyCallable_Check(pdata->n)) { \
+ PyErr_SetString(pygimp_error, #n "argument must be callable"); \
+ goto cleanup; \
+ } \
+ Py_INCREF(pdata->n); \
+} G_STMT_END
+
+ PROCESS_FUNC(start);
+ PROCESS_FUNC(end);
+ PROCESS_FUNC(text);
+ PROCESS_FUNC(value);
+
+ Py_XINCREF(pdata->user_data);
+
+#undef PROCESS_FUNC
+
+ vtable.start = pygimp_progress_start;
+ vtable.end = pygimp_progress_end;
+ vtable.set_text = pygimp_progress_text;
+ vtable.set_value = pygimp_progress_value;
+
+ ret = gimp_progress_install_vtable(&vtable, pdata);
+
+ if (!ret) {
+ PyErr_SetString(pygimp_error,
+ "error occurred while installing progress functions");
+
+ Py_DECREF(pdata->start);
+ Py_DECREF(pdata->end);
+ Py_DECREF(pdata->text);
+ Py_DECREF(pdata->value);
+
+ goto cleanup;
+ }
+
+ return PyString_FromString(ret);
+
+cleanup:
+ g_free(pdata);
+ return NULL;
+}
+
+static PyObject *
+pygimp_progress_uninstall(PyObject *self, PyObject *args)
+{
+ ProgressData *pdata;
+ gchar *callback;
+
+ if (!PyArg_ParseTuple(args, "s:progress_uninstall", &callback))
+ return NULL;
+
+ pdata = gimp_progress_uninstall(callback);
+
+ if (!pdata) {
+ PyErr_SetString(pygimp_error,
+ "error occurred while uninstalling progress functions");
+ return NULL;
+ }
+
+ Py_DECREF(pdata->start);
+ Py_DECREF(pdata->end);
+ Py_DECREF(pdata->text);
+ Py_DECREF(pdata->value);
+
+ Py_XDECREF(pdata->user_data);
+
+ g_free(pdata);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_image_list(PyObject *self)
+{
+ gint32 *imgs;
+ int nimgs, i;
+ PyObject *ret;
+
+ imgs = gimp_image_list(&nimgs);
+ ret = PyList_New(nimgs);
+
+ for (i = 0; i < nimgs; i++)
+ PyList_SetItem(ret, i, (PyObject *)pygimp_image_new(imgs[i]));
+
+ g_free(imgs);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_install_procedure(PyObject *self, PyObject *args)
+{
+ char *name, *blurb, *help, *author, *copyright, *date, *menu_path,
+ *image_types, *n, *d;
+ GimpParamDef *params, *return_vals;
+ int type, nparams, nreturn_vals, i;
+ PyObject *pars, *rets;
+
+ if (!PyArg_ParseTuple(args, "sssssszziOO:install_procedure",
+ &name, &blurb, &help,
+ &author, &copyright, &date, &menu_path, &image_types,
+ &type, &pars, &rets))
+ return NULL;
+
+ if (!PySequence_Check(pars) || !PySequence_Check(rets)) {
+ PyErr_SetString(PyExc_TypeError, "last two args must be sequences");
+ return NULL;
+ }
+
+ nparams = PySequence_Length(pars);
+ nreturn_vals = PySequence_Length(rets);
+ params = g_new(GimpParamDef, nparams);
+
+ for (i = 0; i < nparams; i++) {
+ if (!PyArg_ParseTuple(PySequence_GetItem(pars, i), "iss",
+ &(params[i].type), &n, &d)) {
+ g_free(params);
+ return NULL;
+ }
+
+ params[i].name = g_strdup(n);
+ params[i].description = g_strdup(d);
+ }
+
+ return_vals = g_new(GimpParamDef, nreturn_vals);
+
+ for (i = 0; i < nreturn_vals; i++) {
+ if (!PyArg_ParseTuple(PySequence_GetItem(rets, i), "iss",
+ &(return_vals[i].type), &n, &d)) {
+ g_free(params); g_free(return_vals);
+ return NULL;
+ }
+
+ return_vals[i].name = g_strdup(n);
+ return_vals[i].description = g_strdup(d);
+ }
+
+ gimp_install_procedure(name, blurb, help, author, copyright, date,
+ menu_path, image_types, type, nparams, nreturn_vals,
+ params, return_vals);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_install_temp_proc(PyObject *self, PyObject *args)
+{
+ char *name, *blurb, *help, *author, *copyright, *date, *menu_path,
+ *image_types, *n, *d;
+ GimpParamDef *params, *return_vals;
+ int type, nparams, nreturn_vals, i;
+ PyObject *pars, *rets;
+
+ if (!PyArg_ParseTuple(args, "sssssszziOO:install_temp_proc",
+ &name, &blurb, &help,
+ &author, &copyright, &date, &menu_path, &image_types,
+ &type, &pars, &rets))
+ return NULL;
+
+ if (!PySequence_Check(pars) || !PySequence_Check(rets)) {
+ PyErr_SetString(PyExc_TypeError, "last two args must be sequences");
+ return NULL;
+ }
+
+ nparams = PySequence_Length(pars);
+ nreturn_vals = PySequence_Length(rets);
+ params = g_new(GimpParamDef, nparams);
+
+ for (i = 0; i < nparams; i++) {
+ if (!PyArg_ParseTuple(PySequence_GetItem(pars, i), "iss",
+ &(params[i].type), &n, &d)) {
+ g_free(params);
+ return NULL;
+ }
+
+ params[i].name = g_strdup(n);
+ params[i].description = g_strdup(d);
+ }
+
+ return_vals = g_new(GimpParamDef, nreturn_vals);
+
+ for (i = 0; i < nreturn_vals; i++) {
+ if (!PyArg_ParseTuple(PySequence_GetItem(rets, i), "iss",
+ &(return_vals[i].type), &n, &d)) {
+ g_free(params); g_free(return_vals);
+ return NULL;
+ }
+
+ return_vals[i].name = g_strdup(n);
+ return_vals[i].description = g_strdup(d);
+ }
+
+ gimp_install_temp_proc(name, blurb, help, author, copyright, date,
+ menu_path, image_types, type,
+ nparams, nreturn_vals, params, return_vals,
+ pygimp_run_proc);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_uninstall_temp_proc(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:uninstall_temp_proc", &name))
+ return NULL;
+
+ gimp_uninstall_temp_proc(name);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_register_magic_load_handler(PyObject *self, PyObject *args)
+{
+ char *name, *extensions, *prefixes, *magics;
+
+ if (!PyArg_ParseTuple(args, "ssss:register_magic_load_handler",
+ &name, &extensions, &prefixes, &magics))
+ return NULL;
+
+ gimp_register_magic_load_handler(name, extensions, prefixes, magics);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_register_load_handler(PyObject *self, PyObject *args)
+{
+ char *name, *extensions, *prefixes;
+
+ if (!PyArg_ParseTuple(args, "sss:register_load_handler",
+ &name, &extensions, &prefixes))
+ return NULL;
+
+ gimp_register_load_handler(name, extensions, prefixes);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_register_save_handler(PyObject *self, PyObject *args)
+{
+ char *name, *extensions, *prefixes;
+
+ if (!PyArg_ParseTuple(args, "sss:register_save_handler",
+ &name, &extensions, &prefixes))
+ return NULL;
+
+ gimp_register_save_handler(name, extensions, prefixes);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_domain_register(PyObject *self, PyObject *args)
+{
+ char *name, *path = NULL;
+
+ if (!PyArg_ParseTuple(args, "s|s:domain_register", &name, &path))
+ return NULL;
+
+ gimp_plugin_domain_register(name, path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_menu_register(PyObject *self, PyObject *args)
+{
+ char *name, *path;
+
+ if (!PyArg_ParseTuple(args, "ss:menu_register", &name, &path))
+ return NULL;
+
+ gimp_plugin_menu_register(name, path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_gamma(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_gamma());
+}
+
+static PyObject *
+pygimp_gtkrc(PyObject *self)
+{
+ return PyString_FromString(gimp_gtkrc());
+}
+
+static PyObject *
+pygimp_personal_rc_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *basename, *filename;
+ PyObject *ret;
+
+ static char *kwlist[] = { "basename", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s:personal_rc_file", kwlist,
+ &basename))
+ return NULL;
+
+ filename = gimp_personal_rc_file(basename);
+ ret = PyString_FromString(filename);
+ g_free(filename);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_context_push(PyObject *self)
+{
+ gimp_context_push();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_context_pop(PyObject *self)
+{
+ gimp_context_pop();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_get_background(PyObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_context_get_background(&rgb);
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_get_foreground(PyObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_context_get_foreground(&rgb);
+ return pygimp_rgb_new(&rgb);
+}
+
+static PyObject *
+pygimp_set_background(PyObject *self, PyObject *args)
+{
+ PyObject *color;
+ GimpRGB rgb;
+
+ if (PyArg_ParseTuple(args, "O:set_background", &color)) {
+ if (!pygimp_rgb_from_pyobject(color, &rgb))
+ return NULL;
+ } else {
+ PyErr_Clear();
+ if (!pygimp_rgb_from_pyobject(args, &rgb))
+ return NULL;
+ }
+
+ gimp_context_set_background(&rgb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_set_foreground(PyObject *self, PyObject *args)
+{
+ PyObject *color;
+ GimpRGB rgb;
+
+ if (PyArg_ParseTuple(args, "O:set_foreground", &color)) {
+ if (!pygimp_rgb_from_pyobject(color, &rgb))
+ return NULL;
+ } else {
+ PyErr_Clear();
+ if (!pygimp_rgb_from_pyobject(args, &rgb))
+ return NULL;
+ }
+
+ gimp_context_set_foreground(&rgb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_gradients_get_list(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char **list, *filter = NULL;
+ int num, i;
+ PyObject *ret;
+
+ static char *kwlist[] = { "filter", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|s:gradients_get_list", kwlist,
+ &filter))
+ return NULL;
+
+ list = gimp_gradients_get_list(filter, &num);
+
+ ret = PyList_New(num);
+
+ for (i = 0; i < num; i++) {
+ PyList_SetItem(ret, i, PyString_FromString(list[i]));
+ }
+
+ g_strfreev(list);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_context_get_gradient(PyObject *self)
+{
+ char *name;
+ PyObject *ret;
+
+ name = gimp_context_get_gradient();
+ ret = PyString_FromString(name);
+ g_free(name);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_gradients_get_gradient(PyObject *self)
+{
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimp.context_get_gradient") < 0)
+ return NULL;
+
+ return pygimp_context_get_gradient(self);
+}
+
+static PyObject *
+pygimp_context_set_gradient(PyObject *self, PyObject *args)
+{
+ char *actv;
+
+ if (!PyArg_ParseTuple(args, "s:gradients_set_gradient", &actv))
+ return NULL;
+
+ gimp_context_set_gradient(actv);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_gradients_set_gradient(PyObject *self, PyObject *args)
+{
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimp.context_set_gradient") < 0)
+ return NULL;
+
+ return pygimp_context_set_gradient(self, args);
+}
+
+static PyObject *
+pygimp_gradient_get_uniform_samples(PyObject *self, PyObject *args)
+{
+ int num, reverse = FALSE;
+ char *name;
+ int nsamp;
+ double *samp;
+ int i, j;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "si|i:gradient_get_uniform_samples",
+ &name, &num, &reverse))
+ return NULL;
+
+ if (!gimp_gradient_get_uniform_samples(name, num, reverse, &nsamp, &samp)) {
+ PyErr_SetString(pygimp_error, "gradient_get_uniform_samples failed");
+ return NULL;
+ }
+
+ ret = PyList_New(num);
+ for (i = 0, j = 0; i < num; i++, j += 4)
+ PyList_SetItem(ret, i, Py_BuildValue("(dddd)", samp[j],
+ samp[j+1], samp[j+2], samp[j+3]));
+
+ g_free(samp);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_gradient_get_custom_samples(PyObject *self, PyObject *args)
+{
+ int num, reverse = FALSE;
+ char *name;
+ int nsamp;
+ double *pos, *samp;
+ int i, j;
+ PyObject *ret, *item;
+ gboolean success;
+
+ if (!PyArg_ParseTuple(args, "sO|i:gradient_get_custom_samples",
+ &name, &ret, &reverse))
+ return NULL;
+
+ if (!PySequence_Check(ret)) {
+ PyErr_SetString(PyExc_TypeError,
+ "second arg must be a sequence");
+ return NULL;
+ }
+
+ num = PySequence_Length(ret);
+ pos = g_new(gdouble, num);
+
+ for (i = 0; i < num; i++) {
+ item = PySequence_GetItem(ret, i);
+
+ if (!PyFloat_Check(item)) {
+ PyErr_SetString(PyExc_TypeError,
+ "second arg must be a sequence of floats");
+ g_free(pos);
+ return NULL;
+ }
+
+ pos[i] = PyFloat_AsDouble(item);
+ }
+
+ success = gimp_gradient_get_custom_samples(name, num, pos, reverse,
+ &nsamp, &samp);
+ g_free(pos);
+
+ if (!success) {
+ PyErr_SetString(pygimp_error, "gradient_get_custom_samples failed");
+ return NULL;
+ }
+
+ ret = PyList_New(num);
+ for (i = 0, j = 0; i < num; i++, j += 4)
+ PyList_SetItem(ret, i, Py_BuildValue("(dddd)", samp[j],
+ samp[j+1], samp[j+2], samp[j+3]));
+
+ g_free(samp);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_gradients_sample_uniform(PyObject *self, PyObject *args)
+{
+ char *name;
+ PyObject *arg_list, *str, *new_args, *ret;
+
+ if (PyErr_Warn(PyExc_DeprecationWarning,
+ "use gimp.gradient_get_uniform_samples") < 0)
+ return NULL;
+
+ arg_list = PySequence_List(args);
+
+ name = gimp_context_get_gradient();
+
+ str = PyString_FromString(name);
+ g_free(name);
+
+ PyList_Insert(arg_list, 0, str);
+ Py_XDECREF(str);
+
+ new_args = PyList_AsTuple(arg_list);
+ Py_XDECREF(arg_list);
+
+ ret = pygimp_gradient_get_uniform_samples(self, new_args);
+ Py_XDECREF(new_args);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_gradients_sample_custom(PyObject *self, PyObject *args)
+{
+ char *name;
+ PyObject *arg_list, *str, *new_args, *ret;
+
+ if (PyErr_Warn(PyExc_DeprecationWarning,
+ "use gimp.gradient_get_custom_samples") < 0)
+ return NULL;
+
+ arg_list = PySequence_List(args);
+
+ name = gimp_context_get_gradient();
+
+ str = PyString_FromString(name);
+ g_free(name);
+
+ PyList_Insert(arg_list, 0, str);
+ Py_XDECREF(str);
+
+ new_args = PyList_AsTuple(arg_list);
+ Py_XDECREF(arg_list);
+
+ ret = pygimp_gradient_get_custom_samples(self, new_args);
+
+ return ret;
+}
+
+static PyObject *
+pygimp_delete(PyObject *self, PyObject *args)
+{
+ PyGimpImage *img;
+
+ if (!PyArg_ParseTuple(args, "O:delete", &img))
+ return NULL;
+
+ if (pygimp_image_check(img))
+ gimp_image_delete(img->ID);
+ else if (pygimp_drawable_check(img))
+ gimp_item_delete(img->ID);
+ else if (pygimp_display_check(img))
+ gimp_display_delete(img->ID);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+pygimp_displays_flush(PyObject *self)
+{
+ gimp_displays_flush();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_displays_reconnect(PyObject *self, PyObject *args)
+{
+ PyGimpImage *old_img, *new_img;
+
+ if (!PyArg_ParseTuple(args, "O!O!:displays_reconnect",
+ &PyGimpImage_Type, &old_img,
+ &PyGimpImage_Type, &new_img))
+ return NULL;
+
+ if (!gimp_displays_reconnect (old_img->ID, new_img->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not reconnect the displays of image (ID %d) "
+ "to image (ID %d)",
+ old_img->ID, new_img->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_tile_cache_size(PyObject *self, PyObject *args)
+{
+ unsigned long k;
+
+ if (!PyArg_ParseTuple(args, "l:tile_cache_size", &k))
+ return NULL;
+
+ gimp_tile_cache_size(k);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+pygimp_tile_cache_ntiles(PyObject *self, PyObject *args)
+{
+ unsigned long n;
+
+ if (!PyArg_ParseTuple(args, "l:tile_cache_ntiles", &n))
+ return NULL;
+
+ gimp_tile_cache_ntiles(n);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+pygimp_tile_width(PyObject *self)
+{
+ return PyInt_FromLong(gimp_tile_width());
+}
+
+
+static PyObject *
+pygimp_tile_height(PyObject *self)
+{
+ return PyInt_FromLong(gimp_tile_height());
+}
+
+static PyObject *
+pygimp_extension_ack(PyObject *self)
+{
+ gimp_extension_ack();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_extension_enable(PyObject *self)
+{
+ gimp_extension_enable();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_extension_process(PyObject *self, PyObject *args)
+{
+ guint timeout;
+
+ if (!PyArg_ParseTuple(args, "I:extension_process", &timeout))
+ return NULL;
+
+ gimp_extension_process(timeout);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_parasite_find(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_find", &name))
+ return NULL;
+
+ return pygimp_parasite_new(gimp_get_parasite(name));
+}
+
+static PyObject *
+pygimp_parasite_attach(PyObject *self, PyObject *args)
+{
+ PyGimpParasite *parasite;
+
+ if (!PyArg_ParseTuple(args, "O!:parasite_attach",
+ &PyGimpParasite_Type, &parasite))
+ return NULL;
+
+ if (!gimp_attach_parasite(parasite->para)) {
+ PyErr_Format(pygimp_error, "could not attach parasite '%s'",
+ gimp_parasite_name(parasite->para));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_attach_new_parasite(PyObject *self, PyObject *args)
+{
+ GimpParasite *parasite;
+ char *name, *data;
+ int flags, size;
+
+ if (!PyArg_ParseTuple(args, "sis#:attach_new_parasite", &name, &flags,
+ &data, &size))
+ return NULL;
+
+ parasite = gimp_parasite_new (name, flags, size, data);
+
+ if (!gimp_attach_parasite (parasite)) {
+ PyErr_Format(pygimp_error, "could not attach new parasite '%s'", name);
+ gimp_parasite_free (parasite);
+ return NULL;
+ }
+
+ gimp_parasite_free (parasite);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_parasite_detach(PyObject *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_detach", &name))
+ return NULL;
+
+ if (!gimp_detach_parasite(name)) {
+ PyErr_Format(pygimp_error, "could not detach parasite '%s'", name);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_parasite_list(PyObject *self)
+{
+ gint num_parasites;
+ gchar **parasites;
+ PyObject *ret;
+ gint i;
+
+ parasites = gimp_get_parasite_list (&num_parasites);
+
+ ret = PyTuple_New(num_parasites);
+
+ for (i = 0; i < num_parasites; i++)
+ PyTuple_SetItem(ret, i, PyString_FromString(parasites[i]));
+
+ g_strfreev(parasites);
+ return ret;
+}
+
+static PyObject *
+pygimp_show_tool_tips(PyObject *self)
+{
+ return PyBool_FromLong(gimp_show_tool_tips());
+}
+
+static PyObject *
+pygimp_show_help_button(PyObject *self)
+{
+ return PyBool_FromLong(gimp_show_help_button());
+}
+
+static PyObject *
+pygimp_check_size(PyObject *self)
+{
+ return PyInt_FromLong(gimp_check_size());
+}
+
+static PyObject *
+pygimp_check_type(PyObject *self)
+{
+ return PyInt_FromLong(gimp_check_type());
+}
+
+static PyObject *
+pygimp_default_display(PyObject *self)
+{
+ return pygimp_display_new(gimp_default_display());
+}
+
+static PyObject *
+pygimp_wm_class(PyObject *self)
+{
+ return PyString_FromString(gimp_wm_class());
+}
+
+static PyObject *
+pygimp_display_name(PyObject *self)
+{
+ return PyString_FromString(gimp_display_name());
+}
+
+static PyObject *
+pygimp_monitor_number(PyObject *self)
+{
+ return PyInt_FromLong(gimp_monitor_number());
+}
+
+static PyObject *
+pygimp_get_progname(PyObject *self)
+{
+ return PyString_FromString(gimp_get_progname());
+}
+
+static PyObject *
+pygimp_user_directory(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ GimpUserDirectory type;
+ const char *user_dir;
+ PyObject *py_type, *ret;
+
+ static char *kwlist[] = { "type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:user_directory", kwlist,
+ &py_type))
+ return NULL;
+
+ if (pyg_enum_get_value(GIMP_TYPE_USER_DIRECTORY, py_type, (gpointer)&type))
+ return NULL;
+
+ /* GimpUserDirectory and GUserDirectory are compatible */
+ user_dir = g_get_user_special_dir((GUserDirectory)type);
+
+ if (user_dir) {
+ ret = PyString_FromString(user_dir);
+ } else {
+ Py_INCREF(Py_None);
+ ret = Py_None;
+ }
+
+ return ret;
+}
+
+static PyObject *
+pygimp_fonts_refresh(PyObject *self)
+{
+ if (!gimp_fonts_refresh()) {
+ PyErr_SetString(pygimp_error, "could not refresh fonts");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_checks_get_shades(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ int type;
+ guchar light, dark;
+ static char *kwlist[] = { "type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "i:checks_get_shades", kwlist,
+ &type))
+ return NULL;
+
+ if (type < GIMP_CHECK_TYPE_LIGHT_CHECKS ||
+ type > GIMP_CHECK_TYPE_BLACK_ONLY) {
+ PyErr_SetString(PyExc_ValueError, "Invalid check type");
+ return NULL;
+ }
+
+ gimp_checks_get_shades(type, &light, &dark);
+
+ return Py_BuildValue("(ii)", light, dark);
+}
+
+static PyObject *
+pygimp_fonts_get_list(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char **list, *filter = NULL;
+ int num, i;
+ PyObject *ret;
+
+ static char *kwlist[] = { "filter", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|s:fonts_get_list", kwlist,
+ &filter))
+ return NULL;
+
+ list = gimp_fonts_get_list(filter, &num);
+
+ if (num == 0) {
+ PyErr_SetString(pygimp_error, "could not get font list");
+ return NULL;
+ }
+
+ ret = PyList_New(num);
+
+ for (i = 0; i < num; i++) {
+ PyList_SetItem(ret, i, PyString_FromString(list[i]));
+ }
+
+ g_strfreev(list);
+
+ return ret;
+}
+
+static PyObject *
+vectors_to_objects(int num_vectors, int *vectors)
+{
+ PyObject *ret;
+ int i;
+
+ ret = PyList_New(num_vectors);
+ if (ret == NULL)
+ goto done;
+
+ for (i = 0; i < num_vectors; i++)
+ PyList_SetItem(ret, i, pygimp_vectors_new(vectors[i]));
+
+done:
+ g_free(vectors);
+ return ret;
+}
+
+static PyObject *
+pygimp_vectors_import_from_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ PyObject *py_file;
+ gboolean merge = FALSE, scale = FALSE;
+ int *vectors, num_vectors;
+ gboolean success;
+
+ static char *kwlist[] = { "image", "svg_file", "merge", "scale", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!O|ii:vectors_import_from_file", kwlist,
+ &PyGimpImage_Type, &img, &py_file,
+ &merge, &scale))
+ return NULL;
+
+ if (PyString_Check(py_file)) {
+ success = gimp_vectors_import_from_file(img->ID,
+ PyString_AsString(py_file),
+ merge, scale,
+ &num_vectors, &vectors);
+ } else {
+ PyObject *chunk_size, *buffer, *read_method;
+
+ chunk_size = PyInt_FromLong(16 * 1024);
+ if (chunk_size == NULL)
+ return NULL;
+
+ buffer = PyString_FromString("");
+ if (buffer == NULL) {
+ Py_DECREF(chunk_size);
+ return NULL;
+ }
+
+ read_method = PyString_FromString("read");
+ if (read_method == NULL || !PyCallable_Check(read_method)) {
+ Py_XDECREF(read_method);
+ PyErr_SetString(PyExc_TypeError,
+ "svg_file must be an object that has a \"read\" "
+ "method, or a filename (str)");
+ return NULL;
+ }
+
+ while (1) {
+ PyObject *chunk;
+ chunk = PyObject_CallMethodObjArgs(py_file, read_method,
+ chunk_size, NULL);
+
+ if (!chunk || !PyString_Check(chunk)) {
+ Py_XDECREF(chunk);
+ Py_DECREF(chunk_size);
+ Py_DECREF(buffer);
+ Py_DECREF(read_method);
+ return NULL;
+ }
+
+ if (PyString_GET_SIZE(chunk) != 0) {
+ PyString_ConcatAndDel(&buffer, chunk);
+ if (buffer == NULL) {
+ Py_DECREF(chunk_size);
+ Py_DECREF(read_method);
+ return NULL;
+ }
+ } else {
+ Py_DECREF(chunk);
+ break;
+ }
+ }
+
+ success = gimp_vectors_import_from_string(img->ID,
+ PyString_AsString(buffer),
+ PyString_Size(buffer),
+ merge, scale,
+ &num_vectors, &vectors);
+
+ Py_DECREF(chunk_size);
+ Py_DECREF(buffer);
+ Py_DECREF(read_method);
+ }
+
+ if (!success) {
+ PyErr_Format(pygimp_error,
+ "Vectors import failed: %s", gimp_get_pdb_error());
+ return NULL;
+ }
+
+ return vectors_to_objects(num_vectors, vectors);
+}
+
+static PyObject *
+pygimp_vectors_import_from_string(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ const char *svg_string;
+ int length;
+ gboolean merge = FALSE, scale = FALSE;
+ int *vectors, num_vectors;
+ gboolean success;
+
+ static char *kwlist[] = { "image", "svg_string", "merge", "scale", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!s#|ii:vectors_import_from_string", kwlist,
+ &PyGimpImage_Type, &img,
+ &svg_string, &length,
+ &merge, &scale))
+ return NULL;
+
+ success = gimp_vectors_import_from_string(img->ID, svg_string, length,
+ merge, scale,
+ &num_vectors, &vectors);
+
+ if (!success) {
+ PyErr_Format(pygimp_error,
+ "Vectors import failed: %s", gimp_get_pdb_error());
+ return NULL;
+ }
+
+ return vectors_to_objects(num_vectors, vectors);
+}
+
+static PyObject *
+id2image(PyObject *self, PyObject *args)
+{
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i:_id2image", &id))
+ return NULL;
+
+ if (id >= 0)
+ return pygimp_image_new(id);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+id2drawable(PyObject *self, PyObject *args)
+{
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i:_id2drawable", &id))
+ return NULL;
+
+ if (id >= 0)
+ return pygimp_drawable_new(NULL, id);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+id2display(PyObject *self, PyObject *args)
+{
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i:_id2display", &id))
+ return NULL;
+
+ if (id >= 0)
+ return pygimp_display_new(id);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+id2vectors(PyObject *self, PyObject *args)
+{
+ int id;
+
+ if (!PyArg_ParseTuple(args, "i:_id2vectors", &id))
+ return NULL;
+
+ if (id >= 0)
+ return pygimp_vectors_new(id);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+pygimp_export_image (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ PyGimpDrawable *drw = NULL;
+ gchar *format_name = NULL;
+ unsigned int capabilities = -1;
+ GimpExportReturn result;
+ gint32 img_id;
+ gint32 drw_id;
+ PyObject *return_values;
+
+ static char *kwlist[] = { "image", "drawable", "format_name", "capabilities", NULL };
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|OsI:export_image", kwlist,
+ &PyGimpImage_Type, &img,
+ &drw,
+ &format_name,
+ &capabilities))
+ return NULL;
+ if (capabilities == -1) {
+ PyErr_SetString(PyExc_TypeError,
+ "the \"capabilities\" (4th) parameter must be set with "
+ "a combination of the "
+ "EXPORT_CAN_HANDLE_*/EXPORT_NEEDS_ALPHA values. "
+ "(check developer documentation on the C function "
+ "gimp_export_image for details)"
+ );
+ return NULL;
+ }
+
+ /* If no drawable is given, assume the active drawable */
+ if (drw == NULL) {
+ drw = (PyGimpDrawable *)PyObject_GetAttrString((PyObject *)img,
+ "active_drawable");
+ if ((PyObject *)drw == Py_None) {
+ PyErr_SetString(PyExc_ValueError,
+ "No active drawable in the image and no drawable "
+ " specified for export."
+ );
+ return NULL;
+ }
+ }
+ img_id = img->ID;
+ drw_id = drw->ID;
+
+ result = gimp_export_image(&img_id, &drw_id, format_name, capabilities);
+
+ if (img_id != img->ID) {
+ img = (PyGimpImage *)pygimp_image_new(img_id);
+ }
+ else {
+ Py_INCREF(img);
+ }
+ if (drw_id != drw->ID) {
+ drw = (PyGimpDrawable *)pygimp_drawable_new(NULL, drw_id);
+ }
+ else {
+ Py_INCREF(drw);
+ }
+
+ return_values = PyTuple_New(3);
+ PyTuple_SetItem(return_values, 0, PyInt_FromLong(result));
+ PyTuple_SetItem(return_values, 1, (PyObject *)img);
+ PyTuple_SetItem(return_values, 2, (PyObject *)drw);
+
+ return return_values;
+}
+
+static PyObject *
+pygimp_export_dialog_new (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *format_name;
+ gchar *role = NULL;
+ gchar *help_id = NULL;
+ GtkWidget *dialog = NULL;
+
+ static char *kwlist[] = { "format_name", "role", "help_id", NULL };
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|ss:export_dialog", kwlist,
+ &format_name,
+ &role,
+ &help_id))
+ return NULL;
+
+ if (role == NULL) {
+ role = "gimp_export_image";
+ }
+
+ dialog = gimp_export_dialog_new(format_name, role, help_id);
+
+ /* pyobject_new handles NULL values */
+
+ return pygobject_new((GObject *)dialog);
+
+}
+
+/* No need to expose "gimp_export_dialog_get_content_area",
+ * because one just have to call the "get_content_area" method
+ * on the returned export_dialog
+ */
+
+
+/* List of methods defined in the module */
+
+static struct PyMethodDef gimp_methods[] = {
+ {"main", (PyCFunction)pygimp_main, METH_VARARGS},
+ {"quit", (PyCFunction)pygimp_quit, METH_NOARGS},
+ {"message", (PyCFunction)pygimp_message, METH_VARARGS},
+ {"exit", (PyCFunction)pygimp_exit, METH_VARARGS | METH_KEYWORDS},
+ {"set_data", (PyCFunction)pygimp_set_data, METH_VARARGS},
+ {"get_data", (PyCFunction)pygimp_get_data, METH_VARARGS},
+ {"progress_init", (PyCFunction)pygimp_progress_init, METH_VARARGS},
+ {"progress_update", (PyCFunction)pygimp_progress_update, METH_VARARGS},
+ {"progress_install", (PyCFunction)pygimp_progress_install, METH_VARARGS | METH_KEYWORDS},
+ {"progress_uninstall", (PyCFunction)pygimp_progress_uninstall, METH_VARARGS},
+ {"image_list", (PyCFunction)pygimp_image_list, METH_NOARGS},
+ {"install_procedure", (PyCFunction)pygimp_install_procedure, METH_VARARGS},
+ {"install_temp_proc", (PyCFunction)pygimp_install_temp_proc, METH_VARARGS},
+ {"uninstall_temp_proc", (PyCFunction)pygimp_uninstall_temp_proc, METH_VARARGS},
+ {"register_magic_load_handler", (PyCFunction)pygimp_register_magic_load_handler, METH_VARARGS},
+ {"register_load_handler", (PyCFunction)pygimp_register_load_handler, METH_VARARGS},
+ {"register_save_handler", (PyCFunction)pygimp_register_save_handler, METH_VARARGS},
+ {"domain_register", (PyCFunction)pygimp_domain_register, METH_VARARGS},
+ {"menu_register", (PyCFunction)pygimp_menu_register, METH_VARARGS},
+ {"gamma", (PyCFunction)pygimp_gamma, METH_NOARGS},
+ {"gtkrc", (PyCFunction)pygimp_gtkrc, METH_NOARGS},
+ {"personal_rc_file", (PyCFunction)pygimp_personal_rc_file, METH_VARARGS | METH_KEYWORDS},
+ {"context_push", (PyCFunction)pygimp_context_push, METH_NOARGS},
+ {"context_pop", (PyCFunction)pygimp_context_pop, METH_NOARGS},
+ {"get_background", (PyCFunction)pygimp_get_background, METH_NOARGS},
+ {"get_foreground", (PyCFunction)pygimp_get_foreground, METH_NOARGS},
+ {"set_background", (PyCFunction)pygimp_set_background, METH_VARARGS},
+ {"set_foreground", (PyCFunction)pygimp_set_foreground, METH_VARARGS},
+ {"gradients_get_list", (PyCFunction)pygimp_gradients_get_list, METH_VARARGS | METH_KEYWORDS},
+ {"context_get_gradient", (PyCFunction)pygimp_context_get_gradient, METH_NOARGS},
+ {"context_set_gradient", (PyCFunction)pygimp_context_set_gradient, METH_VARARGS},
+ {"gradients_get_gradient", (PyCFunction)pygimp_gradients_get_gradient, METH_NOARGS},
+ {"gradients_set_gradient", (PyCFunction)pygimp_gradients_set_gradient, METH_VARARGS},
+ {"gradient_get_uniform_samples", (PyCFunction)pygimp_gradient_get_uniform_samples, METH_VARARGS},
+ {"gradient_get_custom_samples", (PyCFunction)pygimp_gradient_get_custom_samples, METH_VARARGS},
+ {"gradients_sample_uniform", (PyCFunction)pygimp_gradients_sample_uniform, METH_VARARGS},
+ {"gradients_sample_custom", (PyCFunction)pygimp_gradients_sample_custom, METH_VARARGS},
+ {"delete", (PyCFunction)pygimp_delete, METH_VARARGS},
+ {"displays_flush", (PyCFunction)pygimp_displays_flush, METH_NOARGS},
+ {"displays_reconnect", (PyCFunction)pygimp_displays_reconnect, METH_VARARGS},
+ {"tile_cache_size", (PyCFunction)pygimp_tile_cache_size, METH_VARARGS},
+ {"tile_cache_ntiles", (PyCFunction)pygimp_tile_cache_ntiles, METH_VARARGS},
+ {"tile_width", (PyCFunction)pygimp_tile_width, METH_NOARGS},
+ {"tile_height", (PyCFunction)pygimp_tile_height, METH_NOARGS},
+ {"extension_ack", (PyCFunction)pygimp_extension_ack, METH_NOARGS},
+ {"extension_enable", (PyCFunction)pygimp_extension_enable, METH_NOARGS},
+ {"extension_process", (PyCFunction)pygimp_extension_process, METH_VARARGS},
+ {"parasite_find", (PyCFunction)pygimp_parasite_find, METH_VARARGS},
+ {"parasite_attach", (PyCFunction)pygimp_parasite_attach, METH_VARARGS},
+ {"attach_new_parasite",(PyCFunction)pygimp_attach_new_parasite,METH_VARARGS},
+ {"parasite_detach", (PyCFunction)pygimp_parasite_detach, METH_VARARGS},
+ {"parasite_list", (PyCFunction)pygimp_parasite_list, METH_NOARGS},
+ {"show_tool_tips", (PyCFunction)pygimp_show_tool_tips, METH_NOARGS},
+ {"show_help_button", (PyCFunction)pygimp_show_help_button, METH_NOARGS},
+ {"check_size", (PyCFunction)pygimp_check_size, METH_NOARGS},
+ {"check_type", (PyCFunction)pygimp_check_type, METH_NOARGS},
+ {"default_display", (PyCFunction)pygimp_default_display, METH_NOARGS},
+ {"wm_class", (PyCFunction)pygimp_wm_class, METH_NOARGS},
+ {"display_name", (PyCFunction)pygimp_display_name, METH_NOARGS},
+ {"monitor_number", (PyCFunction)pygimp_monitor_number, METH_NOARGS},
+ {"get_progname", (PyCFunction)pygimp_get_progname, METH_NOARGS},
+ {"user_directory", (PyCFunction)pygimp_user_directory, METH_VARARGS | METH_KEYWORDS},
+ {"fonts_refresh", (PyCFunction)pygimp_fonts_refresh, METH_NOARGS},
+ {"fonts_get_list", (PyCFunction)pygimp_fonts_get_list, METH_VARARGS | METH_KEYWORDS},
+ {"checks_get_shades", (PyCFunction)pygimp_checks_get_shades, METH_VARARGS | METH_KEYWORDS},
+ {"vectors_import_from_file", (PyCFunction)pygimp_vectors_import_from_file, METH_VARARGS | METH_KEYWORDS},
+ {"vectors_import_from_string", (PyCFunction)pygimp_vectors_import_from_string, METH_VARARGS | METH_KEYWORDS},
+ {"_id2image", (PyCFunction)id2image, METH_VARARGS},
+ {"_id2drawable", (PyCFunction)id2drawable, METH_VARARGS},
+ {"_id2display", (PyCFunction)id2display, METH_VARARGS},
+ {"_id2vectors", (PyCFunction)id2vectors, METH_VARARGS},
+ {"export_image", (PyCFunction)pygimp_export_image, METH_VARARGS | METH_KEYWORDS},
+ {"export_dialog", (PyCFunction)pygimp_export_dialog_new, METH_VARARGS | METH_KEYWORDS},
+ {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
+};
+
+
+static struct _PyGimp_Functions pygimp_api_functions = {
+ &PyGimpImage_Type,
+ pygimp_image_new,
+ &PyGimpDisplay_Type,
+ pygimp_display_new,
+ &PyGimpItem_Type,
+ pygimp_item_new,
+ &PyGimpDrawable_Type,
+ pygimp_drawable_new,
+ &PyGimpLayer_Type,
+ pygimp_layer_new,
+ &PyGimpGroupLayer_Type,
+ pygimp_group_layer_new,
+ &PyGimpChannel_Type,
+ pygimp_channel_new,
+ &PyGimpVectors_Type,
+ pygimp_vectors_new,
+};
+
+
+/* Initialization function for the module (*must* be called initgimp) */
+
+static char gimp_module_documentation[] =
+"This module provides interfaces to allow you to write gimp plug-ins"
+;
+
+void initgimp(void);
+
+PyMODINIT_FUNC
+initgimp(void)
+{
+ PyObject *m;
+
+ PyGimpPDB_Type.ob_type = &PyType_Type;
+ PyGimpPDB_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpPDB_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpPDB_Type) < 0)
+ return;
+
+ PyGimpPDBFunction_Type.ob_type = &PyType_Type;
+ PyGimpPDBFunction_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpPDBFunction_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpPDBFunction_Type) < 0)
+ return;
+
+ PyGimpImage_Type.ob_type = &PyType_Type;
+ PyGimpImage_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpImage_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpImage_Type) < 0)
+ return;
+
+ PyGimpDisplay_Type.ob_type = &PyType_Type;
+ PyGimpDisplay_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpDisplay_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpDisplay_Type) < 0)
+ return;
+
+ PyGimpLayer_Type.ob_type = &PyType_Type;
+ PyGimpLayer_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpLayer_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpLayer_Type) < 0)
+ return;
+
+ PyGimpGroupLayer_Type.ob_type = &PyType_Type;
+ PyGimpGroupLayer_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpGroupLayer_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpGroupLayer_Type) < 0)
+ return;
+
+ PyGimpChannel_Type.ob_type = &PyType_Type;
+ PyGimpChannel_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpChannel_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpChannel_Type) < 0)
+ return;
+
+ PyGimpTile_Type.ob_type = &PyType_Type;
+ PyGimpTile_Type.tp_alloc = PyType_GenericAlloc;
+ if (PyType_Ready(&PyGimpTile_Type) < 0)
+ return;
+
+ PyGimpPixelRgn_Type.ob_type = &PyType_Type;
+ PyGimpPixelRgn_Type.tp_alloc = PyType_GenericAlloc;
+ if (PyType_Ready(&PyGimpPixelRgn_Type) < 0)
+ return;
+
+ PyGimpParasite_Type.ob_type = &PyType_Type;
+ PyGimpParasite_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpParasite_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpParasite_Type) < 0)
+ return;
+
+ PyGimpVectorsStroke_Type.ob_type = &PyType_Type;
+ PyGimpVectorsStroke_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpVectorsStroke_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpVectorsStroke_Type) < 0)
+ return;
+
+ PyGimpVectorsBezierStroke_Type.ob_type = &PyType_Type;
+ PyGimpVectorsBezierStroke_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpVectorsBezierStroke_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpVectorsBezierStroke_Type) < 0)
+ return;
+
+ PyGimpVectors_Type.ob_type = &PyType_Type;
+ PyGimpVectors_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpVectors_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpVectors_Type) < 0)
+ return;
+
+ PyGimpPixelFetcher_Type.ob_type = &PyType_Type;
+ PyGimpPixelFetcher_Type.tp_alloc = PyType_GenericAlloc;
+ PyGimpPixelFetcher_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyGimpPixelFetcher_Type) < 0)
+ return;
+
+ pygimp_init_pygobject();
+ init_pygimpcolor();
+
+ /* initialize i18n support */
+ bindtextdomain (GETTEXT_PACKAGE "-python", gimp_locale_directory ());
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE "-python", "UTF-8");
+#endif
+
+ /* set the default python encoding to utf-8 */
+ PyUnicode_SetDefaultEncoding("utf-8");
+
+ /* initialize gegl */
+ gegl_init(0, NULL);
+
+ /* Create the module and add the functions */
+ m = Py_InitModule4("gimp", gimp_methods,
+ gimp_module_documentation,
+ NULL, PYTHON_API_VERSION);
+
+ /* Add some symbolic constants to the module */
+ pygimp_error = PyErr_NewException("gimp.error", PyExc_RuntimeError, NULL);
+ PyModule_AddObject(m, "error", pygimp_error);
+
+ PyModule_AddObject(m, "pdb", pygimp_pdb_new());
+
+ /* export the types used in gimpmodule */
+ Py_INCREF(&PyGimpImage_Type);
+ PyModule_AddObject(m, "Image", (PyObject *)&PyGimpImage_Type);
+
+ Py_INCREF(&PyGimpItem_Type);
+ PyModule_AddObject(m, "Item", (PyObject *)&PyGimpItem_Type);
+
+ Py_INCREF(&PyGimpDrawable_Type);
+ PyModule_AddObject(m, "Drawable", (PyObject *)&PyGimpDrawable_Type);
+
+ Py_INCREF(&PyGimpLayer_Type);
+ PyModule_AddObject(m, "Layer", (PyObject *)&PyGimpLayer_Type);
+
+ Py_INCREF(&PyGimpGroupLayer_Type);
+ PyModule_AddObject(m, "GroupLayer", (PyObject *)&PyGimpGroupLayer_Type);
+
+ Py_INCREF(&PyGimpChannel_Type);
+ PyModule_AddObject(m, "Channel", (PyObject *)&PyGimpChannel_Type);
+
+ Py_INCREF(&PyGimpDisplay_Type);
+ PyModule_AddObject(m, "Display", (PyObject *)&PyGimpDisplay_Type);
+
+ Py_INCREF(&PyGimpTile_Type);
+ PyModule_AddObject(m, "Tile", (PyObject *)&PyGimpTile_Type);
+
+ Py_INCREF(&PyGimpPixelRgn_Type);
+ PyModule_AddObject(m, "PixelRgn", (PyObject *)&PyGimpPixelRgn_Type);
+
+ Py_INCREF(&PyGimpParasite_Type);
+ PyModule_AddObject(m, "Parasite", (PyObject *)&PyGimpParasite_Type);
+
+ Py_INCREF(&PyGimpVectorsBezierStroke_Type);
+ PyModule_AddObject(m, "VectorsBezierStroke", (PyObject *)&PyGimpVectorsBezierStroke_Type);
+
+ Py_INCREF(&PyGimpVectors_Type);
+ PyModule_AddObject(m, "Vectors", (PyObject *)&PyGimpVectors_Type);
+
+ Py_INCREF(&PyGimpPixelFetcher_Type);
+ PyModule_AddObject(m, "PixelFetcher", (PyObject *)&PyGimpPixelFetcher_Type);
+
+ /* for other modules */
+ pygimp_api_functions.pygimp_error = pygimp_error;
+
+ PyModule_AddObject(m, "_PyGimp_API",
+ PyCObject_FromVoidPtr(&pygimp_api_functions, NULL));
+
+ PyModule_AddObject(m, "version",
+ Py_BuildValue("(iii)",
+ gimp_major_version,
+ gimp_minor_version,
+ gimp_micro_version));
+
+ /* Some environment constants */
+ PyModule_AddObject(m, "directory",
+ PyString_FromString(gimp_directory()));
+ PyModule_AddObject(m, "data_directory",
+ PyString_FromString(gimp_data_directory()));
+ PyModule_AddObject(m, "locale_directory",
+ PyString_FromString(gimp_locale_directory()));
+ PyModule_AddObject(m, "sysconf_directory",
+ PyString_FromString(gimp_sysconf_directory()));
+ PyModule_AddObject(m, "plug_in_directory",
+ PyString_FromString(gimp_plug_in_directory()));
+
+ /* Check for errors */
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module gimp");
+}
diff --git a/plug-ins/pygimp/gimpplugin.py b/plug-ins/pygimp/gimpplugin.py
new file mode 100644
index 0000000..eeaa3f3
--- /dev/null
+++ b/plug-ins/pygimp/gimpplugin.py
@@ -0,0 +1,81 @@
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# plugin.py -- helper for writing gimp plugins
+# Copyright (C) 1997, James Henstridge.
+#
+# This is a small wrapper that makes plugins look like an object class that
+# you can derive to create your plugin. With this wrapper, you are pretty
+# much responsible for doing everything (checking run_mode, gui, etc). If
+# you want to write a quick plugin, you probably want the gimpfu module.
+#
+# A plugin using this module would look something like this:
+#
+# import gimp, gimpplugin
+#
+# pdb = gimp.pdb
+#
+# class myplugin(gimpplugin.plugin):
+# def query(self):
+# gimp.install_procedure("plug_in_mine", ...)
+#
+# def plug_in_mine(self, par1, par2, par3,...):
+# do_something()
+#
+# if __name__ == '__main__':
+# myplugin().start()
+
+import gimp
+
+class plugin:
+ def start(self):
+ # only pass the init()/quit() member functions to gimp.main() if the
+ # plug-in overrides them, to avoid the default NOP versions from being
+ # called unnecessarily. in particular, this avoids plug-ins that don't
+ # implement init() from being registered as having an init function,
+ # causing them to be run at each startup.
+ def get_func(name):
+ if getattr(self.__class__, name) != getattr(plugin, name):
+ return getattr(self, name)
+ else:
+ return None
+
+ gimp.main(get_func("init"),
+ get_func("quit"),
+ self.query,
+ self._run)
+
+ def init(self):
+ pass
+
+ def quit(self):
+ pass
+
+ def query(self):
+ pass
+
+ def _run(self, name, params):
+ import sys
+ if "gimpui" in sys.modules.keys():
+ sys.modules["gimpui"].gimp_ui_init ()
+
+ if hasattr(self, name):
+ return apply(getattr(self, name), params)
+ else:
+ raise AttributeError, name
+
+if __name__ == '__main__':
+ plugin().start()
diff --git a/plug-ins/pygimp/gimpshelf.py b/plug-ins/pygimp/gimpshelf.py
new file mode 100644
index 0000000..999d319
--- /dev/null
+++ b/plug-ins/pygimp/gimpshelf.py
@@ -0,0 +1,92 @@
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# gimpshelf.py -- a simple module to help gimp modules written in Python
+# store persistent data.
+#
+# Copyright (C) 1997, James Henstridge
+#
+# The gimp module provides a basic method for storing information that persists
+# for a whole gimp session, but only allows for the storage of strings. This
+# is because other Python types usually have pointers to other Python objects,
+# making it difficult to work out what to save. This module gives an interface
+# to the gimp module's primitive interface, which resembles the shelve module.
+
+# use cPickle and cStringIO if available
+
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
+import gimp
+
+import copy_reg
+
+def _image_id(obj):
+ return gimp._id2image, (obj.ID,)
+
+def _drawable_id(obj):
+ return gimp._id2drawable, (obj.ID,)
+
+def _display_id(obj):
+ return gimp._id2display, (obj.ID,)
+
+def _vectors_id(obj):
+ return gimp._id2vectors, (int(obj.ID),)
+
+copy_reg.pickle(gimp.Image, _image_id, gimp._id2image)
+copy_reg.pickle(gimp.Layer, _drawable_id, gimp._id2drawable)
+copy_reg.pickle(gimp.GroupLayer, _drawable_id, gimp._id2drawable)
+copy_reg.pickle(gimp.Channel, _drawable_id, gimp._id2drawable)
+copy_reg.pickle(gimp.Display, _display_id, gimp._id2display)
+copy_reg.pickle(gimp.Vectors, _vectors_id, gimp._id2vectors)
+
+del copy_reg, _image_id, _drawable_id, _display_id, _vectors_id
+
+class Gimpshelf:
+ def has_key(self, key):
+ try:
+ s = gimp.get_data(key)
+ return 1
+ except gimp.error:
+ return 0
+
+ def __getitem__(self, key):
+ try:
+ s = gimp.get_data(key)
+ except gimp.error:
+ raise KeyError, key
+
+ f = StringIO.StringIO(s)
+ return pickle.Unpickler(f).load()
+
+ def __setitem__(self, key, value):
+ f = StringIO.StringIO()
+ p = pickle.Pickler(f)
+ p.dump(value)
+ gimp.set_data(key, f.getvalue())
+
+ def __delitem__(self, key):
+ gimp.set_data(key, '')
+
+shelf = Gimpshelf()
+del Gimpshelf
diff --git a/plug-ins/pygimp/gimpthumb.c b/plug-ins/pygimp/gimpthumb.c
new file mode 100644
index 0000000..d917e6b
--- /dev/null
+++ b/plug-ins/pygimp/gimpthumb.c
@@ -0,0 +1,694 @@
+/* -- THIS FILE IS GENERATED - DO NOT EDIT *//* -*- Mode: C; c-basic-offset: 4 -*- */
+
+#include <Python.h>
+
+
+
+#line 3 "gimpthumb.override"
+#include <Python.h>
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <libgimpthumb/gimpthumb.h>
+
+/* TODO: Add a header for these */
+void gimpthumb_register_classes(PyObject *d);
+void gimpthumb_add_constants(PyObject *module, const gchar *strip_prefix);
+#line 21 "gimpthumb.c"
+
+
+/* ---------- types from other modules ---------- */
+static PyTypeObject *_PyGObject_Type;
+#define PyGObject_Type (*_PyGObject_Type)
+static PyTypeObject *_PyGdkPixbuf_Type;
+#define PyGdkPixbuf_Type (*_PyGdkPixbuf_Type)
+
+
+/* ---------- forward type declarations ---------- */
+PyTypeObject G_GNUC_INTERNAL PyGimpThumbnail_Type;
+
+#line 34 "gimpthumb.c"
+
+
+
+/* ----------- GimpThumbnail ----------- */
+
+static int
+_wrap_gimp_thumbnail_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpthumb.Thumbnail.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpthumb.Thumbnail object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_set_uri(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "uri", NULL };
+ char *uri;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.Thumbnail.set_uri", kwlist, &uri))
+ return NULL;
+
+ gimp_thumbnail_set_uri(GIMP_THUMBNAIL(self->obj), uri);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_set_filename(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "filename", NULL };
+ char *filename;
+ int ret;
+ GError *error = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.Thumbnail.set_filename", kwlist, &filename))
+ return NULL;
+
+ ret = gimp_thumbnail_set_filename(GIMP_THUMBNAIL(self->obj), filename, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_set_from_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "filename", NULL };
+ char *filename;
+ int ret;
+ GError *error = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.Thumbnail.set_from_thumb", kwlist, &filename))
+ return NULL;
+
+ ret = gimp_thumbnail_set_from_thumb(GIMP_THUMBNAIL(self->obj), filename, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_peek_image(PyGObject *self)
+{
+ gint ret;
+
+
+ ret = gimp_thumbnail_peek_image(GIMP_THUMBNAIL(self->obj));
+
+ return pyg_enum_from_gtype(GIMP_TYPE_THUMB_STATE, ret);
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_peek_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ gint ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Thumbnail.peek_thumb", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumbnail_peek_thumb(GIMP_THUMBNAIL(self->obj), size);
+
+ return pyg_enum_from_gtype(GIMP_TYPE_THUMB_STATE, ret);
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_check_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ gint ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Thumbnail.check_thumb", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumbnail_check_thumb(GIMP_THUMBNAIL(self->obj), size);
+
+ return pyg_enum_from_gtype(GIMP_TYPE_THUMB_STATE, ret);
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_load_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ GdkPixbuf *ret;
+ GError *error = NULL;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Thumbnail.load_thumb", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumbnail_load_thumb(GIMP_THUMBNAIL(self->obj), size, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_save_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "pixbuf", "software", NULL };
+ PyGObject *pixbuf;
+ char *software;
+ int ret;
+ GError *error = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!s:Gimp.Thumbnail.save_thumb", kwlist, &PyGdkPixbuf_Type, &pixbuf, &software))
+ return NULL;
+
+ ret = gimp_thumbnail_save_thumb(GIMP_THUMBNAIL(self->obj), GDK_PIXBUF(pixbuf->obj), software, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_save_thumb_local(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "pixbuf", "software", NULL };
+ PyGObject *pixbuf;
+ char *software;
+ int ret;
+ GError *error = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!s:Gimp.Thumbnail.save_thumb_local", kwlist, &PyGdkPixbuf_Type, &pixbuf, &software))
+ return NULL;
+
+ ret = gimp_thumbnail_save_thumb_local(GIMP_THUMBNAIL(self->obj), GDK_PIXBUF(pixbuf->obj), software, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_save_failure(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "software", NULL };
+ char *software;
+ int ret;
+ GError *error = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.Thumbnail.save_failure", kwlist, &software))
+ return NULL;
+
+ ret = gimp_thumbnail_save_failure(GIMP_THUMBNAIL(self->obj), software, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_delete_failure(PyGObject *self)
+{
+
+ gimp_thumbnail_delete_failure(GIMP_THUMBNAIL(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_delete_others(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Thumbnail.delete_others", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ gimp_thumbnail_delete_others(GIMP_THUMBNAIL(self->obj), size);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumbnail_has_failed(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_thumbnail_has_failed(GIMP_THUMBNAIL(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static const PyMethodDef _PyGimpThumbnail_methods[] = {
+ { "set_uri", (PyCFunction)_wrap_gimp_thumbnail_set_uri, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_filename", (PyCFunction)_wrap_gimp_thumbnail_set_filename, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_from_thumb", (PyCFunction)_wrap_gimp_thumbnail_set_from_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "peek_image", (PyCFunction)_wrap_gimp_thumbnail_peek_image, METH_NOARGS,
+ NULL },
+ { "peek_thumb", (PyCFunction)_wrap_gimp_thumbnail_peek_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "check_thumb", (PyCFunction)_wrap_gimp_thumbnail_check_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "load_thumb", (PyCFunction)_wrap_gimp_thumbnail_load_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "save_thumb", (PyCFunction)_wrap_gimp_thumbnail_save_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "save_thumb_local", (PyCFunction)_wrap_gimp_thumbnail_save_thumb_local, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "save_failure", (PyCFunction)_wrap_gimp_thumbnail_save_failure, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "delete_failure", (PyCFunction)_wrap_gimp_thumbnail_delete_failure, METH_NOARGS,
+ NULL },
+ { "delete_others", (PyCFunction)_wrap_gimp_thumbnail_delete_others, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "has_failed", (PyCFunction)_wrap_gimp_thumbnail_has_failed, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpThumbnail_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpthumb.Thumbnail", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpThumbnail_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_thumbnail_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- functions ----------- */
+
+#line 25 "gimpthumb.override"
+static PyObject *
+_wrap_gimp_thumb_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *creator, *thumb_basedir = NULL;
+
+ static char *kwlist[] = { "creator", "thumb_basedir", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s|z:init", kwlist,
+ &creator, &thumb_basedir))
+ return NULL;
+
+ return PyBool_FromLong(gimp_thumb_init(creator, thumb_basedir));
+}
+#line 375 "gimpthumb.c"
+
+
+#line 41 "gimpthumb.override"
+static PyObject *
+_wrap_gimp_thumb_find_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *uri, *ret;
+ PyObject *py_size, *py_ret;
+ GimpThumbSize size;
+
+ static char *kwlist[] = { "uri", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO:find_thumb", kwlist,
+ &uri, &py_size))
+ return NULL;
+
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gint *)&size))
+ return NULL;
+
+ ret = gimp_thumb_find_thumb(uri, &size);
+
+ if (ret == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ py_ret = Py_BuildValue("sN", ret,
+ pyg_enum_from_gtype(GIMP_TYPE_THUMB_SIZE, size));
+ g_free(ret);
+ return py_ret;
+}
+#line 407 "gimpthumb.c"
+
+
+#line 71 "gimpthumb.override"
+static PyObject *
+_wrap_gimp_thumb_file_test(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *filename;
+ GimpThumbFileType ret;
+ gint64 mtime, size;
+ gint err_no;
+
+ static char *kwlist[] = { "filename", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:file_test", kwlist,
+ &filename))
+ return NULL;
+
+ ret = gimp_thumb_file_test(filename, &mtime, &size, &err_no);
+
+ if (ret == GIMP_THUMB_FILE_TYPE_NONE) {
+ PyObject *v = Py_BuildValue("iss",
+ err_no, g_strerror(err_no), filename);
+ if (v != NULL) {
+ PyErr_SetObject(PyExc_IOError, v);
+ Py_DECREF(v);
+ }
+
+ return NULL;
+ }
+
+ return Py_BuildValue("NNN",
+ pyg_enum_from_gtype(GIMP_TYPE_THUMB_FILE_TYPE, ret),
+ PyLong_FromLongLong(mtime),
+ PyLong_FromLongLong(size));
+}
+#line 443 "gimpthumb.c"
+
+
+static PyObject *
+_wrap_gimp_thumb_name_from_uri(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "uri", "size", NULL };
+ char *uri;
+ PyObject *py_size = NULL;
+ gchar *ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:name_from_uri", kwlist, &uri, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_name_from_uri(uri, size);
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumb_get_thumb_dir(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ const gchar *ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:get_thumb_dir", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_get_thumb_dir(size);
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumb_ensure_thumb_dir(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "size", NULL };
+ PyObject *py_size = NULL;
+ int ret;
+ GError *error = NULL;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:ensure_thumb_dir", kwlist, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_ensure_thumb_dir(size, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbs_delete_for_uri(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "uri", NULL };
+ char *uri;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:delete_for_uri", kwlist, &uri))
+ return NULL;
+
+ gimp_thumbs_delete_for_uri(uri);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumb_name_from_uri_local(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "uri", "size", NULL };
+ char *uri;
+ PyObject *py_size = NULL;
+ gchar *ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:name_from_uri_local", kwlist, &uri, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_name_from_uri_local(uri, size);
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumb_get_thumb_dir_local(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "dirname", "size", NULL };
+ char *dirname;
+ PyObject *py_size = NULL;
+ gchar *ret;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:get_thumb_dir_local", kwlist, &dirname, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_get_thumb_dir_local(dirname, size);
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_thumb_ensure_thumb_dir_local(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "dirname", "size", NULL };
+ char *dirname;
+ PyObject *py_size = NULL;
+ int ret;
+ GError *error = NULL;
+ GimpThumbSize size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:ensure_thumb_dir_local", kwlist, &dirname, &py_size))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gpointer)&size))
+ return NULL;
+
+ ret = gimp_thumb_ensure_thumb_dir_local(dirname, size, &error);
+
+ if (pyg_error_check(&error))
+ return NULL;
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_thumbs_delete_for_uri_local(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "uri", NULL };
+ char *uri;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:delete_for_uri_local", kwlist, &uri))
+ return NULL;
+
+ gimp_thumbs_delete_for_uri_local(uri);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+const PyMethodDef gimpthumb_functions[] = {
+ { "init", (PyCFunction)_wrap_gimp_thumb_init, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "find_thumb", (PyCFunction)_wrap_gimp_thumb_find_thumb, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "file_test", (PyCFunction)_wrap_gimp_thumb_file_test, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "name_from_uri", (PyCFunction)_wrap_gimp_thumb_name_from_uri, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_thumb_dir", (PyCFunction)_wrap_gimp_thumb_get_thumb_dir, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "ensure_thumb_dir", (PyCFunction)_wrap_gimp_thumb_ensure_thumb_dir, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "delete_for_uri", (PyCFunction)_wrap_gimp_thumbs_delete_for_uri, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "name_from_uri_local", (PyCFunction)_wrap_gimp_thumb_name_from_uri_local, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_thumb_dir_local", (PyCFunction)_wrap_gimp_thumb_get_thumb_dir_local, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "ensure_thumb_dir_local", (PyCFunction)_wrap_gimp_thumb_ensure_thumb_dir_local, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "delete_for_uri_local", (PyCFunction)_wrap_gimp_thumbs_delete_for_uri_local, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+
+/* ----------- enums and flags ----------- */
+
+void
+gimpthumb_add_constants(PyObject *module, const gchar *strip_prefix)
+{
+#ifdef VERSION
+ PyModule_AddStringConstant(module, "__version__", VERSION);
+#endif
+ pyg_enum_add(module, "ThumbFileType", strip_prefix, GIMP_TYPE_THUMB_FILE_TYPE);
+ pyg_enum_add(module, "ThumbSize", strip_prefix, GIMP_TYPE_THUMB_SIZE);
+ pyg_enum_add(module, "ThumbState", strip_prefix, GIMP_TYPE_THUMB_STATE);
+
+ if (PyErr_Occurred())
+ PyErr_Print();
+}
+
+/* initialise stuff extension classes */
+void
+gimpthumb_register_classes(PyObject *d)
+{
+ PyObject *module;
+
+ if ((module = PyImport_ImportModule("gobject")) != NULL) {
+ _PyGObject_Type = (PyTypeObject *)PyObject_GetAttrString(module, "GObject");
+ if (_PyGObject_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name GObject from gobject");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gobject");
+ return ;
+ }
+ if ((module = PyImport_ImportModule("gtk.gdk")) != NULL) {
+ _PyGdkPixbuf_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Pixbuf");
+ if (_PyGdkPixbuf_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Pixbuf from gtk.gdk");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gtk.gdk");
+ return ;
+ }
+
+
+#line 692 "gimpthumb.c"
+ pygobject_register_class(d, "GimpThumbnail", GIMP_TYPE_THUMBNAIL, &PyGimpThumbnail_Type, Py_BuildValue("(O)", &PyGObject_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_THUMBNAIL);
+}
diff --git a/plug-ins/pygimp/gimpthumb.defs b/plug-ins/pygimp/gimpthumb.defs
new file mode 100644
index 0000000..35ecc7b
--- /dev/null
+++ b/plug-ins/pygimp/gimpthumb.defs
@@ -0,0 +1,312 @@
+;; -*- scheme -*-
+; object definitions ...
+(define-object Thumbnail
+ (in-module "Gimp")
+ (parent "GObject")
+ (c-name "GimpThumbnail")
+ (gtype-id "GIMP_TYPE_THUMBNAIL")
+)
+
+;; Enumerations and flags ...
+
+(define-enum ThumbFileType
+ (in-module "Gimp")
+ (c-name "GimpThumbFileType")
+ (gtype-id "GIMP_TYPE_THUMB_FILE_TYPE")
+ (values
+ '("none" "GIMP_THUMB_FILE_TYPE_NONE")
+ '("regular" "GIMP_THUMB_FILE_TYPE_REGULAR")
+ '("folder" "GIMP_THUMB_FILE_TYPE_FOLDER")
+ '("special" "GIMP_THUMB_FILE_TYPE_SPECIAL")
+ )
+)
+
+(define-enum ThumbSize
+ (in-module "Gimp")
+ (c-name "GimpThumbSize")
+ (gtype-id "GIMP_TYPE_THUMB_SIZE")
+ (values
+ '("fail" "GIMP_THUMB_SIZE_FAIL")
+ '("normal" "GIMP_THUMB_SIZE_NORMAL")
+ '("large" "GIMP_THUMB_SIZE_LARGE")
+ )
+)
+
+(define-enum ThumbState
+ (in-module "Gimp")
+ (c-name "GimpThumbState")
+ (gtype-id "GIMP_TYPE_THUMB_STATE")
+ (values
+ '("unknown" "GIMP_THUMB_STATE_UNKNOWN")
+ '("remote" "GIMP_THUMB_STATE_REMOTE")
+ '("folder" "GIMP_THUMB_STATE_FOLDER")
+ '("special" "GIMP_THUMB_STATE_SPECIAL")
+ '("not-found" "GIMP_THUMB_STATE_NOT_FOUND")
+ '("exists" "GIMP_THUMB_STATE_EXISTS")
+ '("old" "GIMP_THUMB_STATE_OLD")
+ '("failed" "GIMP_THUMB_STATE_FAILED")
+ '("ok" "GIMP_THUMB_STATE_OK")
+ )
+)
+
+
+;; From ../../libgimpthumb/gimpthumb-enums.h
+
+(define-function gimp_thumb_file_type_get_type
+ (c-name "gimp_thumb_file_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_thumb_size_get_type
+ (c-name "gimp_thumb_size_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_thumb_state_get_type
+ (c-name "gimp_thumb_state_get_type")
+ (return-type "GType")
+)
+
+
+
+;; From ../../libgimpthumb/gimpthumb-types.h
+
+
+
+;; From ../../libgimpthumb/gimpthumb-utils.h
+
+(define-function init
+ (c-name "gimp_thumb_init")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "creator")
+ '("const-gchar*" "thumb_basedir")
+ )
+)
+
+(define-function find_thumb
+ (c-name "gimp_thumb_find_thumb")
+ (return-type "gchar*")
+ (parameters
+ '("const-gchar*" "uri")
+ '("GimpThumbSize*" "size")
+ )
+)
+
+(define-function file_test
+ (c-name "gimp_thumb_file_test")
+ (return-type "GimpThumbFileType")
+ (parameters
+ '("const-gchar*" "filename")
+ '("gint64*" "mtime")
+ '("gint64*" "size")
+ '("gint*" "err_no")
+ )
+)
+
+(define-function name_from_uri
+ (c-name "gimp_thumb_name_from_uri")
+ (return-type "gchar*")
+ (parameters
+ '("const-gchar*" "uri")
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-function get_thumb_dir
+ (c-name "gimp_thumb_get_thumb_dir")
+ (return-type "const-gchar*")
+ (parameters
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-function ensure_thumb_dir
+ (c-name "gimp_thumb_ensure_thumb_dir")
+ (return-type "gboolean")
+ (parameters
+ '("GimpThumbSize" "size")
+ '("GError**" "error")
+ )
+)
+
+(define-function delete_for_uri
+ (c-name "gimp_thumbs_delete_for_uri")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "uri")
+ )
+)
+
+(define-function name_from_uri_local
+ (c-name "gimp_thumb_name_from_uri_local")
+ (return-type "gchar*")
+ (parameters
+ '("const-gchar*" "uri")
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-function get_thumb_dir_local
+ (c-name "gimp_thumb_get_thumb_dir_local")
+ (return-type "gchar*")
+ (parameters
+ '("const-gchar*" "dirname")
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-function ensure_thumb_dir_local
+ (c-name "gimp_thumb_ensure_thumb_dir_local")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "dirname")
+ '("GimpThumbSize" "size")
+ '("GError**" "error")
+ )
+)
+
+(define-function delete_for_uri_local
+ (c-name "gimp_thumbs_delete_for_uri_local")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "uri")
+ )
+)
+
+
+
+;; From ../../libgimpthumb/gimpthumb.h
+
+
+
+;; From ../../libgimpthumb/gimpthumbnail.h
+
+(define-function gimp_thumbnail_get_type
+ (c-name "gimp_thumbnail_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_thumbnail_new
+ (c-name "gimp_thumbnail_new")
+ (is-constructor-of "GimpThumbnail")
+ (return-type "GimpThumbnail*")
+)
+
+(define-method set_uri
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_set_uri")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "uri")
+ )
+)
+
+(define-method set_filename
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_set_filename")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "filename")
+ '("GError**" "error")
+ )
+)
+
+(define-method set_from_thumb
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_set_from_thumb")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "filename")
+ '("GError**" "error")
+ )
+)
+
+(define-method peek_image
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_peek_image")
+ (return-type "GimpThumbState")
+)
+
+(define-method peek_thumb
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_peek_thumb")
+ (return-type "GimpThumbState")
+ (parameters
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-method check_thumb
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_check_thumb")
+ (return-type "GimpThumbState")
+ (parameters
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-method load_thumb
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_load_thumb")
+ (return-type "GdkPixbuf*")
+ (parameters
+ '("GimpThumbSize" "size")
+ '("GError**" "error")
+ )
+)
+
+(define-method save_thumb
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_save_thumb")
+ (return-type "gboolean")
+ (parameters
+ '("GdkPixbuf*" "pixbuf")
+ '("const-gchar*" "software")
+ '("GError**" "error")
+ )
+)
+
+(define-method save_thumb_local
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_save_thumb_local")
+ (return-type "gboolean")
+ (parameters
+ '("GdkPixbuf*" "pixbuf")
+ '("const-gchar*" "software")
+ '("GError**" "error")
+ )
+)
+
+(define-method save_failure
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_save_failure")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "software")
+ '("GError**" "error")
+ )
+)
+
+(define-method delete_failure
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_delete_failure")
+ (return-type "none")
+)
+
+(define-method delete_others
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_delete_others")
+ (return-type "none")
+ (parameters
+ '("GimpThumbSize" "size")
+ )
+)
+
+(define-method has_failed
+ (of-object "GimpThumbnail")
+ (c-name "gimp_thumbnail_has_failed")
+ (return-type "gboolean")
+)
+
+
diff --git a/plug-ins/pygimp/gimpthumb.override b/plug-ins/pygimp/gimpthumb.override
new file mode 100644
index 0000000..c43e539
--- /dev/null
+++ b/plug-ins/pygimp/gimpthumb.override
@@ -0,0 +1,102 @@
+%%
+headers
+#include <Python.h>
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <libgimpthumb/gimpthumb.h>
+
+/* TODO: Add a header for these */
+void gimpthumb_register_classes(PyObject *d);
+void gimpthumb_add_constants(PyObject *module, const gchar *strip_prefix);
+%%
+modulename gimpthumb
+%%
+import gobject.GObject as PyGObject_Type
+import gtk.gdk.Pixbuf as PyGdkPixbuf_Type
+%%
+ignore-glob
+ *_get_type
+%%
+override gimp_thumb_init kwargs
+static PyObject *
+_wrap_gimp_thumb_init(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *creator, *thumb_basedir = NULL;
+
+ static char *kwlist[] = { "creator", "thumb_basedir", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s|z:init", kwlist,
+ &creator, &thumb_basedir))
+ return NULL;
+
+ return PyBool_FromLong(gimp_thumb_init(creator, thumb_basedir));
+}
+%%
+override gimp_thumb_find_thumb kwargs
+static PyObject *
+_wrap_gimp_thumb_find_thumb(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *uri, *ret;
+ PyObject *py_size, *py_ret;
+ GimpThumbSize size;
+
+ static char *kwlist[] = { "uri", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO:find_thumb", kwlist,
+ &uri, &py_size))
+ return NULL;
+
+ if (pyg_enum_get_value(GIMP_TYPE_THUMB_SIZE, py_size, (gint *)&size))
+ return NULL;
+
+ ret = gimp_thumb_find_thumb(uri, &size);
+
+ if (ret == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ py_ret = Py_BuildValue("sN", ret,
+ pyg_enum_from_gtype(GIMP_TYPE_THUMB_SIZE, size));
+ g_free(ret);
+ return py_ret;
+}
+%%
+override gimp_thumb_file_test kwargs
+static PyObject *
+_wrap_gimp_thumb_file_test(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *filename;
+ GimpThumbFileType ret;
+ gint64 mtime, size;
+ gint err_no;
+
+ static char *kwlist[] = { "filename", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:file_test", kwlist,
+ &filename))
+ return NULL;
+
+ ret = gimp_thumb_file_test(filename, &mtime, &size, &err_no);
+
+ if (ret == GIMP_THUMB_FILE_TYPE_NONE) {
+ PyObject *v = Py_BuildValue("iss",
+ err_no, g_strerror(err_no), filename);
+ if (v != NULL) {
+ PyErr_SetObject(PyExc_IOError, v);
+ Py_DECREF(v);
+ }
+
+ return NULL;
+ }
+
+ return Py_BuildValue("NNN",
+ pyg_enum_from_gtype(GIMP_TYPE_THUMB_FILE_TYPE, ret),
+ PyLong_FromLongLong(mtime),
+ PyLong_FromLongLong(size));
+}
diff --git a/plug-ins/pygimp/gimpthumbmodule.c b/plug-ins/pygimp/gimpthumbmodule.c
new file mode 100644
index 0000000..5f87844
--- /dev/null
+++ b/plug-ins/pygimp/gimpthumbmodule.c
@@ -0,0 +1,60 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005-2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Python.h>
+
+#include <pygobject.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include <libgimpthumb/gimpthumb.h>
+
+#include "pygimp-util.h"
+
+
+void gimpthumb_register_classes(PyObject *d);
+void gimpthumb_add_constants(PyObject *module, const gchar *strip_prefix);
+extern PyMethodDef gimpthumb_functions[];
+
+
+static char gimpthumb_doc[] =
+"This module provides interfaces to allow you to write gimp plug-ins"
+;
+
+void initgimpthumb(void);
+
+PyMODINIT_FUNC
+initgimpthumb(void)
+{
+ PyObject *m, *d;
+
+ pygimp_init_pygobject();
+
+ m = Py_InitModule3("gimpthumb", gimpthumb_functions, gimpthumb_doc);
+ d = PyModule_GetDict(m);
+
+ gimpthumb_register_classes(d);
+ gimpthumb_add_constants(m, "GIMP_THUMB_");
+
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module gimpthumb");
+}
diff --git a/plug-ins/pygimp/gimpui.c b/plug-ins/pygimp/gimpui.c
new file mode 100644
index 0000000..26b9f50
--- /dev/null
+++ b/plug-ins/pygimp/gimpui.c
@@ -0,0 +1,9387 @@
+/* -- THIS FILE IS GENERATED - DO NOT EDIT *//* -*- Mode: C; c-basic-offset: 4 -*- */
+
+#include <Python.h>
+
+
+
+#line 2 "gimpui.override"
+#include <Python.h>
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include <pycairo.h>
+extern Pycairo_CAPI_t *Pycairo_CAPI;
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+#include <libgimp/gimp.h>
+#undef GIMP_DISABLE_DEPRECATED
+#include <libgimp/gimpui.h>
+#define GIMP_DISABLE_DEPRECATED
+
+#define NO_IMPORT_PYGIMP
+#include "pygimp-api.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+typedef struct {
+ PyObject *constraint;
+ PyObject *user_data;
+} PyGimpConstraintData;
+
+typedef struct {
+ PyObject *sensitivity_func;
+ PyObject *user_data;
+} PyGimpIntSensitivityData;
+
+/* TODO: Add a header for these */
+void gimpui_register_classes(PyObject *d);
+void gimpui_add_constants(PyObject *module, const gchar *strip_prefix);
+
+static void
+pygimp_decref_callback(PyObject* obj) {
+ Py_XDECREF (obj);
+}
+
+#line 48 "gimpui.c"
+
+
+/* ---------- types from other modules ---------- */
+static PyTypeObject *_PyGObject_Type;
+#define PyGObject_Type (*_PyGObject_Type)
+static PyTypeObject *_PyGdkPixbuf_Type;
+#define PyGdkPixbuf_Type (*_PyGdkPixbuf_Type)
+static PyTypeObject *_PyGtkObject_Type;
+#define PyGtkObject_Type (*_PyGtkObject_Type)
+static PyTypeObject *_PyGtkWidget_Type;
+#define PyGtkWidget_Type (*_PyGtkWidget_Type)
+static PyTypeObject *_PyGtkDialog_Type;
+#define PyGtkDialog_Type (*_PyGtkDialog_Type)
+static PyTypeObject *_PyGtkWindow_Type;
+#define PyGtkWindow_Type (*_PyGtkWindow_Type)
+static PyTypeObject *_PyGtkLabel_Type;
+#define PyGtkLabel_Type (*_PyGtkLabel_Type)
+static PyTypeObject *_PyGtkButton_Type;
+#define PyGtkButton_Type (*_PyGtkButton_Type)
+static PyTypeObject *_PyGtkToggleButton_Type;
+#define PyGtkToggleButton_Type (*_PyGtkToggleButton_Type)
+static PyTypeObject *_PyGtkRadioButton_Type;
+#define PyGtkRadioButton_Type (*_PyGtkRadioButton_Type)
+static PyTypeObject *_PyGtkSpinButton_Type;
+#define PyGtkSpinButton_Type (*_PyGtkSpinButton_Type)
+static PyTypeObject *_PyGtkEntry_Type;
+#define PyGtkEntry_Type (*_PyGtkEntry_Type)
+static PyTypeObject *_PyGtkDrawingArea_Type;
+#define PyGtkDrawingArea_Type (*_PyGtkDrawingArea_Type)
+static PyTypeObject *_PyGtkTable_Type;
+#define PyGtkTable_Type (*_PyGtkTable_Type)
+static PyTypeObject *_PyGtkFrame_Type;
+#define PyGtkFrame_Type (*_PyGtkFrame_Type)
+static PyTypeObject *_PyGtkHBox_Type;
+#define PyGtkHBox_Type (*_PyGtkHBox_Type)
+static PyTypeObject *_PyGtkVBox_Type;
+#define PyGtkVBox_Type (*_PyGtkVBox_Type)
+static PyTypeObject *_PyGtkHPaned_Type;
+#define PyGtkHPaned_Type (*_PyGtkHPaned_Type)
+static PyTypeObject *_PyGtkVPaned_Type;
+#define PyGtkVPaned_Type (*_PyGtkVPaned_Type)
+static PyTypeObject *_PyGtkScale_Type;
+#define PyGtkScale_Type (*_PyGtkScale_Type)
+static PyTypeObject *_PyGtkProgressBar_Type;
+#define PyGtkProgressBar_Type (*_PyGtkProgressBar_Type)
+static PyTypeObject *_PyGtkOptionMenu_Type;
+#define PyGtkOptionMenu_Type (*_PyGtkOptionMenu_Type)
+static PyTypeObject *_PyGtkComboBox_Type;
+#define PyGtkComboBox_Type (*_PyGtkComboBox_Type)
+static PyTypeObject *_PyGtkListStore_Type;
+#define PyGtkListStore_Type (*_PyGtkListStore_Type)
+static PyTypeObject *_PyGtkTreeModel_Type;
+#define PyGtkTreeModel_Type (*_PyGtkTreeModel_Type)
+static PyTypeObject *_PyGtkCellRenderer_Type;
+#define PyGtkCellRenderer_Type (*_PyGtkCellRenderer_Type)
+static PyTypeObject *_PyGtkCellRendererToggle_Type;
+#define PyGtkCellRendererToggle_Type (*_PyGtkCellRendererToggle_Type)
+static PyTypeObject *_PyGimpParasite_Type;
+#define PyGimpParasite_Type (*_PyGimpParasite_Type)
+
+
+/* ---------- forward type declarations ---------- */
+PyTypeObject G_GNUC_INTERNAL PyGimpBrowser_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpCellRendererColor_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpCellRendererToggle_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpChainButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorArea_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorConfig_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorDisplay_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorDisplayStack_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorHexEntry_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorProfileComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorProfileStore_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorScale_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorScales_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorSelection_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorSelector_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorNotebook_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpDialog_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumLabel_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpFrame_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpHintBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpIntComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpImageComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpDrawableComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpChannelComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpIntStore_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumStore_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpLayerComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpMemsizeEntry_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpNumberPairEntry_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpOffsetArea_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPageSelector_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPathEditor_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPickButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPreview_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpAspectPreview_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPreviewArea_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpProcBrowserDialog_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpProgressBar_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpScrolledPreview_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpDrawablePreview_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPatternSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpPaletteSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpGradientSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpFontSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpBrushSelectButton_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpRuler_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpSizeEntry_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpStringComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpUnitComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpUnitMenu_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpVectorsComboBox_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpZoomModel_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpZoomPreview_Type;
+PyTypeObject G_GNUC_INTERNAL PyGimpColorManaged_Type;
+
+#line 170 "gimpui.c"
+
+
+
+/* ----------- GimpBrowser ----------- */
+
+static int
+_wrap_gimp_browser_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.Browser.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.Browser object");
+ return -1;
+ }
+ return 0;
+}
+
+#line 1375 "gimpui.override"
+static PyObject *
+_wrap_gimp_browser_add_search_types(PyGObject *self, PyObject *args)
+{
+ GimpBrowser *browser;
+ int len, i;
+ PyObject *element;
+ gchar *label;
+ gint id;
+
+ browser = GIMP_BROWSER(self->obj);
+
+ len = PyTuple_Size(args);
+
+ for (i = 0; i < len; ++i) {
+ element = PyTuple_GetItem(args, i);
+ if (!PyTuple_Check(element)) {
+ PyErr_SetString(PyExc_TypeError, "GimpBrowser.add_search_types: Arguments must be tuples");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(element, "si", &label, &id))
+ return NULL;
+ gimp_browser_add_search_types(browser, label, id, NULL);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 224 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_browser_set_widget(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "widget", NULL };
+ PyGObject *widget;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.Browser.set_widget", kwlist, &PyGtkWidget_Type, &widget))
+ return NULL;
+
+ gimp_browser_set_widget(GIMP_BROWSER(self->obj), GTK_WIDGET(widget->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_browser_show_message(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "message", NULL };
+ char *message;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.Browser.show_message", kwlist, &message))
+ return NULL;
+
+ gimp_browser_show_message(GIMP_BROWSER(self->obj), message);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpBrowser_methods[] = {
+ { "add_search_types", (PyCFunction)_wrap_gimp_browser_add_search_types, METH_VARARGS,
+ NULL },
+ { "set_widget", (PyCFunction)_wrap_gimp_browser_set_widget, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "show_message", (PyCFunction)_wrap_gimp_browser_show_message, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpBrowser_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Browser", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpBrowser_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_browser_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpButton ----------- */
+
+static int
+_wrap_gimp_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.Button.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.Button object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_button_extended_clicked(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "state", NULL };
+ PyObject *py_state = NULL;
+ GdkModifierType state;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Button.extended_clicked", kwlist, &py_state))
+ return NULL;
+ if (pyg_flags_get_value(GDK_TYPE_MODIFIER_TYPE, py_state, (gpointer)&state))
+ return NULL;
+
+ gimp_button_extended_clicked(GIMP_BUTTON(self->obj), state);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpButton_methods[] = {
+ { "extended_clicked", (PyCFunction)_wrap_gimp_button_extended_clicked, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Button", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpCellRendererColor ----------- */
+
+static int
+_wrap_gimp_cell_renderer_color_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.CellRendererColor.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.CellRendererColor object");
+ return -1;
+ }
+ return 0;
+}
+
+PyTypeObject G_GNUC_INTERNAL PyGimpCellRendererColor_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.CellRendererColor", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_cell_renderer_color_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpCellRendererToggle ----------- */
+
+static int
+_wrap_gimp_cell_renderer_toggle_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[1];
+ PyObject *parsed_args[1] = {NULL, };
+ char *arg_names[] = {"stock_id", NULL };
+ char *prop_names[] = {"stock-id", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O:gimpui.CellRendererToggle.__init__" , arg_names , &parsed_args[0]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*1);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.CellRendererToggle object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_cell_renderer_toggle_clicked(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "path", "state", NULL };
+ char *path;
+ PyObject *py_state = NULL;
+ GdkModifierType state;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:Gimp.CellRendererToggle.clicked", kwlist, &path, &py_state))
+ return NULL;
+ if (pyg_flags_get_value(GDK_TYPE_MODIFIER_TYPE, py_state, (gpointer)&state))
+ return NULL;
+
+ gimp_cell_renderer_toggle_clicked(GIMP_CELL_RENDERER_TOGGLE(self->obj), path, state);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpCellRendererToggle_methods[] = {
+ { "clicked", (PyCFunction)_wrap_gimp_cell_renderer_toggle_clicked, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpCellRendererToggle_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.CellRendererToggle", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpCellRendererToggle_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_cell_renderer_toggle_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpChainButton ----------- */
+
+ static int
+_wrap_gimp_chain_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[1];
+ PyObject *parsed_args[1] = {NULL, };
+ char *arg_names[] = {"position", NULL };
+ char *prop_names[] = {"position", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:gimpui.ChainButton.__init__" , arg_names , &parsed_args[0]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*1);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ChainButton object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_chain_button_set_active(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "active", NULL };
+ int active;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ChainButton.set_active", kwlist, &active))
+ return NULL;
+
+ gimp_chain_button_set_active(GIMP_CHAIN_BUTTON(self->obj), active);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_chain_button_get_active(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_chain_button_get_active(GIMP_CHAIN_BUTTON(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static const PyMethodDef _PyGimpChainButton_methods[] = {
+ { "set_active", (PyCFunction)_wrap_gimp_chain_button_set_active, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active", (PyCFunction)_wrap_gimp_chain_button_get_active, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpChainButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ChainButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpChainButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_chain_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorArea ----------- */
+
+ static int
+_wrap_gimp_color_area_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[3];
+ PyObject *parsed_args[3] = {NULL, };
+ char *arg_names[] = {"color", "type", "drag_mask", NULL };
+ char *prop_names[] = {"color", "type", "drag-mask", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO:gimpui.ColorArea.__init__" , arg_names , &parsed_args[0] , &parsed_args[1] , &parsed_args[2]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*3);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorArea object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_area_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "color", NULL };
+ PyObject *py_color;
+ GimpRGB *color = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorArea.set_color", kwlist, &py_color))
+ return NULL;
+ if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB");
+ return NULL;
+ }
+
+ gimp_color_area_set_color(GIMP_COLOR_AREA(self->obj), color);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1792 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_area_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_area_get_color(GIMP_COLOR_AREA(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+#line 758 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_area_has_alpha(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_color_area_has_alpha(GIMP_COLOR_AREA(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_color_area_set_type(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "type", NULL };
+ PyObject *py_type = NULL;
+ GimpColorAreaType type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorArea.set_type", kwlist, &py_type))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_AREA_TYPE, py_type, (gpointer)&type))
+ return NULL;
+
+ gimp_color_area_set_type(GIMP_COLOR_AREA(self->obj), type);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_area_set_draw_border(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "draw_border", NULL };
+ int draw_border;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorArea.set_draw_border", kwlist, &draw_border))
+ return NULL;
+
+ gimp_color_area_set_draw_border(GIMP_COLOR_AREA(self->obj), draw_border);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorArea_methods[] = {
+ { "set_color", (PyCFunction)_wrap_gimp_color_area_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_color", (PyCFunction)_wrap_gimp_color_area_get_color, METH_NOARGS,
+ NULL },
+ { "has_alpha", (PyCFunction)_wrap_gimp_color_area_has_alpha, METH_NOARGS,
+ NULL },
+ { "set_type", (PyCFunction)_wrap_gimp_color_area_set_type, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_draw_border", (PyCFunction)_wrap_gimp_color_area_set_draw_border, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorArea_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorArea", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorArea_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_area_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorButton ----------- */
+
+#line 941 "gimpui.override"
+static int
+_wrap_gimp_color_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *title = NULL;
+ gint width = -1, height = -1;
+ PyObject *py_color = NULL, *py_type = NULL;
+ GimpRGB *color, default_color = { 0.0, 0.0, 0.0, 100.0 };
+ GimpColorAreaType type;
+
+ static char *kwlist[] = { "title", "width", "height", "color", "type",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|ziiOO:gimpui.ColorButton.__init__",
+ kwlist,
+ &title, &width, &height,
+ &py_color, &py_type))
+ return -1;
+
+ if (py_color == NULL || (PyObject*)py_color == Py_None)
+ color = &default_color;
+ else if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB or None");
+ return -1;
+ }
+
+ if (py_type == NULL || (PyObject*)py_type == Py_None)
+ type = GIMP_COLOR_AREA_FLAT;
+ else if (pyg_enum_get_value(GIMP_TYPE_COLOR_AREA_TYPE, py_type,
+ (gint*)&type))
+ return -1;
+
+ if (pygobject_construct(self,
+ "title", title,
+ "type", type,
+ "color", color,
+ "area-width", width,
+ "area-height", height,
+ NULL))
+ return -1;
+
+ return 0;
+}
+#line 915 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_button_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "color", NULL };
+ PyObject *py_color;
+ GimpRGB *color = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorButton.set_color", kwlist, &py_color))
+ return NULL;
+ if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB");
+ return NULL;
+ }
+
+ gimp_color_button_set_color(GIMP_COLOR_BUTTON(self->obj), color);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 903 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_button_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_button_get_color(GIMP_COLOR_BUTTON(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+#line 950 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_button_has_alpha(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_color_button_has_alpha(GIMP_COLOR_BUTTON(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_color_button_set_type(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "type", NULL };
+ PyObject *py_type = NULL;
+ GimpColorAreaType type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorButton.set_type", kwlist, &py_type))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_AREA_TYPE, py_type, (gpointer)&type))
+ return NULL;
+
+ gimp_color_button_set_type(GIMP_COLOR_BUTTON(self->obj), type);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_button_get_update(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_color_button_get_update(GIMP_COLOR_BUTTON(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_color_button_set_update(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "continuous", NULL };
+ int continuous;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorButton.set_update", kwlist, &continuous))
+ return NULL;
+
+ gimp_color_button_set_update(GIMP_COLOR_BUTTON(self->obj), continuous);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorButton_methods[] = {
+ { "set_color", (PyCFunction)_wrap_gimp_color_button_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_color", (PyCFunction)_wrap_gimp_color_button_get_color, METH_NOARGS,
+ NULL },
+ { "has_alpha", (PyCFunction)_wrap_gimp_color_button_has_alpha, METH_NOARGS,
+ NULL },
+ { "set_type", (PyCFunction)_wrap_gimp_color_button_set_type, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_update", (PyCFunction)_wrap_gimp_color_button_get_update, METH_NOARGS,
+ NULL },
+ { "set_update", (PyCFunction)_wrap_gimp_color_button_set_update, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorConfig ----------- */
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorConfig_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorConfig", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorDisplay ----------- */
+
+static PyObject *
+_wrap_gimp_color_display_clone(PyGObject *self)
+{
+ GimpColorDisplay *ret;
+
+
+ ret = gimp_color_display_clone(GIMP_COLOR_DISPLAY(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+#line 1996 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_display_convert_surface(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PycairoSurface *pysurface;
+
+ static char *kwlist[] = { "surface", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpColorDisplay.convert_surface",
+ kwlist,
+ &PycairoSurface_Type, &pysurface))
+ return NULL;
+
+ gimp_color_display_convert_surface(GIMP_COLOR_DISPLAY(self->obj),
+ pysurface->surface);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 1157 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_display_convert(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "buf", "width", "height", "bpp", "bpl", NULL };
+ int buf_len, width, height, bpp, bpl;
+ guchar *buf;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s#iiii:Gimp.ColorDisplay.convert", kwlist, &buf, &buf_len, &width, &height, &bpp, &bpl))
+ return NULL;
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use convert_surface(cairo_surface_t*) instead") < 0)
+ return NULL;
+
+ gimp_color_display_convert(GIMP_COLOR_DISPLAY(self->obj), buf, width, height, bpp, bpl);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_load_state(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "state", NULL };
+ GimpParasite *state = NULL;
+ PyObject *py_state;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorDisplay.load_state", kwlist, &py_state))
+ return NULL;
+ if (pyg_boxed_check(py_state, GIMP_TYPE_PARASITE))
+ state = pyg_boxed_get(py_state, GimpParasite);
+ else {
+ PyErr_SetString(PyExc_TypeError, "state should be a GimpParasite");
+ return NULL;
+ }
+
+ gimp_color_display_load_state(GIMP_COLOR_DISPLAY(self->obj), state);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_save_state(PyGObject *self)
+{
+ GimpParasite *ret;
+
+
+ ret = gimp_color_display_save_state(GIMP_COLOR_DISPLAY(self->obj));
+
+ /* pyg_boxed_new handles NULL checking */
+ return pyg_boxed_new(GIMP_TYPE_PARASITE, ret, TRUE, TRUE);
+}
+
+static PyObject *
+_wrap_gimp_color_display_configure(PyGObject *self)
+{
+ GtkWidget *ret;
+
+
+ ret = gimp_color_display_configure(GIMP_COLOR_DISPLAY(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_color_display_configure_reset(PyGObject *self)
+{
+
+ gimp_color_display_configure_reset(GIMP_COLOR_DISPLAY(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_changed(PyGObject *self)
+{
+
+ gimp_color_display_changed(GIMP_COLOR_DISPLAY(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_set_enabled(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enabled", NULL };
+ int enabled;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorDisplay.set_enabled", kwlist, &enabled))
+ return NULL;
+
+ gimp_color_display_set_enabled(GIMP_COLOR_DISPLAY(self->obj), enabled);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_get_enabled(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_color_display_get_enabled(GIMP_COLOR_DISPLAY(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_color_display_get_config(PyGObject *self)
+{
+ GimpColorConfig *ret;
+
+
+ ret = gimp_color_display_get_config(GIMP_COLOR_DISPLAY(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_color_display_get_managed(PyGObject *self)
+{
+ GimpColorManaged *ret;
+
+
+ ret = gimp_color_display_get_managed(GIMP_COLOR_DISPLAY(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static const PyMethodDef _PyGimpColorDisplay_methods[] = {
+ { "clone", (PyCFunction)_wrap_gimp_color_display_clone, METH_NOARGS,
+ NULL },
+ { "convert_surface", (PyCFunction)_wrap_gimp_color_display_convert_surface, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "convert", (PyCFunction)_wrap_gimp_color_display_convert, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "load_state", (PyCFunction)_wrap_gimp_color_display_load_state, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "save_state", (PyCFunction)_wrap_gimp_color_display_save_state, METH_NOARGS,
+ NULL },
+ { "configure", (PyCFunction)_wrap_gimp_color_display_configure, METH_NOARGS,
+ NULL },
+ { "configure_reset", (PyCFunction)_wrap_gimp_color_display_configure_reset, METH_NOARGS,
+ NULL },
+ { "changed", (PyCFunction)_wrap_gimp_color_display_changed, METH_NOARGS,
+ NULL },
+ { "set_enabled", (PyCFunction)_wrap_gimp_color_display_set_enabled, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_enabled", (PyCFunction)_wrap_gimp_color_display_get_enabled, METH_NOARGS,
+ NULL },
+ { "get_config", (PyCFunction)_wrap_gimp_color_display_get_config, METH_NOARGS,
+ NULL },
+ { "get_managed", (PyCFunction)_wrap_gimp_color_display_get_managed, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorDisplay_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorDisplay", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorDisplay_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorDisplayStack ----------- */
+
+ static int
+_wrap_gimp_color_display_stack_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.ColorDisplayStack.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorDisplayStack object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_clone(PyGObject *self)
+{
+ GimpColorDisplayStack *ret;
+
+
+ ret = gimp_color_display_stack_clone(GIMP_COLOR_DISPLAY_STACK(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_changed(PyGObject *self)
+{
+
+ gimp_color_display_stack_changed(GIMP_COLOR_DISPLAY_STACK(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_add(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "display", NULL };
+ PyGObject *display;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorDisplayStack.add", kwlist, &PyGimpColorDisplay_Type, &display))
+ return NULL;
+
+ gimp_color_display_stack_add(GIMP_COLOR_DISPLAY_STACK(self->obj), GIMP_COLOR_DISPLAY(display->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_remove(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "display", NULL };
+ PyGObject *display;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorDisplayStack.remove", kwlist, &PyGimpColorDisplay_Type, &display))
+ return NULL;
+
+ gimp_color_display_stack_remove(GIMP_COLOR_DISPLAY_STACK(self->obj), GIMP_COLOR_DISPLAY(display->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_reorder_up(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "display", NULL };
+ PyGObject *display;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorDisplayStack.reorder_up", kwlist, &PyGimpColorDisplay_Type, &display))
+ return NULL;
+
+ gimp_color_display_stack_reorder_up(GIMP_COLOR_DISPLAY_STACK(self->obj), GIMP_COLOR_DISPLAY(display->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_display_stack_reorder_down(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "display", NULL };
+ PyGObject *display;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorDisplayStack.reorder_down", kwlist, &PyGimpColorDisplay_Type, &display))
+ return NULL;
+
+ gimp_color_display_stack_reorder_down(GIMP_COLOR_DISPLAY_STACK(self->obj), GIMP_COLOR_DISPLAY(display->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 2018 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_display_stack_convert_surface(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PycairoSurface *pysurface;
+
+ static char *kwlist[] = { "surface", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpColorDisplayStack.convert_surface",
+ kwlist,
+ &PycairoSurface_Type, &pysurface))
+ return NULL;
+
+ gimp_color_display_stack_convert_surface(GIMP_COLOR_DISPLAY_STACK(self->obj),
+ pysurface->surface);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 1495 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_display_stack_convert(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "buf", "width", "height", "bpp", "bpl", NULL };
+ int buf_len, width, height, bpp, bpl;
+ guchar *buf;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s#iiii:Gimp.ColorDisplayStack.convert", kwlist, &buf, &buf_len, &width, &height, &bpp, &bpl))
+ return NULL;
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use convert_surface(cairo_surface_t*) instead") < 0)
+ return NULL;
+
+ gimp_color_display_stack_convert(GIMP_COLOR_DISPLAY_STACK(self->obj), buf, width, height, bpp, bpl);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorDisplayStack_methods[] = {
+ { "clone", (PyCFunction)_wrap_gimp_color_display_stack_clone, METH_NOARGS,
+ NULL },
+ { "changed", (PyCFunction)_wrap_gimp_color_display_stack_changed, METH_NOARGS,
+ NULL },
+ { "add", (PyCFunction)_wrap_gimp_color_display_stack_add, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "remove", (PyCFunction)_wrap_gimp_color_display_stack_remove, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "reorder_up", (PyCFunction)_wrap_gimp_color_display_stack_reorder_up, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "reorder_down", (PyCFunction)_wrap_gimp_color_display_stack_reorder_down, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "convert_surface", (PyCFunction)_wrap_gimp_color_display_stack_convert_surface, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "convert", (PyCFunction)_wrap_gimp_color_display_stack_convert, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorDisplayStack_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorDisplayStack", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorDisplayStack_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_display_stack_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorHexEntry ----------- */
+
+static int
+_wrap_gimp_color_hex_entry_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.ColorHexEntry.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorHexEntry object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_hex_entry_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "color", NULL };
+ PyObject *py_color;
+ GimpRGB *color = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorHexEntry.set_color", kwlist, &py_color))
+ return NULL;
+ if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB");
+ return NULL;
+ }
+
+ gimp_color_hex_entry_set_color(GIMP_COLOR_HEX_ENTRY(self->obj), color);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1803 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_hex_entry_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_hex_entry_get_color(GIMP_COLOR_HEX_ENTRY(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+#line 1637 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpColorHexEntry_methods[] = {
+ { "set_color", (PyCFunction)_wrap_gimp_color_hex_entry_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_color", (PyCFunction)_wrap_gimp_color_hex_entry_get_color, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorHexEntry_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorHexEntry", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorHexEntry_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_hex_entry_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorProfileComboBox ----------- */
+
+static int
+_wrap_gimp_color_profile_combo_box_new_with_model(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"dialog", "model", NULL };
+ char *prop_names[] = {"dialog", "model", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:gimpui.ColorProfileComboBox.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorProfileComboBox object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_profile_combo_box_add(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "filename", "label", NULL };
+ char *filename, *label;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ss:Gimp.ColorProfileComboBox.add", kwlist, &filename, &label))
+ return NULL;
+
+ gimp_color_profile_combo_box_add(GIMP_COLOR_PROFILE_COMBO_BOX(self->obj), filename, label);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_profile_combo_box_set_active(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "filename", "label", NULL };
+ char *filename, *label;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ss:Gimp.ColorProfileComboBox.set_active", kwlist, &filename, &label))
+ return NULL;
+
+ gimp_color_profile_combo_box_set_active(GIMP_COLOR_PROFILE_COMBO_BOX(self->obj), filename, label);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_profile_combo_box_get_active(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_color_profile_combo_box_get_active(GIMP_COLOR_PROFILE_COMBO_BOX(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorProfileComboBox_methods[] = {
+ { "add", (PyCFunction)_wrap_gimp_color_profile_combo_box_add, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_active", (PyCFunction)_wrap_gimp_color_profile_combo_box_set_active, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active", (PyCFunction)_wrap_gimp_color_profile_combo_box_get_active, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorProfileComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorProfileComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorProfileComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_profile_combo_box_new_with_model, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorProfileStore ----------- */
+
+ static int
+_wrap_gimp_color_profile_store_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[1];
+ PyObject *parsed_args[1] = {NULL, };
+ char *arg_names[] = {"history", NULL };
+ char *prop_names[] = {"history", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:gimpui.ColorProfileStore.__init__" , arg_names , &parsed_args[0]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*1);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorProfileStore object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_profile_store_add(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "filename", "label", NULL };
+ char *filename, *label;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ss:Gimp.ColorProfileStore.add", kwlist, &filename, &label))
+ return NULL;
+
+ gimp_color_profile_store_add(GIMP_COLOR_PROFILE_STORE(self->obj), filename, label);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorProfileStore_methods[] = {
+ { "add", (PyCFunction)_wrap_gimp_color_profile_store_add, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorProfileStore_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorProfileStore", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorProfileStore_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_profile_store_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorScale ----------- */
+
+#line 990 "gimpui.override"
+static int
+_wrap_gimp_color_scale_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_orientation, *py_channel;
+ GtkOrientation orientation;
+ GimpColorSelectorChannel channel;
+
+ static char *kwlist[] = { "orientation", "channel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OO:gimpui.ColorScale.__init__",
+ kwlist,
+ &py_orientation, &py_channel))
+ return -1;
+
+ if (pyg_enum_get_value(GTK_TYPE_ORIENTATION, py_orientation,
+ (gint*)&orientation))
+ return -1;
+
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_SELECTOR_CHANNEL, py_channel,
+ (gint*)&channel))
+ return -1;
+
+ if (pygobject_construct(self,
+ "orientation", orientation,
+ "channel", channel,
+ NULL))
+ return -1;
+
+ gtk_range_set_flippable (GTK_RANGE (self->obj),
+ orientation == GTK_ORIENTATION_HORIZONTAL);
+
+ return 0;
+}
+#line 1968 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_scale_set_channel(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "channel", NULL };
+ GimpColorSelectorChannel channel;
+ PyObject *py_channel = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorScale.set_channel", kwlist, &py_channel))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_SELECTOR_CHANNEL, py_channel, (gpointer)&channel))
+ return NULL;
+
+ gimp_color_scale_set_channel(GIMP_COLOR_SCALE(self->obj), channel);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_scale_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "rgb", "hsv", NULL };
+ PyObject *py_rgb, *py_hsv;
+ GimpHSV *hsv = NULL;
+ GimpRGB *rgb = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OO:Gimp.ColorScale.set_color", kwlist, &py_rgb, &py_hsv))
+ return NULL;
+ if (pyg_boxed_check(py_rgb, GIMP_TYPE_RGB))
+ rgb = pyg_boxed_get(py_rgb, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "rgb should be a GimpRGB");
+ return NULL;
+ }
+ if (pyg_boxed_check(py_hsv, GIMP_TYPE_HSV))
+ hsv = pyg_boxed_get(py_hsv, GimpHSV);
+ else {
+ PyErr_SetString(PyExc_TypeError, "hsv should be a GimpHSV");
+ return NULL;
+ }
+
+ gimp_color_scale_set_color(GIMP_COLOR_SCALE(self->obj), rgb, hsv);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorScale_methods[] = {
+ { "set_channel", (PyCFunction)_wrap_gimp_color_scale_set_channel, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_color", (PyCFunction)_wrap_gimp_color_scale_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorScale_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorScale", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorScale_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_scale_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorSelector ----------- */
+
+static int
+_wrap_gimp_color_selector_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "selector_type", "rgb", "hsv", "channel", NULL };
+ PyObject *py_selector_type = NULL, *py_rgb, *py_hsv, *py_channel = NULL;
+ GimpHSV *hsv = NULL;
+ GType selector_type;
+ GimpColorSelectorChannel channel;
+ GimpRGB *rgb = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OOOO:Gimp.ColorSelector.__init__", kwlist, &py_selector_type, &py_rgb, &py_hsv, &py_channel))
+ return -1;
+ if ((selector_type = pyg_type_from_object(py_selector_type)) == 0)
+ return -1;
+ if (pyg_boxed_check(py_rgb, GIMP_TYPE_RGB))
+ rgb = pyg_boxed_get(py_rgb, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "rgb should be a GimpRGB");
+ return -1;
+ }
+ if (pyg_boxed_check(py_hsv, GIMP_TYPE_HSV))
+ hsv = pyg_boxed_get(py_hsv, GimpHSV);
+ else {
+ PyErr_SetString(PyExc_TypeError, "hsv should be a GimpHSV");
+ return -1;
+ }
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_SELECTOR_CHANNEL, py_channel, (gpointer)&channel))
+ return -1;
+ self->obj = (GObject *)gimp_color_selector_new(selector_type, rgb, hsv, channel);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpColorSelector object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_toggles_visible(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "visible", NULL };
+ int visible;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorSelector.set_toggles_visible", kwlist, &visible))
+ return NULL;
+
+ gimp_color_selector_set_toggles_visible(GIMP_COLOR_SELECTOR(self->obj), visible);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_toggles_sensitive(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "sensitive", NULL };
+ int sensitive;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorSelector.set_toggles_sensitive", kwlist, &sensitive))
+ return NULL;
+
+ gimp_color_selector_set_toggles_sensitive(GIMP_COLOR_SELECTOR(self->obj), sensitive);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_show_alpha(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "show_alpha", NULL };
+ int show_alpha;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorSelector.set_show_alpha", kwlist, &show_alpha))
+ return NULL;
+
+ gimp_color_selector_set_show_alpha(GIMP_COLOR_SELECTOR(self->obj), show_alpha);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "rgb", "hsv", NULL };
+ PyObject *py_rgb, *py_hsv;
+ GimpHSV *hsv = NULL;
+ GimpRGB *rgb = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OO:Gimp.ColorSelector.set_color", kwlist, &py_rgb, &py_hsv))
+ return NULL;
+ if (pyg_boxed_check(py_rgb, GIMP_TYPE_RGB))
+ rgb = pyg_boxed_get(py_rgb, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "rgb should be a GimpRGB");
+ return NULL;
+ }
+ if (pyg_boxed_check(py_hsv, GIMP_TYPE_HSV))
+ hsv = pyg_boxed_get(py_hsv, GimpHSV);
+ else {
+ PyErr_SetString(PyExc_TypeError, "hsv should be a GimpHSV");
+ return NULL;
+ }
+
+ gimp_color_selector_set_color(GIMP_COLOR_SELECTOR(self->obj), rgb, hsv);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_channel(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "channel", NULL };
+ GimpColorSelectorChannel channel;
+ PyObject *py_channel = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorSelector.set_channel", kwlist, &py_channel))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_SELECTOR_CHANNEL, py_channel, (gpointer)&channel))
+ return NULL;
+
+ gimp_color_selector_set_channel(GIMP_COLOR_SELECTOR(self->obj), channel);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_color_changed(PyGObject *self)
+{
+
+ gimp_color_selector_color_changed(GIMP_COLOR_SELECTOR(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_channel_changed(PyGObject *self)
+{
+
+ gimp_color_selector_channel_changed(GIMP_COLOR_SELECTOR(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selector_set_config(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "config", NULL };
+ PyGObject *config;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorSelector.set_config", kwlist, &PyGimpColorConfig_Type, &config))
+ return NULL;
+
+ gimp_color_selector_set_config(GIMP_COLOR_SELECTOR(self->obj), GIMP_COLOR_CONFIG(config->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorSelector_methods[] = {
+ { "set_toggles_visible", (PyCFunction)_wrap_gimp_color_selector_set_toggles_visible, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_toggles_sensitive", (PyCFunction)_wrap_gimp_color_selector_set_toggles_sensitive, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_show_alpha", (PyCFunction)_wrap_gimp_color_selector_set_show_alpha, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_color", (PyCFunction)_wrap_gimp_color_selector_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_channel", (PyCFunction)_wrap_gimp_color_selector_set_channel, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "color_changed", (PyCFunction)_wrap_gimp_color_selector_color_changed, METH_NOARGS,
+ NULL },
+ { "channel_changed", (PyCFunction)_wrap_gimp_color_selector_channel_changed, METH_NOARGS,
+ NULL },
+ { "set_config", (PyCFunction)_wrap_gimp_color_selector_set_config, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorSelector_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorSelector", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorSelector_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_selector_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorScales ----------- */
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorScales_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorScales", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorSelection ----------- */
+
+ static int
+_wrap_gimp_color_selection_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.ColorSelection.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ColorSelection object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_color_selection_set_show_alpha(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "show_alpha", NULL };
+ int show_alpha;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.ColorSelection.set_show_alpha", kwlist, &show_alpha))
+ return NULL;
+
+ gimp_color_selection_set_show_alpha(GIMP_COLOR_SELECTION(self->obj), show_alpha);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selection_get_show_alpha(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_color_selection_get_show_alpha(GIMP_COLOR_SELECTION(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_color_selection_set_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "color", NULL };
+ PyObject *py_color;
+ GimpRGB *color = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorSelection.set_color", kwlist, &py_color))
+ return NULL;
+ if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB");
+ return NULL;
+ }
+
+ gimp_color_selection_set_color(GIMP_COLOR_SELECTION(self->obj), color);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1825 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_selection_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_selection_get_color(GIMP_COLOR_SELECTION(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+#line 2437 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_selection_set_old_color(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "color", NULL };
+ PyObject *py_color;
+ GimpRGB *color = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.ColorSelection.set_old_color", kwlist, &py_color))
+ return NULL;
+ if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB");
+ return NULL;
+ }
+
+ gimp_color_selection_set_old_color(GIMP_COLOR_SELECTION(self->obj), color);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1836 "gimpui.override"
+static PyObject *
+_wrap_gimp_color_selection_get_old_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_selection_get_old_color(GIMP_COLOR_SELECTION(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+#line 2472 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_color_selection_reset(PyGObject *self)
+{
+
+ gimp_color_selection_reset(GIMP_COLOR_SELECTION(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selection_color_changed(PyGObject *self)
+{
+
+ gimp_color_selection_color_changed(GIMP_COLOR_SELECTION(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_color_selection_set_config(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "config", NULL };
+ PyGObject *config;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.ColorSelection.set_config", kwlist, &PyGimpColorConfig_Type, &config))
+ return NULL;
+
+ gimp_color_selection_set_config(GIMP_COLOR_SELECTION(self->obj), GIMP_COLOR_CONFIG(config->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpColorSelection_methods[] = {
+ { "set_show_alpha", (PyCFunction)_wrap_gimp_color_selection_set_show_alpha, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_show_alpha", (PyCFunction)_wrap_gimp_color_selection_get_show_alpha, METH_NOARGS,
+ NULL },
+ { "set_color", (PyCFunction)_wrap_gimp_color_selection_set_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_color", (PyCFunction)_wrap_gimp_color_selection_get_color, METH_NOARGS,
+ NULL },
+ { "set_old_color", (PyCFunction)_wrap_gimp_color_selection_set_old_color, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_old_color", (PyCFunction)_wrap_gimp_color_selection_get_old_color, METH_NOARGS,
+ NULL },
+ { "reset", (PyCFunction)_wrap_gimp_color_selection_reset, METH_NOARGS,
+ NULL },
+ { "color_changed", (PyCFunction)_wrap_gimp_color_selection_color_changed, METH_NOARGS,
+ NULL },
+ { "set_config", (PyCFunction)_wrap_gimp_color_selection_set_config, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorSelection_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorSelection", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorSelection_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_color_selection_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorNotebook ----------- */
+
+static PyObject *
+_wrap_gimp_color_notebook_set_has_page(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_type", "has_page", NULL };
+ PyObject *py_page_type = NULL;
+ int has_page;
+ GType page_type;
+ GtkWidget *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Oi:Gimp.ColorNotebook.set_has_page", kwlist, &py_page_type, &has_page))
+ return NULL;
+ if ((page_type = pyg_type_from_object(py_page_type)) == 0)
+ return NULL;
+
+ ret = gimp_color_notebook_set_has_page(GIMP_COLOR_NOTEBOOK(self->obj), page_type, has_page);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static const PyMethodDef _PyGimpColorNotebook_methods[] = {
+ { "set_has_page", (PyCFunction)_wrap_gimp_color_notebook_set_has_page, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorNotebook_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorNotebook", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpColorNotebook_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpDialog ----------- */
+
+#line 730 "gimpui.override"
+static void
+pygimp_help_func_marshal(const gchar *help_id, gpointer help_data)
+{
+ GObject *dialog = help_data;
+ PyObject *py_dialog, *help_func, *ret;
+
+ py_dialog = g_object_get_data(dialog, "pygimp-dialog-pyobject");
+ help_func = g_object_get_data(dialog, "pygimp-dialog-help_func");
+
+ ret = PyObject_CallFunction(help_func, "sO", help_id, py_dialog);
+
+ if (ret)
+ Py_DECREF(ret);
+ else
+ PyErr_Print();
+}
+
+static void
+pygimp_help_func_destroy(gpointer data)
+{
+ PyObject *help_func = data;
+
+ Py_DECREF(help_func);
+}
+
+static void
+pygimp_dialog_close(GtkWidget *widget)
+{
+ /* Synthesize delete_event to close dialog. */
+
+ if (gtk_widget_get_window (widget)) {
+ GdkEvent *event = gdk_event_new(GDK_DELETE);
+
+ event->any.window = g_object_ref (gtk_widget_get_window (widget));
+ event->any.send_event = TRUE;
+
+ gtk_main_do_event(event);
+ gdk_event_free(event);
+ }
+}
+
+static int
+_wrap_gimp_dialog_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *title, *role;
+ PyGObject *py_window = NULL;
+ PyObject *py_flags = NULL, *py_buttons = Py_None;
+ PyObject *help_func = NULL;
+ gchar *help_id = NULL;
+ GtkDialogFlags flags = 0;
+ int len, i;
+ GtkWidget *parent;
+ GimpHelpFunc func;
+
+ static char *kwlist[] = { "title", "role", "parent", "flags",
+ "help_func", "help_id", "buttons", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "zz|OOOzO:gimpui.GimpDialog.__init__",
+ kwlist,
+ &title, &role, &py_window, &py_flags,
+ &help_func, &help_id, &py_buttons))
+ return -1;
+
+ if (py_window == NULL || (PyObject*)py_window == Py_None)
+ parent = NULL;
+ else if (pygobject_check(py_window, &PyGtkWindow_Type))
+ parent = GTK_WIDGET(py_window->obj);
+ else {
+ PyErr_SetString(PyExc_TypeError, "parent must be a GtkWindow or None");
+ return -1;
+ }
+
+ if (pyg_flags_get_value(GTK_TYPE_DIALOG_FLAGS, py_flags, (gint *)&flags))
+ return -1;
+
+ if (py_buttons == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_buttons))
+ len = PyTuple_Size(py_buttons);
+ else {
+ PyErr_SetString(PyExc_TypeError, "buttons must be a tuple containing text/response pairs or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "buttons tuple must contain text/response id pairs");
+ return -1;
+ }
+
+ if (help_func) {
+ if (help_func != Py_None) {
+ if (!PyCallable_Check(help_func)) {
+ PyErr_SetString(PyExc_TypeError, "help_func must be callable");
+ return -1;
+ }
+
+ func = pygimp_help_func_marshal;
+
+ } else {
+ func = gimp_standard_help_func;
+ }
+ } else {
+ func = gimp_standard_help_func;
+ }
+
+ pygobject_construct(self,
+ "title", title,
+ "role", role,
+ "modal", (flags & GTK_DIALOG_MODAL),
+ "help-func", func,
+ "help-id", help_id,
+ NULL);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpDialog object");
+ return -1;
+ }
+
+ if (parent) {
+ if (GTK_IS_WINDOW(parent))
+ gtk_window_set_transient_for(GTK_WINDOW(self->obj),
+ GTK_WINDOW(parent));
+ else
+ gtk_window_set_screen(GTK_WINDOW(self->obj),
+ gtk_widget_get_screen(parent));
+
+ if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
+ g_signal_connect_object(parent, "destroy",
+ G_CALLBACK(pygimp_dialog_close),
+ self->obj, G_CONNECT_SWAPPED);
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *text = PyTuple_GetItem(py_buttons, i);
+ PyObject *id = PyTuple_GetItem(py_buttons, i + 1);
+ if (!PyString_Check(text) && !PyUnicode_Check(text)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each text/response id pair "
+ "must be a string");
+ return -1;
+ }
+ if (!PyInt_Check(id)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each text/response id pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_dialog_add_button(GIMP_DIALOG(self->obj), PyString_AsString(text),
+ PyInt_AsLong(id));
+ }
+
+ if (help_func && help_func != Py_None) {
+ g_object_set_data(self->obj, "pygimp-dialog-help-data", self);
+
+ Py_INCREF(help_func);
+ g_object_set_data_full(self->obj, "pygimp-dialog-help-func",
+ help_func, pygimp_help_func_destroy);
+ }
+
+ return 0;
+}
+#line 2826 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_dialog_add_button(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "button_text", "response_id", NULL };
+ char *button_text;
+ int response_id;
+ GtkWidget *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"si:Gimp.Dialog.add_button", kwlist, &button_text, &response_id))
+ return NULL;
+
+ ret = gimp_dialog_add_button(GIMP_DIALOG(self->obj), button_text, response_id);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_dialog_run(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_dialog_run(GIMP_DIALOG(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+#line 932 "gimpui.override"
+static PyObject *
+_wrap_gimp_window_set_transient(PyGObject *self)
+{
+ gimp_window_set_transient(GTK_WINDOW(self->obj));
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 2865 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpDialog_methods[] = {
+ { "add_button", (PyCFunction)_wrap_gimp_dialog_add_button, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "run", (PyCFunction)_wrap_gimp_dialog_run, METH_NOARGS,
+ NULL },
+ { "set_transient", (PyCFunction)_wrap_gimp_window_set_transient, METH_VARARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpDialog_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Dialog", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpDialog_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_dialog_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpEnumLabel ----------- */
+
+#line 1028 "gimpui.override"
+static int
+_wrap_gimp_enum_label_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_enum_type = NULL;
+ GType enum_type;
+ gint value;
+
+ static char *kwlist[] = { "enum_type", "value", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "Oi:gimpui.GimpEnumLabel.__init__",
+ kwlist,
+ &py_enum_type, &value))
+ return -1;
+
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return -1;
+
+ if (pygobject_construct(self,
+ "enum-type", enum_type,
+ "enum-value", value,
+ NULL))
+ return -1;
+
+ return 0;
+}
+#line 2954 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_enum_label_set_value(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", NULL };
+ int value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.EnumLabel.set_value", kwlist, &value))
+ return NULL;
+
+ gimp_enum_label_set_value(GIMP_ENUM_LABEL(self->obj), value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpEnumLabel_methods[] = {
+ { "set_value", (PyCFunction)_wrap_gimp_enum_label_set_value, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumLabel_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.EnumLabel", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpEnumLabel_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_enum_label_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpFrame ----------- */
+
+static int
+_wrap_gimp_frame_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[1];
+ PyObject *parsed_args[1] = {NULL, };
+ char *arg_names[] = {"label", NULL };
+ char *prop_names[] = {"label", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:gimpui.Frame.__init__" , arg_names , &parsed_args[0]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*1);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.Frame object");
+ return -1;
+ }
+ return 0;
+}
+
+PyTypeObject G_GNUC_INTERNAL PyGimpFrame_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Frame", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_frame_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpHintBox ----------- */
+
+ static int
+_wrap_gimp_hint_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"hint", "stock-id", NULL };
+ char *prop_names[] = {"hint", "stock-id", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:gimpui.HintBox.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.HintBox object");
+ return -1;
+ }
+ return 0;
+}
+
+PyTypeObject G_GNUC_INTERNAL PyGimpHintBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.HintBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_hint_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpIntComboBox ----------- */
+
+#line 1058 "gimpui.override"
+static int
+_wrap_gimp_int_combo_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_items = NULL;
+ int len, i;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|O:gimpui.IntComboBox.__init__",
+ kwlist,
+ &py_items))
+ return -1;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return -1;
+ }
+
+ if (pygobject_construct(self, NULL))
+ return -1;
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_int_combo_box_append(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ return 0;
+}
+#line 3252 "gimpui.c"
+
+
+#line 1312 "gimpui.override"
+static PyObject *
+_wrap_gimp_int_combo_box_prepend(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_items;
+ int i, len;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:gimpui.IntComboBox.prepend",
+ kwlist,
+ &py_items))
+ return NULL;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return NULL;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return NULL;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return NULL;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return NULL;
+ }
+
+ gimp_int_combo_box_prepend(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3317 "gimpui.c"
+
+
+#line 1249 "gimpui.override"
+static PyObject *
+_wrap_gimp_int_combo_box_append(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_items;
+ int i, len;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:gimpui.IntComboBox.append",
+ kwlist,
+ &py_items))
+ return NULL;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return NULL;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return NULL;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return NULL;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return NULL;
+ }
+
+ gimp_int_combo_box_append(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3382 "gimpui.c"
+
+
+#line 1224 "gimpui.override"
+static PyObject *
+_wrap_gimp_int_combo_box_set_active(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ int value;
+
+ static char *kwlist[] = { "value", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "i:GimpIntComboBox.set_active", kwlist,
+ &value))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), value)) {
+ PyErr_Format(pygimp_error,
+ "Value %d does not exist in GimpIntComboBox",
+ value);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3409 "gimpui.c"
+
+
+#line 1211 "gimpui.override"
+static PyObject *
+_wrap_gimp_int_combo_box_get_active(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return PyLong_FromLong(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3424 "gimpui.c"
+
+
+#line 1128 "gimpui.override"
+static gboolean
+pygimp_int_combo_box_sensitivity_marshal(gint value, gpointer user_data)
+{
+ PyObject *py_value;
+ PyGimpIntSensitivityData *data;
+ PyObject *ret;
+ gboolean res;
+
+ data = user_data;
+
+ py_value = PyInt_FromLong(value);
+
+ ret = PyObject_CallFunctionObjArgs(data->sensitivity_func, py_value,
+ data->user_data, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(py_value);
+
+ return res;
+}
+
+static void
+pygimp_int_combo_box_sensitivity_data_destroy(gpointer user_data)
+{
+ PyGimpIntSensitivityData *data;
+ data = user_data;
+
+ Py_DECREF(data->sensitivity_func);
+ Py_XDECREF(data->user_data);
+
+ g_free(data);
+}
+
+static PyObject *
+_wrap_gimp_int_combo_box_set_sensitivity(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_sensitivity_func;
+ PyObject *py_user_data = NULL;
+ PyGimpIntSensitivityData *data;
+
+ static char *kwlist[] = { "sensitivity_func", "user_data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O|O:GimpIntComboBox.set_sensitivity",
+ kwlist, &py_sensitivity_func,
+ &py_user_data))
+ return NULL;
+
+ if (!PyCallable_Check(py_sensitivity_func)) {
+ PyErr_SetString(PyExc_TypeError, "first argument must be callable.");
+ return NULL;
+ }
+
+ data = g_new(PyGimpIntSensitivityData, 1);
+
+ data->sensitivity_func = py_sensitivity_func;
+ Py_INCREF(data->sensitivity_func);
+
+ if (py_user_data == NULL || py_user_data == Py_None)
+ data->user_data = NULL;
+ else {
+ data->user_data = py_user_data;
+ Py_INCREF(data->user_data);
+ }
+
+ gimp_int_combo_box_set_sensitivity(GIMP_INT_COMBO_BOX(self->obj),
+ pygimp_int_combo_box_sensitivity_marshal,
+ data,
+ pygimp_int_combo_box_sensitivity_data_destroy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3509 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpIntComboBox_methods[] = {
+ { "prepend", (PyCFunction)_wrap_gimp_int_combo_box_prepend, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "append", (PyCFunction)_wrap_gimp_int_combo_box_append, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_active", (PyCFunction)_wrap_gimp_int_combo_box_set_active, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active", (PyCFunction)_wrap_gimp_int_combo_box_get_active, METH_NOARGS,
+ NULL },
+ { "set_sensitivity", (PyCFunction)_wrap_gimp_int_combo_box_set_sensitivity, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpIntComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.IntComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpIntComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_int_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpImageComboBox ----------- */
+
+#line 609 "gimpui.override"
+static gboolean
+pygimp_image_constraint_marshal(gint32 image_id, gpointer user_data)
+{
+ PyObject *img, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_image_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpImageConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.ImageComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_image_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_image_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_IMAGE_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.ImageComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 3656 "gimpui.c"
+
+
+#line 717 "gimpui.override"
+static PyObject *
+_wrap_gimp_image_combo_box_get_active_image(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_image_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3671 "gimpui.c"
+
+
+#line 691 "gimpui.override"
+static PyObject *
+_wrap_gimp_image_combo_box_set_active_image(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpImage *img;
+
+ static char *kwlist[] = { "image", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpImageComboBox.set_active_image",
+ kwlist,
+ PyGimpImage_Type, &img))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), img->ID)) {
+ PyErr_Format(pygimp_error,
+ "Image (ID %d) does not exist in GimpImageComboBox",
+ img->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3699 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpImageComboBox_methods[] = {
+ { "get_active_image", (PyCFunction)_wrap_gimp_image_combo_box_get_active_image, METH_NOARGS,
+ NULL },
+ { "set_active_image", (PyCFunction)_wrap_gimp_image_combo_box_set_active_image, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpImageComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ImageComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpImageComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_image_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpEnumComboBox ----------- */
+
+static int
+_wrap_gimp_enum_combo_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enum_type", NULL };
+ PyObject *py_enum_type = NULL;
+ GType enum_type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.EnumComboBox.__init__", kwlist, &py_enum_type))
+ return -1;
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return -1;
+ self->obj = (GObject *)gimp_enum_combo_box_new(enum_type);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpEnumComboBox object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_enum_combo_box_set_stock_prefix(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "stock_prefix", NULL };
+ char *stock_prefix;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.EnumComboBox.set_stock_prefix", kwlist, &stock_prefix))
+ return NULL;
+
+ gimp_enum_combo_box_set_stock_prefix(GIMP_ENUM_COMBO_BOX(self->obj), stock_prefix);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpEnumComboBox_methods[] = {
+ { "set_stock_prefix", (PyCFunction)_wrap_gimp_enum_combo_box_set_stock_prefix, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.EnumComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpEnumComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_enum_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpDrawableComboBox ----------- */
+
+#line 89 "gimpui.override"
+static gboolean
+pygimp_drawable_constraint_marshal(gint32 image_id, gint32 drawable_id,
+ gpointer user_data)
+{
+ PyObject *img, *drw, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ drw = pygimp_drawable_new(NULL, drawable_id);
+ if (!drw) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, drw,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, drw, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(drw);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_drawable_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.DrawableComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_XINCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_drawable_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_drawable_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_DRAWABLE_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.DrawableComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 3940 "gimpui.c"
+
+
+#line 180 "gimpui.override"
+static PyObject *
+_wrap_gimp_drawable_combo_box_set_active_drawable(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpDrawable *drw;
+
+ static char *kwlist[] = { "drawable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpDrawableComboBox.set_active_drawable",
+ kwlist,
+ PyGimpDrawable_Type, &drw))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), drw->ID)) {
+ PyErr_Format(pygimp_error,
+ "Drawable (ID %d) does not exist in GimpDrawableComboBox",
+ drw->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3968 "gimpui.c"
+
+
+#line 206 "gimpui.override"
+static PyObject *
+_wrap_gimp_drawable_combo_box_get_active_drawable(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_drawable_new(NULL, value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 3983 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpDrawableComboBox_methods[] = {
+ { "set_active_drawable", (PyCFunction)_wrap_gimp_drawable_combo_box_set_active_drawable, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active_drawable", (PyCFunction)_wrap_gimp_drawable_combo_box_get_active_drawable, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpDrawableComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.DrawableComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpDrawableComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_drawable_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpChannelComboBox ----------- */
+
+#line 219 "gimpui.override"
+static gboolean
+pygimp_channel_constraint_marshal(gint32 image_id, gint32 channel_id,
+ gpointer user_data)
+{
+ PyObject *img, *chn, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ chn = pygimp_channel_new(channel_id);
+ if (!chn) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, chn,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, chn, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(chn);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_channel_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.ChannelComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_channel_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_channel_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_CHANNEL_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.ChannelComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 4133 "gimpui.c"
+
+
+#line 310 "gimpui.override"
+static PyObject *
+_wrap_gimp_channel_combo_box_set_active_channel(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpChannel *chn;
+
+ static char *kwlist[] = { "channel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpChannelComboBox.set_active_channel",
+ kwlist,
+ PyGimpChannel_Type, &chn))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "Channel (ID %d) does not exist in GimpChannelComboBox",
+ chn->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4161 "gimpui.c"
+
+
+#line 336 "gimpui.override"
+static PyObject *
+_wrap_gimp_channel_combo_box_get_active_channel(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_channel_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4176 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpChannelComboBox_methods[] = {
+ { "set_active_channel", (PyCFunction)_wrap_gimp_channel_combo_box_set_active_channel, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active_channel", (PyCFunction)_wrap_gimp_channel_combo_box_get_active_channel, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpChannelComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ChannelComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpChannelComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_channel_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpIntStore ----------- */
+
+ static int
+_wrap_gimp_int_store_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.IntStore.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.IntStore object");
+ return -1;
+ }
+ return 0;
+}
+
+#line 1727 "gimpui.override"
+static PyObject *
+_wrap_gimp_int_store_lookup_by_value(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", NULL };
+ int value, ret;
+ GtkTreeIter iter;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "i:GimpIntStore.gimp_int_store_lookup_by_value",
+ kwlist, &value))
+ return NULL;
+
+ ret = gimp_int_store_lookup_by_value(GTK_TREE_MODEL(self->obj), value,
+ &iter);
+ if (ret)
+ pyg_boxed_new(GTK_TYPE_TREE_ITER, &iter, TRUE, TRUE);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4278 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpIntStore_methods[] = {
+ { "lookup_by_value", (PyCFunction)_wrap_gimp_int_store_lookup_by_value, METH_VARARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpIntStore_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.IntStore", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpIntStore_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_int_store_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpEnumStore ----------- */
+
+#line 1847 "gimpui.override"
+static int
+_wrap_gimp_enum_store_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enum_type", "minimum", "maximum", NULL };
+ PyObject *py_enum_type = NULL;
+ PyObject *py_minimum = NULL;
+ PyObject *py_maximum = NULL;
+ GType enum_type;
+ GEnumClass *enum_class;
+ gint minimum, maximum;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O|O!O!:GimpEnumStore.__init__", kwlist,
+ &py_enum_type, &PyInt_Type, &py_minimum,
+ &PyInt_Type, &py_maximum))
+ return -1;
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return -1;
+
+ enum_class = g_type_class_ref(enum_type);
+
+ if (py_minimum == NULL)
+ minimum = enum_class->minimum;
+ else
+ minimum = PyInt_AsLong(py_minimum);
+
+ if (py_maximum == NULL)
+ maximum = enum_class->maximum;
+ else
+ maximum = PyInt_AsLong(py_maximum);
+
+ g_type_class_unref(enum_class);
+
+ self->obj = (GObject *)gimp_enum_store_new_with_range(enum_type, minimum, maximum);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpEnumStore object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 4379 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_enum_store_set_stock_prefix(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "stock_prefix", NULL };
+ char *stock_prefix;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.EnumStore.set_stock_prefix", kwlist, &stock_prefix))
+ return NULL;
+
+ gimp_enum_store_set_stock_prefix(GIMP_ENUM_STORE(self->obj), stock_prefix);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpEnumStore_methods[] = {
+ { "set_stock_prefix", (PyCFunction)_wrap_gimp_enum_store_set_stock_prefix, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpEnumStore_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.EnumStore", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpEnumStore_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_enum_store_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpLayerComboBox ----------- */
+
+#line 349 "gimpui.override"
+static gboolean
+pygimp_layer_constraint_marshal(gint32 image_id, gint32 layer_id,
+ gpointer user_data)
+{
+ PyObject *img, *lay, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ lay = pygimp_layer_new(layer_id);
+ if (!lay) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, lay,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, lay, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(lay);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_layer_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.LayerComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_layer_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_layer_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_LAYER_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.LayerComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 4542 "gimpui.c"
+
+
+#line 466 "gimpui.override"
+static PyObject *
+_wrap_gimp_layer_combo_box_get_active_layer(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_layer_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4557 "gimpui.c"
+
+
+#line 440 "gimpui.override"
+static PyObject *
+_wrap_gimp_layer_combo_box_set_active_layer(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpLayer *lay;
+
+ static char *kwlist[] = { "layer", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpLayerComboBox.set_active_layer",
+ kwlist,
+ PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "Layer (ID %d) does not exist in GimpLayerComboBox",
+ lay->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4585 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpLayerComboBox_methods[] = {
+ { "get_active_layer", (PyCFunction)_wrap_gimp_layer_combo_box_get_active_layer, METH_NOARGS,
+ NULL },
+ { "set_active_layer", (PyCFunction)_wrap_gimp_layer_combo_box_set_active_layer, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpLayerComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.LayerComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpLayerComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_layer_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpMemsizeEntry ----------- */
+
+#line 1750 "gimpui.override"
+static int
+_wrap_gimp_memsize_entry_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", "lower", "upper", NULL };
+ guint64 value, lower, upper;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "LLL:GimpMemsizeEntry.__init__",
+ kwlist, &value, &lower, &upper))
+ return -1;
+
+ self->obj = (GObject *)gimp_memsize_entry_new(value, lower, upper);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpMemsizeEntry object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 4667 "gimpui.c"
+
+
+#line 1773 "gimpui.override"
+static PyObject *
+_wrap_gimp_memsize_entry_set_value(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", NULL };
+ guint64 value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "L:GimpMemsizeEntry.set_value",
+ kwlist, &value))
+ return NULL;
+
+ gimp_memsize_entry_set_value(GIMP_MEMSIZE_ENTRY(self->obj), value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4688 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_memsize_entry_get_value(PyGObject *self)
+{
+ guint64 ret;
+
+
+ ret = gimp_memsize_entry_get_value(GIMP_MEMSIZE_ENTRY(self->obj));
+
+ return PyLong_FromUnsignedLongLong(ret);
+}
+
+static const PyMethodDef _PyGimpMemsizeEntry_methods[] = {
+ { "set_value", (PyCFunction)_wrap_gimp_memsize_entry_set_value, METH_VARARGS,
+ NULL },
+ { "get_value", (PyCFunction)_wrap_gimp_memsize_entry_get_value, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpMemsizeEntry_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.MemsizeEntry", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpMemsizeEntry_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_memsize_entry_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpNumberPairEntry ----------- */
+
+static int
+_wrap_gimp_number_pair_entry_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[4];
+ PyObject *parsed_args[4] = {NULL, };
+ char *arg_names[] = {"separators", "allow_simplification", "min_valid_value", "max_valid_value", NULL };
+ char *prop_names[] = {"separators", "allow-simplification", "min-valid-value", "max-valid-value", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO:gimpui.NumberPairEntry.__init__" , arg_names , &parsed_args[0] , &parsed_args[1] , &parsed_args[2] , &parsed_args[3]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*4);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.NumberPairEntry object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_number_pair_entry_set_default_values(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "left", "right", NULL };
+ double left, right;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"dd:Gimp.NumberPairEntry.set_default_values", kwlist, &left, &right))
+ return NULL;
+
+ gimp_number_pair_entry_set_default_values(GIMP_NUMBER_PAIR_ENTRY(self->obj), left, right);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1523 "gimpui.override"
+static PyObject *
+_wrap_gimp_number_pair_entry_get_default_values(PyGObject *self)
+{
+ gdouble left, right;
+
+ gimp_number_pair_entry_get_default_values(
+ GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ &left, &right);
+
+ return Py_BuildValue("(dd)", left, right);
+}
+#line 4816 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_number_pair_entry_set_values(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "left", "right", NULL };
+ double left, right;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"dd:Gimp.NumberPairEntry.set_values", kwlist, &left, &right))
+ return NULL;
+
+ gimp_number_pair_entry_set_values(GIMP_NUMBER_PAIR_ENTRY(self->obj), left, right);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1511 "gimpui.override"
+static PyObject *
+_wrap_gimp_number_pair_entry_get_values(PyGObject *self)
+{
+ gdouble left, right;
+
+ gimp_number_pair_entry_get_values(GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ &left, &right);
+
+ return Py_BuildValue("(dd)", left, right);
+}
+#line 4845 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_number_pair_entry_set_default_text(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "string", NULL };
+ char *string;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.NumberPairEntry.set_default_text", kwlist, &string))
+ return NULL;
+
+ gimp_number_pair_entry_set_default_text(GIMP_NUMBER_PAIR_ENTRY(self->obj), string);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_number_pair_entry_get_default_text(PyGObject *self)
+{
+ const gchar *ret;
+
+
+ ret = gimp_number_pair_entry_get_default_text(GIMP_NUMBER_PAIR_ENTRY(self->obj));
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_number_pair_entry_set_ratio(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "ratio", NULL };
+ double ratio;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"d:Gimp.NumberPairEntry.set_ratio", kwlist, &ratio))
+ return NULL;
+
+ gimp_number_pair_entry_set_ratio(GIMP_NUMBER_PAIR_ENTRY(self->obj), ratio);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_number_pair_entry_get_ratio(PyGObject *self)
+{
+ double ret;
+
+
+ ret = gimp_number_pair_entry_get_ratio(GIMP_NUMBER_PAIR_ENTRY(self->obj));
+
+ return PyFloat_FromDouble(ret);
+}
+
+#line 1548 "gimpui.override"
+static PyObject *
+_wrap_gimp_number_pair_entry_set_aspect(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_aspect;
+ GimpAspectType aspect;
+
+ static char *kwlist[] = {"aspect", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:GimpNumberPairEntry.set_aspect",
+ kwlist, &py_aspect))
+ return NULL;
+
+ if (pyg_enum_get_value(GIMP_TYPE_ASPECT_TYPE, py_aspect, (gint*)&aspect))
+ {
+ Py_XDECREF(py_aspect);
+ return NULL;
+ }
+
+ gimp_number_pair_entry_set_aspect(GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ aspect);
+
+ Py_DECREF(py_aspect);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 4932 "gimpui.c"
+
+
+#line 1536 "gimpui.override"
+static PyObject *
+_wrap_gimp_number_pair_entry_get_aspect(PyGObject *self)
+{
+ GimpAspectType aspect;
+
+ aspect =
+ gimp_number_pair_entry_get_aspect(GIMP_NUMBER_PAIR_ENTRY(self->obj));
+
+ return pyg_enum_from_gtype(GIMP_TYPE_ASPECT_TYPE, aspect);
+}
+#line 4946 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_number_pair_entry_set_user_override(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "user_override", NULL };
+ int user_override;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.NumberPairEntry.set_user_override", kwlist, &user_override))
+ return NULL;
+
+ gimp_number_pair_entry_set_user_override(GIMP_NUMBER_PAIR_ENTRY(self->obj), user_override);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_number_pair_entry_get_user_override(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_number_pair_entry_get_user_override(GIMP_NUMBER_PAIR_ENTRY(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static const PyMethodDef _PyGimpNumberPairEntry_methods[] = {
+ { "set_default_values", (PyCFunction)_wrap_gimp_number_pair_entry_set_default_values, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_default_values", (PyCFunction)_wrap_gimp_number_pair_entry_get_default_values, METH_NOARGS,
+ NULL },
+ { "set_values", (PyCFunction)_wrap_gimp_number_pair_entry_set_values, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_values", (PyCFunction)_wrap_gimp_number_pair_entry_get_values, METH_NOARGS,
+ NULL },
+ { "set_default_text", (PyCFunction)_wrap_gimp_number_pair_entry_set_default_text, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_default_text", (PyCFunction)_wrap_gimp_number_pair_entry_get_default_text, METH_NOARGS,
+ NULL },
+ { "set_ratio", (PyCFunction)_wrap_gimp_number_pair_entry_set_ratio, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_ratio", (PyCFunction)_wrap_gimp_number_pair_entry_get_ratio, METH_NOARGS,
+ NULL },
+ { "set_aspect", (PyCFunction)_wrap_gimp_number_pair_entry_set_aspect, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_aspect", (PyCFunction)_wrap_gimp_number_pair_entry_get_aspect, METH_NOARGS,
+ NULL },
+ { "set_user_override", (PyCFunction)_wrap_gimp_number_pair_entry_set_user_override, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_user_override", (PyCFunction)_wrap_gimp_number_pair_entry_get_user_override, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpNumberPairEntry_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.NumberPairEntry", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpNumberPairEntry_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_number_pair_entry_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpOffsetArea ----------- */
+
+static int
+_wrap_gimp_offset_area_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "orig_width", "orig_height", NULL };
+ int orig_width, orig_height;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.OffsetArea.__init__", kwlist, &orig_width, &orig_height))
+ return -1;
+ self->obj = (GObject *)gimp_offset_area_new(orig_width, orig_height);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpOffsetArea object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_offset_area_set_pixbuf(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "pixbuf", NULL };
+ PyGObject *pixbuf;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:Gimp.OffsetArea.set_pixbuf", kwlist, &PyGdkPixbuf_Type, &pixbuf))
+ return NULL;
+
+ gimp_offset_area_set_pixbuf(GIMP_OFFSET_AREA(self->obj), GDK_PIXBUF(pixbuf->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_offset_area_set_size(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "width", "height", NULL };
+ int width, height;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.OffsetArea.set_size", kwlist, &width, &height))
+ return NULL;
+
+ gimp_offset_area_set_size(GIMP_OFFSET_AREA(self->obj), width, height);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_offset_area_set_offsets(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "offset_x", "offset_y", NULL };
+ int offset_x, offset_y;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.OffsetArea.set_offsets", kwlist, &offset_x, &offset_y))
+ return NULL;
+
+ gimp_offset_area_set_offsets(GIMP_OFFSET_AREA(self->obj), offset_x, offset_y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpOffsetArea_methods[] = {
+ { "set_pixbuf", (PyCFunction)_wrap_gimp_offset_area_set_pixbuf, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_size", (PyCFunction)_wrap_gimp_offset_area_set_size, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_offsets", (PyCFunction)_wrap_gimp_offset_area_set_offsets, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpOffsetArea_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.OffsetArea", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpOffsetArea_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_offset_area_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPageSelector ----------- */
+
+ static int
+_wrap_gimp_page_selector_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.PageSelector.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PageSelector object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_set_n_pages(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "n_pages", NULL };
+ int n_pages;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.set_n_pages", kwlist, &n_pages))
+ return NULL;
+
+ gimp_page_selector_set_n_pages(GIMP_PAGE_SELECTOR(self->obj), n_pages);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_get_n_pages(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_page_selector_get_n_pages(GIMP_PAGE_SELECTOR(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static PyObject *
+_wrap_gimp_page_selector_set_target(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "target", NULL };
+ GimpPageSelectorTarget target;
+ PyObject *py_target = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.PageSelector.set_target", kwlist, &py_target))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_PAGE_SELECTOR_TARGET, py_target, (gpointer)&target))
+ return NULL;
+
+ gimp_page_selector_set_target(GIMP_PAGE_SELECTOR(self->obj), target);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_get_target(PyGObject *self)
+{
+ gint ret;
+
+
+ ret = gimp_page_selector_get_target(GIMP_PAGE_SELECTOR(self->obj));
+
+ return pyg_enum_from_gtype(GIMP_TYPE_PAGE_SELECTOR_TARGET, ret);
+}
+
+static PyObject *
+_wrap_gimp_page_selector_set_page_thumbnail(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", "thumbnail", NULL };
+ int page_no;
+ PyGObject *thumbnail;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iO!:Gimp.PageSelector.set_page_thumbnail", kwlist, &page_no, &PyGdkPixbuf_Type, &thumbnail))
+ return NULL;
+
+ gimp_page_selector_set_page_thumbnail(GIMP_PAGE_SELECTOR(self->obj), page_no, GDK_PIXBUF(thumbnail->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_get_page_thumbnail(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", NULL };
+ int page_no;
+ GdkPixbuf *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.get_page_thumbnail", kwlist, &page_no))
+ return NULL;
+
+ ret = gimp_page_selector_get_page_thumbnail(GIMP_PAGE_SELECTOR(self->obj), page_no);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_page_selector_set_page_label(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", "label", NULL };
+ int page_no;
+ char *label;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"is:Gimp.PageSelector.set_page_label", kwlist, &page_no, &label))
+ return NULL;
+
+ gimp_page_selector_set_page_label(GIMP_PAGE_SELECTOR(self->obj), page_no, label);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_get_page_label(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", NULL };
+ int page_no;
+ gchar *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.get_page_label", kwlist, &page_no))
+ return NULL;
+
+ ret = gimp_page_selector_get_page_label(GIMP_PAGE_SELECTOR(self->obj), page_no);
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_select_all(PyGObject *self)
+{
+
+ gimp_page_selector_select_all(GIMP_PAGE_SELECTOR(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_unselect_all(PyGObject *self)
+{
+
+ gimp_page_selector_unselect_all(GIMP_PAGE_SELECTOR(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_select_page(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", NULL };
+ int page_no;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.select_page", kwlist, &page_no))
+ return NULL;
+
+ gimp_page_selector_select_page(GIMP_PAGE_SELECTOR(self->obj), page_no);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_unselect_page(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", NULL };
+ int page_no;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.unselect_page", kwlist, &page_no))
+ return NULL;
+
+ gimp_page_selector_unselect_page(GIMP_PAGE_SELECTOR(self->obj), page_no);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_page_is_selected(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "page_no", NULL };
+ int page_no, ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.PageSelector.page_is_selected", kwlist, &page_no))
+ return NULL;
+
+ ret = gimp_page_selector_page_is_selected(GIMP_PAGE_SELECTOR(self->obj), page_no);
+
+ return PyBool_FromLong(ret);
+
+}
+
+#line 1578 "gimpui.override"
+static PyObject *
+_wrap_gimp_page_selector_get_selected_pages(PyGObject *self)
+{
+ gint *selected_pages;
+ gint n_selected_pages;
+ PyObject *py_selected_pages;
+ int i;
+
+ selected_pages = gimp_page_selector_get_selected_pages(
+ GIMP_PAGE_SELECTOR (self->obj),
+ &n_selected_pages);
+
+ py_selected_pages = PyTuple_New(n_selected_pages);
+ for (i = 0; i < n_selected_pages; ++i)
+ PyTuple_SetItem(py_selected_pages, i,
+ PyInt_FromLong(selected_pages[i]));
+
+ g_free(selected_pages);
+
+ return py_selected_pages;
+}
+#line 5406 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_page_selector_select_range(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "range", NULL };
+ char *range;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PageSelector.select_range", kwlist, &range))
+ return NULL;
+
+ gimp_page_selector_select_range(GIMP_PAGE_SELECTOR(self->obj), range);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_page_selector_get_selected_range(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_page_selector_get_selected_range(GIMP_PAGE_SELECTOR(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpPageSelector_methods[] = {
+ { "set_n_pages", (PyCFunction)_wrap_gimp_page_selector_set_n_pages, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_n_pages", (PyCFunction)_wrap_gimp_page_selector_get_n_pages, METH_NOARGS,
+ NULL },
+ { "set_target", (PyCFunction)_wrap_gimp_page_selector_set_target, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_target", (PyCFunction)_wrap_gimp_page_selector_get_target, METH_NOARGS,
+ NULL },
+ { "set_page_thumbnail", (PyCFunction)_wrap_gimp_page_selector_set_page_thumbnail, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_page_thumbnail", (PyCFunction)_wrap_gimp_page_selector_get_page_thumbnail, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_page_label", (PyCFunction)_wrap_gimp_page_selector_set_page_label, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_page_label", (PyCFunction)_wrap_gimp_page_selector_get_page_label, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "select_all", (PyCFunction)_wrap_gimp_page_selector_select_all, METH_NOARGS,
+ NULL },
+ { "unselect_all", (PyCFunction)_wrap_gimp_page_selector_unselect_all, METH_NOARGS,
+ NULL },
+ { "select_page", (PyCFunction)_wrap_gimp_page_selector_select_page, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "unselect_page", (PyCFunction)_wrap_gimp_page_selector_unselect_page, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "page_is_selected", (PyCFunction)_wrap_gimp_page_selector_page_is_selected, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_selected_pages", (PyCFunction)_wrap_gimp_page_selector_get_selected_pages, METH_NOARGS,
+ NULL },
+ { "select_range", (PyCFunction)_wrap_gimp_page_selector_select_range, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_selected_range", (PyCFunction)_wrap_gimp_page_selector_get_selected_range, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPageSelector_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PageSelector", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPageSelector_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_page_selector_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPathEditor ----------- */
+
+static int
+_wrap_gimp_path_editor_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"title", "path", NULL };
+ char *prop_names[] = {"title", "path", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:gimpui.PathEditor.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PathEditor object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_path_editor_get_path(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_path_editor_get_path(GIMP_PATH_EDITOR(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_path_editor_set_path(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "path", NULL };
+ char *path;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PathEditor.set_path", kwlist, &path))
+ return NULL;
+
+ gimp_path_editor_set_path(GIMP_PATH_EDITOR(self->obj), path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_path_editor_get_writable_path(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_path_editor_get_writable_path(GIMP_PATH_EDITOR(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_path_editor_set_writable_path(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "path", NULL };
+ char *path;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PathEditor.set_writable_path", kwlist, &path))
+ return NULL;
+
+ gimp_path_editor_set_writable_path(GIMP_PATH_EDITOR(self->obj), path);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_path_editor_get_dir_writable(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "directory", NULL };
+ char *directory;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PathEditor.get_dir_writable", kwlist, &directory))
+ return NULL;
+
+ ret = gimp_path_editor_get_dir_writable(GIMP_PATH_EDITOR(self->obj), directory);
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_path_editor_set_dir_writable(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "directory", "writable", NULL };
+ char *directory;
+ int writable;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"si:Gimp.PathEditor.set_dir_writable", kwlist, &directory, &writable))
+ return NULL;
+
+ gimp_path_editor_set_dir_writable(GIMP_PATH_EDITOR(self->obj), directory, writable);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpPathEditor_methods[] = {
+ { "get_path", (PyCFunction)_wrap_gimp_path_editor_get_path, METH_NOARGS,
+ NULL },
+ { "set_path", (PyCFunction)_wrap_gimp_path_editor_set_path, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_writable_path", (PyCFunction)_wrap_gimp_path_editor_get_writable_path, METH_NOARGS,
+ NULL },
+ { "set_writable_path", (PyCFunction)_wrap_gimp_path_editor_set_writable_path, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_dir_writable", (PyCFunction)_wrap_gimp_path_editor_get_dir_writable, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_dir_writable", (PyCFunction)_wrap_gimp_path_editor_set_dir_writable, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPathEditor_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PathEditor", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPathEditor_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_path_editor_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPickButton ----------- */
+
+ static int
+_wrap_gimp_pick_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.PickButton.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PickButton object");
+ return -1;
+ }
+ return 0;
+}
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPickButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PickButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_pick_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPreview ----------- */
+
+static PyObject *
+_wrap_gimp_preview_set_update(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "update", NULL };
+ int update;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.Preview.set_update", kwlist, &update))
+ return NULL;
+
+ gimp_preview_set_update(GIMP_PREVIEW(self->obj), update);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_get_update(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_preview_get_update(GIMP_PREVIEW(self->obj));
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_preview_set_bounds(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "xmin", "ymin", "xmax", "ymax", NULL };
+ int xmin, ymin, xmax, ymax;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iiii:Gimp.Preview.set_bounds", kwlist, &xmin, &ymin, &xmax, &ymax))
+ return NULL;
+
+ gimp_preview_set_bounds(GIMP_PREVIEW(self->obj), xmin, ymin, xmax, ymax);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1601 "gimpui.override"
+static PyObject *
+_wrap_gimp_preview_get_position(PyGObject *self)
+{
+ gint x;
+ gint y;
+
+ gimp_preview_get_position(GIMP_PREVIEW(self->obj), &x, &y);
+
+ return Py_BuildValue("(ii)", x, y);
+}
+#line 5839 "gimpui.c"
+
+
+#line 1613 "gimpui.override"
+static PyObject *
+_wrap_gimp_preview_get_size(PyGObject *self)
+{
+ gint width;
+ gint height;
+
+ gimp_preview_get_size(GIMP_PREVIEW(self->obj), &width, &height);
+
+ return Py_BuildValue("(ii)", width, height);
+}
+#line 5853 "gimpui.c"
+
+
+#line 1625 "gimpui.override"
+static PyObject *
+_wrap_gimp_preview_transform(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gint src_x;
+ gint src_y;
+ gint dest_x;
+ gint dest_y;
+
+ static char *kwlist[] = {"x", "y", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:GimpPreview.transform",
+ kwlist, &src_x, &src_y))
+ return NULL;
+
+ gimp_preview_transform(GIMP_PREVIEW(self->obj), src_x, src_y, &dest_x,
+ &dest_y);
+
+ return Py_BuildValue("(ii)", dest_x, dest_y);
+}
+#line 5876 "gimpui.c"
+
+
+#line 1646 "gimpui.override"
+static PyObject *
+_wrap_gimp_preview_untransform(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ gint src_x;
+ gint src_y;
+ gint dest_x;
+ gint dest_y;
+
+ static char *kwlist[] = {"x", "y", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ii:GimpPreview.untransform",
+ kwlist, &src_x, &src_y))
+ return NULL;
+
+ gimp_preview_untransform(GIMP_PREVIEW(self->obj), src_x, src_y, &dest_x,
+ &dest_y);
+
+ return Py_BuildValue("(ii)", dest_x, dest_y);
+}
+#line 5901 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_preview_get_area(PyGObject *self)
+{
+ GtkWidget *ret;
+
+
+ ret = gimp_preview_get_area(GIMP_PREVIEW(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_preview_draw(PyGObject *self)
+{
+
+ gimp_preview_draw(GIMP_PREVIEW(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_draw_buffer(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "buffer", "rowstride", NULL };
+ int buffer_len, rowstride;
+ guchar *buffer;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s#i:Gimp.Preview.draw_buffer", kwlist, &buffer, &buffer_len, &rowstride))
+ return NULL;
+
+ gimp_preview_draw_buffer(GIMP_PREVIEW(self->obj), buffer, rowstride);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_invalidate(PyGObject *self)
+{
+
+ gimp_preview_invalidate(GIMP_PREVIEW(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_set_default_cursor(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "cursor", NULL };
+ PyObject *py_cursor;
+ GdkCursor *cursor = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.Preview.set_default_cursor", kwlist, &py_cursor))
+ return NULL;
+ if (pyg_boxed_check(py_cursor, GDK_TYPE_CURSOR))
+ cursor = pyg_boxed_get(py_cursor, GdkCursor);
+ else {
+ PyErr_SetString(PyExc_TypeError, "cursor should be a GdkCursor");
+ return NULL;
+ }
+
+ gimp_preview_set_default_cursor(GIMP_PREVIEW(self->obj), cursor);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_get_controls(PyGObject *self)
+{
+ GtkWidget *ret;
+
+
+ ret = gimp_preview_get_controls(GIMP_PREVIEW(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static const PyMethodDef _PyGimpPreview_methods[] = {
+ { "set_update", (PyCFunction)_wrap_gimp_preview_set_update, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_update", (PyCFunction)_wrap_gimp_preview_get_update, METH_NOARGS,
+ NULL },
+ { "set_bounds", (PyCFunction)_wrap_gimp_preview_set_bounds, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_position", (PyCFunction)_wrap_gimp_preview_get_position, METH_NOARGS,
+ NULL },
+ { "get_size", (PyCFunction)_wrap_gimp_preview_get_size, METH_NOARGS,
+ NULL },
+ { "transform", (PyCFunction)_wrap_gimp_preview_transform, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "untransform", (PyCFunction)_wrap_gimp_preview_untransform, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_area", (PyCFunction)_wrap_gimp_preview_get_area, METH_NOARGS,
+ NULL },
+ { "draw", (PyCFunction)_wrap_gimp_preview_draw, METH_NOARGS,
+ NULL },
+ { "draw_buffer", (PyCFunction)_wrap_gimp_preview_draw_buffer, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "invalidate", (PyCFunction)_wrap_gimp_preview_invalidate, METH_NOARGS,
+ NULL },
+ { "set_default_cursor", (PyCFunction)_wrap_gimp_preview_set_default_cursor, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_controls", (PyCFunction)_wrap_gimp_preview_get_controls, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPreview_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Preview", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPreview_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpAspectPreview ----------- */
+
+#line 1928 "gimpui.override"
+static int
+_wrap_gimp_aspect_preview_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", NULL };
+ PyGimpDrawable *py_drawable;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|:GimpAspectPreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable))
+ return -1;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, NULL))
+ return -1;
+
+ g_signal_connect_swapped(self->obj, "destroy",
+ (GCallback)pygimp_decref_callback, py_drawable);
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+#line 6089 "gimpui.c"
+
+
+PyTypeObject G_GNUC_INTERNAL PyGimpAspectPreview_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.AspectPreview", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_aspect_preview_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPreviewArea ----------- */
+
+static int
+_wrap_gimp_preview_area_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.PreviewArea.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PreviewArea object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_draw(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", "width", "height", "type", "buf", "rowstride", NULL };
+ int x, y, width, height, buf_len, rowstride;
+ PyObject *py_type = NULL;
+ guchar *buf;
+ GimpImageType type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iiiiOs#i:Gimp.PreviewArea.draw", kwlist, &x, &y, &width, &height, &py_type, &buf, &buf_len, &rowstride))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_IMAGE_TYPE, py_type, (gpointer)&type))
+ return NULL;
+
+ gimp_preview_area_draw(GIMP_PREVIEW_AREA(self->obj), x, y, width, height, type, buf, rowstride);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_blend(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", "width", "height", "type", "buf1", "rowstride1", "buf2", "rowstride2", "opacity", NULL };
+ int x, y, width, height, buf1_len, rowstride1, buf2_len, rowstride2;
+ PyObject *py_type = NULL;
+ guchar *buf1, *buf2;
+ char opacity;
+ GimpImageType type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iiiiOs#is#ic:Gimp.PreviewArea.blend", kwlist, &x, &y, &width, &height, &py_type, &buf1, &buf1_len, &rowstride1, &buf2, &buf2_len, &rowstride2, &opacity))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_IMAGE_TYPE, py_type, (gpointer)&type))
+ return NULL;
+
+ gimp_preview_area_blend(GIMP_PREVIEW_AREA(self->obj), x, y, width, height, type, buf1, rowstride1, buf2, rowstride2, opacity);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_mask(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", "width", "height", "type", "buf1", "rowstride1", "buf2", "rowstride2", "mask", "rowstride_mask", NULL };
+ int x, y, width, height, buf1_len, rowstride1, buf2_len, rowstride2, mask_len, rowstride_mask;
+ PyObject *py_type = NULL;
+ guchar *buf1, *buf2, *mask;
+ GimpImageType type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iiiiOs#is#is#i:Gimp.PreviewArea.mask", kwlist, &x, &y, &width, &height, &py_type, &buf1, &buf1_len, &rowstride1, &buf2, &buf2_len, &rowstride2, &mask, &mask_len, &rowstride_mask))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_IMAGE_TYPE, py_type, (gpointer)&type))
+ return NULL;
+
+ gimp_preview_area_mask(GIMP_PREVIEW_AREA(self->obj), x, y, width, height, type, buf1, rowstride1, buf2, rowstride2, mask, rowstride_mask);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_fill(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", "width", "height", "red", "green", "blue", NULL };
+ int x, y, width, height;
+ char red, green, blue;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iiiiccc:Gimp.PreviewArea.fill", kwlist, &x, &y, &width, &height, &red, &green, &blue))
+ return NULL;
+
+ gimp_preview_area_fill(GIMP_PREVIEW_AREA(self->obj), x, y, width, height, red, green, blue);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_set_offsets(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", NULL };
+ int x, y;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.PreviewArea.set_offsets", kwlist, &x, &y))
+ return NULL;
+
+ gimp_preview_area_set_offsets(GIMP_PREVIEW_AREA(self->obj), x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_set_colormap(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "colormap", "num_colors", NULL };
+ int colormap_len, num_colors;
+ guchar *colormap;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s#i:Gimp.PreviewArea.set_colormap", kwlist, &colormap, &colormap_len, &num_colors))
+ return NULL;
+
+ gimp_preview_area_set_colormap(GIMP_PREVIEW_AREA(self->obj), colormap, num_colors);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_set_max_size(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "width", "height", NULL };
+ int width, height;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.PreviewArea.set_max_size", kwlist, &width, &height))
+ return NULL;
+
+ gimp_preview_area_set_max_size(GIMP_PREVIEW_AREA(self->obj), width, height);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_preview_area_menu_popup(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "event", NULL };
+ GdkEvent *event = NULL;
+ PyObject *py_event;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O:Gimp.PreviewArea.menu_popup", kwlist, &py_event))
+ return NULL;
+ if (pyg_boxed_check(py_event, GDK_TYPE_EVENT))
+ event = pyg_boxed_get(py_event, GdkEvent);
+ else {
+ PyErr_SetString(PyExc_TypeError, "event should be a GdkEvent");
+ return NULL;
+ }
+
+ gimp_preview_area_menu_popup(GIMP_PREVIEW_AREA(self->obj), (GdkEventButton *)event);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpPreviewArea_methods[] = {
+ { "draw", (PyCFunction)_wrap_gimp_preview_area_draw, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "blend", (PyCFunction)_wrap_gimp_preview_area_blend, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "mask", (PyCFunction)_wrap_gimp_preview_area_mask, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "fill", (PyCFunction)_wrap_gimp_preview_area_fill, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_offsets", (PyCFunction)_wrap_gimp_preview_area_set_offsets, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_colormap", (PyCFunction)_wrap_gimp_preview_area_set_colormap, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_max_size", (PyCFunction)_wrap_gimp_preview_area_set_max_size, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "menu_popup", (PyCFunction)_wrap_gimp_preview_area_menu_popup, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPreviewArea_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PreviewArea", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPreviewArea_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_preview_area_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpProcBrowserDialog ----------- */
+
+#line 1404 "gimpui.override"
+static int
+_wrap_gimp_proc_browser_dialog_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ gchar *title, *role;
+ PyObject *py_buttons = Py_None;
+ PyObject *help_func = NULL;
+ gchar *help_id = NULL;
+ int len, i;
+ GimpHelpFunc func;
+
+ static char *kwlist[] = { "title", "role", "help_func", "help_id",
+ "buttons", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "zz|OzO:gimpui.GimpProcBrowserDialog.__init__",
+ kwlist,
+ &title, &role, &help_func, &help_id,
+ &py_buttons))
+ return -1;
+
+ if (py_buttons == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_buttons))
+ len = PyTuple_Size(py_buttons);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "buttons must be a tuple containing text/response "
+ "pairs or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "buttons tuple must contain text/response id pairs");
+ return -1;
+ }
+
+ if (help_func) {
+ if (help_func != Py_None) {
+ if (!PyCallable_Check(help_func)) {
+ PyErr_SetString(PyExc_TypeError, "help_func must be callable");
+ return -1;
+ }
+
+ func = pygimp_help_func_marshal;
+
+ } else {
+ func = gimp_standard_help_func;
+ }
+ } else {
+ func = gimp_standard_help_func;
+ }
+
+ pygobject_construct(self,
+ "title", title,
+ "role", role,
+ "help-func", func,
+ "help-id", help_id,
+ NULL);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpProcBrowserDialog object");
+ return -1;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *text = PyTuple_GetItem(py_buttons, i);
+ PyObject *id = PyTuple_GetItem(py_buttons, i + 1);
+ if (!PyString_Check(text) && !PyUnicode_Check(text)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each text/response id pair "
+ "must be a string");
+ return -1;
+ }
+ if (!PyInt_Check(id)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each text/response id pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_dialog_add_button(GIMP_DIALOG(self->obj), PyString_AsString(text),
+ PyInt_AsLong(id));
+ }
+
+ if (help_func && help_func != Py_None) {
+ g_object_set_data(self->obj, "pygimp-dialog-help-data", self);
+
+ Py_INCREF(help_func);
+ g_object_set_data_full(self->obj, "pygimp-dialog-help-func",
+ help_func, pygimp_help_func_destroy);
+ }
+
+ g_signal_emit_by_name(GIMP_PROC_BROWSER_DIALOG(self->obj)->browser,
+ "search", "", 0, self->obj);
+ return 0;
+}
+#line 6479 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_proc_browser_dialog_get_selected(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_proc_browser_dialog_get_selected(GIMP_PROC_BROWSER_DIALOG(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpProcBrowserDialog_methods[] = {
+ { "get_selected", (PyCFunction)_wrap_gimp_proc_browser_dialog_get_selected, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpProcBrowserDialog_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ProcBrowserDialog", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpProcBrowserDialog_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_proc_browser_dialog_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpProgressBar ----------- */
+
+static int
+_wrap_gimp_progress_bar_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.ProgressBar.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ProgressBar object");
+ return -1;
+ }
+ return 0;
+}
+
+PyTypeObject G_GNUC_INTERNAL PyGimpProgressBar_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ProgressBar", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_progress_bar_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpScrolledPreview ----------- */
+
+static PyObject *
+_wrap_gimp_scrolled_preview_set_position(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "x", "y", NULL };
+ int x, y;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.ScrolledPreview.set_position", kwlist, &x, &y))
+ return NULL;
+
+ gimp_scrolled_preview_set_position(GIMP_SCROLLED_PREVIEW(self->obj), x, y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_scrolled_preview_set_policy(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "hscrollbar_policy", "vscrollbar_policy", NULL };
+ PyObject *py_hscrollbar_policy = NULL, *py_vscrollbar_policy = NULL;
+ GtkPolicyType hscrollbar_policy, vscrollbar_policy;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"OO:Gimp.ScrolledPreview.set_policy", kwlist, &py_hscrollbar_policy, &py_vscrollbar_policy))
+ return NULL;
+ if (pyg_enum_get_value(GTK_TYPE_POLICY_TYPE, py_hscrollbar_policy, (gpointer)&hscrollbar_policy))
+ return NULL;
+ if (pyg_enum_get_value(GTK_TYPE_POLICY_TYPE, py_vscrollbar_policy, (gpointer)&vscrollbar_policy))
+ return NULL;
+
+ gimp_scrolled_preview_set_policy(GIMP_SCROLLED_PREVIEW(self->obj), hscrollbar_policy, vscrollbar_policy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_scrolled_preview_freeze(PyGObject *self)
+{
+
+ gimp_scrolled_preview_freeze(GIMP_SCROLLED_PREVIEW(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_scrolled_preview_thaw(PyGObject *self)
+{
+
+ gimp_scrolled_preview_thaw(GIMP_SCROLLED_PREVIEW(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpScrolledPreview_methods[] = {
+ { "set_position", (PyCFunction)_wrap_gimp_scrolled_preview_set_position, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_policy", (PyCFunction)_wrap_gimp_scrolled_preview_set_policy, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "freeze", (PyCFunction)_wrap_gimp_scrolled_preview_freeze, METH_NOARGS,
+ NULL },
+ { "thaw", (PyCFunction)_wrap_gimp_scrolled_preview_thaw, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpScrolledPreview_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ScrolledPreview", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpScrolledPreview_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpDrawablePreview ----------- */
+
+#line 1955 "gimpui.override"
+static int
+_wrap_gimp_drawable_preview_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", NULL };
+ PyGimpDrawable *py_drawable;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|:GimpDrawablePreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable))
+ return -1;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, NULL))
+ return -1;
+
+ g_object_set_data_full(self->obj, "pygimp-drawable-preview-pydrawable",
+ py_drawable,
+ (GDestroyNotify)pygimp_decref_callback);
+
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+
+#line 6766 "gimpui.c"
+
+
+#line 1682 "gimpui.override"
+static PyObject *
+_wrap_gimp_drawable_preview_get_drawable(PyGObject *self)
+{
+ PyObject *drawable;
+
+ drawable = g_object_get_data(self->obj,
+ "pygimp-drawable-preview-pydrawable");
+ Py_INCREF(drawable);
+ return drawable;
+}
+#line 6780 "gimpui.c"
+
+
+#line 1706 "gimpui.override"
+static PyObject *
+_wrap_gimp_drawable_preview_draw_region(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+/* PyGimpPixelRgn *pypixelrgn;
+
+ static char *kwlist[] = {"drawable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpDrawablePreview.draw_region",
+ kwlist, PyGimpPixelRgn_Type, &pypixelrgn))
+ return NULL;
+
+ gimp_drawable_preview_draw_region(GIMP_DRAWABLE_PREVIEW(self->obj),
+ &pypixelrgn->pr);
+*/
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 6803 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpDrawablePreview_methods[] = {
+ { "get_drawable", (PyCFunction)_wrap_gimp_drawable_preview_get_drawable, METH_NOARGS,
+ NULL },
+ { "draw_region", (PyCFunction)_wrap_gimp_drawable_preview_draw_region, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpDrawablePreview_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.DrawablePreview", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpDrawablePreview_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_drawable_preview_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpSelectButton ----------- */
+
+static PyObject *
+_wrap_gimp_select_button_close_popup(PyGObject *self)
+{
+
+ gimp_select_button_close_popup(GIMP_SELECT_BUTTON(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpSelectButton_methods[] = {
+ { "close_popup", (PyCFunction)_wrap_gimp_select_button_close_popup, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.SelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPatternSelectButton ----------- */
+
+static int
+_wrap_gimp_pattern_select_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"title", "pattern_name", NULL };
+ char *prop_names[] = {"title", "pattern-name", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:gimpui.PatternSelectButton.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PatternSelectButton object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_pattern_select_button_get_pattern(PyGObject *self)
+{
+ const gchar *ret;
+
+
+ ret = gimp_pattern_select_button_get_pattern(GIMP_PATTERN_SELECT_BUTTON(self->obj));
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_pattern_select_button_set_pattern(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "pattern_name", NULL };
+ char *pattern_name;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PatternSelectButton.set_pattern", kwlist, &pattern_name))
+ return NULL;
+
+ gimp_pattern_select_button_set_pattern(GIMP_PATTERN_SELECT_BUTTON(self->obj), pattern_name);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpPatternSelectButton_methods[] = {
+ { "get_pattern", (PyCFunction)_wrap_gimp_pattern_select_button_get_pattern, METH_NOARGS,
+ NULL },
+ { "set_pattern", (PyCFunction)_wrap_gimp_pattern_select_button_set_pattern, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPatternSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PatternSelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPatternSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_pattern_select_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpPaletteSelectButton ----------- */
+
+ static int
+_wrap_gimp_palette_select_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"title", "palette_name", NULL };
+ char *prop_names[] = {"title", "palette-name", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:gimpui.PaletteSelectButton.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.PaletteSelectButton object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_palette_select_button_get_palette(PyGObject *self)
+{
+ const gchar *ret;
+
+
+ ret = gimp_palette_select_button_get_palette(GIMP_PALETTE_SELECT_BUTTON(self->obj));
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_palette_select_button_set_palette(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "palette_name", NULL };
+ char *palette_name;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.PaletteSelectButton.set_palette", kwlist, &palette_name))
+ return NULL;
+
+ gimp_palette_select_button_set_palette(GIMP_PALETTE_SELECT_BUTTON(self->obj), palette_name);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpPaletteSelectButton_methods[] = {
+ { "get_palette", (PyCFunction)_wrap_gimp_palette_select_button_get_palette, METH_NOARGS,
+ NULL },
+ { "set_palette", (PyCFunction)_wrap_gimp_palette_select_button_set_palette, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpPaletteSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.PaletteSelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpPaletteSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_palette_select_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpGradientSelectButton ----------- */
+
+ static int
+_wrap_gimp_gradient_select_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"title", "gradient_name", NULL };
+ char *prop_names[] = {"title", "gradient-name", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:gimpui.GradientSelectButton.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.GradientSelectButton object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_gradient_select_button_get_gradient(PyGObject *self)
+{
+ const gchar *ret;
+
+
+ ret = gimp_gradient_select_button_get_gradient(GIMP_GRADIENT_SELECT_BUTTON(self->obj));
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_gradient_select_button_set_gradient(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "gradient_name", NULL };
+ char *gradient_name;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.GradientSelectButton.set_gradient", kwlist, &gradient_name))
+ return NULL;
+
+ gimp_gradient_select_button_set_gradient(GIMP_GRADIENT_SELECT_BUTTON(self->obj), gradient_name);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpGradientSelectButton_methods[] = {
+ { "get_gradient", (PyCFunction)_wrap_gimp_gradient_select_button_get_gradient, METH_NOARGS,
+ NULL },
+ { "set_gradient", (PyCFunction)_wrap_gimp_gradient_select_button_set_gradient, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpGradientSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.GradientSelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpGradientSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_gradient_select_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpFontSelectButton ----------- */
+
+ static int
+_wrap_gimp_font_select_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[2];
+ PyObject *parsed_args[2] = {NULL, };
+ char *arg_names[] = {"title", "font_name", NULL };
+ char *prop_names[] = {"title", "font-name", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO:gimpui.FontSelectButton.__init__" , arg_names , &parsed_args[0] , &parsed_args[1]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*2);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.FontSelectButton object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_font_select_button_get_font(PyGObject *self)
+{
+ const gchar *ret;
+
+
+ ret = gimp_font_select_button_get_font(GIMP_FONT_SELECT_BUTTON(self->obj));
+
+ if (ret)
+ return PyString_FromString(ret);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_font_select_button_set_font(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "font_name", NULL };
+ char *font_name;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.FontSelectButton.set_font", kwlist, &font_name))
+ return NULL;
+
+ gimp_font_select_button_set_font(GIMP_FONT_SELECT_BUTTON(self->obj), font_name);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpFontSelectButton_methods[] = {
+ { "get_font", (PyCFunction)_wrap_gimp_font_select_button_get_font, METH_NOARGS,
+ NULL },
+ { "set_font", (PyCFunction)_wrap_gimp_font_select_button_set_font, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpFontSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.FontSelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpFontSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_font_select_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpBrushSelectButton ----------- */
+
+ static int
+_wrap_gimp_brush_select_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[5];
+ PyObject *parsed_args[5] = {NULL, };
+ char *arg_names[] = {"title", "brush_name", "opacity", "spacing", "paint_mode", NULL };
+ char *prop_names[] = {"title", "brush-name", "brush-opacity", "brush-spacing", "brush-paint-mode", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOO:gimpui.BrushSelectButton.__init__" , arg_names , &parsed_args[0] , &parsed_args[1] , &parsed_args[2] , &parsed_args[3] , &parsed_args[4]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*5);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.BrushSelectButton object");
+ return -1;
+ }
+ return 0;
+}
+
+#line 914 "gimpui.override"
+static PyObject *
+_wrap_gimp_brush_select_button_get_brush(PyGObject *self)
+{
+ const gchar *brush_name;
+ gdouble opacity;
+ gint spacing;
+ GimpLayerMode paint_mode;
+
+ brush_name =
+ gimp_brush_select_button_get_brush(GIMP_BRUSH_SELECT_BUTTON(self->obj),
+ &opacity, &spacing, &paint_mode);
+
+ return Py_BuildValue("(sdiN)", brush_name, opacity, spacing,
+ pyg_enum_from_gtype(GIMP_TYPE_LAYER_MODE,
+ paint_mode));
+}
+#line 7439 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpBrushSelectButton_methods[] = {
+ { "get_brush", (PyCFunction)_wrap_gimp_brush_select_button_get_brush, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpBrushSelectButton_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.BrushSelectButton", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpBrushSelectButton_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_brush_select_button_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpRuler ----------- */
+
+ static int
+_wrap_gimp_ruler_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[4];
+ PyObject *parsed_args[4] = {NULL, };
+ char *arg_names[] = {"orientation", "lower", "upper", "max_size", NULL };
+ char *prop_names[] = {"orientation", "lower", "upper", "max-size", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOO:gimpui.Ruler.__init__" , arg_names , &parsed_args[0] , &parsed_args[1] , &parsed_args[2] , &parsed_args[3]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*4);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.Ruler object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_ruler_set_unit(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "unit", NULL };
+ int unit;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.Ruler.set_unit", kwlist, &unit))
+ return NULL;
+
+ gimp_ruler_set_unit(GIMP_RULER(self->obj), unit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_ruler_get_unit(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_ruler_get_unit(GIMP_RULER(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static PyObject *
+_wrap_gimp_ruler_set_position(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "position", NULL };
+ double position;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"d:Gimp.Ruler.set_position", kwlist, &position))
+ return NULL;
+
+ gimp_ruler_set_position(GIMP_RULER(self->obj), position);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_ruler_get_position(PyGObject *self)
+{
+ double ret;
+
+
+ ret = gimp_ruler_get_position(GIMP_RULER(self->obj));
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+_wrap_gimp_ruler_set_range(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "lower", "upper", "max_size", NULL };
+ double lower, upper, max_size;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ddd:Gimp.Ruler.set_range", kwlist, &lower, &upper, &max_size))
+ return NULL;
+
+ gimp_ruler_set_range(GIMP_RULER(self->obj), lower, upper, max_size);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#line 1985 "gimpui.override"
+static PyObject *
+_wrap_gimp_ruler_get_range(PyGObject *self)
+{
+ gdouble lower, upper, max_size;
+
+ gimp_ruler_get_range(GIMP_RULER(self->obj), &lower, &upper, &max_size);
+
+ return Py_BuildValue("(ddd)", lower, upper, max_size);
+}
+#line 7604 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpRuler_methods[] = {
+ { "set_unit", (PyCFunction)_wrap_gimp_ruler_set_unit, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_unit", (PyCFunction)_wrap_gimp_ruler_get_unit, METH_NOARGS,
+ NULL },
+ { "set_position", (PyCFunction)_wrap_gimp_ruler_set_position, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_position", (PyCFunction)_wrap_gimp_ruler_get_position, METH_NOARGS,
+ NULL },
+ { "set_range", (PyCFunction)_wrap_gimp_ruler_set_range, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_range", (PyCFunction)_wrap_gimp_ruler_get_range, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpRuler_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.Ruler", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpRuler_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_ruler_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpSizeEntry ----------- */
+
+static int
+_wrap_gimp_size_entry_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "number_of_fields", "unit", "unit_format", "menu_show_pixels", "menu_show_percent", "show_refval", "spinbutton_width", "update_policy", NULL };
+ int number_of_fields, unit, menu_show_pixels, menu_show_percent, show_refval, spinbutton_width;
+ char *unit_format;
+ GimpSizeEntryUpdatePolicy update_policy;
+ PyObject *py_update_policy = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"iisiiiiO:Gimp.SizeEntry.__init__", kwlist, &number_of_fields, &unit, &unit_format, &menu_show_pixels, &menu_show_percent, &show_refval, &spinbutton_width, &py_update_policy))
+ return -1;
+ if (pyg_enum_get_value(GIMP_TYPE_SIZE_ENTRY_UPDATE_POLICY, py_update_policy, (gpointer)&update_policy))
+ return -1;
+ self->obj = (GObject *)gimp_size_entry_new(number_of_fields, unit, unit_format, menu_show_pixels, menu_show_percent, show_refval, spinbutton_width, update_policy);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpSizeEntry object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_add_field(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "value_spinbutton", "refval_spinbutton", NULL };
+ PyGObject *value_spinbutton, *refval_spinbutton;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!O!:Gimp.SizeEntry.add_field", kwlist, &PyGtkSpinButton_Type, &value_spinbutton, &PyGtkSpinButton_Type, &refval_spinbutton))
+ return NULL;
+
+ gimp_size_entry_add_field(GIMP_SIZE_ENTRY(self->obj), GTK_SPIN_BUTTON(value_spinbutton->obj), GTK_SPIN_BUTTON(refval_spinbutton->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_attach_label(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "text", "row", "column", "alignment", NULL };
+ char *text;
+ int row, column;
+ GtkWidget *ret;
+ double alignment;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"siid:Gimp.SizeEntry.attach_label", kwlist, &text, &row, &column, &alignment))
+ return NULL;
+
+ ret = gimp_size_entry_attach_label(GIMP_SIZE_ENTRY(self->obj), text, row, column, alignment);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_resolution(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "resolution", "keep_size", NULL };
+ int field, keep_size;
+ double resolution;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"idi:Gimp.SizeEntry.set_resolution", kwlist, &field, &resolution, &keep_size))
+ return NULL;
+
+ gimp_size_entry_set_resolution(GIMP_SIZE_ENTRY(self->obj), field, resolution, keep_size);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_size(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "lower", "upper", NULL };
+ int field;
+ double lower, upper;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"idd:Gimp.SizeEntry.set_size", kwlist, &field, &lower, &upper))
+ return NULL;
+
+ gimp_size_entry_set_size(GIMP_SIZE_ENTRY(self->obj), field, lower, upper);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_value_boundaries(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "lower", "upper", NULL };
+ int field;
+ double lower, upper;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"idd:Gimp.SizeEntry.set_value_boundaries", kwlist, &field, &lower, &upper))
+ return NULL;
+
+ gimp_size_entry_set_value_boundaries(GIMP_SIZE_ENTRY(self->obj), field, lower, upper);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_get_value(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", NULL };
+ int field;
+ double ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.get_value", kwlist, &field))
+ return NULL;
+
+ ret = gimp_size_entry_get_value(GIMP_SIZE_ENTRY(self->obj), field);
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_value(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "value", NULL };
+ int field;
+ double value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"id:Gimp.SizeEntry.set_value", kwlist, &field, &value))
+ return NULL;
+
+ gimp_size_entry_set_value(GIMP_SIZE_ENTRY(self->obj), field, value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_refval_boundaries(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "lower", "upper", NULL };
+ int field;
+ double lower, upper;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"idd:Gimp.SizeEntry.set_refval_boundaries", kwlist, &field, &lower, &upper))
+ return NULL;
+
+ gimp_size_entry_set_refval_boundaries(GIMP_SIZE_ENTRY(self->obj), field, lower, upper);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_refval_digits(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "digits", NULL };
+ int field, digits;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"ii:Gimp.SizeEntry.set_refval_digits", kwlist, &field, &digits))
+ return NULL;
+
+ gimp_size_entry_set_refval_digits(GIMP_SIZE_ENTRY(self->obj), field, digits);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_get_refval(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", NULL };
+ int field;
+ double ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.get_refval", kwlist, &field))
+ return NULL;
+
+ ret = gimp_size_entry_get_refval(GIMP_SIZE_ENTRY(self->obj), field);
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_refval(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", "refval", NULL };
+ int field;
+ double refval;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"id:Gimp.SizeEntry.set_refval", kwlist, &field, &refval))
+ return NULL;
+
+ gimp_size_entry_set_refval(GIMP_SIZE_ENTRY(self->obj), field, refval);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_get_unit(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_size_entry_get_unit(GIMP_SIZE_ENTRY(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_unit(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "unit", NULL };
+ int unit;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.set_unit", kwlist, &unit))
+ return NULL;
+
+ gimp_size_entry_set_unit(GIMP_SIZE_ENTRY(self->obj), unit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_show_unit_menu(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "show", NULL };
+ int show;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.show_unit_menu", kwlist, &show))
+ return NULL;
+
+ gimp_size_entry_show_unit_menu(GIMP_SIZE_ENTRY(self->obj), show);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_pixel_digits(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "digits", NULL };
+ int digits;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.set_pixel_digits", kwlist, &digits))
+ return NULL;
+
+ gimp_size_entry_set_pixel_digits(GIMP_SIZE_ENTRY(self->obj), digits);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_grab_focus(PyGObject *self)
+{
+
+ gimp_size_entry_grab_focus(GIMP_SIZE_ENTRY(self->obj));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_set_activates_default(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "setting", NULL };
+ int setting;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.set_activates_default", kwlist, &setting))
+ return NULL;
+
+ gimp_size_entry_set_activates_default(GIMP_SIZE_ENTRY(self->obj), setting);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_size_entry_get_help_widget(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "field", NULL };
+ int field;
+ GtkWidget *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.SizeEntry.get_help_widget", kwlist, &field))
+ return NULL;
+
+ ret = gimp_size_entry_get_help_widget(GIMP_SIZE_ENTRY(self->obj), field);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static const PyMethodDef _PyGimpSizeEntry_methods[] = {
+ { "add_field", (PyCFunction)_wrap_gimp_size_entry_add_field, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "attach_label", (PyCFunction)_wrap_gimp_size_entry_attach_label, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_resolution", (PyCFunction)_wrap_gimp_size_entry_set_resolution, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_size", (PyCFunction)_wrap_gimp_size_entry_set_size, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_value_boundaries", (PyCFunction)_wrap_gimp_size_entry_set_value_boundaries, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_value", (PyCFunction)_wrap_gimp_size_entry_get_value, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_value", (PyCFunction)_wrap_gimp_size_entry_set_value, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_refval_boundaries", (PyCFunction)_wrap_gimp_size_entry_set_refval_boundaries, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_refval_digits", (PyCFunction)_wrap_gimp_size_entry_set_refval_digits, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_refval", (PyCFunction)_wrap_gimp_size_entry_get_refval, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_refval", (PyCFunction)_wrap_gimp_size_entry_set_refval, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_unit", (PyCFunction)_wrap_gimp_size_entry_get_unit, METH_NOARGS,
+ NULL },
+ { "set_unit", (PyCFunction)_wrap_gimp_size_entry_set_unit, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "show_unit_menu", (PyCFunction)_wrap_gimp_size_entry_show_unit_menu, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "set_pixel_digits", (PyCFunction)_wrap_gimp_size_entry_set_pixel_digits, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "grab_focus", (PyCFunction)_wrap_gimp_size_entry_grab_focus, METH_NOARGS,
+ NULL },
+ { "set_activates_default", (PyCFunction)_wrap_gimp_size_entry_set_activates_default, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_help_widget", (PyCFunction)_wrap_gimp_size_entry_get_help_widget, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpSizeEntry_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.SizeEntry", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpSizeEntry_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_size_entry_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpStringComboBox ----------- */
+
+ static int
+_wrap_gimp_string_combo_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ GType obj_type = pyg_type_from_object((PyObject *) self);
+ GParameter params[3];
+ PyObject *parsed_args[3] = {NULL, };
+ char *arg_names[] = {"model", "id_column", "label_column", NULL };
+ char *prop_names[] = {"model", "id-column", "label-column", NULL };
+ guint nparams, i;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO:gimpui.StringComboBox.__init__" , arg_names , &parsed_args[0] , &parsed_args[1] , &parsed_args[2]))
+ return -1;
+
+ memset(params, 0, sizeof(GParameter)*3);
+ if (!pyg_parse_constructor_args(obj_type, arg_names,
+ prop_names, params,
+ &nparams, parsed_args))
+ return -1;
+ pygobject_constructv(self, nparams, params);
+ for (i = 0; i < nparams; ++i)
+ g_value_unset(&params[i].value);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.StringComboBox object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_string_combo_box_set_active(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "id", NULL };
+ char *id;
+ int ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:Gimp.StringComboBox.set_active", kwlist, &id))
+ return NULL;
+
+ ret = gimp_string_combo_box_set_active(GIMP_STRING_COMBO_BOX(self->obj), id);
+
+ return PyBool_FromLong(ret);
+
+}
+
+static PyObject *
+_wrap_gimp_string_combo_box_get_active(PyGObject *self)
+{
+ gchar *ret;
+
+
+ ret = gimp_string_combo_box_get_active(GIMP_STRING_COMBO_BOX(self->obj));
+
+ if (ret) {
+ PyObject *py_ret = PyString_FromString(ret);
+ g_free(ret);
+ return py_ret;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpStringComboBox_methods[] = {
+ { "set_active", (PyCFunction)_wrap_gimp_string_combo_box_set_active, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_active", (PyCFunction)_wrap_gimp_string_combo_box_get_active, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpStringComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.StringComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpStringComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_string_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpUnitComboBox ----------- */
+
+ static int
+_wrap_gimp_unit_combo_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.UnitComboBox.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.UnitComboBox object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_unit_combo_box_get_active(PyGObject *self)
+{
+ int ret;
+
+
+ ret = gimp_unit_combo_box_get_active(GIMP_UNIT_COMBO_BOX(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static PyObject *
+_wrap_gimp_unit_combo_box_set_active(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "unit", NULL };
+ int unit;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.UnitComboBox.set_active", kwlist, &unit))
+ return NULL;
+
+ gimp_unit_combo_box_set_active(GIMP_UNIT_COMBO_BOX(self->obj), unit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static const PyMethodDef _PyGimpUnitComboBox_methods[] = {
+ { "get_active", (PyCFunction)_wrap_gimp_unit_combo_box_get_active, METH_NOARGS,
+ NULL },
+ { "set_active", (PyCFunction)_wrap_gimp_unit_combo_box_set_active, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpUnitComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.UnitComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpUnitComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_unit_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpUnitMenu ----------- */
+
+static int
+_wrap_gimp_unit_menu_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "format", "unit", "show_pixels", "show_percent", "show_custom", NULL };
+ char *format;
+ int unit, show_pixels, show_percent, show_custom;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"siiii:Gimp.UnitMenu.__init__", kwlist, &format, &unit, &show_pixels, &show_percent, &show_custom))
+ return -1;
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimpui.UnitComboBox instead") < 0)
+ return -1;
+ self->obj = (GObject *)gimp_unit_menu_new(format, unit, show_pixels, show_percent, show_custom);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpUnitMenu object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_unit_menu_set_unit(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "unit", NULL };
+ int unit;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.UnitMenu.set_unit", kwlist, &unit))
+ return NULL;
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimpui.UnitComboBox instead") < 0)
+ return NULL;
+
+ gimp_unit_menu_set_unit(GIMP_UNIT_MENU(self->obj), unit);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_unit_menu_get_unit(PyGObject *self)
+{
+ int ret;
+
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimpui.UnitComboBox instead") < 0)
+ return NULL;
+
+ ret = gimp_unit_menu_get_unit(GIMP_UNIT_MENU(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static PyObject *
+_wrap_gimp_unit_menu_set_pixel_digits(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "digits", NULL };
+ int digits;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"i:Gimp.UnitMenu.set_pixel_digits", kwlist, &digits))
+ return NULL;
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimpui.UnitComboBox instead") < 0)
+ return NULL;
+
+ gimp_unit_menu_set_pixel_digits(GIMP_UNIT_MENU(self->obj), digits);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_unit_menu_get_pixel_digits(PyGObject *self)
+{
+ int ret;
+
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use gimpui.UnitComboBox instead") < 0)
+ return NULL;
+
+ ret = gimp_unit_menu_get_pixel_digits(GIMP_UNIT_MENU(self->obj));
+
+ return PyInt_FromLong(ret);
+}
+
+static const PyMethodDef _PyGimpUnitMenu_methods[] = {
+ { "set_unit", (PyCFunction)_wrap_gimp_unit_menu_set_unit, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_unit", (PyCFunction)_wrap_gimp_unit_menu_get_unit, METH_NOARGS,
+ NULL },
+ { "set_pixel_digits", (PyCFunction)_wrap_gimp_unit_menu_set_pixel_digits, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_pixel_digits", (PyCFunction)_wrap_gimp_unit_menu_get_pixel_digits, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpUnitMenu_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.UnitMenu", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpUnitMenu_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_unit_menu_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpVectorsComboBox ----------- */
+
+#line 479 "gimpui.override"
+static gboolean
+pygimp_vectors_constraint_marshal(gint32 image_id, gint32 vectors_id,
+ gpointer user_data)
+{
+ PyObject *img, *vect, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ vect = pygimp_vectors_new(vectors_id);
+ if (!vect) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, vect,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, vect, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(vect);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_vectors_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpVectorsConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.VectorsComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_vectors_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_vectors_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_VECTORS_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.VectorsComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+#line 8510 "gimpui.c"
+
+
+#line 596 "gimpui.override"
+static PyObject *
+_wrap_gimp_vectors_combo_box_get_active_vectors(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_vectors_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 8525 "gimpui.c"
+
+
+#line 570 "gimpui.override"
+static PyObject *
+_wrap_gimp_vectors_combo_box_set_active_vectors(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpVectors *vect;
+
+ static char *kwlist[] = { "vectors", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpVectorsComboBox.set_active_vectors",
+ kwlist,
+ PyGimpVectors_Type, &vect))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), vect->ID)) {
+ PyErr_Format(pygimp_error,
+ "Vectors (ID %d) does not exist in GimpVectorsComboBox",
+ vect->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 8553 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpVectorsComboBox_methods[] = {
+ { "get_active_vectors", (PyCFunction)_wrap_gimp_vectors_combo_box_get_active_vectors, METH_NOARGS,
+ NULL },
+ { "set_active_vectors", (PyCFunction)_wrap_gimp_vectors_combo_box_set_active_vectors, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpVectorsComboBox_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.VectorsComboBox", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpVectorsComboBox_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_vectors_combo_box_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpZoomModel ----------- */
+
+static int
+_wrap_gimp_zoom_model_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char* kwlist[] = { NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ ":gimpui.ZoomModel.__init__",
+ kwlist))
+ return -1;
+
+ pygobject_constructv(self, 0, NULL);
+ if (!self->obj) {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "could not create gimpui.ZoomModel object");
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_wrap_gimp_zoom_model_set_range(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "min", "max", NULL };
+ double min, max;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"dd:Gimp.ZoomModel.set_range", kwlist, &min, &max))
+ return NULL;
+
+ gimp_zoom_model_set_range(GIMP_ZOOM_MODEL(self->obj), min, max);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_zoom_model_zoom(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "zoom_type", "scale", NULL };
+ PyObject *py_zoom_type = NULL;
+ double scale;
+ GimpZoomType zoom_type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Od:Gimp.ZoomModel.zoom", kwlist, &py_zoom_type, &scale))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_ZOOM_TYPE, py_zoom_type, (gpointer)&zoom_type))
+ return NULL;
+
+ gimp_zoom_model_zoom(GIMP_ZOOM_MODEL(self->obj), zoom_type, scale);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_wrap_gimp_zoom_model_get_factor(PyGObject *self)
+{
+ double ret;
+
+
+ ret = gimp_zoom_model_get_factor(GIMP_ZOOM_MODEL(self->obj));
+
+ return PyFloat_FromDouble(ret);
+}
+
+#line 1669 "gimpui.override"
+static PyObject *
+_wrap_gimp_zoom_model_get_fraction(PyGObject *self)
+{
+ gint numerator;
+ gint denominator;
+
+ gimp_zoom_model_get_fraction(GIMP_ZOOM_MODEL(self->obj), &numerator,
+ &denominator);
+
+ return Py_BuildValue("(ii)", numerator, denominator);
+}
+#line 8690 "gimpui.c"
+
+
+static const PyMethodDef _PyGimpZoomModel_methods[] = {
+ { "set_range", (PyCFunction)_wrap_gimp_zoom_model_set_range, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "zoom", (PyCFunction)_wrap_gimp_zoom_model_zoom, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "get_factor", (PyCFunction)_wrap_gimp_zoom_model_get_factor, METH_NOARGS,
+ NULL },
+ { "get_fraction", (PyCFunction)_wrap_gimp_zoom_model_get_fraction, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpZoomModel_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ZoomModel", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpZoomModel_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_zoom_model_new, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpZoomPreview ----------- */
+
+#line 1891 "gimpui.override"
+static int
+_wrap_gimp_zoom_preview_new_with_model(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", "model", NULL };
+ PyGimpDrawable *py_drawable;
+ PyGObject *py_zoom_model = NULL;
+ GimpZoomModel *zoom_model;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|O!:GimpZoomPreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable,
+ &PyGimpZoomModel_Type, &py_zoom_model))
+ return -1;
+
+ if (py_zoom_model)
+ zoom_model = (GimpZoomModel*)py_zoom_model->obj;
+ else
+ zoom_model = NULL;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, "model", zoom_model, NULL))
+ return -1;
+
+ g_object_set_data_full(self->obj, "pygimp-zoom-preview-pydrawable",
+ py_drawable,
+ (GDestroyNotify)pygimp_decref_callback);
+
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+#line 8788 "gimpui.c"
+
+
+#line 2040 "gimpui.override"
+static PyObject *
+_wrap_gimp_zoom_preview_get_source(PyGObject *self)
+{
+ gint width, height, bpp;
+ guchar *image;
+ PyObject *pyimage;
+
+ image = gimp_zoom_preview_get_source(GIMP_ZOOM_PREVIEW(self->obj),
+ &width, &height, &bpp);
+
+ if (image)
+ {
+ pyimage = PyByteArray_FromStringAndSize((const char *)image,
+ width * height * bpp);
+ g_free (image);
+ }
+ else
+ {
+ Py_INCREF(Py_None);
+ pyimage = Py_None;
+ }
+
+ return Py_BuildValue("(Niii)", pyimage, width, height, bpp);
+}
+#line 8816 "gimpui.c"
+
+
+#line 1694 "gimpui.override"
+static PyObject *
+_wrap_gimp_zoom_preview_get_drawable(PyGObject *self)
+{
+ PyObject *drawable;
+
+ drawable = g_object_get_data(self->obj,
+ "pygimp-zoom-preview-pydrawable");
+ Py_INCREF(drawable);
+ return drawable;
+}
+#line 8830 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_zoom_preview_get_model(PyGObject *self)
+{
+ GimpZoomModel *ret;
+
+
+ ret = gimp_zoom_preview_get_model(GIMP_ZOOM_PREVIEW(self->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_zoom_preview_get_factor(PyGObject *self)
+{
+ double ret;
+
+
+ ret = gimp_zoom_preview_get_factor(GIMP_ZOOM_PREVIEW(self->obj));
+
+ return PyFloat_FromDouble(ret);
+}
+
+static const PyMethodDef _PyGimpZoomPreview_methods[] = {
+ { "get_source", (PyCFunction)_wrap_gimp_zoom_preview_get_source, METH_NOARGS,
+ NULL },
+ { "get_drawable", (PyCFunction)_wrap_gimp_zoom_preview_get_drawable, METH_NOARGS,
+ NULL },
+ { "get_model", (PyCFunction)_wrap_gimp_zoom_preview_get_model, METH_NOARGS,
+ NULL },
+ { "get_factor", (PyCFunction)_wrap_gimp_zoom_preview_get_factor, METH_NOARGS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject G_GNUC_INTERNAL PyGimpZoomPreview_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ZoomPreview", /* tp_name */
+ sizeof(PyGObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ offsetof(PyGObject, weakreflist), /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)_PyGimpZoomPreview_methods, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ offsetof(PyGObject, inst_dict), /* tp_dictoffset */
+ (initproc)_wrap_gimp_zoom_preview_new_with_model, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- GimpColorManaged ----------- */
+
+PyTypeObject G_GNUC_INTERNAL PyGimpColorManaged_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpui.ColorManaged", /* tp_name */
+ sizeof(PyObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)0, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ (PySequenceMethods*)0, /* tp_as_sequence */
+ (PyMappingMethods*)0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ (struct PyMethodDef*)NULL, /* tp_methods */
+ (struct PyMemberDef*)0, /* tp_members */
+ (struct PyGetSetDef*)0, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+
+
+/* ----------- functions ----------- */
+
+#line 2066 "gimpui.override"
+static PyObject *
+_wrap_gimp_ui_init(PyObject *self)
+{
+ extern const char *prog_name;
+
+ gimp_ui_init (prog_name, FALSE);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#line 8977 "gimpui.c"
+
+
+static PyObject *
+_wrap_gimp_enum_combo_box_new_with_model(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enum_store", NULL };
+ PyGObject *enum_store;
+ GtkWidget *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"O!:gimp_enum_combo_box_new_with_model", kwlist, &PyGimpEnumStore_Type, &enum_store))
+ return NULL;
+
+ ret = gimp_enum_combo_box_new_with_model(GIMP_ENUM_STORE(enum_store->obj));
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_enum_store_new_with_range(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enum_type", "minimum", "maximum", NULL };
+ PyObject *py_enum_type = NULL;
+ int minimum, maximum;
+ GType enum_type;
+ GtkListStore *ret;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Oii:gimp_enum_store_new_with_range", kwlist, &py_enum_type, &minimum, &maximum))
+ return NULL;
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return NULL;
+
+ ret = gimp_enum_store_new_with_range(enum_type, minimum, maximum);
+
+ /* pygobject_new handles NULL checking */
+ return pygobject_new((GObject *)ret);
+}
+
+static PyObject *
+_wrap_gimp_zoom_model_zoom_step(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "zoom_type", "scale", NULL };
+ PyObject *py_zoom_type = NULL;
+ double scale, ret;
+ GimpZoomType zoom_type;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"Od:gimp_zoom_model_zoom_step", kwlist, &py_zoom_type, &scale))
+ return NULL;
+ if (pyg_enum_get_value(GIMP_TYPE_ZOOM_TYPE, py_zoom_type, (gpointer)&zoom_type))
+ return NULL;
+
+ ret = gimp_zoom_model_zoom_step(zoom_type, scale);
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+_wrap_gimp_pattern_select_destroy(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "pattern_callback", NULL };
+ char *pattern_callback;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:gimp_pattern_select_destroy", kwlist, &pattern_callback))
+ return NULL;
+
+ gimp_pattern_select_destroy(pattern_callback);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+const PyMethodDef gimpui_functions[] = {
+ { "gimp_ui_init", (PyCFunction)_wrap_gimp_ui_init, METH_NOARGS,
+ NULL },
+ { "gimp_enum_combo_box_new_with_model", (PyCFunction)_wrap_gimp_enum_combo_box_new_with_model, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "gimp_enum_store_new_with_range", (PyCFunction)_wrap_gimp_enum_store_new_with_range, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "gimp_zoom_model_zoom_step", (PyCFunction)_wrap_gimp_zoom_model_zoom_step, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { "gimp_pattern_select_destroy", (PyCFunction)_wrap_gimp_pattern_select_destroy, METH_VARARGS|METH_KEYWORDS,
+ NULL },
+ { NULL, NULL, 0, NULL }
+};
+
+
+/* ----------- enums and flags ----------- */
+
+void
+gimpui_add_constants(PyObject *module, const gchar *strip_prefix)
+{
+#ifdef VERSION
+ PyModule_AddStringConstant(module, "__version__", VERSION);
+#endif
+ pyg_enum_add(module, "AspectType", strip_prefix, GIMP_TYPE_ASPECT_TYPE);
+ pyg_enum_add(module, "ChainPosition", strip_prefix, GIMP_TYPE_CHAIN_POSITION);
+ pyg_enum_add(module, "ColorAreaType", strip_prefix, GIMP_TYPE_COLOR_AREA_TYPE);
+ pyg_enum_add(module, "ColorSelectorChannel", strip_prefix, GIMP_TYPE_COLOR_SELECTOR_CHANNEL);
+ pyg_enum_add(module, "PageSelectorTarget", strip_prefix, GIMP_TYPE_PAGE_SELECTOR_TARGET);
+ pyg_enum_add(module, "SizeEntryUpdatePolicy", strip_prefix, GIMP_TYPE_SIZE_ENTRY_UPDATE_POLICY);
+ PyModule_AddIntConstant(module, (char *) pyg_constant_strip_prefix("GIMP_UNIT_PIXEL", strip_prefix), GIMP_UNIT_PIXEL);
+ PyModule_AddIntConstant(module, (char *) pyg_constant_strip_prefix("GIMP_UNIT_INCH", strip_prefix), GIMP_UNIT_INCH);
+ PyModule_AddIntConstant(module, (char *) pyg_constant_strip_prefix("GIMP_UNIT_MM", strip_prefix), GIMP_UNIT_MM);
+ PyModule_AddIntConstant(module, (char *) pyg_constant_strip_prefix("GIMP_UNIT_POINT", strip_prefix), GIMP_UNIT_POINT);
+ PyModule_AddIntConstant(module, (char *) pyg_constant_strip_prefix("GIMP_UNIT_PICA", strip_prefix), GIMP_UNIT_PICA);
+ pyg_enum_add(module, "ZoomType", strip_prefix, GIMP_TYPE_ZOOM_TYPE);
+
+ if (PyErr_Occurred())
+ PyErr_Print();
+}
+
+/* initialise stuff extension classes */
+void
+gimpui_register_classes(PyObject *d)
+{
+ PyObject *module;
+
+ if ((module = PyImport_ImportModule("gobject")) != NULL) {
+ _PyGObject_Type = (PyTypeObject *)PyObject_GetAttrString(module, "GObject");
+ if (_PyGObject_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name GObject from gobject");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gobject");
+ return ;
+ }
+ if ((module = PyImport_ImportModule("gtk")) != NULL) {
+ _PyGtkObject_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Object");
+ if (_PyGtkObject_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Object from gtk");
+ return ;
+ }
+ _PyGtkWidget_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Widget");
+ if (_PyGtkWidget_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Widget from gtk");
+ return ;
+ }
+ _PyGtkDialog_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Dialog");
+ if (_PyGtkDialog_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Dialog from gtk");
+ return ;
+ }
+ _PyGtkWindow_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Window");
+ if (_PyGtkWindow_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Window from gtk");
+ return ;
+ }
+ _PyGtkLabel_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Label");
+ if (_PyGtkLabel_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Label from gtk");
+ return ;
+ }
+ _PyGtkButton_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Button");
+ if (_PyGtkButton_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Button from gtk");
+ return ;
+ }
+ _PyGtkToggleButton_Type = (PyTypeObject *)PyObject_GetAttrString(module, "ToggleButton");
+ if (_PyGtkToggleButton_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name ToggleButton from gtk");
+ return ;
+ }
+ _PyGtkRadioButton_Type = (PyTypeObject *)PyObject_GetAttrString(module, "RadioButton");
+ if (_PyGtkRadioButton_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name RadioButton from gtk");
+ return ;
+ }
+ _PyGtkSpinButton_Type = (PyTypeObject *)PyObject_GetAttrString(module, "SpinButton");
+ if (_PyGtkSpinButton_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name SpinButton from gtk");
+ return ;
+ }
+ _PyGtkEntry_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Entry");
+ if (_PyGtkEntry_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Entry from gtk");
+ return ;
+ }
+ _PyGtkDrawingArea_Type = (PyTypeObject *)PyObject_GetAttrString(module, "DrawingArea");
+ if (_PyGtkDrawingArea_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name DrawingArea from gtk");
+ return ;
+ }
+ _PyGtkTable_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Table");
+ if (_PyGtkTable_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Table from gtk");
+ return ;
+ }
+ _PyGtkFrame_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Frame");
+ if (_PyGtkFrame_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Frame from gtk");
+ return ;
+ }
+ _PyGtkHBox_Type = (PyTypeObject *)PyObject_GetAttrString(module, "HBox");
+ if (_PyGtkHBox_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name HBox from gtk");
+ return ;
+ }
+ _PyGtkVBox_Type = (PyTypeObject *)PyObject_GetAttrString(module, "VBox");
+ if (_PyGtkVBox_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name VBox from gtk");
+ return ;
+ }
+ _PyGtkHPaned_Type = (PyTypeObject *)PyObject_GetAttrString(module, "HPaned");
+ if (_PyGtkHPaned_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name HPaned from gtk");
+ return ;
+ }
+ _PyGtkVPaned_Type = (PyTypeObject *)PyObject_GetAttrString(module, "VPaned");
+ if (_PyGtkVPaned_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name VPaned from gtk");
+ return ;
+ }
+ _PyGtkScale_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Scale");
+ if (_PyGtkScale_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Scale from gtk");
+ return ;
+ }
+ _PyGtkProgressBar_Type = (PyTypeObject *)PyObject_GetAttrString(module, "ProgressBar");
+ if (_PyGtkProgressBar_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name ProgressBar from gtk");
+ return ;
+ }
+ _PyGtkOptionMenu_Type = (PyTypeObject *)PyObject_GetAttrString(module, "OptionMenu");
+ if (_PyGtkOptionMenu_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name OptionMenu from gtk");
+ return ;
+ }
+ _PyGtkComboBox_Type = (PyTypeObject *)PyObject_GetAttrString(module, "ComboBox");
+ if (_PyGtkComboBox_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name ComboBox from gtk");
+ return ;
+ }
+ _PyGtkListStore_Type = (PyTypeObject *)PyObject_GetAttrString(module, "ListStore");
+ if (_PyGtkListStore_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name ListStore from gtk");
+ return ;
+ }
+ _PyGtkTreeModel_Type = (PyTypeObject *)PyObject_GetAttrString(module, "TreeModel");
+ if (_PyGtkTreeModel_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name TreeModel from gtk");
+ return ;
+ }
+ _PyGtkCellRenderer_Type = (PyTypeObject *)PyObject_GetAttrString(module, "CellRenderer");
+ if (_PyGtkCellRenderer_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name CellRenderer from gtk");
+ return ;
+ }
+ _PyGtkCellRendererToggle_Type = (PyTypeObject *)PyObject_GetAttrString(module, "CellRendererToggle");
+ if (_PyGtkCellRendererToggle_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name CellRendererToggle from gtk");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gtk");
+ return ;
+ }
+ if ((module = PyImport_ImportModule("gtk.gdk")) != NULL) {
+ _PyGdkPixbuf_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Pixbuf");
+ if (_PyGdkPixbuf_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Pixbuf from gtk.gdk");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gtk.gdk");
+ return ;
+ }
+ if ((module = PyImport_ImportModule("gimp")) != NULL) {
+ _PyGimpParasite_Type = (PyTypeObject *)PyObject_GetAttrString(module, "Parasite");
+ if (_PyGimpParasite_Type == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "cannot import name Parasite from gimp");
+ return ;
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError,
+ "could not import gimp");
+ return ;
+ }
+
+
+#line 9289 "gimpui.c"
+ pyg_register_interface(d, "ColorManaged", GIMP_TYPE_COLOR_MANAGED, &PyGimpColorManaged_Type);
+ pygobject_register_class(d, "GimpBrowser", GIMP_TYPE_BROWSER, &PyGimpBrowser_Type, Py_BuildValue("(O)", &PyGtkHPaned_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_BROWSER);
+ pygobject_register_class(d, "GimpButton", GIMP_TYPE_BUTTON, &PyGimpButton_Type, Py_BuildValue("(O)", &PyGtkButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_BUTTON);
+ pygobject_register_class(d, "GimpCellRendererColor", GIMP_TYPE_CELL_RENDERER_COLOR, &PyGimpCellRendererColor_Type, Py_BuildValue("(O)", &PyGtkCellRenderer_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_CELL_RENDERER_COLOR);
+ pygobject_register_class(d, "GimpCellRendererToggle", GIMP_TYPE_CELL_RENDERER_TOGGLE, &PyGimpCellRendererToggle_Type, Py_BuildValue("(O)", &PyGtkCellRendererToggle_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_CELL_RENDERER_TOGGLE);
+ pygobject_register_class(d, "GimpChainButton", GIMP_TYPE_CHAIN_BUTTON, &PyGimpChainButton_Type, Py_BuildValue("(O)", &PyGtkTable_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_CHAIN_BUTTON);
+ pygobject_register_class(d, "GimpColorArea", GIMP_TYPE_COLOR_AREA, &PyGimpColorArea_Type, Py_BuildValue("(O)", &PyGtkDrawingArea_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_AREA);
+ pygobject_register_class(d, "GimpColorButton", GIMP_TYPE_COLOR_BUTTON, &PyGimpColorButton_Type, Py_BuildValue("(O)", &PyGimpButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_BUTTON);
+ pygobject_register_class(d, "GimpColorConfig", GIMP_TYPE_COLOR_CONFIG, &PyGimpColorConfig_Type, Py_BuildValue("(O)", &PyGObject_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_CONFIG);
+ pygobject_register_class(d, "GimpColorDisplay", GIMP_TYPE_COLOR_DISPLAY, &PyGimpColorDisplay_Type, Py_BuildValue("(O)", &PyGObject_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_DISPLAY);
+ pygobject_register_class(d, "GimpColorDisplayStack", GIMP_TYPE_COLOR_DISPLAY_STACK, &PyGimpColorDisplayStack_Type, Py_BuildValue("(O)", &PyGObject_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_DISPLAY_STACK);
+ pygobject_register_class(d, "GimpColorHexEntry", GIMP_TYPE_COLOR_HEX_ENTRY, &PyGimpColorHexEntry_Type, Py_BuildValue("(O)", &PyGtkEntry_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_HEX_ENTRY);
+ pygobject_register_class(d, "GimpColorProfileComboBox", GIMP_TYPE_COLOR_PROFILE_COMBO_BOX, &PyGimpColorProfileComboBox_Type, Py_BuildValue("(O)", &PyGtkComboBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_PROFILE_COMBO_BOX);
+ pygobject_register_class(d, "GimpColorProfileStore", GIMP_TYPE_COLOR_PROFILE_STORE, &PyGimpColorProfileStore_Type, Py_BuildValue("(O)", &PyGtkListStore_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_PROFILE_STORE);
+ pygobject_register_class(d, "GimpColorScale", GIMP_TYPE_COLOR_SCALE, &PyGimpColorScale_Type, Py_BuildValue("(O)", &PyGtkScale_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_SCALE);
+ pygobject_register_class(d, "GimpColorSelection", GIMP_TYPE_COLOR_SELECTION, &PyGimpColorSelection_Type, Py_BuildValue("(O)", &PyGtkVBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_COLOR_SELECTION);
+ pygobject_register_class(d, "GimpColorSelector", GIMP_TYPE_COLOR_SELECTOR, &PyGimpColorSelector_Type, Py_BuildValue("(O)", &PyGtkVBox_Type));
+ pygobject_register_class(d, "GimpColorScales", GIMP_TYPE_COLOR_SCALES, &PyGimpColorScales_Type, Py_BuildValue("(O)", &PyGimpColorSelector_Type));
+ pygobject_register_class(d, "GimpColorNotebook", GIMP_TYPE_COLOR_NOTEBOOK, &PyGimpColorNotebook_Type, Py_BuildValue("(O)", &PyGimpColorSelector_Type));
+ pygobject_register_class(d, "GimpDialog", GIMP_TYPE_DIALOG, &PyGimpDialog_Type, Py_BuildValue("(O)", &PyGtkDialog_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_DIALOG);
+ pygobject_register_class(d, "GimpEnumLabel", GIMP_TYPE_ENUM_LABEL, &PyGimpEnumLabel_Type, Py_BuildValue("(O)", &PyGtkLabel_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_ENUM_LABEL);
+ pygobject_register_class(d, "GimpFrame", GIMP_TYPE_FRAME, &PyGimpFrame_Type, Py_BuildValue("(O)", &PyGtkFrame_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_FRAME);
+ pygobject_register_class(d, "GimpHintBox", GIMP_TYPE_HINT_BOX, &PyGimpHintBox_Type, Py_BuildValue("(O)", &PyGtkHBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_HINT_BOX);
+ pygobject_register_class(d, "GimpIntComboBox", GIMP_TYPE_INT_COMBO_BOX, &PyGimpIntComboBox_Type, Py_BuildValue("(O)", &PyGtkComboBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_INT_COMBO_BOX);
+ pygobject_register_class(d, "GimpImageComboBox", GIMP_TYPE_IMAGE_COMBO_BOX, &PyGimpImageComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpEnumComboBox", GIMP_TYPE_ENUM_COMBO_BOX, &PyGimpEnumComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpDrawableComboBox", GIMP_TYPE_DRAWABLE_COMBO_BOX, &PyGimpDrawableComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpChannelComboBox", GIMP_TYPE_CHANNEL_COMBO_BOX, &PyGimpChannelComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpIntStore", GIMP_TYPE_INT_STORE, &PyGimpIntStore_Type, Py_BuildValue("(O)", &PyGtkListStore_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_INT_STORE);
+ pygobject_register_class(d, "GimpEnumStore", GIMP_TYPE_ENUM_STORE, &PyGimpEnumStore_Type, Py_BuildValue("(O)", &PyGimpIntStore_Type));
+ pygobject_register_class(d, "GimpLayerComboBox", GIMP_TYPE_LAYER_COMBO_BOX, &PyGimpLayerComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpMemsizeEntry", GIMP_TYPE_MEMSIZE_ENTRY, &PyGimpMemsizeEntry_Type, Py_BuildValue("(O)", &PyGtkHBox_Type));
+ pygobject_register_class(d, "GimpNumberPairEntry", GIMP_TYPE_NUMBER_PAIR_ENTRY, &PyGimpNumberPairEntry_Type, Py_BuildValue("(O)", &PyGtkEntry_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_NUMBER_PAIR_ENTRY);
+ pygobject_register_class(d, "GimpOffsetArea", GIMP_TYPE_OFFSET_AREA, &PyGimpOffsetArea_Type, Py_BuildValue("(O)", &PyGtkDrawingArea_Type));
+ pygobject_register_class(d, "GimpPageSelector", GIMP_TYPE_PAGE_SELECTOR, &PyGimpPageSelector_Type, Py_BuildValue("(O)", &PyGtkVBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PAGE_SELECTOR);
+ pygobject_register_class(d, "GimpPathEditor", GIMP_TYPE_PATH_EDITOR, &PyGimpPathEditor_Type, Py_BuildValue("(O)", &PyGtkVBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PATH_EDITOR);
+ pygobject_register_class(d, "GimpPickButton", GIMP_TYPE_PICK_BUTTON, &PyGimpPickButton_Type, Py_BuildValue("(O)", &PyGtkButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PICK_BUTTON);
+ pygobject_register_class(d, "GimpPreview", GIMP_TYPE_PREVIEW, &PyGimpPreview_Type, Py_BuildValue("(O)", &PyGtkVBox_Type));
+ pygobject_register_class(d, "GimpAspectPreview", GIMP_TYPE_ASPECT_PREVIEW, &PyGimpAspectPreview_Type, Py_BuildValue("(O)", &PyGimpPreview_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_ASPECT_PREVIEW);
+ pygobject_register_class(d, "GimpPreviewArea", GIMP_TYPE_PREVIEW_AREA, &PyGimpPreviewArea_Type, Py_BuildValue("(O)", &PyGtkDrawingArea_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PREVIEW_AREA);
+ pygobject_register_class(d, "GimpProcBrowserDialog", GIMP_TYPE_PROC_BROWSER_DIALOG, &PyGimpProcBrowserDialog_Type, Py_BuildValue("(O)", &PyGimpDialog_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PROC_BROWSER_DIALOG);
+ pygobject_register_class(d, "GimpProgressBar", GIMP_TYPE_PROGRESS_BAR, &PyGimpProgressBar_Type, Py_BuildValue("(O)", &PyGtkProgressBar_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PROGRESS_BAR);
+ pygobject_register_class(d, "GimpScrolledPreview", GIMP_TYPE_SCROLLED_PREVIEW, &PyGimpScrolledPreview_Type, Py_BuildValue("(O)", &PyGimpPreview_Type));
+ pygobject_register_class(d, "GimpDrawablePreview", GIMP_TYPE_DRAWABLE_PREVIEW, &PyGimpDrawablePreview_Type, Py_BuildValue("(O)", &PyGimpScrolledPreview_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_DRAWABLE_PREVIEW);
+ pygobject_register_class(d, "GimpSelectButton", GIMP_TYPE_SELECT_BUTTON, &PyGimpSelectButton_Type, Py_BuildValue("(O)", &PyGtkHBox_Type));
+ pygobject_register_class(d, "GimpPatternSelectButton", GIMP_TYPE_PATTERN_SELECT_BUTTON, &PyGimpPatternSelectButton_Type, Py_BuildValue("(O)", &PyGimpSelectButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PATTERN_SELECT_BUTTON);
+ pygobject_register_class(d, "GimpPaletteSelectButton", GIMP_TYPE_PALETTE_SELECT_BUTTON, &PyGimpPaletteSelectButton_Type, Py_BuildValue("(O)", &PyGimpSelectButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_PALETTE_SELECT_BUTTON);
+ pygobject_register_class(d, "GimpGradientSelectButton", GIMP_TYPE_GRADIENT_SELECT_BUTTON, &PyGimpGradientSelectButton_Type, Py_BuildValue("(O)", &PyGimpSelectButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_GRADIENT_SELECT_BUTTON);
+ pygobject_register_class(d, "GimpFontSelectButton", GIMP_TYPE_FONT_SELECT_BUTTON, &PyGimpFontSelectButton_Type, Py_BuildValue("(O)", &PyGimpSelectButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_FONT_SELECT_BUTTON);
+ pygobject_register_class(d, "GimpBrushSelectButton", GIMP_TYPE_BRUSH_SELECT_BUTTON, &PyGimpBrushSelectButton_Type, Py_BuildValue("(O)", &PyGimpSelectButton_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_BRUSH_SELECT_BUTTON);
+ pygobject_register_class(d, "GimpRuler", GIMP_TYPE_RULER, &PyGimpRuler_Type, Py_BuildValue("(O)", &PyGtkWidget_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_RULER);
+ pygobject_register_class(d, "GimpSizeEntry", GIMP_TYPE_SIZE_ENTRY, &PyGimpSizeEntry_Type, Py_BuildValue("(O)", &PyGtkTable_Type));
+ pygobject_register_class(d, "GimpStringComboBox", GIMP_TYPE_STRING_COMBO_BOX, &PyGimpStringComboBox_Type, Py_BuildValue("(O)", &PyGtkComboBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_STRING_COMBO_BOX);
+ pygobject_register_class(d, "GimpUnitComboBox", GIMP_TYPE_UNIT_COMBO_BOX, &PyGimpUnitComboBox_Type, Py_BuildValue("(O)", &PyGtkComboBox_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_UNIT_COMBO_BOX);
+ pygobject_register_class(d, "GimpUnitMenu", GIMP_TYPE_UNIT_MENU, &PyGimpUnitMenu_Type, Py_BuildValue("(O)", &PyGtkOptionMenu_Type));
+ pygobject_register_class(d, "GimpVectorsComboBox", GIMP_TYPE_VECTORS_COMBO_BOX, &PyGimpVectorsComboBox_Type, Py_BuildValue("(O)", &PyGimpIntComboBox_Type));
+ pygobject_register_class(d, "GimpZoomModel", GIMP_TYPE_ZOOM_MODEL, &PyGimpZoomModel_Type, Py_BuildValue("(O)", &PyGObject_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_ZOOM_MODEL);
+ pygobject_register_class(d, "GimpZoomPreview", GIMP_TYPE_ZOOM_PREVIEW, &PyGimpZoomPreview_Type, Py_BuildValue("(O)", &PyGimpScrolledPreview_Type));
+ pyg_set_object_has_new_constructor(GIMP_TYPE_ZOOM_PREVIEW);
+}
diff --git a/plug-ins/pygimp/gimpui.defs b/plug-ins/pygimp/gimpui.defs
new file mode 100644
index 0000000..1540534
--- /dev/null
+++ b/plug-ins/pygimp/gimpui.defs
@@ -0,0 +1,3411 @@
+;; -*- scheme -*-
+; object definitions ...
+(define-object Browser
+ (in-module "Gimp")
+ (parent "GtkHPaned")
+ (c-name "GimpBrowser")
+ (gtype-id "GIMP_TYPE_BROWSER")
+)
+
+(define-object Button
+ (in-module "Gimp")
+ (parent "GtkButton")
+ (c-name "GimpButton")
+ (gtype-id "GIMP_TYPE_BUTTON")
+)
+
+(define-object CellRendererColor
+ (in-module "Gimp")
+ (parent "GtkCellRenderer")
+ (c-name "GimpCellRendererColor")
+ (gtype-id "GIMP_TYPE_CELL_RENDERER_COLOR")
+)
+
+(define-object CellRendererToggle
+ (in-module "Gimp")
+ (parent "GtkCellRendererToggle")
+ (c-name "GimpCellRendererToggle")
+ (gtype-id "GIMP_TYPE_CELL_RENDERER_TOGGLE")
+)
+
+(define-object ChainButton
+ (in-module "Gimp")
+ (parent "GtkTable")
+ (c-name "GimpChainButton")
+ (gtype-id "GIMP_TYPE_CHAIN_BUTTON")
+)
+
+(define-object ColorArea
+ (in-module "Gimp")
+ (parent "GtkDrawingArea")
+ (c-name "GimpColorArea")
+ (gtype-id "GIMP_TYPE_COLOR_AREA")
+)
+
+(define-object ColorButton
+ (in-module "Gimp")
+ (parent "GimpButton")
+ (c-name "GimpColorButton")
+ (gtype-id "GIMP_TYPE_COLOR_BUTTON")
+)
+
+(define-object ColorConfig
+ (in-module "Gimp")
+ (parent "GObject")
+ (c-name "GimpColorConfig")
+ (gtype-id "GIMP_TYPE_COLOR_CONFIG")
+)
+
+(define-object ColorDisplay
+ (in-module "Gimp")
+ (parent "GObject")
+ (c-name "GimpColorDisplay")
+ (gtype-id "GIMP_TYPE_COLOR_DISPLAY")
+)
+
+(define-object ColorDisplayStack
+ (in-module "Gimp")
+ (parent "GObject")
+ (c-name "GimpColorDisplayStack")
+ (gtype-id "GIMP_TYPE_COLOR_DISPLAY_STACK")
+)
+
+(define-object ColorHexEntry
+ (in-module "Gimp")
+ (parent "GtkEntry")
+ (c-name "GimpColorHexEntry")
+ (gtype-id "GIMP_TYPE_COLOR_HEX_ENTRY")
+)
+
+(define-object ColorProfileComboBox
+ (in-module "Gimp")
+ (parent "GtkComboBox")
+ (c-name "GimpColorProfileComboBox")
+ (gtype-id "GIMP_TYPE_COLOR_PROFILE_COMBO_BOX")
+)
+
+(define-object ColorProfileStore
+ (in-module "Gimp")
+ (parent "GtkListStore")
+ (c-name "GimpColorProfileStore")
+ (gtype-id "GIMP_TYPE_COLOR_PROFILE_STORE")
+)
+
+(define-object ColorScale
+ (in-module "Gimp")
+ (parent "GtkScale")
+ (c-name "GimpColorScale")
+ (gtype-id "GIMP_TYPE_COLOR_SCALE")
+)
+
+(define-object ColorScales
+ (in-module "Gimp")
+ (parent "GimpColorSelector")
+ (c-name "GimpColorScales")
+ (gtype-id "GIMP_TYPE_COLOR_SCALES")
+)
+
+(define-object ColorSelection
+ (in-module "Gimp")
+ (parent "GtkVBox")
+ (c-name "GimpColorSelection")
+ (gtype-id "GIMP_TYPE_COLOR_SELECTION")
+)
+
+(define-object ColorSelector
+ (in-module "Gimp")
+ (parent "GtkVBox")
+ (c-name "GimpColorSelector")
+ (gtype-id "GIMP_TYPE_COLOR_SELECTOR")
+)
+
+(define-object ColorNotebook
+ (in-module "Gimp")
+ (parent "GimpColorSelector")
+ (c-name "GimpColorNotebook")
+ (gtype-id "GIMP_TYPE_COLOR_NOTEBOOK")
+)
+
+(define-object Dialog
+ (in-module "Gimp")
+ (parent "GtkDialog")
+ (c-name "GimpDialog")
+ (gtype-id "GIMP_TYPE_DIALOG")
+)
+
+(define-object EnumLabel
+ (in-module "Gimp")
+ (parent "GtkLabel")
+ (c-name "GimpEnumLabel")
+ (gtype-id "GIMP_TYPE_ENUM_LABEL")
+)
+
+(define-object Frame
+ (in-module "Gimp")
+ (parent "GtkFrame")
+ (c-name "GimpFrame")
+ (gtype-id "GIMP_TYPE_FRAME")
+)
+
+(define-object HintBox
+ (in-module "Gimp")
+ (parent "GtkHBox")
+ (c-name "GimpHintBox")
+ (gtype-id "GIMP_TYPE_HINT_BOX")
+)
+
+(define-object IntComboBox
+ (in-module "Gimp")
+ (parent "GtkComboBox")
+ (c-name "GimpIntComboBox")
+ (gtype-id "GIMP_TYPE_INT_COMBO_BOX")
+)
+
+(define-object ImageComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpImageComboBox")
+ (gtype-id "GIMP_TYPE_IMAGE_COMBO_BOX")
+)
+
+(define-object EnumComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpEnumComboBox")
+ (gtype-id "GIMP_TYPE_ENUM_COMBO_BOX")
+)
+
+(define-object DrawableComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpDrawableComboBox")
+ (gtype-id "GIMP_TYPE_DRAWABLE_COMBO_BOX")
+)
+
+(define-object ChannelComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpChannelComboBox")
+ (gtype-id "GIMP_TYPE_CHANNEL_COMBO_BOX")
+)
+
+(define-object IntStore
+ (in-module "Gimp")
+ (parent "GtkListStore")
+ (c-name "GimpIntStore")
+ (gtype-id "GIMP_TYPE_INT_STORE")
+)
+
+(define-object EnumStore
+ (in-module "Gimp")
+ (parent "GimpIntStore")
+ (c-name "GimpEnumStore")
+ (gtype-id "GIMP_TYPE_ENUM_STORE")
+)
+
+(define-object LayerComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpLayerComboBox")
+ (gtype-id "GIMP_TYPE_LAYER_COMBO_BOX")
+)
+
+(define-object MemsizeEntry
+ (in-module "Gimp")
+ (parent "GtkHBox")
+ (c-name "GimpMemsizeEntry")
+ (gtype-id "GIMP_TYPE_MEMSIZE_ENTRY")
+)
+
+(define-object NumberPairEntry
+ (in-module "Gimp")
+ (parent "GtkEntry")
+ (c-name "GimpNumberPairEntry")
+ (gtype-id "GIMP_TYPE_NUMBER_PAIR_ENTRY")
+)
+
+(define-object OffsetArea
+ (in-module "Gimp")
+ (parent "GtkDrawingArea")
+ (c-name "GimpOffsetArea")
+ (gtype-id "GIMP_TYPE_OFFSET_AREA")
+)
+
+(define-object PageSelector
+ (in-module "Gimp")
+ (parent "GtkVBox")
+ (c-name "GimpPageSelector")
+ (gtype-id "GIMP_TYPE_PAGE_SELECTOR")
+)
+
+(define-object PathEditor
+ (in-module "Gimp")
+ (parent "GtkVBox")
+ (c-name "GimpPathEditor")
+ (gtype-id "GIMP_TYPE_PATH_EDITOR")
+)
+
+(define-object PickButton
+ (in-module "Gimp")
+ (parent "GtkButton")
+ (c-name "GimpPickButton")
+ (gtype-id "GIMP_TYPE_PICK_BUTTON")
+)
+
+(define-object Preview
+ (in-module "Gimp")
+ (parent "GtkVBox")
+ (c-name "GimpPreview")
+ (gtype-id "GIMP_TYPE_PREVIEW")
+)
+
+(define-object AspectPreview
+ (in-module "Gimp")
+ (parent "GimpPreview")
+ (c-name "GimpAspectPreview")
+ (gtype-id "GIMP_TYPE_ASPECT_PREVIEW")
+)
+
+(define-object PreviewArea
+ (in-module "Gimp")
+ (parent "GtkDrawingArea")
+ (c-name "GimpPreviewArea")
+ (gtype-id "GIMP_TYPE_PREVIEW_AREA")
+)
+
+(define-object ProcBrowserDialog
+ (in-module "Gimp")
+ (parent "GimpDialog")
+ (c-name "GimpProcBrowserDialog")
+ (gtype-id "GIMP_TYPE_PROC_BROWSER_DIALOG")
+)
+
+(define-object ProgressBar
+ (in-module "Gimp")
+ (parent "GtkProgressBar")
+ (c-name "GimpProgressBar")
+ (gtype-id "GIMP_TYPE_PROGRESS_BAR")
+)
+
+(define-object ScrolledPreview
+ (in-module "Gimp")
+ (parent "GimpPreview")
+ (c-name "GimpScrolledPreview")
+ (gtype-id "GIMP_TYPE_SCROLLED_PREVIEW")
+)
+
+(define-object DrawablePreview
+ (in-module "Gimp")
+ (parent "GimpScrolledPreview")
+ (c-name "GimpDrawablePreview")
+ (gtype-id "GIMP_TYPE_DRAWABLE_PREVIEW")
+)
+
+(define-object SelectButton
+ (in-module "Gimp")
+ (parent "GtkHBox")
+ (c-name "GimpSelectButton")
+ (gtype-id "GIMP_TYPE_SELECT_BUTTON")
+)
+
+(define-object PatternSelectButton
+ (in-module "Gimp")
+ (parent "GimpSelectButton")
+ (c-name "GimpPatternSelectButton")
+ (gtype-id "GIMP_TYPE_PATTERN_SELECT_BUTTON")
+)
+
+(define-object PaletteSelectButton
+ (in-module "Gimp")
+ (parent "GimpSelectButton")
+ (c-name "GimpPaletteSelectButton")
+ (gtype-id "GIMP_TYPE_PALETTE_SELECT_BUTTON")
+)
+
+(define-object GradientSelectButton
+ (in-module "Gimp")
+ (parent "GimpSelectButton")
+ (c-name "GimpGradientSelectButton")
+ (gtype-id "GIMP_TYPE_GRADIENT_SELECT_BUTTON")
+)
+
+(define-object FontSelectButton
+ (in-module "Gimp")
+ (parent "GimpSelectButton")
+ (c-name "GimpFontSelectButton")
+ (gtype-id "GIMP_TYPE_FONT_SELECT_BUTTON")
+)
+
+(define-object BrushSelectButton
+ (in-module "Gimp")
+ (parent "GimpSelectButton")
+ (c-name "GimpBrushSelectButton")
+ (gtype-id "GIMP_TYPE_BRUSH_SELECT_BUTTON")
+)
+
+(define-object Ruler
+ (in-module "Gimp")
+ (parent "GtkWidget")
+ (c-name "GimpRuler")
+ (gtype-id "GIMP_TYPE_RULER")
+)
+
+(define-object SizeEntry
+ (in-module "Gimp")
+ (parent "GtkTable")
+ (c-name "GimpSizeEntry")
+ (gtype-id "GIMP_TYPE_SIZE_ENTRY")
+)
+
+(define-object StringComboBox
+ (in-module "Gimp")
+ (parent "GtkComboBox")
+ (c-name "GimpStringComboBox")
+ (gtype-id "GIMP_TYPE_STRING_COMBO_BOX")
+)
+
+(define-object UnitComboBox
+ (in-module "Gimp")
+ (parent "GtkComboBox")
+ (c-name "GimpUnitComboBox")
+ (gtype-id "GIMP_TYPE_UNIT_COMBO_BOX")
+)
+
+(define-object UnitMenu
+ (in-module "Gimp")
+ (parent "GtkOptionMenu")
+ (c-name "GimpUnitMenu")
+ (gtype-id "GIMP_TYPE_UNIT_MENU")
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-object VectorsComboBox
+ (in-module "Gimp")
+ (parent "GimpIntComboBox")
+ (c-name "GimpVectorsComboBox")
+ (gtype-id "GIMP_TYPE_VECTORS_COMBO_BOX")
+)
+
+(define-object ZoomModel
+ (in-module "Gimp")
+ (parent "GObject")
+ (c-name "GimpZoomModel")
+ (gtype-id "GIMP_TYPE_ZOOM_MODEL")
+)
+
+(define-object ZoomPreview
+ (in-module "Gimp")
+ (parent "GimpScrolledPreview")
+ (c-name "GimpZoomPreview")
+ (gtype-id "GIMP_TYPE_ZOOM_PREVIEW")
+)
+
+;; Interfaces
+
+(define-interface ColorManaged
+ (in-module "Gimp")
+ (c-name "GimpColorManaged")
+ (gtype-id "GIMP_TYPE_COLOR_MANAGED")
+)
+
+;; Enumerations and flags ...
+
+(define-enum IntStoreColumns
+ (in-module "Gimp")
+ (c-name "GimpIntStoreColumns")
+ (gtype-id "GIMP_TYPE_INT_STORE_COLUMNS")
+ (values
+ '("value" "GIMP_INT_STORE_VALUE")
+ '("label" "GIMP_INT_STORE_LABEL")
+ '("stock-id" "GIMP_INT_STORE_STOCK_ID")
+ '("pixbuf" "GIMP_INT_STORE_PIXBUF")
+ '("user-data" "GIMP_INT_STORE_USER_DATA")
+ '("num-columns" "GIMP_INT_STORE_NUM_COLUMNS")
+ )
+)
+
+(define-enum AspectType
+ (in-module "Gimp")
+ (c-name "GimpAspectType")
+ (gtype-id "GIMP_TYPE_ASPECT_TYPE")
+ (values
+ '("square" "GIMP_ASPECT_SQUARE")
+ '("portrait" "GIMP_ASPECT_PORTRAIT")
+ '("landscape" "GIMP_ASPECT_LANDSCAPE")
+ )
+)
+
+(define-enum ChainPosition
+ (in-module "Gimp")
+ (c-name "GimpChainPosition")
+ (gtype-id "GIMP_TYPE_CHAIN_POSITION")
+ (values
+ '("top" "GIMP_CHAIN_TOP")
+ '("left" "GIMP_CHAIN_LEFT")
+ '("bottom" "GIMP_CHAIN_BOTTOM")
+ '("right" "GIMP_CHAIN_RIGHT")
+ )
+)
+
+(define-enum ColorAreaType
+ (in-module "Gimp")
+ (c-name "GimpColorAreaType")
+ (gtype-id "GIMP_TYPE_COLOR_AREA_TYPE")
+ (values
+ '("flat" "GIMP_COLOR_AREA_FLAT")
+ '("small-checks" "GIMP_COLOR_AREA_SMALL_CHECKS")
+ '("large-checks" "GIMP_COLOR_AREA_LARGE_CHECKS")
+ )
+)
+
+(define-enum ColorSelectorChannel
+ (in-module "Gimp")
+ (c-name "GimpColorSelectorChannel")
+ (gtype-id "GIMP_TYPE_COLOR_SELECTOR_CHANNEL")
+ (values
+ '("hue" "GIMP_COLOR_SELECTOR_HUE")
+ '("saturation" "GIMP_COLOR_SELECTOR_SATURATION")
+ '("value" "GIMP_COLOR_SELECTOR_VALUE")
+ '("red" "GIMP_COLOR_SELECTOR_RED")
+ '("green" "GIMP_COLOR_SELECTOR_GREEN")
+ '("blue" "GIMP_COLOR_SELECTOR_BLUE")
+ '("alpha" "GIMP_COLOR_SELECTOR_ALPHA")
+ )
+)
+
+(define-enum PageSelectorTarget
+ (in-module "Gimp")
+ (c-name "GimpPageSelectorTarget")
+ (gtype-id "GIMP_TYPE_PAGE_SELECTOR_TARGET")
+ (values
+ '("layers" "GIMP_PAGE_SELECTOR_TARGET_LAYERS")
+ '("images" "GIMP_PAGE_SELECTOR_TARGET_IMAGES")
+ )
+)
+
+(define-enum SizeEntryUpdatePolicy
+ (in-module "Gimp")
+ (c-name "GimpSizeEntryUpdatePolicy")
+ (gtype-id "GIMP_TYPE_SIZE_ENTRY_UPDATE_POLICY")
+ (values
+ '("none" "GIMP_SIZE_ENTRY_UPDATE_NONE")
+ '("size" "GIMP_SIZE_ENTRY_UPDATE_SIZE")
+ '("resolution" "GIMP_SIZE_ENTRY_UPDATE_RESOLUTION")
+ )
+)
+
+(define-enum Unit
+ (in-module "Gimp")
+ (c-name "GimpUnit")
+;; FIXME: make GimpUnit enum more binding-friendly -- gimp_unit_get_type()
+;; (gtype-id "GIMP_TYPE_UNIT")
+ (values
+ '("pixel" "GIMP_UNIT_PIXEL")
+ '("inch" "GIMP_UNIT_INCH")
+ '("mm" "GIMP_UNIT_MM")
+ '("point" "GIMP_UNIT_POINT")
+ '("pica" "GIMP_UNIT_PICA")
+ )
+)
+
+(define-enum ZoomType
+ (in-module "Gimp")
+ (c-name "GimpZoomType")
+ (gtype-id "GIMP_TYPE_ZOOM_TYPE")
+ (values
+ '("in" "GIMP_ZOOM_IN")
+ '("out" "GIMP_ZOOM_OUT")
+ '("in-more" "GIMP_ZOOM_IN_MORE")
+ '("out-more" "GIMP_ZOOM_OUT_MORE")
+ '("in-max" "GIMP_ZOOM_IN_MAX")
+ '("out-max" "GIMP_ZOOM_OUT_MAX")
+ '("to" "GIMP_ZOOM_TO")
+ )
+)
+
+
+;; From gimpbrowser.h
+
+(define-function gimp_browser_get_type
+ (c-name "gimp_browser_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_browser_new
+ (c-name "gimp_browser_new")
+ (is-constructor-of "GimpBrowser")
+ (return-type "GtkWidget*")
+)
+
+(define-method add_search_types
+ (of-object "GimpBrowser")
+ (c-name "gimp_browser_add_search_types")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "first_type_label")
+ '("gint" "first_type_id")
+ )
+ (varargs #t)
+)
+
+(define-method set_widget
+ (of-object "GimpBrowser")
+ (c-name "gimp_browser_set_widget")
+ (return-type "none")
+ (parameters
+ '("GtkWidget*" "widget")
+ )
+)
+
+(define-method show_message
+ (of-object "GimpBrowser")
+ (c-name "gimp_browser_show_message")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "message")
+ )
+)
+
+
+
+;; From gimpbutton.h
+
+(define-function gimp_button_get_type
+ (c-name "gimp_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_button_new
+ (c-name "gimp_button_new")
+ (is-constructor-of "GimpButton")
+ (return-type "GtkWidget*")
+)
+
+(define-method extended_clicked
+ (of-object "GimpButton")
+ (c-name "gimp_button_extended_clicked")
+ (return-type "none")
+ (parameters
+ '("GdkModifierType" "state")
+ )
+)
+
+
+
+;; From gimpcellrenderercolor.h
+
+(define-function gimp_cell_renderer_color_get_type
+ (c-name "gimp_cell_renderer_color_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_cell_renderer_color_new
+ (c-name "gimp_cell_renderer_color_new")
+ (is-constructor-of "GimpCellRendererColor")
+ (return-type "GtkCellRenderer*")
+)
+
+
+
+;; From gimpcellrenderertoggle.h
+
+(define-function gimp_cell_renderer_toggle_get_type
+ (c-name "gimp_cell_renderer_toggle_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_cell_renderer_toggle_new
+ (c-name "gimp_cell_renderer_toggle_new")
+ (is-constructor-of "GimpCellRendererToggle")
+ (return-type "GtkCellRenderer*")
+ (properties
+ '("stock-id" (argname "stock_id") (optional))
+ )
+)
+
+(define-method clicked
+ (of-object "GimpCellRendererToggle")
+ (c-name "gimp_cell_renderer_toggle_clicked")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "path")
+ '("GdkModifierType" "state")
+ )
+)
+
+
+
+;; From gimpchainbutton.h
+
+(define-function gimp_chain_button_get_type
+ (c-name "gimp_chain_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_chain_button_new
+ (c-name "gimp_chain_button_new")
+ (is-constructor-of "GimpChainButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("position")
+ )
+)
+
+(define-method set_active
+ (of-object "GimpChainButton")
+ (c-name "gimp_chain_button_set_active")
+ (return-type "none")
+ (parameters
+ '("gboolean" "active")
+ )
+)
+
+(define-method get_active
+ (of-object "GimpChainButton")
+ (c-name "gimp_chain_button_get_active")
+ (return-type "gboolean")
+)
+
+
+
+;; From gimpcolorarea.h
+
+(define-function gimp_color_area_get_type
+ (c-name "gimp_color_area_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_area_new
+ (c-name "gimp_color_area_new")
+ (is-constructor-of "GimpColorArea")
+ (return-type "GtkWidget*")
+ (properties
+ '("color" (optional))
+ '("type" (optional))
+ '("drag-mask" (argname "drag_mask") (optional))
+ )
+)
+
+(define-method set_color
+ (of-object "GimpColorArea")
+ (c-name "gimp_color_area_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "color")
+ )
+)
+
+(define-method get_color
+ (of-object "GimpColorArea")
+ (c-name "gimp_color_area_get_color")
+ (return-type "none")
+ (parameters
+ '("GimpRGB*" "color")
+ )
+)
+
+(define-method has_alpha
+ (of-object "GimpColorArea")
+ (c-name "gimp_color_area_has_alpha")
+ (return-type "gboolean")
+)
+
+(define-method set_type
+ (of-object "GimpColorArea")
+ (c-name "gimp_color_area_set_type")
+ (return-type "none")
+ (parameters
+ '("GimpColorAreaType" "type")
+ )
+)
+
+(define-method set_draw_border
+ (of-object "GimpColorArea")
+ (c-name "gimp_color_area_set_draw_border")
+ (return-type "none")
+ (parameters
+ '("gboolean" "draw_border")
+ )
+)
+
+
+
+;; From gimpcolorbutton.h
+
+(define-function gimp_color_button_get_type
+ (c-name "gimp_color_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_button_new
+ (c-name "gimp_color_button_new")
+ (is-constructor-of "GimpColorButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("width" (optional))
+ '("height" (optional))
+ '("color" (optional))
+ '("type" (optional))
+ )
+)
+
+(define-method set_color
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "color")
+ )
+)
+
+(define-method get_color
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_get_color")
+ (return-type "none")
+ (parameters
+ '("GimpRGB*" "color")
+ )
+)
+
+(define-method has_alpha
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_has_alpha")
+ (return-type "gboolean")
+)
+
+(define-method set_type
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_set_type")
+ (return-type "none")
+ (parameters
+ '("GimpColorAreaType" "type")
+ )
+)
+
+(define-method get_update
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_get_update")
+ (return-type "gboolean")
+)
+
+(define-method set_update
+ (of-object "GimpColorButton")
+ (c-name "gimp_color_button_set_update")
+ (return-type "none")
+ (parameters
+ '("gboolean" "continuous")
+ )
+)
+
+
+
+;; From gimpcolordisplay.h
+
+(define-function gimp_color_display_get_type
+ (c-name "gimp_color_display_get_type")
+ (return-type "GType")
+)
+
+(define-method clone
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_clone")
+ (return-type "GimpColorDisplay*")
+)
+
+(define-method convert_surface
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_convert_surface")
+ (return-type "none")
+ (parameters
+ '("cairo_surface_t*" "surface")
+ )
+)
+
+(define-method convert
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_convert")
+ (return-type "none")
+ (parameters
+ '("guchar*" "buf")
+ '("gint" "width")
+ '("gint" "height")
+ '("gint" "bpp")
+ '("gint" "bpl")
+ )
+ (deprecated "use convert_surface(cairo_surface_t*) instead")
+)
+
+(define-method load_state
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_load_state")
+ (return-type "none")
+ (parameters
+ '("GimpParasite*" "state")
+ )
+)
+
+(define-method save_state
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_save_state")
+ (return-type "GimpParasite*")
+)
+
+(define-method configure
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_configure")
+ (return-type "GtkWidget*")
+)
+
+(define-method configure_reset
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_configure_reset")
+ (return-type "none")
+)
+
+(define-method changed
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_changed")
+ (return-type "none")
+)
+
+(define-method set_enabled
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_set_enabled")
+ (return-type "none")
+ (parameters
+ '("gboolean" "enabled")
+ )
+)
+
+(define-method get_enabled
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_get_enabled")
+ (return-type "gboolean")
+)
+
+(define-method get_config
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_get_config")
+ (return-type "GimpColorConfig*")
+)
+
+(define-method get_managed
+ (of-object "GimpColorDisplay")
+ (c-name "gimp_color_display_get_managed")
+ (return-type "GimpColorManaged*")
+)
+
+
+
+;; From gimpcolordisplaystack.h
+
+(define-function gimp_color_display_stack_get_type
+ (c-name "gimp_color_display_stack_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_display_stack_new
+ (c-name "gimp_color_display_stack_new")
+ (is-constructor-of "GimpColorDisplayStack")
+ (return-type "GimpColorDisplayStack*")
+)
+
+(define-method clone
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_clone")
+ (return-type "GimpColorDisplayStack*")
+)
+
+(define-method changed
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_changed")
+ (return-type "none")
+)
+
+(define-method add
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_add")
+ (return-type "none")
+ (parameters
+ '("GimpColorDisplay*" "display")
+ )
+)
+
+(define-method remove
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_remove")
+ (return-type "none")
+ (parameters
+ '("GimpColorDisplay*" "display")
+ )
+)
+
+(define-method reorder_up
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_reorder_up")
+ (return-type "none")
+ (parameters
+ '("GimpColorDisplay*" "display")
+ )
+)
+
+(define-method reorder_down
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_reorder_down")
+ (return-type "none")
+ (parameters
+ '("GimpColorDisplay*" "display")
+ )
+)
+
+(define-method convert_surface
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_convert_surface")
+ (return-type "none")
+ (parameters
+ '("cairo_surface_t*" "surface")
+ )
+)
+
+(define-method convert
+ (of-object "GimpColorDisplayStack")
+ (c-name "gimp_color_display_stack_convert")
+ (return-type "none")
+ (parameters
+ '("guchar*" "buf")
+ '("gint" "width")
+ '("gint" "height")
+ '("gint" "bpp")
+ '("gint" "bpl")
+ )
+ (deprecated "use convert_surface(cairo_surface_t*) instead")
+)
+
+
+
+;; From gimpcolorhexentry.h
+
+(define-function gimp_color_hex_entry_get_type
+ (c-name "gimp_color_hex_entry_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_hex_entry_new
+ (c-name "gimp_color_hex_entry_new")
+ (is-constructor-of "GimpColorHexEntry")
+ (return-type "GtkWidget*")
+)
+
+(define-method set_color
+ (of-object "GimpColorHexEntry")
+ (c-name "gimp_color_hex_entry_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "color")
+ )
+)
+
+(define-method get_color
+ (of-object "GimpColorHexEntry")
+ (c-name "gimp_color_hex_entry_get_color")
+ (return-type "none")
+ (parameters
+ '("GimpRGB*" "color")
+ )
+)
+
+
+
+;; From gimpcolornotebook.h
+
+(define-function gimp_color_notebook_get_type
+ (c-name "gimp_color_notebook_get_type")
+ (return-type "GType")
+)
+
+(define-method set_has_page
+ (of-object "GimpColorNotebook")
+ (c-name "gimp_color_notebook_set_has_page")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GType" "page_type")
+ '("gboolean" "has_page")
+ )
+)
+
+
+
+;; From gimpcolorprofilecombobox.h
+
+(define-function gimp_color_profile_combo_box_get_type
+ (c-name "gimp_color_profile_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_profile_combo_box_new_with_model
+ (c-name "gimp_color_profile_combo_box_new_with_model")
+ (is-constructor-of "GimpColorProfileComboBox")
+ (return-type "GtkWidget*")
+ (properties
+ '("dialog")
+ '("model")
+ )
+)
+
+(define-method add
+ (of-object "GimpColorProfileComboBox")
+ (c-name "gimp_color_profile_combo_box_add")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "filename")
+ '("const-gchar*" "label")
+ )
+)
+
+(define-method set_active
+ (of-object "GimpColorProfileComboBox")
+ (c-name "gimp_color_profile_combo_box_set_active")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "filename")
+ '("const-gchar*" "label")
+ )
+)
+
+(define-method get_active
+ (of-object "GimpColorProfileComboBox")
+ (c-name "gimp_color_profile_combo_box_get_active")
+ (return-type "gchar*")
+)
+
+
+
+;; From gimpcolorprofilestore.h
+
+(define-function gimp_color_profile_store_get_type
+ (c-name "gimp_color_profile_store_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_profile_store_new
+ (c-name "gimp_color_profile_store_new")
+ (is-constructor-of "GimpColorProfileStore")
+ (return-type "GtkListStore*")
+ (properties
+ '("history")
+ )
+)
+
+(define-method add
+ (of-object "GimpColorProfileStore")
+ (c-name "gimp_color_profile_store_add")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "filename")
+ '("const-gchar*" "label")
+ )
+)
+
+
+
+;; From gimpcolorscale.h
+
+(define-function gimp_color_scale_get_type
+ (c-name "gimp_color_scale_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_scale_new
+ (c-name "gimp_color_scale_new")
+ (is-constructor-of "GimpColorScale")
+ (return-type "GtkWidget*")
+ (properties
+ '("orientation")
+ '("channel")
+ )
+)
+
+(define-method set_channel
+ (of-object "GimpColorScale")
+ (c-name "gimp_color_scale_set_channel")
+ (return-type "none")
+ (parameters
+ '("GimpColorSelectorChannel" "channel")
+ )
+)
+
+(define-method set_color
+ (of-object "GimpColorScale")
+ (c-name "gimp_color_scale_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "rgb")
+ '("const-GimpHSV*" "hsv")
+ )
+)
+
+
+
+;; From gimpcolorscales.h
+
+(define-function gimp_color_scales_get_type
+ (c-name "gimp_color_scales_get_type")
+ (return-type "GType")
+)
+
+
+
+;; From gimpcolorselect.h
+
+(define-function gimp_color_select_get_type
+ (c-name "gimp_color_select_get_type")
+ (return-type "GType")
+)
+
+
+
+;; From gimpcolorselection.h
+
+(define-function gimp_color_selection_get_type
+ (c-name "gimp_color_selection_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_selection_new
+ (c-name "gimp_color_selection_new")
+ (is-constructor-of "GimpColorSelection")
+ (return-type "GtkWidget*")
+)
+
+(define-method set_show_alpha
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_set_show_alpha")
+ (return-type "none")
+ (parameters
+ '("gboolean" "show_alpha")
+ )
+)
+
+(define-method get_show_alpha
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_get_show_alpha")
+ (return-type "gboolean")
+)
+
+(define-method set_color
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "color")
+ )
+)
+
+(define-method get_color
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_get_color")
+ (return-type "none")
+ (parameters
+ '("GimpRGB*" "color")
+ )
+)
+
+(define-method set_old_color
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_set_old_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "color")
+ )
+)
+
+(define-method get_old_color
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_get_old_color")
+ (return-type "none")
+ (parameters
+ '("GimpRGB*" "color")
+ )
+)
+
+(define-method reset
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_reset")
+ (return-type "none")
+)
+
+(define-method color_changed
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_color_changed")
+ (return-type "none")
+)
+
+(define-method set_config
+ (of-object "GimpColorSelection")
+ (c-name "gimp_color_selection_set_config")
+ (return-type "none")
+ (parameters
+ '("GimpColorConfig*" "config")
+ )
+)
+
+
+
+;; From gimpcolorselector.h
+
+(define-function gimp_color_selector_get_type
+ (c-name "gimp_color_selector_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_selector_new
+ (c-name "gimp_color_selector_new")
+ (is-constructor-of "GimpColorSelector")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GType" "selector_type")
+ '("const-GimpRGB*" "rgb")
+ '("const-GimpHSV*" "hsv")
+ '("GimpColorSelectorChannel" "channel")
+ )
+)
+
+(define-method set_toggles_visible
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_toggles_visible")
+ (return-type "none")
+ (parameters
+ '("gboolean" "visible")
+ )
+)
+
+(define-method set_toggles_sensitive
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_toggles_sensitive")
+ (return-type "none")
+ (parameters
+ '("gboolean" "sensitive")
+ )
+)
+
+(define-method set_show_alpha
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_show_alpha")
+ (return-type "none")
+ (parameters
+ '("gboolean" "show_alpha")
+ )
+)
+
+(define-method set_color
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_color")
+ (return-type "none")
+ (parameters
+ '("const-GimpRGB*" "rgb")
+ '("const-GimpHSV*" "hsv")
+ )
+)
+
+(define-method set_channel
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_channel")
+ (return-type "none")
+ (parameters
+ '("GimpColorSelectorChannel" "channel")
+ )
+)
+
+(define-method color_changed
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_color_changed")
+ (return-type "none")
+)
+
+(define-method channel_changed
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_channel_changed")
+ (return-type "none")
+)
+
+(define-method set_config
+ (of-object "GimpColorSelector")
+ (c-name "gimp_color_selector_set_config")
+ (return-type "none")
+ (parameters
+ '("GimpColorConfig*" "config")
+ )
+)
+
+
+
+;; From gimpdialog.h
+
+(define-function gimp_dialog_get_type
+ (c-name "gimp_dialog_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_dialog_new
+ (c-name "gimp_dialog_new")
+ (is-constructor-of "GimpDialog")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "title")
+ '("const-gchar*" "role")
+ '("GtkWidget*" "parent")
+ '("GtkDialogFlags" "flags")
+ '("GimpHelpFunc" "help_func")
+ '("const-gchar*" "help_id")
+ )
+ (varargs #t)
+)
+
+(define-function gimp_dialog_new_valist
+ (c-name "gimp_dialog_new_valist")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "title")
+ '("const-gchar*" "role")
+ '("GtkWidget*" "parent")
+ '("GtkDialogFlags" "flags")
+ '("GimpHelpFunc" "help_func")
+ '("const-gchar*" "help_id")
+ '("va_list" "args")
+ )
+)
+
+(define-method add_button
+ (of-object "GimpDialog")
+ (c-name "gimp_dialog_add_button")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "button_text")
+ '("gint" "response_id")
+ )
+)
+
+(define-method add_buttons
+ (of-object "GimpDialog")
+ (c-name "gimp_dialog_add_buttons")
+ (return-type "none")
+ (parameters
+ )
+ (varargs #t)
+)
+
+(define-method add_buttons_valist
+ (of-object "GimpDialog")
+ (c-name "gimp_dialog_add_buttons_valist")
+ (return-type "none")
+ (parameters
+ '("va_list" "args")
+ )
+)
+
+(define-method run
+ (of-object "GimpDialog")
+ (c-name "gimp_dialog_run")
+ (return-type "gint")
+)
+
+(function gimp_dialogs_show_help_button
+ (c-name "gimp_dialogs_show_help_button")
+ (return-type "none")
+ (parameters
+ '("gboolean" "show")
+ )
+)
+
+;; from gimpui.h
+
+(define-method set_transient
+ (of-object "GimpDialog")
+ (c-name "gimp_window_set_transient")
+ (return-type "none")
+)
+
+
+;; From gimpenumcombobox.h
+
+(define-function gimp_enum_combo_box_get_type
+ (c-name "gimp_enum_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_enum_combo_box_new
+ (c-name "gimp_enum_combo_box_new")
+ (is-constructor-of "GimpEnumComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GType" "enum_type")
+ )
+)
+
+(define-function gimp_ui_init
+ (c-name "gimp_ui_init")
+ (return-type "none")
+)
+
+(define-function gimp_enum_combo_box_new_with_model
+ (c-name "gimp_enum_combo_box_new_with_model")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpEnumStore*" "enum_store")
+ )
+)
+
+(define-method set_stock_prefix
+ (of-object "GimpEnumComboBox")
+ (c-name "gimp_enum_combo_box_set_stock_prefix")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "stock_prefix")
+ )
+)
+
+
+
+;; From gimpenumlabel.h
+
+(define-function gimp_enum_label_get_type
+ (c-name "gimp_enum_label_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_enum_label_new
+ (c-name "gimp_enum_label_new")
+ (is-constructor-of "GimpEnumLabel")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GType" "enum_type")
+ '("gint" "value")
+ )
+)
+
+(define-method set_value
+ (of-object "GimpEnumLabel")
+ (c-name "gimp_enum_label_set_value")
+ (return-type "none")
+ (parameters
+ '("gint" "value")
+ )
+)
+
+
+
+;; From gimpenumstore.h
+
+(define-function gimp_enum_store_get_type
+ (c-name "gimp_enum_store_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_enum_store_new
+ (c-name "gimp_enum_store_new")
+ (is-constructor-of "GimpEnumStore")
+ (return-type "GtkListStore*")
+ (parameters
+ '("GType" "enum_type")
+ )
+)
+
+(define-function gimp_enum_store_new_with_range
+ (c-name "gimp_enum_store_new_with_range")
+ (return-type "GtkListStore*")
+ (parameters
+ '("GType" "enum_type")
+ '("gint" "minimum")
+ '("gint" "maximum")
+ )
+)
+
+(define-function gimp_enum_store_new_with_values
+ (c-name "gimp_enum_store_new_with_values")
+ (return-type "GtkListStore*")
+ (parameters
+ '("GType" "enum_type")
+ '("gint" "n_values")
+ )
+ (varargs #t)
+)
+
+(define-function gimp_enum_store_new_with_values_valist
+ (c-name "gimp_enum_store_new_with_values_valist")
+ (return-type "GtkListStore*")
+ (parameters
+ '("GType" "enum_type")
+ '("gint" "n_values")
+ '("va_list" "args")
+ )
+)
+
+(define-method set_stock_prefix
+ (of-object "GimpEnumStore")
+ (c-name "gimp_enum_store_set_stock_prefix")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "stock_prefix")
+ )
+)
+
+
+
+;; From gimpframe.h
+
+(define-function gimp_frame_get_type
+ (c-name "gimp_frame_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_frame_new
+ (c-name "gimp_frame_new")
+ (is-constructor-of "GimpFrame")
+ (return-type "GtkWidget*")
+ (properties
+ '("label")
+ )
+)
+
+
+
+;; From gimphintbox.h
+
+(define-function gimp_hint_box_get_type
+ (c-name "gimp_hint_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_hint_box_new
+ (c-name "gimp_hint_box_new")
+ (is-constructor-of "GimpHintBox")
+ (return-type "GtkWidget*")
+ (properties
+ '("hint")
+ '("stock-id" (optional))
+ )
+)
+
+
+
+;; From gimpintcombobox.h
+
+(define-function gimp_int_combo_box_get_type
+ (c-name "gimp_int_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_int_combo_box_new
+ (c-name "gimp_int_combo_box_new")
+ (is-constructor-of "GimpIntComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "first_label")
+ '("gint" "first_value")
+ )
+ (varargs #t)
+)
+
+(define-function gimp_int_combo_box_new_valist
+ (c-name "gimp_int_combo_box_new_valist")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "first_label")
+ '("gint" "first_value")
+ '("va_list" "values")
+ )
+)
+
+(define-function gimp_int_combo_box_new_array
+ (c-name "gimp_int_combo_box_new_array")
+ (return-type "GtkWidget*")
+ (parameters
+ '("gint" "n_values")
+ '("const-gchar*[]" "labels")
+ )
+)
+
+(define-method prepend
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_prepend")
+ (return-type "none")
+ (parameters
+ )
+ (varargs #t)
+)
+
+(define-method append
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_append")
+ (return-type "none")
+ (parameters
+ )
+ (varargs #t)
+)
+
+(define-method set_active
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_set_active")
+ (return-type "gboolean")
+ (parameters
+ '("gint" "value")
+ )
+)
+
+(define-method get_active
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_get_active")
+ (return-type "gboolean")
+ (parameters
+ '("gint*" "value")
+ )
+)
+
+(define-method connect
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_connect")
+ (return-type "gulong")
+ (parameters
+ '("gint" "value")
+ '("GCallback" "callback")
+ '("gpointer" "data")
+ )
+)
+
+(define-method set_sensitivity
+ (of-object "GimpIntComboBox")
+ (c-name "gimp_int_combo_box_set_sensitivity")
+ (return-type "none")
+ (parameters
+ '("GimpIntSensitivityFunc" "func")
+ '("gpointer" "data")
+ '("GDestroyNotify" "destroy")
+ )
+)
+
+
+
+;; From gimpintstore.h
+
+(define-function gimp_int_store_get_type
+ (c-name "gimp_int_store_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_int_store_new
+ (c-name "gimp_int_store_new")
+ (is-constructor-of "GimpIntStore")
+ (return-type "GtkListStore*")
+)
+
+(define-method lookup_by_value
+ (of-object "GimpIntStore")
+ (c-name "gimp_int_store_lookup_by_value")
+ (return-type "gboolean")
+ (parameters
+ '("gint" "value")
+ '("GtkTreeIter*" "iter")
+ )
+)
+
+
+
+;; From gimpmemsizeentry.h
+
+(define-function gimp_memsize_entry_get_type
+ (c-name "gimp_memsize_entry_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_memsize_entry_new
+ (c-name "gimp_memsize_entry_new")
+ (is-constructor-of "GimpMemsizeEntry")
+ (return-type "GtkWidget*")
+ (parameters
+ '("guint64" "value")
+ '("guint64" "lower")
+ '("guint64" "upper")
+ )
+)
+
+(define-method set_value
+ (of-object "GimpMemsizeEntry")
+ (c-name "gimp_memsize_entry_set_value")
+ (return-type "none")
+ (parameters
+ '("guint64" "value")
+ )
+)
+
+(define-method get_value
+ (of-object "GimpMemsizeEntry")
+ (c-name "gimp_memsize_entry_get_value")
+ (return-type "guint64")
+)
+
+
+
+;; From gimpnumberpairentry.h
+
+(define-function gimp_number_pair_entry_get_type
+ (c-name "gimp_number_pair_entry_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_number_pair_entry_new
+ (c-name "gimp_number_pair_entry_new")
+ (is-constructor-of "GimpNumberPairEntry")
+ (return-type "GtkWidget*")
+ (properties
+ '("separators")
+ '("allow-simplification" (argname "allow_simplification"))
+ '("min-valid-value" (argname "min_valid_value"))
+ '("max-valid-value" (argname "max_valid_value"))
+ )
+)
+
+(define-method set_default_values
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_default_values")
+ (return-type "none")
+ (parameters
+ '("gdouble" "left")
+ '("gdouble" "right")
+ )
+)
+
+(define-method get_default_values
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_default_values")
+ (return-type "none")
+ (parameters
+ '("gdouble*" "left")
+ '("gdouble*" "right")
+ )
+)
+
+(define-method set_values
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_values")
+ (return-type "none")
+ (parameters
+ '("gdouble" "left")
+ '("gdouble" "right")
+ )
+)
+
+(define-method get_values
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_values")
+ (return-type "none")
+ (parameters
+ '("gdouble*" "left")
+ '("gdouble*" "right")
+ )
+)
+
+(define-method set_default_text
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_default_text")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "string")
+ )
+)
+
+(define-method get_default_text
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_default_text")
+ (return-type "const-gchar*")
+)
+
+(define-method set_ratio
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_ratio")
+ (return-type "none")
+ (parameters
+ '("gdouble" "ratio")
+ )
+)
+
+(define-method get_ratio
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_ratio")
+ (return-type "gdouble")
+)
+
+(define-method set_aspect
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_aspect")
+ (return-type "none")
+ (parameters
+ '("GimpAspectType" "aspect")
+ )
+)
+
+(define-method get_aspect
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_aspect")
+ (return-type "GimpAspectType")
+)
+
+(define-method set_user_override
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_set_user_override")
+ (return-type "none")
+ (parameters
+ '("gboolean" "user_override")
+ )
+)
+
+(define-method get_user_override
+ (of-object "GimpNumberPairEntry")
+ (c-name "gimp_number_pair_entry_get_user_override")
+ (return-type "gboolean")
+)
+
+
+
+;; From gimpoffsetarea.h
+
+(define-function gimp_offset_area_get_type
+ (c-name "gimp_offset_area_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_offset_area_new
+ (c-name "gimp_offset_area_new")
+ (is-constructor-of "GimpOffsetArea")
+ (return-type "GtkWidget*")
+ (parameters
+ '("gint" "orig_width")
+ '("gint" "orig_height")
+ )
+)
+
+(define-method set_pixbuf
+ (of-object "GimpOffsetArea")
+ (c-name "gimp_offset_area_set_pixbuf")
+ (return-type "none")
+ (parameters
+ '("GdkPixbuf*" "pixbuf")
+ )
+)
+
+(define-method set_size
+ (of-object "GimpOffsetArea")
+ (c-name "gimp_offset_area_set_size")
+ (return-type "none")
+ (parameters
+ '("gint" "width")
+ '("gint" "height")
+ )
+)
+
+(define-method set_offsets
+ (of-object "GimpOffsetArea")
+ (c-name "gimp_offset_area_set_offsets")
+ (return-type "none")
+ (parameters
+ '("gint" "offset_x")
+ '("gint" "offset_y")
+ )
+)
+
+
+
+;; From gimppageselector.h
+
+(define-function gimp_page_selector_get_type
+ (c-name "gimp_page_selector_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_page_selector_new
+ (c-name "gimp_page_selector_new")
+ (is-constructor-of "GimpPageSelector")
+ (return-type "GtkWidget*")
+)
+
+(define-method set_n_pages
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_set_n_pages")
+ (return-type "none")
+ (parameters
+ '("gint" "n_pages")
+ )
+)
+
+(define-method get_n_pages
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_n_pages")
+ (return-type "gint")
+)
+
+(define-method set_target
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_set_target")
+ (return-type "none")
+ (parameters
+ '("GimpPageSelectorTarget" "target")
+ )
+)
+
+(define-method get_target
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_target")
+ (return-type "GimpPageSelectorTarget")
+)
+
+(define-method set_page_thumbnail
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_set_page_thumbnail")
+ (return-type "none")
+ (parameters
+ '("gint" "page_no")
+ '("GdkPixbuf*" "thumbnail")
+ )
+)
+
+(define-method get_page_thumbnail
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_page_thumbnail")
+ (return-type "GdkPixbuf*")
+ (parameters
+ '("gint" "page_no")
+ )
+)
+
+(define-method set_page_label
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_set_page_label")
+ (return-type "none")
+ (parameters
+ '("gint" "page_no")
+ '("const-gchar*" "label")
+ )
+)
+
+(define-method get_page_label
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_page_label")
+ (return-type "gchar*")
+ (parameters
+ '("gint" "page_no")
+ )
+)
+
+(define-method select_all
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_select_all")
+ (return-type "none")
+)
+
+(define-method unselect_all
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_unselect_all")
+ (return-type "none")
+)
+
+(define-method select_page
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_select_page")
+ (return-type "none")
+ (parameters
+ '("gint" "page_no")
+ )
+)
+
+(define-method unselect_page
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_unselect_page")
+ (return-type "none")
+ (parameters
+ '("gint" "page_no")
+ )
+)
+
+(define-method page_is_selected
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_page_is_selected")
+ (return-type "gboolean")
+ (parameters
+ '("gint" "page_no")
+ )
+)
+
+(define-method get_selected_pages
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_selected_pages")
+ (return-type "gint*")
+ (parameters
+ '("gint*" "n_selected_pages")
+ )
+)
+
+(define-method select_range
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_select_range")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "range")
+ )
+)
+
+(define-method get_selected_range
+ (of-object "GimpPageSelector")
+ (c-name "gimp_page_selector_get_selected_range")
+ (return-type "gchar*")
+)
+
+
+
+;; From gimppatheditor.h
+
+(define-function gimp_path_editor_get_type
+ (c-name "gimp_path_editor_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_path_editor_new
+ (c-name "gimp_path_editor_new")
+ (is-constructor-of "GimpPathEditor")
+ (properties
+ '("title")
+ '("path" (optional))
+ )
+ (return-type "GtkWidget*")
+)
+
+(define-method get_path
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_get_path")
+ (return-type "gchar*")
+)
+
+(define-method set_path
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_set_path")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "path")
+ )
+)
+
+(define-method get_writable_path
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_get_writable_path")
+ (return-type "gchar*")
+)
+
+(define-method set_writable_path
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_set_writable_path")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "path")
+ )
+)
+
+(define-method get_dir_writable
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_get_dir_writable")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "directory")
+ )
+)
+
+(define-method set_dir_writable
+ (of-object "GimpPathEditor")
+ (c-name "gimp_path_editor_set_dir_writable")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "directory")
+ '("gboolean" "writable")
+ )
+)
+
+
+
+;; From gimppickbutton.h
+
+(define-function gimp_pick_button_get_type
+ (c-name "gimp_pick_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_pick_button_new
+ (c-name "gimp_pick_button_new")
+ (is-constructor-of "GimpPickButton")
+ (return-type "GtkWidget*")
+)
+
+
+
+;; From gimppreview.h
+
+(define-function gimp_preview_get_type
+ (c-name "gimp_preview_get_type")
+ (return-type "GType")
+)
+
+(define-method set_update
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_set_update")
+ (return-type "none")
+ (parameters
+ '("gboolean" "update")
+ )
+)
+
+(define-method get_update
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_get_update")
+ (return-type "gboolean")
+)
+
+(define-method set_bounds
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_set_bounds")
+ (return-type "none")
+ (parameters
+ '("gint" "xmin")
+ '("gint" "ymin")
+ '("gint" "xmax")
+ '("gint" "ymax")
+ )
+)
+
+(define-method get_position
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_get_position")
+ (return-type "none")
+ (parameters
+ '("gint*" "x")
+ '("gint*" "y")
+ )
+)
+
+(define-method get_size
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_get_size")
+ (return-type "none")
+ (parameters
+ '("gint*" "width")
+ '("gint*" "height")
+ )
+)
+
+(define-method transform
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_transform")
+ (return-type "none")
+ (parameters
+ '("gint" "src_x")
+ '("gint" "src_y")
+ '("gint*" "dest_x")
+ '("gint*" "dest_y")
+ )
+)
+
+(define-method untransform
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_untransform")
+ (return-type "none")
+ (parameters
+ '("gint" "src_x")
+ '("gint" "src_y")
+ '("gint*" "dest_x")
+ '("gint*" "dest_y")
+ )
+)
+
+(define-method get_area
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_get_area")
+ (return-type "GtkWidget*")
+)
+
+(define-method draw
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_draw")
+ (return-type "none")
+)
+
+(define-method draw_buffer
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_draw_buffer")
+ (return-type "none")
+ (parameters
+ '("const-guchar*" "buffer")
+ '("gint" "rowstride")
+ )
+)
+
+(define-method invalidate
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_invalidate")
+ (return-type "none")
+)
+
+(define-method set_default_cursor
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_set_default_cursor")
+ (return-type "none")
+ (parameters
+ '("GdkCursor*" "cursor")
+ )
+)
+
+(define-method get_controls
+ (of-object "GimpPreview")
+ (c-name "gimp_preview_get_controls")
+ (return-type "GtkWidget*")
+)
+
+
+
+;; From gimppreviewarea.h
+
+(define-function gimp_preview_area_get_type
+ (c-name "gimp_preview_area_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_preview_area_new
+ (c-name "gimp_preview_area_new")
+ (is-constructor-of "GimpPreviewArea")
+ (return-type "GtkWidget*")
+)
+
+(define-method draw
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_draw")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ '("gint" "width")
+ '("gint" "height")
+ '("GimpImageType" "type")
+ '("const-guchar*" "buf")
+ '("gint" "rowstride")
+ )
+)
+
+(define-method blend
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_blend")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ '("gint" "width")
+ '("gint" "height")
+ '("GimpImageType" "type")
+ '("const-guchar*" "buf1")
+ '("gint" "rowstride1")
+ '("const-guchar*" "buf2")
+ '("gint" "rowstride2")
+ '("guchar" "opacity")
+ )
+)
+
+(define-method mask
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_mask")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ '("gint" "width")
+ '("gint" "height")
+ '("GimpImageType" "type")
+ '("const-guchar*" "buf1")
+ '("gint" "rowstride1")
+ '("const-guchar*" "buf2")
+ '("gint" "rowstride2")
+ '("const-guchar*" "mask")
+ '("gint" "rowstride_mask")
+ )
+)
+
+(define-method fill
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_fill")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ '("gint" "width")
+ '("gint" "height")
+ '("guchar" "red")
+ '("guchar" "green")
+ '("guchar" "blue")
+ )
+)
+
+(define-method set_offsets
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_set_offsets")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ )
+)
+
+(define-method set_colormap
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_set_colormap")
+ (return-type "none")
+ (parameters
+ '("const-guchar*" "colormap")
+ '("gint" "num_colors")
+ )
+)
+
+(define-method set_max_size
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_set_max_size")
+ (return-type "none")
+ (parameters
+ '("gint" "width")
+ '("gint" "height")
+ )
+)
+
+(define-method menu_popup
+ (of-object "GimpPreviewArea")
+ (c-name "gimp_preview_area_menu_popup")
+ (return-type "none")
+ (parameters
+ '("GdkEventButton*" "event")
+ )
+)
+
+
+
+;; From gimpscrolledpreview.h
+
+(define-function gimp_scrolled_preview_get_type
+ (c-name "gimp_scrolled_preview_get_type")
+ (return-type "GType")
+)
+
+(define-method set_position
+ (of-object "GimpScrolledPreview")
+ (c-name "gimp_scrolled_preview_set_position")
+ (return-type "none")
+ (parameters
+ '("gint" "x")
+ '("gint" "y")
+ )
+)
+
+(define-method set_policy
+ (of-object "GimpScrolledPreview")
+ (c-name "gimp_scrolled_preview_set_policy")
+ (return-type "none")
+ (parameters
+ '("GtkPolicyType" "hscrollbar_policy")
+ '("GtkPolicyType" "vscrollbar_policy")
+ )
+)
+
+(define-method freeze
+ (of-object "GimpScrolledPreview")
+ (c-name "gimp_scrolled_preview_freeze")
+ (return-type "none")
+)
+
+(define-method thaw
+ (of-object "GimpScrolledPreview")
+ (c-name "gimp_scrolled_preview_thaw")
+ (return-type "none")
+)
+
+
+
+;; From gimpsizeentry.h
+
+(define-function gimp_size_entry_get_type
+ (c-name "gimp_size_entry_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_size_entry_new
+ (c-name "gimp_size_entry_new")
+ (is-constructor-of "GimpSizeEntry")
+ (return-type "GtkWidget*")
+ (parameters
+ '("gint" "number_of_fields")
+ '("GimpUnit" "unit")
+ '("const-gchar*" "unit_format")
+ '("gboolean" "menu_show_pixels")
+ '("gboolean" "menu_show_percent")
+ '("gboolean" "show_refval")
+ '("gint" "spinbutton_width")
+ '("GimpSizeEntryUpdatePolicy" "update_policy")
+ )
+)
+
+(define-method add_field
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_add_field")
+ (return-type "none")
+ (parameters
+ '("GtkSpinButton*" "value_spinbutton")
+ '("GtkSpinButton*" "refval_spinbutton")
+ )
+)
+
+(define-method attach_label
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_attach_label")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "text")
+ '("gint" "row")
+ '("gint" "column")
+ '("gfloat" "alignment")
+ )
+)
+
+(define-method set_resolution
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_resolution")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "resolution")
+ '("gboolean" "keep_size")
+ )
+)
+
+(define-method set_size
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_size")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "lower")
+ '("gdouble" "upper")
+ )
+)
+
+(define-method set_value_boundaries
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_value_boundaries")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "lower")
+ '("gdouble" "upper")
+ )
+)
+
+(define-method get_value
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_get_value")
+ (return-type "gdouble")
+ (parameters
+ '("gint" "field")
+ )
+)
+
+(define-method set_value
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_value")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "value")
+ )
+)
+
+(define-method set_refval_boundaries
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_refval_boundaries")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "lower")
+ '("gdouble" "upper")
+ )
+)
+
+(define-method set_refval_digits
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_refval_digits")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gint" "digits")
+ )
+)
+
+(define-method get_refval
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_get_refval")
+ (return-type "gdouble")
+ (parameters
+ '("gint" "field")
+ )
+)
+
+(define-method set_refval
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_refval")
+ (return-type "none")
+ (parameters
+ '("gint" "field")
+ '("gdouble" "refval")
+ )
+)
+
+(define-method get_unit
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_get_unit")
+ (return-type "GimpUnit")
+)
+
+(define-method set_unit
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_unit")
+ (return-type "none")
+ (parameters
+ '("GimpUnit" "unit")
+ )
+)
+
+(define-method show_unit_menu
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_show_unit_menu")
+ (return-type "none")
+ (parameters
+ '("gboolean" "show")
+ )
+)
+
+(define-method set_pixel_digits
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_pixel_digits")
+ (return-type "none")
+ (parameters
+ '("gint" "digits")
+ )
+)
+
+(define-method grab_focus
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_grab_focus")
+ (return-type "none")
+)
+
+(define-method set_activates_default
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_set_activates_default")
+ (return-type "none")
+ (parameters
+ '("gboolean" "setting")
+ )
+)
+
+(define-method get_help_widget
+ (of-object "GimpSizeEntry")
+ (c-name "gimp_size_entry_get_help_widget")
+ (return-type "GtkWidget*")
+ (parameters
+ '("gint" "field")
+ )
+)
+
+
+
+;; From gimpstringcombobox.h
+
+(define-function gimp_string_combo_box_get_type
+ (c-name "gimp_string_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_string_combo_box_new
+ (c-name "gimp_string_combo_box_new")
+ (is-constructor-of "GimpStringComboBox")
+ (return-type "GtkWidget*")
+ (properties
+ '("model")
+ '("id-column" (argname "id_column"))
+ '("label-column" (argname "label_column"))
+ )
+)
+
+(define-method set_active
+ (of-object "GimpStringComboBox")
+ (c-name "gimp_string_combo_box_set_active")
+ (return-type "gboolean")
+ (parameters
+ '("const-gchar*" "id")
+ )
+)
+
+(define-method get_active
+ (of-object "GimpStringComboBox")
+ (c-name "gimp_string_combo_box_get_active")
+ (return-type "gchar*")
+)
+
+
+
+;; From gimpunitcombobox.h
+
+(define-function gimp_unit_combo_box_get_type
+ (c-name "gimp_unit_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_unit_combo_box_new
+ (c-name "gimp_unit_combo_box_new")
+ (is-constructor-of "GimpUnitComboBox")
+ (return-type "GtkWidget*")
+)
+
+(define-function gimp_unit_combo_box_new_with_model
+ (c-name "gimp_unit_combo_box_new_with_model")
+ (is-constructor-of "GimpUnitComboBox")
+ (return-type "GtkWidget*")
+ (properties
+ '("model" (optional))
+ )
+)
+
+(define-method get_active
+ (of-object "GimpUnitComboBox")
+ (c-name "gimp_unit_combo_box_get_active")
+ (return-type "GimpUnit")
+)
+
+(define-method set_active
+ (of-object "GimpUnitComboBox")
+ (c-name "gimp_unit_combo_box_set_active")
+ (return-type "none")
+ (parameters
+ '("GimpUnit" "unit")
+ )
+)
+
+
+
+;; From gimpunitmenu.h
+
+(define-function gimp_unit_menu_get_type
+ (c-name "gimp_unit_menu_get_type")
+ (return-type "GType")
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-function gimp_unit_menu_new
+ (c-name "gimp_unit_menu_new")
+ (is-constructor-of "GimpUnitMenu")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "format")
+ '("GimpUnit" "unit")
+ '("gboolean" "show_pixels")
+ '("gboolean" "show_percent")
+ '("gboolean" "show_custom")
+ )
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-method set_unit
+ (of-object "GimpUnitMenu")
+ (c-name "gimp_unit_menu_set_unit")
+ (return-type "none")
+ (parameters
+ '("GimpUnit" "unit")
+ )
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-method get_unit
+ (of-object "GimpUnitMenu")
+ (c-name "gimp_unit_menu_get_unit")
+ (return-type "GimpUnit")
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-method set_pixel_digits
+ (of-object "GimpUnitMenu")
+ (c-name "gimp_unit_menu_set_pixel_digits")
+ (return-type "none")
+ (parameters
+ '("gint" "digits")
+ )
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+(define-method get_pixel_digits
+ (of-object "GimpUnitMenu")
+ (c-name "gimp_unit_menu_get_pixel_digits")
+ (return-type "gint")
+ (deprecated "use gimpui.UnitComboBox instead")
+)
+
+
+
+;; From gimpzoommodel.h
+
+(define-function gimp_zoom_model_get_type
+ (c-name "gimp_zoom_model_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_zoom_model_new
+ (c-name "gimp_zoom_model_new")
+ (is-constructor-of "GimpZoomModel")
+ (return-type "GimpZoomModel*")
+)
+
+(define-method set_range
+ (of-object "GimpZoomModel")
+ (c-name "gimp_zoom_model_set_range")
+ (return-type "none")
+ (parameters
+ '("gdouble" "min")
+ '("gdouble" "max")
+ )
+)
+
+(define-method zoom
+ (of-object "GimpZoomModel")
+ (c-name "gimp_zoom_model_zoom")
+ (return-type "none")
+ (parameters
+ '("GimpZoomType" "zoom_type")
+ '("gdouble" "scale")
+ )
+)
+
+(define-method get_factor
+ (of-object "GimpZoomModel")
+ (c-name "gimp_zoom_model_get_factor")
+ (return-type "gdouble")
+)
+
+(define-method get_fraction
+ (of-object "GimpZoomModel")
+ (c-name "gimp_zoom_model_get_fraction")
+ (return-type "none")
+ (parameters
+ '("gint*" "numerator")
+ '("gint*" "denominator")
+ )
+)
+
+(define-function gimp_zoom_button_new
+ (c-name "gimp_zoom_button_new")
+ (is-constructor-of "GimpZoomButton")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpZoomModel*" "model")
+ '("GimpZoomType" "zoom_type")
+ '("GtkIconSize" "icon_size")
+ )
+)
+
+(define-function gimp_zoom_model_zoom_step
+ (c-name "gimp_zoom_model_zoom_step")
+ (return-type "gdouble")
+ (parameters
+ '("GimpZoomType" "zoom_type")
+ '("gdouble" "scale")
+ )
+)
+
+
+
+;; From gimpaspectpreview.h
+
+(define-function gimp_aspect_preview_get_type
+ (c-name "gimp_aspect_preview_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_aspect_preview_new
+ (c-name "gimp_aspect_preview_new")
+ (is-constructor-of "GimpAspectPreview")
+ (return-type "GtkWidget*")
+ (properties
+ '("drawable")
+ '("update" (optional))
+ )
+)
+
+
+
+;; From gimpdrawablepreview.h
+
+(define-function gimp_drawable_preview_get_type
+ (c-name "gimp_drawable_preview_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_drawable_preview_new
+ (c-name "gimp_drawable_preview_new")
+ (is-constructor-of "GimpDrawablePreview")
+ (return-type "GtkWidget*")
+ (properties
+ '("drawable")
+ )
+)
+
+(define-method get_drawable
+ (of-object "GimpDrawablePreview")
+ (c-name "gimp_drawable_preview_get_drawable")
+ (return-type "GimpDrawable*")
+)
+
+(define-method draw_region
+ (of-object "GimpDrawablePreview")
+ (c-name "gimp_drawable_preview_draw_region")
+ (return-type "none")
+ (parameters
+ '("const-GimpPixelRgn*" "region")
+ )
+)
+
+
+
+;; From gimpimagecombobox.h
+
+(define-function gimp_image_combo_box_get_type
+ (c-name "gimp_image_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_image_combo_box_new
+ (c-name "gimp_image_combo_box_new")
+ (is-constructor-of "GimpImageComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpImageConstraintFunc" "constraint")
+ '("gpointer" "data")
+ )
+)
+
+
+
+;; From gimpitemcombobox.h
+
+(define-function gimp_drawable_combo_box_get_type
+ (c-name "gimp_drawable_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_channel_combo_box_get_type
+ (c-name "gimp_channel_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_layer_combo_box_get_type
+ (c-name "gimp_layer_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_vectors_combo_box_get_type
+ (c-name "gimp_vectors_combo_box_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_drawable_combo_box_new
+ (c-name "gimp_drawable_combo_box_new")
+ (is-constructor-of "GimpDrawableComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpDrawableConstraintFunc" "constraint")
+ '("gpointer" "data")
+ )
+)
+
+(define-function gimp_channel_combo_box_new
+ (c-name "gimp_channel_combo_box_new")
+ (is-constructor-of "GimpChannelComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpDrawableConstraintFunc" "constraint")
+ '("gpointer" "data")
+ )
+)
+
+(define-function gimp_layer_combo_box_new
+ (c-name "gimp_layer_combo_box_new")
+ (is-constructor-of "GimpLayerComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpDrawableConstraintFunc" "constraint")
+ '("gpointer" "data")
+ )
+)
+
+(define-function gimp_vectors_combo_box_new
+ (c-name "gimp_vectors_combo_box_new")
+ (is-constructor-of "GimpVectorsComboBox")
+ (return-type "GtkWidget*")
+ (parameters
+ '("GimpVectorsConstraintFunc" "constraint")
+ '("gpointer" "data")
+ )
+)
+
+
+
+;; From gimppatternselect.h
+
+(define-function gimp_pattern_select_new
+ (c-name "gimp_pattern_select_new")
+ (is-constructor-of "GimpPatternSelect")
+ (return-type "const-gchar*")
+ (parameters
+ '("const-gchar*" "title")
+ '("const-gchar*" "pattern_name")
+ '("GimpRunPatternCallback" "callback")
+ '("gpointer" "data")
+ )
+)
+
+(define-function gimp_pattern_select_destroy
+ (c-name "gimp_pattern_select_destroy")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "pattern_callback")
+ )
+)
+
+
+
+;; From gimpprocbrowserdialog.h
+
+(define-function gimp_proc_browser_dialog_get_type
+ (c-name "gimp_proc_browser_dialog_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_proc_browser_dialog_new
+ (c-name "gimp_proc_browser_dialog_new")
+ (is-constructor-of "GimpProcBrowserDialog")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "title")
+ '("const-gchar*" "role")
+ '("GimpHelpFunc" "help_func")
+ '("const-gchar*" "help_id")
+ )
+ (varargs #t)
+)
+
+(define-method get_selected
+ (of-object "GimpProcBrowserDialog")
+ (c-name "gimp_proc_browser_dialog_get_selected")
+ (return-type "gchar*")
+)
+
+
+
+;; From gimpprocview.h
+
+(define-function gimp_proc_view_new
+ (c-name "gimp_proc_view_new")
+ (is-constructor-of "GimpProcView")
+ (return-type "GtkWidget*")
+ (parameters
+ '("const-gchar*" "name")
+ '("const-gchar*" "menu_path")
+ '("const-gchar*" "blurb")
+ '("const-gchar*" "help")
+ '("const-gchar*" "author")
+ '("const-gchar*" "copyright")
+ '("const-gchar*" "date")
+ '("GimpPDBProcType" "type")
+ '("gint" "n_params")
+ '("gint" "n_return_vals")
+ '("GimpParamDef*" "params")
+ '("GimpParamDef*" "return_vals")
+ )
+)
+
+
+
+;; From gimpzoompreview.h
+
+(define-function gimp_zoom_preview_get_type
+ (c-name "gimp_zoom_preview_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_zoom_preview_new_with_model
+ (c-name "gimp_zoom_preview_new_with_model")
+ (is-constructor-of "GimpZoomPreview")
+ (return-type "GtkWidget*")
+ (properties
+ '("drawable")
+ '("model" (optional))
+ )
+)
+
+(define-method get_source
+ (of-object "GimpZoomPreview")
+ (c-name "gimp_zoom_preview_get_source")
+ (return-type "guchar*")
+ (parameters
+ '("gint*" "width")
+ '("gint*" "height")
+ '("gint*" "bpp")
+ )
+)
+
+(define-method get_drawable
+ (of-object "GimpZoomPreview")
+ (c-name "gimp_zoom_preview_get_drawable")
+ (return-type "GimpDrawable*")
+)
+
+(define-method get_model
+ (of-object "GimpZoomPreview")
+ (c-name "gimp_zoom_preview_get_model")
+ (return-type "GimpZoomModel*")
+)
+
+(define-method get_factor
+ (of-object "GimpZoomPreview")
+ (c-name "gimp_zoom_preview_get_factor")
+ (return-type "gdouble")
+)
+
+
+
+;; From gimpprogressbar.h
+
+(define-function gimp_progress_bar_get_type
+ (c-name "gimp_progress_bar_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_progress_bar_new
+ (c-name "gimp_progress_bar_new")
+ (is-constructor-of "GimpProgressBar")
+ (return-type "GtkWidget*")
+)
+
+
+
+;; From gimpbrushselectbutton.h
+
+(define-function gimp_brush_select_button_get_type
+ (c-name "gimp_brush_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_brush_select_button_new
+ (c-name "gimp_brush_select_button_new")
+ (is-constructor-of "GimpBrushSelectButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("brush-name" (argname "brush_name") (optional))
+ '("brush-opacity" (argname "opacity") (optional))
+ '("brush-spacing" (argname "spacing") (optional))
+ '("brush-paint-mode" (argname "paint_mode") (optional))
+ )
+)
+
+(define-method get_brush
+ (of-object "GimpBrushSelectButton")
+ (c-name "gimp_brush_select_button_get_brush")
+ (return-type "const-gchar*")
+ (parameters
+ '("gdouble*" "opacity")
+ '("gint*" "spacing")
+ '("GimpLayerMode*" "paint_mode")
+ )
+)
+
+(define-method set_brush
+ (of-object "GimpBrushSelectButton")
+ (c-name "gimp_brush_select_button_set_brush")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "brush_name")
+ '("gdouble" "opacity")
+ '("gint" "spacing")
+ '("GimpLayerMode" "paint_mode")
+ )
+)
+
+
+
+;; From gimpfontselectbutton.h
+
+(define-function gimp_font_select_button_get_type
+ (c-name "gimp_font_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_font_select_button_new
+ (c-name "gimp_font_select_button_new")
+ (is-constructor-of "GimpFontSelectButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("font-name" (argname "font_name") (optional))
+ )
+)
+
+(define-method get_font
+ (of-object "GimpFontSelectButton")
+ (c-name "gimp_font_select_button_get_font")
+ (return-type "const-gchar*")
+)
+
+(define-method set_font
+ (of-object "GimpFontSelectButton")
+ (c-name "gimp_font_select_button_set_font")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "font_name")
+ )
+)
+
+
+
+;; From gimpgradientselectbutton.h
+
+(define-function gimp_gradient_select_button_get_type
+ (c-name "gimp_gradient_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_gradient_select_button_new
+ (c-name "gimp_gradient_select_button_new")
+ (is-constructor-of "GimpGradientSelectButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("gradient-name" (argname "gradient_name") (optional))
+ )
+)
+
+(define-method get_gradient
+ (of-object "GimpGradientSelectButton")
+ (c-name "gimp_gradient_select_button_get_gradient")
+ (return-type "const-gchar*")
+)
+
+(define-method set_gradient
+ (of-object "GimpGradientSelectButton")
+ (c-name "gimp_gradient_select_button_set_gradient")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "gradient_name")
+ )
+)
+
+
+
+;; From gimppaletteselectbutton.h
+
+(define-function gimp_palette_select_button_get_type
+ (c-name "gimp_palette_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_palette_select_button_new
+ (c-name "gimp_palette_select_button_new")
+ (is-constructor-of "GimpPaletteSelectButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("palette-name" (argname "palette_name") (optional))
+ )
+)
+
+(define-method get_palette
+ (of-object "GimpPaletteSelectButton")
+ (c-name "gimp_palette_select_button_get_palette")
+ (return-type "const-gchar*")
+)
+
+(define-method set_palette
+ (of-object "GimpPaletteSelectButton")
+ (c-name "gimp_palette_select_button_set_palette")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "palette_name")
+ )
+)
+
+
+
+;; From gimppatternselectbutton.h
+
+(define-function gimp_pattern_select_button_get_type
+ (c-name "gimp_pattern_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_pattern_select_button_new
+ (c-name "gimp_pattern_select_button_new")
+ (is-constructor-of "GimpPatternSelectButton")
+ (return-type "GtkWidget*")
+ (properties
+ '("title" (optional))
+ '("pattern-name" (argname "pattern_name") (optional))
+ )
+)
+
+(define-method get_pattern
+ (of-object "GimpPatternSelectButton")
+ (c-name "gimp_pattern_select_button_get_pattern")
+ (return-type "const-gchar*")
+)
+
+(define-method set_pattern
+ (of-object "GimpPatternSelectButton")
+ (c-name "gimp_pattern_select_button_set_pattern")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "pattern_name")
+ )
+)
+
+
+
+;; From gimpselectbutton.h
+
+(define-function gimp_select_button_get_type
+ (c-name "gimp_select_button_get_type")
+ (return-type "GType")
+)
+
+(define-method close_popup
+ (of-object "GimpSelectButton")
+ (c-name "gimp_select_button_close_popup")
+ (return-type "none")
+)
+
+
+
+;; From gimpwidgetsenums.h
+
+(define-function gimp_aspect_type_get_type
+ (c-name "gimp_aspect_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_chain_position_get_type
+ (c-name "gimp_chain_position_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_area_type_get_type
+ (c-name "gimp_color_area_type_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_color_selector_channel_get_type
+ (c-name "gimp_color_selector_channel_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_page_selector_target_get_type
+ (c-name "gimp_page_selector_target_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_size_entry_update_policy_get_type
+ (c-name "gimp_size_entry_update_policy_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_zoom_type_get_type
+ (c-name "gimp_zoom_type_get_type")
+ (return-type "GType")
+)
+
+
+
+;; From gimpruler.h
+
+(define-function gimp_ruler_get_type
+ (c-name "gimp_ruler_get_type")
+ (return-type "GType")
+)
+
+(define-function gimp_ruler_new
+ (c-name "gimp_ruler_new")
+ (is-constructor-of "GimpRuler")
+ (return-type "GtkWidget*")
+ (properties
+ '("orientation")
+ '("lower" (optional))
+ '("upper" (optional))
+ '("max-size" (argname "max_size") (optional))
+ )
+)
+
+(define-method set_unit
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_set_unit")
+ (return-type "none")
+ (parameters
+ '("GimpUnit" "unit")
+ )
+)
+
+(define-method get_unit
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_get_unit")
+ (return-type "GimpUnit")
+)
+
+(define-method set_position
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_set_position")
+ (return-type "none")
+ (parameters
+ '("gdouble" "position")
+ )
+)
+
+(define-method get_position
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_get_position")
+ (return-type "gdouble")
+)
+
+(define-method set_range
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_set_range")
+ (return-type "none")
+ (parameters
+ '("gdouble" "lower")
+ '("gdouble" "upper")
+ '("gdouble" "max_size")
+ )
+)
+
+(define-method get_range
+ (of-object "GimpRuler")
+ (c-name "gimp_ruler_get_range")
+ (return-type "none")
+ (parameters
+ '("gdouble*" "lower")
+ '("gdouble*" "upper")
+ '("gdouble*" "max_size")
+ )
+)
+
+
diff --git a/plug-ins/pygimp/gimpui.override b/plug-ins/pygimp/gimpui.override
new file mode 100644
index 0000000..5851989
--- /dev/null
+++ b/plug-ins/pygimp/gimpui.override
@@ -0,0 +1,2075 @@
+headers
+#include <Python.h>
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include <pycairo.h>
+extern Pycairo_CAPI_t *Pycairo_CAPI;
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+#include <libgimp/gimp.h>
+#undef GIMP_DISABLE_DEPRECATED
+#include <libgimp/gimpui.h>
+#define GIMP_DISABLE_DEPRECATED
+
+#define NO_IMPORT_PYGIMP
+#include "pygimp-api.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+typedef struct {
+ PyObject *constraint;
+ PyObject *user_data;
+} PyGimpConstraintData;
+
+typedef struct {
+ PyObject *sensitivity_func;
+ PyObject *user_data;
+} PyGimpIntSensitivityData;
+
+/* TODO: Add a header for these */
+void gimpui_register_classes(PyObject *d);
+void gimpui_add_constants(PyObject *module, const gchar *strip_prefix);
+
+static void
+pygimp_decref_callback(PyObject* obj) {
+ Py_XDECREF (obj);
+}
+
+%%
+modulename gimpui
+%%
+import gobject.GObject as PyGObject_Type
+import gtk.gdk.Pixbuf as PyGdkPixbuf_Type
+import gtk.Object as PyGtkObject_Type
+import gtk.Widget as PyGtkWidget_Type
+import gtk.Dialog as PyGtkDialog_Type
+import gtk.Window as PyGtkWindow_Type
+import gtk.Label as PyGtkLabel_Type
+import gtk.Button as PyGtkButton_Type
+import gtk.ToggleButton as PyGtkToggleButton_Type
+import gtk.RadioButton as PyGtkRadioButton_Type
+import gtk.SpinButton as PyGtkSpinButton_Type
+import gtk.Entry as PyGtkEntry_Type
+import gtk.DrawingArea as PyGtkDrawingArea_Type
+import gtk.Table as PyGtkTable_Type
+import gtk.Frame as PyGtkFrame_Type
+import gtk.HBox as PyGtkHBox_Type
+import gtk.VBox as PyGtkVBox_Type
+import gtk.HPaned as PyGtkHPaned_Type
+import gtk.VPaned as PyGtkVPaned_Type
+import gtk.Scale as PyGtkScale_Type
+import gtk.ProgressBar as PyGtkProgressBar_Type
+import gtk.OptionMenu as PyGtkOptionMenu_Type
+import gtk.ComboBox as PyGtkComboBox_Type
+import gtk.ListStore as PyGtkListStore_Type
+import gtk.TreeModel as PyGtkTreeModel_Type
+import gtk.CellRenderer as PyGtkCellRenderer_Type
+import gtk.CellRendererToggle as PyGtkCellRendererToggle_Type
+import gimp.Parasite as PyGimpParasite_Type
+%%
+ignore
+ gimp_dialog_add_buttons
+ gimp_int_combo_box_connect
+ gimp_color_profile_combo_box_new
+ gimp_enum_store_new_with_values
+ gimp_int_combo_box_new_array
+%%
+ignore-glob
+ *_get_type
+ *_valist
+ gimp_resolution_*
+%%
+ignore-type
+ GimpIntStoreColumns
+%%
+override gimp_drawable_combo_box_new kwargs
+static gboolean
+pygimp_drawable_constraint_marshal(gint32 image_id, gint32 drawable_id,
+ gpointer user_data)
+{
+ PyObject *img, *drw, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ drw = pygimp_drawable_new(NULL, drawable_id);
+ if (!drw) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, drw,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, drw, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(drw);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_drawable_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.DrawableComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_XINCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_drawable_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_drawable_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_DRAWABLE_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.DrawableComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+define GimpDrawableComboBox.set_active_drawable kwargs
+static PyObject *
+_wrap_gimp_drawable_combo_box_set_active_drawable(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpDrawable *drw;
+
+ static char *kwlist[] = { "drawable", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpDrawableComboBox.set_active_drawable",
+ kwlist,
+ PyGimpDrawable_Type, &drw))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), drw->ID)) {
+ PyErr_Format(pygimp_error,
+ "Drawable (ID %d) does not exist in GimpDrawableComboBox",
+ drw->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+define GimpDrawableComboBox.get_active_drawable noargs
+static PyObject *
+_wrap_gimp_drawable_combo_box_get_active_drawable(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_drawable_new(NULL, value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_channel_combo_box_new kwargs
+static gboolean
+pygimp_channel_constraint_marshal(gint32 image_id, gint32 channel_id,
+ gpointer user_data)
+{
+ PyObject *img, *chn, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ chn = pygimp_channel_new(channel_id);
+ if (!chn) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, chn,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, chn, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(chn);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_channel_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.ChannelComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_channel_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_channel_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_CHANNEL_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.ChannelComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+define GimpChannelComboBox.set_active_channel kwargs
+static PyObject *
+_wrap_gimp_channel_combo_box_set_active_channel(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpChannel *chn;
+
+ static char *kwlist[] = { "channel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpChannelComboBox.set_active_channel",
+ kwlist,
+ PyGimpChannel_Type, &chn))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "Channel (ID %d) does not exist in GimpChannelComboBox",
+ chn->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+define GimpChannelComboBox.get_active_channel noargs
+static PyObject *
+_wrap_gimp_channel_combo_box_get_active_channel(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_channel_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_layer_combo_box_new kwargs
+static gboolean
+pygimp_layer_constraint_marshal(gint32 image_id, gint32 layer_id,
+ gpointer user_data)
+{
+ PyObject *img, *lay, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ lay = pygimp_layer_new(layer_id);
+ if (!lay) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, lay,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, lay, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(lay);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_layer_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpDrawableConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.LayerComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_layer_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_layer_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_LAYER_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.LayerComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+define GimpLayerComboBox.set_active_layer kwargs
+static PyObject *
+_wrap_gimp_layer_combo_box_set_active_layer(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpLayer *lay;
+
+ static char *kwlist[] = { "layer", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpLayerComboBox.set_active_layer",
+ kwlist,
+ PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "Layer (ID %d) does not exist in GimpLayerComboBox",
+ lay->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+define GimpLayerComboBox.get_active_layer noargs
+static PyObject *
+_wrap_gimp_layer_combo_box_get_active_layer(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_layer_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_vectors_combo_box_new kwargs
+static gboolean
+pygimp_vectors_constraint_marshal(gint32 image_id, gint32 vectors_id,
+ gpointer user_data)
+{
+ PyObject *img, *vect, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ vect = pygimp_vectors_new(vectors_id);
+ if (!vect) {
+ PyErr_Print();
+ Py_DECREF(img);
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, vect,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, vect, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(vect);
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_vectors_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpVectorsConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.VectorsComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_vectors_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_vectors_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_VECTORS_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.VectorsComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+define GimpVectorsComboBox.set_active_vectors kwargs
+static PyObject *
+_wrap_gimp_vectors_combo_box_set_active_vectors(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpVectors *vect;
+
+ static char *kwlist[] = { "vectors", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpVectorsComboBox.set_active_vectors",
+ kwlist,
+ PyGimpVectors_Type, &vect))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), vect->ID)) {
+ PyErr_Format(pygimp_error,
+ "Vectors (ID %d) does not exist in GimpVectorsComboBox",
+ vect->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+define GimpVectorsComboBox.get_active_vectors noargs
+static PyObject *
+_wrap_gimp_vectors_combo_box_get_active_vectors(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_vectors_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_image_combo_box_new kwargs
+static gboolean
+pygimp_image_constraint_marshal(gint32 image_id, gpointer user_data)
+{
+ PyObject *img, *ret;
+ gboolean res;
+ PyGimpConstraintData *data = user_data;
+
+ img = pygimp_image_new(image_id);
+ if (!img) {
+ PyErr_Print();
+ return FALSE;
+ }
+
+ if (data->user_data && data->user_data != Py_None)
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img,
+ data->user_data, NULL);
+ else
+ ret = PyObject_CallFunctionObjArgs(data->constraint, img, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(img);
+
+ return res;
+}
+
+static int
+_wrap_gimp_image_combo_box_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *constraint = NULL, *user_data = NULL;
+ GimpImageConstraintFunc func = NULL;
+ PyGimpConstraintData *data = NULL;
+
+ static char *kwlist[] = { "constraint", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|OO:gimpui.ImageComboBox.__init__",
+ kwlist,
+ &constraint, &user_data))
+ return -1;
+
+ if (constraint && constraint != Py_None) {
+ if (!PyCallable_Check(constraint)) {
+ PyErr_SetString(PyExc_TypeError, "first arg must be callable");
+ return -1;
+ }
+
+ data = g_new(PyGimpConstraintData, 1);
+
+ data->constraint = constraint;
+ Py_INCREF(constraint);
+
+ data->user_data = user_data;
+ Py_XINCREF(user_data);
+
+ func = pygimp_image_constraint_marshal;
+ }
+
+ self->obj = (GObject *)gimp_image_combo_box_new(func, data);
+
+ Py_XDECREF(constraint);
+ Py_XDECREF(user_data);
+ g_free(data);
+
+ if (pyg_type_from_object((PyObject *)self) != GIMP_TYPE_IMAGE_COMBO_BOX) {
+ PyErr_SetString(PyExc_RuntimeError, "__gobject_init__ must be used "
+ "when subclassing gimpui.ImageComboBox");
+ return -1;
+ }
+
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+define GimpImageComboBox.set_active_image kwargs
+static PyObject *
+_wrap_gimp_image_combo_box_set_active_image(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyGimpImage *img;
+
+ static char *kwlist[] = { "image", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpImageComboBox.set_active_image",
+ kwlist,
+ PyGimpImage_Type, &img))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), img->ID)) {
+ PyErr_Format(pygimp_error,
+ "Image (ID %d) does not exist in GimpImageComboBox",
+ img->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+define GimpImageComboBox.get_active_image noargs
+static PyObject *
+_wrap_gimp_image_combo_box_get_active_image(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return pygimp_image_new(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_dialog_new kwargs
+static void
+pygimp_help_func_marshal(const gchar *help_id, gpointer help_data)
+{
+ GObject *dialog = help_data;
+ PyObject *py_dialog, *help_func, *ret;
+
+ py_dialog = g_object_get_data(dialog, "pygimp-dialog-pyobject");
+ help_func = g_object_get_data(dialog, "pygimp-dialog-help_func");
+
+ ret = PyObject_CallFunction(help_func, "sO", help_id, py_dialog);
+
+ if (ret)
+ Py_DECREF(ret);
+ else
+ PyErr_Print();
+}
+
+static void
+pygimp_help_func_destroy(gpointer data)
+{
+ PyObject *help_func = data;
+
+ Py_DECREF(help_func);
+}
+
+static void
+pygimp_dialog_close(GtkWidget *widget)
+{
+ /* Synthesize delete_event to close dialog. */
+
+ if (gtk_widget_get_window (widget)) {
+ GdkEvent *event = gdk_event_new(GDK_DELETE);
+
+ event->any.window = g_object_ref (gtk_widget_get_window (widget));
+ event->any.send_event = TRUE;
+
+ gtk_main_do_event(event);
+ gdk_event_free(event);
+ }
+}
+
+static int
+_wrap_gimp_dialog_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *title, *role;
+ PyGObject *py_window = NULL;
+ PyObject *py_flags = NULL, *py_buttons = Py_None;
+ PyObject *help_func = NULL;
+ gchar *help_id = NULL;
+ GtkDialogFlags flags = 0;
+ int len, i;
+ GtkWidget *parent;
+ GimpHelpFunc func;
+
+ static char *kwlist[] = { "title", "role", "parent", "flags",
+ "help_func", "help_id", "buttons", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "zz|OOOzO:gimpui.GimpDialog.__init__",
+ kwlist,
+ &title, &role, &py_window, &py_flags,
+ &help_func, &help_id, &py_buttons))
+ return -1;
+
+ if (py_window == NULL || (PyObject*)py_window == Py_None)
+ parent = NULL;
+ else if (pygobject_check(py_window, &PyGtkWindow_Type))
+ parent = GTK_WIDGET(py_window->obj);
+ else {
+ PyErr_SetString(PyExc_TypeError, "parent must be a GtkWindow or None");
+ return -1;
+ }
+
+ if (pyg_flags_get_value(GTK_TYPE_DIALOG_FLAGS, py_flags, (gint *)&flags))
+ return -1;
+
+ if (py_buttons == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_buttons))
+ len = PyTuple_Size(py_buttons);
+ else {
+ PyErr_SetString(PyExc_TypeError, "buttons must be a tuple containing text/response pairs or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "buttons tuple must contain text/response id pairs");
+ return -1;
+ }
+
+ if (help_func) {
+ if (help_func != Py_None) {
+ if (!PyCallable_Check(help_func)) {
+ PyErr_SetString(PyExc_TypeError, "help_func must be callable");
+ return -1;
+ }
+
+ func = pygimp_help_func_marshal;
+
+ } else {
+ func = gimp_standard_help_func;
+ }
+ } else {
+ func = gimp_standard_help_func;
+ }
+
+ pygobject_construct(self,
+ "title", title,
+ "role", role,
+ "modal", (flags & GTK_DIALOG_MODAL),
+ "help-func", func,
+ "help-id", help_id,
+ NULL);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpDialog object");
+ return -1;
+ }
+
+ if (parent) {
+ if (GTK_IS_WINDOW(parent))
+ gtk_window_set_transient_for(GTK_WINDOW(self->obj),
+ GTK_WINDOW(parent));
+ else
+ gtk_window_set_screen(GTK_WINDOW(self->obj),
+ gtk_widget_get_screen(parent));
+
+ if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
+ g_signal_connect_object(parent, "destroy",
+ G_CALLBACK(pygimp_dialog_close),
+ self->obj, G_CONNECT_SWAPPED);
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *text = PyTuple_GetItem(py_buttons, i);
+ PyObject *id = PyTuple_GetItem(py_buttons, i + 1);
+ if (!PyString_Check(text) && !PyUnicode_Check(text)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each text/response id pair "
+ "must be a string");
+ return -1;
+ }
+ if (!PyInt_Check(id)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each text/response id pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_dialog_add_button(GIMP_DIALOG(self->obj), PyString_AsString(text),
+ PyInt_AsLong(id));
+ }
+
+ if (help_func && help_func != Py_None) {
+ g_object_set_data(self->obj, "pygimp-dialog-help-data", self);
+
+ Py_INCREF(help_func);
+ g_object_set_data_full(self->obj, "pygimp-dialog-help-func",
+ help_func, pygimp_help_func_destroy);
+ }
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_DIALOG
+%%
+override gimp_color_button_get_color noargs
+static PyObject *
+_wrap_gimp_color_button_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_button_get_color(GIMP_COLOR_BUTTON(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_brush_select_button_get_brush noargs
+static PyObject *
+_wrap_gimp_brush_select_button_get_brush(PyGObject *self)
+{
+ const gchar *brush_name;
+ gdouble opacity;
+ gint spacing;
+ GimpLayerMode paint_mode;
+
+ brush_name =
+ gimp_brush_select_button_get_brush(GIMP_BRUSH_SELECT_BUTTON(self->obj),
+ &opacity, &spacing, &paint_mode);
+
+ return Py_BuildValue("(sdiN)", brush_name, opacity, spacing,
+ pyg_enum_from_gtype(GIMP_TYPE_LAYER_MODE,
+ paint_mode));
+}
+%%
+override gimp_window_set_transient
+static PyObject *
+_wrap_gimp_window_set_transient(PyGObject *self)
+{
+ gimp_window_set_transient(GTK_WINDOW(self->obj));
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_color_button_new kwargs
+static int
+_wrap_gimp_color_button_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gchar *title = NULL;
+ gint width = -1, height = -1;
+ PyObject *py_color = NULL, *py_type = NULL;
+ GimpRGB *color, default_color = { 0.0, 0.0, 0.0, 100.0 };
+ GimpColorAreaType type;
+
+ static char *kwlist[] = { "title", "width", "height", "color", "type",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|ziiOO:gimpui.ColorButton.__init__",
+ kwlist,
+ &title, &width, &height,
+ &py_color, &py_type))
+ return -1;
+
+ if (py_color == NULL || (PyObject*)py_color == Py_None)
+ color = &default_color;
+ else if (pyg_boxed_check(py_color, GIMP_TYPE_RGB))
+ color = pyg_boxed_get(py_color, GimpRGB);
+ else {
+ PyErr_SetString(PyExc_TypeError, "color should be a GimpRGB or None");
+ return -1;
+ }
+
+ if (py_type == NULL || (PyObject*)py_type == Py_None)
+ type = GIMP_COLOR_AREA_FLAT;
+ else if (pyg_enum_get_value(GIMP_TYPE_COLOR_AREA_TYPE, py_type,
+ (gint*)&type))
+ return -1;
+
+ if (pygobject_construct(self,
+ "title", title,
+ "type", type,
+ "color", color,
+ "area-width", width,
+ "area-height", height,
+ NULL))
+ return -1;
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_COLOR_BUTTON
+%%
+override gimp_color_scale_new kwargs
+static int
+_wrap_gimp_color_scale_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_orientation, *py_channel;
+ GtkOrientation orientation;
+ GimpColorSelectorChannel channel;
+
+ static char *kwlist[] = { "orientation", "channel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OO:gimpui.ColorScale.__init__",
+ kwlist,
+ &py_orientation, &py_channel))
+ return -1;
+
+ if (pyg_enum_get_value(GTK_TYPE_ORIENTATION, py_orientation,
+ (gint*)&orientation))
+ return -1;
+
+ if (pyg_enum_get_value(GIMP_TYPE_COLOR_SELECTOR_CHANNEL, py_channel,
+ (gint*)&channel))
+ return -1;
+
+ if (pygobject_construct(self,
+ "orientation", orientation,
+ "channel", channel,
+ NULL))
+ return -1;
+
+ gtk_range_set_flippable (GTK_RANGE (self->obj),
+ orientation == GTK_ORIENTATION_HORIZONTAL);
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_COLOR_SCALE
+%%
+override gimp_enum_label_new kwargs
+static int
+_wrap_gimp_enum_label_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_enum_type = NULL;
+ GType enum_type;
+ gint value;
+
+ static char *kwlist[] = { "enum_type", "value", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "Oi:gimpui.GimpEnumLabel.__init__",
+ kwlist,
+ &py_enum_type, &value))
+ return -1;
+
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return -1;
+
+ if (pygobject_construct(self,
+ "enum-type", enum_type,
+ "enum-value", value,
+ NULL))
+ return -1;
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_ENUM_LABEL
+%%
+override gimp_int_combo_box_new kwargs
+static int
+_wrap_gimp_int_combo_box_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_items = NULL;
+ int len, i;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|O:gimpui.IntComboBox.__init__",
+ kwlist,
+ &py_items))
+ return -1;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return -1;
+ }
+
+ if (pygobject_construct(self, NULL))
+ return -1;
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_int_combo_box_append(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_INT_COMBO_BOX
+%%
+override gimp_int_combo_box_set_sensitivity kwargs
+static gboolean
+pygimp_int_combo_box_sensitivity_marshal(gint value, gpointer user_data)
+{
+ PyObject *py_value;
+ PyGimpIntSensitivityData *data;
+ PyObject *ret;
+ gboolean res;
+
+ data = user_data;
+
+ py_value = PyInt_FromLong(value);
+
+ ret = PyObject_CallFunctionObjArgs(data->sensitivity_func, py_value,
+ data->user_data, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ res = FALSE;
+ } else {
+ res = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ Py_DECREF(py_value);
+
+ return res;
+}
+
+static void
+pygimp_int_combo_box_sensitivity_data_destroy(gpointer user_data)
+{
+ PyGimpIntSensitivityData *data;
+ data = user_data;
+
+ Py_DECREF(data->sensitivity_func);
+ Py_XDECREF(data->user_data);
+
+ g_free(data);
+}
+
+static PyObject *
+_wrap_gimp_int_combo_box_set_sensitivity(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_sensitivity_func;
+ PyObject *py_user_data = NULL;
+ PyGimpIntSensitivityData *data;
+
+ static char *kwlist[] = { "sensitivity_func", "user_data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O|O:GimpIntComboBox.set_sensitivity",
+ kwlist, &py_sensitivity_func,
+ &py_user_data))
+ return NULL;
+
+ if (!PyCallable_Check(py_sensitivity_func)) {
+ PyErr_SetString(PyExc_TypeError, "first argument must be callable.");
+ return NULL;
+ }
+
+ data = g_new(PyGimpIntSensitivityData, 1);
+
+ data->sensitivity_func = py_sensitivity_func;
+ Py_INCREF(data->sensitivity_func);
+
+ if (py_user_data == NULL || py_user_data == Py_None)
+ data->user_data = NULL;
+ else {
+ data->user_data = py_user_data;
+ Py_INCREF(data->user_data);
+ }
+
+ gimp_int_combo_box_set_sensitivity(GIMP_INT_COMBO_BOX(self->obj),
+ pygimp_int_combo_box_sensitivity_marshal,
+ data,
+ pygimp_int_combo_box_sensitivity_data_destroy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_int_combo_box_get_active noargs
+static PyObject *
+_wrap_gimp_int_combo_box_get_active(PyGObject *self)
+{
+ int value;
+
+ if (gimp_int_combo_box_get_active(GIMP_INT_COMBO_BOX(self->obj), &value))
+ return PyLong_FromLong(value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_int_combo_box_set_active kwargs
+static PyObject *
+_wrap_gimp_int_combo_box_set_active(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ int value;
+
+ static char *kwlist[] = { "value", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "i:GimpIntComboBox.set_active", kwlist,
+ &value))
+ return NULL;
+
+ if (!gimp_int_combo_box_set_active(GIMP_INT_COMBO_BOX(self->obj), value)) {
+ PyErr_Format(pygimp_error,
+ "Value %d does not exist in GimpIntComboBox",
+ value);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_int_combo_box_append kwargs
+static PyObject *
+_wrap_gimp_int_combo_box_append(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_items;
+ int i, len;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:gimpui.IntComboBox.append",
+ kwlist,
+ &py_items))
+ return NULL;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return NULL;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return NULL;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return NULL;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return NULL;
+ }
+
+ gimp_int_combo_box_append(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_int_combo_box_prepend kwargs
+static PyObject *
+_wrap_gimp_int_combo_box_prepend(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_items;
+ int i, len;
+
+ static char *kwlist[] = { "items", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:gimpui.IntComboBox.prepend",
+ kwlist,
+ &py_items))
+ return NULL;
+
+ if (py_items == NULL || py_items == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_items))
+ len = PyTuple_Size(py_items);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "items must be a tuple containing label/value pairs "
+ "or None");
+ return NULL;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "items tuple must contain label/value pairs");
+ return NULL;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *label = PyTuple_GetItem(py_items, i);
+ PyObject *value = PyTuple_GetItem(py_items, i + 1);
+
+ if (!PyString_Check(label)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each label/value pair "
+ "must be a string");
+ return NULL;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each label/value pair "
+ "must be a number");
+ return NULL;
+ }
+
+ gimp_int_combo_box_prepend(GIMP_INT_COMBO_BOX(self->obj),
+ GIMP_INT_STORE_LABEL,
+ PyString_AsString(label),
+ GIMP_INT_STORE_VALUE,
+ PyInt_AsLong(value),
+ -1);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_browser_add_search_types args
+static PyObject *
+_wrap_gimp_browser_add_search_types(PyGObject *self, PyObject *args)
+{
+ GimpBrowser *browser;
+ int len, i;
+ PyObject *element;
+ gchar *label;
+ gint id;
+
+ browser = GIMP_BROWSER(self->obj);
+
+ len = PyTuple_Size(args);
+
+ for (i = 0; i < len; ++i) {
+ element = PyTuple_GetItem(args, i);
+ if (!PyTuple_Check(element)) {
+ PyErr_SetString(PyExc_TypeError, "GimpBrowser.add_search_types: Arguments must be tuples");
+ return NULL;
+ }
+ if (!PyArg_ParseTuple(element, "si", &label, &id))
+ return NULL;
+ gimp_browser_add_search_types(browser, label, id, NULL);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_proc_browser_dialog_new kwargs
+static int
+_wrap_gimp_proc_browser_dialog_new(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ gchar *title, *role;
+ PyObject *py_buttons = Py_None;
+ PyObject *help_func = NULL;
+ gchar *help_id = NULL;
+ int len, i;
+ GimpHelpFunc func;
+
+ static char *kwlist[] = { "title", "role", "help_func", "help_id",
+ "buttons", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "zz|OzO:gimpui.GimpProcBrowserDialog.__init__",
+ kwlist,
+ &title, &role, &help_func, &help_id,
+ &py_buttons))
+ return -1;
+
+ if (py_buttons == Py_None)
+ len = 0;
+ else if (PyTuple_Check(py_buttons))
+ len = PyTuple_Size(py_buttons);
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "buttons must be a tuple containing text/response "
+ "pairs or None");
+ return -1;
+ }
+
+ if (len % 2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "buttons tuple must contain text/response id pairs");
+ return -1;
+ }
+
+ if (help_func) {
+ if (help_func != Py_None) {
+ if (!PyCallable_Check(help_func)) {
+ PyErr_SetString(PyExc_TypeError, "help_func must be callable");
+ return -1;
+ }
+
+ func = pygimp_help_func_marshal;
+
+ } else {
+ func = gimp_standard_help_func;
+ }
+ } else {
+ func = gimp_standard_help_func;
+ }
+
+ pygobject_construct(self,
+ "title", title,
+ "role", role,
+ "help-func", func,
+ "help-id", help_id,
+ NULL);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpProcBrowserDialog object");
+ return -1;
+ }
+
+ for (i = 0; i < len; i += 2) {
+ PyObject *text = PyTuple_GetItem(py_buttons, i);
+ PyObject *id = PyTuple_GetItem(py_buttons, i + 1);
+ if (!PyString_Check(text) && !PyUnicode_Check(text)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "first member of each text/response id pair "
+ "must be a string");
+ return -1;
+ }
+ if (!PyInt_Check(id)) {
+ gtk_object_destroy(GTK_OBJECT(self->obj));
+ self->obj = NULL;
+ PyErr_SetString(PyExc_RuntimeError,
+ "second member of each text/response id pair "
+ "must be a number");
+ return -1;
+ }
+
+ gimp_dialog_add_button(GIMP_DIALOG(self->obj), PyString_AsString(text),
+ PyInt_AsLong(id));
+ }
+
+ if (help_func && help_func != Py_None) {
+ g_object_set_data(self->obj, "pygimp-dialog-help-data", self);
+
+ Py_INCREF(help_func);
+ g_object_set_data_full(self->obj, "pygimp-dialog-help-func",
+ help_func, pygimp_help_func_destroy);
+ }
+
+ g_signal_emit_by_name(GIMP_PROC_BROWSER_DIALOG(self->obj)->browser,
+ "search", "", 0, self->obj);
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_PROC_BROWSER_DIALOG
+%%
+override gimp_number_pair_entry_get_values noargs
+static PyObject *
+_wrap_gimp_number_pair_entry_get_values(PyGObject *self)
+{
+ gdouble left, right;
+
+ gimp_number_pair_entry_get_values(GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ &left, &right);
+
+ return Py_BuildValue("(dd)", left, right);
+}
+%%
+override gimp_number_pair_entry_get_default_values noargs
+static PyObject *
+_wrap_gimp_number_pair_entry_get_default_values(PyGObject *self)
+{
+ gdouble left, right;
+
+ gimp_number_pair_entry_get_default_values(
+ GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ &left, &right);
+
+ return Py_BuildValue("(dd)", left, right);
+}
+%%
+override gimp_number_pair_entry_get_aspect noargs
+static PyObject *
+_wrap_gimp_number_pair_entry_get_aspect(PyGObject *self)
+{
+ GimpAspectType aspect;
+
+ aspect =
+ gimp_number_pair_entry_get_aspect(GIMP_NUMBER_PAIR_ENTRY(self->obj));
+
+ return pyg_enum_from_gtype(GIMP_TYPE_ASPECT_TYPE, aspect);
+}
+%%
+override gimp_number_pair_entry_set_aspect kwargs
+static PyObject *
+_wrap_gimp_number_pair_entry_set_aspect(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject *py_aspect;
+ GimpAspectType aspect;
+
+ static char *kwlist[] = {"aspect", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:GimpNumberPairEntry.set_aspect",
+ kwlist, &py_aspect))
+ return NULL;
+
+ if (pyg_enum_get_value(GIMP_TYPE_ASPECT_TYPE, py_aspect, (gint*)&aspect))
+ {
+ Py_XDECREF(py_aspect);
+ return NULL;
+ }
+
+ gimp_number_pair_entry_set_aspect(GIMP_NUMBER_PAIR_ENTRY(self->obj),
+ aspect);
+
+ Py_DECREF(py_aspect);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_page_selector_get_selected_pages noargs
+static PyObject *
+_wrap_gimp_page_selector_get_selected_pages(PyGObject *self)
+{
+ gint *selected_pages;
+ gint n_selected_pages;
+ PyObject *py_selected_pages;
+ int i;
+
+ selected_pages = gimp_page_selector_get_selected_pages(
+ GIMP_PAGE_SELECTOR (self->obj),
+ &n_selected_pages);
+
+ py_selected_pages = PyTuple_New(n_selected_pages);
+ for (i = 0; i < n_selected_pages; ++i)
+ PyTuple_SetItem(py_selected_pages, i,
+ PyInt_FromLong(selected_pages[i]));
+
+ g_free(selected_pages);
+
+ return py_selected_pages;
+}
+%%
+override gimp_preview_get_position noargs
+static PyObject *
+_wrap_gimp_preview_get_position(PyGObject *self)
+{
+ gint x;
+ gint y;
+
+ gimp_preview_get_position(GIMP_PREVIEW(self->obj), &x, &y);
+
+ return Py_BuildValue("(ii)", x, y);
+}
+%%
+override gimp_preview_get_size noargs
+static PyObject *
+_wrap_gimp_preview_get_size(PyGObject *self)
+{
+ gint width;
+ gint height;
+
+ gimp_preview_get_size(GIMP_PREVIEW(self->obj), &width, &height);
+
+ return Py_BuildValue("(ii)", width, height);
+}
+%%
+override gimp_preview_transform kwargs
+static PyObject *
+_wrap_gimp_preview_transform(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ gint src_x;
+ gint src_y;
+ gint dest_x;
+ gint dest_y;
+
+ static char *kwlist[] = {"x", "y", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:GimpPreview.transform",
+ kwlist, &src_x, &src_y))
+ return NULL;
+
+ gimp_preview_transform(GIMP_PREVIEW(self->obj), src_x, src_y, &dest_x,
+ &dest_y);
+
+ return Py_BuildValue("(ii)", dest_x, dest_y);
+}
+%%
+override gimp_preview_untransform kwargs
+static PyObject *
+_wrap_gimp_preview_untransform(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ gint src_x;
+ gint src_y;
+ gint dest_x;
+ gint dest_y;
+
+ static char *kwlist[] = {"x", "y", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ii:GimpPreview.untransform",
+ kwlist, &src_x, &src_y))
+ return NULL;
+
+ gimp_preview_untransform(GIMP_PREVIEW(self->obj), src_x, src_y, &dest_x,
+ &dest_y);
+
+ return Py_BuildValue("(ii)", dest_x, dest_y);
+}
+%%
+override gimp_zoom_model_get_fraction noargs
+static PyObject *
+_wrap_gimp_zoom_model_get_fraction(PyGObject *self)
+{
+ gint numerator;
+ gint denominator;
+
+ gimp_zoom_model_get_fraction(GIMP_ZOOM_MODEL(self->obj), &numerator,
+ &denominator);
+
+ return Py_BuildValue("(ii)", numerator, denominator);
+}
+%%
+override gimp_drawable_preview_get_drawable noargs
+static PyObject *
+_wrap_gimp_drawable_preview_get_drawable(PyGObject *self)
+{
+ PyObject *drawable;
+
+ drawable = g_object_get_data(self->obj,
+ "pygimp-drawable-preview-pydrawable");
+ Py_INCREF(drawable);
+ return drawable;
+}
+%%
+override gimp_zoom_preview_get_drawable noargs
+static PyObject *
+_wrap_gimp_zoom_preview_get_drawable(PyGObject *self)
+{
+ PyObject *drawable;
+
+ drawable = g_object_get_data(self->obj,
+ "pygimp-zoom-preview-pydrawable");
+ Py_INCREF(drawable);
+ return drawable;
+}
+%%
+override gimp_drawable_preview_draw_region kwargs
+static PyObject *
+_wrap_gimp_drawable_preview_draw_region(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+/* PyGimpPixelRgn *pypixelrgn;
+
+ static char *kwlist[] = {"drawable", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpDrawablePreview.draw_region",
+ kwlist, PyGimpPixelRgn_Type, &pypixelrgn))
+ return NULL;
+
+ gimp_drawable_preview_draw_region(GIMP_DRAWABLE_PREVIEW(self->obj),
+ &pypixelrgn->pr);
+*/
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_int_store_lookup_by_value
+static PyObject *
+_wrap_gimp_int_store_lookup_by_value(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", NULL };
+ int value, ret;
+ GtkTreeIter iter;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "i:GimpIntStore.gimp_int_store_lookup_by_value",
+ kwlist, &value))
+ return NULL;
+
+ ret = gimp_int_store_lookup_by_value(GTK_TREE_MODEL(self->obj), value,
+ &iter);
+ if (ret)
+ pyg_boxed_new(GTK_TYPE_TREE_ITER, &iter, TRUE, TRUE);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_memsize_entry_new
+static int
+_wrap_gimp_memsize_entry_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", "lower", "upper", NULL };
+ guint64 value, lower, upper;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "LLL:GimpMemsizeEntry.__init__",
+ kwlist, &value, &lower, &upper))
+ return -1;
+
+ self->obj = (GObject *)gimp_memsize_entry_new(value, lower, upper);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not create GimpMemsizeEntry object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+override gimp_memsize_entry_set_value
+static PyObject *
+_wrap_gimp_memsize_entry_set_value(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "value", NULL };
+ guint64 value;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "L:GimpMemsizeEntry.set_value",
+ kwlist, &value))
+ return NULL;
+
+ gimp_memsize_entry_set_value(GIMP_MEMSIZE_ENTRY(self->obj), value);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_color_area_get_color noargs
+static PyObject *
+_wrap_gimp_color_area_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_area_get_color(GIMP_COLOR_AREA(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_color_hex_entry_get_color noargs
+static PyObject *
+_wrap_gimp_color_hex_entry_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_hex_entry_get_color(GIMP_COLOR_HEX_ENTRY(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_color_notebook_get_color noargs
+static PyObject *
+_wrap_gimp_color_notebook_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_notebook_get_color(GIMP_COLOR_NOTEBOOK(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_color_selection_get_color noargs
+static PyObject *
+_wrap_gimp_color_selection_get_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_selection_get_color(GIMP_COLOR_SELECTION(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_color_selection_get_old_color noargs
+static PyObject *
+_wrap_gimp_color_selection_get_old_color(PyGObject *self)
+{
+ GimpRGB rgb;
+
+ gimp_color_selection_get_old_color(GIMP_COLOR_SELECTION(self->obj), &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+%%
+override gimp_enum_store_new kwargs
+static int
+_wrap_gimp_enum_store_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "enum_type", "minimum", "maximum", NULL };
+ PyObject *py_enum_type = NULL;
+ PyObject *py_minimum = NULL;
+ PyObject *py_maximum = NULL;
+ GType enum_type;
+ GEnumClass *enum_class;
+ gint minimum, maximum;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O|O!O!:GimpEnumStore.__init__", kwlist,
+ &py_enum_type, &PyInt_Type, &py_minimum,
+ &PyInt_Type, &py_maximum))
+ return -1;
+ if ((enum_type = pyg_type_from_object(py_enum_type)) == 0)
+ return -1;
+
+ enum_class = g_type_class_ref(enum_type);
+
+ if (py_minimum == NULL)
+ minimum = enum_class->minimum;
+ else
+ minimum = PyInt_AsLong(py_minimum);
+
+ if (py_maximum == NULL)
+ maximum = enum_class->maximum;
+ else
+ maximum = PyInt_AsLong(py_maximum);
+
+ g_type_class_unref(enum_class);
+
+ self->obj = (GObject *)gimp_enum_store_new_with_range(enum_type, minimum, maximum);
+
+ if (!self->obj) {
+ PyErr_SetString(PyExc_RuntimeError, "could not create GimpEnumStore object");
+ return -1;
+ }
+ pygobject_register_wrapper((PyObject *)self);
+ return 0;
+}
+%%
+override gimp_zoom_preview_new_with_model kwargs
+static int
+_wrap_gimp_zoom_preview_new_with_model(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", "model", NULL };
+ PyGimpDrawable *py_drawable;
+ PyGObject *py_zoom_model = NULL;
+ GimpZoomModel *zoom_model;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|O!:GimpZoomPreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable,
+ &PyGimpZoomModel_Type, &py_zoom_model))
+ return -1;
+
+ if (py_zoom_model)
+ zoom_model = (GimpZoomModel*)py_zoom_model->obj;
+ else
+ zoom_model = NULL;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, "model", zoom_model, NULL))
+ return -1;
+
+ g_object_set_data_full(self->obj, "pygimp-zoom-preview-pydrawable",
+ py_drawable,
+ (GDestroyNotify)pygimp_decref_callback);
+
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_ZOOM_PREVIEW
+%%
+override gimp_aspect_preview_new kwargs
+static int
+_wrap_gimp_aspect_preview_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", NULL };
+ PyGimpDrawable *py_drawable;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|:GimpAspectPreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable))
+ return -1;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, NULL))
+ return -1;
+
+ g_signal_connect_swapped(self->obj, "destroy",
+ (GCallback)pygimp_decref_callback, py_drawable);
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+%%
+new-constructor GIMP_TYPE_ASPECT_PREVIEW
+%%
+override gimp_drawable_preview_new kwargs
+static int
+_wrap_gimp_drawable_preview_new(PyGObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "drawable", NULL };
+ PyGimpDrawable *py_drawable;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|:GimpDrawablePreview.__init__", kwlist,
+ PyGimpDrawable_Type, &py_drawable))
+ return -1;
+
+ if (!py_drawable->drawable)
+ py_drawable->drawable = gimp_drawable_get(py_drawable->ID);
+
+ if (pygobject_construct(self, "drawable", py_drawable->drawable, NULL))
+ return -1;
+
+ g_object_set_data_full(self->obj, "pygimp-drawable-preview-pydrawable",
+ py_drawable,
+ (GDestroyNotify)pygimp_decref_callback);
+
+ Py_INCREF(py_drawable);
+
+ return 0;
+}
+
+%%
+new-constructor GIMP_TYPE_DRAWABLE_PREVIEW
+%%
+override gimp_ruler_get_range noargs
+static PyObject *
+_wrap_gimp_ruler_get_range(PyGObject *self)
+{
+ gdouble lower, upper, max_size;
+
+ gimp_ruler_get_range(GIMP_RULER(self->obj), &lower, &upper, &max_size);
+
+ return Py_BuildValue("(ddd)", lower, upper, max_size);
+}
+%%
+override gimp_color_display_convert_surface kwargs
+static PyObject *
+_wrap_gimp_color_display_convert_surface(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PycairoSurface *pysurface;
+
+ static char *kwlist[] = { "surface", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpColorDisplay.convert_surface",
+ kwlist,
+ &PycairoSurface_Type, &pysurface))
+ return NULL;
+
+ gimp_color_display_convert_surface(GIMP_COLOR_DISPLAY(self->obj),
+ pysurface->surface);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_color_display_stack_convert_surface kwargs
+static PyObject *
+_wrap_gimp_color_display_stack_convert_surface(PyGObject *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PycairoSurface *pysurface;
+
+ static char *kwlist[] = { "surface", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!:GimpColorDisplayStack.convert_surface",
+ kwlist,
+ &PycairoSurface_Type, &pysurface))
+ return NULL;
+
+ gimp_color_display_stack_convert_surface(GIMP_COLOR_DISPLAY_STACK(self->obj),
+ pysurface->surface);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+%%
+override gimp_zoom_preview_get_source noargs
+static PyObject *
+_wrap_gimp_zoom_preview_get_source(PyGObject *self)
+{
+ gint width, height, bpp;
+ guchar *image;
+ PyObject *pyimage;
+
+ image = gimp_zoom_preview_get_source(GIMP_ZOOM_PREVIEW(self->obj),
+ &width, &height, &bpp);
+
+ if (image)
+ {
+ pyimage = PyByteArray_FromStringAndSize((const char *)image,
+ width * height * bpp);
+ g_free (image);
+ }
+ else
+ {
+ Py_INCREF(Py_None);
+ pyimage = Py_None;
+ }
+
+ return Py_BuildValue("(Niii)", pyimage, width, height, bpp);
+}
+%%
+override gimp_ui_init noargs
+static PyObject *
+_wrap_gimp_ui_init(PyObject *self)
+{
+ extern const char *prog_name;
+
+ gimp_ui_init (prog_name, FALSE);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
diff --git a/plug-ins/pygimp/gimpui.py b/plug-ins/pygimp/gimpui.py
new file mode 100644
index 0000000..76ee584
--- /dev/null
+++ b/plug-ins/pygimp/gimpui.py
@@ -0,0 +1,227 @@
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+'''This module implements the UI items found in the libgimpui library.
+It requires pygtk to work. These functions take use to callbacks -- one
+is a constraint function, and the other is the callback object. The
+constraint function takes an image object as its first argument, and
+a drawable object as its second if appropriate. The callback functions
+get the selected object as their first argument, and the user data as
+the second.
+
+It also implements a number of selector widgets, which can be used to select
+various gimp data types. Each of these selectors takes default as an argument
+to the constructor, and has a get_value() method for retrieving the result.
+'''
+
+import pygtk
+pygtk.require('2.0')
+
+import gtk, gobject, gimp, gimpcolor
+
+from _gimpui import *
+
+import gettext
+t = gettext.translation('gimp20-python', gimp.locale_directory, fallback=True)
+_ = t.ugettext
+
+def _callbackWrapper(menu_item, callback, data):
+ callback(menu_item.get_data("Gimp-ID"), data)
+
+def _createMenu(items, callback, data):
+ menu = gtk.Menu()
+ if not items:
+ items = [("(none)", None)]
+ for label, id in items:
+ menu_item = gtk.MenuItem(label)
+ menu_item.set_data("Gimp-ID", id)
+ menu.add(menu_item)
+ if callback:
+ menu_item.connect("activate", _callbackWrapper,
+ callback, data)
+ menu_item.show()
+ return menu
+
+
+def ImageMenu(constraint=None, callback=None, data=None):
+ items = []
+ for img in gimp.image_list():
+ if constraint and not constraint(img):
+ continue
+ if not img.filename:
+ filename = img.name
+ else:
+ filename = img.filename
+ items.append((filename, img))
+ items.sort()
+ return _createMenu(items, callback, data)
+
+def LayerMenu(constraint=None, callback=None, data=None):
+ items = []
+ for img in gimp.image_list():
+ filename = img.filename
+ if not filename:
+ filename = img.name
+ for layer in img.layers:
+ if constraint and not constraint(img, layer):
+ continue
+ name = filename + "/" + layer.name
+ items.append((name, layer))
+ items.sort()
+ return _createMenu(items, callback, data)
+
+def ChannelMenu(constraint=None, callback=None, data=None):
+ items = []
+ for img in gimp.image_list():
+ filename = img.filename
+ if not filename:
+ filename = img.name
+ for channel in img.channels:
+ if constraint and not constraint(img, channel):
+ continue
+ name = filename + "/" + channel.name
+ items.append((name, channel))
+ items.sort()
+ return _createMenu(items, callback, data)
+
+def DrawableMenu(constraint=None, callback=None, data=None):
+ items = []
+ for img in gimp.image_list():
+ filename = img.filename
+ if not filename:
+ filename = img.name
+ for drawable in img.layers + img.channels:
+ if constraint and not constraint(img, drawable):
+ continue
+ name = filename + "/" + drawable.name
+ items.append((name, drawable))
+ items.sort()
+ return _createMenu(items, callback, data)
+
+def VectorsMenu(constraint=None, callback=None, data=None):
+ items = []
+ for img in gimp.image_list():
+ filename = img.filename
+ if not filename:
+ filename = img.name
+ for vectors in img.vectors:
+ if constraint and not constraint(img, vectors):
+ continue
+ name = filename + "/" + vectors.name
+ items.append((name, vectors))
+ items.sort()
+ return _createMenu(items, callback, data)
+
+class ImageSelector(ImageComboBox):
+ def __init__(self, default=None):
+ ImageComboBox.__init__(self)
+ if default is not None:
+ self.set_active_image(default)
+ def get_value(self):
+ return self.get_active_image()
+
+class LayerSelector(LayerComboBox):
+ def __init__(self, default=None):
+ LayerComboBox.__init__(self)
+ if default is not None:
+ self.set_active_layer(default)
+ def get_value(self):
+ return self.get_active_layer()
+
+class ChannelSelector(ChannelComboBox):
+ def __init__(self, default=None):
+ ChannelComboBox.__init__(self)
+ if default is not None:
+ self.set_active_channel(default)
+ def get_value(self):
+ return self.get_active_channel()
+
+class DrawableSelector(DrawableComboBox):
+ def __init__(self, default=None):
+ DrawableComboBox.__init__(self)
+ if default is not None:
+ self.set_active_drawable(default)
+ def get_value(self):
+ return self.get_active_drawable()
+
+class VectorsSelector(VectorsComboBox):
+ def __init__(self, default=None):
+ VectorsComboBox.__init__(self)
+ if default is not None:
+ self.set_active_vectors(default)
+ def get_value(self):
+ return self.get_active_vectors()
+
+class ColorSelector(ColorButton):
+ def __init__(self, default=gimpcolor.RGB(1.0, 0, 0)):
+ if isinstance(default, gimpcolor.RGB):
+ color = default
+ elif isinstance(default, tuple):
+ color = apply(gimpcolor.RGB, default)
+ elif isinstance(default, str):
+ color = gimpcolor.rgb_parse_css(default)
+ ColorButton.__init__(self, _("Python-Fu Color Selection"), 100, 20,
+ color, COLOR_AREA_FLAT)
+ def get_value(self):
+ return self.get_color();
+
+class PatternSelector(PatternSelectButton):
+ def __init__(self, default=""):
+ PatternSelectButton.__init__(self)
+ if default:
+ self.set_pattern(default)
+ def get_value(self):
+ return self.get_pattern()
+
+class BrushSelector(BrushSelectButton):
+ def __init__(self, default=""):
+ BrushSelectButton.__init__(self)
+ if default:
+ self.set_brush(default, -1.0, -1, -1)
+ def get_value(self):
+ return self.get_brush()[0]
+
+class GradientSelector(GradientSelectButton):
+ def __init__(self, default=""):
+ GradientSelectButton.__init__(self)
+ if default:
+ self.set_gradient(default)
+ def get_value(self):
+ return self.get_gradient()
+
+class PaletteSelector(PaletteSelectButton):
+ def __init__(self, default=""):
+ PaletteSelectButton.__init__(self)
+ if default:
+ self.set_palette(default)
+ def get_value(self):
+ return self.get_palette()
+
+class FontSelector(FontSelectButton):
+ def __init__(self, default="Sans"):
+ FontSelectButton.__init__(self)
+ if default:
+ self.set_font(default)
+ def get_value(self):
+ return self.get_font()
+
+class FileSelector(gtk.FileChooserButton):
+ def __init__(self, default=""):
+ gtk.FileChooserButton.__init__(self, _("Python-Fu File Selection"))
+ if default:
+ self.set_filename(default)
+ def get_value(self):
+ return self.get_filename()
diff --git a/plug-ins/pygimp/gimpuimodule.c b/plug-ins/pygimp/gimpuimodule.c
new file mode 100644
index 0000000..ab4e31d
--- /dev/null
+++ b/plug-ins/pygimp/gimpuimodule.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Python.h>
+
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include <pycairo.h>
+Pycairo_CAPI_t *Pycairo_CAPI;
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "pygimpcolor-api.h"
+#include "pygimp-api.h"
+#include "pygimp-util.h"
+
+
+void gimpui_register_classes(PyObject *d);
+void gimpui_add_constants(PyObject *module, const gchar *strip_prefix);
+extern PyMethodDef gimpui_functions[];
+
+
+static char gimpui_doc[] =
+"This module provides interfaces to allow you to write gimp plug-ins"
+;
+
+void init_gimpui(void);
+
+static gboolean
+init_pycairo(void)
+{
+ Pycairo_IMPORT;
+ if (Pycairo_CAPI == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+extern const char *prog_name;
+
+const char *prog_name = "pygimp";
+
+PyMODINIT_FUNC
+init_gimpui(void)
+{
+ PyObject *m, *d;
+ PyObject *av;
+
+ av = PySys_GetObject("argv");
+ if (av != NULL) {
+ if (PyList_Check(av) && PyList_Size(av) > 0 &&
+ PyString_Check(PyList_GetItem(av, 0)))
+ prog_name = PyString_AsString(PyList_GetItem(av, 0));
+ else
+ PyErr_Warn(PyExc_Warning,
+ "ignoring sys.argv: it must be a list of strings");
+ }
+
+
+ pygimp_init_pygobject();
+
+ init_pygtk();
+ if (!init_pycairo())
+ return;
+ init_pygimpcolor();
+ init_pygimp();
+
+ m = Py_InitModule3("_gimpui", gimpui_functions, gimpui_doc);
+ d = PyModule_GetDict(m);
+
+ gimpui_register_classes(d);
+ gimpui_add_constants(m, "GIMP_");
+
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize module _gimpui");
+}
diff --git a/plug-ins/pygimp/plug-ins/Makefile.am b/plug-ins/pygimp/plug-ins/Makefile.am
new file mode 100644
index 0000000..751f524
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/Makefile.am
@@ -0,0 +1,64 @@
+## Process this file with automake to produce Makefile.in
+
+pluginexecdir = $(gimpplugindir)/plug-ins
+
+source_scripts = \
+ colorxhtml.py \
+ file-openraster.py \
+ foggify.py \
+ gradients-save-as-css.py \
+ histogram-export.py \
+ palette-offset.py \
+ palette-sort.py \
+ palette-to-gradient.py \
+ py-slice.py \
+ python-eval.py \
+ spyro_plus.py \
+ \
+ benchmark-foreground-extract.py \
+ clothify.py \
+ shadow_bevel.py \
+ sphere.py \
+ whirlpinch.py
+
+scripts = \
+ colorxhtml/colorxhtml.py \
+ file-openraster/file-openraster.py \
+ foggify/foggify.py \
+ gradients-save-as-css/gradients-save-as-css.py \
+ histogram-export/histogram-export.py \
+ palette-offset/palette-offset.py \
+ palette-sort/palette-sort.py \
+ palette-to-gradient/palette-to-gradient.py \
+ py-slice/py-slice.py \
+ python-eval/python-eval.py \
+ spyro_plus/spyro_plus.py
+
+test_scripts = \
+ benchmark-foreground-extract/benchmark-foreground-extract.py \
+ clothify/clothify.py \
+ shadow_bevel/shadow_bevel.py \
+ sphere/sphere.py \
+ whirlpinch/whirlpinch.py
+
+$(scripts) $(test_scripts): $(source_scripts)
+ $(AM_V_GEN) mkdir -p $(@D) && cp -f "$(srcdir)/$(@F)" $@
+
+nobase_pluginexec_SCRIPTS = $(scripts)
+
+if GIMP_UNSTABLE
+nobase_pluginexec_SCRIPTS += $(test_scripts)
+endif
+
+# python-console has a data file.
+# Therefore let's move it to its own sub-directory.
+consoleexecdir = $(gimpplugindir)/plug-ins/python-console
+console_scripts = python-console.py
+consoleexec_SCRIPTS = $(console_scripts)
+dist_consoleexec_DATA = pyconsole.py
+
+EXTRA_DIST = \
+ $(source_scripts) \
+ $(console_scripts)
+
+CLEANFILES = $(scripts) $(test_scripts)
diff --git a/plug-ins/pygimp/plug-ins/Makefile.in b/plug-ins/pygimp/plug-ins/Makefile.in
new file mode 100644
index 0000000..a7c7feb
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/Makefile.in
@@ -0,0 +1,946 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@GIMP_UNSTABLE_TRUE@am__append_1 = $(test_scripts)
+subdir = plug-ins/pygimp/plug-ins
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_consoleexec_DATA) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(consoleexecdir)" \
+ "$(DESTDIR)$(pluginexecdir)" "$(DESTDIR)$(consoleexecdir)"
+SCRIPTS = $(consoleexec_SCRIPTS) $(nobase_pluginexec_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(dist_consoleexec_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+pluginexecdir = $(gimpplugindir)/plug-ins
+source_scripts = \
+ colorxhtml.py \
+ file-openraster.py \
+ foggify.py \
+ gradients-save-as-css.py \
+ histogram-export.py \
+ palette-offset.py \
+ palette-sort.py \
+ palette-to-gradient.py \
+ py-slice.py \
+ python-eval.py \
+ spyro_plus.py \
+ \
+ benchmark-foreground-extract.py \
+ clothify.py \
+ shadow_bevel.py \
+ sphere.py \
+ whirlpinch.py
+
+scripts = \
+ colorxhtml/colorxhtml.py \
+ file-openraster/file-openraster.py \
+ foggify/foggify.py \
+ gradients-save-as-css/gradients-save-as-css.py \
+ histogram-export/histogram-export.py \
+ palette-offset/palette-offset.py \
+ palette-sort/palette-sort.py \
+ palette-to-gradient/palette-to-gradient.py \
+ py-slice/py-slice.py \
+ python-eval/python-eval.py \
+ spyro_plus/spyro_plus.py
+
+test_scripts = \
+ benchmark-foreground-extract/benchmark-foreground-extract.py \
+ clothify/clothify.py \
+ shadow_bevel/shadow_bevel.py \
+ sphere/sphere.py \
+ whirlpinch/whirlpinch.py
+
+nobase_pluginexec_SCRIPTS = $(scripts) $(am__append_1)
+
+# python-console has a data file.
+# Therefore let's move it to its own sub-directory.
+consoleexecdir = $(gimpplugindir)/plug-ins/python-console
+console_scripts = python-console.py
+consoleexec_SCRIPTS = $(console_scripts)
+dist_consoleexec_DATA = pyconsole.py
+EXTRA_DIST = \
+ $(source_scripts) \
+ $(console_scripts)
+
+CLEANFILES = $(scripts) $(test_scripts)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/pygimp/plug-ins/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/pygimp/plug-ins/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-consoleexecSCRIPTS: $(consoleexec_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(consoleexec_SCRIPTS)'; test -n "$(consoleexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(consoleexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(consoleexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(consoleexecdir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(consoleexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-consoleexecSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(consoleexec_SCRIPTS)'; test -n "$(consoleexecdir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(consoleexecdir)'; $(am__uninstall_files_from_dir)
+install-nobase_pluginexecSCRIPTS: $(nobase_pluginexec_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(nobase_pluginexec_SCRIPTS)'; test -n "$(pluginexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pluginexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pluginexecdir)" || exit 1; \
+ fi; \
+ $(am__nobase_strip_setup); \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e "s|$$srcdirstrip/||" -e 'h;s|[^/]*$$||; s|^$$|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ case $$type in \
+ d) echo " $(MKDIR_P) '$(DESTDIR)$(pluginexecdir)/$$dir'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pluginexecdir)/$$dir" || exit $$?;; \
+ f) \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pluginexecdir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pluginexecdir)$$dir" || exit $$?; \
+ } \
+ ;; esac \
+ ; done
+
+uninstall-nobase_pluginexecSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nobase_pluginexec_SCRIPTS)'; test -n "$(pluginexecdir)" || exit 0; \
+ $(am__nobase_strip_setup); \
+ files=`$(am__nobase_strip) \
+ -e 'h;s,.*/,,;$(transform);x;s|[^/]*$$||;G;s,\n,,'`; \
+ dir='$(DESTDIR)$(pluginexecdir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-dist_consoleexecDATA: $(dist_consoleexec_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_consoleexec_DATA)'; test -n "$(consoleexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(consoleexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(consoleexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(consoleexecdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(consoleexecdir)" || exit $$?; \
+ done
+
+uninstall-dist_consoleexecDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_consoleexec_DATA)'; test -n "$(consoleexecdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(consoleexecdir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(SCRIPTS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(consoleexecdir)" "$(DESTDIR)$(pluginexecdir)" "$(DESTDIR)$(consoleexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-consoleexecSCRIPTS \
+ install-dist_consoleexecDATA install-nobase_pluginexecSCRIPTS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-consoleexecSCRIPTS \
+ uninstall-dist_consoleexecDATA \
+ uninstall-nobase_pluginexecSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-consoleexecSCRIPTS install-data \
+ install-data-am install-dist_consoleexecDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-nobase_pluginexecSCRIPTS install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-consoleexecSCRIPTS \
+ uninstall-dist_consoleexecDATA \
+ uninstall-nobase_pluginexecSCRIPTS
+
+.PRECIOUS: Makefile
+
+
+$(scripts) $(test_scripts): $(source_scripts)
+ $(AM_V_GEN) mkdir -p $(@D) && cp -f "$(srcdir)/$(@F)" $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/pygimp/plug-ins/benchmark-foreground-extract.py b/plug-ins/pygimp/plug-ins/benchmark-foreground-extract.py
new file mode 100755
index 0000000..2637547
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/benchmark-foreground-extract.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python2
+
+# Foreground Extraction Benchmark
+# Copyright 2005 Sven Neumann <sven@gimp.org>
+#
+# This is a from-scratch implementation of the benchmark proposed in
+# "GrabCut": interactive foreground extraction using iterated graph
+# cuts published in the Proceedings of the 2004 SIGGRAPH Conference.
+#
+# No guarantee is made that this benchmark produces the same results
+# as the cited benchmark but the goal is that it does. So if you find
+# any bugs or inaccuracies in this code, please let us know.
+#
+# The benchmark has been adapted work with the SIOX algorithm
+# (http://www.siox.org). which is (currently) the only
+# implementation of gimp_drawable_foreground_extract(). If other
+# implementations are being added, this benchmark should be changed
+# accordingly.
+#
+# You will need a set of test images to run this benchmark, preferably
+# the original set of 50 images. Some of these images are from the
+# Berkeley Segmentation Dataset
+# (http://www.cs.berkeley.edu/projects/vision/grouping/segbench/).
+# See also http://www.siox.org/details.html for trimaps.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+import os, re, struct, sys, time
+
+from gimpfu import *
+
+
+def benchmark (folder, save_output):
+ folder = os.path.abspath (folder)
+ if not os.path.exists (folder):
+ gimp.message("Folder '" + folder + "' doesn't exist.\n")
+ return;
+
+ total_unclassified = 0
+ total_misclassified = 0
+ total_time = 0.0
+
+ images = os.path.join (folder, "images")
+ for name in os.listdir (images):
+
+ try:
+ gimp.delete (image_display)
+ gimp.delete (mask_display)
+ except UnboundLocalError:
+ pass
+
+ image_name = os.path.join (images, name)
+
+ # FIXME: improve this!
+ name = re.sub (r'\.jpg$', '', name)
+ name = re.sub (r'\.JPG$', '', name)
+ name = re.sub (r'\.bmp$', '', name)
+
+ mask_name = os.path.join (folder, "cm_bmp", name + '.png')
+ truth_name = os.path.join (folder, "truth", name + '.bmp')
+
+ image = pdb.gimp_file_load (image_name, image_name)
+ image_layer = image.active_layer;
+
+ mask = pdb.gimp_file_load (mask_name, mask_name)
+ convert_grayscale (mask)
+ mask_layer = mask.active_layer;
+
+ truth = pdb.gimp_file_load (truth_name, truth_name)
+ convert_grayscale (truth)
+ truth_layer = truth.active_layer;
+
+ unclassified = unclassified_pixels (mask_layer, truth_layer)
+
+ sys.stderr.write (os.path.basename (image_name))
+
+ start = time.time ()
+ pdb.gimp_drawable_foreground_extract (image_layer,
+ FOREGROUND_EXTRACT_SIOX,
+ mask_layer)
+ end = time.time ()
+
+ sys.stderr.write (" ")
+
+ mask_layer.flush ()
+
+ # Ignore errors when creating image displays;
+ # allows us to be used without a display.
+ try:
+ image_display = pdb.gimp_display_new (image)
+ mask_display = pdb.gimp_display_new (mask)
+
+ gimp.displays_flush ()
+ time.sleep (1.0)
+ except:
+ pass
+
+ gimp.delete (image)
+
+ misclassified = misclassified_pixels (mask_layer, truth_layer)
+
+ sys.stderr.write ("%d %d %.2f%% %.3fs\n" %
+ (unclassified, misclassified,
+ (misclassified * 100.0 / unclassified),
+ end - start))
+
+ total_unclassified += unclassified
+ total_misclassified += misclassified
+ total_time += end - start
+
+ gimp.delete (truth)
+
+ if save_output:
+ filename = os.path.join (folder, "output", name + '.png')
+ pdb.gimp_file_save (mask, mask_layer, filename, filename)
+
+ gimp.delete (mask)
+
+ # for loop ends
+
+ try:
+ gimp.delete (image_display)
+ gimp.delete (mask_display)
+ except UnboundLocalError:
+ pass
+
+ sys.stderr.write ("Total: %d %d %.2f%% %.3fs\n" %
+ (total_unclassified, total_misclassified,
+ (total_misclassified * 100.0 / total_unclassified),
+ total_time))
+
+def convert_grayscale (image):
+ if image.base_type != GRAY:
+ pdb.gimp_image_convert_grayscale (image)
+
+
+def unclassified_pixels (mask, truth):
+ (mean, std_dev, median, pixels,
+ count, percentile) = pdb.gimp_histogram (mask, HISTOGRAM_VALUE, 1, 254)
+
+ return count
+
+
+def misclassified_pixels (mask, truth):
+ image = truth.image
+
+ copy = pdb.gimp_layer_new_from_drawable (mask, image)
+ copy.name = "Difference"
+ copy.mode = DIFFERENCE_MODE
+
+ image.insert_layer (copy)
+
+ # The assumption made here is that the output of
+ # foreground_extract is a strict black and white mask. The truth
+ # however may contain unclassified pixels. These are considered
+ # unknown, a strict segmentation isn't possible here.
+ #
+ # The result of using the Difference mode as done here is that
+ # pure black pixels in the result can be considered correct.
+ # White pixels are wrong. Gray values were unknown in the truth
+ # and thus are not counted as wrong.
+
+ (mean, std_dev, median, pixels,
+ count, percentile) = pdb.gimp_histogram (image.flatten (),
+ HISTOGRAM_VALUE, 255, 255)
+
+ return count
+
+
+register (
+ "python-fu-benchmark-foreground-extract",
+ "Benchmark and regression test for the SIOX algorithm",
+ "",
+ "Sven Neumann",
+ "Sven Neumann",
+ "2005",
+ "Foreground Extraction",
+ "",
+ [ (PF_FILE, "image-folder", "Image folder",
+ "~/segmentation/msbench/imagedata"),
+ (PF_TOGGLE, "save-output", "Save output images", False) ],
+ [],
+ benchmark, menu="<Image>/Filters/Extensions/Benchmark")
+
+main ()
diff --git a/plug-ins/pygimp/plug-ins/clothify.py b/plug-ins/pygimp/plug-ins/clothify.py
new file mode 100755
index 0000000..258329c
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/clothify.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import math
+from gimpfu import *
+
+def clothify(timg, tdrawable, bx=9, by=9, azimuth=135, elevation=45, depth=3):
+ width = tdrawable.width
+ height = tdrawable.height
+
+ img = gimp.Image(width, height, RGB)
+ img.disable_undo()
+
+ layer_one = gimp.Layer(img, "X Dots", width, height, RGB_IMAGE,
+ 100, NORMAL_MODE)
+ img.insert_layer(layer_one)
+ pdb.gimp_edit_fill(layer_one, BACKGROUND_FILL)
+
+ pdb.plug_in_noisify(img, layer_one, 0, 0.7, 0.7, 0.7, 0.7)
+
+ layer_two = layer_one.copy()
+ layer_two.mode = MULTIPLY_MODE
+ layer_two.name = "Y Dots"
+ img.insert_layer(layer_two)
+
+ pdb.plug_in_gauss_rle(img, layer_one, bx, 1, 0)
+ pdb.plug_in_gauss_rle(img, layer_two, by, 0, 1)
+
+ img.flatten()
+
+ bump_layer = img.active_layer
+
+ pdb.plug_in_c_astretch(img, bump_layer)
+ pdb.plug_in_noisify(img, bump_layer, 0, 0.2, 0.2, 0.2, 0.2)
+ pdb.plug_in_bump_map(img, tdrawable, bump_layer, azimuth,
+ elevation, depth, 0, 0, 0, 0, True, False, 0)
+
+ gimp.delete(img)
+
+register(
+ "python-fu-clothify",
+ "Make the image look like it is printed on cloth",
+ "Make the specified layer look like it is printed on cloth",
+ "James Henstridge",
+ "James Henstridge",
+ "1997-1999",
+ "_Clothify...",
+ "RGB*, GRAY*",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_INT, "x-blur", "X blur", 9),
+ (PF_INT, "y-blur", "Y blur", 9),
+ (PF_INT, "azimuth", "Azimuth", 135),
+ (PF_INT, "elevation", "Elevation", 45),
+ (PF_INT, "depth", "Depth", 3)
+ ],
+ [],
+ clothify, menu="<Image>/Filters/Artistic")
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/colorxhtml.py b/plug-ins/pygimp/plug-ins/colorxhtml.py
new file mode 100755
index 0000000..011f8ca
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/colorxhtml.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 2003, 2005 Manish Singh <yosh@gimp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import string
+import struct
+import inspect
+import os.path
+
+import gimp
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+all_source_types = (CHARS_SOURCE, CHARS_FILE, CHARS_PARAMETER) = range(3)
+
+escape_table = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;'
+}
+
+style_def = """body {
+ width: 100%%;
+ font-size: %dpx;
+ background-color: #000000;
+ color: #ffffff;
+}
+"""
+
+preamble = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+<title>CSS Color XHTML written by GIMP</title>
+%s
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+</head>
+<body>
+<pre>
+"""
+
+postamble = """\n</pre>\n</body>\n</html>\n"""
+
+def colorxhtml(img, drawable, filename, raw_filename,
+ source_type, characters, size, separate):
+ width = drawable.width
+ height = drawable.height
+ bpp = drawable.bpp
+
+ if not drawable.is_rgb or drawable.has_alpha:
+ return
+
+ if source_type not in all_source_types:
+ return
+
+ gimp.tile_cache_ntiles(width / gimp.tile_width() + 1)
+
+ html = file(filename, 'w')
+
+ if separate:
+ dirname, cssfile = os.path.split(filename)
+ cssfile = os.path.splitext(cssfile)[0] + '.css'
+ cssname = os.path.join(dirname, cssfile)
+
+ css = file(cssname, 'w')
+
+ if source_type == CHARS_SOURCE:
+ chars = file(inspect.getsourcefile(colorxhtml)).read()
+ elif source_type == CHARS_FILE:
+ chars = file(characters).read()
+ elif source_type == CHARS_PARAMETER:
+ chars = characters
+
+ allchars = string.maketrans('', '')
+
+ goodchars = string.digits + string.ascii_letters + string.punctuation
+ badchars = ''.join([c for c in allchars if c not in goodchars])
+
+ chars = chars.translate(allchars, badchars)
+
+ data = []
+
+ for c in chars:
+ data.append(escape_table.get(c, c))
+
+ if data:
+ data.reverse()
+ else:
+ data = list('X' * 80)
+
+ pr = drawable.get_pixel_rgn(0, 0, width, height, False, False)
+
+ gimp.progress_init(_("Saving as colored XHTML"))
+
+ style = style_def % size
+
+ if separate:
+ ss = '<link rel="stylesheet" type="text/css" href="%s" />' % cssfile
+ css.write(style)
+ else:
+ ss = '<style type="text/css">\n%s</style>' % style
+
+ html.write(preamble % ss)
+
+ colors = {}
+ chars = []
+
+ for y in range(0, height):
+ row = pr[0:width, y]
+
+ while len(chars) < width:
+ chars[0:0] = data
+
+ for pixel in RowIterator(row, bpp):
+ color = '%02x%02x%02x' % pixel
+ style = 'background-color:black; color:#%s;' % color
+ char = chars.pop()
+
+ if separate:
+ if color not in colors:
+ css.write('span.N%s { %s }\n' % (color, style))
+ colors[color] = 1
+
+ html.write('<span class="N%s">%s</span>' % (color, char))
+
+ else:
+ html.write('<span style="%s">%s</span>' % (style, char))
+
+ html.write('\n')
+
+ gimp.progress_update(y / float(height))
+
+ html.write(postamble)
+
+ html.close()
+
+ if separate:
+ css.close()
+
+def register_save():
+ gimp.register_save_handler("file-colorxhtml-save", "xhtml", "")
+
+class RowIterator:
+ def __init__(self, row, bpp):
+ self.row = row
+ self.bpp = bpp
+
+ self.start = 0
+ self.stop = bpp
+
+ self.length = len(row)
+ self.fmt = 'B' * bpp
+
+ def __iter__(self):
+ return iter(self.get_pixel, None)
+
+ def get_pixel(self):
+ if self.stop > self.length:
+ return None
+
+ pixel = struct.unpack(self.fmt, self.row[self.start:self.stop])
+
+ self.start += self.bpp
+ self.stop += self.bpp
+
+ return pixel
+
+register(
+ "file-colorxhtml-save",
+ N_("Save as colored XHTML"),
+ "Saves the image as colored XHTML text (based on Perl version by Marc Lehmann)",
+ "Manish Singh and Carol Spears",
+ "Manish Singh and Carol Spears",
+ "2003",
+ N_("Colored XHTML"),
+ "RGB",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_STRING, "filename", "The name of the file", None),
+ (PF_STRING, "raw-filename", "The name of the file", None),
+ (PF_RADIO, "source", _("Character _source"), 0,
+ ((_("Source code"), CHARS_SOURCE),
+ (_("Text file"), CHARS_FILE),
+ (_("Entry box"), CHARS_PARAMETER))),
+ (PF_FILE, "characters", _("_File to read or characters to use"),
+ ""),
+ (PF_INT, "font-size", _("Fo_nt size in pixels"), 10),
+ (PF_BOOL, "separate", _("_Write a separate CSS file"), True)
+ ],
+ [],
+ colorxhtml, on_query=register_save,
+ menu="<Save>", domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/file-openraster.py b/plug-ins/pygimp/plug-ins/file-openraster.py
new file mode 100755
index 0000000..c55b1b6
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/file-openraster.py
@@ -0,0 +1,404 @@
+#!/usr/bin/env python2
+
+# GIMP Plug-in for the OpenRaster file format
+# http://create.freedesktop.org/wiki/OpenRaster
+
+# Copyright (C) 2009 by Jon Nordby <jononor@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Based on MyPaint source code by Martin Renold
+# http://gitorious.org/mypaint/mypaint/blobs/edd84bcc1e091d0d56aa6d26637aa8a925987b6a/lib/document.py
+
+import os, sys, tempfile, zipfile
+import xml.etree.ElementTree as ET
+
+
+from gimpfu import *
+
+NESTED_STACK_END = object()
+
+
+layermodes_map = {
+ "svg:src-over": LAYER_MODE_NORMAL,
+ "svg:multiply": LAYER_MODE_MULTIPLY_LEGACY,
+ "svg:screen": LAYER_MODE_SCREEN_LEGACY,
+ "svg:overlay": LAYER_MODE_OVERLAY,
+ "svg:darken": LAYER_MODE_DARKEN_ONLY_LEGACY,
+ "svg:lighten": LAYER_MODE_LIGHTEN_ONLY_LEGACY,
+ "svg:color-dodge": LAYER_MODE_DODGE_LEGACY,
+ "svg:color-burn": LAYER_MODE_BURN_LEGACY,
+ "svg:hard-light": LAYER_MODE_HARDLIGHT_LEGACY,
+ "svg:soft-light": LAYER_MODE_SOFTLIGHT_LEGACY,
+ "svg:difference": LAYER_MODE_DIFFERENCE_LEGACY,
+ "svg:color": LAYER_MODE_HSL_COLOR_LEGACY,
+ "svg:luminosity": LAYER_MODE_HSV_VALUE_LEGACY,
+ "svg:hue": LAYER_MODE_HSV_HUE_LEGACY,
+ "svg:saturation": LAYER_MODE_HSV_SATURATION_LEGACY,
+ "svg:plus": LAYER_MODE_ADDITION_LEGACY,
+}
+
+def reverse_map(mapping):
+ return dict((v,k) for k, v in mapping.iteritems())
+
+def get_image_attributes(orafile):
+ xml = orafile.read('stack.xml')
+ image = ET.fromstring(xml)
+ stack = image.find('stack')
+ w = int(image.attrib.get('w', ''))
+ h = int(image.attrib.get('h', ''))
+
+ return stack, w, h
+
+def get_layer_attributes(layer):
+ a = layer.attrib
+ path = a.get('src', '')
+ name = a.get('name', '')
+ x = int(a.get('x', '0'))
+ y = int(a.get('y', '0'))
+ opac = float(a.get('opacity', '1.0'))
+ visible = a.get('visibility', 'visible') != 'hidden'
+ m = a.get('composite-op', 'svg:src-over')
+ layer_mode = layermodes_map.get(m, LAYER_MODE_NORMAL)
+
+ return path, name, x, y, opac, visible, layer_mode
+
+def get_group_layer_attributes(layer):
+ a = layer.attrib
+ name = a.get('name', '')
+ opac = float(a.get('opacity', '1.0'))
+ visible = a.get('visibility', 'visible') != 'hidden'
+ m = a.get('composite-op', 'svg:src-over')
+ layer_mode = layermodes_map.get(m, NORMAL_MODE)
+
+ return name, 0, 0, opac, visible, layer_mode
+
+def thumbnail_ora(filename, thumb_size):
+ # FIXME: Untested. Does not seem to be used at all? should be run
+ # when registered and there is no thumbnail in cache
+ tempdir = tempfile.mkdtemp('gimp-plugin-file-openraster')
+ original_name = filename
+ try:
+ if not isinstance(filename, str):
+ filename = filename.decode("utf-8")
+ orafile = zipfile.ZipFile(filename.encode(sys.getfilesystemencoding() or "utf-8"))
+ except (UnicodeDecodeError, IOError):
+ # Someone may try to open an actually garbled name, and pass a raw
+ # non-utf 8 filename:
+ orafile = zipfile.ZipFile(original_name)
+ orafile = zipfile.ZipFile(filename)
+ stack, w, h = get_image_attributes(orafile)
+
+ # create temp file
+ tmp = os.path.join(tempdir, 'tmp.png')
+ f = open(tmp, 'wb')
+ f.write(orafile.read('Thumbnails/thumbnail.png'))
+ f.close()
+
+ img = pdb['file-png-load'](tmp, 'tmp.png')
+ # TODO: scaling
+ os.remove(tmp)
+ os.rmdir(tempdir)
+
+ return (img, w, h)
+
+def save_ora(img, drawable, filename, raw_filename):
+ def write_file_str(zfile, fname, data):
+ # work around a permission bug in the zipfile library:
+ # http://bugs.python.org/issue3394
+ zi = zipfile.ZipInfo(fname)
+ zi.external_attr = int("100644", 8) << 16
+ zfile.writestr(zi, data)
+
+ tempdir = tempfile.mkdtemp('gimp-plugin-file-openraster')
+
+ if isinstance(filename, str):
+ try:
+ filename = filename.decode("utf-8")
+ except UnicodeDecodeError:
+ # 1 - 1 correspondence between raw_bytes and UCS-2 used by Python
+ # Unicode characters
+ filename = filename.decode("latin1")
+ encoding = sys.getfilesystemencoding() or "utf-8"
+ filename = filename.encode(encoding)
+ tmp_sufix = ".tmpsave".encode(encoding)
+ # use .tmpsave extension, so we don't overwrite a valid file if
+ # there is an exception
+ orafile = zipfile.ZipFile(filename + tmp_sufix, 'w', compression=zipfile.ZIP_STORED)
+
+ write_file_str(orafile, 'mimetype', 'image/openraster') # must be the first file written
+
+ # build image attributes
+ image = ET.Element('image')
+ stack = ET.SubElement(image, 'stack')
+ a = image.attrib
+ a['w'] = str(img.width)
+ a['h'] = str(img.height)
+
+ def store_layer(img, drawable, path):
+ tmp = os.path.join(tempdir, 'tmp.png')
+ interlace, compression = 0, 2
+ png_chunks = (1, 1, 0, 1, 1) # write all PNG chunks except oFFs(ets)
+ pdb['file-png-save'](img, drawable, tmp, 'tmp.png',
+ interlace, compression, *png_chunks)
+ orafile.write(tmp, path)
+ os.remove(tmp)
+
+ def add_layer(parent, x, y, opac, gimp_layer, path, visible=True):
+ store_layer(img, gimp_layer, path)
+ # create layer attributes
+ layer = ET.Element('layer')
+ parent.append(layer)
+ a = layer.attrib
+ a['src'] = path
+ a['name'] = gimp_layer.name
+ a['x'] = str(x)
+ a['y'] = str(y)
+ a['opacity'] = str(opac)
+ a['visibility'] = 'visible' if visible else 'hidden'
+ a['composite-op'] = reverse_map(layermodes_map).get(gimp_layer.mode, 'svg:src-over')
+ return layer
+
+ def add_group_layer(parent, opac, gimp_layer, visible=True):
+ # create layer attributes
+ group_layer = ET.Element('stack')
+ parent.append(group_layer)
+ a = group_layer.attrib
+ a['name'] = gimp_layer.name
+ a['opacity'] = str(opac)
+ a['visibility'] = 'visible' if visible else 'hidden'
+ a['composite-op'] = reverse_map(layermodes_map).get(gimp_layer.mode, 'svg:src-over')
+ return group_layer
+
+
+ def enumerate_layers(group):
+ for layer in group.layers:
+ if not isinstance(layer, gimp.GroupLayer):
+ yield layer
+ else:
+ yield layer
+ for sublayer in enumerate_layers(layer):
+ yield sublayer
+ yield NESTED_STACK_END
+
+ # save layers
+ parent_groups = []
+ i = 0
+ for lay in enumerate_layers(img):
+ if lay is NESTED_STACK_END:
+ parent_groups.pop()
+ continue
+ x, y = lay.offsets
+ opac = lay.opacity / 100.0 # needs to be between 0.0 and 1.0
+
+ if not parent_groups:
+ path_name = 'data/{:03d}.png'.format(i)
+ i += 1
+ else:
+ path_name = 'data/{}-{:03d}.png'.format(
+ parent_groups[-1][1], parent_groups[-1][2])
+ parent_groups[-1][2] += 1
+
+ parent = stack if not parent_groups else parent_groups[-1][0]
+
+ if isinstance(lay, gimp.GroupLayer):
+ group = add_group_layer(parent, opac, lay, lay.visible)
+ group_path = ("{:03d}".format(i) if not parent_groups else
+ parent_groups[-1][1] + "-{:03d}".format(parent_groups[-1][2]))
+ parent_groups.append([group, group_path , 0])
+ else:
+ add_layer(parent, x, y, opac, lay, path_name, lay.visible)
+
+ # save mergedimage
+ thumb = pdb['gimp-image-duplicate'](img)
+ thumb_layer = thumb.merge_visible_layers (CLIP_TO_IMAGE)
+ store_layer (thumb, thumb_layer, 'mergedimage.png')
+
+ # save thumbnail
+ w, h = img.width, img.height
+ if max (w, h) > 256:
+ # should be at most 256x256, without changing aspect ratio
+ if w > h:
+ w, h = 256, max(h*256/w, 1)
+ else:
+ w, h = max(w*256/h, 1), 256
+ thumb_layer.scale(w, h)
+ if thumb.precision != PRECISION_U8_GAMMA:
+ pdb.gimp_image_convert_precision (thumb, PRECISION_U8_GAMMA)
+ store_layer(thumb, thumb_layer, 'Thumbnails/thumbnail.png')
+ gimp.delete(thumb)
+
+ # write stack.xml
+ xml = ET.tostring(image, encoding='UTF-8')
+ write_file_str(orafile, 'stack.xml', xml)
+
+ # finish up
+ orafile.close()
+ os.rmdir(tempdir)
+ if os.path.exists(filename):
+ os.remove(filename) # win32 needs that
+ os.rename(filename + tmp_sufix, filename)
+
+
+def load_ora(filename, raw_filename):
+ tempdir = tempfile.mkdtemp('gimp-plugin-file-openraster')
+ original_name = filename
+ try:
+ if not isinstance(filename, str):
+ filename = filename.decode("utf-8")
+ orafile = zipfile.ZipFile(filename.encode(sys.getfilesystemencoding() or "utf-8"))
+ except (UnicodeDecodeError, IOError):
+ # Someone may try to open an actually garbled name, and pass a raw
+ # non-utf 8 filename:
+ orafile = zipfile.ZipFile(original_name)
+ stack, w, h = get_image_attributes(orafile)
+
+ img = gimp.Image(w, h, RGB)
+ img.filename = filename
+
+ def get_layers(root):
+ """iterates over layers and nested stacks"""
+ for item in root:
+ if item.tag == 'layer':
+ yield item
+ elif item.tag == 'stack':
+ yield item
+ for subitem in get_layers(item):
+ yield subitem
+ yield NESTED_STACK_END
+
+ parent_groups = []
+
+ layer_no = 0
+ for item in get_layers(stack):
+
+ if item is NESTED_STACK_END:
+ parent_groups.pop()
+ continue
+
+ if item.tag == 'stack':
+ name, x, y, opac, visible, layer_mode = get_group_layer_attributes(item)
+ gimp_layer = gimp.GroupLayer(img)
+
+ else:
+ path, name, x, y, opac, visible, layer_mode = get_layer_attributes(item)
+
+ if not path.lower().endswith('.png'):
+ continue
+ if not name:
+ # use the filename without extension as name
+ n = os.path.basename(path)
+ name = os.path.splitext(n)[0]
+
+ # create temp file. Needed because gimp cannot load files from inside a zip file
+ tmp = os.path.join(tempdir, 'tmp.png')
+ f = open(tmp, 'wb')
+ try:
+ data = orafile.read(path)
+ except KeyError:
+ # support for bad zip files (saved by old versions of this plugin)
+ data = orafile.read(path.encode('utf-8'))
+ print 'WARNING: bad OpenRaster ZIP file. There is an utf-8 encoded filename that does not have the utf-8 flag set:', repr(path)
+ f.write(data)
+ f.close()
+
+ # import layer, set attributes and add to image
+ gimp_layer = pdb['gimp-file-load-layer'](img, tmp)
+ os.remove(tmp)
+ gimp_layer.name = name
+ gimp_layer.mode = layer_mode
+ gimp_layer.set_offsets(x, y) # move to correct position
+ gimp_layer.opacity = opac * 100 # a float between 0 and 100
+ gimp_layer.visible = visible
+
+ pdb.gimp_image_insert_layer(img, gimp_layer,
+ parent_groups[-1][0] if parent_groups else None,
+ parent_groups[-1][1] if parent_groups else layer_no)
+ if parent_groups:
+ parent_groups[-1][1] += 1
+ else:
+ layer_no += 1
+
+ if isinstance(gimp_layer, gimp.GroupLayer):
+ parent_groups.append([gimp_layer, 0])
+
+ os.rmdir(tempdir)
+
+ return img
+
+
+def register_load_handlers():
+ gimp.register_load_handler('file-openraster-load', 'ora', '')
+ pdb['gimp-register-file-handler-mime']('file-openraster-load', 'image/openraster')
+ pdb['gimp-register-thumbnail-loader']('file-openraster-load', 'file-openraster-load-thumb')
+
+def register_save_handlers():
+ gimp.register_save_handler('file-openraster-save', 'ora', '')
+
+register(
+ 'file-openraster-load-thumb', #name
+ 'loads a thumbnail from an OpenRaster (.ora) file', #description
+ 'loads a thumbnail from an OpenRaster (.ora) file',
+ 'Jon Nordby', #author
+ 'Jon Nordby', #copyright
+ '2009', #year
+ None,
+ None, #image type
+ [ #input args. Format (type, name, description, default [, extra])
+ (PF_STRING, 'filename', 'The name of the file to load', None),
+ (PF_INT, 'thumb-size', 'Preferred thumbnail size', None),
+ ],
+ [ #results. Format (type, name, description)
+ (PF_IMAGE, 'image', 'Thumbnail image'),
+ (PF_INT, 'image-width', 'Width of full-sized image'),
+ (PF_INT, 'image-height', 'Height of full-sized image')
+ ],
+ thumbnail_ora, #callback
+ run_mode_param = False
+)
+
+register(
+ 'file-openraster-save', #name
+ 'save an OpenRaster (.ora) file', #description
+ 'save an OpenRaster (.ora) file',
+ 'Jon Nordby', #author
+ 'Jon Nordby', #copyright
+ '2009', #year
+ 'OpenRaster',
+ '*',
+ [ #input args. Format (type, name, description, default [, extra])
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_STRING, "filename", "The name of the file", None),
+ (PF_STRING, "raw-filename", "The name of the file", None),
+ ],
+ [], #results. Format (type, name, description)
+ save_ora, #callback
+ on_query = register_save_handlers,
+ menu = '<Save>'
+)
+
+register(
+ 'file-openraster-load', #name
+ 'load an OpenRaster (.ora) file', #description
+ 'load an OpenRaster (.ora) file',
+ 'Jon Nordby', #author
+ 'Jon Nordby', #copyright
+ '2009', #year
+ 'OpenRaster',
+ None, #image type
+ [ #input args. Format (type, name, description, default [, extra])
+ (PF_STRING, 'filename', 'The name of the file to load', None),
+ (PF_STRING, 'raw-filename', 'The name entered', None),
+ ],
+ [(PF_IMAGE, 'image', 'Output image')], #results. Format (type, name, description)
+ load_ora, #callback
+ on_query = register_load_handlers,
+ menu = "<Load>",
+)
+
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/foggify.py b/plug-ins/pygimp/plug-ins/foggify.py
new file mode 100755
index 0000000..4964d8c
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/foggify.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+import time
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def foggify(img, layer, name, colour, turbulence, opacity):
+
+ gimp.context_push()
+ img.undo_group_start()
+
+ if img.base_type is RGB:
+ type = RGBA_IMAGE
+ else:
+ type = GRAYA_IMAGE
+ fog = gimp.Layer(img, name,
+ layer.width, layer.height, type, opacity, NORMAL_MODE)
+ fog.fill(FILL_TRANSPARENT)
+ img.insert_layer(fog)
+
+ gimp.set_background(colour)
+ pdb.gimp_edit_fill(fog, FILL_BACKGROUND)
+
+ # create a layer mask for the new layer
+ mask = fog.create_mask(0)
+ fog.add_mask(mask)
+
+ # add some clouds to the layer
+ pdb.plug_in_plasma(img, mask, int(time.time()), turbulence)
+
+ # apply the clouds to the layer
+ fog.remove_mask(MASK_APPLY)
+
+ img.undo_group_end()
+ gimp.context_pop()
+
+register(
+ "python-fu-foggify",
+ N_("Add a layer of fog"),
+ "Adds a layer of fog to the image.",
+ "James Henstridge",
+ "James Henstridge",
+ "1999,2007",
+ N_("_Fog..."),
+ "RGB*, GRAY*",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_STRING, "name", _("_Layer name"), _("Clouds")),
+ (PF_COLOUR, "colour", _("_Fog color"), (240, 180, 70)),
+ (PF_SLIDER, "turbulence", _("_Turbulence"), 1.0, (0, 7, 0.1)),
+ (PF_SLIDER, "opacity", _("Op_acity"), 100, (0, 100, 1)),
+ ],
+ [],
+ foggify,
+ menu="<Image>/Filters/Decor",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/gradients-save-as-css.py b/plug-ins/pygimp/plug-ins/gradients-save-as-css.py
new file mode 100755
index 0000000..44179e4
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/gradients-save-as-css.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Allows saving (TODO: and loading) CSS gradient files
+# Copyright (C) 2011 João S. O. Bueno <gwidion@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+# Currently this exports all color segments as RGB linear centered segments.
+# TODO: Respect gradient alpha, off-center segments, different blending
+# functions and HSV colors
+
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+w3c_template = """background-image: linear-gradient(top, %s);\n"""
+moz_template = """background-image: -moz-linear-gradient(center top, %s);\n"""
+webkit_template = """background-image: -webkit-gradient(linear, """ \
+ """left top, left bottom, %s);\n"""
+
+color_to_html = lambda c: "rgb(%d,%d,%d)" % tuple(c)[:3]
+
+def format_text(text):
+ counter = 0
+ new_text = []
+ for token in text.split(","):
+ if counter + len(token) > 77:
+ token = "\n " + token
+ counter = 4
+ new_text.append(token)
+ if "\n" in token:
+ counter = len(token.rsplit("\n")[-1]) + 1
+ else:
+ counter += len(token) + 1
+
+ return ",".join(new_text)
+
+def gradient_css_save(gradient, file_name):
+ stops = []
+ wk_stops = []
+ n_segments = pdb.gimp_gradient_get_number_of_segments(gradient)
+ last_stop = None
+ for index in xrange(n_segments):
+ lcolor, lopacity = pdb.gimp_gradient_segment_get_left_color(
+ gradient,
+ index)
+ rcolor, ropacity = pdb.gimp_gradient_segment_get_right_color(
+ gradient,
+ index)
+ lpos = pdb.gimp_gradient_segment_get_left_pos(gradient, index)
+ rpos = pdb.gimp_gradient_segment_get_right_pos(gradient, index)
+
+ lstop = color_to_html(lcolor) + " %d%%" % int(100 * lpos)
+ wk_lstop = "color-stop(%.03f, %s)" %(lpos, color_to_html(lcolor))
+ if lstop != last_stop:
+ stops.append(lstop)
+ wk_stops.append(wk_lstop)
+
+ rstop = color_to_html(rcolor) + " %d%%" % int(100 * rpos)
+ wk_rstop = "color-stop(%.03f, %s)" %(rpos, color_to_html(rcolor))
+
+ stops.append(rstop)
+ wk_stops.append(wk_rstop)
+ last_stop = rstop
+
+ final_text = w3c_template % ", ".join(stops)
+ final_text += moz_template % ",".join(stops)
+ final_text += webkit_template % ",".join(wk_stops)
+
+ with open(file_name, "wt") as file_:
+ file_.write(format_text(final_text))
+
+register(
+ "gradient-save-as-css",
+ "Creates a new palette from a given gradient",
+ "palette_from_gradient (gradient, number, segment_colors) -> None",
+ "Joao S. O. Bueno",
+ "(c) GPL V3.0 or later",
+ "2011",
+ "Save as CSS...",
+ "",
+ [
+ (PF_GRADIENT, "gradient", N_("Gradient to use"),""),
+ (PF_FILENAME, "file_name", N_("File Name"), ""),
+ ],
+ [],
+ gradient_css_save,
+ menu="<Gradients>",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+main() \ No newline at end of file
diff --git a/plug-ins/pygimp/plug-ins/histogram-export.py b/plug-ins/pygimp/plug-ins/histogram-export.py
new file mode 100755
index 0000000..11da1ba
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/histogram-export.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python2
+#coding: utf-8
+
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""
+Exports the image histogram to a text file,
+so that it can be used by other programs
+and loaded into spreadsheets.
+
+The resulting file is a CSV file (Comma Separated
+Values), which can be imported
+directly in most spreadsheet programs.
+
+The first two collums are the bucket boundaries,
+followed by the selected columns. The histogram
+refers to the selected image area, and
+can use either Sample Average data or data
+from the current drawable only.;
+
+The output is in "weighted pixels" - meaning
+all fully transparent pixels are not counted.
+
+Check the gimp-histogram call
+"""
+
+
+from gimpfu import *
+import csv
+import gettext
+
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def histogram_export(img, drw, filename,
+ bucket_size, sample_average, output_format):
+ if sample_average:
+ new_img = pdb.gimp_image_duplicate(img)
+ drw = pdb.gimp_image_merge_visible_layers(new_img, CLIP_TO_IMAGE)
+ # TODO: grey images, alpha and non alpha images.
+ channels_txt = ["Value"]
+ channels_gimp = [HISTOGRAM_VALUE]
+ if drw.is_rgb:
+ channels_txt += ["Red", "Green", "Blue"]
+ channels_gimp += [HISTOGRAM_RED, HISTOGRAM_GREEN, HISTOGRAM_BLUE]
+ if drw.has_alpha:
+ channels_txt += ["Alpha"]
+ channels_gimp += [HISTOGRAM_ALPHA]
+ with open(filename, "wt") as hfile:
+ writer = csv.writer(hfile)
+ #headers:
+ writer.writerow(["Range start"] + channels_txt)
+
+ # FIXME: Will need a specialized 'range' for FP color numbers
+ bucket_size = int(bucket_size)
+ for start_range in range(0, 256, bucket_size):
+ row = [start_range]
+ for channel in channels_gimp:
+ result = pdb.gimp_histogram(
+ drw, channel,
+ start_range,
+ min(start_range + bucket_size - 1, 255)
+ )
+ if output_format == "pixel count":
+ count = result[4]
+ else:
+ count = (result[4] / result[3]) if result[3] else 0
+ if output_format == "percent":
+ count = "%.2f%%" % (count * 100)
+ row.append(str(count))
+ writer.writerow(row)
+ if sample_average:
+ pdb.gimp_image_delete(new_img)
+
+register(
+ "histogram-export",
+ N_("Exports the image histogram to a text file (CSV)"),
+ globals()["__doc__"], # This includes the docstring, on the top of the file
+ "João S. O. Bueno",
+ "João S. O. Bueno, 2014",
+ "2014",
+ N_("_Export histogram..."),
+ "*",
+ [(PF_IMAGE, "img", _("_Image"), None),
+ (PF_DRAWABLE, "drw", _("_Drawable"), None),
+ (PF_FILENAME, "filename", _("Histogram _File"), ""),
+ (PF_FLOAT, "bucket_size", _("_Bucket Size"), 1.0),
+ (PF_BOOL, "sample_average", _("Sample _Average"), False),
+ (PF_RADIO, "output_format", _("Output format"), "pixel count",
+ ((_("Pixel count"), "pixel count"),
+ (_("Normalized"), "normalized"),
+ (_("Percent"), "percent"),
+ )
+ )
+ ],
+ [],
+ histogram_export,
+ menu="<Image>/Colors/Info",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/palette-offset.py b/plug-ins/pygimp/plug-ins/palette-offset.py
new file mode 100644
index 0000000..8ba0bfe
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/palette-offset.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def palette_offset(palette, amount):
+ #If palette is read only, work on a copy:
+ editable = pdb.gimp_palette_is_editable(palette)
+ if not editable:palette = pdb.gimp_palette_duplicate (palette)
+
+ num_colors = pdb.gimp_palette_get_info (palette)
+
+ tmp_entry_array = []
+ for i in xrange (num_colors):
+ tmp_entry_array.append ((pdb.gimp_palette_entry_get_name (palette, i),
+ pdb.gimp_palette_entry_get_color (palette, i)))
+ for i in xrange (num_colors):
+ target_index = i + amount
+ if target_index >= num_colors:
+ target_index -= num_colors
+ elif target_index < 0:
+ target_index += num_colors
+ pdb.gimp_palette_entry_set_name (palette, target_index, tmp_entry_array[i][0])
+ pdb.gimp_palette_entry_set_color (palette, target_index, tmp_entry_array[i][1])
+ return palette
+
+
+register(
+ "python-fu-palette-offset",
+ N_("Offset the colors in a palette"),
+ "palette_offset (palette, amount) -> modified_palette",
+ "Joao S. O. Bueno Calligaris, Carol Spears",
+ "(c) Joao S. O. Bueno Calligaris",
+ "2004, 2006",
+ N_("_Offset Palette..."),
+ "",
+ [
+ (PF_PALETTE, "palette", _("Palette"), ""),
+ (PF_INT, "amount", _("Off_set"), 1),
+ ],
+ [(PF_PALETTE, "new-palette", "Result")],
+ palette_offset,
+ menu="<Palettes>",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main ()
diff --git a/plug-ins/pygimp/plug-ins/palette-sort.py b/plug-ins/pygimp/plug-ins/palette-sort.py
new file mode 100644
index 0000000..c21b8ad
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/palette-sort.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+# little known, colorsys is part of Python's stdlib
+from colorsys import rgb_to_yiq
+from textwrap import dedent
+from random import randint
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+AVAILABLE_CHANNELS = (_("Red"), _("Green"), _("Blue"),
+ _("Luma (Y)"),
+ _("Hue"), _("Saturation"), _("Value"),
+ _("Saturation (HSL)"), _("Lightness (HSL)"),
+ _("Index"),
+ _("Random"))
+
+GRAIN_SCALE = (1.0, 1.0 , 1.0,
+ 1.0,
+ 360., 100., 100.,
+ 100., 100.,
+ 16384.,
+ float(0x7ffffff),
+ 100., 256., 256.,
+ 256., 360.,)
+
+SELECT_ALL = 0
+SELECT_SLICE = 1
+SELECT_AUTOSLICE = 2
+SELECT_PARTITIONED = 3
+SELECTIONS = (SELECT_ALL, SELECT_SLICE, SELECT_AUTOSLICE, SELECT_PARTITIONED)
+
+
+def noop(v, i):
+ return v
+
+
+def to_hsv(v, i):
+ return v.to_hsv()
+
+
+def to_hsl(v, i):
+ return v.to_hsl()
+
+
+def to_yiq(v, i):
+ return rgb_to_yiq(*v[:-1])
+
+
+def to_index(v, i):
+ return (i,)
+
+def to_random(v, i):
+ return (randint(0, 0x7fffffff),)
+
+
+channel_getters = [ (noop, 0), (noop, 1), (noop, 2),
+ (to_yiq, 0),
+ (to_hsv, 0), (to_hsv, 1), (to_hsv, 2),
+ (to_hsl, 1), (to_hsl, 2),
+ (to_index, 0),
+ (to_random, 0)]
+
+
+try:
+ from colormath.color_objects import RGBColor, LabColor, LCHabColor
+ AVAILABLE_CHANNELS = AVAILABLE_CHANNELS + (_("Lightness (LAB)"),
+ _("A-color"), _("B-color"),
+ _("Chroma (LCHab)"),
+ _("Hue (LCHab)"))
+ to_lab = lambda v,i: RGBColor(*v[:-1]).convert_to('LAB').get_value_tuple()
+ to_lchab = (lambda v,i:
+ RGBColor(*v[:-1]).convert_to('LCHab').get_value_tuple())
+ channel_getters.extend([(to_lab, 0), (to_lab, 1), (to_lab, 2),
+ (to_lchab, 1), (to_lchab, 2)])
+except ImportError:
+ pass
+
+
+def parse_slice(s, numcolors):
+ """Parse a slice spec and return (start, nrows, length)
+ All items are optional. Omitting them makes the largest possible selection that
+ exactly fits the other items.
+
+ start:nrows,length
+
+
+ '' selects all items, as does ':'
+ ':4,' makes a 4-row selection out of all colors (length auto-determined)
+ ':4' also.
+ ':1,4' selects the first 4 colors
+ ':,4' selects rows of 4 colors (nrows auto-determined)
+ ':4,4' selects 4 rows of 4 colors
+ '4:' selects a single row of all colors after 4, inclusive.
+ '4:,4' selects rows of 4 colors, starting at 4 (nrows auto-determined)
+ '4:4,4' selects 4 rows of 4 colors (16 colors total), beginning at index 4.
+ '4' is illegal (ambiguous)
+
+
+ In general, slices are comparable to a numpy sub-array.
+ 'start at element START, with shape (NROWS, LENGTH)'
+
+ """
+ s = s.strip()
+
+ def notunderstood():
+ raise ValueError('Slice %r not understood. Should be in format'
+ ' START?:NROWS?,ROWLENGTH? eg. "0:4,16".' % s)
+ def _int(v):
+ try:
+ return int(v)
+ except ValueError:
+ notunderstood()
+ if s in ('', ':', ':,'):
+ return 0, 1, numcolors # entire palette, one row
+ if s.count(':') != 1:
+ notunderstood()
+ rowpos = s.find(':')
+ start = 0
+ if rowpos > 0:
+ start = _int(s[:rowpos])
+ numcolors -= start
+ nrows = 1
+ if ',' in s:
+ commapos = s.find(',')
+ nrows = s[rowpos+1:commapos]
+ length = s[commapos+1:]
+ if not nrows:
+ if not length:
+ notunderstood()
+ else:
+ length = _int(length)
+ if length == 0:
+ notunderstood()
+ nrows = numcolors // length
+ if numcolors % length:
+ nrows = -nrows
+ elif not length:
+ nrows = _int(nrows)
+ if nrows == 0:
+ notunderstood()
+ length = numcolors // nrows
+ if numcolors % nrows:
+ length = -length
+ else:
+ nrows = _int(nrows)
+ if nrows == 0:
+ notunderstood()
+ length = _int(length)
+ if length == 0:
+ notunderstood()
+ else:
+ nrows = _int(s[rowpos+1:])
+ if nrows == 0:
+ notunderstood()
+ length = numcolors // nrows
+ if numcolors % nrows:
+ length = -length
+ return start, nrows, length
+
+
+def quantization_grain(channel, g):
+ "Given a channel and a quantization, return the size of a quantization grain"
+ g = max(1.0, g)
+ if g <= 1.0:
+ g = 0.00001
+ else:
+ g = max(0.00001, GRAIN_SCALE[channel] / g)
+ return g
+
+
+def palette_sort(palette, selection, slice_expr, channel1, ascending1,
+ channel2, ascending2, quantize, pchannel, pquantize):
+
+ grain1 = quantization_grain(channel1, quantize)
+ grain2 = quantization_grain(channel2, quantize)
+ pgrain = quantization_grain(pchannel, pquantize)
+
+ #If palette is read only, work on a copy:
+ editable = pdb.gimp_palette_is_editable(palette)
+ if not editable:
+ palette = pdb.gimp_palette_duplicate (palette)
+
+ num_colors = pdb.gimp_palette_get_info (palette)
+
+ start, nrows, length = None, None, None
+ if selection == SELECT_AUTOSLICE:
+ def find_index(color, startindex=0):
+ for i in range(startindex, num_colors):
+ c = pdb.gimp_palette_entry_get_color (palette, i)
+ if c == color:
+ return i
+ return None
+ def hexcolor(c):
+ return "#%02x%02x%02x" % tuple(c[:-1])
+ fg = pdb.gimp_context_get_foreground()
+ bg = pdb.gimp_context_get_background()
+ start = find_index(fg)
+ end = find_index(bg)
+ if start is None:
+ raise ValueError("Couldn't find foreground color %r in palette" % list(fg))
+ if end is None:
+ raise ValueError("Couldn't find background color %r in palette" % list(bg))
+ if find_index(fg, start + 1):
+ raise ValueError('Autoslice cannot be used when more than one'
+ ' instance of an endpoint'
+ ' (%s) is present' % hexcolor(fg))
+ if find_index(bg, end + 1):
+ raise ValueError('Autoslice cannot be used when more than one'
+ ' instance of an endpoint'
+ ' (%s) is present' % hexcolor(bg))
+ if start > end:
+ end, start = start, end
+ length = (end - start) + 1
+ try:
+ _, nrows, _ = parse_slice(slice_expr, length)
+ nrows = abs(nrows)
+ if length % nrows:
+ raise ValueError('Total length %d not evenly divisible'
+ ' by number of rows %d' % (length, nrows))
+ length /= nrows
+ except ValueError:
+ # bad expression is okay here, just assume one row
+ nrows = 1
+ # remaining behaviour is implemented by SELECT_SLICE 'inheritance'.
+ selection= SELECT_SLICE
+ elif selection in (SELECT_SLICE, SELECT_PARTITIONED):
+ start, nrows, length = parse_slice(slice_expr, num_colors)
+
+ channels_getter_1, channel_index = channel_getters[channel1]
+ channels_getter_2, channel2_index = channel_getters[channel2]
+
+ def get_colors(start, end):
+ result = []
+ for i in range(start, end):
+ entry = (pdb.gimp_palette_entry_get_name (palette, i),
+ pdb.gimp_palette_entry_get_color (palette, i))
+ index1 = channels_getter_1(entry[1], i)[channel_index]
+ index2 = channels_getter_2(entry[1], i)[channel2_index]
+ index = ((index1 - (index1 % grain1)) * (1 if ascending1 else -1),
+ (index2 - (index2 % grain2)) * (1 if ascending2 else -1)
+ )
+ result.append((index, entry))
+ return result
+
+ if selection == SELECT_ALL:
+ entry_list = get_colors(0, num_colors)
+ entry_list.sort(key=lambda v:v[0])
+ for i in range(num_colors):
+ pdb.gimp_palette_entry_set_name (palette, i, entry_list[i][1][0])
+ pdb.gimp_palette_entry_set_color (palette, i, entry_list[i][1][1])
+
+ elif selection == SELECT_PARTITIONED:
+ if num_colors < (start + length * nrows) - 1:
+ raise ValueError('Not enough entries in palette to '
+ 'sort complete rows! Got %d, expected >=%d' %
+ (num_colors, start + length * nrows))
+ pchannels_getter, pchannel_index = channel_getters[pchannel]
+ for row in range(nrows):
+ partition_spans = [1]
+ rowstart = start + (row * length)
+ old_color = pdb.gimp_palette_entry_get_color (palette,
+ rowstart)
+ old_partition = pchannels_getter(old_color, rowstart)[pchannel_index]
+ old_partition = old_partition - (old_partition % pgrain)
+ for i in range(rowstart + 1, rowstart + length):
+ this_color = pdb.gimp_palette_entry_get_color (palette, i)
+ this_partition = pchannels_getter(this_color, i)[pchannel_index]
+ this_partition = this_partition - (this_partition % pgrain)
+ if this_partition == old_partition:
+ partition_spans[-1] += 1
+ else:
+ partition_spans.append(1)
+ old_partition = this_partition
+ base = rowstart
+ for size in partition_spans:
+ palette_sort(palette, SELECT_SLICE, '%d:1,%d' % (base, size),
+ channel1, ascending1, channel2, ascending2,
+ quantize, 0, 1.0)
+ base += size
+ else:
+ stride = length
+ if num_colors < (start + stride * nrows) - 1:
+ raise ValueError('Not enough entries in palette to sort '
+ 'complete rows! Got %d, expected >=%d' %
+ (num_colors, start + stride * nrows))
+
+ for row_start in range(start, start + stride * nrows, stride):
+ sublist = get_colors(row_start, row_start + stride)
+ sublist.sort(key=lambda v:v[0])
+ for i, entry in zip(range(row_start, row_start + stride), sublist):
+ pdb.gimp_palette_entry_set_name (palette, i, entry[1][0])
+ pdb.gimp_palette_entry_set_color (palette, i, entry[1][1])
+
+ return palette
+
+register(
+ "python-fu-palette-sort",
+ N_("Sort the colors in a palette"),
+ # FIXME: Write humanly readable help -
+ # (I can't figure out what the plugin does, or how to use the parameters after
+ # David's enhancements even looking at the code -
+ # let alone someone just using GIMP (JS) )
+ dedent("""\
+ palette_sort (palette, selection, slice_expr, channel,
+ channel2, quantize, ascending, pchannel, pquantize) -> new_palette
+ Sorts a palette, or part of a palette, using several options.
+ One can select two color channels over which to sort,
+ and several auxiliary parameters create a 2D sorted
+ palette with sorted rows, among other things.
+ One can optionally install colormath
+ (https://pypi.python.org/pypi/colormath/1.0.8)
+ to GIMP's Python to get even more channels to choose from.
+ """),
+ "João S. O. Bueno, Carol Spears, David Gowers",
+ "João S. O. Bueno, Carol Spears, David Gowers",
+ "2006-2014",
+ N_("_Sort Palette..."),
+ "",
+ [
+ (PF_PALETTE, "palette", _("Palette"), ""),
+ (PF_OPTION, "selections", _("Se_lections"), SELECT_ALL,
+ (_("All"), _("Slice / Array"), _("Autoslice (fg->bg)"),
+ _("Partitioned"))),
+ (PF_STRING, "slice-expr", _("Slice _expression"), ''),
+ (PF_OPTION, "channel1", _("Channel to _sort"), 3,
+ AVAILABLE_CHANNELS),
+ (PF_BOOL, "ascending1", _("_Ascending"), True),
+ (PF_OPTION, "channel2", _("Secondary Channel to s_ort"), 5,
+ AVAILABLE_CHANNELS),
+ (PF_BOOL, "ascending2", _("_Ascending"), True),
+ (PF_FLOAT, "quantize", _("_Quantization"), 0.0),
+ (PF_OPTION, "pchannel", _("_Partitioning channel"), 3,
+ AVAILABLE_CHANNELS),
+ (PF_FLOAT, "pquantize", _("Partition q_uantization"), 0.0),
+ ],
+ [],
+ palette_sort,
+ menu="<Palettes>",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main ()
diff --git a/plug-ins/pygimp/plug-ins/palette-to-gradient.py b/plug-ins/pygimp/plug-ins/palette-to-gradient.py
new file mode 100644
index 0000000..9b2da49
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/palette-to-gradient.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python2
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def make_gradient(palette, num_segments, num_colors):
+ gradient = pdb.gimp_gradient_new(palette)
+
+ if (num_segments > 1):
+ pdb.gimp_gradient_segment_range_split_uniform(gradient, 0, -1,
+ num_segments)
+
+ for color_number in range(0,num_segments):
+ if (color_number == num_colors-1):color_number_next = 0
+ else: color_number_next = color_number + 1
+ color_left = pdb.gimp_palette_entry_get_color(palette,
+ color_number)
+ color_right = pdb.gimp_palette_entry_get_color(palette,
+ color_number_next)
+ pdb.gimp_gradient_segment_set_left_color(gradient,
+ color_number, color_left,
+ 100.0)
+ pdb.gimp_gradient_segment_set_right_color(gradient,
+ color_number, color_right,
+ 100.0)
+ pdb.gimp_context_set_gradient(gradient)
+ return gradient
+
+
+def palette_to_gradient_repeating(palette):
+ num_colors = pdb.gimp_palette_get_info(palette)
+ num_segments = num_colors
+ return make_gradient(palette, num_segments, num_colors)
+
+
+register(
+ "python-fu-palette-to-gradient-repeating",
+ N_("Create a repeating gradient using colors from the palette"),
+ "Create a new repeating gradient using colors from the palette.",
+ "Carol Spears, reproduced from previous work by Adrian Likins and Jeff Trefftz",
+ "Carol Spears",
+ "2006",
+ N_("Palette to _Repeating Gradient"),
+ "",
+ [(PF_PALETTE, "palette", _("Palette"), "")],
+ [(PF_GRADIENT, "new-gradient", "Result")],
+ palette_to_gradient_repeating,
+ menu="<Palettes>",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+
+def palette_to_gradient(palette):
+ num_colors = pdb.gimp_palette_get_info(palette)
+ num_segments = num_colors - 1
+ return make_gradient(palette, num_segments, num_colors)
+
+register(
+ "python-fu-palette-to-gradient",
+ N_("Create a gradient using colors from the palette"),
+ "Create a new gradient using colors from the palette.",
+ "Carol Spears, reproduced from previous work by Adrian Likins and Jeff Trefftz",
+ "Carol Spears",
+ "2006",
+ N_("Palette to _Gradient"),
+ "",
+ [(PF_PALETTE, "palette", _("Palette"), "")],
+ [(PF_GRADIENT, "new-gradient", "Result")],
+ palette_to_gradient,
+ menu="<Palettes>",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main ()
diff --git a/plug-ins/pygimp/plug-ins/py-slice.py b/plug-ins/pygimp/plug-ins/py-slice.py
new file mode 100755
index 0000000..90159aa
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/py-slice.py
@@ -0,0 +1,457 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+#Copyright (c) Manish Singh
+#javascript animation support by Joao S. O. Bueno Calligaris (2004)
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 2003, 2005 Manish Singh <yosh@gimp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# (c) 2003 Manish Singh.
+#"Guillotine implemented ala python, with html output
+# (based on perlotine by Seth Burgess)",
+# Modified by João S. O. Bueno Calligaris to allow dhtml animations (2005)
+
+import os
+
+from gimpfu import *
+import os.path
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def pyslice(image, drawable, save_path, html_filename,
+ image_basename, image_extension, separate,
+ image_path, cellspacing, animate, skip_caps):
+
+ cellspacing = int (cellspacing)
+
+ if animate:
+ count = 0
+ drw = []
+ #image.layers is a reversed list of the layers on the image
+ #so, count indexes from number of layers to 0.
+ for i in xrange (len (image.layers) -1, -1, -1):
+ if image.layers[i].visible:
+ drw.append(image.layers[i])
+ count += 1
+ if count == 3:
+ break
+
+
+ vert, horz = get_guides(image)
+
+ if len(vert) == 0 and len(horz) == 0:
+ return
+
+ gimp.progress_init(_("Slice"))
+ progress_increment = 1 / ((len(horz) + 1) * (len(vert) + 1))
+ progress = 0.0
+
+ def check_path(path):
+ path = os.path.abspath(path)
+
+ if not os.path.exists(path):
+ os.mkdir(path)
+
+ return path
+
+ save_path = check_path(save_path)
+
+ if not os.path.isdir(save_path):
+ save_path = os.path.dirname(save_path)
+
+ if separate:
+ image_relative_path = image_path
+ if not image_relative_path.endswith("/"):
+ image_relative_path += "/"
+ image_path = check_path(os.path.join(save_path, image_path))
+ else:
+ image_relative_path = ''
+ image_path = save_path
+
+ tw = TableWriter(os.path.join(save_path, html_filename),
+ cellspacing=cellspacing, animate=animate)
+
+ top = 0
+
+ for i in range(0, len(horz) + 1):
+ if i == len(horz):
+ bottom = image.height
+ else:
+ bottom = image.get_guide_position(horz[i])
+
+ tw.row_start()
+
+ left = 0
+
+ for j in range(0, len(vert) + 1):
+ if j == len(vert):
+ right = image.width
+ else:
+ right = image.get_guide_position(vert[j])
+ if (skip_caps and
+ (
+ (len(horz) >= 2 and (i == 0 or i == len(horz) )) or
+ (len(vert) >= 2 and (j == 0 or j == len(vert) ))
+ )
+ ):
+ skip_stub = True
+ else:
+ skip_stub = False
+
+ if (not animate or skip_stub):
+ src = (image_relative_path +
+ slice (image, None, image_path,
+ image_basename, image_extension,
+ left, right, top, bottom, i, j, ""))
+ else:
+ src = []
+ for layer, postfix in zip (drw, ("", "hover", "clicked")):
+ src.append (image_relative_path +
+ slice(image, layer, image_path,
+ image_basename, image_extension,
+ left, right, top, bottom, i, j, postfix))
+
+ tw.cell(src, right - left, bottom - top, i, j, skip_stub)
+
+ left = right + cellspacing
+
+ progress += progress_increment
+ gimp.progress_update(progress)
+
+ tw.row_end()
+
+ top = bottom + cellspacing
+
+ tw.close()
+
+def slice(image, drawable, image_path, image_basename, image_extension,
+ left, right, top, bottom, i, j, postfix):
+ if postfix:
+ postfix = "_" + postfix
+ src = "%s_%d_%d%s.%s" % (image_basename, i, j, postfix, image_extension)
+ filename = os.path.join(image_path, src)
+
+ if not drawable:
+ temp_image = image.duplicate()
+ temp_drawable = temp_image.active_layer
+ else:
+ if image.base_type == INDEXED:
+ #gimp_layer_new_from_drawable doesn't work for indexed images.
+ #(no colormap on new images)
+ original_active = image.active_layer
+ image.active_layer = drawable
+ temp_image = image.duplicate()
+ temp_drawable = temp_image.active_layer
+ image.active_layer = original_active
+ temp_image.disable_undo()
+ #remove all layers but the intended one
+ while len (temp_image.layers) > 1:
+ if temp_image.layers[0] != temp_drawable:
+ pdb.gimp_image_remove_layer (temp_image, temp_image.layers[0])
+ else:
+ pdb.gimp_image_remove_layer (temp_image, temp_image.layers[1])
+ else:
+ temp_image = pdb.gimp_image_new (drawable.width, drawable.height,
+ image.base_type)
+ temp_drawable = pdb.gimp_layer_new_from_drawable (drawable, temp_image)
+ temp_image.insert_layer (temp_drawable)
+
+ temp_image.disable_undo()
+ temp_image.crop(right - left, bottom - top, left, top)
+ if image_extension == "gif" and image.base_type == RGB:
+ pdb.gimp_image_convert_indexed (temp_image, CONVERT_DITHER_NONE,
+ CONVERT_PALETTE_GENERATE, 255,
+ True, False, False)
+ if image_extension == "jpg" and image.base_type == INDEXED:
+ pdb.gimp_image_convert_rgb (temp_image)
+
+ pdb.gimp_file_save(temp_image, temp_drawable, filename, filename)
+
+ gimp.delete(temp_image)
+ return src
+
+class GuideIter:
+ def __init__(self, image):
+ self.image = image
+ self.guide = 0
+
+ def __iter__(self):
+ return iter(self.next_guide, 0)
+
+ def next_guide(self):
+ self.guide = self.image.find_next_guide(self.guide)
+ return self.guide
+
+def get_guides(image):
+ vguides = []
+ hguides = []
+
+ for guide in GuideIter(image):
+ orientation = image.get_guide_orientation(guide)
+
+ guide_position = image.get_guide_position(guide)
+
+ if guide_position > 0:
+ if orientation == ORIENTATION_VERTICAL:
+ if guide_position < image.width:
+ vguides.append((guide_position, guide))
+ elif orientation == ORIENTATION_HORIZONTAL:
+ if guide_position < image.height:
+ hguides.append((guide_position, guide))
+
+ def position_sort(x, y):
+ return cmp(x[0], y[0])
+
+ vguides.sort(position_sort)
+ hguides.sort(position_sort)
+
+ vguides = [g[1] for g in vguides]
+ hguides = [g[1] for g in hguides]
+
+ return vguides, hguides
+
+class TableWriter:
+ def __init__(self, filename, cellpadding=0, cellspacing=0, border=0,
+ animate=False):
+
+ self.filename = filename
+ self.table_attrs = {}
+
+ #Hellraisen IE 6 doesn't support CSS for table control.
+ self.table_attrs['cellpadding'] = cellpadding
+ self.table_attrs['cellspacing'] = cellspacing
+ self.table_attrs['border'] = border
+
+ self.image_prefix = os.path.basename (filename)
+ self.image_prefix = self.image_prefix.split(".")[0]
+ self.image_prefix = self.image_prefix.replace ("-", "_")
+ self.image_prefix = self.image_prefix.replace (" ", "_")
+
+
+ if animate:
+ self.animate = True
+ self.images = []
+ else:
+ self.animate = False
+
+ if os.path.exists (filename):
+ #The plug-in is running to overwrite a previous
+ #version of the file. This will parse the href targets already
+ #in the file to preserve them.
+ self.urls = self.parse_urls ()
+ else:
+ self.urls = []
+
+ self.url_index = 0
+
+ self.html = open(filename, 'wt')
+ self.open()
+
+ def next_url (self):
+ if self.url_index < len (self.urls):
+ self.url_index += 1
+ return self.urls [self.url_index - 1]
+ else:
+ #Default url to use in the anchor tags:
+ return ("#")
+
+ def write(self, s, vals=None):
+ if vals:
+ s = s % vals
+
+ self.html.write(s + '\n')
+
+ def open(self):
+ out = '''<!--HTML SNIPPET GENERATED BY GIMP
+
+WARNING!! This is NOT a fully valid HTML document, it is rather a piece of
+HTML generated by GIMP's py-slice plugin that should be embedded in an HTML
+or XHTML document to be valid.
+
+Replace the href targets in the anchor (<a >) for your URLS to have it working
+as a menu.
+ -->\n'''
+ out += '<table'
+
+ for attr, value in self.table_attrs.iteritems():
+ out += ' %s="%s"' % (attr, value)
+
+ out += '>'
+
+ self.write(out)
+
+ def close(self):
+ self.write('</table>\n')
+ prefix = self.image_prefix
+ if self.animate:
+ out = """
+<script language="javascript" type="text/javascript">
+/* Made with GIMP */
+
+/* Preload images: */
+ images_%s = new Array();
+ \n""" % prefix
+ for image in self.images:
+ for type_ in ("plain", "hover", "clicked"):
+ if image.has_key(type_):
+ image_index = ("%d_%d_%s" %
+ (image["index"][0],
+ image["index"][1], type_))
+ out += (" images_%s[\"%s\"] = new Image();\n" %
+ (prefix, image_index))
+ out += (" images_%s[\"%s\"].src = \"%s\";\n" %
+ (prefix, image_index, image[type_]))
+
+ out+= """
+function exchange (image, images_array_name, event)
+ {
+ name = image.name;
+ images = eval (images_array_name);
+
+ switch (event)
+ {
+ case 0:
+ image.src = images[name + "_plain"].src;
+ break;
+ case 1:
+ image.src = images[name + "_hover"].src;
+ break;
+ case 2:
+ image.src = images[name + "_clicked"].src;
+ break;
+ case 3:
+ image.src = images[name + "_hover"].src;
+ break;
+ }
+
+ }
+</script>
+<!--
+End of the part generated by GIMP
+-->
+"""
+ self.write (out)
+
+
+ def row_start(self):
+ self.write(' <tr>')
+
+ def row_end(self):
+ self.write('</tr>\n')
+
+ def cell(self, src, width, height, row=0, col=0, skip_stub = False):
+ if isinstance (src, list):
+ prefix = "images_%s" % self.image_prefix
+ self.images.append ({"index" : (row, col), "plain" : src[0]})
+
+ out = (' <td><a href="%s"><img alt="" src="%s" ' +
+ 'style="width: %dpx; height: %dpx; border-width: 0px" \n') %\
+ (self.next_url(), src[0], width, height)
+ out += 'name="%d_%d" \n' % (row, col)
+ if len(src) >= 2:
+ self.images[-1]["hover"] = src [1]
+ out += """ onmouseout="exchange(this, '%s', 0);"\n""" % \
+ prefix
+ out += """ onmouseover="exchange(this, '%s', 1);"\n""" % \
+ prefix
+ if len(src) >= 3:
+ self.images[-1]["clicked"] = src [2]
+ out += """ onmousedown="exchange(this, '%s', 2);"\n""" % \
+ prefix
+ out += """ onmouseup="exchange(this, '%s', 3);"\n""" % \
+ prefix
+
+
+
+ out += "/></a></td>\n"
+
+ else:
+ if skip_stub:
+ out = (' <td><img alt=" " src="%s" style="width: %dpx; ' +
+ ' height: %dpx; border-width: 0px;"></td>') % \
+ (src, width, height)
+ else:
+ out = (' <td><a href="#"><img alt=" " src="%s" ' +
+ ' style="width: %dpx; height: %dpx; border-width: 0px;">' +
+ '</a></td>') % (src, width, height)
+ self.write(out)
+ def parse_urls (self):
+ """
+ This will parse any url targets in the href="XX" fields
+ of the given file and return then as a list
+ """
+ import re
+ url_list = []
+ try:
+ html_file = open (self.filename)
+
+ # Regular expression to pick everything up to the next
+ # doublequote character after finding the sequence 'href="'.
+ # The found sequences will be returned as a list by the
+ # "findall" method.
+ expr = re.compile (r"""href\=\"([^\"]*?)\"""")
+ url_list = expr.findall (html_file.read (2 ** 18))
+ html_file.close()
+
+ except:
+ # silently ignore any errors parsing this. The file being
+ # overwritten may not be a file created by py-slice.
+ pass
+
+ return url_list
+
+
+register(
+ "python-fu-slice",
+ # table snippet means a small piece of HTML code here
+ N_("Cuts an image along its guides, creates images and a HTML table snippet"),
+ """Add guides to an image. Then run this. It will cut along the guides,
+ and give you the html to reassemble the resulting images. If you
+ choose to generate javascript for onmouseover and clicked events, it
+ will use the lower three visible layers on the image for normal,
+ onmouseover and clicked states, in that order. If skip caps is
+ enabled, table cells on the edge of the table won't become animated,
+ and its images will be taken from the active layer.""",
+ "Manish Singh",
+ "Manish Singh",
+ "2003",
+ _("_Slice..."),
+ "*",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_DIRNAME, "save-path", _("Path for HTML export"), os.getcwd()),
+ (PF_STRING, "html-filename", _("Filename for export"), "slice.html"),
+ (PF_STRING, "image-basename", _("Image name prefix"), "slice"),
+ (PF_RADIO, "image-extension", _("Image format"), "gif", (("gif", "gif"), ("jpg", "jpg"), ("png", "png"))),
+ (PF_TOGGLE, "separate-image-dir", _("Separate image folder"),
+ False),
+ (PF_STRING, "relative-image-path", _("Folder for image export"), "images"),
+ (PF_SPINNER, "cellspacing", _("Space between table elements"), 0,
+ (0,15,1)),
+ (PF_TOGGLE, "animate", _("Javascript for onmouseover and clicked"),
+ False),
+ # table caps are table cells on the edge of the table
+ (PF_TOGGLE, "skip-caps", _("Skip animation for table caps"), True)
+ ],
+ [],
+ pyslice,
+ menu="<Image>/Filters/Web",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/pyconsole.py b/plug-ins/pygimp/plug-ins/pyconsole.py
new file mode 100644
index 0000000..ee096d5
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/pyconsole.py
@@ -0,0 +1,749 @@
+#
+# pyconsole.py
+#
+# Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
+# Portions of code by Geoffrey French.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public version 2.1 as
+# published by the Free Software Foundation.
+#
+# See COPYING.lib file that comes with this distribution for full text
+# of the license.
+#
+
+# This module 'runs' python interpreter in a TextView widget.
+# The main class is Console, usage is:
+# Console(locals=None, banner=None, completer=None, use_rlcompleter=True, start_script='') -
+# it creates the widget and 'starts' interactive session; see the end
+# of this file. If start_script is not empty, it pastes it as it was
+# entered from keyboard.
+#
+# Console has "command" signal which is emitted when code is about to
+# be executed. You may connect to it using console.connect or
+# console.connect_after to get your callback ran before or after the
+# code is executed.
+#
+# To modify output appearance, set attributes of console.stdout_tag and
+# console.stderr_tag.
+#
+# Console may subclass a type other than gtk.TextView, to allow syntax
+# highlighting and stuff,
+# e.g.:
+# console_type = pyconsole.ConsoleType(moo.edit.TextView)
+# console = console_type(use_rlcompleter=False, start_script="import moo\nimport gtk\n")
+#
+# This widget is not a replacement for real terminal with python running
+# inside: GtkTextView is not a terminal.
+# The use case is: you have a python program, you create this widget,
+# and inspect your program interiors.
+
+import gtk
+import gtk.gdk as gdk
+import gobject
+import pango
+import gtk.keysyms as _keys
+import code
+import sys
+import keyword
+import re
+
+# commonprefix() from posixpath
+def _commonprefix(m):
+ "Given a list of pathnames, returns the longest common leading component"
+ if not m: return ''
+ prefix = m[0]
+ for item in m:
+ for i in range(len(prefix)):
+ if prefix[:i+1] != item[:i+1]:
+ prefix = prefix[:i]
+ if i == 0:
+ return ''
+ break
+ return prefix
+
+class _ReadLine(object):
+
+ class Output(object):
+ def __init__(self, console, tag_name):
+ object.__init__(self)
+ self.buffer = console.get_buffer()
+ self.tag_name = tag_name
+ def write(self, text):
+ pos = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+ self.buffer.insert_with_tags_by_name(pos, text, self.tag_name)
+
+ class History(object):
+ def __init__(self):
+ object.__init__(self)
+ self.items = ['']
+ self.ptr = 0
+ self.edited = {}
+
+ def commit(self, text):
+ if text and self.items[-1] != text:
+ self.items.append(text)
+ self.ptr = 0
+ self.edited = {}
+
+ def get(self, dir, text):
+ if len(self.items) == 1:
+ return None
+
+ if text != self.items[self.ptr]:
+ self.edited[self.ptr] = text
+ elif self.edited.has_key(self.ptr):
+ del self.edited[self.ptr]
+
+ self.ptr = self.ptr + dir
+ if self.ptr >= len(self.items):
+ self.ptr = 0
+ elif self.ptr < 0:
+ self.ptr = len(self.items) - 1
+
+ try:
+ return self.edited[self.ptr]
+ except KeyError:
+ return self.items[self.ptr]
+
+ def __init__(self, quit_func=None):
+ object.__init__(self)
+
+ self.quit_func = quit_func
+
+ self.set_wrap_mode(gtk.WRAP_CHAR)
+ self.modify_font(pango.FontDescription("Monospace"))
+
+ self.buffer = self.get_buffer()
+ self.buffer.connect("insert-text", self.on_buf_insert)
+ self.buffer.connect("delete-range", self.on_buf_delete)
+ self.buffer.connect("mark-set", self.on_buf_mark_set)
+ self.do_insert = False
+ self.do_delete = False
+
+ self.stdout_tag = self.buffer.create_tag("stdout", foreground="#006000")
+ self.stderr_tag = self.buffer.create_tag("stderr", foreground="#B00000")
+ self._stdout = _ReadLine.Output(self, "stdout")
+ self._stderr = _ReadLine.Output(self, "stderr")
+
+ self.cursor = self.buffer.create_mark("cursor",
+ self.buffer.get_start_iter(),
+ False)
+ insert = self.buffer.get_insert()
+ self.cursor.set_visible(True)
+ insert.set_visible(False)
+
+ self.ps = ''
+ self.in_raw_input = False
+ self.in_modal_raw_input = False
+ self.run_on_raw_input = None
+ self.tab_pressed = 0
+ self.history = _ReadLine.History()
+ self.nonword_re = re.compile("[^\w\._]")
+
+ def freeze_undo(self):
+ try: self.begin_not_undoable_action()
+ except: pass
+
+ def thaw_undo(self):
+ try: self.end_not_undoable_action()
+ except: pass
+
+ def raw_input(self, ps=None):
+ '''Show prompt 'ps' and enter input mode until the current input
+ is committed.'''
+
+ if ps:
+ self.ps = ps
+ else:
+ self.ps = ''
+
+ iter = self.buffer.get_iter_at_mark(self.buffer.get_insert())
+
+ if ps:
+ self.freeze_undo()
+ self.buffer.insert(iter, self.ps)
+ self.thaw_undo()
+
+ self.__move_cursor_to(iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ self.in_raw_input = True
+
+ if self.run_on_raw_input:
+ run_now = self.run_on_raw_input
+ self.run_on_raw_input = None
+ self.buffer.insert_at_cursor(run_now + '\n')
+
+ def modal_raw_input(self, text):
+ '''Starts raw input in modal mode. The event loop is spinned until
+ the input is committed. Returns the text entered after the prompt.'''
+ orig_ps = self.ps
+
+ self.raw_input(text)
+ self.in_modal_raw_input = True
+
+ while self.in_modal_raw_input:
+ gtk.main_iteration()
+
+ self.ps = orig_ps
+ self.in_modal_raw_input = False
+ self.in_raw_input = False
+
+ return self.modal_raw_input_result
+
+ def modal_input(self, text):
+ return eval(self.modal_raw_input(text))
+
+ # Each time the insert mark is modified, move the cursor to it.
+ def on_buf_mark_set(self, buffer, iter, mark):
+ if mark is not buffer.get_insert():
+ return
+ start = self.__get_start()
+ end = self.__get_end()
+ if iter.compare(self.__get_start()) >= 0 and \
+ iter.compare(self.__get_end()) <= 0:
+ buffer.move_mark_by_name("cursor", iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __insert(self, iter, text):
+ self.do_insert = True
+ self.buffer.insert(iter, text)
+ self.do_insert = False
+
+ # Make sure that text insertions while in text input mode are properly
+ # committed to the history.
+ def on_buf_insert(self, buf, iter, text, len):
+ # Bail out if not in input mode.
+ if not self.in_raw_input or self.do_insert or not len:
+ return
+
+ buf.stop_emission("insert-text")
+ lines = text.splitlines()
+ need_eol = False
+ for l in lines:
+ if need_eol:
+ self._commit()
+ iter = self.__get_cursor()
+ else:
+ cursor = self.__get_cursor()
+ if iter.compare(self.__get_start()) < 0:
+ iter = cursor
+ elif iter.compare(self.__get_end()) > 0:
+ iter = cursor
+ else:
+ self.__move_cursor_to(iter)
+ need_eol = True
+ self.__insert(iter, l)
+ self.__move_cursor(0)
+
+ def __delete(self, start, end):
+ self.do_delete = True
+ self.buffer.delete(start, end)
+ self.do_delete = False
+
+ def on_buf_delete(self, buf, start, end):
+ if not self.in_raw_input or self.do_delete:
+ return
+
+ buf.stop_emission("delete-range")
+
+ start.order(end)
+ line_start = self.__get_start()
+ line_end = self.__get_end()
+
+ if start.compare(line_end) > 0:
+ return
+ if end.compare(line_start) < 0:
+ return
+
+ self.__move_cursor(0)
+
+ if start.compare(line_start) < 0:
+ start = line_start
+ if end.compare(line_end) > 0:
+ end = line_end
+ self.__delete(start, end)
+
+ # We overload the key press event handler to handle "special keys"
+ # when in input mode to make history browsing, completions, etc. work.
+ def do_key_press_event(self, event, parent_type):
+ if not self.in_raw_input:
+ return parent_type.do_key_press_event(self, event)
+
+ tab_pressed = self.tab_pressed
+ self.tab_pressed = 0
+ handled = True
+
+ state = event.state & (gdk.SHIFT_MASK |
+ gdk.CONTROL_MASK |
+ gdk.MOD1_MASK)
+ keyval = event.keyval
+
+ if not state:
+ if keyval == _keys.Escape:
+ pass
+ elif keyval == _keys.Return:
+ self._commit()
+ elif keyval == _keys.Up:
+ self.__history(-1)
+ elif keyval == _keys.Down:
+ self.__history(1)
+ elif keyval == _keys.Left:
+ self.__move_cursor(-1)
+ elif keyval == _keys.Right:
+ self.__move_cursor(1)
+ elif keyval == _keys.Home:
+ self.__move_cursor(-10000)
+ elif keyval == _keys.End:
+ self.__move_cursor(10000)
+ elif keyval == _keys.Tab:
+ cursor = self.__get_cursor()
+ if cursor.starts_line():
+ handled = False
+ else:
+ cursor.backward_char()
+ if cursor.get_char().isspace():
+ handled = False
+ else:
+ self.tab_pressed = tab_pressed + 1
+ self.__complete()
+ else:
+ handled = False
+ elif state == gdk.CONTROL_MASK:
+ if keyval == _keys.u:
+ start = self.__get_start()
+ end = self.__get_cursor()
+ self.__delete(start, end)
+ elif keyval == _keys.d:
+ if self.quit_func:
+ self.quit_func()
+ else:
+ handled = False
+ else:
+ handled = False
+
+ # Handle ordinary keys
+ if not handled:
+ return parent_type.do_key_press_event(self, event)
+ else:
+ return True
+
+ def __history(self, dir):
+ text = self._get_line()
+ new_text = self.history.get(dir, text)
+ if not new_text is None:
+ self.__replace_line(new_text)
+ self.__move_cursor(0)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __get_cursor(self):
+ '''Returns an iterator at the current cursor position.'''
+ return self.buffer.get_iter_at_mark(self.cursor)
+
+ def __get_start(self):
+ '''Returns an iterator at the start of the input on the current
+ cursor line.'''
+
+ iter = self.__get_cursor()
+ iter.set_line(iter.get_line())
+ iter.forward_chars(len(self.ps))
+ return iter
+
+ def __get_end(self):
+ '''Returns an iterator at the end of the cursor line.'''
+ iter = self.__get_cursor()
+ if not iter.ends_line():
+ iter.forward_to_line_end()
+ return iter
+
+ def __get_text(self, start, end):
+ '''Get text between 'start' and 'end' markers.'''
+ return self.buffer.get_text(start, end, False)
+
+ def __move_cursor_to(self, iter):
+ self.buffer.place_cursor(iter)
+ self.buffer.move_mark_by_name("cursor", iter)
+
+ def __move_cursor(self, howmany):
+ iter = self.__get_cursor()
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+ line_len = end.get_line_offset()
+ move_to = iter.get_line_offset() + howmany
+ move_to = min(max(move_to, len(self.ps)), line_len)
+ iter.set_line_offset(move_to)
+ self.__move_cursor_to(iter)
+
+ def __delete_at_cursor(self, howmany):
+ iter = self.__get_cursor()
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+ line_len = end.get_line_offset()
+ erase_to = iter.get_line_offset() + howmany
+ if erase_to > line_len:
+ erase_to = line_len
+ elif erase_to < len(self.ps):
+ erase_to = len(self.ps)
+ end.set_line_offset(erase_to)
+ self.__delete(iter, end)
+
+ def __get_width(self):
+ '''Estimate the number of characters that will fit in the area
+ currently allocated to this widget.'''
+
+ if not (self.flags() & gtk.REALIZED):
+ return 80
+
+ context = self.get_pango_context()
+ metrics = context.get_metrics(context.get_font_description(),
+ context.get_language())
+ pix_width = metrics.get_approximate_char_width()
+ return self.allocation.width * pango.SCALE / pix_width
+
+ def __print_completions(self, completions):
+ line_start = self.__get_text(self.__get_start(), self.__get_cursor())
+ line_end = self.__get_text(self.__get_cursor(), self.__get_end())
+ iter = self.buffer.get_end_iter()
+ self.__move_cursor_to(iter)
+ self.__insert(iter, "\n")
+
+ width = max(self.__get_width(), 4)
+ max_width = max([len(s) for s in completions])
+ n_columns = max(int(width / (max_width + 1)), 1)
+ col_width = int(width / n_columns)
+ total = len(completions)
+ col_length = total / n_columns
+ if total % n_columns:
+ col_length = col_length + 1
+ col_length = max(col_length, 1)
+
+ if col_length == 1:
+ n_columns = total
+ col_width = width / total
+
+ for i in range(col_length):
+ for j in range(n_columns):
+ ind = i + j*col_length
+ if ind < total:
+ if j == n_columns - 1:
+ n_spaces = 0
+ else:
+ n_spaces = col_width - len(completions[ind])
+ self.__insert(iter, completions[ind] + " " * n_spaces)
+ self.__insert(iter, "\n")
+
+ self.__insert(iter, "%s%s%s" % (self.ps, line_start, line_end))
+ iter.set_line_offset(len(self.ps) + len(line_start))
+ self.__move_cursor_to(iter)
+ self.scroll_to_mark(self.cursor, 0.2)
+
+ def __complete(self):
+ text = self.__get_text(self.__get_start(), self.__get_cursor())
+ start = ''
+ word = text
+ nonwords = self.nonword_re.findall(text)
+ if nonwords:
+ last = text.rfind(nonwords[-1]) + len(nonwords[-1])
+ start = text[:last]
+ word = text[last:]
+
+ completions = self.complete(word)
+
+ if completions:
+ prefix = _commonprefix(completions)
+ if prefix != word:
+ start_iter = self.__get_start()
+ start_iter.forward_chars(len(start))
+ end_iter = start_iter.copy()
+ end_iter.forward_chars(len(word))
+ self.__delete(start_iter, end_iter)
+ self.__insert(end_iter, prefix)
+ elif self.tab_pressed > 1:
+ self.freeze_undo()
+ self.__print_completions(completions)
+ self.thaw_undo()
+ self.tab_pressed = 0
+
+ def complete(self, text):
+ return None
+
+ def _get_line(self):
+ '''Return the current input behind the prompt.'''
+ start = self.__get_start()
+ end = self.__get_end()
+ return self.buffer.get_text(start, end, False)
+
+ def __replace_line(self, new_text):
+ '''Replace the current input with 'new_text' '''
+ start = self.__get_start()
+ end = self.__get_end()
+ self.__delete(start, end)
+ self.__insert(end, new_text)
+
+ def _commit(self):
+ '''Commit the input entered on the current line.'''
+
+ # Find iterator and end of cursor line.
+ end = self.__get_cursor()
+ if not end.ends_line():
+ end.forward_to_line_end()
+
+ # Get text at current line.
+ text = self._get_line()
+
+ # Move cursor to the end of the line, insert new line.
+ self.__move_cursor_to(end)
+ self.freeze_undo()
+ self.__insert(end, "\n")
+
+ self.history.commit(text)
+ if self.in_modal_raw_input:
+ self.in_modal_raw_input = False
+ self.modal_raw_input_result = text
+ else:
+ self.in_raw_input = False
+ self.do_raw_input(text)
+
+ self.thaw_undo()
+
+ def do_raw_input(self, text):
+ pass
+
+
+class _Console(_ReadLine, code.InteractiveInterpreter):
+ def __init__(self, locals=None, banner=None,
+ completer=None, use_rlcompleter=True,
+ start_script=None, quit_func=None):
+ _ReadLine.__init__(self, quit_func)
+
+ code.InteractiveInterpreter.__init__(self, locals)
+ self.locals["__console__"] = self
+
+ # The builtin raw_input function reads from stdin, we don't want
+ # this. Therefore, replace this function with our own modal raw
+ # input function.
+ exec "import __builtin__" in self.locals
+ self.locals['__builtin__'].__dict__['raw_input'] = lambda text='': self.modal_raw_input(text)
+ self.locals['__builtin__'].__dict__['input'] = lambda text='': self.modal_input(text)
+
+ self.start_script = start_script
+ self.completer = completer
+ self.banner = banner
+
+ if not self.completer and use_rlcompleter:
+ try:
+ import rlcompleter
+ self.completer = rlcompleter.Completer()
+ except ImportError:
+ pass
+
+ self.ps1 = ">>> "
+ self.ps2 = "... "
+ self.__start()
+ self.run_on_raw_input = start_script
+ self.raw_input(self.ps1)
+
+ def __start(self):
+ self.cmd_buffer = ""
+
+ self.freeze_undo()
+ self.thaw_undo()
+
+ self.do_delete = True
+ self.buffer.set_text("")
+ self.do_delete = False
+
+ if self.banner:
+ iter = self.buffer.get_start_iter()
+ self.buffer.insert_with_tags_by_name(iter, self.banner, "stdout")
+ if not iter.starts_line():
+ self.buffer.insert(iter, "\n")
+
+ def clear(self, start_script=None):
+ if start_script is None:
+ start_script = self.start_script
+ else:
+ self.start_script = start_script
+
+ self.__start()
+ self.run_on_raw_input = start_script
+ self.raw_input(self.ps1)
+
+ def do_raw_input(self, text):
+ if self.cmd_buffer:
+ cmd = self.cmd_buffer + "\n" + text
+ else:
+ cmd = text
+
+ saved_stdout, saved_stderr = sys.stdout, sys.stderr
+ sys.stdout, sys.stderr = self._stdout, self._stderr
+
+ if self.runsource(cmd):
+ self.cmd_buffer = cmd
+ ps = self.ps2
+ else:
+ self.cmd_buffer = ''
+ ps = self.ps1
+
+ sys.stdout, sys.stderr = saved_stdout, saved_stderr
+ self.raw_input(ps)
+
+ def do_command(self, code):
+ try:
+ eval(code, self.locals)
+ except SystemExit:
+ if self.quit_func:
+ self.quit_func()
+ else:
+ raise
+ except:
+ self.showtraceback()
+
+ def runcode(self, code):
+ if gtk.pygtk_version[1] < 8:
+ self.do_command(code)
+ else:
+ self.emit("command", code)
+
+ def complete_attr(self, start, end):
+ try:
+ obj = eval(start, self.locals)
+ strings = dir(obj)
+
+ if end:
+ completions = {}
+ for s in strings:
+ if s.startswith(end):
+ completions[s] = None
+ completions = completions.keys()
+ else:
+ completions = strings
+
+ completions.sort()
+ return [start + "." + s for s in completions]
+ except:
+ return None
+
+ def complete(self, text):
+ if self.completer:
+ completions = []
+ i = 0
+ try:
+ while 1:
+ s = self.completer.complete(text, i)
+ if s:
+ completions.append(s)
+ i = i + 1
+ else:
+ completions.sort()
+ return completions
+ except NameError:
+ return None
+
+ dot = text.rfind(".")
+ if dot >= 0:
+ return self.complete_attr(text[:dot], text[dot+1:])
+
+ completions = {}
+ strings = keyword.kwlist
+
+ if self.locals:
+ strings.extend(self.locals.keys())
+
+ try: strings.extend(eval("globals()", self.locals).keys())
+ except: pass
+
+ try:
+ exec "import __builtin__" in self.locals
+ strings.extend(eval("dir(__builtin__)", self.locals))
+ except:
+ pass
+
+ for s in strings:
+ if s.startswith(text):
+ completions[s] = None
+ completions = completions.keys()
+ completions.sort()
+ return completions
+
+
+def ReadLineType(t=gtk.TextView):
+ class readline(t, _ReadLine):
+ def __init__(self, *args, **kwargs):
+ t.__init__(self)
+ _ReadLine.__init__(self, *args, **kwargs)
+ def do_key_press_event(self, event):
+ return _ReadLine.do_key_press_event(self, event, t)
+ gobject.type_register(readline)
+ return readline
+
+def ConsoleType(t=gtk.TextView):
+ class console_type(t, _Console):
+ __gsignals__ = {
+ 'command' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object,)),
+ 'key-press-event' : 'override'
+ }
+
+ def __init__(self, *args, **kwargs):
+ if gtk.pygtk_version[1] < 8:
+ gobject.GObject.__init__(self)
+ else:
+ t.__init__(self)
+ _Console.__init__(self, *args, **kwargs)
+
+ def do_command(self, code):
+ return _Console.do_command(self, code)
+
+ def do_key_press_event(self, event):
+ return _Console.do_key_press_event(self, event, t)
+
+ def get_default_size(self):
+ context = self.get_pango_context()
+ metrics = context.get_metrics(context.get_font_description(),
+ context.get_language())
+ width = metrics.get_approximate_char_width()
+ height = metrics.get_ascent() + metrics.get_descent()
+
+ # Default to a 80x40 console
+ width = pango.PIXELS(int(width * 80 * 1.05))
+ height = pango.PIXELS(height * 40)
+
+ return width, height
+
+ if gtk.pygtk_version[1] < 8:
+ gobject.type_register(console_type)
+
+ return console_type
+
+ReadLine = ReadLineType()
+Console = ConsoleType()
+
+def _make_window():
+ window = gtk.Window()
+ window.set_title("pyconsole.py")
+ swin = gtk.ScrolledWindow()
+ swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+ window.add(swin)
+ console = Console(banner="Hello there!",
+ use_rlcompleter=False,
+ start_script="from gtk import *\n")
+ swin.add(console)
+
+ width, height = console.get_default_size()
+ sb_width, sb_height = swin.get_vscrollbar().size_request()
+
+ window.set_default_size(width + sb_width, height)
+ window.show_all()
+
+ if not gtk.main_level():
+ window.connect("destroy", gtk.main_quit)
+ gtk.main()
+
+ return console
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2 or sys.argv[1] != '-gimp':
+ _make_window()
diff --git a/plug-ins/pygimp/plug-ins/python-console.py b/plug-ins/pygimp/plug-ins/python-console.py
new file mode 100755
index 0000000..a8c7e4c
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/python-console.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+t = gettext.translation('gimp20-python', gimp.locale_directory, fallback=True)
+_ = t.ugettext
+
+PROC_NAME = 'python-fu-console'
+
+RESPONSE_BROWSE, RESPONSE_CLEAR, RESPONSE_SAVE = range(3)
+
+def do_console():
+ import pygtk
+ pygtk.require('2.0')
+
+ import sys, gobject, gtk, gimpenums, gimpshelf, gimpui, pyconsole
+ gimpui.gimp_ui_init ()
+
+ namespace = {'__builtins__': __builtins__,
+ '__name__': '__main__', '__doc__': None,
+ 'gimp': gimp, 'pdb': gimp.pdb,
+ 'shelf': gimpshelf.shelf}
+
+ for s in gimpenums.__dict__.keys():
+ if s[0] != '_':
+ namespace[s] = getattr(gimpenums, s)
+
+ class GimpConsole(pyconsole.Console):
+ def __init__(self, quit_func=None):
+ banner = ('GIMP %s Python Console\nPython %s\n' %
+ (gimp.pdb.gimp_version(), sys.version))
+ pyconsole.Console.__init__(self,
+ locals=namespace, banner=banner,
+ quit_func=quit_func)
+ def _commit(self):
+ pyconsole.Console._commit(self)
+ gimp.displays_flush()
+
+ class ConsoleDialog(gimpui.Dialog):
+ def __init__(self):
+ gimpui.Dialog.__init__(self, title=_("Python Console"),
+ role=PROC_NAME, help_id=PROC_NAME,
+ buttons=(gtk.STOCK_SAVE, RESPONSE_SAVE,
+ gtk.STOCK_CLEAR, RESPONSE_CLEAR,
+ _("_Browse..."), RESPONSE_BROWSE,
+ gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
+
+ self.set_name (PROC_NAME)
+ self.set_alternative_button_order((gtk.RESPONSE_CLOSE,
+ RESPONSE_BROWSE,
+ RESPONSE_CLEAR,
+ RESPONSE_SAVE))
+
+ self.cons = GimpConsole(quit_func=lambda: gtk.main_quit())
+
+ self.style_set (None, None)
+
+ self.connect('response', self.response)
+ self.connect('style-set', self.style_set)
+
+ self.browse_dlg = None
+ self.save_dlg = None
+
+ vbox = gtk.VBox(False, 12)
+ vbox.set_border_width(12)
+ self.vbox.pack_start(vbox)
+
+ scrl_win = gtk.ScrolledWindow()
+ scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+ vbox.pack_start(scrl_win)
+
+ scrl_win.add(self.cons)
+
+ width, height = self.cons.get_default_size()
+ sb_width, sb_height = scrl_win.get_vscrollbar().size_request()
+
+ # Account for scrollbar width and border width to ensure
+ # the text view gets a width of 80 characters. We don't care
+ # so much whether the height will be exactly 40 characters.
+ self.set_default_size(width + sb_width + 2 * 12, height)
+
+ def style_set(self, old_style, user_data):
+ style = self.get_style ()
+ self.cons.stdout_tag.set_property ("foreground", style.text[gtk.STATE_NORMAL])
+ self.cons.stderr_tag.set_property ("foreground", style.text[gtk.STATE_INSENSITIVE])
+
+ def response(self, dialog, response_id):
+ if response_id == RESPONSE_BROWSE:
+ self.browse()
+ elif response_id == RESPONSE_CLEAR:
+ self.cons.banner = None
+ self.cons.clear()
+ elif response_id == RESPONSE_SAVE:
+ self.save_dialog()
+ else:
+ gtk.main_quit()
+
+ self.cons.grab_focus()
+
+ def browse_response(self, dlg, response_id):
+ if response_id != gtk.RESPONSE_APPLY:
+ dlg.hide()
+ return
+
+ proc_name = dlg.get_selected()
+
+ if not proc_name:
+ return
+
+ proc = pdb[proc_name]
+
+ cmd = ''
+
+ if len(proc.return_vals) > 0:
+ cmd = ', '.join([x[1].replace('-', '_')
+ for x in proc.return_vals]) + ' = '
+
+ cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_')
+
+ if len(proc.params) > 0 and proc.params[0][1] == 'run-mode':
+ params = proc.params[1:]
+ else:
+ params = proc.params
+
+ cmd = cmd + '(%s)' % ', '.join([x[1].replace('-', '_')
+ for x in params])
+
+ buffer = self.cons.buffer
+
+ lines = buffer.get_line_count()
+ iter = buffer.get_iter_at_line_offset(lines - 1, 4)
+ buffer.delete(iter, buffer.get_end_iter())
+ buffer.place_cursor(buffer.get_end_iter())
+ buffer.insert_at_cursor(cmd)
+
+ def browse(self):
+ if not self.browse_dlg:
+ dlg = gimpui.ProcBrowserDialog(_("Python Procedure Browser"),
+ role=PROC_NAME,
+ buttons=(gtk.STOCK_APPLY,
+ gtk.RESPONSE_APPLY,
+ gtk.STOCK_CLOSE,
+ gtk.RESPONSE_CLOSE))
+
+ dlg.set_default_response(gtk.RESPONSE_APPLY)
+ dlg.set_alternative_button_order((gtk.RESPONSE_CLOSE,
+ gtk.RESPONSE_APPLY))
+
+ dlg.connect('response', self.browse_response)
+ dlg.connect('row-activated',
+ lambda dlg: dlg.response(gtk.RESPONSE_APPLY))
+
+ self.browse_dlg = dlg
+
+ self.browse_dlg.present()
+
+ def save_response(self, dlg, response_id):
+ if response_id == gtk.RESPONSE_DELETE_EVENT:
+ self.save_dlg = None
+ return
+ elif response_id == gtk.RESPONSE_OK:
+ filename = dlg.get_filename()
+
+ try:
+ logfile = open(filename, 'w')
+ except IOError, e:
+ gimp.message(_("Could not open '%s' for writing: %s") %
+ (filename, e.strerror))
+ return
+
+ buffer = self.cons.buffer
+
+ start = buffer.get_start_iter()
+ end = buffer.get_end_iter()
+
+ log = buffer.get_text(start, end, False)
+
+ try:
+ logfile.write(log)
+ logfile.close()
+ except IOError, e:
+ gimp.message(_("Could not write to '%s': %s") %
+ (filename, e.strerror))
+ return
+
+ dlg.hide()
+
+ def save_dialog(self):
+ if not self.save_dlg:
+ dlg = gtk.FileChooserDialog(_("Save Python-Fu Console Output"),
+ parent=self,
+ action=gtk.FILE_CHOOSER_ACTION_SAVE,
+ buttons=(gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL,
+ gtk.STOCK_SAVE,
+ gtk.RESPONSE_OK))
+
+ dlg.set_default_response(gtk.RESPONSE_OK)
+ dlg.set_alternative_button_order((gtk.RESPONSE_OK,
+ gtk.RESPONSE_CANCEL))
+
+ dlg.connect('response', self.save_response)
+
+ self.save_dlg = dlg
+
+ self.save_dlg.present()
+
+ def run(self):
+ self.show_all()
+ gtk.main()
+
+ ConsoleDialog().run()
+
+register(
+ PROC_NAME,
+ N_("Interactive GIMP Python interpreter"),
+ "Type in commands and see results",
+ "James Henstridge",
+ "James Henstridge",
+ "1997-1999",
+ N_("_Console"),
+ "",
+ [],
+ [],
+ do_console,
+ menu="<Image>/Filters/Languages/Python-Fu",
+ domain=("gimp20-python", gimp.locale_directory))
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/python-eval.py b/plug-ins/pygimp/plug-ins/python-eval.py
new file mode 100755
index 0000000..9c1663d
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/python-eval.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 2006 Manish Singh <yosh@gimp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+def code_eval(code):
+ if code == '-':
+ import sys
+ code = sys.stdin
+ exec code in globals()
+
+register(
+ "python-fu-eval",
+ "Evaluate Python code",
+ "Evaluate python code under the python interpreter (primarily for batch mode)",
+ "Manish Singh",
+ "Manish Singh",
+ "2006",
+ None,
+ None,
+ [
+ (PF_STRING, "code", "The code to evaluate", "")
+ ],
+ [],
+ code_eval)
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/shadow_bevel.py b/plug-ins/pygimp/plug-ins/shadow_bevel.py
new file mode 100755
index 0000000..29a9246
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/shadow_bevel.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpfu import *
+
+gettext.install("gimp20-python", gimp.locale_directory, unicode=True)
+
+def shadow_bevel(img, drawable, blur, bevel, do_shadow, drop_x, drop_y):
+ # disable undo for the image
+ img.undo_group_start()
+
+ # copy the layer
+ shadow = drawable.copy(True)
+ img.insert_layer(shadow, position=img.layers.index(drawable) + 1)
+ shadow.name = drawable.name + " shadow"
+ shadow.lock_alpha = False
+
+ # threshold the shadow layer to all white
+ pdb.gimp_threshold(shadow, 0, 255)
+
+ # blur the shadow layer
+ pdb.plug_in_gauss_iir(img, shadow, blur, True, True)
+
+ # do the bevel thing ...
+ if bevel:
+ pdb.plug_in_bump_map(img, drawable, shadow, 135, 45, 3,
+ 0, 0, 0, 0, True, False, 0)
+
+ # make the shadow layer black now ...
+ pdb.gimp_drawable_invert(shadow, False)
+
+ # translate the drop shadow
+ shadow.translate(drop_x, drop_y)
+
+ if not do_shadow:
+ # delete shadow ...
+ gimp.delete(shadow)
+
+ # enable undo again
+ img.undo_group_end()
+
+
+register(
+ "python-fu-shadow-bevel",
+ N_("Add a drop shadow to a layer, and optionally bevel it"),
+ "Add a drop shadow to a layer, and optionally bevel it",
+ "James Henstridge",
+ "James Henstridge",
+ "1999",
+ N_("_Drop Shadow and Bevel..."),
+ "RGBA, GRAYA",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_SLIDER, "blur", _("_Shadow blur"), 6, (1, 30, 1)),
+ (PF_BOOL, "bevel", _("_Bevel"), True),
+ (PF_BOOL, "shadow", _("_Drop shadow"), True),
+ (PF_INT, "drop-x", _("Drop shadow _X displacement"), 3),
+ (PF_INT, "drop-y", _("Drop shadow _Y displacement"), 6)
+ ],
+ [],
+ shadow_bevel,
+ menu="<Image>/Filters/Light and Shadow/Shadow",
+ domain=("gimp20-python", gimp.locale_directory)
+ )
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/sphere.py b/plug-ins/pygimp/plug-ins/sphere.py
new file mode 100755
index 0000000..889c3f7
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/sphere.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import math
+from gimpfu import *
+
+def sphere(radius, light, shadow, foo, bg_colour, sphere_colour):
+ if radius < 1:
+ radius = 1
+
+ width = int(radius * 3.75)
+ height = int(radius * 2.5)
+
+ gimp.context_push()
+
+ img = gimp.Image(width, height, RGB)
+
+ drawable = gimp.Layer(img, "Sphere Layer", width, height,
+ RGB_IMAGE, 100, NORMAL_MODE)
+
+ radians = light * math.pi / 180
+
+ cx = width / 2
+ cy = height / 2
+
+ light_x = cx + radius * 0.6 * math.cos(radians)
+ light_y = cy - radius * 0.6 * math.sin(radians)
+
+ light_end_x = cx + radius * math.cos(math.pi + radians)
+ light_end_y = cy - radius * math.sin(math.pi + radians)
+
+ offset = radius * 0.1
+
+ img.disable_undo()
+ img.insert_layer(drawable)
+
+ gimp.set_foreground(sphere_colour)
+
+ gimp.set_background(bg_colour)
+ pdb.gimp_edit_fill(drawable, BACKGROUND_FILL)
+
+ gimp.set_background(20, 20, 20)
+
+ if (light >= 45 and light <= 75 or light <= 135 and
+ light >= 105) and shadow:
+ shadow_w = radius * 2.5 * math.cos(math.pi + radians)
+ shadow_h = radius * 0.5
+ shadow_x = cx
+ shadow_y = cy + radius * 0.65
+
+ if shadow_w < 0:
+ shadow_x = cx + shadow_w
+ shadow_w = -shadow_w
+
+ pdb.gimp_ellipse_select(img, shadow_x, shadow_y, shadow_w, shadow_h,
+ CHANNEL_OP_REPLACE, True, True, 7.5)
+ pdb.gimp_edit_bucket_fill(drawable, BG_BUCKET_FILL,
+ MULTIPLY_MODE, 100, 0, False, 0, 0)
+
+ pdb.gimp_ellipse_select(img, cx - radius, cy - radius, 2 * radius,
+ 2 * radius, CHANNEL_OP_REPLACE, True, False, 0)
+ pdb.gimp_edit_blend(drawable, FG_BG_RGB_MODE, NORMAL_MODE, GRADIENT_RADIAL,
+ 100, offset, REPEAT_NONE, False, False, 0, 0, True,
+ light_x, light_y, light_end_x, light_end_y)
+
+ pdb.gimp_selection_none(img)
+
+ img.enable_undo()
+
+ disp = gimp.Display(img)
+
+ gimp.context_pop()
+
+
+register(
+ "python-fu-sphere",
+ "Simple sphere with drop shadow",
+ "Simple sphere with drop shadow",
+ "James Henstridge",
+ "James Henstridge",
+ "1997-1999, 2007",
+ "_Sphere",
+ "",
+ [
+ (PF_INT, "radius", "Radius for sphere", 100),
+ (PF_SLIDER, "light", "Light angle", 45, (0,360,1)),
+ (PF_TOGGLE, "shadow", "Shadow?", 1),
+ (PF_RADIO, "foo", "Test", "foo", (("Foo", "foo"), ("Bar", "bar"))),
+ (PF_COLOR, "bg-color", "Background", (1.0, 1.0, 1.0)),
+ (PF_COLOR, "sphere-color", "Sphere", "orange")
+ ],
+ [],
+ sphere,
+ menu="<Image>/Filters/Languages/Python-Fu/Test")
+
+main()
diff --git a/plug-ins/pygimp/plug-ins/spyro_plus.py b/plug-ins/pygimp/plug-ins/spyro_plus.py
new file mode 100644
index 0000000..7d31c54
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/spyro_plus.py
@@ -0,0 +1,2212 @@
+#!/usr/bin/env python2
+
+# Draw Spyrographs, Epitrochoids, and Lissajous curves with interactive feedback.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+from gimpshelf import shelf
+from gimpenums import *
+import gimp
+import gimpplugin
+import gimpui
+import gobject
+import gtk
+gdk = gtk.gdk
+
+from math import pi, sin, cos, atan, atan2, fmod, radians, sqrt
+import gettext
+import fractions
+import time
+
+
+# i18n
+t = gettext.translation("gimp20-python", gimp.locale_directory, fallback=True)
+_ = t.ugettext
+
+def N_(message):
+ return message
+
+
+pdb = gimp.pdb
+
+two_pi, half_pi = 2 * pi, pi / 2
+layer_name = _("Spyro Layer")
+path_name = _("Spyro Path")
+
+# "Enums"
+GEAR_NOTATION, TOY_KIT_NOTATION, VISUAL_NOTATION = range(3) # Pattern notations
+
+# Mapping of pattern notation to the corresponding tab in the pattern notation notebook.
+pattern_notation_page = {}
+
+# Save options of the dialog
+SAVE_AS_NEW_LAYER, SAVE_BY_REDRAW, SAVE_AS_PATH = range(3)
+save_options = [
+ _("Save\nas New Layer"),
+ _("Redraw on\nActive layer"),
+ _("Save\nas Path")
+]
+
+ring_teeth = [96, 144, 105, 150]
+
+# Moving gear. Each gear is a pair of (#teeth, #holes)
+# Hole #1 is closest to the edge of the wheel.
+# The last hole is closest to the center.
+wheel = [
+ (24, 5), (30, 8), (32, 9), (36, 11), (40, 13), (42, 14), (45, 16),
+ (48, 17), (50, 18), (52, 19), (56, 21), (60, 23), (63, 25), (64, 25),
+ (72, 29), (75, 31), (80, 33), (84, 35)
+]
+wheel_teeth = [wh[0] for wh in wheel]
+
+
+def lcm(a, b):
+ """ Least common multiplier """
+ return a * b // fractions.gcd(a, b)
+
+
+### Shapes
+
+
+class CanRotateShape:
+ pass
+
+
+class Shape:
+ def configure(self, img, pp, cp):
+ self.image, self.pp, self.cp = img, pp, cp
+
+ def can_equal_w_h(self):
+ return True
+
+ def has_sides(self):
+ return isinstance(self, SidedShape)
+
+ def can_rotate(self):
+ return isinstance(self, CanRotateShape)
+
+ def can_morph(self):
+ return self.has_sides()
+
+
+class CircleShape(Shape):
+ name = _("Circle")
+
+ def get_center_of_moving_gear(self, oangle, dist=None):
+ """
+ :return: x,y - position where the center of the moving gear should be,
+ after going over oangle/two_pi of a full cycle over the outer gear.
+ """
+ cp = self.cp
+ if dist is None:
+ dist = cp.moving_gear_radius
+
+ return (cp.x_center + (cp.x_half_size - dist) * cos(oangle),
+ cp.y_center + (cp.y_half_size - dist) * sin(oangle))
+
+
+class SidedShape(CanRotateShape, Shape):
+
+ def configure(self, img, pp, cp):
+ Shape.configure(self, img, pp, cp)
+ self.angle_of_each_side = two_pi / pp.sides
+ self.half_angle = self.angle_of_each_side / 2.0
+ self.cos_half_angle = cos(self.half_angle)
+
+ def get_center_of_moving_gear(self, oangle, dist=None):
+ if dist is None:
+ dist = self.cp.moving_gear_radius
+ shape_factor = self.get_shape_factor(oangle)
+ return (
+ self.cp.x_center +
+ (self.cp.x_half_size - dist) * shape_factor * cos(oangle),
+ self.cp.y_center +
+ (self.cp.y_half_size - dist) * shape_factor * sin(oangle)
+ )
+
+
+class PolygonShape(SidedShape):
+ name = _("Polygon-Star")
+
+ def get_shape_factor(self, oangle):
+ oangle_mod = fmod(oangle + self.cp.shape_rotation_radians, self.angle_of_each_side)
+ if oangle_mod > self.half_angle:
+ oangle_mod = self.angle_of_each_side - oangle_mod
+
+ # When oangle_mod = 0, the shape_factor will be cos(half_angle)) - which is the minimal shape_factor.
+ # When oangle_mod is near the half_angle, the shape_factor will near 1.
+ shape_factor = self.cos_half_angle / cos(oangle_mod)
+ shape_factor -= self.pp.morph * (1 - shape_factor) * (1 + (self.pp.sides - 3) * 2)
+ return shape_factor
+
+
+class SineShape(SidedShape):
+ # Sine wave on a circle ring.
+ name = _("Sine")
+
+ def get_shape_factor(self, oangle):
+ oangle_mod = fmod(oangle + self.cp.shape_rotation_radians, self.angle_of_each_side)
+ oangle_stretched = oangle_mod * self.pp.sides
+ return 1 - self.pp.morph * (cos(oangle_stretched) + 1)
+
+
+class BumpShape(SidedShape):
+ # Semi-circles, based on a polygon
+ name = _("Bumps")
+
+ def get_shape_factor(self, oangle):
+ oangle_mod = fmod(oangle + self.cp.shape_rotation_radians, self.angle_of_each_side)
+ # Stretch back to angle between 0 and pi
+ oangle_stretched = oangle_mod/2.0 * self.pp.sides
+
+ # Compute factor for polygon.
+ poly_angle = oangle_mod
+ if poly_angle > self.half_angle:
+ poly_angle = self.angle_of_each_side - poly_angle
+ # When poly_oangle = 0, the shape_factor will be cos(half_angle)) - the minimal shape_factor.
+ # When poly_angle is near the half_angle, the shape_factor will near 1.
+ polygon_factor = self.cos_half_angle / cos(poly_angle)
+
+ # Bump
+ return polygon_factor - self.pp.morph * (1 - abs(cos(oangle_stretched)))
+
+
+class ShapePart(object):
+ def set_bounds(self, start, end):
+ self.bound_start, self.bound_end = start, end
+ self.bound_diff = self.bound_end - self.bound_start
+
+
+class StraightPart(ShapePart):
+
+ def __init__(self, teeth, perp_direction, x1, y1, x2, y2):
+ self.teeth, self.perp_direction = max(teeth, 1), perp_direction
+ self.x1, self.y1, self.x2, self.y2 = x1, y1, x2, y2
+ self.x_diff = self.x2 - self.x1
+ self.y_diff = self.y2 - self.y1
+
+ angle = atan2(self.y_diff, self.x_diff) # - shape_rotation_radians
+ perp_angle = angle + perp_direction * half_pi
+ self.sin_angle = sin(perp_angle)
+ self.cos_angle = cos(perp_angle)
+
+ def perpendicular_at_oangle(self, oangle, perp_distance):
+ factor = (oangle - self.bound_start) / self.bound_diff
+ return (self.x1 + factor * self.x_diff + perp_distance * self.cos_angle,
+ self.y1 + factor * self.y_diff + perp_distance * self.sin_angle)
+
+
+class RoundPart(ShapePart):
+
+ def __init__(self, teeth, x, y, start_angle, end_angle):
+ self.teeth = max(teeth, 1)
+ self.start_angle, self.end_angle = start_angle, end_angle
+ self.x, self.y = x, y
+
+ self.diff_angle = self.end_angle - self.start_angle
+
+ def perpendicular_at_oangle(self, oangle, perp_distance):
+ angle = (
+ self.start_angle +
+ self.diff_angle * (oangle - self.bound_start) / self.bound_diff
+ )
+ return (self.x + perp_distance * cos(angle),
+ self.y + perp_distance * sin(angle))
+
+
+class ShapeParts(list):
+ """ A list of shape parts. """
+
+ def __init__(self):
+ list.__init__(self)
+ self.total_teeth = 0
+
+ def finish(self):
+ for part in self:
+ self.total_teeth += part.teeth
+ teeth = 0
+ bound_end = 0.0
+ for part in self:
+ bound_start = bound_end
+ teeth += part.teeth
+ bound_end = teeth/float(self.total_teeth) * two_pi
+ part.set_bounds(bound_start, bound_end)
+
+ def perpendicular_at_oangle(self, oangle, perp_distance):
+ for part in self:
+ if oangle <= part.bound_end:
+ return part.perpendicular_at_oangle(oangle, perp_distance)
+
+ # We shouldn't reach here
+ return 0.0, 0.0
+
+
+class AbstractShapeFromParts(Shape):
+ def __init__(self):
+ self.parts = None
+
+ def get_center_of_moving_gear(self, oangle, dist=None):
+ """
+ :param oangle: an angle in radians, between 0 and 2*pi
+ :return: x,y - position where the center of the moving gear should be,
+ after going over oangle/two_pi of a full cycle over the outer gear.
+ """
+ if dist is None:
+ dist = self.cp.moving_gear_radius
+ return self.parts.perpendicular_at_oangle(oangle, dist)
+
+
+class RackShape(CanRotateShape, AbstractShapeFromParts):
+ name = _("Rack")
+
+ def configure(self, img, pp, cp):
+ Shape.configure(self, img, pp, cp)
+
+ round_teeth = 12
+ side_teeth = (cp.fixed_gear_teeth - 2 * round_teeth) / 2
+
+ # Determine start and end points of rack.
+
+ cos_rot = cos(cp.shape_rotation_radians)
+ sin_rot = sin(cp.shape_rotation_radians)
+
+ x_size = cp.x2 - cp.x1 - cp.moving_gear_radius * 4
+ y_size = cp.y2 - cp.y1 - cp.moving_gear_radius * 4
+
+ size = ((x_size * cos_rot)**2 + (y_size * sin_rot)**2) ** 0.5
+
+ x1 = cp.x_center - size/2.0 * cos_rot
+ y1 = cp.y_center - size/2.0 * sin_rot
+ x2 = cp.x_center + size/2.0 * cos_rot
+ y2 = cp.y_center + size/2.0 * sin_rot
+
+ # Build shape from shape parts.
+ self.parts = ShapeParts()
+ self.parts.append(StraightPart(side_teeth, -1, x2, y2, x1, y1))
+ self.parts.append(
+ RoundPart(
+ round_teeth, x1, y1,
+ half_pi + cp.shape_rotation_radians,
+ 3 * half_pi + cp.shape_rotation_radians
+ )
+ )
+ self.parts.append(StraightPart(side_teeth, -1, x1, y1, x2, y2))
+ self.parts.append(
+ RoundPart(
+ round_teeth, x2, y2,
+ 3 * half_pi + cp.shape_rotation_radians,
+ 5 * half_pi + cp.shape_rotation_radians)
+ )
+ self.parts.finish()
+
+
+class FrameShape(AbstractShapeFromParts):
+ name = _("Frame")
+
+ def configure(self, img, pp, cp):
+ Shape.configure(self, img, pp, cp)
+
+ x1, x2 = cp.x1 + cp.moving_gear_radius, cp.x2 - cp.moving_gear_radius
+ y1, y2 = cp.y1 + cp.moving_gear_radius, cp.y2 - cp.moving_gear_radius
+ x_diff, y_diff = abs(x2 - x1), abs(y2 - y1)
+
+ # Build shape from shape parts.
+ self.parts = ShapeParts()
+ self.parts.append(StraightPart(x_diff, 1, x2, cp.y2, x1, cp.y2))
+ self.parts.append(StraightPart(y_diff, 1, cp.x1, y2, cp.x1, y1))
+ self.parts.append(StraightPart(x_diff, 1, x1, cp.y1, x2, cp.y1))
+ self.parts.append(StraightPart(y_diff, 1, cp.x2, y1, cp.x2, y2))
+ self.parts.finish()
+
+
+class SelectionToPath:
+ """ Converts a selection to a path """
+
+ def __init__(self, image):
+ self.image = image
+
+ # Compute hash of selection, so we can detect when it was modified.
+ self.last_selection_hash = self.compute_selection_hash()
+
+ self.convert_selection_to_path()
+
+ def convert_selection_to_path(self):
+
+ if pdb.gimp_selection_is_empty(self.image):
+ selection_was_empty = True
+ pdb.gimp_selection_all(self.image)
+ else:
+ selection_was_empty = False
+
+ pdb.plug_in_sel2path(self.image, self.image.active_layer)
+
+ self.path = self.image.vectors[0]
+
+ self.num_strokes, self.stroke_ids = pdb.gimp_vectors_get_strokes(self.path)
+ self.stroke_ids = list(self.stroke_ids)
+
+ # A path may contain several strokes. If so lets throw away a stroke that
+ # simply describes the borders of the image, if one exists.
+ if self.num_strokes > 1:
+ # Lets compute what a stroke of the image borders should look like.
+ w, h = float(self.image.width), float(self.image.height)
+ frame_strokes = [0.0] * 6 + [0.0, h] * 3 + [w, h] * 3 + [w, 0.0] * 3
+
+ for stroke in range(self.num_strokes):
+ strokes = self.path.strokes[stroke].points[0]
+ if strokes == frame_strokes:
+ del self.stroke_ids[stroke]
+ self.num_strokes -= 1
+ break
+
+ self.set_current_stroke(0)
+
+ if selection_was_empty:
+ # Restore empty selection if it was empty.
+ pdb.gimp_selection_none(self.image)
+
+ def compute_selection_hash(self):
+ px = self.image.selection.get_pixel_rgn(0, 0, self.image.width, self.image.height)
+ return px[0:self.image.width, 0:self.image.height].__hash__()
+
+ def regenerate_path_if_selection_changed(self):
+ current_selection_hash = self.compute_selection_hash()
+ if self.last_selection_hash != current_selection_hash:
+ self.last_selection_hash = current_selection_hash
+ self.convert_selection_to_path()
+
+ def get_num_strokes(self):
+ return self.num_strokes
+
+ def set_current_stroke(self, stroke_id=0):
+ # Compute path length.
+ self.path_length = pdb.gimp_vectors_stroke_get_length(self.path, self.stroke_ids[stroke_id], 1.0)
+ self.current_stroke = stroke_id
+
+ def point_at_angle(self, oangle):
+ oangle_mod = fmod(oangle, two_pi)
+ dist = self.path_length * oangle_mod / two_pi
+ return pdb.gimp_vectors_stroke_get_point_at_dist(self.path, self.stroke_ids[self.current_stroke], dist, 1.0)
+
+
+class SelectionShape(Shape):
+ name = _("Selection")
+
+ def __init__(self):
+ self.path = None
+
+ def process_selection(self, img):
+ if self.path is None:
+ self.path = SelectionToPath(img)
+ else:
+ self.path.regenerate_path_if_selection_changed()
+
+ def configure(self, img, pp, cp):
+ """ Set bounds of pattern """
+ Shape.configure(self, img, pp, cp)
+ self.drawing_no = cp.current_drawing
+ self.path.set_current_stroke(self.drawing_no)
+
+ def get_num_drawings(self):
+ return self.path.get_num_strokes()
+
+ def can_equal_w_h(self):
+ return False
+
+ def get_center_of_moving_gear(self, oangle, dist=None):
+ """
+ :param oangle: an angle in radians, between 0 and 2*pi
+ :return: x,y - position where the center of the moving gear should be,
+ after going over oangle/two_pi of a full cycle over the outer gear.
+ """
+ cp = self.cp
+ if dist is None:
+ dist = cp.moving_gear_radius
+ x, y, slope, valid = self.path.point_at_angle(oangle)
+ slope_angle = atan(slope)
+ # We want to find an angle perpendicular to the slope, but in which direction?
+ # Lets try both sides and see which of them is inside the selection.
+ perpendicular_p, perpendicular_m = slope_angle + half_pi, slope_angle - half_pi
+ step_size = 2 # The distance we are going to go in the direction of each angle.
+ xp, yp = x + step_size * cos(perpendicular_p), y + step_size * sin(perpendicular_p)
+ value_plus = pdb.gimp_selection_value(self.image, xp, yp)
+ xp, yp = x + step_size * cos(perpendicular_m), y + step_size * sin(perpendicular_m)
+ value_minus = pdb.gimp_selection_value(self.image, xp, yp)
+
+ perpendicular = perpendicular_p if value_plus > value_minus else perpendicular_m
+ return x + dist * cos(perpendicular), y + dist * sin(perpendicular)
+
+
+shapes = [
+ CircleShape(), RackShape(), FrameShape(), SelectionShape(),
+ PolygonShape(), SineShape(), BumpShape()
+]
+
+
+### Tools
+
+
+def get_gradient_samples(num_samples):
+ gradient_name = pdb.gimp_context_get_gradient()
+ reverse_mode = pdb.gimp_context_get_gradient_reverse()
+ repeat_mode = pdb.gimp_context_get_gradient_repeat_mode()
+
+ if repeat_mode == REPEAT_TRIANGULAR:
+ # Get two uniform samples, which are reversed from each other, and connect them.
+
+ samples = num_samples/2 + 1
+ num, color_samples = pdb.gimp_gradient_get_uniform_samples(gradient_name,
+ samples, reverse_mode)
+
+ color_samples = list(color_samples)
+ del color_samples[-4:] # Delete last color because it will appear in the next sample
+
+ # If num_samples is odd, lets get an extra sample this time.
+ if num_samples % 2 == 1:
+ samples += 1
+
+ num, color_samples2 = pdb.gimp_gradient_get_uniform_samples(gradient_name,
+ samples, 1 - reverse_mode)
+
+ color_samples2 = list(color_samples2)
+ del color_samples2[-4:] # Delete last color because it will appear in the very first sample
+
+ color_samples.extend(color_samples2)
+ color_samples = tuple(color_samples)
+ else:
+ num, color_samples = pdb.gimp_gradient_get_uniform_samples(gradient_name, num_samples, reverse_mode)
+
+ return color_samples
+
+
+class PencilTool():
+ name = _("Pencil")
+ can_color = True
+
+ def draw(self, layer, strokes, color=None):
+ if color:
+ pdb.gimp_context_push()
+ pdb.gimp_context_set_dynamics('Dynamics Off')
+ pdb.gimp_context_set_foreground(color)
+
+ pdb.gimp_pencil(layer, len(strokes), strokes)
+
+ if color:
+ pdb.gimp_context_pop()
+
+
+class AirBrushTool():
+ name = _("AirBrush")
+ can_color = True
+
+ def draw(self, layer, strokes, color=None):
+ if color:
+ pdb.gimp_context_push()
+ pdb.gimp_context_set_dynamics('Dynamics Off')
+ pdb.gimp_context_set_foreground(color)
+
+ pdb.gimp_airbrush_default(layer, len(strokes), strokes)
+
+ if color:
+ pdb.gimp_context_pop()
+
+
+class AbstractStrokeTool():
+
+ def draw(self, layer, strokes, color=None):
+ # We need to multiply every point by 3, because we are creating a path,
+ # where each point has two additional control points.
+ control_points = []
+ for i, k in zip(strokes[0::2], strokes[1::2]):
+ control_points += [i, k] * 3
+
+ # Create path
+ path = pdb.gimp_vectors_new(layer.image, 'temp_path')
+ pdb.gimp_image_add_vectors(layer.image, path, 0)
+ sid = pdb.gimp_vectors_stroke_new_from_points(path, 0, len(control_points),
+ control_points, False)
+
+ # Draw it.
+
+ pdb.gimp_context_push()
+
+ # Call template method to set the kind of stroke to draw.
+ self.prepare_stroke_context(color)
+
+ pdb.gimp_drawable_edit_stroke_item(layer, path)
+ pdb.gimp_context_pop()
+
+ # Get rid of the path.
+ pdb.gimp_image_remove_vectors(layer.image, path)
+
+
+# Drawing tool that should be quick, for purposes of previewing the pattern.
+class PreviewTool:
+
+ # Implementation using pencil. (A previous implementation using stroke was slower, and thus removed).
+ def draw(self, layer, strokes, color=None):
+ foreground = pdb.gimp_context_get_foreground()
+ pdb.gimp_context_push()
+ pdb.gimp_context_set_defaults()
+ pdb.gimp_context_set_foreground(foreground)
+ pdb.gimp_context_set_dynamics('Dynamics Off')
+ pdb.gimp_context_set_brush('1. Pixel')
+ pdb.gimp_context_set_brush_size(1.0)
+ pdb.gimp_context_set_brush_spacing(3.0)
+ pdb.gimp_pencil(layer, len(strokes), strokes)
+ pdb.gimp_context_pop()
+
+ name = _("Preview")
+ can_color = False
+
+
+class StrokeTool(AbstractStrokeTool):
+ name = _("Stroke")
+ can_color = True
+
+ def prepare_stroke_context(self, color):
+ if color:
+ pdb.gimp_context_set_dynamics('Dynamics Off')
+ pdb.gimp_context_set_foreground(color)
+
+ pdb.gimp_context_set_stroke_method(STROKE_LINE)
+
+
+class StrokePaintTool(AbstractStrokeTool):
+ def __init__(self, name, paint_method, can_color=True):
+ self.name = name
+ self.paint_method = paint_method
+ self.can_color = can_color
+
+ def prepare_stroke_context(self, color):
+ if self.can_color and color is not None:
+ pdb.gimp_context_set_dynamics('Dynamics Off')
+ pdb.gimp_context_set_foreground(color)
+
+ pdb.gimp_context_set_stroke_method(STROKE_PAINT_METHOD)
+ pdb.gimp_context_set_paint_method(self.paint_method)
+
+
+class SaveToPathTool():
+ """ This tool cannot be chosen by the user from the tools menu.
+ We dont add this to the list of tools. """
+
+ def __init__(self, img):
+ self.path = pdb.gimp_vectors_new(img, path_name)
+ pdb.gimp_image_add_vectors(img, self.path, 0)
+
+ def draw(self, layer, strokes, color=None):
+ # We need to multiply every point by 3, because we are creating a path,
+ # where each point has two additional control points.
+ control_points = []
+ for i, k in zip(strokes[0::2], strokes[1::2]):
+ control_points += [i, k] * 3
+
+ sid = pdb.gimp_vectors_stroke_new_from_points(self.path, 0, len(control_points),
+ control_points, False)
+
+
+tools = [
+ PreviewTool(),
+ StrokePaintTool(_("PaintBrush"), "gimp-paintbrush"),
+ PencilTool(), AirBrushTool(), StrokeTool(),
+ StrokePaintTool(_("Ink"), 'gimp-ink'),
+ StrokePaintTool(_("MyPaintBrush"), 'gimp-mybrush')
+ # Clone does not work properly when an image is not set. When that happens, drawing fails, and
+ # I am unable to catch the error. This causes the plugin to crash, and subsequent problems with undo.
+ # StrokePaintTool("Clone", 'gimp-clone', False)
+]
+
+
+class PatternParameters:
+ """
+ All the parameters that define a pattern live in objects of this class.
+ If you serialize and saved this class, you should reproduce
+ the pattern that the plugin would draw.
+ """
+ def __init__(self):
+ if not hasattr(self, 'curve_type'):
+ self.curve_type = 0
+
+ # Pattern
+ if not hasattr(self, 'pattern_notation'):
+ self.pattern_notation = 0
+ if not hasattr(self, 'outer_teeth'):
+ self.outer_teeth = 96
+ if not hasattr(self, 'inner_teeth'):
+ self.inner_teeth = 36
+ if not hasattr(self, 'pattern_rotation'):
+ self.pattern_rotation = 0
+ # Location of hole as a percent of the radius of the inner gear - runs between 0 and 100.
+ # A value of 0 means, the hole is at the center of the wheel, which would produce a boring circle.
+ # A value of 100 means the edge of the wheel.
+ if not hasattr(self, 'hole_percent'):
+ self.hole_percent = 100.0
+
+ # Toy Kit parameters
+ # Hole number in Toy Kit notation. Hole #1 is at the edge of the wheel, and the last hole is
+ # near the center of the wheel, but not exactly at the center.
+ if not hasattr(self, 'hole_number'):
+ self.hole_number = 1
+ if not hasattr(self, 'kit_fixed_gear_index'):
+ self.kit_fixed_gear_index = 1
+ if not hasattr(self, 'kit_moving_gear_index'):
+ self.kit_moving_gear_index = 1
+
+ # Visual notation parameters
+ if not hasattr(self, 'petals'):
+ self.petals = 5
+ if not hasattr(self, 'petal_skip'):
+ self.petal_skip = 2
+ if not hasattr(self, 'doughnut_hole'):
+ self.doughnut_hole = 50.0
+ if not hasattr(self, 'doughnut_width'):
+ self.doughnut_width = 50.0
+
+ # Shape
+ if not hasattr(self, 'shape_index'):
+ self.shape_index = 0 # Index in the shapes array
+ if not hasattr(self, 'sides'):
+ self.sides = 5
+ if not hasattr(self, 'morph'):
+ self.morph = 0.5
+ if not hasattr(self, 'shape_rotation'):
+ self.shape_rotation = 0
+
+ if not hasattr(self, 'equal_w_h'):
+ self.equal_w_h = False
+ if not hasattr(self, 'margin_pixels'):
+ self.margin_pixels = 0 # Distance between the drawn shape, and the selection borders.
+
+ # Drawing style
+ if not hasattr(self, 'tool_index'):
+ self.tool_index = 0 # Index in the tools array.
+ if not hasattr(self, 'long_gradient'):
+ self.long_gradient = False
+
+ if not hasattr(self, 'save_option'):
+ self.save_option = SAVE_AS_NEW_LAYER
+
+ def kit_max_hole_number(self):
+ return wheel[self.kit_moving_gear_index][1]
+
+
+# Handle shelving of plugin parameters
+
+def unshelf_parameters():
+ if shelf.has_key("p"):
+ parameters = shelf["p"]
+ parameters.__init__() # Fill in missing values with defaults.
+ return parameters
+
+ return PatternParameters()
+
+
+def shelf_parameters(pp):
+ shelf["p"] = pp
+
+
+class ComputedParameters:
+ """
+ Stores computations performed on a PatternParameters object.
+ The results of these computations are used to perform the drawing.
+ Having all these computations in one place makes it convenient to pass
+ around as a parameter.
+
+ If the pattern parameters should result in multiple pattern to be drawn, the
+ compute parameters also stores which one is currently being drawn.
+ """
+
+ def __init__(self, pp, img):
+
+ def compute_gradients():
+ self.use_gradient = self.pp.long_gradient and tools[self.pp.tool_index].can_color
+
+ # If gradient is used, determine how the lines are two be split to different colors.
+ if self.use_gradient:
+ # We want to use enough samples to be beautiful, but not too many, that would
+ # force us to make many separate calls for drawing the pattern.
+ if self.rotations > 30:
+ self.chunk_num = self.rotations
+ self.chunk_size_lines = self.fixed_gear_teeth
+ else:
+ # Lets try to find a chunk size, such that it divides num_lines, and we get at least 30 chunks.
+ # In the worse case, we will just use "1"
+ for chunk_size in range(self.fixed_gear_teeth - 1, 0, -1):
+ if self.num_lines % chunk_size == 0:
+ if self.num_lines / chunk_size > 30:
+ break
+
+ self.chunk_num = self.num_lines / chunk_size
+ self.chunk_size_lines = chunk_size
+
+ self.gradients = get_gradient_samples(self.chunk_num)
+ else:
+ self.chunk_num, self.chunk_size_lines = None, None
+
+ def compute_sizes():
+ # Get rid of the margins.
+ self.x1 = x1 + pp.margin_pixels
+ self.y1 = y1 + pp.margin_pixels
+ self.x2 = x2 - pp.margin_pixels
+ self.y2 = y2 - pp.margin_pixels
+
+ # Compute size and position of the pattern
+ self.x_half_size, self.y_half_size = (self.x2 - self.x1) / 2, (self.y2 - self.y1) / 2
+ self.x_center, self.y_center = (self.x1 + self.x2) / 2.0, (self.y1 + self.y2) / 2.0
+
+ if pp.equal_w_h:
+ if self.x_half_size < self.y_half_size:
+ self.y_half_size = self.x_half_size
+ self.y1, self.y2 = self.y_center - self.y_half_size, self.y_center + self.y_half_size
+ elif self.x_half_size > self.y_half_size:
+ self.x_half_size = self.y_half_size
+ self.x1, self.x2 = self.x_center - self.x_half_size, self.x_center + self.x_half_size
+
+ # Find the distance between the hole and the center of the inner circle.
+ # To do this, we compute the size of the gears, by the number of teeth.
+ # The circumference of the outer ring is 2 * pi * outer_R = #fixed_gear_teeth * tooth size.
+ outer_R = min(self.x_half_size, self.y_half_size)
+ if self.pp.pattern_notation == VISUAL_NOTATION:
+ doughnut_width = self.pp.doughnut_width
+ if doughnut_width + self.pp.doughnut_hole > 100:
+ doughnut_width = 100.0 - self.pp.doughnut_hole
+
+ # Let R, r be the radius of fixed and moving gear, and let hp be the hole percent.
+ # Let dwp, dhp be the doughnut width and hole in percents of R.
+ # The two sides of the following equation calculate how to reach the center of the moving
+ # gear from the center of the fixed gear:
+ # I) R * (dhp/100 + dwp/100/2) = R - r
+ # The following equation expresses which r and hp would generate a doughnut of width dw.
+ # II) R * dw/100 = 2 * r * hp/100
+ # We solve the two above equations to calculate hp and r:
+ self.hole_percent = doughnut_width / (2.0 * (1 - (self.pp.doughnut_hole + doughnut_width/2.0)/100.0))
+ self.moving_gear_radius = outer_R * doughnut_width / (2 * self.hole_percent)
+ else:
+ size_of_tooth_in_pixels = two_pi * outer_R / self.fixed_gear_teeth
+ self.moving_gear_radius = size_of_tooth_in_pixels * self.moving_gear_teeth / two_pi
+
+ self.hole_dist_from_center = self.hole_percent / 100.0 * self.moving_gear_radius
+
+ self.pp = pp
+
+ # Check if the shape is made of multiple shapes, as in using Selection as fixed gear.
+ if (isinstance(shapes[self.pp.shape_index], SelectionShape) and
+ curve_types[self.pp.curve_type].supports_shapes()):
+ shapes[self.pp.shape_index].process_selection(img)
+ pdb.gimp_displays_flush()
+ self.num_drawings = shapes[self.pp.shape_index].get_num_drawings()
+ else:
+ self.num_drawings = 1
+ self.current_drawing = 0
+
+ # Get bounds. We don't care weather a selection exists or not.
+ exists, x1, y1, x2, y2 = pdb.gimp_selection_bounds(img)
+
+ # Combine different ways to specify patterns, into a unified set of computed parameters.
+ self.num_notation_drawings = 1
+ self.current_notation_drawing = 0
+ if self.pp.pattern_notation == GEAR_NOTATION:
+ self.fixed_gear_teeth = int(round(pp.outer_teeth))
+ self.moving_gear_teeth = int(round(pp.inner_teeth))
+ self.petals = self.num_petals()
+ self.hole_percent = pp.hole_percent
+ elif self.pp.pattern_notation == TOY_KIT_NOTATION:
+ self.fixed_gear_teeth = ring_teeth[pp.kit_fixed_gear_index]
+ self.moving_gear_teeth = wheel[pp.kit_moving_gear_index][0]
+ self.petals = self.num_petals()
+ # We want to map hole #1 to 100% and hole of max_hole_number to 2.5%
+ # We don't want 0% because that would be the exact center of the moving gear,
+ # and that would create a boring pattern.
+ max_hole_number = wheel[pp.kit_moving_gear_index][1]
+ self.hole_percent = (max_hole_number - pp.hole_number) / float(max_hole_number - 1) * 97.5 + 2.5
+ elif self.pp.pattern_notation == VISUAL_NOTATION:
+ self.petals = pp.petals
+ self.fixed_gear_teeth = pp.petals
+ self.moving_gear_teeth = pp.petals - pp.petal_skip
+ if self.moving_gear_teeth < 20:
+ self.fixed_gear_teeth *= 10
+ self.moving_gear_teeth *= 10
+ self.hole_percent = 100.0
+ self.num_notation_drawings = fractions.gcd(pp.petals, pp.petal_skip)
+ self.notation_drawings_rotation = two_pi/pp.petals
+
+ # Rotations
+ self.shape_rotation_radians = self.radians_from_degrees(pp.shape_rotation)
+ self.pattern_rotation_start_radians = self.radians_from_degrees(pp.pattern_rotation)
+ self.pattern_rotation_radians = self.pattern_rotation_start_radians
+ # Additional fixed pattern rotation for lissajous.
+ self.lissajous_rotation = two_pi/self.petals/4.0
+
+ # Compute the total number of teeth we have to go over.
+ # Another way to view it is the total of lines we are going to draw.
+ # To find this we compute the Least Common Multiplier.
+ self.num_lines = lcm(self.fixed_gear_teeth, self.moving_gear_teeth)
+ # The number of points we are going to compute. This is the number of lines, plus 1, because to draw
+ # a line we need two points.
+ self.num_points = self.num_lines + 1
+
+ # Compute gradients.
+
+ # The number or rotations needed in order to complete the pattern.
+ # Each rotation has cp.fixed_gear_teeth points + 1 points.
+ self.rotations = self.num_lines / self.fixed_gear_teeth
+
+ compute_gradients()
+
+ # Computations needed for the actual drawing of the patterns - how much should we advance each angle
+ # in each step of the computation.
+
+ # How many radians is each tooth of outer gear. This is also the amount that we
+ # will step in the iterations that generate the points of the pattern.
+ self.oangle_factor = two_pi / self.fixed_gear_teeth
+ # How many radians should the moving gear be moved, for each tooth of the fixed gear
+ angle_factor = curve_types[pp.curve_type].get_angle_factor(self)
+ self.iangle_factor = self.oangle_factor * angle_factor
+
+ compute_sizes()
+
+ def num_petals(self):
+ """ The number of 'petals' (or points) that will be produced by a spirograph drawing. """
+ return lcm(self.fixed_gear_teeth, self.moving_gear_teeth) / self.moving_gear_teeth
+
+ def radians_from_degrees(self, degrees):
+ positive_degrees = degrees if degrees >= 0 else degrees + 360
+ return radians(positive_degrees)
+
+ def get_color(self, n):
+ return self.gradients[4*n:4*(n+1)]
+
+ def next_drawing(self):
+ """ Multiple drawings can be drawn either when the selection is used as a fixed
+ gear, and/or the visual tab is used, which causes multiple drawings
+ to be drawn at different rotations. """
+ if self.current_notation_drawing < self.num_notation_drawings - 1:
+ self.current_notation_drawing += 1
+ self.pattern_rotation_radians = self.pattern_rotation_start_radians + (
+ self.current_notation_drawing * self.notation_drawings_rotation)
+ else:
+ self.current_drawing += 1
+ self.current_notation_drawing = 0
+ self.pattern_rotation_radians = self.pattern_rotation_start_radians
+
+ def has_more_drawings(self):
+ return (self.current_notation_drawing < self.num_notation_drawings - 1 or
+ self.current_drawing < self.num_drawings - 1)
+
+
+### Curve types
+
+
+class CurveType:
+
+ def supports_shapes(self):
+ return True
+
+class RouletteCurveType(CurveType):
+
+ def get_strokes(self, p, cp):
+ strokes = []
+ for curr_tooth in range(cp.num_points):
+ iangle = fmod(curr_tooth * cp.iangle_factor + cp.pattern_rotation_radians, two_pi)
+ oangle = fmod(curr_tooth * cp.oangle_factor + cp.pattern_rotation_radians, two_pi)
+
+ x, y = shapes[p.shape_index].get_center_of_moving_gear(oangle)
+ strokes.append(x + cp.hole_dist_from_center * cos(iangle))
+ strokes.append(y + cp.hole_dist_from_center * sin(iangle))
+
+ return strokes
+
+
+class SpyroCurveType(RouletteCurveType):
+ name = _("Spyrograph")
+
+ def get_angle_factor(self, cp):
+ return - (cp.fixed_gear_teeth - cp.moving_gear_teeth) / float(cp.moving_gear_teeth)
+
+
+class EpitrochoidCurvetype(RouletteCurveType):
+ name = _("Epitrochoid")
+
+ def get_angle_factor(self, cp):
+ return (cp.fixed_gear_teeth + cp.moving_gear_teeth) / float(cp.moving_gear_teeth)
+
+
+class SineCurveType(CurveType):
+ name = _("Sine")
+
+ def get_angle_factor(self, cp):
+ return cp.fixed_gear_teeth / float(cp.moving_gear_teeth)
+
+ def get_strokes(self, p, cp):
+ strokes = []
+ for curr_tooth in range(cp.num_points):
+ iangle = curr_tooth * cp.iangle_factor
+ oangle = fmod(curr_tooth * cp.oangle_factor + cp.pattern_rotation_radians, two_pi)
+
+ dist = cp.moving_gear_radius + sin(iangle) * cp.hole_dist_from_center
+ x, y = shapes[p.shape_index].get_center_of_moving_gear(oangle, dist)
+ strokes.append(x)
+ strokes.append(y)
+
+ return strokes
+
+
+class LissaCurveType:
+ name = _("Lissajous")
+
+ def get_angle_factor(self, cp):
+ return cp.fixed_gear_teeth / float(cp.moving_gear_teeth)
+
+ def get_strokes(self, p, cp):
+ strokes = []
+ for curr_tooth in range(cp.num_points):
+ iangle = curr_tooth * cp.iangle_factor
+ # Adding the cp.lissajous_rotation rotation makes the pattern have the same number of curves
+ # as the other curve types. Without it, many lissajous patterns would redraw the same lines twice,
+ # and thus look less dense than the other curves.
+ oangle = fmod(curr_tooth * cp.oangle_factor + cp.pattern_rotation_radians + cp.lissajous_rotation, two_pi)
+
+ strokes.append(cp.x_center + cp.x_half_size * cos(oangle))
+ strokes.append(cp.y_center + cp.y_half_size * cos(iangle))
+
+ return strokes
+
+ def supports_shapes(self):
+ return False
+
+
+curve_types = [SpyroCurveType(), EpitrochoidCurvetype(), SineCurveType(), LissaCurveType()]
+
+# Drawing engine. Also implements drawing incrementally.
+# We don't draw the entire stroke, because it could take several seconds,
+# Instead, we break it into chunks. Incremental drawing is also used for drawing gradients.
+class DrawingEngine:
+
+ def __init__(self, img, p):
+ self.img, self.p = img, p
+ self.cp = None
+
+ # For incremental drawing
+ self.strokes = []
+ self.start = 0
+ self.chunk_size_lines = 600
+ self.chunk_no = 0
+ # We are aiming for the drawing time of a chunk to be no longer than max_time.
+ self.max_time_sec = 0.1
+
+ self.dynamic_chunk_size = True
+
+ def pre_draw(self):
+ """ Needs to be called before starting to draw a pattern. """
+
+ self.cp = ComputedParameters(self.p, self.img)
+
+ def draw_full(self, layer):
+ """ Non incremental drawing. """
+
+ self.pre_draw()
+ self.img.undo_group_start()
+
+ while true:
+ self.set_strokes()
+
+ if self.cp.use_gradient:
+ while self.has_more_strokes():
+ self.draw_next_chunk(layer, fetch_next_drawing=False)
+ else:
+ tools[self.p.tool_index].draw(layer, self.strokes)
+
+ if self.cp.has_more_drawings():
+ self.cp.next_drawing()
+ else:
+ break
+
+ self.img.undo_group_end()
+
+ pdb.gimp_displays_flush()
+
+ # Methods for incremental drawing.
+
+ def draw_next_chunk(self, layer, fetch_next_drawing=True, tool=None):
+ stroke_chunk, color = self.next_chunk(fetch_next_drawing)
+ if not tool:
+ tool = tools[self.p.tool_index]
+ tool.draw(layer, stroke_chunk, color)
+ return len(stroke_chunk)
+
+ def set_strokes(self):
+ """ Compute the strokes of the current pattern. The heart of the plugin. """
+
+ shapes[self.p.shape_index].configure(self.img, self.p, self.cp)
+
+ self.strokes = curve_types[self.p.curve_type].get_strokes(self.p, self.cp)
+
+ self.start = 0
+ self.chunk_no = 0
+
+ if self.cp.use_gradient:
+ self.chunk_size_lines = self.cp.chunk_size_lines
+ self.dynamic_chunk_size = False
+ else:
+ self.dynamic_chunk_size = True
+
+ def reset_incremental(self):
+ """ Setup incremental drawing to start drawing from scratch. """
+ self.pre_draw()
+ self.set_strokes()
+
+ def next_chunk(self, fetch_next_drawing):
+
+ # chunk_size_lines, is the number of lines we want to draw. We need 1 extra point to draw that.
+ end = self.start + (self.chunk_size_lines + 1) * 2
+ if end > len(self.strokes):
+ end = len(self.strokes)
+ result = self.strokes[self.start:end]
+ # Promote the start to the last point. This is the start of the first line to draw next time.
+ self.start = end - 2
+ color = self.cp.get_color(self.chunk_no) if self.cp.use_gradient else None
+
+ self.chunk_no += 1
+
+ # If self.strokes has ended, lets fetch strokes for the next drawing.
+ if fetch_next_drawing and not self.has_more_strokes():
+ if self.cp.has_more_drawings():
+ self.cp.next_drawing()
+ self.set_strokes()
+
+ return result, color
+
+ def has_more_strokes(self):
+ return self.start + 2 < len(self.strokes)
+
+ # Used for displaying progress.
+ def fraction_done(self):
+ return (self.start + 2.0) / len(self.strokes)
+
+ def report_time(self, time_sec):
+ """
+ Report the time it took, in seconds, to draw the last stroke chunk.
+ This helps to determine the size of chunks to return in future calls of 'next_chunk',
+ since we want the calls to be short, to not make the user interface feel stuck.
+ """
+ if time_sec != 0 and self.dynamic_chunk_size:
+ self.chunk_size_lines = int(self.chunk_size_lines * self.max_time_sec / time_sec)
+ # Don't let chunk size be too large or small.
+ self.chunk_size_lines = max(10, self.chunk_size_lines)
+ self.chunk_size_lines = min(1000, self.chunk_size_lines)
+
+
+# Constants for DoughnutWidget
+
+# Enum - When the mouse is pressed, which target value is being changed.
+TARGET_NONE, TARGET_HOLE, TARGET_WIDTH = range(3)
+
+CIRCLE_CENTER_X = 4
+RIGHT_MARGIN = 2
+TOTAL_MARGIN = CIRCLE_CENTER_X + RIGHT_MARGIN
+
+# A widget for displaying and setting the pattern of a spirograph, using a "doughnut" as
+# a visual metaphore. This widget replaces two scale widgets.
+class DoughnutWidget(gtk.DrawingArea):
+ __gtype_name__ = 'DoughnutWidget'
+
+ def __init__(self, *args, **kwds):
+ super(DoughnutWidget, self).__init__(*args, **kwds)
+ self.set_size_request(80, 40)
+
+ self.add_events(
+ gdk.BUTTON1_MOTION_MASK |
+ gdk.BUTTON_PRESS_MASK |
+ gdk.BUTTON_RELEASE_MASK |
+ gdk.POINTER_MOTION_MASK
+ )
+
+ self.default_cursor = self.get_screen().get_root_window().get_cursor()
+ self.resize_cursor = gdk.Cursor(gdk.SB_H_DOUBLE_ARROW)
+
+ self.button_pressed = False
+ self.target = TARGET_NONE
+
+ self.hole_radius = 30
+ self.doughnut_width = 30
+ self.connect("expose-event", self.expose)
+
+ def set_hole_radius(self, hole_radius):
+ self.queue_draw()
+ self.hole_radius = hole_radius
+
+ def get_hole_radius(self):
+ return self.hole_radius
+
+ def set_width(self, width):
+ self.queue_draw()
+ self.doughnut_width = width
+
+ def get_width(self):
+ return self.doughnut_width
+
+ def compute_doughnut(self):
+ """ Compute the location of the doughnut circles.
+ Returns (circle center x, circle center y, radius of inner circle, radius of outer circle) """
+ allocation = self.get_allocation()
+ alloc_width = allocation.width - TOTAL_MARGIN
+ return (
+ CIRCLE_CENTER_X, allocation.height / 2,
+ alloc_width * self.hole_radius / 100.0,
+ alloc_width * min(self.hole_radius + self.doughnut_width, 100.0) / 100.0
+ )
+
+ def set_cursor_h_resize(self):
+ """Set the mouse to be a double arrow."""
+ gdk_window = self.get_window()
+ gdk_window.set_cursor(self.resize_cursor)
+
+ def set_default_cursor(self):
+ gdk_window = self.get_window()
+ gdk_window.set_cursor(self.default_cursor)
+
+ def get_target(self, x, y):
+ # Find out if x, y is over one of the circle edges.
+
+ center_x, center_y, hole_radius, outer_radius = self.compute_doughnut()
+
+ # Compute distance from circle center to point
+ dist = sqrt((center_x - x) ** 2 + (center_y - y) ** 2)
+
+ if abs(dist - hole_radius) <= 3:
+ return TARGET_HOLE
+ if abs(dist - outer_radius) <= 3:
+ return TARGET_WIDTH
+
+ return TARGET_NONE
+
+ def expose(self, widget, event):
+
+ cr = widget.window.cairo_create()
+ center_x, center_y, hole_radius, outer_radius = self.compute_doughnut()
+ fg_color = gtk.widget_get_default_style().fg[gtk.STATE_NORMAL]
+
+ # Draw doughnut interior
+ arc = pi * 3 / 2.0
+ cr.set_source_rgba(fg_color.red, fg_color.green, fg_color.blue, 0.5)
+ cr.arc(center_x, center_y, hole_radius, -arc, arc)
+ cr.arc_negative(center_x, center_y, outer_radius, arc, -arc)
+ cr.close_path()
+ cr.fill()
+
+ # Draw doughnut border.
+ cr.set_source_rgb(fg_color.red, fg_color.green, fg_color.blue)
+ cr.set_line_width(3)
+ cr.arc_negative(center_x, center_y, outer_radius, arc, -arc)
+ cr.stroke()
+ if hole_radius < 1.0:
+ # If the radius is too small, nothing will be drawn, so draw a small cross marker instead.
+ cr.set_line_width(2)
+ cr.move_to(center_x - 4, center_y)
+ cr.line_to(center_x + 4, center_y)
+ cr.move_to(center_x, center_y - 4)
+ cr.line_to(center_x, center_y + 4)
+ else:
+ cr.arc(center_x, center_y, hole_radius, -arc, arc)
+ cr.stroke()
+
+ def compute_new_radius(self, x):
+ """ This method is called during mouse dragging of the widget.
+ Compute the new radius based on the current x location of the mouse pointer. """
+ allocation = self.get_allocation()
+
+ # How much does a single pixel difference in x, change the radius?
+ # Note that: allocation.width - TOTAL_MARGIN = 100 radius units,
+ radius_per_pixel = 100.0 / (allocation.width - TOTAL_MARGIN)
+ new_radius = self.start_radius + (x - self.start_x) * radius_per_pixel
+
+ if self.target == TARGET_HOLE:
+ self.hole_radius = max(min(new_radius, 99.0), 0.0)
+ else:
+ self.doughnut_width = max(min(new_radius, 100.0), 1.0)
+
+ self.queue_draw()
+
+ def do_button_press_event(self, event):
+ self.button_pressed = True
+
+ # If we clicked on one of the doughnut borders, remember which
+ # border we clicked on, and setup variable to start dragging it.
+ target = self.get_target(event.x, event.y)
+ if target == TARGET_HOLE or target == TARGET_WIDTH:
+ self.target = target
+ self.start_x = event.x
+ self.start_radius = (
+ self.hole_radius if target == TARGET_HOLE else
+ self.doughnut_width
+ )
+
+ def do_button_release_event(self, event):
+ # If one the doughnut borders was being dragged, recompute the doughnut size.
+ if self.target != TARGET_NONE:
+ self.compute_new_radius(event.x)
+ # Clip the width, if it is too large to fit.
+ if self.hole_radius + self.doughnut_width > 100:
+ self.doughnut_width = 100 - self.hole_radius
+ self.emit("values_changed", self.hole_radius, self.doughnut_width)
+
+ self.button_pressed = False
+ self.target = TARGET_NONE
+
+ def do_motion_notify_event(self, event):
+ if self.button_pressed:
+ # We are dragging one of the doughnut borders; recompute its size.
+ if self.target != TARGET_NONE:
+ self.compute_new_radius(event.x)
+ else:
+ # Set cursor according to whether we are over one of the
+ # doughnut borders.
+ target = self.get_target(event.x, event.y)
+ if target == TARGET_NONE:
+ self.set_default_cursor()
+ else:
+ self.set_cursor_h_resize()
+
+
+# Create signal that returns change parameters.
+gobject.type_register(DoughnutWidget)
+gobject.signal_new("values_changed", DoughnutWidget, gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE, (gobject.TYPE_INT, gobject.TYPE_INT))
+
+
+class SpyroWindow(gtk.Window):
+
+ # Define signal to catch escape key.
+ __gsignals__ = dict(
+ myescape=(gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION,
+ None, # return type
+ (str,)) # arguments
+ )
+
+ class MyScale():
+ """ Combintation of scale and spin that control the same adjuster. """
+ def __init__(self, scale, spin):
+ self.scale, self.spin = scale, spin
+
+ def set_sensitive(self, val):
+ self.scale.set_sensitive(val)
+ self.spin.set_sensitive(val)
+
+ def __init__(self, img, layer):
+
+ def add_horizontal_separator(vbox):
+ hsep = gtk.HSeparator()
+ vbox.add(hsep)
+ hsep.show()
+
+ def add_vertical_space(vbox, height):
+ hbox = gtk.HBox()
+ hbox.set_border_width(height/2)
+ vbox.add(hbox)
+ hbox.show()
+
+ def add_to_box(box, w):
+ box.add(w)
+ w.show()
+
+ def create_table(rows, columns, border_width):
+ table = gtk.Table(rows=rows, columns=columns, homogeneous=False)
+ table.set_border_width(border_width)
+ table.set_col_spacings(10)
+ table.set_row_spacings(10)
+ return table
+
+ def label_in_table(label_text, table, row, tooltip_text=None, col=0, col_add=1):
+ """ Create a label and set it in first col of table. """
+ label = gtk.Label(label_text)
+ label.set_alignment(xalign=0.0, yalign=1.0)
+ if tooltip_text:
+ label.set_tooltip_text(tooltip_text)
+ table.attach(label, col, col + col_add, row, row + 1, xoptions=gtk.FILL, yoptions=0)
+ label.show()
+
+ def spin_in_table(adj, table, row, callback, digits=0, col=0):
+ spin = gtk.SpinButton(adj, climb_rate=0.5, digits=digits)
+ spin.set_numeric(True)
+ spin.set_snap_to_ticks(True)
+ spin.set_max_length(5)
+ spin.set_width_chars(5)
+ table.attach(spin, col, col + 1, row, row + 1, xoptions=0, yoptions=0)
+ spin.show()
+ adj.connect("value_changed", callback)
+ return spin
+
+ def hscale_in_table(adj, table, row, callback, digits=0, col=1, cols=1):
+ """ Create an hscale and a spinner using the same Adjustment, and set it in table. """
+ scale = gtk.HScale(adj)
+ scale.set_size_request(150, -1)
+ scale.set_digits(digits)
+ scale.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ table.attach(scale, col, col + cols, row, row + 1, xoptions=gtk.EXPAND|gtk.FILL, yoptions=0)
+ scale.show()
+
+ spin = gtk.SpinButton(adj, climb_rate=0.5, digits=digits)
+ spin.set_numeric(True)
+ spin.set_snap_to_ticks(True)
+ spin.set_max_length(5)
+ spin.set_width_chars(5)
+ table.attach(spin, col + cols , col + cols + 1, row, row + 1, xoptions=0, yoptions=0)
+ spin.show()
+
+ adj.connect("value_changed", callback)
+
+ return self.MyScale(scale, spin)
+
+ def rotation_in_table(val, table, row, callback):
+ adj = gtk.Adjustment(val, -180.0, 180.0, 1.0)
+ myscale = hscale_in_table(adj, table, row, callback, digits=1)
+ myscale.scale.add_mark(0.0, gtk.POS_BOTTOM, None)
+ myscale.spin.set_max_length(6)
+ myscale.spin.set_width_chars(6)
+ return adj, myscale
+
+ def set_combo_in_table(txt_list, table, row, callback):
+ combo = gtk.combo_box_new_text()
+ for txt in txt_list:
+ combo.append_text(txt)
+ table.attach(combo, 1, 2, row, row + 1, xoptions=gtk.FILL, yoptions=0)
+ combo.show()
+ combo.connect("changed", callback)
+ return combo
+
+ # Return table which is at the top of the dialog, and has several major input widgets.
+ def top_table():
+
+ # Add table for displaying attributes, each having a label and an input widget.
+ table = create_table(2, 3, 10)
+
+ # Curve type
+ row = 0
+ label_in_table(_("Curve Type"), table, row,
+ _("An Epitrochoid pattern is when the moving gear is on the outside of the fixed gear."))
+ self.curve_type_combo = set_combo_in_table([ct.name for ct in curve_types], table, row,
+ self.curve_type_changed)
+
+ row += 1
+ label_in_table(_("Tool"), table, row,
+ _("The tool with which to draw the pattern. "
+ "The Preview tool just draws quickly."))
+ self.tool_combo = set_combo_in_table([tool.name for tool in tools], table, row,
+ self.tool_combo_changed)
+
+ self.long_gradient_checkbox = gtk.CheckButton(_("Long Gradient"))
+ self.long_gradient_checkbox.set_tooltip_text(
+ _("When unchecked, the current tool settings will be used. "
+ "When checked, will use a long gradient to match the length of the pattern, "
+ "based on current gradient and repeat mode from the gradient tool settings.")
+ )
+ self.long_gradient_checkbox.set_border_width(0)
+ table.attach(self.long_gradient_checkbox, 2, 3, row, row + 1, xoptions=0, yoptions=0)
+ self.long_gradient_checkbox.show()
+ self.long_gradient_checkbox.connect("toggled", self.long_gradient_changed)
+
+ return table
+
+ def pattern_notation_frame():
+
+ vbox = gtk.VBox(spacing=0, homogeneous=False)
+
+ add_vertical_space(vbox, 14)
+
+ hbox = gtk.HBox(spacing=5)
+ hbox.set_border_width(5)
+
+ label = gtk.Label(_("Specify pattern using one of the following tabs:"))
+ label.set_tooltip_text(_(
+ "The pattern is specified only by the active tab. Toy Kit is similar to Gears, "
+ "but it uses gears and hole numbers which are found in toy kits. "
+ "If you follow the instructions from the toy kit manuals, results should be similar."))
+ hbox.pack_start(label)
+ label.show()
+
+ alignment = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0)
+ alignment.add(hbox)
+ hbox.show()
+ vbox.add(alignment)
+ alignment.show()
+
+ self.pattern_notebook = gtk.Notebook()
+ self.pattern_notebook.set_border_width(0)
+ self.pattern_notebook.connect('switch-page', self.pattern_notation_tab_changed)
+
+ # "Gear" pattern notation.
+
+ # Add table for displaying attributes, each having a label and an input widget.
+ gear_table = create_table(3, 3, 5)
+
+ # Teeth
+ row = 0
+ fixed_gear_tooltip = _(
+ "Number of teeth of fixed gear. The size of the fixed gear is "
+ "proportional to the number of teeth."
+ )
+ label_in_table(_("Fixed Gear Teeth"), gear_table, row, fixed_gear_tooltip)
+ self.outer_teeth_adj = gtk.Adjustment(self.p.outer_teeth, 10, 180, 1)
+ hscale_in_table(self.outer_teeth_adj, gear_table, row, self.outer_teeth_changed)
+
+ row += 1
+ moving_gear_tooltip = _(
+ "Number of teeth of moving gear. The size of the moving gear is "
+ "proportional to the number of teeth."
+ )
+ label_in_table(_("Moving Gear Teeth"), gear_table, row, moving_gear_tooltip)
+ self.inner_teeth_adj = gtk.Adjustment(self.p.inner_teeth, 2, 100, 1)
+ hscale_in_table(self.inner_teeth_adj, gear_table, row, self.inner_teeth_changed)
+
+ row += 1
+ label_in_table(_("Hole percent"), gear_table, row,
+ _("How far is the hole from the center of the moving gear. "
+ "100% means that the hole is at the gear's edge."))
+ self.hole_percent_adj = gtk.Adjustment(self.p.hole_percent, 2.5, 100.0, 0.5)
+ self.hole_percent_myscale = hscale_in_table(self.hole_percent_adj, gear_table,
+ row, self.hole_percent_changed, digits=1)
+
+ # "Kit" pattern notation.
+
+ kit_table = create_table(3, 3, 5)
+
+ row = 0
+ label_in_table(_("Fixed Gear Teeth"), kit_table, row, fixed_gear_tooltip)
+ self.kit_outer_teeth_combo = set_combo_in_table([str(t) for t in ring_teeth], kit_table, row,
+ self.kit_outer_teeth_combo_changed)
+
+ row += 1
+ label_in_table(_("Moving Gear Teeth"), kit_table, row, moving_gear_tooltip)
+ self.kit_inner_teeth_combo = set_combo_in_table([str(t) for t in wheel_teeth], kit_table, row,
+ self.kit_inner_teeth_combo_changed)
+
+ row += 1
+ label_in_table(_("Hole Number"), kit_table, row,
+ _("Hole #1 is at the edge of the gear. "
+ "The maximum hole number is near the center. "
+ "The maximum hole number is different for each gear."))
+ self.kit_hole_adj = gtk.Adjustment(self.p.hole_number, 1, self.p.kit_max_hole_number(), 1)
+ self.kit_hole_myscale = hscale_in_table(self.kit_hole_adj, kit_table, row, self.kit_hole_changed)
+
+ # "Visual" pattern notation.
+
+ visual_table = create_table(3, 5, 5)
+
+ row = 0
+ label_in_table(_("Flower Petals"), visual_table, row, _("The number of petals in the pattern."))
+ self.petals_adj = gtk.Adjustment(self.p.petals, 2, 100, 1)
+ hscale_in_table(self.petals_adj, visual_table, row, self.petals_changed, cols=3)
+
+ row += 1
+ label_in_table(_("Petal Skip"), visual_table, row,
+ _("The number of petals to advance for drawing the next petal."))
+ self.petal_skip_adj = gtk.Adjustment(self.p.petal_skip, 1, 50, 1)
+ hscale_in_table(self.petal_skip_adj, visual_table, row, self.petal_skip_changed, cols=3)
+
+ row += 1
+ label_in_table(_("Hole Radius(%)"), visual_table, row,
+ _("The radius of the hole in the center of the pattern "
+ "where nothing will be drawn. Given as a percentage of the "
+ "size of the pattern. A value of 0 will produce no hole. "
+ "A Value of 99 will produce a thin line on the edge."))
+ self.doughnut_hole_adj = gtk.Adjustment(self.p.doughnut_hole, 0.0, 99.0, 0.1)
+ self.doughnut_hole_myscale = spin_in_table(self.doughnut_hole_adj,
+ visual_table, row, self.doughnut_hole_changed, 1, 1)
+
+ self.doughnut = DoughnutWidget()
+ visual_table.attach(self.doughnut, 2, 3, row, row+1, xoptions=gtk.EXPAND|gtk.FILL, yoptions=0)
+ self.doughnut.connect('values_changed', self.doughnut_changed)
+ self.doughnut.show()
+
+ label_in_table(_("Width(%)"), visual_table, row,
+ _("The width of the pattern as a percentage of the "
+ "size of the pattern. A Value of 1 will just draw a thin pattern. "
+ "A Value of 100 will fill the entire fixed gear."), 3)
+ self.doughnut_width_adj = gtk.Adjustment(self.p.doughnut_width, 1.0, 100.0, 0.1)
+ self.doughnut_width_myscale = spin_in_table(self.doughnut_width_adj,
+ visual_table, row, self.doughnut_width_changed, 1, 4)
+
+ # Add tables as children of the pattern notebook
+
+ pattern_notation_page[VISUAL_NOTATION] = self.pattern_notebook.append_page(visual_table)
+ self.pattern_notebook.set_tab_label_text(visual_table, _("Visual"))
+ self.pattern_notebook.set_tab_label_packing(visual_table, 0, 0, gtk.PACK_END)
+ visual_table.show()
+
+ pattern_notation_page[TOY_KIT_NOTATION] = self.pattern_notebook.append_page(kit_table)
+ self.pattern_notebook.set_tab_label_text(kit_table, _("Toy Kit"))
+ self.pattern_notebook.set_tab_label_packing(kit_table, 0, 0, gtk.PACK_END)
+ kit_table.show()
+
+ pattern_notation_page[GEAR_NOTATION] = self.pattern_notebook.append_page(gear_table)
+ self.pattern_notebook.set_tab_label_text(gear_table, _("Gears"))
+ self.pattern_notebook.set_tab_label_packing(gear_table, 0, 0, gtk.PACK_END)
+ gear_table.show()
+
+ add_to_box(vbox, self.pattern_notebook)
+
+ add_vertical_space(vbox, 14)
+
+ hbox = gtk.HBox(spacing=5)
+ pattern_table = create_table(1, 3, 5)
+
+ row = 0
+ label_in_table(_("Rotation"), pattern_table, row,
+ _("Rotation of the pattern, in degrees. "
+ "The starting position of the moving gear in the fixed gear."))
+ self.pattern_rotation_adj, myscale = rotation_in_table(
+ self.p.pattern_rotation, pattern_table, row, self.pattern_rotation_changed
+ )
+
+ hbox.pack_end(pattern_table, expand=True, fill=True, padding=0)
+ pattern_table.show()
+
+ vbox.add(hbox)
+ hbox.show()
+
+ return vbox
+
+ def fixed_gear_page():
+
+ vbox = gtk.VBox(spacing=0, homogeneous=False)
+
+ add_vertical_space(vbox, 14)
+
+ table = create_table(4, 2, 10)
+
+ row = 0
+ label_in_table(_("Shape"), table, row,
+ _("The shape of the fixed gear to be used inside current selection. "
+ "Rack is a long round-edged shape provided in the toy kits. "
+ "Frame hugs the boundaries of the rectangular selection, "
+ "use hole=100 in Gear notation to touch boundary. "
+ "Selection will hug boundaries of current selection - try something non-rectangular."))
+ self.shape_combo = set_combo_in_table([shape.name for shape in shapes], table, row,
+ self.shape_combo_changed)
+
+ row += 1
+ label_in_table(_("Sides"), table, row, _("Number of sides of the shape."))
+ self.sides_adj = gtk.Adjustment(self.p.sides, 3, 16, 1)
+ self.sides_myscale = hscale_in_table(self.sides_adj, table, row, self.sides_changed)
+
+ row += 1
+ label_in_table(_("Morph"), table, row, _("Morph fixed gear shape. Only affects some of the shapes."))
+ self.morph_adj = gtk.Adjustment(self.p.morph, 0.0, 1.0, 0.01)
+ self.morph_myscale = hscale_in_table(self.morph_adj, table, row, self.morph_changed, digits=2)
+
+ row += 1
+ label_in_table(_("Rotation"), table, row, _("Rotation of the fixed gear, in degrees"))
+ self.shape_rotation_adj, self.shape_rotation_myscale = rotation_in_table(
+ self.p.shape_rotation, table, row, self.shape_rotation_changed
+ )
+
+ add_to_box(vbox, table)
+ return vbox
+
+ def size_page():
+
+ vbox = gtk.VBox(spacing=0, homogeneous=False)
+ add_vertical_space(vbox, 14)
+ table = create_table(2, 2, 10)
+
+ row = 0
+ label_in_table(_("Margin (px)"), table, row, _("Margin from edge of selection."))
+ self.margin_adj = gtk.Adjustment(self.p.margin_pixels, 0, max(img.height, img.width), 1)
+ hscale_in_table(self.margin_adj, table, row, self.margin_changed)
+
+ row += 1
+ self.equal_w_h_checkbox = gtk.CheckButton(_("Make width and height equal"))
+ self.equal_w_h_checkbox.set_tooltip_text(
+ _("When unchecked, the pattern will fill the current image or selection. "
+ "When checked, the pattern will have same width and height, and will be centered.")
+ )
+ self.equal_w_h_checkbox.set_border_width(15)
+ table.attach(self.equal_w_h_checkbox, 0, 2, row, row + 1)
+ self.equal_w_h_checkbox.show()
+ self.equal_w_h_checkbox.connect("toggled", self.equal_w_h_checkbox_changed)
+
+
+ add_to_box(vbox, table)
+ return vbox
+
+ def add_button_to_box(box, text, callback, tooltip_text=None):
+ btn = gtk.Button(text)
+ if tooltip_text:
+ btn.set_tooltip_text(tooltip_text)
+ box.add(btn)
+ btn.show()
+ btn.connect("clicked", callback)
+ return btn
+
+ def dialog_button_box():
+ hbox = gtk.HBox(homogeneous=True, spacing=20)
+
+ add_button_to_box(hbox, _("Re_draw"), self.redraw,
+ _("If you change the settings of a tool, change color, or change the selection, "
+ "press this to preview how the pattern looks."))
+ add_button_to_box(hbox, _("_Reset"), self.reset_params)
+ add_button_to_box(hbox, _("_Cancel"), self.cancel_window)
+ self.ok_btn = add_button_to_box(hbox, _("_OK"), self.ok_window)
+
+ self.save_option_combo = gtk.combo_box_new_text()
+ for txt in save_options:
+ self.save_option_combo.append_text(txt)
+ self.save_option_combo.set_tooltip_text(
+ _("Choose whether to save as new layer, redraw on last active layer, or save to path")
+ )
+ hbox.add(self.save_option_combo)
+ self.save_option_combo.show()
+ self.save_option_combo.connect("changed", self.save_option_changed)
+
+ return hbox
+
+ def create_ui():
+
+ # Create the dialog
+ gtk.Window.__init__(self)
+ self.set_title(_("Spyrogimp"))
+ self.set_default_size(350, -1)
+ self.set_border_width(10)
+ # self.set_keep_above(True) # keep the window on top
+
+ # Vertical box in which we will add all the UI elements.
+ vbox = gtk.VBox(spacing=10, homogeneous=False)
+ self.add(vbox)
+
+ box = gimpui.HintBox(_("Draw spyrographs using current tool settings and selection."))
+ vbox.pack_start(box, expand=False)
+ box.show()
+
+ add_horizontal_separator(vbox)
+
+ add_to_box(vbox, top_table())
+
+ self.main_notebook = gtk.Notebook()
+ self.main_notebook.set_show_tabs(True)
+ self.main_notebook.set_border_width(5)
+
+ pattern_frame = pattern_notation_frame()
+ self.main_notebook.append_page(pattern_frame, gtk.Label(_("Curve Pattern")))
+ pattern_frame.show()
+ fixed_g_page = fixed_gear_page()
+ self.main_notebook.append_page(fixed_g_page, gtk.Label(_("Fixed Gear")))
+ fixed_g_page.show()
+ size_p = size_page()
+ self.main_notebook.append_page(size_p, gtk.Label(_("Size")))
+ size_p.show()
+
+ vbox.add(self.main_notebook)
+ self.main_notebook.show()
+
+ add_horizontal_separator(vbox)
+
+ self.progress_bar = gtk.ProgressBar() # gimpui.ProgressBar() - causes gimppdbprogress error message.
+ self.progress_bar.set_size_request(-1, 30)
+ vbox.add(self.progress_bar)
+ self.progress_bar.show()
+
+ add_to_box(vbox, dialog_button_box())
+
+ vbox.show()
+ self.show()
+
+ self.enable_incremental_drawing = False
+
+ self.img = img
+ # Remember active layer, so we can restore it when the plugin is done.
+ self.active_layer = layer
+
+ self.p = unshelf_parameters() # Model
+
+ self.engine = DrawingEngine(img, self.p)
+
+ # Make a new GIMP layer to draw on
+ self.spyro_layer = gimp.Layer(img, layer_name, img.width, img.height,
+ layer.type_with_alpha, 100, NORMAL_MODE)
+ img.add_layer(self.spyro_layer, 0)
+
+ self.drawing_layer = self.spyro_layer
+
+ gimpui.gimp_ui_init()
+ create_ui()
+ self.update_view()
+
+ # Obey the window manager quit signal
+ self.connect("destroy", self.cancel_window)
+ # Connect Escape key to quit the window as well.
+ self.connect('myescape', self.cancel_window)
+
+ # Setup for Handling incremental/interactive drawing of pattern
+ self.idle_task = None
+ self.enable_incremental_drawing = True
+
+ # Draw pattern of the current settings.
+ self.start_new_incremental_drawing()
+
+ # Callbacks for closing the plugin
+
+ def clear_idle_task(self):
+ if self.idle_task:
+ gobject.source_remove(self.idle_task)
+ # Close the undo group in the likely case the idle task left it open.
+ self.img.undo_group_end()
+ self.idle_task = None
+
+ def ok_window(self, widget):
+ """ Called when clicking on the 'close' button. """
+
+ self.ok_btn.set_sensitive(False)
+
+ shelf_parameters(self.p)
+
+ if self.p.save_option == SAVE_AS_NEW_LAYER:
+ if self.spyro_layer in self.img.layers:
+ self.img.active_layer = self.spyro_layer
+
+ # If we are in the middle of incremental draw, we want to complete it, and only then to exit.
+ # However, in order to complete it, we need to create another idle task.
+ if self.idle_task:
+ def quit_dialog_on_completion():
+ while self.idle_task:
+ yield True
+
+ gtk.main_quit() # This will quit the dialog.
+ yield False
+
+ task = quit_dialog_on_completion()
+ gobject.idle_add(task.next)
+ else:
+ gtk.main_quit()
+ else:
+ # If there is an incremental drawing taking place, lets stop it.
+ self.clear_idle_task()
+
+ if self.spyro_layer in self.img.layers:
+ self.img.remove_layer(self.spyro_layer)
+ self.img.active_layer = self.active_layer
+
+ self.drawing_layer = self.active_layer
+
+ def draw_full(tool):
+ self.progress_start()
+ yield True
+
+ self.engine.reset_incremental()
+
+ self.img.undo_group_start()
+
+ while self.engine.has_more_strokes():
+ yield True
+ self.draw_next_chunk(tool=tool)
+
+ self.img.undo_group_end()
+
+ pdb.gimp_displays_flush()
+
+ gtk.main_quit()
+ yield False
+
+ tool = SaveToPathTool(self.img) if self.p.save_option == SAVE_AS_PATH else None
+ task = draw_full(tool)
+ gobject.idle_add(task.next)
+
+ def cancel_window(self, widget, what=None):
+ self.clear_idle_task()
+
+ # We want to delete the temporary layer, but as a precaution, lets ask first,
+ # maybe it was already deleted by the user.
+ if self.spyro_layer in self.img.layers:
+ self.img.remove_layer(self.spyro_layer)
+ pdb.gimp_displays_flush()
+ gtk.main_quit()
+
+ def update_view(self):
+ """ Update the UI to reflect the values in the Pattern Parameters. """
+ self.curve_type_combo.set_active(self.p.curve_type)
+ self.curve_type_side_effects()
+
+ self.pattern_notebook.set_current_page(pattern_notation_page[self.p.pattern_notation])
+
+ self.outer_teeth_adj.set_value(self.p.outer_teeth)
+ self.inner_teeth_adj.set_value(self.p.inner_teeth)
+ self.hole_percent_adj.set_value(self.p.hole_percent)
+ self.pattern_rotation_adj.set_value(self.p.pattern_rotation)
+
+ self.kit_outer_teeth_combo.set_active(self.p.kit_fixed_gear_index)
+ self.kit_inner_teeth_combo.set_active(self.p.kit_moving_gear_index)
+ self.kit_hole_adj.set_value(self.p.hole_number)
+ self.kit_inner_teeth_combo_side_effects()
+
+ self.petals_adj.set_value(self.p.petals)
+ self.petal_skip_adj.set_value(self.p.petal_skip)
+ self.doughnut_hole_adj.set_value(self.p.doughnut_hole)
+ self.doughnut.set_hole_radius(self.p.doughnut_hole)
+ self.doughnut_width_adj.set_value(self.p.doughnut_width)
+ self.doughnut.set_width(self.p.doughnut_width)
+ self.petals_changed_side_effects()
+
+ self.shape_combo.set_active(self.p.shape_index)
+ self.shape_combo_side_effects()
+ self.sides_adj.set_value(self.p.sides)
+ self.morph_adj.set_value(self.p.morph)
+ self.equal_w_h_checkbox.set_active(self.p.equal_w_h)
+ self.shape_rotation_adj.set_value(self.p.shape_rotation)
+
+ self.margin_adj.set_value(self.p.margin_pixels)
+ self.tool_combo.set_active(self.p.tool_index)
+ self.long_gradient_checkbox.set_active(self.p.long_gradient)
+ self.save_option_combo.set_active(self.p.save_option)
+
+ def reset_params(self, widget):
+ self.engine.p = self.p = PatternParameters()
+ self.update_view()
+
+ # Callbacks to handle changes in dialog parameters.
+
+ def curve_type_side_effects(self):
+ if curve_types[self.p.curve_type].supports_shapes():
+ self.shape_combo.set_sensitive(True)
+
+ self.sides_myscale.set_sensitive(shapes[self.p.shape_index].has_sides())
+ self.morph_myscale.set_sensitive(shapes[self.p.shape_index].can_morph())
+ self.shape_rotation_myscale.set_sensitive(shapes[self.p.shape_index].can_rotate())
+
+ self.hole_percent_myscale.set_sensitive(True)
+ self.kit_hole_myscale.set_sensitive(True)
+
+ self.doughnut_hole_myscale.set_sensitive(True)
+ self.doughnut_width_myscale.set_sensitive(True)
+ else:
+ # Lissajous curves do not have shapes, or holes for moving gear
+ self.shape_combo.set_sensitive(False)
+
+ self.sides_myscale.set_sensitive(False)
+ self.morph_myscale.set_sensitive(False)
+ self.shape_rotation_myscale.set_sensitive(False)
+
+ self.hole_percent_myscale.set_sensitive(False)
+ self.kit_hole_myscale.set_sensitive(False)
+
+ self.doughnut_hole_myscale.set_sensitive(False)
+ self.doughnut_width_myscale.set_sensitive(False)
+
+ def curve_type_changed(self, val):
+ self.p.curve_type = val.get_active()
+ self.curve_type_side_effects()
+ self.redraw()
+
+ def pattern_notation_tab_changed(self, notebook, page, page_num, user_param1=None):
+ if self.enable_incremental_drawing:
+ for notation in pattern_notation_page:
+ if pattern_notation_page[notation] == page_num:
+ self.p.pattern_notation = notation
+
+ self.redraw()
+
+ # Callbacks: pattern changes using the Toy Kit notation.
+
+ def kit_outer_teeth_combo_changed(self, val):
+ self.p.kit_fixed_gear_index = val.get_active()
+ self.redraw()
+
+ def kit_inner_teeth_combo_side_effects(self):
+ # Change the max hole number according to the newly activated wheel.
+ # We might also need to update the hole value, if it is larger than the new max.
+ max_hole_number = self.p.kit_max_hole_number()
+ if self.p.hole_number > max_hole_number:
+ self.p.hole_number = max_hole_number
+ self.kit_hole_adj.set_value(max_hole_number)
+ self.kit_hole_adj.set_upper(max_hole_number)
+
+ def kit_inner_teeth_combo_changed(self, val):
+ self.p.kit_moving_gear_index = val.get_active()
+ self.kit_inner_teeth_combo_side_effects()
+ self.redraw()
+
+ def kit_hole_changed(self, val):
+ self.p.hole_number = val.value
+ self.redraw()
+
+ # Callbacks: pattern changes using the Gears notation.
+
+ def outer_teeth_changed(self, val):
+ self.p.outer_teeth = val.value
+ self.redraw()
+
+ def inner_teeth_changed(self, val):
+ self.p.inner_teeth = val.value
+ self.redraw()
+
+ def hole_percent_changed(self, val):
+ self.p.hole_percent = val.value
+ self.redraw()
+
+ def pattern_rotation_changed(self, val):
+ self.p.pattern_rotation = val.value
+ self.redraw()
+
+ # Callbacks: pattern changes using the Visual notation.
+
+ def petals_changed_side_effects(self):
+ max_petal_skip = int(self.p.petals/2)
+ if self.p.petal_skip > max_petal_skip:
+ self.p.petal_skip = max_petal_skip
+ self.petal_skip_adj.set_value(max_petal_skip)
+ self.petal_skip_adj.set_upper(max_petal_skip)
+
+ def petals_changed(self, val):
+ self.p.petals = int(val.value)
+ self.petals_changed_side_effects()
+ self.redraw()
+
+ def petal_skip_changed(self, val):
+ self.p.petal_skip = int(val.value)
+ self.redraw()
+
+ def doughnut_hole_changed(self, val):
+ self.p.doughnut_hole = val.value
+ self.doughnut.set_hole_radius(val.value)
+ self.redraw()
+
+ def doughnut_width_changed(self, val):
+ self.p.doughnut_width = val.value
+ self.doughnut.set_width(val.value)
+ self.redraw()
+
+ def doughnut_changed(self, widget, hole, width):
+ self.doughnut_hole_adj.set_value(hole)
+ self.doughnut_width_adj.set_value(width)
+ # We don't need to redraw, because the callbacks of the doughnut hole and
+ # width spinners will be triggered by the above lines.
+
+ # Callbacks: Fixed gear
+
+ def shape_combo_side_effects(self):
+ self.sides_myscale.set_sensitive(shapes[self.p.shape_index].has_sides())
+ self.morph_myscale.set_sensitive(shapes[self.p.shape_index].can_morph())
+ self.shape_rotation_myscale.set_sensitive(shapes[self.p.shape_index].can_rotate())
+ self.equal_w_h_checkbox.set_sensitive(shapes[self.p.shape_index].can_equal_w_h())
+
+ def shape_combo_changed(self, val):
+ self.p.shape_index = val.get_active()
+ self.shape_combo_side_effects()
+ self.redraw()
+
+ def sides_changed(self, val):
+ self.p.sides = val.value
+ self.redraw()
+
+ def morph_changed(self, val):
+ self.p.morph = val.value
+ self.redraw()
+
+ def equal_w_h_checkbox_changed(self, val):
+ self.p.equal_w_h = val.get_active()
+ self.redraw()
+
+ def shape_rotation_changed(self, val):
+ self.p.shape_rotation = val.value
+ self.redraw()
+
+ def margin_changed(self, val) :
+ self.p.margin_pixels = val.value
+ self.redraw()
+
+ # Style callbacks
+
+ def tool_changed_side_effects(self):
+ self.long_gradient_checkbox.set_sensitive(tools[self.p.tool_index].can_color)
+
+ def tool_combo_changed(self, val):
+ self.p.tool_index = val.get_active()
+ self.tool_changed_side_effects()
+ self.redraw()
+
+ def long_gradient_changed(self, val):
+ self.p.long_gradient = val.get_active()
+ self.redraw()
+
+ def save_option_changed(self, val):
+ self.p.save_option = self.save_option_combo.get_active()
+
+ # Progress bar of plugin window.
+
+ def progress_start(self):
+ self.progress_bar.set_text(_("Rendering Pattern"))
+ self.progress_bar.set_fraction(0.0)
+ pdb.gimp_displays_flush()
+
+ def progress_end(self):
+ self.progress_bar.set_text("")
+ self.progress_bar.set_fraction(0.0)
+
+ def progress_update(self):
+ self.progress_bar.set_fraction(self.engine.fraction_done())
+
+ def progress_unknown(self):
+ self.progress_bar.set_text(_("Please wait : Rendering Pattern"))
+ self.progress_bar.pulse()
+ pdb.gimp_displays_flush()
+
+ # Incremental drawing.
+
+ def draw_next_chunk(self, tool=None):
+ """ Incremental drawing """
+
+ t = time.time()
+
+ chunk_size = self.engine.draw_next_chunk(self.drawing_layer, tool=tool)
+
+ draw_time = time.time() - t
+ self.engine.report_time(draw_time)
+ print("Chunk size " + str(chunk_size) + " time " + str(draw_time))
+
+ if self.engine.has_more_strokes():
+ self.progress_update()
+ else:
+ self.progress_end()
+
+ pdb.gimp_displays_flush()
+
+ def start_new_incremental_drawing(self):
+ """
+ Compute strokes for the current pattern, and store then in the IncrementalDraw object,
+ so they can be drawn in pieces without blocking the user.
+ Finally, draw the first chunk of strokes.
+ """
+
+ def incremental_drawing():
+ self.progress_start()
+ yield True
+ self.engine.reset_incremental()
+
+ self.img.undo_group_start()
+ while self.engine.has_more_strokes():
+ yield True
+ self.draw_next_chunk()
+ self.img.undo_group_end()
+
+ self.idle_task = None
+ yield False
+
+ # Start new idle task to perform incremental drawing in the background.
+ self.clear_idle_task()
+ task = incremental_drawing()
+ self.idle_task = gobject.idle_add(task.next)
+
+ def clear(self):
+ """ Clear current drawing. """
+ # pdb.gimp_edit_clear(self.spyro_layer)
+ self.spyro_layer.fill(FILL_TRANSPARENT)
+
+ def redraw(self, data=None):
+ if self.enable_incremental_drawing:
+ self.clear()
+ self.start_new_incremental_drawing()
+
+
+# Bind escape to the new signal we created, named "myescape".
+gobject.type_register(SpyroWindow)
+gtk.binding_entry_add_signal(SpyroWindow, gtk.keysyms.Escape, 0, 'myescape', str, 'escape')
+
+
+class SpyrogimpPlusPlugin(gimpplugin.plugin):
+
+ # Implementation of plugin.
+ def plug_in_spyrogimp(self, run_mode, image, layer,
+ curve_type=0, shape=0, sides=3, morph=0.0,
+ fixed_teeth=96, moving_teeth=36, hole_percent=100.0,
+ margin=0, equal_w_h=0,
+ pattern_rotation=0.0, shape_rotation=0.0,
+ tool=1, long_gradient=False):
+ if run_mode == RUN_NONINTERACTIVE:
+ pp = PatternParameters()
+ pp.curve_type = curve_type
+ pp.shape_index = shape
+ pp.sides = sides
+ pp.morph = morph
+ pp.outer_teeth = fixed_teeth
+ pp.inner_teeth = moving_teeth
+ pp.hole_percent = hole_percent
+ pp.margin_pixels = margin
+ pp.equal_w_h = equal_w_h
+ pp.pattern_rotation = pattern_rotation
+ pp.shape_rotation = shape_rotation
+ pp.tool_index = tool
+ pp.long_gradient = long_gradient
+
+ engine = DrawingEngine(image, pp)
+ engine.draw_full(layer)
+
+ elif run_mode == RUN_INTERACTIVE:
+ window = SpyroWindow(image, layer)
+ gtk.main()
+
+ elif run_mode == RUN_WITH_LAST_VALS:
+ pp = unshelf_parameters()
+ engine = DrawingEngine(image, pp)
+ engine.draw_full(layer)
+
+ def query(self):
+ plugin_name = "plug_in_spyrogimp"
+ label = N_("Spyrogimp...")
+ menu = "<Image>/Filters/Render/"
+
+ params = [
+ # (type, name, description
+ (PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }"),
+ (PDB_IMAGE, "image", "Input image"),
+ (PDB_DRAWABLE, "drawable", "Input drawable"),
+ (PDB_INT32, "curve_type",
+ "The curve type { Spyrograph (0), Epitrochoid (1), Sine (2), Lissajous(3) }"),
+ (PDB_INT32, "shape", "Shape of fixed gear"),
+ (PDB_INT32, "sides", "Number of sides of fixed gear (3 or greater). Only used by some shapes."),
+ (PDB_FLOAT, "morph", "Morph shape of fixed gear, between 0 and 1. Only used by some shapes."),
+ (PDB_INT32, "fixed_teeth", "Number of teeth for fixed gear"),
+ (PDB_INT32, "moving_teeth", "Number of teeth for moving gear"),
+ (PDB_FLOAT, "hole_percent", "Location of hole in moving gear in percent, where 100 means that "
+ "the hole is at the edge of the gear, and 0 means the hole is at the center"),
+ (PDB_INT32, "margin", "Margin from selection, in pixels"),
+ (PDB_INT32, "equal_w_h", "Make height and width equal (TRUE or FALSE)"),
+ (PDB_FLOAT, "pattern_rotation", "Pattern rotation, in degrees"),
+ (PDB_FLOAT, "shape_rotation", "Shape rotation of fixed gear, in degrees"),
+ (PDB_INT32, "tool", "Tool to use for drawing the pattern."),
+ (PDB_INT32, "long_gradient",
+ "Whether to apply a long gradient to match the length of the pattern (TRUE or FALSE). "
+ "Only applicable to some of the tools.")
+ ]
+
+ gimp.domain_register("gimp20-python", gimp.locale_directory)
+
+ gimp.install_procedure(
+ plugin_name,
+ N_("Draw spyrographs using current tool settings and selection."),
+ "Uses current tool settings to draw Spyrograph patterns. "
+ "The size and location of the pattern is based on the current selection.",
+ "Elad Shahar",
+ "Elad Shahar",
+ "2018",
+ label,
+ "*",
+ PLUGIN,
+ params,
+ []
+ )
+
+ gimp.menu_register(plugin_name, menu)
+
+
+if __name__ == '__main__':
+ SpyrogimpPlusPlugin().start()
diff --git a/plug-ins/pygimp/plug-ins/whirlpinch.py b/plug-ins/pygimp/plug-ins/whirlpinch.py
new file mode 100755
index 0000000..6c50e89
--- /dev/null
+++ b/plug-ins/pygimp/plug-ins/whirlpinch.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python2
+
+# Gimp-Python - allows the writing of Gimp plugins in Python.
+# Copyright (C) 1997 James Henstridge <james@daa.com.au>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+# Algorithms stolen from the whirl and pinch plugin distributed with Gimp,
+# by Federico Mena Quintero and Scott Goehring
+#
+# This version does the same thing, except there is no preview, and it is
+# written in python and is slower.
+
+import math, struct
+from gimpfu import *
+
+class pixel_fetcher:
+ def __init__(self, drawable):
+ self.col = -1
+ self.row = -1
+ self.img_width = drawable.width
+ self.img_height = drawable.height
+ self.img_bpp = drawable.bpp
+ self.img_has_alpha = drawable.has_alpha
+ self.tile_width = gimp.tile_width()
+ self.tile_height = gimp.tile_height()
+ self.bg_colour = '\0\0\0\0'
+ self.bounds = drawable.mask_bounds
+ self.drawable = drawable
+ self.tile = None
+ def set_bg_colour(self, r, g, b, a):
+ self.bg_colour = struct.pack('BBB', r,g,b)
+ if self.img_has_alpha:
+ self.bg_colour = self.bg_colour + chr(a)
+ def get_pixel(self, x, y):
+ sel_x1, sel_y1, sel_x2, sel_y2 = self.bounds
+ if x < sel_x1 or x >= sel_x2 or y < sel_y1 or y >= sel_y2:
+ return self.bg_colour
+ col = x / self.tile_width
+ coloff = x % self.tile_width
+ row = y / self.tile_height
+ rowoff = y % self.tile_height
+
+ if col != self.col or row != self.row or self.tile == None:
+ self.tile = self.drawable.get_tile(False, row, col)
+ self.col = col
+ self.row = row
+ return self.tile[coloff, rowoff]
+
+class Dummy:
+ pass
+
+def whirl_pinch(image, drawable, whirl, pinch, radius):
+ self = Dummy()
+ self.width = drawable.width
+ self.height = drawable.height
+ self.bpp = drawable.bpp
+ self.has_alpha = drawable.has_alpha
+ self.bounds = drawable.mask_bounds
+ self.sel_x1, self.sel_y1, self.sel_x2, self.sel_y2 = \
+ drawable.mask_bounds
+ self.sel_w = self.sel_x2 - self.sel_x1
+ self.sel_h = self.sel_y2 - self.sel_y1
+ self.cen_x = (self.sel_x1 + self.sel_x2 - 1) / 2.0
+ self.cen_y = (self.sel_y1 + self.sel_y2 - 1) / 2.0
+ xhsiz = (self.sel_w - 1) / 2.0
+ yhsiz = (self.sel_h - 1) / 2.0
+
+ if xhsiz < yhsiz:
+ self.scale_x = yhsiz / xhsiz
+ self.scale_y = 1.0
+ elif xhsiz > yhsiz:
+ self.scale_x = 1.0
+ self.scale_y = xhsiz / yhsiz
+ else:
+ self.scale_x = 1.0
+ self.scale_y = 1.0
+
+ self.radius = max(xhsiz, yhsiz);
+
+ if not drawable.is_rgb and not drawable.is_grey:
+ return
+
+ gimp.tile_cache_ntiles(2 * (1 + self.width / gimp.tile_width()))
+
+ whirl = whirl * math.pi / 180
+ dest_rgn = drawable.get_pixel_rgn(self.sel_x1, self.sel_y1,
+ self.sel_w, self.sel_h, True, True)
+ pft = pixel_fetcher(drawable)
+ pfb = pixel_fetcher(drawable)
+
+ bg_colour = gimp.get_background()
+
+ pft.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0)
+ pfb.set_bg_colour(bg_colour[0], bg_colour[1], bg_colour[2], 0)
+
+ progress = 0
+ max_progress = self.sel_w * self.sel_h
+
+ gimp.progress_init("Whirling and pinching")
+
+ self.radius2 = self.radius * self.radius * radius
+ pixel = ['', '', '', '']
+ values = [0,0,0,0]
+
+ for row in range(self.sel_y1, (self.sel_y1+self.sel_y2)/2+1):
+ top_p = ''
+ bot_p = ''
+ for col in range(self.sel_x1, self.sel_x2):
+ q, cx, cy = calc_undistorted_coords(self, col,
+ row, whirl, pinch,
+ radius)
+ if q:
+ if cx >= 0: ix = int(cx)
+ else: ix = -(int(-cx) + 1)
+ if cy >= 0: iy = int(cy)
+ else: iy = -(int(-cy) + 1)
+ pixel[0] = pft.get_pixel(ix, iy)
+ pixel[1] = pft.get_pixel(ix+1, iy)
+ pixel[2] = pft.get_pixel(ix, iy+1)
+ pixel[3] = pft.get_pixel(ix+1, iy+1)
+ for i in range(self.bpp):
+ values[0] = ord(pixel[0][i])
+ values[1] = ord(pixel[1][i])
+ values[2] = ord(pixel[2][i])
+ values[3] = ord(pixel[3][i])
+ top_p = top_p + bilinear(cx,cy, values)
+ cx = self.cen_x + (self.cen_x - cx)
+ cy = self.cen_y + (self.cen_y - cy)
+ if cx >= 0: ix = int(cx)
+ else: ix = -(int(-cx) + 1)
+ if cy >= 0: iy = int(cy)
+ else: iy = -(int(-cy) + 1)
+ pixel[0] = pfb.get_pixel(ix, iy)
+ pixel[1] = pfb.get_pixel(ix+1, iy)
+ pixel[2] = pfb.get_pixel(ix, iy+1)
+ pixel[3] = pfb.get_pixel(ix+1, iy+1)
+ tmp = ''
+ for i in range(self.bpp):
+ values[0] = ord(pixel[0][i])
+ values[1] = ord(pixel[1][i])
+ values[2] = ord(pixel[2][i])
+ values[3] = ord(pixel[3][i])
+ tmp = tmp + bilinear(cx,cy, values)
+ bot_p = tmp + bot_p
+ else:
+ top_p = top_p + pft.get_pixel(col, row)
+ bot_p = pfb.get_pixel((self.sel_x2 - 1) -
+ (col - self.sel_x1), (self.sel_y2-1) -
+ (row - self.sel_y1)) + bot_p
+
+ dest_rgn[self.sel_x1:self.sel_x2, row] = top_p
+ dest_rgn[self.sel_x1:self.sel_x2, (self.sel_y2 - 1)
+ - (row - self.sel_y1)] = bot_p
+
+ progress = progress + self.sel_w * 2
+ gimp.progress_update(float(progress) / max_progress)
+
+ drawable.flush()
+ drawable.merge_shadow(True)
+ drawable.update(self.sel_x1,self.sel_y1,self.sel_w,self.sel_h)
+
+def calc_undistorted_coords(self, wx, wy, whirl, pinch, radius):
+ dx = (wx - self.cen_x) * self.scale_x
+ dy = (wy - self.cen_y) * self.scale_y
+ d = dx * dx + dy * dy
+ inside = d < self.radius2
+
+ if inside:
+ dist = math.sqrt(d / radius) / self.radius
+ if (d == 0.0):
+ factor = 1.0
+ else:
+ factor = math.pow(math.sin(math.pi / 2 * dist),
+ -pinch)
+ dx = dx * factor
+ dy = dy * factor
+ factor = 1 - dist
+ ang = whirl * factor * factor
+ sina = math.sin(ang)
+ cosa = math.cos(ang)
+ x = (cosa * dx - sina * dy) / self.scale_x + self.cen_x
+ y = (sina * dx + cosa * dy) / self.scale_y + self.cen_y
+ else:
+ x = wx
+ y = wy
+ return inside, float(x), float(y)
+
+def bilinear(x, y, values):
+ x = x % 1.0
+ y = y % 1.0
+ m0 = values[0] + x * (values[1] - values[0])
+ m1 = values[2] + x * (values[3] - values[2])
+ return chr(int(m0 + y * (m1 - m0)))
+
+
+register(
+ "python-fu-whirl-pinch",
+ "Distorts an image by whirling and pinching",
+ "Distorts an image by whirling and pinching",
+ "James Henstridge (translated from C plugin)",
+ "James Henstridge",
+ "1997-1999",
+ "_Whirl and Pinch...",
+ "RGB*, GRAY*",
+ [
+ (PF_IMAGE, "image", "Input image", None),
+ (PF_DRAWABLE, "drawable", "Input drawable", None),
+ (PF_SLIDER, "whirl", "Whirl angle", 90, (-360, 360, 1)),
+ (PF_FLOAT, "pinch", "Pinch amount", 0),
+ (PF_FLOAT, "radius", "radius", 1)
+ ],
+ [],
+ whirl_pinch, menu="<Image>/Filters/Distorts")
+
+main()
diff --git a/plug-ins/pygimp/py-compile b/plug-ins/pygimp/py-compile
new file mode 100755
index 0000000..3448c71
--- /dev/null
+++ b/plug-ins/pygimp/py-compile
@@ -0,0 +1,72 @@
+#!/bin/sh
+# called as "py-compile [--basedir DIR] PY_FILES ...
+
+if [ -z "$PYTHON" ]; then
+ if [[ -x "$(command -v python2)" ]]; then
+ PYTHON=python2
+ else
+ # Let's hope it's Python2
+ PYTHON=python
+ fi
+fi
+
+basedir=
+
+case "$1" in
+ --basedir)
+ basedir=$2
+ shift 2
+ ;;
+ --help)
+ echo "Usage: py-compile [--basedir DIR] PY_FILES ..."
+ echo "Byte compile some python scripts. This should be performed"
+ echo "after they have been moved to the final installation location"
+ exit 0
+ ;;
+ --version)
+ echo "py-compile version 0.0"
+ exit 0
+ ;;
+esac
+
+if [ $# = 0 ]; then
+ echo "No files given to $0" 1>&2
+ exit 1
+fi
+
+# if basedir was given, then it should be prepended to filenames before
+# byte compilation.
+if [ -z "$basedir" ]; then
+ trans="path = file"
+else
+ trans="path = os.path.join('$basedir', file)"
+fi
+
+$PYTHON -c "
+import sys, os, string, py_compile
+
+files = '''$*'''
+print 'Byte-compiling python modules...'
+for file in string.split(files):
+ $trans
+ if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'):
+ continue
+ print file,
+ sys.stdout.flush()
+ py_compile.compile(path)
+print" || exit $?
+
+# this will fail for python < 1.5, but that doesn't matter ...
+$PYTHON -O -c "
+import sys, os, string, py_compile
+
+files = '''$*'''
+print 'Byte-compiling python modules (optimised versions) ...'
+for file in string.split(files):
+ $trans
+ if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'):
+ continue
+ print file,
+ sys.stdout.flush()
+ py_compile.compile(path)
+print" 2>/dev/null || :
diff --git a/plug-ins/pygimp/pygimp-api.h b/plug-ins/pygimp/pygimp-api.h
new file mode 100644
index 0000000..2b83222
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-api.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYGIMP_API_H_
+#define _PYGIMP_API_H_
+
+#include <Python.h>
+
+#include <libgimp/gimp.h>
+
+typedef struct {
+ PyObject_HEAD
+ gint32 ID;
+} PyGimpImage, PyGimpItem;
+
+typedef struct {
+ PyObject_HEAD
+ gint32 ID;
+} PyGimpDisplay;
+
+typedef struct {
+ PyObject_HEAD
+ gint32 ID;
+ GimpDrawable *drawable;
+} PyGimpDrawable, PyGimpLayer, PyGimpGroupLayer, PyGimpChannel;
+
+typedef struct {
+ PyObject_HEAD
+ gint32 ID;
+} PyGimpVectors;
+
+struct _PyGimp_Functions {
+ PyTypeObject *Image_Type;
+ PyObject *(* image_new)(gint32 ID);
+
+ PyTypeObject *Display_Type;
+ PyObject *(* display_new)(gint32 ID);
+
+ PyTypeObject *Item_Type;
+ PyObject *(* item_new)(gint32 ID);
+
+ PyTypeObject *Drawable_Type;
+ PyObject *(* drawable_new)(GimpDrawable *drawable, gint32 ID);
+
+ PyTypeObject *Layer_Type;
+ PyObject *(* layer_new)(gint32 ID);
+
+ PyTypeObject *GroupLayer_Type;
+ PyObject *(* group_layer_new)(gint32 ID);
+
+ PyTypeObject *Channel_Type;
+ PyObject *(* channel_new)(gint32 ID);
+
+ PyTypeObject *Vectors_Type;
+ PyObject *(* vectors_new)(gint32 ID);
+
+ PyObject *pygimp_error;
+};
+
+#ifndef _INSIDE_PYGIMP_
+
+#if defined(NO_IMPORT) || defined(NO_IMPORT_PYGIMP)
+extern struct _PyGimp_Functions *_PyGimp_API;
+#else
+struct _PyGimp_Functions *_PyGimp_API;
+#endif
+
+#define PyGimpImage_Type (_PyGimp_API->Image_Type)
+#define pygimp_image_new (_PyGimp_API->image_new)
+#define PyGimpDisplay_Type (_PyGimp_API->Display_Type)
+#define pygimp_display_new (_PyGimp_API->display_new)
+#define PyGimpItem_Type (_PyGimp_API->Item_Type)
+#define pygimp_item_new (_PyGimp_API->item_new)
+#define PyGimpDrawable_Type (_PyGimp_API->Drawable_Type)
+#define pygimp_drawable_new (_PyGimp_API->drawable_new)
+#define PyGimpLayer_Type (_PyGimp_API->Layer_Type)
+#define pygimp_layer_new (_PyGimp_API->layer_new)
+#define PyGimpGroupLayer_Type (_PyGimp_API->GroupLayer_Type)
+#define pygimp_group_layer_new (_PyGimp_API->group_layer_new)
+#define PyGimpChannel_Type (_PyGimp_API->Channel_Type)
+#define pygimp_channel_new (_PyGimp_API->channel_new)
+#define PyGimpVectors_Type (_PyGimp_API->Vectors_Type)
+#define pygimp_vectors_new (_PyGimp_API->vectors_new)
+#define pygimp_error (_PyGimp_API->pygimp_error)
+
+#define init_pygimp() G_STMT_START { \
+ PyObject *gimpmodule = PyImport_ImportModule("gimp"); \
+ if (gimpmodule != NULL) { \
+ PyObject *mdict = PyModule_GetDict(gimpmodule); \
+ PyObject *cobject = PyDict_GetItemString(mdict, "_PyGimp_API"); \
+ if (PyCObject_Check(cobject)) \
+ _PyGimp_API = PyCObject_AsVoidPtr(cobject); \
+ else { \
+ PyErr_SetString(PyExc_RuntimeError, \
+ "could not find _PyGimp_API object"); \
+ return; \
+ } \
+ } else { \
+ PyErr_SetString(PyExc_ImportError, \
+ "could not import gimp"); \
+ return; \
+ } \
+} G_STMT_END
+
+#endif /* ! _INSIDE_PYGIMP_ */
+
+#endif /* _PYGIMP_API_H_ */
diff --git a/plug-ins/pygimp/pygimp-colors.c b/plug-ins/pygimp/pygimp-colors.c
new file mode 100644
index 0000000..fe37b5d
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-colors.c
@@ -0,0 +1,2407 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005-2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#define NO_IMPORT_PYGOBJECT
+
+#include "pygimp.h"
+#include "pygimpcolor.h"
+
+#include <libgimpmath/gimpmath.h>
+
+static PyObject *
+rgb_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *r = NULL, *g = NULL, *b = NULL, *a = NULL;
+ GimpRGB tmprgb, *rgb;
+ static char *kwlist[] = { "r", "g", "b", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &r, &g, &b, &a))
+ return NULL;
+
+ if (!r && !g && !b && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide r,g,b or a arguments");
+ return NULL;
+ }
+
+ if ((r && (!g || !b)) ||
+ (g && (!r || !b)) ||
+ (b && (!r || !g))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 r,g,b arguments");
+ return NULL;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+ tmprgb = *rgb;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmprgb.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ tmprgb.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (r) {
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+ }
+
+ if (a)
+ SET_MEMBER(a);
+
+#undef SET_MEMBER
+
+ *rgb = tmprgb;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpRGB *rgb;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ if (PyInt_Check(py_a))
+ rgb->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ rgb->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be an int or a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_add(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "color", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:add", kwlist,
+ &PyGimpRGB_Type, &color, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_add(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+ else
+ gimp_rgb_add(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_subtract(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "color", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:subtract", kwlist,
+ &PyGimpRGB_Type, &color, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_subtract(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+ else
+ gimp_rgb_subtract(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_multiply(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ double factor;
+ gboolean with_alpha = FALSE;
+ static char *kwlist[] = { "factor", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d|i:multiply", kwlist,
+ &factor, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ gimp_rgba_multiply(pyg_boxed_get(self, GimpRGB), factor);
+ else
+ gimp_rgb_multiply(pyg_boxed_get(self, GimpRGB), factor);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_distance(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ gboolean alpha = FALSE;
+ double ret;
+ static char *kwlist[] = { "color", "alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|i:distance", kwlist,
+ &PyGimpRGB_Type, &color, &alpha))
+ return NULL;
+
+ ret = gimp_rgb_distance(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB));
+
+
+ return PyFloat_FromDouble(ret);
+}
+
+static PyObject *
+rgb_max(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_max(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_min(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_min(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_clamp(PyObject *self)
+{
+ gimp_rgb_clamp(pyg_boxed_get(self, GimpRGB));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_gamma(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ double gamma;
+ static char *kwlist[] = { "gamma", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:gamma", kwlist, &gamma))
+ return NULL;
+
+ gimp_rgb_gamma(pyg_boxed_get(self, GimpRGB), gamma);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_luminance(PyObject *self)
+{
+ return PyFloat_FromDouble(gimp_rgb_luminance(pyg_boxed_get(self, GimpRGB)));
+}
+
+static PyObject *
+rgb_composite(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *color;
+ int mode = GIMP_RGB_COMPOSITE_NORMAL;
+ static char *kwlist[] = { "color", "mode", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|i:composite", kwlist,
+ &PyGimpRGB_Type, &color, &mode))
+ return NULL;
+
+ if (mode < GIMP_RGB_COMPOSITE_NONE || mode > GIMP_RGB_COMPOSITE_BEHIND) {
+ PyErr_SetString(PyExc_TypeError, "composite type is not valid");
+ return NULL;
+ }
+
+ gimp_rgb_composite(pyg_boxed_get(self, GimpRGB),
+ pyg_boxed_get(color, GimpRGB),
+ mode);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_name(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int len;
+ gboolean success;
+ static char *kwlist[] = { "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:parse_name", kwlist,
+ &name, &len))
+ return NULL;
+
+ success = gimp_rgb_parse_name(pyg_boxed_get(self, GimpRGB), name, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse color name");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_hex(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *hex;
+ int len;
+ gboolean success;
+ static char *kwlist[] = { "hex", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#:parse_hex", kwlist,
+ &hex, &len))
+ return NULL;
+
+ success = gimp_rgb_parse_hex(pyg_boxed_get(self, GimpRGB), hex, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse hex value");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_parse_css(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ char *css;
+ int len;
+ gboolean success, with_alpha = FALSE;
+ static char *kwlist[] = { "css", "with_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s#|i:parse_css", kwlist,
+ &css, &len, &with_alpha))
+ return NULL;
+
+ if (with_alpha)
+ success = gimp_rgba_parse_css(pyg_boxed_get(self, GimpRGB), css, len);
+ else
+ success = gimp_rgb_parse_css(pyg_boxed_get(self, GimpRGB), css, len);
+
+ if (!success) {
+ PyErr_SetString(PyExc_ValueError, "unable to parse CSS color");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+rgb_to_hsv(PyObject *self)
+{
+ GimpRGB *rgb;
+ GimpHSV hsv;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_hsv(rgb, &hsv);
+
+ return pygimp_hsv_new(&hsv);
+}
+
+static PyObject *
+rgb_to_hsl(PyObject *self)
+{
+ GimpRGB *rgb;
+ GimpHSL hsl;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_hsl(rgb, &hsl);
+
+ return pygimp_hsl_new(&hsl);
+}
+
+static PyObject *
+rgb_to_cmyk(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ GimpRGB *rgb;
+ GimpCMYK cmyk;
+ gdouble pullout = 1.0;
+ static char *kwlist[] = { "pullout", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|d:to_cmyk", kwlist,
+ &pullout))
+ return NULL;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ gimp_rgb_to_cmyk(rgb, pullout, &cmyk);
+
+ return pygimp_cmyk_new(&cmyk);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+rgb_getstate(PyObject *self)
+{
+ GimpRGB *rgb;
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ return Py_BuildValue("dddd", rgb->r, rgb->g, rgb->b, rgb->a);
+}
+
+static PyObject *
+rgb_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, rgb_getstate(self));
+}
+
+static PyMethodDef rgb_methods[] = {
+ { "set", (PyCFunction)rgb_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)rgb_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "add", (PyCFunction)rgb_add, METH_VARARGS|METH_KEYWORDS },
+ { "subtract", (PyCFunction)rgb_subtract, METH_VARARGS|METH_KEYWORDS },
+ { "multiply", (PyCFunction)rgb_multiply, METH_VARARGS|METH_KEYWORDS },
+ { "distance", (PyCFunction)rgb_distance, METH_VARARGS|METH_KEYWORDS },
+ { "max", (PyCFunction)rgb_max, METH_NOARGS },
+ { "min", (PyCFunction)rgb_min, METH_NOARGS },
+ { "clamp", (PyCFunction)rgb_clamp, METH_NOARGS },
+ { "gamma", (PyCFunction)rgb_gamma, METH_VARARGS|METH_KEYWORDS },
+ { "luminance", (PyCFunction)rgb_luminance, METH_NOARGS },
+ { "composite", (PyCFunction)rgb_composite, METH_VARARGS|METH_KEYWORDS },
+ { "parse_name", (PyCFunction)rgb_parse_name, METH_VARARGS|METH_KEYWORDS },
+ { "parse_hex", (PyCFunction)rgb_parse_hex, METH_VARARGS|METH_KEYWORDS },
+ { "parse_css", (PyCFunction)rgb_parse_css, METH_VARARGS|METH_KEYWORDS },
+ { "to_hsv", (PyCFunction)rgb_to_hsv, METH_NOARGS },
+ { "to_hsl", (PyCFunction)rgb_to_hsl, METH_NOARGS },
+ { "to_cmyk", (PyCFunction)rgb_to_cmyk, METH_VARARGS|METH_KEYWORDS },
+ { "__reduce__", (PyCFunction)rgb_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m) \
+static PyObject * \
+rgb_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpRGB)->m); \
+} \
+static int \
+rgb_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpRGB *rgb = pyg_boxed_get(self, GimpRGB); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ rgb->m = (double) PyInt_AS_LONG(value) / 255.0; \
+ else if (PyFloat_Check(value)) \
+ rgb->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(r);
+MEMBER_ACCESSOR(g);
+MEMBER_ACCESSOR(b);
+MEMBER_ACCESSOR(a);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef rgb_getsets[] = {
+ { "r", (getter)rgb_get_r, (setter)rgb_set_r },
+ { "g", (getter)rgb_get_g, (setter)rgb_set_g },
+ { "b", (getter)rgb_get_b, (setter)rgb_set_b },
+ { "a", (getter)rgb_get_a, (setter)rgb_set_a },
+ { "red", (getter)rgb_get_r, (setter)rgb_set_r },
+ { "green", (getter)rgb_get_g, (setter)rgb_set_g },
+ { "blue", (getter)rgb_get_b, (setter)rgb_set_b },
+ { "alpha", (getter)rgb_get_a, (setter)rgb_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+rgb_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+rgb_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpRGB *rgb;
+ double val;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ switch (pos) {
+ case 0: val = rgb->r; break;
+ case 1: val = rgb->g; break;
+ case 2: val = rgb->b; break;
+ case 3: val = rgb->a; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * 255.0));
+}
+
+static int
+rgb_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return rgb_set_r(self, value, NULL);
+ case 1: return rgb_set_g(self, value, NULL);
+ case 2: return rgb_set_b(self, value, NULL);
+ case 3: return rgb_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+rgb_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, rgb_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods rgb_as_sequence = {
+ rgb_length,
+ (binaryfunc)0,
+ 0,
+ rgb_getitem,
+ rgb_slice,
+ rgb_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+rgb_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return rgb_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return rgb_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, rgb_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "r") == 0 ||
+ g_ascii_strcasecmp(s, "red") == 0)
+ return rgb_get_r(self, NULL);
+ else if (g_ascii_strcasecmp(s, "g") == 0 ||
+ g_ascii_strcasecmp(s, "green") == 0)
+ return rgb_get_g(self, NULL);
+ else if (g_ascii_strcasecmp(s, "b") == 0 ||
+ g_ascii_strcasecmp(s, "blue") == 0)
+ return rgb_get_b(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return rgb_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods rgb_as_mapping = {
+ rgb_length,
+ (binaryfunc)rgb_subscript,
+ (objobjargproc)0
+};
+
+static long
+rgb_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = rgb_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+rgb_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpRGB *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_rgb_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpRGB);
+ c2 = pyg_boxed_get(other, GimpRGB);
+
+ if ((c1->r == c2->r && c1->g == c2->g && c1->b == c2->b && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+rgb_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpRGB *rgb;
+ PyObject *ret = NULL;
+ PyObject *r_f = NULL, *g_f = NULL, *b_f = NULL, *a_f = NULL;
+ PyObject *r = NULL, *g = NULL, *b = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "RGB ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ rgb = pyg_boxed_get(self, GimpRGB);
+
+ if ((r_f = PyFloat_FromDouble(rgb->r)) == NULL) goto cleanup;
+ if ((g_f = PyFloat_FromDouble(rgb->g)) == NULL) goto cleanup;
+ if ((b_f = PyFloat_FromDouble(rgb->b)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(rgb->a)) == NULL) goto cleanup;
+
+ if ((r = repr(r_f)) == NULL) goto cleanup;
+ if ((g = repr(g_f)) == NULL) goto cleanup;
+ if ((b = repr(b_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(r),
+ PyString_AsString(g),
+ PyString_AsString(b),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(r); Py_XDECREF(g); Py_XDECREF(b); Py_XDECREF(a);
+ Py_XDECREF(r_f); Py_XDECREF(g_f); Py_XDECREF(b_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+rgb_repr(PyObject *self)
+{
+ return rgb_pretty_print(self, FALSE);
+}
+
+static PyObject *
+rgb_str(PyObject *self)
+{
+ return rgb_pretty_print(self, TRUE);
+}
+
+static int
+rgb_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *r, *g, *b, *a = NULL;
+ GimpRGB rgb;
+ static char *kwlist[] = { "r", "g", "b", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &r, &g, &b, &a))
+ return -1;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ rgb.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ rgb.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ rgb.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_RGB;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_RGB, &rgb);
+
+ return 0;
+}
+
+PyTypeObject PyGimpRGB_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.RGB", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)rgb_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &rgb_as_sequence, /* tp_as_sequence */
+ &rgb_as_mapping, /* tp_as_mapping */
+ (hashfunc)rgb_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)rgb_str, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)rgb_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ rgb_methods, /* tp_methods */
+ 0, /* tp_members */
+ rgb_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)rgb_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_rgb_new(const GimpRGB *rgb)
+{
+ return pyg_boxed_new(GIMP_TYPE_RGB, (gpointer)rgb, TRUE, TRUE);
+}
+
+
+static PyObject *
+hsv_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h = NULL, *s = NULL, *v = NULL, *a = NULL;
+ GimpHSV tmphsv, *hsv;
+ static char *kwlist[] = { "h", "s", "v", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &h, &s, &v, &a))
+ return NULL;
+
+ if (!h && !s && !v && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide h,s,v or a arguments");
+ return NULL;
+ }
+
+ if ((h && (!s || !v)) ||
+ (s && (!h || !v)) ||
+ (v && (!h || !s))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 h,s,v arguments");
+ return NULL;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+ tmphsv = *hsv;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmphsv.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ tmphsv.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (h) {
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(v, 100.0);
+ }
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+
+#undef SET_MEMBER
+
+ *hsv = tmphsv;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpHSV *hsv;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ if (PyInt_Check(py_a))
+ hsv->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ hsv->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_clamp(PyObject *self)
+{
+ gimp_hsv_clamp(pyg_boxed_get(self, GimpHSV));
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsv_to_rgb(PyObject *self)
+{
+ GimpHSV *hsv;
+ GimpRGB rgb;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ gimp_hsv_to_rgb(hsv, &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+hsv_getstate(PyObject *self)
+{
+ GimpHSV *hsv;
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ return Py_BuildValue("dddd", hsv->h, hsv->s, hsv->v, hsv->a);
+}
+
+static PyObject *
+hsv_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, hsv_getstate(self));
+}
+
+static PyMethodDef hsv_methods[] = {
+ { "set", (PyCFunction)hsv_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)hsv_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "clamp", (PyCFunction)hsv_clamp, METH_NOARGS },
+ { "to_rgb", (PyCFunction)hsv_to_rgb, METH_NOARGS },
+ { "__reduce__", (PyCFunction)hsv_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m, s) \
+static PyObject * \
+hsv_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpHSV)->m); \
+} \
+static int \
+hsv_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpHSV *hsv = pyg_boxed_get(self, GimpHSV); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ hsv->m = (double) PyInt_AS_LONG(value) / s; \
+ else if (PyFloat_Check(value)) \
+ hsv->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(h, 360.0);
+MEMBER_ACCESSOR(s, 100.0);
+MEMBER_ACCESSOR(v, 100.0);
+MEMBER_ACCESSOR(a, 255.0);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef hsv_getsets[] = {
+ { "h", (getter)hsv_get_h, (setter)hsv_set_h },
+ { "s", (getter)hsv_get_s, (setter)hsv_set_s },
+ { "v", (getter)hsv_get_v, (setter)hsv_set_v },
+ { "a", (getter)hsv_get_a, (setter)hsv_set_a },
+ { "hue", (getter)hsv_get_h, (setter)hsv_set_h },
+ { "saturation", (getter)hsv_get_s, (setter)hsv_set_s },
+ { "value", (getter)hsv_get_v, (setter)hsv_set_v },
+ { "alpha", (getter)hsv_get_a, (setter)hsv_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+hsv_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+hsv_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpHSV *hsv;
+ double val, scale_factor;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ switch (pos) {
+ case 0: val = hsv->h; scale_factor = 360.0; break;
+ case 1: val = hsv->s; scale_factor = 100.0; break;
+ case 2: val = hsv->v; scale_factor = 100.0; break;
+ case 3: val = hsv->a; scale_factor = 255.0; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * scale_factor));
+}
+
+static int
+hsv_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return hsv_set_h(self, value, NULL);
+ case 1: return hsv_set_s(self, value, NULL);
+ case 2: return hsv_set_v(self, value, NULL);
+ case 3: return hsv_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+hsv_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, hsv_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods hsv_as_sequence = {
+ hsv_length,
+ (binaryfunc)0,
+ 0,
+ hsv_getitem,
+ hsv_slice,
+ hsv_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+hsv_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return hsv_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return hsv_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, hsv_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "h") == 0 ||
+ g_ascii_strcasecmp(s, "hue") == 0)
+ return hsv_get_h(self, NULL);
+ else if (g_ascii_strcasecmp(s, "s") == 0 ||
+ g_ascii_strcasecmp(s, "saturation") == 0)
+ return hsv_get_s(self, NULL);
+ else if (g_ascii_strcasecmp(s, "v") == 0 ||
+ g_ascii_strcasecmp(s, "value") == 0)
+ return hsv_get_v(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return hsv_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods hsv_as_mapping = {
+ hsv_length,
+ (binaryfunc)hsv_subscript,
+ (objobjargproc)0
+};
+
+static long
+hsv_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = hsv_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+hsv_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpHSV *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_hsv_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpHSV);
+ c2 = pyg_boxed_get(other, GimpHSV);
+
+ if ((c1->h == c2->h && c1->s == c2->s && c1->v == c2->v && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+hsv_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpHSV *hsv;
+ PyObject *ret = NULL;
+ PyObject *h_f = NULL, *s_f = NULL, *v_f = NULL, *a_f = NULL;
+ PyObject *h = NULL, *s = NULL, *v = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "HSV ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ hsv = pyg_boxed_get(self, GimpHSV);
+
+ if ((h_f = PyFloat_FromDouble(hsv->h)) == NULL) goto cleanup;
+ if ((s_f = PyFloat_FromDouble(hsv->s)) == NULL) goto cleanup;
+ if ((v_f = PyFloat_FromDouble(hsv->v)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(hsv->a)) == NULL) goto cleanup;
+
+ if ((h = repr(h_f)) == NULL) goto cleanup;
+ if ((s = repr(s_f)) == NULL) goto cleanup;
+ if ((v = repr(v_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(h),
+ PyString_AsString(s),
+ PyString_AsString(v),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(h); Py_XDECREF(s); Py_XDECREF(v); Py_XDECREF(a);
+ Py_XDECREF(h_f); Py_XDECREF(s_f); Py_XDECREF(v_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+hsv_repr(PyObject *self)
+{
+ return hsv_pretty_print(self, FALSE);
+}
+
+static PyObject *
+hsv_str(PyObject *self)
+{
+ return hsv_pretty_print(self, TRUE);
+}
+
+static int
+hsv_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h, *s, *v, *a = NULL;
+ GimpHSV hsv;
+ static char *kwlist[] = { "h", "s", "v", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &h, &s, &v, &a))
+ return -1;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ hsv.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ hsv.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(v, 100.0);
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+ else
+ hsv.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_HSV;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_HSV, &hsv);
+
+ return 0;
+}
+
+PyTypeObject PyGimpHSV_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.HSV", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)hsv_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &hsv_as_sequence, /* tp_as_sequence */
+ &hsv_as_mapping, /* tp_as_mapping */
+ (hashfunc)hsv_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)hsv_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)hsv_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ hsv_methods, /* tp_methods */
+ 0, /* tp_members */
+ hsv_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)hsv_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_hsv_new(const GimpHSV *hsv)
+{
+ return pyg_boxed_new(GIMP_TYPE_HSV, (gpointer)hsv, TRUE, TRUE);
+}
+
+
+static PyObject *
+hsl_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h = NULL, *s = NULL, *l = NULL, *a = NULL;
+ GimpHSL tmphsl, *hsl;
+ static char *kwlist[] = { "h", "s", "l", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO:set", kwlist,
+ &h, &s, &l, &a))
+ return NULL;
+
+ if (!h && !s && !l && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide h,s,l or a arguments");
+ return NULL;
+ }
+
+ if ((h && (!s || !l)) ||
+ (s && (!h || !l)) ||
+ (l && (!h || !s))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 3 h,s,l arguments");
+ return NULL;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+ tmphsl = *hsl;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmphsl.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ tmphsl.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (h) {
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(l, 100.0);
+ }
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+
+#undef SET_MEMBER
+
+ *hsl = tmphsl;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsl_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpHSL *hsl;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ if (PyInt_Check(py_a))
+ hsl->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ hsl->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+hsl_to_rgb(PyObject *self)
+{
+ GimpHSL *hsl;
+ GimpRGB rgb;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ gimp_hsl_to_rgb(hsl, &rgb);
+
+ return pygimp_rgb_new(&rgb);
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+hsl_getstate(PyObject *self)
+{
+ GimpHSL *hsl;
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ return Py_BuildValue("dddd", hsl->h, hsl->s, hsl->l, hsl->a);
+}
+
+static PyObject *
+hsl_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, hsl_getstate(self));
+}
+
+static PyMethodDef hsl_methods[] = {
+ { "set", (PyCFunction)hsl_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)hsl_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "to_rgb", (PyCFunction)hsl_to_rgb, METH_NOARGS },
+ { "__reduce__", (PyCFunction)hsl_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m, s) \
+static PyObject * \
+hsl_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpHSL)->m); \
+} \
+static int \
+hsl_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpHSL *hsl = pyg_boxed_get(self, GimpHSL); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ hsl->m = (double) PyInt_AS_LONG(value) / s; \
+ else if (PyFloat_Check(value)) \
+ hsl->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(h, 360.0);
+MEMBER_ACCESSOR(s, 100.0);
+MEMBER_ACCESSOR(l, 100.0);
+MEMBER_ACCESSOR(a, 255.0);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef hsl_getsets[] = {
+ { "h", (getter)hsl_get_h, (setter)hsl_set_h },
+ { "s", (getter)hsl_get_s, (setter)hsl_set_s },
+ { "l", (getter)hsl_get_l, (setter)hsl_set_l },
+ { "a", (getter)hsl_get_a, (setter)hsl_set_a },
+ { "hue", (getter)hsl_get_h, (setter)hsl_set_h },
+ { "saturation", (getter)hsl_get_s, (setter)hsl_set_s },
+ { "lightness", (getter)hsl_get_l, (setter)hsl_set_l },
+ { "alpha", (getter)hsl_get_a, (setter)hsl_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+hsl_length(PyObject *self)
+{
+ return 4;
+}
+
+static PyObject *
+hsl_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpHSL *hsl;
+ double val, scale_factor;
+
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ switch (pos) {
+ case 0: val = hsl->h; scale_factor = 360.0; break;
+ case 1: val = hsl->s; scale_factor = 100.0; break;
+ case 2: val = hsl->l; scale_factor = 100.0; break;
+ case 3: val = hsl->a; scale_factor = 255.0; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * scale_factor));
+}
+
+static int
+hsl_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 4;
+
+ if (pos < 0 || pos >= 4) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return hsl_set_h(self, value, NULL);
+ case 1: return hsl_set_s(self, value, NULL);
+ case 2: return hsl_set_l(self, value, NULL);
+ case 3: return hsl_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+hsl_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 4)
+ end = 4;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, hsl_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods hsl_as_sequence = {
+ hsl_length,
+ (binaryfunc)0,
+ 0,
+ hsl_getitem,
+ hsl_slice,
+ hsl_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+hsl_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return hsl_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return hsl_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 4,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, hsl_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "h") == 0 ||
+ g_ascii_strcasecmp(s, "hue") == 0)
+ return hsl_get_h(self, NULL);
+ else if (g_ascii_strcasecmp(s, "s") == 0 ||
+ g_ascii_strcasecmp(s, "saturation") == 0)
+ return hsl_get_s(self, NULL);
+ else if (g_ascii_strcasecmp(s, "l") == 0 ||
+ g_ascii_strcasecmp(s, "lightness") == 0)
+ return hsl_get_l(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return hsl_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods hsl_as_mapping = {
+ hsl_length,
+ (binaryfunc)hsl_subscript,
+ (objobjargproc)0
+};
+
+static long
+hsl_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = hsl_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+hsl_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpHSL *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_hsl_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpHSL);
+ c2 = pyg_boxed_get(other, GimpHSL);
+
+ if ((c1->h == c2->h && c1->s == c2->s && c1->l == c2->l && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+hsl_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpHSL *hsl;
+ PyObject *ret = NULL;
+ PyObject *h_f = NULL, *s_f = NULL, *l_f = NULL, *a_f = NULL;
+ PyObject *h = NULL, *s = NULL, *l = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "HSL ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ hsl = pyg_boxed_get(self, GimpHSL);
+
+ if ((h_f = PyFloat_FromDouble(hsl->h)) == NULL) goto cleanup;
+ if ((s_f = PyFloat_FromDouble(hsl->s)) == NULL) goto cleanup;
+ if ((l_f = PyFloat_FromDouble(hsl->l)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(hsl->a)) == NULL) goto cleanup;
+
+ if ((h = repr(h_f)) == NULL) goto cleanup;
+ if ((s = repr(s_f)) == NULL) goto cleanup;
+ if ((l = repr(l_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(h),
+ PyString_AsString(s),
+ PyString_AsString(l),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(h); Py_XDECREF(s); Py_XDECREF(l); Py_XDECREF(a);
+ Py_XDECREF(h_f); Py_XDECREF(s_f); Py_XDECREF(l_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+hsl_repr(PyObject *self)
+{
+ return hsl_pretty_print(self, FALSE);
+}
+
+static PyObject *
+hsl_str(PyObject *self)
+{
+ return hsl_pretty_print(self, TRUE);
+}
+
+static int
+hsl_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *h, *s, *l, *a = NULL;
+ GimpHSL hsl;
+ static char *kwlist[] = { "h", "s", "l", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOO|O:set", kwlist,
+ &h, &s, &l, &a))
+ return -1;
+
+#define SET_MEMBER(m, s) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ hsl.m = (double) PyInt_AS_LONG(m) / s; \
+ else if (PyFloat_Check(m)) \
+ hsl.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(h, 360.0);
+ SET_MEMBER(s, 100.0);
+ SET_MEMBER(l, 100.0);
+
+ if (a)
+ SET_MEMBER(a, 255.0);
+ else
+ hsl.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_HSL;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_HSL, &hsl);
+
+ return 0;
+}
+
+PyTypeObject PyGimpHSL_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.HSL", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)hsl_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &hsl_as_sequence, /* tp_as_sequence */
+ &hsl_as_mapping, /* tp_as_mapping */
+ (hashfunc)hsl_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)hsl_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)hsl_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ hsl_methods, /* tp_methods */
+ 0, /* tp_members */
+ hsl_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)hsl_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_hsl_new(const GimpHSL *hsl)
+{
+ return pyg_boxed_new(GIMP_TYPE_HSL, (gpointer)hsl, TRUE, TRUE);
+}
+
+
+static PyObject *
+cmyk_set(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *c = NULL, *m = NULL, *y = NULL, *k = NULL, *a = NULL;
+ GimpCMYK tmpcmyk, *cmyk;
+ static char *kwlist[] = { "c", "m", "y", "k", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOO:set", kwlist,
+ &c, &m, &y, &k, &a))
+ return NULL;
+
+ if (!c && !y && !m && !k && !a) {
+ PyErr_SetString(PyExc_TypeError, "must provide c,m,y,k or a arguments");
+ return NULL;
+ }
+
+ if ((c && (!m || !y || !k)) ||
+ (m && (!c || !y || !k)) ||
+ (y && (!c || !m || !k)) ||
+ (k && (!c || !m || !y))) {
+ PyErr_SetString(PyExc_TypeError, "must provide all 4 c,m,y,k arguments");
+ return NULL;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+ tmpcmyk = *cmyk;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ tmpcmyk.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ tmpcmyk.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return NULL; \
+ } \
+} G_STMT_END
+
+ if (c) {
+ SET_MEMBER(c);
+ SET_MEMBER(y);
+ SET_MEMBER(m);
+ SET_MEMBER(k);
+ }
+
+ if (a)
+ SET_MEMBER(a);
+
+#undef SET_MEMBER
+
+ *cmyk = tmpcmyk;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+cmyk_set_alpha(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_a;
+ GimpCMYK *cmyk;
+ static char *kwlist[] = { "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:set_alpha", kwlist,
+ &py_a))
+ return NULL;
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ if (PyInt_Check(py_a))
+ cmyk->a = (double) PyInt_AS_LONG(py_a) / 255.0;
+ else if (PyFloat_Check(py_a))
+ cmyk->a = PyFloat_AS_DOUBLE(py_a);
+ else {
+ PyErr_SetString(PyExc_TypeError, "a must be an int or a float");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* __getstate__ isn't exposed */
+static PyObject *
+cmyk_getstate(PyObject *self)
+{
+ GimpCMYK *cmyk;
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ return Py_BuildValue("ddddd", cmyk->c, cmyk->m, cmyk->y, cmyk->k, cmyk->a);
+}
+
+static PyObject *
+cmyk_reduce(PyObject *self)
+{
+ return Py_BuildValue("ON", self->ob_type, cmyk_getstate(self));
+}
+
+static PyMethodDef cmyk_methods[] = {
+ { "set", (PyCFunction)cmyk_set, METH_VARARGS|METH_KEYWORDS },
+ { "set_alpha", (PyCFunction)cmyk_set_alpha, METH_VARARGS|METH_KEYWORDS },
+ { "__reduce__", (PyCFunction)cmyk_reduce, METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+#define MEMBER_ACCESSOR(m) \
+static PyObject * \
+cmyk_get_ ## m(PyObject *self, void *closure) \
+{ \
+ return PyFloat_FromDouble(pyg_boxed_get(self, GimpCMYK)->m); \
+} \
+static int \
+cmyk_set_ ## m(PyObject *self, PyObject *value, void *closure) \
+{ \
+ GimpCMYK *cmyk = pyg_boxed_get(self, GimpCMYK); \
+ if (value == NULL) { \
+ PyErr_SetString(PyExc_TypeError, "cannot delete value"); \
+ return -1; \
+ } \
+ else if (PyInt_Check(value)) \
+ cmyk->m = (double) PyInt_AS_LONG(value) / 255.0; \
+ else if (PyFloat_Check(value)) \
+ cmyk->m = PyFloat_AS_DOUBLE(value); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, "type mismatch"); \
+ return -1; \
+ } \
+ return 0; \
+}
+
+MEMBER_ACCESSOR(c);
+MEMBER_ACCESSOR(m);
+MEMBER_ACCESSOR(y);
+MEMBER_ACCESSOR(k);
+MEMBER_ACCESSOR(a);
+
+#undef MEMBER_ACCESSOR
+
+static PyGetSetDef cmyk_getsets[] = {
+ { "c", (getter)cmyk_get_c, (setter)cmyk_set_c },
+ { "m", (getter)cmyk_get_m, (setter)cmyk_set_m },
+ { "y", (getter)cmyk_get_y, (setter)cmyk_set_y },
+ { "k", (getter)cmyk_get_k, (setter)cmyk_set_k },
+ { "a", (getter)cmyk_get_a, (setter)cmyk_set_a },
+ { "cyan", (getter)cmyk_get_c, (setter)cmyk_set_c },
+ { "magenta", (getter)cmyk_get_m, (setter)cmyk_set_m },
+ { "yellow", (getter)cmyk_get_y, (setter)cmyk_set_y },
+ { "black", (getter)cmyk_get_k, (setter)cmyk_set_k },
+ { "alpha", (getter)cmyk_get_a, (setter)cmyk_set_a },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static Py_ssize_t
+cmyk_length(PyObject *self)
+{
+ return 5;
+}
+
+static PyObject *
+cmyk_getitem(PyObject *self, Py_ssize_t pos)
+{
+ GimpCMYK *cmyk;
+ double val;
+
+ if (pos < 0)
+ pos += 5;
+
+ if (pos < 0 || pos >= 5) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ switch (pos) {
+ case 0: val = cmyk->c; break;
+ case 1: val = cmyk->m; break;
+ case 2: val = cmyk->y; break;
+ case 3: val = cmyk->k; break;
+ case 4: val = cmyk->a; break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ return PyInt_FromLong(ROUND(CLAMP(val, 0.0, 1.0) * 255.0));
+}
+
+static int
+cmyk_setitem(PyObject *self, Py_ssize_t pos, PyObject *value)
+{
+ if (pos < 0)
+ pos += 5;
+
+ if (pos < 0 || pos >= 5) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ switch (pos) {
+ case 0: return cmyk_set_c(self, value, NULL);
+ case 1: return cmyk_set_m(self, value, NULL);
+ case 2: return cmyk_set_y(self, value, NULL);
+ case 3: return cmyk_set_k(self, value, NULL);
+ case 4: return cmyk_set_a(self, value, NULL);
+ default:
+ g_assert_not_reached();
+ return -1;
+ }
+}
+
+static PyObject *
+cmyk_slice(PyObject *self, Py_ssize_t start, Py_ssize_t end)
+{
+ PyTupleObject *ret;
+ Py_ssize_t i;
+
+ if (start < 0)
+ start = 0;
+ if (end > 5)
+ end = 5;
+ if (end < start)
+ end = start;
+
+ ret = (PyTupleObject *)PyTuple_New(end - start);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = start; i < end; i++)
+ PyTuple_SET_ITEM(ret, i - start, cmyk_getitem(self, i));
+
+ return (PyObject *)ret;
+}
+
+static PySequenceMethods cmyk_as_sequence = {
+ cmyk_length,
+ (binaryfunc)0,
+ 0,
+ cmyk_getitem,
+ cmyk_slice,
+ cmyk_setitem,
+ 0,
+ (objobjproc)0,
+};
+
+static PyObject *
+cmyk_subscript(PyObject *self, PyObject *item)
+{
+ if (PyInt_Check(item)) {
+ long i = PyInt_AS_LONG(item);
+ return cmyk_getitem(self, i);
+ } else if (PyLong_Check(item)) {
+ long i = PyLong_AsLong(item);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return cmyk_getitem(self, i);
+ } else if (PySlice_Check(item)) {
+ Py_ssize_t start, stop, step, slicelength, cur, i;
+ PyObject *ret;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)item, 5,
+ &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+
+ if (slicelength <= 0) {
+ return PyTuple_New(0);
+ } else {
+ ret = PyTuple_New(slicelength);
+ if (!ret)
+ return NULL;
+
+ for (cur = start, i = 0; i < slicelength; cur += step, i++)
+ PyTuple_SET_ITEM(ret, i, cmyk_getitem(self, cur));
+
+ return ret;
+ }
+ } else if (PyString_Check(item)) {
+ char *s = PyString_AsString(item);
+
+ if (g_ascii_strcasecmp(s, "c") == 0 ||
+ g_ascii_strcasecmp(s, "cyan") == 0)
+ return cmyk_get_c(self, NULL);
+ else if (g_ascii_strcasecmp(s, "m") == 0 ||
+ g_ascii_strcasecmp(s, "magenta") == 0)
+ return cmyk_get_m(self, NULL);
+ else if (g_ascii_strcasecmp(s, "y") == 0 ||
+ g_ascii_strcasecmp(s, "yellow") == 0)
+ return cmyk_get_y(self, NULL);
+ else if (g_ascii_strcasecmp(s, "k") == 0 ||
+ g_ascii_strcasecmp(s, "black") == 0)
+ return cmyk_get_k(self, NULL);
+ else if (g_ascii_strcasecmp(s, "a") == 0 ||
+ g_ascii_strcasecmp(s, "alpha") == 0)
+ return cmyk_get_a(self, NULL);
+ else {
+ PyErr_SetObject(PyExc_KeyError, item);
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError,
+ "indices must be integers");
+ return NULL;
+ }
+}
+
+static PyMappingMethods cmyk_as_mapping = {
+ cmyk_length,
+ (binaryfunc)cmyk_subscript,
+ (objobjargproc)0
+};
+
+static long
+cmyk_hash(PyObject *self)
+{
+ long ret = -1;
+
+ PyObject *temp = cmyk_getstate(self);
+ if (temp != NULL) {
+ ret = PyObject_Hash(temp);
+ Py_DECREF(temp);
+ }
+
+ return ret;
+}
+
+static PyObject *
+cmyk_richcompare(PyObject *self, PyObject *other, int op)
+{
+ GimpCMYK *c1, *c2;
+ PyObject *ret;
+
+ if (!pygimp_cmyk_check(other)) {
+ PyErr_Format(PyExc_TypeError,
+ "can't compare %s to %s",
+ self->ob_type->tp_name, other->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (op != Py_EQ && op != Py_NE) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't compare color values using <, <=, >, >=");
+ return NULL;
+ }
+
+ c1 = pyg_boxed_get(self, GimpCMYK);
+ c2 = pyg_boxed_get(other, GimpCMYK);
+
+ if ((c1->c == c2->c && c1->m == c2->m && c1->y == c2->y && c1->k == c2->k && c1->a == c2->a) == (op == Py_EQ))
+ ret = Py_True;
+ else
+ ret = Py_False;
+
+ Py_INCREF(ret);
+ return ret;
+}
+
+static PyObject *
+cmyk_pretty_print(PyObject *self, gboolean inexact)
+{
+ GimpCMYK *cmyk;
+ PyObject *ret = NULL;
+ PyObject *c_f = NULL, *m_f = NULL, *y_f = NULL, *k_f = NULL, *a_f = NULL;
+ PyObject *c = NULL, *m = NULL, *y = NULL, *k = NULL, *a = NULL;
+ reprfunc repr;
+ const char *prefix;
+
+ if (inexact) {
+ repr = PyObject_Str;
+ prefix = "CMYK ";
+ } else {
+ repr = PyObject_Repr;
+ prefix = self->ob_type->tp_name;
+ }
+
+ cmyk = pyg_boxed_get(self, GimpCMYK);
+
+ if ((c_f = PyFloat_FromDouble(cmyk->c)) == NULL) goto cleanup;
+ if ((m_f = PyFloat_FromDouble(cmyk->m)) == NULL) goto cleanup;
+ if ((y_f = PyFloat_FromDouble(cmyk->y)) == NULL) goto cleanup;
+ if ((k_f = PyFloat_FromDouble(cmyk->k)) == NULL) goto cleanup;
+ if ((a_f = PyFloat_FromDouble(cmyk->a)) == NULL) goto cleanup;
+
+ if ((c = repr(c_f)) == NULL) goto cleanup;
+ if ((m = repr(m_f)) == NULL) goto cleanup;
+ if ((y = repr(y_f)) == NULL) goto cleanup;
+ if ((k = repr(k_f)) == NULL) goto cleanup;
+ if ((a = repr(a_f)) == NULL) goto cleanup;
+
+ ret = PyString_FromFormat("%s(%s, %s, %s, %s, %s)",
+ prefix,
+ PyString_AsString(c),
+ PyString_AsString(m),
+ PyString_AsString(y),
+ PyString_AsString(k),
+ PyString_AsString(a));
+
+cleanup:
+ Py_XDECREF(c); Py_XDECREF(m); Py_XDECREF(y); Py_XDECREF(k); Py_XDECREF(a);
+ Py_XDECREF(c_f); Py_XDECREF(m_f); Py_XDECREF(y_f); Py_XDECREF(k_f); Py_XDECREF(a_f);
+
+ return ret;
+}
+
+static PyObject *
+cmyk_repr(PyObject *self)
+{
+ return cmyk_pretty_print(self, FALSE);
+}
+
+static PyObject *
+cmyk_str(PyObject *self)
+{
+ return cmyk_pretty_print(self, TRUE);
+}
+
+static int
+cmyk_init(PyGBoxed *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *c, *m, *y, *k, *a = NULL;
+ GimpCMYK cmyk;
+ static char *kwlist[] = { "c", "m", "y", "k", "a", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OOOO|O:set", kwlist,
+ &c, &m, &y, &k, &a))
+ return -1;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ cmyk.m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ cmyk.m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return -1; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(c);
+ SET_MEMBER(m);
+ SET_MEMBER(y);
+ SET_MEMBER(k);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ cmyk.a = 1.0;
+
+#undef SET_MEMBER
+
+ self->gtype = GIMP_TYPE_CMYK;
+ self->free_on_dealloc = TRUE;
+ self->boxed = g_boxed_copy(GIMP_TYPE_CMYK, &cmyk);
+
+ return 0;
+}
+
+PyTypeObject PyGimpCMYK_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimpcolor.CMYK", /* tp_name */
+ sizeof(PyGBoxed), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)0, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)cmyk_repr, /* tp_repr */
+ (PyNumberMethods*)0, /* tp_as_number */
+ &cmyk_as_sequence, /* tp_as_sequence */
+ &cmyk_as_mapping, /* tp_as_mapping */
+ (hashfunc)cmyk_hash, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)cmyk_str, /* tp_repr */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ (PyBufferProcs*)0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)cmyk_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ cmyk_methods, /* tp_methods */
+ 0, /* tp_members */
+ cmyk_getsets, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ (descrgetfunc)0, /* tp_descr_get */
+ (descrsetfunc)0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)cmyk_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+ (freefunc)0, /* tp_free */
+ (inquiry)0 /* tp_is_gc */
+};
+
+PyObject *
+pygimp_cmyk_new(const GimpCMYK *cmyk)
+{
+ return pyg_boxed_new(GIMP_TYPE_CMYK, (gpointer)cmyk, TRUE, TRUE);
+}
+
+int
+pygimp_rgb_from_pyobject(PyObject *object, GimpRGB *color)
+{
+ g_return_val_if_fail(color != NULL, FALSE);
+
+ if (pygimp_rgb_check(object)) {
+ *color = *pyg_boxed_get(object, GimpRGB);
+ return 1;
+ } else if (PyString_Check(object)) {
+ if (gimp_rgb_parse_css (color, PyString_AsString(object), -1)) {
+ return 1;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "unable to parse color string");
+ return 0;
+ }
+ } else if (PySequence_Check(object)) {
+ PyObject *r, *g, *b, *a = NULL;
+
+ if (!PyArg_ParseTuple(object, "OOO|O", &r, &g, &b, &a))
+ return 0;
+
+#define SET_MEMBER(m) G_STMT_START { \
+ if (PyInt_Check(m)) \
+ color->m = (double) PyInt_AS_LONG(m) / 255.0; \
+ else if (PyFloat_Check(m)) \
+ color->m = PyFloat_AS_DOUBLE(m); \
+ else { \
+ PyErr_SetString(PyExc_TypeError, \
+ #m " must be an int or a float"); \
+ return 0; \
+ } \
+} G_STMT_END
+
+ SET_MEMBER(r);
+ SET_MEMBER(g);
+ SET_MEMBER(b);
+
+ if (a)
+ SET_MEMBER(a);
+ else
+ color->a = 1.0;
+
+ gimp_rgb_clamp(color);
+
+ return 1;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "could not convert to GimpRGB");
+ return 0;
+}
diff --git a/plug-ins/pygimp/pygimp-display.c b/plug-ins/pygimp/pygimp-display.c
new file mode 100644
index 0000000..cf138d3
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-display.c
@@ -0,0 +1,140 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "pygimp.h"
+
+static PyMethodDef disp_methods[] = {
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+disp_get_ID(PyGimpDisplay *self, void *closure)
+{
+ return PyInt_FromLong(self->ID);
+}
+
+static PyGetSetDef disp_getsets[] = {
+ { "ID", (getter)disp_get_ID, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+/* ---------- */
+
+
+PyObject *
+pygimp_display_new(gint32 ID)
+{
+ PyGimpDisplay *self;
+
+ if (!gimp_display_is_valid(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ self = PyObject_NEW(PyGimpDisplay, &PyGimpDisplay_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+
+ return (PyObject *)self;
+}
+
+static void
+disp_dealloc(PyGimpDisplay *self)
+{
+ PyObject_DEL(self);
+}
+
+static PyObject *
+disp_repr(PyGimpDisplay *self)
+{
+ PyObject *s;
+
+ s = PyString_FromString("<display>");
+
+ return s;
+}
+
+static int
+disp_init(PyGimpDisplay *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+
+ if (!PyArg_ParseTuple(args, "O!:gimp.Display.__init__",
+ &PyGimpImage_Type, &img))
+ return -1;
+
+ self->ID = gimp_display_new(img->ID);
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error, "could not create display for image (ID %d)",
+ img->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyTypeObject PyGimpDisplay_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Display", /* tp_name */
+ sizeof(PyGimpDisplay), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)disp_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)disp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ disp_methods, /* tp_methods */
+ 0, /* tp_members */
+ disp_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)disp_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
diff --git a/plug-ins/pygimp/pygimp-drawable.c b/plug-ins/pygimp/pygimp-drawable.c
new file mode 100644
index 0000000..9134d9b
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-drawable.c
@@ -0,0 +1,2459 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+#include "pygimp.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+#include <glib-object.h>
+
+#include <gegl.h>
+
+static void
+ensure_drawable(PyGimpDrawable *self)
+{
+ if (!self->drawable)
+ self->drawable = gimp_drawable_get(self->ID);
+}
+
+static PyObject *
+drw_flush(PyGimpDrawable *self)
+{
+ ensure_drawable(self);
+
+ gimp_drawable_flush(self->drawable);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+drw_update(PyGimpDrawable *self, PyObject *args)
+{
+ int x, y;
+ unsigned int w, h;
+
+ if (!PyArg_ParseTuple(args, "iiii:update", &x, &y, &w, &h))
+ return NULL;
+
+ if (!gimp_drawable_update(self->ID, x, y, w, h)) {
+ PyErr_Format(pygimp_error,
+ "could not update drawable (ID %d): "
+ "x=%d, y=%d, w=%d, h=%d",
+ self->ID, x, y, (int)w, (int)h);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+drw_merge_shadow(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ gboolean undo = FALSE;
+
+ static char *kwlist[] = { "undo", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:merge_shadow", kwlist,
+ &undo))
+ return NULL;
+
+ if (!gimp_drawable_merge_shadow(self->ID, undo)) {
+ PyErr_Format(pygimp_error,
+ "could not merge the shadow buffer on drawable (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_free_shadow(PyGimpDrawable *self)
+{
+ if (!gimp_drawable_free_shadow(self->ID)) {
+ PyErr_Format(pygimp_error, "could not free shadow tiles on drawable (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_fill(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int fill = GIMP_FILL_FOREGROUND;
+
+ static char *kwlist[] = { "fill", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:fill", kwlist, &fill))
+ return NULL;
+
+ if (!gimp_drawable_fill(self->ID, fill)) {
+ PyErr_Format(pygimp_error,
+ "could not fill drawable (ID %d) with fill mode %d",
+ self->ID, fill);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+drw_get_tile(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ GimpTile *t;
+ int shadow, row, col;
+
+ static char *kwlist[] = { "shadow", "row", "col", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii:get_tile", kwlist,
+ &shadow, &row, &col))
+ return NULL;
+
+ ensure_drawable(self);
+
+ if(row < 0 || row >= self->drawable->ntile_rows ||
+ col < 0 || col >= self->drawable->ntile_cols) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ t = gimp_drawable_get_tile(self->drawable, shadow, row, col);
+ return pygimp_tile_new(t, self);
+}
+
+static PyObject *
+drw_get_tile2(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ GimpTile *t;
+ int shadow, x, y, row, col;
+
+ static char *kwlist[] = { "shadow", "x", "y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iii:get_tile2", kwlist,
+ &shadow, &x ,&y))
+ return NULL;
+
+ ensure_drawable(self);
+ if(x < 0 || x >= self->drawable->width ||
+ y < 0 || y >= self->drawable->height) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ col = x / gimp_tile_width ();
+ row = y / gimp_tile_height ();
+
+ t = gimp_drawable_get_tile(self->drawable, shadow, row, col);
+ return pygimp_tile_new(t, self);
+}
+
+static PyObject *
+drw_get_pixel_rgn(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int x, y, width, height, dirty = 1, shadow = 0;
+
+ static char *kwlist[] = { "x", "y", "width", "height", "dirty", "shadow",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "iiii|ii:get_pixel_rgn", kwlist,
+ &x, &y, &width, &height, &dirty, &shadow))
+ return NULL;
+
+ ensure_drawable(self);
+
+ return pygimp_pixel_rgn_new(self, x, y, width, height, dirty, shadow);
+}
+
+static PyObject *
+drw_offset(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int wrap_around;
+ GimpOffsetType fill_type;
+ int offset_x, offset_y;
+
+ static char *kwlist[] = { "wrap_around", "fill_type",
+ "offset_x", "offset_y",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iiii:offset", kwlist,
+ &wrap_around, &fill_type,
+ &offset_x, &offset_y))
+ return NULL;
+
+ if (!gimp_drawable_offset(self->ID, wrap_around, fill_type,
+ offset_x, offset_y)) {
+ PyErr_Format(pygimp_error,
+ "could not offset drawable (ID %d) by x: %d, y: %d",
+ self->ID, offset_x, offset_y);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_parasite_find(PyGimpDrawable *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_find", &name))
+ return NULL;
+
+ return pygimp_parasite_new(gimp_item_get_parasite(self->ID, name));
+}
+
+static PyObject *
+drw_parasite_attach(PyGimpDrawable *self, PyObject *args)
+{
+ PyGimpParasite *parasite;
+
+ if (!PyArg_ParseTuple(args, "O!:parasite_attach", &PyGimpParasite_Type,
+ &parasite))
+ return NULL;
+
+ if (!gimp_item_attach_parasite(self->ID, parasite->para)) {
+ PyErr_Format(pygimp_error,
+ "could not attach parasite '%s' on drawable (ID %d)",
+ gimp_parasite_name(parasite->para), self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_attach_new_parasite(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int flags, size;
+ guint8 *data;
+ GimpParasite *parasite;
+ gboolean success;
+
+ static char *kwlist[] = { "name", "flags", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "sis#:attach_new_parasite", kwlist,
+ &name, &flags, &data, &size))
+ return NULL;
+
+ parasite = gimp_parasite_new (name,
+ flags, size + 1, data);
+ success = gimp_item_attach_parasite (self->ID, parasite);
+ gimp_parasite_free (parasite);
+
+ if (!success) {
+ PyErr_Format(pygimp_error,
+ "could not attach new parasite '%s' to drawable (ID %d)",
+ name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_parasite_detach(PyGimpDrawable *self, PyObject *args)
+{
+ char *name;
+ if (!PyArg_ParseTuple(args, "s:detach_parasite", &name))
+ return NULL;
+
+ if (!gimp_item_detach_parasite(self->ID, name)) {
+ PyErr_Format(pygimp_error,
+ "could not detach parasite '%s' from drawable (ID %d)",
+ name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+drw_parasite_list(PyGimpDrawable *self)
+{
+ gint num_parasites;
+ gchar **parasites;
+ PyObject *ret;
+ gint i;
+
+ parasites = gimp_item_get_parasite_list(self->ID, &num_parasites);
+
+ ret = PyTuple_New(num_parasites);
+
+ for (i = 0; i < num_parasites; i++)
+ PyTuple_SetItem(ret, i, PyString_FromString(parasites[i]));
+
+ g_strfreev(parasites);
+ return ret;
+}
+
+static PyObject *
+drw_get_pixel(PyGimpDrawable *self, PyObject *args)
+{
+ int x, y;
+ int num_channels, i;
+ guint8 *pixel;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "(ii):get_pixel", &x, &y)) {
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(args, "ii:get_pixel", &x, &y))
+ return NULL;
+ }
+
+ pixel = gimp_drawable_get_pixel(self->ID, x, y, &num_channels);
+
+ if (!pixel) {
+ PyErr_Format(pygimp_error,
+ "could not get pixel (%d, %d) on drawable (ID %d)",
+ x, y, self->ID);
+ return NULL;
+ }
+
+ ret = PyTuple_New(num_channels);
+
+ for (i = 0; i < num_channels; i++)
+ PyTuple_SetItem(ret, i, PyInt_FromLong(pixel[i]));
+
+ g_free(pixel);
+
+ return ret;
+}
+
+static PyObject *
+drw_set_pixel(PyGimpDrawable *self, PyObject *args)
+{
+ int x, y;
+ int num_channels, i, val;
+ guint8 *pixel;
+ PyObject *seq, *item;
+ gboolean is_string, error = TRUE;
+
+ if (!PyArg_ParseTuple(args, "(ii)O:set_pixel", &x, &y, &seq)) {
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(args, "iiO:set_pixel", &x, &y, &seq))
+ return NULL;
+ }
+
+ if (!PyString_Check(seq)) {
+ if (!PySequence_Check(seq)) {
+ PyErr_SetString(PyExc_TypeError,
+ "pixel values must be a sequence");
+ return NULL;
+ }
+
+ is_string = FALSE;
+
+ num_channels = PySequence_Length(seq);
+ pixel = g_new(guint8, num_channels);
+
+ for (i = 0; i < num_channels; i++) {
+ item = PySequence_GetItem(seq, i);
+
+ if (!PyInt_Check(item)) {
+ PyErr_SetString(PyExc_TypeError,
+ "pixel values must be a sequence of ints");
+ goto out;
+ }
+
+ val = PyInt_AsLong(item);
+
+ if (val < 0 || val > 255) {
+ PyErr_SetString(PyExc_TypeError,
+ "pixel values must be between 0 and 255");
+ goto out;
+ }
+
+ pixel[i] = val;
+ }
+ } else {
+ is_string = TRUE;
+
+ num_channels = PyString_Size(seq);
+ pixel = (guint8 *)PyString_AsString(seq);
+ }
+
+ error = !gimp_drawable_set_pixel(self->ID, x, y, num_channels, pixel);
+
+ if (error)
+ PyErr_Format(pygimp_error,
+ "could not set %d-element pixel (%d, %d) on "
+ "drawable (ID %d)",
+ num_channels, x, y, self->ID);
+
+out:
+ if (!is_string)
+ g_free(pixel);
+
+ if (!error) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ } else
+ return NULL;
+}
+
+static PyObject *
+drw_mask_intersect(PyGimpDrawable *self)
+{
+ int x, y, width, height;
+
+ if (!gimp_drawable_mask_intersect(self->ID, &x, &y, &width, &height)) {
+ PyErr_Format(pygimp_error,
+ "could not get selection bounds of drawable (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ return Py_BuildValue("(iiii)", x, y, width, height);
+}
+
+static PyObject *
+transform_result(PyGimpDrawable *self, gint32 id, const char *err_desc)
+{
+ if (id == self->ID) {
+ Py_INCREF(self);
+ return (PyObject *)self;
+ } else if (id != -1) {
+ return pygimp_drawable_new(NULL, id);
+ } else {
+ PyErr_Format(pygimp_error, "could not %s drawable (ID %d)",
+ err_desc, self->ID);
+ return NULL;
+ }
+}
+
+static PyObject *
+drw_transform_flip(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1;
+ int transform_direction, interpolation, recursion_level = 3;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddddii|iii:transform_flip", kwlist,
+ &x0, &y0, &x1, &y1, &transform_direction,
+ &interpolation, &supersample,
+ &recursion_level, &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_flip (self->ID, x0, y0, x1, y1);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "flip");
+}
+
+static PyObject *
+drw_transform_flip_simple(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int flip_type;
+ gboolean auto_center, clip_result = FALSE;
+ double axis;
+ gint32 id;
+
+ static char *kwlist[] = { "flip_type", "auto_center", "axis",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "iid|i:transform_flip_simple", kwlist,
+ &flip_type, &auto_center, &axis,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_flip_simple (self->ID, flip_type,
+ auto_center, axis);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "flip");
+}
+
+static PyObject *
+drw_transform_flip_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", "interpolate",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddd|ii:transform_flip_default", kwlist,
+ &x0, &y0, &x1, &y1, &interpolate,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_flip (self->ID, x0, y0, x1, y1);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "flip");
+}
+
+static PyObject *
+drw_transform_perspective(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1, x2, y2, x3, y3;
+ int transform_direction, interpolation, recursion_level = 3;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", "x2", "y2", "x3", "y3",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddddddddii|iii:transform_perspective",
+ kwlist,
+ &x0, &y0, &x1, &y1, &x2, &y2, &x3, &y3,
+ &transform_direction, &interpolation,
+ &supersample, &recursion_level,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_perspective (self->ID,
+ x0, y0, x1, y1, x2, y2, x3, y3);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply perspective transform to");
+}
+
+static PyObject *
+drw_transform_perspective_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1, x2, y2, x3, y3;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", "x2", "y2", "x3", "y3",
+ "interpolate", "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddddddd|ii:transform_perspective_default",
+ kwlist,
+ &x0, &y0, &x1, &y1, &x2, &y2, &x3, &y3,
+ &interpolate, &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_perspective (self->ID,
+ x0, y0, x1, y1, x2, y2, x3, y3);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply perspective transform to");
+}
+
+static PyObject *
+drw_transform_rotate(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double angle;
+ gboolean auto_center, supersample = FALSE, clip_result = FALSE;
+ int center_x, center_y, transform_direction, interpolation,
+ recursion_level = 3;
+ gint32 id;
+
+ static char *kwlist[] = { "angle", "auto_center", "center_x", "center_y",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "diiiii|iii:transform_rotate", kwlist,
+ &angle, &auto_center, &center_x, &center_y,
+ &transform_direction, &interpolation,
+ &supersample, &recursion_level,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_rotate (self->ID, angle, auto_center,
+ center_x, center_y);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "rotate");
+}
+
+static PyObject *
+drw_transform_rotate_simple(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int rotate_type, center_x, center_y;
+ gboolean auto_center, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "rotate_type", "auto_center",
+ "center_x", "center_y",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "iiii|i:transform_rotate_simple", kwlist,
+ &rotate_type, &auto_center,
+ &center_x, &center_y,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_rotate_simple (self->ID, rotate_type,
+ auto_center,
+ center_x, center_y);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "rotate");
+}
+
+static PyObject *
+drw_transform_rotate_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double angle;
+ gboolean auto_center, interpolate = FALSE, clip_result = FALSE;
+ int center_x, center_y;
+ gint32 id;
+
+ static char *kwlist[] = { "angle", "auto_center", "center_x", "center_y",
+ "interpolate", "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddd|ii:transform_rotate_default", kwlist,
+ &angle, &auto_center, &center_x, &center_y,
+ &interpolate, &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_rotate (self->ID, angle, auto_center,
+ center_x, center_y);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "rotate");
+}
+
+static PyObject *
+drw_transform_scale(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1;
+ int transform_direction, interpolation, recursion_level = 3;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddddii|iii:transform_scale", kwlist,
+ &x0, &y0, &x1, &y1, &transform_direction,
+ &interpolation, &supersample,
+ &recursion_level, &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_scale (self->ID, x0, y0, x1, y1);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "scale");
+}
+
+static PyObject *
+drw_transform_scale_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", "interpolate",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddd|ii:transform_scale_default", kwlist,
+ &x0, &y0, &x1, &y1, &interpolate,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_scale (self->ID, x0, y0, x1, y1);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "scale");
+}
+
+static PyObject *
+drw_transform_shear(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int shear_type, transform_direction, interpolation, recursion_level = 3;
+ double magnitude;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "shear_type", "magnitude",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "idii|iii:transform_shear", kwlist,
+ &shear_type, &magnitude,
+ &transform_direction, &interpolation,
+ &supersample, &recursion_level,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_shear (self->ID, shear_type, magnitude);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "shear");
+}
+
+static PyObject *
+drw_transform_shear_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ int shear_type;
+ double magnitude;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "shear_type", "magnitude", "interpolate",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "id|ii:transform_shear_default", kwlist,
+ &shear_type, &magnitude, &interpolate,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_shear (self->ID, shear_type, magnitude);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "shear");
+}
+
+static PyObject *
+drw_transform_2d(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double source_x, source_y, scale_x, scale_y, angle, dest_x, dest_y;
+ int transform_direction, interpolation, recursion_level = 3;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "source_x", "source_y", "scale_x", "scale_y",
+ "angle", "dest_x", "dest_y",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddddddii|iii:transform_2d", kwlist,
+ &source_x, &source_y, &scale_x, &scale_y,
+ &angle, &dest_x, &dest_y,
+ &transform_direction, &interpolation,
+ &supersample, &recursion_level,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_2d (self->ID, source_x, source_y,
+ scale_x, scale_y, angle, dest_x, dest_y);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply 2d transform to");
+}
+
+static PyObject *
+drw_transform_2d_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double source_x, source_y, scale_x, scale_y, angle, dest_x, dest_y;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "source_x", "source_y", "scale_x", "scale_y",
+ "angle", "dest_x", "dest_y", "interpolate",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddddddd|ii:transform_2d_default", kwlist,
+ &source_x, &source_y, &scale_x, &scale_y,
+ &angle, &dest_x, &dest_y, &interpolate,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_2d (self->ID, source_x, source_y,
+ scale_x, scale_y, angle, dest_x, dest_y);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply 2d transform to");
+}
+
+static PyObject *
+drw_transform_matrix(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double coeff_0_0, coeff_0_1, coeff_0_2,
+ coeff_1_0, coeff_1_1, coeff_1_2,
+ coeff_2_0, coeff_2_1, coeff_2_2;
+ int transform_direction, interpolation, recursion_level = 3;
+ gboolean supersample = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "coeff_0_0", "coeff_0_1", "coeff_0_2",
+ "coeff_1_0", "coeff_1_1", "coeff_1_2",
+ "coeff_2_0", "coeff_2_1", "coeff_2_2",
+ "transform_direction", "interpolation",
+ "supersample", "recursion_level",
+ "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddddddddii|iii:transform_matrix", kwlist,
+ &coeff_0_0, &coeff_0_1, &coeff_0_2,
+ &coeff_1_0, &coeff_1_1, &coeff_1_2,
+ &coeff_2_0, &coeff_2_1, &coeff_2_2,
+ &transform_direction, &interpolation,
+ &supersample, &recursion_level,
+ &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ gimp_context_set_transform_direction (transform_direction);
+ gimp_context_set_interpolation (interpolation);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_matrix (self->ID,
+ coeff_0_0, coeff_0_1, coeff_0_2,
+ coeff_1_0, coeff_1_1, coeff_1_2,
+ coeff_2_0, coeff_2_1, coeff_2_2);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply 2d matrix transform to");
+}
+
+static PyObject *
+drw_transform_matrix_default(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ double coeff_0_0, coeff_0_1, coeff_0_2,
+ coeff_1_0, coeff_1_1, coeff_1_2,
+ coeff_2_0, coeff_2_1, coeff_2_2;
+ gboolean interpolate = FALSE, clip_result = FALSE;
+ gint32 id;
+
+ static char *kwlist[] = { "coeff_0_0", "coeff_0_1", "coeff_0_2",
+ "coeff_1_0", "coeff_1_1", "coeff_1_2",
+ "coeff_2_0", "coeff_2_1", "coeff_2_2",
+ "interpolate", "clip_result", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ddddddddd|ii:transform_matrix_default",
+ kwlist,
+ &coeff_0_0, &coeff_0_1, &coeff_0_2,
+ &coeff_1_0, &coeff_1_1, &coeff_1_2,
+ &coeff_2_0, &coeff_2_1, &coeff_2_2,
+ &interpolate, &clip_result))
+ return NULL;
+
+ gimp_context_push ();
+ if (! interpolate)
+ gimp_context_set_interpolation (GIMP_INTERPOLATION_NONE);
+ gimp_context_set_transform_resize (clip_result);
+
+ id = gimp_item_transform_matrix (self->ID,
+ coeff_0_0, coeff_0_1, coeff_0_2,
+ coeff_1_0, coeff_1_1, coeff_1_2,
+ coeff_2_0, coeff_2_1, coeff_2_2);
+
+ gimp_context_pop ();
+
+ return transform_result(self, id, "apply 2d matrix transform to");
+}
+
+static PyObject *
+drw_get_data(PyGimpDrawable *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = { "format", NULL };
+ gchar *format = "RGBA float";
+ const Babl *bbl_format;
+ void *output_buffer;
+ GeglBuffer *buffer;
+ int bpp;
+ Py_ssize_t size;
+ PyObject *buffer_data, *ret;
+ PyObject *array_module;
+ PyObject *array_type;
+ char array_data_type;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "|s:get_data",
+ kwlist, &format
+ ))
+ return NULL;
+
+ if (g_str_has_suffix (format, "double")) {
+ array_data_type = 'd';
+ } else if (g_str_has_suffix (format, "float")) {
+ array_data_type = 'f';
+ }
+ else if (g_str_has_suffix (format, "u16")) {
+ array_data_type = 'H';
+ } else if (g_str_has_suffix (format, "u8")) {
+ array_data_type = 'B';
+ } else {
+ PyErr_Warn (PyExc_Warning,
+ "Could not find appropriate data format - returning raw bytes");
+ array_data_type = 'B';
+ }
+
+ bbl_format = babl_format (format);
+ bpp = babl_format_get_bytes_per_pixel (bbl_format);
+ ensure_drawable(self);
+ buffer = gimp_drawable_get_buffer (self->ID);
+ size = bpp * self->drawable->width * self->drawable->height;
+ output_buffer = g_malloc ((gsize) size);
+ if (output_buffer == NULL) {
+ return PyErr_NoMemory();
+ }
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, 0, self->drawable->width, self->drawable->height),
+ 1.0,
+ bbl_format,
+ output_buffer,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ buffer_data = PyString_FromStringAndSize (output_buffer, size);
+
+ array_module = PyImport_ImportModule ("array");
+ if (!array_module) {
+ PyErr_SetString (pygimp_error, "could not import array module");
+ return NULL;
+ }
+
+ array_type = PyObject_GetAttrString (array_module, "array");
+ Py_DECREF(array_module);
+ if (!array_type) {
+ PyErr_SetString (pygimp_error, "could not get array.array type");
+ return NULL;
+ }
+
+ ret = PyObject_CallFunction (array_type, "cO", array_data_type, buffer_data);
+ if (!ret) {
+ PyErr_SetString (pygimp_error, "could not create array object");
+ return NULL;
+ }
+
+ Py_DECREF (buffer_data);
+ g_free (output_buffer);
+
+ return ret;
+}
+
+
+/* for inclusion with the methods of layer and channel objects */
+static PyMethodDef drw_methods[] = {
+ {"flush", (PyCFunction)drw_flush, METH_NOARGS},
+ {"update", (PyCFunction)drw_update, METH_VARARGS},
+ {"merge_shadow", (PyCFunction)drw_merge_shadow, METH_VARARGS | METH_KEYWORDS},
+ {"free_shadow", (PyCFunction)drw_free_shadow, METH_NOARGS},
+ {"fill", (PyCFunction)drw_fill, METH_VARARGS | METH_KEYWORDS},
+ {"get_tile", (PyCFunction)drw_get_tile, METH_VARARGS | METH_KEYWORDS},
+ {"get_tile2", (PyCFunction)drw_get_tile2, METH_VARARGS | METH_KEYWORDS},
+ {"get_pixel_rgn", (PyCFunction)drw_get_pixel_rgn, METH_VARARGS | METH_KEYWORDS},
+ {"get_data", (PyCFunction)drw_get_data, METH_VARARGS | METH_KEYWORDS,
+ "Takes a BABL format string, returns a Python array.array object"},
+ {"offset", (PyCFunction)drw_offset, METH_VARARGS | METH_KEYWORDS},
+ {"parasite_find", (PyCFunction)drw_parasite_find, METH_VARARGS},
+ {"parasite_attach", (PyCFunction)drw_parasite_attach, METH_VARARGS},
+ {"attach_new_parasite",(PyCFunction)drw_attach_new_parasite,METH_VARARGS | METH_KEYWORDS},
+ {"parasite_detach", (PyCFunction)drw_parasite_detach, METH_VARARGS},
+ {"parasite_list", (PyCFunction)drw_parasite_list, METH_VARARGS},
+ {"get_pixel", (PyCFunction)drw_get_pixel, METH_VARARGS},
+ {"set_pixel", (PyCFunction)drw_set_pixel, METH_VARARGS},
+ {"mask_intersect", (PyCFunction)drw_mask_intersect, METH_NOARGS},
+ {"transform_flip", (PyCFunction)drw_transform_flip, METH_VARARGS | METH_KEYWORDS},
+ {"transform_flip_simple", (PyCFunction)drw_transform_flip_simple, METH_VARARGS | METH_KEYWORDS},
+ {"transform_flip_default", (PyCFunction)drw_transform_flip_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_perspective", (PyCFunction)drw_transform_perspective, METH_VARARGS | METH_KEYWORDS},
+ {"transform_perspective_default", (PyCFunction)drw_transform_perspective_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_rotate", (PyCFunction)drw_transform_rotate, METH_VARARGS | METH_KEYWORDS},
+ {"transform_rotate_simple", (PyCFunction)drw_transform_rotate_simple, METH_VARARGS | METH_KEYWORDS},
+ {"transform_rotate_default", (PyCFunction)drw_transform_rotate_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_scale", (PyCFunction)drw_transform_scale, METH_VARARGS | METH_KEYWORDS},
+ {"transform_scale_default", (PyCFunction)drw_transform_scale_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_shear", (PyCFunction)drw_transform_shear, METH_VARARGS | METH_KEYWORDS},
+ {"transform_shear_default", (PyCFunction)drw_transform_shear_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_2d", (PyCFunction)drw_transform_2d, METH_VARARGS | METH_KEYWORDS},
+ {"transform_2d_default", (PyCFunction)drw_transform_2d_default, METH_VARARGS | METH_KEYWORDS},
+ {"transform_matrix", (PyCFunction)drw_transform_matrix, METH_VARARGS | METH_KEYWORDS},
+ {"transform_matrix_default", (PyCFunction)drw_transform_matrix_default, METH_VARARGS | METH_KEYWORDS},
+ {NULL, NULL, 0}
+};
+
+static PyObject *
+drw_get_ID(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(self->ID);
+}
+
+static PyObject *
+drw_get_name(PyGimpDrawable *self, void *closure)
+{
+ return PyString_FromString(gimp_item_get_name(self->ID));
+}
+
+static int
+drw_set_name(PyGimpDrawable *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete name");
+ return -1;
+ }
+
+ if (!PyString_Check(value) && !PyUnicode_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_name(self->ID, PyString_AsString(value));
+
+ return 0;
+}
+
+static PyObject *
+drw_get_bpp(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_drawable_bpp(self->ID));
+}
+
+static PyObject *
+drw_get_has_alpha(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_drawable_has_alpha(self->ID));
+}
+
+static PyObject *
+drw_get_height(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_drawable_height(self->ID));
+}
+
+static PyObject *
+drw_get_image(PyGimpDrawable *self, void *closure)
+{
+ return pygimp_image_new(gimp_item_get_image(self->ID));
+}
+
+static PyObject *
+drw_get_is_rgb(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_drawable_is_rgb(self->ID));
+}
+
+static PyObject *
+drw_get_is_gray(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_drawable_is_gray(self->ID));
+}
+
+static PyObject *
+drw_get_is_indexed(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_drawable_is_indexed(self->ID));
+}
+
+static PyObject *
+drw_get_is_layer_mask(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_item_is_layer_mask(self->ID));
+}
+
+static PyObject *
+drw_get_mask_bounds(PyGimpDrawable *self, void *closure)
+{
+ gint x1, y1, x2, y2;
+
+ gimp_drawable_mask_bounds(self->ID, &x1, &y1, &x2, &y2);
+ return Py_BuildValue("(iiii)", x1, y1, x2, y2);
+}
+
+static PyObject *
+drw_get_mask_intersect(PyGimpDrawable *self, void *closure)
+{
+ gint x, y, w, h;
+
+ if(!gimp_drawable_mask_intersect(self->ID, &x, &y, &w, &h))
+ return Py_BuildValue("");
+ return Py_BuildValue("(iiii)", x, y, w, h);
+}
+
+static PyObject *
+drw_get_offsets(PyGimpDrawable *self, void *closure)
+{
+ gint x, y;
+
+ gimp_drawable_offsets(self->ID, &x, &y);
+
+ return Py_BuildValue("(ii)", x, y);
+}
+
+static PyObject *
+drw_get_type(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_drawable_type(self->ID));
+}
+
+static PyObject *
+drw_get_type_with_alpha(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_drawable_type_with_alpha(self->ID));
+}
+
+static PyObject *
+drw_get_width(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_drawable_width(self->ID));
+}
+
+static PyObject *
+drw_get_linked(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_item_get_linked(self->ID));
+}
+
+static int
+drw_set_linked(PyGimpDrawable *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete linked");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_linked(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+drw_get_tattoo(PyGimpDrawable *self, void *closure)
+{
+ return PyInt_FromLong(gimp_item_get_tattoo(self->ID));
+}
+
+static int
+drw_set_tattoo(PyGimpDrawable *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete tattoo");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_tattoo(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+drw_get_visible(PyGimpDrawable *self, void *closure)
+{
+ return PyBool_FromLong(gimp_item_get_visible(self->ID));
+}
+
+static int
+drw_set_visible(PyGimpDrawable *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete visible");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_visible(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyGetSetDef drw_getsets[] = {
+ { "ID", (getter)drw_get_ID, (setter)0 },
+ { "name", (getter)drw_get_name, (setter)drw_set_name },
+ { "bpp", (getter)drw_get_bpp, (setter)0 },
+ { "has_alpha", (getter)drw_get_has_alpha, (setter)0 },
+ { "height", (getter)drw_get_height, (setter)0 },
+ { "image", (getter)drw_get_image, (setter)0 },
+ { "is_rgb", (getter)drw_get_is_rgb, (setter)0 },
+ { "is_gray", (getter)drw_get_is_gray, (setter)0 },
+ { "is_grey", (getter)drw_get_is_gray, (setter)0 },
+ { "is_indexed", (getter)drw_get_is_indexed, (setter)0 },
+ { "is_layer_mask", (getter)drw_get_is_layer_mask, (setter)0 },
+ { "mask_bounds", (getter)drw_get_mask_bounds, (setter)0 },
+ { "mask_intersect", (getter)drw_get_mask_intersect, (setter)0 },
+ { "offsets", (getter)drw_get_offsets, (setter)0 },
+ { "type", (getter)drw_get_type, (setter)0 },
+ { "type_with_alpha", (getter)drw_get_type_with_alpha, (setter)0 },
+ { "width", (getter)drw_get_width, (setter)0 },
+ { "linked", (getter)drw_get_linked, (setter)drw_set_linked },
+ { "tattoo", (getter)drw_get_tattoo, (setter)drw_set_tattoo },
+ { "visible", (getter)drw_get_visible, (setter)drw_set_visible },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static void
+drw_dealloc(PyGimpDrawable *self)
+{
+ if (self->drawable)
+ gimp_drawable_detach(self->drawable);
+
+ PyObject_DEL(self);
+}
+
+static PyObject *
+drw_repr(PyGimpDrawable *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.Drawable '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+drw_cmp(PyGimpDrawable *self, PyGimpDrawable *other)
+{
+ if (self->ID == other->ID)
+ return 0;
+ if (self->ID > other->ID)
+ return -1;
+ return 1;
+}
+
+PyTypeObject PyGimpDrawable_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Drawable", /* tp_name */
+ sizeof(PyGimpDrawable), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)drw_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)drw_cmp, /* tp_compare */
+ (reprfunc)drw_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ drw_methods, /* tp_methods */
+ 0, /* tp_members */
+ drw_getsets, /* tp_getset */
+ &PyGimpItem_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+
+PyObject *
+pygimp_drawable_new(GimpDrawable *drawable, gint32 ID)
+{
+ PyObject *self;
+
+ if (drawable != NULL)
+ ID = drawable->drawable_id;
+
+ if (!gimp_item_is_valid(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ /* create the appropriate object type */
+ if (gimp_item_is_layer(ID))
+ self = pygimp_layer_new(ID);
+ else
+ self = pygimp_channel_new(ID);
+
+ if (self == NULL)
+ return NULL;
+
+ if (PyObject_TypeCheck(self, &PyGimpDrawable_Type))
+ ((PyGimpDrawable *)self)->drawable = drawable;
+
+ return self;
+}
+
+/* End of code for Drawable objects */
+/* -------------------------------------------------------- */
+
+
+static PyObject *
+lay_copy(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ int nreturn_vals;
+ GimpParam *return_vals;
+ gboolean add_alpha = FALSE;
+ gint32 id = -1;
+
+ static char *kwlist[] = { "add_alpha", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:copy", kwlist,
+ &add_alpha))
+ return NULL;
+
+ return_vals = gimp_run_procedure("gimp-layer-copy",
+ &nreturn_vals,
+ GIMP_PDB_LAYER, self->ID,
+ GIMP_PDB_INT32, add_alpha,
+ GIMP_PDB_END);
+
+ if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
+ id = return_vals[1].data.d_layer;
+ else
+ PyErr_Format(pygimp_error,
+ "could not create new layer copy from layer (ID %d)",
+ self->ID);
+
+ gimp_destroy_params(return_vals, nreturn_vals);
+
+ return id != -1 ? pygimp_layer_new(id) : NULL;
+}
+
+
+static PyObject *
+lay_add_alpha(PyGimpLayer *self)
+{
+ if (!gimp_layer_add_alpha(self->ID)) {
+ PyErr_Format(pygimp_error, "could not add alpha to layer (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+lay_add_mask(PyGimpLayer *self, PyObject *args)
+{
+ PyGimpChannel *mask;
+
+ if (!PyArg_ParseTuple(args, "O!:add_mask", &PyGimpChannel_Type, &mask))
+ return NULL;
+
+ if (!gimp_layer_add_mask(self->ID, mask->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not add mask (ID %d) to layer (ID %d)",
+ mask->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+lay_create_mask(PyGimpLayer *self, PyObject *args)
+{
+ int type;
+ gint32 id;
+
+ if (!PyArg_ParseTuple(args, "i:create_mask", &type))
+ return NULL;
+
+ id = gimp_layer_create_mask(self->ID, type);
+
+ if (id == -1) {
+ PyErr_Format(pygimp_error,
+ "could not create mask of type %d on layer (ID %d)",
+ type, self->ID);
+ return NULL;
+ }
+
+ return pygimp_channel_new(id);
+}
+
+static PyObject *
+lay_remove_mask(PyGimpLayer *self, PyObject *args)
+{
+ int mode;
+
+ if (!PyArg_ParseTuple(args, "i:remove_mask", &mode))
+ return NULL;
+
+ if (!gimp_layer_remove_mask(self->ID, mode)) {
+ PyErr_Format(pygimp_error,
+ "could not remove mask from layer (ID %d) with mode %d",
+ self->ID, mode);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+lay_resize(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ unsigned int new_h, new_w;
+ int offs_x = 0, offs_y = 0;
+
+ static char *kwlist[] = { "width", "height", "offset_x", "offset_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii:resize", kwlist,
+ &new_w, &new_h, &offs_x, &offs_y))
+ return NULL;
+
+ if (!gimp_layer_resize(self->ID, new_w, new_h, offs_x, offs_y)) {
+ PyErr_Format(pygimp_error,
+ "could not resize layer (ID %d) to size %dx%d "
+ "(offset %d, %d)",
+ self->ID, new_w, new_h, offs_x, offs_y);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+lay_resize_to_image_size(PyGimpLayer *self)
+{
+ if (!gimp_layer_resize_to_image_size(self->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not resize layer (ID %d) to image size",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+lay_scale(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ int new_width, new_height;
+ int interpolation = -1;
+ gboolean local_origin = FALSE;
+
+ static char *kwlist[] = { "width", "height", "local_origin",
+ "interpolation", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii:scale", kwlist,
+ &new_width, &new_height,
+ &local_origin, &interpolation))
+ return NULL;
+
+ if (interpolation != -1) {
+ gimp_context_push();
+ gimp_context_set_interpolation(interpolation);
+ }
+
+ if (!gimp_layer_scale(self->ID, new_width, new_height, local_origin)) {
+ PyErr_Format(pygimp_error,
+ "could not scale layer (ID %d) to size %dx%d",
+ self->ID, new_width, new_height);
+ if (interpolation != -1) {
+ gimp_context_pop();
+ }
+ return NULL;
+ }
+
+ if (interpolation != -1) {
+ gimp_context_pop();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+lay_translate(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ int offs_x, offs_y;
+
+ static char *kwlist[] = { "offset_x", "offset_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:translate", kwlist,
+ &offs_x, &offs_y))
+ return NULL;
+
+ if (!gimp_item_transform_translate(self->ID, offs_x, offs_y)) {
+ PyErr_Format(pygimp_error,
+ "could not translate layer (ID %d) to offset %d, %d",
+ self->ID, offs_x, offs_y);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+lay_set_offsets(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ int offs_x, offs_y;
+
+ static char *kwlist[] = { "offset_x", "offset_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii:set_offsets", kwlist,
+ &offs_x, &offs_y))
+ return NULL;
+
+ if (!gimp_layer_set_offsets(self->ID, offs_x, offs_y)) {
+ PyErr_Format(pygimp_error,
+ "could not set offset %d, %d on layer (ID %d)",
+ offs_x, offs_y, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef lay_methods[] = {
+ {"copy", (PyCFunction)lay_copy, METH_VARARGS | METH_KEYWORDS},
+ {"add_alpha", (PyCFunction)lay_add_alpha, METH_NOARGS},
+ {"add_mask", (PyCFunction)lay_add_mask, METH_VARARGS},
+ {"create_mask", (PyCFunction)lay_create_mask, METH_VARARGS},
+ {"remove_mask", (PyCFunction)lay_remove_mask, METH_VARARGS},
+ {"resize", (PyCFunction)lay_resize, METH_VARARGS | METH_KEYWORDS},
+ {"resize_to_image_size", (PyCFunction)lay_resize_to_image_size, METH_NOARGS},
+ {"scale", (PyCFunction)lay_scale, METH_VARARGS | METH_KEYWORDS},
+ {"translate", (PyCFunction)lay_translate, METH_VARARGS | METH_KEYWORDS},
+ {"set_offsets", (PyCFunction)lay_set_offsets, METH_VARARGS | METH_KEYWORDS},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+lay_get_is_floating_sel(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_layer_is_floating_sel(self->ID));
+}
+
+static PyObject *
+lay_get_mask(PyGimpLayer *self, void *closure)
+{
+ gint32 id = gimp_layer_get_mask(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_channel_new(id);
+}
+
+static PyObject *
+lay_get_apply_mask(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_layer_get_apply_mask(self->ID));
+}
+
+static int
+lay_set_apply_mask(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete apply_mask");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_apply_mask(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set layer mask on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_edit_mask(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_layer_get_edit_mask(self->ID));
+}
+
+static int
+lay_set_edit_mask(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete edit_mask");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_edit_mask(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set layer mask active on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_mode(PyGimpLayer *self, void *closure)
+{
+ return PyInt_FromLong(gimp_layer_get_mode(self->ID));
+}
+
+static int
+lay_set_mode(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete mode");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_mode(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error, "could not set mode on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_opacity(PyGimpLayer *self, void *closure)
+{
+ return PyFloat_FromDouble(gimp_layer_get_opacity(self->ID));
+}
+
+static int
+lay_set_opacity(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete opacity");
+ return -1;
+ }
+
+ if (!PyFloat_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_opacity(self->ID, PyFloat_AsDouble(value))) {
+ PyErr_Format(pygimp_error, "could not set opacity on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_lock_alpha(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_layer_get_lock_alpha(self->ID));
+}
+
+static int
+lay_set_lock_alpha(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot delete lock_alpha");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_lock_alpha(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set lock alpha setting on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_show_mask(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_layer_get_show_mask(self->ID));
+}
+
+static int
+lay_set_show_mask(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete show_mask");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_layer_set_show_mask(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set mask visibility on layer (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+lay_get_preserve_trans(PyGimpLayer *self, void *closure)
+{
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use lock_alpha attribute") < 0)
+ return NULL;
+
+ return lay_get_lock_alpha(self, closure);
+}
+
+static int
+lay_set_preserve_trans(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (PyErr_Warn(PyExc_DeprecationWarning, "use lock_alpha attribute") < 0)
+ return -1;
+
+ return lay_set_lock_alpha(self, value, closure);
+}
+
+static PyGetSetDef lay_getsets[] = {
+ { "is_floating_sel", (getter)lay_get_is_floating_sel, (setter)0 },
+ { "mask", (getter)lay_get_mask, (setter)0 },
+ { "apply_mask", (getter)lay_get_apply_mask, (setter)lay_set_apply_mask },
+ { "edit_mask", (getter)lay_get_edit_mask, (setter)lay_set_edit_mask },
+ { "mode", (getter)lay_get_mode, (setter)lay_set_mode },
+ { "opacity", (getter)lay_get_opacity, (setter)lay_set_opacity },
+ { "lock_alpha", (getter)lay_get_lock_alpha, (setter)lay_set_lock_alpha },
+ { "show_mask", (getter)lay_get_show_mask, (setter)lay_set_show_mask },
+ { "preserve_trans", (getter)lay_get_preserve_trans,
+ (setter)lay_set_preserve_trans },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static PyObject *
+lay_repr(PyGimpLayer *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.Layer '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+lay_init(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ char *name;
+ unsigned int width, height;
+ GimpImageType type = GIMP_RGB_IMAGE;
+ double opacity = 100.0;
+ GimpLayerMode mode = GIMP_LAYER_MODE_NORMAL;
+
+
+ if (!PyArg_ParseTuple(args, "O!sii|idi:gimp.Layer.__init__",
+ &PyGimpImage_Type, &img, &name, &width, &height,
+ &type, &opacity, &mode))
+ return -1;
+
+ self->ID = gimp_layer_new(img->ID, name, width, height,
+ type, opacity, mode);
+
+ self->drawable = NULL;
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error,
+ "could not create %dx%d layer '%s' of type %d on "
+ "image (ID %d)",
+ width, height, name, type, img->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyTypeObject PyGimpLayer_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Layer", /* tp_name */
+ sizeof(PyGimpLayer), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)drw_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)drw_cmp, /* tp_compare */
+ (reprfunc)lay_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ lay_methods, /* tp_methods */
+ 0, /* tp_members */
+ lay_getsets, /* tp_getset */
+ &PyGimpDrawable_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)lay_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_layer_new(gint32 ID)
+{
+ PyGimpLayer *self;
+
+ if (!gimp_item_is_valid(ID) || !gimp_item_is_layer(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+
+ if (gimp_item_is_group(ID)) {
+ self = PyObject_NEW(PyGimpGroupLayer, &PyGimpGroupLayer_Type);
+ }
+ else {
+ self = PyObject_NEW(PyGimpLayer, &PyGimpLayer_Type);
+ }
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+ self->drawable = NULL;
+
+ return (PyObject *)self;
+}
+
+/* End of code for Layer objects */
+/* -------------------------------------------------------- */
+
+/* Since this help will primarily be seen from within
+ * GIMP's Python console, we should make it fit in that
+ * window's default size.
+ */
+
+#define GROUPLAYER_DOC "" \
+"gimp.GroupLayer(img, name="", opacity=100.0, " \
+"mode=gimp.LAYER_MODE_NORMAL)\n" \
+"\n" \
+" Creates a new GroupLayer object that has to be \n" \
+"subsequently added to an image. Use Image.add_layer \n" \
+"or pdb.gimp_image_insert_layer calls to do that. \n" \
+
+static PyMethodDef grouplay_methods[] = {
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+grouplay_get_layers(PyGimpGroupLayer *self, void *closure)
+{
+ gint32 *layers;
+ gint n_layers, i;
+ PyObject *ret;
+
+ layers = gimp_item_get_children(self->ID, &n_layers);
+
+ ret = PyList_New(n_layers);
+
+ for (i = 0; i < n_layers; i++)
+ PyList_SetItem(ret, i, pygimp_group_layer_new(layers[i]));
+
+ g_free(layers);
+
+ return ret;
+}
+
+static PyGetSetDef grouplay_getsets[] = {
+ { "layers", (getter)grouplay_get_layers, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static PyObject *
+grouplay_repr(PyGimpLayer *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.GroupLayer '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+grouplay_init(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ char *name = "Layer Group";
+ GimpImageType type = GIMP_RGB_IMAGE;
+ double opacity = 100.0;
+ GimpLayerMode mode = GIMP_LAYER_MODE_NORMAL;
+
+
+ if (!PyArg_ParseTuple(args, "O!|sdi:gimp.Layer.__init__",
+ &PyGimpImage_Type, &img, &name,
+ &opacity, &mode))
+ return -1;
+
+ self->ID = gimp_layer_group_new(img->ID);
+
+ self->drawable = NULL;
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error,
+ "could not create layer group '%s' of type %d on "
+ "image (ID %d)",
+ name, type, img->ID);
+ return -1;
+ }
+
+ gimp_layer_set_opacity(self->ID, opacity);
+ gimp_layer_set_mode(self->ID, mode);
+
+ gimp_item_set_name(self->ID, name);
+
+ return 0;
+}
+
+PyTypeObject PyGimpGroupLayer_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.GroupLayer", /* tp_name */
+ sizeof(PyGimpGroupLayer), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)drw_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)drw_cmp, /* tp_compare */
+ (reprfunc)grouplay_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ GROUPLAYER_DOC, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ grouplay_methods, /* tp_methods */
+ 0, /* tp_members */
+ grouplay_getsets, /* tp_getset */
+ &PyGimpLayer_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)grouplay_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_group_layer_new(gint32 ID)
+{
+ PyGimpGroupLayer *self;
+
+ if (!gimp_item_is_valid(ID) || !gimp_item_is_layer(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ if (!gimp_item_is_group(ID)) {
+ return pygimp_layer_new(ID);
+ }
+
+ self = PyObject_NEW(PyGimpGroupLayer, &PyGimpGroupLayer_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+ self->drawable = NULL;
+
+ return (PyObject *)self;
+}
+
+/* End of code for GroupLayer objects */
+/* -------------------------------------------------------- */
+
+
+static PyObject *
+chn_copy(PyGimpChannel *self)
+{
+ gint32 id;
+
+ id = gimp_channel_copy(self->ID);
+
+ if (id == -1) {
+ PyErr_Format(pygimp_error,
+ "could not create new channel copy from channel (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ return pygimp_channel_new(id);
+}
+
+static PyObject *
+chn_combine_masks(PyGimpChannel *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpChannel *channel2;
+ GimpChannelOps operation;
+ int offx = 0, offy = 0;
+
+ static char *kwlist[] = { "channel", "operation", "offset_x", "offset_y",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|ii:combine_masks",
+ kwlist,
+ &PyGimpChannel_Type, &channel2,
+ &operation, &offx, &offy))
+ return NULL;
+
+ if (!gimp_channel_combine_masks(self->ID, channel2->ID, operation,
+ offx, offy)) {
+ PyErr_Format(pygimp_error,
+ "could not combine masks with channels (ID %d and ID %d) "
+ "with operation %d, offset %d, %d",
+ self->ID, channel2->ID, operation, offx, offy);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef chn_methods[] = {
+ {"copy", (PyCFunction)chn_copy, METH_NOARGS},
+ {"combine_masks", (PyCFunction)chn_combine_masks, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+chn_get_color(PyGimpChannel *self, void *closure)
+{
+ GimpRGB rgb;
+
+ if (!gimp_channel_get_color(self->ID, &rgb)) {
+ PyErr_Format(pygimp_error,
+ "could not get compositing color of channel (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ return pygimp_rgb_new(&rgb);
+}
+
+static int
+chn_set_color(PyGimpChannel *self, PyObject *value, void *closure)
+{
+ guchar r, g, b;
+ GimpRGB tmprgb, *rgb;
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete color");
+ return -1;
+ }
+
+ if (pygimp_rgb_check(value)) {
+ rgb = pyg_boxed_get(value, GimpRGB);
+ } else if (PyTuple_Check(value) &&
+ PyArg_ParseTuple(value, "(BBB)", &r, &g, &b)) {
+ gimp_rgb_set_uchar(&tmprgb, r, g, b);
+ rgb = &tmprgb;
+ } else {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_channel_set_color(self->ID, rgb)) {
+ PyErr_Format(pygimp_error,
+ "could not set compositing color on channel (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+chn_get_opacity(PyGimpLayer *self, void *closure)
+{
+ return PyFloat_FromDouble(gimp_channel_get_opacity(self->ID));
+}
+
+static int
+chn_set_opacity(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete opacity");
+ return -1;
+ }
+
+ if (!PyFloat_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_channel_set_opacity(self->ID, PyFloat_AsDouble(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set opacity on channel (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+chn_get_show_masked(PyGimpLayer *self, void *closure)
+{
+ return PyBool_FromLong(gimp_channel_get_show_masked(self->ID));
+}
+
+static int
+chn_set_show_masked(PyGimpLayer *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete show_masked");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_channel_set_show_masked(self->ID, PyInt_AsLong(value))) {
+ PyErr_Format(pygimp_error,
+ "could not set composite method on channel (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyGetSetDef chn_getsets[] = {
+ { "color", (getter)chn_get_color, (setter)chn_set_color },
+ { "colour", (getter)chn_get_color, (setter)chn_set_color },
+ { "opacity", (getter)chn_get_opacity, (setter)chn_set_opacity },
+ { "show_masked", (getter)chn_get_show_masked, (setter)chn_set_show_masked},
+ { NULL, (getter)0, (setter)0 }
+};
+
+static PyObject *
+chn_repr(PyGimpChannel *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.Channel '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+chn_init(PyGimpChannel *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ PyObject *color;
+ char *name;
+ unsigned int width, height, r, g, b;
+ double opacity;
+ GimpRGB tmprgb, *rgb;
+
+ if (!PyArg_ParseTuple(args, "O!siidO:gimp.Channel.__init__",
+ &PyGimpImage_Type, &img, &name, &width,
+ &height, &opacity, &color))
+ return -1;
+
+ if (pygimp_rgb_check(color)) {
+ rgb = pyg_boxed_get(color, GimpRGB);
+ } else if (PyTuple_Check(color) &&
+ PyArg_ParseTuple(color, "(BBB)", &r, &g, &b)) {
+ gimp_rgb_set_uchar(&tmprgb, r, g, b);
+ rgb = &tmprgb;
+ } else {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ self->ID = gimp_channel_new(img->ID, name, width, height, opacity, rgb);
+
+ self->drawable = NULL;
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error,
+ "could not create %dx%d channel '%s' on image (ID %d)",
+ width, height, name, img->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyTypeObject PyGimpChannel_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Channel", /* tp_name */
+ sizeof(PyGimpChannel), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)drw_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)drw_cmp, /* tp_compare */
+ (reprfunc)chn_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ chn_methods, /* tp_methods */
+ 0, /* tp_members */
+ chn_getsets, /* tp_getset */
+ &PyGimpDrawable_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)chn_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_channel_new(gint32 ID)
+{
+ PyGimpChannel *self;
+
+ if (!gimp_item_is_valid(ID) || !gimp_item_is_channel(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ self = PyObject_NEW(PyGimpChannel, &PyGimpChannel_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+ self->drawable = NULL;
+
+ return (PyObject *)self;
+}
diff --git a/plug-ins/pygimp/pygimp-image.c b/plug-ins/pygimp/pygimp-image.c
new file mode 100644
index 0000000..d8c0cbf
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-image.c
@@ -0,0 +1,1564 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "pygimp.h"
+
+static PyObject *
+img_add_channel(PyGimpImage *self, PyObject *args)
+{
+ PyGimpChannel *chn;
+ int pos = -1;
+
+ if (!PyArg_ParseTuple(args, "O!|i:add_channel",
+ &PyGimpChannel_Type, &chn, &pos))
+ return NULL;
+
+ if (!gimp_image_insert_channel(self->ID, chn->ID, -1, pos)) {
+ PyErr_Format(pygimp_error,
+ "could not add channel (ID %d) to image (ID %d)",
+ chn->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_insert_channel(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpChannel *chn;
+ PyGimpChannel *parent = NULL;
+ int pos = -1;
+
+ static char *kwlist[] = { "channel", "parent", "position", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|O!i:insert_channel", kwlist,
+ &PyGimpChannel_Type, &chn,
+ &PyGimpChannel_Type, &parent,
+ &pos))
+ return NULL;
+
+ if (!gimp_image_insert_channel(self->ID,
+ chn->ID, parent ? parent->ID : -1, pos)) {
+ PyErr_Format(pygimp_error,
+ "could not insert channel (ID %d) to image (ID %d)",
+ chn->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_add_layer(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+ int pos = -1;
+
+ if (!PyArg_ParseTuple(args, "O!|i:add_layer", &PyGimpLayer_Type, &lay,
+ &pos))
+ return NULL;
+
+ if (!gimp_image_insert_layer(self->ID, lay->ID, -1, pos)) {
+ PyErr_Format(pygimp_error,
+ "could not add layer (ID %d) to image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_insert_layer(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpLayer *lay;
+ PyGimpLayer *parent = NULL;
+ int pos = -1;
+
+ static char *kwlist[] = { "layer", "parent", "position", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|O!i:insert_layer", kwlist,
+ &PyGimpLayer_Type, &lay,
+ &PyGimpLayer_Type, &parent,
+ &pos))
+ return NULL;
+
+ if (!gimp_image_insert_layer(self->ID,
+ lay->ID, parent ? parent->ID : -1, pos)) {
+ PyErr_Format(pygimp_error,
+ "could not insert layer (ID %d) to image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_new_layer(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ char *layer_name;
+ int layer_id;
+ int width, height;
+ int layer_type;
+ int offs_x = 0, offs_y = 0;
+ gboolean alpha = TRUE;
+ int pos = -1;
+ double opacity = 100.0;
+ GimpLayerMode mode = GIMP_LAYER_MODE_NORMAL;
+ GimpFillType fill_mode = -1;
+
+ static char *kwlist[] = { "name", "width", "height", "offset_x", "offset_y",
+ "alpha", "pos", "opacity", "mode", "fill_mode",
+ NULL };
+
+ layer_name = "New Layer";
+
+ width = gimp_image_width(self->ID);
+ height = gimp_image_height(self->ID);
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|siiiiiidii:new_layer", kwlist,
+ &layer_name, &width, &height,
+ &offs_x, &offs_y, &alpha, &pos,
+ &opacity, &mode, &fill_mode))
+ return NULL;
+
+
+ switch (gimp_image_base_type(self->ID)) {
+ case GIMP_RGB:
+ layer_type = alpha ? GIMP_RGBA_IMAGE: GIMP_RGB_IMAGE;
+ break;
+ case GIMP_GRAY:
+ layer_type = alpha ? GIMP_GRAYA_IMAGE: GIMP_GRAY_IMAGE;
+ break;
+ case GIMP_INDEXED:
+ layer_type = alpha ? GIMP_INDEXEDA_IMAGE: GIMP_INDEXED_IMAGE;
+ break;
+ default:
+ PyErr_SetString(pygimp_error, "Unknown image base type");
+ return NULL;
+ }
+
+ if (fill_mode == -1)
+ fill_mode = alpha ? GIMP_FILL_TRANSPARENT: GIMP_FILL_BACKGROUND;
+
+
+ layer_id = gimp_layer_new(self->ID, layer_name, width, height,
+ layer_type, opacity, mode);
+
+ if (!layer_id) {
+ PyErr_Format(pygimp_error,
+ "could not create new layer in image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ if (!gimp_drawable_fill(layer_id, fill_mode)) {
+ gimp_item_delete(layer_id);
+ PyErr_Format(pygimp_error,
+ "could not fill new layer with fill mode %d",
+ fill_mode);
+ return NULL;
+ }
+
+ if (!gimp_image_insert_layer(self->ID, layer_id, -1, pos)) {
+ gimp_item_delete(layer_id);
+ PyErr_Format(pygimp_error,
+ "could not add layer (ID %d) to image (ID %d)",
+ layer_id, self->ID);
+ return NULL;
+ }
+
+ if (!gimp_layer_set_offsets(layer_id, offs_x, offs_y)) {
+ gimp_image_remove_layer(self->ID, layer_id);
+ PyErr_Format(pygimp_error,
+ "could not set offset %d, %d on layer (ID %d)",
+ offs_x, offs_y, layer_id);
+ return NULL;
+ }
+
+ return pygimp_group_layer_new(layer_id);
+}
+
+
+static PyObject *
+img_clean_all(PyGimpImage *self)
+{
+ if (!gimp_image_clean_all(self->ID)) {
+ PyErr_Format(pygimp_error, "could not clean all on image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_disable_undo(PyGimpImage *self)
+{
+ return PyBool_FromLong(gimp_image_undo_disable(self->ID));
+}
+
+static PyObject *
+img_enable_undo(PyGimpImage *self)
+{
+ return PyBool_FromLong(gimp_image_undo_enable(self->ID));
+}
+
+static PyObject *
+img_flatten(PyGimpImage *self)
+{
+ return pygimp_group_layer_new(gimp_image_flatten(self->ID));
+}
+
+static PyObject *
+img_lower_channel(PyGimpImage *self, PyObject *args)
+{
+ PyGimpChannel *chn;
+
+ if (!PyArg_ParseTuple(args, "O!:lower_channel", &PyGimpChannel_Type, &chn))
+ return NULL;
+
+ if (!gimp_image_lower_item(self->ID, chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not lower channel (ID %d) on image (ID %d)",
+ chn->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_lower_layer(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+
+ if (!PyArg_ParseTuple(args, "O!:lower_layer", &PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_image_lower_item(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not lower layer (ID %d) on image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_lower_layer_to_bottom(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+
+ if (!PyArg_ParseTuple(args, "O!:lower_layer_to_bottom",
+ &PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_image_lower_item_to_bottom(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not lower layer (ID %d) to bottom on image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_merge_visible_layers(PyGimpImage *self, PyObject *args)
+{
+ gint32 id;
+ int merge;
+
+ if (!PyArg_ParseTuple(args, "i:merge_visible_layers", &merge))
+ return NULL;
+
+ id = gimp_image_merge_visible_layers(self->ID, merge);
+
+ if (id == -1) {
+ PyErr_Format(pygimp_error,
+ "could not merge visible layers on image (ID %d) "
+ "with merge type %d",
+ self->ID, merge);
+ return NULL;
+ }
+
+ return pygimp_group_layer_new(id);
+}
+
+static PyObject *
+img_merge_down(PyGimpImage *self, PyObject *args)
+{
+ gint32 id;
+ PyGimpLayer *layer;
+ int merge;
+
+ if (!PyArg_ParseTuple(args, "O!i:merge_down",
+ &PyGimpLayer_Type, &layer, &merge))
+ return NULL;
+
+ id = gimp_image_merge_down(self->ID, layer->ID, merge);
+
+ if (id == -1) {
+ PyErr_Format(pygimp_error,
+ "could not merge down layer (ID %d) on image (ID %d) "
+ "with merge type %d",
+ layer->ID, self->ID, merge);
+ return NULL;
+ }
+
+ return pygimp_group_layer_new(id);
+}
+
+static PyObject *
+img_pick_correlate_layer(PyGimpImage *self, PyObject *args)
+{
+ int x,y;
+ gint32 id;
+
+ if (!PyArg_ParseTuple(args, "ii:pick_correlate_layer", &x, &y))
+ return NULL;
+
+ id = gimp_image_pick_correlate_layer(self->ID, x, y);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_group_layer_new(id);
+}
+
+static PyObject *
+img_raise_channel(PyGimpImage *self, PyObject *args)
+{
+ PyGimpChannel *chn;
+
+ if (!PyArg_ParseTuple(args, "O!:raise_channel", &PyGimpChannel_Type, &chn))
+ return NULL;
+
+ if (!gimp_image_raise_item(self->ID, chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not raise channel (ID %d) on image (ID %d)",
+ chn->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_raise_layer(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+
+ if (!PyArg_ParseTuple(args, "O!:raise_layer", &PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_image_raise_item(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not raise layer (ID %d) on image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_raise_layer_to_top(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+
+ if (!PyArg_ParseTuple(args, "O!:raise_layer_to_top",
+ &PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_image_raise_item_to_top(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not raise layer (ID %d) to top on image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_remove_channel(PyGimpImage *self, PyObject *args)
+{
+ PyGimpChannel *chn;
+
+ if (!PyArg_ParseTuple(args, "O!:remove_channel", &PyGimpChannel_Type, &chn))
+ return NULL;
+
+ if (!gimp_image_remove_channel(self->ID, chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not remove channel (ID %d) from image (ID %d)",
+ chn->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_remove_layer(PyGimpImage *self, PyObject *args)
+{
+ PyGimpLayer *lay;
+
+ if (!PyArg_ParseTuple(args, "O!:remove_layer", &PyGimpLayer_Type, &lay))
+ return NULL;
+
+ if (!gimp_image_remove_layer(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not remove layer (ID %d) from image (ID %d)",
+ lay->ID, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_resize(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ int new_w, new_h;
+ int offs_x = 0, offs_y = 0;
+
+ static char *kwlist[] = { "width", "height", "offset_x", "offset_y",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii:resize", kwlist,
+ &new_w, &new_h, &offs_x, &offs_y))
+ return NULL;
+
+ if (!gimp_image_resize(self->ID, new_w, new_h, offs_x, offs_y)) {
+ PyErr_Format(pygimp_error,
+ "could not resize image (ID %d) to %dx%d, offset %d, %d",
+ self->ID, new_w, new_h, offs_x, offs_y);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_resize_to_layers(PyGimpImage *self)
+{
+ if (!gimp_image_resize_to_layers(self->ID)) {
+ PyErr_Format(pygimp_error, "could not resize to layers on image "
+ "(ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_scale(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ int new_width, new_height;
+ int interpolation = -1;
+
+ static char *kwlist[] = { "width", "height", "interpolation", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|i:scale", kwlist,
+ &new_width, &new_height, &interpolation))
+ return NULL;
+
+ if (interpolation != -1) {
+ gimp_context_push();
+ gimp_context_set_interpolation(interpolation);
+ }
+
+ if (!gimp_image_scale(self->ID, new_width, new_height)) {
+ PyErr_Format(pygimp_error, "could not scale image (ID %d) to %dx%d",
+ self->ID, new_width, new_height);
+ if (interpolation != -1) {
+ gimp_context_pop();
+ }
+ return NULL;
+ }
+
+ if (interpolation != -1) {
+ gimp_context_pop();
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_crop(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ int new_w, new_h;
+ int offs_x = 0, offs_y = 0;
+
+ static char *kwlist[] = { "width", "height", "offset_x", "offset_y",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii:crop", kwlist,
+ &new_w, &new_h, &offs_x, &offs_y))
+ return NULL;
+
+ if (!gimp_image_crop(self->ID, new_w, new_h, offs_x, offs_y)) {
+ PyErr_Format(pygimp_error,
+ "could not crop image (ID %d) to %dx%d, offset %d, %d",
+ self->ID, new_w, new_h, offs_x, offs_y);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_free_shadow(PyGimpImage *self)
+{
+ /* this procedure is deprecated and does absolutely nothing */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_unset_active_channel(PyGimpImage *self)
+{
+ if (!gimp_image_unset_active_channel(self->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not unset active channel on image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_get_component_active(PyGimpImage *self, PyObject *args)
+{
+ int comp;
+
+ if (!PyArg_ParseTuple(args, "i:get_component_active", &comp))
+ return NULL;
+
+ return PyBool_FromLong(gimp_image_get_component_active(self->ID, comp));
+}
+
+
+static PyObject *
+img_get_component_visible(PyGimpImage *self, PyObject *args)
+{
+ int comp;
+
+ if (!PyArg_ParseTuple(args, "i:get_component_visible", &comp))
+ return NULL;
+
+ return PyBool_FromLong(gimp_image_get_component_visible(self->ID, comp));
+}
+
+
+static PyObject *
+img_set_component_active(PyGimpImage *self, PyObject *args)
+{
+ int comp, a;
+
+ if (!PyArg_ParseTuple(args, "ii:set_component_active", &comp, &a))
+ return NULL;
+
+ if (!gimp_image_set_component_active(self->ID, comp, a)) {
+ PyErr_Format(pygimp_error,
+ "could not set component (%d) %sactive on image (ID %d)",
+ comp, a ? "" : "in", self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_set_component_visible(PyGimpImage *self, PyObject *args)
+{
+ int comp, v;
+
+ if (!PyArg_ParseTuple(args, "ii:set_component_visible", &comp, &v))
+ return NULL;
+
+ if (!gimp_image_set_component_visible(self->ID, comp, v)) {
+ PyErr_Format(pygimp_error,
+ "could not set component (%d) %svisible on image (ID %d)",
+ comp, v ? "" : "in", self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_parasite_find(PyGimpImage *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_find", &name))
+ return NULL;
+
+ return pygimp_parasite_new (gimp_image_get_parasite (self->ID, name));
+}
+
+static PyObject *
+img_parasite_attach(PyGimpImage *self, PyObject *args)
+{
+ PyGimpParasite *parasite;
+
+ if (!PyArg_ParseTuple(args, "O!:parasite_attach", &PyGimpParasite_Type,
+ &parasite))
+ return NULL;
+
+ if (! gimp_image_attach_parasite (self->ID, parasite->para)) {
+ PyErr_Format(pygimp_error,
+ "could not attach parasite '%s' to image (ID %d)",
+ parasite->para->name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_attach_new_parasite(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int flags, size;
+ guint8 *data;
+ GimpParasite *parasite;
+ gboolean success;
+
+ static char *kwlist[] = { "name", "flags", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "sis#:attach_new_parasite", kwlist,
+ &name, &flags, &data, &size))
+ return NULL;
+
+ parasite = gimp_parasite_new (name, flags, size, data);
+ success = gimp_image_attach_parasite (self->ID, parasite);
+ gimp_parasite_free (parasite);
+
+ if (!success) {
+ PyErr_Format(pygimp_error,
+ "could not attach new parasite '%s' to image (ID %d)",
+ name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_parasite_detach(PyGimpImage *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_detach", &name))
+ return NULL;
+
+ if (!gimp_image_detach_parasite (self->ID, name)) {
+ PyErr_Format(pygimp_error,
+ "could not detach parasite '%s' from image (ID %d)",
+ name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_parasite_list(PyGimpImage *self)
+{
+ gint num_parasites;
+ gchar **parasites;
+ PyObject *ret;
+ gint i;
+
+ parasites = gimp_image_get_parasite_list (self->ID, &num_parasites);
+
+ ret = PyTuple_New(num_parasites);
+
+ for (i = 0; i < num_parasites; i++)
+ PyTuple_SetItem(ret, i, PyString_FromString(parasites[i]));
+
+ g_strfreev(parasites);
+ return ret;
+}
+
+static PyObject *
+img_get_layer_by_tattoo(PyGimpImage *self, PyObject *args)
+{
+ int tattoo;
+
+ if (!PyArg_ParseTuple(args, "i:get_layer_by_tattoo", &tattoo))
+ return NULL;
+
+ return pygimp_group_layer_new(gimp_image_get_layer_by_tattoo(self->ID, tattoo));
+}
+
+static PyObject *
+img_get_channel_by_tattoo(PyGimpImage *self, PyObject *args)
+{
+ int tattoo;
+
+ if (!PyArg_ParseTuple(args, "i:get_channel_by_tattoo", &tattoo))
+ return NULL;
+
+ return pygimp_channel_new(gimp_image_get_channel_by_tattoo(self->ID,
+ tattoo));
+}
+
+static PyObject *
+img_add_hguide(PyGimpImage *self, PyObject *args)
+{
+ int ypos;
+
+ if (!PyArg_ParseTuple(args, "i:add_hguide", &ypos))
+ return NULL;
+
+ return PyInt_FromLong(gimp_image_add_hguide(self->ID, ypos));
+}
+
+static PyObject *
+img_add_vguide(PyGimpImage *self, PyObject *args)
+{
+ int xpos;
+
+ if (!PyArg_ParseTuple(args, "i:add_vguide", &xpos))
+ return NULL;
+
+ return PyInt_FromLong(gimp_image_add_vguide(self->ID, xpos));
+}
+
+static PyObject *
+img_delete_guide(PyGimpImage *self, PyObject *args)
+{
+ int guide;
+
+ if (!PyArg_ParseTuple(args, "i:delete_guide", &guide))
+ return NULL;
+
+ if (!gimp_image_delete_guide(self->ID, guide)) {
+ PyErr_Format(pygimp_error,
+ "could not delete guide (ID %d) from image (ID %d)",
+ guide, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_find_next_guide(PyGimpImage *self, PyObject *args)
+{
+ int guide;
+
+ if (!PyArg_ParseTuple(args, "i:find_next_guide", &guide))
+ return NULL;
+
+ return PyInt_FromLong(gimp_image_find_next_guide(self->ID, guide));
+}
+
+static PyObject *
+img_get_guide_orientation(PyGimpImage *self, PyObject *args)
+{
+ int guide;
+
+ if (!PyArg_ParseTuple(args, "i:get_guide_orientation", &guide))
+ return NULL;
+
+ return PyInt_FromLong(gimp_image_get_guide_orientation(self->ID, guide));
+}
+
+static PyObject *
+img_get_guide_position(PyGimpImage *self, PyObject *args)
+{
+ int guide;
+
+ if (!PyArg_ParseTuple(args, "i:get_guide_position", &guide))
+ return NULL;
+
+ return PyInt_FromLong(gimp_image_get_guide_position(self->ID, guide));
+}
+
+static PyObject *
+img_undo_is_enabled(PyGimpImage *self)
+{
+ return PyBool_FromLong(gimp_image_undo_is_enabled(self->ID));
+}
+
+static PyObject *
+img_undo_freeze(PyGimpImage *self)
+{
+ return PyBool_FromLong(gimp_image_undo_freeze(self->ID));
+}
+
+static PyObject *
+img_undo_thaw(PyGimpImage *self)
+{
+ return PyBool_FromLong(gimp_image_undo_thaw(self->ID));
+}
+
+static PyObject *
+img_duplicate(PyGimpImage *self)
+{
+ return pygimp_image_new(gimp_image_duplicate(self->ID));
+}
+
+static PyObject *
+img_undo_group_start(PyGimpImage *self)
+{
+ if (!gimp_image_undo_group_start(self->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not start undo group on image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_undo_group_end(PyGimpImage *self)
+{
+ if (!gimp_image_undo_group_end(self->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not end undo group on image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef img_methods[] = {
+ {"add_channel", (PyCFunction)img_add_channel, METH_VARARGS},
+ {"insert_channel", (PyCFunction)img_insert_channel, METH_VARARGS | METH_KEYWORDS},
+ {"add_layer", (PyCFunction)img_add_layer, METH_VARARGS},
+ {"insert_layer", (PyCFunction)img_insert_layer, METH_VARARGS | METH_KEYWORDS},
+ {"new_layer", (PyCFunction)img_new_layer, METH_VARARGS | METH_KEYWORDS},
+ {"clean_all", (PyCFunction)img_clean_all, METH_NOARGS},
+ {"disable_undo", (PyCFunction)img_disable_undo, METH_NOARGS},
+ {"enable_undo", (PyCFunction)img_enable_undo, METH_NOARGS},
+ {"flatten", (PyCFunction)img_flatten, METH_NOARGS},
+ {"lower_channel", (PyCFunction)img_lower_channel, METH_VARARGS},
+ {"lower_layer", (PyCFunction)img_lower_layer, METH_VARARGS},
+ {"lower_layer_to_bottom", (PyCFunction)img_lower_layer_to_bottom, METH_VARARGS},
+ {"merge_visible_layers", (PyCFunction)img_merge_visible_layers, METH_VARARGS},
+ {"merge_down", (PyCFunction)img_merge_down, METH_VARARGS},
+ {"pick_correlate_layer", (PyCFunction)img_pick_correlate_layer, METH_VARARGS},
+ {"raise_channel", (PyCFunction)img_raise_channel, METH_VARARGS},
+ {"raise_layer", (PyCFunction)img_raise_layer, METH_VARARGS},
+ {"raise_layer_to_top", (PyCFunction)img_raise_layer_to_top, METH_VARARGS},
+ {"remove_channel", (PyCFunction)img_remove_channel, METH_VARARGS},
+ {"remove_layer", (PyCFunction)img_remove_layer, METH_VARARGS},
+ {"resize", (PyCFunction)img_resize, METH_VARARGS | METH_KEYWORDS},
+ {"resize_to_layers", (PyCFunction)img_resize_to_layers, METH_NOARGS},
+ {"get_component_active", (PyCFunction)img_get_component_active, METH_VARARGS},
+ {"get_component_visible", (PyCFunction)img_get_component_visible, METH_VARARGS},
+ {"set_component_active", (PyCFunction)img_set_component_active, METH_VARARGS},
+ {"set_component_visible", (PyCFunction)img_set_component_visible, METH_VARARGS},
+ {"parasite_find", (PyCFunction)img_parasite_find, METH_VARARGS},
+ {"parasite_attach", (PyCFunction)img_parasite_attach, METH_VARARGS},
+ {"attach_new_parasite", (PyCFunction)img_attach_new_parasite, METH_VARARGS | METH_KEYWORDS},
+ {"parasite_detach", (PyCFunction)img_parasite_detach, METH_VARARGS},
+ {"parasite_list", (PyCFunction)img_parasite_list, METH_NOARGS},
+ {"get_layer_by_tattoo",(PyCFunction)img_get_layer_by_tattoo,METH_VARARGS},
+ {"get_channel_by_tattoo",(PyCFunction)img_get_channel_by_tattoo,METH_VARARGS},
+ {"add_hguide", (PyCFunction)img_add_hguide, METH_VARARGS},
+ {"add_vguide", (PyCFunction)img_add_vguide, METH_VARARGS},
+ {"delete_guide", (PyCFunction)img_delete_guide, METH_VARARGS},
+ {"find_next_guide", (PyCFunction)img_find_next_guide, METH_VARARGS},
+ {"get_guide_orientation",(PyCFunction)img_get_guide_orientation,METH_VARARGS},
+ {"get_guide_position", (PyCFunction)img_get_guide_position, METH_VARARGS},
+ {"scale", (PyCFunction)img_scale, METH_VARARGS | METH_KEYWORDS},
+ {"crop", (PyCFunction)img_crop, METH_VARARGS | METH_KEYWORDS},
+ {"free_shadow", (PyCFunction)img_free_shadow, METH_NOARGS},
+ {"unset_active_channel", (PyCFunction)img_unset_active_channel, METH_NOARGS},
+ {"undo_is_enabled", (PyCFunction)img_undo_is_enabled, METH_NOARGS},
+ {"undo_freeze", (PyCFunction)img_undo_freeze, METH_NOARGS},
+ {"undo_thaw", (PyCFunction)img_undo_thaw, METH_NOARGS},
+ {"duplicate", (PyCFunction)img_duplicate, METH_NOARGS},
+ {"undo_group_start", (PyCFunction)img_undo_group_start, METH_NOARGS},
+ {"undo_group_end", (PyCFunction)img_undo_group_end, METH_NOARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+img_get_ID(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(self->ID);
+}
+
+static PyObject *
+img_get_active_channel(PyGimpImage *self, void *closure)
+{
+ gint32 id = gimp_image_get_active_channel(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_channel_new(id);
+}
+
+static int
+img_set_active_channel(PyGimpImage *self, PyObject *value, void *closure)
+{
+ PyGimpChannel *chn;
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete active_channel");
+ return -1;
+ }
+
+ if (!pygimp_channel_check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ chn = (PyGimpChannel *)value;
+
+ if (!gimp_image_set_active_channel(self->ID, chn->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not set active channel (ID %d) on image (ID %d)",
+ chn->ID, self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_active_drawable(PyGimpImage *self, void *closure)
+{
+ gint32 id = gimp_image_get_active_drawable(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_drawable_new(NULL, id);
+}
+
+static PyObject *
+img_get_active_layer(PyGimpImage *self, void *closure)
+{
+ gint32 id = gimp_image_get_active_layer(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_group_layer_new(id);
+}
+
+static int
+img_set_active_layer(PyGimpImage *self, PyObject *value, void *closure)
+{
+ PyGimpLayer *lay;
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete active_layer");
+ return -1;
+ }
+
+ if (!pygimp_layer_check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ lay = (PyGimpLayer *)value;
+
+ if (!gimp_image_set_active_layer(self->ID, lay->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not set active layer (ID %d) on image (ID %d)",
+ lay->ID, self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_base_type(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_base_type(self->ID));
+}
+
+static PyObject *
+img_get_channels(PyGimpImage *self, void *closure)
+{
+ gint32 *channels;
+ gint n_channels, i;
+ PyObject *ret;
+
+ channels = gimp_image_get_channels(self->ID, &n_channels);
+
+ ret = PyList_New(n_channels);
+
+ for (i = 0; i < n_channels; i++)
+ PyList_SetItem(ret, i, pygimp_channel_new(channels[i]));
+
+ g_free(channels);
+
+ return ret;
+}
+
+static PyObject *
+img_get_colormap(PyGimpImage *self, void *closure)
+{
+ guchar *cmap;
+ gint n_colours;
+ PyObject *ret;
+
+ cmap = gimp_image_get_colormap(self->ID, &n_colours);
+
+ if (cmap == NULL) {
+ PyErr_Format(pygimp_error, "could not get colormap for image (ID %d)",
+ self->ID);
+ return NULL;
+ }
+
+ ret = PyString_FromStringAndSize((char *)cmap, n_colours * 3);
+ g_free(cmap);
+
+ return ret;
+}
+
+static int
+img_set_colormap(PyGimpImage *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete colormap");
+ return -1;
+ }
+
+ if (!PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_image_set_colormap(self->ID, (guchar *)PyString_AsString(value),
+ PyString_Size(value) / 3)) {
+ PyErr_Format(pygimp_error, "could not set colormap on image (ID %d)",
+ self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_is_dirty(PyGimpImage *self, void *closure)
+{
+ return PyBool_FromLong(gimp_image_is_dirty(self->ID));
+}
+
+static PyObject *
+img_get_filename(PyGimpImage *self, void *closure)
+{
+ gchar *filename;
+
+ filename = gimp_image_get_filename(self->ID);
+
+ if (filename) {
+ PyObject *ret = PyString_FromString(filename);
+ g_free(filename);
+ return ret;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int
+img_set_filename(PyGimpImage *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete filename");
+ return -1;
+ }
+
+ if (!PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_image_set_filename(self->ID, PyString_AsString(value))) {
+ PyErr_SetString(PyExc_TypeError, "could not set filename "
+ "(possibly bad encoding)");
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_uri(PyGimpImage *self, void *closure)
+{
+ gchar *uri;
+
+ uri = gimp_image_get_uri(self->ID);
+
+ if (uri) {
+ PyObject *ret = PyString_FromString(uri);
+ g_free(uri);
+ return ret;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_get_floating_selection(PyGimpImage *self, void *closure)
+{
+ gint32 id;
+
+ id = gimp_image_get_floating_sel(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_layer_new(id);
+}
+
+static PyObject *
+img_get_floating_sel_attached_to(PyGimpImage *self, void *closure)
+{
+ gint32 id;
+
+ id = gimp_image_floating_sel_attached_to(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_layer_new(id);
+}
+
+static PyObject *
+img_get_layers(PyGimpImage *self, void *closure)
+{
+ gint32 *layers;
+ gint n_layers, i;
+ PyObject *ret;
+
+ layers = gimp_image_get_layers(self->ID, &n_layers);
+
+ ret = PyList_New(n_layers);
+
+ for (i = 0; i < n_layers; i++)
+ PyList_SetItem(ret, i, pygimp_group_layer_new(layers[i]));
+
+ g_free(layers);
+
+ return ret;
+}
+
+static PyObject *
+img_get_name(PyGimpImage *self, void *closure)
+{
+ gchar *name;
+
+ name = gimp_image_get_name(self->ID);
+
+ if (name) {
+ PyObject *ret = PyString_FromString(name);
+ g_free(name);
+ return ret;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+img_get_selection(PyGimpImage *self, void *closure)
+{
+ return pygimp_channel_new(gimp_image_get_selection(self->ID));
+}
+
+static PyObject *
+img_get_tattoo_state(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_get_tattoo_state(self->ID));
+}
+
+static int
+img_set_tattoo_state(PyGimpImage *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete tattoo_state");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_image_set_tattoo_state(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+img_get_height(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_height(self->ID));
+}
+
+static PyObject *
+img_get_width(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_width(self->ID));
+}
+
+
+static PyObject *
+img_get_precision(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_get_precision(self->ID));
+}
+
+static PyObject *
+img_get_resolution(PyGimpImage *self, void *closure)
+{
+ double xres, yres;
+
+ gimp_image_get_resolution(self->ID, &xres, &yres);
+
+ return Py_BuildValue("(dd)", xres, yres);
+}
+
+static int
+img_set_resolution(PyGimpImage *self, PyObject *value, void *closure)
+{
+ gdouble xres, yres;
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete resolution");
+ return -1;
+ }
+
+ if (!PySequence_Check(value) ||
+ !PyArg_ParseTuple(value, "dd", &xres, &yres)) {
+ PyErr_Clear();
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_image_set_resolution(self->ID, xres, yres)) {
+ PyErr_SetString(PyExc_TypeError, "could not set resolution");
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_unit(PyGimpImage *self, void *closure)
+{
+ return PyInt_FromLong(gimp_image_get_unit(self->ID));
+}
+
+static int
+img_set_unit(PyGimpImage *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete unit");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ if (!gimp_image_set_unit(self->ID, PyInt_AsLong(value))) {
+ PyErr_SetString(PyExc_TypeError, "could not set unit");
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+img_get_vectors(PyGimpImage *self, void *closure)
+{
+ int *vectors;
+ int i, num_vectors;
+ PyObject *ret;
+
+ vectors = gimp_image_get_vectors(self->ID, &num_vectors);
+
+ ret = PyList_New(num_vectors);
+
+ for (i = 0; i < num_vectors; i++)
+ PyList_SetItem(ret, i, pygimp_vectors_new(vectors[i]));
+
+ g_free(vectors);
+
+ return ret;
+}
+
+static PyObject *
+img_get_active_vectors(PyGimpImage *self, void *closure)
+{
+ gint32 id = gimp_image_get_active_vectors(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_vectors_new(id);
+}
+
+static int
+img_set_active_vectors(PyGimpImage *self, PyObject *value, void *closure)
+{
+ PyGimpVectors *vtr;
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete active_vectors");
+ return -1;
+ }
+
+ if (!pygimp_vectors_check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ vtr = (PyGimpVectors *)value;
+
+ if (!gimp_image_set_active_vectors(self->ID, vtr->ID)) {
+ PyErr_Format(pygimp_error,
+ "could not set active vectors (ID %d) on image (ID %d)",
+ vtr->ID, self->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyGetSetDef img_getsets[] = {
+ { "ID", (getter)img_get_ID, (setter)0 },
+ { "active_channel", (getter)img_get_active_channel,
+ (setter)img_set_active_channel },
+ { "active_drawable", (getter)img_get_active_drawable, (setter)0 },
+ { "active_layer", (getter)img_get_active_layer,
+ (setter)img_set_active_layer },
+ { "active_vectors", (getter)img_get_active_vectors,
+ (setter)img_set_active_vectors},
+ { "base_type", (getter)img_get_base_type, (setter)0 },
+ { "channels", (getter)img_get_channels, (setter)0 },
+ { "colormap", (getter)img_get_colormap, (setter)img_set_colormap },
+ { "dirty", (getter)img_get_is_dirty, (setter)0 },
+ { "filename", (getter)img_get_filename, (setter)img_set_filename },
+ { "floating_selection", (getter)img_get_floating_selection, (setter)0 },
+ { "floating_sel_attached_to", (getter)img_get_floating_sel_attached_to,
+ (setter)0 },
+ { "height", (getter)img_get_height, (setter)0 },
+ { "layers", (getter)img_get_layers, (setter)0 },
+ { "name", (getter)img_get_name, (setter)0 },
+ { "precision", (getter)img_get_precision, (setter)0 },
+ { "resolution", (getter)img_get_resolution, (setter)img_set_resolution },
+ { "selection", (getter)img_get_selection, (setter)0 },
+ { "tattoo_state", (getter)img_get_tattoo_state,
+ (setter)img_set_tattoo_state },
+ { "unit", (getter)img_get_unit, (setter)img_set_unit },
+ { "uri", (getter)img_get_uri, (setter)0 },
+ { "vectors", (getter)img_get_vectors, (setter)0 },
+ { "width", (getter)img_get_width, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+/* ---------- */
+
+
+PyObject *
+pygimp_image_new(gint32 ID)
+{
+ PyGimpImage *self;
+
+ if (!gimp_image_is_valid(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ self = PyObject_NEW(PyGimpImage, &PyGimpImage_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+
+ return (PyObject *)self;
+}
+
+
+static void
+img_dealloc(PyGimpImage *self)
+{
+ /* XXXX Add your own cleanup code here */
+ PyObject_DEL(self);
+}
+
+static PyObject *
+img_repr(PyGimpImage *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_image_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.Image '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+img_cmp(PyGimpImage *self, PyGimpImage *other)
+{
+ if (self->ID == other->ID)
+ return 0;
+
+ if (self->ID > other->ID)
+ return -1;
+
+ return 1;
+}
+
+static int
+img_init(PyGimpImage *self, PyObject *args, PyObject *kwargs)
+{
+ guint width, height;
+ GimpImageBaseType type = GIMP_RGB;
+
+ static char *kwlist[] = { "width", "height", "type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ii|i:gimp.Image.__init__", kwlist,
+ &width, &height, &type))
+ return -1;
+
+ self->ID = gimp_image_new(width, height, type);
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error,
+ "could not create image (width: %d, height: %d, type: %d)",
+ width, height, type);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyTypeObject PyGimpImage_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Image", /* tp_name */
+ sizeof(PyGimpImage), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)img_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)img_cmp, /* tp_compare */
+ (reprfunc)img_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ img_methods, /* tp_methods */
+ 0, /* tp_members */
+ img_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)img_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
diff --git a/plug-ins/pygimp/pygimp-intl.h b/plug-ins/pygimp/pygimp-intl.h
new file mode 100644
index 0000000..ef6fdf2
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-intl.h
@@ -0,0 +1,48 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * pygimp-intl.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PYGIMP_INTL_H__
+#define __PYGIMP_INTL_H__
+
+#ifndef GETTEXT_PACKAGE
+#error "config.h must be included prior to pygimp-intl.h"
+#endif
+
+#include <libintl.h>
+
+
+#define _(String) dgettext (GETTEXT_PACKAGE "-python", String)
+#define Q_(String) g_dpgettext (GETTEXT_PACKAGE "-python", String, 0)
+#define C_(Context,String) g_dpgettext (GETTEXT_PACKAGE "-python", Context "\004" String, strlen (Context) + 1)
+
+#undef gettext
+#define gettext(String) dgettext (GETTEXT_PACKAGE "-python", String)
+
+#undef ngettext
+#define ngettext(String1, String2, number) dngettext (GETTEXT_PACKAGE "-python", String1, String2, number)
+
+#ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+#else
+# define N_(String) (String)
+#endif
+
+
+#endif /* __PYGIMP_INTL_H__ */
diff --git a/plug-ins/pygimp/pygimp-item.c b/plug-ins/pygimp/pygimp-item.c
new file mode 100644
index 0000000..b3b0883
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-item.c
@@ -0,0 +1,181 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include "pygimp.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+#include <glib-object.h>
+
+static PyObject *
+item_from_id(PyObject *not_used, PyObject *args)
+{
+ gint32 ID;
+
+ if (!PyArg_ParseTuple(args, "i", &ID))
+ return NULL;
+ return pygimp_item_new(ID);
+}
+
+static PyMethodDef item_methods[] = {
+ {"from_id", (PyCFunction)item_from_id, METH_VARARGS | METH_STATIC},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+item_get_parent(PyGimpLayer *self, void *closure)
+{
+ gint32 id = gimp_item_get_parent(self->ID);
+
+ if (id == -1) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return pygimp_item_new(id);
+}
+
+static PyObject *
+item_get_children(PyGimpLayer *self, void *closure)
+{
+ gint32 *children;
+ gint n_children, i;
+ PyObject *ret;
+
+ children = gimp_item_get_children(self->ID, &n_children);
+
+ ret = PyList_New(n_children);
+
+ for (i = 0; i < n_children; i++)
+ PyList_SetItem(ret, i, pygimp_item_new(children[i]));
+
+ g_free(children);
+
+ return ret;
+}
+
+static PyGetSetDef item_getsets[] = {
+ { "parent", (getter)item_get_parent, (setter)0 },
+ { "children", (getter) item_get_children, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+
+static void
+item_dealloc(PyGimpItem *self)
+{
+ PyObject_DEL(self);
+}
+
+static PyObject *
+item_repr(PyGimpItem *self)
+{
+ PyObject *s;
+
+ s = PyString_FromFormat("<gimp.Item '%d'>", self->ID);
+
+ return s;
+}
+
+static int
+item_init(PyGimpLayer *self, PyObject *args, PyObject *kwargs)
+{
+ return -1;
+}
+
+
+
+PyTypeObject PyGimpItem_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Item", /* tp_name */
+ sizeof(PyGimpItem), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)item_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)item_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ item_methods, /* tp_methods */
+ 0, /* tp_members */
+ item_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)item_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+
+PyObject *
+pygimp_item_new(gint32 ID)
+{
+ PyObject *self;
+
+ if (!gimp_item_is_valid(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ /* create the appropriate object type */
+ if (gimp_item_is_drawable(ID)) {
+ if (gimp_item_is_group(ID)) {
+ self = pygimp_group_layer_new(ID);
+ }
+ else {
+ self = pygimp_drawable_new(NULL, ID);
+ }
+ }
+ else /* Vectors */
+ self = pygimp_vectors_new(ID);
+
+ if (self == NULL)
+ return NULL;
+
+ return self;
+}
diff --git a/plug-ins/pygimp/pygimp-logo.png b/plug-ins/pygimp/pygimp-logo.png
new file mode 100644
index 0000000..6f7ef4d
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-logo.png
Binary files differ
diff --git a/plug-ins/pygimp/pygimp-parasite.c b/plug-ins/pygimp/pygimp-parasite.c
new file mode 100644
index 0000000..279cfbd
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-parasite.c
@@ -0,0 +1,216 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "pygimp.h"
+
+static PyObject *
+para_copy(PyGimpParasite *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":copy"))
+ return NULL;
+
+ return pygimp_parasite_new(gimp_parasite_copy(self->para));
+}
+
+static PyObject *
+para_is_type(PyGimpParasite *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:is_type", &name))
+ return NULL;
+
+ return PyInt_FromLong(gimp_parasite_is_type(self->para, name));
+}
+
+static PyObject *
+para_has_flag(PyGimpParasite *self, PyObject *args)
+{
+ int flag;
+
+ if (!PyArg_ParseTuple(args, "i:has_flag", &flag))
+ return NULL;
+
+ return PyInt_FromLong(gimp_parasite_has_flag(self->para, flag));
+}
+
+
+static PyMethodDef para_methods[] = {
+ {"copy", (PyCFunction)para_copy, METH_VARARGS},
+ {"is_type", (PyCFunction)para_is_type, METH_VARARGS},
+ {"has_flag",(PyCFunction)para_has_flag, METH_VARARGS},
+
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+para_get_is_persistent(PyGimpParasite *self, void *closure)
+{
+ return PyBool_FromLong(gimp_parasite_is_persistent(self->para));
+}
+
+static PyObject *
+para_get_is_undoable(PyGimpParasite *self, void *closure)
+{
+ return PyBool_FromLong(gimp_parasite_is_undoable(self->para));
+}
+
+static PyObject *
+para_get_flags(PyGimpParasite *self, void *closure)
+{
+ return PyInt_FromLong(gimp_parasite_flags(self->para));
+}
+
+static PyObject *
+para_get_name(PyGimpParasite *self, void *closure)
+{
+ return PyString_FromString(gimp_parasite_name(self->para));
+}
+
+static PyObject *
+para_get_data(PyGimpParasite *self, void *closure)
+{
+ return PyString_FromStringAndSize(gimp_parasite_data(self->para),
+ gimp_parasite_data_size(self->para));
+}
+
+static PyGetSetDef para_getsets[] = {
+ { "is_persistent", (getter)para_get_is_persistent, (setter)0 },
+ { "is_undoable", (getter)para_get_is_undoable, (setter)0 },
+ { "flags", (getter)para_get_flags, (setter)0 },
+ { "name", (getter)para_get_name, (setter)0 },
+ { "data", (getter)para_get_data, (setter)0 },
+ { NULL, (getter)0, (setter)0 },
+};
+
+static void
+para_dealloc(PyGimpParasite *self)
+{
+ gimp_parasite_free(self->para);
+ PyObject_DEL(self);
+}
+
+static PyObject *
+para_repr(PyGimpParasite *self)
+{
+ PyObject *s;
+
+ s = PyString_FromFormat("<parasite %s>", gimp_parasite_name(self->para));
+
+ return s;
+}
+
+static PyObject *
+para_str(PyGimpParasite *self)
+{
+ return PyString_FromStringAndSize(gimp_parasite_data(self->para),
+ gimp_parasite_data_size(self->para));
+}
+
+static int
+para_init(PyGimpParasite *self, PyObject *args, PyObject *kwargs)
+{
+ char *name;
+ int flags, size;
+ guint8 *data;
+
+ static char *kwlist[] = { "name", "flags", "data", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "sis#:gimp.Parasite.__init__", kwlist,
+ &name, &flags,
+ &data, &size))
+ return -1;
+
+ self->para = gimp_parasite_new(name, flags, size, data);
+
+ if (!self->para) {
+ PyErr_Format(pygimp_error, "could not create parasite '%s'", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PyTypeObject PyGimpParasite_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Parasite", /* tp_name */
+ sizeof(PyGimpParasite), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)para_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)para_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)para_str, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ para_methods, /* tp_methods */
+ 0, /* tp_members */
+ para_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)para_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_parasite_new(GimpParasite *para)
+{
+ PyGimpParasite *self;
+
+ if (!para) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ self = PyObject_NEW(PyGimpParasite, &PyGimpParasite_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->para = para;
+
+ return (PyObject *)self;
+}
diff --git a/plug-ins/pygimp/pygimp-pdb.c b/plug-ins/pygimp/pygimp-pdb.c
new file mode 100644
index 0000000..2ec0da0
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-pdb.c
@@ -0,0 +1,1110 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define NO_IMPORT_PYGOBJECT
+#include <pygobject.h>
+
+#include "pygimp.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+#include <structmember.h>
+
+#include <glib-object.h>
+#include <glib/gprintf.h>
+
+#ifndef PG_DEBUG
+# define PG_DEBUG 0
+#endif
+
+/* ----------------------------------------------------- */
+
+/* Declarations for objects of type pdb */
+
+typedef struct {
+ PyObject_HEAD
+} PyGimpPDB;
+
+
+/* ---------------------------------------------------------------- */
+
+/* Declarations for objects of type pdbFunc */
+
+typedef struct {
+ PyObject_HEAD
+ char *name;
+ PyObject *proc_name, *proc_blurb, *proc_help, *proc_author,
+ *proc_copyright, *proc_date, *proc_type, *py_params,
+ *py_return_vals;
+ int nparams, nreturn_vals;
+ GimpParamDef *params, *return_vals;
+} PyGimpPDBFunction;
+
+static PyObject *pygimp_pdb_function_new_from_proc_db(char *name);
+
+/* ---------------------------------------------------------------- */
+
+/* routines to convert between Python tuples and gimp GimpParam's */
+
+#if PG_DEBUG > 0
+
+static void
+pygimp_param_print(int nparams, GimpParam *params)
+{
+ int i;
+
+ for (i = 0; i < nparams; i++) {
+ g_printf("param_print: type: %d, PDB_ITEM: %d\n", params[i].type, GIMP_PDB_ITEM);
+ switch(params[i].type) {
+ case GIMP_PDB_INT32:
+ g_printerr("%i. int %i\n", i,
+ params[i].data.d_int32);
+ break;
+ case GIMP_PDB_INT16:
+ g_printerr("%i. int %i\n", i,
+ params[i].data.d_int16);
+ break;
+ case GIMP_PDB_INT8:
+ g_printerr("%i. int %u\n", i,
+ params[i].data.d_int8);
+ break;
+ case GIMP_PDB_FLOAT:
+ g_printerr("%i. float %f\n", i,
+ params[i].data.d_float);
+ break;
+ case GIMP_PDB_STRING:
+ g_printerr("%i. string %s\n", i,
+ params[i].data.d_string);
+ break;
+ case GIMP_PDB_INT32ARRAY:
+ case GIMP_PDB_INT16ARRAY:
+ case GIMP_PDB_INT8ARRAY:
+ case GIMP_PDB_FLOATARRAY:
+ case GIMP_PDB_STRINGARRAY:
+ g_printerr("%i. array of type %i %s\n", i,
+ params[i].type,
+ params[i].data.d_int32array == NULL ? "(null)":"");
+ break;
+ case GIMP_PDB_STATUS:
+ g_printerr("%i. status %i\n", i,
+ params[i].data.d_status);
+ break;
+ default:
+ g_printerr("%i. other %i\n", i,
+ params[i].data.d_int32);
+ break;
+ }
+ }
+}
+
+#endif
+
+PyObject *
+pygimp_param_to_tuple(int nparams, const GimpParam *params)
+{
+ PyObject *args, *tmp;
+ int i, j, n;
+
+ args = PyTuple_New(nparams);
+ for (i = 0; i < nparams && params[i].type != GIMP_PDB_END; i++) {
+ PyObject *value = NULL;
+
+#if PG_DEBUG > 1
+ g_printf("param_to_tuple: type: %d, PDB_ITEM: %d\n", params[i].type, GIMP_PDB_ITEM);
+#endif
+
+ switch(params[i].type) {
+ case GIMP_PDB_INT32:
+ value = PyInt_FromLong(params[i].data.d_int32);
+ break;
+ case GIMP_PDB_INT16:
+ value = PyInt_FromLong(params[i].data.d_int16);
+ break;
+ case GIMP_PDB_INT8:
+ value = PyInt_FromLong(params[i].data.d_int8);
+ break;
+ case GIMP_PDB_FLOAT:
+ value = PyFloat_FromDouble(params[i].data.d_float);
+ break;
+ case GIMP_PDB_STRING:
+ if (params[i].data.d_string == NULL) {
+ Py_INCREF(Py_None);
+ value = Py_None;
+ } else
+ value = PyString_FromString(params[i].data.d_string);
+ break;
+
+ /* For these to work, the previous argument must have
+ * been an integer
+ */
+ case GIMP_PDB_INT32ARRAY:
+ if (params[i].data.d_int32array == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ PyInt_FromLong(params[i].data.d_int32array[j]));
+ break;
+ case GIMP_PDB_INT16ARRAY:
+ if (params[i].data.d_int16array == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ PyInt_FromLong(params[i].data.d_int16array[j]));
+ break;
+ case GIMP_PDB_INT8ARRAY:
+ if (params[i].data.d_int8array == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ PyInt_FromLong(params[i].data.d_int8array[j]));
+ break;
+ case GIMP_PDB_FLOATARRAY:
+ if (params[i].data.d_floatarray == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ PyFloat_FromDouble(params[i].data.d_floatarray[j]));
+ break;
+ case GIMP_PDB_STRINGARRAY:
+ if (params[i].data.d_stringarray == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ params[i].data.d_stringarray[j] ?
+ PyString_FromString(params[i].data.d_stringarray[j]) :
+ PyString_FromString(""));
+ break;
+ case GIMP_PDB_COLOR:
+ value = pygimp_rgb_new(&params[i].data.d_color);
+ break;
+ /*
+ GIMP_PDB_REGION is deprecated in libgimpbase/gimpbaseenums.h
+ and conflicts with GIMP_PDB_ITEM
+ case GIMP_PDB_REGION:
+ value = Py_BuildValue("(iiii)",
+ (int) params[i].data.d_region.x,
+ (int) params[i].data.d_region.y,
+ (int) params[i].data.d_region.width,
+ (int) params[i].data.d_region.height);
+ break;
+ */
+ case GIMP_PDB_DISPLAY:
+ value = pygimp_display_new(params[i].data.d_display);
+ break;
+ case GIMP_PDB_IMAGE:
+ value = pygimp_image_new(params[i].data.d_image);
+ break;
+ case GIMP_PDB_LAYER:
+ value = pygimp_layer_new(params[i].data.d_layer);
+ break;
+ case GIMP_PDB_CHANNEL:
+ value = pygimp_channel_new(params[i].data.d_channel);
+ break;
+ case GIMP_PDB_ITEM:
+ value = pygimp_item_new(params[i].data.d_item);
+ break;
+ case GIMP_PDB_DRAWABLE:
+ value = pygimp_drawable_new(NULL, params[i].data.d_drawable);
+ break;
+ case GIMP_PDB_SELECTION:
+ value = pygimp_channel_new(params[i].data.d_selection);
+ break;
+ case GIMP_PDB_COLORARRAY:
+ if (params[i].data.d_colorarray == NULL) {
+ value = PyTuple_New(0);
+ break;
+ }
+ if ((tmp=PyTuple_GetItem(args, i-1)) == NULL) {
+ Py_DECREF(args);
+ return NULL;
+ }
+ if (!PyInt_Check(tmp)) {
+ PyErr_SetString(PyExc_TypeError,
+ "count type must be integer");
+ Py_DECREF(args);
+ return NULL;
+ }
+ n = PyInt_AsLong(tmp);
+ value = PyTuple_New(n);
+ for (j = 0; j < n; j++)
+ PyTuple_SetItem(value, j,
+ pygimp_rgb_new(&params[i].data.d_colorarray[j]));
+ break;
+ case GIMP_PDB_VECTORS:
+ value = pygimp_vectors_new(params[i].data.d_vectors);
+ break;
+ case GIMP_PDB_PARASITE:
+ value = pygimp_parasite_new(gimp_parasite_copy(
+ &(params[i].data.d_parasite)));
+ break;
+ case GIMP_PDB_STATUS:
+ value = PyInt_FromLong(params[i].data.d_status);
+ break;
+ case GIMP_PDB_END:
+ break;
+ }
+ PyTuple_SetItem(args, i, value);
+ }
+ return args;
+}
+
+GimpParam *
+pygimp_param_from_tuple(PyObject *args, const GimpParamDef *ptype, int nparams)
+{
+ PyObject *tuple, *item, *x, *y;
+ GimpParam *ret;
+ int i, j, len;
+ gint32 *i32a; gint16 *i16a; guint8 *i8a; gdouble *fa; gchar **sa;
+
+ if (nparams == 0)
+ tuple = PyTuple_New(0);
+ else if (!PyTuple_Check(args) && nparams == 1)
+ tuple = Py_BuildValue("(O)", args);
+ else {
+ Py_INCREF(args);
+ tuple = args;
+ }
+ if (!PyTuple_Check(tuple)) {
+ PyErr_SetString(PyExc_TypeError, "wrong type of parameter");
+ Py_DECREF(tuple);
+ return NULL;
+ }
+
+ if (PyTuple_Size(tuple) != nparams) {
+ PyErr_SetString(PyExc_TypeError, "wrong number of parameters");
+ Py_DECREF(tuple);
+ return NULL;
+ }
+
+ ret = g_new(GimpParam, nparams+1);
+ for (i = 0; i <= nparams; i++)
+ ret[i].type = GIMP_PDB_STATUS;
+#define check(expr) if (expr) { \
+ PyErr_SetString(PyExc_TypeError, "wrong parameter type"); \
+ Py_DECREF(tuple); \
+ gimp_destroy_params(ret, nparams); \
+ return NULL; \
+ }
+#define arraycheck(expr, ar) if (expr) { \
+ PyErr_SetString(PyExc_TypeError, "subscript of wrong type"); \
+ Py_DECREF(tuple); \
+ gimp_destroy_params(ret, nparams); \
+ g_free(ar); \
+ return NULL; \
+ }
+ for (i = 1; i <= nparams; i++) {
+ item = PyTuple_GetItem(tuple, i-1);
+#if PG_DEBUG > 1
+ g_printf("param_from_tuple: type: %d, PDB_ITEM: %d\n", ptype[i-1].type, GIMP_PDB_ITEM);
+#endif
+ switch (ptype[i-1].type) {
+ case GIMP_PDB_INT32:
+ check((x = PyNumber_Int(item)) == NULL);
+ ret[i].data.d_int32 = (gint32)PyInt_AsLong(x);
+ Py_DECREF(x);
+ break;
+ case GIMP_PDB_INT16:
+ check((x = PyNumber_Int(item)) == NULL);
+ ret[i].data.d_int16 = (gint16)PyInt_AsLong(x);
+ Py_DECREF(x);
+ break;
+ case GIMP_PDB_INT8:
+ check((x = PyNumber_Int(item)) == NULL);
+ ret[i].data.d_int8 = (guint8)PyInt_AsLong(x);
+ Py_DECREF(x);
+ break;
+ case GIMP_PDB_FLOAT:
+ check((x = PyNumber_Float(item)) == NULL);
+ ret[i].data.d_float = PyFloat_AsDouble(x);
+ Py_DECREF(x);
+ break;
+ case GIMP_PDB_STRING:
+ if (item == Py_None) {
+ ret[i].data.d_string = NULL;
+ break;
+ }
+ check((x = PyObject_Str(item)) == NULL);
+ ret[i].data.d_string = g_strdup(PyString_AsString(x));
+ Py_DECREF(x);
+ break;
+ case GIMP_PDB_INT32ARRAY:
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ i32a = g_new(gint32, len);
+ for (j = 0; j < len; j++) {
+ x = PySequence_GetItem(item, j);
+ arraycheck((y=PyNumber_Int(x))==NULL,
+ i32a);
+ i32a[j] = PyInt_AsLong(y);
+ Py_DECREF(y);
+ }
+ ret[i].data.d_int32array = i32a;
+ break;
+ case GIMP_PDB_INT16ARRAY:
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ i16a = g_new(gint16, len);
+ for (j = 0; j < len; j++) {
+ x = PySequence_GetItem(item, j);
+ arraycheck((y=PyNumber_Int(x))==NULL,
+ i16a);
+ i16a[j] = PyInt_AsLong(y);
+ Py_DECREF(y);
+ }
+ ret[i].data.d_int16array = i16a;
+ break;
+ case GIMP_PDB_INT8ARRAY:
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ i8a = g_new(guint8, len);
+ for (j = 0; j < len; j++) {
+ x = PySequence_GetItem(item, j);
+ arraycheck((y=PyNumber_Int(x))==NULL,
+ i8a);
+ i8a[j] = PyInt_AsLong(y);
+ Py_DECREF(y);
+ }
+ ret[i].data.d_int8array = i8a;
+ break;
+ case GIMP_PDB_FLOATARRAY:
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ fa = g_new(gdouble, len);
+ for (j = 0; j < len; j++) {
+ x = PySequence_GetItem(item, j);
+ arraycheck((y=PyNumber_Float(x))==NULL,
+ fa);
+ fa[j] = PyFloat_AsDouble(y);
+ Py_DECREF(y);
+ }
+ ret[i].data.d_floatarray = fa;
+ break;
+ case GIMP_PDB_STRINGARRAY:
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ sa = g_new(gchar *, len);
+ for (j = 0; j < len; j++) {
+ x = PySequence_GetItem(item, j);
+ if (x == Py_None) {
+ sa[j] = NULL;
+ continue;
+ }
+ arraycheck((y=PyObject_Str(x))==NULL,
+ sa);
+ sa[j] = g_strdup(PyString_AsString(y));
+ Py_DECREF(y);
+ }
+ ret[i].data.d_stringarray = sa;
+ break;
+ case GIMP_PDB_COLOR:
+ {
+ GimpRGB rgb;
+
+ if (!pygimp_rgb_from_pyobject(item, &rgb)) {
+ Py_DECREF(tuple);
+ gimp_destroy_params(ret, nparams);
+ return NULL;
+ }
+
+ ret[i].data.d_color = rgb;
+ }
+ break;
+/*
+ case GIMP_PDB_REGION:
+ check(!PySequence_Check(item) ||
+ PySequence_Length(item) < 4);
+ x = PySequence_GetItem(item, 0);
+ y = PySequence_GetItem(item, 1);
+ w = PySequence_GetItem(item, 2);
+ h = PySequence_GetItem(item, 3);
+ check(!PyInt_Check(x) || !PyInt_Check(y) ||
+ !PyInt_Check(w) || !PyInt_Check(h));
+ ret[i].data.d_region.x = PyInt_AsLong(x);
+ ret[i].data.d_region.y = PyInt_AsLong(y);
+ ret[i].data.d_region.width = PyInt_AsLong(w);
+ ret[i].data.d_region.height = PyInt_AsLong(h);
+ break;
+*/
+ case GIMP_PDB_DISPLAY:
+ if (item == Py_None) {
+ ret[i].data.d_display = -1;
+ break;
+ }
+ check(!pygimp_display_check(item));
+ ret[i].data.d_display = ((PyGimpDisplay *)item)->ID;
+ break;
+ case GIMP_PDB_IMAGE:
+ if (item == Py_None) {
+ ret[i].data.d_image = -1;
+ break;
+ }
+ check(!pygimp_image_check(item));
+ ret[i].data.d_image = ((PyGimpImage *)item)->ID;
+ break;
+ case GIMP_PDB_LAYER:
+ if (item == Py_None) {
+ ret[i].data.d_layer = -1;
+ break;
+ }
+ check(!pygimp_layer_check(item));
+ ret[i].data.d_layer = ((PyGimpLayer *)item)->ID;
+ break;
+ case GIMP_PDB_CHANNEL:
+ if (item == Py_None) {
+ ret[i].data.d_channel = -1;
+ break;
+ }
+ check(!pygimp_channel_check(item));
+ ret[i].data.d_channel = ((PyGimpChannel *)item)->ID;
+ break;
+ case GIMP_PDB_ITEM:
+ if (item == Py_None) {
+ ret[i].data.d_channel = -1;
+ break;
+ }
+ check(!pygimp_item_check(item));
+ ret[i].data.d_item = ((PyGimpItem *)item)->ID;
+ break;
+ case GIMP_PDB_DRAWABLE:
+ if (item == Py_None) {
+ ret[i].data.d_channel = -1;
+ break;
+ }
+ check(!pygimp_drawable_check(item));
+ ret[i].data.d_channel = ((PyGimpDrawable *)item)->ID;
+ break;
+ case GIMP_PDB_SELECTION:
+ if (item == Py_None) {
+ ret[i].data.d_channel = -1;
+ break;
+ }
+ check(!pygimp_channel_check(item));
+ ret[i].data.d_selection = ((PyGimpChannel *)item)->ID;
+ break;
+ case GIMP_PDB_COLORARRAY:
+ {
+ GimpRGB *rgb;
+
+ check(!PySequence_Check(item));
+ len = PySequence_Length(item);
+ rgb = g_new(GimpRGB, len);
+ for (j = 0; j < len; j++) {
+ if (!pygimp_rgb_from_pyobject(item, &rgb[j])) {
+ Py_DECREF(tuple);
+ g_free(rgb);
+ gimp_destroy_params(ret, nparams);
+ return NULL;
+ }
+ }
+ ret[i].data.d_colorarray = rgb;
+ }
+ break;
+ case GIMP_PDB_VECTORS:
+ if (item == Py_None) {
+ ret[i].data.d_vectors = -1;
+ break;
+ }
+ check(!pygimp_vectors_check(item));
+ ret[i].data.d_vectors = ((PyGimpVectors *)item)->ID;
+ break;
+ case GIMP_PDB_PARASITE:
+ /* can't do anything, since size of GimpParasite is not known */
+ break;
+ case GIMP_PDB_STATUS:
+ check(!PyInt_Check(item));
+ ret[i].data.d_status = PyInt_AsLong(item);
+ break;
+ case GIMP_PDB_END:
+ break;
+ }
+#undef check
+#undef arraycheck
+ ret[i].type = ptype[i-1].type;
+ }
+
+ Py_DECREF(tuple);
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+
+static PyObject *
+pdb_query(PyGimpPDB *self, PyObject *args)
+{
+ char *n=".*", *b=".*", *h=".*", *a=".*", *c=".*", *d=".*", *t=".*";
+ int num, i;
+ char **names;
+ PyObject *ret;
+
+ if (!PyArg_ParseTuple(args, "|zzzzzzz:gimp.pdb.query", &n, &b, &h, &a,
+ &c, &d, &t))
+ return NULL;
+
+ gimp_procedural_db_query(n, b, h, a, c, d, t, &num, &names);
+
+ ret = PyList_New(num);
+
+ for (i = 0; i < num; i++)
+ PyList_SetItem(ret, i, PyString_FromString(names[i]));
+
+ g_strfreev(names);
+
+ return ret;
+}
+
+static PyMethodDef pdb_methods[] = {
+ {"query", (PyCFunction)pdb_query, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+/* ---------- */
+
+
+PyObject *
+pygimp_pdb_new(void)
+{
+ PyGimpPDB *self = PyObject_NEW(PyGimpPDB, &PyGimpPDB_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ return (PyObject *)self;
+}
+
+
+static void
+pdb_dealloc(PyGimpPDB *self)
+{
+ PyObject_DEL(self);
+}
+
+static PyObject *
+pdb_repr(PyGimpPDB *self)
+{
+ return PyString_FromString("<gimp procedural database>");
+}
+
+/* Code to access pdb objects as mappings */
+
+static PyObject *
+pdb_subscript(PyGimpPDB *self, PyObject *key)
+{
+ PyObject *r;
+
+ if (!PyString_Check(key)) {
+ PyErr_SetString(PyExc_TypeError, "Subscript must be a string");
+ return NULL;
+ }
+
+ r = (PyObject *)pygimp_pdb_function_new_from_proc_db(PyString_AsString(key));
+
+ if (r == NULL) {
+ PyErr_Clear();
+ PyErr_SetObject(PyExc_KeyError, key);
+ }
+
+ return r;
+}
+
+static PyMappingMethods pdb_as_mapping = {
+ (lenfunc)0, /*mp_length*/
+ (binaryfunc)pdb_subscript, /*mp_subscript*/
+ (objobjargproc)0, /*mp_ass_subscript*/
+};
+
+/* -------------------------------------------------------- */
+
+static PyObject *
+build_procedure_list(void)
+{
+ int num, i;
+ char **names, *name, *p;
+ PyObject *ret;
+
+ gimp_procedural_db_query(".*", ".*", ".*", ".*", ".*", ".*", ".*",
+ &num, &names);
+
+ ret = PyList_New(num);
+
+ for (i = 0; i < num; i++) {
+ name = g_strdup(names[i]);
+ for (p = name; *p != '\0'; p++) {
+ if (*p == '-')
+ *p = '_';
+ }
+ PyList_SetItem(ret, i, PyString_FromString(name));
+ }
+
+ g_strfreev(names);
+
+ return ret;
+}
+
+static PyObject *
+pdb_getattro(PyGimpPDB *self, PyObject *attr)
+{
+ char *attr_name;
+ PyObject *ret;
+
+ attr_name = PyString_AsString(attr);
+ if (!attr_name) {
+ PyErr_Clear();
+ return PyObject_GenericGetAttr((PyObject *)self, attr);
+ }
+
+ if (attr_name[0] == '_') {
+ if (!strcmp(attr_name, "__members__")) {
+ return build_procedure_list();
+ } else {
+ return PyObject_GenericGetAttr((PyObject *)self, attr);
+ }
+ }
+
+ ret = PyObject_GenericGetAttr((PyObject *)self, attr);
+ if (ret)
+ return ret;
+
+ PyErr_Clear();
+
+ return pygimp_pdb_function_new_from_proc_db(attr_name);
+}
+
+PyTypeObject PyGimpPDB_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.PDB", /* tp_name */
+ sizeof(PyGimpPDB), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)pdb_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)pdb_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &pdb_as_mapping, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)pdb_getattro, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ pdb_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+/* End of code for pdb objects */
+/* -------------------------------------------------------- */
+
+
+static PyObject *
+pygimp_pdb_function_new_from_proc_db(char *name)
+{
+ PyObject *ret;
+ char *b,*h,*a,*c,*d;
+ int np, nr;
+ GimpPDBProcType pt;
+ GimpParamDef *p, *r;
+
+ if (!gimp_procedural_db_proc_info (name, &b, &h, &a, &c, &d, &pt,
+ &np, &nr, &p, &r)) {
+ PyErr_SetString(pygimp_error, "procedure not found");
+ return NULL;
+ }
+
+ ret = pygimp_pdb_function_new(name, b, h, a, c, d, pt, np, nr, p, r);
+
+ g_free(b); g_free(h); g_free(a); g_free(c); g_free(d);
+
+ return ret;
+}
+
+static void
+pf_dealloc(PyGimpPDBFunction *self)
+{
+ g_free(self->name);
+
+ Py_DECREF(self->proc_name);
+ Py_DECREF(self->proc_blurb);
+ Py_DECREF(self->proc_help);
+ Py_DECREF(self->proc_author);
+ Py_DECREF(self->proc_copyright);
+ Py_DECREF(self->proc_date);
+ Py_DECREF(self->proc_type);
+ Py_DECREF(self->py_params);
+ Py_DECREF(self->py_return_vals);
+
+ gimp_destroy_paramdefs(self->params, self->nparams);
+ gimp_destroy_paramdefs(self->return_vals, self->nreturn_vals);
+
+ PyObject_DEL(self);
+}
+
+#define OFF(x) offsetof(PyGimpPDBFunction, x)
+static struct PyMemberDef pf_members[] = {
+ {"proc_name", T_OBJECT, OFF(proc_name), RO},
+ {"proc_blurb", T_OBJECT, OFF(proc_blurb), RO},
+ {"proc_help", T_OBJECT, OFF(proc_help), RO},
+ {"proc_author", T_OBJECT, OFF(proc_author), RO},
+ {"proc_copyright", T_OBJECT, OFF(proc_copyright), RO},
+ {"proc_date", T_OBJECT, OFF(proc_date), RO},
+ {"proc_type", T_OBJECT, OFF(proc_type), RO},
+ {"nparams", T_INT, OFF(nparams), RO},
+ {"nreturn_vals", T_INT, OFF(nreturn_vals), RO},
+ {"params", T_OBJECT, OFF(py_params), RO},
+ {"return_vals", T_OBJECT, OFF(py_return_vals), RO},
+ {NULL} /* Sentinel */
+};
+#undef OFF
+
+static PyObject *
+pf_repr(PyGimpPDBFunction *self)
+{
+ return PyString_FromFormat("<pdb function '%s'>",
+ PyString_AsString(self->proc_name));
+}
+
+static PyObject *
+pf_call(PyGimpPDBFunction *self, PyObject *args, PyObject *kwargs)
+{
+ GimpParam *params, *ret;
+ int nret;
+ PyObject *t = NULL, *r;
+ GimpRunMode run_mode = GIMP_RUN_NONINTERACTIVE;
+
+#if PG_DEBUG > 0
+ g_printerr("--- %s --- ", PyString_AsString(self->proc_name));
+#endif
+
+ if (kwargs) {
+ Py_ssize_t len, pos;
+ PyObject *key, *val;
+
+ len = PyDict_Size(kwargs);
+
+ if (len == 1) {
+ pos = 0;
+ PyDict_Next(kwargs, &pos, &key, &val);
+
+ if (!PyString_Check(key)) {
+ PyErr_SetString(PyExc_TypeError,
+ "keyword argument name is not a string");
+ return NULL;
+ }
+
+ if (strcmp(PyString_AsString(key), "run_mode") != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "only 'run_mode' keyword argument accepted");
+ return NULL;
+ }
+
+ if (pyg_enum_get_value(GIMP_TYPE_RUN_MODE, val, (gpointer)&run_mode))
+ return NULL;
+ } else if (len != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "expecting at most one keyword argument");
+ return NULL;
+ }
+ }
+
+ if (self->nparams > 0 && !strcmp(self->params[0].name, "run-mode")) {
+ params = pygimp_param_from_tuple(args, self->params + 1,
+ self->nparams - 1);
+
+ if (params == NULL)
+ return NULL;
+
+ params[0].type = self->params[0].type;
+ params[0].data.d_int32 = run_mode;
+
+#if PG_DEBUG > 1
+ pygimp_param_print(self->nparams, params);
+#endif
+
+ ret = gimp_run_procedure2(self->name, &nret, self->nparams, params);
+ } else {
+ params = pygimp_param_from_tuple(args, self->params, self->nparams);
+
+ if (params == NULL)
+ return NULL;
+
+#if PG_DEBUG > 1
+ pygimp_param_print(self->nparams, params+1);
+#endif
+
+ ret = gimp_run_procedure2(self->name, &nret, self->nparams, params + 1);
+ }
+
+ gimp_destroy_params(params, self->nparams);
+
+ if (!ret) {
+ PyErr_SetString(pygimp_error, "no status returned");
+#if PG_DEBUG >= 1
+ g_printerr("ret == NULL\n");
+#endif
+ return NULL;
+ }
+
+ switch(ret[0].data.d_status) {
+ case GIMP_PDB_SUCCESS:
+#if PG_DEBUG > 0
+ g_printerr("success\n");
+#endif
+ t = pygimp_param_to_tuple(nret-1, ret+1);
+ gimp_destroy_params(ret, nret);
+
+ if (t == NULL) {
+ PyErr_SetString(pygimp_error, "could not make return value");
+ return NULL;
+ }
+ break;
+
+ case GIMP_PDB_EXECUTION_ERROR:
+#if PG_DEBUG > 0
+ g_printerr("execution error\n");
+#endif
+ PyErr_SetString(PyExc_RuntimeError, gimp_get_pdb_error());
+ gimp_destroy_params(ret, nret);
+ return NULL;
+
+ case GIMP_PDB_CALLING_ERROR:
+#if PG_DEBUG > 0
+ g_printerr("calling error\n");
+#endif
+ PyErr_SetString(PyExc_RuntimeError, gimp_get_pdb_error());
+ gimp_destroy_params(ret, nret);
+ return NULL;
+
+ case GIMP_PDB_CANCEL:
+#if PG_DEBUG > 0
+ g_printerr("cancel\n");
+#endif
+ PyErr_SetString(PyExc_RuntimeError, gimp_get_pdb_error());
+ gimp_destroy_params(ret, nret);
+ return NULL;
+
+ default:
+#if PG_DEBUG > 0
+ g_printerr("unknown - %i (type %i)\n",
+ ret[0].data.d_status, ret[0].type);
+#endif
+ PyErr_SetString(pygimp_error, "unknown return code");
+ return NULL;
+ }
+
+ if (PyTuple_Size(t) == 1) {
+ r = PyTuple_GetItem(t, 0);
+ Py_INCREF(r);
+ Py_DECREF(t);
+ return r;
+ }
+
+ if (PyTuple_Size(t) == 0) {
+ r = Py_None;
+ Py_INCREF(r);
+ Py_DECREF(t);
+ return r;
+ }
+
+ return t;
+}
+
+
+PyTypeObject PyGimpPDBFunction_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.PDBFunction", /* tp_name */
+ sizeof(PyGimpPDBFunction), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)pf_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)pf_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)pf_call, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ 0, /* tp_methods */
+ pf_members, /* tp_members */
+ 0, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_pdb_function_new(const char *name, const char *blurb, const char *help,
+ const char *author, const char *copyright,
+ const char *date, GimpPDBProcType proc_type,
+ int n_params, int n_return_vals,
+ GimpParamDef *params, GimpParamDef *return_vals)
+{
+ PyGimpPDBFunction *self;
+ int i;
+
+ self = PyObject_NEW(PyGimpPDBFunction, &PyGimpPDBFunction_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->name = g_strdup(name);
+ self->proc_name = PyString_FromString(name ? name : "");
+ self->proc_blurb = PyString_FromString(blurb ? blurb : "");
+ self->proc_help = PyString_FromString(help ? help : "");
+ self->proc_author = PyString_FromString(author ? author : "");
+ self->proc_copyright = PyString_FromString(copyright ? copyright : "");
+ self->proc_date = PyString_FromString(date ? date : "");
+ self->proc_type = PyInt_FromLong(proc_type);
+ self->nparams = n_params;
+ self->nreturn_vals = n_return_vals;
+ self->params = params;
+ self->return_vals = return_vals;
+
+ self->py_params = PyTuple_New(n_params);
+ for (i = 0; i < n_params; i++)
+ PyTuple_SetItem(self->py_params, i,
+ Py_BuildValue("(iss)",
+ params[i].type,
+ params[i].name,
+ params[i].description));
+
+ self->py_return_vals = PyTuple_New(n_return_vals);
+ for (i = 0; i < n_return_vals; i++)
+ PyTuple_SetItem(self->py_return_vals, i,
+ Py_BuildValue("(iss)",
+ return_vals[i].type,
+ return_vals[i].name,
+ return_vals[i].description));
+
+ return (PyObject *)self;
+}
diff --git a/plug-ins/pygimp/pygimp-tile.c b/plug-ins/pygimp/pygimp-tile.c
new file mode 100644
index 0000000..b4f3722
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-tile.c
@@ -0,0 +1,1046 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "pygimp.h"
+
+#define NO_IMPORT_PYGIMPCOLOR
+#include "pygimpcolor-api.h"
+
+#include <structmember.h>
+
+static PyObject *
+tile_flush(PyGimpTile *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":flush"))
+ return NULL;
+
+ gimp_tile_flush(self->tile);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyMethodDef tile_methods[] = {
+ {"flush", (PyCFunction)tile_flush, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+/* ---------- */
+
+
+PyObject *
+pygimp_tile_new(GimpTile *t, PyGimpDrawable *drw)
+{
+ PyGimpTile *self;
+
+ self = PyObject_NEW(PyGimpTile, &PyGimpTile_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ gimp_tile_ref(t);
+
+ self->tile = t;
+
+ Py_INCREF(drw);
+ self->drawable = drw;
+
+ return (PyObject *)self;
+}
+
+
+static void
+tile_dealloc(PyGimpTile *self)
+{
+ gimp_tile_unref(self->tile, FALSE);
+
+ Py_DECREF(self->drawable);
+ PyObject_DEL(self);
+}
+
+static PyObject *
+tile_get_uint_field(PyGimpTile *self, void *closure)
+{
+ gint offset = GPOINTER_TO_INT(closure);
+ guint value;
+ gchar *addr;
+
+ addr = (gchar *)self->tile;
+ addr += offset;
+ value = *(guint *)addr;
+
+ return PyInt_FromLong(value);
+}
+
+static PyObject *
+tile_get_dirty(PyGimpTile *self, void *closure)
+{
+ return PyBool_FromLong(self->tile->dirty);
+}
+
+static PyObject *
+tile_get_shadow(PyGimpTile *self, void *closure)
+{
+ return PyBool_FromLong(self->tile->shadow);
+}
+
+static PyObject *
+tile_get_drawable(PyGimpTile *self, void *closure)
+{
+ return pygimp_drawable_new(self->tile->drawable, 0);
+}
+
+
+#define OFF(x) GINT_TO_POINTER(offsetof(GimpTile, x))
+static PyGetSetDef tile_getsets[] = {
+ { "ewidth", (getter)tile_get_uint_field, 0, NULL, OFF(ewidth) },
+ { "eheight", (getter)tile_get_uint_field, 0, NULL, OFF(eheight) },
+ { "bpp", (getter)tile_get_uint_field, 0, NULL, OFF(bpp) },
+ { "tile_num", (getter)tile_get_uint_field, 0, NULL, OFF(tile_num) },
+ { "dirty", (getter)tile_get_dirty, 0, NULL },
+ { "shadow", (getter)tile_get_shadow, 0, NULL },
+ { "drawable", (getter)tile_get_drawable, 0, NULL },
+ { NULL, (getter)0, (setter)0 }
+};
+#undef OFF
+
+static PyObject *
+tile_repr(PyGimpTile *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->tile->drawable->drawable_id);
+
+ if (self->tile->shadow)
+ s = PyString_FromFormat("<gimp.Tile for drawable '%s' (shadow)>", name);
+ else
+ s = PyString_FromFormat("<gimp.Tile for drawable '%s'>", name);
+
+ g_free(name);
+
+ return s;
+}
+
+static Py_ssize_t
+tile_length(PyObject *self)
+{
+ return ((PyGimpTile*)self)->tile->ewidth * ((PyGimpTile*)self)->tile->eheight;
+}
+
+static PyObject *
+tile_subscript(PyGimpTile *self, PyObject *sub)
+{
+ GimpTile *tile = self->tile;
+ int bpp = tile->bpp;
+ long x, y;
+
+ if (PyInt_Check(sub)) {
+ x = PyInt_AsLong(sub);
+
+ if (x < 0 || x >= tile->ewidth * tile->eheight) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ return PyString_FromStringAndSize
+ ((char *)tile->data + bpp * x, bpp);
+ }
+
+ if (PyTuple_Check(sub)) {
+ if (!PyArg_ParseTuple(sub, "ll", &x, &y))
+ return NULL;
+
+ if (x < 0 || y < 0 || x >= tile->ewidth || y>=tile->eheight) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ return PyString_FromStringAndSize
+ ((char *)tile->data + bpp * (x + y * tile->ewidth), bpp);
+ }
+
+ PyErr_SetString(PyExc_TypeError, "tile subscript not int or 2-tuple");
+ return NULL;
+}
+
+static int
+tile_ass_sub(PyGimpTile *self, PyObject *v, PyObject *w)
+{
+ GimpTile *tile = self->tile;
+ int bpp = tile->bpp, i;
+ long x, y;
+ guchar *pix, *data;
+
+ if (w == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "can not delete pixels in tile");
+ return -1;
+ }
+
+ if (!PyString_Check(w) && PyString_Size(w) == bpp) {
+ PyErr_SetString(PyExc_TypeError, "invalid subscript");
+ return -1;
+ }
+
+ pix = (guchar *)PyString_AsString(w);
+
+ if (PyInt_Check(v)) {
+ x = PyInt_AsLong(v);
+
+ if (x < 0 || x >= tile->ewidth * tile->eheight) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ data = tile->data + x * bpp;
+
+ for (i = 0; i < bpp; i++)
+ data[i] = pix[i];
+
+ tile->dirty = TRUE;
+
+ return 0;
+ }
+
+ if (PyTuple_Check(v)) {
+ if (!PyArg_ParseTuple(v, "ll", &x, &y))
+ return -1;
+
+ if (x < 0 || y < 0 || x >= tile->ewidth || y>=tile->eheight) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return -1;
+ }
+
+ data = tile->data + bpp * (x + y * tile->ewidth);
+
+ for (i = 0; i < bpp; i++)
+ data[i] = pix[i];
+
+ tile->dirty = TRUE;
+
+ return 0;
+ }
+
+ PyErr_SetString(PyExc_TypeError, "tile subscript not int or 2-tuple");
+ return -1;
+}
+
+static PyMappingMethods tile_as_mapping = {
+ tile_length, /*length*/
+ (binaryfunc)tile_subscript, /*subscript*/
+ (objobjargproc)tile_ass_sub, /*ass_sub*/
+};
+
+PyTypeObject PyGimpTile_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Tile", /* tp_name */
+ sizeof(PyGimpTile), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)tile_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)tile_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &tile_as_mapping, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ tile_methods, /* tp_methods */
+ 0, /* tp_members */
+ tile_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+/* End of code for Tile objects */
+/* -------------------------------------------------------- */
+
+
+static PyObject *
+pr_resize(PyGimpPixelRgn *self, PyObject *args)
+{
+ int x, y, w, h;
+
+ if (!PyArg_ParseTuple(args, "iiii:resize", &x, &y, &w, &h))
+ return NULL;
+
+ gimp_pixel_rgn_resize(&(self->pr), x, y, w, h);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+
+static PyMethodDef pr_methods[] = {
+ {"resize", (PyCFunction)pr_resize, METH_VARARGS},
+
+ {NULL, NULL} /* sentinel */
+};
+
+/* ---------- */
+
+
+PyObject *
+pygimp_pixel_rgn_new(PyGimpDrawable *drawable, int x, int y,
+ int width, int height, int dirty, int shadow)
+{
+ PyGimpPixelRgn *self;
+ int drw_width;
+ int drw_height;
+
+ self = PyObject_NEW(PyGimpPixelRgn, &PyGimpPixelRgn_Type);
+
+ if (self == NULL || drawable == NULL)
+ return NULL;
+
+ drw_width = gimp_drawable_width(drawable->ID);
+ drw_height = gimp_drawable_height(drawable->ID);
+
+ if(x < 0) x = 0;
+ if(y < 0) y = 0;
+ if(width < 0) width = drw_width - x;
+ if(height < 0) height = drw_height - y;
+ if(x >= drw_width) x = drw_width - 1;
+ if(y >= drw_height) y = drw_height - 1;
+ if(x + width > drw_width) width = drw_width - x;
+ if(y + height > drw_height) height = drw_height - y;
+
+ gimp_pixel_rgn_init(&(self->pr), drawable->drawable, x, y, width, height,
+ dirty, shadow);
+
+ self->drawable = drawable;
+ Py_INCREF(drawable);
+
+ return (PyObject *)self;
+}
+
+
+static void
+pr_dealloc(PyGimpPixelRgn *self)
+{
+ Py_DECREF(self->drawable);
+ PyObject_DEL(self);
+}
+
+/* Code to access pr objects as mappings */
+
+static Py_ssize_t
+pr_length(PyObject *self)
+{
+ PyErr_SetString(pygimp_error, "Can't get size of pixel region");
+ return -1;
+}
+
+static PyObject *
+pr_subscript(PyGimpPixelRgn *self, PyObject *key)
+{
+ GimpPixelRgn *pr = &(self->pr);
+ PyObject *x, *y;
+ Py_ssize_t x1, y1, x2, y2, xs, ys;
+ PyObject *ret;
+
+ if (!PyTuple_Check(key) || PyTuple_Size(key) != 2) {
+ PyErr_SetString(PyExc_TypeError, "subscript must be a 2-tuple");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(key, "OO", &x, &y))
+ return NULL;
+
+ if (PyInt_Check(x)) {
+ x1 = PyInt_AsSsize_t(x);
+
+ if (x1 < pr->x || x1 >= pr->x + pr->w) {
+ PyErr_SetString(PyExc_IndexError, "x subscript out of range");
+ return NULL;
+ }
+
+ if (PyInt_Check(y)) {
+ y1 = PyInt_AsSsize_t(y);
+
+ if (y1 < pr->y || y1 >= pr->y + pr->h) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return NULL;
+ }
+
+ ret = PyString_FromStringAndSize(NULL, pr->bpp);
+ gimp_pixel_rgn_get_pixel(pr, (guchar*)PyString_AS_STRING(ret),
+ x1, y1);
+
+ } else if (PySlice_Check(y)) {
+ if (PySlice_GetIndices((PySliceObject*)y, pr->y + pr->h,
+ &y1, &y2, &ys) ||
+ y1 >= y2 || ys != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid y slice");
+ return NULL;
+ }
+
+ if(y1 == 0)
+ y1 = pr->y;
+
+ if(y1 < pr->y || y2 < pr->y) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return NULL;
+ }
+
+ ret = PyString_FromStringAndSize(NULL, pr->bpp * (y2 - y1));
+ gimp_pixel_rgn_get_col(pr, (guchar*)PyString_AS_STRING(ret),
+ x1, y1, y2-y1);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "invalid y subscript");
+ return NULL;
+ }
+ } else if (PySlice_Check(x)) {
+ if (PySlice_GetIndices((PySliceObject *)x, pr->x + pr->w,
+ &x1, &x2, &xs) ||
+ x1 >= x2 || xs != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid x slice");
+ return NULL;
+ }
+ if (x1 == 0)
+ x1 = pr->x;
+ if(x1 < pr->x || x2 < pr->x) {
+ PyErr_SetString(PyExc_IndexError, "x subscript out of range");
+ return NULL;
+ }
+
+ if (PyInt_Check(y)) {
+ y1 = PyInt_AsSsize_t(y);
+ if (y1 < pr->y || y1 >= pr->y + pr->h) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return NULL;
+ }
+ ret = PyString_FromStringAndSize(NULL, pr->bpp * (x2 - x1));
+ gimp_pixel_rgn_get_row(pr, (guchar*)PyString_AS_STRING(ret),
+ x1, y1, x2 - x1);
+
+ } else if (PySlice_Check(y)) {
+ if (PySlice_GetIndices((PySliceObject*)y, pr->y + pr->h,
+ &y1, &y2, &ys) ||
+ y1 >= y2 || ys != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid y slice");
+ return NULL;
+ }
+
+ if(y1 == 0)
+ y1 = pr->y;
+ if(y1 < pr->y || y2 < pr->y) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return NULL;
+ }
+
+ ret = PyString_FromStringAndSize(NULL,
+ pr->bpp * (x2 - x1) * (y2 - y1));
+ gimp_pixel_rgn_get_rect(pr, (guchar*)PyString_AS_STRING(ret),
+ x1, y1, x2 - x1, y2 - y1);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "invalid y subscript");
+ return NULL;
+ }
+
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid x subscript");
+ return NULL;
+ }
+ return ret;
+}
+
+static int
+pr_ass_sub(PyGimpPixelRgn *self, PyObject *v, PyObject *w)
+{
+ GimpPixelRgn *pr = &(self->pr);
+ PyObject *x, *y;
+ const guchar *buf;
+ Py_ssize_t len, x1, x2, xs, y1, y2, ys;
+
+ if (w == NULL) {
+ PyErr_SetString(PyExc_TypeError, "can't delete subscripts");
+ return -1;
+ }
+
+ if (!PyString_Check(w)) {
+ PyErr_SetString(PyExc_TypeError, "must assign string to subscript");
+ return -1;
+ }
+
+ if (!PyTuple_Check(v) || PyTuple_Size(v) != 2) {
+ PyErr_SetString(PyExc_TypeError, "subscript must be a 2-tuple");
+ return -1;
+ }
+
+ if (!PyArg_ParseTuple(v, "OO", &x, &y))
+ return -1;
+
+ buf = (const guchar *)PyString_AsString(w);
+ len = PyString_Size(w);
+ if (!buf || len > INT_MAX) {
+ return -1;
+ }
+
+ if (PyInt_Check(x)) {
+ x1 = PyInt_AsSsize_t(x);
+ if (x1 < pr->x || x1 >= pr->x + pr->w) {
+ PyErr_SetString(PyExc_IndexError, "x subscript out of range");
+ return -1;
+ }
+
+ if (PyInt_Check(y)) {
+ y1 = PyInt_AsSsize_t(y);
+
+ if (y1 < pr->y || y1 >= pr->y + pr->h) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return -1;
+ }
+
+ if (len != pr->bpp) {
+ PyErr_SetString(PyExc_TypeError, "string is wrong length");
+ return -1;
+ }
+ gimp_pixel_rgn_set_pixel(pr, buf, x1, y1);
+
+ } else if (PySlice_Check(y)) {
+ if (PySlice_GetIndices((PySliceObject *)y, pr->y + pr->h,
+ &y1, &y2, &ys) ||
+ y1 >= y2 || ys != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid y slice");
+ return -1;
+ }
+ if (y1 == 0)
+ y1 = pr->y;
+
+ if(y1 < pr->y || y2 < pr->y) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return -1;
+ }
+ if (len != pr->bpp * (y2 - y1)) {
+ PyErr_SetString(PyExc_TypeError, "string is wrong length");
+ return -1;
+ }
+ gimp_pixel_rgn_set_col(pr, buf, x1, y1, y2 - y1);
+
+ } else {
+ PyErr_SetString(PyExc_IndexError,"invalid y subscript");
+ return -1;
+ }
+ } else if (PySlice_Check(x)) {
+ if (PySlice_GetIndices((PySliceObject *)x, pr->x + pr->w,
+ &x1, &x2, &xs) ||
+ x1 >= x2 || xs != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid x slice");
+ return -1;
+ }
+ if(x1 == 0)
+ x1 = pr->x;
+
+ if(x1 < pr->x || x2 < pr->x) {
+ PyErr_SetString(PyExc_IndexError, "x subscript out of range");
+ return -1;
+ }
+
+ if (PyInt_Check(y)) {
+ y1 = PyInt_AsSsize_t(y);
+
+ if (y1 < pr->y || y1 >= pr->y + pr->h) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return -1;
+ }
+
+ if (len != pr->bpp * (x2 - x1)) {
+ PyErr_SetString(PyExc_TypeError, "string is wrong length");
+ return -1;
+ }
+ gimp_pixel_rgn_set_row(pr, buf, x1, y1, x2 - x1);
+
+ } else if (PySlice_Check(y)) {
+ if (PySlice_GetIndices((PySliceObject *)y, pr->y + pr->h,
+ &y1, &y2, &ys) ||
+ y1 >= y2 || ys != 1) {
+ PyErr_SetString(PyExc_IndexError, "invalid y slice");
+ return -1;
+ }
+ if (y1 == 0)
+ y1 = pr->y;
+
+ if(y1 < pr->y || y2 < pr->y) {
+ PyErr_SetString(PyExc_IndexError, "y subscript out of range");
+ return -1;
+ }
+ if (len != pr->bpp * (x2 - x1) * (y2 - y1)) {
+ PyErr_SetString(PyExc_TypeError, "string is wrong length");
+ return -1;
+ }
+ gimp_pixel_rgn_set_rect(pr, buf, x1, y1, x2 - x1, y2 - y1);
+
+ } else {
+ PyErr_SetString(PyExc_IndexError,"invalid y subscript");
+ return -1;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid x subscript");
+ return -1;
+ }
+ return 0;
+}
+
+static PyMappingMethods pr_as_mapping = {
+ pr_length, /*mp_length*/
+ (binaryfunc)pr_subscript, /*mp_subscript*/
+ (objobjargproc)pr_ass_sub, /*mp_ass_subscript*/
+};
+
+/* -------------------------------------------------------- */
+
+static PyObject *
+pr_get_drawable(PyGimpPixelRgn *self, void *closure)
+{
+ return pygimp_drawable_new(self->pr.drawable, 0);
+}
+
+static PyObject *
+pr_get_uint_field(PyGimpPixelRgn *self, void *closure)
+{
+ gint offset = GPOINTER_TO_INT(closure);
+ guint value;
+ gchar *addr;
+
+ addr = (gchar *)&self->pr;
+ addr += offset;
+ value = *(guint *)addr;
+
+ return PyInt_FromLong(value);
+}
+
+static PyObject *
+pr_get_dirty(PyGimpPixelRgn *self, void *closure)
+{
+ return PyBool_FromLong(self->pr.dirty);
+}
+
+static PyObject *
+pr_get_shadow(PyGimpPixelRgn *self, void *closure)
+{
+ return PyBool_FromLong(self->pr.shadow);
+}
+
+#define OFF(x) GINT_TO_POINTER(offsetof(GimpPixelRgn, x))
+static PyGetSetDef pr_getsets[] = {
+ { "drawable", (getter)pr_get_drawable, 0, NULL },
+ { "bpp", (getter)pr_get_uint_field, 0, NULL, OFF(bpp) },
+ { "rowstride", (getter)pr_get_uint_field, 0, NULL, OFF(rowstride) },
+ { "x", (getter)pr_get_uint_field, 0, NULL, OFF(x) },
+ { "y", (getter)pr_get_uint_field, 0, NULL, OFF(y) },
+ { "w", (getter)pr_get_uint_field, 0, NULL, OFF(w) },
+ { "h", (getter)pr_get_uint_field, 0, NULL, OFF(h) },
+ { "dirty", (getter)pr_get_dirty, 0, NULL },
+ { "shadow", (getter)pr_get_shadow, 0, NULL },
+ { NULL, (getter)0, (setter)0 },
+};
+#undef OFF
+
+static PyObject *
+pr_repr(PyGimpPixelRgn *self)
+{
+ PyObject *s;
+ gchar *name;
+
+ name = gimp_item_get_name(self->drawable->drawable->drawable_id);
+ s = PyString_FromFormat("<gimp.PixelRgn for drawable '%s'>", name);
+ g_free(name);
+
+ return s;
+}
+
+PyTypeObject PyGimpPixelRgn_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.PixelRgn", /* tp_name */
+ sizeof(PyGimpPixelRgn), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)pr_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)pr_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &pr_as_mapping, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ pr_methods, /* tp_methods */
+ 0, /* tp_members */
+ pr_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+static PyObject *
+pf_get_pixel(PyGimpPixelFetcher *self, PyObject *args, PyObject *kwargs)
+{
+ int x, y;
+ guchar pixel[4];
+ static char *kwlist[] = { "x", "y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "ii:get_pixel", kwlist,
+ &x, &y))
+ return NULL;
+
+ gimp_pixel_fetcher_get_pixel(self->pf, x, y, pixel);
+
+ return PyString_FromStringAndSize((char *)pixel, self->bpp);
+}
+
+static PyObject *
+pf_put_pixel(PyGimpPixelFetcher *self, PyObject *args, PyObject *kwargs)
+{
+ int x, y, len;
+ guchar *pixel;
+ static char *kwlist[] = { "x", "y", "pixel", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "iis#:put_pixel", kwlist,
+ &x, &y, &pixel, &len))
+ return NULL;
+
+ if (len != self->bpp) {
+ PyErr_Format(PyExc_TypeError, "pixel must be %d bpp", self->bpp);
+ return NULL;
+ }
+
+ gimp_pixel_fetcher_put_pixel(self->pf, x, y, pixel);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef pf_methods[] = {
+ {"get_pixel", (PyCFunction)pf_get_pixel, METH_VARARGS | METH_KEYWORDS},
+ {"put_pixel", (PyCFunction)pf_put_pixel, METH_VARARGS | METH_KEYWORDS},
+ {NULL, NULL}
+};
+
+static int
+pf_length(PyGimpPixelFetcher *self)
+{
+ PyErr_SetString(pygimp_error, "Can't get size of pixel fetcher");
+ return -1;
+}
+
+static PyObject *
+pf_subscript(PyGimpPixelFetcher *self, PyObject *key)
+{
+ PyObject *py_x, *py_y;
+ int x, y;
+ guchar pixel[4];
+
+ if (!PyTuple_Check(key) || PyTuple_Size(key) != 2) {
+ PyErr_SetString(PyExc_TypeError, "subscript must be a 2-tuple");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(key, "OO", &py_x, &py_y))
+ return NULL;
+
+ if (PyInt_Check(py_x)) {
+ if (PyInt_Check(py_y)) {
+ x = PyInt_AsLong(py_x);
+ y = PyInt_AsLong(py_y);
+
+ gimp_pixel_fetcher_get_pixel(self->pf, x, y, pixel);
+ return PyString_FromStringAndSize((char *)pixel, self->bpp);
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid y subscript");
+ return NULL;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid x subscript");
+ return NULL;
+ }
+}
+
+static int
+pf_ass_sub(PyGimpPixelFetcher *self, PyObject *v, PyObject *w)
+{
+ PyObject *py_x, *py_y;
+ int x, y, len;
+ guchar *pixel;
+
+ if (w == NULL) {
+ PyErr_SetString(PyExc_TypeError, "can't delete subscripts");
+ return -1;
+ }
+
+ if (!PyString_Check(w)) {
+ PyErr_SetString(PyExc_TypeError, "must assign string to subscript");
+ return -1;
+ }
+
+ if (!PyTuple_Check(v) || PyTuple_Size(v) != 2) {
+ PyErr_SetString(PyExc_TypeError, "subscript must be a 2-tuple");
+ return -1;
+ }
+
+ if (!PyArg_ParseTuple(v, "OO", &py_x, &py_y))
+ return -1;
+
+ pixel = (guchar *)PyString_AsString(w);
+ len = PyString_Size(w);
+
+ if (len != self->bpp) {
+ PyErr_Format(PyExc_TypeError, "pixel must be %d bpp", self->bpp);
+ return -1;
+ }
+
+ if (PyInt_Check(py_x)) {
+ if (PyInt_Check(py_y)) {
+ x = PyInt_AsLong(py_x);
+ y = PyInt_AsLong(py_y);
+
+ gimp_pixel_fetcher_put_pixel(self->pf, x, y, pixel);
+ return 0;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid y subscript");
+ return -1;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "invalid x subscript");
+ return -1;
+ }
+}
+
+static PyMappingMethods pf_as_mapping = {
+ (lenfunc)pf_length,
+ (binaryfunc)pf_subscript,
+ (objobjargproc)pf_ass_sub,
+};
+
+static PyObject *
+pf_get_bg_color(PyGimpPixelFetcher *self, void *closure)
+{
+ return pygimp_rgb_new(&self->bg_color);
+}
+
+static int
+pf_set_bg_color(PyGimpPixelFetcher *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete bg_color");
+ return -1;
+ }
+
+ if (!pygimp_rgb_from_pyobject(value, &self->bg_color))
+ return -1;
+
+ gimp_pixel_fetcher_set_bg_color(self->pf, &self->bg_color);
+
+ return 0;
+}
+
+static PyObject *
+pf_get_edge_mode(PyGimpPixelFetcher *self, void *closure)
+{
+ return PyInt_FromLong(self->edge_mode);
+}
+
+static int
+pf_set_edge_mode(PyGimpPixelFetcher *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete edge_mode");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ self->edge_mode = PyInt_AsLong(value);
+
+ gimp_pixel_fetcher_set_edge_mode(self->pf, self->edge_mode);
+
+ return 0;
+}
+
+static PyGetSetDef pf_getsets[] = {
+ { "bg_color", (getter)pf_get_bg_color, (setter)pf_set_bg_color },
+ { "edge_mode", (getter)pf_get_edge_mode, (setter)pf_set_edge_mode },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static void
+pf_dealloc(PyGimpPixelFetcher *self)
+{
+ gimp_pixel_fetcher_destroy(self->pf);
+
+ Py_XDECREF(self->drawable);
+
+ PyObject_DEL(self);
+}
+
+static PyObject *
+pf_repr(PyGimpPixelFetcher *self)
+{
+ PyObject *s;
+ char *name;
+
+ name = gimp_item_get_name(self->drawable->drawable->drawable_id);
+
+ if (self->shadow)
+ s = PyString_FromFormat("<gimp.PixelFetcher for drawable '%s' (shadow)>", name);
+ else
+ s = PyString_FromFormat("<gimp.PixelFetcher for drawable '%s'>", name);
+
+ g_free(name);
+
+ return s;
+}
+
+static int
+pf_init(PyGimpPixelFetcher *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpDrawable *drw;
+ gboolean shadow = FALSE;
+ GimpRGB bg_color = { 0.0, 0.0, 0.0, 1.0 };
+ GimpPixelFetcherEdgeMode edge_mode = GIMP_PIXEL_FETCHER_EDGE_NONE;
+ static char *kwlist[] = { "drawable", "shadow", "bg_color", "edge_mode",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!|iO&i:gimp.PixelFetcher.__init__",
+ kwlist,
+ &PyGimpDrawable_Type, &drw, &shadow,
+ pygimp_rgb_from_pyobject, &bg_color,
+ &edge_mode))
+ return -1;
+
+ if(!drw->drawable)
+ drw->drawable = gimp_drawable_get(drw->ID);
+
+ self->pf = gimp_pixel_fetcher_new(drw->drawable, shadow);
+
+ Py_INCREF(drw);
+ self->drawable = drw;
+
+ self->shadow = shadow;
+ self->bg_color = bg_color;
+ self->edge_mode = edge_mode;
+
+ self->bpp = gimp_drawable_bpp(drw->drawable->drawable_id);
+
+ gimp_pixel_fetcher_set_bg_color(self->pf, &bg_color);
+ gimp_pixel_fetcher_set_edge_mode(self->pf, edge_mode);
+
+ return 0;
+}
+
+PyTypeObject PyGimpPixelFetcher_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.PixelFetcher", /* tp_name */
+ sizeof(PyGimpPixelFetcher), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)pf_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)0, /* tp_compare */
+ (reprfunc)pf_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &pf_as_mapping, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ pf_methods, /* tp_methods */
+ 0, /* tp_members */
+ pf_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)pf_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
diff --git a/plug-ins/pygimp/pygimp-util.h b/plug-ins/pygimp/pygimp-util.h
new file mode 100644
index 0000000..9a06432
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-util.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYGIMP_UTIL_H_
+#define _PYGIMP_UTIL_H_
+
+#include <Python.h>
+
+#include <glib-object.h>
+
+#include <pygobject.h>
+
+#define pygimp_init_pygobject() G_STMT_START { \
+ PyObject *pygtkmodule = PyImport_ImportModule("pygtk"); \
+ if (pygtkmodule != NULL) { \
+ PyObject *mdict, *require_obj, *require_ver, *require_res; \
+ mdict = PyModule_GetDict(pygtkmodule); \
+ require_obj = PyDict_GetItemString(mdict, "require"); \
+ require_ver = PyString_FromString("2.0"); \
+ require_res = PyObject_CallFunctionObjArgs(require_obj, require_ver, \
+ NULL); \
+ Py_XDECREF(require_ver); \
+ if (require_res) { \
+ Py_DECREF(require_res); \
+ if (PyErr_Occurred()) \
+ return; \
+ } else { \
+ return; \
+ } \
+ } else { \
+ PyErr_SetString(PyExc_ImportError, \
+ "could not import pygtk"); \
+ return; \
+ } \
+ init_pygobject(); \
+} G_STMT_END
+
+#endif /* _PYGIMP_UTIL_H_ */
diff --git a/plug-ins/pygimp/pygimp-vectors.c b/plug-ins/pygimp/pygimp-vectors.c
new file mode 100644
index 0000000..7ac400a
--- /dev/null
+++ b/plug-ins/pygimp/pygimp-vectors.c
@@ -0,0 +1,992 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "pygimp.h"
+
+
+static PyObject *vectors_bezier_stroke_new(PyGimpVectors *vectors, int stroke);
+
+
+typedef struct {
+ PyObject_HEAD
+ gint32 vectors_ID;
+ int stroke;
+} PyGimpVectorsStroke;
+
+static PyObject *
+vs_get_length(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double precision;
+ double length;
+
+ static char *kwlist[] = { "precision", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:get_length", kwlist,
+ &precision))
+ return NULL;
+
+ length = gimp_vectors_stroke_get_length(self->vectors_ID, self->stroke,
+ precision);
+
+ return PyFloat_FromDouble(length);
+}
+
+static PyObject *
+vs_get_point_at_dist(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double dist, precision;
+ double x, y, slope;
+ gboolean valid;
+ PyObject *ret;
+
+ static char *kwlist[] = { "dist", "precision", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dd:get_point_at_dist", kwlist,
+ &dist, &precision))
+ return NULL;
+
+ gimp_vectors_stroke_get_point_at_dist(self->vectors_ID, self->stroke,
+ dist, precision,
+ &x, &y, &slope, &valid);
+
+ ret = PyTuple_New(4);
+ if (ret == NULL)
+ return NULL;
+
+ PyTuple_SetItem(ret, 0, PyFloat_FromDouble(x));
+ PyTuple_SetItem(ret, 1, PyFloat_FromDouble(y));
+ PyTuple_SetItem(ret, 2, PyFloat_FromDouble(slope));
+ PyTuple_SetItem(ret, 3, PyBool_FromLong(valid));
+
+ return ret;
+}
+
+static PyObject *
+vs_close(PyGimpVectorsStroke *self)
+{
+ gimp_vectors_stroke_close(self->vectors_ID, self->stroke);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+vs_translate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double off_x, off_y;
+
+ static char *kwlist[] = { "off_x", "off_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd:translate", kwlist,
+ &off_x, &off_y))
+ return NULL;
+
+ gimp_vectors_stroke_translate(self->vectors_ID, self->stroke, off_x, off_y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vs_scale(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double scale_x, scale_y;
+
+ static char *kwlist[] = { "scale_x", "scale_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dd:scale", kwlist,
+ &scale_x, &scale_y))
+ return NULL;
+
+ gimp_vectors_stroke_scale(self->vectors_ID, self->stroke, scale_x, scale_y);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vs_rotate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double center_x, center_y, angle;
+
+ static char *kwlist[] = { "center_x", "center_y", "angle", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ddd:rotate", kwlist,
+ &center_x, &center_y, &angle))
+ return NULL;
+
+ gimp_vectors_stroke_rotate(self->vectors_ID, self->stroke, center_x,
+ center_y, angle);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vs_flip(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ int flip_type;
+ double axis;
+
+ static char *kwlist[] = { "flip_type", "axis", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "id:flip", kwlist,
+ &flip_type, &axis))
+ return NULL;
+
+ gimp_vectors_stroke_flip(self->vectors_ID, self->stroke, flip_type, axis);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vs_flip_free(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double x1,y1,x2,y2;
+
+ static char *kwlist[] = { "x1", "y1", "x2", "y2", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "dddd:flip_free", kwlist,
+ &x1, &y1, &x2, &y2))
+ return NULL;
+
+ gimp_vectors_stroke_flip_free(self->vectors_ID, self->stroke,
+ x1, y1, x2, y2);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+
+static PyObject *
+vs_interpolate(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double precision;
+ double *coords;
+ int i, num_coords;
+ gboolean closed;
+ PyObject *ret, *ret_coords;
+
+ static char *kwlist[] = { "precision", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:interpolate", kwlist,
+ &precision))
+ return NULL;
+
+ coords = gimp_vectors_stroke_interpolate(self->vectors_ID, self->stroke,
+ precision, &num_coords, &closed);
+
+ ret = PyTuple_New(2);
+ if (ret == NULL)
+ return NULL;
+
+ ret_coords = PyList_New(num_coords);
+ if (ret_coords == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < num_coords; i++)
+ PyList_SetItem(ret_coords, i, PyFloat_FromDouble(coords[i]));
+
+ PyTuple_SetItem(ret, 0, ret_coords);
+ PyTuple_SetItem(ret, 1, PyBool_FromLong(closed));
+
+ return ret;
+}
+
+static PyMethodDef vs_methods[] = {
+ { "get_length", (PyCFunction)vs_get_length, METH_VARARGS | METH_KEYWORDS },
+ { "get_point_at_dist", (PyCFunction)vs_get_point_at_dist, METH_VARARGS | METH_KEYWORDS },
+ { "close", (PyCFunction)vs_close, METH_NOARGS },
+ { "translate", (PyCFunction)vs_translate, METH_VARARGS | METH_KEYWORDS },
+ { "scale", (PyCFunction)vs_scale, METH_VARARGS | METH_KEYWORDS },
+ { "rotate", (PyCFunction)vs_rotate, METH_VARARGS | METH_KEYWORDS },
+ { "flip", (PyCFunction)vs_flip, METH_VARARGS | METH_KEYWORDS },
+ { "flip_free", (PyCFunction)vs_flip_free, METH_VARARGS | METH_KEYWORDS },
+ { "interpolate", (PyCFunction)vs_interpolate, METH_VARARGS | METH_KEYWORDS },
+ { NULL, NULL, 0 }
+};
+
+static PyObject *
+vs_get_ID(PyGimpVectorsStroke *self, void *closure)
+{
+ return PyInt_FromLong(self->stroke);
+}
+
+static PyObject *
+vs_get_vectors_ID(PyGimpVectorsStroke *self, void *closure)
+{
+ return PyInt_FromLong(self->vectors_ID);
+}
+
+static PyObject *
+vs_get_points(PyGimpVectorsStroke *self, void *closure)
+{
+ double *controlpoints;
+ int i, num_points;
+ gboolean closed;
+ PyObject *ret, *ret_points;
+
+ gimp_vectors_stroke_get_points(self->vectors_ID, self->stroke,
+ &num_points, &controlpoints, &closed);
+
+ ret = PyTuple_New(2);
+ if (ret == NULL)
+ return NULL;
+
+ ret_points = PyList_New(num_points);
+ if (ret_points == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < num_points; i++)
+ PyList_SetItem(ret_points, i, PyFloat_FromDouble(controlpoints[i]));
+
+ PyTuple_SetItem(ret, 0, ret_points);
+ PyTuple_SetItem(ret, 1, PyBool_FromLong(closed));
+
+ return ret;
+}
+
+static PyGetSetDef vs_getsets[] = {
+ { "ID", (getter)vs_get_ID, (setter)0 },
+ { "vectors_ID", (getter)vs_get_vectors_ID, (setter)0 },
+ { "points", (getter)vs_get_points, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static void
+vs_dealloc(PyGimpVectorsStroke *self)
+{
+ PyObject_DEL(self);
+}
+
+static PyObject *
+vs_repr(PyGimpVectorsStroke *self)
+{
+ PyObject *s;
+ char *name;
+
+ name = gimp_item_get_name(self->vectors_ID);
+ s = PyString_FromFormat("<gimp.VectorsStroke %d of gimp.Vectors '%s'>",
+ self->stroke, name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+vs_cmp(PyGimpVectorsStroke *self, PyGimpVectorsStroke *other)
+{
+ if (self->vectors_ID == other->vectors_ID) {
+ if (self->stroke == other->stroke)
+ return 0;
+ if (self->stroke > other->stroke)
+ return -1;
+ return 1;
+ }
+ if (self->vectors_ID > other->vectors_ID)
+ return -1;
+ return 1;
+}
+
+PyTypeObject PyGimpVectorsStroke_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.VectorsStroke", /* tp_name */
+ sizeof(PyGimpVectorsStroke), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)vs_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)vs_cmp, /* tp_compare */
+ (reprfunc)vs_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ vs_methods, /* tp_methods */
+ 0, /* tp_members */
+ vs_getsets, /* tp_getset */
+ (PyTypeObject *)0, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+
+static PyObject *
+vbs_new_moveto(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyGimpVectors *vectors;
+ double x0, y0;
+ int stroke;
+
+ static char *kwlist[] = { "vectors", "x0", "y0", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!dd:new_moveto", kwlist,
+ &PyGimpVectors_Type, &vectors,
+ &x0, &y0))
+ return NULL;
+
+ stroke = gimp_vectors_bezier_stroke_new_moveto(vectors->ID, x0, y0);
+
+ return vectors_bezier_stroke_new(vectors, stroke);
+}
+
+static PyObject *
+vbs_new_ellipse(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyGimpVectors *vectors;
+ double x0, y0, radius_x, radius_y, angle;
+ int stroke;
+
+ static char *kwlist[] = { "vectors", "x0", "y0", "radius_x", "radius_y",
+ "angle", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!ddddd:new_ellipse", kwlist,
+ &PyGimpVectors_Type, &vectors,
+ &x0, &y0, &radius_x, &radius_y, &angle))
+ return NULL;
+
+ stroke = gimp_vectors_bezier_stroke_new_ellipse(vectors->ID, x0, y0,
+ radius_x, radius_y, angle);
+
+ return vectors_bezier_stroke_new(vectors, stroke);
+}
+
+static PyObject *
+vbs_lineto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0;
+
+ static char *kwlist[] = { "x0", "y0", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dd:lineto", kwlist,
+ &x0, &y0))
+ return NULL;
+
+ gimp_vectors_bezier_stroke_lineto(self->vectors_ID, self->stroke, x0, y0);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vbs_conicto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddd:conicto", kwlist,
+ &x0, &y0, &x1, &y1))
+ return NULL;
+
+ gimp_vectors_bezier_stroke_conicto(self->vectors_ID, self->stroke,
+ x0, y0, x1, y1);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vbs_cubicto(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ double x0, y0, x1, y1, x2, y2;
+
+ static char *kwlist[] = { "x0", "y0", "x1", "y1", "x2", "y2", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "dddddd:cubicto", kwlist,
+ &x0, &y0, &x1, &y1, &x2, &y2))
+ return NULL;
+
+ gimp_vectors_bezier_stroke_cubicto(self->vectors_ID, self->stroke,
+ x0, y0, x1, y1, x2, y2);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef vbs_methods[] = {
+ { "new_moveto", (PyCFunction)vbs_new_moveto, METH_VARARGS | METH_KEYWORDS | METH_CLASS },
+ { "new_ellipse", (PyCFunction)vbs_new_ellipse, METH_VARARGS | METH_KEYWORDS | METH_CLASS },
+ { "lineto", (PyCFunction)vbs_lineto, METH_VARARGS | METH_KEYWORDS },
+ { "conicto", (PyCFunction)vbs_conicto, METH_VARARGS | METH_KEYWORDS },
+ { "cubicto", (PyCFunction)vbs_cubicto, METH_VARARGS | METH_KEYWORDS },
+ { NULL, NULL, 0 }
+};
+
+static PyObject *
+vbs_repr(PyGimpVectorsStroke *self)
+{
+ PyObject *s;
+ char *name;
+
+ name = gimp_item_get_name(self->vectors_ID);
+ s = PyString_FromFormat("<gimp.VectorsBezierStroke %d of gimp.Vectors '%s'>",
+ self->stroke, name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+vbs_init(PyGimpVectorsStroke *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpVectors *vectors;
+ double *controlpoints;
+ gboolean closed = FALSE;
+ PyObject *py_controlpoints, *item;
+ int i, num_points;
+
+ static char *kwlist[] = { "vectors", "controlpoints", "closed", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!O|i:gimp.VectorsBezierStroke.__init__",
+ kwlist,
+ &PyGimpVectors_Type, &vectors,
+ &py_controlpoints, &closed))
+ return -1;
+
+ if (!PySequence_Check(py_controlpoints)) {
+ PyErr_SetString(PyExc_TypeError,
+ "controlpoints must be a sequence");
+ return -1;
+ }
+
+ num_points = PySequence_Length(py_controlpoints);
+ controlpoints = g_new(gdouble, num_points);
+
+ for (i = 0; i < num_points; i++) {
+ item = PySequence_GetItem(py_controlpoints, i);
+
+ if (!PyFloat_Check(item)) {
+ PyErr_SetString(PyExc_TypeError,
+ "controlpoints must be a sequence of floats");
+ g_free(controlpoints);
+ return -1;
+ }
+
+ controlpoints[i] = PyFloat_AsDouble(item);
+ }
+
+ self->vectors_ID = vectors->ID;
+ self->stroke =
+ gimp_vectors_stroke_new_from_points(self->vectors_ID,
+ GIMP_VECTORS_STROKE_TYPE_BEZIER,
+ num_points, controlpoints, closed);
+
+ g_free(controlpoints);
+
+ return 0;
+}
+
+PyTypeObject PyGimpVectorsBezierStroke_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.VectorsBezierStroke", /* tp_name */
+ sizeof(PyGimpVectorsStroke), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)vs_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)vs_cmp, /* tp_compare */
+ (reprfunc)vbs_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ vbs_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ &PyGimpVectorsStroke_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)vbs_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+static PyObject *
+vectors_bezier_stroke_new(PyGimpVectors *vectors, int stroke)
+{
+ PyGimpVectorsStroke *self;
+
+ self = PyObject_NEW(PyGimpVectorsStroke, &PyGimpVectorsBezierStroke_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->vectors_ID = vectors->ID;
+ self->stroke = stroke;
+
+ return (PyObject *)self;
+}
+
+
+static PyObject *
+vectors_remove_stroke(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
+{
+ int stroke_id ;
+ /* PyGimpVectorsStroke *stroke; */
+ PyObject *stroke = NULL;
+
+ static char *kwlist[] = { "stroke", NULL };
+
+ PyArg_ParseTupleAndKeywords(args, kwargs, "O:remove_stroke", kwlist, &stroke);
+
+ if (PyInt_Check(stroke))
+ stroke_id = PyInt_AsLong(stroke);
+ else if (PyObject_IsInstance(stroke, (PyObject *) &PyGimpVectorsStroke_Type))
+ stroke_id = ((PyGimpVectorsStroke *) stroke)->stroke;
+ else {
+ PyErr_SetString(PyExc_TypeError, "stroke must be a gimp.VectorsBezierStroke object or an Integer");
+ return NULL;
+ }
+
+ gimp_vectors_remove_stroke(self->ID, stroke_id);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vectors_to_selection(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
+{
+ GimpChannelOps operation = GIMP_CHANNEL_OP_REPLACE;
+ gboolean antialias = TRUE, feather = FALSE;
+ double feather_radius_x = 0.0, feather_radius_y = 0.0;
+
+ static char *kwlist[] = { "operation", "antialias", "feather",
+ "feather_radius_x", "feather_radius_y", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "|iiidd:to_selection", kwlist,
+ &operation, &antialias, &feather,
+ &feather_radius_x, &feather_radius_y))
+ return NULL;
+
+ gimp_context_push();
+ gimp_context_set_antialias(antialias);
+ gimp_context_set_feather(feather);
+ gimp_context_set_feather_radius(feather_radius_x, feather_radius_y);
+ gimp_image_select_item(gimp_item_get_image(self->ID), operation, self->ID);
+ gimp_context_pop();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vectors_parasite_find(PyGimpVectors *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_find", &name))
+ return NULL;
+
+ return pygimp_parasite_new(gimp_item_get_parasite(self->ID, name));
+}
+
+static PyObject *
+vectors_parasite_attach(PyGimpVectors *self, PyObject *args)
+{
+ PyGimpParasite *parasite;
+
+ if (!PyArg_ParseTuple(args, "O!:parasite_attach", &PyGimpParasite_Type,
+ &parasite))
+ return NULL;
+
+ if (!gimp_item_attach_parasite(self->ID, parasite->para)) {
+ PyErr_Format(pygimp_error,
+ "could not attach parasite '%s' to vectors (ID %d)",
+ parasite->para->name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vectors_parasite_detach(PyGimpVectors *self, PyObject *args)
+{
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s:parasite_detach", &name))
+ return NULL;
+
+ if (!gimp_item_detach_parasite(self->ID, name)) {
+ PyErr_Format(pygimp_error,
+ "could not detach parasite '%s' from vectors (ID %d)",
+ name, self->ID);
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+vectors_parasite_list(PyGimpVectors *self)
+{
+ gint num_parasites;
+ gchar **parasites;
+ PyObject *ret;
+ gint i;
+
+ parasites = gimp_item_get_parasite_list(self->ID, &num_parasites);
+
+ ret = PyTuple_New(num_parasites);
+
+ for (i = 0; i < num_parasites; i++)
+ PyTuple_SetItem(ret, i, PyString_FromString(parasites[i]));
+
+ g_strfreev(parasites);
+ return ret;
+}
+
+static PyMethodDef vectors_methods[] = {
+ { "remove_stroke",
+ (PyCFunction)vectors_remove_stroke,
+ METH_VARARGS | METH_KEYWORDS },
+ { "to_selection",
+ (PyCFunction)vectors_to_selection,
+ METH_VARARGS | METH_KEYWORDS },
+ { "parasite_find",
+ (PyCFunction)vectors_parasite_find,
+ METH_VARARGS },
+ { "parasite_attach",
+ (PyCFunction)vectors_parasite_attach,
+ METH_VARARGS },
+ { "parasite_detach",
+ (PyCFunction)vectors_parasite_detach,
+ METH_VARARGS },
+ { "parasite_list",
+ (PyCFunction)vectors_parasite_list,
+ METH_NOARGS },
+ { NULL, NULL, 0 }
+};
+
+static PyObject *
+vectors_get_image(PyGimpVectors *self, void *closure)
+{
+ return pygimp_image_new(gimp_item_get_image(self->ID));
+}
+
+static PyObject *
+vectors_get_ID(PyGimpVectors *self, void *closure)
+{
+ return PyInt_FromLong(self->ID);
+}
+
+static PyObject *
+vectors_get_name(PyGimpVectors *self, void *closure)
+{
+ return PyString_FromString(gimp_item_get_name(self->ID));
+}
+
+static int
+vectors_set_name(PyGimpVectors *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete name");
+ return -1;
+ }
+
+ if (!PyString_Check(value) && !PyUnicode_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_name(self->ID, PyString_AsString(value));
+
+ return 0;
+}
+
+static PyObject *
+vectors_get_visible(PyGimpVectors *self, void *closure)
+{
+ return PyBool_FromLong(gimp_item_get_visible(self->ID));
+}
+
+static int
+vectors_set_visible(PyGimpVectors *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete visible");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_visible(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+vectors_get_linked(PyGimpVectors *self, void *closure)
+{
+ return PyBool_FromLong(gimp_item_get_linked(self->ID));
+}
+
+static int
+vectors_set_linked(PyGimpVectors *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete linked");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_linked(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+vectors_get_tattoo(PyGimpVectors *self, void *closure)
+{
+ return PyInt_FromLong(gimp_item_get_tattoo(self->ID));
+}
+
+static int
+vectors_set_tattoo(PyGimpVectors *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "cannot delete tattoo");
+ return -1;
+ }
+
+ if (!PyInt_Check(value)) {
+ PyErr_SetString(PyExc_TypeError, "type mismatch");
+ return -1;
+ }
+
+ gimp_item_set_tattoo(self->ID, PyInt_AsLong(value));
+
+ return 0;
+}
+
+static PyObject *
+vectors_get_strokes(PyGimpVectors *self, void *closure)
+{
+ int *strokes;
+ int i, num_strokes;
+ PyObject *ret;
+
+ strokes = gimp_vectors_get_strokes(self->ID, &num_strokes);
+
+ ret = PyList_New(num_strokes);
+ if (ret == NULL)
+ return NULL;
+
+ for (i = 0; i < num_strokes; i++)
+ PyList_SetItem(ret, i, vectors_bezier_stroke_new(self, strokes[i]));
+
+ g_free(strokes);
+
+ return ret;
+}
+
+static PyGetSetDef vectors_getsets[] = {
+ { "ID", (getter)vectors_get_ID, (setter)0 },
+ { "image", (getter)vectors_get_image, (setter)0 },
+ { "name", (getter)vectors_get_name, (setter)vectors_set_name },
+ { "visible", (getter)vectors_get_visible, (setter)vectors_set_visible },
+ { "linked", (getter)vectors_get_linked, (setter)vectors_set_linked },
+ { "tattoo", (getter)vectors_get_tattoo, (setter)vectors_set_tattoo },
+ { "strokes", (getter)vectors_get_strokes, (setter)0 },
+ { NULL, (getter)0, (setter)0 }
+};
+
+static void
+vectors_dealloc(PyGimpVectors *self)
+{
+ PyObject_DEL(self);
+}
+
+static PyObject *
+vectors_repr(PyGimpVectors *self)
+{
+ PyObject *s;
+ char *name;
+
+ name = gimp_item_get_name(self->ID);
+ s = PyString_FromFormat("<gimp.Vectors '%s'>", name ? name : "(null)");
+ g_free(name);
+
+ return s;
+}
+
+static int
+vectors_cmp(PyGimpVectors *self, PyGimpVectors *other)
+{
+ if (self->ID == other->ID)
+ return 0;
+ if (self->ID > other->ID)
+ return -1;
+ return 1;
+}
+
+static int
+vectors_init(PyGimpVectors *self, PyObject *args, PyObject *kwargs)
+{
+ PyGimpImage *img;
+ char *name;
+
+ static char *kwlist[] = { "image", "name", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O!s:gimp.Vectors.__init__",
+ kwlist,
+ &PyGimpImage_Type, &img, &name))
+ return -1;
+
+ self->ID = gimp_vectors_new(img->ID, name);
+
+ if (self->ID < 0) {
+ PyErr_Format(pygimp_error,
+ "could not create vectors '%s' on image (ID %d)",
+ name, img->ID);
+ return -1;
+ }
+
+ return 0;
+}
+
+PyTypeObject PyGimpVectors_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "gimp.Vectors", /* tp_name */
+ sizeof(PyGimpVectors), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)vectors_dealloc, /* tp_dealloc */
+ (printfunc)0, /* tp_print */
+ (getattrfunc)0, /* tp_getattr */
+ (setattrfunc)0, /* tp_setattr */
+ (cmpfunc)vectors_cmp, /* tp_compare */
+ (reprfunc)vectors_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)0, /* tp_hash */
+ (ternaryfunc)0, /* tp_call */
+ (reprfunc)0, /* tp_str */
+ (getattrofunc)0, /* tp_getattro */
+ (setattrofunc)0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NULL, /* Documentation string */
+ (traverseproc)0, /* tp_traverse */
+ (inquiry)0, /* tp_clear */
+ (richcmpfunc)0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ (getiterfunc)0, /* tp_iter */
+ (iternextfunc)0, /* tp_iternext */
+ vectors_methods, /* tp_methods */
+ 0, /* tp_members */
+ vectors_getsets, /* tp_getset */
+ &PyGimpItem_Type, /* tp_base */
+ (PyObject *)0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)vectors_init, /* tp_init */
+ (allocfunc)0, /* tp_alloc */
+ (newfunc)0, /* tp_new */
+};
+
+PyObject *
+pygimp_vectors_new(gint32 ID)
+{
+ PyGimpVectors *self;
+
+ if (!gimp_item_is_valid(ID)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ self = PyObject_NEW(PyGimpVectors, &PyGimpVectors_Type);
+
+ if (self == NULL)
+ return NULL;
+
+ self->ID = ID;
+
+ return (PyObject *)self;
+}
diff --git a/plug-ins/pygimp/pygimp.h b/plug-ins/pygimp/pygimp.h
new file mode 100644
index 0000000..c7d927b
--- /dev/null
+++ b/plug-ins/pygimp/pygimp.h
@@ -0,0 +1,139 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 1997-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYGIMP_H_
+#define _PYGIMP_H_
+
+#include <Python.h>
+
+#include <libgimp/gimp.h>
+
+#define _INSIDE_PYGIMP_
+#include "pygimp-api.h"
+
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+#define PY_SSIZE_T_MAX INT_MAX
+#define PY_SSIZE_T_MIN INT_MIN
+#define PyInt_AsSsize_t(o) PyInt_AsLong(o)
+#endif
+
+G_BEGIN_DECLS
+
+extern PyObject *pygimp_error;
+
+PyObject *pygimp_param_to_tuple(int nparams, const GimpParam *params);
+GimpParam *pygimp_param_from_tuple(PyObject *args, const GimpParamDef *ptype,
+ int nparams);
+
+
+extern PyTypeObject PyGimpPDB_Type;
+#define pygimp_pdb_check(v) (PyObject_TypeCheck(v, &PyGimpPDB_Type))
+PyObject *pygimp_pdb_new(void);
+
+extern PyTypeObject PyGimpPDBFunction_Type;
+#define pygimp_pdb_function_check(v) (PyObject_TypeCheck(v, &PyGimpPDBFunction_Type))
+PyObject *pygimp_pdb_function_new(const char *name, const char *blurb,
+ const char *help, const char *author,
+ const char *copyright, const char *date,
+ GimpPDBProcType proc_type,
+ int n_params, int n_return_vals,
+ GimpParamDef *params,
+ GimpParamDef *return_vals);
+
+extern PyTypeObject PyGimpImage_Type;
+#define pygimp_image_check(v) (PyObject_TypeCheck(v, &PyGimpImage_Type))
+PyObject *pygimp_image_new(gint32 ID);
+
+extern PyTypeObject PyGimpDisplay_Type;
+#define pygimp_display_check(v) (PyObject_TypeCheck(v, &PyGimpDisplay_Type))
+PyObject *pygimp_display_new(gint32 ID);
+
+extern PyTypeObject PyGimpItem_Type;
+#define pygimp_item_check(v) (PyObject_TypeCheck(v, &PyGimpItem_Type))
+PyObject *pygimp_item_new(gint32 ID);
+
+extern PyTypeObject PyGimpDrawable_Type;
+#define pygimp_drawable_check(v) (PyObject_TypeCheck(v, &PyGimpDrawable_Type))
+PyObject *pygimp_drawable_new(GimpDrawable *drawable, gint32 ID);
+
+extern PyTypeObject PyGimpLayer_Type;
+#define pygimp_layer_check(v) (PyObject_TypeCheck(v, &PyGimpLayer_Type))
+PyObject *pygimp_layer_new(gint32 ID);
+
+extern PyTypeObject PyGimpGroupLayer_Type;
+#define pygimp_layer__group_check(v) (PyObject_TypeCheck(v, &PyGimpGroupLayer_Type))
+PyObject *pygimp_group_layer_new(gint32 ID);
+
+extern PyTypeObject PyGimpChannel_Type;
+#define pygimp_channel_check(v) (PyObject_TypeCheck(v, &PyGimpChannel_Type))
+PyObject *pygimp_channel_new(gint32 ID);
+
+typedef struct {
+ PyObject_HEAD
+ GimpTile *tile;
+ PyGimpDrawable *drawable; /* we keep a reference to the drawable */
+} PyGimpTile;
+
+extern PyTypeObject PyGimpTile_Type;
+#define pygimp_tile_check(v) (PyObject_TypeCheck(v, &PyGimpTile_Type))
+PyObject *pygimp_tile_new(GimpTile *tile, PyGimpDrawable *drw);
+
+typedef struct {
+ PyObject_HEAD
+ GimpPixelRgn pr;
+ PyGimpDrawable *drawable; /* keep the drawable around */
+} PyGimpPixelRgn;
+
+extern PyTypeObject PyGimpPixelRgn_Type;
+#define pygimp_pixel_rgn_check(v) (PyObject_TypeCheck(v, &PyGimpPixelRgn_Type))
+PyObject *pygimp_pixel_rgn_new(PyGimpDrawable *drw, int x, int y,
+ int w, int h, int dirty, int shadow);
+
+typedef struct {
+ PyObject_HEAD
+ GimpParasite *para;
+} PyGimpParasite;
+
+extern PyTypeObject PyGimpParasite_Type;
+#define pygimp_parasite_check(v) (PyObject_TypeCheck(v, &PyGimpParasite_Type))
+PyObject *pygimp_parasite_new(GimpParasite *para);
+
+extern PyTypeObject PyGimpVectors_Type;
+#define pygimp_vectors_check(v) (PyObject_TypeCheck(v, &PyGimpVectors_Type))
+PyObject *pygimp_vectors_new(gint32 vectors_ID);
+
+extern PyTypeObject PyGimpVectorsStroke_Type;
+extern PyTypeObject PyGimpVectorsBezierStroke_Type;
+
+typedef struct {
+ PyObject_HEAD
+ GimpPixelFetcher *pf;
+ PyGimpDrawable *drawable; /* keep the drawable around */
+ gboolean shadow;
+ GimpRGB bg_color;
+ GimpPixelFetcherEdgeMode edge_mode;
+ int bpp;
+} PyGimpPixelFetcher;
+
+extern PyTypeObject PyGimpPixelFetcher_Type;
+#define pygimp_pixel_fetcher_check(v) (PyObject_TypeCheck(v, &PyGimpPixelFetcher_Type))
+
+G_END_DECLS
+
+#endif
diff --git a/plug-ins/pygimp/pygimpcolor-api.h b/plug-ins/pygimp/pygimpcolor-api.h
new file mode 100644
index 0000000..b741538
--- /dev/null
+++ b/plug-ins/pygimp/pygimpcolor-api.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2005-2006 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYGIMPCOLOR_API_H_
+#define _PYGIMPCOLOR_API_H_
+
+#include <Python.h>
+
+#include <libgimpcolor/gimpcolor.h>
+
+struct _PyGimpColor_Functions {
+ PyTypeObject *RGB_Type;
+ PyObject *(* rgb_new)(const GimpRGB *rgb);
+ PyTypeObject *HSV_Type;
+ PyObject *(* hsv_new)(const GimpHSV *hsv);
+ PyTypeObject *HSL_Type;
+ PyObject *(* hsl_new)(const GimpHSL *hsl);
+ PyTypeObject *CMYK_Type;
+ PyObject *(* cmyk_new)(const GimpCMYK *cmyk);
+ int (* rgb_from_pyobject)(PyObject *object, GimpRGB *color);
+};
+
+#ifndef _INSIDE_PYGIMPCOLOR_
+
+#if defined(NO_IMPORT) || defined(NO_IMPORT_PYGIMPCOLOR)
+extern struct _PyGimpColor_Functions *_PyGimpColor_API;
+#else
+struct _PyGimpColor_Functions *_PyGimpColor_API;
+#endif
+
+#define PyGimpRGB_Type (_PyGimpColor_API->RGB_Type)
+#define PyGimpHSV_Type (_PyGimpColor_API->HSV_Type)
+#define PyGimpHSL_Type (_PyGimpColor_API->HSL_Type)
+#define PyGimpCMYK_Type (_PyGimpColor_API->CMYK_Type)
+
+#define pygimp_rgb_check(v) (pyg_boxed_check((v), GIMP_TYPE_RGB))
+#define pygimp_hsv_check(v) (pyg_boxed_check((v), GIMP_TYPE_HSV))
+#define pygimp_hsl_check(v) (pyg_boxed_check((v), GIMP_TYPE_HSL))
+#define pygimp_cmyk_check(v) (pyg_boxed_check((v), GIMP_TYPE_CMYK))
+
+#define pygimp_rgb_new (_PyGimpColor_API->rgb_new)
+#define pygimp_hsv_new (_PyGimpColor_API->hsv_new)
+#define pygimp_hsl_new (_PyGimpColor_API->hsl_new)
+#define pygimp_cmyk_new (_PyGimpColor_API->cmyk_new)
+
+#define pygimp_rgb_from_pyobject (_PyGimpColor_API->rgb_from_pyobject)
+
+#define init_pygimpcolor() G_STMT_START { \
+ PyObject *gimpcolormodule = PyImport_ImportModule("gimpcolor"); \
+ if (gimpcolormodule != NULL) { \
+ PyObject *mdict = PyModule_GetDict(gimpcolormodule); \
+ PyObject *cobject = PyDict_GetItemString(mdict, "_PyGimpColor_API"); \
+ if (PyCObject_Check(cobject)) \
+ _PyGimpColor_API = PyCObject_AsVoidPtr(cobject); \
+ else { \
+ PyErr_SetString(PyExc_RuntimeError, \
+ "could not find _PyGimpColor_API object"); \
+ return; \
+ } \
+ } else { \
+ PyErr_SetString(PyExc_ImportError, \
+ "could not import gimpcolor"); \
+ return; \
+ } \
+} G_STMT_END
+
+#endif /* ! _INSIDE_PYGIMPCOLOR_ */
+
+#endif /* _PYGIMPCOLOR_API_H_ */
diff --git a/plug-ins/pygimp/pygimpcolor.h b/plug-ins/pygimp/pygimpcolor.h
new file mode 100644
index 0000000..2e98329
--- /dev/null
+++ b/plug-ins/pygimp/pygimpcolor.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * Gimp-Python - allows the writing of Gimp plugins in Python.
+ * Copyright (C) 2003 Manish Singh <yosh@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYGIMPCOLOR_H_
+#define _PYGIMPCOLOR_H_
+
+#include <Python.h>
+
+#include <pygobject.h>
+
+#include <libgimp/gimp.h>
+
+G_BEGIN_DECLS
+
+extern PyTypeObject PyGimpRGB_Type;
+#define pygimp_rgb_check(v) (pyg_boxed_check((v), GIMP_TYPE_RGB))
+PyObject *pygimp_rgb_new(const GimpRGB *rgb);
+
+extern PyTypeObject PyGimpHSV_Type;
+#define pygimp_hsv_check(v) (pyg_boxed_check((v), GIMP_TYPE_HSV))
+PyObject *pygimp_hsv_new(const GimpHSV *hsv);
+
+extern PyTypeObject PyGimpHSL_Type;
+#define pygimp_hsl_check(v) (pyg_boxed_check((v), GIMP_TYPE_HSL))
+PyObject *pygimp_hsl_new(const GimpHSL *hsl);
+
+extern PyTypeObject PyGimpCMYK_Type;
+#define pygimp_cmyk_check(v) (pyg_boxed_check((v), GIMP_TYPE_CMYK))
+PyObject *pygimp_cmyk_new(const GimpCMYK *cmyk);
+
+int pygimp_rgb_from_pyobject(PyObject *object, GimpRGB *color);
+
+G_END_DECLS
+
+#endif
diff --git a/plug-ins/screenshot/Makefile.am b/plug-ins/screenshot/Makefile.am
new file mode 100644
index 0000000..3db7e5b
--- /dev/null
+++ b/plug-ins/screenshot/Makefile.am
@@ -0,0 +1,73 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+screenshot_RC = screenshot-win32-res.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(XFIXES_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(SCREENSHOT_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(screenshot_RC)
+
+libexecdir = $(gimpplugindir)/plug-ins/screenshot
+
+libexec_PROGRAMS = screenshot
+
+EXTRA_PROGRAMS = screenshot
+
+screenshot_SOURCES = \
+ screenshot.c \
+ screenshot.h \
+ screenshot-freedesktop.c \
+ screenshot-freedesktop.h \
+ screenshot-icon.h \
+ screenshot-kwin.c \
+ screenshot-kwin.h \
+ screenshot-osx.c \
+ screenshot-osx.h \
+ screenshot-x11.c \
+ screenshot-x11.h \
+ screenshot-win32.rc \
+ screenshot-win32.c \
+ screenshot-win32.h \
+ screenshot-win32-dwm-api.h \
+ screenshot-win32-magnification-api.h \
+ screenshot-win32-resource.h
+
+EXTRA_DIST = \
+ screenshot-win32-select.cur \
+ screenshot-win32-small.ico \
+ screenshot-win32.ico
+
+if OS_WIN32
+screenshot-win32-res.o: screenshot-win32.rc screenshot-win32-select.cur screenshot-win32-small.ico screenshot-win32.ico
+ $(WINDRES) $(srcdir)/screenshot-win32.rc screenshot-win32-res.o
+endif
diff --git a/plug-ins/screenshot/Makefile.in b/plug-ins/screenshot/Makefile.in
new file mode 100644
index 0000000..67d0848
--- /dev/null
+++ b/plug-ins/screenshot/Makefile.in
@@ -0,0 +1,1031 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = screenshot$(EXEEXT)
+EXTRA_PROGRAMS = screenshot$(EXEEXT)
+subdir = plug-ins/screenshot
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_screenshot_OBJECTS = screenshot.$(OBJEXT) \
+ screenshot-freedesktop.$(OBJEXT) screenshot-kwin.$(OBJEXT) \
+ screenshot-osx.$(OBJEXT) screenshot-x11.$(OBJEXT) \
+ screenshot-win32.$(OBJEXT)
+screenshot_OBJECTS = $(am_screenshot_OBJECTS)
+screenshot_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+screenshot_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(screenshot_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/screenshot-freedesktop.Po \
+ ./$(DEPDIR)/screenshot-kwin.Po ./$(DEPDIR)/screenshot-osx.Po \
+ ./$(DEPDIR)/screenshot-win32.Po ./$(DEPDIR)/screenshot-x11.Po \
+ ./$(DEPDIR)/screenshot.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(screenshot_SOURCES)
+DIST_SOURCES = $(screenshot_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/screenshot
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_TRUE@screenshot_RC = screenshot-win32-res.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(XFIXES_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(SCREENSHOT_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(screenshot_RC)
+
+screenshot_SOURCES = \
+ screenshot.c \
+ screenshot.h \
+ screenshot-freedesktop.c \
+ screenshot-freedesktop.h \
+ screenshot-icon.h \
+ screenshot-kwin.c \
+ screenshot-kwin.h \
+ screenshot-osx.c \
+ screenshot-osx.h \
+ screenshot-x11.c \
+ screenshot-x11.h \
+ screenshot-win32.rc \
+ screenshot-win32.c \
+ screenshot-win32.h \
+ screenshot-win32-dwm-api.h \
+ screenshot-win32-magnification-api.h \
+ screenshot-win32-resource.h
+
+EXTRA_DIST = \
+ screenshot-win32-select.cur \
+ screenshot-win32-small.ico \
+ screenshot-win32.ico
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/screenshot/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/screenshot/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+screenshot$(EXEEXT): $(screenshot_OBJECTS) $(screenshot_DEPENDENCIES) $(EXTRA_screenshot_DEPENDENCIES)
+ @rm -f screenshot$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(screenshot_OBJECTS) $(screenshot_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot-freedesktop.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot-kwin.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot-osx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot-win32.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot-x11.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/screenshot.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/screenshot-freedesktop.Po
+ -rm -f ./$(DEPDIR)/screenshot-kwin.Po
+ -rm -f ./$(DEPDIR)/screenshot-osx.Po
+ -rm -f ./$(DEPDIR)/screenshot-win32.Po
+ -rm -f ./$(DEPDIR)/screenshot-x11.Po
+ -rm -f ./$(DEPDIR)/screenshot.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/screenshot-freedesktop.Po
+ -rm -f ./$(DEPDIR)/screenshot-kwin.Po
+ -rm -f ./$(DEPDIR)/screenshot-osx.Po
+ -rm -f ./$(DEPDIR)/screenshot-win32.Po
+ -rm -f ./$(DEPDIR)/screenshot-x11.Po
+ -rm -f ./$(DEPDIR)/screenshot.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+@OS_WIN32_TRUE@screenshot-win32-res.o: screenshot-win32.rc screenshot-win32-select.cur screenshot-win32-small.ico screenshot-win32.ico
+@OS_WIN32_TRUE@ $(WINDRES) $(srcdir)/screenshot-win32.rc screenshot-win32-res.o
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/screenshot/screenshot-freedesktop.c b/plug-ins/screenshot/screenshot-freedesktop.c
new file mode 100644
index 0000000..206d70a
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-freedesktop.c
@@ -0,0 +1,199 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2016 Michael Natterer <mitch@gimp.org>
+ * Copyright 2017 Jehan <jehan@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-freedesktop.h"
+
+
+static GDBusProxy *proxy = NULL;
+
+gboolean
+screenshot_freedesktop_available (void)
+{
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.freedesktop.portal.Desktop",
+ "/org/freedesktop/portal/desktop",
+ "org.freedesktop.portal.Screenshot",
+ NULL, NULL);
+
+ if (proxy)
+ {
+ GError *error = NULL;
+
+ g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &error);
+ if (! error)
+ return TRUE;
+
+ g_clear_error (&error);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+ }
+
+ return FALSE;
+}
+
+ScreenshotCapabilities
+screenshot_freedesktop_get_capabilities (void)
+{
+ /* Portal has no capabilities other than root screenshot! */
+ return 0;
+}
+
+static void
+screenshot_freedesktop_dbus_signal (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gint32 *image_ID)
+{
+ if (g_strcmp0 (signal_name, "Response") == 0)
+ {
+ GVariant *results;
+ guint32 response;
+
+ g_variant_get (parameters, "(u@a{sv})",
+ &response,
+ &results);
+
+ /* Possible values:
+ * 0: Success, the request is carried out
+ * 1: The user cancelled the interaction
+ * 2: The user interaction was ended in some other way
+ * Cf. https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.Request.xml
+ */
+ if (response == 0)
+ {
+ gchar *uri;
+
+ if (g_variant_lookup (results, "uri", "s", &uri))
+ {
+ GFile *file = g_file_new_for_uri (uri);
+ gchar *path = g_file_get_path (file);
+
+ *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ path, path);
+ gimp_image_set_filename (*image_ID, "screenshot.png");
+
+ /* Delete the actual file. */
+ g_file_delete (file, NULL, NULL);
+
+ g_object_unref (file);
+ g_free (path);
+ g_free (uri);
+ }
+ }
+
+ g_variant_unref (results);
+ /* Quit anyway. */
+ gtk_main_quit ();
+ }
+}
+
+GimpPDBStatusType
+screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ GVariant *retval;
+ gchar *opath = NULL;
+
+ if (shootvals->shoot_type != SHOOT_ROOT)
+ {
+ /* This should not happen. */
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (shootvals->screenshot_delay > 0)
+ screenshot_delay (shootvals->screenshot_delay);
+
+ retval = g_dbus_proxy_call_sync (proxy, "Screenshot",
+ g_variant_new ("(sa{sv})", "", NULL),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, error);
+ g_object_unref (proxy);
+ proxy = NULL;
+ if (retval)
+ {
+ g_variant_get (retval, "(o)", &opath);
+ g_variant_unref (retval);
+ }
+
+ if (opath)
+ {
+ GDBusProxy *proxy2 = NULL;
+
+ proxy2 = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.freedesktop.portal.Desktop",
+ opath,
+ "org.freedesktop.portal.Request",
+ NULL, NULL);
+ *image_ID = 0;
+ g_signal_connect (proxy2, "g-signal",
+ G_CALLBACK (screenshot_freedesktop_dbus_signal),
+ image_ID);
+
+ gtk_main ();
+ g_object_unref (proxy2);
+ g_free (opath);
+
+ /* Signal got a response. */
+ if (*image_ID)
+ {
+ GimpColorProfile *profile;
+
+ /* Just assign profile of current monitor. This will work only
+ * as long as this is a single-display setup.
+ * We need to figure out how to do better color management for
+ * portal screenshots.
+ * TODO!
+ */
+ profile = gimp_screen_get_color_profile (screen,
+ shootvals->monitor);
+ if (profile)
+ {
+ gimp_image_set_color_profile (*image_ID, profile);
+ g_object_unref (profile);
+ }
+
+ return GIMP_PDB_SUCCESS;
+ }
+ }
+
+ return GIMP_PDB_EXECUTION_ERROR;
+}
diff --git a/plug-ins/screenshot/screenshot-freedesktop.h b/plug-ins/screenshot/screenshot-freedesktop.h
new file mode 100644
index 0000000..a3a366b
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-freedesktop.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_FREEDESKTOP_H__
+#define __SCREENSHOT_FREEDESKTOP_H__
+
+
+gboolean screenshot_freedesktop_available (void);
+
+ScreenshotCapabilities screenshot_freedesktop_get_capabilities (void);
+
+GimpPDBStatusType screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+
+#endif /* __SCREENSHOT_FREEDESKTOP_H__ */
diff --git a/plug-ins/screenshot/screenshot-icon.h b/plug-ins/screenshot/screenshot-icon.h
new file mode 100644
index 0000000..4e4de0c
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-icon.h
@@ -0,0 +1,80 @@
+/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (screenshot_icon)
+#endif
+#ifdef __GNUC__
+static const guint8 screenshot_icon[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 screenshot_icon[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1582) */
+ "\0\0\6F"
+ /* pixdata_type (0x2010002) */
+ "\2\1\0\2"
+ /* rowstride (88) */
+ "\0\0\0X"
+ /* width (22) */
+ "\0\0\0\26"
+ /* height (22) */
+ "\0\0\0\26"
+ /* pixel_data: */
+ "\213\0\0\0\0\1\242\242\242\5\203\242\242\242\31\221\0\0\0\0\2\27\27\26"
+ "D\40\40\40\372\203)))\375\2\32\32\32\362\26\26\25""0\217\0\0\0\0\2\2"
+ "\2\2\322\307\310\307\377\203\377\377\377\377\2\263\263\262\363\0\0\0"
+ "\314\217\0\0\0\0\2\6\6\6\334\206\207\205\377\202\252\253\251\377\3\252"
+ "\252\251\377ghe\376\1\1\1\320\217\0\0\0\0\2\11\11\11\346./-\345\202G"
+ "HE\25\3JKH\32""01/\364\5\5\5\333\212\0\0\0\0\17\26\26\25\31\26\26\25"
+ "0\26\26\25g\0\0\0\314+,+\331LMK\375EFD\362ffe\347iig\346lmk\346RSQ\362"
+ "\13\13\13\360\0\0\0\314\2\2\2\321\26\26\25V\203\0\0\0\0#\26\26\25\31"
+ "\26\26\25""0\26\26\25n\3\3\3\331\2\2\2\332JJI\355\215\215\215\372\246"
+ "\246\246\347\267\270\266\362\177\202~\377BC\77\377TUR\377\\]Y\377gid"
+ "\377[]X\377\204\206\204\374wxw\276\224\225\224\314LLK\343\26\26\25D\0"
+ "\0\0\0\26\26\25D\3\3\3\341\17\17\17\373ghg\377\237\240\236\377\273\274"
+ "\272\377\302\303\300\377\272\273\270\377\200\201\177\377zzz\377tws\377"
+ "\220\223\217\377\221\225\221\377\224\227\223\377\202\226\232\226\377"
+ "K\177\202}\377xyu\377\217\221\215\377\226\227\226\365\0\0\0\371\0\0\0"
+ "\0\2\2\2\330\257\261\257\377\275\276\273\377\262\263\260\377UWT\377Q"
+ "SP\377suq\377]^[\377\304\304\304\377\303\303\303\377kmi\377MNJ\377WZ"
+ "X\377[`_\377aeb\377gid\377}\200{\377SUQ\377FGE\377\200\201\200\377\12"
+ "\12\12\357\0\0\0\0\4\4\4\345xyv\377\241\242\236\377\210\212\205\377v"
+ "xt\377\220\222\217\377GIF\377+,)\377```\377jji\377UXV\377y\204\210\377"
+ "\203\215\220\377~\204\207\377flo\377PW\\\377SWU\377JLI\377452\377tut"
+ "\377\12\12\12\357\0\0\0\0\4\4\4\344opm\377\221\223\217\377}\177{\377"
+ "\215\217\213\377`b_\377()'\377*+)\377785\377VXV\377u~\202\377nsu\377"
+ "VYZ\377OST\377OQS\377JOP\377^cd\377698\377!\"!\377llk\377\12\12\12\357"
+ "\0\0\0\0\3\3\3\345npl\377\220\221\216\377\202UWS\377\24GIF\377\40!\37"
+ "\377*+)\3779:7\377^ce\377NRT\377UXY\377(,.\377\22\24\25\377!$&\377IL"
+ "M\3778=\77\377RVX\377''&\377klk\377\12\12\12\357\0\0\0\1\3\3\3\345no"
+ "l\377\220\221\216\377\202UWS\377\24GIF\377\40!\37\377*+)\3778:8\377T"
+ "XY\377289\377\23\25\26\377\16\16\16\377\1\1\1\377\2\2\2\377\24\25\25"
+ "\377.34\377289\377011\377aa`\377\11\11\11\360\0\0\0\3\3\3\3\345nol\377"
+ "\204\205\202\377\202UWS\377\24GIF\377\40!\37\377*+)\377\77BA\37728:\377"
+ "\34\40!\377\31\32\32\377\377\377\377\377hhh\377\40\40\40\377\22\22\22"
+ "\377.00\377.46\377:>\77\377`a`\377\11\11\10\361\0\0\0\7\3\3\3\345mok"
+ "\377z{x\377\202UWS\377\24GIF\377\40!\37\377*+)\377HJI\377.46\377\25\27"
+ "\30\377\40\40\40\377hhh\377\232\232\232\377}}}\377'''\3779;;\377/57\377"
+ "599\377``_\377\11\11\10\362\0\0\0\15\3\3\3\345lnk\377rsp\377\202UWS\377"
+ "\24GIF\377\40!\37\377,-*\377>@>\377.46\377\35\37\40\377\1\1\1\377888"
+ "\377\214\214\214\377\213\213\213\377AAA\377JKK\377/57\3776:;\377WXV\377"
+ "\6\6\6\364\0\0\0\21\0\0\0\371\201\202\177\377mok\377\202UWS\377iGIF\377"
+ "MNM\377RRR\372ghg\364[`b\377+/0\377\17\17\17\377\"\"\"\377'''\377HHH"
+ "\377\263\263\263\377SUU\377W\\^\377CFF\374<<<\376\20\20\17\317\0\0\0"
+ "\20\24\24\23]\4\4\4\353\177\177~\375|}z\373}~|\374hih\3769:9\371\0\0"
+ "\0\347\0\0\0\351\7\7\7\367.46\377256\377\40\40\40\377(((\377:::\377`"
+ "ab\3779>\77\377\11\13\13\376\0\0\0\353\0\0\0\300\14\14\13H\0\0\0\15\0"
+ "\0\0\4\23\23\22L\0\0\0\332\0\0\0\341\0\0\0\346\0\0\0\341\14\14\13{\0"
+ "\0\0V\0\0\0a\0\0\0l\10\11\11\371\\^_\377LOP\377ILL\377LOQ\377QUV\377"
+ "\16\17\17\377\34\37\40\244\0\0\0`\0\0\0""4\0\0\0\27\0\0\0\7\0\0\0\1\0"
+ "\0\0\3\0\0\0\10\0\0\0\21\0\0\0\34\0\0\0'\0\0\0""0\0\0\0""6\0\0\0=\0\0"
+ "\0A\0\0\0F\4\5\5\355\12\13\13\371\17\20\20\376\15\16\16\375\10\10\11"
+ "\365\7\10\11j\0\0\0G\0\0\0-\0\0\0\30\0\0\0\13\0\0\0\4\0\0\0\0\0\0\0\1"
+ "\0\0\0\2\0\0\0\4\0\0\0\11\0\0\0\16\0\0\0\23\0\0\0\31\0\0\0\35\0\0\0!"
+ "\0\0\0#\0\0\0$\0\0\0%\0\0\0$\0\0\0&\0\0\0%\0\0\0\36\0\0\0\27\0\0\0\20"
+ "\0\0\0\10\0\0\0\3\0\0\0\1\204\0\0\0\0\5\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0"
+ "\5\0\0\0\6\202\0\0\0\10\11\0\0\0\11\0\0\0\7\0\0\0\10\0\0\0\5\0\0\0\7"
+ "\0\0\0\6\0\0\0\5\0\0\0\3\0\0\0\1\202\0\0\0\0"
+};
diff --git a/plug-ins/screenshot/screenshot-kwin.c b/plug-ins/screenshot/screenshot-kwin.c
new file mode 100644
index 0000000..21db1f3
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-kwin.c
@@ -0,0 +1,207 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2016 Michael Natterer <mitch@gimp.org>
+ * Copyright 2017 Jehan <jehan@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gstdio.h> /* g_unlink() */
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-kwin.h"
+
+
+static GDBusProxy *proxy = NULL;
+
+
+gboolean
+screenshot_kwin_available (void)
+{
+ proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ "org.kde.KWin",
+ "/Screenshot",
+ "org.kde.kwin.Screenshot",
+ NULL, NULL);
+
+ if (proxy)
+ {
+ GError *error = NULL;
+
+ g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &error);
+ if (! error)
+ return TRUE;
+
+ g_clear_error (&error);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+ }
+
+ return FALSE;
+}
+
+ScreenshotCapabilities
+screenshot_kwin_get_capabilities (void)
+{
+ return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
+ SCREENSHOT_CAN_SHOOT_POINTER |
+ SCREENSHOT_CAN_SHOOT_WINDOW |
+ SCREENSHOT_CAN_PICK_WINDOW);
+ /* TODO: SCREENSHOT_CAN_SHOOT_REGION.
+ * The KDE API has "screenshotArea" method but no method to get
+ * coordinates could be found. See below.
+ */
+}
+
+GimpPDBStatusType
+screenshot_kwin_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ gchar *filename = NULL;
+ const gchar *method = NULL;
+ GVariant *args = NULL;
+ GVariant *retval;
+ gint monitor = shootvals->monitor;
+ gint32 mask;
+
+ switch (shootvals->shoot_type)
+ {
+ case SHOOT_ROOT:
+ if (shootvals->screenshot_delay > 0)
+ screenshot_delay (shootvals->screenshot_delay);
+ else
+ {
+ /* As an exception, I force a delay of at least 0.5 seconds
+ * for KWin. Because of windows effect slowly fading out, the
+ * screenshot plug-in GUI was constantly visible (with
+ * transparency as it is fading out) in 0s-delay screenshots.
+ */
+ g_usleep (500000);
+ }
+
+ method = "screenshotFullscreen";
+ args = g_variant_new ("(b)", shootvals->show_cursor);
+
+ /* FIXME: figure profile */
+ break;
+
+ case SHOOT_REGION:
+ break;
+ /* FIXME: GNOME-shell has a "SelectArea" returning coordinates
+ * which can be fed to "ScreenshotArea". KDE has the equivalent
+ * "screenshotArea", but no "SelectArea" equivalent that I could
+ * find.
+ * Also at first, I expected "interactive" method to take care of
+ * the whole selecting-are-then-screenshotting workflow, but this
+ * is apparently only made to select interactively a specific
+ * window, not an area.
+ */
+ method = "screenshotArea";
+ args = g_variant_new ("(iiii)",
+ shootvals->x1,
+ shootvals->y1,
+ shootvals->x2 - shootvals->x1,
+ shootvals->y2 - shootvals->y1);
+ args = NULL;
+
+ break;
+
+ case SHOOT_WINDOW:
+ if (shootvals->select_delay > 0)
+ screenshot_delay (shootvals->select_delay);
+
+ /* XXX I expected "screenshotWindowUnderCursor" method to be the
+ * right one, but it returns nothing, nor is there a file
+ * descriptor in argument. So I don't understand how to grab the
+ * screenshot. Also "interactive" changes the cursor to a
+ * crosshair, waiting for click, which is more helpful than
+ * immediate screenshot under cursor.
+ */
+ method = "interactive";
+ mask = (shootvals->decorate ? 1 : 0) |
+ (shootvals->show_cursor ? 1 << 1 : 0);
+ args = g_variant_new ("(i)", mask);
+
+ /* FIXME: figure monitor */
+ break;
+ }
+
+ retval = g_dbus_proxy_call_sync (proxy, method, args,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, error);
+ if (! retval)
+ goto failure;
+
+ g_variant_get (retval, "(s)",
+ &filename);
+ g_variant_unref (retval);
+
+ if (filename)
+ {
+ GimpColorProfile *profile;
+
+ *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ filename, filename);
+ gimp_image_set_filename (*image_ID, "screenshot.png");
+
+ /* This is very wrong in multi-display setups since we have no
+ * idea which profile is to be used. Let's keep it anyway and
+ * assume always the monitor 0, which will still work in common
+ * cases.
+ */
+ profile = gimp_screen_get_color_profile (screen, monitor);
+
+ if (profile)
+ {
+ gimp_image_set_color_profile (*image_ID, profile);
+ g_object_unref (profile);
+ }
+
+ g_unlink (filename);
+ g_free (filename);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+
+ return GIMP_PDB_SUCCESS;
+ }
+
+ failure:
+
+ if (filename)
+ g_free (filename);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+
+ return GIMP_PDB_EXECUTION_ERROR;
+}
diff --git a/plug-ins/screenshot/screenshot-kwin.h b/plug-ins/screenshot/screenshot-kwin.h
new file mode 100644
index 0000000..85eb58c
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-kwin.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_KWIN_H__
+#define __SCREENSHOT_KWIN_H__
+
+
+gboolean screenshot_kwin_available (void);
+
+ScreenshotCapabilities screenshot_kwin_get_capabilities (void);
+
+GimpPDBStatusType screenshot_kwin_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+
+#endif /* __SCREENSHOT_KWIN_H__ */
diff --git a/plug-ins/screenshot/screenshot-osx.c b/plug-ins/screenshot/screenshot-osx.c
new file mode 100644
index 0000000..dcd9372
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-osx.c
@@ -0,0 +1,159 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#ifdef PLATFORM_OSX
+
+#include <stdlib.h> /* for system() on OSX */
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h> /* g_unlink() */
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-osx.h"
+
+
+/*
+ * Mac OS X uses a rootless X server. This won't let us use
+ * gdk_pixbuf_get_from_drawable() and similar function on the root
+ * window to get the entire screen contents. With a native OS X build
+ * we have to do this without X as well.
+ *
+ * Since Mac OS X 10.2 a system utility for screencapturing is
+ * included. We can safely use this, since it's available on every OS
+ * X version GIMP is running on.
+ *
+ * The main drawbacks are that it's not possible to shoot windows or
+ * regions in scripts in noninteractive mode, and that windows always
+ * include decorations, since decorations are different between X11
+ * windows and native OS X app windows. But we can use this switch
+ * to capture the shadow of a window, which is indeed very Mac-ish.
+ *
+ * This routines works well with X11 and as a native build.
+ */
+
+gboolean
+screenshot_osx_available (void)
+{
+ return TRUE;
+}
+
+ScreenshotCapabilities
+screenshot_osx_get_capabilities (void)
+{
+ return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
+ SCREENSHOT_CAN_SHOOT_POINTER |
+ SCREENSHOT_CAN_SHOOT_REGION |
+ SCREENSHOT_CAN_SHOOT_WINDOW |
+ SCREENSHOT_CAN_PICK_WINDOW |
+ SCREENSHOT_CAN_DELAY_WINDOW_SHOT);
+}
+
+GimpPDBStatusType
+screenshot_osx_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ const gchar *mode = " ";
+ const gchar *cursor = " ";
+ gchar *delay = NULL;
+ gchar *filename;
+ gchar *quoted;
+ gchar *command = NULL;
+
+ switch (shootvals->shoot_type)
+ {
+ case SHOOT_REGION:
+ if (shootvals->select_delay > 0)
+ screenshot_delay (shootvals->select_delay);
+
+ mode = "-is";
+ break;
+
+ case SHOOT_WINDOW:
+ if (shootvals->select_delay > 0)
+ screenshot_delay (shootvals->select_delay);
+
+ if (shootvals->decorate)
+ mode = "-iwo";
+ else
+ mode = "-iw";
+ if (shootvals->show_cursor)
+ cursor = "-C";
+ break;
+
+ case SHOOT_ROOT:
+ mode = " ";
+ if (shootvals->show_cursor)
+ cursor = "-C";
+ break;
+
+ default:
+ g_return_val_if_reached (GIMP_PDB_CALLING_ERROR);
+ break;
+ }
+
+ delay = g_strdup_printf ("-T %i", shootvals->screenshot_delay);
+
+ filename = gimp_temp_name ("png");
+ quoted = g_shell_quote (filename);
+
+ command = g_strjoin (" ",
+ "/usr/sbin/screencapture",
+ mode,
+ cursor,
+ delay,
+ quoted,
+ NULL);
+
+ g_free (quoted);
+ g_free (delay);
+
+ if (system ((const char *) command) == EXIT_SUCCESS)
+ {
+ /* don't attach a profile, screencapture attached one
+ */
+
+ *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ filename, filename);
+ gimp_image_set_filename (*image_ID, "screenshot.png");
+
+ g_unlink (filename);
+ g_free (filename);
+ g_free (command);
+
+ return GIMP_PDB_SUCCESS;
+ }
+
+ g_free (command);
+ g_free (filename);
+
+ return GIMP_PDB_EXECUTION_ERROR;
+}
+
+#endif /* PLATFORM_OSX */
diff --git a/plug-ins/screenshot/screenshot-osx.h b/plug-ins/screenshot/screenshot-osx.h
new file mode 100644
index 0000000..40a9875
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-osx.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_OSX_H__
+#define __SCREENSHOT_OSX_H__
+
+
+#ifdef PLATFORM_OSX
+
+gboolean screenshot_osx_available (void);
+
+ScreenshotCapabilities screenshot_osx_get_capabilities (void);
+
+GimpPDBStatusType screenshot_osx_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+#endif /* PLATFORM_OSX */
+
+
+#endif /* __SCREENSHOT_OSX_H__ */
diff --git a/plug-ins/screenshot/screenshot-win32-dwm-api.h b/plug-ins/screenshot/screenshot-win32-dwm-api.h
new file mode 100644
index 0000000..5f5c0f9
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32-dwm-api.h
@@ -0,0 +1,76 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * screenshot-win32-dwm-api.h
+ * Copyright (C) 2018 Gil Eliyahu <gileli121@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+void UnloadRequiredDwmFunctions (void);
+BOOL LoadRequiredDwmFunctions (void);
+
+typedef HRESULT (WINAPI* DWMGETWINDOWATTRIBUTE)(HWND, DWORD, _Out_ PVOID, DWORD);
+DWMGETWINDOWATTRIBUTE DwmGetWindowAttribute;
+
+typedef enum _DWMWINDOWATTRIBUTE {
+ DWMWA_NCRENDERING_ENABLED = 1,
+ DWMWA_NCRENDERING_POLICY,
+ DWMWA_TRANSITIONS_FORCEDISABLED,
+ DWMWA_ALLOW_NCPAINT,
+ DWMWA_CAPTION_BUTTON_BOUNDS,
+ DWMWA_NONCLIENT_RTL_LAYOUT,
+ DWMWA_FORCE_ICONIC_REPRESENTATION,
+ DWMWA_FLIP3D_POLICY,
+ DWMWA_EXTENDED_FRAME_BOUNDS,
+ DWMWA_HAS_ICONIC_BITMAP,
+ DWMWA_DISALLOW_PEEK,
+ DWMWA_EXCLUDED_FROM_PEEK,
+ DWMWA_CLOAK,
+ DWMWA_CLOAKED,
+ DWMWA_FREEZE_REPRESENTATION,
+ DWMWA_LAST
+} DWMWINDOWATTRIBUTE;
+
+typedef HRESULT (WINAPI* DWMISCOMPOSITIONENABLED) (BOOL *pfEnabled);
+DWMISCOMPOSITIONENABLED DwmIsCompositionEnabled;
+
+static HMODULE dwmApi = NULL;
+
+void
+UnloadRequiredDwmFunctions (void)
+{
+ if (! dwmApi) return;
+ FreeLibrary(dwmApi);
+ dwmApi = NULL;
+}
+
+BOOL
+LoadRequiredDwmFunctions (void)
+{
+ if (dwmApi) return TRUE;
+
+ dwmApi = LoadLibrary ("dwmapi");
+ if (! dwmApi) return FALSE;
+
+ DwmGetWindowAttribute = (DWMGETWINDOWATTRIBUTE) GetProcAddress (dwmApi, "DwmGetWindowAttribute");
+ DwmIsCompositionEnabled = (DWMISCOMPOSITIONENABLED) GetProcAddress (dwmApi, "DwmIsCompositionEnabled");
+ if (! (DwmGetWindowAttribute && DwmIsCompositionEnabled))
+ {
+ UnloadRequiredDwmFunctions ();
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/screenshot/screenshot-win32-magnification-api.h b/plug-ins/screenshot/screenshot-win32-magnification-api.h
new file mode 100644
index 0000000..ff3a824
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32-magnification-api.h
@@ -0,0 +1,154 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Magnification-win32.h
+ * Copyright (C) 2018 Gil Eliyahu
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <winapifamily.h>
+
+#pragma region Desktop Family
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+
+#ifndef __wincodec_h__
+#include <wincodec.h>
+#endif
+
+#define WC_MAGNIFIERA "Magnifier"
+#define WC_MAGNIFIERW L"Magnifier"
+
+#ifdef UNICODE
+#define WC_MAGNIFIER WC_MAGNIFIERW
+#else
+#define WC_MAGNIFIER WC_MAGNIFIERA
+#endif
+
+#else
+#define WC_MAGNIFIER "Magnifier"
+#endif
+
+/* Magnifier Window Styles */
+#define MS_SHOWMAGNIFIEDCURSOR 0x0001L
+#define MS_CLIPAROUNDCURSOR 0x0002L
+#define MS_INVERTCOLORS 0x0004L
+
+
+/* Filter Modes */
+#define MW_FILTERMODE_EXCLUDE 0
+#define MW_FILTERMODE_INCLUDE 1
+
+
+/* Structures */
+typedef struct tagMAGTRANSFORM
+{
+ float v[3][3];
+} MAGTRANSFORM, *PMAGTRANSFORM;
+
+typedef struct tagMAGIMAGEHEADER
+{
+ UINT width;
+ UINT height;
+ WICPixelFormatGUID format;
+ UINT stride;
+ UINT offset;
+ SIZE_T cbSize;
+} MAGIMAGEHEADER, *PMAGIMAGEHEADER;
+
+typedef struct tagMAGCOLOREFFECT
+{
+ float transform[5][5];
+} MAGCOLOREFFECT, *PMAGCOLOREFFECT;
+
+
+/* Proptypes for the public functions */
+typedef BOOL(WINAPI* MAGINITIALIZE)();
+MAGINITIALIZE MagInitialize;
+
+typedef BOOL(WINAPI* MAGUNINITIALIZE)();
+MAGUNINITIALIZE MagUninitialize;
+
+typedef BOOL(WINAPI* MAGSETWINDOWSOURCE)(HWND, RECT);
+MAGSETWINDOWSOURCE MagSetWindowSource;
+
+typedef BOOL(WINAPI* MAGSETWINDOWFILTERLIST)(HWND, DWORD, int, HWND*);
+MAGSETWINDOWFILTERLIST MagSetWindowFilterList;
+
+typedef BOOL(CALLBACK* MagImageScalingCallback)(HWND hwnd, void * srcdata, MAGIMAGEHEADER srcheader, void * destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty);
+
+typedef BOOL(WINAPI* MAGSETIMAGESCALINGCALLBACK)(HWND, MagImageScalingCallback);
+MAGSETIMAGESCALINGCALLBACK MagSetImageScalingCallback;
+
+
+/* Library DLL */
+static HINSTANCE magnificationLibrary;
+
+
+void UnLoadMagnificationLibrary(void);
+BOOL LoadMagnificationLibrary(void);
+
+
+void UnLoadMagnificationLibrary()
+{
+ if (!magnificationLibrary) return;
+ FreeLibrary(magnificationLibrary);
+}
+
+
+
+BOOL LoadMagnificationLibrary()
+{
+ if (magnificationLibrary) return TRUE;
+
+ magnificationLibrary = LoadLibrary("Magnification");
+ if (!magnificationLibrary) return FALSE;
+
+ MagInitialize = (MAGINITIALIZE)GetProcAddress(magnificationLibrary,"MagInitialize");
+ if (!MagInitialize)
+ {
+ UnLoadMagnificationLibrary();
+ return FALSE;
+ }
+
+ MagUninitialize = (MAGUNINITIALIZE)GetProcAddress(magnificationLibrary, "MagUninitialize");
+ if (!MagUninitialize)
+ {
+ UnLoadMagnificationLibrary();
+ return FALSE;
+ }
+
+ MagSetWindowSource = (MAGSETWINDOWSOURCE)GetProcAddress(magnificationLibrary, "MagSetWindowSource");
+ if (!MagSetWindowSource)
+ {
+ UnLoadMagnificationLibrary();
+ return FALSE;
+ }
+
+ MagSetWindowFilterList = (MAGSETWINDOWFILTERLIST)GetProcAddress(magnificationLibrary, "MagSetWindowFilterList");
+ if (!MagSetWindowFilterList)
+ {
+ UnLoadMagnificationLibrary();
+ return FALSE;
+ }
+
+ MagSetImageScalingCallback = (MAGSETIMAGESCALINGCALLBACK)GetProcAddress(magnificationLibrary, "MagSetImageScalingCallback");
+ if (!MagSetImageScalingCallback)
+ {
+ UnLoadMagnificationLibrary();
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/plug-ins/screenshot/screenshot-win32-resource.h b/plug-ins/screenshot/screenshot-win32-resource.h
new file mode 100644
index 0000000..57b6ec2
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32-resource.h
@@ -0,0 +1,26 @@
+/* {{NO_DEPENDENCIES}}
+ Microsoft Developer Studio generated include file.
+ Used by snappy.rc
+*/
+#define IDM_CAPTURE 100
+#define IDM_EXIT 101
+
+#define IDC_SELECT 102
+#define IDD_SELECT 103
+#define IDC_TEXT 1000
+#define IDC_GROUP 1001
+#define IDM_CAPTUREFULL 40003
+#define IDM_HOOK 40004
+#define IDM_UNHOOK 40005
+
+/* Next default values for new objects */
+
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40006
+#define _APS_NEXT_CONTROL_VALUE 1002
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plug-ins/screenshot/screenshot-win32-select.cur b/plug-ins/screenshot/screenshot-win32-select.cur
new file mode 100644
index 0000000..2357f84
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32-select.cur
Binary files differ
diff --git a/plug-ins/screenshot/screenshot-win32-small.ico b/plug-ins/screenshot/screenshot-win32-small.ico
new file mode 100644
index 0000000..33f9a51
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32-small.ico
Binary files differ
diff --git a/plug-ins/screenshot/screenshot-win32.c b/plug-ins/screenshot/screenshot-win32.c
new file mode 100644
index 0000000..ff8cbc4
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32.c
@@ -0,0 +1,1317 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ * Copyright 2018 Gil Eliyahu <gileli121@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Necessary in order to have SetProcessDPIAware() defined.
+ * This value of _WIN32_WINNT corresponds to Windows 7, which is our
+ * minimum supported platform.
+ */
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0601
+#include <windows.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-win32.h"
+#include "screenshot-win32-resource.h"
+
+#include "libgimp/stdplugins-intl.h"
+#include "screenshot-win32-magnification-api.h"
+#include "screenshot-win32-dwm-api.h"
+
+/*
+ * Application definitions
+ */
+#define SELECT_FRAME 0
+#define SELECT_CLIENT 1
+#define SELECT_WINDOW 2
+
+#define SHOW_WINDOW FALSE
+#define APP_NAME "plug_in_screenshot_win"
+#define WM_DOCAPTURE (WM_USER + 100)
+
+/* Prototypes */
+void setCaptureType (int capType);
+BOOL InitApplication (HINSTANCE hInstance);
+BOOL InitInstance (HINSTANCE hInstance,
+ int nCmdShow);
+int winsnapWinMain (void);
+
+/* File variables */
+static int captureType;
+static char buffer[512];
+static guchar *capBytes = NULL;
+static HWND mainHwnd = NULL;
+static HINSTANCE hInst = NULL;
+static HCURSOR selectCursor = 0;
+static ICONINFO iconInfo;
+static MAGIMAGEHEADER returnedSrcheader;
+static int rectScreensCount = 0;
+
+static gint32 *image_id;
+static gboolean capturePointer = FALSE;
+
+static void sendBMPToGimp (HBITMAP hBMP,
+ HDC hDC,
+ RECT rect);
+static int doWindowCapture (void);
+static gboolean doCapture (HWND selectedHwnd);
+static BOOL isWindowIsAboveCaptureRegion (HWND hwndWindow,
+ RECT rectCapture);
+static gboolean doCaptureMagnificationAPI (HWND selectedHwnd,
+ RECT rect);
+static void doCaptureMagnificationAPI_callback (HWND hwnd,
+ void *srcdata,
+ MAGIMAGEHEADER srcheader,
+ void *destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+static gboolean doCaptureBitBlt (HWND selectedHwnd,
+ RECT rect);
+
+BOOL CALLBACK dialogProc (HWND, UINT, WPARAM, LPARAM);
+LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
+
+gboolean isDwmCompositionEnabled (void);
+
+/* Data structure holding data between runs */
+typedef struct {
+ gint root;
+ guint delay;
+ gint decor;
+} WinSnapValues;
+
+/* Default WinSnap values */
+static WinSnapValues winsnapvals =
+{
+ FALSE,
+ 0,
+ TRUE,
+};
+
+/* The dialog information */
+typedef struct {
+#ifdef CAN_SET_DECOR
+ GtkWidget *decor_button;
+#endif
+ GtkWidget *single_button;
+ GtkWidget *root_button;
+ GtkWidget *delay_spinner;
+} WinSnapInterface;
+
+/* We create a DIB section to hold the grabbed area. The scanlines in
+ * DIB sections are aligned on a LONG (four byte) boundary. Its pixel
+ * data is in RGB (BGR actually) format, three bytes per pixel.
+ *
+ * GIMP uses no alignment for its pixel regions. The GIMP image we
+ * create is of type RGB, i.e. three bytes per pixel, too. Thus in
+ * order to be able to quickly transfer all of the image at a time, we
+ * must use a DIB section and pixel region the scanline width in
+ * bytes of which is evenly divisible with both 3 and 4. I.e. it must
+ * be a multiple of 12 bytes, or in pixels, a multiple of four pixels.
+ */
+
+#define ROUND4(width) (((width-1)/4+1)*4)
+
+
+gboolean
+screenshot_win32_available (void)
+{
+ return TRUE;
+}
+
+ScreenshotCapabilities
+screenshot_win32_get_capabilities (void)
+{
+ return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
+ SCREENSHOT_CAN_SHOOT_WINDOW |
+ SCREENSHOT_CAN_SHOOT_POINTER);
+}
+
+GimpPDBStatusType
+screenshot_win32_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
+
+ /* leave "shootvals->monitor" alone until somebody patches the code
+ * to be able to get a monitor's color profile
+ */
+
+ image_id = image_ID;
+
+ winsnapvals.delay = shootvals->screenshot_delay;
+ capturePointer = shootvals->show_cursor;
+
+ if (shootvals->shoot_type == SHOOT_ROOT)
+ {
+ doCapture (0);
+
+ status = GIMP_PDB_SUCCESS;
+ }
+ else if (shootvals->shoot_type == SHOOT_WINDOW)
+ {
+ status = 0 == doWindowCapture () ? GIMP_PDB_CANCEL : GIMP_PDB_SUCCESS;
+ }
+ else if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ /* FIXME */
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpColorProfile *profile;
+
+ /* XXX No idea if the "monitor" value is right at all, especially
+ * considering above comment. Just make so that it at least
+ * compiles!
+ */
+ profile = gimp_screen_get_color_profile (screen, shootvals->monitor);
+
+ if (profile)
+ {
+ gimp_image_set_color_profile (*image_ID, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ return status;
+}
+
+
+
+
+
+/******************************************************************
+ * GIMP format and transfer functions
+ ******************************************************************/
+
+/*
+ * flipRedAndBlueBytes
+ *
+ * Microsoft has chosen to provide us a very nice (not!)
+ * interface for retrieving bitmap bits. DIBSections have
+ * RGB information as BGR instead. So, we have to swap
+ * the silly red and blue bytes before sending to the
+ * GIMP.
+ */
+static void
+flipRedAndBlueBytes (int width,
+ int height)
+{
+ int i, j;
+ guchar *bufp;
+ guchar temp;
+
+ j = 0;
+ while (j < height) {
+ i = width;
+ bufp = capBytes + j*ROUND4(width)*3;
+ while (i--) {
+ temp = bufp[2];
+ bufp[2] = bufp[0];
+ bufp[0] = temp;
+ bufp += 3;
+ }
+ j++;
+ }
+}
+
+/*
+ * rgbaToRgbBytes
+ *
+ * Convert rgba array to rgb array
+ */
+static void
+rgbaToRgbBytes (guchar *rgbBufp,
+ guchar *rgbaBufp,
+ int rgbaBufSize)
+{
+ int rgbPoint = 0, rgbaPoint;
+
+ for (rgbaPoint = 0; rgbaPoint < rgbaBufSize; rgbaPoint += 4)
+ {
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint];
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 1];
+ rgbBufp[rgbPoint++] = rgbaBufp[rgbaPoint + 2];
+ }
+}
+
+/*
+ * sendBMPToGIMP
+ *
+ * Take the captured data and send it across
+ * to GIMP.
+ */
+static void
+sendBMPToGimp (HBITMAP hBMP,
+ HDC hDC,
+ RECT rect)
+{
+ int width, height;
+ int imageType, layerType;
+ gint32 new_image_id;
+ gint32 layer_id;
+ GeglBuffer *buffer;
+ GeglRectangle *rectangle;
+
+ /* Our width and height */
+ width = (rect.right - rect.left);
+ height = (rect.bottom - rect.top);
+
+ /* Check that we got the memory */
+ if (!capBytes)
+ {
+ g_message (_("No data captured"));
+ return;
+ }
+
+ /* Flip the red and blue bytes */
+ flipRedAndBlueBytes (width, height);
+
+ /* Set up the image and layer types */
+ imageType = GIMP_RGB;
+ layerType = GIMP_RGB_IMAGE;
+
+ /* Create the GIMP image and layers */
+ new_image_id = gimp_image_new (width, height, imageType);
+ layer_id = gimp_layer_new (new_image_id, _("Background"),
+ ROUND4 (width), height,
+ layerType,
+ 100,
+ gimp_image_get_default_new_layer_mode (new_image_id));
+ gimp_image_insert_layer (new_image_id, layer_id, -1, 0);
+
+ /* make rectangle */
+ rectangle = g_new (GeglRectangle, 1);
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = ROUND4(width);
+ rectangle->height = height;
+
+ /* get the buffer */
+ buffer = gimp_drawable_get_buffer (layer_id);
+
+ /* fill the buffer */
+ gegl_buffer_set (buffer, rectangle, 0, NULL, (guchar *) capBytes,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* flushing data */
+ gegl_buffer_flush (buffer);
+
+ /* Now resize the layer down to the correct size if necessary. */
+ if (width != ROUND4 (width))
+ {
+ gimp_layer_resize (layer_id, width, height, 0, 0);
+ gimp_image_resize (new_image_id, width, height, 0, 0);
+ }
+
+ *image_id = new_image_id;
+
+ return;
+}
+
+/*
+ * doNonRootWindowCapture
+ *
+ * Capture a selected window.
+ * ENTRY POINT FOR WINSNAP NONROOT
+ *
+ */
+static int
+doWindowCapture (void)
+{
+ /* Start up a standard Win32
+ * message handling loop for
+ * selection of the window
+ * to be captured
+ */
+ return winsnapWinMain ();
+}
+
+/******************************************************************
+ * Debug stuff
+ ******************************************************************/
+
+/*
+ * formatWindowsError
+ *
+ * Format the latest Windows error message into
+ * a readable string. Store in the provided
+ * buffer.
+ */
+static void
+formatWindowsError (char *buffer,
+ int buf_size)
+{
+ LPVOID lpMsgBuf;
+
+ /* Format the message */
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL );
+
+ /* Copy to the buffer */
+ strncpy(buffer, lpMsgBuf, buf_size - 1);
+
+ LocalFree(lpMsgBuf);
+}
+
+/******************************************************************
+ * Bitmap capture and handling
+ ******************************************************************/
+
+/*
+ * primDoWindowCapture
+ *
+ * The primitive window capture functionality. Accepts
+ * the two device contexts and the rectangle to be
+ * captured.
+ */
+static HBITMAP
+primDoWindowCapture (HDC hdcWindow,
+ HDC hdcCompat,
+ RECT rect)
+{
+ HBITMAP hbmCopy;
+ HGDIOBJ oldObject;
+ BITMAPINFO bmi;
+
+ int width = (rect.right - rect.left);
+ int height = (rect.bottom - rect.top);
+
+ /* Create the bitmap info header */
+ bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = ROUND4(width);
+ bmi.bmiHeader.biHeight = -height;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 24;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = 0;
+ bmi.bmiHeader.biXPelsPerMeter =
+ bmi.bmiHeader.biYPelsPerMeter = 0;
+ bmi.bmiHeader.biClrUsed = 0;
+ bmi.bmiHeader.biClrImportant = 0;
+
+ /* Create the bitmap storage space */
+ hbmCopy = CreateDIBSection (hdcCompat,
+ (BITMAPINFO *) &bmi,
+ DIB_RGB_COLORS,
+ (void **)&capBytes, NULL, 0);
+ if (!hbmCopy)
+ {
+ formatWindowsError(buffer, sizeof buffer);
+ g_error("Error creating DIB section: %s", buffer);
+ return NULL;
+ }
+
+ /* Select the bitmap into the compatible DC. */
+ oldObject = SelectObject (hdcCompat, hbmCopy);
+ if (!oldObject)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error selecting object: %s", buffer);
+ return NULL;
+ }
+
+ /* Copy the data from the application to the bitmap. Even if we did
+ * round up the width, BitBlt only the actual data.
+ */
+ if (!BitBlt(hdcCompat, 0,0,
+ width, height,
+ hdcWindow, rect.left, rect.top,
+ SRCCOPY))
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error copying bitmap: %s", buffer);
+ return NULL;
+ }
+
+ /* Draw mouse pointer over screenshot if option was selected */
+ if (capturePointer)
+ {
+ CURSORINFO pointer = { sizeof (pointer) };
+ GetCursorInfo (&pointer);
+ DrawIcon (hdcCompat, pointer.ptScreenPos.x, pointer.ptScreenPos.y, GetCursor());
+ }
+
+ /* Restore the original object */
+ SelectObject (hdcCompat, oldObject);
+
+ return hbmCopy;
+}
+
+static BOOL CALLBACK
+GetAccurateWindowRect_MonitorEnumProc (HMONITOR hMonitor,
+ HDC hdcMonitor,
+ LPRECT lprcMonitor,
+ LPARAM dwData)
+{
+ RECT **rectScreens = (RECT**) dwData;
+
+ if (!lprcMonitor) return FALSE;
+
+ if (! (*rectScreens))
+ *rectScreens = (RECT*) g_malloc (sizeof (RECT)*(rectScreensCount+1));
+ else
+ *rectScreens = (RECT*) g_realloc (*rectScreens,
+ sizeof (RECT)*(rectScreensCount+1));
+
+ if (*rectScreens == NULL)
+ {
+ rectScreensCount = 0;
+ return FALSE;
+ }
+
+ (*rectScreens)[rectScreensCount] = *lprcMonitor;
+
+ rectScreensCount++;
+
+ return TRUE;
+}
+
+static BOOL
+GetAccurateWindowRect (HWND hwndTarget,
+ RECT *outRect)
+{
+ HRESULT dwmResult;
+ WINDOWPLACEMENT windowplacment;
+
+ ZeroMemory (outRect, sizeof (RECT));
+
+ /* First we try to get the rect using the dwm api. it will also avoid us from doing more ugly fixes when the window is maximized */
+ if (LoadRequiredDwmFunctions ())
+ {
+ dwmResult = DwmGetWindowAttribute (hwndTarget, DWMWA_EXTENDED_FRAME_BOUNDS, (PVOID) outRect, sizeof (RECT));
+ UnloadRequiredDwmFunctions ();
+ if (dwmResult == S_OK) return TRUE;
+ }
+
+ /* In this case, we did not got the rect from the dwm api so we try to get the rect with the normal function */
+ if (GetWindowRect (hwndTarget, outRect))
+ {
+ /* If the window is maximized then we need and can fix the rect variable
+ * (we need to do this if the rect isn't coming from dwm api)
+ */
+ ZeroMemory (&windowplacment, sizeof (WINDOWPLACEMENT));
+ if (GetWindowPlacement (hwndTarget, &windowplacment) && windowplacment.showCmd == SW_SHOWMAXIMIZED)
+ {
+ RECT *rectScreens = NULL;
+ int xCenter;
+ int yCenter;
+ int i;
+
+ /* If this is not the first time we call this function for some
+ * reason then we reset the rectScreens count
+ */
+ if (rectScreensCount)
+ rectScreensCount = 0;
+
+ /* Get the screens rects */
+ EnumDisplayMonitors (NULL, NULL, GetAccurateWindowRect_MonitorEnumProc,
+ (LPARAM) &rectScreens);
+
+ /* If for some reason the array size is 0 then we fill it with the desktop rect */
+ if (! rectScreensCount)
+ {
+ rectScreens = (RECT*) g_malloc (sizeof (RECT));
+ if (! GetWindowRect (GetDesktopWindow (), rectScreens))
+ {
+ /* error: could not get rect screens */
+ g_free (rectScreens);
+ return FALSE;
+ }
+
+ rectScreensCount = 1;
+ }
+
+ xCenter = outRect->left + (outRect->right - outRect->left) / 2;
+ yCenter = outRect->top + (outRect->bottom - outRect->top) / 2;
+
+ /* find on which screen the window exist */
+ for (i = 0; i < rectScreensCount; i++)
+ if (xCenter > rectScreens[i].left && xCenter < rectScreens[i].right &&
+ yCenter > rectScreens[i].top && yCenter < rectScreens[i].bottom)
+ break;
+
+ if (i == rectScreensCount)
+ /* Error: did not found on which screen the window exist */
+ return FALSE;
+
+ if (rectScreens[i].left > outRect->left) outRect->left = rectScreens[i].left;
+ if (rectScreens[i].right < outRect->right) outRect->right = rectScreens[i].right;
+ if (rectScreens[i].top > outRect->top) outRect->top = rectScreens[i].top;
+ if (rectScreens[i].bottom < outRect->bottom) outRect->bottom = rectScreens[i].bottom;
+
+ g_free (rectScreens);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean isDwmCompositionEnabled (void)
+{
+ HRESULT err;
+ BOOL result;
+
+ if (LoadRequiredDwmFunctions ())
+ {
+ err = DwmIsCompositionEnabled (&result);
+ if (err != S_OK)
+ {
+ return FALSE;
+ }
+ return result;
+ }
+
+ return FALSE;
+}
+
+/*
+ * doCapture
+ *
+ * Do the capture. Accepts the window
+ * handle to be captured or the NULL value
+ * to specify the root window.
+ */
+static gboolean
+doCapture (HWND selectedHwnd)
+{
+ RECT rect;
+
+ /* Try and get everything out of the way before the
+ * capture.
+ */
+ Sleep (500 + winsnapvals.delay * 1000);
+
+ /* Are we capturing a window or the whole screen */
+ if (selectedHwnd)
+ {
+ gboolean compositionEnabled;
+
+ if (! GetAccurateWindowRect (selectedHwnd, &rect))
+ /* For some reason it works only if we return here TRUE. strange... */
+ return TRUE;
+
+ /* First try to capture the window with the magnification api.
+ * This will solve the bug https://bugzilla.gnome.org/show_bug.cgi?id=793722/
+ */
+ compositionEnabled = isDwmCompositionEnabled ();
+
+ if (! (compositionEnabled && doCaptureMagnificationAPI (selectedHwnd, rect)))
+ {
+ /* If for some reason this capture method failed then
+ * capture the window with the normal method:
+ */
+ HWND previousHwnd = GetForegroundWindow ();
+ gboolean success;
+
+ SetForegroundWindow (selectedHwnd);
+ BringWindowToTop (selectedHwnd);
+
+ success = doCaptureBitBlt (selectedHwnd, rect);
+
+ if (previousHwnd)
+ SetForegroundWindow (previousHwnd);
+
+ return success;
+ }
+
+ return TRUE;
+
+ }
+ else
+ {
+ /* Get the screen's rectangle */
+ rect.top = GetSystemMetrics (SM_YVIRTUALSCREEN);
+ rect.bottom = GetSystemMetrics (SM_YVIRTUALSCREEN) + GetSystemMetrics (SM_CYVIRTUALSCREEN);
+ rect.left = GetSystemMetrics (SM_XVIRTUALSCREEN);
+ rect.right = GetSystemMetrics (SM_XVIRTUALSCREEN) + GetSystemMetrics (SM_CXVIRTUALSCREEN);
+
+ return doCaptureBitBlt (selectedHwnd, rect);
+
+ }
+
+ return FALSE; /* we should never get here... */
+}
+
+static gboolean
+doCaptureBitBlt (HWND selectedHwnd,
+ RECT rect)
+{
+
+ HDC hdcSrc;
+ HDC hdcCompat;
+ HBITMAP hbm;
+
+ /* Get the device context for the whole screen
+ * even if we just want to capture a window.
+ * this will allow to capture applications that
+ * don't render to their main window's device
+ * context (e.g. browsers).
+ */
+ hdcSrc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL);
+
+ if (!hdcSrc)
+ {
+ formatWindowsError(buffer, sizeof buffer);
+ g_error ("Error getting device context: %s", buffer);
+ return FALSE;
+ }
+ hdcCompat = CreateCompatibleDC (hdcSrc);
+ if (!hdcCompat)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error getting compat device context: %s", buffer);
+ return FALSE;
+ }
+
+ /* Do the window capture */
+ hbm = primDoWindowCapture (hdcSrc, hdcCompat, rect);
+ if (!hbm)
+ return FALSE;
+
+ /* Release the device context */
+ ReleaseDC(selectedHwnd, hdcSrc);
+
+ if (hbm == NULL) return FALSE;
+
+ sendBMPToGimp (hbm, hdcCompat, rect);
+
+ return TRUE;
+
+}
+
+static void
+doCaptureMagnificationAPI_callback (HWND hwnd,
+ void *srcdata,
+ MAGIMAGEHEADER srcheader,
+ void *destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty)
+{
+ if (!srcdata) return;
+ capBytes = (guchar*) malloc (sizeof (guchar)*srcheader.cbSize);
+ rgbaToRgbBytes (capBytes, srcdata, srcheader.cbSize);
+ returnedSrcheader = srcheader;
+}
+
+static BOOL
+isWindowIsAboveCaptureRegion (HWND hwndWindow,
+ RECT rectCapture)
+{
+ RECT rectWindow;
+ ZeroMemory (&rectWindow, sizeof (RECT));
+ if (!GetWindowRect (hwndWindow, &rectWindow)) return FALSE;
+ if (
+ (
+ (rectWindow.left >= rectCapture.left && rectWindow.left < rectCapture.right)
+ ||
+ (rectWindow.right <= rectCapture.right && rectWindow.right > rectCapture.left)
+ ||
+ (rectWindow.left <= rectCapture.left && rectWindow.right >= rectCapture.right)
+ )
+ &&
+ (
+ (rectWindow.top >= rectCapture.top && rectWindow.top < rectCapture.bottom)
+ ||
+ (rectWindow.bottom <= rectCapture.bottom && rectWindow.bottom > rectCapture.top)
+ ||
+ (rectWindow.top <= rectCapture.top && rectWindow.bottom >= rectCapture.bottom)
+ )
+ )
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+doCaptureMagnificationAPI (HWND selectedHwnd,
+ RECT rect)
+{
+ HWND hwndMag;
+ HWND hwndHost;
+ HWND nextWindow;
+ HWND excludeWins[24];
+ RECT round4Rect;
+ int excludeWinsCount = 0;
+ int magStyles;
+
+ if (!LoadMagnificationLibrary ()) return FALSE;
+
+ if (!MagInitialize ()) return FALSE;
+
+ round4Rect = rect;
+ round4Rect.right = round4Rect.left + ROUND4(round4Rect.right - round4Rect.left);
+
+ /* Create the host window that will store the mag child window */
+ hwndHost = CreateWindowEx (0x08000000 | 0x080000 | 0x80 | 0x20, APP_NAME, NULL, 0x80000000,
+ 0, 0, 0, 0, NULL, NULL, GetModuleHandle (NULL), NULL);
+
+ if (!hwndHost)
+ {
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ SetLayeredWindowAttributes (hwndHost, (COLORREF)0, (BYTE)255, (DWORD)0x02);
+
+ magStyles = WS_CHILD;
+ if (capturePointer)
+ magStyles |= MS_SHOWMAGNIFIEDCURSOR;
+
+ /* Create the mag child window inside the host window */
+ hwndMag = CreateWindow (WC_MAGNIFIER, TEXT ("MagnifierWindow"),
+ magStyles,
+ 0, 0, round4Rect.right - round4Rect.left, round4Rect.bottom - round4Rect.top,
+ hwndHost, NULL, GetModuleHandle (NULL), NULL);
+
+ /* Set the callback function that will be called by the api to get the pixels */
+ if (!MagSetImageScalingCallback (hwndMag, (MagImageScalingCallback)doCaptureMagnificationAPI_callback))
+ {
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ /* Add only windows that above the target window */
+ for (nextWindow = GetNextWindow (selectedHwnd, GW_HWNDPREV); nextWindow != NULL; nextWindow = GetNextWindow (nextWindow, GW_HWNDPREV))
+ if (isWindowIsAboveCaptureRegion (nextWindow, rect))
+ {
+ excludeWins[excludeWinsCount++] = nextWindow;
+ /* This api can't work with more than 24 windows. we stop on the 24 window */
+ if (excludeWinsCount >= 24) break;
+ }
+
+ if (excludeWinsCount)
+ MagSetWindowFilterList (hwndMag, MW_FILTERMODE_EXCLUDE, excludeWinsCount, excludeWins);
+
+ /* Call the api to capture the window */
+ capBytes = NULL;
+
+ if (!MagSetWindowSource (hwndMag, round4Rect) || !capBytes)
+ {
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+ return FALSE;
+ }
+
+ /* Send it to Gimp */
+ sendBMPToGimp (NULL, NULL, rect);
+
+ DestroyWindow (hwndHost);
+ MagUninitialize ();
+
+ return TRUE;
+}
+
+/******************************************************************
+ * Win32 entry point and setup...
+ ******************************************************************/
+
+#define DINV 3
+
+/*
+ * highlightWindowFrame
+ *
+ * Highlight (or unhighlight) the specified
+ * window handle's frame.
+ */
+static void
+highlightWindowFrame (HWND hWnd)
+{
+ HDC hdc;
+ RECT rc;
+
+ if (!IsWindow (hWnd))
+ return;
+
+ hdc = GetWindowDC (hWnd);
+ GetWindowRect (hWnd, &rc);
+ OffsetRect (&rc, -rc.left, -rc.top);
+
+ if (!IsRectEmpty (&rc))
+ {
+ PatBlt (hdc, rc.left, rc.top, rc.right-rc.left, DINV, DSTINVERT);
+ PatBlt (hdc, rc.left, rc.bottom-DINV, DINV, -(rc.bottom-rc.top-2*DINV),
+ DSTINVERT);
+ PatBlt (hdc, rc.right-DINV, rc.top+DINV, DINV, rc.bottom-rc.top-2*DINV,
+ DSTINVERT);
+ PatBlt (hdc, rc.right, rc.bottom-DINV, -(rc.right-rc.left), DINV,
+ DSTINVERT);
+ }
+
+ ReleaseDC (hWnd, hdc);
+ UpdateWindow (hWnd);
+}
+
+/*
+ * setCaptureType
+ *
+ * Set the capture type. Should be one of:
+ * SELECT_FRAME
+ * SELECT_CLIENT
+ * SELECT_WINDOW
+ */
+void
+setCaptureType (int capType)
+{
+ captureType = capType;
+}
+
+/*
+ * myWindowFromPoint
+ *
+ * Map to the appropriate window from the
+ * specified point. The chosen window is
+ * based on the current capture type.
+ */
+static HWND
+myWindowFromPoint (POINT pt)
+{
+ HWND myHwnd;
+ HWND nextHwnd;
+
+ switch (captureType)
+ {
+ case SELECT_FRAME:
+ case SELECT_CLIENT:
+ nextHwnd = WindowFromPoint (pt);
+
+ do {
+ myHwnd = nextHwnd;
+ nextHwnd = GetParent (myHwnd);
+ } while (nextHwnd);
+
+ return myHwnd;
+ break;
+
+ case SELECT_WINDOW:
+ return WindowFromPoint (pt);
+ break;
+ }
+
+ return WindowFromPoint (pt);
+}
+
+/*
+ * dialogProc
+ *
+ * The window procedure for the window
+ * selection dialog box.
+ */
+BOOL CALLBACK
+dialogProc (HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ static int mouseCaptured;
+ static int buttonDown;
+ static HCURSOR oldCursor;
+ static RECT bitmapRect;
+ static HWND highlightedHwnd = NULL;
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ int nonclientHeight;
+ HWND hwndGroup;
+ RECT dlgRect;
+ RECT clientRect;
+ RECT groupRect;
+ BITMAP bm;
+
+ /* Set the mouse capture flag */
+ buttonDown = 0;
+ mouseCaptured = 0;
+
+ /* Calculate the bitmap dimensions */
+ GetObject (iconInfo.hbmMask, sizeof(BITMAP), (VOID *)&bm);
+
+ /* Calculate the dialog window dimensions */
+ GetWindowRect (hwndDlg, &dlgRect);
+
+ /* Calculate the group box dimensions */
+ hwndGroup = GetDlgItem(hwndDlg, IDC_GROUP);
+ GetWindowRect (hwndGroup, &groupRect);
+ OffsetRect (&groupRect, -dlgRect.left, -dlgRect.top);
+
+ /* The client's rectangle */
+ GetClientRect (hwndDlg, &clientRect);
+
+ /* The non-client height */
+ nonclientHeight = (dlgRect.bottom - dlgRect.top) -
+ (clientRect.bottom - clientRect.top);
+
+ /* Calculate the bitmap rectangle */
+ bitmapRect.top = ((groupRect.top + groupRect.bottom) / 2) -
+ (bm.bmHeight / 2);
+ bitmapRect.top -= nonclientHeight;
+ bitmapRect.bottom = bitmapRect.top + bm.bmHeight;
+ bitmapRect.left = ((groupRect.left + groupRect.right) / 2) - (bm.bmWidth / 2);
+ bitmapRect.right = bitmapRect.left + bm.bmWidth;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ /* Track the button down state */
+ buttonDown = 1;
+ break;
+
+ case WM_LBUTTONUP:
+ buttonDown = 0;
+
+ /* If we have mouse captured
+ * we do this stuff.
+ */
+ if (mouseCaptured)
+ {
+ HWND selectedHwnd;
+ POINT cursorPos;
+
+ /* Release the capture */
+ mouseCaptured = 0;
+ SetCursor (oldCursor);
+ ReleaseCapture ();
+
+ /* Remove the highlight */
+ if (highlightedHwnd)
+ highlightWindowFrame (highlightedHwnd);
+ RedrawWindow (hwndDlg, NULL, NULL, RDW_INVALIDATE);
+
+ /* Return the selected window */
+ GetCursorPos (&cursorPos);
+ selectedHwnd = myWindowFromPoint (cursorPos);
+ EndDialog (hwndDlg, (INT_PTR) selectedHwnd);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ /* If the mouse is captured, show
+ * the window which is tracking
+ * under the mouse position.
+ */
+ if (mouseCaptured)
+ {
+ HWND currentHwnd;
+ POINT cursorPos;
+
+ /* Get the window */
+ GetCursorPos (&cursorPos);
+ currentHwnd = myWindowFromPoint (cursorPos);
+
+ /* Do the highlighting */
+ if (highlightedHwnd != currentHwnd)
+ {
+ if (highlightedHwnd)
+ highlightWindowFrame (highlightedHwnd);
+ if (currentHwnd)
+ highlightWindowFrame (currentHwnd);
+ highlightedHwnd = currentHwnd;
+ }
+ /* If the mouse has not been captured,
+ * try to figure out if we should capture
+ * the mouse.
+ */
+ }
+ else if (buttonDown)
+ {
+ POINT cursorPos;
+
+ /* Get the current client position */
+ GetCursorPos (&cursorPos);
+ ScreenToClient (hwndDlg, &cursorPos);
+
+ /* Check if within the rectangle formed
+ * by the bitmap
+ */
+ if (PtInRect (&bitmapRect, cursorPos)) {
+ mouseCaptured = 1;
+ oldCursor = SetCursor (selectCursor);
+ SetCapture (hwndDlg);
+ RedrawWindow (hwndDlg, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
+ }
+ }
+
+ break;
+
+ case WM_PAINT:
+ {
+ HDC hDC;
+ PAINTSTRUCT ps;
+
+ /* If the mouse is not captured draw
+ * the cursor image
+ */
+ if (!mouseCaptured)
+ {
+ hDC = BeginPaint (hwndDlg, &ps);
+ DrawIconEx (hDC, bitmapRect.left, bitmapRect.top, selectCursor,
+ 0, 0, 0, NULL, DI_NORMAL);
+ EndPaint (hwndDlg, &ps);
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ /* Handle the cancel button */
+ switch (LOWORD (wParam))
+ {
+ case IDCANCEL:
+ EndDialog (hwndDlg, 0);
+ return TRUE;
+ break;
+ }
+
+ }
+
+ return FALSE;
+}
+
+///* Don't use the normal WinMain from gimp.h */
+//#define WinMain WinMain_no_thanks
+//MAIN()
+//#undef WinMain
+
+/*
+ * WinMain
+ *
+ * The standard gimp plug-in WinMain won't quite cut it for
+ * this plug-in.
+ */
+//int APIENTRY
+//WinMain(HINSTANCE hInstance,
+// HINSTANCE hPrevInstance,
+// LPSTR lpCmdLine,
+// int nCmdShow)
+//{
+// /*
+// * Normally, we would do all of the Windows-ish set up of
+// * the window classes and stuff here in WinMain. But,
+// * the only time we really need the window and message
+// * queues is during the plug-in run. So, all of that will
+// * be done during run(). This avoids all of the Windows
+// * setup stuff for the query(). Stash the instance handle now
+// * so it is available from the run() procedure.
+// */
+// hInst = hInstance;
+//
+// /*
+// * Now, call gimp_main... This is what the normal WinMain()
+// * would do.
+// */
+//// return gimp_main(&PLUG_IN_INFO, __argc, __argv);
+//}
+
+/*
+ * InitApplication
+ *
+ * Initialize window data and register the window class
+ */
+BOOL
+InitApplication (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ BOOL retValue;
+
+ /* Get some resources */
+#ifdef _MSC_VER
+ /* For some reason this works only with MSVC */
+ selectCursor = LoadCursor (hInstance, MAKEINTRESOURCE(IDC_SELECT));
+#else
+ selectCursor = LoadCursor (NULL, IDC_CROSS);
+#endif
+ GetIconInfo (selectCursor, &iconInfo);
+
+ /*
+ * Fill in window class structure with parameters to describe
+ * the main window.
+ */
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wc.lpszClassName = APP_NAME;
+ wc.lpszMenuName = NULL;
+
+ /* Register the window class and stash success/failure code. */
+ retValue = RegisterClass (&wc);
+
+ /* Log error */
+ if (!retValue)
+ {
+ formatWindowsError (buffer, sizeof buffer);
+ g_error ("Error registering class: %s", buffer);
+ return retValue;
+ }
+
+ return retValue;
+}
+
+/*
+ * InitInstance
+ *
+ * Create the main window for the application.
+ */
+BOOL
+InitInstance (HINSTANCE hInstance,
+ int nCmdShow)
+{
+ HINSTANCE User32Library = LoadLibrary ("user32.dll");
+
+ if (User32Library)
+ {
+ typedef BOOL (WINAPI* SET_PROC_DPI_AWARE)();
+ SET_PROC_DPI_AWARE SetProcessDPIAware;
+
+ /* This line fix bug: https://bugzilla.gnome.org/show_bug.cgi?id=796121#c4 */
+ SetProcessDPIAware = (SET_PROC_DPI_AWARE) GetProcAddress (User32Library,
+ "SetProcessDPIAware");
+ if (SetProcessDPIAware)
+ SetProcessDPIAware();
+
+ FreeLibrary (User32Library);
+ }
+
+ /* Create our window */
+ mainHwnd = CreateWindow (APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+ NULL, NULL, hInstance, NULL);
+
+ if (!mainHwnd)
+ {
+ return (FALSE);
+ }
+
+ ShowWindow (mainHwnd, nCmdShow);
+ UpdateWindow (mainHwnd);
+
+ return TRUE;
+}
+
+/*
+ * winsnapWinMain
+ *
+ * This is the function that represents the code that
+ * would normally reside in WinMain (see above). This
+ * function will get called during run() in order to set
+ * up the windowing environment necessary for WinSnap to
+ * operate.
+ */
+int
+winsnapWinMain (void)
+{
+ MSG msg;
+
+ /* Perform instance initialization */
+ if (!InitApplication (hInst))
+ return (FALSE);
+
+ /* Perform application initialization */
+ if (!InitInstance (hInst, SHOW_WINDOW))
+ return (FALSE);
+
+ /* Main message loop */
+ while (GetMessage (&msg, NULL, 0, 0))
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+ return (msg.wParam);
+}
+
+/*
+ * WndProc
+ *
+ * Process window message for the main window.
+ */
+LRESULT CALLBACK
+WndProc (HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ HWND selectedHwnd;
+
+ switch (message)
+ {
+
+ case WM_CREATE:
+ /* The window is created... Send the capture message */
+ PostMessage (hwnd, WM_DOCAPTURE, 0, 0);
+ break;
+
+ case WM_DOCAPTURE:
+ /* Get the selected window handle */
+ selectedHwnd = (HWND) DialogBox (hInst, MAKEINTRESOURCE(IDD_SELECT),
+ hwnd, (DLGPROC) dialogProc);
+ if (selectedHwnd)
+ doCapture (selectedHwnd);
+
+ PostQuitMessage (selectedHwnd != NULL);
+
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage (0);
+ break;
+
+ default:
+ return (DefWindowProc (hwnd, message, wParam, lParam));
+ }
+
+ return 0;
+}
+
+#endif /* G_OS_WIN32 */
diff --git a/plug-ins/screenshot/screenshot-win32.h b/plug-ins/screenshot/screenshot-win32.h
new file mode 100644
index 0000000..d84be25
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32.h
@@ -0,0 +1,44 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_WIN32_H__
+#define __SCREENSHOT_WIN32_H__
+
+
+#ifdef G_OS_WIN32
+
+#define IDC_STATIC -1
+
+#define IDS_APP_TITLE 500
+#define IDS_DISPLAYCHANGED 501
+#define IDS_VER_INFO_LANG 502
+#define IDS_VERSION_ERROR 503
+#define IDS_NO_HELP 504
+
+gboolean screenshot_win32_available (void);
+
+ScreenshotCapabilities screenshot_win32_get_capabilities (void);
+
+GimpPDBStatusType screenshot_win32_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+#endif /* G_OS_WIN32 */
+
+
+#endif /* __SCREENSHOT_WIN32_H__ */
diff --git a/plug-ins/screenshot/screenshot-win32.ico b/plug-ins/screenshot/screenshot-win32.ico
new file mode 100644
index 0000000..35ea973
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32.ico
Binary files differ
diff --git a/plug-ins/screenshot/screenshot-win32.rc b/plug-ins/screenshot/screenshot-win32.rc
new file mode 100644
index 0000000..0272e93
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-win32.rc
@@ -0,0 +1,189 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "screenshot-win32-resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+#include "screenshot-win32.h"
+#include "winver.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(65001)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+SNAPPY ICON DISCARDABLE "screenshot-win32.ico"
+SMALL ICON DISCARDABLE "screenshot-win32-small.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+SNAPPY MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Capture", IDM_CAPTURE
+ MENUITEM "Capture Fullscreen", IDM_CAPTUREFULL
+ MENUITEM SEPARATOR
+ MENUITEM "Begin Hook", IDM_HOOK
+ MENUITEM "End Hook", IDM_UNHOOK
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_EXIT
+ END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""screenshot-win32.h""\r\n"
+ "#include ""winver.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+IDC_SELECT CURSOR DISCARDABLE "screenshot-win32-select.cur"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "SeteraSoft\0"
+ VALUE "FileDescription", "snappy\0"
+ VALUE "FileVersion", "1, 0, 0, 1\0"
+ VALUE "InternalName", "snappy\0"
+ VALUE "LegalCopyright", "Copyright © 1999\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "snappy.exe\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "SeteraSoft snappy\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_SELECT DIALOG DISCARDABLE 0, 0, 141, 95
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Window"
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Cancel",IDCANCEL,44,74,50,14
+ CTEXT "Drag crosshair to select window",IDC_TEXT,7,7,127,8
+ GROUPBOX "",IDC_GROUP,51,25,37,37,NOT WS_VISIBLE
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_SELECT, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 134
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 88
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/plug-ins/screenshot/screenshot-x11.c b/plug-ins/screenshot/screenshot-x11.c
new file mode 100644
index 0000000..820ec04
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-x11.c
@@ -0,0 +1,697 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#ifdef GDK_WINDOWING_X11
+
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
+#include <X11/extensions/shape.h>
+#endif
+
+#ifdef HAVE_X11_XMU_WINUTIL_H
+#include <X11/Xmu/WinUtil.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#include "screenshot.h"
+#include "screenshot-x11.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static guint32 select_window (ScreenshotValues *shootvals,
+ GdkScreen *screen);
+static gint32 create_image (cairo_surface_t *surface,
+ cairo_region_t *shape,
+ const gchar *name);
+
+
+/* Allow the user to select a window or a region with the mouse */
+
+static guint32
+select_window (ScreenshotValues *shootvals,
+ GdkScreen *screen)
+{
+ Display *x_dpy = GDK_SCREEN_XDISPLAY (screen);
+ gint x_scr = GDK_SCREEN_XNUMBER (screen);
+ Window x_root = RootWindow (x_dpy, x_scr);
+ Window x_win = None;
+ GC x_gc = NULL;
+ Cursor x_cursor = XCreateFontCursor (x_dpy, GDK_CROSSHAIR);
+ GdkKeymap *keymap;
+ GdkKeymapKey *keys = NULL;
+ gint status;
+ gint num_keys;
+ gint i;
+ gint buttons = 0;
+ gint mask = ButtonPressMask | ButtonReleaseMask;
+ gboolean cancel = FALSE;
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ mask |= PointerMotionMask;
+
+ status = XGrabPointer (x_dpy, x_root, False,
+ mask, GrabModeSync, GrabModeAsync,
+ x_root, x_cursor, CurrentTime);
+
+ if (status != GrabSuccess)
+ {
+ gint x, y;
+ guint xmask;
+
+ /* if we can't grab the pointer, return the window under the pointer */
+ XQueryPointer (x_dpy, x_root, &x_root, &x_win, &x, &y, &x, &y, &xmask);
+
+ if (x_win == None || x_win == x_root)
+ g_message (_("Error selecting the window"));
+ }
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ XGCValues gc_values;
+
+ gc_values.function = GXxor;
+ gc_values.plane_mask = AllPlanes;
+ gc_values.foreground = WhitePixel (x_dpy, x_scr);
+ gc_values.background = BlackPixel (x_dpy, x_scr);
+ gc_values.line_width = 0;
+ gc_values.line_style = LineSolid;
+ gc_values.fill_style = FillSolid;
+ gc_values.cap_style = CapButt;
+ gc_values.join_style = JoinMiter;
+ gc_values.graphics_exposures = FALSE;
+ gc_values.clip_x_origin = 0;
+ gc_values.clip_y_origin = 0;
+ gc_values.clip_mask = None;
+ gc_values.subwindow_mode = IncludeInferiors;
+
+ x_gc = XCreateGC (x_dpy, x_root,
+ GCFunction | GCPlaneMask | GCForeground | GCLineWidth |
+ GCLineStyle | GCCapStyle | GCJoinStyle |
+ GCGraphicsExposures | GCBackground | GCFillStyle |
+ GCClipXOrigin | GCClipYOrigin | GCClipMask |
+ GCSubwindowMode,
+ &gc_values);
+ }
+
+ keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
+
+ if (gdk_keymap_get_entries_for_keyval (keymap, GDK_KEY_Escape,
+ &keys, &num_keys))
+ {
+ gdk_error_trap_push ();
+
+#define X_GRAB_KEY(index, modifiers) \
+ XGrabKey (x_dpy, keys[index].keycode, modifiers, x_root, False, \
+ GrabModeAsync, GrabModeAsync)
+
+ for (i = 0; i < num_keys; i++)
+ {
+ X_GRAB_KEY (i, 0);
+ X_GRAB_KEY (i, LockMask); /* CapsLock */
+ X_GRAB_KEY (i, Mod2Mask); /* NumLock */
+ X_GRAB_KEY (i, Mod5Mask); /* ScrollLock */
+ X_GRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
+ X_GRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
+ X_GRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
+ X_GRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
+ }
+
+#undef X_GRAB_KEY
+
+ gdk_flush ();
+
+ if (gdk_error_trap_pop ())
+ {
+ /* ignore errors */
+ }
+ }
+
+ while (! cancel && ((x_win == None) || (buttons != 0)))
+ {
+ XEvent x_event;
+ gint x, y, w, h;
+
+ XAllowEvents (x_dpy, SyncPointer, CurrentTime);
+ XWindowEvent (x_dpy, x_root, mask | KeyPressMask, &x_event);
+
+ switch (x_event.type)
+ {
+ case ButtonPress:
+ if (x_win == None)
+ {
+ x_win = x_event.xbutton.subwindow;
+
+ if (x_win == None)
+ x_win = x_root;
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ else if (! shootvals->decorate)
+ x_win = XmuClientWindow (x_dpy, x_win);
+#endif
+
+ shootvals->x2 = shootvals->x1 = x_event.xbutton.x_root;
+ shootvals->y2 = shootvals->y1 = x_event.xbutton.y_root;
+ }
+
+ buttons++;
+ break;
+
+ case ButtonRelease:
+ if (buttons > 0)
+ buttons--;
+
+ if (! buttons && shootvals->shoot_type == SHOOT_REGION)
+ {
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+
+ shootvals->x2 = x_event.xbutton.x_root;
+ shootvals->y2 = x_event.xbutton.y_root;
+ }
+ break;
+
+ case MotionNotify:
+ if (buttons > 0)
+ {
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+
+ shootvals->x2 = x_event.xmotion.x_root;
+ shootvals->y2 = x_event.xmotion.y_root;
+
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+ }
+ break;
+
+ case KeyPress:
+ {
+ guint *keyvals;
+ gint n;
+
+ if (gdk_keymap_get_entries_for_keycode (NULL, x_event.xkey.keycode,
+ NULL, &keyvals, &n))
+ {
+ gint i;
+
+ for (i = 0; i < n && ! cancel; i++)
+ if (keyvals[i] == GDK_KEY_Escape)
+ cancel = TRUE;
+
+ g_free (keyvals);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (keys)
+ {
+#define X_UNGRAB_KEY(index, modifiers) \
+ XUngrabKey (x_dpy, keys[index].keycode, modifiers, x_root)
+
+ for (i = 0; i < num_keys; i++)
+ {
+ X_UNGRAB_KEY (i, 0);
+ X_UNGRAB_KEY (i, LockMask); /* CapsLock */
+ X_UNGRAB_KEY (i, Mod2Mask); /* NumLock */
+ X_UNGRAB_KEY (i, Mod5Mask); /* ScrollLock */
+ X_UNGRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
+ X_UNGRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
+ X_UNGRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
+ X_UNGRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
+ }
+#undef X_UNGRAB_KEY
+
+ g_free (keys);
+ }
+
+ if (status == GrabSuccess)
+ XUngrabPointer (x_dpy, CurrentTime);
+
+ XFreeCursor (x_dpy, x_cursor);
+
+ if (x_gc != NULL)
+ XFreeGC (x_dpy, x_gc);
+
+ return x_win;
+}
+
+static gchar *
+window_get_utf8_property (GdkDisplay *display,
+ guint32 window,
+ const gchar *name)
+{
+ gchar *retval = NULL;
+ Atom utf8_string;
+ Atom type = None;
+ guchar *val = NULL;
+ gulong nitems = 0;
+ gulong after = 0;
+ gint format = 0;
+
+ utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), window,
+ gdk_x11_get_xatom_by_name_for_display (display, name),
+ 0, G_MAXLONG, False, utf8_string,
+ &type, &format, &nitems, &after, &val);
+
+ if (type != utf8_string || format != 8 || nitems == 0)
+ {
+ if (val)
+ XFree (val);
+ return NULL;
+ }
+
+ if (g_utf8_validate ((const gchar *) val, nitems, NULL))
+ retval = g_strndup ((const gchar *) val, nitems);
+
+ XFree (val);
+
+ return retval;
+}
+
+static gchar *
+window_get_title (GdkDisplay *display,
+ guint window)
+{
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ window = XmuClientWindow (GDK_DISPLAY_XDISPLAY (display), window);
+#endif
+
+ return window_get_utf8_property (display, window, "_NET_WM_NAME");
+}
+
+static cairo_region_t *
+window_get_shape (GdkScreen *screen,
+ guint32 window)
+{
+ cairo_region_t *shape = NULL;
+
+#if defined(HAVE_X11_EXTENSIONS_SHAPE_H)
+ XRectangle *rects;
+ gint rect_count;
+ gint rect_order;
+
+ rects = XShapeGetRectangles (GDK_SCREEN_XDISPLAY (screen), window,
+ ShapeBounding,
+ &rect_count, &rect_order);
+
+ if (rects)
+ {
+ if (rect_count > 1)
+ {
+ gint i;
+
+ shape = cairo_region_create ();
+
+ for (i = 0; i < rect_count; i++)
+ {
+ cairo_rectangle_int_t rect = { rects[i].x,
+ rects[i].y,
+ rects[i].width,
+ rects[i].height };
+
+ cairo_region_union_rectangle (shape, &rect);
+ }
+ }
+
+ XFree (rects);
+ }
+#endif
+
+ return shape;
+}
+
+static void
+image_select_shape (gint32 image,
+ cairo_region_t *shape)
+{
+ gint num_rects;
+ gint i;
+
+ gimp_selection_none (image);
+
+ num_rects = cairo_region_num_rectangles (shape);
+
+ for (i = 0; i < num_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (shape, i, &rect);
+
+ gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+
+ gimp_selection_invert (image);
+}
+
+
+/* Create a GimpImage from a GdkPixbuf */
+
+static gint32
+create_image (cairo_surface_t *surface,
+ cairo_region_t *shape,
+ const gchar *name)
+{
+ gint32 image;
+ gint32 layer;
+ gdouble xres, yres;
+ gint width, height;
+
+ gimp_progress_init (_("Importing screenshot"));
+
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+
+ image = gimp_image_new (width, height, GIMP_RGB);
+ gimp_image_undo_disable (image);
+
+ gimp_get_monitor_resolution (&xres, &yres);
+ gimp_image_set_resolution (image, xres, yres);
+
+ layer = gimp_layer_new_from_surface (image,
+ name ? name : _("Screenshot"),
+ surface,
+ 0.0, 1.0);
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ if (shape && ! cairo_region_is_empty (shape))
+ {
+ image_select_shape (image, shape);
+
+ if (! gimp_selection_is_empty (image))
+ {
+ gimp_layer_add_alpha (layer);
+ gimp_drawable_edit_clear (layer);
+ gimp_selection_none (image);
+ }
+ }
+
+ gimp_image_undo_enable (image);
+
+ return image;
+}
+
+static void
+add_cursor_image (gint32 image,
+ GdkDisplay *display)
+{
+#ifdef HAVE_XFIXES
+ XFixesCursorImage *cursor;
+ GeglBuffer *buffer;
+ GeglBufferIterator *iter;
+ GeglRectangle *roi;
+ gint32 layer;
+ gint32 active;
+
+ cursor = XFixesGetCursorImage (GDK_DISPLAY_XDISPLAY (display));
+
+ if (!cursor)
+ return;
+
+ active = gimp_image_get_active_layer (image);
+
+ layer = gimp_layer_new (image, _("Mouse Pointer"),
+ cursor->width, cursor->height,
+ GIMP_RGBA_IMAGE,
+ 100.0,
+ gimp_image_get_default_new_layer_mode (image));
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gimp_drawable_width (layer),
+ gimp_drawable_height (layer)),
+ 0, babl_format ("R'G'B'A u8"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
+ roi = &iter->items[0].roi;
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const gulong *src = cursor->pixels + roi->y * cursor->width + roi->x;
+ guchar *dest = iter->items[0].data;
+ gint x, y;
+
+ for (y = 0; y < roi->height; y++)
+ {
+ const gulong *s = src;
+ guchar *d = dest;
+
+ for (x = 0; x < roi->width; x++)
+ {
+ /* the cursor pixels are pre-multiplied ARGB */
+ guint a = (*s >> 24) & 0xff;
+ guint r = (*s >> 16) & 0xff;
+ guint g = (*s >> 8) & 0xff;
+ guint b = (*s >> 0) & 0xff;
+
+ d[0] = a ? (r * 255) / a : r;
+ d[1] = a ? (g * 255) / a : g;
+ d[2] = a ? (b * 255) / a : b;
+ d[3] = a;
+
+ s++;
+ d += 4;
+ }
+
+ src += cursor->width;
+ dest += 4 * roi->width;
+ }
+ }
+
+ g_object_unref (buffer);
+
+ gimp_image_insert_layer (image, layer, -1, -1);
+ gimp_layer_set_offsets (layer,
+ cursor->x - cursor->xhot, cursor->y - cursor->yhot);
+
+ gimp_image_set_active_layer (image, active);
+#endif
+}
+
+
+/* The main Screenshot function */
+
+gboolean
+screenshot_x11_available (void)
+{
+ return TRUE;
+}
+
+ScreenshotCapabilities
+screenshot_x11_get_capabilities (void)
+{
+ ScreenshotCapabilities capabilities = SCREENSHOT_CAN_PICK_NONINTERACTIVELY;
+
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ capabilities |= SCREENSHOT_CAN_SHOOT_DECORATIONS;
+#endif
+
+#ifdef HAVE_XFIXES
+ capabilities |= SCREENSHOT_CAN_SHOOT_POINTER;
+#endif
+
+ capabilities |= SCREENSHOT_CAN_SHOOT_REGION |
+ SCREENSHOT_CAN_SHOOT_WINDOW |
+ SCREENSHOT_CAN_PICK_WINDOW |
+ SCREENSHOT_CAN_DELAY_WINDOW_SHOT;
+
+ return capabilities;
+}
+
+GimpPDBStatusType
+screenshot_x11_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+ GdkDisplay *display;
+ GdkWindow *window;
+ cairo_surface_t *screenshot;
+ cairo_region_t *shape = NULL;
+ cairo_t *cr;
+ GimpColorProfile *profile;
+ GdkRectangle rect;
+ GdkRectangle screen_rect;
+ gchar *name = NULL;
+ gint screen_x;
+ gint screen_y;
+ gint monitor = shootvals->monitor;
+ gint x, y;
+
+ /* use default screen if we are running non-interactively */
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ if (shootvals->shoot_type != SHOOT_ROOT && ! shootvals->window_id)
+ {
+ if (shootvals->select_delay > 0)
+ screenshot_delay (shootvals->select_delay);
+
+ shootvals->window_id = select_window (shootvals, screen);
+
+ if (! shootvals->window_id)
+ return GIMP_PDB_CANCEL;
+ }
+
+ if (shootvals->screenshot_delay > 0)
+ screenshot_delay (shootvals->screenshot_delay);
+
+ display = gdk_screen_get_display (screen);
+
+ screen_rect.x = 0;
+ screen_rect.y = 0;
+ screen_rect.width = gdk_screen_get_width (screen);
+ screen_rect.height = gdk_screen_get_height (screen);
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ rect.x = MIN (shootvals->x1, shootvals->x2);
+ rect.y = MIN (shootvals->y1, shootvals->y2);
+ rect.width = ABS (shootvals->x2 - shootvals->x1);
+ rect.height = ABS (shootvals->y2 - shootvals->y1);
+
+ monitor = gdk_screen_get_monitor_at_point (screen,
+ rect.x + rect.width / 2,
+ rect.y + rect.height / 2);
+ }
+ else
+ {
+ if (shootvals->shoot_type == SHOOT_ROOT)
+ {
+ window = gdk_screen_get_root_window (screen);
+
+ /* FIXME: figure monitor */
+ }
+ else
+ {
+ window = gdk_x11_window_foreign_new_for_display (display,
+ shootvals->window_id);
+
+ monitor = gdk_screen_get_monitor_at_window (screen, window);
+ }
+
+ if (! window)
+ {
+ g_set_error_literal (error, 0, 0, _("Specified window not found"));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ rect.width = gdk_window_get_width (window);
+ rect.height = gdk_window_get_height (window);
+ gdk_window_get_origin (window, &x, &y);
+
+ rect.x = x;
+ rect.y = y;
+ }
+
+ if (! gdk_rectangle_intersect (&rect, &screen_rect, &rect))
+ return GIMP_PDB_EXECUTION_ERROR;
+
+ window = gdk_screen_get_root_window (screen);
+ gdk_window_get_origin (window, &screen_x, &screen_y);
+
+ screenshot = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ rect.width, rect.height);
+
+ cr = cairo_create (screenshot);
+
+ gdk_cairo_set_source_window (cr, window,
+ - (rect.x - screen_x),
+ - (rect.y - screen_y));
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ gdk_display_beep (display);
+
+ if (shootvals->shoot_type == SHOOT_WINDOW)
+ {
+ name = window_get_title (display, shootvals->window_id);
+
+ shape = window_get_shape (screen, shootvals->window_id);
+
+ if (shape)
+ cairo_region_translate (shape, x - rect.x, y - rect.y);
+ }
+
+ *image_ID = create_image (screenshot, shape, name);
+
+ cairo_surface_destroy (screenshot);
+
+ if (shape)
+ cairo_region_destroy (shape);
+
+ g_free (name);
+
+ /* FIXME: Some time might have passed until we get here.
+ * The cursor image should be grabbed together with the screenshot.
+ */
+ if ((shootvals->shoot_type == SHOOT_ROOT ||
+ shootvals->shoot_type == SHOOT_WINDOW) && shootvals->show_cursor)
+ add_cursor_image (*image_ID, display);
+
+ profile = gimp_screen_get_color_profile (screen, monitor);
+
+ if (profile)
+ {
+ gimp_image_set_color_profile (*image_ID, profile);
+ g_object_unref (profile);
+ }
+
+ return GIMP_PDB_SUCCESS;
+}
+
+#endif /* GDK_WINDOWING_X11 */
diff --git a/plug-ins/screenshot/screenshot-x11.h b/plug-ins/screenshot/screenshot-x11.h
new file mode 100644
index 0000000..9e988b1
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-x11.h
@@ -0,0 +1,36 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_X11_H__
+#define __SCREENSHOT_X11_H__
+
+
+#ifdef GDK_WINDOWING_X11
+
+gboolean screenshot_x11_available (void);
+
+ScreenshotCapabilities screenshot_x11_get_capabilities (void);
+
+GimpPDBStatusType screenshot_x11_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+#endif /* GDK_WINDOWING_X11 */
+
+
+#endif /* __SCREENSHOT_X11_H__ */
diff --git a/plug-ins/screenshot/screenshot.c b/plug-ins/screenshot/screenshot.c
new file mode 100644
index 0000000..73601f4
--- /dev/null
+++ b/plug-ins/screenshot/screenshot.c
@@ -0,0 +1,873 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
+ * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ * Copyright 2016 Michael Natterer <mitch@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-freedesktop.h"
+#include "screenshot-icon.h"
+#include "screenshot-kwin.h"
+#include "screenshot-osx.h"
+#include "screenshot-x11.h"
+#include "screenshot-win32.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+/* Defines */
+
+#define PLUG_IN_PROC "plug-in-screenshot"
+#define PLUG_IN_BINARY "screenshot"
+#define PLUG_IN_ROLE "gimp-screenshot"
+
+#ifdef __GNUC__
+#ifdef GDK_NATIVE_WINDOW_POINTER
+#if GLIB_SIZEOF_VOID_P != 4
+#warning window_id does not fit in PDB_INT32
+#endif
+#endif
+#endif
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static GimpPDBStatusType shoot (GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error);
+
+static gboolean shoot_dialog (GdkScreen **screen);
+static gboolean shoot_quit_timeout (gpointer data);
+static gboolean shoot_delay_timeout (gpointer data);
+
+
+/* Global Variables */
+
+static ScreenshotBackend backend = SCREENSHOT_BACKEND_NONE;
+static ScreenshotCapabilities capabilities = 0;
+static GtkWidget *select_delay_table = NULL;
+static GtkWidget *shot_delay_table = NULL;
+
+static ScreenshotValues shootvals =
+{
+ SHOOT_WINDOW, /* root window */
+ TRUE, /* include WM decorations */
+ 0, /* window ID */
+ 0, /* monitor */
+ 0, /* select delay */
+ 0, /* screenshot delay */
+ 0, /* coords of region dragged out by pointer */
+ 0,
+ 0,
+ 0,
+ FALSE, /* show cursor */
+ SCREENSHOT_PROFILE_POLICY_MONITOR
+};
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run /* run_proc */
+};
+
+
+/* Functions */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_INT32, "shoot-type", "The Shoot type { SHOOT-WINDOW (0), SHOOT-ROOT (1), SHOOT-REGION (2) }" },
+ { GIMP_PDB_INT32, "window-id", "Window id for SHOOT-WINDOW" },
+ { GIMP_PDB_INT32, "x1", "Region left x coord for SHOOT-REGION" },
+ { GIMP_PDB_INT32, "y1", "Region top y coord for SHOOT-REGION" },
+ { GIMP_PDB_INT32, "x2", "Region right x coord for SHOOT-REGION" },
+ { GIMP_PDB_INT32, "y2", "Region bottom y coord for SHOOT-REGION" }
+ };
+
+ static const GimpParamDef return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+ gimp_install_procedure (PLUG_IN_PROC,
+ N_("Create an image from an area of the screen"),
+ "The plug-in takes screenshots of an "
+ "interactively selected window or of the desktop, "
+ "either the whole desktop or an interactively "
+ "selected region. When called non-interactively, it "
+ "may grab the root window or use the window-id "
+ "passed as a parameter. The last four parameters "
+ "are optional and can be used to specify the corners "
+ "of the region to be grabbed."
+ "On Mac OS X or on gnome-shell, "
+ "when called non-interactively, the plug-in"
+ "only can take screenshots of the entire root window."
+ "Grabbing a window or a region is not supported"
+ "non-interactively. To grab a region or a particular"
+ "window, you need to use the interactive mode."
+ ,
+ "Sven Neumann <sven@gimp.org>, "
+ "Henrik Brix Andersen <brix@gimp.org>,"
+ "Simone Karin Lehmann",
+ "1998 - 2008",
+ "v1.1 (2008/04)",
+ N_("_Screenshot..."),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args),
+ G_N_ELEMENTS (return_vals),
+ args, return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/File/Create/Acquire");
+ gimp_plugin_icon_register (PLUG_IN_PROC, GIMP_ICON_TYPE_INLINE_PIXBUF,
+ screenshot_icon);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+ GdkScreen *screen = NULL;
+ gint32 image_ID;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+#ifdef PLATFORM_OSX
+ if (! backend && screenshot_osx_available ())
+ {
+ backend = SCREENSHOT_BACKEND_OSX;
+ capabilities = screenshot_osx_get_capabilities ();
+
+ /* on OS X, this just means shoot the shadow, default to nope */
+ shootvals.decorate = FALSE;
+ }
+#endif
+
+#ifdef G_OS_WIN32
+ if (! backend && screenshot_win32_available ())
+ {
+ backend = SCREENSHOT_BACKEND_WIN32;
+ capabilities = screenshot_win32_get_capabilities ();
+ }
+#endif
+
+#ifdef GDK_WINDOWING_X11
+ if (! backend && screenshot_x11_available ())
+ {
+ backend = SCREENSHOT_BACKEND_X11;
+ capabilities = screenshot_x11_get_capabilities ();
+ }
+#endif
+ if (! backend && screenshot_freedesktop_available ())
+ {
+ backend = SCREENSHOT_BACKEND_FREEDESKTOP;
+ capabilities = screenshot_freedesktop_get_capabilities ();
+ }
+
+ if (! backend && screenshot_kwin_available ())
+ {
+ backend = SCREENSHOT_BACKEND_KWIN;
+ capabilities = screenshot_kwin_get_capabilities ();
+ }
+
+ /* how are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_PROC, &shootvals);
+ shootvals.window_id = 0;
+
+ if ((shootvals.shoot_type == SHOOT_WINDOW &&
+ ! (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)) ||
+ (shootvals.shoot_type == SHOOT_REGION &&
+ ! (capabilities & SCREENSHOT_CAN_SHOOT_REGION)))
+ {
+ /* Shoot root is the only type of shoot which is definitely
+ * shared by all screenshot backends (basically just snap the
+ * whole display setup).
+ */
+ shootvals.shoot_type = SHOOT_ROOT;
+ }
+
+ /* Get information from the dialog */
+ if (! shoot_dialog (&screen))
+ status = GIMP_PDB_CANCEL;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams == 3 || nparams == 7)
+ {
+ shootvals.shoot_type = param[1].data.d_int32;
+ shootvals.window_id = param[2].data.d_int32;
+ shootvals.select_delay = 0;
+
+ if (shootvals.shoot_type < SHOOT_WINDOW ||
+ shootvals.shoot_type > SHOOT_REGION)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else if (shootvals.shoot_type == SHOOT_REGION)
+ {
+ if (nparams == 7)
+ {
+ shootvals.x1 = param[3].data.d_int32;
+ shootvals.y1 = param[4].data.d_int32;
+ shootvals.x2 = param[5].data.d_int32;
+ shootvals.y2 = param[6].data.d_int32;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! gdk_init_check (NULL, NULL))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (! (capabilities & SCREENSHOT_CAN_PICK_NONINTERACTIVELY))
+ {
+ if (shootvals.shoot_type == SHOOT_WINDOW ||
+ shootvals.shoot_type == SHOOT_REGION)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ }
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data from a previous run */
+ gimp_get_data (PLUG_IN_PROC, &shootvals);
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ status = shoot (screen, &image_ID, &error);
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gchar *comment = gimp_get_default_comment ();
+
+ gimp_image_undo_disable (image_ID);
+
+ if (shootvals.profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
+ {
+ GimpColorProfile *srgb_profile = gimp_color_profile_new_rgb_srgb ();
+
+ gimp_image_convert_color_profile (image_ID,
+ srgb_profile,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ TRUE);
+ g_object_unref (srgb_profile);
+ }
+
+ if (comment)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1, comment);
+
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ g_free (comment);
+ }
+
+ gimp_image_undo_enable (image_ID);
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ /* Store variable states for next run */
+ gimp_set_data (PLUG_IN_PROC, &shootvals, sizeof (ScreenshotValues));
+
+ gimp_display_new (image_ID);
+
+ /* Give some sort of feedback that the shot is done */
+ if (shootvals.select_delay > 0)
+ {
+ gdk_display_beep (gdk_screen_get_display (screen));
+ gdk_flush (); /* flush so the beep makes it to the server */
+ }
+ }
+
+ *nreturn_vals = 2;
+
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+
+/* The main Screenshot function */
+
+static GimpPDBStatusType
+shoot (GdkScreen *screen,
+ gint32 *image_ID,
+ GError **error)
+{
+#ifdef PLATFORM_OSX
+ if (backend == SCREENSHOT_BACKEND_OSX)
+ return screenshot_osx_shoot (&shootvals, screen, image_ID, error);
+#endif
+
+#ifdef G_OS_WIN32
+ if (backend == SCREENSHOT_BACKEND_WIN32)
+ return screenshot_win32_shoot (&shootvals, screen, image_ID, error);
+#endif
+
+ if (backend == SCREENSHOT_BACKEND_FREEDESKTOP)
+ return screenshot_freedesktop_shoot (&shootvals, screen, image_ID, error);
+ else if (backend == SCREENSHOT_BACKEND_KWIN)
+ return screenshot_kwin_shoot (&shootvals, screen, image_ID, error);
+
+#ifdef GDK_WINDOWING_X11
+ if (backend == SCREENSHOT_BACKEND_X11)
+ return screenshot_x11_shoot (&shootvals, screen, image_ID, error);
+#endif
+
+ return GIMP_PDB_CALLING_ERROR; /* silence compiler */
+}
+
+
+/* Screenshot dialog */
+
+static void
+shoot_dialog_add_hint (GtkNotebook *notebook,
+ ShootType type,
+ const gchar *hint)
+{
+ GtkWidget *label;
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "label", hint,
+ "wrap", TRUE,
+ "justify", GTK_JUSTIFY_LEFT,
+ "xalign", 0.0,
+ "yalign", 0.0,
+ NULL);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+
+ gtk_notebook_insert_page (notebook, label, NULL, type);
+ gtk_widget_show (label);
+}
+
+static void
+shoot_radio_button_toggled (GtkWidget *widget,
+ GtkWidget *notebook)
+{
+ gimp_radio_button_update (widget, &shootvals.shoot_type);
+
+ if (select_delay_table)
+ {
+ if (shootvals.shoot_type == SHOOT_ROOT ||
+ (shootvals.shoot_type == SHOOT_WINDOW &&
+ ! (capabilities & SCREENSHOT_CAN_PICK_WINDOW)))
+ {
+ gtk_widget_hide (select_delay_table);
+ }
+ else
+ {
+ gtk_widget_show (select_delay_table);
+ }
+ }
+ if (shot_delay_table)
+ {
+ if (shootvals.shoot_type == SHOOT_WINDOW &&
+ (capabilities & SCREENSHOT_CAN_PICK_WINDOW) &&
+ ! (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
+ {
+ gtk_widget_hide (shot_delay_table);
+ }
+ else
+ {
+ gtk_widget_show (shot_delay_table);
+ }
+ }
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
+}
+
+static gboolean
+shoot_dialog (GdkScreen **screen)
+{
+ GtkWidget *dialog;
+ GtkWidget *main_vbox;
+ GtkWidget *notebook1;
+ GtkWidget *notebook2;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *button;
+ GtkWidget *toggle;
+ GtkWidget *spinner;
+ GtkWidget *table;
+ GSList *radio_group = NULL;
+ GtkAdjustment *adj;
+ gboolean run;
+ GtkWidget *cursor_toggle = NULL;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dialog = gimp_dialog_new (_("Screenshot"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, PLUG_IN_PROC,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("S_nap"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ main_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (main_vbox);
+
+
+ /* Create delay hints notebooks early */
+ notebook1 = g_object_new (GTK_TYPE_NOTEBOOK,
+ "show-border", FALSE,
+ "show-tabs", FALSE,
+ NULL);
+ notebook2 = g_object_new (GTK_TYPE_NOTEBOOK,
+ "show-border", FALSE,
+ "show-tabs", FALSE,
+ NULL);
+
+ /* Area */
+ frame = gimp_frame_new (_("Area"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Single window */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)
+ {
+ button = gtk_radio_button_new_with_mnemonic (radio_group,
+ _("Take a screenshot of "
+ "a single _window"));
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (SHOOT_WINDOW));
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook1);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook2);
+
+ /* Window decorations */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
+ {
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Include window _decoration"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ shootvals.decorate);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &shootvals.decorate);
+
+ g_object_bind_property (button, "active",
+ toggle, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
+ /* Mouse pointer */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
+ {
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ cursor_toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cursor_toggle),
+ shootvals.show_cursor);
+ gtk_box_pack_start (GTK_BOX (hbox), cursor_toggle, TRUE, TRUE, 24);
+ gtk_widget_show (cursor_toggle);
+
+ g_signal_connect (cursor_toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &shootvals.show_cursor);
+
+ g_object_bind_property (button, "active",
+ cursor_toggle, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
+
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ shootvals.shoot_type == SHOOT_WINDOW);
+ }
+
+ /* Whole screen */
+ button = gtk_radio_button_new_with_mnemonic (radio_group,
+ _("Take a screenshot of "
+ "the entire _screen"));
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (SHOOT_ROOT));
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook1);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook2);
+
+ /* Mouse pointer */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
+ {
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ shootvals.show_cursor);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &shootvals.show_cursor);
+
+ if (cursor_toggle)
+ {
+ g_object_bind_property (cursor_toggle, "active",
+ toggle, "active",
+ G_BINDING_BIDIRECTIONAL);
+ }
+ g_object_bind_property (button, "active",
+ toggle, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ shootvals.shoot_type == SHOOT_ROOT);
+
+ /* Dragged region */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_REGION)
+ {
+ button = gtk_radio_button_new_with_mnemonic (radio_group,
+ _("Select a _region to grab"));
+ radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
+ shootvals.shoot_type == SHOOT_REGION);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ g_object_set_data (G_OBJECT (button), "gimp-item-data",
+ GINT_TO_POINTER (SHOOT_REGION));
+
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook1);
+ g_signal_connect (button, "toggled",
+ G_CALLBACK (shoot_radio_button_toggled),
+ notebook2);
+ }
+
+ frame = gimp_frame_new (_("Delay"));
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+
+ /* Selection delay */
+ table = gtk_table_new (2, 3, FALSE);
+ select_delay_table = table;
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ /* Check if this delay must be hidden from start. */
+ if (shootvals.shoot_type == SHOOT_REGION ||
+ (shootvals.shoot_type == SHOOT_WINDOW &&
+ capabilities & SCREENSHOT_CAN_PICK_WINDOW))
+ {
+ gtk_widget_show (select_delay_table);
+ }
+
+ label = gtk_label_new (_("Selection delay: "));
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (label);
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (shootvals.select_delay,
+ 0.0, 100.0, 1.0, 5.0, 0.0);
+ spinner = gimp_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_table_attach (GTK_TABLE (table), spinner, 1, 2, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (spinner);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &shootvals.select_delay);
+
+ /* translators: this is the unit label of a spinbutton */
+ label = gtk_label_new (_("seconds"));
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_SHRINK, 1.0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.1, 0.5);
+ gtk_widget_show (label);
+
+ /* Selection delay hints */
+ gtk_table_attach (GTK_TABLE (table), notebook1, 0, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+ gtk_widget_show (notebook1);
+
+ /* No selection delay for full-screen. */
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_ROOT, "");
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_REGION,
+ _("After the delay, drag your mouse to select "
+ "the region for the screenshot."));
+#ifdef G_OS_WIN32
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
+ _("Click in a window to snap it after delay."));
+#else
+ if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
+ {
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
+ _("At the end of the delay, click in a window "
+ "to snap it."));
+ }
+ else
+ {
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW, "");
+ }
+#endif
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook1), shootvals.shoot_type);
+
+ /* Screenshot delay */
+ table = gtk_table_new (2, 3, FALSE);
+ shot_delay_table = table;
+ gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
+ if (shootvals.shoot_type != SHOOT_WINDOW ||
+ ! (capabilities & SCREENSHOT_CAN_PICK_WINDOW) ||
+ (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
+ {
+ gtk_widget_show (table);
+ }
+
+ label = gtk_label_new_with_mnemonic (_("Screenshot dela_y: "));
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_widget_show (label);
+
+ adj = (GtkAdjustment *)
+ gtk_adjustment_new (shootvals.screenshot_delay,
+ 0.0, 100.0, 1.0, 5.0, 0.0);
+ spinner = gimp_spin_button_new (adj, 0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
+ gtk_table_attach (GTK_TABLE (table), spinner, 1, 2, 0, 1,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (spinner));
+ gtk_widget_show (spinner);
+
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_int_adjustment_update),
+ &shootvals.screenshot_delay);
+
+ /* translators: this is the unit label of a spinbutton */
+ label = gtk_label_new (_("seconds"));
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_SHRINK, 1.0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.1, 0.5);
+ gtk_widget_show (label);
+
+ /* Screenshot delay hints */
+ gtk_table_attach (GTK_TABLE (table), notebook2, 0, 3, 1, 2,
+ GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+ gtk_widget_show (notebook2);
+
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_ROOT,
+ _("After the delay, the screenshot is taken."));
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_REGION,
+ _("Once the region is selected, it will be "
+ "captured after this delay."));
+ if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
+ {
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
+ _("Once the window is selected, it will be "
+ "captured after this delay."));
+ }
+ else
+ {
+ shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
+ _("After the delay, the active window "
+ "will be captured."));
+ }
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook2), shootvals.shoot_type);
+
+ /* Color profile */
+ frame = gimp_int_radio_group_new (TRUE,
+ _("Color Profile"),
+ G_CALLBACK (gimp_radio_button_update),
+ &shootvals.profile_policy,
+ shootvals.profile_policy,
+
+ _("Tag image with _monitor profile"),
+ SCREENSHOT_PROFILE_POLICY_MONITOR,
+ NULL,
+
+ _("Convert image to sR_GB"),
+ SCREENSHOT_PROFILE_POLICY_SRGB,
+ NULL,
+
+ NULL);
+ gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ /* get the screen on which we are running */
+ *screen = gtk_widget_get_screen (dialog);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ if (run)
+ {
+ /* A short timeout to give the server a chance to
+ * redraw the area that was obscured by our dialog.
+ */
+ g_timeout_add (100, shoot_quit_timeout, NULL);
+ gtk_main ();
+ }
+
+ return run;
+}
+
+static gboolean
+shoot_quit_timeout (gpointer data)
+{
+ gtk_main_quit ();
+
+ return FALSE;
+}
+
+
+static gboolean
+shoot_delay_timeout (gpointer data)
+{
+ gint *seconds_left = data;
+
+ (*seconds_left)--;
+
+ if (!*seconds_left)
+ gtk_main_quit ();
+
+ return *seconds_left;
+}
+
+
+/* public functions */
+
+void
+screenshot_delay (gint seconds)
+{
+ g_timeout_add (1000, shoot_delay_timeout, &seconds);
+ gtk_main ();
+}
diff --git a/plug-ins/screenshot/screenshot.h b/plug-ins/screenshot/screenshot.h
new file mode 100644
index 0000000..13e51fa
--- /dev/null
+++ b/plug-ins/screenshot/screenshot.h
@@ -0,0 +1,80 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_H__
+#define __SCREENSHOT_H__
+
+
+typedef enum
+{
+ SCREENSHOT_BACKEND_NONE,
+ SCREENSHOT_BACKEND_OSX,
+ SCREENSHOT_BACKEND_WIN32,
+ SCREENSHOT_BACKEND_FREEDESKTOP,
+ SCREENSHOT_BACKEND_GNOME_SHELL,
+ SCREENSHOT_BACKEND_KWIN,
+ SCREENSHOT_BACKEND_X11
+} ScreenshotBackend;
+
+typedef enum
+{
+ SCREENSHOT_CAN_SHOOT_DECORATIONS = 0x1 << 0,
+ SCREENSHOT_CAN_SHOOT_POINTER = 0x1 << 1,
+ SCREENSHOT_CAN_PICK_NONINTERACTIVELY = 0x1 << 2,
+ SCREENSHOT_CAN_SHOOT_REGION = 0x1 << 3,
+ /* SHOOT_WINDOW mode only: it window selection requires active click. */
+ SCREENSHOT_CAN_PICK_WINDOW = 0x1 << 4,
+ /* SHOOT_WINDOW + SCREENSHOT_CAN_PICK_WINDOW only: if a delay can be
+ * inserted in-between selection click and actual snapshot. */
+ SCREENSHOT_CAN_DELAY_WINDOW_SHOT = 0x1 << 5,
+ SCREENSHOT_CAN_SHOOT_WINDOW = 0x1 << 6
+} ScreenshotCapabilities;
+
+typedef enum
+{
+ SCREENSHOT_PROFILE_POLICY_MONITOR,
+ SCREENSHOT_PROFILE_POLICY_SRGB
+} ScreenshotProfilePolicy;
+
+typedef enum
+{
+ SHOOT_WINDOW,
+ SHOOT_ROOT,
+ SHOOT_REGION
+} ShootType;
+
+typedef struct
+{
+ ShootType shoot_type;
+ gboolean decorate;
+ guint window_id;
+ gint monitor;
+ guint select_delay;
+ guint screenshot_delay;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gboolean show_cursor;
+ ScreenshotProfilePolicy profile_policy;
+} ScreenshotValues;
+
+
+void screenshot_delay (gint seconds);
+
+
+#endif /* __SCREENSHOT_H__ */
diff --git a/plug-ins/script-fu/Makefile.am b/plug-ins/script-fu/Makefile.am
new file mode 100644
index 0000000..46ad336
--- /dev/null
+++ b/plug-ins/script-fu/Makefile.am
@@ -0,0 +1,117 @@
+## Process this file with automake to produce Makefile.in
+
+if PLATFORM_OSX
+xobjective_c = "-xobjective-c"
+xobjective_cxx = "-xobjective-c++"
+xnone = "-xnone"
+framework_cocoa = -framework Cocoa
+endif
+
+if OS_WIN32
+mwindows = -mwindows
+WINSOCK_LIBS = -lws2_32
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la $(libm)
+
+libtinyscheme=tinyscheme/libtinyscheme.a $(libm)
+libftx=ftx/libftx.a
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+script_fu_RC = script-fu.rc.o
+endif
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir) \
+ -DSTANDALONE=0 \
+ -DUSE_INTERFACE=1 \
+ -DUSE_STRLWR=0
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(mwindows) \
+ $(framework_cocoa) \
+ $(xnone)
+
+SUBDIRS = tinyscheme ftx scripts
+
+
+libexecdir = $(gimpplugindir)/plug-ins/script-fu
+
+libexec_PROGRAMS = script-fu
+
+script_fu_SOURCES = \
+ script-fu-types.h \
+ script-fu-enums.h \
+ \
+ script-fu.c \
+ script-fu-console.c \
+ script-fu-console.h \
+ script-fu-eval.c \
+ script-fu-eval.h \
+ script-fu-interface.c \
+ script-fu-interface.h \
+ script-fu-text-console.h \
+ script-fu-text-console.c \
+ script-fu-intl.h \
+ script-fu-regex.c \
+ script-fu-regex.h \
+ script-fu-script.c \
+ script-fu-script.h \
+ script-fu-scripts.c \
+ script-fu-scripts.h \
+ script-fu-server.c \
+ script-fu-server.h \
+ script-fu-utils.c \
+ script-fu-utils.h \
+ scheme-wrapper.c \
+ scheme-wrapper.h
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libtinyscheme) \
+ $(libftx) \
+ $(GTK_LIBS) \
+ $(SOCKET_LIBS) \
+ $(WINSOCK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(script_fu_RC)
+
+
+# Perform static analysis on all *.scm files and look for usage of
+# deprecated pdb procedures
+check-for-deprecated-procedures-in-script-fu:
+ @echo "Looking for deprecated procedures in *.scm files"
+ @scm_files=`find $(top_srcdir)/plug-ins/script-fu -name "*.scm"`; \
+ deprecated_procs=`$(top_builddir)/app/gimp-$(GIMP_APP_VERSION)$(EXEEXT) --dump-pdb-procedures-deprecated`; \
+ for scm_file in $$scm_files; do \
+ for proc in $$deprecated_procs; do \
+ if grep -Eq "^([^;]*[[:blank:](])?$$proc([[:blank:])]|$$)" $$scm_file; then \
+ echo "$${scm_file} uses deprecated procedure '$${proc}'"; \
+ fi \
+ done \
+ done
diff --git a/plug-ins/script-fu/Makefile.in b/plug-ins/script-fu/Makefile.in
new file mode 100644
index 0000000..493b4f7
--- /dev/null
+++ b/plug-ins/script-fu/Makefile.in
@@ -0,0 +1,1225 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = script-fu$(EXEEXT)
+subdir = plug-ins/script-fu
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_script_fu_OBJECTS = script-fu.$(OBJEXT) script-fu-console.$(OBJEXT) \
+ script-fu-eval.$(OBJEXT) script-fu-interface.$(OBJEXT) \
+ script-fu-text-console.$(OBJEXT) script-fu-regex.$(OBJEXT) \
+ script-fu-script.$(OBJEXT) script-fu-scripts.$(OBJEXT) \
+ script-fu-server.$(OBJEXT) script-fu-utils.$(OBJEXT) \
+ scheme-wrapper.$(OBJEXT)
+script_fu_OBJECTS = $(am_script_fu_OBJECTS)
+script_fu_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la \
+ $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = tinyscheme/libtinyscheme.a $(am__DEPENDENCIES_1)
+script_fu_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(am__DEPENDENCIES_2) $(libgimp) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_3) $(libftx) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(script_fu_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/scheme-wrapper.Po \
+ ./$(DEPDIR)/script-fu-console.Po ./$(DEPDIR)/script-fu-eval.Po \
+ ./$(DEPDIR)/script-fu-interface.Po \
+ ./$(DEPDIR)/script-fu-regex.Po ./$(DEPDIR)/script-fu-script.Po \
+ ./$(DEPDIR)/script-fu-scripts.Po \
+ ./$(DEPDIR)/script-fu-server.Po \
+ ./$(DEPDIR)/script-fu-text-console.Po \
+ ./$(DEPDIR)/script-fu-utils.Po ./$(DEPDIR)/script-fu.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(script_fu_SOURCES)
+DIST_SOURCES = $(script_fu_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/script-fu
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@PLATFORM_OSX_TRUE@xobjective_c = "-xobjective-c"
+@PLATFORM_OSX_TRUE@xobjective_cxx = "-xobjective-c++"
+@PLATFORM_OSX_TRUE@xnone = "-xnone"
+@PLATFORM_OSX_TRUE@framework_cocoa = -framework Cocoa
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_TRUE@WINSOCK_LIBS = -lws2_32
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la $(libm)
+libtinyscheme = tinyscheme/libtinyscheme.a $(libm)
+libftx = ftx/libftx.a
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@script_fu_RC = script-fu.rc.o
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir) \
+ -DSTANDALONE=0 \
+ -DUSE_INTERFACE=1 \
+ -DUSE_STRLWR=0
+
+AM_CFLAGS = \
+ $(xobjective_c)
+
+AM_CXXFLAGS = \
+ $(xobjective_cxx)
+
+AM_LDFLAGS = \
+ $(mwindows) \
+ $(framework_cocoa) \
+ $(xnone)
+
+SUBDIRS = tinyscheme ftx scripts
+script_fu_SOURCES = \
+ script-fu-types.h \
+ script-fu-enums.h \
+ \
+ script-fu.c \
+ script-fu-console.c \
+ script-fu-console.h \
+ script-fu-eval.c \
+ script-fu-eval.h \
+ script-fu-interface.c \
+ script-fu-interface.h \
+ script-fu-text-console.h \
+ script-fu-text-console.c \
+ script-fu-intl.h \
+ script-fu-regex.c \
+ script-fu-regex.h \
+ script-fu-script.c \
+ script-fu-script.h \
+ script-fu-scripts.c \
+ script-fu-scripts.h \
+ script-fu-server.c \
+ script-fu-server.h \
+ script-fu-utils.c \
+ script-fu-utils.h \
+ scheme-wrapper.c \
+ scheme-wrapper.h
+
+LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(libtinyscheme) \
+ $(libftx) \
+ $(GTK_LIBS) \
+ $(SOCKET_LIBS) \
+ $(WINSOCK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(script_fu_RC)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/script-fu/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/script-fu/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+script-fu$(EXEEXT): $(script_fu_OBJECTS) $(script_fu_DEPENDENCIES) $(EXTRA_script_fu_DEPENDENCIES)
+ @rm -f script-fu$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(script_fu_OBJECTS) $(script_fu_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scheme-wrapper.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-console.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-eval.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-interface.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-regex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-script.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-scripts.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-text-console.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu-utils.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-fu.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/scheme-wrapper.Po
+ -rm -f ./$(DEPDIR)/script-fu-console.Po
+ -rm -f ./$(DEPDIR)/script-fu-eval.Po
+ -rm -f ./$(DEPDIR)/script-fu-interface.Po
+ -rm -f ./$(DEPDIR)/script-fu-regex.Po
+ -rm -f ./$(DEPDIR)/script-fu-script.Po
+ -rm -f ./$(DEPDIR)/script-fu-scripts.Po
+ -rm -f ./$(DEPDIR)/script-fu-server.Po
+ -rm -f ./$(DEPDIR)/script-fu-text-console.Po
+ -rm -f ./$(DEPDIR)/script-fu-utils.Po
+ -rm -f ./$(DEPDIR)/script-fu.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/scheme-wrapper.Po
+ -rm -f ./$(DEPDIR)/script-fu-console.Po
+ -rm -f ./$(DEPDIR)/script-fu-eval.Po
+ -rm -f ./$(DEPDIR)/script-fu-interface.Po
+ -rm -f ./$(DEPDIR)/script-fu-regex.Po
+ -rm -f ./$(DEPDIR)/script-fu-script.Po
+ -rm -f ./$(DEPDIR)/script-fu-scripts.Po
+ -rm -f ./$(DEPDIR)/script-fu-server.Po
+ -rm -f ./$(DEPDIR)/script-fu-text-console.Po
+ -rm -f ./$(DEPDIR)/script-fu-utils.Po
+ -rm -f ./$(DEPDIR)/script-fu.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libexecPROGRAMS clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Perform static analysis on all *.scm files and look for usage of
+# deprecated pdb procedures
+check-for-deprecated-procedures-in-script-fu:
+ @echo "Looking for deprecated procedures in *.scm files"
+ @scm_files=`find $(top_srcdir)/plug-ins/script-fu -name "*.scm"`; \
+ deprecated_procs=`$(top_builddir)/app/gimp-$(GIMP_APP_VERSION)$(EXEEXT) --dump-pdb-procedures-deprecated`; \
+ for scm_file in $$scm_files; do \
+ for proc in $$deprecated_procs; do \
+ if grep -Eq "^([^;]*[[:blank:](])?$$proc([[:blank:])]|$$)" $$scm_file; then \
+ echo "$${scm_file} uses deprecated procedure '$${proc}'"; \
+ fi \
+ done \
+ done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/script-fu/ftx/LICENSE b/plug-ins/script-fu/ftx/LICENSE
new file mode 100644
index 0000000..e820d87
--- /dev/null
+++ b/plug-ins/script-fu/ftx/LICENSE
@@ -0,0 +1,31 @@
+ LICENSE TERMS
+
+(c) 2002 Manuel Heras-Gilsanz (manuel@heras-gilsanz.com)
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of Manuel Heras-Gilsanz nor the names of the
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/plug-ins/script-fu/ftx/Makefile.am b/plug-ins/script-fu/ftx/Makefile.am
new file mode 100644
index 0000000..46537bf
--- /dev/null
+++ b/plug-ins/script-fu/ftx/Makefile.am
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CFLAGS = \
+ -DUSE_INTERFACE=1 \
+ -I$(srcdir)/.. \
+ $(GLIB_CFLAGS)
+
+noinst_LIBRARIES = libftx.a
+
+libftx_a_SOURCES = ftx.c ftx.h
+
+EXTRA_DIST = \
+ LICENSE \
+ README \
+ ftx-functions.txt \
+ listhome.scm
diff --git a/plug-ins/script-fu/ftx/Makefile.in b/plug-ins/script-fu/ftx/Makefile.in
new file mode 100644
index 0000000..e71d6ae
--- /dev/null
+++ b/plug-ins/script-fu/ftx/Makefile.in
@@ -0,0 +1,912 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/script-fu/ftx
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libftx_a_AR = $(AR) $(ARFLAGS)
+libftx_a_LIBADD =
+am_libftx_a_OBJECTS = ftx.$(OBJEXT)
+libftx_a_OBJECTS = $(am_libftx_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ftx.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libftx_a_SOURCES)
+DIST_SOURCES = $(libftx_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CFLAGS = \
+ -DUSE_INTERFACE=1 \
+ -I$(srcdir)/.. \
+ $(GLIB_CFLAGS)
+
+noinst_LIBRARIES = libftx.a
+libftx_a_SOURCES = ftx.c ftx.h
+EXTRA_DIST = \
+ LICENSE \
+ README \
+ ftx-functions.txt \
+ listhome.scm
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/script-fu/ftx/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/script-fu/ftx/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libftx.a: $(libftx_a_OBJECTS) $(libftx_a_DEPENDENCIES) $(EXTRA_libftx_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libftx.a
+ $(AM_V_AR)$(libftx_a_AR) libftx.a $(libftx_a_OBJECTS) $(libftx_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libftx.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ftx.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/ftx.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/ftx.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/script-fu/ftx/README b/plug-ins/script-fu/ftx/README
new file mode 100644
index 0000000..c20b215
--- /dev/null
+++ b/plug-ins/script-fu/ftx/README
@@ -0,0 +1,99 @@
+TinyScheme Extensions (TSX) 1.1 [September, 2002]
+(c) 2002 Manuel Heras-Gilsanz (manuel@heras-gilsanz.com)
+
+This software is subject to the license terms contained in the
+LICENSE file.
+
+Changelog:
+1.1 (Sept. 2002) Updated to tinyscheme 1.31
+1.0 (April 2002) First released version
+
+
+WHAT IS TSX?
+
+TinyScheme Extensions is a set of dynamic libraries incorporating
+additional funcionality to TinyScheme, a lightweight
+implementation of the Scheme programming language. TinyScheme
+(http://tinyscheme.sourceforge.net) is maintained by D. Souflis
+(dsouflis@acm.org), and is based on MiniSCHEME version 0.85k4.
+
+Scheme is a very nice and powerful programming language, but the
+basic language is very minimalistic in terms of library functions;
+only basic file input / output functionality is specified.
+Different implementations of the language (MIT Scheme, GUILE,
+Bigloo...) provide their own extension libraries. TSX attempts to
+provide commonly needed functions at a small cost in terms of
+additional program footprint. The library is modularized, so that
+it is possible (and easy!) to select desired functionality via
+#defines in tsx.h.
+
+
+INSTALLATION
+
+TSX has been tested on GNU/Linux 2.4.2 with gcc 2.96 and
+libc-2.2.2, with TinyScheme 1.31.
+
+To install, copy the distribution file to the directory
+where TinyScheme is installed (and where scheme.h lies),
+and run make. If building succeeds, a file called tsx.so
+should be created. This file can be loaded as a TinyScheme
+extension with
+
+ (load-extension "tsx-1.0/tsx")
+
+After loading TSX, you can make use of its functions.
+To reduce footprint, you can choose the functionality which
+will be included. To do so, have a look at tsx.h and
+comment the #defines for unneeded modules.
+
+If you get compiler errors, make sure you have enabled
+dynamic modules in your tinyscheme runtime (define USE_DL
+somewhere near the top in scheme.h).
+
+
+SAMPLE APPLICATIONS
+
+Three sample applications are distributed with TSX 1.0.
+The code is not particularly elegant, nor written in proper
+functional style, but is provided for illustration of the
+implemented calls.
+
+-smtp.scm
+ Sends an email to the user getting the username from
+ the USER shell variable, connecting to the SMTP port
+ on the local machine.
+
+-listhome.scm
+ Provides a list of all the files on the user's home
+ directory (obtained with the HOME environment variable).
+
+-srepl.scm
+ Provides a socket-based read-eval-print-loop. It listens
+ for connections on the 9000 port of the local machines,
+ and executes the commands received. To test it, run
+
+ telnet localhost 9000
+
+ after starting the sample application, and type Scheme
+ expressions. You will get the evaluations. To exit the
+ session, type "quit" and TinyScheme will exit, closing
+ the socket. The output of some functions will not
+ be the same as you would obtain on TinyScheme's
+ "command line", because standard output is not
+ redirected to the socket, but most commands work ok.
+
+You should copy these applications to the directory where
+TinyScheme is installed (i.e., where the "scheme" binary
+file resides), and can be run with:
+
+ ./scheme listhome.scm
+ ./scheme smtp.scm
+ ./scheme srepl.scm
+
+
+TSX FUNCTIONS
+
+The extension functions implemented by TinyScheme Extensions are
+documented in the file "tsx-functions.txt".
+
+END
diff --git a/plug-ins/script-fu/ftx/ftx-functions.txt b/plug-ins/script-fu/ftx/ftx-functions.txt
new file mode 100644
index 0000000..5365bc5
--- /dev/null
+++ b/plug-ins/script-fu/ftx/ftx-functions.txt
@@ -0,0 +1,119 @@
+File and Time Extensions for TinyScheme (FTX) 1.0 [August, 2004]
+
+Based on the TinyScheme Extensions (TSX) 1.1 [September, 2002]
+(c) 2002 Manuel Heras-Gilsanz (manuel@heras-gilsanz.com)
+
+This software is subject to the license terms contained in the
+LICENSE file.
+
+
+TSX FUNCTIONS
+
+TSX incorporates the following functions:
+
+*File system (included if HAVE_FILESYSTEM is defined in tsx.h)
+
+Scheme already defines functions to read and write files. These
+functions allow access to the filesystem to check if a certain
+file exists, to get its size, etc.
+
+In addition to these functions, a string constant DIR-SEPARATOR
+has been defined. It should be used in scripts which build file
+names that include one or more directories to keep the scripts
+portable to different operating systems.
+
+(file-exists? filename)
+ filename: string
+
+ This function returns #t if the indicated file exists, and
+ #f if it does not exist or if it is not accessible to the
+ requesting user. Accessibility is based on the real user
+ and group ID rather than the effective user ID and group ID.
+
+(file-type filename)
+ filename: string
+
+ This function returns a value based on the file type. It
+ returns FILE_TYPE_FILE (1) for regular files, FILE_TYPE_DIR
+ (2) for directories, and FILE_TYPE_LINK (3) for symbolic
+ links. The value FILE_TYPE_OTHER (0) is returned if the file
+ is of some other type, does not exist, or if the user does
+ not have sufficient privileges to allow the file type to be
+ determined.
+
+(file-size filename)
+ filename: string
+
+ This function returns the size (in bytes) of the
+ indicated file, or #f if the file does not exists or
+ is not accessible to the requesting user.
+
+(file-delete filename)
+ filename: string
+
+ Removes the specified file. It returns #t if the operation
+ succeeds, or #f otherwise (e.g., because the file is
+ read-only, or because the file does not exist).
+
+(dir-open-stream path)
+ path: string
+
+ Opens a "directory stream" on the provided directory path.
+ This stream will provide all the files within the directory,
+ using the function read-dir-entry. The stream should be closed
+ at the end with dir-close-stream.
+
+(dir-read-entry dirstream)
+ dirstream: directory stream, obtained with dir-open-stream.
+
+ It returns the name of the following directory entry, or eof
+ if all the entries were provided. Check the return value with
+ with eof-object?.
+
+(dir-rewind dirstream)
+ dirstream: directory stream, obtained with dir-open-stream.
+
+ Resets the given directory stream. The next call to dir-read-entry
+ will return the first entry again. It returns #t if the operation
+ succeeds, or #f otherwise (ie. dirstream not valid)..
+
+(dir-close-stream dirstream)
+ dirstream: directory stream, obtained with dir-open-stream.
+
+ Close directory stream. No further calls to read-dir-entry should
+ be performed.
+
+(dir-make dirname . mode)
+ dirname: string
+ mode: integer representing permissions
+
+ Create the directory specified, setting the directory permissions based
+ upon the optional mode argument (taking into account the current
+ umask). If no mode is specified then use the default (umask)
+ permissions. Returns #t if the operation succeeds, otherwise #f.
+ Possible reasons for failure are that the directory already exists,
+ the user is not authorized to create it, or the mode is incorrectly
+ specified).
+
+*Time (available if HAVE_TIME is defined in tsx.h)
+
+(time)
+ Returns the current local time, as a list of integer
+ containing:
+ (year month day-of-month hour min sec millisec)
+ The year is expressed as an offset from 1900.
+
+(gettimeofday)
+ Returns a list containing the number of seconds from
+ the beginning of the day, and microseconds within the
+ current second.
+
+(usleep microsec)
+ microsec: integer
+
+ Suspends execution of the calling thread during the
+ specified number of microseconds.
+
+
+END
+
diff --git a/plug-ins/script-fu/ftx/ftx.c b/plug-ins/script-fu/ftx/ftx.c
new file mode 100644
index 0000000..f9de1bf
--- /dev/null
+++ b/plug-ins/script-fu/ftx/ftx.c
@@ -0,0 +1,415 @@
+/* TinyScheme Extensions
+ * (c) 2002 Visual Tools, S.A.
+ * Manuel Heras-Gilsanz (manuel@heras-gilsanz.com)
+ *
+ * This software is subject to the terms stated in the
+ * LICENSE file.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+
+#include <glib.h>
+
+#include "tinyscheme/scheme-private.h"
+
+#undef cons
+
+typedef enum
+{
+ FILE_TYPE_UNKNOWN = 0, FILE_TYPE_FILE, FILE_TYPE_DIR, FILE_TYPE_LINK
+} FileType;
+
+struct
+named_constant {
+ const char *name;
+ FileType value;
+};
+
+struct named_constant
+file_type_constants[] = {
+ { "FILE-TYPE-UNKNOWN", FILE_TYPE_UNKNOWN },
+ { "FILE-TYPE-FILE", FILE_TYPE_FILE },
+ { "FILE-TYPE-DIR", FILE_TYPE_DIR },
+ { "FILE-TYPE-LINK", FILE_TYPE_LINK },
+ { NULL, 0 }
+};
+
+pointer foreign_fileexists(scheme *sc, pointer args);
+pointer foreign_filetype(scheme *sc, pointer args);
+pointer foreign_filesize(scheme *sc, pointer args);
+pointer foreign_filedelete(scheme *sc, pointer args);
+pointer foreign_diropenstream(scheme *sc, pointer args);
+pointer foreign_dirreadentry(scheme *sc, pointer args);
+pointer foreign_dirrewind(scheme *sc, pointer args);
+pointer foreign_dirclosestream(scheme *sc, pointer args);
+pointer foreign_mkdir(scheme *sc, pointer args);
+
+pointer foreign_getenv(scheme *sc, pointer args);
+pointer foreign_time(scheme *sc, pointer args);
+pointer foreign_gettimeofday(scheme *sc, pointer args);
+pointer foreign_usleep(scheme *sc, pointer args);
+void init_ftx (scheme *sc);
+
+
+pointer foreign_fileexists(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ char *filename;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+
+ filename = sc->vptr->string_value(first_arg);
+ filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+ if (g_file_test(filename, G_FILE_TEST_EXISTS))
+ return sc->T;
+
+ return sc->F;
+}
+
+pointer foreign_filetype(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ char *filename;
+ int retcode;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+
+ filename = sc->vptr->string_value(first_arg);
+ filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+
+ if (g_file_test(filename, G_FILE_TEST_IS_SYMLINK))
+ retcode = FILE_TYPE_LINK;
+ else if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+ retcode = FILE_TYPE_FILE;
+ else if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+ retcode = FILE_TYPE_DIR;
+ else
+ retcode = FILE_TYPE_UNKNOWN;
+
+ return sc->vptr->mk_integer(sc, retcode);
+}
+
+pointer foreign_filesize(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ pointer ret;
+ struct stat buf;
+ char * filename;
+ int retcode;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+
+ filename = sc->vptr->string_value(first_arg);
+ filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+ retcode = stat(filename, &buf);
+ if (retcode == 0)
+ ret = sc->vptr->mk_integer(sc,buf.st_size);
+ else
+ ret = sc->F;
+ return ret;
+}
+
+pointer foreign_filedelete(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ pointer ret;
+ char * filename;
+ int retcode;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg)) {
+ return sc->F;
+ }
+
+ filename = sc->vptr->string_value(first_arg);
+ filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
+ retcode = unlink(filename);
+ if (retcode == 0)
+ ret = sc->T;
+ else
+ ret = sc->F;
+ return ret;
+}
+
+pointer foreign_diropenstream(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ char *dirpath;
+ GDir *dir;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+
+ dirpath = sc->vptr->string_value(first_arg);
+ dirpath = g_filename_from_utf8 (dirpath, -1, NULL, NULL, NULL);
+
+ dir = g_dir_open(dirpath, 0, NULL);
+ if (dir == NULL)
+ return sc->F;
+
+ /* Stuffing a pointer in a long may not always be portable ~~~~~ */
+ return (sc->vptr->mk_integer(sc, (long) dir));
+}
+
+pointer foreign_dirreadentry(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ GDir *dir;
+ gchar *entry;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_integer(first_arg))
+ return sc->F;
+
+ dir = (GDir *) sc->vptr->ivalue(first_arg);
+ if (dir == NULL)
+ return sc->F;
+
+ entry = (gchar *)g_dir_read_name(dir);
+ if (entry == NULL)
+ return sc->EOF_OBJ;
+
+ entry = g_filename_to_utf8 (entry, -1, NULL, NULL, NULL);
+ return (sc->vptr->mk_string(sc, entry));
+}
+
+pointer foreign_dirrewind(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ GDir *dir;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_integer(first_arg))
+ return sc->F;
+
+ dir = (GDir *) sc->vptr->ivalue(first_arg);
+ if (dir == NULL)
+ return sc->F;
+
+ g_dir_rewind(dir);
+ return sc->T;
+}
+
+pointer foreign_dirclosestream(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ GDir *dir;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_integer(first_arg))
+ return sc->F;
+
+ dir = (GDir *) sc->vptr->ivalue(first_arg);
+ if (dir == NULL)
+ return sc->F;
+
+ g_dir_close(dir);
+ return sc->T;
+}
+
+pointer foreign_mkdir(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ pointer rest;
+ pointer second_arg;
+ char *dirname;
+ mode_t mode;
+ int retcode;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+ dirname = sc->vptr->string_value(first_arg);
+ dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
+
+ rest = sc->vptr->pair_cdr(args);
+ if (sc->vptr->is_pair(rest)) /* optional mode argument */
+ {
+ second_arg = sc->vptr->pair_car(rest);
+ if (!sc->vptr->is_integer(second_arg))
+ return sc->F;
+ mode = sc->vptr->ivalue(second_arg);
+ }
+ else
+ mode = 0777;
+
+ retcode = g_mkdir(dirname, (mode_t)mode);
+ if (retcode == 0)
+ return sc->T;
+ else
+ return sc->F;
+}
+
+pointer foreign_getenv(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ pointer ret;
+ char *varname;
+ const char *value;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+
+ if (!sc->vptr->is_string(first_arg))
+ return sc->F;
+
+ varname = sc->vptr->string_value(first_arg);
+ value = g_getenv(varname);
+ if (value == NULL)
+ ret = sc->F;
+ else
+ ret = sc->vptr->mk_string(sc,value);
+
+ return ret;
+}
+
+pointer foreign_time(scheme *sc, pointer args)
+{
+ time_t now;
+ struct tm *now_tm;
+ pointer ret;
+
+ if (args != sc->NIL)
+ return sc->F;
+
+ time(&now);
+ now_tm = localtime(&now);
+
+ ret = sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_year),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_mon),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_mday),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_hour),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_min),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) now_tm->tm_sec),sc->NIL))))));
+
+ return ret;
+}
+
+pointer foreign_gettimeofday(scheme *sc, pointer args)
+{
+ pointer ret;
+ gint64 time;
+
+ time = g_get_real_time ();
+
+ ret = sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) time / G_USEC_PER_SEC),
+ sc->vptr->cons(sc, sc->vptr->mk_integer(sc,(long) time % G_USEC_PER_SEC),
+ sc->NIL));
+
+ return ret;
+}
+
+pointer foreign_usleep(scheme *sc, pointer args)
+{
+ pointer first_arg;
+ long usec;
+
+ if (args == sc->NIL)
+ return sc->F;
+
+ first_arg = sc->vptr->pair_car(args);
+ if (!sc->vptr->is_integer(first_arg))
+ return sc->F;
+
+ usec = sc->vptr->ivalue(first_arg);
+ g_usleep(usec);
+
+ return sc->T;
+}
+
+/* This function gets called when TinyScheme is loading the extension */
+void init_ftx (scheme *sc)
+{
+ int i;
+
+ sc->vptr->scheme_define(sc,sc->global_env,
+ sc->vptr->mk_symbol(sc,"getenv"),
+ sc->vptr->mk_foreign_func(sc, foreign_getenv));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"time"),
+ sc->vptr->mk_foreign_func(sc, foreign_time));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"gettimeofday"),
+ sc->vptr->mk_foreign_func(sc, foreign_gettimeofday));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"usleep"),
+ sc->vptr->mk_foreign_func(sc, foreign_usleep));
+
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"file-exists?"),
+ sc->vptr->mk_foreign_func(sc, foreign_fileexists));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"file-type"),
+ sc->vptr->mk_foreign_func(sc, foreign_filetype));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"file-size"),
+ sc->vptr->mk_foreign_func(sc, foreign_filesize));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"file-delete"),
+ sc->vptr->mk_foreign_func(sc, foreign_filedelete));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"dir-open-stream"),
+ sc->vptr->mk_foreign_func(sc, foreign_diropenstream));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"dir-read-entry"),
+ sc->vptr->mk_foreign_func(sc, foreign_dirreadentry));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"dir-rewind"),
+ sc->vptr->mk_foreign_func(sc, foreign_dirrewind));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"dir-close-stream"),
+ sc->vptr->mk_foreign_func(sc, foreign_dirclosestream));
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc,"dir-make"),
+ sc->vptr->mk_foreign_func(sc, foreign_mkdir));
+
+ for (i = 0; file_type_constants[i].name != NULL; ++i)
+ {
+ sc->vptr->scheme_define(sc, sc->global_env,
+ sc->vptr->mk_symbol(sc, file_type_constants[i].name),
+ sc->vptr->mk_integer(sc, file_type_constants[i].value));
+ }
+}
diff --git a/plug-ins/script-fu/ftx/ftx.h b/plug-ins/script-fu/ftx/ftx.h
new file mode 100644
index 0000000..f40c30d
--- /dev/null
+++ b/plug-ins/script-fu/ftx/ftx.h
@@ -0,0 +1,2 @@
+/* This function gets called when TinyScheme is initializing the extension */
+void init_ftx (scheme *sc);
diff --git a/plug-ins/script-fu/ftx/listhome.scm b/plug-ins/script-fu/ftx/listhome.scm
new file mode 100644
index 0000000..1fa8d14
--- /dev/null
+++ b/plug-ins/script-fu/ftx/listhome.scm
@@ -0,0 +1,58 @@
+; listhome.scm
+; Sample usage of TinyScheme Extension
+; This simple program lists the directory entries on the
+; user's home directory.
+
+; It uses the following TinyScheme Extension functions:
+; getenv
+; Used to get HOME environment variable.
+; open-dir-stream
+; Used to open directory stream.
+; read-dir-entry
+; Used to read directory entries.
+; close-dir-entry
+; Used at the end, to close directory stream when done.
+
+; check that extensions are enabled
+(if (not (defined? 'load-extension))
+ (begin
+ (display "TinyScheme has extensions disabled. Enable them!!")
+ (newline)
+ (quit)))
+
+; load TinyScheme extension
+(load-extension "tsx-1.1/tsx")
+
+; check that the necessary functions are available (the user
+; might have removed some functionality...)
+(if (or
+ (not (defined? 'getenv))
+ (not (defined? 'dir-open-stream))
+ (not (defined? 'dir-read-entry))
+ (not (defined? 'dir-close-stream)))
+ (begin
+ (display "Some necessary functions are not available. Exiting!")
+ (newline)
+ (quit)))
+
+; get user's home dir from HOME environment var
+(define homedir (getenv "HOME"))
+(display "Listing contents of ") (display homedir) (newline)
+
+; create directory stream to read dir entries
+(define dirstream (dir-open-stream homedir))
+(if (not dirstream)
+ (begin
+ (display "Unable to open home directory!! Check value of HOME environment var.")
+ (quit)))
+
+(let listentry ((entry (dir-read-entry dirstream)))
+ (if (eof-object? entry)
+ #t
+ (begin
+ (display entry)
+ (newline)
+ (listentry (dir-read-entry dirstream)))))
+
+(dir-close-stream dirstream)
+
diff --git a/plug-ins/script-fu/scheme-wrapper.c b/plug-ins/script-fu/scheme-wrapper.c
new file mode 100644
index 0000000..69e22c0
--- /dev/null
+++ b/plug-ins/script-fu/scheme-wrapper.c
@@ -0,0 +1,1671 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if 0
+#define DEBUG_MARSHALL 0 /* No need to define this until you need it */
+#define DEBUG_SCRIPTS 0
+#endif
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include <gtk/gtk.h>
+
+#include "libgimp/gimp.h"
+
+#include "tinyscheme/scheme-private.h"
+#if USE_DL
+#include "tinyscheme/dynload.h"
+#endif
+#include "ftx/ftx.h"
+
+#include "script-fu-types.h"
+
+#include "script-fu-console.h"
+#include "script-fu-interface.h"
+#include "script-fu-regex.h"
+#include "script-fu-scripts.h"
+#include "script-fu-server.h"
+
+#include "scheme-wrapper.h"
+
+
+#undef cons
+
+static void ts_init_constants (scheme *sc);
+static void ts_init_enum (scheme *sc,
+ GType enum_type);
+static void ts_init_procedures (scheme *sc,
+ gboolean register_scipts);
+static void convert_string (gchar *str);
+static pointer script_fu_marshal_procedure_call (scheme *sc,
+ pointer a,
+ gboolean permissive);
+static pointer script_fu_marshal_procedure_call_strict (scheme *sc,
+ pointer a);
+static pointer script_fu_marshal_procedure_call_permissive (scheme *sc,
+ pointer a);
+static void script_fu_marshal_destroy_args (GimpParam *params,
+ gint n_params);
+
+static pointer script_fu_register_call (scheme *sc,
+ pointer a);
+static pointer script_fu_menu_register_call (scheme *sc,
+ pointer a);
+static pointer script_fu_quit_call (scheme *sc,
+ pointer a);
+static pointer script_fu_nil_call (scheme *sc,
+ pointer a);
+
+static gboolean ts_load_file (const gchar *dirname,
+ const gchar *basename);
+
+typedef struct
+{
+ const gchar *name;
+ gint value;
+} NamedConstant;
+
+static const NamedConstant script_constants[] =
+{
+ /* Useful values from libgimpbase/gimplimits.h */
+ { "MIN-IMAGE-SIZE", GIMP_MIN_IMAGE_SIZE },
+ { "MAX-IMAGE-SIZE", GIMP_MAX_IMAGE_SIZE },
+ { "MIN-RESOLUTION", GIMP_MIN_RESOLUTION },
+ { "MAX-RESOLUTION", GIMP_MAX_RESOLUTION },
+
+ /* Useful misc stuff */
+ { "TRUE", TRUE },
+ { "FALSE", FALSE },
+
+ /* Builtin units */
+ { "UNIT-PIXEL", GIMP_UNIT_PIXEL },
+ { "UNIT-INCH", GIMP_UNIT_INCH },
+ { "UNIT-MM", GIMP_UNIT_MM },
+ { "UNIT-POINT", GIMP_UNIT_POINT },
+ { "UNIT-PICA", GIMP_UNIT_PICA },
+
+ /* Script-Fu types */
+ { "SF-IMAGE", SF_IMAGE },
+ { "SF-DRAWABLE", SF_DRAWABLE },
+ { "SF-LAYER", SF_LAYER },
+ { "SF-CHANNEL", SF_CHANNEL },
+ { "SF-VECTORS", SF_VECTORS },
+ { "SF-COLOR", SF_COLOR },
+ { "SF-TOGGLE", SF_TOGGLE },
+ { "SF-VALUE", SF_VALUE },
+ { "SF-STRING", SF_STRING },
+ { "SF-FILENAME", SF_FILENAME },
+ { "SF-DIRNAME", SF_DIRNAME },
+ { "SF-ADJUSTMENT", SF_ADJUSTMENT },
+ { "SF-FONT", SF_FONT },
+ { "SF-PATTERN", SF_PATTERN },
+ { "SF-BRUSH", SF_BRUSH },
+ { "SF-GRADIENT", SF_GRADIENT },
+ { "SF-OPTION", SF_OPTION },
+ { "SF-PALETTE", SF_PALETTE },
+ { "SF-TEXT", SF_TEXT },
+ { "SF-ENUM", SF_ENUM },
+ { "SF-DISPLAY", SF_DISPLAY },
+
+ /* For SF-ADJUSTMENT */
+ { "SF-SLIDER", SF_SLIDER },
+ { "SF-SPINNER", SF_SPINNER },
+
+ { NULL, 0 }
+};
+
+
+static scheme sc;
+
+
+void
+tinyscheme_init (GList *path,
+ gboolean register_scripts)
+{
+ /* init the interpreter */
+ if (! scheme_init (&sc))
+ {
+ g_message ("Could not initialize TinyScheme!");
+ return;
+ }
+
+ scheme_set_input_port_file (&sc, stdin);
+ scheme_set_output_port_file (&sc, stdout);
+ ts_register_output_func (ts_stdout_output_func, NULL);
+
+ /* Initialize the TinyScheme extensions */
+ init_ftx (&sc);
+ script_fu_regex_init (&sc);
+
+ /* register in the interpreter the gimp functions and types. */
+ ts_init_constants (&sc);
+ ts_init_procedures (&sc, register_scripts);
+
+ if (path)
+ {
+ GList *list;
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ gchar *dir = g_file_get_path (list->data);
+
+ if (ts_load_file (dir, "script-fu.init"))
+ {
+ /* To improve compatibility with older Script-Fu scripts,
+ * load script-fu-compat.init from the same directory.
+ */
+ ts_load_file (dir, "script-fu-compat.init");
+
+ /* To improve compatibility with older GIMP version,
+ * load plug-in-compat.init from the same directory.
+ */
+ ts_load_file (dir, "plug-in-compat.init");
+
+ g_free (dir);
+
+ break;
+ }
+
+ g_free (dir);
+ }
+
+ if (list == NULL)
+ g_printerr ("Unable to read initialization file script-fu.init\n");
+ }
+}
+
+/* Create an SF-RUN-MODE constant for use in scripts.
+ * It is set to the run mode state determined by GIMP.
+ */
+void
+ts_set_run_mode (GimpRunMode run_mode)
+{
+ pointer symbol;
+
+ symbol = sc.vptr->mk_symbol (&sc, "SF-RUN-MODE");
+ sc.vptr->scheme_define (&sc, sc.global_env, symbol,
+ sc.vptr->mk_integer (&sc, run_mode));
+ sc.vptr->setimmutable (symbol);
+}
+
+void
+ts_set_print_flag (gint print_flag)
+{
+ sc.print_output = print_flag;
+}
+
+void
+ts_print_welcome (void)
+{
+ ts_output_string (TS_OUTPUT_NORMAL,
+ "Welcome to TinyScheme, Version 1.40\n", -1);
+ ts_output_string (TS_OUTPUT_NORMAL,
+ "Copyright (c) Dimitrios Souflis\n", -1);
+}
+
+void
+ts_interpret_stdin (void)
+{
+ scheme_load_file (&sc, stdin);
+}
+
+gint
+ts_interpret_string (const gchar *expr)
+{
+#if DEBUG_SCRIPTS
+ sc.print_output = 1;
+ sc.tracing = 1;
+#endif
+
+ sc.vptr->load_string (&sc, (char *) expr);
+
+ return sc.retcode;
+}
+
+const gchar *
+ts_get_success_msg (void)
+{
+ if (sc.vptr->is_string (sc.value))
+ return sc.vptr->string_value (sc.value);
+
+ return "Success";
+}
+
+void
+ts_stdout_output_func (TsOutputType type,
+ const char *string,
+ int len,
+ gpointer user_data)
+{
+ if (len < 0)
+ len = strlen (string);
+ fprintf (stdout, "%.*s", len, string);
+ fflush (stdout);
+}
+
+void
+ts_gstring_output_func (TsOutputType type,
+ const char *string,
+ int len,
+ gpointer user_data)
+{
+ GString *gstr = (GString *) user_data;
+
+ g_string_append_len (gstr, string, len);
+}
+
+
+/* private functions */
+
+/*
+ * Below can be found the functions responsible for registering the
+ * gimp functions and types against the scheme interpreter.
+ */
+static void
+ts_init_constants (scheme *sc)
+{
+ const gchar **enum_type_names;
+ gint n_enum_type_names;
+ gint i;
+ pointer symbol;
+ GQuark quark;
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-directory");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-data-directory");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_data_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-plug-in-directory");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_plug_in_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-locale-directory");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_locale_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-sysconf-directory");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_sysconf_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ enum_type_names = gimp_enums_get_type_names (&n_enum_type_names);
+ quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ for (i = 0; i < n_enum_type_names; i++)
+ {
+ const gchar *enum_name = enum_type_names[i];
+ GType enum_type = g_type_from_name (enum_name);
+
+ ts_init_enum (sc, enum_type);
+
+ enum_type = (GType) g_type_get_qdata (enum_type, quark);
+
+ if (enum_type)
+ ts_init_enum (sc, enum_type);
+ }
+
+ /* Constants used in the register block of scripts */
+ for (i = 0; script_constants[i].name != NULL; ++i)
+ {
+ symbol = sc->vptr->mk_symbol (sc, script_constants[i].name);
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_integer (sc,
+ script_constants[i].value));
+ sc->vptr->setimmutable (symbol);
+ }
+
+ /* Define string constant for use in building paths to files/directories */
+ symbol = sc->vptr->mk_symbol (sc, "DIR-SEPARATOR");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, G_DIR_SEPARATOR_S));
+ sc->vptr->setimmutable (symbol);
+
+ /* Define string constant for use in building search paths */
+ symbol = sc->vptr->mk_symbol (sc, "SEARCHPATH-SEPARATOR");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, G_SEARCHPATH_SEPARATOR_S));
+ sc->vptr->setimmutable (symbol);
+
+ /* These constants are deprecated and will be removed at a later date. */
+ symbol = sc->vptr->mk_symbol (sc, "gimp-dir");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-data-dir");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_data_directory ()));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "gimp-plugin-dir");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_string (sc, gimp_plug_in_directory ()));
+ sc->vptr->setimmutable (symbol);
+}
+
+static void
+ts_init_enum (scheme *sc,
+ GType enum_type)
+{
+ GEnumClass *enum_class = g_type_class_ref (enum_type);
+ GEnumValue *value;
+
+ for (value = enum_class->values; value->value_name; value++)
+ {
+ if (g_str_has_prefix (value->value_name, "GIMP_"))
+ {
+ gchar *scheme_name;
+ pointer symbol;
+
+ scheme_name = g_strdup (value->value_name + strlen ("GIMP_"));
+ convert_string (scheme_name);
+
+ symbol = sc->vptr->mk_symbol (sc, scheme_name);
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_integer (sc, value->value));
+ sc->vptr->setimmutable (symbol);
+
+ g_free (scheme_name);
+ }
+ }
+
+ g_type_class_unref (enum_class);
+}
+
+static void
+ts_init_procedures (scheme *sc,
+ gboolean register_scripts)
+{
+ gchar **proc_list;
+ gint num_procs;
+ gint i;
+ pointer symbol;
+
+#if USE_DL
+ symbol = sc->vptr->mk_symbol (sc,"load-extension");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc, scm_load_ext));
+ sc->vptr->setimmutable (symbol);
+#endif
+
+ symbol = sc->vptr->mk_symbol (sc, "script-fu-register");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc,
+ register_scripts ?
+ script_fu_register_call :
+ script_fu_nil_call));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "script-fu-menu-register");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc,
+ register_scripts ?
+ script_fu_menu_register_call :
+ script_fu_nil_call));
+ sc->vptr->setimmutable (symbol);
+
+ symbol = sc->vptr->mk_symbol (sc, "script-fu-quit");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc, script_fu_quit_call));
+ sc->vptr->setimmutable (symbol);
+
+ /* register the database execution procedure */
+ symbol = sc->vptr->mk_symbol (sc, "gimp-proc-db-call");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc,
+ script_fu_marshal_procedure_call_strict));
+ sc->vptr->setimmutable (symbol);
+
+ /* register the internal database execution procedure; see comment below */
+ symbol = sc->vptr->mk_symbol (sc, "-gimp-proc-db-call");
+ sc->vptr->scheme_define (sc, sc->global_env, symbol,
+ sc->vptr->mk_foreign_func (sc,
+ script_fu_marshal_procedure_call_permissive));
+ sc->vptr->setimmutable (symbol);
+
+ gimp_procedural_db_query (".*", ".*", ".*", ".*", ".*", ".*", ".*",
+ &num_procs, &proc_list);
+
+ /* Register each procedure as a scheme func */
+ for (i = 0; i < num_procs; i++)
+ {
+ gchar *buff;
+
+ /* Build a define that will call the foreign function.
+ * The Scheme statement was suggested by Simon Budig.
+ *
+ * We call the procedure through -gimp-proc-db-call, which is a more
+ * permissive version of gimp-proc-db-call, that accepts (and ignores)
+ * any number of arguments for nullary procedures, for backward
+ * compatibility.
+ */
+ buff = g_strdup_printf (" (define (%s . args)"
+ " (apply -gimp-proc-db-call \"%s\" args))",
+ proc_list[i], proc_list[i]);
+
+ /* Execute the 'define' */
+ sc->vptr->load_string (sc, buff);
+
+ g_free (buff);
+ }
+
+ g_strfreev (proc_list);
+}
+
+static gboolean
+ts_load_file (const gchar *dirname,
+ const gchar *basename)
+{
+ gchar *filename;
+ FILE *fin;
+
+ filename = g_build_filename (dirname, basename, NULL);
+
+ fin = g_fopen (filename, "rb");
+
+ g_free (filename);
+
+ if (fin)
+ {
+ scheme_load_file (&sc, fin);
+ fclose (fin);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+convert_string (gchar *str)
+{
+ while (*str)
+ {
+ if (*str == '_') *str = '-';
+ str++;
+ }
+}
+
+/* This is called by the Scheme interpreter to allow calls to GIMP functions */
+static pointer
+script_fu_marshal_procedure_call (scheme *sc,
+ pointer a,
+ gboolean permissive)
+{
+ GimpParam *args;
+ GimpParam *values = NULL;
+ gint nvalues;
+ gchar *proc_name;
+ gchar *proc_blurb;
+ gchar *proc_help;
+ gchar *proc_author;
+ gchar *proc_copyright;
+ gchar *proc_date;
+ GimpPDBProcType proc_type;
+ gint nparams;
+ gint nreturn_vals;
+ GimpParamDef *params;
+ GimpParamDef *return_vals;
+ gchar error_str[1024];
+ gint i;
+ gint success = TRUE;
+ pointer return_val = sc->NIL;
+
+#if DEBUG_MARSHALL
+/* These three #defines are from Tinyscheme (tinyscheme/scheme.c) */
+#define T_MASKTYPE 31
+#define typeflag(p) ((p)->_flag)
+#define type(p) (typeflag(p)&T_MASKTYPE)
+
+ static const char *ts_types[] =
+ {
+ "T_NONE",
+ "T_STRING", "T_NUMBER", "T_SYMBOL", "T_PROC",
+ "T_PAIR", "T_CLOSURE", "T_CONTINUATION", "T_FOREIGN",
+ "T_CHARACTER", "T_PORT", "T_VECTOR", "T_MACRO",
+ "T_PROMISE", "T_ENVIRONMENT","T_ARRAY"
+ };
+
+ g_printerr ("\nIn %s()\n", G_STRFUNC);
+#endif
+
+ /* Make sure there are arguments */
+ if (a == sc->NIL)
+ return foreign_error (sc,
+ "Procedure argument marshaller was called with no arguments. "
+ "The procedure to be executed and the arguments it requires "
+ "(possibly none) must be specified.", 0);
+
+ /* The PDB procedure name is the argument or first argument of the list */
+ if (sc->vptr->is_pair (a))
+ proc_name = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ else
+ proc_name = g_strdup (sc->vptr->string_value (a));
+
+#ifdef DEBUG_MARSHALL
+ g_printerr (" proc name: %s\n", proc_name);
+ g_printerr (" parms rcvd: %d\n", sc->vptr->list_length (sc, a)-1);
+#endif
+
+ /* report the current command */
+ script_fu_interface_report_cc (proc_name);
+
+ /* Attempt to fetch the procedure from the database */
+ if (! gimp_procedural_db_proc_info (proc_name,
+ &proc_blurb,
+ &proc_help,
+ &proc_author,
+ &proc_copyright,
+ &proc_date,
+ &proc_type,
+ &nparams, &nreturn_vals,
+ &params, &return_vals))
+ {
+#ifdef DEBUG_MARSHALL
+ g_printerr (" Invalid procedure name\n");
+#endif
+ g_snprintf (error_str, sizeof (error_str),
+ "Invalid procedure name %s specified", proc_name);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ /* Free the name and the description which are of no use here. */
+ for (i = 0; i < nparams; i++)
+ {
+ g_free (params[i].name);
+ g_free (params[i].description);
+ }
+ for (i = 0; i < nreturn_vals; i++)
+ {
+ g_free (return_vals[i].name);
+ g_free (return_vals[i].description);
+ }
+
+ /* Check the supplied number of arguments */
+ if ((nparams > 0 || ! permissive) &&
+ (sc->vptr->list_length (sc, a) - 1) != nparams)
+ {
+#if DEBUG_MARSHALL
+ g_printerr (" Invalid number of arguments (expected %d but received %d)",
+ nparams, (sc->vptr->list_length (sc, a) - 1));
+#endif
+ g_snprintf (error_str, sizeof (error_str),
+ "Invalid number of arguments for %s (expected %d but received %d)",
+ proc_name, nparams, (sc->vptr->list_length (sc, a) - 1));
+ return foreign_error (sc, error_str, 0);
+ }
+
+ /* Marshall the supplied arguments */
+ if (nparams)
+ args = g_new (GimpParam, nparams);
+ else
+ args = NULL;
+
+ for (i = 0; i < nparams; i++)
+ {
+ gint32 n_elements;
+ pointer vector;
+ gint j;
+
+ a = sc->vptr->pair_cdr (a);
+
+#if DEBUG_MARSHALL
+ {
+ const gchar *type_name;
+
+ gimp_enum_get_value (GIMP_TYPE_PDB_ARG_TYPE,
+ params[i].type,
+ &type_name, NULL, NULL, NULL);
+
+ g_printerr (" param %d - expecting type %s (%d)\n",
+ i + 1, type_name, params[i].type);
+ g_printerr (" passed arg is type %s (%d)\n",
+ ts_types[ type(sc->vptr->pair_car (a)) ],
+ type(sc->vptr->pair_car (a)));
+ }
+#endif
+
+ args[i].type = params[i].type;
+
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ case GIMP_PDB_DISPLAY:
+ case GIMP_PDB_IMAGE:
+ case GIMP_PDB_ITEM:
+ case GIMP_PDB_LAYER:
+ case GIMP_PDB_CHANNEL:
+ case GIMP_PDB_DRAWABLE:
+ case GIMP_PDB_SELECTION:
+ case GIMP_PDB_VECTORS:
+ if (!sc->vptr->is_number (sc->vptr->pair_car (a)))
+ success = FALSE;
+ if (success)
+ {
+ args[i].data.d_int32 = sc->vptr->ivalue (sc->vptr->pair_car (a));
+#if DEBUG_MARSHALL
+ g_printerr (" int32 arg is '%d'\n", args[i].data.d_int32);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_INT16:
+ if (!sc->vptr->is_number (sc->vptr->pair_car (a)))
+ success = FALSE;
+ if (success)
+ {
+ args[i].data.d_int16 = (gint16) sc->vptr->ivalue (sc->vptr->pair_car (a));
+#if DEBUG_MARSHALL
+ g_printerr (" int16 arg is '%d'\n", args[i].data.d_int16);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_INT8:
+ if (!sc->vptr->is_number (sc->vptr->pair_car (a)))
+ success = FALSE;
+ if (success)
+ {
+ args[i].data.d_int8 = (guint8) sc->vptr->ivalue (sc->vptr->pair_car (a));
+#if DEBUG_MARSHALL
+ g_printerr (" int8 arg is '%u'\n", args[i].data.d_int8);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_FLOAT:
+ if (!sc->vptr->is_number (sc->vptr->pair_car (a)))
+ success = FALSE;
+ if (success)
+ {
+ args[i].data.d_float = sc->vptr->rvalue (sc->vptr->pair_car (a));
+#if DEBUG_MARSHALL
+ g_printerr (" float arg is '%f'\n", args[i].data.d_float);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_STRING:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ success = FALSE;
+ if (success)
+ {
+ args[i].data.d_string = sc->vptr->string_value (sc->vptr->pair_car (a));
+#if DEBUG_MARSHALL
+ g_printerr (" string arg is '%s'\n", args[i].data.d_string);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ vector = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_vector (vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i-1].data.d_int32;
+ if (n_elements < 0 ||
+ n_elements > sc->vptr->vector_length (vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "INT32 vector (argument %d) for function %s has "
+ "size of %ld but expected size of %d",
+ i+1, proc_name,
+ sc->vptr->vector_length (vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_int32array = g_new (gint32, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->vector_elem (vector, j);
+
+ /* FIXME: Check values in vector stay within range for each type. */
+ if (!sc->vptr->is_number (v_element))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a number (argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ args[i].data.d_int32array[j] =
+ (gint32) sc->vptr->ivalue (v_element);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->vector_length (vector);
+ g_printerr (" int32 vector has %ld elements\n", count);
+ if (count > 0)
+ {
+ g_printerr (" ");
+ for (j = 0; j < count; ++j)
+ g_printerr (" %ld",
+ sc->vptr->ivalue ( sc->vptr->vector_elem (vector, j) ));
+ g_printerr ("\n");
+ }
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ vector = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_vector (vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i-1].data.d_int32;
+ if (n_elements < 0 || n_elements > sc->vptr->vector_length (vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "INT16 vector (argument %d) for function %s has "
+ "size of %ld but expected size of %d",
+ i+1, proc_name, sc->vptr->vector_length (vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_int16array = g_new (gint16, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->vector_elem (vector, j);
+
+ if (!sc->vptr->is_number (v_element))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a number (argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ args[i].data.d_int16array[j] =
+ (gint16) sc->vptr->ivalue (v_element);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->vector_length (vector);
+ g_printerr (" int16 vector has %ld elements\n", count);
+ if (count > 0)
+ {
+ g_printerr (" ");
+ for (j = 0; j < count; ++j)
+ g_printerr (" %ld",
+ sc->vptr->ivalue ( sc->vptr->vector_elem (vector, j) ));
+ g_printerr ("\n");
+ }
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ vector = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_vector (vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i-1].data.d_int32;
+ if (n_elements < 0 ||
+ n_elements > sc->vptr->vector_length (vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "INT8 vector (argument %d) for function %s has "
+ "size of %ld but expected size of %d",
+ i+1, proc_name,
+ sc->vptr->vector_length (vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_int8array = g_new (guint8, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->vector_elem (vector, j);
+
+ if (!sc->vptr->is_number (v_element))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a number (argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ args[i].data.d_int8array[j] =
+ (guint8) sc->vptr->ivalue (v_element);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->vector_length (vector);
+ g_printerr (" int8 vector has %ld elements\n", count);
+ if (count > 0)
+ {
+ g_printerr (" ");
+ for (j = 0; j < count; ++j)
+ g_printerr (" %ld",
+ sc->vptr->ivalue ( sc->vptr->vector_elem (vector, j) ));
+ g_printerr ("\n");
+ }
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ vector = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_vector (vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i-1].data.d_int32;
+ if (n_elements < 0 ||
+ n_elements > sc->vptr->vector_length (vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "FLOAT vector (argument %d) for function %s has "
+ "size of %ld but expected size of %d",
+ i+1, proc_name,
+ sc->vptr->vector_length (vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_floatarray = g_new (gdouble, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->vector_elem (vector, j);
+
+ if (!sc->vptr->is_number (v_element))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a number (argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ args[i].data.d_floatarray[j] =
+ (gfloat) sc->vptr->rvalue (v_element);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->vector_length (vector);
+ g_printerr (" float vector has %ld elements\n", count);
+ if (count > 0)
+ {
+ g_printerr (" ");
+ for (j = 0; j < count; ++j)
+ g_printerr (" %f",
+ sc->vptr->rvalue ( sc->vptr->vector_elem (vector, j) ));
+ g_printerr ("\n");
+ }
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ vector = sc->vptr->pair_car (a); /* vector is pointing to a list */
+ if (!sc->vptr->is_list (sc, vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i - 1].data.d_int32;
+ if (n_elements < 0 ||
+ n_elements > sc->vptr->list_length (sc, vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "STRING vector (argument %d) for function %s has "
+ "length of %d but expected length of %d",
+ i+1, proc_name,
+ sc->vptr->list_length (sc, vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_stringarray = g_new (gchar *, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->pair_car (vector);
+
+ if (!sc->vptr->is_string (v_element))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a string (argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ args[i].data.d_stringarray[j] =
+ (gchar *) sc->vptr->string_value (v_element);
+
+ vector = sc->vptr->pair_cdr (vector);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->list_length ( sc, sc->vptr->pair_car (a) );
+ g_printerr (" string vector has %ld elements\n", count);
+ if (count > 0)
+ {
+ g_printerr (" ");
+ for (j = 0; j < count; ++j)
+ g_printerr (" \"%s\"",
+ args[i].data.d_stringarray[j]);
+ g_printerr ("\n");
+ }
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_COLOR:
+ if (sc->vptr->is_string (sc->vptr->pair_car (a)))
+ {
+ if (! gimp_rgb_parse_css (&args[i].data.d_color,
+ sc->vptr->string_value (sc->vptr->pair_car (a)),
+ -1))
+ success = FALSE;
+
+ gimp_rgb_set_alpha (&args[i].data.d_color, 1.0);
+#if DEBUG_MARSHALL
+ g_printerr (" (%s)\n",
+ sc->vptr->string_value (sc->vptr->pair_car (a)));
+#endif
+ }
+ else if (sc->vptr->is_list (sc, sc->vptr->pair_car (a)) &&
+ sc->vptr->list_length (sc, sc->vptr->pair_car (a)) == 3)
+ {
+ pointer color_list;
+ guchar r = 0, g = 0, b = 0;
+
+ color_list = sc->vptr->pair_car (a);
+ if (sc->vptr->is_number (sc->vptr->pair_car (color_list)))
+ r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+ else
+ success = FALSE;
+
+ color_list = sc->vptr->pair_cdr (color_list);
+ if (sc->vptr->is_number (sc->vptr->pair_car (color_list)))
+ g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+ else
+ success = FALSE;
+
+ color_list = sc->vptr->pair_cdr (color_list);
+ if (sc->vptr->is_number (sc->vptr->pair_car (color_list)))
+ b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+ else
+ success = FALSE;
+
+ if (success)
+ gimp_rgba_set_uchar (&args[i].data.d_color, r, g, b, 255);
+#if DEBUG_MARSHALL
+ if (success)
+ g_printerr (" (%d %d %d)\n", r, g, b);
+ else
+ g_printerr (" COLOR list contains non-numbers\n");
+#endif
+ }
+ else
+ {
+ success = FALSE;
+ }
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ vector = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_vector (vector))
+ success = FALSE;
+ if (success)
+ {
+ n_elements = args[i-1].data.d_int32;
+ if (n_elements < 0 ||
+ n_elements > sc->vptr->vector_length (vector))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "COLOR vector (argument %d) for function %s has "
+ "size of %ld but expected size of %d",
+ i+1, proc_name,
+ sc->vptr->vector_length (vector), n_elements);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ args[i].data.d_colorarray = g_new (GimpRGB, n_elements);
+
+ for (j = 0; j < n_elements; j++)
+ {
+ pointer v_element = sc->vptr->vector_elem (vector, j);
+ pointer color_list;
+ guchar r, g, b;
+
+ if (! (sc->vptr->is_list (sc,
+ sc->vptr->pair_car (v_element)) &&
+ sc->vptr->list_length (sc,
+ sc->vptr->pair_car (v_element)) == 3))
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Item %d in vector is not a color "
+ "(argument %d for function %s)",
+ j+1, i+1, proc_name);
+ return foreign_error (sc, error_str, vector);
+ }
+
+ color_list = sc->vptr->pair_car (v_element);
+ r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)),
+ 0, 255);
+
+ gimp_rgba_set_uchar (&args[i].data.d_colorarray[j],
+ r, g, b, 255);
+ }
+#if DEBUG_MARSHALL
+ {
+ glong count = sc->vptr->vector_length (vector);
+ g_printerr (" color vector has %ld elements\n", count);
+ }
+#endif
+ }
+ break;
+
+ case GIMP_PDB_PARASITE:
+ if (!sc->vptr->is_list (sc, sc->vptr->pair_car (a)) ||
+ sc->vptr->list_length (sc, sc->vptr->pair_car (a)) != 3)
+ success = FALSE;
+ if (success)
+ {
+ pointer temp_val;
+
+ /* parasite->name */
+ temp_val = sc->vptr->pair_car (a);
+
+ if (!sc->vptr->is_string (sc->vptr->pair_car (temp_val)))
+ {
+ success = FALSE;
+ break;
+ }
+
+ args[i].data.d_parasite.name =
+ sc->vptr->string_value (sc->vptr->pair_car (temp_val));
+#if DEBUG_MARSHALL
+ g_printerr (" name '%s'\n", args[i].data.d_parasite.name);
+#endif
+
+ /* parasite->flags */
+ temp_val = sc->vptr->pair_cdr (temp_val);
+
+ if (!sc->vptr->is_number (sc->vptr->pair_car (temp_val)))
+ {
+ success = FALSE;
+ break;
+ }
+
+ args[i].data.d_parasite.flags =
+ sc->vptr->ivalue (sc->vptr->pair_car (temp_val));
+#if DEBUG_MARSHALL
+ g_printerr (" flags %d", args[i].data.d_parasite.flags);
+#endif
+
+ /* parasite->data */
+ temp_val = sc->vptr->pair_cdr (temp_val);
+
+ if (!sc->vptr->is_string (sc->vptr->pair_car (temp_val)))
+ {
+ success = FALSE;
+ break;
+ }
+
+ args[i].data.d_parasite.data =
+ sc->vptr->string_value (sc->vptr->pair_car (temp_val));
+ args[i].data.d_parasite.size = strlen (args[i].data.d_parasite.data);
+
+#if DEBUG_MARSHALL
+ g_printerr (", size %d\n", args[i].data.d_parasite.size);
+ g_printerr (" data '%s'\n", (char *)args[i].data.d_parasite.data);
+#endif
+ }
+ break;
+
+ case GIMP_PDB_STATUS:
+ return foreign_error (sc,
+ "Status is for return types, not arguments",
+ sc->vptr->pair_car (a));
+ break;
+
+ default:
+ g_snprintf (error_str, sizeof (error_str),
+ "Argument %d for %s is an unknown type",
+ i+1, proc_name);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ /* Break out of loop before i gets updated when error was detected */
+ if (! success)
+ break;
+ }
+
+ if (success)
+ {
+ /* refuse to refresh scripts from a script, better than crashing
+ * see bug #575830
+ */
+ if (strcmp (proc_name, "script-fu-refresh-scripts"))
+ {
+#if DEBUG_MARSHALL
+ g_printerr (" calling %s...", proc_name);
+#endif
+ values = gimp_run_procedure2 (proc_name, &nvalues, nparams, args);
+#if DEBUG_MARSHALL
+ g_printerr (" done.\n");
+#endif
+ }
+ }
+ else
+ {
+#if DEBUG_MARSHALL
+ g_printerr (" Invalid type for argument %d\n", i+1);
+#endif
+ g_snprintf (error_str, sizeof (error_str),
+ "Invalid type for argument %d to %s",
+ i+1, proc_name);
+ return foreign_error (sc, error_str, 0);
+ }
+
+ /* Check the return status */
+ if (! values)
+ {
+#if DEBUG_MARSHALL
+ g_printerr (" Did not return status\n");
+#endif
+ g_snprintf (error_str, sizeof(error_str),
+ "Procedure execution of %s did not return a status",
+ proc_name);
+
+ return foreign_error (sc, error_str, 0);
+ }
+
+#if DEBUG_MARSHALL
+ {
+ const gchar *status_name;
+
+ gimp_enum_get_value (GIMP_TYPE_PDB_STATUS_TYPE,
+ values[0].data.d_status,
+ &status_name, NULL, NULL, NULL);
+ g_printerr (" return value is %s\n", status_name);
+ }
+#endif
+
+ switch (values[0].data.d_status)
+ {
+ case GIMP_PDB_EXECUTION_ERROR:
+ if (nvalues > 1 && values[1].type == GIMP_PDB_STRING)
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Procedure execution of %s failed: %s",
+ proc_name, values[1].data.d_string);
+ }
+ else
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Procedure execution of %s failed",
+ proc_name);
+ }
+ return foreign_error (sc, error_str, 0);
+ break;
+
+ case GIMP_PDB_CALLING_ERROR:
+ if (nvalues > 1 && values[1].type == GIMP_PDB_STRING)
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Procedure execution of %s failed on invalid input arguments: %s",
+ proc_name, values[1].data.d_string);
+ }
+ else
+ {
+ g_snprintf (error_str, sizeof (error_str),
+ "Procedure execution of %s failed on invalid input arguments",
+ proc_name);
+ }
+ return foreign_error (sc, error_str, 0);
+ break;
+
+ case GIMP_PDB_SUCCESS:
+#if DEBUG_MARSHALL
+ g_printerr (" values returned: %d\n", nvalues-1);
+#endif
+ for (i = nvalues - 2; i >= 0; --i)
+ {
+ const gchar *string;
+ gint j;
+
+#if DEBUG_MARSHALL
+ {
+ const gchar *type_name;
+
+ gimp_enum_get_value (GIMP_TYPE_PDB_ARG_TYPE,
+ return_vals[i].type,
+ &type_name, NULL, NULL, NULL);
+
+ g_printerr (" value %d is type %s (%d)\n",
+ i, type_name, return_vals[i].type);
+ }
+#endif
+ switch (return_vals[i].type)
+ {
+ case GIMP_PDB_INT32:
+ case GIMP_PDB_DISPLAY:
+ case GIMP_PDB_IMAGE:
+ case GIMP_PDB_ITEM:
+ case GIMP_PDB_LAYER:
+ case GIMP_PDB_CHANNEL:
+ case GIMP_PDB_DRAWABLE:
+ case GIMP_PDB_SELECTION:
+ case GIMP_PDB_VECTORS:
+ return_val = sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc,
+ values[i + 1].data.d_int32),
+ return_val);
+ break;
+
+ case GIMP_PDB_INT16:
+ return_val = sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc,
+ values[i + 1].data.d_int16),
+ return_val);
+ break;
+
+ case GIMP_PDB_INT8:
+ return_val = sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc,
+ values[i + 1].data.d_int8),
+ return_val);
+ break;
+
+ case GIMP_PDB_FLOAT:
+ return_val = sc->vptr->cons (sc,
+ sc->vptr->mk_real (sc,
+ values[i + 1].data.d_float),
+ return_val);
+ break;
+
+ case GIMP_PDB_STRING:
+ string = values[i + 1].data.d_string;
+ if (! string)
+ string = "";
+ return_val = sc->vptr->cons (sc,
+ sc->vptr->mk_string (sc, string),
+ return_val);
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ {
+ gint32 num_int32s = values[i].data.d_int32;
+ gint32 *array = (gint32 *) values[i + 1].data.d_int32array;
+ pointer vector = sc->vptr->mk_vector (sc, num_int32s);
+
+ for (j = 0; j < num_int32s; j++)
+ {
+ sc->vptr->set_vector_elem (vector, j,
+ sc->vptr->mk_integer (sc,
+ array[j]));
+ }
+
+ return_val = sc->vptr->cons (sc, vector, return_val);
+ }
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ {
+ gint32 num_int16s = values[i].data.d_int32;
+ gint16 *array = (gint16 *) values[i + 1].data.d_int16array;
+ pointer vector = sc->vptr->mk_vector (sc, num_int16s);
+
+ for (j = 0; j < num_int16s; j++)
+ {
+ sc->vptr->set_vector_elem (vector, j,
+ sc->vptr->mk_integer (sc,
+ array[j]));
+ }
+
+ return_val = sc->vptr->cons (sc, vector, return_val);
+ }
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ {
+ gint32 num_int8s = values[i].data.d_int32;
+ guint8 *array = (guint8 *) values[i + 1].data.d_int8array;
+ pointer vector = sc->vptr->mk_vector (sc, num_int8s);
+
+ for (j = 0; j < num_int8s; j++)
+ {
+ sc->vptr->set_vector_elem (vector, j,
+ sc->vptr->mk_integer (sc,
+ array[j]));
+ }
+
+ return_val = sc->vptr->cons (sc, vector, return_val);
+ }
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ {
+ gint32 num_floats = values[i].data.d_int32;
+ gdouble *array = (gdouble *) values[i + 1].data.d_floatarray;
+ pointer vector = sc->vptr->mk_vector (sc, num_floats);
+
+ for (j = 0; j < num_floats; j++)
+ {
+ sc->vptr->set_vector_elem (vector, j,
+ sc->vptr->mk_real (sc,
+ array[j]));
+ }
+
+ return_val = sc->vptr->cons (sc, vector, return_val);
+ }
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ {
+ gint num_strings = values[i].data.d_int32;
+ gchar **array = (gchar **) values[i + 1].data.d_stringarray;
+ pointer list = sc->NIL;
+
+ for (j = num_strings - 1; j >= 0; j--)
+ {
+ list = sc->vptr->cons (sc,
+ sc->vptr->mk_string (sc,
+ array[j] ?
+ array[j] : ""),
+ list);
+ }
+
+ return_val = sc->vptr->cons (sc, list, return_val);
+ }
+ break;
+
+ case GIMP_PDB_COLOR:
+ {
+ guchar r, g, b;
+ gpointer temp_val;
+
+ gimp_rgb_get_uchar (&values[i + 1].data.d_color, &r, &g, &b);
+
+ temp_val = sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, r),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, g),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, b),
+ sc->NIL)));
+ return_val = sc->vptr->cons (sc,
+ temp_val,
+ return_val);
+ break;
+ }
+
+ case GIMP_PDB_COLORARRAY:
+ {
+ gint32 num_colors = values[i].data.d_int32;
+ GimpRGB *array = (GimpRGB *) values[i + 1].data.d_colorarray;
+ pointer vector = sc->vptr->mk_vector (sc, num_colors);
+
+ for (j = 0; j < num_colors; j++)
+ {
+ guchar r, g, b;
+ pointer temp_val;
+
+ gimp_rgb_get_uchar (&array[j], &r, &g, &b);
+
+ temp_val = sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, r),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, g),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, b),
+ sc->NIL)));
+ sc->vptr->set_vector_elem (vector, j, temp_val);
+ }
+
+ return_val = sc->vptr->cons (sc, vector, return_val);
+ }
+ break;
+
+ case GIMP_PDB_PARASITE:
+ {
+ if (values[i + 1].data.d_parasite.name == NULL)
+ {
+ return_val = foreign_error (sc, "Error: null parasite", 0);
+ }
+ else
+ {
+ GimpParasite *p = &values[i + 1].data.d_parasite;
+ gchar *data = g_strndup (p->data, p->size);
+ gint char_cnt = g_utf8_strlen (data, p->size);
+ pointer temp_val;
+
+ /* don't move the mk_foo() calls outside this function call,
+ * otherwise they might be garbage collected away!
+ */
+ temp_val = sc->vptr->cons (sc,
+ sc->vptr->mk_string (sc, p->name),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_integer (sc, p->flags),
+ sc->vptr->cons (sc,
+ sc->vptr->mk_counted_string (sc,
+ data,
+ char_cnt),
+ sc->NIL)));
+ return_val = sc->vptr->cons (sc,
+ temp_val,
+ return_val);
+ g_free (data);
+
+#if DEBUG_MARSHALL
+ g_printerr (" name '%s'\n", p->name);
+ g_printerr (" flags %d", p->flags);
+ g_printerr (", size %d\n", p->size);
+ g_printerr (" data '%.*s'\n",
+ p->size, (gchar *) p->data);
+#endif
+ }
+ }
+ break;
+
+ case GIMP_PDB_STATUS:
+ return foreign_error (sc, "Procedure execution returned multiple status values", 0);
+ break;
+
+ default:
+ return foreign_error (sc, "Unknown return type", 0);
+ }
+ }
+
+ case GIMP_PDB_PASS_THROUGH:
+ case GIMP_PDB_CANCEL: /* should we do something here? */
+ break;
+ }
+
+ /* If we have no return value(s) from PDB call, return
+ * either TRUE or FALSE to indicate if call succeeded.
+ */
+ if (return_val == sc->NIL)
+ {
+ if (values[0].data.d_status == GIMP_PDB_SUCCESS)
+ return_val = sc->vptr->cons (sc, sc->T, sc->NIL);
+ else
+ return_val = sc->vptr->cons (sc, sc->F, sc->NIL);
+ }
+
+ /* free the proc name */
+ g_free (proc_name);
+
+ /* free up the executed procedure return values */
+ gimp_destroy_params (values, nvalues);
+
+ /* free up arguments and values */
+ script_fu_marshal_destroy_args (args, nparams);
+
+ /* free the query information */
+ g_free (proc_blurb);
+ g_free (proc_help);
+ g_free (proc_author);
+ g_free (proc_copyright);
+ g_free (proc_date);
+ g_free (params);
+ g_free (return_vals);
+
+ /* if we're in server mode, listen for additional commands for 10 ms */
+ if (script_fu_server_get_mode ())
+ script_fu_server_listen (10);
+
+#ifdef GDK_WINDOWING_WIN32
+ /* This seems to help a lot on Windoze. */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+#endif
+
+ return return_val;
+}
+
+static pointer
+script_fu_marshal_procedure_call_strict (scheme *sc,
+ pointer a)
+{
+ return script_fu_marshal_procedure_call (sc, a, FALSE);
+}
+
+static pointer
+script_fu_marshal_procedure_call_permissive (scheme *sc,
+ pointer a)
+{
+ return script_fu_marshal_procedure_call (sc, a, TRUE);
+}
+
+static void
+script_fu_marshal_destroy_args (GimpParam *params,
+ gint n_params)
+{
+ gint i;
+
+ for (i = 0; i < n_params; i++)
+ {
+ switch (params[i].type)
+ {
+ case GIMP_PDB_INT32:
+ case GIMP_PDB_INT16:
+ case GIMP_PDB_INT8:
+ case GIMP_PDB_FLOAT:
+ case GIMP_PDB_STRING:
+ break;
+
+ case GIMP_PDB_INT32ARRAY:
+ g_free (params[i].data.d_int32array);
+ break;
+
+ case GIMP_PDB_INT16ARRAY:
+ g_free (params[i].data.d_int16array);
+ break;
+
+ case GIMP_PDB_INT8ARRAY:
+ g_free (params[i].data.d_int8array);
+ break;
+
+ case GIMP_PDB_FLOATARRAY:
+ g_free (params[i].data.d_floatarray);
+ break;
+
+ case GIMP_PDB_STRINGARRAY:
+ g_free (params[i].data.d_stringarray);
+ break;
+
+ case GIMP_PDB_COLORARRAY:
+ g_free (params[i].data.d_colorarray);
+ break;
+
+ case GIMP_PDB_COLOR:
+ case GIMP_PDB_DISPLAY:
+ case GIMP_PDB_IMAGE:
+ case GIMP_PDB_ITEM:
+ case GIMP_PDB_LAYER:
+ case GIMP_PDB_CHANNEL:
+ case GIMP_PDB_DRAWABLE:
+ case GIMP_PDB_SELECTION:
+ case GIMP_PDB_VECTORS:
+ case GIMP_PDB_PARASITE:
+ case GIMP_PDB_STATUS:
+ case GIMP_PDB_END:
+ break;
+ }
+ }
+
+ g_free (params);
+}
+
+static pointer
+script_fu_register_call (scheme *sc,
+ pointer a)
+{
+ return script_fu_add_script (sc, a);
+}
+
+static pointer
+script_fu_menu_register_call (scheme *sc,
+ pointer a)
+{
+ return script_fu_add_menu (sc, a);
+}
+
+static pointer
+script_fu_quit_call (scheme *sc,
+ pointer a)
+{
+ script_fu_server_quit ();
+
+ scheme_deinit (sc);
+
+ return sc->NIL;
+}
+
+static pointer
+script_fu_nil_call (scheme *sc,
+ pointer a)
+{
+ return sc->NIL;
+}
diff --git a/plug-ins/script-fu/scheme-wrapper.h b/plug-ins/script-fu/scheme-wrapper.h
new file mode 100644
index 0000000..9ab44ef
--- /dev/null
+++ b/plug-ins/script-fu/scheme-wrapper.h
@@ -0,0 +1,47 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCHEME_WRAPPER_H__
+#define __SCHEME_WRAPPER_H__
+
+#include "tinyscheme/scheme.h"
+
+void tinyscheme_init (GList *path,
+ gboolean register_scripts);
+
+void ts_set_run_mode (GimpRunMode run_mode);
+
+void ts_set_print_flag (gint print_flag);
+void ts_print_welcome (void);
+
+const gchar * ts_get_success_msg (void);
+
+void ts_interpret_stdin (void);
+
+/* if the return value is 0, success. error otherwise. */
+gint ts_interpret_string (const gchar *expr);
+
+void ts_stdout_output_func (TsOutputType type,
+ const char *string,
+ int len,
+ gpointer user_data);
+void ts_gstring_output_func (TsOutputType type,
+ const char *string,
+ int len,
+ gpointer user_data);
+
+#endif /* __SCHEME_WRAPPER_H__ */
diff --git a/plug-ins/script-fu/script-fu-console.c b/plug-ins/script-fu/script-fu-console.c
new file mode 100644
index 0000000..835b3db
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-console.c
@@ -0,0 +1,703 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include <gdk/gdkkeysyms.h>
+
+#include "scheme-wrapper.h"
+#include "script-fu-console.h"
+
+#include "script-fu-intl.h"
+
+
+#define TEXT_WIDTH 480
+#define TEXT_HEIGHT 400
+
+#define PROC_NAME "plug-in-script-fu-console"
+
+typedef struct
+{
+ GtkWidget *dialog;
+ GtkTextBuffer *console;
+ GtkWidget *cc;
+ GtkWidget *text_view;
+ GtkWidget *proc_browser;
+ GtkWidget *save_dialog;
+
+ GList *history;
+ gint history_len;
+ gint history_cur;
+ gint history_max;
+} ConsoleInterface;
+
+enum
+{
+ RESPONSE_CLEAR,
+ RESPONSE_SAVE
+};
+
+/*
+ * Local Functions
+ */
+static void script_fu_console_interface (void);
+static void script_fu_console_response (GtkWidget *widget,
+ gint response_id,
+ ConsoleInterface *console);
+static void script_fu_console_save_dialog (ConsoleInterface *console);
+static void script_fu_console_save_response (GtkWidget *dialog,
+ gint response_id,
+ ConsoleInterface *console);
+
+static void script_fu_browse_callback (GtkWidget *widget,
+ ConsoleInterface *console);
+static void script_fu_browse_response (GtkWidget *widget,
+ gint response_id,
+ ConsoleInterface *console);
+static void script_fu_browse_row_activated (GtkDialog *dialog);
+
+static gboolean script_fu_cc_is_empty (ConsoleInterface *console);
+static gboolean script_fu_cc_key_function (GtkWidget *widget,
+ GdkEventKey *event,
+ ConsoleInterface *console);
+
+static void script_fu_output_to_console (TsOutputType type,
+ const gchar *text,
+ gint len,
+ gpointer user_data);
+
+/*
+ * Function definitions
+ */
+
+void
+script_fu_console_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+
+ ts_set_print_flag (1);
+ script_fu_console_interface ();
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+}
+
+static void
+script_fu_console_interface (void)
+{
+ ConsoleInterface console = { 0, };
+ GtkWidget *vbox;
+ GtkWidget *button;
+ GtkWidget *scrolled_window;
+ GtkWidget *hbox;
+
+ gimp_ui_init ("script-fu", FALSE);
+
+ console.history_max = 50;
+
+ console.dialog = gimp_dialog_new (_("Script-Fu Console"),
+ "gimp-script-fu-console",
+ NULL, 0,
+ gimp_standard_help_func, PROC_NAME,
+
+ _("_Save"), RESPONSE_SAVE,
+ _("C_lear"), RESPONSE_CLEAR,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (console.dialog),
+ GTK_RESPONSE_CLOSE,
+ RESPONSE_CLEAR,
+ RESPONSE_SAVE,
+ -1);
+
+ g_object_add_weak_pointer (G_OBJECT (console.dialog),
+ (gpointer) &console.dialog);
+
+ g_signal_connect (console.dialog, "response",
+ G_CALLBACK (script_fu_console_response),
+ &console);
+
+ /* The main vbox */
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (console.dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* The output text widget */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_ALWAYS);
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+
+ console.console = gtk_text_buffer_new (NULL);
+ console.text_view = gtk_text_view_new_with_buffer (console.console);
+ g_object_unref (console.console);
+
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (console.text_view), FALSE);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (console.text_view),
+ GTK_WRAP_WORD);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (console.text_view), 6);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (console.text_view), 6);
+ gtk_widget_set_size_request (console.text_view, TEXT_WIDTH, TEXT_HEIGHT);
+ gtk_container_add (GTK_CONTAINER (scrolled_window), console.text_view);
+ gtk_widget_show (console.text_view);
+
+ gtk_text_buffer_create_tag (console.console, "strong",
+ "weight", PANGO_WEIGHT_BOLD,
+ "scale", PANGO_SCALE_LARGE,
+ NULL);
+ gtk_text_buffer_create_tag (console.console, "emphasis",
+ "style", PANGO_STYLE_OBLIQUE,
+ NULL);
+
+ {
+ const gchar * const greetings[] =
+ {
+ "strong", N_("Welcome to TinyScheme"),
+ NULL, "\n",
+ NULL, "Copyright (c) Dimitrios Souflis",
+ NULL, "\n",
+ "strong", N_("Script-Fu Console"),
+ NULL, " - ",
+ "emphasis", N_("Interactive Scheme Development"),
+ NULL, "\n"
+ };
+
+ GtkTextIter cursor;
+ gint i;
+
+ gtk_text_buffer_get_end_iter (console.console, &cursor);
+
+ for (i = 0; i < G_N_ELEMENTS (greetings); i += 2)
+ {
+ if (greetings[i])
+ gtk_text_buffer_insert_with_tags_by_name (console.console, &cursor,
+ gettext (greetings[i + 1]),
+ -1, greetings[i],
+ NULL);
+ else
+ gtk_text_buffer_insert (console.console, &cursor,
+ gettext (greetings[i + 1]), -1);
+ }
+ }
+
+ /* The current command */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ console.cc = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), console.cc, TRUE, TRUE, 0);
+ gtk_widget_grab_focus (console.cc);
+ gtk_widget_show (console.cc);
+
+ g_signal_connect (console.cc, "key-press-event",
+ G_CALLBACK (script_fu_cc_key_function),
+ &console);
+
+ button = gtk_button_new_with_mnemonic (_("_Browse..."));
+ gtk_misc_set_padding (GTK_MISC (gtk_bin_get_child (GTK_BIN (button))), 2, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (script_fu_browse_callback),
+ &console);
+
+ /* Initialize the history */
+ console.history = g_list_append (console.history, NULL);
+ console.history_len = 1;
+
+ gtk_widget_show (console.dialog);
+
+ gtk_main ();
+
+ if (console.save_dialog)
+ gtk_widget_destroy (console.save_dialog);
+
+ if (console.dialog)
+ gtk_widget_destroy (console.dialog);
+}
+
+static void
+script_fu_console_response (GtkWidget *widget,
+ gint response_id,
+ ConsoleInterface *console)
+{
+ GtkTextIter start, end;
+
+ switch (response_id)
+ {
+ case RESPONSE_CLEAR:
+ gtk_text_buffer_get_start_iter (console->console, &start);
+ gtk_text_buffer_get_end_iter (console->console, &end);
+ gtk_text_buffer_delete (console->console, &start, &end);
+ break;
+
+ case RESPONSE_SAVE:
+ script_fu_console_save_dialog (console);
+ break;
+
+ default:
+ gtk_main_quit ();
+ break;
+ }
+}
+
+
+static void
+script_fu_console_save_dialog (ConsoleInterface *console)
+{
+ if (! console->save_dialog)
+ {
+ console->save_dialog =
+ gtk_file_chooser_dialog_new (_("Save Script-Fu Console Output"),
+ GTK_WINDOW (console->dialog),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (console->save_dialog),
+ GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (console->save_dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (console->save_dialog),
+ TRUE);
+
+ g_object_add_weak_pointer (G_OBJECT (console->save_dialog),
+ (gpointer) &console->save_dialog);
+
+ g_signal_connect (console->save_dialog, "response",
+ G_CALLBACK (script_fu_console_save_response),
+ console);
+ }
+
+ gtk_window_present (GTK_WINDOW (console->save_dialog));
+}
+
+static void
+script_fu_console_save_response (GtkWidget *dialog,
+ gint response_id,
+ ConsoleInterface *console)
+{
+ GtkTextIter start, end;
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ gchar *filename;
+ gchar *str;
+ FILE *fh;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+
+ fh = g_fopen (filename, "w");
+
+ if (! fh)
+ {
+ g_message (_("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename),
+ g_strerror (errno));
+
+ g_free (filename);
+ return;
+ }
+
+ gtk_text_buffer_get_start_iter (console->console, &start);
+ gtk_text_buffer_get_end_iter (console->console, &end);
+
+ str = gtk_text_buffer_get_text (console->console, &start, &end, FALSE);
+
+ fputs (str, fh);
+ fclose (fh);
+
+ g_free (str);
+ }
+
+ gtk_widget_hide (dialog);
+}
+
+static void
+script_fu_browse_callback (GtkWidget *widget,
+ ConsoleInterface *console)
+{
+ if (! console->proc_browser)
+ {
+ console->proc_browser =
+ gimp_proc_browser_dialog_new (_("Script-Fu Procedure Browser"),
+ "script-fu-procedure-browser",
+ gimp_standard_help_func, PROC_NAME,
+
+ _("_Apply"), GTK_RESPONSE_APPLY,
+ _("_Close"), GTK_RESPONSE_CLOSE,
+
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (console->proc_browser),
+ GTK_RESPONSE_APPLY);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (console->proc_browser),
+ GTK_RESPONSE_CLOSE,
+ GTK_RESPONSE_APPLY,
+ -1);
+
+ g_object_add_weak_pointer (G_OBJECT (console->proc_browser),
+ (gpointer) &console->proc_browser);
+
+ g_signal_connect (console->proc_browser, "response",
+ G_CALLBACK (script_fu_browse_response),
+ console);
+ g_signal_connect (console->proc_browser, "row-activated",
+ G_CALLBACK (script_fu_browse_row_activated),
+ console);
+ }
+
+ gtk_window_present (GTK_WINDOW (console->proc_browser));
+}
+
+static void
+script_fu_browse_response (GtkWidget *widget,
+ gint response_id,
+ ConsoleInterface *console)
+{
+ GimpProcBrowserDialog *dialog = GIMP_PROC_BROWSER_DIALOG (widget);
+ gchar *proc_name;
+ gchar *proc_blurb;
+ gchar *proc_help;
+ gchar *proc_author;
+ gchar *proc_copyright;
+ gchar *proc_date;
+ GimpPDBProcType proc_type;
+ gint n_params;
+ gint n_return_vals;
+ GimpParamDef *params;
+ GimpParamDef *return_vals;
+ gint i;
+ GString *text;
+
+ if (response_id != GTK_RESPONSE_APPLY)
+ {
+ gtk_widget_destroy (widget);
+ return;
+ }
+
+ proc_name = gimp_proc_browser_dialog_get_selected (dialog);
+
+ if (proc_name == NULL)
+ return;
+
+ gimp_procedural_db_proc_info (proc_name,
+ &proc_blurb,
+ &proc_help,
+ &proc_author,
+ &proc_copyright,
+ &proc_date,
+ &proc_type,
+ &n_params,
+ &n_return_vals,
+ &params,
+ &return_vals);
+
+ text = g_string_new ("(");
+ text = g_string_append (text, proc_name);
+
+ for (i = 0; i < n_params; i++)
+ {
+ text = g_string_append_c (text, ' ');
+ text = g_string_append (text, params[i].name);
+ }
+
+ text = g_string_append_c (text, ')');
+
+ gtk_window_set_focus (GTK_WINDOW (console->dialog), console->cc);
+
+ gtk_entry_set_text (GTK_ENTRY (console->cc), text->str);
+ gtk_editable_set_position (GTK_EDITABLE (console->cc),
+ g_utf8_pointer_to_offset (text->str,
+ text->str +
+ strlen (proc_name) + 2));
+
+ g_string_free (text, TRUE);
+
+ gtk_window_present (GTK_WINDOW (console->dialog));
+
+ g_free (proc_name);
+ g_free (proc_blurb);
+ g_free (proc_help);
+ g_free (proc_author);
+ g_free (proc_copyright);
+ g_free (proc_date);
+
+ gimp_destroy_paramdefs (params, n_params);
+ gimp_destroy_paramdefs (return_vals, n_return_vals);
+}
+
+static void
+script_fu_browse_row_activated (GtkDialog *dialog)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_APPLY);
+}
+
+static gboolean
+script_fu_console_idle_scroll_end (GtkWidget *view)
+{
+ GtkWidget *parent = gtk_widget_get_parent (view);
+
+ if (parent)
+ {
+ GtkAdjustment *adj;
+
+ adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (parent));
+
+ gtk_adjustment_set_value (adj,
+ gtk_adjustment_get_upper (adj) -
+ gtk_adjustment_get_page_size (adj));
+ }
+
+ g_object_unref (view);
+
+ return FALSE;
+}
+
+static void
+script_fu_console_scroll_end (GtkWidget *view)
+{
+ /* the text view idle updates, so we need to idle scroll too
+ */
+ g_object_ref (view);
+
+ g_idle_add ((GSourceFunc) script_fu_console_idle_scroll_end, view);
+}
+
+static void
+script_fu_output_to_console (TsOutputType type,
+ const gchar *text,
+ gint len,
+ gpointer user_data)
+{
+ ConsoleInterface *console = user_data;
+
+ if (console && console->text_view)
+ {
+ GtkTextBuffer *buffer;
+ GtkTextIter cursor;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (console->text_view));
+
+ gtk_text_buffer_get_end_iter (buffer, &cursor);
+
+ if (type == TS_OUTPUT_NORMAL)
+ {
+ gtk_text_buffer_insert (buffer, &cursor, text, len);
+ }
+ else
+ {
+ gtk_text_buffer_insert_with_tags_by_name (console->console, &cursor,
+ text, len, "emphasis",
+ NULL);
+ }
+
+ script_fu_console_scroll_end (console->text_view);
+ }
+}
+
+static gboolean
+script_fu_cc_is_empty (ConsoleInterface *console)
+{
+ const gchar *str;
+
+ if ((str = gtk_entry_get_text (GTK_ENTRY (console->cc))) == NULL)
+ return TRUE;
+
+ while (*str)
+ {
+ if (*str != ' ' && *str != '\t' && *str != '\n')
+ return FALSE;
+
+ str ++;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+script_fu_cc_key_function (GtkWidget *widget,
+ GdkEventKey *event,
+ ConsoleInterface *console)
+{
+ GList *list;
+ gint direction = 0;
+ GtkTextIter cursor;
+ GString *output;
+
+ switch (event->keyval)
+ {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_ISO_Enter:
+ if (script_fu_cc_is_empty (console))
+ return TRUE;
+
+ list = g_list_nth (console->history,
+ (g_list_length (console->history) - 1));
+
+ if (list->data)
+ g_free (list->data);
+
+ list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (console->cc)));
+
+ gtk_text_buffer_get_end_iter (console->console, &cursor);
+
+ gtk_text_buffer_insert (console->console, &cursor, "\n", 1);
+ gtk_text_buffer_insert_with_tags_by_name (console->console, &cursor,
+ "> ", 2,
+ "strong",
+ NULL);
+
+ gtk_text_buffer_insert (console->console, &cursor,
+ gtk_entry_get_text (GTK_ENTRY (console->cc)), -1);
+ gtk_text_buffer_insert (console->console, &cursor, "\n", 1);
+
+ script_fu_console_scroll_end (console->text_view);
+
+ gtk_entry_set_text (GTK_ENTRY (console->cc), "");
+
+ output = g_string_new (NULL);
+ ts_register_output_func (ts_gstring_output_func, output);
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ if (ts_interpret_string (list->data) != 0)
+ {
+ script_fu_output_to_console (TS_OUTPUT_ERROR,
+ output->str,
+ output->len,
+ console);
+ }
+ else
+ {
+ script_fu_output_to_console (TS_OUTPUT_NORMAL,
+ output->str,
+ output->len,
+ console);
+ }
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_INTERNAL);
+
+ g_string_free (output, TRUE);
+
+ gimp_displays_flush ();
+
+ console->history = g_list_append (console->history, NULL);
+
+ if (console->history_len == console->history_max)
+ {
+ console->history = g_list_remove (console->history,
+ console->history->data);
+ if (console->history->data)
+ g_free (console->history->data);
+ }
+ else
+ {
+ console->history_len++;
+ }
+
+ console->history_cur = g_list_length (console->history) - 1;
+
+ return TRUE;
+ break;
+
+ case GDK_KEY_KP_Up:
+ case GDK_KEY_Up:
+ direction = -1;
+ break;
+
+ case GDK_KEY_KP_Down:
+ case GDK_KEY_Down:
+ direction = 1;
+ break;
+
+ case GDK_KEY_P:
+ case GDK_KEY_p:
+ if (event->state & GDK_CONTROL_MASK)
+ direction = -1;
+ break;
+
+ case GDK_KEY_N:
+ case GDK_KEY_n:
+ if (event->state & GDK_CONTROL_MASK)
+ direction = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if (direction)
+ {
+ /* Make sure we keep track of the current one */
+ if (console->history_cur == g_list_length (console->history) - 1)
+ {
+ list = g_list_nth (console->history, console->history_cur);
+
+ g_free (list->data);
+ list->data = g_strdup (gtk_entry_get_text (GTK_ENTRY (console->cc)));
+ }
+
+ console->history_cur += direction;
+
+ if (console->history_cur < 0)
+ console->history_cur = 0;
+
+ if (console->history_cur >= console->history_len)
+ console->history_cur = console->history_len - 1;
+
+ gtk_entry_set_text (GTK_ENTRY (console->cc),
+ (gchar *) (g_list_nth (console->history,
+ console->history_cur))->data);
+
+ gtk_editable_set_position (GTK_EDITABLE (console->cc), -1);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/plug-ins/script-fu/script-fu-console.h b/plug-ins/script-fu/script-fu-console.h
new file mode 100644
index 0000000..8410a61
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-console.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_CONSOLE_H__
+#define __SCRIPT_FU_CONSOLE_H__
+
+
+void script_fu_console_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+#endif /* __SCRIPT_FU_CONSOLE_H__ */
diff --git a/plug-ins/script-fu/script-fu-enums.h b/plug-ins/script-fu/script-fu-enums.h
new file mode 100644
index 0000000..581441f
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-enums.h
@@ -0,0 +1,54 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_ENUMS_H__
+#define __SCRIPT_FU_ENUMS_H__
+
+/* Typedefs for script-fu argument types */
+
+typedef enum
+{
+ SF_IMAGE = 0,
+ SF_DRAWABLE,
+ SF_LAYER,
+ SF_CHANNEL,
+ SF_VECTORS,
+ SF_COLOR,
+ SF_TOGGLE,
+ SF_VALUE,
+ SF_STRING,
+ SF_ADJUSTMENT,
+ SF_FONT,
+ SF_PATTERN,
+ SF_BRUSH,
+ SF_GRADIENT,
+ SF_FILENAME,
+ SF_DIRNAME,
+ SF_OPTION,
+ SF_PALETTE,
+ SF_TEXT,
+ SF_ENUM,
+ SF_DISPLAY
+} SFArgType;
+
+typedef enum
+{
+ SF_SLIDER = 0,
+ SF_SPINNER
+} SFAdjustmentType;
+
+#endif /* __SCRIPT_FU_ENUMS__ */
diff --git a/plug-ins/script-fu/script-fu-eval.c b/plug-ins/script-fu/script-fu-eval.c
new file mode 100644
index 0000000..6f2e354
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-eval.c
@@ -0,0 +1,80 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "libgimp/gimp.h"
+
+#include "scheme-wrapper.h"
+#include "script-fu-eval.h"
+
+#include "script-fu-intl.h"
+
+
+void
+script_fu_eval_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GString *output = g_string_new (NULL);
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+
+ run_mode = params[0].data.d_int32;
+
+ ts_set_run_mode (run_mode);
+ ts_register_output_func (ts_gstring_output_func, output);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ if (ts_interpret_string (params[1].data.d_string) != 0)
+ status = GIMP_PDB_EXECUTION_ERROR;
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ status = GIMP_PDB_CALLING_ERROR;
+ g_string_assign (output, _("Script-Fu evaluation mode only allows "
+ "non-interactive invocation"));
+ break;
+
+ default:
+ break;
+ }
+
+ values[0].data.d_status = status;
+
+ if (status != GIMP_PDB_SUCCESS && output->len > 0)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = g_string_free (output, FALSE);
+ }
+ else
+ {
+ g_string_free (output, TRUE);
+ }
+}
diff --git a/plug-ins/script-fu/script-fu-eval.h b/plug-ins/script-fu/script-fu-eval.h
new file mode 100644
index 0000000..83ec9c4
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-eval.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_EVAL_H__
+#define __SCRIPT_FU_EVAL_H__
+
+
+void script_fu_eval_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+#endif /* __SCRIPT_FU_EVAL_H__ */
diff --git a/plug-ins/script-fu/script-fu-interface.c b/plug-ins/script-fu/script-fu-interface.c
new file mode 100644
index 0000000..0fa1811
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-interface.c
@@ -0,0 +1,1070 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#ifdef GDK_WINDOWING_QUARTZ
+#import <Cocoa/Cocoa.h>
+#elif defined (G_OS_WIN32)
+#include <windows.h>
+#endif
+
+#include "tinyscheme/scheme-private.h"
+#include "scheme-wrapper.h"
+
+#include "script-fu-types.h"
+
+#include "script-fu-interface.h"
+#include "script-fu-scripts.h"
+#include "script-fu-script.h"
+
+#include "script-fu-intl.h"
+
+
+#define RESPONSE_RESET 1
+
+#define TEXT_WIDTH 100
+#define COLOR_SAMPLE_WIDTH 60
+#define COLOR_SAMPLE_HEIGHT 15
+#define SLIDER_WIDTH 80
+
+
+typedef struct
+{
+ GtkWidget *dialog;
+
+ GtkWidget *table;
+ GtkWidget **widgets;
+
+ GtkWidget *progress_label;
+ GtkWidget *progress_bar;
+
+ gchar *title;
+ gchar *last_command;
+ gint command_count;
+ gint consec_command_count;
+} SFInterface;
+
+
+/*
+ * Local Functions
+ */
+
+static void script_fu_interface_quit (SFScript *script);
+
+static void script_fu_response (GtkWidget *widget,
+ gint response_id,
+ SFScript *script);
+static void script_fu_ok (SFScript *script);
+static void script_fu_reset (SFScript *script);
+
+static void script_fu_file_callback (GtkWidget *widget,
+ SFFilename *file);
+static void script_fu_combo_callback (GtkWidget *widget,
+ SFOption *option);
+static void script_fu_pattern_callback (gpointer data,
+ const gchar *name,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data,
+ gboolean closing);
+static void script_fu_gradient_callback (gpointer data,
+ const gchar *name,
+ gint width,
+ const gdouble *mask_data,
+ gboolean closing);
+static void script_fu_font_callback (gpointer data,
+ const gchar *name,
+ gboolean closing);
+static void script_fu_palette_callback (gpointer data,
+ const gchar *name,
+ gboolean closing);
+static void script_fu_brush_callback (gpointer data,
+ const gchar *name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean closing);
+static void script_fu_flush_events (void);
+static void script_fu_activate_main_dialog (void);
+
+
+/*
+ * Local variables
+ */
+
+static SFInterface *sf_interface = NULL; /* there can only be at most
+ * one interactive interface
+ */
+
+static GimpPDBStatusType sf_status = GIMP_PDB_SUCCESS;
+
+
+/*
+ * Function definitions
+ */
+
+gboolean
+script_fu_interface_is_active (void)
+{
+ return (sf_interface != NULL);
+}
+
+void
+script_fu_interface_report_cc (const gchar *command)
+{
+ if (sf_interface == NULL)
+ return;
+
+ if (sf_interface->last_command &&
+ strcmp (sf_interface->last_command, command) == 0)
+ {
+ sf_interface->command_count++;
+
+ if (! g_str_has_prefix (command, "gimp-progress-"))
+ {
+ gchar *new_command;
+
+ new_command = g_strdup_printf ("%s <%d>",
+ command, sf_interface->command_count);
+ gtk_label_set_text (GTK_LABEL (sf_interface->progress_label),
+ new_command);
+ g_free (new_command);
+ }
+ }
+ else
+ {
+ sf_interface->command_count = 1;
+
+ g_free (sf_interface->last_command);
+ sf_interface->last_command = g_strdup (command);
+
+ if (! g_str_has_prefix (command, "gimp-progress-"))
+ {
+ gtk_label_set_text (GTK_LABEL (sf_interface->progress_label),
+ command);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (sf_interface->progress_label), "");
+ }
+ }
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+}
+
+GimpPDBStatusType
+script_fu_interface (SFScript *script,
+ gint start_arg)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkSizeGroup *group;
+ GSList *list;
+ gchar *title;
+ gint i;
+
+ static gboolean gtk_initted = FALSE;
+
+ /* Simply return if there is already an interface. This is an
+ * ugly workaround for the fact that we can not process two
+ * scripts at a time.
+ */
+ if (sf_interface != NULL)
+ {
+ gchar *message =
+ g_strdup_printf ("%s\n\n%s",
+ _("Script-Fu cannot process two scripts "
+ "at the same time."),
+ _("You are already running the \"%s\" script."));
+
+ g_message (message, sf_interface->title);
+ g_free (message);
+
+ return GIMP_PDB_CANCEL;
+ }
+
+ g_return_val_if_fail (script != NULL, FALSE);
+
+ if (!gtk_initted)
+ {
+ INIT_I18N();
+
+ gimp_ui_init ("script-fu", TRUE);
+
+ gtk_initted = TRUE;
+ }
+
+ sf_status = GIMP_PDB_SUCCESS;
+
+ sf_interface = g_slice_new0 (SFInterface);
+
+ sf_interface->widgets = g_new0 (GtkWidget *, script->n_args);
+ sf_interface->title = script_fu_script_get_title (script);
+
+ title = g_strdup_printf (_("Script-Fu: %s"), sf_interface->title);
+
+ sf_interface->dialog = dialog =
+ gimp_dialog_new (title, "gimp-script-fu",
+ NULL, 0,
+ gimp_standard_help_func, script->name,
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+ g_free (title);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (script_fu_response),
+ script);
+
+ g_signal_connect_swapped (dialog, "destroy",
+ G_CALLBACK (script_fu_interface_quit),
+ script);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* The argument table */
+ sf_interface->table = gtk_table_new (script->n_args - start_arg, 3, FALSE);
+
+ gtk_table_set_col_spacings (GTK_TABLE (sf_interface->table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (sf_interface->table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), sf_interface->table, FALSE, FALSE, 0);
+ gtk_widget_show (sf_interface->table);
+
+ group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ for (i = start_arg; i < script->n_args; i++)
+ {
+ GtkWidget *widget = NULL;
+ gchar *label_text;
+ gfloat label_yalign = 0.5;
+ gint *ID_ptr = NULL;
+ gint row = i;
+ gboolean left_align = FALSE;
+ SFArg *arg = &script->args[i];
+
+ row -= start_arg;
+
+ /* we add a colon after the label;
+ * some languages want an extra space here
+ */
+ label_text = g_strdup_printf (_("%s:"), arg->label);
+
+ switch (arg->type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ switch (arg->type)
+ {
+ case SF_IMAGE:
+ widget = gimp_image_combo_box_new (NULL, NULL);
+ ID_ptr = &arg->value.sfa_image;
+ break;
+
+ case SF_DRAWABLE:
+ widget = gimp_drawable_combo_box_new (NULL, NULL);
+ ID_ptr = &arg->value.sfa_drawable;
+ break;
+
+ case SF_LAYER:
+ widget = gimp_layer_combo_box_new (NULL, NULL);
+ ID_ptr = &arg->value.sfa_layer;
+ break;
+
+ case SF_CHANNEL:
+ widget = gimp_channel_combo_box_new (NULL, NULL);
+ ID_ptr = &arg->value.sfa_channel;
+ break;
+
+ case SF_VECTORS:
+ widget = gimp_vectors_combo_box_new (NULL, NULL);
+ ID_ptr = &arg->value.sfa_vectors;
+ break;
+
+ default:
+ break;
+ }
+
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (widget), *ID_ptr,
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ ID_ptr);
+ break;
+
+ case SF_COLOR:
+ {
+ GimpColorConfig *config;
+
+ left_align = TRUE;
+ widget = gimp_color_button_new (_("Script-Fu Color Selection"),
+ COLOR_SAMPLE_WIDTH,
+ COLOR_SAMPLE_HEIGHT,
+ &arg->value.sfa_color,
+ GIMP_COLOR_AREA_FLAT);
+
+ gimp_color_button_set_update (GIMP_COLOR_BUTTON (widget), TRUE);
+
+ config = gimp_get_color_configuration ();
+ gimp_color_button_set_color_config (GIMP_COLOR_BUTTON (widget),
+ config);
+ g_object_unref (config);
+
+ g_signal_connect (widget, "color-changed",
+ G_CALLBACK (gimp_color_button_get_color),
+ &arg->value.sfa_color);
+ }
+ break;
+
+ case SF_TOGGLE:
+ g_free (label_text);
+ label_text = NULL;
+ widget = gtk_check_button_new_with_mnemonic (arg->label);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ arg->value.sfa_toggle);
+
+ g_signal_connect (widget, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &arg->value.sfa_toggle);
+ break;
+
+ case SF_VALUE:
+ case SF_STRING:
+ widget = gtk_entry_new ();
+ gtk_widget_set_size_request (widget, TEXT_WIDTH, -1);
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+
+ gtk_entry_set_text (GTK_ENTRY (widget), arg->value.sfa_value);
+ break;
+
+ case SF_TEXT:
+ {
+ GtkWidget *view;
+ GtkTextBuffer *buffer;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request (widget, TEXT_WIDTH, -1);
+
+ view = gtk_text_view_new ();
+ gtk_container_add (GTK_CONTAINER (widget), view);
+ gtk_widget_show (view);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (view), TRUE);
+
+ gtk_text_buffer_set_text (buffer, arg->value.sfa_value, -1);
+
+ label_yalign = 0.0;
+ }
+ break;
+
+ case SF_ADJUSTMENT:
+ switch (arg->default_value.sfa_adjustment.type)
+ {
+ case SF_SLIDER:
+ arg->value.sfa_adjustment.adj = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (sf_interface->table),
+ 0, row,
+ label_text, SLIDER_WIDTH, -1,
+ arg->value.sfa_adjustment.value,
+ arg->default_value.sfa_adjustment.lower,
+ arg->default_value.sfa_adjustment.upper,
+ arg->default_value.sfa_adjustment.step,
+ arg->default_value.sfa_adjustment.page,
+ arg->default_value.sfa_adjustment.digits,
+ TRUE, 0.0, 0.0,
+ NULL, NULL);
+ gtk_entry_set_activates_default (GIMP_SCALE_ENTRY_SPINBUTTON (arg->value.sfa_adjustment.adj), TRUE);
+ break;
+
+ default:
+ g_warning ("unexpected adjustment type: %d",
+ arg->default_value.sfa_adjustment.type);
+ /* fallthrough */
+
+ case SF_SPINNER:
+ left_align = TRUE;
+ arg->value.sfa_adjustment.adj = (GtkAdjustment *)
+ gtk_adjustment_new (arg->value.sfa_adjustment.value,
+ arg->default_value.sfa_adjustment.lower,
+ arg->default_value.sfa_adjustment.upper,
+ arg->default_value.sfa_adjustment.step,
+ arg->default_value.sfa_adjustment.page,
+ 0);
+ widget = gimp_spin_button_new (arg->value.sfa_adjustment.adj,
+ arg->default_value.sfa_adjustment.step,
+ arg->default_value.sfa_adjustment.digits);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (widget), TRUE);
+ gtk_entry_set_activates_default (GTK_ENTRY (widget), TRUE);
+ break;
+ }
+
+ g_signal_connect (arg->value.sfa_adjustment.adj,
+ "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &arg->value.sfa_adjustment.value);
+ break;
+
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ if (arg->type == SF_FILENAME)
+ widget = gtk_file_chooser_button_new (_("Script-Fu File Selection"),
+ GTK_FILE_CHOOSER_ACTION_OPEN);
+ else
+ widget = gtk_file_chooser_button_new (_("Script-Fu Folder Selection"),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+
+ if (arg->value.sfa_file.filename)
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget),
+ arg->value.sfa_file.filename);
+
+ g_signal_connect (widget, "selection-changed",
+ G_CALLBACK (script_fu_file_callback),
+ &arg->value.sfa_file);
+ break;
+
+ case SF_FONT:
+ widget = gimp_font_select_button_new (_("Script-Fu Font Selection"),
+ arg->value.sfa_font);
+ g_signal_connect_swapped (widget, "font-set",
+ G_CALLBACK (script_fu_font_callback),
+ &arg->value.sfa_font);
+ break;
+
+ case SF_PALETTE:
+ widget = gimp_palette_select_button_new (_("Script-Fu Palette Selection"),
+ arg->value.sfa_palette);
+ g_signal_connect_swapped (widget, "palette-set",
+ G_CALLBACK (script_fu_palette_callback),
+ &arg->value.sfa_palette);
+ break;
+
+ case SF_PATTERN:
+ left_align = TRUE;
+ widget = gimp_pattern_select_button_new (_("Script-Fu Pattern Selection"),
+ arg->value.sfa_pattern);
+ g_signal_connect_swapped (widget, "pattern-set",
+ G_CALLBACK (script_fu_pattern_callback),
+ &arg->value.sfa_pattern);
+ break;
+
+ case SF_GRADIENT:
+ left_align = TRUE;
+ widget = gimp_gradient_select_button_new (_("Script-Fu Gradient Selection"),
+ arg->value.sfa_gradient);
+ g_signal_connect_swapped (widget, "gradient-set",
+ G_CALLBACK (script_fu_gradient_callback),
+ &arg->value.sfa_gradient);
+ break;
+
+ case SF_BRUSH:
+ left_align = TRUE;
+ widget = gimp_brush_select_button_new (_("Script-Fu Brush Selection"),
+ arg->value.sfa_brush.name,
+ arg->value.sfa_brush.opacity,
+ arg->value.sfa_brush.spacing,
+ arg->value.sfa_brush.paint_mode);
+ g_signal_connect_swapped (widget, "brush-set",
+ G_CALLBACK (script_fu_brush_callback),
+ &arg->value.sfa_brush);
+ break;
+
+ case SF_OPTION:
+ widget = gtk_combo_box_text_new ();
+ for (list = arg->default_value.sfa_option.list;
+ list;
+ list = g_slist_next (list))
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (widget),
+ list->data);
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget),
+ arg->value.sfa_option.history);
+
+ g_signal_connect (widget, "changed",
+ G_CALLBACK (script_fu_combo_callback),
+ &arg->value.sfa_option);
+ break;
+
+ case SF_ENUM:
+ widget = gimp_enum_combo_box_new (g_type_from_name (arg->default_value.sfa_enum.type_name));
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget),
+ arg->value.sfa_enum.history);
+
+ g_signal_connect (widget, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &arg->value.sfa_enum.history);
+ break;
+
+ case SF_DISPLAY:
+ break;
+ }
+
+ if (widget)
+ {
+ if (label_text)
+ {
+ gimp_table_attach_aligned (GTK_TABLE (sf_interface->table),
+ 0, row,
+ label_text, 0.0, label_yalign,
+ widget, 2, left_align);
+ g_free (label_text);
+ }
+ else
+ {
+ gtk_table_attach (GTK_TABLE (sf_interface->table),
+ widget, 0, 3, row, row + 1,
+ GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (widget);
+ }
+
+ if (left_align)
+ gtk_size_group_add_widget (group, widget);
+ }
+
+ sf_interface->widgets[i] = widget;
+ }
+
+ g_object_unref (group);
+
+ /* the script progress bar */
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_end (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ sf_interface->progress_bar = gimp_progress_bar_new ();
+ gtk_box_pack_start (GTK_BOX (vbox2), sf_interface->progress_bar,
+ FALSE, FALSE, 0);
+ gtk_widget_show (sf_interface->progress_bar);
+
+ sf_interface->progress_label = gtk_label_new (NULL);
+ gtk_label_set_xalign (GTK_LABEL (sf_interface->progress_label), 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (sf_interface->progress_label),
+ PANGO_ELLIPSIZE_MIDDLE);
+ gimp_label_set_attributes (GTK_LABEL (sf_interface->progress_label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox2), sf_interface->progress_label,
+ FALSE, FALSE, 0);
+ gtk_widget_show (sf_interface->progress_label);
+#ifdef G_OS_WIN32
+ {
+ HWND foreground = GetForegroundWindow ();
+#endif
+
+ gtk_widget_show (dialog);
+
+ gtk_main ();
+
+#ifdef G_OS_WIN32
+ if (! GetForegroundWindow ())
+ SetForegroundWindow (foreground);
+ }
+#endif
+ return sf_status;
+}
+
+static void
+script_fu_interface_quit (SFScript *script)
+{
+ gint i;
+
+ g_return_if_fail (script != NULL);
+ g_return_if_fail (sf_interface != NULL);
+
+ g_free (sf_interface->title);
+
+ for (i = 0; i < script->n_args; i++)
+ switch (script->args[i].type)
+ {
+ case SF_FONT:
+ case SF_PALETTE:
+ case SF_PATTERN:
+ case SF_GRADIENT:
+ case SF_BRUSH:
+ gimp_select_button_close_popup
+ (GIMP_SELECT_BUTTON (sf_interface->widgets[i]));
+ break;
+
+ default:
+ break;
+ }
+
+ g_free (sf_interface->widgets);
+ g_free (sf_interface->last_command);
+
+ g_slice_free (SFInterface, sf_interface);
+ sf_interface = NULL;
+
+ /* We do not call gtk_main_quit() earlier to reduce the possibility
+ * that script_fu_script_proc() is called from gimp_extension_process()
+ * while we are not finished with the current script. This sucks!
+ */
+
+ gtk_main_quit ();
+}
+
+static void
+script_fu_file_callback (GtkWidget *widget,
+ SFFilename *file)
+{
+ if (file->filename)
+ g_free (file->filename);
+
+ file->filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
+ script_fu_activate_main_dialog ();
+}
+
+static void
+script_fu_combo_callback (GtkWidget *widget,
+ SFOption *option)
+{
+ option->history = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
+}
+
+static void
+script_fu_string_update (gchar **dest,
+ const gchar *src)
+{
+ if (*dest)
+ g_free (*dest);
+
+ *dest = g_strdup (src);
+}
+
+static void
+script_fu_pattern_callback (gpointer data,
+ const gchar *name,
+ gint width,
+ gint height,
+ gint bytes,
+ const guchar *mask_data,
+ gboolean closing)
+{
+ script_fu_string_update (data, name);
+ if (closing) script_fu_activate_main_dialog ();
+}
+
+static void
+script_fu_gradient_callback (gpointer data,
+ const gchar *name,
+ gint width,
+ const gdouble *mask_data,
+ gboolean closing)
+{
+ script_fu_string_update (data, name);
+ if (closing) script_fu_activate_main_dialog ();
+}
+
+static void
+script_fu_font_callback (gpointer data,
+ const gchar *name,
+ gboolean closing)
+{
+ script_fu_string_update (data, name);
+ if (closing) script_fu_activate_main_dialog ();
+}
+
+static void
+script_fu_palette_callback (gpointer data,
+ const gchar *name,
+ gboolean closing)
+{
+ script_fu_string_update (data, name);
+ if (closing) script_fu_activate_main_dialog ();
+}
+
+static void
+script_fu_brush_callback (gpointer data,
+ const gchar *name,
+ gdouble opacity,
+ gint spacing,
+ GimpLayerMode paint_mode,
+ gint width,
+ gint height,
+ const guchar *mask_data,
+ gboolean closing)
+{
+ SFBrush *brush = data;
+
+ g_free (brush->name);
+
+ brush->name = g_strdup (name);
+ brush->opacity = opacity;
+ brush->spacing = spacing;
+ brush->paint_mode = paint_mode;
+
+ if (closing) script_fu_activate_main_dialog ();
+}
+
+static void
+unset_transient_for (GtkWidget *dialog)
+{
+ GdkWindow *window = gtk_widget_get_window (dialog);
+
+ if (window)
+ gdk_property_delete (window,
+ gdk_atom_intern_static_string ("WM_TRANSIENT_FOR"));
+}
+
+static void
+script_fu_response (GtkWidget *widget,
+ gint response_id,
+ SFScript *script)
+{
+ GtkWidget *action_area;
+
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (sf_interface->dialog));
+
+ if (! gtk_widget_is_sensitive (action_area))
+ return;
+
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ script_fu_reset (script);
+ break;
+
+ case GTK_RESPONSE_OK:
+ gtk_widget_set_sensitive (sf_interface->table, FALSE);
+ gtk_widget_set_sensitive (action_area, FALSE);
+
+ script_fu_ok (script);
+
+ script_fu_flush_events ();
+ /*
+ * The script could have created a new GimpImageWindow, so
+ * unset the transient-for property not to focus the
+ * ImageWindow from which the script was started
+ */
+ unset_transient_for (sf_interface->dialog);
+
+ gtk_widget_destroy (sf_interface->dialog);
+ break;
+
+ default:
+ sf_status = GIMP_PDB_CANCEL;
+
+ script_fu_flush_events ();
+ gtk_widget_destroy (sf_interface->dialog);
+ break;
+ }
+
+ script_fu_flush_events ();
+}
+
+static void
+script_fu_ok (SFScript *script)
+{
+ GString *output;
+ gchar *command;
+ gint i;
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArgValue *arg_value = &script->args[i].value;
+ GtkWidget *widget = sf_interface->widgets[i];
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ case SF_COLOR:
+ case SF_TOGGLE:
+ break;
+
+ case SF_VALUE:
+ case SF_STRING:
+ g_free (arg_value->sfa_value);
+ arg_value->sfa_value =
+ g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+ break;
+
+ case SF_TEXT:
+ {
+ GtkWidget *view;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+
+ view = gtk_bin_get_child (GTK_BIN (widget));
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ g_free (arg_value->sfa_value);
+ arg_value->sfa_value = gtk_text_buffer_get_text (buffer,
+ &start, &end,
+ FALSE);
+ }
+ break;
+
+ case SF_ADJUSTMENT:
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ case SF_FONT:
+ case SF_PALETTE:
+ case SF_PATTERN:
+ case SF_GRADIENT:
+ case SF_BRUSH:
+ case SF_OPTION:
+ case SF_ENUM:
+ break;
+ }
+ }
+
+ command = script_fu_script_get_command (script);
+
+ /* run the command through the interpreter */
+ output = g_string_new (NULL);
+ ts_register_output_func (ts_gstring_output_func, output);
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ if (ts_interpret_string (command))
+ {
+ gchar *message = g_strdup_printf (_("Error while executing %s:"),
+ script->name);
+
+ g_message ("%s\n\n%s", message, output->str);
+ g_free (message);
+ }
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_INTERNAL);
+
+ g_string_free (output, TRUE);
+
+ g_free (command);
+}
+
+static void
+script_fu_reset (SFScript *script)
+{
+ gint i;
+
+ script_fu_script_reset (script, FALSE);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArgValue *value = &script->args[i].value;
+ GtkWidget *widget = sf_interface->widgets[i];
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ break;
+
+ case SF_COLOR:
+ gimp_color_button_set_color (GIMP_COLOR_BUTTON (widget),
+ &value->sfa_color);
+ break;
+
+ case SF_TOGGLE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ value->sfa_toggle);
+ break;
+
+ case SF_VALUE:
+ case SF_STRING:
+ gtk_entry_set_text (GTK_ENTRY (widget), value->sfa_value);
+ break;
+
+ case SF_TEXT:
+ {
+ GtkWidget *view;
+ GtkTextBuffer *buffer;
+
+ view = gtk_bin_get_child (GTK_BIN (widget));
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ gtk_text_buffer_set_text (buffer, value->sfa_value, -1);
+ }
+ break;
+
+ case SF_ADJUSTMENT:
+ gtk_adjustment_set_value (value->sfa_adjustment.adj,
+ value->sfa_adjustment.value);
+ break;
+
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (widget),
+ value->sfa_file.filename);
+ break;
+
+ case SF_FONT:
+ gimp_font_select_button_set_font (GIMP_FONT_SELECT_BUTTON (widget),
+ value->sfa_font);
+ break;
+
+ case SF_PALETTE:
+ gimp_palette_select_button_set_palette (GIMP_PALETTE_SELECT_BUTTON (widget),
+ value->sfa_palette);
+ break;
+
+ case SF_PATTERN:
+ gimp_pattern_select_button_set_pattern (GIMP_PATTERN_SELECT_BUTTON (widget),
+ value->sfa_pattern);
+ break;
+
+ case SF_GRADIENT:
+ gimp_gradient_select_button_set_gradient (GIMP_GRADIENT_SELECT_BUTTON (widget),
+ value->sfa_gradient);
+ break;
+
+ case SF_BRUSH:
+ gimp_brush_select_button_set_brush (GIMP_BRUSH_SELECT_BUTTON (widget),
+ value->sfa_brush.name,
+ value->sfa_brush.opacity,
+ value->sfa_brush.spacing,
+ value->sfa_brush.paint_mode);
+ break;
+
+ case SF_OPTION:
+ gtk_combo_box_set_active (GTK_COMBO_BOX (widget),
+ value->sfa_option.history);
+ break;
+
+ case SF_ENUM:
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (widget),
+ value->sfa_enum.history);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Functions for window front/back management.
+ * These might only be necessary for MacOS.
+ *
+ * One problem is that the GIMP and the scriptfu extension process are separate "apps".
+ * Closing a main scriptfu dialog does not terminate the scriptfu extension process,
+ * and MacOS does not then activate some other app.
+ * On other platforms, the select dialogs are transient to the GIMP app's progress bar,
+ * but that doesn't seem to work on MacOS.
+ *
+ * Also some of the GIMP "select" widgets (for brush, pattern, gradient, font, palette)
+ * for plugins are independent and tool like:
+ * their popup dialog window is not a child of the button which pops it up.
+ * The button "owns" the popup but GDK is not aware of that relation,
+ * and so does not handle closing automatically.
+ * There are PDB callbacks in each direction:
+ * from the select dialog to the scriptfu extension on user's new selection
+ * from the scriptfu extension to the select dialog on closing
+ *
+ * This might change in the future.
+ * 1) in GIMP 3, scriptfu and python plugins should use a common API for a control dialog,
+ * (script-fu-interface.c is obsoleted?)
+ * 2) the code for select dialogs is significantly changed in GIMP 3
+ * 3) Gtk3 might solve this (now using Gtk2.)
+ */
+
+/* On MacOS, without calls to this, scriptfu dialog gets spinning ball of doom,
+ * meaning MacOS thinks app is not responding to events,
+ * and dialog stays visible even after destroyed.
+ */
+static void
+script_fu_flush_events (void)
+{
+ /* Ensure all GLib events have been processed. */
+
+ /* Former code also hid GUI of the script-fu extension process using: [NSApp hide: nil];
+ * (In Objective-C, get instance of NSApplication and send it a "hide" message with nil sender.)
+ * Hiding is not necessary, since this is only called when
+ * scriptfu is done interpreting the current script and is destroying its widgets.
+ */
+#ifdef GDK_WINDOWING_QUARTZ
+ /* Alternative code might be a call to gtk_main()?
+ * This is not an infinite loop since there are finite events, and iteration reduces them.
+ * Somehow, this lets MacOS think the app is responsive.
+ */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, TRUE);
+#else
+ /* empty function, optimized out. */
+#endif
+}
+
+
+/* On MacOS, without calls to this,
+ * when user closes GIMP "select" dialogs (child of main dialog)
+ * the main scriptfu dialog can be obscured by GIMP main window.
+ * The main scriptfu dialog must be visible so user can choose the OK button,
+ * and it contains a progress bar.
+ *
+ * Note the color "select" dialog has no callback specialized for scriptfu.
+ * And the file "chooser" dialog is also different.
+ */
+static void
+script_fu_activate_main_dialog (void)
+{
+ /* Ensure the main dialog of the script-fu extension process is not obscured. */
+#ifdef GDK_WINDOWING_QUARTZ
+ /* In Objective-C, get instance of NSApplication
+ * and send it a "activateIgnoringOtherApps" message, i.e. bring to front.
+ */
+ [NSApp activateIgnoringOtherApps: YES];
+#else
+ /* empty function, optimized out. */
+#endif
+}
diff --git a/plug-ins/script-fu/script-fu-interface.h b/plug-ins/script-fu/script-fu-interface.h
new file mode 100644
index 0000000..3d5091c
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-interface.h
@@ -0,0 +1,28 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_INTERFACE_H__
+#define __SCRIPT_FU_INTERFACE_H__
+
+
+GimpPDBStatusType script_fu_interface (SFScript *script,
+ gint start_arg);
+void script_fu_interface_report_cc (const gchar *command);
+gboolean script_fu_interface_is_active (void);
+
+
+#endif /* __SCRIPT_FU_INTERFACE_H__ */
diff --git a/plug-ins/script-fu/script-fu-intl.h b/plug-ins/script-fu/script-fu-intl.h
new file mode 100644
index 0000000..462582c
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-intl.h
@@ -0,0 +1,43 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * script-fu-intl.h
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_INTL_H__
+#define __SCRIPT_FU_INTL_H__
+
+#ifndef GETTEXT_PACKAGE
+#error "config.h must be included prior to script-fu-intl.h"
+#endif
+
+#include <glib/gi18n.h>
+
+
+#ifndef HAVE_BIND_TEXTDOMAIN_CODESET
+# define bind_textdomain_codeset(Domain, Codeset) (Domain)
+#endif
+
+#define INIT_I18N() G_STMT_START{ \
+ bindtextdomain (GETTEXT_PACKAGE"-script-fu", \
+ gimp_locale_directory ()); \
+ bind_textdomain_codeset (GETTEXT_PACKAGE"-script-fu", "UTF-8"); \
+ textdomain (GETTEXT_PACKAGE"-script-fu"); \
+}G_STMT_END
+
+
+#endif /* __SCRIPT_FU_INTL_H__ */
diff --git a/plug-ins/script-fu/script-fu-regex.c b/plug-ins/script-fu/script-fu-regex.c
new file mode 100644
index 0000000..02255d2
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-regex.c
@@ -0,0 +1,179 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/* Based on re.c
+ *
+ * Henry Spencer's implementation of Regular Expressions,
+ * used for TinyScheme
+ *
+ * Refurbished by Stephen Gildea
+ *
+ * Ported to GRegex and de-uglified by Michael Natterer
+ */
+
+#include "config.h"
+
+#include "tinyscheme/scheme-private.h"
+#include "script-fu-regex.h"
+
+
+/* local function prototypes */
+
+static pointer foreign_re_match (scheme *sc,
+ pointer args);
+static void set_vector_elem (pointer vec,
+ int ielem,
+ pointer newel);
+
+
+/* public functions */
+
+void
+script_fu_regex_init (scheme *sc)
+{
+ sc->vptr->scheme_define (sc,
+ sc->global_env,
+ sc->vptr->mk_symbol(sc,"re-match"),
+ sc->vptr->mk_foreign_func(sc, foreign_re_match));
+
+#if 0
+ sc->vptr->load_string
+ (sc,
+ ";; return the substring of STRING matched in MATCH-VECTOR,\n"
+ ";; the Nth subexpression match (default 0).\n"
+ "\n"
+ "(define (re-match-nth string match-vector . n)\n"
+ " (let ((n (if (pair? n) (car n) 0)))\n"
+ " (substring string (car (vector-ref match-vector n))\n"
+ " (cdr (vector-ref match-vector n)))))\n"
+ "(define (re-before-nth string match-vector . n)\n"
+ " (let ((n (if (pair? n) (car n) 0)))\n"
+ " (substring string 0 (car (vector-ref match-vector n)))))\n"
+ "(define (re-after-nth string match-vector . n)\n"
+ " (let ((n (if (pair? n) (car n) 0)))\n"
+ " (substring string (cdr (vector-ref match-vector n))\n"
+ " (string-length string))))\n");
+#endif
+}
+
+
+/* private functions */
+
+static pointer
+foreign_re_match (scheme *sc,
+ pointer args)
+{
+ pointer retval = sc->F;
+ gboolean success;
+ gboolean is_valid_utf8;
+ GRegex *regex;
+ pointer first_arg, second_arg;
+ pointer third_arg = sc->NIL;
+ char *string;
+ char *pattern;
+ int num = 0;
+
+ if (!((args != sc->NIL)
+ && sc->vptr->is_string ((first_arg = sc->vptr->pair_car (args)))
+ && (args = sc->vptr->pair_cdr (args))
+ && sc->vptr->is_pair (args)
+ && sc->vptr->is_string ((second_arg = sc->vptr->pair_car (args)))))
+ {
+ return sc->F;
+ }
+
+ pattern = sc->vptr->string_value (first_arg);
+ string = sc->vptr->string_value (second_arg);
+
+ is_valid_utf8 = g_utf8_validate (string, -1, NULL);
+
+ args = sc->vptr->pair_cdr (args);
+
+ if (args != sc->NIL)
+ {
+ if (!(sc->vptr->is_pair (args)
+ && sc->vptr->is_vector ((third_arg = sc->vptr->pair_car (args)))))
+ {
+ return sc->F;
+ }
+ else
+ {
+ num = third_arg->_object._number.value.ivalue;
+ }
+ }
+
+ regex = g_regex_new (pattern, G_REGEX_EXTENDED, 0, NULL);
+ if (! regex)
+ return sc->F;
+
+ if (! num)
+ {
+ success = g_regex_match (regex, string, 0, NULL);
+ }
+ else
+ {
+ GMatchInfo *match_info;
+ gint i;
+
+ success = g_regex_match (regex, string, 0, &match_info);
+
+ for (i = 0; i < num; i++)
+ {
+ gint start, end;
+
+ g_match_info_fetch_pos (match_info, i, &start, &end);
+
+ if (is_valid_utf8)
+ {
+ start = g_utf8_pointer_to_offset (string, string + start);
+ end = g_utf8_pointer_to_offset (string, string + end);
+ }
+
+#undef cons
+ set_vector_elem (third_arg, i,
+ sc->vptr->cons(sc,
+ sc->vptr->mk_integer(sc, start),
+ sc->vptr->mk_integer(sc, end)));
+ }
+
+ g_match_info_free (match_info);
+ }
+
+ if (success)
+ retval = sc->T;
+
+ g_regex_unref (regex);
+
+ return retval;
+}
+
+static void
+set_vector_elem (pointer vec,
+ int ielem,
+ pointer newel)
+{
+ int n = ielem / 2;
+
+ if (ielem % 2 == 0)
+ {
+ vec[1 + n]._object._cons._car = newel;
+ }
+ else
+ {
+ vec[1 + n]._object._cons._cdr = newel;
+ }
+}
diff --git a/plug-ins/script-fu/script-fu-regex.h b/plug-ins/script-fu/script-fu-regex.h
new file mode 100644
index 0000000..9c0cbb2
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-regex.h
@@ -0,0 +1,25 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_REGEX_H__
+#define __SCRIPT_FU_REGEX_H__
+
+
+void script_fu_regex_init (scheme *sc);
+
+
+#endif /* __SCRIPT_FU_REGEX_H__ */
diff --git a/plug-ins/script-fu/script-fu-script.c b/plug-ins/script-fu/script-fu-script.c
new file mode 100644
index 0000000..cd37163
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-script.c
@@ -0,0 +1,780 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "tinyscheme/scheme-private.h"
+
+#include "script-fu-types.h"
+
+#include "script-fu-script.h"
+#include "script-fu-scripts.h"
+#include "script-fu-utils.h"
+
+#include "script-fu-intl.h"
+
+
+/*
+ * Local Functions
+ */
+
+static gboolean script_fu_script_param_init (SFScript *script,
+ gint nparams,
+ const GimpParam *params,
+ SFArgType type,
+ gint n);
+
+
+/*
+ * Function definitions
+ */
+
+SFScript *
+script_fu_script_new (const gchar *name,
+ const gchar *menu_label,
+ const gchar *blurb,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *image_types,
+ gint n_args)
+{
+ SFScript *script;
+
+ script = g_slice_new0 (SFScript);
+
+ script->name = g_strdup (name);
+ script->menu_label = g_strdup (menu_label);
+ script->blurb = g_strdup (blurb);
+ script->author = g_strdup (author);
+ script->copyright = g_strdup (copyright);
+ script->date = g_strdup (date);
+ script->image_types = g_strdup (image_types);
+
+ script->n_args = n_args;
+ script->args = g_new0 (SFArg, script->n_args);
+
+ return script;
+}
+
+void
+script_fu_script_free (SFScript *script)
+{
+ gint i;
+
+ g_return_if_fail (script != NULL);
+
+ g_free (script->name);
+ g_free (script->blurb);
+ g_free (script->menu_label);
+ g_free (script->author);
+ g_free (script->copyright);
+ g_free (script->date);
+ g_free (script->image_types);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArg *arg = &script->args[i];
+
+ g_free (arg->label);
+
+ switch (arg->type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ case SF_COLOR:
+ case SF_TOGGLE:
+ break;
+
+ case SF_VALUE:
+ case SF_STRING:
+ case SF_TEXT:
+ g_free (arg->default_value.sfa_value);
+ g_free (arg->value.sfa_value);
+ break;
+
+ case SF_ADJUSTMENT:
+ break;
+
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ g_free (arg->default_value.sfa_file.filename);
+ g_free (arg->value.sfa_file.filename);
+ break;
+
+ case SF_FONT:
+ g_free (arg->default_value.sfa_font);
+ g_free (arg->value.sfa_font);
+ break;
+
+ case SF_PALETTE:
+ g_free (arg->default_value.sfa_palette);
+ g_free (arg->value.sfa_palette);
+ break;
+
+ case SF_PATTERN:
+ g_free (arg->default_value.sfa_pattern);
+ g_free (arg->value.sfa_pattern);
+ break;
+
+ case SF_GRADIENT:
+ g_free (arg->default_value.sfa_gradient);
+ g_free (arg->value.sfa_gradient);
+ break;
+
+ case SF_BRUSH:
+ g_free (arg->default_value.sfa_brush.name);
+ g_free (arg->value.sfa_brush.name);
+ break;
+
+ case SF_OPTION:
+ g_slist_free_full (arg->default_value.sfa_option.list,
+ (GDestroyNotify) g_free);
+ break;
+
+ case SF_ENUM:
+ g_free (arg->default_value.sfa_enum.type_name);
+ break;
+ }
+ }
+
+ g_free (script->args);
+
+ g_slice_free (SFScript, script);
+}
+
+void
+script_fu_script_install_proc (SFScript *script,
+ GimpRunProc run_proc)
+{
+ const gchar *menu_label = NULL;
+ GimpParamDef *args;
+ gint i;
+
+ g_return_if_fail (script != NULL);
+ g_return_if_fail (run_proc != NULL);
+
+ /* Allow scripts with no menus */
+ if (strncmp (script->menu_label, "<None>", 6) != 0)
+ menu_label = script->menu_label;
+
+ args = g_new0 (GimpParamDef, script->n_args + 1);
+
+ args[0].type = GIMP_PDB_INT32;
+ args[0].name = "run-mode";
+ args[0].description = "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }";
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ GimpPDBArgType type = 0;
+ const gchar *name = NULL;
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ type = GIMP_PDB_IMAGE;
+ name = "image";
+ break;
+
+ case SF_DRAWABLE:
+ type = GIMP_PDB_DRAWABLE;
+ name = "drawable";
+ break;
+
+ case SF_LAYER:
+ type = GIMP_PDB_LAYER;
+ name = "layer";
+ break;
+
+ case SF_CHANNEL:
+ type = GIMP_PDB_CHANNEL;
+ name = "channel";
+ break;
+
+ case SF_VECTORS:
+ type = GIMP_PDB_VECTORS;
+ name = "vectors";
+ break;
+
+ case SF_DISPLAY:
+ type = GIMP_PDB_DISPLAY;
+ name = "display";
+ break;
+
+ case SF_COLOR:
+ type = GIMP_PDB_COLOR;
+ name = "color";
+ break;
+
+ case SF_TOGGLE:
+ type = GIMP_PDB_INT32;
+ name = "toggle";
+ break;
+
+ case SF_VALUE:
+ type = GIMP_PDB_STRING;
+ name = "value";
+ break;
+
+ case SF_STRING:
+ case SF_TEXT:
+ type = GIMP_PDB_STRING;
+ name = "string";
+ break;
+
+ case SF_ADJUSTMENT:
+ type = GIMP_PDB_FLOAT;
+ name = "value";
+ break;
+
+ case SF_FILENAME:
+ type = GIMP_PDB_STRING;
+ name = "filename";
+ break;
+
+ case SF_DIRNAME:
+ type = GIMP_PDB_STRING;
+ name = "dirname";
+ break;
+
+ case SF_FONT:
+ type = GIMP_PDB_STRING;
+ name = "font";
+ break;
+
+ case SF_PALETTE:
+ type = GIMP_PDB_STRING;
+ name = "palette";
+ break;
+
+ case SF_PATTERN:
+ type = GIMP_PDB_STRING;
+ name = "pattern";
+ break;
+
+ case SF_BRUSH:
+ type = GIMP_PDB_STRING;
+ name = "brush";
+ break;
+
+ case SF_GRADIENT:
+ type = GIMP_PDB_STRING;
+ name = "gradient";
+ break;
+
+ case SF_OPTION:
+ type = GIMP_PDB_INT32;
+ name = "option";
+ break;
+
+ case SF_ENUM:
+ type = GIMP_PDB_INT32;
+ name = "enum";
+ break;
+ }
+
+ args[i + 1].type = type;
+ args[i + 1].name = (gchar *) name;
+ args[i + 1].description = script->args[i].label;
+ }
+
+ gimp_install_temp_proc (script->name,
+ script->blurb,
+ "",
+ script->author,
+ script->copyright,
+ script->date,
+ menu_label,
+ script->image_types,
+ GIMP_TEMPORARY,
+ script->n_args + 1, 0,
+ args, NULL,
+ run_proc);
+
+ g_free (args);
+}
+
+void
+script_fu_script_uninstall_proc (SFScript *script)
+{
+ g_return_if_fail (script != NULL);
+
+ gimp_uninstall_temp_proc (script->name);
+}
+
+gchar *
+script_fu_script_get_title (SFScript *script)
+{
+ gchar *title;
+ gchar *tmp;
+
+ g_return_val_if_fail (script != NULL, NULL);
+
+ /* strip mnemonics from the menupath */
+ title = gimp_strip_uline (script->menu_label);
+
+ /* if this looks like a full menu path, use only the last part */
+ if (title[0] == '<' && (tmp = strrchr (title, '/')) && tmp[1])
+ {
+ tmp = g_strdup (tmp + 1);
+
+ g_free (title);
+ title = tmp;
+ }
+
+ /* cut off ellipsis */
+ tmp = (strstr (title, "..."));
+ if (! tmp)
+ /* U+2026 HORIZONTAL ELLIPSIS */
+ tmp = strstr (title, "\342\200\246");
+
+ if (tmp && tmp == (title + strlen (title) - 3))
+ *tmp = '\0';
+
+ return title;
+}
+
+void
+script_fu_script_reset (SFScript *script,
+ gboolean reset_ids)
+{
+ gint i;
+
+ g_return_if_fail (script != NULL);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArgValue *value = &script->args[i].value;
+ SFArgValue *default_value = &script->args[i].default_value;
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ if (reset_ids)
+ value->sfa_image = default_value->sfa_image;
+ break;
+
+ case SF_COLOR:
+ value->sfa_color = default_value->sfa_color;
+ break;
+
+ case SF_TOGGLE:
+ value->sfa_toggle = default_value->sfa_toggle;
+ break;
+
+ case SF_VALUE:
+ case SF_STRING:
+ case SF_TEXT:
+ g_free (value->sfa_value);
+ value->sfa_value = g_strdup (default_value->sfa_value);
+ break;
+
+ case SF_ADJUSTMENT:
+ value->sfa_adjustment.value = default_value->sfa_adjustment.value;
+ break;
+
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ g_free (value->sfa_file.filename);
+ value->sfa_file.filename = g_strdup (default_value->sfa_file.filename);
+ break;
+
+ case SF_FONT:
+ g_free (value->sfa_font);
+ value->sfa_font = g_strdup (default_value->sfa_font);
+ break;
+
+ case SF_PALETTE:
+ g_free (value->sfa_palette);
+ value->sfa_palette = g_strdup (default_value->sfa_palette);
+ break;
+
+ case SF_PATTERN:
+ g_free (value->sfa_pattern);
+ value->sfa_pattern = g_strdup (default_value->sfa_pattern);
+ break;
+
+ case SF_GRADIENT:
+ g_free (value->sfa_gradient);
+ value->sfa_gradient = g_strdup (default_value->sfa_gradient);
+ break;
+
+ case SF_BRUSH:
+ g_free (value->sfa_brush.name);
+ value->sfa_brush.name = g_strdup (default_value->sfa_brush.name);
+ value->sfa_brush.opacity = default_value->sfa_brush.opacity;
+ value->sfa_brush.spacing = default_value->sfa_brush.spacing;
+ value->sfa_brush.paint_mode = default_value->sfa_brush.paint_mode;
+ break;
+
+ case SF_OPTION:
+ value->sfa_option.history = default_value->sfa_option.history;
+ break;
+
+ case SF_ENUM:
+ value->sfa_enum.history = default_value->sfa_enum.history;
+ break;
+ }
+ }
+}
+
+gint
+script_fu_script_collect_standard_args (SFScript *script,
+ gint n_params,
+ const GimpParam *params)
+{
+ gint params_consumed = 0;
+
+ g_return_val_if_fail (script != NULL, 0);
+
+ /* the first parameter may be a DISPLAY id */
+ if (script_fu_script_param_init (script,
+ n_params, params, SF_DISPLAY,
+ params_consumed))
+ {
+ params_consumed++;
+ }
+
+ /* an IMAGE id may come first or after the DISPLAY id */
+ if (script_fu_script_param_init (script,
+ n_params, params, SF_IMAGE,
+ params_consumed))
+ {
+ params_consumed++;
+
+ /* and may be followed by a DRAWABLE, LAYER, CHANNEL or
+ * VECTORS id
+ */
+ if (script_fu_script_param_init (script,
+ n_params, params, SF_DRAWABLE,
+ params_consumed) ||
+ script_fu_script_param_init (script,
+ n_params, params, SF_LAYER,
+ params_consumed) ||
+ script_fu_script_param_init (script,
+ n_params, params, SF_CHANNEL,
+ params_consumed) ||
+ script_fu_script_param_init (script,
+ n_params, params, SF_VECTORS,
+ params_consumed))
+ {
+ params_consumed++;
+ }
+ }
+
+ return params_consumed;
+}
+
+gchar *
+script_fu_script_get_command (SFScript *script)
+{
+ GString *s;
+ gint i;
+
+ g_return_val_if_fail (script != NULL, NULL);
+
+ s = g_string_new ("(");
+ g_string_append (s, script->name);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArgValue *arg_value = &script->args[i].value;
+
+ g_string_append_c (s, ' ');
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ g_string_append_printf (s, "%d", arg_value->sfa_image);
+ break;
+
+ case SF_COLOR:
+ {
+ guchar r, g, b;
+
+ gimp_rgb_get_uchar (&arg_value->sfa_color, &r, &g, &b);
+ g_string_append_printf (s, "'(%d %d %d)",
+ (gint) r, (gint) g, (gint) b);
+ }
+ break;
+
+ case SF_TOGGLE:
+ g_string_append (s, arg_value->sfa_toggle ? "TRUE" : "FALSE");
+ break;
+
+ case SF_VALUE:
+ g_string_append (s, arg_value->sfa_value);
+ break;
+
+ case SF_STRING:
+ case SF_TEXT:
+ {
+ gchar *tmp;
+
+ tmp = script_fu_strescape (arg_value->sfa_value);
+ g_string_append_printf (s, "\"%s\"", tmp);
+ g_free (tmp);
+ }
+ break;
+
+ case SF_ADJUSTMENT:
+ {
+ gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buffer, sizeof (buffer),
+ arg_value->sfa_adjustment.value);
+ g_string_append (s, buffer);
+ }
+ break;
+
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ {
+ gchar *tmp;
+
+ tmp = script_fu_strescape (arg_value->sfa_file.filename);
+ g_string_append_printf (s, "\"%s\"", tmp);
+ g_free (tmp);
+ }
+ break;
+
+ case SF_FONT:
+ g_string_append_printf (s, "\"%s\"", arg_value->sfa_font);
+ break;
+
+ case SF_PALETTE:
+ g_string_append_printf (s, "\"%s\"", arg_value->sfa_palette);
+ break;
+
+ case SF_PATTERN:
+ g_string_append_printf (s, "\"%s\"", arg_value->sfa_pattern);
+ break;
+
+ case SF_GRADIENT:
+ g_string_append_printf (s, "\"%s\"", arg_value->sfa_gradient);
+ break;
+
+ case SF_BRUSH:
+ {
+ gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buffer, sizeof (buffer),
+ arg_value->sfa_brush.opacity);
+ g_string_append_printf (s, "'(\"%s\" %s %d %d)",
+ arg_value->sfa_brush.name,
+ buffer,
+ arg_value->sfa_brush.spacing,
+ arg_value->sfa_brush.paint_mode);
+ }
+ break;
+
+ case SF_OPTION:
+ g_string_append_printf (s, "%d", arg_value->sfa_option.history);
+ break;
+
+ case SF_ENUM:
+ g_string_append_printf (s, "%d", arg_value->sfa_enum.history);
+ break;
+ }
+ }
+
+ g_string_append_c (s, ')');
+
+ return g_string_free (s, FALSE);
+}
+
+gchar *
+script_fu_script_get_command_from_params (SFScript *script,
+ const GimpParam *params)
+{
+ GString *s;
+ gint i;
+
+ g_return_val_if_fail (script != NULL, NULL);
+
+ s = g_string_new ("(");
+ g_string_append (s, script->name);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ const GimpParam *param = &params[i + 1];
+
+ g_string_append_c (s, ' ');
+
+ switch (script->args[i].type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ g_string_append_printf (s, "%d", param->data.d_int32);
+ break;
+
+ case SF_COLOR:
+ {
+ guchar r, g, b;
+
+ gimp_rgb_get_uchar (&param->data.d_color, &r, &g, &b);
+ g_string_append_printf (s, "'(%d %d %d)",
+ (gint) r, (gint) g, (gint) b);
+ }
+ break;
+
+ case SF_TOGGLE:
+ g_string_append_printf (s, (param->data.d_int32 ? "TRUE" : "FALSE"));
+ break;
+
+ case SF_VALUE:
+ g_string_append (s, param->data.d_string);
+ break;
+
+ case SF_STRING:
+ case SF_TEXT:
+ case SF_FILENAME:
+ case SF_DIRNAME:
+ {
+ gchar *tmp;
+
+ tmp = script_fu_strescape (param->data.d_string);
+ g_string_append_printf (s, "\"%s\"", tmp);
+ g_free (tmp);
+ }
+ break;
+
+ case SF_ADJUSTMENT:
+ {
+ gchar buffer[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (buffer, sizeof (buffer), param->data.d_float);
+ g_string_append (s, buffer);
+ }
+ break;
+
+ case SF_FONT:
+ case SF_PALETTE:
+ case SF_PATTERN:
+ case SF_GRADIENT:
+ case SF_BRUSH:
+ g_string_append_printf (s, "\"%s\"", param->data.d_string);
+ break;
+
+ case SF_OPTION:
+ case SF_ENUM:
+ g_string_append_printf (s, "%d", param->data.d_int32);
+ break;
+ }
+ }
+
+ g_string_append_c (s, ')');
+
+ return g_string_free (s, FALSE);
+}
+
+
+/*
+ * Local Functions
+ */
+
+static gboolean
+script_fu_script_param_init (SFScript *script,
+ gint nparams,
+ const GimpParam *params,
+ SFArgType type,
+ gint n)
+{
+ SFArg *arg = &script->args[n];
+
+ if (script->n_args > n && arg->type == type && nparams > n + 1)
+ {
+ switch (type)
+ {
+ case SF_IMAGE:
+ if (params[n + 1].type == GIMP_PDB_IMAGE)
+ {
+ arg->value.sfa_image = params[n + 1].data.d_image;
+ return TRUE;
+ }
+ break;
+
+ case SF_DRAWABLE:
+ if (params[n + 1].type == GIMP_PDB_DRAWABLE)
+ {
+ arg->value.sfa_drawable = params[n + 1].data.d_drawable;
+ return TRUE;
+ }
+ break;
+
+ case SF_LAYER:
+ if (params[n + 1].type == GIMP_PDB_LAYER)
+ {
+ arg->value.sfa_layer = params[n + 1].data.d_layer;
+ return TRUE;
+ }
+ break;
+
+ case SF_CHANNEL:
+ if (params[n + 1].type == GIMP_PDB_CHANNEL)
+ {
+ arg->value.sfa_channel = params[n + 1].data.d_channel;
+ return TRUE;
+ }
+ break;
+
+ case SF_VECTORS:
+ if (params[n + 1].type == GIMP_PDB_VECTORS)
+ {
+ arg->value.sfa_vectors = params[n + 1].data.d_vectors;
+ return TRUE;
+ }
+ break;
+
+ case SF_DISPLAY:
+ if (params[n + 1].type == GIMP_PDB_DISPLAY)
+ {
+ arg->value.sfa_display = params[n + 1].data.d_display;
+ return TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/plug-ins/script-fu/script-fu-script.h b/plug-ins/script-fu/script-fu-script.h
new file mode 100644
index 0000000..86b318f
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-script.h
@@ -0,0 +1,49 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_SCRIPT_H__
+#define __SCRIPT_FU_SCRIPT_H__
+
+
+SFScript * script_fu_script_new (const gchar *name,
+ const gchar *menu_label,
+ const gchar *blurb,
+ const gchar *author,
+ const gchar *copyright,
+ const gchar *date,
+ const gchar *image_types,
+ gint n_args);
+void script_fu_script_free (SFScript *script);
+
+void script_fu_script_install_proc (SFScript *script,
+ GimpRunProc run_proc);
+void script_fu_script_uninstall_proc (SFScript *script);
+
+gchar * script_fu_script_get_title (SFScript *script);
+void script_fu_script_reset (SFScript *script,
+ gboolean reset_ids);
+
+gint script_fu_script_collect_standard_args (SFScript *script,
+ gint n_params,
+ const GimpParam *params);
+
+gchar * script_fu_script_get_command (SFScript *script);
+gchar * script_fu_script_get_command_from_params (SFScript *script,
+ const GimpParam *params);
+
+
+#endif /* __SCRIPT_FU_SCRIPT__ */
diff --git a/plug-ins/script-fu/script-fu-scripts.c b/plug-ins/script-fu/script-fu-scripts.c
new file mode 100644
index 0000000..bf56edb
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-scripts.c
@@ -0,0 +1,934 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "tinyscheme/scheme-private.h"
+
+#include "scheme-wrapper.h"
+
+#include "script-fu-types.h"
+
+#include "script-fu-interface.h"
+#include "script-fu-script.h"
+#include "script-fu-scripts.h"
+#include "script-fu-utils.h"
+
+#include "script-fu-intl.h"
+
+
+typedef struct
+{
+ SFScript *script;
+ gchar *menu_path;
+} SFMenu;
+
+
+/*
+ * Local Functions
+ */
+
+static gboolean script_fu_run_command (const gchar *command,
+ GError **error);
+static void script_fu_load_directory (GFile *directory);
+static void script_fu_load_script (GFile *file);
+static gboolean script_fu_install_script (gpointer foo,
+ GList *scripts,
+ gpointer bar);
+static void script_fu_install_menu (SFMenu *menu);
+static gboolean script_fu_remove_script (gpointer foo,
+ GList *scripts,
+ gpointer bar);
+static void script_fu_script_proc (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static SFScript *script_fu_find_script (const gchar *name);
+
+static gchar * script_fu_menu_map (const gchar *menu_path);
+static gint script_fu_menu_compare (gconstpointer a,
+ gconstpointer b);
+
+
+/*
+ * Local variables
+ */
+
+static GTree *script_tree = NULL;
+static GList *script_menu_list = NULL;
+
+
+/*
+ * Function definitions
+ */
+
+void
+script_fu_find_scripts (GList *path)
+{
+ GList *list;
+
+ /* Make sure to clear any existing scripts */
+ if (script_tree != NULL)
+ {
+ g_tree_foreach (script_tree,
+ (GTraverseFunc) script_fu_remove_script,
+ NULL);
+ g_tree_destroy (script_tree);
+ }
+
+ if (! path)
+ return;
+
+ script_tree = g_tree_new ((GCompareFunc) g_utf8_collate);
+
+ for (list = path; list; list = g_list_next (list))
+ {
+ script_fu_load_directory (list->data);
+ }
+
+ /* Now that all scripts are read in and sorted, tell gimp about them */
+ g_tree_foreach (script_tree,
+ (GTraverseFunc) script_fu_install_script,
+ NULL);
+
+ script_menu_list = g_list_sort (script_menu_list,
+ (GCompareFunc) script_fu_menu_compare);
+
+ /* Install and nuke the list of menu entries */
+ g_list_free_full (script_menu_list,
+ (GDestroyNotify) script_fu_install_menu);
+ script_menu_list = NULL;
+}
+
+pointer
+script_fu_add_script (scheme *sc,
+ pointer a)
+{
+ SFScript *script;
+ const gchar *name;
+ const gchar *menu_label;
+ const gchar *blurb;
+ const gchar *author;
+ const gchar *copyright;
+ const gchar *date;
+ const gchar *image_types;
+ gint n_args;
+ gint i;
+
+ /* Check the length of a */
+ if (sc->vptr->list_length (sc, a) < 7)
+ {
+ g_message (_("Too few arguments to 'script-fu-register' call"));
+ return sc->NIL;
+ }
+
+ /* Find the script name */
+ name = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script menu_label */
+ menu_label = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script blurb */
+ blurb = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script author */
+ author = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script copyright */
+ copyright = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script date */
+ date = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ /* Find the script image types */
+ if (sc->vptr->is_pair (a))
+ {
+ image_types = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ {
+ image_types = sc->vptr->string_value (a);
+ a = sc->NIL;
+ }
+
+ /* Check the supplied number of arguments */
+ n_args = sc->vptr->list_length (sc, a) / 3;
+
+ /* Create a new script */
+ script = script_fu_script_new (name,
+ menu_label,
+ blurb,
+ author,
+ copyright,
+ date,
+ image_types,
+ n_args);
+
+ for (i = 0; i < script->n_args; i++)
+ {
+ SFArg *arg = &script->args[i];
+
+ if (a != sc->NIL)
+ {
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: argument types must be integer values", 0);
+
+ arg->type = sc->vptr->ivalue (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ return foreign_error (sc, "script-fu-register: missing type specifier", 0);
+
+ if (a != sc->NIL)
+ {
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: argument labels must be strings", 0);
+
+ arg->label = g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ return foreign_error (sc, "script-fu-register: missing arguments label", 0);
+
+ if (a != sc->NIL)
+ {
+ switch (arg->type)
+ {
+ case SF_IMAGE:
+ case SF_DRAWABLE:
+ case SF_LAYER:
+ case SF_CHANNEL:
+ case SF_VECTORS:
+ case SF_DISPLAY:
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: default IDs must be integer values", 0);
+
+ arg->default_value.sfa_image =
+ sc->vptr->ivalue (sc->vptr->pair_car (a));
+ break;
+
+ case SF_COLOR:
+ if (sc->vptr->is_string (sc->vptr->pair_car (a)))
+ {
+ if (! gimp_rgb_parse_css (&arg->default_value.sfa_color,
+ sc->vptr->string_value (sc->vptr->pair_car (a)),
+ -1))
+ return foreign_error (sc, "script-fu-register: invalid default color name", 0);
+
+ gimp_rgb_set_alpha (&arg->default_value.sfa_color, 1.0);
+ }
+ else if (sc->vptr->is_list (sc, sc->vptr->pair_car (a)) &&
+ sc->vptr->list_length(sc, sc->vptr->pair_car (a)) == 3)
+ {
+ pointer color_list;
+ guchar r, g, b;
+
+ color_list = sc->vptr->pair_car (a);
+ r = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ g = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+ color_list = sc->vptr->pair_cdr (color_list);
+ b = CLAMP (sc->vptr->ivalue (sc->vptr->pair_car (color_list)), 0, 255);
+
+ gimp_rgb_set_uchar (&arg->default_value.sfa_color, r, g, b);
+ }
+ else
+ {
+ return foreign_error (sc, "script-fu-register: color defaults must be a list of 3 integers or a color name", 0);
+ }
+ break;
+
+ case SF_TOGGLE:
+ if (!sc->vptr->is_integer (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: toggle default must be an integer value", 0);
+
+ arg->default_value.sfa_toggle =
+ (sc->vptr->ivalue (sc->vptr->pair_car (a))) ? TRUE : FALSE;
+ break;
+
+ case SF_VALUE:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: value defaults must be string values", 0);
+
+ arg->default_value.sfa_value =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_STRING:
+ case SF_TEXT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: string defaults must be string values", 0);
+
+ arg->default_value.sfa_value =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_ADJUSTMENT:
+ {
+ pointer adj_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: adjustment defaults must be a list", 0);
+
+ adj_list = sc->vptr->pair_car (a);
+ arg->default_value.sfa_adjustment.value =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.lower =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.upper =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.step =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.page =
+ sc->vptr->rvalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.digits =
+ sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
+
+ adj_list = sc->vptr->pair_cdr (adj_list);
+ arg->default_value.sfa_adjustment.type =
+ sc->vptr->ivalue (sc->vptr->pair_car (adj_list));
+ }
+ break;
+
+ case SF_FILENAME:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: filename defaults must be string values", 0);
+ /* fallthrough */
+
+ case SF_DIRNAME:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: dirname defaults must be string values", 0);
+
+ arg->default_value.sfa_file.filename =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+
+#ifdef G_OS_WIN32
+ {
+ /* Replace POSIX slashes with Win32 backslashes. This
+ * is just so script-fus can be written with only
+ * POSIX directory separators.
+ */
+ gchar *filename = arg->default_value.sfa_file.filename;
+
+ while (*filename)
+ {
+ if (*filename == '/')
+ *filename = G_DIR_SEPARATOR;
+
+ filename++;
+ }
+ }
+#endif
+ break;
+
+ case SF_FONT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: font defaults must be string values", 0);
+
+ arg->default_value.sfa_font =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_PALETTE:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: palette defaults must be string values", 0);
+
+ arg->default_value.sfa_palette =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_PATTERN:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: pattern defaults must be string values", 0);
+
+ arg->default_value.sfa_pattern =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_BRUSH:
+ {
+ pointer brush_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: brush defaults must be a list", 0);
+
+ brush_list = sc->vptr->pair_car (a);
+ arg->default_value.sfa_brush.name =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (brush_list)));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.opacity =
+ sc->vptr->rvalue (sc->vptr->pair_car (brush_list));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.spacing =
+ sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
+
+ brush_list = sc->vptr->pair_cdr (brush_list);
+ arg->default_value.sfa_brush.paint_mode =
+ sc->vptr->ivalue (sc->vptr->pair_car (brush_list));
+ }
+ break;
+
+ case SF_GRADIENT:
+ if (!sc->vptr->is_string (sc->vptr->pair_car (a)))
+ return foreign_error (sc, "script-fu-register: gradient defaults must be string values", 0);
+
+ arg->default_value.sfa_gradient =
+ g_strdup (sc->vptr->string_value (sc->vptr->pair_car (a)));
+ break;
+
+ case SF_OPTION:
+ {
+ pointer option_list;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: option defaults must be a list", 0);
+
+ for (option_list = sc->vptr->pair_car (a);
+ option_list != sc->NIL;
+ option_list = sc->vptr->pair_cdr (option_list))
+ {
+ arg->default_value.sfa_option.list =
+ g_slist_append (arg->default_value.sfa_option.list,
+ g_strdup (sc->vptr->string_value
+ (sc->vptr->pair_car (option_list))));
+ }
+ }
+ break;
+
+ case SF_ENUM:
+ {
+ pointer option_list;
+ const gchar *val;
+ gchar *type_name;
+ GEnumValue *enum_value;
+ GType enum_type;
+
+ if (!sc->vptr->is_list (sc, a))
+ return foreign_error (sc, "script-fu-register: enum defaults must be a list", 0);
+
+ option_list = sc->vptr->pair_car (a);
+ if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
+ return foreign_error (sc, "script-fu-register: first element in enum defaults must be a type-name", 0);
+
+ val = sc->vptr->string_value (sc->vptr->pair_car (option_list));
+
+ if (g_str_has_prefix (val, "Gimp"))
+ type_name = g_strdup (val);
+ else
+ type_name = g_strconcat ("Gimp", val, NULL);
+
+ enum_type = g_type_from_name (type_name);
+ if (! G_TYPE_IS_ENUM (enum_type))
+ {
+ g_free (type_name);
+ return foreign_error (sc, "script-fu-register: first element in enum defaults must be the name of a registered type", 0);
+ }
+
+ arg->default_value.sfa_enum.type_name = type_name;
+
+ option_list = sc->vptr->pair_cdr (option_list);
+ if (!sc->vptr->is_string (sc->vptr->pair_car (option_list)))
+ return foreign_error (sc, "script-fu-register: second element in enum defaults must be a string", 0);
+
+ enum_value =
+ g_enum_get_value_by_nick (g_type_class_peek (enum_type),
+ sc->vptr->string_value (sc->vptr->pair_car (option_list)));
+ if (enum_value)
+ arg->default_value.sfa_enum.history = enum_value->value;
+ }
+ break;
+ }
+
+ a = sc->vptr->pair_cdr (a);
+ }
+ else
+ {
+ return foreign_error (sc, "script-fu-register: missing default argument", 0);
+ }
+ }
+
+ /* fill all values from defaults */
+ script_fu_script_reset (script, TRUE);
+
+ if (script->menu_label[0] == '<')
+ {
+ gchar *mapped = script_fu_menu_map (script->menu_label);
+
+ if (mapped)
+ {
+ g_free (script->menu_label);
+ script->menu_label = mapped;
+ }
+ }
+
+ {
+ GList *list = g_tree_lookup (script_tree, script->menu_label);
+
+ g_tree_insert (script_tree, (gpointer) script->menu_label,
+ g_list_append (list, script));
+ }
+
+ return sc->NIL;
+}
+
+pointer
+script_fu_add_menu (scheme *sc,
+ pointer a)
+{
+ SFScript *script;
+ SFMenu *menu;
+ const gchar *name;
+ const gchar *path;
+
+ /* Check the length of a */
+ if (sc->vptr->list_length (sc, a) != 2)
+ return foreign_error (sc, "Incorrect number of arguments for script-fu-menu-register", 0);
+
+ /* Find the script PDB entry name */
+ name = sc->vptr->string_value (sc->vptr->pair_car (a));
+ a = sc->vptr->pair_cdr (a);
+
+ script = script_fu_find_script (name);
+
+ if (! script)
+ {
+ g_message ("Procedure %s in script-fu-menu-register does not exist",
+ name);
+ return sc->NIL;
+ }
+
+ /* Create a new list of menus */
+ menu = g_slice_new0 (SFMenu);
+
+ menu->script = script;
+
+ /* Find the script menu path */
+ path = sc->vptr->string_value (sc->vptr->pair_car (a));
+
+ menu->menu_path = script_fu_menu_map (path);
+
+ if (! menu->menu_path)
+ menu->menu_path = g_strdup (path);
+
+ script_menu_list = g_list_prepend (script_menu_list, menu);
+
+ return sc->NIL;
+}
+
+
+/* private functions */
+
+static gboolean
+script_fu_run_command (const gchar *command,
+ GError **error)
+{
+ GString *output;
+ gboolean success = FALSE;
+
+ output = g_string_new (NULL);
+ ts_register_output_func (ts_gstring_output_func, output);
+
+ if (ts_interpret_string (command))
+ {
+ g_set_error (error, 0, 0, "%s", output->str);
+ }
+ else
+ {
+ success = TRUE;
+ }
+
+ g_string_free (output, TRUE);
+
+ return success;
+}
+
+static void
+script_fu_load_directory (GFile *directory)
+{
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children (directory,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (enumerator)
+ {
+ GFileInfo *info;
+
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)))
+ {
+ GFileType file_type = g_file_info_get_file_type (info);
+
+ if ((file_type == G_FILE_TYPE_REGULAR ||
+ file_type == G_FILE_TYPE_DIRECTORY) &&
+ ! g_file_info_get_is_hidden (info))
+ {
+ GFile *child = g_file_enumerator_get_child (enumerator, info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY)
+ script_fu_load_directory (child);
+ else
+ script_fu_load_script (child);
+
+ g_object_unref (child);
+ }
+
+ g_object_unref (info);
+ }
+
+ g_object_unref (enumerator);
+ }
+}
+
+static void
+script_fu_load_script (GFile *file)
+{
+ if (gimp_file_has_extension (file, ".scm"))
+ {
+ gchar *path = g_file_get_path (file);
+ gchar *escaped = script_fu_strescape (path);
+ gchar *command;
+ GError *error = NULL;
+
+ command = g_strdup_printf ("(load \"%s\")", escaped);
+ g_free (escaped);
+
+ if (! script_fu_run_command (command, &error))
+ {
+ gchar *message = g_strdup_printf (_("Error while loading %s:"),
+ gimp_file_get_utf8_name (file));
+
+ g_message ("%s\n\n%s", message, error->message);
+
+ g_clear_error (&error);
+ g_free (message);
+ }
+
+#ifdef G_OS_WIN32
+ /* No, I don't know why, but this is
+ * necessary on NT 4.0.
+ */
+ Sleep (0);
+#endif
+
+ g_free (command);
+ g_free (path);
+ }
+}
+
+/*
+ * The following function is a GTraverseFunction.
+ */
+static gboolean
+script_fu_install_script (gpointer foo G_GNUC_UNUSED,
+ GList *scripts,
+ gpointer bar G_GNUC_UNUSED)
+{
+ GList *list;
+
+ for (list = scripts; list; list = g_list_next (list))
+ {
+ SFScript *script = list->data;
+
+ script_fu_script_install_proc (script, script_fu_script_proc);
+ }
+
+ return FALSE;
+}
+
+static void
+script_fu_install_menu (SFMenu *menu)
+{
+ gimp_plugin_menu_register (menu->script->name, menu->menu_path);
+
+ g_free (menu->menu_path);
+ g_slice_free (SFMenu, menu);
+}
+
+/*
+ * The following function is a GTraverseFunction.
+ */
+static gboolean
+script_fu_remove_script (gpointer foo G_GNUC_UNUSED,
+ GList *scripts,
+ gpointer bar G_GNUC_UNUSED)
+{
+ GList *list;
+
+ for (list = scripts; list; list = g_list_next (list))
+ {
+ SFScript *script = list->data;
+
+ script_fu_script_uninstall_proc (script);
+ script_fu_script_free (script);
+ }
+
+ g_list_free (scripts);
+
+ return FALSE;
+}
+
+static void
+script_fu_script_proc (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2] = { { 0, }, { 0, } };
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ SFScript *script;
+ GError *error = NULL;
+
+ if (values[1].type == GIMP_PDB_STRING && values[1].data.d_string)
+ {
+ g_free (values[1].data.d_string);
+ values[1].data.d_string = NULL;
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+
+ script = script_fu_find_script (name);
+
+ if (! script)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ GimpRunMode run_mode = params[0].data.d_int32;
+
+ ts_set_run_mode (run_mode);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ {
+ gint min_args = 0;
+
+ /* First, try to collect the standard script arguments... */
+ min_args = script_fu_script_collect_standard_args (script,
+ nparams, params);
+
+ /* ...then acquire the rest of arguments (if any) with a dialog */
+ if (script->n_args > min_args)
+ {
+ status = script_fu_interface (script, min_args);
+ break;
+ }
+ /* otherwise (if the script takes no more arguments), skip
+ * this part and run the script directly (fallthrough)
+ */
+ }
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there */
+ if (nparams != (script->n_args + 1))
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gchar *command;
+
+ command = script_fu_script_get_command_from_params (script,
+ params);
+
+ /* run the command through the interpreter */
+ if (! script_fu_run_command (command, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+
+ error->message = NULL;
+ g_error_free (error);
+ }
+
+ g_free (command);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ gchar *command;
+
+ /* First, try to collect the standard script arguments */
+ script_fu_script_collect_standard_args (script, nparams, params);
+
+ command = script_fu_script_get_command (script);
+
+ /* run the command through the interpreter */
+ if (! script_fu_run_command (command, &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+
+ error->message = NULL;
+ g_error_free (error);
+ }
+
+ g_free (command);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ values[0].data.d_status = status;
+}
+
+/* this is a GTraverseFunction */
+static gboolean
+script_fu_lookup_script (gpointer *foo G_GNUC_UNUSED,
+ GList *scripts,
+ gconstpointer *name)
+{
+ GList *list;
+
+ for (list = scripts; list; list = g_list_next (list))
+ {
+ SFScript *script = list->data;
+
+ if (strcmp (script->name, *name) == 0)
+ {
+ /* store the script in the name pointer and stop the traversal */
+ *name = script;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static SFScript *
+script_fu_find_script (const gchar *name)
+{
+ gconstpointer script = name;
+
+ g_tree_foreach (script_tree,
+ (GTraverseFunc) script_fu_lookup_script,
+ &script);
+
+ if (script == name)
+ return NULL;
+
+ return (SFScript *) script;
+}
+
+static gchar *
+script_fu_menu_map (const gchar *menu_path)
+{
+ /* for backward compatibility, we fiddle with some menu paths */
+ const struct
+ {
+ const gchar *old;
+ const gchar *new;
+ } mapping[] = {
+ { "<Image>/Script-Fu/Alchemy", "<Image>/Filters/Artistic" },
+ { "<Image>/Script-Fu/Alpha to Logo", "<Image>/Filters/Alpha to Logo" },
+ { "<Image>/Script-Fu/Animators", "<Image>/Filters/Animation/Animators" },
+ { "<Image>/Script-Fu/Decor", "<Image>/Filters/Decor" },
+ { "<Image>/Script-Fu/Render", "<Image>/Filters/Render" },
+ { "<Image>/Script-Fu/Selection", "<Image>/Select/Modify" },
+ { "<Image>/Script-Fu/Shadow", "<Image>/Filters/Light and Shadow/Shadow" },
+ { "<Image>/Script-Fu/Stencil Ops", "<Image>/Filters/Decor" }
+ };
+
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (mapping); i++)
+ {
+ if (g_str_has_prefix (menu_path, mapping[i].old))
+ {
+ const gchar *suffix = menu_path + strlen (mapping[i].old);
+
+ if (*suffix != '/')
+ continue;
+
+ return g_strconcat (mapping[i].new, suffix, NULL);
+ }
+ }
+
+ return NULL;
+}
+
+static gint
+script_fu_menu_compare (gconstpointer a,
+ gconstpointer b)
+{
+ const SFMenu *menu_a = a;
+ const SFMenu *menu_b = b;
+ gint retval = 0;
+
+ if (menu_a->menu_path && menu_b->menu_path)
+ {
+ retval = g_utf8_collate (menu_a->menu_path,
+ menu_b->menu_path);
+
+ if (retval == 0 &&
+ menu_a->script->menu_label && menu_b->script->menu_label)
+ {
+ retval = g_utf8_collate (menu_a->script->menu_label,
+ menu_b->script->menu_label);
+ }
+ }
+
+ return retval;
+}
diff --git a/plug-ins/script-fu/script-fu-scripts.h b/plug-ins/script-fu/script-fu-scripts.h
new file mode 100644
index 0000000..ef63870
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-scripts.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_SCRIPTS_H__
+#define __SCRIPT_FU_SCRIPTS_H__
+
+
+void script_fu_find_scripts (GList *path);
+pointer script_fu_add_script (scheme *sc,
+ pointer a);
+pointer script_fu_add_menu (scheme *sc,
+ pointer a);
+
+
+#endif /* __SCRIPT_FU_SCRIPTS__ */
diff --git a/plug-ins/script-fu/script-fu-server.c b/plug-ins/script-fu/script-fu-server.c
new file mode 100644
index 0000000..74270bf
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-server.c
@@ -0,0 +1,1113 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <sys/types.h>
+
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0502
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+typedef short sa_family_t; /* Not defined by winsock */
+
+#ifndef AI_ADDRCONFIG
+/* Missing from mingw headers, but value is publicly documented
+ * on http://msdn.microsoft.com/en-us/library/ms737530%28v=VS.85%29.aspx
+ */
+#define AI_ADDRCONFIG 0x0400
+#endif
+#include <libgimpbase/gimpwin32-io.h>
+#else
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+#endif
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "script-fu-intl.h"
+
+#include "scheme-wrapper.h"
+#include "script-fu-server.h"
+
+#ifdef G_OS_WIN32
+#define CLOSESOCKET(fd) closesocket(fd)
+#else
+#define CLOSESOCKET(fd) close(fd)
+#endif
+
+#define COMMAND_HEADER 3
+#define RESPONSE_HEADER 4
+#define MAGIC 'G'
+
+#ifndef HAVE_DIFFTIME
+#define difftime(a,b) (((gdouble)(a)) - ((gdouble)(b)))
+#endif
+
+#ifndef NO_FD_SET
+# define SELECT_MASK fd_set
+#else
+# ifndef _AIX
+ typedef long fd_mask;
+# endif
+# if defined(_IBMR2)
+# define SELECT_MASK void
+# else
+# define SELECT_MASK int
+# endif
+#endif
+
+
+/* image information */
+
+/* Header format for incoming commands...
+ * bytes: 1 2 3
+ * MAGIC CMD_LEN_H CMD_LEN_L
+ */
+
+/* Header format for outgoing responses...
+ * bytes: 1 2 3 4
+ * MAGIC ERROR? RSP_LEN_H RSP_LEN_L
+ */
+
+#define MAGIC_BYTE 0
+
+#define CMD_LEN_H_BYTE 1
+#define CMD_LEN_L_BYTE 2
+
+#define ERROR_BYTE 1
+#define RSP_LEN_H_BYTE 2
+#define RSP_LEN_L_BYTE 3
+
+/*
+ * Local Types
+ */
+
+typedef struct
+{
+ gchar *command;
+ gint filedes;
+ gint request_no;
+} SFCommand;
+
+typedef struct
+{
+ GtkWidget *ip_entry;
+ GtkWidget *port_entry;
+ GtkWidget *log_entry;
+
+ gchar *listen_ip;
+ gint port;
+ gchar *logfile;
+
+ gboolean run;
+} ServerInterface;
+
+typedef union
+{
+ sa_family_t family;
+ struct sockaddr_storage ss;
+ struct sockaddr sa;
+ struct sockaddr_in sa_in;
+ struct sockaddr_in6 sa_in6;
+} sa_union;
+
+/*
+ * Local Functions
+ */
+
+static void server_start (const gchar *listen_ip,
+ gint port,
+ const gchar *logfile);
+static gboolean execute_command (SFCommand *cmd);
+static gint read_from_client (gint filedes);
+static gint make_socket (const struct addrinfo
+ *ai);
+static void server_log (const gchar *format,
+ ...) G_GNUC_PRINTF (1, 2);
+static void server_quit (void);
+
+static gboolean server_interface (void);
+static void response_callback (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void print_socket_api_error (const gchar *api_name);
+
+/*
+ * Local variables
+ */
+static gint server_socks[2],
+ server_socks_used = 0;
+static const gint server_socks_len = sizeof (server_socks) /
+ sizeof (server_socks[0]);
+static GList *command_queue = NULL;
+static gint queue_length = 0;
+static gint request_no = 0;
+static FILE *server_log_file = NULL;
+static GHashTable *clients = NULL;
+static gboolean script_fu_done = FALSE;
+static gboolean server_mode = FALSE;
+
+static ServerInterface sint =
+{
+ NULL, /* port entry widget */
+ NULL, /* log entry widget */
+ NULL, /* ip entry widget */
+
+ NULL, /* ip to bind to */
+ 10008, /* default port number */
+ NULL, /* use stdout */
+
+ FALSE /* run */
+};
+
+/*
+ * Server interface functions
+ */
+
+void
+script_fu_server_quit (void)
+{
+ script_fu_done = TRUE;
+}
+
+gint
+script_fu_server_get_mode (void)
+{
+ return server_mode;
+}
+
+void
+script_fu_server_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpRunMode run_mode;
+
+ run_mode = params[0].data.d_int32;
+
+ ts_set_run_mode (run_mode);
+ ts_set_print_flag (1);
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (server_interface ())
+ {
+ server_mode = TRUE;
+
+ /* Start the server */
+ server_start (sint.listen_ip, sint.port, sint.logfile);
+ }
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Set server_mode to TRUE */
+ server_mode = TRUE;
+
+ /* Start the server */
+ server_start ((params[1].data.d_string &&
+ strlen (params[1].data.d_string)) ?
+ params[1].data.d_string : "127.0.0.1",
+ params[2].data.d_int32,
+ params[3].data.d_string);
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ status = GIMP_PDB_CALLING_ERROR;
+ g_warning ("Script-Fu server does not handle \"GIMP_RUN_WITH_LAST_VALS\"");
+
+ default:
+ break;
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
+
+static void
+script_fu_server_add_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ FD_SET (GPOINTER_TO_INT (key), (SELECT_MASK *) data);
+}
+
+static gboolean
+script_fu_server_read_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ gint fd = GPOINTER_TO_INT (key);
+
+ if (FD_ISSET (fd, (SELECT_MASK *) data))
+ {
+ if (read_from_client (fd) < 0)
+ {
+ GList *list;
+
+ server_log ("Server: disconnect from host %s.\n", (gchar *) value);
+
+ CLOSESOCKET (fd);
+
+ /* Invalidate the file descriptor for pending commands
+ from the disconnected client. */
+ for (list = command_queue; list; list = list->next)
+ {
+ SFCommand *cmd = (SFCommand *) command_queue->data;
+
+ if (cmd->filedes == fd)
+ cmd->filedes = -1;
+ }
+
+ return TRUE; /* remove this client from the hash table */
+ }
+ }
+
+ return FALSE;
+}
+
+void
+script_fu_server_listen (gint timeout)
+{
+ struct timeval tv;
+ struct timeval *tvp = NULL;
+ SELECT_MASK fds;
+ gint sockno;
+
+ /* Set time struct */
+ if (timeout)
+ {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = timeout % 1000;
+ tvp = &tv;
+ }
+
+ FD_ZERO (&fds);
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ FD_SET (server_socks[sockno], &fds);
+ }
+ g_hash_table_foreach (clients, script_fu_server_add_fd, &fds);
+
+ /* Block until input arrives on one or more active sockets
+ or timeout occurs. */
+
+ if (select (FD_SETSIZE, &fds, NULL, NULL, tvp) < 0)
+ {
+ print_socket_api_error ("select");
+ return;
+ }
+
+ /* Service the server sockets if any has input pending. */
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ sa_union client;
+ gchar clientname[NI_MAXHOST];
+
+ /* Connection request on original socket. */
+ socklen_t size = sizeof (client);
+ gint new;
+ guint portno;
+
+ if (! FD_ISSET (server_socks[sockno], &fds))
+ {
+ continue;
+ }
+
+ new = accept (server_socks[sockno], &(client.sa), &size);
+
+ if (new < 0)
+ {
+ print_socket_api_error ("accept");
+ return;
+ }
+
+ /* Associate the client address with the socket */
+
+ /* If all else fails ... */
+ strncpy (clientname, "(error during host address lookup)", NI_MAXHOST-1);
+
+ /* Lookup address */
+ (void) getnameinfo (&(client.sa), size, clientname, sizeof (clientname),
+ NULL, 0, NI_NUMERICHOST);
+
+ g_hash_table_insert (clients, GINT_TO_POINTER (new),
+ g_strdup (clientname));
+
+ /* Determine port number */
+ switch (client.family)
+ {
+ case AF_INET:
+ portno = (guint) g_ntohs (client.sa_in.sin_port);
+ break;
+ case AF_INET6:
+ portno = (guint) g_ntohs (client.sa_in6.sin6_port);
+ break;
+ default:
+ portno = 0;
+ }
+
+ server_log ("Server: connect from host %s, port %d.\n",
+ clientname, portno);
+ }
+
+ /* Service the client sockets. */
+ g_hash_table_foreach_remove (clients, script_fu_server_read_fd, &fds);
+}
+
+static void
+server_progress_start (const gchar *message,
+ gboolean cancelable,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_end (gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_set_text (const gchar *message,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+server_progress_set_value (gdouble percentage,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+
+/*
+ * Suppress progress popups by installing progress handlers that do nothing.
+ */
+static const gchar *
+server_progress_install (void)
+{
+ GimpProgressVtable vtable = { 0, };
+
+ vtable.start = server_progress_start;
+ vtable.end = server_progress_end;
+ vtable.set_text = server_progress_set_text;
+ vtable.set_value = server_progress_set_value;
+
+ return gimp_progress_install_vtable (&vtable, NULL);
+}
+
+static void
+server_progress_uninstall (const gchar *progress)
+{
+ gimp_progress_uninstall (progress);
+}
+
+static void
+server_start (const gchar *listen_ip,
+ gint port,
+ const gchar *logfile)
+{
+ struct addrinfo *ai;
+ struct addrinfo *ai_curr;
+ struct addrinfo hints;
+ gint e;
+ gint sockno;
+ gchar *port_s;
+ const gchar *progress;
+
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ hints.ai_socktype = SOCK_STREAM;
+
+ port_s = g_strdup_printf ("%d", port);
+ e = getaddrinfo (listen_ip, port_s, &hints, &ai);
+ g_free (port_s);
+
+ if (e != 0)
+ {
+ g_printerr ("getaddrinfo: %s\n", gai_strerror (e));
+ return;
+ }
+
+ for (ai_curr = ai, sockno = 0;
+ ai_curr != NULL && sockno < server_socks_len;
+ ai_curr = ai_curr->ai_next, sockno++)
+ {
+ /* Create the socket and set it up to accept connections. */
+ /* This may fail if there's a server running on this port already. */
+ server_socks[sockno] = make_socket (ai_curr);
+
+ if (listen (server_socks[sockno], 5) < 0)
+ {
+ print_socket_api_error ("listen");
+ freeaddrinfo (ai);
+ return;
+ }
+ }
+
+ server_socks_used = sockno;
+
+ /* Setup up the server log file */
+ if (logfile && *logfile)
+ server_log_file = g_fopen (logfile, "a");
+ else
+ server_log_file = NULL;
+
+ if (! server_log_file)
+ server_log_file = stdout;
+
+ /* Set up the clientname hash table */
+ clients = g_hash_table_new_full (g_direct_hash, NULL,
+ NULL, (GDestroyNotify) g_free);
+
+ progress = server_progress_install ();
+
+ server_log ("Script-Fu server initialized and listening...\n");
+
+ /* Loop until the server is finished */
+ while (! script_fu_done)
+ {
+ script_fu_server_listen (0);
+
+ while (command_queue)
+ {
+ SFCommand *cmd = (SFCommand *) command_queue->data;
+
+ /* Process the command */
+ execute_command (cmd);
+
+ /* Remove the command from the list */
+ command_queue = g_list_remove (command_queue, cmd);
+ queue_length--;
+
+ /* Free the request */
+ g_free (cmd->command);
+ g_free (cmd);
+ }
+ }
+
+ server_progress_uninstall (progress);
+
+ freeaddrinfo (ai);
+ server_quit ();
+}
+
+static gboolean
+execute_command (SFCommand *cmd)
+{
+ guchar buffer[RESPONSE_HEADER];
+ GString *response;
+ time_t clocknow;
+ gboolean error;
+ gint i;
+ gdouble total_time;
+ GTimer *timer;
+
+ server_log ("Processing request #%d\n", cmd->request_no);
+ timer = g_timer_new ();
+
+ response = g_string_new (NULL);
+ ts_register_output_func (ts_gstring_output_func, response);
+
+ /* run the command */
+ if (ts_interpret_string (cmd->command) != 0)
+ {
+ error = TRUE;
+
+ server_log ("%s\n", response->str);
+ }
+ else
+ {
+ error = FALSE;
+
+ if (response->len == 0)
+ g_string_assign (response, ts_get_success_msg ());
+
+ total_time = g_timer_elapsed (timer, NULL);
+ time (&clocknow);
+ server_log ("Request #%d processed in %.3f seconds, finishing on %s",
+ cmd->request_no, total_time, ctime (&clocknow));
+ }
+ g_timer_destroy (timer);
+
+ buffer[MAGIC_BYTE] = MAGIC;
+ buffer[ERROR_BYTE] = error ? TRUE : FALSE;
+ buffer[RSP_LEN_H_BYTE] = (guchar) (response->len >> 8);
+ buffer[RSP_LEN_L_BYTE] = (guchar) (response->len & 0xFF);
+
+ /* Write the response to the client */
+ for (i = 0; i < RESPONSE_HEADER; i++)
+ if (cmd->filedes > 0 && send (cmd->filedes, (const void *) (buffer + i), 1, 0) < 0)
+ {
+ /* Write error */
+ print_socket_api_error ("send");
+ return FALSE;
+ }
+
+ for (i = 0; i < response->len; i++)
+ if (cmd->filedes > 0 && send (cmd->filedes, response->str + i, 1, 0) < 0)
+ {
+ /* Write error */
+ print_socket_api_error ("send");
+ return FALSE;
+ }
+
+ g_string_free (response, TRUE);
+
+ return FALSE;
+}
+
+static gint
+read_from_client (gint filedes)
+{
+ SFCommand *cmd;
+ guchar buffer[COMMAND_HEADER];
+ gchar *command;
+ gchar *clientaddr;
+ time_t clock;
+ gint command_len;
+ gint nbytes;
+ gint i;
+
+ for (i = 0; i < COMMAND_HEADER;)
+ {
+ nbytes = recv (filedes, (void *) (buffer + i), COMMAND_HEADER - i, 0);
+
+ if (nbytes < 0)
+ {
+#ifndef G_OS_WIN32
+ if (errno == EINTR)
+ continue;
+#endif
+ server_log ("Error reading command header.\n");
+ return -1;
+ }
+
+ if (nbytes == 0)
+ return -1; /* EOF */
+
+ i += nbytes;
+ }
+
+ if (buffer[MAGIC_BYTE] != MAGIC)
+ {
+ server_log ("Error in script-fu command transmission.\n");
+ return -1;
+ }
+
+ command_len = (buffer [CMD_LEN_H_BYTE] << 8) | buffer [CMD_LEN_L_BYTE];
+ command = g_new (gchar, command_len + 1);
+
+ for (i = 0; i < command_len;)
+ {
+ nbytes = recv (filedes, command + i, command_len - i, 0);
+
+ if (nbytes <= 0)
+ {
+#ifndef G_OS_WIN32
+ if (nbytes < 0 && errno == EINTR)
+ continue;
+#endif
+ server_log ("Error reading command. Read %d out of %d bytes.\n",
+ i, command_len);
+ g_free (command);
+ return -1;
+ }
+
+ i += nbytes;
+ }
+
+ command[command_len] = '\0';
+ cmd = g_new (SFCommand, 1);
+
+ cmd->filedes = filedes;
+ cmd->command = command;
+ cmd->request_no = request_no ++;
+
+ /* Add the command to the queue */
+ command_queue = g_list_append (command_queue, cmd);
+ queue_length ++;
+
+ /* Get the client address from the address/socket table */
+ clientaddr = g_hash_table_lookup (clients, GINT_TO_POINTER (cmd->filedes));
+ time (&clock);
+ server_log ("Received request #%d from IP address %s: %s on %s,"
+ "[Request queue length: %d]",
+ cmd->request_no,
+ clientaddr ? clientaddr : "<invalid>",
+ cmd->command, ctime (&clock), queue_length);
+
+ return 0;
+}
+
+static gint
+make_socket (const struct addrinfo *ai)
+{
+ gint sock;
+ gint v = 1;
+
+ /* Win32 needs the winsock library initialized. */
+#ifdef G_OS_WIN32
+ static gboolean winsock_initialized = FALSE;
+
+ if (! winsock_initialized)
+ {
+ WORD wVersionRequested = MAKEWORD (2, 2);
+ WSADATA wsaData;
+
+ if (WSAStartup (wVersionRequested, &wsaData) == 0)
+ {
+ winsock_initialized = TRUE;
+ }
+ else
+ {
+ print_socket_api_error ("WSAStartup");
+ gimp_quit ();
+ }
+ }
+#endif
+
+ /* Create the socket. */
+ sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0)
+ {
+ print_socket_api_error ("socket");
+ gimp_quit ();
+ }
+
+ setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const void *) &v, sizeof(v));
+
+#ifdef IPV6_V6ONLY
+ /* Only listen on IPv6 addresses, otherwise bind() will fail. */
+ if (ai->ai_family == AF_INET6)
+ {
+ v = 1;
+ if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &v, sizeof(v)) < 0)
+ {
+ print_socket_api_error ("setsockopt");
+ gimp_quit();
+ }
+ }
+#endif
+
+ if (bind (sock, ai->ai_addr, ai->ai_addrlen) < 0)
+ {
+ print_socket_api_error ("bind");
+ gimp_quit ();
+ }
+
+ return sock;
+}
+
+static void
+server_log (const gchar *format,
+ ...)
+{
+ va_list args;
+ gchar *buf;
+
+ va_start (args, format);
+ buf = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ fputs (buf, server_log_file);
+ g_free (buf);
+
+ if (server_log_file != stdout)
+ fflush (server_log_file);
+}
+
+static void
+script_fu_server_shutdown_fd (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ shutdown (GPOINTER_TO_INT (key), 2);
+}
+
+static void
+server_quit (void)
+{
+ gint sockno;
+
+ for (sockno = 0; sockno < server_socks_used; sockno++)
+ {
+ CLOSESOCKET (server_socks[sockno]);
+ }
+
+ if (clients)
+ {
+ g_hash_table_foreach (clients, script_fu_server_shutdown_fd, NULL);
+ g_hash_table_destroy (clients);
+ clients = NULL;
+ }
+
+ while (command_queue)
+ {
+ SFCommand *cmd = command_queue->data;
+
+ g_free (cmd->command);
+ g_free (cmd);
+ }
+
+ g_list_free (command_queue);
+ command_queue = NULL;
+ queue_length = 0;
+
+ /* Close the server log file */
+ if (server_log_file != stdout)
+ fclose (server_log_file);
+
+ server_log_file = NULL;
+}
+
+static gboolean
+server_interface (void)
+{
+ GtkWidget *dlg;
+ GtkWidget *main_vbox;
+ GtkWidget *table;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *label;
+
+ INIT_I18N();
+
+ gimp_ui_init ("script-fu", FALSE);
+
+ dlg = gimp_dialog_new (_("Script-Fu Server Options"), "gimp-script-fu",
+ NULL, 0,
+ gimp_standard_help_func, "plug-in-script-fu-server",
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Start Server"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (response_callback),
+ NULL);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ /* The table to hold port, logfile and listen-to entries */
+ table = gtk_table_new (3, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ /* The server ip to listen to */
+ sint.ip_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (sint.ip_entry), "127.0.0.1");
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
+ _("Listen on IP:"), 0.0, 0.5,
+ sint.ip_entry, 1, FALSE);
+
+ /* The server port */
+ sint.port_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (sint.port_entry), "10008");
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
+ _("Server port:"), 0.0, 0.5,
+ sint.port_entry, 1, FALSE);
+
+ /* The server logfile */
+ sint.log_entry = gtk_entry_new ();
+ gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
+ _("Server logfile:"), 0.0, 0.5,
+ sint.log_entry, 1, FALSE);
+
+ /* Warning */
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_WARNING,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
+ gtk_widget_show (image);
+
+ label = gtk_label_new (_("Listening on an IP address other than "
+ "127.0.0.1 (especially 0.0.0.0) can allow "
+ "attackers to remotely execute arbitrary code "
+ "on this machine."));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gimp_label_set_attributes (GTK_LABEL (label),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ gtk_widget_show (dlg);
+
+ gtk_main ();
+
+ return sint.run;
+}
+
+static void
+response_callback (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ g_free (sint.logfile);
+ g_free (sint.listen_ip);
+
+ sint.port = atoi (gtk_entry_get_text (GTK_ENTRY (sint.port_entry)));
+ sint.logfile = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.log_entry)));
+ sint.listen_ip = g_strdup (gtk_entry_get_text (GTK_ENTRY (sint.ip_entry)));
+ sint.run = TRUE;
+ }
+
+ gtk_widget_destroy (widget);
+}
+
+static void
+print_socket_api_error (const gchar *api_name)
+{
+#ifdef G_OS_WIN32
+ /* Yes, this functionality really belongs to GLib. */
+ const gchar *emsg;
+ gchar unk[100];
+ int number = WSAGetLastError ();
+
+ switch (number)
+ {
+ case WSAEINTR:
+ emsg = "Interrupted function call";
+ break;
+ case WSAEACCES:
+ emsg = "Permission denied";
+ break;
+ case WSAEFAULT:
+ emsg = "Bad address";
+ break;
+ case WSAEINVAL:
+ emsg = "Invalid argument";
+ break;
+ case WSAEMFILE:
+ emsg = "Too many open sockets";
+ break;
+ case WSAEWOULDBLOCK:
+ emsg = "Resource temporarily unavailable";
+ break;
+ case WSAEINPROGRESS:
+ emsg = "Operation now in progress";
+ break;
+ case WSAEALREADY:
+ emsg = "Operation already in progress";
+ break;
+ case WSAENOTSOCK:
+ emsg = "Socket operation on nonsocket";
+ break;
+ case WSAEDESTADDRREQ:
+ emsg = "Destination address required";
+ break;
+ case WSAEMSGSIZE:
+ emsg = "Message too long";
+ break;
+ case WSAEPROTOTYPE:
+ emsg = "Protocol wrong type for socket";
+ break;
+ case WSAENOPROTOOPT:
+ emsg = "Bad protocol option";
+ break;
+ case WSAEPROTONOSUPPORT:
+ emsg = "Protocol not supported";
+ break;
+ case WSAESOCKTNOSUPPORT:
+ emsg = "Socket type not supported";
+ break;
+ case WSAEOPNOTSUPP:
+ emsg = "Operation not supported on transport endpoint";
+ break;
+ case WSAEPFNOSUPPORT:
+ emsg = "Protocol family not supported";
+ break;
+ case WSAEAFNOSUPPORT:
+ emsg = "Address family not supported by protocol family";
+ break;
+ case WSAEADDRINUSE:
+ emsg = "Address already in use";
+ break;
+ case WSAEADDRNOTAVAIL:
+ emsg = "Address not available";
+ break;
+ case WSAENETDOWN:
+ emsg = "Network interface is not configured";
+ break;
+ case WSAENETUNREACH:
+ emsg = "Network is unreachable";
+ break;
+ case WSAENETRESET:
+ emsg = "Network dropped connection on reset";
+ break;
+ case WSAECONNABORTED:
+ emsg = "Software caused connection abort";
+ break;
+ case WSAECONNRESET:
+ emsg = "Connection reset by peer";
+ break;
+ case WSAENOBUFS:
+ emsg = "No buffer space available";
+ break;
+ case WSAEISCONN:
+ emsg = "Socket is already connected";
+ break;
+ case WSAENOTCONN:
+ emsg = "Socket is not connected";
+ break;
+ case WSAESHUTDOWN:
+ emsg = "Can't send after socket shutdown";
+ break;
+ case WSAETIMEDOUT:
+ emsg = "Connection timed out";
+ break;
+ case WSAECONNREFUSED:
+ emsg = "Connection refused";
+ break;
+ case WSAEHOSTDOWN:
+ emsg = "Host is down";
+ break;
+ case WSAEHOSTUNREACH:
+ emsg = "Host is unreachable";
+ break;
+ case WSAEPROCLIM:
+ emsg = "Too many processes";
+ break;
+ case WSASYSNOTREADY:
+ emsg = "Network subsystem is unavailable";
+ break;
+ case WSAVERNOTSUPPORTED:
+ emsg = "Winsock.dll version out of range";
+ break;
+ case WSANOTINITIALISED:
+ emsg = "Successful WSAStartup not yet performed";
+ break;
+ case WSAEDISCON:
+ emsg = "Graceful shutdown in progress";
+ break;
+ case WSATYPE_NOT_FOUND:
+ emsg = "Class type not found";
+ break;
+ case WSAHOST_NOT_FOUND:
+ emsg = "Host not found";
+ break;
+ case WSATRY_AGAIN:
+ emsg = "Nonauthoritative host not found";
+ break;
+ case WSANO_RECOVERY:
+ emsg = "This is a nonrecoverable error";
+ break;
+ case WSANO_DATA:
+ emsg = "Valid name, no data record of requested type";
+ break;
+ case WSA_INVALID_HANDLE:
+ emsg = "Specified event object handle is invalid";
+ break;
+ case WSA_INVALID_PARAMETER:
+ emsg = "One or more parameters are invalid";
+ break;
+ case WSA_IO_INCOMPLETE:
+ emsg = "Overlapped I/O event object not in signaled state";
+ break;
+ case WSA_NOT_ENOUGH_MEMORY:
+ emsg = "Insufficient memory available";
+ break;
+ case WSA_OPERATION_ABORTED:
+ emsg = "Overlapped operation aborted";
+ break;
+ case WSAEINVALIDPROCTABLE:
+ emsg = "Invalid procedure table from service provider";
+ break;
+ case WSAEINVALIDPROVIDER:
+ emsg = "Invalid service provider version number";
+ break;
+ case WSAEPROVIDERFAILEDINIT:
+ emsg = "Unable to initialize a service provider";
+ break;
+ case WSASYSCALLFAILURE:
+ emsg = "System call failure";
+ break;
+ default:
+ sprintf (unk, "Unknown WinSock error %d", number);
+ emsg = unk;
+ break;
+ }
+
+ g_printerr ("%s failed: %s\n", api_name, emsg);
+#else
+ perror (api_name);
+#endif
+}
diff --git a/plug-ins/script-fu/script-fu-server.h b/plug-ins/script-fu/script-fu-server.h
new file mode 100644
index 0000000..96b88ee
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-server.h
@@ -0,0 +1,32 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_SERVER_H__
+#define __SCRIPT_FU_SERVER_H__
+
+
+void script_fu_server_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+void script_fu_server_listen (gint timeout);
+gint script_fu_server_get_mode (void);
+void script_fu_server_quit (void);
+
+
+#endif /* __SCRIPT_FU_SERVER__ */
diff --git a/plug-ins/script-fu/script-fu-text-console.c b/plug-ins/script-fu/script-fu-text-console.c
new file mode 100644
index 0000000..94c8d7c
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-text-console.c
@@ -0,0 +1,60 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "libgimp/gimp.h"
+
+#include "scheme-wrapper.h"
+#include "script-fu-text-console.h"
+
+#include "script-fu-intl.h"
+
+void
+script_fu_text_console_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+
+ /* Enable Script-Fu output */
+ ts_register_output_func (ts_stdout_output_func, NULL);
+
+ ts_print_welcome ();
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_PLUGIN);
+
+ /* Run the interface */
+ ts_interpret_stdin ();
+
+ gimp_plugin_set_pdb_error_handler (GIMP_PDB_ERROR_HANDLER_INTERNAL);
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+}
diff --git a/plug-ins/script-fu/script-fu-text-console.h b/plug-ins/script-fu/script-fu-text-console.h
new file mode 100644
index 0000000..83c9860
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-text-console.h
@@ -0,0 +1,29 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_TEXT_CONSOLE_H__
+#define __SCRIPT_FU_TEXT_CONSOLE_H__
+
+
+void script_fu_text_console_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+#endif /* __SCRIPT_FU_TEXT_CONSOLE_H__ */
diff --git a/plug-ins/script-fu/script-fu-types.h b/plug-ins/script-fu/script-fu-types.h
new file mode 100644
index 0000000..f0231b5
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-types.h
@@ -0,0 +1,107 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_TYPES_H__
+#define __SCRIPT_FU_TYPES_H__
+
+
+#include "script-fu-enums.h"
+
+
+typedef struct
+{
+ GtkAdjustment *adj;
+ gdouble value;
+ gdouble lower;
+ gdouble upper;
+ gdouble step;
+ gdouble page;
+ gint digits;
+ SFAdjustmentType type;
+} SFAdjustment;
+
+typedef struct
+{
+ gchar *filename;
+} SFFilename;
+
+typedef struct
+{
+ gchar *name;
+ gdouble opacity;
+ gint spacing;
+ GimpLayerMode paint_mode;
+} SFBrush;
+
+typedef struct
+{
+ GSList *list;
+ gint history;
+} SFOption;
+
+typedef struct
+{
+ gchar *type_name;
+ gint history;
+} SFEnum;
+
+typedef union
+{
+ gint32 sfa_image;
+ gint32 sfa_drawable;
+ gint32 sfa_layer;
+ gint32 sfa_channel;
+ gint32 sfa_vectors;
+ gint32 sfa_display;
+ GimpRGB sfa_color;
+ gint32 sfa_toggle;
+ gchar *sfa_value;
+ SFAdjustment sfa_adjustment;
+ SFFilename sfa_file;
+ gchar *sfa_font;
+ gchar *sfa_gradient;
+ gchar *sfa_palette;
+ gchar *sfa_pattern;
+ SFBrush sfa_brush;
+ SFOption sfa_option;
+ SFEnum sfa_enum;
+} SFArgValue;
+
+typedef struct
+{
+ SFArgType type;
+ gchar *label;
+ SFArgValue default_value;
+ SFArgValue value;
+} SFArg;
+
+typedef struct
+{
+ gchar *name;
+ gchar *menu_label;
+ gchar *blurb;
+ gchar *author;
+ gchar *copyright;
+ gchar *date;
+ gchar *image_types;
+
+ gint n_args;
+ SFArg *args;
+} SFScript;
+
+
+#endif /* __SCRIPT_FU_TYPES__ */
diff --git a/plug-ins/script-fu/script-fu-utils.c b/plug-ins/script-fu/script-fu-utils.c
new file mode 100644
index 0000000..3e068cc
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-utils.c
@@ -0,0 +1,69 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "script-fu-utils.h"
+
+
+/*
+ * Escapes the special characters '\b', '\f', '\n', '\r', '\t', '\' and '"'
+ * in the string source by inserting a '\' before them.
+ */
+gchar *
+script_fu_strescape (const gchar *source)
+{
+ const guchar *p;
+ gchar *dest;
+ gchar *q;
+
+ g_return_val_if_fail (source != NULL, NULL);
+
+ p = (const guchar *) source;
+
+ /* Each source byte needs maximally two destination chars */
+ q = dest = g_malloc (strlen (source) * 2 + 1);
+
+ while (*p)
+ {
+ switch (*p)
+ {
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\\':
+ case '"':
+ *q++ = '\\';
+ /* fallthrough */
+ default:
+ *q++ = *p;
+ break;
+ }
+
+ p++;
+ }
+
+ *q = 0;
+
+ return dest;
+}
diff --git a/plug-ins/script-fu/script-fu-utils.h b/plug-ins/script-fu/script-fu-utils.h
new file mode 100644
index 0000000..a62a226
--- /dev/null
+++ b/plug-ins/script-fu/script-fu-utils.h
@@ -0,0 +1,25 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCRIPT_FU_UTILS_H__
+#define __SCRIPT_FU_UTILS_H__
+
+
+gchar * script_fu_strescape (const gchar *source);
+
+
+#endif /* __SCRIPT_FU_UTILS__ */
diff --git a/plug-ins/script-fu/script-fu.c b/plug-ins/script-fu/script-fu.c
new file mode 100644
index 0000000..558e15a
--- /dev/null
+++ b/plug-ins/script-fu/script-fu.c
@@ -0,0 +1,385 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "tinyscheme/scheme.h"
+
+#include "script-fu-types.h"
+
+#include "script-fu-console.h"
+#include "script-fu-eval.h"
+#include "script-fu-interface.h"
+#include "script-fu-scripts.h"
+#include "script-fu-server.h"
+#include "script-fu-text-console.h"
+
+#include "scheme-wrapper.h"
+
+#include "script-fu-intl.h"
+
+
+/* Declare local functions. */
+
+static void script_fu_query (void);
+static void script_fu_run (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static GList * script_fu_search_path (void);
+static void script_fu_extension_init (void);
+static void script_fu_refresh_proc (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ script_fu_query, /* query_proc */
+ script_fu_run /* run_proc */
+};
+
+
+MAIN ()
+
+
+static void
+script_fu_query (void)
+{
+ static const GimpParamDef console_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }
+ };
+
+ static const GimpParamDef textconsole_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0) }" }
+ };
+
+ static const GimpParamDef eval_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "code", "The code to evaluate" }
+ };
+
+ static const GimpParamDef server_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "ip", "The ip on which to listen for requests" },
+ { GIMP_PDB_INT32, "port", "The port on which to listen for requests" },
+ { GIMP_PDB_STRING, "logfile", "The file to log server activity to" }
+ };
+
+ gimp_plugin_domain_register (GETTEXT_PACKAGE "-script-fu", NULL);
+
+ gimp_install_procedure ("extension-script-fu",
+ "A scheme interpreter for scripting GIMP operations",
+ "More help here later",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ NULL,
+ NULL,
+ GIMP_EXTENSION,
+ 0, 0, NULL, NULL);
+
+ gimp_install_procedure ("plug-in-script-fu-console",
+ N_("Interactive console for Script-Fu development"),
+ "Provides an interface which allows interactive "
+ "scheme development.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("_Console"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (console_args), 0,
+ console_args, NULL);
+
+ gimp_plugin_menu_register ("plug-in-script-fu-console",
+ "<Image>/Filters/Languages/Script-Fu");
+
+ gimp_install_procedure ("plug-in-script-fu-text-console",
+ "Provides a text console mode for script-fu "
+ "development",
+ "Provides an interface which allows interactive "
+ "scheme development.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (textconsole_args), 0,
+ textconsole_args, NULL);
+
+ gimp_install_procedure ("plug-in-script-fu-server",
+ N_("Server for remote Script-Fu operation"),
+ "Provides a server for remote script-fu operation. "
+ "NOTE that for security reasons this procedure's "
+ "API was changed in an incompatible way since "
+ "GIMP 2.8.12. You now have to pass the IP to listen "
+ "on as first parameter. Calling this procedure with "
+ "the old API will fail on purpose.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("_Start Server..."),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (server_args), 0,
+ server_args, NULL);
+
+ gimp_plugin_menu_register ("plug-in-script-fu-server",
+ "<Image>/Filters/Languages/Script-Fu");
+
+ gimp_install_procedure ("plug-in-script-fu-eval",
+ "Evaluate scheme code",
+ "Evaluate the code under the scheme interpreter "
+ "(primarily for batch mode)",
+ "Manish Singh",
+ "Manish Singh",
+ "1998",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (eval_args), 0,
+ eval_args, NULL);
+}
+
+static void
+script_fu_run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GList *path;
+
+ INIT_I18N();
+
+ path = script_fu_search_path ();
+
+ /* Determine before we allow scripts to register themselves
+ * whether this is the base, automatically installed script-fu extension
+ */
+ if (strcmp (name, "extension-script-fu") == 0)
+ {
+ /* Setup auxiliary temporary procedures for the base extension */
+ script_fu_extension_init ();
+
+ /* Init the interpreter and register scripts */
+ tinyscheme_init (path, TRUE);
+ }
+ else
+ {
+ /* Init the interpreter */
+ tinyscheme_init (path, FALSE);
+ }
+
+ if (param != NULL)
+ ts_set_run_mode ((GimpRunMode) param[0].data.d_int32);
+
+ /* Load all of the available scripts */
+ script_fu_find_scripts (path);
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ if (strcmp (name, "extension-script-fu") == 0)
+ {
+ /*
+ * The main script-fu extension.
+ */
+
+ static GimpParam values[1];
+
+ /* Acknowledge that the extension is properly initialized */
+ gimp_extension_ack ();
+
+ /* Go into an endless loop */
+ while (TRUE)
+ gimp_extension_process (0);
+
+ /* Set return values; pointless because we never get out of the loop */
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+ }
+ else if (strcmp (name, "plug-in-script-fu-text-console") == 0)
+ {
+ /*
+ * The script-fu text console for interactive Scheme development
+ */
+
+ script_fu_text_console_run (name, nparams, param,
+ nreturn_vals, return_vals);
+ }
+ else if (strcmp (name, "plug-in-script-fu-console") == 0)
+ {
+ /*
+ * The script-fu console for interactive Scheme development
+ */
+
+ script_fu_console_run (name, nparams, param,
+ nreturn_vals, return_vals);
+ }
+ else if (strcmp (name, "plug-in-script-fu-server") == 0)
+ {
+ /*
+ * The script-fu server for remote operation
+ */
+
+ script_fu_server_run (name, nparams, param,
+ nreturn_vals, return_vals);
+ }
+ else if (strcmp (name, "plug-in-script-fu-eval") == 0)
+ {
+ /*
+ * A non-interactive "console" (for batch mode)
+ */
+
+ script_fu_eval_run (name, nparams, param,
+ nreturn_vals, return_vals);
+ }
+}
+
+static GList *
+script_fu_search_path (void)
+{
+ gchar *path_str;
+ GList *path = NULL;
+
+ path_str = gimp_gimprc_query ("script-fu-path");
+
+ if (path_str)
+ {
+ GError *error = NULL;
+
+ path = gimp_config_path_expand_to_files (path_str, &error);
+ g_free (path_str);
+
+ if (! path)
+ {
+ g_warning ("Can't convert script-fu-path to filesystem encoding: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ return path;
+}
+
+static void
+script_fu_extension_init (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "[Interactive], non-interactive" }
+ };
+
+ gimp_plugin_menu_branch_register ("<Image>/Help", N_("_GIMP Online"));
+ gimp_plugin_menu_branch_register ("<Image>/Help", N_("_User Manual"));
+
+ gimp_plugin_menu_branch_register ("<Image>/Filters/Languages",
+ N_("_Script-Fu"));
+ gimp_plugin_menu_branch_register ("<Image>/Filters/Languages/Script-Fu",
+ N_("_Test"));
+
+ gimp_plugin_menu_branch_register ("<Image>/File/Create",
+ N_("_Buttons"));
+ gimp_plugin_menu_branch_register ("<Image>/File/Create",
+ N_("_Logos"));
+ gimp_plugin_menu_branch_register ("<Image>/File/Create",
+ N_("_Patterns"));
+
+ gimp_plugin_menu_branch_register ("<Image>/File/Create",
+ N_("_Web Page Themes"));
+ gimp_plugin_menu_branch_register ("<Image>/File/Create/Web Page Themes",
+ N_("_Alien Glow"));
+ gimp_plugin_menu_branch_register ("<Image>/File/Create/Web Page Themes",
+ N_("_Beveled Pattern"));
+ gimp_plugin_menu_branch_register ("<Image>/File/Create/Web Page Themes",
+ N_("_Classic.Gimp.Org"));
+
+ gimp_plugin_menu_branch_register ("<Image>/Filters",
+ N_("Alpha to _Logo"));
+
+ gimp_install_temp_proc ("script-fu-refresh",
+ N_("Re-read all available Script-Fu scripts"),
+ "Re-read all available Script-Fu scripts",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1997",
+ N_("_Refresh Scripts"),
+ NULL,
+ GIMP_TEMPORARY,
+ G_N_ELEMENTS (args), 0,
+ args, NULL,
+ script_fu_refresh_proc);
+
+ gimp_plugin_menu_register ("script-fu-refresh",
+ "<Image>/Filters/Languages/Script-Fu");
+}
+
+static void
+script_fu_refresh_proc (const gchar *name,
+ gint nparams,
+ const GimpParam *params,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ GimpPDBStatusType status;
+
+ if (script_fu_interface_is_active ())
+ {
+ g_message (_("You can not use \"Refresh Scripts\" while a "
+ "Script-Fu dialog box is open. Please close "
+ "all Script-Fu windows and try again."));
+
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ else
+ {
+ /* Reload all of the available scripts */
+ GList *path = script_fu_search_path ();
+
+ script_fu_find_scripts (path);
+
+ g_list_free_full (path, (GDestroyNotify) g_object_unref);
+
+ status = GIMP_PDB_SUCCESS;
+ }
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+}
diff --git a/plug-ins/script-fu/scripts/Makefile.am b/plug-ins/script-fu/scripts/Makefile.am
new file mode 100644
index 0000000..4cce60d
--- /dev/null
+++ b/plug-ins/script-fu/scripts/Makefile.am
@@ -0,0 +1,70 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = images
+
+scriptdatadir = $(gimpdatadir)/scripts
+
+scripts = \
+ script-fu.init \
+ script-fu-compat.init \
+ plug-in-compat.init \
+ add-bevel.scm \
+ addborder.scm \
+ blend-anim.scm \
+ burn-in-anim.scm \
+ carve-it.scm \
+ chrome-it.scm \
+ circuit.scm \
+ clothify.scm \
+ coffee.scm \
+ copy-visible.scm \
+ difference-clouds.scm \
+ distress-selection.scm \
+ drop-shadow.scm \
+ erase-rows.scm \
+ font-map.scm \
+ fuzzyborder.scm \
+ gimp-online.scm \
+ gradient-example.scm \
+ grid-system.scm \
+ guides-from-selection.scm \
+ guides-new.scm \
+ guides-new-percent.scm \
+ guides-remove-all.scm \
+ lava.scm \
+ line-nova.scm \
+ mkbrush.scm \
+ old-photo.scm \
+ palette-export.scm \
+ paste-as-brush.scm \
+ paste-as-pattern.scm \
+ perspective-shadow.scm \
+ predator.scm \
+ reverse-layers.scm \
+ ripply-anim.scm \
+ round-corners.scm \
+ script-fu-set-cmap.scm \
+ script-fu-util.scm \
+ select-to-brush.scm \
+ select-to-image.scm \
+ select-to-pattern.scm \
+ selection-round.scm \
+ slide.scm \
+ spinning-globe.scm \
+ spyrogimp.scm \
+ tileblur.scm \
+ unsharp-mask.scm \
+ waves-anim.scm \
+ weave.scm \
+ xach-effect.scm
+
+test_scripts = \
+ contactsheet.scm \
+ test-sphere.scm \
+ ts-helloworld.scm
+
+
+scriptdata_DATA = $(scripts)
+
+
+EXTRA_DIST = $(scripts) $(test_scripts)
diff --git a/plug-ins/script-fu/scripts/Makefile.in b/plug-ins/script-fu/scripts/Makefile.in
new file mode 100644
index 0000000..e4554bf
--- /dev/null
+++ b/plug-ins/script-fu/scripts/Makefile.in
@@ -0,0 +1,1044 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/script-fu/scripts
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(scriptdatadir)"
+DATA = $(scriptdata_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = images
+scriptdatadir = $(gimpdatadir)/scripts
+scripts = \
+ script-fu.init \
+ script-fu-compat.init \
+ plug-in-compat.init \
+ add-bevel.scm \
+ addborder.scm \
+ blend-anim.scm \
+ burn-in-anim.scm \
+ carve-it.scm \
+ chrome-it.scm \
+ circuit.scm \
+ clothify.scm \
+ coffee.scm \
+ copy-visible.scm \
+ difference-clouds.scm \
+ distress-selection.scm \
+ drop-shadow.scm \
+ erase-rows.scm \
+ font-map.scm \
+ fuzzyborder.scm \
+ gimp-online.scm \
+ gradient-example.scm \
+ grid-system.scm \
+ guides-from-selection.scm \
+ guides-new.scm \
+ guides-new-percent.scm \
+ guides-remove-all.scm \
+ lava.scm \
+ line-nova.scm \
+ mkbrush.scm \
+ old-photo.scm \
+ palette-export.scm \
+ paste-as-brush.scm \
+ paste-as-pattern.scm \
+ perspective-shadow.scm \
+ predator.scm \
+ reverse-layers.scm \
+ ripply-anim.scm \
+ round-corners.scm \
+ script-fu-set-cmap.scm \
+ script-fu-util.scm \
+ select-to-brush.scm \
+ select-to-image.scm \
+ select-to-pattern.scm \
+ selection-round.scm \
+ slide.scm \
+ spinning-globe.scm \
+ spyrogimp.scm \
+ tileblur.scm \
+ unsharp-mask.scm \
+ waves-anim.scm \
+ weave.scm \
+ xach-effect.scm
+
+test_scripts = \
+ contactsheet.scm \
+ test-sphere.scm \
+ ts-helloworld.scm
+
+scriptdata_DATA = $(scripts)
+EXTRA_DIST = $(scripts) $(test_scripts)
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/script-fu/scripts/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/script-fu/scripts/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-scriptdataDATA: $(scriptdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(scriptdata_DATA)'; test -n "$(scriptdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(scriptdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(scriptdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(scriptdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(scriptdatadir)" || exit $$?; \
+ done
+
+uninstall-scriptdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(scriptdata_DATA)'; test -n "$(scriptdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(scriptdatadir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(DATA)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(scriptdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-scriptdataDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-scriptdataDATA
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+ check-am clean clean-generic clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-scriptdataDATA install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-scriptdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/script-fu/scripts/add-bevel.scm b/plug-ins/script-fu/scripts/add-bevel.scm
new file mode 100644
index 0000000..6e0f951
--- /dev/null
+++ b/plug-ins/script-fu/scripts/add-bevel.scm
@@ -0,0 +1,202 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; add-bevel.scm version 1.04
+; Time-stamp: <2004-02-09 17:07:06 simon>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Copyright (C) 1997 Andrew Donkin (ard@cs.waikato.ac.nz)
+; Contains code from add-shadow.scm by Sven Neumann
+; (neumanns@uni-duesseldorf.de) (thanks Sven).
+;
+; Adds a bevel to an image. See http://www.cs.waikato.ac.nz/~ard/gimp/
+;
+; If there is a selection, it is bevelled.
+; Otherwise if there is an alpha channel, the selection is taken from it
+; and bevelled.
+; Otherwise the part of the layer inside the image boundaries is bevelled.
+;
+; The selection is set on exit, so Select->Invert then Edit->Clear will
+; leave a cut-out. Then use Sven's add-shadow for that
+; floating-bumpmapped-texture cliche.
+
+;
+; 1.01: now works on offset layers.
+; 1.02: has crop-pixel-border option to trim one pixel off each edge of the
+; bevelled image. Bumpmapping leaves edge pixels unchanged, which
+; looks bad. Oddly, this is not apparent in GIMP - you have to
+; save the image and load it into another viewer. First noticed in
+; Nutscrape.
+; Changed path (removed "filters/").
+; 1.03: adds one-pixel border before bumpmapping, and removes it after.
+; Got rid of the crop-pixel-border option (no longer reqd).
+; 1.04: Fixed undo handling, ensure that bumpmap is big enough,
+; (instead of resizing the image). Removed references to outdated
+; bumpmap plugin. (Simon)
+; 1.05 When there is no selection, bevel the whole layer instead of the
+; whole image (which was broken in the first place).
+; Also fixed some bugs with setting the selection when there is no
+; initial selection. (Barak Itkin)
+;
+
+(define (script-fu-add-bevel img
+ drawable
+ thickness
+ work-on-copy
+ keep-bump-layer)
+
+ (let* (
+ (index 1)
+ (greyness 0)
+ (thickness (abs thickness))
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image (if (= work-on-copy TRUE) (car (gimp-image-duplicate img)) img))
+ (pic-layer (car (gimp-image-get-active-drawable image)))
+ (offsets (gimp-drawable-offsets pic-layer))
+ (width (car (gimp-drawable-width pic-layer)))
+ (height (car (gimp-drawable-height pic-layer)))
+
+ ; Bumpmap has a one pixel border on each side
+ (bump-layer (car (gimp-layer-new image
+ (+ width 2)
+ (+ height 2)
+ RGB-IMAGE
+ _"Bumpmap"
+ 100
+ LAYER-MODE-NORMAL)))
+
+ (selection-exists (car (gimp-selection-bounds image)))
+ (selection 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ ; disable undo on copy, start group otherwise
+ (if (= work-on-copy TRUE)
+ (gimp-image-undo-disable image)
+ (gimp-image-undo-group-start image)
+ )
+
+ (gimp-image-insert-layer image bump-layer 0 1)
+
+ ; If the layer we're bevelling is offset from the image's origin, we
+ ; have to do the same to the bumpmap
+ (gimp-layer-set-offsets bump-layer (- (car offsets) 1)
+ (- (cadr offsets) 1))
+
+ ;------------------------------------------------------------
+ ;
+ ; Set the selection to the area we want to bevel.
+ ;
+ (if (= selection-exists 0)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE pic-layer)
+ )
+
+ ; Store it for later.
+ (set! selection (car (gimp-selection-save image)))
+ ; Try to lose the jaggies
+ (gimp-selection-feather image 2)
+
+ ;------------------------------------------------------------
+ ;
+ ; Initialise our bumpmap
+ ;
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-fill bump-layer FILL-BACKGROUND)
+
+ (while (and (< index thickness)
+ (= (car (gimp-selection-is-empty image)) FALSE)
+ )
+ (set! greyness (/ (* index 255) thickness))
+ (gimp-context-set-background (list greyness greyness greyness))
+ ;(gimp-selection-feather image 1) ;Stop the slopey jaggies?
+ (gimp-drawable-edit-fill bump-layer FILL-BACKGROUND)
+ (gimp-selection-shrink image 1)
+ (set! index (+ index 1))
+ )
+ ; Now the white interior
+ (if (= (car (gimp-selection-is-empty image)) FALSE)
+ (begin
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill bump-layer FILL-BACKGROUND)
+ )
+ )
+
+ ;------------------------------------------------------------
+ ;
+ ; Do the bump.
+ ;
+ (gimp-selection-none image)
+
+ ; To further lessen jaggies?
+ ;(plug-in-gauss-rle RUN-NONINTERACTIVE image bump-layer thickness TRUE TRUE)
+
+
+ ;
+ ; BUMPMAP INVOCATION:
+ ;
+ (plug-in-bump-map RUN-NONINTERACTIVE image pic-layer bump-layer 125 45 3 0 0 0 0 TRUE FALSE 1)
+
+ ;------------------------------------------------------------
+ ;
+ ; Restore things
+ ;
+ (if (= selection-exists 0)
+ (gimp-selection-none image) ; No selection to start with
+ (gimp-image-select-item image CHANNEL-OP-REPLACE selection)
+ )
+ ; If they started with a selection, they can Select->Invert then
+ ; Edit->Clear for a cutout.
+
+ ; clean up
+ (gimp-image-remove-channel image selection)
+ (if (= keep-bump-layer TRUE)
+ (gimp-item-set-visible bump-layer 0)
+ (gimp-image-remove-layer image bump-layer)
+ )
+
+ (gimp-image-set-active-layer image pic-layer)
+
+ ; enable undo / end undo group
+ (if (= work-on-copy TRUE)
+ (begin
+ (gimp-display-new image)
+ (gimp-image-undo-enable image)
+ )
+ (gimp-image-undo-group-end image)
+ )
+
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-add-bevel"
+ _"Add B_evel..."
+ _"Add a beveled border to an image"
+ "Andrew Donkin <ard@cs.waikato.ac.nz>"
+ "Andrew Donkin"
+ "1997/11/06"
+ "RGB*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Thickness" '(5 0 30 1 2 0 0)
+ SF-TOGGLE _"Work on copy" TRUE
+ SF-TOGGLE _"Keep bump layer" FALSE
+)
+
+(script-fu-menu-register "script-fu-add-bevel" "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/addborder.scm b/plug-ins/script-fu/scripts/addborder.scm
new file mode 100644
index 0000000..ed47feb
--- /dev/null
+++ b/plug-ins/script-fu/scripts/addborder.scm
@@ -0,0 +1,177 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Copyright (C) 1997 Andy Thomas alt@picnic.demon.co.uk
+;
+; Version 0.2 10.6.97 Changed to new script-fu interface in 0.99.10
+
+; Delta the color by the given amount. Check for boundary conditions
+; If < 0 set to zero
+; If > 255 set to 255
+; Return the new value
+
+(define (script-fu-addborder aimg adraw xsize ysize color dvalue)
+
+ (define (deltacolor col delta)
+ (let* ((newcol (+ col delta)))
+ (if (< newcol 0) (set! newcol 0))
+ (if (> newcol 255) (set! newcol 255))
+ newcol
+ )
+ )
+
+ (define (adjcolor col delta)
+ (mapcar (lambda (x) (deltacolor x delta)) col)
+ )
+
+ (define (gen_top_array xsize ysize owidth oheight width height)
+ (let* ((n_array (cons-array 10 'double)))
+ (aset n_array 0 0 )
+ (aset n_array 1 0 )
+ (aset n_array 2 xsize)
+ (aset n_array 3 ysize)
+ (aset n_array 4 (+ xsize owidth))
+ (aset n_array 5 ysize)
+ (aset n_array 6 width)
+ (aset n_array 7 0 )
+ (aset n_array 8 0 )
+ (aset n_array 9 0 )
+ n_array)
+ )
+
+ (define (gen_left_array xsize ysize owidth oheight width height)
+ (let* ((n_array (cons-array 10 'double)))
+ (aset n_array 0 0 )
+ (aset n_array 1 0 )
+ (aset n_array 2 xsize)
+ (aset n_array 3 ysize)
+ (aset n_array 4 xsize)
+ (aset n_array 5 (+ ysize oheight))
+ (aset n_array 6 0 )
+ (aset n_array 7 height )
+ (aset n_array 8 0 )
+ (aset n_array 9 0 )
+ n_array)
+ )
+
+ (define (gen_right_array xsize ysize owidth oheight width height)
+ (let* ((n_array (cons-array 10 'double)))
+ (aset n_array 0 width )
+ (aset n_array 1 0 )
+ (aset n_array 2 (+ xsize owidth))
+ (aset n_array 3 ysize)
+ (aset n_array 4 (+ xsize owidth))
+ (aset n_array 5 (+ ysize oheight))
+ (aset n_array 6 width)
+ (aset n_array 7 height)
+ (aset n_array 8 width )
+ (aset n_array 9 0 )
+ n_array)
+ )
+
+ (define (gen_bottom_array xsize ysize owidth oheight width height)
+ (let* ((n_array (cons-array 10 'double)))
+ (aset n_array 0 0 )
+ (aset n_array 1 height)
+ (aset n_array 2 xsize)
+ (aset n_array 3 (+ ysize oheight))
+ (aset n_array 4 (+ xsize owidth))
+ (aset n_array 5 (+ ysize oheight))
+ (aset n_array 6 width)
+ (aset n_array 7 height)
+ (aset n_array 8 0 )
+ (aset n_array 9 height)
+ n_array)
+ )
+
+ (let* ((img (car (gimp-item-get-image adraw)))
+ (owidth (car (gimp-image-width img)))
+ (oheight (car (gimp-image-height img)))
+ (width (+ owidth (* 2 xsize)))
+ (height (+ oheight (* 2 ysize)))
+ (layer (car (gimp-layer-new img
+ width height
+ (car (gimp-drawable-type-with-alpha adraw))
+ _"Border Layer" 100 LAYER-MODE-NORMAL))))
+
+ (gimp-context-push)
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+ (gimp-context-set-antialias FALSE)
+ (gimp-context-set-feather FALSE)
+
+ (gimp-image-undo-group-start img)
+
+ (gimp-image-resize img
+ width
+ height
+ xsize
+ ysize)
+
+ (gimp-image-insert-layer img layer 0 0)
+ (gimp-drawable-fill layer FILL-TRANSPARENT)
+
+ (gimp-context-set-background (adjcolor color dvalue))
+ (gimp-image-select-polygon img
+ CHANNEL-OP-REPLACE
+ 10
+ (gen_top_array xsize ysize owidth oheight width height))
+ (gimp-drawable-edit-fill layer FILL-BACKGROUND)
+ (gimp-context-set-background (adjcolor color (/ dvalue 2)))
+ (gimp-image-select-polygon img
+ CHANNEL-OP-REPLACE
+ 10
+ (gen_left_array xsize ysize owidth oheight width height))
+ (gimp-drawable-edit-fill layer FILL-BACKGROUND)
+ (gimp-context-set-background (adjcolor color (- 0 (/ dvalue 2))))
+ (gimp-image-select-polygon img
+ CHANNEL-OP-REPLACE
+ 10
+ (gen_right_array xsize ysize owidth oheight width height))
+
+ (gimp-drawable-edit-fill layer FILL-BACKGROUND)
+ (gimp-context-set-background (adjcolor color (- 0 dvalue)))
+ (gimp-image-select-polygon img
+ CHANNEL-OP-REPLACE
+ 10
+ (gen_bottom_array xsize ysize owidth oheight width height))
+
+ (gimp-drawable-edit-fill layer FILL-BACKGROUND)
+ (gimp-selection-none img)
+ (gimp-image-undo-group-end img)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-addborder"
+ _"Add _Border..."
+ _"Add a border around an image"
+ "Andy Thomas <alt@picnic.demon.co.uk>"
+ "Andy Thomas"
+ "6/10/97"
+ "*"
+ SF-IMAGE "Input image" 0
+ SF-DRAWABLE "Input drawable" 0
+ SF-ADJUSTMENT _"Border X size" '(12 1 250 1 10 0 1)
+ SF-ADJUSTMENT _"Border Y size" '(12 1 250 1 10 0 1)
+ SF-COLOR _"Border color" '(38 31 207)
+ SF-ADJUSTMENT _"Delta value on color" '(25 1 255 1 10 0 1)
+)
+
+(script-fu-menu-register "script-fu-addborder"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/blend-anim.scm b/plug-ins/script-fu/scripts/blend-anim.scm
new file mode 100644
index 0000000..517b1c5
--- /dev/null
+++ b/plug-ins/script-fu/scripts/blend-anim.scm
@@ -0,0 +1,242 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; blend-anim.scm version 1.03 1999/12/21
+;
+; CHANGE-LOG:
+; 1.00 - initial release
+; 1.01 - some code cleanup, no real changes
+; 1.02 - use gimp-message to output an error message if called
+; with less than three layers
+; 1.03 - only call blur plugin when blut-radius >= 1.0
+;
+; Copyright (C) 1997-1999 Sven Neumann <sven@gimp.org>
+;
+;
+; Blends two or more layers over a background, so that an animation can
+; be saved. A minimum of three layers is required.
+
+(define (script-fu-blend-anim img
+ drawable
+ frames
+ max-blur
+ looped)
+
+ (define (multi-raise-layer image layer times)
+ (while (> times 0)
+ (gimp-image-raise-item image layer)
+ (set! times (- times 1))
+ )
+ )
+
+ (let* (
+ (max-blur (max max-blur 0))
+ (frames (max frames 0))
+ (image (car (gimp-image-duplicate img)))
+ (width (car (gimp-image-width image)))
+ (height (car (gimp-image-height image)))
+ (layers (gimp-image-get-layers image))
+ (num-layers (car layers))
+ (layer-array (cadr layers))
+ (slots (- num-layers 2))
+ (bg-layer (aref layer-array (- num-layers 1)))
+ (max-width 0)
+ (max-height 0)
+ (offset-x 0)
+ (offset-y 0)
+ )
+
+ (if (> num-layers 2)
+ (begin
+ (gimp-image-undo-disable image)
+
+ (if (= looped TRUE)
+ ; add a copy of the lowest blend layer on top
+ (let* ((copy (car (gimp-layer-copy
+ (aref layer-array (- num-layers 2)) TRUE))))
+ (gimp-image-insert-layer image copy 0 0)
+ (set! layers (gimp-image-get-layers image))
+ (set! num-layers (car layers))
+ (set! layer-array (cadr layers))
+ (set! slots (- num-layers 2))
+ (set! bg-layer (aref layer-array (- num-layers 1)))))
+
+ ; make all layers invisible and check for sizes
+ (let* ((min-offset-x width)
+ (min-offset-y height)
+ (layer-count slots))
+ (gimp-item-set-visible bg-layer FALSE)
+ (while (> layer-count -1)
+ (let* ((layer (aref layer-array layer-count))
+ (layer-width (+ (car (gimp-drawable-width layer))
+ (* max-blur 2)))
+ (layer-height (+ (car (gimp-drawable-height layer))
+ (* max-blur 2)))
+ (layer-offsets (gimp-drawable-offsets layer))
+ (layer-offset-x (- (car layer-offsets) max-blur))
+ (layer-offset-y (- (cadr layer-offsets) max-blur)))
+ (gimp-item-set-visible layer FALSE)
+ (set! max-width (max max-width layer-width))
+ (set! max-height (max max-height layer-height))
+ (set! min-offset-x (min min-offset-x layer-offset-x))
+ (set! min-offset-y (min min-offset-y layer-offset-y))
+ (set! layer-count (- layer-count 1))))
+ (set! offset-x (- (car (gimp-drawable-offsets bg-layer))
+ min-offset-x))
+ (set! offset-y (- (cadr (gimp-drawable-offsets bg-layer))
+ min-offset-y)))
+
+ ; create intermediate frames by merging copies of adjacent layers
+ ; with the background layer
+ (let* ((layer-count slots))
+ (while (> layer-count 0)
+ (let* ((frame-count frames)
+ (lower-layer (aref layer-array layer-count))
+ (upper-layer (aref layer-array (- layer-count 1))))
+ (while (> frame-count 0)
+ (let* ((opacity (* (/ frame-count (+ frames 1)) 100))
+ (blur (/ (* opacity max-blur) 100))
+ (upper-copy (car (gimp-layer-copy upper-layer TRUE)))
+ (lower-copy (car (gimp-layer-copy lower-layer TRUE)))
+ (bg-copy (car (gimp-layer-copy bg-layer TRUE))))
+ (gimp-image-insert-layer image bg-copy 0 0)
+ (gimp-image-insert-layer image lower-copy 0 0)
+ (gimp-image-insert-layer image upper-copy 0 0)
+ (gimp-item-set-visible upper-copy TRUE)
+ (gimp-item-set-visible lower-copy TRUE)
+ (gimp-item-set-visible bg-copy TRUE)
+ (gimp-layer-set-opacity upper-copy (- 100 opacity))
+ (gimp-layer-set-opacity lower-copy opacity)
+ (gimp-layer-set-opacity bg-copy 100)
+ (if (> max-blur 0)
+ (let* ((layer-width (car (gimp-drawable-width upper-copy)))
+ (layer-height (car (gimp-drawable-height upper-copy))))
+ (gimp-layer-set-lock-alpha upper-copy FALSE)
+ (gimp-layer-resize upper-copy
+ (+ layer-width (* blur 2))
+ (+ layer-height (* blur 2))
+ blur
+ blur)
+ (if (>= blur 1.0)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ image
+ upper-copy
+ blur
+ TRUE TRUE))
+ (set! blur (- max-blur blur))
+ (gimp-layer-set-lock-alpha lower-copy FALSE)
+ (set! layer-width (car (gimp-drawable-width
+ lower-copy)))
+ (set! layer-height (car (gimp-drawable-height
+ lower-copy)))
+ (gimp-layer-resize lower-copy
+ (+ layer-width (* blur 2))
+ (+ layer-height (* blur 2))
+ blur
+ blur)
+ (if (>= blur 1.0)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ image
+ lower-copy
+ blur
+ TRUE TRUE))))
+ (gimp-layer-resize bg-copy
+ max-width
+ max-height
+ offset-x
+ offset-y)
+ (let* ((merged-layer (car (gimp-image-merge-visible-layers
+ image CLIP-TO-IMAGE))))
+ (gimp-item-set-visible merged-layer FALSE))
+ (set! frame-count (- frame-count 1))))
+ (set! layer-count (- layer-count 1)))))
+
+ ; merge all original blend layers but the lowest one
+ ; with copies of the background layer
+ (let* ((layer-count 0))
+ (while (< layer-count slots)
+ (let* ((orig-layer (aref layer-array layer-count))
+ (bg-copy (car (gimp-layer-copy bg-layer TRUE))))
+ (gimp-image-insert-layer image
+ bg-copy
+ -1
+ (* layer-count (+ frames 1)))
+ (multi-raise-layer image
+ orig-layer
+ (+ (* (- slots layer-count) frames) 1))
+ (gimp-item-set-visible orig-layer TRUE)
+ (gimp-item-set-visible bg-copy TRUE)
+ (gimp-layer-resize bg-copy
+ max-width
+ max-height
+ offset-x
+ offset-y)
+ (let* ((merged-layer (car (gimp-image-merge-visible-layers
+ image CLIP-TO-IMAGE))))
+ (gimp-item-set-visible merged-layer FALSE))
+ (set! layer-count (+ layer-count 1)))))
+
+ ; merge the lowest blend layer with the background layer
+ (let* ((orig-layer (aref layer-array (- num-layers 2))))
+ (gimp-item-set-visible bg-layer TRUE)
+ (gimp-item-set-visible orig-layer TRUE)
+ (gimp-image-merge-visible-layers image CLIP-TO-IMAGE))
+
+ ; make all layers visible again
+ (let* ((result-layers (gimp-image-get-layers image))
+ (num-result-layers (car result-layers))
+ (result-layer-array (cadr result-layers))
+ (layer-count (- num-result-layers 1)))
+ (while (> layer-count -1)
+ (let* ((layer (aref result-layer-array layer-count))
+ (name (string-append _"Frame" " "
+ (number->string
+ (- num-result-layers layer-count) 10))))
+ (gimp-item-set-visible layer TRUE)
+ (gimp-item-set-name layer name)
+ (set! layer-count (- layer-count 1))))
+
+ (if (= looped TRUE)
+ ; remove the topmost layer
+ (gimp-image-remove-layer image (aref result-layer-array 0))))
+
+ (gimp-image-undo-enable image)
+ (gimp-display-new image)
+ (gimp-displays-flush)
+ )
+
+ (gimp-message _"Blend Animation needs at least three source layers")
+ )
+ )
+)
+
+(script-fu-register "script-fu-blend-anim"
+ _"_Blend..."
+ _"Create intermediate layers to blend two or more layers over a background as an animation"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "1999/12/21"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Intermediate frames" '(3 1 1024 1 10 0 1)
+ SF-ADJUSTMENT _"Max. blur radius" '(0 0 1024 1 10 0 1)
+ SF-TOGGLE _"Looped" TRUE
+)
+
+(script-fu-menu-register "script-fu-blend-anim"
+ "<Image>/Filters/Animation/Animators")
diff --git a/plug-ins/script-fu/scripts/burn-in-anim.scm b/plug-ins/script-fu/scripts/burn-in-anim.scm
new file mode 100644
index 0000000..4842e8a
--- /dev/null
+++ b/plug-ins/script-fu/scripts/burn-in-anim.scm
@@ -0,0 +1,243 @@
+;
+; burn-in-anim.scm V2.1 - script-fu for GIMP 1.1 and higher
+;
+; Copyright (C) 9/2000 Roland Berger
+; roland@fuchur.leute.server.de
+; http://fuchur.leute.server.de
+;
+; Let text appear and fade out with a "burn-in" like SFX.
+; Works on an image with a text and a background layer
+;
+; Copying Policy: GNU Public License http://www.gnu.org
+;
+
+(define (script-fu-burn-in-anim org-img
+ org-layer
+ glow-color
+ fadeout
+ bl-width
+ corona-width
+ after-glow
+ show-glow
+ optimize
+ speed)
+
+ (let* (
+ ;--- main variable: "bl-x" runs from 0 to layer-width
+ (bl-x 0)
+ (frame-nr 0)
+ (img 0)
+ (source-layer 0)
+ (bg-source-layer 0)
+ (source-layer-width 0)
+ (bg-layer 0)
+ (bg-layer-name 0)
+ (bl-layer 0)
+ (bl-layer-name 0)
+ (bl-mask 0)
+ (bl-layer-width 0)
+ (bl-height 0)
+ (bl-x-off 0)
+ (bl-y-off 0)
+ (nofadeout-bl-x-off 0)
+ (nofadeout-bl-width 0)
+ (blended-layer 0)
+ (img-display 0)
+ )
+
+ (if (< speed 1)
+ (set! speed (* -1 speed)) )
+
+ ;--- check image and work on a copy
+ (if (and (= (car (gimp-image-get-layers org-img)) 2)
+ (= (car (gimp-image-get-floating-sel org-img)) -1))
+
+ ;--- main program structure starts here, begin of "if-1"
+ (begin
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (set! img (car (gimp-image-duplicate org-img)))
+ (gimp-image-undo-disable img)
+ (if (> (car (gimp-drawable-type org-layer)) 1 )
+ (gimp-image-convert-rgb img))
+ (set! source-layer (aref (cadr (gimp-image-get-layers img)) 0 ))
+ (set! bg-source-layer (aref (cadr (gimp-image-get-layers img)) 1 ))
+ (set! source-layer-width (car (gimp-drawable-width source-layer)))
+
+ ;--- hide layers, cause we want to "merge visible layers" later
+ (gimp-item-set-visible source-layer FALSE)
+ (gimp-item-set-visible bg-source-layer FALSE)
+
+ ;--- process image horizontal with pixel-speed
+ (while (< bl-x (+ source-layer-width bl-width))
+ (set! bl-layer (car (gimp-layer-copy source-layer TRUE)))
+ (set! bl-layer-name (string-append "fr-nr"
+ (number->string frame-nr 10) ) )
+
+ (gimp-image-insert-layer img bl-layer 0 -2)
+ (gimp-item-set-name bl-layer bl-layer-name)
+ (gimp-item-set-visible bl-layer TRUE)
+ (gimp-layer-set-lock-alpha bl-layer TRUE)
+ (gimp-layer-add-alpha bl-layer)
+
+ ;--- add an alpha mask for blending and select it
+ (gimp-image-select-item img CHANNEL-OP-REPLACE bl-layer)
+ (set! bl-mask (car (gimp-layer-create-mask bl-layer ADD-MASK-BLACK)))
+ (gimp-layer-add-mask bl-layer bl-mask)
+
+ ;--- handle layer geometry
+ (set! bl-layer-width source-layer-width)
+ (set! bl-height (car (gimp-drawable-height bl-layer)))
+ (set! bl-x-off (- bl-x bl-width))
+ (set! bl-x-off (+ bl-x-off (car (gimp-drawable-offsets bl-layer))))
+ (set! bl-y-off (cadr (gimp-drawable-offsets bl-layer)))
+
+ ;--- select a rectangular area to blend
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE bl-x-off bl-y-off bl-width bl-height)
+ ;--- select at least 1 pixel!
+ (gimp-image-select-rectangle img CHANNEL-OP-ADD bl-x-off bl-y-off (+ bl-width 1) bl-height)
+
+ (if (= fadeout FALSE)
+ (begin
+ (set! nofadeout-bl-x-off (car (gimp-drawable-offsets bl-layer)))
+ (set! nofadeout-bl-width (+ nofadeout-bl-x-off bl-x))
+ (set! nofadeout-bl-width (max nofadeout-bl-width 1))
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE
+ nofadeout-bl-x-off bl-y-off
+ nofadeout-bl-width bl-height)
+ )
+ )
+
+ ;--- alpha blending text to trans (fadeout)
+ (gimp-context-set-foreground '(255 255 255))
+ (gimp-context-set-background '( 0 0 0))
+ (if (= fadeout TRUE)
+ (begin
+ ; blend with 20% offset to get less transparency in the front
+ (gimp-context-set-gradient-fg-bg-rgb)
+ (gimp-drawable-edit-gradient-fill bl-mask
+ GRADIENT-LINEAR 20
+ FALSE 0 0
+ TRUE
+ (+ bl-x-off bl-width) 0
+ bl-x-off 0)
+ )
+ )
+
+ (if (= fadeout FALSE)
+ (begin
+ (gimp-context-set-foreground '(255 255 255))
+ (gimp-drawable-edit-fill bl-mask FILL-FOREGROUND)
+ )
+ )
+
+ (gimp-layer-remove-mask bl-layer MASK-APPLY)
+
+ ;--- add bright glow in front
+ (if (= show-glow TRUE)
+ (begin
+ ;--- add some brightness to whole text
+ (if (= fadeout TRUE)
+ (gimp-drawable-brightness-contrast bl-layer 0.787 0)
+ )
+
+ ;--- blend glow color inside the letters
+ (gimp-context-set-foreground glow-color)
+ (gimp-context-set-gradient-fg-transparent)
+ (gimp-drawable-edit-gradient-fill bl-layer
+ GRADIENT-LINEAR 0
+ FALSE 0 0
+ TRUE
+ (+ bl-x-off bl-width) 0
+ (- (+ bl-x-off bl-width) after-glow) 0)
+
+ ;--- add corona effect
+ (gimp-image-select-item img CHANNEL-OP-REPLACE bl-layer)
+ (gimp-selection-sharpen img)
+ (gimp-selection-grow img corona-width)
+ (gimp-layer-set-lock-alpha bl-layer FALSE)
+ (gimp-selection-feather img corona-width)
+ (gimp-context-set-foreground glow-color)
+ (gimp-drawable-edit-gradient-fill bl-layer
+ GRADIENT-LINEAR 0
+ FALSE 0 0
+ TRUE
+ (- (+ bl-x-off bl-width) corona-width) 0
+ (- (+ bl-x-off bl-width) after-glow) 0)
+ )
+ )
+
+ ;--- merge with bg layer
+ (set! bg-layer (car (gimp-layer-copy bg-source-layer FALSE)))
+ (gimp-image-insert-layer img bg-layer 0 -1)
+ (gimp-image-lower-item img bg-layer)
+ (set! bg-layer-name (string-append "bg-"
+ (number->string frame-nr 10)))
+ (gimp-item-set-name bg-layer bg-layer-name)
+ (gimp-item-set-visible bg-layer TRUE)
+ (set! blended-layer (car (gimp-image-merge-visible-layers img
+ CLIP-TO-IMAGE)))
+ ;(set! blended-layer bl-layer)
+ (gimp-item-set-visible blended-layer FALSE)
+
+ ;--- end of "while" loop
+ (set! frame-nr (+ frame-nr 1))
+ (set! bl-x (+ bl-x speed))
+ )
+
+ ;--- finalize the job
+ (gimp-selection-none img)
+ (gimp-image-remove-layer img source-layer)
+ (gimp-image-remove-layer img bg-source-layer)
+
+ (gimp-image-set-filename img "burn-in")
+
+ (if (= optimize TRUE)
+ (begin
+ (gimp-image-convert-indexed img CONVERT-DITHER-FS CONVERT-PALETTE-WEB 250 FALSE TRUE "")
+ (set! img (car (plug-in-animationoptimize RUN-NONINTERACTIVE
+ img
+ blended-layer)))
+ )
+ )
+
+ (gimp-item-set-visible (aref (cadr (gimp-image-get-layers img)) 0)
+ TRUE)
+ (gimp-image-undo-enable img)
+ (gimp-image-clean-all img)
+ (set! img-display (car (gimp-display-new img)))
+
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+
+ ;--- false form of "if-1"
+ (gimp-message _"The Burn-In script needs two layers in total. A foreground layer with transparency and a background layer.")
+ )
+ )
+)
+
+
+(script-fu-register "script-fu-burn-in-anim"
+ _"B_urn-In..."
+ _"Create intermediate layers to produce an animated 'burn-in' transition between two layers"
+ "Roland Berger roland@fuchur.leute.server.de"
+ "Roland Berger"
+ "January 2001"
+ "RGBA GRAYA INDEXEDA"
+ SF-IMAGE "The image" 0
+ SF-DRAWABLE "Layer to animate" 0
+ SF-COLOR _"Glow color" "white"
+ SF-TOGGLE _"Fadeout" FALSE
+ SF-VALUE _"Fadeout width" "100"
+ SF-VALUE _"Corona width" "7"
+ SF-VALUE _"After glow" "50"
+ SF-TOGGLE _"Add glowing" TRUE
+ SF-TOGGLE _"Prepare for GIF" FALSE
+ SF-VALUE _"Speed (pixels/frame)" "50"
+)
+
+(script-fu-menu-register "script-fu-burn-in-anim"
+ "<Image>/Filters/Animation/Animators")
diff --git a/plug-ins/script-fu/scripts/carve-it.scm b/plug-ins/script-fu/scripts/carve-it.scm
new file mode 100644
index 0000000..a88fa08
--- /dev/null
+++ b/plug-ins/script-fu/scripts/carve-it.scm
@@ -0,0 +1,205 @@
+; CARVE-IT
+; Carving, embossing, & stamping
+; Process taken from "The Photoshop 3 WOW! Book"
+; http://www.peachpit.com
+; This script requires a grayscale image containing a single layer.
+; This layer is used as the mask for the carving effect
+; NOTE: This script requires the image to be carved to either be an
+; RGB color or grayscale image with a single layer. An indexed file
+; can not be used due to the use of gimp-drawable-histogram and
+; gimp-drawable-levels.
+
+
+(define (carve-scale val scale)
+ (* (sqrt val) scale))
+
+(define (calculate-inset-gamma img layer)
+ (let* ((stats (gimp-drawable-histogram layer 0 0.0 1.0))
+ (mean (car stats)))
+ (cond ((< mean 127) (+ 1.0 (* 0.5 (/ (- 127 mean) 127.0))))
+ ((>= mean 127) (- 1.0 (* 0.5 (/ (- mean 127) 127.0)))))))
+
+
+(define (copy-layer-carve-it dest-image dest-drawable source-image source-drawable)
+ (gimp-selection-all dest-image)
+ (gimp-drawable-edit-clear dest-drawable)
+ (gimp-selection-none dest-image)
+ (gimp-selection-all source-image)
+ (gimp-edit-copy source-drawable)
+ (let ((floating-sel (car (gimp-edit-paste dest-drawable FALSE))))
+ (gimp-floating-sel-anchor floating-sel)))
+
+
+
+(define (script-fu-carve-it mask-img mask-drawable bg-layer carve-white)
+ (let* (
+ (width (car (gimp-drawable-width mask-drawable)))
+ (height (car (gimp-drawable-height mask-drawable)))
+ (type (car (gimp-drawable-type bg-layer)))
+ (img (car (gimp-image-new width height (cond ((= type RGB-IMAGE) RGB)
+ ((= type RGBA-IMAGE) RGB)
+ ((= type GRAY-IMAGE) GRAY)
+ ((= type GRAYA-IMAGE) GRAY)
+ ((= type INDEXED-IMAGE) INDEXED)
+ ((= type INDEXEDA-IMAGE) INDEXED)))))
+ (size (min width height))
+ (offx (carve-scale size 0.33))
+ (offy (carve-scale size 0.25))
+ (feather (carve-scale size 0.3))
+ (brush-size (carve-scale size 0.3))
+ (brush-name (car (gimp-brush-new "Carve It")))
+ (mask-fs 0)
+ (mask (car (gimp-channel-new img width height "Engraving Mask" 50 '(0 0 0))))
+ (inset-gamma (calculate-inset-gamma (car (gimp-item-get-image bg-layer)) bg-layer))
+ (mask-fat 0)
+ (mask-emboss 0)
+ (mask-highlight 0)
+ (mask-shadow 0)
+ (shadow-layer 0)
+ (highlight-layer 0)
+ (cast-shadow-layer 0)
+ (csl-mask 0)
+ (inset-layer 0)
+ (il-mask 0)
+ (bg-width (car (gimp-drawable-width bg-layer)))
+ (bg-height (car (gimp-drawable-height bg-layer)))
+ (bg-type (car (gimp-drawable-type bg-layer)))
+ (bg-image (car (gimp-item-get-image bg-layer)))
+ (layer1 (car (gimp-layer-new img bg-width bg-height bg-type "Layer1" 100 LAYER-MODE-NORMAL)))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+
+ (gimp-image-insert-layer img layer1 0 0)
+
+ (gimp-selection-all img)
+ (gimp-drawable-edit-clear layer1)
+ (gimp-selection-none img)
+ (copy-layer-carve-it img layer1 bg-image bg-layer)
+
+ (gimp-edit-copy mask-drawable)
+ (gimp-image-insert-channel img mask -1 0)
+
+ (plug-in-tile RUN-NONINTERACTIVE img layer1 width height FALSE)
+ (set! mask-fs (car (gimp-edit-paste mask FALSE)))
+ (gimp-floating-sel-anchor mask-fs)
+ (if (= carve-white FALSE)
+ (gimp-drawable-invert mask FALSE))
+
+ (set! mask-fat (car (gimp-channel-copy mask)))
+ (gimp-image-insert-channel img mask-fat -1 0)
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask-fat)
+
+ (gimp-brush-set-shape brush-name BRUSH-GENERATED-CIRCLE)
+ (gimp-brush-set-spikes brush-name 2)
+ (gimp-brush-set-hardness brush-name 1.0)
+ (gimp-brush-set-spacing brush-name 25)
+ (gimp-brush-set-aspect-ratio brush-name 1)
+ (gimp-brush-set-angle brush-name 0)
+ (cond (<= brush-size 17) (gimp-brush-set-radius brush-name (\ brush-size 2))
+ (else gimp-brush-set-radius brush-name (\ 19 2)))
+ (gimp-context-set-brush brush-name)
+
+ (gimp-context-set-foreground '(255 255 255))
+ (gimp-drawable-edit-stroke-selection mask-fat)
+ (gimp-selection-none img)
+
+ (set! mask-emboss (car (gimp-channel-copy mask-fat)))
+ (gimp-image-insert-channel img mask-emboss -1 0)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img mask-emboss feather TRUE TRUE)
+ (plug-in-emboss RUN-NONINTERACTIVE img mask-emboss 315.0 45.0 7 TRUE)
+
+ (gimp-context-set-background '(180 180 180))
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask-fat)
+ (gimp-selection-invert img)
+ (gimp-drawable-edit-fill mask-emboss FILL-BACKGROUND)
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask)
+ (gimp-drawable-edit-fill mask-emboss FILL-BACKGROUND)
+ (gimp-selection-none img)
+
+ (set! mask-highlight (car (gimp-channel-copy mask-emboss)))
+ (gimp-image-insert-channel img mask-highlight -1 0)
+ (gimp-drawable-levels mask-highlight 0
+ 0.7056 1.0 TRUE
+ 1.0
+ 0.0 1.0 TRUE)
+
+ (set! mask-shadow mask-emboss)
+ (gimp-drawable-levels mask-shadow 0
+ 0.0 0.70586 TRUE
+ 1.0
+ 0.0 1.0 TRUE)
+
+ (gimp-edit-copy mask-shadow)
+ (set! shadow-layer (car (gimp-edit-paste layer1 FALSE)))
+ (gimp-floating-sel-to-layer shadow-layer)
+ (gimp-layer-set-mode shadow-layer LAYER-MODE-MULTIPLY)
+
+ (gimp-edit-copy mask-highlight)
+ (set! highlight-layer (car (gimp-edit-paste shadow-layer FALSE)))
+ (gimp-floating-sel-to-layer highlight-layer)
+ (gimp-layer-set-mode highlight-layer LAYER-MODE-SCREEN)
+
+ (gimp-edit-copy mask)
+ (set! cast-shadow-layer (car (gimp-edit-paste highlight-layer FALSE)))
+ (gimp-floating-sel-to-layer cast-shadow-layer)
+ (gimp-layer-set-mode cast-shadow-layer LAYER-MODE-MULTIPLY)
+ (gimp-layer-set-opacity cast-shadow-layer 75)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img cast-shadow-layer feather TRUE TRUE)
+ (gimp-item-transform-translate cast-shadow-layer offx offy)
+
+ (set! csl-mask (car (gimp-layer-create-mask cast-shadow-layer ADD-MASK-BLACK)))
+ (gimp-layer-add-mask cast-shadow-layer csl-mask)
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask)
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill csl-mask FILL-BACKGROUND)
+
+ (set! inset-layer (car (gimp-layer-copy layer1 TRUE)))
+ (gimp-image-insert-layer img inset-layer 0 1)
+
+ (set! il-mask (car (gimp-layer-create-mask inset-layer ADD-MASK-BLACK)))
+ (gimp-layer-add-mask inset-layer il-mask)
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask)
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill il-mask FILL-BACKGROUND)
+ (gimp-selection-none img)
+ (gimp-selection-none bg-image)
+ (gimp-drawable-levels inset-layer 0 0.0 1.0 TRUE inset-gamma 0.0 1.0 TRUE)
+ (gimp-image-remove-channel img mask)
+ (gimp-image-remove-channel img mask-fat)
+ (gimp-image-remove-channel img mask-highlight)
+ (gimp-image-remove-channel img mask-shadow)
+
+ (gimp-item-set-name layer1 _"Carved Surface")
+ (gimp-item-set-name shadow-layer _"Bevel Shadow")
+ (gimp-item-set-name highlight-layer _"Bevel Highlight")
+ (gimp-item-set-name cast-shadow-layer _"Cast Shadow")
+ (gimp-item-set-name inset-layer _"Inset")
+
+ (gimp-brush-delete brush-name)
+
+ (gimp-display-new img)
+ (gimp-image-undo-enable img)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-carve-it"
+ _"Stencil C_arve..."
+ _"Use the specified drawable as a stencil to carve from the specified image."
+ "Spencer Kimball"
+ "Spencer Kimball"
+ "1997"
+ "GRAY"
+ SF-IMAGE "Mask image" 0
+ SF-DRAWABLE "Mask drawable" 0
+ SF-DRAWABLE _"Image to carve" 0
+ SF-TOGGLE _"Carve white areas" TRUE
+)
+
+(script-fu-menu-register "script-fu-carve-it"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/chrome-it.scm b/plug-ins/script-fu/scripts/chrome-it.scm
new file mode 100644
index 0000000..761ac47
--- /dev/null
+++ b/plug-ins/script-fu/scripts/chrome-it.scm
@@ -0,0 +1,252 @@
+; CHROME-IT
+; State of the art chrome effect for user-specified mask
+; This script requires a grayscale image containing a single layer.
+; This layer is used as the mask for the SOTA chrome effect
+
+(define (script-fu-sota-chrome-it mask-img mask-drawable chrome-saturation
+ chrome-lightness chrome-factor env-map hc cc carve-white)
+
+ (define (set-pt a index x y)
+ (begin
+ (aset a (* index 2) x)
+ (aset a (+ (* index 2) 1) y)
+ )
+ )
+
+ (define (spline-chrome-it)
+ (let* ((a (cons-array 18 'double)))
+ (set-pt a 0 0.0 0.0)
+ (set-pt a 1 0.125 0.9216)
+ (set-pt a 2 0.25 0.0902)
+ (set-pt a 3 0.375 0.9020)
+ (set-pt a 4 0.5 0.0989)
+ (set-pt a 5 0.625 0.9549)
+ (set-pt a 6 0.75 00784)
+ (set-pt a 7 0.875 0.9412)
+ (set-pt a 8 1.0 0.1216)
+ a
+ )
+ )
+
+
+ (define (shadows val)
+ (/ (* 0.96 val) 2.55)
+ )
+
+ (define (midtones val)
+ (/ val 2.55)
+ )
+
+ (define (highlights val)
+ ; The result is used as "gimp-drawable-color-balance" color parameter
+ ; and thus must be restricted to -100.0 <= highlights <= 100.0.
+ (min (/ (* 1.108 val) 2.55) 100.0)
+ )
+
+ (define (rval col)
+ (car col)
+ )
+
+ (define (gval col)
+ (cadr col)
+ )
+
+ (define (bval col)
+ (caddr col)
+ )
+
+ (define (sota-scale val scale chrome-factor)
+ (* (sqrt val) (* scale chrome-factor))
+ )
+
+ (define (copy-layer-chrome-it dest-image dest-drawable source-image source-drawable)
+ (gimp-selection-all dest-image)
+ (gimp-drawable-edit-clear dest-drawable)
+ (gimp-selection-none dest-image)
+ (gimp-selection-all source-image)
+ (gimp-edit-copy source-drawable)
+ (let (
+ (floating-sel (car (gimp-edit-paste dest-drawable FALSE)))
+ )
+ (gimp-floating-sel-anchor floating-sel)
+ )
+ )
+
+ (let* (
+ (banding-img (car (gimp-file-load RUN-NONINTERACTIVE env-map env-map)))
+ (banding-layer (car (gimp-image-get-active-drawable banding-img)))
+ (banding-height (car (gimp-drawable-height banding-layer)))
+ (banding-width (car (gimp-drawable-width banding-layer)))
+ (banding-type (car (gimp-drawable-type banding-layer)))
+ (width (car (gimp-drawable-width mask-drawable)))
+ (height (car (gimp-drawable-height mask-drawable)))
+ (img (car (gimp-image-new width height GRAY)))
+ (size (min width height))
+ (offx1 (sota-scale size 0.33 chrome-factor))
+ (offy1 (sota-scale size 0.25 chrome-factor))
+ (offx2 (sota-scale size (- 0.33) chrome-factor))
+ (offy2 (sota-scale size (- 0.25) chrome-factor))
+ (feather (sota-scale size 0.5 chrome-factor))
+ (brush-size (sota-scale size 0.5 chrome-factor))
+ (brush-name (car (gimp-brush-new "Chrome It")))
+ (mask (car (gimp-channel-new img width height "Chrome Stencil" 50 '(0 0 0))))
+ (bg-layer (car (gimp-layer-new img width height GRAY-IMAGE _"Background" 100 LAYER-MODE-NORMAL)))
+ (layer1 (car (gimp-layer-new img banding-width banding-height banding-type _"Layer 1" 100 LAYER-MODE-NORMAL)))
+ (layer2 (car (gimp-layer-new img width height GRAYA-IMAGE _"Layer 2" 100 LAYER-MODE-DIFFERENCE)))
+ (layer3 (car (gimp-layer-new img width height GRAYA-IMAGE _"Layer 3" 100 LAYER-MODE-NORMAL)))
+ (shadow (car (gimp-layer-new img width height GRAYA-IMAGE _"Drop Shadow" 100 LAYER-MODE-NORMAL)))
+ (mask-fs 0)
+ (layer-mask 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+
+ (gimp-image-insert-channel img mask -1 0)
+ (gimp-image-insert-layer img bg-layer 0 0)
+ (gimp-image-insert-layer img shadow 0 0)
+ (gimp-image-insert-layer img layer3 0 0)
+ (gimp-image-insert-layer img layer2 0 0)
+
+ (gimp-edit-copy mask-drawable)
+ (set! mask-fs (car (gimp-edit-paste mask FALSE)))
+ (gimp-floating-sel-anchor mask-fs)
+ (if (= carve-white FALSE)
+ (gimp-drawable-invert mask FALSE)
+ )
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-selection-none img)
+ (gimp-drawable-edit-fill layer2 FILL-BACKGROUND)
+ (gimp-drawable-edit-fill layer3 FILL-BACKGROUND)
+ (gimp-drawable-edit-clear shadow)
+
+ (gimp-item-set-visible bg-layer FALSE)
+ (gimp-item-set-visible shadow FALSE)
+
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask)
+ (gimp-context-set-background '(0 0 0))
+ (gimp-selection-translate img offx1 offy1)
+ (gimp-selection-feather img feather)
+ (gimp-drawable-edit-fill layer2 FILL-BACKGROUND)
+ (gimp-selection-translate img (* 2 offx2) (* 2 offy2))
+ (gimp-drawable-edit-fill layer3 FILL-BACKGROUND)
+ (gimp-selection-none img)
+ (set! layer2 (car (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)))
+ (gimp-drawable-invert layer2 FALSE)
+
+ (gimp-image-insert-layer img layer1 0 0)
+ (copy-layer-chrome-it img layer1 banding-img banding-layer)
+ (gimp-image-delete banding-img)
+ (gimp-layer-scale layer1 width height FALSE)
+ (plug-in-gauss-iir RUN-NONINTERACTIVE img layer1 10 TRUE TRUE)
+ (gimp-layer-set-opacity layer1 50)
+ (set! layer1 (car (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)))
+ (gimp-drawable-curves-spline layer1 HISTOGRAM-VALUE 18 (spline-chrome-it))
+
+ (set! layer-mask (car (gimp-layer-create-mask layer1 ADD-MASK-BLACK)))
+ (gimp-layer-add-mask layer1 layer-mask)
+ (gimp-image-select-item img CHANNEL-OP-REPLACE mask)
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill layer-mask FILL-BACKGROUND)
+
+ (set! layer2 (car (gimp-layer-copy layer1 TRUE)))
+ (gimp-image-insert-layer img layer2 0 0)
+
+ (gimp-brush-set-shape brush-name BRUSH-GENERATED-CIRCLE)
+ (gimp-brush-set-spikes brush-name 2)
+ (gimp-brush-set-hardness brush-name 1.0)
+ (gimp-brush-set-spacing brush-name 25)
+ (gimp-brush-set-aspect-ratio brush-name 1)
+ (gimp-brush-set-angle brush-name 0)
+ (cond (<= brush-size 17) (gimp-brush-set-radius brush-name (\ brush-size 2))
+ (else gimp-brush-set-radius brush-name (\ 19 2)))
+ (gimp-context-set-brush brush-name)
+
+ (gimp-context-set-foreground '(255 255 255))
+ (gimp-drawable-edit-stroke-selection layer-mask)
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-selection-feather img (* feather 1.5))
+ (gimp-selection-translate img (* 2.5 offx1) (* 2.5 offy1))
+ (gimp-drawable-edit-fill shadow FILL-BACKGROUND)
+
+ (gimp-selection-all img)
+ (gimp-context-set-pattern "Marble #1")
+ (gimp-drawable-edit-fill bg-layer FILL-PATTERN)
+ (gimp-selection-none img)
+
+ (gimp-image-convert-rgb img)
+
+ (gimp-drawable-color-balance layer1 TRANSFER-SHADOWS TRUE
+ (shadows (rval hc))
+ (shadows (gval hc))
+ (shadows (bval hc)))
+ (gimp-drawable-color-balance layer1 TRANSFER-MIDTONES TRUE
+ (midtones (rval hc))
+ (midtones (gval hc))
+ (midtones (bval hc)))
+ (gimp-drawable-color-balance layer1 TRANSFER-HIGHLIGHTS TRUE
+ (highlights (rval hc))
+ (highlights (gval hc))
+ (highlights (bval hc)))
+
+ (gimp-drawable-color-balance layer2 TRANSFER-SHADOWS TRUE
+ (shadows (rval cc))
+ (shadows (gval cc))
+ (shadows (bval cc)))
+ (gimp-drawable-color-balance layer2 TRANSFER-MIDTONES TRUE
+ (midtones (rval cc))
+ (midtones (gval cc))
+ (midtones (bval cc)))
+ (gimp-drawable-color-balance layer2 TRANSFER-HIGHLIGHTS TRUE
+ (highlights (rval cc))
+ (highlights (gval cc))
+ (highlights (bval cc)))
+ (gimp-drawable-hue-saturation layer2 HUE-RANGE-ALL
+ 0.0
+ chrome-lightness
+ chrome-saturation
+ 0.0)
+
+ (gimp-item-set-visible shadow TRUE)
+ (gimp-item-set-visible bg-layer TRUE)
+
+ (gimp-item-set-name layer2 _"Chrome")
+ (gimp-item-set-name layer1 _"Highlight")
+
+ (gimp-image-remove-channel img mask)
+
+ (gimp-brush-delete brush-name)
+
+ (gimp-display-new img)
+ (gimp-image-undo-enable img)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-sota-chrome-it"
+ _"Stencil C_hrome..."
+ _"Add a chrome effect to the selected region (or alpha) using a specified (grayscale) stencil"
+ "Spencer Kimball"
+ "Spencer Kimball"
+ "1997"
+ "GRAY"
+ SF-IMAGE "Chrome image" 0
+ SF-DRAWABLE "Chrome mask" 0
+ SF-ADJUSTMENT _"Chrome saturation" '(-80 -100 100 1 10 0 0)
+ SF-ADJUSTMENT _"Chrome lightness" '(-47 -100 100 1 10 0 0)
+ SF-ADJUSTMENT _"Chrome factor" '(0.75 0 1 0.1 0.01 2 0)
+ SF-FILENAME _"Environment map"
+ (string-append gimp-data-directory
+ "/scripts/images/beavis.jpg")
+ SF-COLOR _"Highlight balance" '(211 95 0)
+ SF-COLOR _"Chrome balance" "black"
+ SF-TOGGLE _"Chrome white areas" TRUE
+)
+
+(script-fu-menu-register "script-fu-sota-chrome-it"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/circuit.scm b/plug-ins/script-fu/scripts/circuit.scm
new file mode 100644
index 0000000..174a155
--- /dev/null
+++ b/plug-ins/script-fu/scripts/circuit.scm
@@ -0,0 +1,143 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Circuit board effect
+; Copyright (c) 1997 Adrian Likins
+;
+; Generates what looks a little like the back of an old circuit board.
+; Looks even better when gradient-mapp'ed with a suitable gradient.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-circuit image
+ drawable
+ mask-size
+ seed
+ remove-bg
+ keep-selection
+ separate-layer)
+ (let* (
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (image-height (car (gimp-image-height image)))
+ (active-selection 0)
+ (from-selection 0)
+ (selection-bounds 0)
+ (select-offset-x 0)
+ (select-offset-y 0)
+ (select-width 0)
+ (select-height 0)
+ (effect-layer 0)
+ (active-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-group-start image)
+
+ (gimp-layer-add-alpha drawable)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! active-selection (car (gimp-selection-save image)))
+ (set! from-selection FALSE))
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))))
+
+ (set! selection-bounds (gimp-selection-bounds image))
+ (set! select-offset-x (cadr selection-bounds))
+ (set! select-offset-y (caddr selection-bounds))
+ (set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))
+
+ (if (= separate-layer TRUE)
+ (begin
+ (set! effect-layer (car (gimp-layer-new image
+ select-width
+ select-height
+ type
+ _"Effect layer"
+ 100
+ LAYER-MODE-NORMAL)))
+
+ (gimp-image-insert-layer image effect-layer 0 -1)
+ (gimp-layer-set-offsets effect-layer select-offset-x select-offset-y)
+ (gimp-selection-none image)
+ (gimp-drawable-edit-clear effect-layer)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-edit-copy drawable)
+
+ (let ((floating-sel (car (gimp-edit-paste effect-layer FALSE))))
+ (gimp-floating-sel-anchor floating-sel)
+ )
+ (gimp-image-set-active-layer image effect-layer ))
+ (set! effect-layer drawable)
+ )
+ (set! active-layer effect-layer)
+
+ (if (= remove-bg TRUE)
+ (gimp-context-set-foreground '(0 0 0))
+ (gimp-context-set-foreground '(14 14 14))
+ )
+
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (plug-in-maze RUN-NONINTERACTIVE image active-layer 5 5 TRUE 0 seed 57 1)
+ (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask-size 0)
+ (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 1 0)
+ (if (= type RGBA-IMAGE)
+ (gimp-drawable-desaturate active-layer DESATURATE-LIGHTNESS))
+
+ (if (and
+ (= remove-bg TRUE)
+ (= separate-layer TRUE))
+ (begin
+ (gimp-image-select-color image CHANNEL-OP-REPLACE active-layer '(0 0 0))
+ (gimp-drawable-edit-clear active-layer)))
+
+ (if (= keep-selection FALSE)
+ (gimp-selection-none image))
+
+ (gimp-image-remove-channel image active-selection)
+ (gimp-image-set-active-layer image drawable)
+
+ (gimp-image-undo-group-end image)
+
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-circuit"
+ _"_Circuit..."
+ _"Fill the selected region (or alpha) with traces like those on a circuit board"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "10/17/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Oilify mask size" '(17 3 50 1 10 0 1)
+ SF-ADJUSTMENT _"Circuit seed" '(3 1 3000000 1 10 0 1)
+ SF-TOGGLE _"No background (only for separate layer)" FALSE
+ SF-TOGGLE _"Keep selection" TRUE
+ SF-TOGGLE _"Separate layer" TRUE
+)
+
+(script-fu-menu-register "script-fu-circuit"
+ "<Image>/Filters/Render")
diff --git a/plug-ins/script-fu/scripts/clothify.scm b/plug-ins/script-fu/scripts/clothify.scm
new file mode 100644
index 0000000..7b6f101
--- /dev/null
+++ b/plug-ins/script-fu/scripts/clothify.scm
@@ -0,0 +1,68 @@
+; CLOTHIFY version 1.02
+; Gives the current layer in the indicated image a cloth-like texture.
+; Process invented by Zach Beane (Xath@irc.gimp.net)
+;
+; Tim Newsome <drz@froody.bloke.com> 4/11/97
+
+(define (script-fu-clothify timg tdrawable bx by azimuth elevation depth)
+ (let* (
+ (width (car (gimp-drawable-width tdrawable)))
+ (height (car (gimp-drawable-height tdrawable)))
+ (img (car (gimp-image-new width height RGB)))
+; (layer-two (car (gimp-layer-new img width height RGB-IMAGE "Y Dots" 100 LAYER-MODE-MULTIPLY)))
+ (layer-one (car (gimp-layer-new img width height RGB-IMAGE "X Dots" 100 LAYER-MODE-NORMAL)))
+ (layer-two 0)
+ (bump-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+
+ (gimp-image-insert-layer img layer-one 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill layer-one FILL-BACKGROUND)
+
+ (plug-in-noisify RUN-NONINTERACTIVE img layer-one FALSE 0.7 0.7 0.7 0.7)
+
+ (set! layer-two (car (gimp-layer-copy layer-one 0)))
+ (gimp-layer-set-mode layer-two LAYER-MODE-MULTIPLY)
+ (gimp-image-insert-layer img layer-two 0 0)
+
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img layer-one bx TRUE FALSE)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img layer-two by FALSE TRUE)
+ (gimp-image-flatten img)
+ (set! bump-layer (car (gimp-image-get-active-layer img)))
+
+ (plug-in-c-astretch RUN-NONINTERACTIVE img bump-layer)
+ (plug-in-noisify RUN-NONINTERACTIVE img bump-layer FALSE 0.2 0.2 0.2 0.2)
+
+ (plug-in-bump-map RUN-NONINTERACTIVE img tdrawable bump-layer azimuth elevation depth 0 0 0 0 FALSE FALSE 0)
+ (gimp-image-delete img)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+
+(script-fu-register "script-fu-clothify"
+ _"_Clothify..."
+ _"Add a cloth-like texture to the selected region (or alpha)"
+ "Tim Newsome <drz@froody.bloke.com>"
+ "Tim Newsome"
+ "4/11/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Input image" 0
+ SF-DRAWABLE "Input drawable" 0
+ SF-ADJUSTMENT _"Blur X" '(9 3 100 1 10 0 1)
+ SF-ADJUSTMENT _"Blur Y" '(9 3 100 1 10 0 1)
+ SF-ADJUSTMENT _"Azimuth" '(135 0 360 1 10 1 0)
+ SF-ADJUSTMENT _"Elevation" '(45 0 90 1 10 1 0)
+ SF-ADJUSTMENT _"Depth" '(3 1 50 1 10 0 1)
+)
+
+(script-fu-menu-register "script-fu-clothify"
+ "<Image>/Filters/Artistic")
diff --git a/plug-ins/script-fu/scripts/coffee.scm b/plug-ins/script-fu/scripts/coffee.scm
new file mode 100644
index 0000000..d72bcb7
--- /dev/null
+++ b/plug-ins/script-fu/scripts/coffee.scm
@@ -0,0 +1,94 @@
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-coffee-stain inImage inLayer inNumber inDark)
+ (let* (
+ (theImage inImage)
+ (theHeight (car (gimp-image-height theImage)))
+ (theWidth (car (gimp-image-width theImage)))
+ (theNumber inNumber)
+ (theSize (min theWidth theHeight))
+ (theStain 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-group-start theImage)
+
+ (while (> theNumber 0)
+ (set! theNumber (- theNumber 1))
+ (set! theStain (car (gimp-layer-new theImage theSize theSize
+ RGBA-IMAGE _"Stain" 100
+ (if (= inDark TRUE)
+ LAYER-MODE-DARKEN-ONLY LAYER-MODE-NORMAL))))
+
+ (gimp-image-insert-layer theImage theStain 0 0)
+ (gimp-selection-all theImage)
+ (gimp-drawable-edit-clear theStain)
+
+ (let ((blobSize (/ (rand (- theSize 40)) (+ (rand 3) 1))))
+ (gimp-image-select-ellipse theImage
+ CHANNEL-OP-REPLACE
+ (/ (- theSize blobSize) 2)
+ (/ (- theSize blobSize) 2)
+ blobSize blobSize)
+ )
+
+ (script-fu-distress-selection theImage theStain
+ (- (* (+ (rand 15) 1) (+ (rand 15) 1)) 1)
+ (/ theSize 25) 4 2 TRUE TRUE)
+
+ (gimp-context-set-gradient "Coffee")
+
+ (gimp-drawable-edit-gradient-fill theStain
+ GRADIENT-SHAPEBURST-DIMPLED 0
+ FALSE 0 0
+ TRUE
+ 0 0 0 0)
+
+ (gimp-layer-set-offsets theStain
+ (- (rand theWidth) (/ theSize 2))
+ (- (rand theHeight) (/ theSize 2)))
+ )
+
+ (gimp-selection-none theImage)
+
+ (gimp-image-undo-group-end theImage)
+
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+; Register the function with GIMP:
+
+(script-fu-register "script-fu-coffee-stain"
+ _"_Coffee Stain..."
+ _"Add realistic looking coffee stains to the image"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "25th April 1998"
+ "RGB*"
+ SF-IMAGE "The image" 0
+ SF-DRAWABLE "The layer" 0
+ SF-ADJUSTMENT _"Stains" '(3 1 10 1 1 0 0)
+ SF-TOGGLE _"Darken only" TRUE
+)
+
+(script-fu-menu-register "script-fu-coffee-stain" "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/contactsheet.scm b/plug-ins/script-fu/scripts/contactsheet.scm
new file mode 100644
index 0000000..5f7520e
--- /dev/null
+++ b/plug-ins/script-fu/scripts/contactsheet.scm
@@ -0,0 +1,337 @@
+; "Contact Sheet" v1.2 September 5, 2007
+; by Kevin Cozens <kcozens@interlog.com>
+;
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Version 1.0 (July 27, 2004)
+; Created
+;
+; Version 1.1 (September 2, 2004)
+; Added ability to select sheet size, set font used for sheet and image
+;
+; Version 1.2 (September 5, 2007)
+; Preserve aspect ratio of original image. Center thumbnail in the area
+; allowed for the thumbnail. Added disable/enable of undo operations.
+; Added 1600x1200 sheet size.
+
+(define (script-fu-contactsheet dir sheet-size
+ title-font legend-font text-color bg-color)
+
+ (define (init-sheet-data size)
+ (let (
+ (sheet-w 0)
+ (sheet-h 0)
+ (thumb-w 0)
+ (thumb-h 0)
+ (border-x 0) ;Space between rows and at top and bottom of thumbnails
+ (border-y 0) ;Space between columns and at left and right of thumbnails
+ (off-x 0) ; Additional X shift to properly center a row of thumbnails
+ (off-y 0) ; Additional Y shift to properly center rows of thumbnails
+ (count 0)
+ )
+
+ (case size
+ ((0) (set! sheet-w 640)
+ (set! sheet-h 480)
+ (set! thumb-w 90)
+ (set! thumb-h 68)
+ (set! border-x 32)
+ (set! border-y 23)
+ (set! off-x -1)
+ (set! off-y 0)
+ (set! count 4)
+ )
+
+ ((1) (set! sheet-w 800)
+ (set! sheet-h 600)
+ (set! thumb-w 119)
+ (set! thumb-h 90)
+ (set! border-x 34)
+ (set! border-y 25)
+ (set! off-x 0)
+ (set! off-y 0)
+ (set! count 4)
+ )
+
+ ((2) (set! sheet-w 1024)
+ (set! sheet-h 768)
+ (set! thumb-w 133)
+ (set! thumb-h 100)
+ (set! border-x 32)
+ (set! border-y 24)
+ (set! off-x 1)
+ (set! off-y 0)
+ (set! count 5)
+ )
+
+ ((3) (set! sheet-w 1280)
+ (set! sheet-h 1024)
+ (set! thumb-w 133)
+ (set! thumb-h 100)
+ (set! border-x 24)
+ (set! border-y 25)
+ (set! off-x 0)
+ (set! off-y 0)
+ (set! count 7)
+ )
+
+ ((4) (set! sheet-w 1600)
+ (set! sheet-h 1200)
+ (set! thumb-w 120)
+ (set! thumb-h 90)
+ (set! border-x 36)
+ (set! border-y 25)
+ (set! off-x 2)
+ (set! off-y 0)
+ (set! count 9)
+ )
+ )
+
+ (list sheet-w sheet-h thumb-w thumb-h border-x border-y off-x off-y count)
+ )
+ )
+
+ (define (init-sheet-img img num img-width border-y off-y)
+ (let* (
+ (text-layer 0)
+ (text-width 0)
+ (text-height 0)
+ )
+ (gimp-selection-all img)
+ (gimp-drawable-fill (car (gimp-image-get-active-layer img))
+ FILL-BACKGROUND)
+ (gimp-selection-none img)
+ (set! text-layer (car (gimp-text-fontname img -1 0 0
+ (string-append _"Contact Sheet "
+ (number->string num)
+ _" for directory " dir)
+ 0 TRUE 14 PIXELS title-font)))
+ (set! text-width (car (gimp-drawable-width text-layer)))
+ (set! text-height (car (gimp-drawable-height text-layer)))
+ (gimp-layer-set-offsets text-layer
+ (/ (- img-width text-width) 2)
+ (/ (- (+ border-y off-y) text-height) 2)
+ )
+ (gimp-image-merge-visible-layers img CLIP-TO-IMAGE)
+ )
+ )
+
+ (define (make-thumbnail-size img thumb-w thumb-h)
+ (let* (
+ (file-height (car (gimp-image-height img)))
+ (file-width (car (gimp-image-width img)))
+ (aspect-ratio (/ file-width file-height))
+ )
+
+ ;Preserve the aspect ratio of the original image
+ (if (> file-width file-height)
+ (set! thumb-h (/ thumb-w aspect-ratio))
+ (set! thumb-w (* thumb-h aspect-ratio))
+ )
+
+ (gimp-image-scale img thumb-w thumb-h)
+ )
+ )
+
+ (let* (
+ (dir-stream (dir-open-stream dir))
+ (sheet-num 1)
+ (img-count 0)
+ (pos-x 0)
+ (pos-y 0)
+
+ (sheet-data 0)
+ (sheet-width 0)
+ (sheet-height 0)
+ (thumb-w 0)
+ (thumb-h 0)
+ (border-x 0)
+ (border-y 0)
+ (off-x 0)
+ (off-y 0)
+ (max-x 0)
+ (max-y 0)
+
+ (sheet-img 0)
+ (sheet-layer 0)
+
+ (new-img 0)
+ (file 0)
+ (file-path 0)
+ (tmp-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+ (gimp-context-set-foreground text-color)
+ (gimp-context-set-background bg-color)
+
+ (set! sheet-data (init-sheet-data sheet-size))
+ (set! sheet-width (car sheet-data))
+ (set! sheet-height (cadr sheet-data))
+ (set! sheet-data (cddr sheet-data))
+ (set! thumb-w (car sheet-data))
+ (set! thumb-h (cadr sheet-data))
+ (set! sheet-data (cddr sheet-data))
+ (set! border-x (car sheet-data))
+ (set! border-y (cadr sheet-data))
+ (set! sheet-data (cddr sheet-data))
+ (set! off-x (car sheet-data))
+ (set! off-y (cadr sheet-data))
+ (set! max-x (caddr sheet-data))
+ (set! max-y max-x)
+
+ (set! sheet-img (car (gimp-image-new sheet-width sheet-height RGB)))
+
+ (gimp-image-undo-disable sheet-img)
+
+ (set! sheet-layer (car (gimp-layer-new sheet-img sheet-width sheet-height
+ RGB-IMAGE "Background"
+ 100 LAYER-MODE-NORMAL)))
+ (gimp-image-insert-layer sheet-img sheet-layer 0 0)
+
+ (init-sheet-img sheet-img sheet-num sheet-width border-y off-y)
+
+ (if (not dir-stream)
+ (gimp-message (string-append _"Unable to open directory " dir))
+ (begin
+ (do
+ ( (file (dir-read-entry dir-stream) (dir-read-entry dir-stream)) )
+ ( (eof-object? file) )
+
+ (set! file-path (string-append dir DIR-SEPARATOR file))
+ (if (and (not (re-match "index.*" file))
+ (= (file-type file-path) FILE-TYPE-FILE)
+ )
+ (catch ()
+ (set! new-img
+ (car (gimp-file-load RUN-NONINTERACTIVE file-path file)))
+
+ (make-thumbnail-size new-img thumb-w thumb-h)
+
+ (if (> (car (gimp-image-get-layers new-img)) 1)
+ (gimp-image-flatten new-img)
+ )
+ (set! tmp-layer (car (gimp-layer-new-from-drawable
+ (car (gimp-image-get-active-drawable new-img))
+ sheet-img)))
+
+ (gimp-image-insert-layer sheet-img tmp-layer 0 0)
+
+ ;Move thumbnail in to position and center it in area available.
+ (gimp-layer-set-offsets tmp-layer
+ (+ border-x off-x (* pos-x (+ thumb-w border-x))
+ (/ (- thumb-w (car (gimp-image-width new-img))) 2)
+ )
+ (+ border-y off-y (* pos-y (+ thumb-h border-y))
+ (/ (- thumb-h (car (gimp-image-height new-img))) 2)
+ )
+ )
+
+ (gimp-image-delete new-img)
+
+ (set! tmp-layer (car (gimp-text-fontname sheet-img -1 0 0 file
+ 0 TRUE 12 PIXELS legend-font)))
+ (gimp-layer-set-offsets tmp-layer
+ (+ border-x off-x (* pos-x (+ thumb-w border-x))
+ (/ (- thumb-w (car (gimp-drawable-width tmp-layer))) 2))
+ (+ border-y off-y (* pos-y (+ thumb-h border-y)) thumb-h 6)
+ )
+
+ (set! img-count (+ img-count 1))
+
+ (set! pos-x (+ pos-x 1))
+ (if (> pos-x max-x)
+ (begin
+ (set! pos-x 0)
+ (set! pos-y (+ pos-y 1))
+ (if (> pos-y max-y)
+ (begin
+ (set! pos-y 0)
+ (set! sheet-layer (car (gimp-image-flatten sheet-img)))
+ (gimp-file-save
+ RUN-NONINTERACTIVE
+ sheet-img
+ sheet-layer
+ (string-append dir DIR-SEPARATOR
+ "index" (number->string sheet-num) ".jpg")
+ (string-append
+ "index" (number->string sheet-num) ".jpg")
+ )
+
+ (set! sheet-num (+ sheet-num 1))
+ (init-sheet-img sheet-img sheet-num sheet-width
+ border-y off-y)
+ (set! img-count 0)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+
+ (dir-close-stream dir-stream)
+
+ (if (> img-count 0)
+ (begin
+ (set! sheet-layer (car (gimp-image-flatten sheet-img)))
+ (gimp-file-save
+ RUN-NONINTERACTIVE
+ sheet-img
+ sheet-layer
+ (string-append dir DIR-SEPARATOR
+ "index" (number->string sheet-num) ".jpg")
+ (string-append "index" (number->string sheet-num) ".jpg")
+ )
+ )
+ )
+ )
+
+ (gimp-image-undo-enable sheet-img)
+ (gimp-image-delete sheet-img)
+
+ (display (string-append _"Created " (number->string sheet-num)
+ _" contact sheets from a total of "
+ (number->string img-count) _" images"))
+ (newline)
+ )
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-contactsheet"
+ _"_Contact Sheet..."
+ _"Create a series of images containing thumbnail sized versions of all of the images in a specified directory."
+ "Kevin Cozens <kcozens@interlog.com>"
+ "Kevin Cozens"
+ "July 19, 2004"
+ ""
+ SF-DIRNAME _"Images Directory" "/tmp/test"
+ SF-OPTION _"Sheet size" '("640 x 480"
+ "800 x 600"
+ "1024 x 768"
+ "1280 x 1024"
+ "1600 x 1200")
+ SF-FONT _"Title font" "Sans Bold Italic"
+ SF-FONT _"Legend font" "Sans Bold"
+ SF-COLOR _"Text color" "white"
+ SF-COLOR _"Background color" "black"
+)
+
+(script-fu-menu-register "script-fu-contactsheet" "<Image>/Filters/Combine")
diff --git a/plug-ins/script-fu/scripts/copy-visible.scm b/plug-ins/script-fu/scripts/copy-visible.scm
new file mode 100644
index 0000000..942793c
--- /dev/null
+++ b/plug-ins/script-fu/scripts/copy-visible.scm
@@ -0,0 +1,49 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; "Copy Visible" -- copy the visible selection so that it can be pasted easily
+; Copyright (C) 2004 Raphaël Quinet, Adrian Likins, Sven Neumann
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; 2004-04-14 This script was almost rewritten from scratch
+; by Raphaël Quinet <raphael@gimp.org>
+; see also http://bugzilla.gnome.org/show_bug.cgi?id=139989
+;
+; The code is new but the API is the same as in the previous version:
+; "Copy Visible" version 0.11 01/24/98
+; by Adrian Likins <adrian@gimp.org>
+; _heavily_ based on:
+; cyn-merge.scm version 0.02 10/10/97
+; Copyright (C) 1997 Sven Neumann (neumanns@uni-duesseldorf.de)
+;
+; Removed all code and made it a backward-compat wrapper around
+; (gimp-edit-copy-visible)
+; 2004-12-12 Michael Natterer <mitch@gimp.org>
+;
+
+(define (script-fu-copy-visible image drawable)
+ (gimp-edit-copy-visible image)
+)
+
+(script-fu-register "script-fu-copy-visible"
+ "Copy Visible"
+ "This procedure is deprecated! Use \'gimp-edit-copy-visible\' instead."
+ ""
+ ""
+ ""
+ "RGB* INDEXED* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+)
diff --git a/plug-ins/script-fu/scripts/difference-clouds.scm b/plug-ins/script-fu/scripts/difference-clouds.scm
new file mode 100644
index 0000000..15b8fc8
--- /dev/null
+++ b/plug-ins/script-fu/scripts/difference-clouds.scm
@@ -0,0 +1,80 @@
+; Plugin for the GNU Image Manipulation Program
+; Copyright (C) 2006 Martin Nordholts
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Renders Difference Clouds onto a layer, i.e. solid noise merged down with the
+; Difference Mode
+;
+
+(define (script-fu-difference-clouds image
+ drawable)
+
+ (let* ((draw-offset-x (car (gimp-drawable-offsets drawable)))
+ (draw-offset-y (cadr (gimp-drawable-offsets drawable)))
+ (has-sel (car (gimp-drawable-mask-intersect drawable)))
+ (sel-offset-x (cadr (gimp-drawable-mask-intersect drawable)))
+ (sel-offset-y (caddr (gimp-drawable-mask-intersect drawable)))
+ (width (cadddr (gimp-drawable-mask-intersect drawable)))
+ (height (caddr (cddr (gimp-drawable-mask-intersect drawable))))
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (diff-clouds -1)
+ (offset-x 0)
+ (offset-y 0)
+ )
+
+ (gimp-image-undo-group-start image)
+
+ ; Create the cloud layer
+ (set! diff-clouds (car (gimp-layer-new image width height type
+ "Clouds" 100 LAYER-MODE-DIFFERENCE)))
+
+ ; Add the cloud layer above the current layer
+ (gimp-image-insert-layer image diff-clouds 0 -1)
+
+ ; Clear the layer (so there are no noise in it)
+ (gimp-drawable-fill diff-clouds FILL-TRANSPARENT)
+
+ ; Selections are relative to the drawable; adjust the final offset
+ (set! offset-x (+ draw-offset-x sel-offset-x))
+ (set! offset-y (+ draw-offset-y sel-offset-y))
+
+ ; Offset the clouds layer
+ (if (gimp-item-is-layer drawable)
+ (gimp-item-transform-translate diff-clouds offset-x offset-y))
+
+ ; Show the solid noise dialog
+ (plug-in-solid-noise SF-RUN-MODE image diff-clouds 0 0 0 1 4.0 4.0)
+
+ ; Merge the clouds layer with the layer below
+ (gimp-image-merge-down image diff-clouds EXPAND-AS-NECESSARY)
+
+ (gimp-image-undo-group-end image)
+
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-difference-clouds"
+ _"_Difference Clouds..."
+ _"Solid noise applied with Difference layer mode"
+ "Martin Nordholts <enselic@hotmail.com>"
+ "Martin Nordholts"
+ "2006/10/25"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0)
+
+(script-fu-menu-register "script-fu-difference-clouds"
+ "<Image>/Filters/Render/Noise")
diff --git a/plug-ins/script-fu/scripts/distress-selection.scm b/plug-ins/script-fu/scripts/distress-selection.scm
new file mode 100644
index 0000000..ef87b79
--- /dev/null
+++ b/plug-ins/script-fu/scripts/distress-selection.scm
@@ -0,0 +1,122 @@
+;
+; distress selection
+;
+;
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+; Define the function:
+
+(define (script-fu-distress-selection inImage
+ inDrawable
+ inThreshold
+ inSpread
+ inGranu
+ inSmooth
+ inSmoothH
+ inSmoothV)
+
+ (let (
+ (theImage inImage)
+ (theWidth (car (gimp-image-width inImage)))
+ (theHeight (car (gimp-image-height inImage)))
+ (theLayer 0)
+ (theMode (car (gimp-image-base-type inImage)))
+ (prevLayer (car (gimp-image-get-active-layer inImage)))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+ (gimp-image-undo-group-start theImage)
+
+ (if (= theMode GRAY)
+ (set! theMode GRAYA-IMAGE)
+ (set! theMode RGBA-IMAGE)
+ )
+ (set! theLayer (car (gimp-layer-new theImage
+ theWidth
+ theHeight
+ theMode
+ "Distress Scratch Layer"
+ 100
+ LAYER-MODE-NORMAL)))
+
+ (gimp-image-insert-layer theImage theLayer 0 0)
+
+ (if (= FALSE (car (gimp-selection-is-empty theImage)))
+ (gimp-drawable-edit-fill theLayer FILL-BACKGROUND)
+ )
+
+ (gimp-selection-invert theImage)
+
+ (if (= FALSE (car (gimp-selection-is-empty theImage)))
+ (gimp-drawable-edit-clear theLayer)
+ )
+
+ (gimp-selection-invert theImage)
+ (gimp-selection-none inImage)
+
+ (gimp-layer-scale theLayer
+ (/ theWidth inGranu)
+ (/ theHeight inGranu)
+ TRUE)
+
+ (plug-in-spread RUN-NONINTERACTIVE
+ theImage
+ theLayer
+ inSpread
+ inSpread)
+
+ (plug-in-gauss-iir RUN-NONINTERACTIVE
+ theImage theLayer inSmooth inSmoothH inSmoothV)
+ (gimp-layer-scale theLayer theWidth theHeight TRUE)
+ (plug-in-threshold-alpha RUN-NONINTERACTIVE theImage theLayer inThreshold)
+ (plug-in-gauss-iir RUN-NONINTERACTIVE theImage theLayer 1 TRUE TRUE)
+ (gimp-image-select-item inImage CHANNEL-OP-REPLACE theLayer)
+ (gimp-image-remove-layer theImage theLayer)
+ (if (and (= (car (gimp-item-is-channel inDrawable)) TRUE)
+ (= (car (gimp-item-is-layer-mask inDrawable)) FALSE))
+ (gimp-image-set-active-channel theImage inDrawable)
+ )
+ (gimp-image-undo-group-end theImage)
+
+ (gimp-image-set-active-layer theImage prevLayer)
+
+ (gimp-displays-flush)
+ (gimp-context-pop)
+ )
+)
+
+
+(script-fu-register "script-fu-distress-selection"
+ _"_Distort..."
+ _"Distress the selection"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "23rd April 1998"
+ "RGB*,GRAY*"
+ SF-IMAGE "The image" 0
+ SF-DRAWABLE "The layer" 0
+ SF-ADJUSTMENT _"_Threshold (bigger 1<-->254 smaller)" '(127 1 254 1 10 0 0)
+ SF-ADJUSTMENT _"_Spread" '(8 0 1000 1 10 0 1)
+ SF-ADJUSTMENT _"_Granularity (1 is low)" '(4 1 25 1 10 0 1)
+ SF-ADJUSTMENT _"S_mooth" '(2 1 150 1 10 0 1)
+ SF-TOGGLE _"Smooth hor_izontally" TRUE
+ SF-TOGGLE _"Smooth _vertically" TRUE
+)
+
+(script-fu-menu-register "script-fu-distress-selection"
+ "<Image>/Select/Modify")
diff --git a/plug-ins/script-fu/scripts/drop-shadow.scm b/plug-ins/script-fu/scripts/drop-shadow.scm
new file mode 100644
index 0000000..c1b243a
--- /dev/null
+++ b/plug-ins/script-fu/scripts/drop-shadow.scm
@@ -0,0 +1,187 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; drop-shadow.scm version 1.05 2011/4/21
+;
+; CHANGE-LOG:
+; 1.00 - initial release
+; 1.01 - fixed the problem with a remaining copy of the selection
+; 1.02 - some code cleanup, no real changes
+; 1.03 - can't call gimp-drawable-edit-fill until layer is added to image!
+; 1.04
+; 1.05 - replaced deprecated function calls with new ones for 2.8
+;
+; Copyright (C) 1997-1999 Sven Neumann <sven@gimp.org>
+;
+;
+; Adds a drop-shadow of the current selection or alpha-channel.
+;
+; This script is derived from my script add-shadow, which has become
+; obsolete now. Thanks to Andrew Donkin (ard@cs.waikato.ac.nz) for his
+; idea to add alpha-support to add-shadow.
+
+
+(define (script-fu-drop-shadow image
+ drawable
+ shadow-transl-x
+ shadow-transl-y
+ shadow-blur
+ shadow-color
+ shadow-opacity
+ allow-resize)
+ (let* (
+ (shadow-blur (max shadow-blur 0))
+ (shadow-opacity (min shadow-opacity 100))
+ (shadow-opacity (max shadow-opacity 0))
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (image-height (car (gimp-image-height image)))
+ (from-selection 0)
+ (active-selection 0)
+ (shadow-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-set-active-layer image drawable)
+
+ (gimp-image-undo-group-start image)
+
+ (gimp-layer-add-alpha drawable)
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! from-selection FALSE))
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))))
+
+ (let* ((selection-bounds (gimp-selection-bounds image))
+ (select-offset-x (cadr selection-bounds))
+ (select-offset-y (caddr selection-bounds))
+ (select-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (select-height (- (caddr (cddr selection-bounds)) select-offset-y))
+
+ (shadow-width (+ select-width (* 2 shadow-blur)))
+ (shadow-height (+ select-height (* 2 shadow-blur)))
+
+ (shadow-offset-x (- select-offset-x shadow-blur))
+ (shadow-offset-y (- select-offset-y shadow-blur)))
+
+ (if (= allow-resize TRUE)
+ (let* ((new-image-width image-width)
+ (new-image-height image-height)
+ (image-offset-x 0)
+ (image-offset-y 0))
+
+ (if (< (+ shadow-offset-x shadow-transl-x) 0)
+ (begin
+ (set! image-offset-x (- 0 (+ shadow-offset-x
+ shadow-transl-x)))
+ (set! shadow-offset-x (- 0 shadow-transl-x))
+ (set! new-image-width (+ new-image-width image-offset-x))))
+
+ (if (< (+ shadow-offset-y shadow-transl-y) 0)
+ (begin
+ (set! image-offset-y (- 0 (+ shadow-offset-y
+ shadow-transl-y)))
+ (set! shadow-offset-y (- 0 shadow-transl-y))
+ (set! new-image-height (+ new-image-height image-offset-y))))
+
+ (if (> (+ (+ shadow-width shadow-offset-x) shadow-transl-x)
+ new-image-width)
+ (set! new-image-width
+ (+ (+ shadow-width shadow-offset-x) shadow-transl-x)))
+
+ (if (> (+ (+ shadow-height shadow-offset-y) shadow-transl-y)
+ new-image-height)
+ (set! new-image-height
+ (+ (+ shadow-height shadow-offset-y) shadow-transl-y)))
+
+ (gimp-image-resize image
+ new-image-width
+ new-image-height
+ image-offset-x
+ image-offset-y)
+ )
+ )
+
+ (set! shadow-layer (car (gimp-layer-new image
+ shadow-width
+ shadow-height
+ type
+ "Drop Shadow"
+ shadow-opacity
+ LAYER-MODE-NORMAL)))
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-insert-layer image shadow-layer 0 -1)
+ (gimp-layer-set-offsets shadow-layer
+ shadow-offset-x
+ shadow-offset-y))
+
+ (gimp-drawable-fill shadow-layer FILL-TRANSPARENT)
+ (gimp-context-set-background shadow-color)
+ (gimp-drawable-edit-fill shadow-layer FILL-BACKGROUND)
+ (gimp-selection-none image)
+ (gimp-layer-set-lock-alpha shadow-layer FALSE)
+ (if (>= shadow-blur 1.0) (plug-in-gauss-rle RUN-NONINTERACTIVE
+ image
+ shadow-layer
+ shadow-blur
+ TRUE
+ TRUE))
+ (gimp-item-transform-translate shadow-layer shadow-transl-x shadow-transl-y)
+
+ (if (= from-selection TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-drawable-edit-clear shadow-layer)
+ (gimp-image-remove-channel image active-selection)))
+
+ (if (and
+ (= (car (gimp-layer-is-floating-sel drawable)) 0)
+ (= from-selection FALSE))
+ (gimp-image-raise-item image drawable))
+
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-drop-shadow"
+ _"_Drop Shadow (legacy)..."
+ _"Add a drop shadow to the selected region (or alpha)"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "1999/12/21"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Offset X" '(4 -4096 4096 1 10 0 1)
+ SF-ADJUSTMENT _"Offset Y" '(4 -4096 4096 1 10 0 1)
+ SF-ADJUSTMENT _"Blur radius" '(15 0 1024 1 10 0 1)
+ SF-COLOR _"Color" "black"
+ SF-ADJUSTMENT _"Opacity" '(60 0 100 1 10 0 0)
+ SF-TOGGLE _"Allow resizing" TRUE
+)
+
+(script-fu-menu-register "script-fu-drop-shadow"
+ "<Image>/Filters/Light and Shadow/Shadow")
diff --git a/plug-ins/script-fu/scripts/erase-rows.scm b/plug-ins/script-fu/scripts/erase-rows.scm
new file mode 100644
index 0000000..4ae4c77
--- /dev/null
+++ b/plug-ins/script-fu/scripts/erase-rows.scm
@@ -0,0 +1,71 @@
+(define (script-fu-erase-rows img drawable orientation which type)
+ (script-fu-erase-nth-rows img drawable orientation which type 2)
+)
+
+(define (script-fu-erase-nth-rows img drawable orientation offset type nth)
+ (let* (
+ (width (car (gimp-drawable-width drawable)))
+ (height (car (gimp-drawable-height drawable)))
+ (position-x (car (gimp-drawable-offsets drawable)))
+ (position-y (cadr (gimp-drawable-offsets drawable)))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+ (gimp-context-set-feather FALSE)
+ (gimp-image-undo-group-start img)
+ (letrec ((loop (lambda (i max)
+ (if (< i max)
+ (begin
+ (if (= orientation 0)
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE position-x (+ i position-y) width 1)
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE (+ i position-x) position-y 1 height))
+ (if (= type 0)
+ (gimp-drawable-edit-clear drawable)
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND))
+ (loop (+ i nth) max))))))
+ (loop offset
+ (if (= orientation 0)
+ height
+ width)
+ )
+ )
+ (gimp-selection-none img)
+ (gimp-image-undo-group-end img)
+ (gimp-context-pop)
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-erase-nth-rows"
+ _"_Erase Every Nth Row..."
+ _"Erase every nth row or column"
+ "Federico Mena Quintero, Nikc M. (Altered)"
+ "Federico Mena Quintero"
+ "June 1997, February 2020"
+ "RGB* GRAY* INDEXED*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-OPTION _"Rows/cols" '(_"Rows" _"Columns")
+ SF-ADJUSTMENT "Offset" '(0 0 1024 1 10 0 SF-SPINNER)
+ SF-OPTION _"Erase/fill" '(_"Erase" _"Fill with BG")
+ SF-ADJUSTMENT "Skip by" '(1 1 1024 1 10 0 SF-SPINNER)
+)
+
+(script-fu-register "script-fu-erase-rows"
+ _"_Erase Every Other Row..."
+ _"Erase every other row or column"
+ "Federico Mena Quintero"
+ "Federico Mena Quintero"
+ "June 1997"
+ "RGB* GRAY* INDEXED*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-OPTION _"Rows/cols" '(_"Rows" _"Columns")
+ SF-OPTION _"Even/odd" '(_"Even" _"Odd")
+ SF-OPTION _"Erase/fill" '(_"Erase" _"Fill with BG")
+)
+
+; (script-fu-menu-register "script-fu-erase-rows"
+; "<Image>/Filters/Distorts")
diff --git a/plug-ins/script-fu/scripts/font-map.scm b/plug-ins/script-fu/scripts/font-map.scm
new file mode 100644
index 0000000..7481d28
--- /dev/null
+++ b/plug-ins/script-fu/scripts/font-map.scm
@@ -0,0 +1,168 @@
+;; font-select
+;; Spencer Kimball
+
+(define (script-fu-font-map text
+ use-name
+ labels
+ font-filter
+ font-size
+ border
+ colors)
+
+ (define (max-font-width text use-name list-cnt list font-size)
+ (let* ((count 0)
+ (width 0)
+ (maxwidth 0)
+ (font "")
+ (extents '()))
+ (while (< count list-cnt)
+ (set! font (car list))
+
+ (if (= use-name TRUE)
+ (set! text font))
+ (set! extents (gimp-text-get-extents-fontname text
+ font-size PIXELS
+ font))
+ (set! width (car extents))
+ (if (> width maxwidth)
+ (set! maxwidth width))
+
+ (set! list (cdr list))
+ (set! count (+ count 1))
+ )
+
+ maxwidth
+ )
+ )
+
+ (define (max-font-height text use-name list-cnt list font-size)
+ (let* ((count 0)
+ (height 0)
+ (maxheight 0)
+ (font "")
+ (extents '()))
+ (while (< count list-cnt)
+ (set! font (car list))
+
+ (if (= use-name TRUE)
+ (set! text font)
+ )
+ (set! extents (gimp-text-get-extents-fontname text
+ font-size PIXELS
+ font))
+ (set! height (cadr extents))
+ (if (> height maxheight)
+ (set! maxheight height)
+ )
+
+ (set! list (cdr list))
+ (set! count (+ count 1))
+ )
+
+ maxheight
+ )
+ )
+
+ (let* (
+ (font-data (gimp-fonts-get-list font-filter))
+ (font-list (cadr font-data))
+ (num-fonts (car font-data))
+ (label-size (/ font-size 2))
+ (border (+ border (* labels (/ label-size 2))))
+ (y border)
+ (maxheight (max-font-height text use-name num-fonts font-list font-size))
+ (maxwidth (max-font-width text use-name num-fonts font-list font-size))
+ (width (+ maxwidth (* 2 border)))
+ (height (+ (+ (* maxheight num-fonts) (* 2 border))
+ (* labels (* label-size num-fonts))))
+ (img (car (gimp-image-new width height (if (= colors 0)
+ GRAY RGB))))
+ (drawable (car (gimp-layer-new img width height (if (= colors 0)
+ GRAY-IMAGE RGB-IMAGE)
+ "Background" 100 LAYER-MODE-NORMAL)))
+ (count 0)
+ (font "")
+ )
+
+ (gimp-context-push)
+
+ (gimp-image-undo-disable img)
+
+ (if (= colors 0)
+ (begin
+ (gimp-context-set-background '(255 255 255))
+ (gimp-context-set-foreground '(0 0 0))))
+
+ (gimp-image-insert-layer img drawable 0 0)
+ (gimp-drawable-edit-clear drawable)
+
+ (if (= labels TRUE)
+ (begin
+ (set! drawable (car (gimp-layer-new img width height
+ (if (= colors 0)
+ GRAYA-IMAGE RGBA-IMAGE)
+ "Labels" 100 LAYER-MODE-NORMAL)))
+ (gimp-image-insert-layer img drawable 0 -1)))
+ (gimp-drawable-edit-clear drawable)
+
+ (while (< count num-fonts)
+ (set! font (car font-list))
+
+ (if (= use-name TRUE)
+ (set! text font))
+
+ (gimp-text-fontname img -1
+ border
+ y
+ text
+ 0 TRUE font-size PIXELS
+ font)
+
+ (set! y (+ y maxheight))
+
+ (if (= labels TRUE)
+ (begin
+ (gimp-floating-sel-anchor (car (gimp-text-fontname img drawable
+ (- border
+ (/ label-size 2))
+ (- y
+ (/ label-size 2))
+ font
+ 0 TRUE
+ label-size PIXELS
+ "Sans")))
+ (set! y (+ y label-size))
+ )
+ )
+
+ (set! font-list (cdr font-list))
+ (set! count (+ count 1))
+ )
+
+ (gimp-image-set-active-layer img drawable)
+
+ (gimp-image-undo-enable img)
+ (gimp-display-new img)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-font-map"
+ _"Render _Font Map..."
+ _"Create an image filled with previews of fonts matching a fontname filter"
+ "Spencer Kimball"
+ "Spencer Kimball"
+ "1997"
+ ""
+ SF-STRING _"_Text" "How quickly daft jumping zebras vex."
+ SF-TOGGLE _"Use font _name as text" FALSE
+ SF-TOGGLE _"_Labels" TRUE
+ SF-STRING _"_Filter (regexp)" "Sans"
+ SF-ADJUSTMENT _"Font _size (pixels)" '(32 2 1000 1 10 0 1)
+ SF-ADJUSTMENT _"_Border (pixels)" '(10 0 200 1 10 0 1)
+ SF-OPTION _"_Color scheme" '(_"Black on white" _"Active colors")
+)
+
+(script-fu-menu-register "script-fu-font-map"
+ "<Fonts>")
diff --git a/plug-ins/script-fu/scripts/fuzzyborder.scm b/plug-ins/script-fu/scripts/fuzzyborder.scm
new file mode 100644
index 0000000..9c3ddbe
--- /dev/null
+++ b/plug-ins/script-fu/scripts/fuzzyborder.scm
@@ -0,0 +1,168 @@
+;
+; fuzzy-border
+;
+; Do a cool fade to a given color at the border of an image (optional shadow)
+; Will make image RGB if it isn't already.
+;
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+; Define the function:
+
+(define (script-fu-fuzzy-border inImage
+ inLayer
+ inColor
+ inSize
+ inBlur
+ inGranu
+ inShadow
+ inShadWeight
+ inCopy
+ inFlatten
+ )
+
+ (define (chris-color-edge inImage inLayer inColor inSize)
+ (gimp-selection-all inImage)
+ (gimp-selection-shrink inImage inSize)
+ (gimp-selection-invert inImage)
+ (gimp-context-set-background inColor)
+ (gimp-drawable-edit-fill inLayer FILL-BACKGROUND)
+ (gimp-selection-none inImage)
+ )
+
+ (let (
+ (theWidth (car (gimp-image-width inImage)))
+ (theHeight (car (gimp-image-height inImage)))
+ (theImage (if (= inCopy TRUE) (car (gimp-image-duplicate inImage))
+ inImage))
+ (theLayer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (if (= inCopy TRUE)
+ (gimp-image-undo-disable theImage)
+ (gimp-image-undo-group-start theImage)
+ )
+
+ (gimp-selection-all theImage)
+
+ (if (> (car (gimp-drawable-type inLayer)) 1)
+ (gimp-image-convert-rgb theImage)
+ )
+
+ (set! theLayer (car (gimp-layer-new theImage
+ theWidth
+ theHeight
+ RGBA-IMAGE
+ "layer 1"
+ 100
+ LAYER-MODE-NORMAL)))
+
+ (gimp-image-insert-layer theImage theLayer 0 0)
+
+
+ (gimp-drawable-edit-clear theLayer)
+ (chris-color-edge theImage theLayer inColor inSize)
+
+ (gimp-layer-scale theLayer
+ (/ theWidth inGranu)
+ (/ theHeight inGranu)
+ TRUE)
+
+ (plug-in-spread RUN-NONINTERACTIVE
+ theImage
+ theLayer
+ (/ inSize inGranu)
+ (/ inSize inGranu))
+ (chris-color-edge theImage theLayer inColor 1)
+ (gimp-layer-scale theLayer theWidth theHeight TRUE)
+
+ (gimp-image-select-item theImage CHANNEL-OP-REPLACE theLayer)
+ (gimp-selection-invert theImage)
+ (gimp-drawable-edit-clear theLayer)
+ (gimp-selection-invert theImage)
+ (gimp-drawable-edit-clear theLayer)
+ (gimp-context-set-background inColor)
+ (gimp-drawable-edit-fill theLayer FILL-BACKGROUND)
+ (gimp-selection-none theImage)
+ (chris-color-edge theImage theLayer inColor 1)
+
+ (if (= inBlur TRUE)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ theImage theLayer inSize TRUE TRUE)
+ )
+ (if (= inShadow TRUE)
+ (begin
+ (gimp-image-insert-layer theImage
+ (car (gimp-layer-copy theLayer FALSE)) 0 -1)
+ (gimp-layer-scale theLayer
+ (- theWidth inSize) (- theHeight inSize) TRUE)
+ (gimp-drawable-desaturate theLayer DESATURATE-LIGHTNESS)
+ (gimp-drawable-brightness-contrast theLayer 0.5 0.5)
+ (gimp-drawable-invert theLayer FALSE)
+ (gimp-layer-resize theLayer
+ theWidth
+ theHeight
+ (/ inSize 2)
+ (/ inSize 2))
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ theImage
+ theLayer
+ (/ inSize 2)
+ TRUE
+ TRUE)
+ (gimp-layer-set-opacity theLayer inShadWeight)
+ )
+ )
+ (if (= inFlatten TRUE)
+ (gimp-image-flatten theImage)
+ )
+ (if (= inCopy TRUE)
+ (begin (gimp-image-clean-all theImage)
+ (gimp-display-new theImage)
+ (gimp-image-undo-enable theImage)
+ )
+ (gimp-image-undo-group-end theImage)
+ )
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-fuzzy-border"
+ _"_Fuzzy Border..."
+ _"Add a jagged, fuzzy border to an image"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "3rd April 1998"
+ "RGB* GRAY*"
+ SF-IMAGE "The image" 0
+ SF-DRAWABLE "The layer" 0
+ SF-COLOR _"Color" "white"
+ SF-ADJUSTMENT _"Border size" '(16 1 300 1 10 0 1)
+ SF-TOGGLE _"Blur border" TRUE
+ SF-ADJUSTMENT _"Granularity (1 is Low)" '(4 1 16 0.25 5 2 0)
+ SF-TOGGLE _"Add shadow" FALSE
+ SF-ADJUSTMENT _"Shadow weight (%)" '(100 0 100 1 10 0 0)
+ SF-TOGGLE _"Work on copy" TRUE
+ SF-TOGGLE _"Flatten image" TRUE
+)
+
+(script-fu-menu-register "script-fu-fuzzy-border"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/gimp-online.scm b/plug-ins/script-fu/scripts/gimp-online.scm
new file mode 100644
index 0000000..8d3569a
--- /dev/null
+++ b/plug-ins/script-fu/scripts/gimp-online.scm
@@ -0,0 +1,277 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; gimp-online.scm
+; Copyright (C) 2003 Henrik Brix Andersen <brix@gimp.org>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+(define (gimp-online-docs-web-site)
+ (plug-in-web-browser "https://docs.gimp.org/")
+)
+
+(define (gimp-help-concepts-usage)
+ (gimp-help "" "gimp-concepts-usage")
+)
+
+(define (gimp-help-using-docks)
+ (gimp-help "" "gimp-concepts-docks")
+)
+
+(define (gimp-help-using-simpleobjects)
+ (gimp-help "" "gimp-using-simpleobjects")
+)
+
+(define (gimp-help-using-selections)
+ (gimp-help "" "gimp-using-selections")
+)
+
+(define (gimp-help-using-fileformats)
+ (gimp-help "" "gimp-using-fileformats")
+)
+
+(define (gimp-help-using-photography)
+ (gimp-help "" "gimp-using-photography")
+)
+
+(define (gimp-help-using-web)
+ (gimp-help "" "gimp-using-web")
+)
+
+(define (gimp-help-concepts-paths)
+ (gimp-help "" "gimp-concepts-paths")
+)
+
+
+; shortcuts to help topics
+(script-fu-register "gimp-help-concepts-paths"
+ _"Using _Paths"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-concepts-paths"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-web"
+ _"_Preparing your Images for the Web"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-web"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-photography"
+ _"_Working with Digital Camera Photos"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-photography"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-fileformats"
+ _"Create, Open and Save _Files"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-fileformats"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-concepts-usage"
+ _"_Basic Concepts"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-concepts-usage"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-docks"
+ _"How to Use _Dialogs"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-docks"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-simpleobjects"
+ _"Drawing _Simple Objects"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-simpleobjects"
+ "<Image>/Help/User Manual")
+
+
+(script-fu-register "gimp-help-using-selections"
+ _"Create and Use _Selections"
+ _"Bookmark to the user manual"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-help-using-simpleobjects"
+ "<Image>/Help/User Manual")
+
+
+;; Links to GIMP related web sites
+
+(define (gimp-online-main-web-site)
+ (plug-in-web-browser "https://www.gimp.org/")
+)
+
+(define (gimp-online-developer-web-site)
+ (plug-in-web-browser "https://developer.gimp.org/")
+)
+
+(define (gimp-online-roadmap)
+ (plug-in-web-browser "https://wiki.gimp.org/wiki/Roadmap")
+)
+
+(define (gimp-online-wiki)
+ (plug-in-web-browser "https://wiki.gimp.org/wiki/Main_Page")
+)
+
+(define (gimp-online-bugs-features)
+ (plug-in-web-browser "https://gitlab.gnome.org/GNOME/gimp/issues")
+)
+
+; (define (gimp-online-plug-in-web-site)
+; (plug-in-web-browser "https://registry.gimp.org/")
+; )
+
+
+(script-fu-register "gimp-online-main-web-site"
+ _"_Main Web Site"
+ _"Bookmark to the GIMP web site"
+ "Henrik Brix Andersen <brix@gimp.org>"
+ "Henrik Brix Andersen <brix@gimp.org>"
+ "2003"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-main-web-site"
+ "<Image>/Help/GIMP Online")
+
+
+(script-fu-register "gimp-online-developer-web-site"
+ _"_Developer Web Site"
+ _"Bookmark to the GIMP web site"
+ "Henrik Brix Andersen <brix@gimp.org>"
+ "Henrik Brix Andersen <brix@gimp.org>"
+ "2003"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-developer-web-site"
+ "<Image>/Help/GIMP Online")
+
+
+(script-fu-register "gimp-online-roadmap"
+ _"_Roadmap"
+ _"Bookmark to the roadmap of GIMP"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "2018"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-roadmap"
+ "<Image>/Help/GIMP Online")
+
+
+(script-fu-register "gimp-online-wiki"
+ _"_Wiki"
+ _"Bookmark to the wiki of GIMP"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "2018"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-wiki"
+ "<Image>/Help/GIMP Online")
+
+
+(script-fu-register "gimp-online-bugs-features"
+ _"_Bug Reports and Feature Requests"
+ _"Bookmark to the bug tracker of GIMP"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>"
+ "2018"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-bugs-features"
+ "<Image>/Help")
+
+
+(script-fu-register "gimp-online-docs-web-site"
+ _"_User Manual Web Site"
+ _"Bookmark to the GIMP web site"
+ "Roman Joost <romanofski@gimp.org>"
+ "Roman Joost <romanofski@gimp.org>"
+ "2006"
+ ""
+)
+
+(script-fu-menu-register "gimp-online-docs-web-site"
+ "<Image>/Help/GIMP Online")
+
+
+; (script-fu-register "gimp-online-plug-in-web-site"
+; _"Plug-in _Registry"
+; _"Bookmark to the GIMP web site"
+; "Henrik Brix Andersen <brix@gimp.org>"
+; "Henrik Brix Andersen <brix@gimp.org>"
+; "2003"
+; ""
+; )
+
+; (script-fu-menu-register "gimp-online-plug-in-web-site"
+; "<Image>/Help/GIMP Online")
diff --git a/plug-ins/script-fu/scripts/gradient-example.scm b/plug-ins/script-fu/scripts/gradient-example.scm
new file mode 100644
index 0000000..3157c88
--- /dev/null
+++ b/plug-ins/script-fu/scripts/gradient-example.scm
@@ -0,0 +1,81 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Gradient example script --- create an example image of a custom gradient
+; Copyright (C) 1997 Federico Mena Quintero
+; federico@nuclecu.unam.mx
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+(define (script-fu-gradient-example width
+ height
+ gradient-reverse)
+ (let* (
+ (img (car (gimp-image-new width height RGB)))
+ (drawable (car (gimp-layer-new img width height RGB
+ "Gradient example" 100 LAYER-MODE-NORMAL)))
+
+ ; Calculate colors for checkerboard... just like in the gradient editor
+
+ (fg-color (* 255 (/ 2 3)))
+ (bg-color (* 255 (/ 1 3)))
+ )
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ ; Render background checkerboard
+
+ (gimp-context-push)
+
+ (gimp-context-set-foreground (list fg-color fg-color fg-color))
+ (gimp-context-set-background (list bg-color bg-color bg-color))
+ (plug-in-checkerboard RUN-NONINTERACTIVE img drawable 0 8)
+
+ (gimp-context-pop)
+
+ ; Render gradient
+
+ (gimp-context-push)
+
+ (gimp-context-set-gradient-reverse gradient-reverse)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-LINEAR 0
+ FALSE 0 0
+ TRUE
+ 0 0 (- width 1) 0)
+
+ (gimp-context-pop)
+
+ ; Terminate
+
+ (gimp-image-undo-enable img)
+ (gimp-display-new img)
+ )
+)
+
+(script-fu-register "script-fu-gradient-example"
+ _"Custom _Gradient..."
+ _"Create an image filled with an example of the current gradient"
+ "Federico Mena Quintero"
+ "Federico Mena Quintero"
+ "June 1997"
+ ""
+ SF-ADJUSTMENT _"Width" '(400 1 2000 1 10 0 1)
+ SF-ADJUSTMENT _"Height" '(30 1 2000 1 10 0 1)
+ SF-TOGGLE _"Gradient reverse" FALSE
+)
+
+(script-fu-menu-register "script-fu-gradient-example"
+ "<Gradients>")
diff --git a/plug-ins/script-fu/scripts/grid-system.scm b/plug-ins/script-fu/scripts/grid-system.scm
new file mode 100644
index 0000000..6607a3e
--- /dev/null
+++ b/plug-ins/script-fu/scripts/grid-system.scm
@@ -0,0 +1,95 @@
+;;; grid-system.scm -*-scheme-*-
+;;; Time-stamp: <1998/01/20 23:22:02 narazaki@InetQ.or.jp>
+;;; This file is a part of:
+;;; GIMP (Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis)
+;;; Author: Shuji Narazaki (narazaki@InetQ.or.jp)
+;;; Version 0.6
+
+;;; Code:
+(if (not (symbol-bound? 'script-fu-grid-system-x-divides (current-environment)))
+ (define script-fu-grid-system-x-divides "'(1 g 1)"))
+(if (not (symbol-bound? 'script-fu-grid-system-y-divides (current-environment)))
+ (define script-fu-grid-system-y-divides "'(1 g 1)"))
+
+(define (script-fu-grid-system img drw x-divides-orig y-divides-orig)
+ (define (update-segment! s x0 y0 x1 y1)
+ (aset s 0 x0)
+ (aset s 1 y0)
+ (aset s 2 x1)
+ (aset s 3 y1))
+ (define (map proc seq)
+ (if (null? seq)
+ '()
+ (cons (proc (car seq))
+ (map proc (cdr seq)))))
+ (define (convert-g l)
+ (cond ((null? l) '())
+ ((eq? (car l) 'g) (cons 1.618 (convert-g (cdr l))))
+ ((eq? (car l) '1/g) (cons 0.618 (convert-g (cdr l))))
+ ('else (cons (car l) (convert-g (cdr l))))))
+ (define (wrap-list l)
+ (define (wrap-object obj)
+ (cond ((number? obj) (string-append (number->string obj) " "))
+ ((eq? obj 'g) "g ")
+ (eq? obj '1/g) "1/g "))
+ (string-append "'(" (apply string-append (map wrap-object l)) ")"))
+ (let* ((drw-width (car (gimp-drawable-width drw)))
+ (drw-height (car (gimp-drawable-height drw)))
+ (drw-offset-x (nth 0 (gimp-drawable-offsets drw)))
+ (drw-offset-y (nth 1 (gimp-drawable-offsets drw)))
+ (grid-layer #f)
+ (segment (cons-array 4 'double))
+ (stepped-x 0)
+ (stepped-y 0)
+ (temp 0)
+ (total-step-x 0)
+ (total-step-y 0)
+ (x-divides (convert-g x-divides-orig))
+ (y-divides (convert-g y-divides-orig))
+ (total-step-x (apply + x-divides))
+ (total-step-y (apply + y-divides)))
+
+ (gimp-image-undo-group-start img)
+
+ (set! grid-layer (car (gimp-layer-copy drw TRUE)))
+ (gimp-image-insert-layer img grid-layer 0 0)
+ (gimp-drawable-edit-clear grid-layer)
+ (gimp-item-set-name grid-layer "Grid Layer")
+
+ (while (not (null? (cdr x-divides)))
+ (set! stepped-x (+ stepped-x (car x-divides)))
+ (set! temp (* drw-width (/ stepped-x total-step-x)))
+ (set! x-divides (cdr x-divides))
+ (update-segment! segment
+ (+ drw-offset-x temp) drw-offset-y
+ (+ drw-offset-x temp) (+ drw-offset-y drw-height))
+ (gimp-pencil grid-layer 4 segment))
+
+ (while (not (null? (cdr y-divides)))
+ (set! stepped-y (+ stepped-y (car y-divides)))
+ (set! temp (* drw-height (/ stepped-y total-step-y)))
+ (set! y-divides (cdr y-divides))
+ (update-segment! segment
+ drw-offset-x (+ drw-offset-y temp)
+ (+ drw-offset-x drw-width) (+ drw-offset-y temp))
+ (gimp-pencil grid-layer 4 segment))
+
+ (gimp-image-undo-group-end img)
+
+ (set! script-fu-grid-system-x-divides (wrap-list x-divides-orig))
+ (set! script-fu-grid-system-y-divides (wrap-list y-divides-orig))
+ (gimp-displays-flush)))
+
+(script-fu-register "script-fu-grid-system"
+ _"_Grid..."
+ _"Draw a grid as specified by the lists of X and Y locations using the current brush"
+ "Shuji Narazaki <narazaki@InetQ.or.jp>"
+ "Shuji Narazaki"
+ "1997"
+ "RGB*, INDEXED*, GRAY*"
+ SF-IMAGE "Image to use" 0
+ SF-DRAWABLE "Drawable to draw grid" 0
+ SF-VALUE _"X divisions" script-fu-grid-system-x-divides
+ SF-VALUE _"Y divisions" script-fu-grid-system-y-divides
+)
+
diff --git a/plug-ins/script-fu/scripts/guides-from-selection.scm b/plug-ins/script-fu/scripts/guides-from-selection.scm
new file mode 100644
index 0000000..b790c86
--- /dev/null
+++ b/plug-ins/script-fu/scripts/guides-from-selection.scm
@@ -0,0 +1,43 @@
+;; -*-scheme-*-
+
+(define (script-fu-guides-from-selection image drawable)
+ (let* (
+ (boundaries (gimp-selection-bounds image))
+ ;; non-empty INT32 TRUE if there is a selection
+ (selection (car boundaries))
+ (x1 (cadr boundaries))
+ (y1 (caddr boundaries))
+ (x2 (cadr (cddr boundaries)))
+ (y2 (caddr (cddr boundaries)))
+ )
+
+ ;; need to check for a selection or we get guides right at edges of the image
+ (if (= selection TRUE)
+ (begin
+ (gimp-image-undo-group-start image)
+
+ (gimp-image-add-vguide image x1)
+ (gimp-image-add-hguide image y1)
+ (gimp-image-add-vguide image x2)
+ (gimp-image-add-hguide image y2)
+
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+ )
+ )
+ )
+)
+
+(script-fu-register "script-fu-guides-from-selection"
+ _"New Guides from _Selection"
+ _"Create four guides around the bounding box of the current selection"
+ "Alan Horkan"
+ "Alan Horkan, 2004. Public Domain."
+ "2004-08-13"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+)
+
+(script-fu-menu-register "script-fu-guides-from-selection"
+ "<Image>/Image/Guides")
diff --git a/plug-ins/script-fu/scripts/guides-new-percent.scm b/plug-ins/script-fu/scripts/guides-new-percent.scm
new file mode 100644
index 0000000..c42d457
--- /dev/null
+++ b/plug-ins/script-fu/scripts/guides-new-percent.scm
@@ -0,0 +1,41 @@
+;; -*-scheme-*-
+
+;; Alan Horkan 2004. No copyright. Public Domain.
+
+(define (script-fu-guide-new-percent image drawable direction position)
+ (let* (
+ (width (car (gimp-image-width image)))
+ (height (car (gimp-image-height image)))
+ )
+
+ (if (= direction 0)
+ (set! position (/ (* height position) 100))
+ (set! position (/ (* width position) 100))
+ )
+
+ (if (= direction 0)
+ ;; convert position to pixel
+ (if (<= position height) (gimp-image-add-hguide image position))
+ (if (<= position width) (gimp-image-add-vguide image position))
+ )
+
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-guide-new-percent"
+ _"New Guide (by _Percent)..."
+ _"Add a guide at the position specified as a percentage of the image size"
+ "Alan Horkan"
+ "Alan Horkan, 2004"
+ "April 2004"
+ "*"
+ SF-IMAGE "Input Image" 0
+ SF-DRAWABLE "Input Drawable" 0
+ SF-OPTION _"_Direction" '(_"Horizontal"
+ _"Vertical")
+ SF-ADJUSTMENT _"_Position (in %)" '(50 0 100 1 10 2 1)
+)
+
+(script-fu-menu-register "script-fu-guide-new-percent"
+ "<Image>/Image/Guides")
diff --git a/plug-ins/script-fu/scripts/guides-new.scm b/plug-ins/script-fu/scripts/guides-new.scm
new file mode 100644
index 0000000..08217af
--- /dev/null
+++ b/plug-ins/script-fu/scripts/guides-new.scm
@@ -0,0 +1,40 @@
+;; -*-scheme-*-
+
+;; Alan Horkan 2004. Public Domain.
+;; so long as remove this block of comments from your script
+;; feel free to use it for whatever you like.
+
+(define (script-fu-guide-new image
+ drawable
+ direction
+ position)
+ (let* (
+ (width (car (gimp-image-width image)))
+ (height (car (gimp-image-height image)))
+ )
+
+ (if (= direction 0)
+ ;; check position is inside the image boundaries
+ (if (<= position height) (gimp-image-add-hguide image position))
+ (if (<= position width) (gimp-image-add-vguide image position))
+ )
+
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-guide-new"
+ _"New _Guide..."
+ _"Add a guide at the orientation and position specified (in pixels)"
+ "Alan Horkan"
+ "Alan Horkan, 2004. Public Domain."
+ "2004-04-02"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-OPTION _"_Direction" '(_"Horizontal" _"Vertical")
+ SF-ADJUSTMENT _"_Position" (list 0 0 MAX-IMAGE-SIZE 1 10 0 1)
+)
+
+(script-fu-menu-register "script-fu-guide-new"
+ "<Image>/Image/Guides")
diff --git a/plug-ins/script-fu/scripts/guides-remove-all.scm b/plug-ins/script-fu/scripts/guides-remove-all.scm
new file mode 100644
index 0000000..61cc3ed
--- /dev/null
+++ b/plug-ins/script-fu/scripts/guides-remove-all.scm
@@ -0,0 +1,30 @@
+;; -*-scheme-*-
+
+(define (script-fu-guides-remove image drawable)
+ (let* ((guide-id 0))
+ (gimp-image-undo-group-start image)
+
+ (set! guide-id (car (gimp-image-find-next-guide image 0)))
+ (while (> guide-id 0)
+ (gimp-image-delete-guide image guide-id)
+ (set! guide-id (car (gimp-image-find-next-guide image 0)))
+ )
+
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-guides-remove"
+ _"_Remove all Guides"
+ _"Remove all horizontal and vertical guides"
+ "Alan Horkan"
+ "Alan Horkan, 2004. Public Domain."
+ "April 2004"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+)
+
+(script-fu-menu-register "script-fu-guides-remove"
+ "<Image>/Image/Guides")
diff --git a/plug-ins/script-fu/scripts/images/Makefile.am b/plug-ins/script-fu/scripts/images/Makefile.am
new file mode 100644
index 0000000..5457226
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/Makefile.am
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in
+
+imagesdatadir = $(gimpdatadir)/scripts/images
+
+imagesdata_DATA = \
+ beavis.jpg \
+ texture.jpg \
+ texture1.jpg \
+ texture2.jpg \
+ texture3.jpg
+
+EXTRA_DIST = $(imagesdata_DATA)
diff --git a/plug-ins/script-fu/scripts/images/Makefile.in b/plug-ins/script-fu/scripts/images/Makefile.in
new file mode 100644
index 0000000..abc16d0
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/Makefile.in
@@ -0,0 +1,810 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/script-fu/scripts/images
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(imagesdatadir)"
+DATA = $(imagesdata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+imagesdatadir = $(gimpdatadir)/scripts/images
+imagesdata_DATA = \
+ beavis.jpg \
+ texture.jpg \
+ texture1.jpg \
+ texture2.jpg \
+ texture3.jpg
+
+EXTRA_DIST = $(imagesdata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/script-fu/scripts/images/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/script-fu/scripts/images/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-imagesdataDATA: $(imagesdata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(imagesdata_DATA)'; test -n "$(imagesdatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(imagesdatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(imagesdatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(imagesdatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(imagesdatadir)" || exit $$?; \
+ done
+
+uninstall-imagesdataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(imagesdata_DATA)'; test -n "$(imagesdatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(imagesdatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(imagesdatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-imagesdataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-imagesdataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-imagesdataDATA install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-imagesdataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/script-fu/scripts/images/beavis.jpg b/plug-ins/script-fu/scripts/images/beavis.jpg
new file mode 100644
index 0000000..d555047
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/beavis.jpg
Binary files differ
diff --git a/plug-ins/script-fu/scripts/images/texture.jpg b/plug-ins/script-fu/scripts/images/texture.jpg
new file mode 100644
index 0000000..8890c7f
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/texture.jpg
Binary files differ
diff --git a/plug-ins/script-fu/scripts/images/texture1.jpg b/plug-ins/script-fu/scripts/images/texture1.jpg
new file mode 100644
index 0000000..8bb8acd
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/texture1.jpg
Binary files differ
diff --git a/plug-ins/script-fu/scripts/images/texture2.jpg b/plug-ins/script-fu/scripts/images/texture2.jpg
new file mode 100644
index 0000000..6b5d2ef
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/texture2.jpg
Binary files differ
diff --git a/plug-ins/script-fu/scripts/images/texture3.jpg b/plug-ins/script-fu/scripts/images/texture3.jpg
new file mode 100644
index 0000000..34d0988
--- /dev/null
+++ b/plug-ins/script-fu/scripts/images/texture3.jpg
Binary files differ
diff --git a/plug-ins/script-fu/scripts/lava.scm b/plug-ins/script-fu/scripts/lava.scm
new file mode 100644
index 0000000..8adec0d
--- /dev/null
+++ b/plug-ins/script-fu/scripts/lava.scm
@@ -0,0 +1,135 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Lava effect
+; Copyright (c) 1997 Adrian Likins
+; aklikins@eos.ncsu.edu
+;
+; based on a idea by Sven Riedel <lynx@heim8.tu-clausthal.de>
+; tweaked a bit by Sven Neumann <neumanns@uni-duesseldorf.de>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-lava image
+ drawable
+ seed
+ tile_size
+ mask_size
+ gradient
+ keep-selection
+ separate-layer
+ current-grad)
+ (let* (
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (image-height (car (gimp-image-height image)))
+ (active-selection 0)
+ (selection-bounds 0)
+ (select-offset-x 0)
+ (select-offset-y 0)
+ (select-width 0)
+ (select-height 0)
+ (lava-layer 0)
+ (active-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+ (gimp-image-undo-group-start image)
+
+ (if (= (car (gimp-drawable-has-alpha drawable)) FALSE)
+ (gimp-layer-add-alpha drawable)
+ )
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ )
+
+ (set! active-selection (car (gimp-selection-save image)))
+ (gimp-image-set-active-layer image drawable)
+
+ (set! selection-bounds (gimp-selection-bounds image))
+ (set! select-offset-x (cadr selection-bounds))
+ (set! select-offset-y (caddr selection-bounds))
+ (set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))
+
+ (if (= separate-layer TRUE)
+ (begin
+ (set! lava-layer (car (gimp-layer-new image
+ select-width
+ select-height
+ type
+ "Lava Layer"
+ 100
+ LAYER-MODE-NORMAL-LEGACY)))
+
+ (gimp-image-insert-layer image lava-layer 0 -1)
+ (gimp-layer-set-offsets lava-layer select-offset-x select-offset-y)
+ (gimp-selection-none image)
+ (gimp-drawable-edit-clear lava-layer)
+
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (gimp-image-set-active-layer image lava-layer)
+ )
+ )
+
+ (set! active-layer (car (gimp-image-get-active-layer image)))
+
+ (if (= current-grad FALSE)
+ (gimp-context-set-gradient gradient)
+ )
+
+ (plug-in-solid-noise RUN-NONINTERACTIVE image active-layer FALSE TRUE seed 2 2 2)
+ (plug-in-cubism RUN-NONINTERACTIVE image active-layer tile_size 2.5 0)
+ (plug-in-oilify RUN-NONINTERACTIVE image active-layer mask_size 0)
+ (plug-in-edge RUN-NONINTERACTIVE image active-layer 2 0 0)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE image active-layer 2 TRUE TRUE)
+ (plug-in-gradmap RUN-NONINTERACTIVE image active-layer)
+
+ (if (= keep-selection FALSE)
+ (gimp-selection-none image)
+ )
+
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-remove-channel image active-selection)
+
+ (gimp-image-undo-group-end image)
+ (gimp-context-pop)
+
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-lava"
+ _"_Lava..."
+ _"Fill the current selection with lava"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "10/12/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Seed" '(10 1 30000 1 10 0 1)
+ SF-ADJUSTMENT _"Size" '(10 0 100 1 10 0 1)
+ SF-ADJUSTMENT _"Roughness" '(7 3 50 1 10 0 0)
+ SF-GRADIENT _"Gradient" "German flag smooth"
+ SF-TOGGLE _"Keep selection" TRUE
+ SF-TOGGLE _"Separate layer" TRUE
+ SF-TOGGLE _"Use current gradient" FALSE
+)
+
+(script-fu-menu-register "script-fu-lava"
+ "<Image>/Filters/Render")
diff --git a/plug-ins/script-fu/scripts/line-nova.scm b/plug-ins/script-fu/scripts/line-nova.scm
new file mode 100644
index 0000000..8c5c35b
--- /dev/null
+++ b/plug-ins/script-fu/scripts/line-nova.scm
@@ -0,0 +1,123 @@
+;;; line-nova.scm for gimp-1.1 -*-scheme-*-
+;;; Time-stamp: <1998/11/25 13:26:44 narazaki@gimp.org>
+;;; Author Shuji Narazaki <narazaki@gimp.org>
+;;; Version 0.7
+
+(define (script-fu-line-nova img drw num-of-lines corn-deg offset variation)
+ (let* (
+ (*points* (cons-array (* 3 2) 'double))
+ (modulo fmod) ; in R4RS way
+ (pi/2 (/ *pi* 2))
+ (pi/4 (/ *pi* 4))
+ (pi3/4 (* 3 pi/4))
+ (pi5/4 (* 5 pi/4))
+ (pi3/2 (* 3 pi/2))
+ (pi7/4 (* 7 pi/4))
+ (2pi (* 2 *pi*))
+ (rad/deg (/ 2pi 360))
+ (variation/2 (/ variation 2))
+ (drw-width (car (gimp-drawable-width drw)))
+ (drw-height (car (gimp-drawable-height drw)))
+ (drw-offsets (gimp-drawable-offsets drw))
+ (old-selection FALSE)
+ (radius (max drw-height drw-width))
+ (index 0)
+ (dir-deg/line (/ 360 num-of-lines))
+ (fg-color (car (gimp-context-get-foreground)))
+ )
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+ (gimp-context-set-foreground fg-color)
+
+ (define (draw-vector beg-x beg-y direction)
+
+ (define (set-point! index x y)
+ (aset *points* (* 2 index) x)
+ (aset *points* (+ (* 2 index) 1) y)
+ )
+ (define (deg->rad rad)
+ (* (modulo rad 360) rad/deg)
+ )
+ (define (set-marginal-point beg-x beg-y direction)
+ (let (
+ (dir1 (deg->rad (+ direction corn-deg)))
+ (dir2 (deg->rad (- direction corn-deg)))
+ )
+
+ (define (aux dir index)
+ (set-point! index
+ (+ beg-x (* (cos dir) radius))
+ (+ beg-y (* (sin dir) radius)))
+ )
+
+ (aux dir1 1)
+ (aux dir2 2)
+ )
+ )
+
+ (let (
+ (dir0 (deg->rad direction))
+ (off (+ offset (- (modulo (rand) variation) variation/2)))
+ )
+
+ (set-point! 0
+ (+ beg-x (* off (cos dir0)))
+ (+ beg-y (* off (sin dir0)))
+ )
+ (set-marginal-point beg-x beg-y direction)
+ (gimp-image-select-polygon img CHANNEL-OP-ADD 6 *points*)
+ )
+ )
+
+ (gimp-image-undo-group-start img)
+
+ (set! old-selection
+ (if (eq? (car (gimp-selection-is-empty img)) TRUE)
+ #f
+ (car (gimp-selection-save img))
+ )
+ )
+
+ (gimp-selection-none img)
+ (srand (realtime))
+ (while (< index num-of-lines)
+ (draw-vector (+ (nth 0 drw-offsets) (/ drw-width 2))
+ (+ (nth 1 drw-offsets) (/ drw-height 2))
+ (* index dir-deg/line)
+ )
+ (set! index (+ index 1))
+ )
+ (gimp-drawable-edit-fill drw FILL-FOREGROUND)
+
+ (if old-selection
+ (begin
+ (gimp-image-select-item img CHANNEL-OP-REPLACE old-selection)
+ ;; (gimp-image-set-active-layer img drw)
+ ;; delete extra channel by Sven Neumann <neumanns@uni-duesseldorf.de>
+ (gimp-image-remove-channel img old-selection)
+ )
+ )
+
+ (gimp-image-undo-group-end img)
+ (gimp-displays-flush)
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-line-nova"
+ _"Line _Nova..."
+ _"Fill a layer with rays emanating outward from its center using the foreground color"
+ "Shuji Narazaki <narazaki@gimp.org>"
+ "Shuji Narazaki"
+ "1997,1998"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Number of lines" '(200 40 1000 1 1 0 1)
+ SF-ADJUSTMENT _"Sharpness (degrees)" '(1.0 0.0 10.0 0.1 1 1 1)
+ SF-ADJUSTMENT _"Offset radius" '(100 0 2000 1 1 0 1)
+ SF-ADJUSTMENT _"Randomness" '(30 1 2000 1 1 0 1)
+)
+
+(script-fu-menu-register "script-fu-line-nova"
+ "<Image>/Filters/Render")
diff --git a/plug-ins/script-fu/scripts/mkbrush.scm b/plug-ins/script-fu/scripts/mkbrush.scm
new file mode 100644
index 0000000..7a316a4
--- /dev/null
+++ b/plug-ins/script-fu/scripts/mkbrush.scm
@@ -0,0 +1,272 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Make-Brush - a script for the script-fu program
+; by Seth Burgess 1997 <sjburges@ou.edu>
+;
+; 18-Dec-2000 fixed to work with the new convention (not inverted) of
+; gbr saver (jtl@gimp.org)
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-make-brush-rectangular name width height spacing)
+ (let* (
+ (img (car (gimp-image-new width height GRAY)))
+ (drawable (car (gimp-layer-new img
+ width height GRAY-IMAGE
+ "MakeBrush" 100 LAYER-MODE-NORMAL)))
+ (filename (string-append gimp-directory
+ "/brushes/r"
+ (number->string width)
+ "x"
+ (number->string height)
+ ".gbr"))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-fill drawable FILL-BACKGROUND)
+
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE 0 0 width height)
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ (file-gbr-save 1 img drawable filename "" spacing name)
+ (gimp-image-delete img)
+
+ (gimp-context-pop)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+)
+
+(script-fu-register "script-fu-make-brush-rectangular"
+ _"_Rectangular..."
+ _"Create a rectangular brush"
+ "Seth Burgess <sjburges@ou.edu>"
+ "Seth Burgess"
+ "1997"
+ ""
+ SF-STRING _"Name" "Rectangle"
+ SF-ADJUSTMENT _"Width" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Height" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Spacing" '(25 1 100 1 10 1 0)
+)
+
+(script-fu-menu-register "script-fu-make-brush-rectangular"
+ "<Brushes>")
+
+
+(define (script-fu-make-brush-rectangular-feathered name width height
+ feathering spacing)
+ (let* (
+ (widthplus (+ width feathering))
+ (heightplus (+ height feathering))
+ (img (car (gimp-image-new widthplus heightplus GRAY)))
+ (drawable (car (gimp-layer-new img
+ widthplus heightplus GRAY-IMAGE
+ "MakeBrush" 100 LAYER-MODE-NORMAL)))
+ (filename (string-append gimp-directory
+ "/brushes/r"
+ (number->string width)
+ "x"
+ (number->string height)
+ "f"
+ (number->string feathering)
+ ".gbr"))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-fill drawable FILL-BACKGROUND)
+
+ (cond
+ ((< 0 feathering)
+ (gimp-context-set-feather TRUE)
+ (gimp-context-set-feather-radius feathering feathering)
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE
+ (/ feathering 2) (/ feathering 2) width height))
+ ((>= 0 feathering)
+ (gimp-context-set-feather FALSE)
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE 0 0 width height))
+ )
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ (file-gbr-save 1 img drawable filename "" spacing name)
+ (gimp-image-delete img)
+
+ (gimp-context-pop)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+)
+
+(script-fu-register "script-fu-make-brush-rectangular-feathered"
+ _"Re_ctangular, Feathered..."
+ _"Create a rectangular brush with feathered edges"
+ "Seth Burgess <sjburges@ou.edu>"
+ "Seth Burgess"
+ "1997"
+ ""
+ SF-STRING _"Name" "Rectangle"
+ SF-ADJUSTMENT _"Width" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Height" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Feathering" '(4 1 100 1 10 0 1)
+ SF-ADJUSTMENT _"Spacing" '(25 1 100 1 10 1 0)
+)
+
+(script-fu-menu-register "script-fu-make-brush-rectangular-feathered"
+ "<Brushes>")
+
+
+(define (script-fu-make-brush-elliptical name width height spacing)
+ (let* (
+ (img (car (gimp-image-new width height GRAY)))
+ (drawable (car (gimp-layer-new img
+ width height GRAY-IMAGE
+ "MakeBrush" 100 LAYER-MODE-NORMAL)))
+ (filename (string-append gimp-directory
+ "/brushes/e"
+ (number->string width)
+ "x"
+ (number->string height)
+ ".gbr"))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-antialias TRUE)
+ (gimp-context-set-feather FALSE)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-fill drawable FILL-BACKGROUND)
+ (gimp-context-set-background '(0 0 0))
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE 0 0 width height)
+
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ (file-gbr-save 1 img drawable filename "" spacing name)
+ (gimp-image-delete img)
+
+ (gimp-context-pop)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+)
+
+(script-fu-register "script-fu-make-brush-elliptical"
+ _"_Elliptical..."
+ _"Create an elliptical brush"
+ "Seth Burgess <sjburges@ou.edu>"
+ "Seth Burgess"
+ "1997"
+ ""
+ SF-STRING _"Name" "Ellipse"
+ SF-ADJUSTMENT _"Width" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Height" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Spacing" '(25 1 100 1 10 1 0)
+)
+
+(script-fu-menu-register "script-fu-make-brush-elliptical"
+ "<Brushes>")
+
+
+(define (script-fu-make-brush-elliptical-feathered name
+ width height
+ feathering spacing)
+ (let* (
+ (widthplus (+ feathering width)) ; add 3 for blurring
+ (heightplus (+ feathering height))
+ (img (car (gimp-image-new widthplus heightplus GRAY)))
+ (drawable (car (gimp-layer-new img
+ widthplus heightplus GRAY-IMAGE
+ "MakeBrush" 100 LAYER-MODE-NORMAL)))
+ (filename (string-append gimp-directory
+ "/brushes/e"
+ (number->string width)
+ "x"
+ (number->string height)
+ "f"
+ (number->string feathering)
+ ".gbr"))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-antialias TRUE)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-fill drawable FILL-BACKGROUND)
+
+ (cond ((> feathering 0) ; keep from taking out gimp with stupid entry.
+ (gimp-context-set-feather TRUE)
+ (gimp-context-set-feather-radius feathering feathering)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE
+ (/ feathering 2) (/ feathering 2)
+ width height))
+ ((<= feathering 0)
+ (gimp-context-set-feather FALSE)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE 0 0 width height)))
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ (file-gbr-save 1 img drawable filename "" spacing name)
+ (gimp-image-delete img)
+
+ (gimp-context-pop)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+)
+
+(script-fu-register "script-fu-make-brush-elliptical-feathered"
+ _"Elli_ptical, Feathered..."
+ _"Create an elliptical brush with feathered edges"
+ "Seth Burgess <sjburges@ou.edu>"
+ "Seth Burgess"
+ "1997"
+ ""
+ SF-STRING _"Name" "Ellipse"
+ SF-ADJUSTMENT _"Width" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Height" '(20 1 200 1 10 0 1)
+ SF-ADJUSTMENT _"Feathering" '(4 1 100 1 10 0 1)
+ SF-ADJUSTMENT _"Spacing" '(25 1 100 1 10 1 0)
+)
+
+(script-fu-menu-register "script-fu-make-brush-elliptical-feathered"
+ "<Brushes>")
diff --git a/plug-ins/script-fu/scripts/old-photo.scm b/plug-ins/script-fu/scripts/old-photo.scm
new file mode 100644
index 0000000..110c313
--- /dev/null
+++ b/plug-ins/script-fu/scripts/old-photo.scm
@@ -0,0 +1,108 @@
+;
+; old-photo
+;
+;
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Branko Collin <collin@xs4all.nl> added the possibility to change
+; the border size in October 2001.
+
+; Define the function:
+
+(define (script-fu-old-photo inImage inLayer inDefocus inBorderSize inSepia inMottle inCopy)
+ (let (
+ (theImage (if (= inCopy TRUE) (car (gimp-image-duplicate inImage)) inImage))
+ (theLayer 0)
+ (theWidth 0)
+ (theHeight 0)
+ )
+ (if (= inCopy TRUE)
+ (gimp-image-undo-disable theImage)
+ (gimp-image-undo-group-start theImage)
+ )
+
+ (gimp-selection-all theImage)
+
+ (set! theLayer (car (gimp-image-flatten theImage)))
+ (if (= inDefocus TRUE)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE theImage theLayer 1.5 TRUE TRUE)
+ )
+ (if (> inBorderSize 0)
+ (script-fu-fuzzy-border theImage theLayer '(255 255 255)
+ inBorderSize TRUE 8 FALSE 100 FALSE TRUE )
+ )
+ (set! theLayer (car (gimp-image-flatten theImage)))
+
+ (if (= inSepia TRUE)
+ (begin (gimp-drawable-desaturate theLayer DESATURATE-LIGHTNESS)
+ (gimp-drawable-brightness-contrast theLayer -0.078125 -0.15625)
+ (gimp-drawable-color-balance theLayer TRANSFER-SHADOWS TRUE 30 0 -30)
+ )
+ )
+ (set! theWidth (car (gimp-image-width theImage)))
+ (set! theHeight (car (gimp-image-height theImage)))
+ (if (= inMottle TRUE)
+ (let (
+ (mLayer (car (gimp-layer-new theImage theWidth theHeight
+ RGBA-IMAGE "Mottle"
+ 100 LAYER-MODE-DARKEN-ONLY)))
+ )
+
+ (gimp-image-insert-layer theImage mLayer 0 0)
+ (gimp-selection-all theImage)
+ (gimp-drawable-edit-clear mLayer)
+ (gimp-selection-none theImage)
+ (plug-in-noisify RUN-NONINTERACTIVE theImage mLayer TRUE 0 0 0 0.5)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE theImage mLayer 5 TRUE TRUE)
+ (set! theLayer (car (gimp-image-flatten theImage)))
+ )
+ )
+ (gimp-selection-none theImage)
+
+ (if (= inCopy TRUE)
+ (begin (gimp-image-clean-all theImage)
+ (gimp-display-new theImage)
+ (gimp-image-undo-enable theImage)
+ )
+ (gimp-image-undo-group-end theImage)
+ )
+
+ (gimp-displays-flush theImage)
+ )
+)
+
+(script-fu-register "script-fu-old-photo"
+ _"_Old Photo..."
+ _"Make an image look like an old photo"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "16th April 1998"
+ "RGB* GRAY*"
+ SF-IMAGE "The image" 0
+ SF-DRAWABLE "The layer" 0
+ SF-TOGGLE _"Defocus" TRUE
+ SF-ADJUSTMENT _"Border size" '(20 0 300 1 10 0 1)
+ ; since this plug-in uses the fuzzy-border plug-in, I used the
+ ; values of the latter, with the exception of the initial value
+ ; and the 'minimum' value.
+ SF-TOGGLE _"Sepia" TRUE
+ SF-TOGGLE _"Mottle" FALSE
+ SF-TOGGLE _"Work on copy" TRUE
+)
+
+(script-fu-menu-register "script-fu-old-photo"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/palette-export.scm b/plug-ins/script-fu/scripts/palette-export.scm
new file mode 100644
index 0000000..ecb16df
--- /dev/null
+++ b/plug-ins/script-fu/scripts/palette-export.scm
@@ -0,0 +1,402 @@
+; -----------------------------------------------------------------------------
+; GIMP palette export toolkit -
+; Written by Barak Itkin <lightningismyname@gmail.com>
+;
+; This script includes various exporters for GIMP palettes, and other
+; utility function to help in exporting to other (text-based) formats.
+; See instruction on adding new exporters at the end
+;
+; -----------------------------------------------------------------------------
+; Numbers and Math
+; -----------------------------------------------------------------------------
+
+; For all the operations below, this is the order of respectable digits:
+(define conversion-digits (list "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
+ "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k"
+ "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v"
+ "w" "x" "y" "z"))
+
+; Converts a decimal number to another base. The returned number is a string
+(define (convert-decimal-to-base num base)
+ (if (< num base)
+ (list-ref conversion-digits num)
+ (let loop ((val num)
+ (order (inexact->exact (truncate (/ (log num)
+ (log base)))))
+ (result ""))
+ (let* ((power (expt base order))
+ (digit (quotient val power)))
+ (if (zero? order)
+ (string-append result (list-ref conversion-digits digit))
+ (loop (- val (* digit power))
+ (pred order)
+ (string-append result (list-ref conversion-digits digit))))))))
+
+; Convert a string representation of a number in some base, to a decimal number
+(define (convert-base-to-decimal base num-str)
+ (define (convert-char num-char)
+ (if (char-numeric? num-char)
+ (string->number (string num-char))
+ (+ 10 (- (char->integer num-char) (char->integer #\a)))
+ )
+ )
+ (define (calc base num-str num)
+ (if (equal? num-str "")
+ num
+ (calc base
+ (substring num-str 1)
+ (+ (* num base) (convert-char (string-ref num-str 0)))
+ )
+ )
+ )
+ (calc base num-str 0)
+ )
+
+; If a string num-str is shorter then size, pad it with pad-str in the
+; beginning until it's at least size long
+(define (pre-pad-number num-str size pad-str)
+ (if (< (string-length num-str) size)
+ (pre-pad-number (string-append pad-str num-str) size pad-str)
+ num-str
+ )
+ )
+
+; -----------------------------------------------------------------------------
+; Color convertors
+; -----------------------------------------------------------------------------
+
+; The standard way for representing a color would be a list of red
+; green and blue (GIMP's default)
+(define color-get-red car)
+(define color-get-green cadr)
+(define color-get-blue caddr)
+
+; Convert a color to a hexadecimal string
+; '(255 255 255) => "#ffffff"
+
+(define (color-rgb-to-hexa-decimal color)
+ (string-append "#"
+ (pre-pad-number
+ (convert-decimal-to-base (color-get-red color) 16) 2 "0")
+ (pre-pad-number
+ (convert-decimal-to-base (color-get-green color) 16) 2 "0")
+ (pre-pad-number
+ (convert-decimal-to-base (color-get-blue color) 16) 2 "0")
+ )
+ )
+
+; Convert a color to a css color
+; '(255 255 255) => "rgb(255, 255, 255)"
+(define (color-rgb-to-css color)
+ (string-append "rgb(" (number->string (color-get-red color))
+ ", " (number->string (color-get-green color))
+ ", " (number->string (color-get-blue color)) ")")
+ )
+
+; Convert a color to a simple pair of braces with comma separated values
+; '(255 255 255) => "(255, 255, 255)"
+(define (color-rgb-to-comma-seperated-list color)
+ (string-append "(" (number->string (color-get-red color))
+ ", " (number->string (color-get-green color))
+ ", " (number->string (color-get-blue color)) ")")
+ )
+
+
+; -----------------------------------------------------------------------------
+; Export utils
+; -----------------------------------------------------------------------------
+
+; List of characters that should not appear in file names
+(define illegal-file-name-chars (list #\\ #\/ #\: #\* #\? #\" #\< #\> #\|))
+
+; A function to filter a list lst by a given predicate pred
+(define (filter pred lst)
+ (if (null? lst)
+ '()
+ (if (pred (car lst))
+ (cons (car lst) (filter pred (cdr lst)))
+ (filter pred (cdr lst))
+ )
+ )
+ )
+
+; A function to check if a certain value obj is inside a list lst
+(define (contained? obj lst) (member obj lst))
+
+; This functions filters a string to have only characters which are
+; either alpha-numeric or contained in more-legal (which is a variable
+; holding a list of characters)
+(define (clean str more-legal)
+ (list->string (filter (lambda (ch) (or (char-alphabetic? ch)
+ (char-numeric? ch)
+ (contained? ch more-legal)))
+ (string->list str)))
+ )
+
+; A function that receives the a file-name, and filters out all the
+; character that shouldn't appear in file names. Then, it makes sure
+; the remaining name isn't only white-spaces. If it's only
+; white-spaces, the function returns false. Otherwise, it returns the
+; fixed file-name
+(define (valid-file-name name)
+ (let* ((clean (list->string (filter (lambda (ch)
+ (not (contained?
+ ch illegal-file-name-chars)))
+ (string->list name))))
+ (clean-without-spaces (list->string (filter (lambda (ch)
+ (not (char-whitespace?
+ ch)))
+ (string->list clean))))
+ )
+ (if (equal? clean-without-spaces "")
+ #f
+ clean
+ )
+ )
+ )
+
+; Filters a string from all the characters which are not alpha-numeric
+; (this also removes whitespaces)
+(define (name-alpha-numeric str)
+ (clean str '())
+ )
+
+; This function does the same as name-alpha-numeric, with an added
+; operation - it removes any numbers from the beginning
+(define (name-standard str)
+ (let ((cleaned (clean str '())))
+ (while (char-numeric? (string-ref cleaned 0))
+ (set! cleaned (substring cleaned 1))
+ )
+ cleaned
+ )
+ )
+
+(define name-no-conversion (lambda (obj) obj))
+(define color-none (lambda (x) ""))
+(define name-none (lambda (x) ""))
+
+(define displayln (lambda (obj) (display obj) (display "\n")))
+
+; The loop for exporting all the colors
+(define (export-palette palette-name color-convertor name-convertor
+ start name-pre name-after name-color-seperator
+ color-pre color-after entry-seperator end)
+
+ (define (write-color-line index)
+ (display name-pre)
+ (display (name-convertor
+ (car (gimp-palette-entry-get-name palette-name index))))
+ (display name-after)
+ (display name-color-seperator)
+ (display color-pre)
+ (display (color-convertor
+ (car (gimp-palette-entry-get-color palette-name index))))
+ (display color-after)
+ )
+
+ (let ((color-count (car (gimp-palette-get-colors palette-name)))
+ (i 0)
+ )
+
+ (display start)
+
+ (while (< i (- color-count 1))
+ (begin
+ (write-color-line i)
+ (display entry-seperator)
+ (set! i (+ 1 i))
+ )
+ )
+
+ (write-color-line i)
+ (display end)
+ )
+ )
+
+(define (register-palette-exporter
+ export-type export-name file-type description author copyright date)
+ (script-fu-register (string-append "gimp-palette-export-" export-type)
+ export-name
+ description
+ author
+ copyright
+ date
+ ""
+ SF-DIRNAME _"Folder for the output file" ""
+ SF-STRING _"The name of the file to create (if a file with this name already exist, it will be replaced)"
+ (string-append "palette." file-type)
+ )
+ (script-fu-menu-register (string-append "gimp-palette-export-" export-type)
+ "<Palettes>/Export as")
+ )
+
+(define (bad-file-name)
+ (gimp-message (string-append _"The filename you entered is not a suitable name for a file."
+ "\n\n"
+ _"All characters in the name are either white-spaces or characters which can not appear in filenames.")))
+
+; -----------------------------------------------------------------------------
+; Exporters
+; -----------------------------------------------------------------------------
+
+(define (gimp-palette-export-css directory-name file-name)
+ (let ((valid-name (valid-file-name file-name)))
+ (if valid-name
+ (with-output-to-file (string-append
+ directory-name DIR-SEPARATOR file-name)
+ (lambda () (export-palette (car (gimp-context-get-palette))
+ color-rgb-to-css
+ name-alpha-numeric ; name-convertor
+ "/* Generated with GIMP Palette Export */\n" ; start
+ "." ; name-pre
+ "" ; name-after
+ " { " ; name-color-seperator
+ "color: " ; color-pre
+ " }" ; color-after
+ "\n" ; entry-seperator
+ "" ; end
+ )))
+ (bad-file-name)
+ )
+ )
+ )
+(register-palette-exporter "css" "_CSS stylesheet..." "css"
+ (string-append _"Export the active palette as a CSS stylesheet with the color entry name as their class name, and the color itself as the color attribute")
+ "Barak Itkin <lightningismyname@gmail.com>"
+ "Barak Itkin" "May 15th, 2009")
+
+(define (gimp-palette-export-php directory-name file-name)
+ (let ((valid-name (valid-file-name file-name)))
+ (if valid-name
+ (with-output-to-file (string-append
+ directory-name DIR-SEPARATOR file-name)
+ (lambda () (export-palette (car (gimp-context-get-palette))
+ color-rgb-to-hexa-decimal
+ name-standard ; name-convertor
+ "<?php\n/* Generated with GIMP Palette Export */\n$colors={\n" ; start
+ "'" ; name-pre
+ "'" ; name-after
+ " => " ; name-color-seperator
+ "'" ; color-pre
+ "'" ; color-after
+ ",\n" ; entry-seperator
+ "}\n?>" ; end
+ )))
+ (bad-file-name)
+ )
+ )
+ )
+(register-palette-exporter "php" "P_HP dictionary..." "php"
+ _"Export the active palette as a PHP dictionary (name => color)"
+ "Barak Itkin <lightningismyname@gmail.com>"
+ "Barak Itkin" "May 15th, 2009")
+
+(define (gimp-palette-export-python directory-name file-name)
+ (let ((valid-name (valid-file-name file-name)))
+ (if valid-name
+ (with-output-to-file (string-append
+ directory-name DIR-SEPARATOR file-name)
+ (lambda ()
+ (let ((palette-name (car (gimp-context-get-palette))))
+ (begin (displayln "# Generated with GIMP Palette Export")
+ (displayln (string-append
+ "# Based on the palette " palette-name))
+ (export-palette palette-name
+ color-rgb-to-hexa-decimal
+ name-standard ; name-convertor
+ "colors={\n" ; start
+ "'" ; name-pre
+ "'" ; name-after
+ ": " ; name-color-seperator
+ "'" ; color-pre
+ "'" ; color-after
+ ",\n" ; entry-seperator
+ "}" ; end
+ ))))
+ )
+ (bad-file-name)
+ )
+ )
+ )
+(register-palette-exporter "python" "_Python dictionary" "py"
+ _"Export the active palette as a Python dictionary (name: color)"
+ "Barak Itkin <lightningismyname@gmail.com>"
+ "Barak Itkin" "May 15th, 2009")
+
+(define (gimp-palette-export-text directory-name file-name)
+ (let ((valid-name (valid-file-name file-name)))
+ (if valid-name
+ (with-output-to-file (string-append
+ directory-name DIR-SEPARATOR file-name)
+ (lambda ()
+ (export-palette (car (gimp-context-get-palette))
+ color-rgb-to-hexa-decimal
+ name-none ; name-convertor
+ "" ; start
+ "" ; name-pre
+ "" ; name-after
+ "" ; name-color-seperator
+ "" ; color-pre
+ "" ; color-after
+ "\n" ; entry-seperator
+ "" ; end
+ )
+ )
+ )
+ (bad-file-name)
+ )
+ )
+ )
+(register-palette-exporter "text" "_Text file..." "txt"
+ _"Write all the colors in a palette to a text file, one hexadecimal value per line (no names)"
+ "Barak Itkin <lightningismyname@gmail.com>"
+ "Barak Itkin" "May 15th, 2009")
+
+(define (gimp-palette-export-java directory-name file-name)
+ (let ((valid-name (valid-file-name file-name)))
+ (if valid-name
+ (with-output-to-file (string-append directory-name
+ DIR-SEPARATOR file-name)
+ (lambda ()
+ (let ((palette-name (car (gimp-context-get-palette))))
+ (begin (displayln "")
+ (displayln "import java.awt.Color;")
+ (displayln "import java.util.Hashtable;")
+ (displayln "")
+ (displayln "// Generated with GIMP palette Export ")
+ (displayln (string-append
+ "// Based on the palette " palette-name))
+ (displayln (string-append
+ "public class "
+ (name-standard palette-name) " {"))
+ (displayln "")
+ (displayln " Hashtable<String, Color> colors;")
+ (displayln "")
+ (displayln (string-append
+ " public "
+ (name-standard palette-name) "() {"))
+ (export-palette (car (gimp-context-get-palette))
+ color-rgb-to-comma-seperated-list
+ name-no-conversion
+ " colors = new Hashtable<String,Color>();\n" ; start
+ " colors.put(\"" ; name-pre
+ "\"" ; name-after
+ ", " ; name-color-seperator
+ "new Color" ; color-pre
+ ");" ; color-after
+ "\n" ; entry-seperator
+ "\n }" ; end
+ )
+ (display "\n}"))))
+ )
+ (bad-file-name)
+ )
+ )
+ )
+
+(register-palette-exporter "java" "J_ava map..." "java"
+ _"Export the active palette as a java.util.Hashtable<String, Color>"
+ "Barak Itkin <lightningismyname@gmail.com>"
+ "Barak Itkin" "May 15th, 2009")
+
diff --git a/plug-ins/script-fu/scripts/paste-as-brush.scm b/plug-ins/script-fu/scripts/paste-as-brush.scm
new file mode 100644
index 0000000..062e97d
--- /dev/null
+++ b/plug-ins/script-fu/scripts/paste-as-brush.scm
@@ -0,0 +1,74 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; script-fu-paste-as-brush
+; Based on select-to-brush by Copyright (c) 1997 Adrian Likins
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-paste-as-brush name filename spacing)
+
+ (let* ((brush-image (car (gimp-edit-paste-as-new-image)))
+ (brush-draw 0)
+ (type 0)
+ (path 0))
+
+ (if (= TRUE (car (gimp-image-is-valid brush-image)))
+ (begin
+ (set! brush-draw (car (gimp-image-get-active-drawable brush-image)))
+ (set! type (car (gimp-drawable-type brush-draw)))
+ (set! path (string-append gimp-directory
+ "/brushes/"
+ filename
+ (number->string brush-image)
+ ".gbr"))
+
+ (if (= type GRAYA-IMAGE)
+ (begin
+ (gimp-context-push)
+ (gimp-context-set-background '(255 255 255))
+ (set! brush-draw (car (gimp-image-flatten brush-image)))
+ (gimp-context-pop)
+ )
+ )
+
+ (file-gbr-save RUN-NONINTERACTIVE
+ brush-image brush-draw path path
+ spacing name)
+
+ (gimp-image-delete brush-image)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+ (gimp-message _"There is no image data in the clipboard to paste.")
+ )
+ )
+)
+
+(script-fu-register "script-fu-paste-as-brush"
+ _"New _Brush..."
+ _"Paste the clipboard contents into a new brush"
+ "Michael Natterer <mitch@gimp.org>"
+ "Michael Natterer"
+ "2005-09-25"
+ ""
+ SF-STRING _"_Brush name" "My Brush"
+ SF-STRING _"_File name" "mybrush"
+ SF-ADJUSTMENT _"_Spacing" '(25 0 1000 1 1 1 0)
+)
+
+(script-fu-menu-register "script-fu-paste-as-brush"
+ "<Image>/Edit/Paste as")
diff --git a/plug-ins/script-fu/scripts/paste-as-pattern.scm b/plug-ins/script-fu/scripts/paste-as-pattern.scm
new file mode 100644
index 0000000..01381a2
--- /dev/null
+++ b/plug-ins/script-fu/scripts/paste-as-pattern.scm
@@ -0,0 +1,61 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; script-fu-paste-as-pattern
+; Based on select-to-pattern by Cameron Gregory, http://www.flamingtext.com/
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-paste-as-pattern name filename)
+ (let* ((pattern-image (car (gimp-edit-paste-as-new-image)))
+ (pattern-draw 0)
+ (path 0))
+
+ (if (= TRUE (car (gimp-image-is-valid pattern-image)))
+ (begin
+ (set! pattern-draw (car (gimp-image-get-active-drawable pattern-image)))
+ (set! path (string-append gimp-directory
+ "/patterns/"
+ filename
+ (number->string pattern-image)
+ ".pat"))
+
+ (file-pat-save RUN-NONINTERACTIVE
+ pattern-image pattern-draw path path
+ name)
+
+ (gimp-image-delete pattern-image)
+
+ (gimp-patterns-refresh)
+ (gimp-context-set-pattern name)
+ )
+ (gimp-message _"There is no image data in the clipboard to paste.")
+ )
+ )
+)
+
+(script-fu-register "script-fu-paste-as-pattern"
+ _"New _Pattern..."
+ _"Paste the clipboard contents into a new pattern"
+ "Michael Natterer <mitch@gimp.org>"
+ "Michael Natterer"
+ "2005-09-25"
+ ""
+ SF-STRING _"_Pattern name" "My Pattern"
+ SF-STRING _"_File name" "mypattern"
+)
+
+(script-fu-menu-register "script-fu-paste-as-pattern"
+ "<Image>/Edit/Paste as")
diff --git a/plug-ins/script-fu/scripts/perspective-shadow.scm b/plug-ins/script-fu/scripts/perspective-shadow.scm
new file mode 100644
index 0000000..1eb40e3
--- /dev/null
+++ b/plug-ins/script-fu/scripts/perspective-shadow.scm
@@ -0,0 +1,217 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; perspective-shadow.scm version 1.2 2000/11/08
+;
+; Copyright (C) 1997-2000 Sven Neumann <sven@gimp.org>
+;
+;
+; Adds a perspective shadow of the current selection or alpha-channel
+; as a layer below the active layer
+;
+
+(define (script-fu-perspective-shadow image
+ drawable
+ alpha
+ rel-distance
+ rel-length
+ shadow-blur
+ shadow-color
+ shadow-opacity
+ interpolation
+ allow-resize)
+ (let* (
+ (shadow-blur (max shadow-blur 0))
+ (shadow-opacity (min shadow-opacity 100))
+ (shadow-opacity (max shadow-opacity 0))
+ (rel-length (abs rel-length))
+ (alpha (* (/ alpha 180) *pi*))
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (image-height (car (gimp-image-height image)))
+ (from-selection 0)
+ (active-selection 0)
+ (shadow-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (if (> rel-distance 24) (set! rel-distance 999999))
+ (if (= rel-distance rel-length) (set! rel-distance (+ rel-distance 0.01)))
+
+ (gimp-image-undo-group-start image)
+
+ (gimp-layer-add-alpha drawable)
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! from-selection FALSE))
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))))
+
+ (let* ((selection-bounds (gimp-selection-bounds image))
+ (select-offset-x (cadr selection-bounds))
+ (select-offset-y (caddr selection-bounds))
+ (select-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (select-height (- (caddr (cddr selection-bounds)) select-offset-y))
+
+ (abs-length (* rel-length select-height))
+ (abs-distance (* rel-distance select-height))
+ (half-bottom-width (/ select-width 2))
+ (half-top-width (* half-bottom-width
+ (/ (- rel-distance rel-length) rel-distance)))
+
+ (x0 (+ select-offset-x (+ (- half-bottom-width half-top-width)
+ (* (cos alpha) abs-length))))
+ (y0 (+ select-offset-y (- select-height
+ (* (sin alpha) abs-length))))
+ (x1 (+ x0 (* 2 half-top-width)))
+ (y1 y0)
+ (x2 select-offset-x)
+ (y2 (+ select-offset-y select-height))
+ (x3 (+ x2 select-width))
+ (y3 y2)
+
+ (shadow-width (+ (- (max x1 x3) (min x0 x2)) (* 2 shadow-blur)))
+ (shadow-height (+ (- (max y1 y3) (min y0 y2)) (* 2 shadow-blur)))
+ (shadow-offset-x (- (min x0 x2) shadow-blur))
+ (shadow-offset-y (- (min y0 y2) shadow-blur)))
+
+
+ (set! shadow-layer (car (gimp-layer-new image
+ select-width
+ select-height
+ type
+ "Perspective Shadow"
+ shadow-opacity
+ LAYER-MODE-NORMAL)))
+
+
+ (gimp-image-insert-layer image shadow-layer 0 -1)
+ (gimp-layer-set-offsets shadow-layer select-offset-x select-offset-y)
+ (gimp-drawable-fill shadow-layer FILL-TRANSPARENT)
+ (gimp-context-set-background shadow-color)
+ (gimp-drawable-edit-fill shadow-layer FILL-BACKGROUND)
+ (gimp-selection-none image)
+
+ (if (= allow-resize TRUE)
+ (let* ((new-image-width image-width)
+ (new-image-height image-height)
+ (image-offset-x 0)
+ (image-offset-y 0))
+
+ (if (< shadow-offset-x 0)
+ (begin
+ (set! image-offset-x (abs shadow-offset-x))
+ (set! new-image-width (+ new-image-width image-offset-x))
+ ; adjust to new coordinate system
+ (set! x0 (+ x0 image-offset-x))
+ (set! x1 (+ x1 image-offset-x))
+ (set! x2 (+ x2 image-offset-x))
+ (set! x3 (+ x3 image-offset-x))
+ ))
+
+ (if (< shadow-offset-y 0)
+ (begin
+ (set! image-offset-y (abs shadow-offset-y))
+ (set! new-image-height (+ new-image-height image-offset-y))
+ ; adjust to new coordinate system
+ (set! y0 (+ y0 image-offset-y))
+ (set! y1 (+ y1 image-offset-y))
+ (set! y2 (+ y2 image-offset-y))
+ (set! y3 (+ y3 image-offset-y))
+ ))
+
+ (if (> (+ shadow-width shadow-offset-x) new-image-width)
+ (set! new-image-width (+ shadow-width shadow-offset-x)))
+
+ (if (> (+ shadow-height shadow-offset-y) new-image-height)
+ (set! new-image-height (+ shadow-height shadow-offset-y)))
+ (gimp-image-resize image
+ new-image-width
+ new-image-height
+ image-offset-x
+ image-offset-y)))
+
+ (gimp-context-set-transform-direction TRANSFORM-FORWARD)
+ (gimp-context-set-interpolation interpolation)
+ (gimp-context-set-transform-recursion 3)
+ (gimp-context-set-transform-resize TRANSFORM-RESIZE-ADJUST)
+
+ (gimp-item-transform-perspective shadow-layer
+ x0 y0
+ x1 y1
+ x2 y2
+ x3 y3)
+
+ (if (>= shadow-blur 1.0)
+ (begin
+ (gimp-layer-set-lock-alpha shadow-layer FALSE)
+ (gimp-layer-resize shadow-layer
+ shadow-width
+ shadow-height
+ shadow-blur
+ shadow-blur)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ image
+ shadow-layer
+ shadow-blur
+ TRUE
+ TRUE))))
+
+ (if (= from-selection TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-drawable-edit-clear shadow-layer)
+ (gimp-image-remove-channel image active-selection)))
+
+ (if (and
+ (= (car (gimp-layer-is-floating-sel drawable)) 0)
+ (= from-selection FALSE))
+ (gimp-image-raise-item image drawable))
+
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-perspective-shadow"
+ _"_Perspective..."
+ _"Add a perspective shadow to the selected region (or alpha)"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "2000/11/08"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Angle" '(45 0 180 1 10 1 0)
+ SF-ADJUSTMENT _"Relative distance of horizon" '(5 0.1 24.1 0.1 1 1 1)
+ SF-ADJUSTMENT _"Relative length of shadow" '(1 0.1 24 0.1 1 1 1)
+ SF-ADJUSTMENT _"Blur radius" '(3 0 1024 1 10 0 0)
+ SF-COLOR _"Color" '(0 0 0)
+ SF-ADJUSTMENT _"Opacity" '(80 0 100 1 10 0 0)
+ SF-ENUM _"Interpolation" '("InterpolationType" "linear")
+ SF-TOGGLE _"Allow resizing" FALSE
+)
+
+(script-fu-menu-register "script-fu-perspective-shadow"
+ "<Image>/Filters/Light and Shadow/Shadow")
diff --git a/plug-ins/script-fu/scripts/plug-in-compat.init b/plug-ins/script-fu/scripts/plug-in-compat.init
new file mode 100644
index 0000000..dd68c2c
--- /dev/null
+++ b/plug-ins/script-fu/scripts/plug-in-compat.init
@@ -0,0 +1,24 @@
+; The Scheme code in this file provides some compatibility with
+; scripts that were originally written for use with older versions of
+; GIMP.
+;
+; It provides PDB procedures that used to be provided by plug-ins that
+; were since then removed from the GIMP distribution. You should not
+; use these in newly written scripts as the functions defined here may
+; be removed at some later date.
+
+
+(define (plug-in-color-map run-mode img layer
+ src-color-1 src-color-2 dest-color-1 dest-color-2
+ map-mode)
+ (gimp-levels layer HISTOGRAM-RED
+ (car src-color-1) (car src-color-2) 1.0
+ (- 255 (car dest-color-1)) (- 255 (car dest-color-2)))
+ (gimp-levels layer HISTOGRAM-GREEN
+ (cadr src-color-1) (cadr src-color-2) 1.0
+ (- 255 (cadr dest-color-1)) (- 255 (cadr dest-color-2)))
+ (gimp-levels layer HISTOGRAM-BLUE
+ (caddr src-color-1) (caddr src-color-2) 1.0
+ (- 255 (caddr dest-color-1)) (- 255 (caddr dest-color-2)))
+ (gimp-levels layer HISTOGRAM-VALUE 0 255 1.0 255 0)
+)
diff --git a/plug-ins/script-fu/scripts/predator.scm b/plug-ins/script-fu/scripts/predator.scm
new file mode 100644
index 0000000..cd1ab14
--- /dev/null
+++ b/plug-ins/script-fu/scripts/predator.scm
@@ -0,0 +1,137 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Predator effect
+; Copyright (c) 1997 Adrian Likins
+; aklikins@eos.ncsu.ed
+;
+; The idea here is too make the image/selection look sort of like
+; the view the predator had in the movies. ie, kind of a thermogram
+; type of thing. Works best on colorful rgb images.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-predator image
+ drawable
+ edge-amount
+ pixelize
+ pixel-size
+ keep-selection
+ separate-layer)
+ (let* (
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (image-height (car (gimp-image-height image)))
+ (active-selection 0)
+ (from-selection 0)
+ (selection-bounds 0)
+ (select-offset-x 0)
+ (select-offset-y 0)
+ (select-width 0)
+ (select-height 0)
+ (effect-layer 0)
+ (active-layer 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+ (gimp-image-undo-group-start image)
+ (gimp-layer-add-alpha drawable)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! active-selection (car (gimp-selection-save image)))
+ (set! from-selection FALSE)
+ )
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))
+ )
+ )
+
+ (set! selection-bounds (gimp-selection-bounds image))
+ (set! select-offset-x (cadr selection-bounds))
+ (set! select-offset-y (caddr selection-bounds))
+ (set! select-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (set! select-height (- (caddr (cddr selection-bounds)) select-offset-y))
+
+ (if (= separate-layer TRUE)
+ (begin
+ (set! effect-layer (car (gimp-layer-new image
+ select-width
+ select-height
+ type
+ "glow layer"
+ 100
+ LAYER-MODE-NORMAL))
+ )
+
+ (gimp-layer-set-offsets effect-layer select-offset-x select-offset-y)
+ (gimp-image-insert-layer image effect-layer 0 -1)
+ (gimp-selection-none image)
+ (gimp-drawable-edit-clear effect-layer)
+
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-edit-copy drawable)
+ (let ((floating-sel (car (gimp-edit-paste effect-layer FALSE))))
+ (gimp-floating-sel-anchor floating-sel)
+ )
+ (gimp-image-set-active-layer image effect-layer)
+ )
+ (set! effect-layer drawable)
+ )
+ (set! active-layer effect-layer)
+
+ ; all the fun stuff goes here
+ (if (= pixelize TRUE)
+ (plug-in-pixelize RUN-NONINTERACTIVE image active-layer pixel-size)
+ )
+ (plug-in-max-rgb RUN-NONINTERACTIVE image active-layer 0)
+ (plug-in-edge RUN-NONINTERACTIVE image active-layer edge-amount 1 0)
+
+ ; clean up the selection copy
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+
+ (if (= keep-selection FALSE)
+ (gimp-selection-none image)
+ )
+
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-remove-channel image active-selection)
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-predator"
+ _"_Predator..."
+ _"Add a 'Predator' effect to the selected region (or alpha)"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "10/12/97"
+ "RGB*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Edge amount" '(2 0 24 1 1 0 0)
+ SF-TOGGLE _"Pixelize" TRUE
+ SF-ADJUSTMENT _"Pixel amount" '(3 1 16 1 1 0 0)
+ SF-TOGGLE _"Keep selection" TRUE
+ SF-TOGGLE _"Separate layer" TRUE
+)
+
+(script-fu-menu-register "script-fu-predator"
+ "<Image>/Filters/Artistic")
diff --git a/plug-ins/script-fu/scripts/reverse-layers.scm b/plug-ins/script-fu/scripts/reverse-layers.scm
new file mode 100644
index 0000000..d7e2882
--- /dev/null
+++ b/plug-ins/script-fu/scripts/reverse-layers.scm
@@ -0,0 +1,53 @@
+; reverse-layers.scm: Reverse the order of layers in the current image.
+; Copyright (C) 2006 by Akkana Peck.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+(define (script-fu-reverse-layers img drawable)
+ (let* (
+ (layers (gimp-image-get-layers img))
+ (num-layers (car layers))
+ (layer-array (cadr layers))
+ (i (- num-layers 1))
+ )
+
+ (gimp-image-undo-group-start img)
+
+ (while (>= i 0)
+ (let ((layer (aref layer-array i)))
+ (if (= (car (gimp-layer-is-floating-sel layer)) FALSE)
+ (gimp-image-lower-item-to-bottom img layer))
+ )
+
+ (set! i (- i 1))
+ )
+
+ (gimp-image-undo-group-end img)
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-reverse-layers"
+ _"Reverse Layer _Order"
+ _"Reverse the order of layers in the image"
+ "Akkana Peck"
+ "Akkana Peck"
+ "August 2006"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+)
+
+(script-fu-menu-register "script-fu-reverse-layers"
+ "<Image>/Layer/Stack")
diff --git a/plug-ins/script-fu/scripts/ripply-anim.scm b/plug-ins/script-fu/scripts/ripply-anim.scm
new file mode 100644
index 0000000..25a2462
--- /dev/null
+++ b/plug-ins/script-fu/scripts/ripply-anim.scm
@@ -0,0 +1,83 @@
+; "Rippling Image" animation generator (ripply-anim.scm)
+; Adam D. Moss (adam@foxbox.org)
+; 97/05/18
+; Revised by Saul Goode April 2015.
+;
+; Designed to be used in conjunction with a plugin capable
+; of saving animations (i.e. the GIF plugin).
+;
+
+(define (script-fu-ripply-anim image drawable displacement num-frames edge-type)
+ (let* ((width (car (gimp-drawable-width drawable)))
+ (height (car (gimp-drawable-height drawable)))
+ (work-image (car (gimp-image-new width
+ height
+ (quotient (car (gimp-drawable-type drawable))
+ 2))))
+ (map-layer (car (gimp-layer-new work-image
+ width
+ height
+ (car (gimp-drawable-type drawable))
+ "Ripple Map"
+ 100
+ LAYER-MODE-NORMAL))))
+ (gimp-context-push)
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+ (gimp-image-undo-disable work-image)
+
+ ; Create a tile-able displacement map in the first layer
+ (gimp-context-set-background '(127 127 127))
+ (gimp-image-insert-layer work-image map-layer 0 0)
+ (gimp-drawable-edit-fill map-layer FILL-BACKGROUND)
+ (plug-in-noisify RUN-NONINTERACTIVE work-image map-layer FALSE 1.0 1.0 1.0 0.0)
+ (plug-in-tile RUN-NONINTERACTIVE work-image map-layer (* width 3) (* height 3) FALSE)
+ (plug-in-gauss-iir RUN-NONINTERACTIVE work-image map-layer 35 TRUE TRUE)
+ (gimp-drawable-equalize map-layer TRUE)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE work-image map-layer 5 TRUE TRUE)
+ (gimp-drawable-equalize map-layer TRUE)
+ (gimp-image-crop work-image width height width height)
+
+ ; Create the frame layers
+ (let loop ((remaining-frames num-frames))
+ (unless (zero? remaining-frames)
+ (let ((frame-layer (car (gimp-layer-new-from-drawable drawable work-image))))
+ (gimp-image-insert-layer work-image frame-layer 0 0)
+ (gimp-item-set-name frame-layer
+ (string-append "Frame "
+ (number->string (+ 1 (- num-frames
+ remaining-frames)))
+ " (replace)"))
+ (plug-in-displace RUN-NONINTERACTIVE work-image frame-layer
+ displacement displacement
+ TRUE TRUE map-layer map-layer (+ edge-type 1))
+ (gimp-item-set-visible frame-layer TRUE))
+ (gimp-drawable-offset map-layer
+ TRUE
+ OFFSET-BACKGROUND
+ (/ width num-frames)
+ (/ height num-frames))
+ (loop (- remaining-frames 1))))
+
+ (gimp-image-remove-layer work-image map-layer)
+ (gimp-image-undo-enable work-image)
+ (gimp-display-new work-image)
+
+ (gimp-context-pop)))
+
+(script-fu-register "script-fu-ripply-anim"
+ _"_Rippling..."
+ _"Create a multi-layer image by adding a ripple effect to the current layer"
+ "Adam D. Moss (adam@foxbox.org), Saul Goode"
+ "Adam D. Moss, Saul Goode"
+ "1997, 2015"
+ "RGB* GRAY*"
+ SF-IMAGE "Image to animage" 0
+ SF-DRAWABLE "Drawable to animate" 0
+ SF-ADJUSTMENT _"Rippling strength" '(3 0 256 1 10 1 0)
+ SF-ADJUSTMENT _"Number of frames" '(15 0 256 1 10 0 1)
+ SF-OPTION _"Edge behavior" '(_"Wrap" _"Smear" _"Black")
+ )
+
+(script-fu-menu-register "script-fu-ripply-anim"
+ "<Image>/Filters/Animation/Animators")
diff --git a/plug-ins/script-fu/scripts/round-corners.scm b/plug-ins/script-fu/scripts/round-corners.scm
new file mode 100644
index 0000000..53711d5
--- /dev/null
+++ b/plug-ins/script-fu/scripts/round-corners.scm
@@ -0,0 +1,149 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; round-corners.scm version 1.02 1999/12/21
+;
+; CHANGE-LOG:
+; 1.00 - initial release
+; 1.01 - some code cleanup, no real changes
+;
+; Copyright (C) 1997-1999 Sven Neumann <sven@gimp.org>
+;
+;
+; Rounds the corners of an image, optionally adding a drop-shadow and
+; a background layer
+;
+; The script works on RGB and grayscale images that contain only
+; one layer. It creates a copy of the image or can optionally work
+; on the original. The script uses the current background color to
+; create a background layer. It makes a call to the script drop-shadow.
+;
+; This script is derived from my script add-shadow, which has become
+; obsolete now.
+
+
+
+(define (script-fu-round-corners img
+ drawable
+ radius
+ shadow-toggle
+ shadow-x
+ shadow-y
+ shadow-blur
+ background-toggle
+ work-on-copy)
+ (let* ((shadow-blur (abs shadow-blur))
+ (radius (abs radius))
+ (diam (* 2 radius))
+ (width (car (gimp-image-width img)))
+ (height (car (gimp-image-height img)))
+ (image (cond ((= work-on-copy TRUE)
+ (car (gimp-image-duplicate img)))
+ ((= work-on-copy FALSE)
+ img)))
+ ; active drawable is not necessarily the active layer
+ (pic-layer (car (gimp-image-get-active-layer image)))
+ (type (car (gimp-drawable-type-with-alpha pic-layer)))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (if (= work-on-copy TRUE)
+ (gimp-image-undo-disable image)
+ (gimp-image-undo-group-start image)
+ )
+
+ ; add an alpha channel to the image
+ (gimp-layer-add-alpha pic-layer)
+
+ ; round the edges
+ (gimp-selection-none image)
+ (gimp-image-select-rectangle image CHANNEL-OP-ADD 0 0 radius radius)
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT 0 0 diam diam)
+ (gimp-image-select-rectangle image CHANNEL-OP-ADD (- width radius) 0 radius radius)
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT (- width diam) 0 diam diam)
+ (gimp-image-select-rectangle image CHANNEL-OP-ADD 0 (- height radius) radius radius)
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT 0 (- height diam) diam diam)
+ (gimp-image-select-rectangle image CHANNEL-OP-ADD (- width radius) (- height radius)
+ radius radius)
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT (- width diam) (- height diam)
+ diam diam)
+ (gimp-drawable-edit-clear pic-layer)
+ (gimp-selection-none image)
+
+ ; optionally add a shadow
+ (if (= shadow-toggle TRUE)
+ (begin
+ (script-fu-drop-shadow image
+ pic-layer
+ shadow-x
+ shadow-y
+ shadow-blur
+ '(0 0 0)
+ 80
+ TRUE)
+ (set! width (car (gimp-image-width image)))
+ (set! height (car (gimp-image-height image)))))
+
+ ; optionally add a background
+ (if (= background-toggle TRUE)
+ (let* ((bg-layer (car (gimp-layer-new image
+ width
+ height
+ type
+ "Background"
+ 100
+ LAYER-MODE-NORMAL))))
+ (gimp-drawable-fill bg-layer FILL-BACKGROUND)
+ (gimp-image-insert-layer image bg-layer 0 -1)
+ (gimp-image-raise-item image pic-layer)
+ (if (= shadow-toggle TRUE)
+ (gimp-image-lower-item image bg-layer))))
+
+; clean up after the script
+ (if (= work-on-copy TRUE)
+ (gimp-image-undo-enable image)
+ (gimp-image-undo-group-end image)
+ )
+
+ (if (= work-on-copy TRUE)
+ (gimp-display-new image))
+ (gimp-context-pop)
+ (gimp-displays-flush))
+)
+
+(script-fu-register "script-fu-round-corners"
+ _"_Round Corners..."
+ _"Round the corners of an image and optionally add a drop-shadow and background"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "1999/12/21"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Edge radius" '(15 0 4096 1 10 0 1)
+ SF-TOGGLE _"Add drop-shadow" TRUE
+ SF-ADJUSTMENT _"Shadow X offset" '(8 -4096 4096 1 10 0 1)
+ SF-ADJUSTMENT _"Shadow Y offset" '(8 -4096 4096 1 10 0 1)
+ SF-ADJUSTMENT _"Blur radius" '(15 0 1024 1 10 0 1)
+ SF-TOGGLE _"Add background" TRUE
+ SF-TOGGLE _"Work on copy" TRUE
+)
+
+(script-fu-menu-register "script-fu-round-corners"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/script-fu-compat.init b/plug-ins/script-fu/scripts/script-fu-compat.init
new file mode 100644
index 0000000..865cf80
--- /dev/null
+++ b/plug-ins/script-fu/scripts/script-fu-compat.init
@@ -0,0 +1,457 @@
+;The Scheme code in this file provides some compatibility with scripts that
+;were originally written for use with the older SIOD based Script-Fu plug-in
+;of GIMP.
+;
+;All items defined in this file except for the random number routines are
+;deprecated. Existing scripts should be updated to avoid the use of the
+;compatibility functions and define statements which follow the random number
+;generator routines.
+;
+;The items marked as deprecated at the end of this file may be removed
+;at some later date.
+
+
+;The random number generator routines below have been slightly reformatted.
+;A couple of define blocks which are not needed have been commented out.
+;It has also been extended to enable it to generate numbers with exactly 31
+;bits or more.
+;The original file was called rand2.scm and can be found in:
+;http://www-2.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/scheme/code/math/random/
+
+; Minimal Standard Random Number Generator
+; Park & Miller, CACM 31(10), Oct 1988, 32 bit integer version.
+; better constants, as proposed by Park.
+; By Ozan Yigit
+
+;(define *seed* 1)
+
+(define (srand seed)
+ (set! *seed* seed)
+ *seed*
+)
+
+(define (msrg-rand)
+ (let (
+ (A 48271)
+ (M 2147483647)
+ (Q 44488)
+ (R 3399)
+ )
+ (let* (
+ (hi (quotient *seed* Q))
+ (lo (modulo *seed* Q))
+ (test (- (* A lo) (* R hi)))
+ )
+ (if (> test 0)
+ (set! *seed* test)
+ (set! *seed* (+ test M))
+ )
+ )
+ )
+ *seed*
+)
+
+; poker test
+; seed 1
+; cards 0-9 inclusive (random 10)
+; five cards per hand
+; 10000 hands
+;
+; Poker Hand Example Probability Calculated
+; 5 of a kind (aaaaa) 0.0001 0
+; 4 of a kind (aaaab) 0.0045 0.0053
+; Full house (aaabb) 0.009 0.0093
+; 3 of a kind (aaabc) 0.072 0.0682
+; two pairs (aabbc) 0.108 0.1104
+; Pair (aabcd) 0.504 0.501
+; Bust (abcde) 0.3024 0.3058
+
+(define (random n)
+ (define (internal-random n)
+ (let* (
+ (n (inexact->exact (truncate n)))
+ (M 2147483647)
+ (slop (modulo M (abs n)))
+ )
+ (let loop ((r (msrg-rand)))
+ (if (>= r slop)
+ (modulo r n)
+ (loop (msrg-rand))
+ )
+ )
+ )
+ )
+
+ ; Negative numbers have a bigger range in twos complement platforms
+ ; (nearly all platforms out there) than positive ones, so we deal with
+ ; the numbers in negative form.
+ (if (> n 0)
+ (+ n (random (- n)))
+
+ (if (>= n -2147483647)
+ (internal-random n)
+
+ ; 31-or-more-bits number requested - needs multiple extractions
+ ; because we don't generate enough random bits.
+ (if (>= n -1152921504606846975)
+ ; Up to 2^60-1, two extractions are enough
+ (let ((q (- (quotient (+ n 1) 1073741824) 1))) ; q=floor(n/2^30)
+ (let loop ()
+ (let ((big (+ (* (internal-random q) 1073741824)
+ (internal-random -1073741824)
+ )
+ ))
+ (if (> big n)
+ big
+ (loop)
+ )
+ )
+ )
+ )
+
+ ; From 2^60 up, we do three extractions.
+ ; The code is better understood if seen as generating three
+ ; digits in base 2^30. q is the maximum value the first digit
+ ; can take. The other digits can take the full range.
+ ;
+ ; The strategy is to generate a random number digit by digit.
+ ; Here's an example in base 10. Say the input n is 348
+ ; (thus requesting a number between 0 and 347). Then the algorithm
+ ; first calls (internal-random 4) to get a digit between 0 and 3,
+ ; then (internal-random 10) twice to get two more digits between
+ ; 0 and 9. Say the result is 366: since it is greater than 347,
+ ; it's discarded and the process restarted. When the result is
+ ; <= 347, that's the returned value. The probability of it being
+ ; greater than the max is always strictly less than 1/2.
+ ;
+ ; This is the same idea but in base 2^30 (1073741824). The
+ ; first digit's weight is (2^30)^2 = 1152921504606846976,
+ ; similarly to how in our base 10 example, the first digit's
+ ; weight is 10^2 = 100. In the base 10 example we first divide
+ ; the target number 348 by 100, taking the ceiling, to get 4.
+ ; Here we divide by (2^30)^2 instead, taking the ceiling too.
+ ;
+ ; The math is a bit obscured by the fact that we generate
+ ; the digits as negative, so that the result is negative as
+ ; well, but it's really the same thing. Changing the sign of
+ ; every digit just changes the sign of the result.
+ ;
+ ; This method works for n up to (2^30)^2*(2^31-1) which is
+ ; 2475880077417839045191401472 (slightly under 91 bits). That
+ ; covers the 64-bit range comfortably, and some more. If larger
+ ; numbers are needed, they'll have to be composed with a
+ ; user-defined procedure.
+
+ (if (>= n -2475880077417839045191401472)
+ (let ((q (- (quotient (+ n 1) 1152921504606846976) 1))) ; q=floor(n/2^60)
+ (let loop ()
+ (let ((big (+ (* (internal-random q) 1152921504606846976)
+ (* (internal-random -1073741824) 1073741824)
+ (internal-random -1073741824)
+ )
+ ))
+ (if (> big n)
+ big
+ (loop)
+ )
+ )
+ )
+ )
+ (error "requested (random n) range too large")
+ )
+ )
+ )
+ )
+)
+
+;(define (rngtest)
+; (display "implementation ")
+; (srand 1)
+; (do
+; ( (n 0 (+ n 1)) )
+; ( (>= n 10000) )
+; (msrg-rand)
+; )
+; (if (= *seed* 399268537)
+; (display "looks correct.")
+; (begin
+; (display "failed.")
+; (newline)
+; (display " current seed ") (display *seed*)
+; (newline)
+; (display " correct seed 399268537")
+; )
+; )
+; (newline)
+;)
+
+
+;This macro defines a while loop which is needed by some older scripts.
+;This is here since it is not defined in R5RS and could be handy to have.
+
+;This while macro was found at:
+;http://www.aracnet.com/~briand/scheme_eval.html
+(define-macro (while test . body)
+ `(let loop ()
+ (cond
+ (,test
+ ,@body
+ (loop)
+ )
+ )
+ )
+)
+
+
+;The following define block(s) require the tsx extension to be loaded
+
+(define (realtime)
+ (car (gettimeofday))
+)
+
+
+;Items below this line are for compatibility with Script-Fu but
+;may be useful enough to keep around
+
+(define (delq item lis)
+ (let ((l '()))
+ (unless (null? lis)
+ (while (pair? lis)
+ (if (<> item (car lis))
+ (set! l (append l (list (car lis))))
+ )
+ (set! lis (cdr lis))
+ )
+ )
+
+ l
+ )
+)
+
+(define (make-list count fill)
+ (vector->list (make-vector count fill))
+)
+
+(define (strbreakup str sep)
+ (let* (
+ (seplen (string-length sep))
+ (start 0)
+ (end (string-length str))
+ (i start)
+ (l '())
+ )
+
+ (if (= seplen 0)
+ (set! l (list str))
+ (begin
+ (while (<= i (- end seplen))
+ (if (substring-equal? sep str i (+ i seplen))
+ (begin
+ (if (= start 0)
+ (set! l (list (substring str start i)))
+ (set! l (append l (list (substring str start i))))
+ )
+ (set! start (+ i seplen))
+ (set! i (+ i seplen -1))
+ )
+ )
+
+ (set! i (+ i 1))
+ )
+
+ (set! l (append l (list (substring str start end))))
+ )
+ )
+
+ l
+ )
+)
+
+(define (string-downcase str)
+ (list->string (map char-downcase (string->list str)))
+)
+
+(define (string-trim str)
+ (string-trim-right (string-trim-left str))
+)
+
+(define (string-trim-left str)
+ (let (
+ (strlen (string-length str))
+ (i 0)
+ )
+
+ (while (and (< i strlen)
+ (char-whitespace? (string-ref str i))
+ )
+ (set! i (+ i 1))
+ )
+
+ (substring str i (string-length str))
+ )
+)
+
+(define (string-trim-right str)
+ (let ((i (- (string-length str) 1)))
+
+ (while (and (>= i 0)
+ (char-whitespace? (string-ref str i))
+ )
+ (set! i (- i 1))
+ )
+
+ (substring str 0 (+ i 1))
+ )
+)
+
+(define (string-upcase str)
+ (list->string (map char-upcase (string->list str)))
+)
+
+(define (substring-equal? str str2 start end)
+ (string=? str (substring str2 start end))
+)
+
+(define (unbreakupstr stringlist sep)
+ (let ((str (car stringlist)))
+
+ (set! stringlist (cdr stringlist))
+ (while (not (null? stringlist))
+ (set! str (string-append str sep (car stringlist)))
+ (set! stringlist (cdr stringlist))
+ )
+
+ str
+ )
+)
+
+
+;Items below this line are deprecated and should not be used in new scripts.
+
+(define aset vector-set!)
+(define aref vector-ref)
+(define fopen open-input-file)
+(define mapcar map)
+(define nil '())
+(define nreverse reverse)
+(define pow expt)
+(define prin1 write)
+
+(define (print obj . port)
+ (apply write obj port)
+ (newline)
+)
+
+(define strcat string-append)
+(define string-lessp string<?)
+(define symbol-bound? defined?)
+(define the-environment current-environment)
+
+(define *pi*
+ (* 4 (atan 1.0))
+)
+
+(define (butlast x)
+ (if (= (length x) 1)
+ '()
+ (reverse (cdr (reverse x)))
+ )
+)
+
+(define (cons-array count type)
+ (case type
+ ((long) (make-vector count 0))
+ ((short) (make-vector count 0))
+ ((byte) (make-vector count 0))
+ ((double) (make-vector count 0.0))
+ ((string) (vector->list (make-vector count "")))
+ (else type)
+ )
+)
+
+(define (fmod a b)
+ (- a (* (truncate (/ a b)) b))
+)
+
+(define (fread arg1 file)
+
+ (define (fread-get-chars count file)
+ (let (
+ (str "")
+ (c 0)
+ )
+
+ (while (> count 0)
+ (set! count (- count 1))
+ (set! c (read-char file))
+ (if (eof-object? c)
+ (set! count 0)
+ (set! str (string-append str (make-string 1 c)))
+ )
+ )
+
+ (if (eof-object? c)
+ ()
+ str
+ )
+ )
+ )
+
+ (if (number? arg1)
+ (begin
+ (set! arg1 (inexact->exact (truncate arg1)))
+ (fread-get-chars arg1 file)
+ )
+ (begin
+ (set! arg1 (fread-get-chars (string-length arg1) file))
+ (string-length arg1)
+ )
+ )
+)
+
+(define (last x)
+ (cons (car (reverse x)) '())
+)
+
+(define (nth k list)
+ (list-ref list k)
+)
+
+(define (prog1 form1 . form2)
+ (let ((a form1))
+ (if (not (null? form2))
+ form2
+ )
+ a
+ )
+)
+
+(define (rand . modulus)
+ (if (null? modulus)
+ (msrg-rand)
+ (apply random modulus)
+ )
+)
+
+(define (strcmp str1 str2)
+ (if (string<? str1 str2)
+ -1
+ (if (string>? str1 str2)
+ 1
+ 0
+ )
+ )
+)
+
+(define (trunc n)
+ (inexact->exact (truncate n))
+)
+
+(define verbose
+ (lambda n
+ (if (or (null? n) (not (number? (car n))))
+ 0
+ (car n)
+ )
+ )
+)
diff --git a/plug-ins/script-fu/scripts/script-fu-set-cmap.scm b/plug-ins/script-fu/scripts/script-fu-set-cmap.scm
new file mode 100644
index 0000000..259d5de
--- /dev/null
+++ b/plug-ins/script-fu/scripts/script-fu-set-cmap.scm
@@ -0,0 +1,64 @@
+; Set Colormap v1.1 September 29, 2004
+; by Kevin Cozens <kcozens@interlog.com>
+;
+; Change the colormap of an image to the colors in a specified palette.
+; Included is script-fu-make-cmap-array (available for use in scripts) which
+; returns an INT8ARRAY containing the colors from a specified palette.
+; This array can be used as the cmap argument for gimp-image-set-colormap.
+
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+(define (script-fu-make-cmap-array palette)
+ (let* (
+ (num-colors (car (gimp-palette-get-info palette)))
+ (cmap (cons-array (* num-colors 3) 'byte))
+ (color 0)
+ (i 0)
+ )
+
+ (while (< i num-colors)
+ (set! color (car (gimp-palette-entry-get-color palette i)))
+ (aset cmap (* i 3) (car color))
+ (aset cmap (+ (* i 3) 1) (cadr color))
+ (aset cmap (+ (* i 3) 2) (caddr color))
+ (set! i (+ i 1))
+ )
+
+ cmap
+ )
+)
+
+(define (script-fu-set-cmap img drawable palette)
+ (gimp-image-set-colormap img
+ (* (car (gimp-palette-get-info palette)) 3)
+ (script-fu-make-cmap-array palette))
+ (gimp-displays-flush)
+)
+
+(script-fu-register "script-fu-set-cmap"
+ _"Se_t Colormap..."
+ _"Change the colormap of an image to the colors in a specified palette."
+ "Kevin Cozens <kcozens@interlog.com>"
+ "Kevin Cozens"
+ "September 29, 2004"
+ "INDEXED*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-PALETTE _"Palette" "Default"
+)
+
+(script-fu-menu-register "script-fu-set-cmap" "<Image>/Colors/Map/Colormap")
diff --git a/plug-ins/script-fu/scripts/script-fu-util.scm b/plug-ins/script-fu/scripts/script-fu-util.scm
new file mode 100644
index 0000000..a22114d
--- /dev/null
+++ b/plug-ins/script-fu/scripts/script-fu-util.scm
@@ -0,0 +1,94 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+; Resizes the image so as to include the selected layer.
+; The resulting image has the selected layer size.
+; Copyright (C) 2002 Chauk-Mean PROUM
+;
+(define (script-fu-util-image-resize-from-layer image layer)
+ (let* (
+ (width (car (gimp-drawable-width layer)))
+ (height (car (gimp-drawable-height layer)))
+ (posx (- (car (gimp-drawable-offsets layer))))
+ (posy (- (cadr (gimp-drawable-offsets layer))))
+ )
+
+ (gimp-image-resize image width height posx posy)
+ )
+)
+
+; Add the specified layers to the image.
+; The layers will be added in the given order below the
+; active layer.
+;
+(define (script-fu-util-image-add-layers image . layers)
+ (while (not (null? layers))
+ (let ((layer (car layers)))
+ (set! layers (cdr layers))
+ (gimp-image-insert-layer image layer 0 -1)
+ (gimp-image-lower-item image layer)
+ )
+ )
+)
+
+; Allow command line usage of GIMP such as:
+;
+; gimp -i -b '(with-files "*.png" <body>)'
+;
+; where <body> is the code that handles whatever processing you want to
+; perform on the files. There are four variables that are available
+; within the <body>: 'basename', 'image', 'filename' and 'layer'.
+; The 'basename' is the name of the file with its extension removed,
+; while the other three variables are self-explanatory.
+; You basically write your code as though it were processing a single
+; 'image' and the 'with-files' macro applies it to all of the files
+; matching the pattern.
+;
+; For example, to invert the colors of all of the PNG files in the
+; start directory:
+;
+; gimp -i -b '(with-files "*.png" (gimp-drawable-invert layer FALSE) \
+; (gimp-file-save 1 image layer filename filename ))'
+;
+; To do the same thing, but saving them as jpeg instead:
+;
+; gimp -i -b '(with-files "*.png" (gimp-drawable-invert layer FALSE) \
+; (gimp-file-save 1 image layer \
+; (string-append basename ".jpg") \
+; (string-append basename ".jpg") ))'
+
+(define-macro (with-files pattern . body)
+ (let ((loop (gensym))
+ (filenames (gensym))
+ (filename (gensym)))
+ `(begin
+ (let ,loop ((,filenames (cadr (file-glob ,pattern 1))))
+ (unless (null? ,filenames)
+ (let* ((filename (car ,filenames))
+ (image (catch #f (car (gimp-file-load RUN-NONINTERACTIVE
+ filename
+ filename ))))
+ (layer (if image (car (gimp-image-get-active-layer image)) #f))
+ (basename (unbreakupstr (butlast (strbreakup filename ".")) ".")))
+ (when image
+ ,@body
+ (gimp-image-delete image)))
+ (,loop (cdr ,filenames))
+ )
+ )
+ )
+ )
+)
diff --git a/plug-ins/script-fu/scripts/script-fu.init b/plug-ins/script-fu/scripts/script-fu.init
new file mode 100644
index 0000000..d0c9e30
--- /dev/null
+++ b/plug-ins/script-fu/scripts/script-fu.init
@@ -0,0 +1,716 @@
+; Initialization file for TinySCHEME 1.40
+
+; Per R5RS, up to four deep compositions should be defined
+(define (caar x) (car (car x)))
+(define (cadr x) (car (cdr x)))
+(define (cdar x) (cdr (car x)))
+(define (cddr x) (cdr (cdr x)))
+(define (caaar x) (car (car (car x))))
+(define (caadr x) (car (car (cdr x))))
+(define (cadar x) (car (cdr (car x))))
+(define (caddr x) (car (cdr (cdr x))))
+(define (cdaar x) (cdr (car (car x))))
+(define (cdadr x) (cdr (car (cdr x))))
+(define (cddar x) (cdr (cdr (car x))))
+(define (cdddr x) (cdr (cdr (cdr x))))
+(define (caaaar x) (car (car (car (car x)))))
+(define (caaadr x) (car (car (car (cdr x)))))
+(define (caadar x) (car (car (cdr (car x)))))
+(define (caaddr x) (car (car (cdr (cdr x)))))
+(define (cadaar x) (car (cdr (car (car x)))))
+(define (cadadr x) (car (cdr (car (cdr x)))))
+(define (caddar x) (car (cdr (cdr (car x)))))
+(define (cadddr x) (car (cdr (cdr (cdr x)))))
+(define (cdaaar x) (cdr (car (car (car x)))))
+(define (cdaadr x) (cdr (car (car (cdr x)))))
+(define (cdadar x) (cdr (car (cdr (car x)))))
+(define (cdaddr x) (cdr (car (cdr (cdr x)))))
+(define (cddaar x) (cdr (cdr (car (car x)))))
+(define (cddadr x) (cdr (cdr (car (cdr x)))))
+(define (cdddar x) (cdr (cdr (cdr (car x)))))
+(define (cddddr x) (cdr (cdr (cdr (cdr x)))))
+
+;;;; Utility to ease macro creation
+(define (macro-expand form)
+ ((eval (get-closure-code (eval (car form)))) form))
+
+(define (macro-expand-all form)
+ (if (macro? form)
+ (macro-expand-all (macro-expand form))
+ form))
+
+(define *compile-hook* macro-expand-all)
+
+
+(macro (unless form)
+ `(if (not ,(cadr form)) (begin ,@(cddr form))))
+
+(macro (when form)
+ `(if ,(cadr form) (begin ,@(cddr form))))
+
+; DEFINE-MACRO Contributed by Andy Gaynor
+(macro (define-macro dform)
+ (if (symbol? (cadr dform))
+ `(macro ,@(cdr dform))
+ (let ((form (gensym)))
+ `(macro (,(caadr dform) ,form)
+ (apply (lambda ,(cdadr dform) ,@(cddr dform)) (cdr ,form))))))
+
+; Utilities for math. Notice that inexact->exact is primitive,
+; but exact->inexact is not.
+(define exact? integer?)
+(define (inexact? x) (and (real? x) (not (integer? x))))
+(define (even? n) (= (remainder n 2) 0))
+(define (odd? n) (not (= (remainder n 2) 0)))
+(define (zero? n) (= n 0))
+(define (positive? n) (> n 0))
+(define (negative? n) (< n 0))
+(define complex? number?)
+(define rational? real?)
+(define (abs n) (if (>= n 0) n (- n)))
+(define (exact->inexact n) (* n 1.0))
+(define (<> n1 n2) (not (= n1 n2)))
+
+; min and max must return inexact if any arg is inexact; use (+ n 0.0)
+(define (max . lst)
+ (foldr (lambda (a b)
+ (if (> a b)
+ (if (exact? b) a (+ a 0.0))
+ (if (exact? a) b (+ b 0.0))))
+ (car lst) (cdr lst)))
+(define (min . lst)
+ (foldr (lambda (a b)
+ (if (< a b)
+ (if (exact? b) a (+ a 0.0))
+ (if (exact? a) b (+ b 0.0))))
+ (car lst) (cdr lst)))
+
+(define (succ x) (+ x 1))
+(define (pred x) (- x 1))
+(define gcd
+ (lambda a
+ (if (null? a)
+ 0
+ (let ((aa (abs (car a)))
+ (bb (abs (cadr a))))
+ (if (= bb 0)
+ aa
+ (gcd bb (remainder aa bb)))))))
+(define lcm
+ (lambda a
+ (if (null? a)
+ 1
+ (let ((aa (abs (car a)))
+ (bb (abs (cadr a))))
+ (if (or (= aa 0) (= bb 0))
+ 0
+ (abs (* (quotient aa (gcd aa bb)) bb)))))))
+
+
+(define (string . charlist)
+ (list->string charlist))
+
+(define (list->string charlist)
+ (let* ((len (length charlist))
+ (newstr (make-string len))
+ (fill-string!
+ (lambda (str i len charlist)
+ (if (= i len)
+ str
+ (begin (string-set! str i (car charlist))
+ (fill-string! str (+ i 1) len (cdr charlist)))))))
+ (fill-string! newstr 0 len charlist)))
+
+(define (string-fill! s e)
+ (let ((n (string-length s)))
+ (let loop ((i 0))
+ (if (= i n)
+ s
+ (begin (string-set! s i e) (loop (succ i)))))))
+
+(define (string->list s)
+ (let loop ((n (pred (string-length s))) (l '()))
+ (if (= n -1)
+ l
+ (loop (pred n) (cons (string-ref s n) l)))))
+
+(define (string-copy str)
+ (string-append str))
+
+(define (string->anyatom str pred)
+ (let* ((a (string->atom str)))
+ (if (pred a) a
+ (error "string->xxx: not a xxx" a))))
+
+(define (string->number str . base)
+ (let ((n (string->atom str (if (null? base) 10 (car base)))))
+ (if (number? n) n #f)))
+
+(define (anyatom->string n pred)
+ (if (pred n)
+ (atom->string n)
+ (error "xxx->string: not a xxx" n)))
+
+
+(define (number->string n . base)
+ (atom->string n (if (null? base) 10 (car base))))
+
+(define (char-cmp? cmp a b)
+ (cmp (char->integer a) (char->integer b)))
+(define (char-ci-cmp? cmp a b)
+ (cmp (char->integer (char-downcase a)) (char->integer (char-downcase b))))
+
+(define (char=? a b) (char-cmp? = a b))
+(define (char<? a b) (char-cmp? < a b))
+(define (char>? a b) (char-cmp? > a b))
+(define (char<=? a b) (char-cmp? <= a b))
+(define (char>=? a b) (char-cmp? >= a b))
+
+(define (char-ci=? a b) (char-ci-cmp? = a b))
+(define (char-ci<? a b) (char-ci-cmp? < a b))
+(define (char-ci>? a b) (char-ci-cmp? > a b))
+(define (char-ci<=? a b) (char-ci-cmp? <= a b))
+(define (char-ci>=? a b) (char-ci-cmp? >= a b))
+
+; Note the trick of returning (cmp x y)
+(define (string-cmp? chcmp cmp a b)
+ (let ((na (string-length a)) (nb (string-length b)))
+ (let loop ((i 0))
+ (cond
+ ((= i na)
+ (if (= i nb) (cmp 0 0) (cmp 0 1)))
+ ((= i nb)
+ (cmp 1 0))
+ ((chcmp = (string-ref a i) (string-ref b i))
+ (loop (succ i)))
+ (else
+ (chcmp cmp (string-ref a i) (string-ref b i)))))))
+
+
+(define (string=? a b) (string-cmp? char-cmp? = a b))
+(define (string<? a b) (string-cmp? char-cmp? < a b))
+(define (string>? a b) (string-cmp? char-cmp? > a b))
+(define (string<=? a b) (string-cmp? char-cmp? <= a b))
+(define (string>=? a b) (string-cmp? char-cmp? >= a b))
+
+(define (string-ci=? a b) (string-cmp? char-ci-cmp? = a b))
+(define (string-ci<? a b) (string-cmp? char-ci-cmp? < a b))
+(define (string-ci>? a b) (string-cmp? char-ci-cmp? > a b))
+(define (string-ci<=? a b) (string-cmp? char-ci-cmp? <= a b))
+(define (string-ci>=? a b) (string-cmp? char-ci-cmp? >= a b))
+
+(define (list . x) x)
+
+(define (foldr f x lst)
+ (if (null? lst)
+ x
+ (foldr f (f x (car lst)) (cdr lst))))
+
+(define (unzip1-with-cdr . lists)
+ (unzip1-with-cdr-iterative lists '() '()))
+
+(define (unzip1-with-cdr-iterative lists cars cdrs)
+ (if (null? lists)
+ (cons cars cdrs)
+ (let ((car1 (caar lists))
+ (cdr1 (cdar lists)))
+ (unzip1-with-cdr-iterative
+ (cdr lists)
+ (append cars (list car1))
+ (append cdrs (list cdr1))))))
+
+(define (map proc . lists)
+ (if (null? lists)
+ (apply proc)
+ (if (null? (car lists))
+ '()
+ (let* ((unz (apply unzip1-with-cdr lists))
+ (cars (car unz))
+ (cdrs (cdr unz)))
+ (cons (apply proc cars) (apply map (cons proc cdrs)))))))
+
+(define (for-each proc . lists)
+ (if (null? lists)
+ (apply proc)
+ (if (null? (car lists))
+ #t
+ (let* ((unz (apply unzip1-with-cdr lists))
+ (cars (car unz))
+ (cdrs (cdr unz)))
+ (apply proc cars) (apply map (cons proc cdrs))))))
+
+(define (list-tail x k)
+ (if (zero? k)
+ x
+ (list-tail (cdr x) (- k 1))))
+
+(define (list-ref x k)
+ (car (list-tail x k)))
+
+(define (last-pair x)
+ (if (pair? (cdr x))
+ (last-pair (cdr x))
+ x))
+
+(define (head stream) (car stream))
+
+(define (tail stream) (force (cdr stream)))
+
+(define (vector-equal? x y)
+ (and (vector? x) (vector? y) (= (vector-length x) (vector-length y))
+ (let ((n (vector-length x)))
+ (let loop ((i 0))
+ (if (= i n)
+ #t
+ (and (equal? (vector-ref x i) (vector-ref y i))
+ (loop (succ i))))))))
+
+(define (list->vector x)
+ (apply vector x))
+
+(define (vector-fill! v e)
+ (let ((n (vector-length v)))
+ (let loop ((i 0))
+ (if (= i n)
+ v
+ (begin (vector-set! v i e) (loop (succ i)))))))
+
+(define (vector->list v)
+ (let loop ((n (pred (vector-length v))) (l '()))
+ (if (= n -1)
+ l
+ (loop (pred n) (cons (vector-ref v n) l)))))
+
+;; The following quasiquote macro is due to Eric S. Tiedemann.
+;; Copyright 1988 by Eric S. Tiedemann; all rights reserved.
+;;
+;; Subsequently modified to handle vectors: D. Souflis
+
+(macro
+ quasiquote
+ (lambda (l)
+ (define (mcons f l r)
+ (if (and (pair? r)
+ (eq? (car r) 'quote)
+ (eq? (car (cdr r)) (cdr f))
+ (pair? l)
+ (eq? (car l) 'quote)
+ (eq? (car (cdr l)) (car f)))
+ (if (or (procedure? f) (number? f) (string? f))
+ f
+ (list 'quote f))
+ (if (eqv? l vector)
+ (apply l (eval r))
+ (list 'cons l r)
+ )))
+ (define (mappend f l r)
+ (if (or (null? (cdr f))
+ (and (pair? r)
+ (eq? (car r) 'quote)
+ (eq? (car (cdr r)) '())))
+ l
+ (list 'append l r)))
+ (define (foo level form)
+ (cond ((not (pair? form))
+ (if (or (procedure? form) (number? form) (string? form))
+ form
+ (list 'quote form))
+ )
+ ((eq? 'quasiquote (car form))
+ (mcons form ''quasiquote (foo (+ level 1) (cdr form))))
+ (#t (if (zero? level)
+ (cond ((eq? (car form) 'unquote) (car (cdr form)))
+ ((eq? (car form) 'unquote-splicing)
+ (error "Unquote-splicing wasn't in a list:"
+ form))
+ ((and (pair? (car form))
+ (eq? (car (car form)) 'unquote-splicing))
+ (mappend form (car (cdr (car form)))
+ (foo level (cdr form))))
+ (#t (mcons form (foo level (car form))
+ (foo level (cdr form)))))
+ (cond ((eq? (car form) 'unquote)
+ (mcons form ''unquote (foo (- level 1)
+ (cdr form))))
+ ((eq? (car form) 'unquote-splicing)
+ (mcons form ''unquote-splicing
+ (foo (- level 1) (cdr form))))
+ (#t (mcons form (foo level (car form))
+ (foo level (cdr form)))))))))
+ (foo 0 (car (cdr l)))))
+
+;;;;;Helper for the dynamic-wind definition. By Tom Breton (Tehom)
+(define (shared-tail x y)
+ (let ((len-x (length x))
+ (len-y (length y)))
+ (define (shared-tail-helper x y)
+ (if
+ (eq? x y)
+ x
+ (shared-tail-helper (cdr x) (cdr y))))
+
+ (cond
+ ((> len-x len-y)
+ (shared-tail-helper
+ (list-tail x (- len-x len-y))
+ y))
+ ((< len-x len-y)
+ (shared-tail-helper
+ x
+ (list-tail y (- len-y len-x))))
+ (#t (shared-tail-helper x y)))))
+
+;;;;;Dynamic-wind by Tom Breton (Tehom)
+
+;;Guarded because we must only eval this once, because doing so
+;;redefines call/cc in terms of old call/cc
+(unless (defined? 'dynamic-wind)
+ (let
+ ;;These functions are defined in the context of a private list of
+ ;;pairs of before/after procs.
+ ( (*active-windings* '())
+ ;;We'll define some functions into the larger environment, so
+ ;;we need to know it.
+ (outer-env (current-environment)))
+
+ ;;Poor-man's structure operations
+ (define before-func car)
+ (define after-func cdr)
+ (define make-winding cons)
+
+ ;;Manage active windings
+ (define (activate-winding! new)
+ ((before-func new))
+ (set! *active-windings* (cons new *active-windings*)))
+ (define (deactivate-top-winding!)
+ (let ((old-top (car *active-windings*)))
+ ;;Remove it from the list first so it's not active during its
+ ;;own exit.
+ (set! *active-windings* (cdr *active-windings*))
+ ((after-func old-top))))
+
+ (define (set-active-windings! new-ws)
+ (unless (eq? new-ws *active-windings*)
+ (let ((shared (shared-tail new-ws *active-windings*)))
+
+ ;;Define the looping functions.
+ ;;Exit the old list. Do deeper ones last. Don't do
+ ;;any shared ones.
+ (define (pop-many)
+ (unless (eq? *active-windings* shared)
+ (deactivate-top-winding!)
+ (pop-many)))
+ ;;Enter the new list. Do deeper ones first so that the
+ ;;deeper windings will already be active. Don't do any
+ ;;shared ones.
+ (define (push-many new-ws)
+ (unless (eq? new-ws shared)
+ (push-many (cdr new-ws))
+ (activate-winding! (car new-ws))))
+
+ ;;Do it.
+ (pop-many)
+ (push-many new-ws))))
+
+ ;;The definitions themselves.
+ (eval
+ `(define call-with-current-continuation
+ ;;It internally uses the built-in call/cc, so capture it.
+ ,(let ((old-c/cc call-with-current-continuation))
+ (lambda (func)
+ ;;Use old call/cc to get the continuation.
+ (old-c/cc
+ (lambda (continuation)
+ ;;Call func with not the continuation itself
+ ;;but a procedure that adjusts the active
+ ;;windings to what they were when we made
+ ;;this, and only then calls the
+ ;;continuation.
+ (func
+ (let ((current-ws *active-windings*))
+ (lambda (x)
+ (set-active-windings! current-ws)
+ (continuation x)))))))))
+ outer-env)
+ ;;We can't just say "define (dynamic-wind before thunk after)"
+ ;;because the lambda it's defined to lives in this environment,
+ ;;not in the global environment.
+ (eval
+ `(define dynamic-wind
+ ,(lambda (before thunk after)
+ ;;Make a new winding
+ (activate-winding! (make-winding before after))
+ (let ((result (thunk)))
+ ;;Get rid of the new winding.
+ (deactivate-top-winding!)
+ ;;The return value is that of thunk.
+ result)))
+ outer-env)))
+
+(define call/cc call-with-current-continuation)
+
+
+;;;;; atom? and equal? written by a.k
+
+;;;; atom?
+(define (atom? x)
+ (not (pair? x)))
+
+;;;; equal?
+(define (equal? x y)
+ (cond
+ ((pair? x)
+ (and (pair? y)
+ (equal? (car x) (car y))
+ (equal? (cdr x) (cdr y))))
+ ((vector? x)
+ (and (vector? y) (vector-equal? x y)))
+ ((string? x)
+ (and (string? y) (string=? x y)))
+ (else (eqv? x y))))
+
+;;;; (do ((var init inc) ...) (endtest result ...) body ...)
+;;
+(macro do
+ (lambda (do-macro)
+ (apply (lambda (do vars endtest . body)
+ (let ((do-loop (gensym)))
+ `(letrec ((,do-loop
+ (lambda ,(map (lambda (x)
+ (if (pair? x) (car x) x))
+ `,vars)
+ (if ,(car endtest)
+ (begin ,@(cdr endtest))
+ (begin
+ ,@body
+ (,do-loop
+ ,@(map (lambda (x)
+ (cond
+ ((not (pair? x)) x)
+ ((< (length x) 3) (car x))
+ (else (car (cdr (cdr x))))))
+ `,vars)))))))
+ (,do-loop
+ ,@(map (lambda (x)
+ (if (and (pair? x) (cdr x))
+ (car (cdr x))
+ '()))
+ `,vars)))))
+ do-macro)))
+
+;;;; generic-member
+(define (generic-member cmp obj lst)
+ (cond
+ ((null? lst) #f)
+ ((cmp obj (car lst)) lst)
+ (else (generic-member cmp obj (cdr lst)))))
+
+(define (memq obj lst)
+ (generic-member eq? obj lst))
+(define (memv obj lst)
+ (generic-member eqv? obj lst))
+(define (member obj lst)
+ (generic-member equal? obj lst))
+
+;;;; generic-assoc
+(define (generic-assoc cmp obj alst)
+ (cond
+ ((null? alst) #f)
+ ((cmp obj (caar alst)) (car alst))
+ (else (generic-assoc cmp obj (cdr alst)))))
+
+(define (assq obj alst)
+ (generic-assoc eq? obj alst))
+(define (assv obj alst)
+ (generic-assoc eqv? obj alst))
+(define (assoc obj alst)
+ (generic-assoc equal? obj alst))
+
+(define (acons x y z) (cons (cons x y) z))
+
+;;;; Handy for imperative programs
+;;;; Used as: (define-with-return (foo x y) .... (return z) ...)
+(macro (define-with-return form)
+ `(define ,(cadr form)
+ (call/cc (lambda (return) ,@(cddr form)))))
+
+;;;; Simple exception handling
+;
+; Exceptions are caught as follows:
+;
+; (catch (do-something to-recover and-return meaningful-value)
+; (if-something goes-wrong)
+; (with-these calls))
+;
+; "Catch" establishes a scope spanning multiple call-frames
+; until another "catch" is encountered.
+;
+; Exceptions are thrown with:
+;
+; (throw "message")
+;
+; If used outside a (catch ...), reverts to (error "message)
+
+(define *handlers* (list))
+
+(define (push-handler proc)
+ (set! *handlers* (cons proc *handlers*)))
+
+(define (pop-handler)
+ (let ((h (car *handlers*)))
+ (set! *handlers* (cdr *handlers*))
+ h))
+
+(define (more-handlers?)
+ (pair? *handlers*))
+
+(define (throw . x)
+ (if (more-handlers?)
+ (apply (pop-handler))
+ (apply error x)))
+
+(macro (catch form)
+ (let ((label (gensym)))
+ `(call/cc (lambda (exit)
+ (push-handler (lambda () (exit ,(cadr form))))
+ (let ((,label (begin ,@(cddr form))))
+ (pop-handler)
+ ,label)))))
+
+(define *error-hook* throw)
+
+
+;;;;; Definition of MAKE-ENVIRONMENT, to be used with two-argument EVAL
+
+(macro (make-environment form)
+ `(apply (lambda ()
+ ,@(cdr form)
+ (current-environment))))
+
+(define-macro (eval-polymorphic x . envl)
+ (display envl)
+ (let* ((env (if (null? envl) (current-environment) (eval (car envl))))
+ (xval (eval x env)))
+ (if (closure? xval)
+ (make-closure (get-closure-code xval) env)
+ xval)))
+
+; Redefine this if you install another package infrastructure
+; Also redefine 'package'
+(define *colon-hook* eval)
+
+;;;;; I/O
+
+(define (input-output-port? p)
+ (and (input-port? p) (output-port? p)))
+
+(define (close-port p)
+ (cond
+ ((input-output-port? p) (close-input-port p) (close-output-port p))
+ ((input-port? p) (close-input-port p))
+ ((output-port? p) (close-output-port p))
+ (else (throw "Not a port" p))))
+
+(define (call-with-input-file s p)
+ (let ((inport (open-input-file s)))
+ (if (eq? inport #f)
+ #f
+ (let ((res (p inport)))
+ (close-input-port inport)
+ res))))
+
+(define (call-with-output-file s p)
+ (let ((outport (open-output-file s)))
+ (if (eq? outport #f)
+ #f
+ (let ((res (p outport)))
+ (close-output-port outport)
+ res))))
+
+(define (with-input-from-file s p)
+ (let ((inport (open-input-file s)))
+ (if (eq? inport #f)
+ #f
+ (let ((prev-inport (current-input-port)))
+ (set-input-port inport)
+ (let ((res (p)))
+ (close-input-port inport)
+ (set-input-port prev-inport)
+ res)))))
+
+(define (with-output-to-file s p)
+ (let ((outport (open-output-file s)))
+ (if (eq? outport #f)
+ #f
+ (let ((prev-outport (current-output-port)))
+ (set-output-port outport)
+ (let ((res (p)))
+ (close-output-port outport)
+ (set-output-port prev-outport)
+ res)))))
+
+(define (with-input-output-from-to-files si so p)
+ (let ((inport (open-input-file si))
+ (outport (open-input-file so)))
+ (if (not (and inport outport))
+ (begin
+ (close-input-port inport)
+ (close-output-port outport)
+ #f)
+ (let ((prev-inport (current-input-port))
+ (prev-outport (current-output-port)))
+ (set-input-port inport)
+ (set-output-port outport)
+ (let ((res (p)))
+ (close-input-port inport)
+ (close-output-port outport)
+ (set-input-port prev-inport)
+ (set-output-port prev-outport)
+ res)))))
+
+; Random number generator (maximum cycle)
+(define *seed* 1)
+(define (random-next)
+ (let* ((a 16807) (m 2147483647) (q (quotient m a)) (r (modulo m a)))
+ (set! *seed*
+ (- (* a (- *seed*
+ (* (quotient *seed* q) q)))
+ (* (quotient *seed* q) r)))
+ (if (< *seed* 0) (set! *seed* (+ *seed* m)))
+ *seed*))
+;; SRFI-0
+;; COND-EXPAND
+;; Implemented as a macro
+(define *features* '(srfi-0 tinyscheme))
+
+(define-macro (cond-expand . cond-action-list)
+ (cond-expand-runtime cond-action-list))
+
+(define (cond-expand-runtime cond-action-list)
+ (if (null? cond-action-list)
+ #t
+ (if (cond-eval (caar cond-action-list))
+ `(begin ,@(cdar cond-action-list))
+ (cond-expand-runtime (cdr cond-action-list)))))
+
+(define (cond-eval-and cond-list)
+ (foldr (lambda (x y) (and (cond-eval x) (cond-eval y))) #t cond-list))
+
+(define (cond-eval-or cond-list)
+ (foldr (lambda (x y) (or (cond-eval x) (cond-eval y))) #f cond-list))
+
+(define (cond-eval condition)
+ (cond
+ ((symbol? condition)
+ (if (member condition *features*) #t #f))
+ ((eq? condition #t) #t)
+ ((eq? condition #f) #f)
+ (else (case (car condition)
+ ((and) (cond-eval-and (cdr condition)))
+ ((or) (cond-eval-or (cdr condition)))
+ ((not) (if (not (null? (cddr condition)))
+ (error "cond-expand : 'not' takes 1 argument")
+ (not (cond-eval (cadr condition)))))
+ (else (error "cond-expand : unknown operator" (car condition)))))))
+
+(gc-verbose #f)
diff --git a/plug-ins/script-fu/scripts/select-to-brush.scm b/plug-ins/script-fu/scripts/select-to-brush.scm
new file mode 100644
index 0000000..c4ccf87
--- /dev/null
+++ b/plug-ins/script-fu/scripts/select-to-brush.scm
@@ -0,0 +1,144 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Selection-to-brush
+; Copyright (c) 1997 Adrian Likins
+; aklikins@eos.ncsu.edu
+;
+; Takes the current selection, saves it as a brush, and makes it the
+; active brush..
+;
+; Parts of this script from Sven Neuman's Drop-Shadow and
+; Seth Burgess's mkbrush scripts.
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-selection-to-brush image
+ drawable
+ name
+ filename
+ spacing)
+ (let* (
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (selection-bounds (gimp-selection-bounds image))
+ (select-offset-x (cadr selection-bounds))
+ (select-offset-y (caddr selection-bounds))
+ (selection-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (selection-height (- (caddr (cddr selection-bounds)) select-offset-y))
+ (from-selection 0)
+ (active-selection 0)
+ (brush-draw-type 0)
+ (brush-image-type 0)
+ (brush-image 0)
+ (brush-draw 0)
+ (filename2 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable image)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! from-selection FALSE)
+ )
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))
+ )
+ )
+
+ (gimp-edit-copy drawable)
+
+ (set! brush-draw-type
+ (if (= type GRAYA-IMAGE)
+ GRAY-IMAGE
+ RGBA-IMAGE))
+
+ (set! brush-image-type
+ (if (= type GRAYA-IMAGE)
+ GRAY
+ RGB))
+
+ (set! brush-image (car (gimp-image-new selection-width
+ selection-height
+ brush-image-type)))
+
+ (set! brush-draw
+ (car (gimp-layer-new brush-image
+ selection-width
+ selection-height
+ brush-draw-type
+ "Brush"
+ 100
+ LAYER-MODE-NORMAL)))
+
+ (gimp-image-insert-layer brush-image brush-draw 0 0)
+
+ (gimp-selection-none brush-image)
+
+ (if (= type GRAYA-IMAGE)
+ (begin
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-fill brush-draw FILL-BACKGROUND))
+ (gimp-drawable-fill brush-draw FILL-TRANSPARENT)
+ )
+
+ (let ((floating-sel (car (gimp-edit-paste brush-draw FALSE))))
+ (gimp-floating-sel-anchor floating-sel)
+ )
+
+ (set! filename2 (string-append gimp-directory
+ "/brushes/"
+ filename
+ (number->string image)
+ ".gbr"))
+
+ (file-gbr-save 1 brush-image brush-draw filename2 "" spacing name)
+
+ (if (= from-selection TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-image-remove-channel image active-selection)
+ )
+ )
+
+ (gimp-image-undo-enable image)
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-delete brush-image)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+
+ (gimp-brushes-refresh)
+ (gimp-context-set-brush name)
+ )
+)
+
+(script-fu-register "script-fu-selection-to-brush"
+ _"To _Brush..."
+ _"Convert a selection to a brush"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "10/07/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-STRING _"_Brush name" "My Brush"
+ SF-STRING _"_File name" "mybrush"
+ SF-ADJUSTMENT _"_Spacing" '(25 0 1000 1 1 1 0)
+)
diff --git a/plug-ins/script-fu/scripts/select-to-image.scm b/plug-ins/script-fu/scripts/select-to-image.scm
new file mode 100644
index 0000000..dec088d
--- /dev/null
+++ b/plug-ins/script-fu/scripts/select-to-image.scm
@@ -0,0 +1,89 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Selection to Image
+; Copyright (c) 1997 Adrian Likins
+; aklikins@eos.ncsu.edu
+;
+; Takes the Current selection and saves it as a separate image.
+;
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-selection-to-image image drawable)
+ (let* (
+ (draw-type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-type (car (gimp-image-base-type image)))
+ (selection-bounds (gimp-selection-bounds image))
+ (select-offset-x (cadr selection-bounds))
+ (select-offset-y (caddr selection-bounds))
+ (selection-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (selection-height (- (caddr (cddr selection-bounds)) select-offset-y))
+ (active-selection 0)
+ (from-selection 0)
+ (new-image 0)
+ (new-draw 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable image)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! active-selection (car (gimp-selection-save image)))
+ (set! from-selection FALSE)
+ )
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))
+ )
+ )
+
+ (gimp-edit-copy drawable)
+
+ (set! new-image (car (gimp-image-new selection-width
+ selection-height image-type)))
+ (set! new-draw (car (gimp-layer-new new-image
+ selection-width selection-height
+ draw-type "Selection" 100 LAYER-MODE-NORMAL)))
+ (gimp-image-insert-layer new-image new-draw 0 0)
+ (gimp-drawable-fill new-draw FILL-BACKGROUND)
+
+ (let ((floating-sel (car (gimp-edit-paste new-draw FALSE))))
+ (gimp-floating-sel-anchor floating-sel)
+ )
+
+ (gimp-image-undo-enable image)
+ (gimp-image-set-active-layer image drawable)
+ (gimp-display-new new-image)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-selection-to-image"
+ _"To _Image"
+ _"Convert a selection to an image"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "10/07/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+)
diff --git a/plug-ins/script-fu/scripts/select-to-pattern.scm b/plug-ins/script-fu/scripts/select-to-pattern.scm
new file mode 100644
index 0000000..6b2f9eb
--- /dev/null
+++ b/plug-ins/script-fu/scripts/select-to-pattern.scm
@@ -0,0 +1,103 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Based on select-to-brush by
+; Copyright (c) 1997 Adrian Likins aklikins@eos.ncsu.edu
+; Author Cameron Gregory, http://www.flamingtext.com/
+;
+; Takes the current selection, saves it as a pattern and makes it the active
+; pattern
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-selection-to-pattern image drawable desc filename)
+
+ (let* (
+ (selection-width 0)
+ (selection-height 0)
+ (selection-bounds 0)
+ (select-offset-x 0)
+ (select-offset-y 0)
+ (pattern-draw-type 0)
+ (pattern-image-type 0)
+ (pattern-image 0)
+ (pattern-draw 0)
+ (filename2 0)
+ )
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (set! selection-width (car (gimp-drawable-width drawable)))
+ (set! selection-height (car (gimp-drawable-height drawable)))
+ )
+ (begin
+ (set! selection-bounds (gimp-drawable-mask-bounds drawable))
+ (set! select-offset-x (cadr selection-bounds))
+ (set! select-offset-y (caddr selection-bounds))
+ (set! selection-width (- (cadr (cddr selection-bounds)) select-offset-x))
+ (set! selection-height (- (caddr (cddr selection-bounds)) select-offset-y))
+ )
+ )
+
+ (if (= (car (gimp-drawable-has-alpha drawable)) TRUE)
+ (set! pattern-draw-type RGBA-IMAGE)
+ (set! pattern-draw-type RGB-IMAGE)
+ )
+
+ (set! pattern-image-type RGB)
+
+ (set! pattern-image (car (gimp-image-new selection-width selection-height
+ pattern-image-type)))
+
+ (set! pattern-draw
+ (car (gimp-layer-new pattern-image selection-width selection-height
+ pattern-draw-type "Pattern" 100 LAYER-MODE-NORMAL)))
+
+ (gimp-drawable-fill pattern-draw FILL-TRANSPARENT)
+
+ (gimp-image-insert-layer pattern-image pattern-draw 0 0)
+
+ (gimp-edit-copy drawable)
+
+ (let ((floating-sel (car (gimp-edit-paste pattern-draw FALSE))))
+ (gimp-floating-sel-anchor floating-sel))
+
+ (set! filename2 (string-append gimp-directory
+ "/patterns/"
+ filename
+ (number->string image)
+ ".pat"))
+
+ (file-pat-save 1 pattern-image pattern-draw filename2 "" desc)
+ (gimp-patterns-refresh)
+ (gimp-context-set-pattern desc)
+
+ (gimp-image-delete pattern-image)
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-selection-to-pattern"
+ _"To _Pattern..."
+ _"Convert a selection to a pattern"
+ "Cameron Gregory <cameron@bloke.com>"
+ "Cameron Gregory"
+ "09/02/2003"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-STRING _"_Pattern name" "My Pattern"
+ SF-STRING _"_File name" "mypattern"
+)
diff --git a/plug-ins/script-fu/scripts/selection-round.scm b/plug-ins/script-fu/scripts/selection-round.scm
new file mode 100644
index 0000000..afbc91c
--- /dev/null
+++ b/plug-ins/script-fu/scripts/selection-round.scm
@@ -0,0 +1,164 @@
+; selection-rounded-rectangle.scm -*-scheme-*-
+
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+; CHANGE-LOG:
+; 1.00 - initial release
+; 1.01 - some code cleanup, no real changes
+; 1.02 - made script undoable
+
+; 2.00 - ALAN's Branch. changed name, menu, location, and description
+; 2.01 - fixed to work if there was no current selection.
+; 2.02 - changed scale to percentages, usability tweaking.
+; 2.10 - added concave round edges, updated description.
+; 2.11 - tweeked description, changed comments, relinquished any rights.
+
+; Copyright (C) 1997, 1998, Sven Neumann
+; Copyright (C) 2004, Alan Horkan.
+; Alan Horkan relinquishes all rights to his changes,
+; full ownership of this script belongs to Sven Neumann.
+
+(define (script-fu-selection-rounded-rectangle image drawable radius concave)
+ (gimp-image-undo-group-start image)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE) (gimp-selection-all image))
+ (let* (
+ (radius (/ radius 100)) ; convert from percentages
+ (radius (min radius 1.0))
+ (radius (max radius 0.0))
+ (select-bounds (gimp-selection-bounds image))
+ (has-selection (car select-bounds))
+ (select-x1 (cadr select-bounds))
+ (select-y1 (caddr select-bounds))
+ (select-x2 (cadr (cddr select-bounds)))
+ (select-y2 (caddr (cddr select-bounds)))
+ (select-width (- select-x2 select-x1))
+ (select-height (- select-y2 select-y1))
+ (cut-radius 0)
+ (ellipse-radius 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ ;; select to the full bounds of the selection,
+ ;; fills in irregular shapes or holes.
+ (gimp-image-select-rectangle image CHANNEL-OP-ADD
+ select-x1 select-y1 select-width select-height)
+
+ (if (> select-width select-height)
+ (set! cut-radius (trunc (+ 1 (* radius (/ select-height 2)))))
+ (set! cut-radius (trunc (+ 1 (* radius (/ select-width 2)))))
+ )
+ (set! ellipse-radius (* cut-radius 2))
+
+ (gimp-context-set-antialias TRUE)
+ ;; cut away rounded (concave) corners
+ ; top right
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT
+ (- select-x1 cut-radius)
+ (- select-y1 cut-radius)
+ (* cut-radius 2)
+ (* cut-radius 2))
+ ; lower left
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT
+ (- select-x1 cut-radius)
+ (- select-y2 cut-radius)
+ (* cut-radius 2)
+ (* cut-radius 2))
+ ; top right
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT
+ (- select-x2 cut-radius)
+ (- select-y1 cut-radius)
+ (* cut-radius 2)
+ (* cut-radius 2))
+ ; bottom left
+ (gimp-image-select-ellipse image CHANNEL-OP-SUBTRACT
+ (- select-x2 cut-radius)
+ (- select-y2 cut-radius)
+ (* cut-radius 2)
+ (* cut-radius 2))
+
+ ;; add in rounded (convex) corners
+ (if (= concave FALSE)
+ (begin
+ (gimp-image-select-ellipse image
+ CHANNEL-OP-ADD
+ select-x1
+ select-y1
+ ellipse-radius
+ ellipse-radius)
+ (gimp-image-select-ellipse image
+ CHANNEL-OP-ADD
+ select-x1
+ (- select-y2 ellipse-radius)
+ ellipse-radius
+ ellipse-radius)
+ (gimp-image-select-ellipse image
+ CHANNEL-OP-ADD
+ (- select-x2 ellipse-radius)
+ select-y1
+ ellipse-radius
+ ellipse-radius)
+ (gimp-image-select-ellipse image
+ CHANNEL-OP-ADD
+ (- select-x2 ellipse-radius)
+ (- select-y2 ellipse-radius)
+ ellipse-radius
+ ellipse-radius)
+ )
+ )
+
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+ (gimp-context-pop)
+ )
+)
+
+
+(define (script-fu-selection-round image drawable radius)
+ (script-fu-selection-rounded-rectangle image drawable (* radius 100) FALSE)
+)
+
+
+(script-fu-register "script-fu-selection-rounded-rectangle"
+ _"Rounded R_ectangle..."
+ _"Round the corners of the current selection"
+ "Alan Horkan, Sven Neumann" ; authors
+ "Sven Neumann" ; copyright
+ "2004/06/07"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"R_adius (%)" '(50 0 100 1 10 0 0)
+ SF-TOGGLE _"Co_ncave" FALSE
+)
+
+(script-fu-register "script-fu-selection-round"
+ ""
+ "This procedure is deprecated! Use 'script-fu-selection-rounded-rectangle' instead."
+ "Sven Neumann" ; authors
+ "Sven Neumann" ; copyright
+ "1998/02/06"
+ "*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT "Relative radius" '(1 0 128 0.1 1 1 1)
+)
+
+(script-fu-menu-register "script-fu-selection-rounded-rectangle"
+ "<Image>/Select/Modify")
diff --git a/plug-ins/script-fu/scripts/slide.scm b/plug-ins/script-fu/scripts/slide.scm
new file mode 100644
index 0000000..4889d62
--- /dev/null
+++ b/plug-ins/script-fu/scripts/slide.scm
@@ -0,0 +1,261 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; slide.scm version 0.41 2004/03/28
+;
+; CHANGE-LOG:
+; 0.20 - first public release
+; 0.30 - some code cleanup
+; now uses the rotate plug-in to improve speed
+; 0.40 - changes to work with gimp-1.1
+; if the image was rotated, rotate the whole thing back when finished
+; 0.41 - changes to work with gimp-2.0, slightly correct text offsets,
+; Nils Philippsen <nphilipp@redhat.com> 2004/03/28
+;
+; !still in development!
+; TODO: - change the script so that the film is rotated, not the image
+; - antialiasing
+; - make 'add background' an option
+; - ?
+;
+; Copyright (C) 1997-1999 Sven Neumann <sven@gimp.org>
+;
+; makes your picture look like a slide
+;
+; The script works on RGB and grayscale images that contain only
+; one layer. The image is cropped to fit into an aspect ratio of 1:1,5.
+; It creates a copy of the image or can optionally work on the original.
+; The script uses the current background color to create a background
+; layer.
+
+
+(define (script-fu-slide img
+ drawable
+ text
+ number
+ fontname
+ font-color
+ work-on-copy)
+
+ (define (crop width height ratio)
+ (if (>= width (* ratio height))
+ (* ratio height)
+ width
+ )
+ )
+
+ (let* (
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image (cond ((= work-on-copy TRUE)
+ (car (gimp-image-duplicate img)))
+ ((= work-on-copy FALSE)
+ img)))
+ (owidth (car (gimp-image-width image)))
+ (oheight (car (gimp-image-height image)))
+ (ratio (if (>= owidth oheight) (/ 3 2)
+ (/ 2 3)))
+ (crop-width (crop owidth oheight ratio))
+ (crop-height (/ crop-width ratio))
+ (width (* (max crop-width crop-height) 1.05))
+ (height (* (min crop-width crop-height) 1.5))
+ (hole-width (/ width 20))
+ (hole-space (/ width 8))
+ (hole-height (/ width 12))
+ (hole-radius (/ hole-width 4))
+ (hole-start (- (/ (rand 1000) 1000) 0.5))
+ (film-layer (car (gimp-layer-new image
+ width
+ height
+ type
+ "Film"
+ 100
+ LAYER-MODE-NORMAL)))
+ (bg-layer (car (gimp-layer-new image
+ width
+ height
+ type
+ "Background"
+ 100
+ LAYER-MODE-NORMAL)))
+ (pic-layer (car (gimp-image-get-active-drawable image)))
+ (numbera (string-append number "A"))
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+ (gimp-context-set-feather FALSE)
+
+ (if (= work-on-copy TRUE)
+ (gimp-image-undo-disable image)
+ (gimp-image-undo-group-start image)
+ )
+
+; add an alpha channel to the image
+ (gimp-layer-add-alpha pic-layer)
+
+; crop, resize and eventually rotate the image
+ (gimp-image-crop image
+ crop-width
+ crop-height
+ (/ (- owidth crop-width) 2)
+ (/ (- oheight crop-height) 2))
+ (gimp-image-resize image
+ width
+ height
+ (/ (- width crop-width) 2)
+ (/ (- height crop-height) 2))
+ (if (< ratio 1)
+ (plug-in-rotate RUN-NONINTERACTIVE image pic-layer 1 FALSE)
+ )
+
+; add the background layer
+ (gimp-drawable-fill bg-layer FILL-BACKGROUND)
+ (gimp-image-insert-layer image bg-layer 0 -1)
+
+; add the film layer
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-fill film-layer FILL-BACKGROUND)
+ (gimp-image-insert-layer image film-layer 0 -1)
+
+; add the text
+ (gimp-context-set-foreground font-color)
+ (gimp-floating-sel-anchor (car (gimp-text-fontname image
+ film-layer
+ (+ hole-start (* -0.25 width))
+ (* 0.01 height)
+ text
+ 0
+ TRUE
+ (* 0.040 height) PIXELS fontname)))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname image
+ film-layer
+ (+ hole-start (* 0.75 width))
+ (* 0.01 height)
+ text
+ 0
+ TRUE
+ (* 0.040 height) PIXELS
+ fontname )))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname image
+ film-layer
+ (+ hole-start (* 0.35 width))
+ 0.0
+ number
+ 0
+ TRUE
+ (* 0.050 height) PIXELS
+ fontname )))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname image
+ film-layer
+ (+ hole-start (* 0.35 width))
+ (* 0.94 height)
+ number
+ 0
+ TRUE
+ (* 0.050 height) PIXELS
+ fontname )))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname image
+ film-layer
+ (+ hole-start (* 0.85 width))
+ (* 0.945 height)
+ numbera
+ 0
+ TRUE
+ (* 0.045 height) PIXELS
+ fontname )))
+
+; create a mask for the holes and cut them out
+ (let* (
+ (film-mask (car (gimp-layer-create-mask film-layer ADD-MASK-WHITE)))
+ (hole hole-start)
+ (top-y (* height 0.06))
+ (bottom-y (* height 0.855))
+ )
+
+ (gimp-layer-add-mask film-layer film-mask)
+
+ (gimp-selection-none image)
+ (while (< hole 8)
+ (gimp-image-select-rectangle image
+ CHANNEL-OP-ADD
+ (* hole-space hole)
+ top-y
+ hole-width
+ hole-height)
+ (gimp-image-select-rectangle image
+ CHANNEL-OP-ADD
+ (* hole-space hole)
+ bottom-y
+ hole-width
+ hole-height)
+ (set! hole (+ hole 1))
+ )
+
+ (gimp-context-set-foreground '(0 0 0))
+ (gimp-drawable-edit-fill film-mask FILL-BACKGROUND)
+ (gimp-selection-none image)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE image film-mask hole-radius TRUE TRUE)
+ (gimp-threshold film-mask 127 255)
+
+ (gimp-layer-remove-mask film-layer MASK-APPLY)
+ )
+
+; reorder the layers
+ (gimp-image-raise-item image pic-layer)
+ (gimp-image-raise-item image pic-layer)
+
+; eventually rotate the whole thing back
+ (if (< ratio 1)
+ (plug-in-rotate RUN-NONINTERACTIVE image pic-layer 3 TRUE)
+ )
+
+; clean up after the script
+ (gimp-selection-none image)
+
+ (if (= work-on-copy TRUE)
+ (begin
+ (gimp-display-new image)
+ (gimp-image-undo-enable image)
+ )
+ (gimp-image-undo-group-end image)
+ )
+
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-slide"
+ _"_Slide..."
+ _"Add a slide-film like frame, sprocket holes, and labels to an image"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "2004/03/28"
+ "RGB GRAY"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-STRING _"Text" "GIMP"
+ SF-STRING _"Number" "32"
+ SF-FONT _"Font" "Serif"
+ SF-COLOR _"Font color" '(255 180 0)
+ SF-TOGGLE _"Work on copy" TRUE
+)
+
+(script-fu-menu-register "script-fu-slide"
+ "<Image>/Filters/Decor")
diff --git a/plug-ins/script-fu/scripts/spinning-globe.scm b/plug-ins/script-fu/scripts/spinning-globe.scm
new file mode 100644
index 0000000..1549830
--- /dev/null
+++ b/plug-ins/script-fu/scripts/spinning-globe.scm
@@ -0,0 +1,110 @@
+;
+; anim_sphere
+;
+;
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+; Define the function:
+
+(define (script-fu-spinning-globe inImage
+ inLayer
+ inFrames
+ inFromLeft
+ inTransparent
+ inIndex
+ inCopy)
+ (let* (
+ (theImage (if (= inCopy TRUE)
+ (car (gimp-image-duplicate inImage))
+ inImage))
+ (theLayer (car (gimp-image-get-active-layer theImage)))
+ (n 0)
+ (ang (* (/ 360 inFrames)
+ (if (= inFromLeft TRUE) 1 -1) ))
+ (theFrame 0)
+ )
+
+ (gimp-layer-add-alpha theLayer)
+
+ (while (> inFrames n)
+ (set! n (+ n 1))
+ (set! theFrame (car (gimp-layer-copy theLayer FALSE)))
+ (gimp-image-insert-layer theImage theFrame 0 0)
+ (gimp-item-set-name theFrame
+ (string-append "Anim Frame: "
+ (number->string (- inFrames n) 10)
+ " (replace)"))
+ (plug-in-map-object RUN-NONINTERACTIVE
+ theImage theFrame ; mapping
+ 1 ; viewpoint
+ 0.5 0.5 2.0 ; object pos
+ 0.5 0.5 0.0 ; first axis
+ 1.0 0.0 0.0 ; 2nd axis
+ 0.0 1.0 0.0 ; axis rotation
+ 0.0 (* n ang) 0.0 ; light (type, color)
+ 0 '(255 255 255) ; light position
+ -0.5 -0.5 2.0 ; light direction
+ -1.0 -1.0 1.0 ; material (amb, diff, refl, spec, high)
+ 0.3 1.0 0.5 0.0 27.0 ; antialias
+ TRUE ; tile
+ FALSE ; new image
+ FALSE ; transparency
+ inTransparent ; radius
+ 0.25 ; unused parameters
+ 1.0 1.0 1.0 1.0
+ -1 -1 -1 -1 -1 -1 -1 -1
+ )
+ )
+
+ (gimp-image-remove-layer theImage theLayer)
+ (plug-in-autocrop RUN-NONINTERACTIVE theImage theFrame)
+
+ (if (= inIndex 0)
+ ()
+ (gimp-image-convert-indexed theImage CONVERT-DITHER-FS CONVERT-PALETTE-GENERATE inIndex
+ FALSE FALSE ""))
+
+ (if (= inCopy TRUE)
+ (begin
+ (gimp-image-clean-all theImage)
+ (gimp-display-new theImage)
+ )
+ )
+
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register
+ "script-fu-spinning-globe"
+ _"_Spinning Globe..."
+ _"Create an animation by mapping the current image onto a spinning sphere"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "16th April 1998"
+ "RGB* GRAY*"
+ SF-IMAGE "The Image" 0
+ SF-DRAWABLE "The Layer" 0
+ SF-ADJUSTMENT _"Frames" '(10 1 360 1 10 0 1)
+ SF-TOGGLE _"Turn from left to right" FALSE
+ SF-TOGGLE _"Transparent background" TRUE
+ SF-ADJUSTMENT _"Index to n colors (0 = remain RGB)" '(63 0 256 1 10 0 1)
+ SF-TOGGLE _"Work on copy" TRUE
+)
+
+(script-fu-menu-register "script-fu-spinning-globe"
+ "<Image>/Filters/Animation/Animators")
diff --git a/plug-ins/script-fu/scripts/spyrogimp.scm b/plug-ins/script-fu/scripts/spyrogimp.scm
new file mode 100644
index 0000000..0a1e40c
--- /dev/null
+++ b/plug-ins/script-fu/scripts/spyrogimp.scm
@@ -0,0 +1,352 @@
+; spyrogimp.scm -*-scheme-*-
+; Draws Spirographs, Epitrochoids and Lissajous Curves.
+; More info at http://www.wisdom.weizmann.ac.il/~elad/spyrogimp/
+; Version 1.2
+;
+; Copyright (C) 2003 by Elad Shahar <elad@wisdom.weizmann.ac.il>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+; This routine is invoked by a dialog.
+; It is the main routine in this file.
+(define (script-fu-spyrogimp img drw
+ type shape
+ oteeth iteeth
+ margin hole-ratio start-angle
+ tool brush
+ color-method color grad)
+
+ ; Internal function to draw the spyro.
+ (define (script-fu-spyrogimp-internal img drw
+ x1 y1 x2 y2 ; Bounding box.
+ type ; = 0 (Spirograph), 1 (Epitrochoid), 2(Lissajous) .
+ shape ; = 0 (Circle), 1 (Frame), >2 (Polygons) .
+ oteeth iteeth ; Outer and inner teeth.
+ margin hole-ratio
+ start-angle ; 0 <= start-angle < 360 .
+ tool ; = 0 (Pencil), 1 (Brush), 2 (Airbrush) .
+ brush
+ color-method ; = 0 (Single color), 1 (Grad. Loop Sawtooth),
+ ; 2 (Grad. Loop triangle) .
+ color ; Used when color-method = Single color .
+ grad ; Gradient used in Gradient color methods.
+ )
+
+
+ ; This function returns a list of samples according to the gradient.
+ (define (get-gradient steps color-method grad)
+ (if (= color-method 1)
+ ; option 1
+ ; Just return the gradient
+ (gimp-gradient-get-uniform-samples grad (min steps 50) FALSE)
+
+ ; option 2
+ ; The returned list is such that the gradient appears two times, once
+ ; in the normal order and once in reverse. This way there are no color
+ ; jumps if we go beyond the edge
+ (let* (
+ ; Sample the gradient into array "gr".
+ (gr (gimp-gradient-get-uniform-samples grad
+ (/ (min steps 50) 2)
+ FALSE))
+
+ (grn (car gr)) ; length of sample array.
+ (gra (cadr gr)) ; array of color samples (R1,G1,B1,A1, R2,....)
+
+ ; Allocate array gra-new of size (2 * grn) - 8,
+ ; but since each 4 items is actually one (RGBA) tuple,
+ ; it contains 2x - 2 entries.
+ (grn-new (+ grn grn -8))
+ (gra-new (cons-array grn-new 'double))
+
+ (gr-index 0)
+ (gr-index2 0)
+ )
+
+ ; Copy original array gra to gra_new.
+ (while (< gr-index grn)
+ (aset gra-new gr-index (aref gra gr-index))
+ (set! gr-index (+ 1 gr-index))
+ )
+
+ ; Copy second time, but in reverse
+ (set! gr-index2 (- gr-index 8))
+ (while (< gr-index grn-new)
+ (aset gra-new gr-index (aref gra gr-index2))
+ (set! gr-index (+ 1 gr-index))
+ (set! gr-index2 (+ 1 gr-index2))
+
+ (if (= (fmod gr-index 4) 0)
+ (set! gr-index2 (- gr-index2 8))
+ )
+ )
+
+ ; Return list.
+ (list grn-new gra-new)
+ )
+ )
+ )
+
+
+ (let* (
+ (steps (+ 1 (lcm oteeth iteeth)))
+ (*points* (cons-array (* steps 2) 'double))
+
+ (ot 0) ; current outer tooth
+ (cx 0) ; Current x,y
+ (cy 0)
+
+ ; If its a polygon or frame, how many sides does it have.
+ (poly (if (= shape 1) 4 ; A frame has four sides.
+ (if (> shape 1) (+ shape 1) 0)))
+
+ (2pi (* 2 *pi*))
+
+ (drw-width (- x2 x1))
+ (drw-height (- y2 y1))
+ (half-width (/ drw-width 2))
+ (half-height (/ drw-height 2))
+ (midx (+ x1 half-width))
+ (midy (+ y1 half-height))
+
+ (hole (* hole-ratio
+ (- (/ (min drw-width drw-height) 2) margin)
+ )
+ )
+ (irad (+ hole margin))
+
+ (radx (- half-width irad)) ;
+ (rady (- half-height irad)) ;
+
+ (gradt (get-gradient steps color-method grad))
+ (grada (cadr gradt)) ; Gradient array.
+ (gradn (car gradt)) ; Number of entries of gradients.
+
+ ; Indexes
+ (grad-index 0) ; for array: grada
+ (point-index 0) ; for array: *points*
+ (index 0)
+ )
+
+ ; Do one step of the loop.
+ (define (calc-and-step!)
+ (let* (
+ (oangle (* 2pi (/ ot oteeth)) )
+ (shifted-oangle (+ oangle (* 2pi (/ start-angle 360))) )
+ (xfactor (cos shifted-oangle))
+ (yfactor (sin shifted-oangle))
+ (lenfactor 1)
+ (ofactor (/ (+ oteeth iteeth) iteeth))
+
+ ; The direction of the factor changes according
+ ; to whether the type is a sypro or an epitcorhoid.
+ (mfactor (if (= type 0) (- ofactor) ofactor))
+ )
+
+ ; If we are drawing a polygon then compute a contortion
+ ; factor "lenfactor" which deforms the standard circle.
+ (if (> poly 2)
+ (let* (
+ (pi4 (/ *pi* poly))
+ (pi2 (* pi4 2))
+
+ (oanglemodpi2 (fmod (+ oangle
+ (if (= 1 (fmod poly 2))
+ 0 ;(/ pi4 2)
+ 0
+ )
+ )
+ pi2))
+ )
+
+ (set! lenfactor (/ ( if (= shape 1) 1 (cos pi4) )
+ (cos
+ (if (< oanglemodpi2 pi4)
+ oanglemodpi2
+ (- pi2 oanglemodpi2)
+ )
+ )
+ )
+ )
+ )
+ )
+
+ (if (= type 2)
+ (begin ; Lissajous
+ (set! cx (+ midx
+ (* half-width (cos shifted-oangle)) ))
+ (set! cy (+ midy
+ (* half-height (cos (* mfactor oangle))) ))
+ )
+ (begin ; Spyrograph or Epitrochoid
+ (set! cx (+ midx
+ (* radx xfactor lenfactor)
+ (* hole (cos (* mfactor oangle) ) ) ))
+ (set! cy (+ midy
+ (* rady yfactor lenfactor)
+ (* hole (sin (* mfactor oangle) ) ) ))
+ )
+ )
+
+ ;; Advance teeth
+ (set! ot (+ ot 1))
+ )
+ )
+
+
+ ;; Draw all the points in *points* with appropriate tool.
+ (define (flush-points len)
+ (if (= tool 0)
+ (gimp-pencil drw len *points*) ; Use pencil
+ (if (= tool 1)
+ (gimp-paintbrush-default drw len *points*); use paintbrush
+ (gimp-airbrush-default drw len *points*) ; use airbrush
+ )
+ )
+
+ ; Reset points array, but copy last point to first
+ ; position so it will connect the next time.
+ (aset *points* 0 (aref *points* (- point-index 2)))
+ (aset *points* 1 (aref *points* (- point-index 1)))
+ (set! point-index 2)
+ )
+
+ ;;
+ ;; Execution starts here.
+ ;;
+
+ (gimp-context-push)
+
+ (gimp-image-undo-group-start img)
+
+ ; Set new color, brush, opacity, paint mode.
+ (gimp-context-set-foreground color)
+ (gimp-context-set-brush (car brush))
+ (gimp-context-set-opacity (car (cdr brush)))
+ (gimp-context-set-paint-mode (car (cdr (cdr (cdr brush)))))
+
+ (gimp-progress-set-text _"Rendering Spyro")
+
+ (while (< index steps)
+
+ (calc-and-step!)
+
+ (aset *points* point-index cx)
+ (aset *points* (+ point-index 1) cy)
+ (set! point-index (+ point-index 2))
+
+ ; Change color and draw points if using gradient.
+ (if (< 0 color-method) ; use gradient.
+ (if (< (/ (+ grad-index 4) gradn) (/ index steps))
+ (begin
+ (gimp-context-set-foreground
+ (list
+ (* 255 (aref grada grad-index))
+ (* 255 (aref grada (+ 1 grad-index)) )
+ (* 255 (aref grada (+ 2 grad-index)) )
+ )
+ )
+ (gimp-context-set-opacity (* 100 (aref grada (+ 3 grad-index) ) ) )
+ (set! grad-index (+ 4 grad-index))
+
+ ; Draw points
+ (flush-points point-index)
+ )
+ )
+ )
+
+ (set! index (+ index 1))
+
+ (if (= 0 (modulo index 16))
+ (gimp-progress-update (/ index steps))
+ )
+ )
+
+ ; Draw remaining points.
+ (flush-points point-index)
+
+ (gimp-progress-update 1.0)
+
+ (gimp-image-undo-group-end img)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+ )
+
+ (let* (
+ ; Get current selection to determine where to draw.
+ (bounds (cdr (gimp-selection-bounds img)))
+ (x1 (car bounds))
+ (y1 (cadr bounds))
+ (x2 (caddr bounds))
+ (y2 (car (cdddr bounds)))
+ )
+
+ (set! oteeth (trunc (+ oteeth 0.5)))
+ (set! iteeth (trunc (+ iteeth 0.5)))
+
+ (script-fu-spyrogimp-internal img drw
+ x1 y1 x2 y2
+ type shape
+ oteeth iteeth
+ margin hole-ratio start-angle
+ tool brush
+ color-method color grad)
+ )
+)
+
+
+
+(script-fu-register "script-fu-spyrogimp"
+ _"_Spyrogimp (older script-fu version)..."
+ _"This procedure is deprecated! Use 'plug-in-spyrogimp' instead."
+ "Elad Shahar <elad@wisdom.weizmann.ac.il>"
+ "Elad Shahar"
+ "June 2003"
+ "RGB*, INDEXED*, GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+
+ SF-OPTION _"Type" '(_"Spyrograph"
+ _"Epitrochoid"
+ _"Lissajous")
+ SF-OPTION _"Shape" '(_"Circle"
+ _"Frame"
+ _"Triangle"
+ _"Square"
+ _"Pentagon"
+ _"Hexagon"
+ _"Polygon: 7 sides"
+ _"Polygon: 8 sides"
+ _"Polygon: 9 sides"
+ _"Polygon: 10 sides")
+ SF-ADJUSTMENT _"Outer teeth" '(86 1 120 1 10 0 0)
+ SF-ADJUSTMENT _"Inner teeth" '(70 1 120 1 10 0 0)
+ SF-ADJUSTMENT _"Margin (pixels)" '(0 -10000 10000 1 10 0 1)
+ SF-ADJUSTMENT _"Hole ratio" '(0.4 0.0 1.0 0.01 0.1 2 0)
+ SF-ADJUSTMENT _"Start angle" '(0 0 359 1 10 0 0)
+
+ SF-OPTION _"Tool" '(_"Pencil"
+ _"Brush"
+ _"Airbrush")
+ SF-BRUSH _"Brush" '("Circle (01)" 100 -1 0)
+
+ SF-OPTION _"Color method" '(_"Solid Color"
+ _"Gradient: Loop Sawtooth"
+ _"Gradient: Loop Triangle")
+ SF-COLOR _"Color" "black"
+ SF-GRADIENT _"Gradient" "Deep Sea"
+)
diff --git a/plug-ins/script-fu/scripts/test-sphere.scm b/plug-ins/script-fu/scripts/test-sphere.scm
new file mode 100644
index 0000000..763e9b0
--- /dev/null
+++ b/plug-ins/script-fu/scripts/test-sphere.scm
@@ -0,0 +1,307 @@
+; This is a a test script to show and test the possibilities of the
+; Script-Fu parameter API.
+;
+; ----------------------------------------------------------------------
+; SF-ADJUSTMENT
+; is only useful in interactive mode, if you call a script from
+; the console, it acts just like a normal SF-VALUE
+; In interactive mode it creates an adjustment widget in the dialog.
+;
+; Usage:
+; SF-ADJUSTMENT "label" '(value lower upper step_inc page_inc digits type)
+;
+; type is one of: SF-SLIDER(0), SF-SPINNER(1)
+;
+; ----------------------------------------------------------------------
+; SF-COLOR
+; creates a color button in the dialog. It accepts either a list of three
+; values for the red, green and blue components or a color name in CSS
+; notatation
+;
+; Usage:
+; SF-COLOR "label" '(red green blue)
+; SF-COLOR "label" "color"
+;
+; ----------------------------------------------------------------------
+; SF-FONT
+; creates a font-selection widget in the dialog. It returns a fontname as
+; a string. There are two new gimp-text procedures to ease the use of this
+; return parameter:
+;
+; (gimp-text-fontname image drawable
+; x-pos y-pos text border antialias size unit font)
+; (gimp-text-get-extents-fontname text size unit font))
+;
+; where font is the fontname you get. The size specified in the fontname
+; is silently ignored. It is only used in the font-selector. So you are
+; asked to set it to a useful value (24 pixels is a good choice) when
+; using SF-FONT.
+;
+; Usage:
+; SF-FONT "label" "fontname"
+;
+; ----------------------------------------------------------------------
+; SF-BRUSH
+; is only useful in interactive mode. It will create a widget in the control
+; dialog. The widget consists of a preview area (which when pressed will
+; produce a popup preview ) and a button with the "..." label. The button will
+; popup a dialog where brushes can be selected and each of the
+; characteristics of the brush can be modified.
+;
+; The actual value returned when the script is invoked is a list
+; consisting of Brush name, opacity, spacing and brush mode in the same
+; units as passed in as the default value.
+;
+; Usage:
+; SF-BRUSH "Brush" '("Circle (03)" 100 44 0)
+;
+; Here the brush dialog will be popped up with a default brush of Circle (03)
+; opacity 100 spacing 44 and paint mode of Normal (value 0).
+; If this selection was unchanged the value passed to the function as a
+; parameter would be '("Circle (03)" 100 44 0).
+;
+; ----------------------------------------------------------------------
+; SF-PATTERN
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget consists of a preview area (which when pressed will
+; produce a popup preview ) and a button with the "..." label. The button will
+; popup a dialog where patterns can be selected.
+;
+; Usage:
+; SF-PATTERN "Pattern" "Maple Leaves"
+;
+; The value returned when the script is invoked is a string containing the
+; pattern name. If the above selection was not altered the string would
+; contain "Maple Leaves"
+;
+; ----------------------------------------------------------------------
+; SF-GRADIENT
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget consists of a button containing a preview of the selected
+; gradient. If the button is pressed a gradient selection dialog will popup.
+;
+; Usage:
+; SF-GRADIENT "Gradient" "Deep Sea"
+;
+; The value returned when the script is invoked is a string containing the
+; gradient name. If the above selection was not altered the string would
+; contain "Deep Sea"
+;
+; ----------------------------------------------------------------------
+; SF-PALETTE
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget consists of a button containing a preview of the selected
+; palette. If the button is pressed a palette selection dialog will popup.
+;
+; Usage:
+; SF-PALETTE "Palette" "Named Colors"
+;
+; The value returned when the script is invoked is a string containing the
+; palette name. If the above selection was not altered the string would
+; contain "Named Colors"
+;
+; ----------------------------------------------------------------------
+; SF-FILENAME
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget consists of a button containing the name of a file.
+; If the button is pressed a file selection dialog will popup.
+;
+; Usage:
+; SF-FILENAME "Environment Map"
+; (string-append "" gimp-data-directory "/scripts/beavis.jpg")
+;
+; The value returned when the script is invoked is a string containing the
+; filename.
+;
+; ----------------------------------------------------------------------
+; SF-DIRNAME
+; Only useful in interactive mode. Very similar to SF-FILENAME, but the
+; created widget allows to choose a directory instead of a file.
+;
+; Usage:
+; SF-DIRNAME "Image Directory" "/var/tmp/images"
+;
+; The value returned when the script is invoked is a string containing the
+; dirname.
+;
+; ----------------------------------------------------------------------
+; SF-OPTION
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget is a combo-box showing the options that are passed
+; as a list. The first option is the default choice.
+;
+; Usage:
+; SF-OPTION "Orientation" '("Horizontal" "Vertical")
+;
+; The value returned when the script is invoked is the number of the
+; chosen option, where the option first is counted as 0.
+;
+; ----------------------------------------------------------------------
+; SF-ENUM
+; Only useful in interactive mode. It will create a widget in the control
+; dialog. The widget is a combo-box showing all enum values for the given
+; enum type. This has to be the name of a registered enum, without the
+; "Gimp" prefix. The second parameter specifies the default value, using
+; the enum value's nick.
+;
+; Usage:
+; SF-ENUM "Interpolation" '("InterpolationType" "linear")
+;
+; The value returned when the script is invoked corresponds to chosen
+; enum value.
+;
+; ----------------------------------------------------------------------
+
+
+(define (script-fu-test-sphere radius
+ light
+ shadow
+ bg-color
+ sphere-color
+ brush
+ text
+ multi-text
+ pattern
+ gradient
+ gradient-reverse
+ font
+ size
+ unused-palette
+ unused-filename
+ unused-orientation
+ unused-interpolation
+ unused-dirname
+ unused-image
+ unused-layer
+ unused-channel
+ unused-drawable)
+ (let* (
+ (width (* radius 3.75))
+ (height (* radius 2.5))
+ (img (car (gimp-image-new width height RGB)))
+ (drawable (car (gimp-layer-new img width height RGB-IMAGE
+ "Sphere Layer" 100 LAYER-MODE-NORMAL)))
+ (radians (/ (* light *pi*) 180))
+ (cx (/ width 2))
+ (cy (/ height 2))
+ (light-x (+ cx (* radius (* 0.6 (cos radians)))))
+ (light-y (- cy (* radius (* 0.6 (sin radians)))))
+ (light-end-x (+ cx (* radius (cos (+ *pi* radians)))))
+ (light-end-y (- cy (* radius (sin (+ *pi* radians)))))
+ (offset (* radius 0.1))
+ (text-extents (gimp-text-get-extents-fontname multi-text
+ size PIXELS
+ font))
+ (x-position (- cx (/ (car text-extents) 2)))
+ (y-position (- cy (/ (cadr text-extents) 2)))
+ (shadow-w 0)
+ (shadow-x 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+ (gimp-context-set-foreground sphere-color)
+ (gimp-context-set-background bg-color)
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+ (gimp-context-set-background '(20 20 20))
+
+ (if (and
+ (or (and (>= light 45) (<= light 75))
+ (and (<= light 135) (>= light 105)))
+ (= shadow TRUE))
+ (let ((shadow-w (* (* radius 2.5) (cos (+ *pi* radians))))
+ (shadow-h (* radius 0.5))
+ (shadow-x cx)
+ (shadow-y (+ cy (* radius 0.65))))
+ (if (< shadow-w 0)
+ (begin (set! shadow-x (+ cx shadow-w))
+ (set! shadow-w (- shadow-w))))
+
+ (gimp-context-set-feather TRUE)
+ (gimp-context-set-feather-radius 7.5 7.5)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE shadow-x shadow-y shadow-w shadow-h)
+ (gimp-context-set-pattern pattern)
+ (gimp-drawable-edit-fill drawable FILL-PATTERN)))
+
+ (gimp-context-set-feather FALSE)
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE (- cx radius) (- cy radius)
+ (* 2 radius) (* 2 radius))
+
+ (gimp-context-set-gradient-fg-bg-rgb)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-RADIAL offset
+ FALSE 0 0
+ TRUE
+ light-x light-y
+ light-end-x light-end-y)
+
+ (gimp-selection-none img)
+
+ (gimp-image-select-ellipse img CHANNEL-OP-REPLACE 10 10 50 50)
+
+ (gimp-context-set-gradient gradient)
+ (gimp-context-set-gradient-reverse gradient-reverse)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-LINEAR offset
+ FALSE 0 0
+ TRUE
+ 10 10
+ 30 60)
+
+ (gimp-selection-none img)
+
+ (gimp-context-set-foreground '(0 0 0))
+ (gimp-floating-sel-anchor (car (gimp-text-fontname img drawable
+ x-position y-position
+ multi-text
+ 0 TRUE
+ size PIXELS
+ font)))
+
+ (gimp-image-undo-enable img)
+ (gimp-display-new img)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-test-sphere"
+ _"_Sphere..."
+ "Simple script to test and show the usage of the new Script-Fu API extensions."
+ "Spencer Kimball, Sven Neumann"
+ "Spencer Kimball"
+ "1996, 1998"
+ ""
+ SF-ADJUSTMENT "Radius (in pixels)" (list 100 1 5000 1 10 0 SF-SPINNER)
+ SF-ADJUSTMENT "Lighting (degrees)" (list 45 0 360 1 10 1 SF-SLIDER)
+ SF-TOGGLE "Shadow" TRUE
+ SF-COLOR "Background color" "white"
+ SF-COLOR "Sphere color" "red"
+ SF-BRUSH "Brush" '("2. Hardness 100" 100 44 0)
+ SF-STRING "Text" "Tiny-Fu rocks!"
+ SF-TEXT "Multi-line text" "Hello,\nWorld!"
+ SF-PATTERN "Pattern" "Maple Leaves"
+ SF-GRADIENT "Gradient" "Deep Sea"
+ SF-TOGGLE "Gradient reverse" FALSE
+ SF-FONT "Font" "Agate"
+ SF-ADJUSTMENT "Font size (pixels)" '(50 1 1000 1 10 0 1)
+ SF-PALETTE "Palette" "Default"
+ SF-FILENAME "Environment map"
+ (string-append gimp-data-directory
+ "/scripts/images/beavis.jpg")
+ SF-OPTION "Orientation" '("Horizontal"
+ "Vertical")
+ SF-ENUM "Interpolation" '("InterpolationType" "linear")
+ SF-DIRNAME "Output directory" "/var/tmp/"
+ SF-IMAGE "Image" -1
+ SF-LAYER "Layer" -1
+ SF-CHANNEL "Channel" -1
+ SF-DRAWABLE "Drawable" -1
+ SF-VECTORS "Vectors" -1
+)
+
+(script-fu-menu-register "script-fu-test-sphere"
+ "<Image>/Filters/Languages/Script-Fu/Test")
diff --git a/plug-ins/script-fu/scripts/tileblur.scm b/plug-ins/script-fu/scripts/tileblur.scm
new file mode 100644
index 0000000..4be7a6c
--- /dev/null
+++ b/plug-ins/script-fu/scripts/tileblur.scm
@@ -0,0 +1,83 @@
+; Chris Gutteridge (cjg@ecs.soton.ac.uk)
+; At ECS Dept, University of Southampton, England.
+
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-tile-blur inImage inLayer inRadius inVert inHoriz inType)
+
+ (let* (
+ (theImage inImage)
+ (theLayer inLayer)
+ (theHeight (car (gimp-drawable-height theLayer)))
+ (theWidth (car (gimp-drawable-width theLayer)))
+ )
+
+ (define (pasteat xoff yoff)
+ (let ((theFloat (car(gimp-edit-paste theLayer 0))))
+ (gimp-layer-set-offsets theFloat (* xoff theWidth) (* yoff theHeight) )
+ (gimp-floating-sel-anchor theFloat)
+ )
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-feather FALSE)
+ (gimp-image-undo-group-start theImage)
+
+ (gimp-layer-resize theLayer (* 3 theWidth) (* 3 theHeight) 0 0)
+
+ (gimp-image-select-rectangle theImage CHANNEL-OP-REPLACE 0 0 theWidth theHeight)
+ (gimp-edit-cut theLayer)
+
+ (gimp-selection-none theImage)
+ (gimp-layer-set-offsets theLayer theWidth theHeight)
+
+ (pasteat 1 1) (pasteat 1 2) (pasteat 1 3)
+ (pasteat 2 1) (pasteat 2 2) (pasteat 2 3)
+ (pasteat 3 1) (pasteat 3 2) (pasteat 3 3)
+
+ (gimp-selection-none theImage)
+ (if (= inType 0)
+ (plug-in-gauss-iir RUN-NONINTERACTIVE
+ theImage theLayer inRadius inHoriz inVert)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE
+ theImage theLayer inRadius inHoriz inVert)
+ )
+
+ (gimp-layer-resize theLayer
+ theWidth theHeight (- 0 theWidth) (- 0 theHeight))
+ (gimp-layer-set-offsets theLayer 0 0)
+ (gimp-image-undo-group-end theImage)
+ (gimp-displays-flush)
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-tile-blur"
+ _"_Tileable Blur..."
+ _"Blur the edges of an image so the result tiles seamlessly"
+ "Chris Gutteridge"
+ "1998, Chris Gutteridge / ECS dept, University of Southampton, England."
+ "25th April 1998"
+ "RGB*"
+ SF-IMAGE "The Image" 0
+ SF-DRAWABLE "The Layer" 0
+ SF-ADJUSTMENT _"Radius" '(5 0 128 1 1 0 0)
+ SF-TOGGLE _"Blur vertically" TRUE
+ SF-TOGGLE _"Blur horizontally" TRUE
+ SF-OPTION _"Blur type" '(_"IIR" _"RLE")
+)
+
+(script-fu-menu-register "script-fu-tile-blur"
+ "<Image>/Filters/Blur")
diff --git a/plug-ins/script-fu/scripts/ts-helloworld.scm b/plug-ins/script-fu/scripts/ts-helloworld.scm
new file mode 100644
index 0000000..2c04105
--- /dev/null
+++ b/plug-ins/script-fu/scripts/ts-helloworld.scm
@@ -0,0 +1,65 @@
+; "Hello, World" Test v1.00 February 29, 2004
+; by Kevin Cozens <kcozens@interlog.com>
+;
+; Creates an image with the text "Hello, World!"
+; This was the first TinyScheme based script ever created and run for the
+; 2.x version of GIMP.
+
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+; Tiny-Fu first successfully ran this script at 2:07am on March 6, 2004.
+
+(define (script-fu-helloworld text font size color)
+ (let* (
+ (width 10)
+ (height 10)
+ (img (car (gimp-image-new width height RGB)))
+ (text-layer)
+ )
+
+ (gimp-context-push)
+
+ (gimp-image-undo-disable img)
+ (gimp-context-set-foreground color)
+
+ (set! text-layer (car (gimp-text-fontname img -1 0 0 text 10 TRUE size PIXELS font)))
+ (set! width (car (gimp-drawable-width text-layer)))
+ (set! height (car (gimp-drawable-height text-layer)))
+ (gimp-image-resize img width height 0 0)
+
+ (gimp-image-undo-enable img)
+ (gimp-display-new img)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-helloworld"
+ "_Hello World..."
+ "Creates an image with a user specified text string."
+ "Kevin Cozens <kcozens@interlog.com>"
+ "Kevin Cozens"
+ "February 29, 2004"
+ ""
+ SF-STRING "Text string" "Hello, World!"
+ SF-FONT "Font" "Sans"
+ SF-ADJUSTMENT "Font size (pixels)" '(100 2 1000 1 10 0 1)
+ SF-COLOR "Color" '(0 0 0)
+)
+
+(script-fu-menu-register "script-fu-helloworld"
+ "<Image>/Filters/Languages/Script-Fu/Test")
diff --git a/plug-ins/script-fu/scripts/unsharp-mask.scm b/plug-ins/script-fu/scripts/unsharp-mask.scm
new file mode 100644
index 0000000..77daf82
--- /dev/null
+++ b/plug-ins/script-fu/scripts/unsharp-mask.scm
@@ -0,0 +1,84 @@
+;;; unsharp-mask.scm
+;;; Time-stamp: <1998/11/17 13:18:39 narazaki@gimp.org>
+;;; Author: Narazaki Shuji <narazaki@gimp.org>
+;;; Version 0.8
+
+(define (script-fu-unsharp-mask img drw mask-size mask-opacity)
+ (let* (
+ (drawable-width (car (gimp-drawable-width drw)))
+ (drawable-height (car (gimp-drawable-height drw)))
+ (new-image (car (gimp-image-new drawable-width drawable-height RGB)))
+ (original-layer (car (gimp-layer-new new-image
+ drawable-width drawable-height
+ RGB-IMAGE "Original"
+ 100 LAYER-MODE-NORMAL)))
+ (original-layer-for-darker 0)
+ (original-layer-for-lighter 0)
+ (blurred-layer-for-darker 0)
+ (blurred-layer-for-lighter 0)
+ (darker-layer 0)
+ (lighter-layer 0)
+ )
+
+ (gimp-selection-all img)
+ (gimp-edit-copy drw)
+
+ (gimp-image-undo-disable new-image)
+
+ (gimp-image-insert-layer new-image original-layer 0 0)
+ (gimp-floating-sel-anchor
+ (car (gimp-edit-paste original-layer FALSE)))
+
+ (set! original-layer-for-darker (car (gimp-layer-copy original-layer TRUE)))
+ (set! original-layer-for-lighter (car (gimp-layer-copy original-layer TRUE)))
+ (set! blurred-layer-for-darker (car (gimp-layer-copy original-layer TRUE)))
+ (gimp-item-set-visible original-layer FALSE)
+ (gimp-display-new new-image)
+
+ ;; make darker mask
+ (gimp-image-insert-layer new-image blurred-layer-for-darker 0 -1)
+ (plug-in-gauss-iir RUN-NONINTERACTIVE
+ new-image blurred-layer-for-darker mask-size TRUE TRUE)
+ (set! blurred-layer-for-lighter
+ (car (gimp-layer-copy blurred-layer-for-darker TRUE)))
+ (gimp-image-insert-layer new-image original-layer-for-darker 0 -1)
+ (gimp-layer-set-mode original-layer-for-darker LAYER-MODE-SUBTRACT)
+ (set! darker-layer
+ (car (gimp-image-merge-visible-layers new-image CLIP-TO-IMAGE)))
+ (gimp-item-set-name darker-layer "darker mask")
+ (gimp-item-set-visible darker-layer FALSE)
+
+ ;; make lighter mask
+ (gimp-image-insert-layer new-image original-layer-for-lighter 0 -1)
+ (gimp-image-insert-layer new-image blurred-layer-for-lighter 0 -1)
+ (gimp-layer-set-mode blurred-layer-for-lighter LAYER-MODE-SUBTRACT)
+ (set! lighter-layer
+ (car (gimp-image-merge-visible-layers new-image CLIP-TO-IMAGE)))
+ (gimp-item-set-name lighter-layer "lighter mask")
+
+ ;; combine them
+ (gimp-item-set-visible original-layer TRUE)
+ (gimp-layer-set-mode darker-layer LAYER-MODE-SUBTRACT)
+ (gimp-layer-set-opacity darker-layer mask-opacity)
+ (gimp-item-set-visible darker-layer TRUE)
+ (gimp-layer-set-mode lighter-layer LAYER-MODE-ADDITION)
+ (gimp-layer-set-opacity lighter-layer mask-opacity)
+ (gimp-item-set-visible lighter-layer TRUE)
+
+ (gimp-image-undo-enable new-image)
+ (gimp-displays-flush)
+ )
+)
+
+(script-fu-register "script-fu-unsharp-mask"
+ "Unsharp Mask..."
+ "Make a new image from the current layer by applying the unsharp mask method"
+ "Shuji Narazaki <narazaki@gimp.org>"
+ "Shuji Narazaki"
+ "1997,1998"
+ ""
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable to apply" 0
+ SF-ADJUSTMENT _"Mask size" '(5 1 100 1 1 0 1)
+ SF-ADJUSTMENT _"Mask opacity" '(50 0 100 1 1 0 1)
+)
diff --git a/plug-ins/script-fu/scripts/waves-anim.scm b/plug-ins/script-fu/scripts/waves-anim.scm
new file mode 100644
index 0000000..4e43d98
--- /dev/null
+++ b/plug-ins/script-fu/scripts/waves-anim.scm
@@ -0,0 +1,110 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+;
+;
+; waves-anim.scm version 1.01 1997/12/13
+;
+; CHANGE-LOG:
+; 1.00 - initial release
+; 1.01 - some code cleanup, no real changes
+;
+; Copyright (C) 1997 Sven Neumann <sven@gimp.org>
+;
+;
+; Makes a copy of your image and creates an animation of the active layer
+; as if a stone was thrown into the image. The animation may be saved with
+; the gif-plug-in.
+
+(define (script-fu-waves-anim img
+ drawable
+ amplitude
+ wavelength
+ num-frames
+ invert)
+ (let* ((amplitude (max 0 amplitude))
+ (wavelength (max 0 wavelength))
+ (num-frames (max 1 num-frames))
+ (remaining-frames num-frames)
+ (phase 0)
+ (phaseshift (/ 360 num-frames))
+ (image (car (gimp-image-duplicate img)))
+ (source-layer (car (gimp-image-get-active-layer image))))
+
+ (gimp-image-undo-disable image)
+
+ (if (= invert TRUE)
+ (set! phaseshift (- 0 phaseshift)))
+
+ (while (> remaining-frames 1)
+ (let* (
+ (waves-layer (car (gimp-layer-copy source-layer TRUE)))
+ (layer-name (string-append "Frame "
+ (number->string
+ (- (+ num-frames 2)
+ remaining-frames) 10
+ )
+ " (replace)"))
+ )
+ (gimp-layer-set-lock-alpha waves-layer FALSE)
+ (gimp-image-insert-layer image waves-layer 0 -1)
+ (gimp-item-set-name waves-layer layer-name)
+
+ (plug-in-waves RUN-NONINTERACTIVE
+ image
+ waves-layer
+ amplitude
+ phase
+ wavelength
+ 0
+ FALSE)
+
+ (set! remaining-frames (- remaining-frames 1))
+ (set! phase (- phase phaseshift))
+ )
+ )
+
+ (gimp-item-set-name source-layer "Frame 1")
+ (plug-in-waves RUN-NONINTERACTIVE
+ image
+ source-layer
+ amplitude
+ phase
+ wavelength
+ 0
+ FALSE)
+
+ (gimp-image-undo-enable image)
+ (gimp-display-new image)
+ )
+)
+
+(script-fu-register "script-fu-waves-anim"
+ _"_Waves..."
+ _"Create a multi-layer image with an effect like a stone was thrown into the current image"
+ "Sven Neumann <sven@gimp.org>"
+ "Sven Neumann"
+ "1997/13/12"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Amplitude" '(10 1 101 1 10 1 0)
+ SF-ADJUSTMENT _"Wavelength" '(10 0.1 100 1 10 1 0)
+ SF-ADJUSTMENT _"Number of frames" '(6 1 512 1 10 0 1)
+ SF-TOGGLE _"Invert direction" FALSE
+)
+
+(script-fu-menu-register "script-fu-waves-anim"
+ "<Image>/Filters/Animation/Animators")
diff --git a/plug-ins/script-fu/scripts/weave.scm b/plug-ins/script-fu/scripts/weave.scm
new file mode 100644
index 0000000..81fd0a7
--- /dev/null
+++ b/plug-ins/script-fu/scripts/weave.scm
@@ -0,0 +1,415 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; Weave script --- make an image look as if it were woven
+; Copyright (C) 1997 Federico Mena Quintero
+; federico@nuclecu.unam.mx
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+; Copies the specified rectangle from/to the specified drawable
+
+(define (copy-rectangle img
+ drawable
+ x1
+ y1
+ width
+ height
+ dest-x
+ dest-y)
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE x1 y1 width height)
+ (gimp-edit-copy drawable)
+ (let ((floating-sel (car (gimp-edit-paste drawable FALSE))))
+ (gimp-layer-set-offsets floating-sel dest-x dest-y)
+ (gimp-floating-sel-anchor floating-sel))
+ (gimp-selection-none img))
+
+; Creates a single weaving tile
+
+(define (create-weave-tile ribbon-width
+ ribbon-spacing
+ shadow-darkness
+ shadow-depth)
+ (let* ((tile-size (+ (* 2 ribbon-width) (* 2 ribbon-spacing)))
+ (darkness (* 255 (/ (- 100 shadow-darkness) 100)))
+ (img (car (gimp-image-new tile-size tile-size RGB)))
+ (drawable (car (gimp-layer-new img tile-size tile-size RGB-IMAGE
+ "Weave tile" 100 LAYER-MODE-NORMAL))))
+
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ ; Create main horizontal ribbon
+
+ (gimp-context-set-foreground '(255 255 255))
+ (gimp-context-set-background (list darkness darkness darkness))
+
+ (gimp-image-select-rectangle img
+ CHANNEL-OP-REPLACE
+ 0
+ ribbon-spacing
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ ribbon-width)
+
+ (gimp-context-set-gradient-fg-bg-rgb)
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-BILINEAR (- 100 shadow-depth)
+ FALSE 0 0
+ TRUE
+ (/ (+ (* 2 ribbon-spacing) ribbon-width -1) 2) 0
+ 0 0)
+
+ ; Create main vertical ribbon
+
+ (gimp-image-select-rectangle img
+ CHANNEL-OP-REPLACE
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ 0
+ ribbon-width
+ (+ (* 2 ribbon-spacing) ribbon-width))
+
+ (gimp-drawable-edit-gradient-fill drawable
+ GRADIENT-BILINEAR (- 100 shadow-depth)
+ FALSE 0 0
+ TRUE
+ 0 (/ (+ (* 2 ribbon-spacing) ribbon-width -1) 2)
+ 0 0)
+
+ ; Create the secondary horizontal ribbon
+
+ (copy-rectangle img
+ drawable
+ 0
+ ribbon-spacing
+ (+ ribbon-width ribbon-spacing)
+ ribbon-width
+ (+ ribbon-width ribbon-spacing)
+ (+ (* 2 ribbon-spacing) ribbon-width))
+
+ (copy-rectangle img
+ drawable
+ (+ ribbon-width ribbon-spacing)
+ ribbon-spacing
+ ribbon-spacing
+ ribbon-width
+ 0
+ (+ (* 2 ribbon-spacing) ribbon-width))
+
+ ; Create the secondary vertical ribbon
+
+ (copy-rectangle img
+ drawable
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ 0
+ ribbon-width
+ (+ ribbon-width ribbon-spacing)
+ ribbon-spacing
+ (+ ribbon-width ribbon-spacing))
+
+ (copy-rectangle img
+ drawable
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ (+ ribbon-width ribbon-spacing)
+ ribbon-width
+ ribbon-spacing
+ ribbon-spacing
+ 0)
+
+ ; Done
+
+ (gimp-image-undo-enable img)
+ (list img drawable)))
+
+; Creates a complete weaving mask
+
+(define (create-weave width
+ height
+ ribbon-width
+ ribbon-spacing
+ shadow-darkness
+ shadow-depth)
+ (let* ((tile (create-weave-tile ribbon-width ribbon-spacing shadow-darkness
+ shadow-depth))
+ (tile-img (car tile))
+ (tile-layer (cadr tile))
+ (weaving (plug-in-tile RUN-NONINTERACTIVE tile-img tile-layer width height TRUE)))
+ (gimp-image-delete tile-img)
+ weaving))
+
+; Creates a single tile for masking
+
+(define (create-mask-tile ribbon-width
+ ribbon-spacing
+ r1-x1
+ r1-y1
+ r1-width
+ r1-height
+ r2-x1
+ r2-y1
+ r2-width
+ r2-height
+ r3-x1
+ r3-y1
+ r3-width
+ r3-height)
+ (let* ((tile-size (+ (* 2 ribbon-width) (* 2 ribbon-spacing)))
+ (img (car (gimp-image-new tile-size tile-size RGB)))
+ (drawable (car (gimp-layer-new img tile-size tile-size RGB-IMAGE
+ "Mask" 100 LAYER-MODE-NORMAL))))
+ (gimp-image-undo-disable img)
+ (gimp-image-insert-layer img drawable 0 0)
+
+ (gimp-context-set-background '(0 0 0))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+
+ (gimp-image-select-rectangle img CHANNEL-OP-REPLACE r1-x1 r1-y1 r1-width r1-height)
+ (gimp-image-select-rectangle img CHANNEL-OP-ADD r2-x1 r2-y1 r2-width r2-height)
+ (gimp-image-select-rectangle img CHANNEL-OP-ADD r3-x1 r3-y1 r3-width r3-height)
+
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+ (gimp-selection-none img)
+
+ (gimp-image-undo-enable img)
+
+ (list img drawable)))
+
+; Creates a complete mask image
+
+(define (create-mask final-width
+ final-height
+ ribbon-width
+ ribbon-spacing
+ r1-x1
+ r1-y1
+ r1-width
+ r1-height
+ r2-x1
+ r2-y1
+ r2-width
+ r2-height
+ r3-x1
+ r3-y1
+ r3-width
+ r3-height)
+ (let* ((tile (create-mask-tile ribbon-width ribbon-spacing
+ r1-x1 r1-y1 r1-width r1-height
+ r2-x1 r2-y1 r2-width r2-height
+ r3-x1 r3-y1 r3-width r3-height))
+ (tile-img (car tile))
+ (tile-layer (cadr tile))
+ (mask (plug-in-tile RUN-NONINTERACTIVE tile-img tile-layer final-width final-height
+ TRUE)))
+ (gimp-image-delete tile-img)
+ mask))
+
+; Creates the mask for horizontal ribbons
+
+(define (create-horizontal-mask ribbon-width
+ ribbon-spacing
+ final-width
+ final-height)
+ (create-mask final-width
+ final-height
+ ribbon-width
+ ribbon-spacing
+ 0
+ ribbon-spacing
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ ribbon-width
+ 0
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ ribbon-spacing
+ ribbon-width
+ (+ ribbon-width ribbon-spacing)
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ (+ ribbon-width ribbon-spacing)
+ ribbon-width))
+
+; Creates the mask for vertical ribbons
+
+(define (create-vertical-mask ribbon-width
+ ribbon-spacing
+ final-width
+ final-height)
+ (create-mask final-width
+ final-height
+ ribbon-width
+ ribbon-spacing
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ 0
+ ribbon-width
+ (+ (* 2 ribbon-spacing) ribbon-width)
+ ribbon-spacing
+ 0
+ ribbon-width
+ ribbon-spacing
+ ribbon-spacing
+ (+ ribbon-width ribbon-spacing)
+ ribbon-width
+ (+ ribbon-width ribbon-spacing)))
+
+; Adds a threads layer at a certain orientation to the specified image
+
+(define (create-threads-layer img
+ width
+ height
+ length
+ density
+ orientation)
+ (let* ((drawable (car (gimp-layer-new img width height RGBA-IMAGE
+ "Threads" 100 LAYER-MODE-NORMAL)))
+ (dense (/ density 100.0)))
+ (gimp-image-insert-layer img drawable 0 -1)
+ (gimp-context-set-background '(255 255 255))
+ (gimp-drawable-edit-fill drawable FILL-BACKGROUND)
+ (plug-in-noisify RUN-NONINTERACTIVE img drawable FALSE dense dense dense dense)
+ (plug-in-c-astretch RUN-NONINTERACTIVE img drawable)
+ (cond ((eq? orientation 'horizontal)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img drawable length TRUE FALSE))
+ ((eq? orientation 'vertical)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE img drawable length FALSE TRUE)))
+ (plug-in-c-astretch RUN-NONINTERACTIVE img drawable)
+ drawable))
+
+(define (create-complete-weave width
+ height
+ ribbon-width
+ ribbon-spacing
+ shadow-darkness
+ shadow-depth
+ thread-length
+ thread-density
+ thread-intensity)
+ (let* ((weave (create-weave width height ribbon-width ribbon-spacing
+ shadow-darkness shadow-depth))
+ (w-img (car weave))
+ (w-layer (cadr weave))
+
+ (h-layer (create-threads-layer w-img width height thread-length
+ thread-density 'horizontal))
+ (h-mask (car (gimp-layer-create-mask h-layer ADD-MASK-WHITE)))
+
+ (v-layer (create-threads-layer w-img width height thread-length
+ thread-density 'vertical))
+ (v-mask (car (gimp-layer-create-mask v-layer ADD-MASK-WHITE)))
+
+ (hmask (create-horizontal-mask ribbon-width ribbon-spacing
+ width height))
+ (hm-img (car hmask))
+ (hm-layer (cadr hmask))
+
+ (vmask (create-vertical-mask ribbon-width ribbon-spacing width height))
+ (vm-img (car vmask))
+ (vm-layer (cadr vmask)))
+
+ (gimp-layer-add-mask h-layer h-mask)
+ (gimp-selection-all hm-img)
+ (gimp-edit-copy hm-layer)
+ (gimp-image-delete hm-img)
+ (gimp-floating-sel-anchor (car (gimp-edit-paste h-mask FALSE)))
+ (gimp-layer-set-opacity h-layer thread-intensity)
+ (gimp-layer-set-mode h-layer LAYER-MODE-MULTIPLY)
+
+ (gimp-layer-add-mask v-layer v-mask)
+ (gimp-selection-all vm-img)
+ (gimp-edit-copy vm-layer)
+ (gimp-image-delete vm-img)
+ (gimp-floating-sel-anchor (car (gimp-edit-paste v-mask FALSE)))
+ (gimp-layer-set-opacity v-layer thread-intensity)
+ (gimp-layer-set-mode v-layer LAYER-MODE-MULTIPLY)
+
+ ; Uncomment this if you want to keep the weaving mask image
+ ; (gimp-display-new (car (gimp-image-duplicate w-img)))
+
+ (list w-img
+ (car (gimp-image-flatten w-img)))))
+
+; The main weave function
+
+(define (script-fu-weave img
+ drawable
+ ribbon-width
+ ribbon-spacing
+ shadow-darkness
+ shadow-depth
+ thread-length
+ thread-density
+ thread-intensity)
+ (gimp-context-push)
+ (gimp-image-undo-group-start img)
+
+ (let* (
+ (d-img (car (gimp-item-get-image drawable)))
+ (d-width (car (gimp-drawable-width drawable)))
+ (d-height (car (gimp-drawable-height drawable)))
+ (d-offsets (gimp-drawable-offsets drawable))
+
+ (weaving (create-complete-weave d-width
+ d-height
+ ribbon-width
+ ribbon-spacing
+ shadow-darkness
+ shadow-depth
+ thread-length
+ thread-density
+ thread-intensity))
+ (w-img (car weaving))
+ (w-layer (cadr weaving))
+ )
+
+ (gimp-context-set-paint-mode LAYER-MODE-NORMAL)
+ (gimp-context-set-opacity 100.0)
+ (gimp-context-set-feather FALSE)
+
+ (gimp-selection-all w-img)
+ (gimp-edit-copy w-layer)
+ (gimp-image-delete w-img)
+ (let ((floating-sel (car (gimp-edit-paste drawable FALSE))))
+ (gimp-layer-set-offsets floating-sel
+ (car d-offsets)
+ (cadr d-offsets))
+ (gimp-layer-set-mode floating-sel LAYER-MODE-MULTIPLY)
+ (gimp-floating-sel-to-layer floating-sel)
+ )
+ )
+ (gimp-context-pop)
+ (gimp-image-undo-group-end img)
+ (gimp-displays-flush)
+)
+
+(script-fu-register "script-fu-weave"
+ _"_Weave..."
+ _"Create a new layer filled with a weave effect to be used as an overlay or bump map"
+ "Federico Mena Quintero"
+ "Federico Mena Quintero"
+ "June 1997"
+ "RGB* GRAY*"
+ SF-IMAGE "Image to Weave" 0
+ SF-DRAWABLE "Drawable to Weave" 0
+ SF-ADJUSTMENT _"Ribbon width" '(30 0 256 1 10 1 1)
+ SF-ADJUSTMENT _"Ribbon spacing" '(10 0 256 1 10 1 1)
+ SF-ADJUSTMENT _"Shadow darkness" '(75 0 100 1 10 1 1)
+ SF-ADJUSTMENT _"Shadow depth" '(75 0 100 1 10 1 1)
+ SF-ADJUSTMENT _"Thread length" '(200 0 256 1 10 1 1)
+ SF-ADJUSTMENT _"Thread density" '(50 0 100 1 10 1 1)
+ SF-ADJUSTMENT _"Thread intensity" '(100 0 100 1 10 1 1)
+)
+
+(script-fu-menu-register "script-fu-weave"
+ "<Image>/Filters/Artistic")
diff --git a/plug-ins/script-fu/scripts/xach-effect.scm b/plug-ins/script-fu/scripts/xach-effect.scm
new file mode 100644
index 0000000..ae83885
--- /dev/null
+++ b/plug-ins/script-fu/scripts/xach-effect.scm
@@ -0,0 +1,142 @@
+; GIMP - The GNU Image Manipulation Program
+; Copyright (C) 1995 Spencer Kimball and Peter Mattis
+;
+; xach effect script
+; Copyright (c) 1997 Adrian Likins
+; aklikins@eos.ncsu.edu
+;
+; based on a idea by Xach Beane <xach@mint.net>
+;
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+(define (script-fu-xach-effect image
+ drawable
+ hl-offset-x
+ hl-offset-y
+ hl-color
+ hl-opacity-comp
+ ds-color
+ ds-opacity
+ ds-blur
+ ds-offset-x
+ ds-offset-y
+ keep-selection)
+ (let* (
+ (ds-blur (max ds-blur 0))
+ (ds-opacity (min ds-opacity 100))
+ (ds-opacity (max ds-opacity 0))
+ (type (car (gimp-drawable-type-with-alpha drawable)))
+ (image-width (car (gimp-image-width image)))
+ (hl-opacity (list hl-opacity-comp hl-opacity-comp hl-opacity-comp))
+ (image-height (car (gimp-image-height image)))
+ (active-selection 0)
+ (from-selection 0)
+ (theLayer 0)
+ (hl-layer 0)
+ (shadow-layer 0)
+ (mask 0)
+ )
+
+ (gimp-context-push)
+ (gimp-context-set-defaults)
+
+ (gimp-image-undo-group-start image)
+ (gimp-layer-add-alpha drawable)
+
+ (if (= (car (gimp-selection-is-empty image)) TRUE)
+ (begin
+ (gimp-image-select-item image CHANNEL-OP-REPLACE drawable)
+ (set! active-selection (car (gimp-selection-save image)))
+ (set! from-selection FALSE))
+ (begin
+ (set! from-selection TRUE)
+ (set! active-selection (car (gimp-selection-save image)))))
+
+ (set! hl-layer (car (gimp-layer-new image image-width image-height type _"Highlight" 100 LAYER-MODE-NORMAL)))
+ (gimp-image-insert-layer image hl-layer 0 -1)
+
+ (gimp-selection-none image)
+ (gimp-drawable-edit-clear hl-layer)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+
+ (gimp-context-set-background hl-color)
+ (gimp-drawable-edit-fill hl-layer FILL-BACKGROUND)
+ (gimp-selection-translate image hl-offset-x hl-offset-y)
+ (gimp-drawable-edit-fill hl-layer FILL-BACKGROUND)
+ (gimp-selection-none image)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+
+ (set! mask (car (gimp-layer-create-mask hl-layer ADD-MASK-WHITE)))
+ (gimp-layer-add-mask hl-layer mask)
+
+ (gimp-context-set-background hl-opacity)
+ (gimp-drawable-edit-fill mask FILL-BACKGROUND)
+
+ (set! shadow-layer (car (gimp-layer-new image
+ image-width
+ image-height
+ type
+ _"Shadow"
+ ds-opacity
+ LAYER-MODE-NORMAL)))
+ (gimp-image-insert-layer image shadow-layer 0 -1)
+ (gimp-selection-none image)
+ (gimp-drawable-edit-clear shadow-layer)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-selection-translate image ds-offset-x ds-offset-y)
+ (gimp-context-set-background ds-color)
+ (gimp-drawable-edit-fill shadow-layer FILL-BACKGROUND)
+ (gimp-selection-none image)
+ (plug-in-gauss-rle RUN-NONINTERACTIVE image shadow-layer ds-blur TRUE TRUE)
+ (gimp-image-select-item image CHANNEL-OP-REPLACE active-selection)
+ (gimp-drawable-edit-clear shadow-layer)
+ (gimp-image-lower-item image shadow-layer)
+
+ (if (= keep-selection FALSE)
+ (gimp-selection-none image))
+
+ (gimp-image-set-active-layer image drawable)
+ (gimp-image-remove-channel image active-selection)
+ (gimp-image-undo-group-end image)
+ (gimp-displays-flush)
+
+ (gimp-context-pop)
+ )
+)
+
+(script-fu-register "script-fu-xach-effect"
+ _"_Xach-Effect..."
+ _"Add a subtle translucent 3D effect to the selected region (or alpha)"
+ "Adrian Likins <adrian@gimp.org>"
+ "Adrian Likins"
+ "9/28/97"
+ "RGB* GRAY*"
+ SF-IMAGE "Image" 0
+ SF-DRAWABLE "Drawable" 0
+ SF-ADJUSTMENT _"Highlight X offset" '(-1 -100 100 1 10 0 1)
+ SF-ADJUSTMENT _"Highlight Y offset" '(-1 -100 100 1 10 0 1)
+ SF-COLOR _"Highlight color" "white"
+ SF-ADJUSTMENT _"Highlight opacity" '(66 0 255 1 10 0 0)
+ SF-COLOR _"Drop shadow color" "black"
+ SF-ADJUSTMENT _"Drop shadow opacity" '(100 0 100 1 10 0 0)
+ SF-ADJUSTMENT _"Drop shadow blur radius" '(12 0 255 1 10 0 1)
+ SF-ADJUSTMENT _"Drop shadow X offset" '(5 0 255 1 10 0 1)
+ SF-ADJUSTMENT _"Drop shadow Y offset" '(5 0 255 1 10 0 1)
+ SF-TOGGLE _"Keep selection" TRUE
+)
+
+(script-fu-menu-register "script-fu-xach-effect"
+ "<Image>/Filters/Light and Shadow/Shadow")
diff --git a/plug-ins/script-fu/tinyscheme/BUILDING b/plug-ins/script-fu/tinyscheme/BUILDING
new file mode 100644
index 0000000..5c00236
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/BUILDING
@@ -0,0 +1,139 @@
+ Building TinyScheme
+ -------------------
+
+The included makefile includes logic for Linux, Solaris and Win32, and can
+readily serve as an example for other OSes, especially Unixes. There are
+a lot of compile-time flags in TinyScheme (preprocessor defines) that can trim
+unwanted features. See next section. 'make all' and 'make clean' function as
+expected.
+
+Autoconfing TinyScheme was once proposed, but the distribution would not be
+so small anymore. There are few platform dependencies in TinyScheme, and in
+general compiles out of the box.
+
+ Customizing
+ -----------
+
+ The following symbols are defined to default values in scheme.h.
+ Use the -D flag of cc to set to either 1 or 0.
+
+ STANDALONE
+ Define this to produce a standalone interpreter.
+
+ USE_MATH
+ Includes math routines.
+
+ USE_CHAR_CLASSIFIERS
+ Includes character classifier procedures.
+
+ USE_ASCII_NAMES
+ Enable extended character notation based on ASCII names.
+
+ USE_STRING_PORTS
+ Enables string ports.
+
+ USE_ERROR_HOOK
+ To force system errors through user-defined error handling.
+ (see "Error handling")
+
+ USE_TRACING
+ To enable use of TRACING.
+
+ USE_COLON_HOOK
+ Enable use of qualified identifiers. (see "Colon Qualifiers - Packages")
+ Defining this as 0 has the rather drastic consequence that any code using
+ packages will stop working, and will have to be modified. It should only
+ be used if you *absolutely* need to use '::' in identifiers.
+
+ USE_STRCASECMP
+ Defines stricmp as strcasecmp, for Unix.
+
+ STDIO_ADDS_CR
+ Informs TinyScheme that stdio translates "\n" to "\r\n". For DOS/Windows.
+
+ USE_DL
+ Enables dynamically loaded routines. If you define this symbol, you
+ should also include dynload.c in your compile.
+
+ USE_PLIST
+ Enables property lists (not Standard Scheme stuff). Off by default.
+
+ USE_NO_FEATURES
+ Shortcut to disable USE_MATH, USE_CHAR_CLASSIFIERS, USE_ASCII_NAMES,
+ USE_STRING_PORTS, USE_ERROR_HOOK, USE_TRACING, USE_COLON_HOOK,
+ USE_DL.
+
+ USE_SCHEME_STACK
+ Enables 'cons' stack (the alternative is a faster calling scheme, which
+ breaks continuations). Undefine it if you don't care about strict compatibility
+ but you do care about faster execution.
+
+
+ OS-X tip
+ --------
+ I don't have access to OS-X, but Brian Maher submitted the following tip:
+
+[1] Download and install fink (I installed fink in
+/usr/local/fink)
+[2] Install the 'dlcompat' package using fink as such:
+> fink install dlcompat
+[3] Make the following changes to the
+tinyscheme-1.32.tar.gz
+
+diff -r tinyscheme-1.32/dynload.c
+tinyscheme-1.32-new/dynload.c
+24c24
+< #define SUN_DL
+---
+>
+Only in tinyscheme-1.32-new/: dynload.o
+Only in tinyscheme-1.32-new/: libtinyscheme.a Only in tinyscheme-1.32-new/: libtinyscheme.so diff -r tinyscheme-1.32/makefile tinyscheme-1.32-new/makefile
+33,34c33,43
+< LD = gcc
+< LDFLAGS = -shared
+---
+> #LD = gcc
+> #LDFLAGS = -shared
+> #DEBUG=-g -Wno-char-subscripts -O
+> #SYS_LIBS= -ldl
+> #PLATFORM_FEATURES= -DSUN_DL=1
+>
+> # Mac OS X
+> CC = gcc
+> CFLAGS = -I/usr/local/fink/include
+> LD = gcc
+> LDFLAGS = -L/usr/local/fink/lib
+37c46
+< PLATFORM_FEATURES= -DSUN_DL=1
+---
+> PLATFORM_FEATURES= -DSUN_DL=1 -DOSX
+60c69
+< $(CC) -I. -c $(DEBUG) $(FEATURES)
+$(DL_FLAGS) $<
+---
+> $(CC) $(CFLAGS) -I. -c $(DEBUG)
+$(FEATURES) $(DL_FLAGS) $<
+66c75
+< $(CC) -o $@ $(DEBUG) $(OBJS) $(SYS_LIBS)
+---
+> $(CC) $(LDFLAGS) -o $@ $(DEBUG) $(OBJS)
+$(SYS_LIBS)
+Only in tinyscheme-1.32-new/: scheme
+diff -r tinyscheme-1.32/scheme.c
+tinyscheme-1.32-new/scheme.c
+60,61c60,61
+< #ifndef macintosh
+< # include <malloc.h>
+---
+> #ifdef OSX
+> /* Do nothing */
+62a63,65
+> # ifndef macintosh
+> # include <malloc.h>
+> # else
+77c80,81
+< #endif /* macintosh */
+---
+> # endif /* macintosh */
+> #endif /* !OSX */
+Only in tinyscheme-1.32-new/: scheme.o
diff --git a/plug-ins/script-fu/tinyscheme/CHANGES b/plug-ins/script-fu/tinyscheme/CHANGES
new file mode 100644
index 0000000..2f7a33a
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/CHANGES
@@ -0,0 +1,326 @@
+Change Log
+----------
+
+Version 1.41
+ Bugs fixed:
+ #3020389 - Added makefile section for Mac OS X (SL)
+ #3286135 - Fixed num_mod routine which caused errors in use of modulo
+ #3290232 - Corrected version number shown on startup (GM)
+ #3394882 - Added missing #if in opdefines.h around get and put (DC)
+ #3395547 - Fix for the modulo procedure (DC)
+ #3400290 - Optimized append to make it an O(n) operation (DC)
+ #3493926 - Corrected flag used when building shared files on OSX (J)
+
+ R5RS related changes:
+ #2866196 - Parser does not handle delimiters correctly
+ #3395548 - Add a decimal point to inexact numbers in atom2str (DC)
+ #3399331 - Make min/max return inexact when any argument is inexact
+ #3399332 - Compatibility fix for expt.
+ #3399335 - Optional radix for string->number and number->string (DC)
+ #3400202 - Append with one argument should not return a list (DC)
+ #3400284 - Compatibility fix for integer?
+
+ Other changes:
+ - Added flags to makefile for MinGW/MSYS (TC)
+ - Moved variable declarations to avoid warnings with some compilers
+ - Don't print space after initial #( when printing vectors.
+ - Minor optimization for is_nonneg().
+ - No need to round integers in OP_ROUND (#3400284)
+ - Fixes to code that reports line number with error (RC)
+
+ Contributors:
+ Kevin Cozens, Gordon McNutt, Doug Currie, Sean Long, Tim Cas, Joey,
+ and Richard Copley, and CMarinier.
+
+Version 1.40
+ Bugs fixed:
+ #1964950 - Stop core dumps due to bad syntax in LET (and variants)
+ #2826594 - allow reverse to work on empty list (Tony Garnock-Jones)
+ Potential problem of arglist to foreign calls being wrongly GC'ed.
+ Fixed bug that read could loop forever (tehom).
+
+ API changes:
+ Exposed is_list and list_length.
+ Added scheme_register_foreign_func_list and declarations for it (tehom)
+ Defined *compile-hook* (tehom)
+
+ Other changes:
+ Updated is_list and list_length to handle circular lists.
+ Nested calling thru C has been made now safer (tehom)
+ Peter Michaux cleaned up port_rep_from_file
+ Added unwind-protect (tehom)
+ Some cleanups to in/outport and Eval_Cycle by Peter Michaux
+ Report error line number (Mostly by Sanel Zukan, back-compatibility by Tehom)
+
+ Contributors:
+ Kevin Cozens, Dimitrios Souflis, Tom Breton, Peter Michaux, Sanel Zukan,
+ and Tony Garnock-Jones.
+
+Version 1.39
+ Bugs fixed:
+ Fix for the load bug
+ Fixed parsing of octal coded characters. Fixes bug #1818018.
+ Added tests for when mk_vector is out of memory. Can't rely on sc->sink.
+ Fix for bug #1794369
+ Finished feature-request 1599947: scheme_apply0 etc return values.
+ Partly provided feature-request 1599947: Expose list_length, eqv, etc
+ Provided feature-request 1599945, Scheme->C->Scheme calling.
+ Fix for bug 1593861 (behavior of is_integer)
+ Fix for bug 1589711
+ Error checking of binding spec syntax in LET and LETREC. The bad syntax
+ was causing a segmentation fault in Linux. Complete fixes for bug #1817986.
+ Error checking of binding spec syntax in LET*
+ Bad syntax was causing core dump in Linux.
+ Fix for nasty gc bug
+
+ R5RS changes:
+ R5RS requires numbers to be of equal value AND of the same type (ie. both
+ exact or inexact) in order to return #t from eqv?. R5RS compliance fix.
+ String output ports now conform to SRFI-6
+
+ Other changes:
+ Drew Yao fixed buffer overflow problems in mk_sharp_const.
+ put OP_T0LVL in charge of reacting to EOF
+ file_push checks array bounds (patch from Ray Lehtiniemi)
+ Changed to always use snprintf (Patch due to Ramiro bsd1628)
+ Updated usage information using text from the Manual.txt file.
+
+Version 1.38
+ Interim release until the rewrite, mostly incorporating modifications
+ from Kevin Cozens. Small addition for Cygwin in the makefile, and
+ modifications by Andrew Guenther for Apple platforms.
+
+Version 1.37
+ Joe Buehler submitted reserve_cells.
+
+Version 1.36
+ Joe Buehler fixed a patch in the allocator.
+ Alexander Shendi moved the comment handling in the scanner, which
+ fixed an obscure bug for which Mike E had provided a patch as well.
+ Kevin Cozens has submitted some fixes and modifications which have
+ not been incorporated yet in their entirety.
+
+Version 1.35
+ Todd Showalter discovered that the number of free cells reported
+ after GC was incorrect, which could also cause unnecessary allocations.
+
+Version 1.34
+ Long missing version. Lots of bugfixes have accumulated in my email, so
+ I had to start using them. In this version, Keenan Pepper has submitted
+ a bugfix for the string comparison library procedure, Wouter Boeke
+ modified some code that was casting to the wrong type and crashed on
+ some machines, "SheppardCo" submitted a replacement "modulo" code and
+ Scott Fenton submitted lots of corrections that shut up some compiler
+ warnings. Brian Maher submitted instructions on how to build on OS-X.
+ I have to dig deeper into my mailbox and find earlier emails, too.
+
+Version 1.33
+ Charles Hayden fixed a nasty GC bug of the new stack frame, while in
+ the process of porting TinyScheme to C++. He also submitted other
+ changes, and other people also had comments or requests, but the GC
+ bug was so important that this version is put through the door to
+ correct it.
+
+Version 1.32
+ Stephen Gildea put some quality time on TinyScheme again, and made
+ a whole lot of changes to the interpreter that made it noticeably
+ faster.
+
+Version 1.31
+ Patches to the hastily-done version 1.30. Stephen Gildea fixed
+ some things done wrongly, and Richard Russo fixed the makefile
+ for building on Windows. Property lists (heritage from MiniScheme)
+ are now optional and have disappeared from the interface. They
+ should be considered as deprecated.
+
+Version 1.30
+ After many months, I followed Preston Bannister's advice of
+ using macros and a single source text to keep the enums and the
+ dispatch table in sync, and I used his contributed "opdefines.h".
+ Timothy Downs contributed a helpful function, "scheme_call".
+ Stephen Gildea contributed new versions of the makefile and
+ practically all other sources. He created a built-in STRING-APPEND,
+ and fixed a lot of other bugs.
+ Ruhi Bloodworth reported fixes necessary for OS X and a small
+ bug in dynload.c.
+
+Version 1.29
+ The previous version contained a lot of corrections, but there
+ were a lot more that still wait on a sheet of paper lost in a
+ carton someplace after my house move... Manuel Heras-Gilsanz
+ noticed this and resent his own contribution, which relies on
+ another bugfix that v.1.28 was missing: a problem with string
+ output, that this version fixes. I hope other people will take
+ the time to resend their contributions, if they didn't make it
+ to v.1.28.
+
+Version 1.28
+ Many people have contacted me with bugfixes or remarks in
+ the three months I was inactive. A lot of them spotted that
+ scheme_deinit crashed while reporting gc results. They suggested
+ that sc->outport be set to NIL in scheme_deinit, which I did.
+ Dennis Taylor remarked that OP_VALUEPRINT reset sc->value instead
+ of preserving it. He submitted a modification which I adopted
+ partially. David Hovemeyer sent me many little changes, that you
+ will find in version 1.28, and Partice Stoessel modified the
+ float reader to conform to R5RS.
+
+Version 1.27
+ Version 1.27 is the successor of 1.25. Bug fixes only, but I had to
+ release them so that everybody can profit. 'Backchar' tried to write
+ back to the string, which obviously didn't work for const strings.
+ 'Substring' didn't check for crossed start and end indices. Defines
+ changed to restore the ability to compile under MSVC.
+
+Version 1.26
+ Version 1.26 was never released. I changed a lot of things, in fact
+ too much, even the garbage collector, and hell broke loose. I'll
+ try a more gradual approach next time.
+
+Version 1.25
+ Types have been homogenized to be able to accommodate a different
+ representation. Plus, promises are no longer closures. Unfortunately,
+ I discovered that continuations and force/delay do not pass the SCM
+ test (and never did)... However, on the bright side, what little
+ modifications I did had a large impact on the footprint:
+ USE_NO_FEATURES now produces an object file of 63960 bytes on Linux!
+
+Version 1.24
+ SCM tests now pass again after change in atom2str.
+
+Version 1.23
+ Finally I managed to mess it up with my version control. Version
+ 1.22 actually lacked some of the things I have been fixing in the
+ meantime. This should be considered as a complete replacement for
+ 1.22.
+
+Version 1.22
+ The new ports had a bug in LOAD. MK_CLOSURE is introduced.
+ Shawn Wagner inquired about string->number and number->string.
+ I added string->atom and atom->string and defined the number
+ functions from them. Doing that, I fixed WRITE applied to symbols
+ (it didn't quote them). Unfortunately, minimum build is now
+ slightly larger than 64k... I postpone action because Jason's idea
+ might solve it elegantly.
+
+Version 1.21
+ Jason Felice submitted a radically different datatype representation
+ which he had implemented. While discussing its pros and cons, it
+ became apparent that the current implementation of ports suffered
+ from a grave fault: ports were not garbage-collected. I changed the
+ ports to be heap-allocated, which enabled the use of string ports
+ for loading. Jason also fixed errors in the garbage collection of
+ vectors. USE_VERBATIM is gone. "ssp_compiler.c" has a better solution
+ on HTML generation. A bug involving backslash notation in strings
+ has been fixed. '-c' flag now executes next argument as a stream of
+ Scheme commands. Foreign functions are now also heap allocated,
+ and scheme_define is used to define everything.
+
+Version 1.20
+ Tracing has been added. The toplevel loop has been slightly
+ rearranged. Backquote reading for vector templates has been
+ sanitized. Symbol interning is now correct. Arithmetic functions
+ have been corrected. APPLY, MAP, FOR-EACH, numeric comparison
+ functions fixed. String reader/writer understands \xAA notation.
+
+Version 1.19
+ Carriage Return now delimits identifiers. DOS-formatted Scheme files
+ can be used by Unix. Random number generator added to library.
+ Fixed some glitches of the new type-checking scheme. Fixed erroneous
+ (append '() 'a) behavior. Will continue with r4rstest.scm to
+ fix errors.
+
+Version 1.18
+ The FFI has been extended. USE_VERBOSE_GC has gone. Anyone wanting
+ the same functionality can put (gcverbose #t) in init.scm.
+ print-width was removed, along with three corresponding op-codes.
+ Extended character constants with ASCII names were added.
+ mk_counted_string paves the way for full support of binary strings.
+ As much as possible of the type-checking chores were delegated
+ to the inner loop, thus reducing the code size to less than 4200 loc!
+
+Version 1.17
+ Dynamically-loaded extensions are more fully integrated.
+ TinyScheme is now distributed under the BSD open-source license.
+
+Version 1.16
+ Dynamically-loaded extensions introduced (USE_DL).
+ Santeri Paavolainen found a race condition: When a cons is executed,
+ and each of the two arguments is a constructing function, GC could
+ happen before all arguments are evaluated and cons() is called, and
+ the evaluated arguments would all be reclaimed!
+ Fortunately, such a case was rare in the code, although it is
+ a pitfall in new code and code in foreign functions. Currently, only
+ one such case remains, when COLON_HOOK is defined.
+
+Version 1.15
+ David Gould also contributed some changes that speed up operation.
+ Kirk Zurell fixed HASPROP.
+ The Garbage Collection didn't collect all the garbage...fixed.
+
+Version 1.14
+ Unfortunately, after Andre fixed the GC it became obvious that the
+ algorithm was too slow... Fortunately, David Gould found a way to
+ speed it up.
+
+Version 1.13
+ Silly bug involving division by zero resolved by Roland Kaufman.
+ Macintoch support from Shmulik Regev.
+ Float parser bug fixed by Alexander Shendi.
+ GC bug from Andru Luvisi.
+
+Version 1.12
+ Cis* incorrectly called isalpha() instead of isascii()
+ Added USE_CHAR_CLASSIFIERS, USE_STRING_PORTS.
+
+Version 1.11
+ BSDI defines isnumber... changed all similar functions to is_*
+ EXPT now has correct definition. Added FLOOR,CEILING,TRUNCATE
+ and ROUND, courtesy of Bengt Kleberg. Preprocessor symbols now
+ have values 1 or 0, and can be set as compiler defines (proposed
+ by Andy Ganor *months* ago). 'prompt' and 'InitFile' can now be
+ defined during compilation, too.
+
+Version 1.10
+ Another bug when file ends with comment!
+ Added DEFINE-MACRO in init.scm, courtesy of Andy Gaynor.
+
+Version 1.09
+ Removed bug when READ met EOF. lcm.
+
+Version 1.08
+ quotient,remainder and modulo. gcd.
+
+Version 1.07
+ '=>' in cond now exists
+ list? now checks for circularity
+ some reader bugs removed
+ Reader is more consistent wrt vectors
+ Quote and Quasiquote work with vectors
+
+Version 1.06
+ #! is now skipped
+ generic-assoc bug removed
+ strings are now managed differently, hack.txt is removed
+ various delicate points fixed
+
+Version 1.05
+ Support for scripts, *args*, "-1" option.
+ Various R5RS procedures.
+ *sharp-hook*
+ Handles unmatched parentheses.
+ New architecture for procedures.
+
+Version 1.04
+ Added missing T_ATOM bits...
+ Added vectors
+ Free-list is sorted by address, since vectors need consecutive cells.
+ (quit <exitcode>) for use with scripts
+
+Version 1.03 (26 Aug 1998):
+ Extended .h with useful functions for FFI
+ Library: with-input-* etc.
+ Finished R5RS I/O, added string ports.
+
+Version 1.02 (25 Aug 1998):
+ First part of R5RS I/O.
diff --git a/plug-ins/script-fu/tinyscheme/COPYING b/plug-ins/script-fu/tinyscheme/COPYING
new file mode 100644
index 0000000..a6c49a2
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/COPYING
@@ -0,0 +1,31 @@
+ LICENSE TERMS
+
+Copyright (c) 2000, Dimitrios Souflis
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+Neither the name of Dimitrios Souflis nor the names of the
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/plug-ins/script-fu/tinyscheme/Makefile.am b/plug-ins/script-fu/tinyscheme/Makefile.am
new file mode 100644
index 0000000..49ce643
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/Makefile.am
@@ -0,0 +1,26 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CFLAGS = \
+ -DSTANDALONE=0 \
+ -DUSE_INTERFACE=1 \
+ -DUSE_MATH=1 \
+ -DUSE_ASCII_NAMES=0 \
+ -DUSE_STRLWR=0 \
+ -I$(top_srcdir) \
+ $(GLIB_CFLAGS)
+
+noinst_LIBRARIES = libtinyscheme.a
+
+libtinyscheme_a_SOURCES = \
+ scheme.c \
+ opdefines.h \
+ scheme-private.h \
+ scheme.h
+
+EXTRA_DIST = \
+ BUILDING \
+ CHANGES \
+ COPYING \
+ hack.txt \
+ Manual.txt \
+ MiniSCHEMETribute.txt
diff --git a/plug-ins/script-fu/tinyscheme/Makefile.in b/plug-ins/script-fu/tinyscheme/Makefile.in
new file mode 100644
index 0000000..c506dfe
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/Makefile.in
@@ -0,0 +1,924 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/script-fu/tinyscheme
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo " AR " $@;
+am__v_AR_1 =
+libtinyscheme_a_AR = $(AR) $(ARFLAGS)
+libtinyscheme_a_LIBADD =
+am_libtinyscheme_a_OBJECTS = scheme.$(OBJEXT)
+libtinyscheme_a_OBJECTS = $(am_libtinyscheme_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/scheme.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libtinyscheme_a_SOURCES)
+DIST_SOURCES = $(libtinyscheme_a_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp COPYING \
+ README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CFLAGS = \
+ -DSTANDALONE=0 \
+ -DUSE_INTERFACE=1 \
+ -DUSE_MATH=1 \
+ -DUSE_ASCII_NAMES=0 \
+ -DUSE_STRLWR=0 \
+ -I$(top_srcdir) \
+ $(GLIB_CFLAGS)
+
+noinst_LIBRARIES = libtinyscheme.a
+libtinyscheme_a_SOURCES = \
+ scheme.c \
+ opdefines.h \
+ scheme-private.h \
+ scheme.h
+
+EXTRA_DIST = \
+ BUILDING \
+ CHANGES \
+ COPYING \
+ hack.txt \
+ Manual.txt \
+ MiniSCHEMETribute.txt
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/script-fu/tinyscheme/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/script-fu/tinyscheme/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libtinyscheme.a: $(libtinyscheme_a_OBJECTS) $(libtinyscheme_a_DEPENDENCIES) $(EXTRA_libtinyscheme_a_DEPENDENCIES)
+ $(AM_V_at)-rm -f libtinyscheme.a
+ $(AM_V_AR)$(libtinyscheme_a_AR) libtinyscheme.a $(libtinyscheme_a_OBJECTS) $(libtinyscheme_a_LIBADD)
+ $(AM_V_at)$(RANLIB) libtinyscheme.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scheme.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/scheme.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/scheme.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/script-fu/tinyscheme/Manual.txt b/plug-ins/script-fu/tinyscheme/Manual.txt
new file mode 100644
index 0000000..7ef62e9
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/Manual.txt
@@ -0,0 +1,452 @@
+
+
+ TinySCHEME Version 1.41
+
+ "Safe if used as prescribed"
+ -- Philip K. Dick, "Ubik"
+
+This software is open source, covered by a BSD-style license.
+Please read accompanying file COPYING.
+-------------------------------------------------------------------------------
+
+ This Scheme interpreter is based on MiniSCHEME version 0.85k4
+ (see miniscm.tar.gz in the Scheme Repository)
+ Original credits in file MiniSCHEMETribute.txt.
+
+ D. Souflis (dsouflis@acm.org)
+
+-------------------------------------------------------------------------------
+ What is TinyScheme?
+ -------------------
+
+ TinyScheme is a lightweight Scheme interpreter that implements as large
+ a subset of R5RS as was possible without getting very large and
+ complicated. It is meant to be used as an embedded scripting interpreter
+ for other programs. As such, it does not offer IDEs or extensive toolkits
+ although it does sport a small top-level loop, included conditionally.
+ A lot of functionality in TinyScheme is included conditionally, to allow
+ developers freedom in balancing features and footprint.
+
+ As an embedded interpreter, it allows multiple interpreter states to
+ coexist in the same program, without any interference between them.
+ Programmatically, foreign functions in C can be added and values
+ can be defined in the Scheme environment. Being a quite small program,
+ it is easy to comprehend, get to grips with, and use.
+
+ Known bugs
+ ----------
+
+ TinyScheme is known to misbehave when memory is exhausted.
+
+
+ Things that keep missing, or that need fixing
+ ---------------------------------------------
+
+ There are no hygienic macros. No rational or
+ complex numbers. No unwind-protect and call-with-values.
+
+ Maybe (a subset of) SLIB will work with TinySCHEME...
+
+ Decent debugging facilities are missing. Only tracing is supported
+ natively.
+
+
+ Scheme Reference
+ ----------------
+
+ If something seems to be missing, please refer to the code and
+ "init.scm", since some are library functions. Refer to the MiniSCHEME
+ readme as a last resort.
+
+ Environments
+ (interaction-environment)
+ See R5RS. In TinySCHEME, immutable list of association lists.
+
+ (current-environment)
+ The environment in effect at the time of the call. An example of its
+ use and its utility can be found in the sample code that implements
+ packages in "init.scm":
+
+ (macro (package form)
+ `(apply (lambda ()
+ ,@(cdr form)
+ (current-environment))))
+
+ The environment containing the (local) definitions inside the closure
+ is returned as an immutable value.
+
+ (defined? <symbol>) (defined? <symbol> <environment>)
+ Checks whether the given symbol is defined in the current (or given)
+ environment.
+
+ Symbols
+ (gensym)
+ Returns a new interned symbol each time. Will probably move to the
+ library when string->symbol is implemented.
+
+ Directives
+ (gc)
+ Performs garbage collection immediately.
+
+ (gcverbose) (gcverbose <bool>)
+ The argument (defaulting to #t) controls whether GC produces
+ visible outcome.
+
+ (quit) (quit <num>)
+ Stops the interpreter and sets the 'retcode' internal field (defaults
+ to 0). When standalone, 'retcode' is returned as exit code to the OS.
+
+ (tracing <num>)
+ 1, turns on tracing. 0 turns it off. (Only when USE_TRACING is 1).
+
+ Mathematical functions
+ Since rationals and complexes are absent, the respective functions
+ are also missing.
+ Supported: exp, log, sin, cos, tan, asin, acos, atan, floor, ceiling,
+ trunc, round and also sqrt and expt when USE_MATH=1.
+ Number-theoretical quotient, remainder and modulo, gcd, lcm.
+ Library: exact?, inexact?, odd?, even?, zero?, positive?, negative?,
+ exact->inexact. inexact->exact is a core function.
+
+ Type predicates
+ boolean?,eof-object?,symbol?,number?,string?,integer?,real?,list?,null?,
+ char?,port?,input-port?,output-port?,procedure?,pair?,environment?',
+ vector?. Also closure?, macro?.
+
+ Types
+ Types supported:
+
+ Numbers (integers and reals)
+ Symbols
+ Pairs
+ Strings
+ Characters
+ Ports
+ Eof object
+ Environments
+ Vectors
+
+ Literals
+ String literals can contain escaped quotes \" as usual, but also
+ \n, \r, \t, \xDD (hex representations) and \DDD (octal representations).
+ Note also that it is possible to include literal newlines in string
+ literals, e.g.
+
+ (define s "String with newline here
+ and here
+ that can function like a HERE-string")
+
+ Character literals contain #\space and #\newline and are supplemented
+ with #\return and #\tab, with obvious meanings. Hex character
+ representations are allowed (e.g. #\x20 is #\space).
+ When USE_ASCII_NAMES is defined, various control characters can be
+ referred to by their ASCII name.
+ 0 #\nul 17 #\dc1
+ 1 #\soh 18 #\dc2
+ 2 #\stx 19 #\dc3
+ 3 #\etx 20 #\dc4
+ 4 #\eot 21 #\nak
+ 5 #\enq 22 #\syn
+ 6 #\ack 23 #\etv
+ 7 #\bel 24 #\can
+ 8 #\bs 25 #\em
+ 9 #\ht 26 #\sub
+ 10 #\lf 27 #\esc
+ 11 #\vt 28 #\fs
+ 12 #\ff 29 #\gs
+ 13 #\cr 30 #\rs
+ 14 #\so 31 #\us
+ 15 #\si
+ 16 #\dle 127 #\del
+
+ Numeric literals support #x #o #b and #d. Flonums are currently read only
+ in decimal notation. Full grammar will be supported soon.
+
+ Quote, quasiquote etc.
+ As usual.
+
+ Immutable values
+ Immutable pairs cannot be modified by set-car! and set-cdr!.
+ Immutable strings cannot be modified via string-set!
+
+ I/O
+ As per R5RS, plus String Ports (see below).
+ current-input-port, current-output-port,
+ close-input-port, close-output-port, input-port?, output-port?,
+ open-input-file, open-output-file.
+ read, write, display, newline, write-char, read-char, peek-char.
+ char-ready? returns #t only for string ports, because there is no
+ portable way in stdio to determine if a character is available.
+ Also open-input-output-file, set-input-port, set-output-port (not R5RS)
+ Library: call-with-input-file, call-with-output-file,
+ with-input-from-file, with-output-from-file and
+ with-input-output-from-to-files, close-port and input-output-port?
+ (not R5RS).
+ String Ports: open-input-string, open-output-string, get-output-string,
+ open-input-output-string. Strings can be used with I/O routines.
+
+ Vectors
+ make-vector, vector, vector-length, vector-ref, vector-set!, list->vector,
+ vector-fill!, vector->list, vector-equal? (auxiliary function, not R5RS)
+
+ Strings
+ string, make-string, list->string, string-length, string-ref, string-set!,
+ substring, string->list, string-fill!, string-append, string-copy.
+ string=?, string<?, string>?, string>?, string<=?, string>=?.
+ (No string-ci*? yet). string->number, number->string. Also atom->string,
+ string->atom (not R5RS).
+
+ Symbols
+ symbol->string, string->symbol
+
+ Characters
+ integer->char, char->integer.
+ char=?, char<?, char>?, char<=?, char>=?.
+ (No char-ci*?)
+
+ Pairs & Lists
+ cons, car, cdr, list, length, map, for-each, foldr, list-tail,
+ list-ref, last-pair, reverse, append.
+ Also member, memq, memv, based on generic-member, assoc, assq, assv
+ based on generic-assoc.
+
+ Streams
+ head, tail, cons-stream
+
+ Control features
+ Apart from procedure?, also macro? and closure?
+ map, for-each, force, delay, call-with-current-continuation (or call/cc),
+ eval, apply. 'Forcing' a value that is not a promise produces the value.
+ There is no call-with-values, values, nor dynamic-wind. Dynamic-wind in
+ the presence of continuations would require support from the abstract
+ machine itself.
+
+ Property lists
+ TinyScheme inherited from MiniScheme property lists for symbols.
+ put, get.
+
+ Dynamically-loaded extensions
+ (load-extension <filename without extension>)
+ Loads a DLL declaring foreign procedures. On Unix/Linux, one can make use
+ of the ld.so.conf file or the LD_RUN_PATH system variable in order to place
+ the library in a directory other than the current one. Please refer to the
+ appropriate 'man' page.
+
+ Esoteric procedures
+ (oblist)
+ Returns the oblist, an immutable list of all the symbols.
+
+ (macro-expand <form>)
+ Returns the expanded form of the macro call denoted by the argument
+
+ (define-with-return (<procname> <args>...) <body>)
+ Like plain 'define', but makes the continuation available as 'return'
+ inside the procedure. Handy for imperative programs.
+
+ (new-segment <num>)
+ Allocates more memory segments.
+
+ defined?
+ See "Environments"
+
+ (get-closure-code <closure>)
+ Gets the code as scheme data.
+
+ (make-closure <code> <environment>)
+ Makes a new closure in the given environment.
+
+ Obsolete procedures
+ (print-width <object>)
+
+ Programmer's Reference
+ ----------------------
+
+ The interpreter state is initialized with "scheme_init".
+ Custom memory allocation routines can be installed with an alternate
+ initialization function: "scheme_init_custom_alloc".
+ Files can be loaded with "scheme_load_file". Strings containing Scheme
+ code can be loaded with "scheme_load_string". It is a good idea to
+ "scheme_load" init.scm before anything else.
+
+ External data for keeping external state (of use to foreign functions)
+ can be installed with "scheme_set_external_data".
+ Foreign functions are installed with "assign_foreign". Additional
+ definitions can be added to the interpreter state, with "scheme_define"
+ (this is the way HTTP header data and HTML form data are passed to the
+ Scheme script in the Altera SQL Server). If you wish to define the
+ foreign function in a specific environment (to enhance modularity),
+ use "assign_foreign_env".
+
+ The procedure "scheme_apply0" has been added with persistent scripts in
+ mind. Persistent scripts are loaded once, and every time they are needed
+ to produce HTTP output, appropriate data are passed through global
+ definitions and function "main" is called to do the job. One could
+ add easily "scheme_apply1" etc.
+
+ The interpreter state should be deinitialized with "scheme_deinit".
+
+ DLLs containing foreign functions should define a function named
+ init_<base-name>. E.g. foo.dll should define init_foo, and bar.so
+ should define init_bar. This function should assign_foreign any foreign
+ function contained in the DLL.
+
+ The first dynamically loaded extension available for TinyScheme is
+ a regular expression library. Although it's by no means an
+ established standard, this library is supposed to be installed in
+ a directory mirroring its name under the TinyScheme location.
+
+
+ Foreign Functions
+ -----------------
+
+ The user can add foreign functions in C. For example, a function
+ that squares its argument:
+
+ pointer square(scheme *sc, pointer args) {
+ if(args!=sc->NIL) {
+ if(sc->isnumber(sc->pair_car(args))) {
+ double v=sc->rvalue(sc->pair_car(args));
+ return sc->mk_real(sc,v*v);
+ }
+ }
+ return sc->NIL;
+ }
+
+ Foreign functions are now defined as closures:
+
+ sc->interface->scheme_define(
+ sc,
+ sc->global_env,
+ sc->interface->mk_symbol(sc,"square"),
+ sc->interface->mk_foreign_func(sc, square));
+
+
+ Foreign functions can use the external data in the "scheme" struct
+ to implement any kind of external state.
+
+ External data are set with the following function:
+ void scheme_set_external_data(scheme *sc, void *p);
+
+ As of v.1.17, the canonical way for a foreign function in a DLL to
+ manipulate Scheme data is using the function pointers in sc->interface.
+
+ Standalone
+ ----------
+
+ Usage: tinyscheme -?
+ or: tinyscheme [<file1> <file2> ...]
+ followed by
+ -1 <file> [<arg1> <arg2> ...]
+ -c <Scheme commands> [<arg1> <arg2> ...]
+ assuming that the executable is named tinyscheme.
+
+ Use - in the place of a filename to denote stdin.
+ The -1 flag is meant for #! usage in shell scripts. If you specify
+ #! /somewhere/tinyscheme -1
+ then tinyscheme will be called to process the file. For example, the
+ following script echoes the Scheme list of its arguments.
+
+ #! /somewhere/tinyscheme -1
+ (display *args*)
+
+ The -c flag permits execution of arbitrary Scheme code.
+
+
+ Error Handling
+ --------------
+
+ Errors are recovered from without damage. The user can install his
+ own handler for system errors, by defining *error-hook*. Defining
+ to '() gives the default behavior, which is equivalent to "error".
+ USE_ERROR_HOOK must be defined.
+
+ A simple exception handling mechanism can be found in "init.scm".
+ A new syntactic form is introduced:
+
+ (catch <expr returned exceptionally>
+ <expr1> <expr2> ... <exprN>)
+
+ "Catch" establishes a scope spanning multiple call-frames
+ until another "catch" is encountered.
+
+ Exceptions are thrown with:
+
+ (throw "message")
+
+ If used outside a (catch ...), reverts to (error "message").
+
+ Example of use:
+
+ (define (foo x) (write x) (newline) (/ x 0))
+
+ (catch (begin (display "Error!\n") 0)
+ (write "Before foo ... ")
+ (foo 5)
+ (write "After foo"))
+
+ The exception mechanism can be used even by system errors, by
+
+ (define *error-hook* throw)
+
+ which makes use of the error hook described above.
+
+ If necessary, the user can devise his own exception mechanism with
+ tagged exceptions etc.
+
+
+ Reader extensions
+ -----------------
+
+ When encountering an unknown character after '#', the user-specified
+ procedure *sharp-hook* (if any), is called to read the expression.
+ This can be used to extend the reader to handle user-defined constants
+ or whatever. It should be a procedure without arguments, reading from
+ the current input port (which will be the load-port).
+
+
+ Colon Qualifiers - Packages
+ ---------------------------
+
+ When USE_COLON_HOOK=1:
+ The lexer now recognizes the construction <qualifier>::<symbol> and
+ transforms it in the following manner (T is the transformation function):
+
+ T(<qualifier>::<symbol>) = (*colon-hook* 'T(<symbol>) <qualifier>)
+
+ where <qualifier> is a symbol not containing any double-colons.
+
+ As the definition is recursive, qualifiers can be nested.
+ The user can define his own *colon-hook*, to handle qualified names.
+ By default, "init.scm" defines *colon-hook* as EVAL. Consequently,
+ the qualifier must denote a Scheme environment, such as one returned
+ by (interaction-environment). "Init.scm" defines a new syntantic form,
+ PACKAGE, as a simple example. It is used like this:
+
+ (define toto
+ (package
+ (define foo 1)
+ (define bar +)))
+
+ foo ==> Error, "foo" undefined
+ (eval 'foo) ==> Error, "foo" undefined
+ (eval 'foo toto) ==> 1
+ toto::foo ==> 1
+ ((eval 'bar toto) 2 (eval 'foo toto)) ==> 3
+ (toto::bar 2 toto::foo) ==> 3
+ (eval (bar 2 foo) toto) ==> 3
+
+ If the user installs another package infrastructure, he must define
+ a new 'package' procedure or macro to retain compatibility with supplied
+ code.
+
+ Note: Older versions used ':' as a qualifier. Unfortunately, the use
+ of ':' as a pseudo-qualifier in existing code (i.e. SLIB) essentially
+ precludes its use as a real qualifier.
+
+
+
+
+
+
+
+
diff --git a/plug-ins/script-fu/tinyscheme/MiniSCHEMETribute.txt b/plug-ins/script-fu/tinyscheme/MiniSCHEMETribute.txt
new file mode 100644
index 0000000..185f0ec
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/MiniSCHEMETribute.txt
@@ -0,0 +1,88 @@
+ TinyScheme would not exist if it wasn't for MiniScheme. I had just
+ written the HTTP server for Ovrimos SQL Server, and I was lamenting the
+ lack of a scripting language. Server-side Javascript would have been the
+ preferred solution, had there been a Javascript interpreter I could
+ lay my hands on. But there weren't. Perl would have been another solution,
+ but it was probably ten times bigger that the program it was supposed to
+ be embedded in. There would also be thorny licencing issues.
+
+ So, the obvious thing to do was find a truly small interpreter. Forth
+ was a language I had once quasi-implemented, but the difficulty of
+ handling dynamic data and the weirdness of the language put me off. I then
+ looked around for a LISP interpreter, the next thing I knew was easy to
+ implement. Alas, the LeLisp I knew from my days in UPMC (Universite Pierre
+ et Marie Curie) had given way to Common Lisp, a megalith of a language!
+ Then my search lead me to Scheme, a language I knew was very orthogonal
+ and clean. When I found Mini-Scheme, a single C file of some 2400 loc, I
+ fell in love with it! What if it lacked floating-point numbers and
+ strings! The rest, as they say, is history.
+
+ Below are the original credits. Don't email Akira KIDA, the address has
+ changed.
+
+ ---------- Mini-Scheme Interpreter Version 0.85 ----------
+
+ coded by Atsushi Moriwaki (11/5/1989)
+
+ E-MAIL : moriwaki@kurims.kurims.kyoto-u.ac.jp
+
+ THIS SOFTWARE IS IN THE PUBLIC DOMAIN
+ ------------------------------------
+ This software is completely free to copy, modify and/or re-distribute.
+ But I would appreciate it if you left my name on the code as the author.
+
+ This version has been modified by R.C. Secrist.
+
+ Mini-Scheme is now maintained by Akira KIDA.
+
+ This is a revised and modified version by Akira KIDA.
+ current version is 0.85k4 (15 May 1994)
+
+ Please send suggestions, bug reports and/or requests to:
+ <SDI00379@niftyserve.or.jp>
+
+
+ Features compared to MiniSCHEME
+ -------------------------------
+
+ All code is now reentrant. Interpreter state is held in a 'scheme'
+ struct, and many interpreters can coexist in the same program, possibly
+ in different threads. The user can specify user-defined memory allocation
+ primitives. (see "Programmer's Reference")
+
+ The reader is more consistent.
+
+ Strings, characters and flonums are supported. (see "Types")
+
+ Files being loaded can be nested up to some depth.
+
+ R5RS I/O is there, plus String Ports. (see "Scheme Reference","I/O")
+
+ Vectors exist.
+
+ As a standalone application, it supports command-line arguments.
+ (see "Standalone")
+
+ Running out of memory is now handled.
+
+ The user can add foreign functions in C. (see "Foreign Functions")
+
+ The code has been changed slightly, core functions have been moved
+ to the library, behavior has been aligned with R5RS etc.
+
+ Support has been added for user-defined error recovery.
+ (see "Error Handling")
+
+ Support has been added for modular programming.
+ (see "Colon Qualifiers - Packages")
+
+ To enable this, EVAL has changed internally, and can
+ now take two arguments, as per R5RS. Environments are supported.
+ (see "Colon Qualifiers - Packages")
+
+ Promises are now evaluated once only.
+
+ (macro (foo form) ...) is now equivalent to (macro foo (lambda(form) ...))
+
+ The reader can be extended using new #-expressions
+ (see "Reader extensions")
diff --git a/plug-ins/script-fu/tinyscheme/README b/plug-ins/script-fu/tinyscheme/README
new file mode 100644
index 0000000..4411119
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/README
@@ -0,0 +1,14 @@
+This directory contains a version of TinyScheme which has been modified
+to support UTF-8 coded strings. The strings stored in a data cell are
+expected to be in UTF-8 format. This allows the continued use of gchar
+pointers to pass around the strings. Processing the strings will require
+conversion to unicode at times depending on the specific operation that
+needs to be done on the UTF-8 coded strings.
+
+The string length value stored in a data cell is the length in bytes of that
+string including the terminating NUL.
+
+Routines that want a string length for a UTF-8 coded string will be passed
+the number of characters and not the number of bytes. If the number of bytes
+is needed, the normal call to strlen() will work.
+
diff --git a/plug-ins/script-fu/tinyscheme/hack.txt b/plug-ins/script-fu/tinyscheme/hack.txt
new file mode 100644
index 0000000..6aba7a7
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/hack.txt
@@ -0,0 +1,233 @@
+
+ How to hack TinyScheme
+ ----------------------
+
+ TinyScheme is easy to learn and modify. It is structured like a
+ meta-interpreter, only it is written in C. All data are Scheme
+ objects, which facilitates both understanding/modifying the
+ code and reifying the interpreter workings.
+
+ In place of a dry description, we will pace through the addition
+ of a useful new datatype: garbage-collected memory blocks.
+ The interface will be:
+
+ (make-block <n> [<fill>]) makes a new block of the specified size
+ optionally filling it with a specified byte
+ (block? <obj>)
+ (block-length <block>)
+ (block-ref <block> <index>) retrieves byte at location
+ (block-set! <block> <index> <byte>) modifies byte at location
+
+ In the sequel, lines that begin with '>' denote lines to add to the
+ code. Lines that begin with '|' are just citations of existing code.
+ Lines that begin with X are deleted.
+
+ First of all, we need to assign a typeid to our new type. Typeids
+ in TinyScheme are small integers declared in an enum, very close to
+ the top of scheme.c; it begins with T_STRING. Add a new one before the
+ end, call it T_MEMBLOCK. Adjust T_LAST_SYSTEM_TYPE.
+
+| T_ENVIRONMENT=14,
+X T_LAST_SYSTEM_TYPE=14
+> T_MEMBLOCK=15,
+> T_LAST_SYSTEM_TYPE=15
+| };
+
+ Then, some helper macros would be useful. Go to where is_string() and
+ the rest are defined and define:
+
+> int is_memblock(pointer p) { return (type(p)==T_MEMBLOCK); }
+
+ This actually is a function, because it is meant to be exported by
+ scheme.h. If no foreign function will ever manipulate a memory block,
+ you can instead define it as a macro
+
+> #define is_memblock(p) (type(p)==T_MEMBLOCK)
+
+ Then we make space for the new type in the main data structure:
+ struct cell. As it happens, the _string part of the union _object
+ (that is used to hold character strings) has two fields that suit us:
+
+| struct {
+| char *_svalue;
+| int _keynum;
+| } _string;
+
+ We can use _svalue to hold the actual pointer and _keynum to hold its
+ length. If we couldn't reuse existing fields, we could always add other
+ alternatives in union _object.
+
+ We then proceed to write the function that actually makes a new block.
+ For conformance reasons, we name it mk_memblock
+
+> static pointer mk_memblock(scheme *sc, int len, char fill) {
+> pointer x;
+> char *p=(char*)sc->malloc(len);
+>
+> if(p==0) {
+> return sc->NIL;
+> }
+> x = get_cell(sc, sc->NIL, sc->NIL);
+>
+> typeflag(x) = T_MEMBLOCK|T_ATOM;
+> strvalue(x)=p;
+> keynum(x)=len;
+> memset(p,fill,len);
+> return (x);
+> }
+
+ The memory used by the MEMBLOCK will have to be freed when the cell
+ is reclaimed during garbage collection. There is a placeholder for
+ that staff, function finalize_cell(), currently handling strings only.
+
+| static void finalize_cell(scheme *sc, pointer a) {
+| if(is_string(a)) {
+| sc->free(strvalue(a));
+> else if(is_memblock(a)) {
+> sc->free(strvalue(a));
+| } else if(is_port(a)) {
+
+ There are no MEMBLOCK literals, so we don't concern ourselves with
+ the READER part (yet!). We must cater to the PRINTER, though. We
+ add one case more in atom2str().
+
+| } else if (is_foreign(l)) {
+| p = sc->strbuff;
+| snprintf(p,STRBUFFSIZE,"#<FOREIGN PROCEDURE %ld>", procnum(l));
+> } else if (ismemblock(l)) {
+> p = "#<MEMBLOCK>";
+| } else if (is_continuation(l)) {
+| p = "#<CONTINUATION>";
+| } else {
+
+ Whenever a MEMBLOCK is displayed, it will look like that.
+
+ Now, we must add the interface functions: constructor, predicate,
+ accessor, modifier. We must in fact create new op-codes for the
+ virtual machine underlying TinyScheme. Since version 1.30, TinyScheme
+ uses macros and a single source text to keep the enums and the
+ dispatch table in sync. That's where the op-codes are declared. Note
+ that the opdefines.h file uses unusually long lines to accommodate
+ all the information; adjust your editor to handle this. The file has
+ six columns: A to Z. they contain:
+ - Column A is the name of a routine to handle the scheme function.
+ - Column B is the name the scheme function.
+ - Columns C and D are the minimum and maximum number of arguments
+ that are accepted by the scheme function.
+ - Column E is a set of flags that are used when the interpreter
+ verifies that the passed parameters are of the correct type.
+ - Column F is used to create a set of enums. The enum is used in a
+ switch in the routine listed in column A to get to the code that
+ does the work needed for the scheme function.
+ For reasons of cohesion, we add the new op-codes right after those
+ for vectors:
+
+| _OP_DEF(opexe_2, "vector-set!", 3, 3, TST_VECTOR TST_NATURAL TST_ANY, OP_VECSET )
+> _OP_DEF(opexe_2, "make-block", 1, 2, TST_NATURAL TST_CHAR, OP_MKBLOCK )
+> _OP_DEF(opexe_2, "block-length", 1, 1, T_MEMBLOCK, OP_BLOCKLEN )
+> _OP_DEF(opexe_2, "block-ref", 2, 2, T_MEMBLOCK TST_NATURAL, OP_BLOCKREF )
+> _OP_DEF(opexe_2, "block-set!", 1, 1, T_MEMBLOCK TST_NATURAL TST_CHAR, OP_BLOCKSET )
+| _OP_DEF(opexe_3, "not", 1, 1, TST_NONE, OP_NOT )
+
+ We add the predicate along the other predicates:
+
+| _OP_DEF(opexe_3, "vector?", 1, 1, TST_ANY, OP_VECTORP )
+> _OP_DEF(opexe_3, "block?", 1, 1, TST_ANY, OP_BLOCKP )
+| _OP_DEF(opexe_3, "eq?", 2, 2, TST_ANY, OP_EQ )
+
+ All that remains is to write the actual processing in opexe_2, right
+ after OP_VECSET.
+
+> case OP_MKBLOCK: { /* make-block */
+> int fill=0;
+> int len;
+>
+> if(!isnumber(car(sc->args))) {
+> Error_1(sc,"make-block: not a number:",car(sc->args));
+> }
+> len=ivalue(car(sc->args));
+> if(len<=0) {
+> Error_1(sc,"make-block: not positive:",car(sc->args));
+> }
+>
+> if(cdr(sc->args)!=sc->NIL) {
+> if(!isnumber(cadr(sc->args)) || ivalue(cadr(sc->args))<0) {
+> Error_1(sc,"make-block: not a positive number:",cadr(sc->args));
+> }
+> fill=charvalue(cadr(sc->args))%255;
+> }
+> s_return(sc,mk_memblock(sc,len,(char)fill));
+> }
+>
+> case OP_BLOCKLEN: /* block-length */
+> if(!ismemblock(car(sc->args))) {
+> Error_1(sc,"block-length: not a memory block:",car(sc->args));
+> }
+> s_return(sc,mk_integer(sc,keynum(car(sc->args))));
+>
+> case OP_BLOCKREF: { /* block-ref */
+> char *str;
+> int index;
+>
+> if(!ismemblock(car(sc->args))) {
+> Error_1(sc,"block-ref: not a memory block:",car(sc->args));
+> }
+> str=strvalue(car(sc->args));
+>
+> if(cdr(sc->args)==sc->NIL) {
+> Error_0(sc,"block-ref: needs two arguments");
+> }
+> if(!isnumber(cadr(sc->args))) {
+> Error_1(sc,"block-ref: not a number:",cadr(sc->args));
+> }
+> index=ivalue(cadr(sc->args));
+>
+> if(index<0 || index>=keynum(car(sc->args))) {
+> Error_1(sc,"block-ref: out of bounds:",cadr(sc->args));
+> }
+>
+> s_return(sc,mk_integer(sc,str[index]));
+> }
+>
+> case OP_BLOCKSET: { /* block-set! */
+> char *str;
+> int index;
+> int c;
+>
+> if(!ismemblock(car(sc->args))) {
+> Error_1(sc,"block-set!: not a memory block:",car(sc->args));
+> }
+> if(isimmutable(car(sc->args))) {
+> Error_1(sc,"block-set!: unable to alter immutable memory block:",car(sc->args));
+> }
+> str=strvalue(car(sc->args));
+>
+> if(cdr(sc->args)==sc->NIL) {
+> Error_0(sc,"block-set!: needs three arguments");
+> }
+> if(!isnumber(cadr(sc->args))) {
+> Error_1(sc,"block-set!: not a number:",cadr(sc->args));
+> }
+> index=ivalue(cadr(sc->args));
+> if(index<0 || index>=keynum(car(sc->args))) {
+> Error_1(sc,"block-set!: out of bounds:",cadr(sc->args));
+> }
+>
+> if(cddr(sc->args)==sc->NIL) {
+> Error_0(sc,"block-set!: needs three arguments");
+> }
+> if(!isinteger(caddr(sc->args))) {
+> Error_1(sc,"block-set!: not an integer:",caddr(sc->args));
+> }
+> c=ivalue(caddr(sc->args))%255;
+>
+> str[index]=(char)c;
+> s_return(sc,car(sc->args));
+> }
+
+ Same for the predicate in opexe_3.
+
+| case OP_VECTORP: /* vector? */
+| s_retbool(isvector(car(sc->args)));
+> case OP_BLOCKP: /* block? */
+> s_retbool(ismemblock(car(sc->args)));
diff --git a/plug-ins/script-fu/tinyscheme/opdefines.h b/plug-ins/script-fu/tinyscheme/opdefines.h
new file mode 100644
index 0000000..ceb4d0e
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/opdefines.h
@@ -0,0 +1,195 @@
+ _OP_DEF(opexe_0, "load", 1, 1, TST_STRING, OP_LOAD )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_T0LVL )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_T1LVL )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_READ_INTERNAL )
+ _OP_DEF(opexe_0, "gensym", 0, 0, 0, OP_GENSYM )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_VALUEPRINT )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_EVAL )
+#if USE_TRACING
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_REAL_EVAL )
+#endif
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_E0ARGS )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_E1ARGS )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_APPLY )
+#if USE_TRACING
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_REAL_APPLY )
+ _OP_DEF(opexe_0, "tracing", 1, 1, TST_NATURAL, OP_TRACING )
+#endif
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_DOMACRO )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LAMBDA )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LAMBDA1 )
+ _OP_DEF(opexe_0, "make-closure", 1, 2, TST_PAIR TST_ENVIRONMENT, OP_MKCLOSURE )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_QUOTE )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_DEF0 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_DEF1 )
+ _OP_DEF(opexe_0, "defined?", 1, 2, TST_SYMBOL TST_ENVIRONMENT, OP_DEFP )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_BEGIN )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_IF0 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_IF1 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_SET0 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_SET1 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET0 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET1 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET2 )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET0AST )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET1AST )
+ _OP_DEF(opexe_0, 0, 0, 0, 0, OP_LET2AST )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_LET0REC )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_LET1REC )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_LET2REC )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_COND0 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_COND1 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_DELAY )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_AND0 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_AND1 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_OR0 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_OR1 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_C0STREAM )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_C1STREAM )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_MACRO0 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_MACRO1 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_CASE0 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_CASE1 )
+ _OP_DEF(opexe_1, 0, 0, 0, 0, OP_CASE2 )
+ _OP_DEF(opexe_1, "eval", 1, 2, TST_ANY TST_ENVIRONMENT, OP_PEVAL )
+ _OP_DEF(opexe_1, "apply", 1, INF_ARG, TST_NONE, OP_PAPPLY )
+ _OP_DEF(opexe_1, "call-with-current-continuation", 1, 1, TST_NONE, OP_CONTINUATION )
+#if USE_MATH
+ _OP_DEF(opexe_2, "inexact->exact", 1, 1, TST_NUMBER, OP_INEX2EX )
+ _OP_DEF(opexe_2, "exp", 1, 1, TST_NUMBER, OP_EXP )
+ _OP_DEF(opexe_2, "log", 1, 1, TST_NUMBER, OP_LOG )
+ _OP_DEF(opexe_2, "sin", 1, 1, TST_NUMBER, OP_SIN )
+ _OP_DEF(opexe_2, "cos", 1, 1, TST_NUMBER, OP_COS )
+ _OP_DEF(opexe_2, "tan", 1, 1, TST_NUMBER, OP_TAN )
+ _OP_DEF(opexe_2, "asin", 1, 1, TST_NUMBER, OP_ASIN )
+ _OP_DEF(opexe_2, "acos", 1, 1, TST_NUMBER, OP_ACOS )
+ _OP_DEF(opexe_2, "atan", 1, 2, TST_NUMBER, OP_ATAN )
+ _OP_DEF(opexe_2, "sqrt", 1, 1, TST_NUMBER, OP_SQRT )
+ _OP_DEF(opexe_2, "expt", 2, 2, TST_NUMBER, OP_EXPT )
+ _OP_DEF(opexe_2, "floor", 1, 1, TST_NUMBER, OP_FLOOR )
+ _OP_DEF(opexe_2, "ceiling", 1, 1, TST_NUMBER, OP_CEILING )
+ _OP_DEF(opexe_2, "truncate", 1, 1, TST_NUMBER, OP_TRUNCATE )
+ _OP_DEF(opexe_2, "round", 1, 1, TST_NUMBER, OP_ROUND )
+#endif
+ _OP_DEF(opexe_2, "+", 0, INF_ARG, TST_NUMBER, OP_ADD )
+ _OP_DEF(opexe_2, "-", 1, INF_ARG, TST_NUMBER, OP_SUB )
+ _OP_DEF(opexe_2, "*", 0, INF_ARG, TST_NUMBER, OP_MUL )
+ _OP_DEF(opexe_2, "/", 1, INF_ARG, TST_NUMBER, OP_DIV )
+ _OP_DEF(opexe_2, "quotient", 1, INF_ARG, TST_INTEGER, OP_INTDIV )
+ _OP_DEF(opexe_2, "remainder", 2, 2, TST_INTEGER, OP_REM )
+ _OP_DEF(opexe_2, "modulo", 2, 2, TST_INTEGER, OP_MOD )
+ _OP_DEF(opexe_2, "car", 1, 1, TST_PAIR, OP_CAR )
+ _OP_DEF(opexe_2, "cdr", 1, 1, TST_PAIR, OP_CDR )
+ _OP_DEF(opexe_2, "cons", 2, 2, TST_NONE, OP_CONS )
+ _OP_DEF(opexe_2, "set-car!", 2, 2, TST_PAIR TST_ANY, OP_SETCAR )
+ _OP_DEF(opexe_2, "set-cdr!", 2, 2, TST_PAIR TST_ANY, OP_SETCDR )
+ _OP_DEF(opexe_2, "char->integer", 1, 1, TST_CHAR, OP_CHAR2INT )
+ _OP_DEF(opexe_2, "integer->char", 1, 1, TST_NATURAL, OP_INT2CHAR )
+ _OP_DEF(opexe_2, "char-upcase", 1, 1, TST_CHAR, OP_CHARUPCASE )
+ _OP_DEF(opexe_2, "char-downcase", 1, 1, TST_CHAR, OP_CHARDNCASE )
+ _OP_DEF(opexe_2, "symbol->string", 1, 1, TST_SYMBOL, OP_SYM2STR )
+ _OP_DEF(opexe_2, "atom->string", 1, 2, TST_ANY TST_NATURAL, OP_ATOM2STR )
+ _OP_DEF(opexe_2, "string->symbol", 1, 1, TST_STRING, OP_STR2SYM )
+ _OP_DEF(opexe_2, "string->atom", 1, 2, TST_STRING TST_NATURAL, OP_STR2ATOM )
+ _OP_DEF(opexe_2, "make-string", 1, 2, TST_NATURAL TST_CHAR, OP_MKSTRING )
+ _OP_DEF(opexe_2, "string-length", 1, 1, TST_STRING, OP_STRLEN )
+ _OP_DEF(opexe_2, "string-ref", 2, 2, TST_STRING TST_NATURAL, OP_STRREF )
+ _OP_DEF(opexe_2, "string-set!", 3, 3, TST_STRING TST_NATURAL TST_CHAR, OP_STRSET )
+ _OP_DEF(opexe_2, "string-append", 0, INF_ARG, TST_STRING, OP_STRAPPEND )
+ _OP_DEF(opexe_2, "substring", 2, 3, TST_STRING TST_NATURAL, OP_SUBSTR )
+ _OP_DEF(opexe_2, "vector", 0, INF_ARG, TST_NONE, OP_VECTOR )
+ _OP_DEF(opexe_2, "make-vector", 1, 2, TST_NATURAL TST_ANY, OP_MKVECTOR )
+ _OP_DEF(opexe_2, "vector-length", 1, 1, TST_VECTOR, OP_VECLEN )
+ _OP_DEF(opexe_2, "vector-ref", 2, 2, TST_VECTOR TST_NATURAL, OP_VECREF )
+ _OP_DEF(opexe_2, "vector-set!", 3, 3, TST_VECTOR TST_NATURAL TST_ANY, OP_VECSET )
+ _OP_DEF(opexe_3, "not", 1, 1, TST_NONE, OP_NOT )
+ _OP_DEF(opexe_3, "boolean?", 1, 1, TST_NONE, OP_BOOLP )
+ _OP_DEF(opexe_3, "eof-object?", 1, 1, TST_NONE, OP_EOFOBJP )
+ _OP_DEF(opexe_3, "null?", 1, 1, TST_NONE, OP_NULLP )
+ _OP_DEF(opexe_3, "=", 2, INF_ARG, TST_NUMBER, OP_NUMEQ )
+ _OP_DEF(opexe_3, "<", 2, INF_ARG, TST_NUMBER, OP_LESS )
+ _OP_DEF(opexe_3, ">", 2, INF_ARG, TST_NUMBER, OP_GRE )
+ _OP_DEF(opexe_3, "<=", 2, INF_ARG, TST_NUMBER, OP_LEQ )
+ _OP_DEF(opexe_3, ">=", 2, INF_ARG, TST_NUMBER, OP_GEQ )
+ _OP_DEF(opexe_3, "symbol?", 1, 1, TST_ANY, OP_SYMBOLP )
+ _OP_DEF(opexe_3, "number?", 1, 1, TST_ANY, OP_NUMBERP )
+ _OP_DEF(opexe_3, "string?", 1, 1, TST_ANY, OP_STRINGP )
+ _OP_DEF(opexe_3, "integer?", 1, 1, TST_ANY, OP_INTEGERP )
+ _OP_DEF(opexe_3, "real?", 1, 1, TST_ANY, OP_REALP )
+ _OP_DEF(opexe_3, "char?", 1, 1, TST_ANY, OP_CHARP )
+#if USE_CHAR_CLASSIFIERS
+ _OP_DEF(opexe_3, "char-alphabetic?", 1, 1, TST_CHAR, OP_CHARAP )
+ _OP_DEF(opexe_3, "char-numeric?", 1, 1, TST_CHAR, OP_CHARNP )
+ _OP_DEF(opexe_3, "char-whitespace?", 1, 1, TST_CHAR, OP_CHARWP )
+ _OP_DEF(opexe_3, "char-upper-case?", 1, 1, TST_CHAR, OP_CHARUP )
+ _OP_DEF(opexe_3, "char-lower-case?", 1, 1, TST_CHAR, OP_CHARLP )
+#endif
+ _OP_DEF(opexe_3, "port?", 1, 1, TST_ANY, OP_PORTP )
+ _OP_DEF(opexe_3, "input-port?", 1, 1, TST_ANY, OP_INPORTP )
+ _OP_DEF(opexe_3, "output-port?", 1, 1, TST_ANY, OP_OUTPORTP )
+ _OP_DEF(opexe_3, "procedure?", 1, 1, TST_ANY, OP_PROCP )
+ _OP_DEF(opexe_3, "pair?", 1, 1, TST_ANY, OP_PAIRP )
+ _OP_DEF(opexe_3, "list?", 1, 1, TST_ANY, OP_LISTP )
+ _OP_DEF(opexe_3, "environment?", 1, 1, TST_ANY, OP_ENVP )
+ _OP_DEF(opexe_3, "vector?", 1, 1, TST_ANY, OP_VECTORP )
+ _OP_DEF(opexe_3, "eq?", 2, 2, TST_ANY, OP_EQ )
+ _OP_DEF(opexe_3, "eqv?", 2, 2, TST_ANY, OP_EQV )
+ _OP_DEF(opexe_4, "force", 1, 1, TST_ANY, OP_FORCE )
+ _OP_DEF(opexe_4, 0, 0, 0, 0, OP_SAVE_FORCED )
+ _OP_DEF(opexe_4, "write", 1, 2, TST_ANY TST_OUTPORT, OP_WRITE )
+ _OP_DEF(opexe_4, "write-char", 1, 2, TST_CHAR TST_OUTPORT, OP_WRITE_CHAR )
+ _OP_DEF(opexe_4, "display", 1, 2, TST_ANY TST_OUTPORT, OP_DISPLAY )
+ _OP_DEF(opexe_4, "newline", 0, 1, TST_OUTPORT, OP_NEWLINE )
+ _OP_DEF(opexe_4, "error", 1, INF_ARG, TST_NONE, OP_ERR0 )
+ _OP_DEF(opexe_4, 0, 0, 0, 0, OP_ERR1 )
+ _OP_DEF(opexe_4, "reverse", 1, 1, TST_LIST, OP_REVERSE )
+ _OP_DEF(opexe_4, "list*", 1, INF_ARG, TST_NONE, OP_LIST_STAR )
+ _OP_DEF(opexe_4, "append", 0, INF_ARG, TST_NONE, OP_APPEND )
+#if USE_PLIST
+ _OP_DEF(opexe_4, "put", 3, 3, TST_NONE, OP_PUT )
+ _OP_DEF(opexe_4, "get", 2, 2, TST_NONE, OP_GET )
+#endif
+ _OP_DEF(opexe_4, "quit", 0, 1, TST_NUMBER, OP_QUIT )
+ _OP_DEF(opexe_4, "gc", 0, 0, 0, OP_GC )
+ _OP_DEF(opexe_4, "gc-verbose", 0, 1, TST_NONE, OP_GCVERB )
+ _OP_DEF(opexe_4, "new-segment", 0, 1, TST_NUMBER, OP_NEWSEGMENT )
+ _OP_DEF(opexe_4, "oblist", 0, 0, 0, OP_OBLIST )
+ _OP_DEF(opexe_4, "current-input-port", 0, 0, 0, OP_CURR_INPORT )
+ _OP_DEF(opexe_4, "current-output-port", 0, 0, 0, OP_CURR_OUTPORT )
+ _OP_DEF(opexe_4, "open-input-file", 1, 1, TST_STRING, OP_OPEN_INFILE )
+ _OP_DEF(opexe_4, "open-output-file", 1, 1, TST_STRING, OP_OPEN_OUTFILE )
+ _OP_DEF(opexe_4, "open-input-output-file", 1, 1, TST_STRING, OP_OPEN_INOUTFILE )
+#if USE_STRING_PORTS
+ _OP_DEF(opexe_4, "open-input-string", 1, 1, TST_STRING, OP_OPEN_INSTRING )
+ _OP_DEF(opexe_4, "open-input-output-string", 1, 1, TST_STRING, OP_OPEN_INOUTSTRING )
+ _OP_DEF(opexe_4, "open-output-string", 0, 1, TST_STRING, OP_OPEN_OUTSTRING )
+ _OP_DEF(opexe_4, "get-output-string", 1, 1, TST_OUTPORT, OP_GET_OUTSTRING )
+#endif
+ _OP_DEF(opexe_4, "close-input-port", 1, 1, TST_INPORT, OP_CLOSE_INPORT )
+ _OP_DEF(opexe_4, "close-output-port", 1, 1, TST_OUTPORT, OP_CLOSE_OUTPORT )
+ _OP_DEF(opexe_4, "interaction-environment", 0, 0, 0, OP_INT_ENV )
+ _OP_DEF(opexe_4, "current-environment", 0, 0, 0, OP_CURR_ENV )
+ _OP_DEF(opexe_5, "read", 0, 1, TST_INPORT, OP_READ )
+ _OP_DEF(opexe_5, "read-char", 0, 1, TST_INPORT, OP_READ_CHAR )
+ _OP_DEF(opexe_5, "peek-char", 0, 1, TST_INPORT, OP_PEEK_CHAR )
+ _OP_DEF(opexe_5, "char-ready?", 0, 1, TST_INPORT, OP_CHAR_READY )
+ _OP_DEF(opexe_5, "set-input-port", 1, 1, TST_INPORT, OP_SET_INPORT )
+ _OP_DEF(opexe_5, "set-output-port", 1, 1, TST_OUTPORT, OP_SET_OUTPORT )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDSEXPR )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDLIST )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDDOT )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDQUOTE )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDQQUOTE )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDQQUOTEVEC )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDUNQUOTE )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDUQTSP )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_RDVEC )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_P0LIST )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_P1LIST )
+ _OP_DEF(opexe_5, 0, 0, 0, 0, OP_PVECFROM )
+ _OP_DEF(opexe_6, "length", 1, 1, TST_LIST, OP_LIST_LENGTH )
+ _OP_DEF(opexe_6, "assq", 2, 2, TST_NONE, OP_ASSQ )
+ _OP_DEF(opexe_6, "get-closure-code", 1, 1, TST_NONE, OP_GET_CLOSURE )
+ _OP_DEF(opexe_6, "closure?", 1, 1, TST_NONE, OP_CLOSUREP )
+ _OP_DEF(opexe_6, "macro?", 1, 1, TST_NONE, OP_MACROP )
+#undef _OP_DEF
diff --git a/plug-ins/script-fu/tinyscheme/scheme-private.h b/plug-ins/script-fu/tinyscheme/scheme-private.h
new file mode 100644
index 0000000..89840c2
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/scheme-private.h
@@ -0,0 +1,227 @@
+/* scheme-private.h */
+
+#ifndef _SCHEME_PRIVATE_H
+#define _SCHEME_PRIVATE_H
+
+#include "scheme.h"
+/*------------------ Ugly internals -----------------------------------*/
+/*------------------ Of interest only to FFI users --------------------*/
+
+enum scheme_port_kind {
+ port_free=0,
+ port_file=1,
+ port_string=2,
+ port_srfi6=4,
+ port_input=16,
+ port_output=32,
+ port_saw_EOF=64
+};
+
+typedef struct port {
+ unsigned char kind;
+ union {
+ struct {
+ FILE *file;
+ int closeit;
+#if SHOW_ERROR_LINE
+ int curr_line;
+ char *filename;
+#endif
+ } stdio;
+ struct {
+ char *start;
+ char *past_the_end;
+ char *curr;
+ } string;
+ } rep;
+} port;
+
+/* cell structure */
+struct cell {
+ unsigned int _flag;
+ union {
+ struct {
+ char *_svalue;
+ int _length;
+ } _string;
+ num _number;
+ port *_port;
+ foreign_func _ff;
+ struct {
+ struct cell *_car;
+ struct cell *_cdr;
+ } _cons;
+ } _object;
+};
+
+struct scheme {
+/* arrays for segments */
+func_alloc malloc;
+func_dealloc free;
+
+/* return code */
+int retcode;
+int tracing;
+
+
+#ifndef CELL_SEGSIZE
+#define CELL_SEGSIZE 25000 /* # of cells in one segment */
+#endif
+#ifndef CELL_NSEGMENT
+#define CELL_NSEGMENT 50 /* # of segments for cells */
+#endif
+char *alloc_seg[CELL_NSEGMENT];
+pointer cell_seg[CELL_NSEGMENT];
+int last_cell_seg;
+
+/* We use 5 registers. */
+pointer args; /* register for arguments of function */
+pointer envir; /* stack register for current environment */
+pointer code; /* register for current code */
+pointer dump; /* stack register for next evaluation */
+pointer foreign_error; /* used for foreign functions to signal an error */
+
+int interactive_repl; /* are we in an interactive REPL? */
+int print_output; /* set to 1 to print results and error messages */
+
+struct cell _sink;
+pointer sink; /* when mem. alloc. fails */
+struct cell _NIL;
+pointer NIL; /* special cell representing empty cell */
+struct cell _HASHT;
+pointer T; /* special cell representing #t */
+struct cell _HASHF;
+pointer F; /* special cell representing #f */
+struct cell _EOF_OBJ;
+pointer EOF_OBJ; /* special cell representing end-of-file object */
+pointer oblist; /* pointer to symbol table */
+pointer global_env; /* pointer to global environment */
+
+pointer c_nest; /* stack for nested calls from C */
+
+/* global pointers to special symbols */
+pointer LAMBDA; /* pointer to syntax lambda */
+pointer QUOTE; /* pointer to syntax quote */
+
+pointer QQUOTE; /* pointer to symbol quasiquote */
+pointer UNQUOTE; /* pointer to symbol unquote */
+pointer UNQUOTESP; /* pointer to symbol unquote-splicing */
+pointer FEED_TO; /* => */
+pointer COLON_HOOK; /* *colon-hook* */
+pointer ERROR_HOOK; /* *error-hook* */
+pointer SHARP_HOOK; /* *sharp-hook* */
+pointer COMPILE_HOOK; /* *compile-hook* */
+
+pointer free_cell; /* pointer to top of free cells */
+long fcells; /* # of free cells */
+
+pointer inport;
+pointer outport;
+pointer save_inport;
+pointer loadport;
+
+#ifndef MAXFIL
+#define MAXFIL 64
+#endif
+port load_stack[MAXFIL]; /* Stack of open files for port -1 (LOADing) */
+int nesting_stack[MAXFIL];
+int file_i;
+int nesting;
+
+char gc_verbose; /* if gc_verbose is not zero, print gc status */
+char no_memory; /* Whether mem. alloc. has failed */
+
+#ifndef LINESIZE
+#define LINESIZE 1024
+#endif
+char linebuff[LINESIZE];
+#ifndef STRBUFFSIZE
+#define STRBUFFSIZE 1024
+#endif
+char strbuff[STRBUFFSIZE];
+
+FILE *tmpfp;
+int tok;
+int print_flag;
+pointer value;
+int op;
+
+void *ext_data; /* For the benefit of foreign functions */
+long gensym_cnt;
+
+struct scheme_interface *vptr;
+void *dump_base; /* pointer to base of allocated dump stack */
+int dump_size; /* number of frames allocated for dump stack */
+
+gunichar backchar[2];
+int bc_flag;
+};
+
+/* operator code */
+enum scheme_opcodes {
+#define _OP_DEF(A,B,C,D,E,OP) OP,
+#include "opdefines.h"
+ OP_MAXDEFINED
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define cons(sc,a,b) _cons(sc,a,b,0)
+#define immutable_cons(sc,a,b) _cons(sc,a,b,1)
+
+int is_string(pointer p);
+char *string_value(pointer p);
+int is_number(pointer p);
+num nvalue(pointer p);
+long ivalue(pointer p);
+double rvalue(pointer p);
+int is_integer(pointer p);
+int is_real(pointer p);
+int is_character(pointer p);
+int string_length(pointer p);
+gunichar charvalue(pointer p);
+int is_vector(pointer p);
+
+int is_port(pointer p);
+int is_inport(pointer p);
+int is_outport(pointer p);
+
+int is_pair(pointer p);
+pointer pair_car(pointer p);
+pointer pair_cdr(pointer p);
+pointer set_car(pointer p, pointer q);
+pointer set_cdr(pointer p, pointer q);
+
+int is_symbol(pointer p);
+char *symname(pointer p);
+char *symkey(pointer p);
+int hasprop(pointer p);
+
+int is_syntax(pointer p);
+int is_proc(pointer p);
+int is_foreign(pointer p);
+char *syntaxname(pointer p);
+int is_closure(pointer p);
+int is_macro(pointer p);
+pointer closure_code(pointer p);
+pointer closure_env(pointer p);
+
+int is_continuation(pointer p);
+int is_promise(pointer p);
+int is_environment(pointer p);
+int is_immutable(pointer p);
+void setimmutable(pointer p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*
+Local variables:
+c-file-style: "k&r"
+End:
+*/
diff --git a/plug-ins/script-fu/tinyscheme/scheme.c b/plug-ins/script-fu/tinyscheme/scheme.c
new file mode 100644
index 0000000..ff73801
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/scheme.c
@@ -0,0 +1,5352 @@
+/* T I N Y S C H E M E 1 . 4 1
+ * Dimitrios Souflis (dsouflis@acm.org)
+ * Based on MiniScheme (original credits follow)
+ * (MINISCM) coded by Atsushi Moriwaki (11/5/1989)
+ * (MINISCM) E-MAIL : moriwaki@kurims.kurims.kyoto-u.ac.jp
+ * (MINISCM) This version has been modified by R.C. Secrist.
+ * (MINISCM)
+ * (MINISCM) Mini-Scheme is now maintained by Akira KIDA.
+ * (MINISCM)
+ * (MINISCM) This is a revised and modified version by Akira KIDA.
+ * (MINISCM) current version is 0.85k4 (15 May 1994)
+ *
+ */
+
+/* ******** READ THE FOLLOWING BEFORE MODIFYING THIS FILE! ******** */
+/* This copy of TinyScheme has been modified to support UTF-8 coded */
+/* character strings. As a result, the length of a string in bytes */
+/* may not be the same as the length of a string in characters. You */
+/* must keep this in mind at all times while making any changes to */
+/* the routines in this file and when adding new features. */
+/* */
+/* UTF-8 modifications made by Kevin Cozens (kcozens@interlog.com) */
+/* **************************************************************** */
+
+#include "config.h"
+
+#define _SCHEME_SOURCE
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if USE_DL
+# include "dynload.h"
+#endif
+#if USE_MATH
+# include <math.h>
+#endif
+
+#include <limits.h>
+#include <float.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "../script-fu-intl.h"
+
+#include "scheme-private.h"
+
+#if !STANDALONE
+static ts_output_func ts_output_handler = NULL;
+static gpointer ts_output_data = NULL;
+
+void
+ts_register_output_func (ts_output_func func,
+ gpointer user_data)
+{
+ ts_output_handler = func;
+ ts_output_data = user_data;
+}
+
+/* len is length of 'string' in bytes or -1 for null terminated strings */
+void
+ts_output_string (TsOutputType type,
+ const char *string,
+ int len)
+{
+ if (len < 0)
+ len = strlen (string);
+
+ if (ts_output_handler && len > 0)
+ (* ts_output_handler) (type, string, len, ts_output_data);
+}
+#endif
+
+/* Used for documentation purposes, to signal functions in 'interface' */
+#define INTERFACE
+
+#define TOK_EOF (-1)
+#define TOK_LPAREN 0
+#define TOK_RPAREN 1
+#define TOK_DOT 2
+#define TOK_ATOM 3
+#define TOK_QUOTE 4
+#define TOK_COMMENT 5
+#define TOK_DQUOTE 6
+#define TOK_BQUOTE 7
+#define TOK_COMMA 8
+#define TOK_ATMARK 9
+#define TOK_SHARP 10
+#define TOK_SHARP_CONST 11
+#define TOK_VEC 12
+#define TOK_USCORE 13
+
+#define BACKQUOTE '`'
+#define DELIMITERS "()\";\f\t\v\n\r "
+
+/*
+ * Basic memory allocation units
+ */
+
+#define banner "TinyScheme 1.41 (with UTF-8 support)"
+
+#include <string.h>
+#include <stdlib.h>
+
+#define stricmp utf8_stricmp
+
+static int utf8_stricmp(const char *s1, const char *s2)
+{
+ char *s1a, *s2a;
+ int result;
+
+ s1a = g_utf8_casefold(s1, -1);
+ s2a = g_utf8_casefold(s2, -1);
+
+ result = g_utf8_collate(s1a, s2a);
+
+ g_free(s1a);
+ g_free(s2a);
+ return result;
+}
+
+#define min(a, b) ((a) <= (b) ? (a) : (b))
+
+#if USE_STRLWR
+/*
+#error FIXME: Can't just use g_utf8_strdown since it allocates a new string
+#define strlwr(s) g_utf8_strdown(s, -1)
+*/
+#else
+#define strlwr(s) s
+#endif
+
+#ifndef prompt
+# define prompt "ts> "
+#endif
+
+#ifndef InitFile
+# define InitFile "init.scm"
+#endif
+
+#ifndef FIRST_CELLSEGS
+# define FIRST_CELLSEGS 3
+#endif
+
+enum scheme_types {
+ T_STRING=1,
+ T_NUMBER=2,
+ T_SYMBOL=3,
+ T_PROC=4,
+ T_PAIR=5,
+ T_CLOSURE=6,
+ T_CONTINUATION=7,
+ T_FOREIGN=8,
+ T_CHARACTER=9,
+ T_PORT=10,
+ T_VECTOR=11,
+ T_MACRO=12,
+ T_PROMISE=13,
+ T_ENVIRONMENT=14,
+ T_LAST_SYSTEM_TYPE=14
+};
+
+/* ADJ is enough slack to align cells in a TYPE_BITS-bit boundary */
+#define ADJ 32
+#define TYPE_BITS 5
+#define T_MASKTYPE 31 /* 0000000000011111 */
+#define T_SYNTAX 4096 /* 0001000000000000 */
+#define T_IMMUTABLE 8192 /* 0010000000000000 */
+#define T_ATOM 16384 /* 0100000000000000 */ /* only for gc */
+#define CLRATOM 49151 /* 1011111111111111 */ /* only for gc */
+#define MARK 32768 /* 1000000000000000 */
+#define UNMARK 32767 /* 0111111111111111 */
+
+static num num_add(num a, num b);
+static num num_mul(num a, num b);
+static num num_div(num a, num b);
+static num num_intdiv(num a, num b);
+static num num_sub(num a, num b);
+static num num_rem(num a, num b);
+static num num_mod(num a, num b);
+static int num_eq(num a, num b);
+static int num_gt(num a, num b);
+static int num_ge(num a, num b);
+static int num_lt(num a, num b);
+static int num_le(num a, num b);
+
+#if USE_MATH
+static double round_per_R5RS(double x);
+#endif
+static int is_zero_double(double x);
+static INLINE int num_is_integer(pointer p) {
+ return ((p)->_object._number.is_fixnum);
+}
+
+static num num_zero;
+static num num_one;
+
+/* macros for cell operations */
+#define typeflag(p) ((p)->_flag)
+#define type(p) (typeflag(p)&T_MASKTYPE)
+
+INTERFACE INLINE int is_string(pointer p) { return (type(p)==T_STRING); }
+#define strvalue(p) ((p)->_object._string._svalue)
+#define strlength(p) ((p)->_object._string._length)
+
+INTERFACE static int is_list(scheme *sc, pointer a);
+INTERFACE INLINE int is_vector(pointer p) { return (type(p)==T_VECTOR); }
+INTERFACE static void fill_vector(pointer vec, pointer obj);
+INTERFACE static pointer vector_elem(pointer vec, int ielem);
+INTERFACE static pointer set_vector_elem(pointer vec, int ielem, pointer a);
+INTERFACE INLINE int is_number(pointer p) { return (type(p)==T_NUMBER); }
+INTERFACE INLINE int is_integer(pointer p) {
+ if (!is_number(p))
+ return 0;
+ if (num_is_integer(p) || (double)ivalue(p) == rvalue(p))
+ return 1;
+ return 0;
+}
+
+INTERFACE INLINE int is_real(pointer p) {
+ return is_number(p) && (!(p)->_object._number.is_fixnum);
+}
+
+INTERFACE INLINE int is_character(pointer p) { return (type(p)==T_CHARACTER); }
+INTERFACE INLINE int string_length(pointer p) { return strlength(p); }
+INTERFACE INLINE char *string_value(pointer p) { return strvalue(p); }
+INLINE num nvalue(pointer p) { return ((p)->_object._number); }
+INTERFACE long ivalue(pointer p) { return (num_is_integer(p)?(p)->_object._number.value.ivalue:(long)(p)->_object._number.value.rvalue); }
+INTERFACE double rvalue(pointer p) { return (!num_is_integer(p)?(p)->_object._number.value.rvalue:(double)(p)->_object._number.value.ivalue); }
+#define ivalue_unchecked(p) ((p)->_object._number.value.ivalue)
+#define rvalue_unchecked(p) ((p)->_object._number.value.rvalue)
+#define set_num_integer(p) (p)->_object._number.is_fixnum=1;
+#define set_num_real(p) (p)->_object._number.is_fixnum=0;
+INTERFACE gunichar charvalue(pointer p) { return (gunichar)ivalue_unchecked(p); }
+
+INTERFACE INLINE int is_port(pointer p) { return (type(p)==T_PORT); }
+INTERFACE INLINE int is_inport(pointer p) { return is_port(p) && p->_object._port->kind & port_input; }
+INTERFACE INLINE int is_outport(pointer p) { return is_port(p) && p->_object._port->kind & port_output; }
+
+INTERFACE INLINE int is_pair(pointer p) { return (type(p)==T_PAIR); }
+#define car(p) ((p)->_object._cons._car)
+#define cdr(p) ((p)->_object._cons._cdr)
+INTERFACE pointer pair_car(pointer p) { return car(p); }
+INTERFACE pointer pair_cdr(pointer p) { return cdr(p); }
+INTERFACE pointer set_car(pointer p, pointer q) { return car(p)=q; }
+INTERFACE pointer set_cdr(pointer p, pointer q) { return cdr(p)=q; }
+
+INTERFACE INLINE int is_symbol(pointer p) { return (type(p)==T_SYMBOL); }
+INTERFACE INLINE char *symname(pointer p) { return strvalue(car(p)); }
+#if USE_PLIST
+SCHEME_EXPORT INLINE int hasprop(pointer p) { return (typeflag(p)&T_SYMBOL); }
+#define symprop(p) cdr(p)
+#endif
+
+INTERFACE INLINE int is_syntax(pointer p) { return (typeflag(p)&T_SYNTAX); }
+INTERFACE INLINE int is_proc(pointer p) { return (type(p)==T_PROC); }
+INTERFACE INLINE int is_foreign(pointer p) { return (type(p)==T_FOREIGN); }
+INTERFACE INLINE char *syntaxname(pointer p) { return strvalue(car(p)); }
+#define procnum(p) ivalue(p)
+static const char *procname(pointer x);
+
+INTERFACE INLINE int is_closure(pointer p) { return (type(p)==T_CLOSURE); }
+INTERFACE INLINE int is_macro(pointer p) { return (type(p)==T_MACRO); }
+INTERFACE INLINE pointer closure_code(pointer p) { return car(p); }
+INTERFACE INLINE pointer closure_env(pointer p) { return cdr(p); }
+
+INTERFACE INLINE int is_continuation(pointer p) { return (type(p)==T_CONTINUATION); }
+#define cont_dump(p) cdr(p)
+
+/* To do: promise should be forced ONCE only */
+INTERFACE INLINE int is_promise(pointer p) { return (type(p)==T_PROMISE); }
+
+INTERFACE INLINE int is_environment(pointer p) { return (type(p)==T_ENVIRONMENT); }
+#define setenvironment(p) typeflag(p) = T_ENVIRONMENT
+
+#define is_atom(p) (typeflag(p)&T_ATOM)
+#define setatom(p) typeflag(p) |= T_ATOM
+#define clratom(p) typeflag(p) &= CLRATOM
+
+#define is_mark(p) (typeflag(p)&MARK)
+#define setmark(p) typeflag(p) |= MARK
+#define clrmark(p) typeflag(p) &= UNMARK
+
+INTERFACE INLINE int is_immutable(pointer p) { return (typeflag(p)&T_IMMUTABLE); }
+/*#define setimmutable(p) typeflag(p) |= T_IMMUTABLE*/
+INTERFACE INLINE void setimmutable(pointer p) { typeflag(p) |= T_IMMUTABLE; }
+
+#define caar(p) car(car(p))
+#define cadr(p) car(cdr(p))
+#define cdar(p) cdr(car(p))
+#define cddr(p) cdr(cdr(p))
+#define cadar(p) car(cdr(car(p)))
+#define caddr(p) car(cdr(cdr(p)))
+#define cdaar(p) cdr(car(car(p)))
+#define cadaar(p) car(cdr(car(car(p))))
+#define cadddr(p) car(cdr(cdr(cdr(p))))
+#define cddddr(p) cdr(cdr(cdr(cdr(p))))
+
+#if USE_CHAR_CLASSIFIERS
+static INLINE int Cisalpha(gunichar c) { return g_unichar_isalpha(c); }
+static INLINE int Cisdigit(gunichar c) { return g_unichar_isdigit(c); }
+static INLINE int Cisspace(gunichar c) { return g_unichar_isspace(c); }
+static INLINE int Cisupper(gunichar c) { return g_unichar_isupper(c); }
+static INLINE int Cislower(gunichar c) { return g_unichar_islower(c); }
+#endif
+
+#if USE_ASCII_NAMES
+static const char *charnames[32]={
+ "nul",
+ "soh",
+ "stx",
+ "etx",
+ "eot",
+ "enq",
+ "ack",
+ "bel",
+ "bs",
+ "ht",
+ "lf",
+ "vt",
+ "ff",
+ "cr",
+ "so",
+ "si",
+ "dle",
+ "dc1",
+ "dc2",
+ "dc3",
+ "dc4",
+ "nak",
+ "syn",
+ "etb",
+ "can",
+ "em",
+ "sub",
+ "esc",
+ "fs",
+ "gs",
+ "rs",
+ "us"
+};
+
+static int is_ascii_name(const char *name, int *pc) {
+ int i;
+ for(i=0; i<32; i++) {
+ if(stricmp(name,charnames[i])==0) {
+ *pc=i;
+ return 1;
+ }
+ }
+ if(stricmp(name,"del")==0) {
+ *pc=127;
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+
+/* Number of bytes expected AFTER lead byte of UTF-8 character. */
+static const char utf8_length[64] =
+{
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xc0-0xcf */
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 0xd0-0xdf */
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 0xe0-0xef */
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,0,0 /* 0xf0-0xff */
+};
+
+static int file_push(scheme *sc, const char *fname);
+static void file_pop(scheme *sc);
+static int file_interactive(scheme *sc);
+static INLINE int is_one_of(char *s, gunichar c);
+static int alloc_cellseg(scheme *sc, int n);
+static long binary_decode(const char *s);
+static INLINE pointer get_cell(scheme *sc, pointer a, pointer b);
+static pointer _get_cell(scheme *sc, pointer a, pointer b);
+static pointer reserve_cells(scheme *sc, int n);
+static pointer get_consecutive_cells(scheme *sc, int n);
+static pointer find_consecutive_cells(scheme *sc, int n);
+static void finalize_cell(scheme *sc, pointer a);
+static int count_consecutive_cells(pointer x, int needed);
+static pointer find_slot_in_env(scheme *sc, pointer env, pointer sym, int all);
+static pointer mk_number(scheme *sc, num n);
+static char *store_string(scheme *sc, int len, const char *str, gunichar fill);
+static pointer mk_vector(scheme *sc, int len);
+static pointer mk_atom(scheme *sc, char *q);
+static pointer mk_sharp_const(scheme *sc, char *name);
+static pointer mk_port(scheme *sc, port *p);
+static pointer port_from_filename(scheme *sc, const char *fn, int prop);
+static pointer port_from_file(scheme *sc, FILE *, int prop);
+static pointer port_from_string(scheme *sc, char *start, char *past_the_end, int prop);
+static port *port_rep_from_filename(scheme *sc, const char *fn, int prop);
+static port *port_rep_from_file(scheme *sc, FILE *, int prop);
+static port *port_rep_from_string(scheme *sc, char *start, char *past_the_end, int prop);
+static void port_close(scheme *sc, pointer p, int flag);
+static void mark(pointer a);
+static void gc(scheme *sc, pointer a, pointer b);
+static gunichar basic_inchar(port *pt);
+static gunichar inchar(scheme *sc);
+static void backchar(scheme *sc, gunichar c);
+static char *readstr_upto(scheme *sc, char *delim);
+static pointer readstrexp(scheme *sc);
+static INLINE int skipspace(scheme *sc);
+static int token(scheme *sc);
+static void printslashstring(scheme *sc, char *s, int len);
+static void atom2str(scheme *sc, pointer l, int f, char **pp, int *plen);
+static void printatom(scheme *sc, pointer l, int f);
+static pointer mk_proc(scheme *sc, enum scheme_opcodes op);
+static pointer mk_closure(scheme *sc, pointer c, pointer e);
+static pointer mk_continuation(scheme *sc, pointer d);
+static pointer reverse(scheme *sc, pointer a);
+static pointer reverse_in_place(scheme *sc, pointer term, pointer list);
+static pointer revappend(scheme *sc, pointer a, pointer b);
+int list_length(scheme *sc, pointer a);
+int eqv(pointer a, pointer b);
+
+static INLINE void dump_stack_mark(scheme *);
+static pointer opexe_0(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_1(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_2(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_3(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_4(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_5(scheme *sc, enum scheme_opcodes op);
+static pointer opexe_6(scheme *sc, enum scheme_opcodes op);
+static void Eval_Cycle(scheme *sc, enum scheme_opcodes op);
+static void assign_syntax(scheme *sc, char *name);
+static int syntaxnum(pointer p);
+static void assign_proc(scheme *sc, enum scheme_opcodes, char *name);
+scheme *scheme_init_new(void);
+
+#define num_ivalue(n) (n.is_fixnum?(n).value.ivalue:(long)(n).value.rvalue)
+#define num_rvalue(n) (!n.is_fixnum?(n).value.rvalue:(double)(n).value.ivalue)
+
+static num num_add(num a, num b) {
+ num ret;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(ret.is_fixnum) {
+ ret.value.ivalue= a.value.ivalue+b.value.ivalue;
+ } else {
+ ret.value.rvalue=num_rvalue(a)+num_rvalue(b);
+ }
+ return ret;
+}
+
+static num num_mul(num a, num b) {
+ num ret;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(ret.is_fixnum) {
+ ret.value.ivalue= a.value.ivalue*b.value.ivalue;
+ } else {
+ ret.value.rvalue=num_rvalue(a)*num_rvalue(b);
+ }
+ return ret;
+}
+
+static num num_div(num a, num b) {
+ num ret;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum && a.value.ivalue%b.value.ivalue==0;
+ if(ret.is_fixnum) {
+ ret.value.ivalue= a.value.ivalue/b.value.ivalue;
+ } else {
+ ret.value.rvalue=num_rvalue(a)/num_rvalue(b);
+ }
+ return ret;
+}
+
+static num num_intdiv(num a, num b) {
+ num ret;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(ret.is_fixnum) {
+ ret.value.ivalue= a.value.ivalue/b.value.ivalue;
+ } else {
+ ret.value.rvalue=num_rvalue(a)/num_rvalue(b);
+ }
+ return ret;
+}
+
+static num num_sub(num a, num b) {
+ num ret;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(ret.is_fixnum) {
+ ret.value.ivalue= a.value.ivalue-b.value.ivalue;
+ } else {
+ ret.value.rvalue=num_rvalue(a)-num_rvalue(b);
+ }
+ return ret;
+}
+
+static num num_rem(num a, num b) {
+ num ret;
+ long e1, e2, res;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ e1=num_ivalue(a);
+ e2=num_ivalue(b);
+ res=e1%e2;
+ /* remainder should have same sign as first operand */
+ if (res > 0) {
+ if (e1 < 0) {
+ res -= labs(e2);
+ }
+ } else if (res < 0) {
+ if (e1 > 0) {
+ res += labs(e2);
+ }
+ }
+ ret.value.ivalue=res;
+ return ret;
+}
+
+static num num_mod(num a, num b) {
+ num ret;
+ long e1, e2, res;
+ ret.is_fixnum=a.is_fixnum && b.is_fixnum;
+ e1=num_ivalue(a);
+ e2=num_ivalue(b);
+ res=e1%e2;
+ /* modulo should have same sign as second operand */
+ if ((res < 0) != (e2 < 0) && res) { /* if their sign is different... */
+ res+=e2;
+ }
+ ret.value.ivalue=res;
+ return ret;
+}
+
+static int num_eq(num a, num b) {
+ int ret;
+ int is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(is_fixnum) {
+ ret= a.value.ivalue==b.value.ivalue;
+ } else {
+ ret=num_rvalue(a)==num_rvalue(b);
+ }
+ return ret;
+}
+
+
+static int num_gt(num a, num b) {
+ int ret;
+ int is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(is_fixnum) {
+ ret= a.value.ivalue>b.value.ivalue;
+ } else {
+ ret=num_rvalue(a)>num_rvalue(b);
+ }
+ return ret;
+}
+
+static int num_ge(num a, num b) {
+ return !num_lt(a,b);
+}
+
+static int num_lt(num a, num b) {
+ int ret;
+ int is_fixnum=a.is_fixnum && b.is_fixnum;
+ if(is_fixnum) {
+ ret= a.value.ivalue<b.value.ivalue;
+ } else {
+ ret=num_rvalue(a)<num_rvalue(b);
+ }
+ return ret;
+}
+
+static int num_le(num a, num b) {
+ return !num_gt(a,b);
+}
+
+#if USE_MATH
+/* Round to nearest. Round to even if midway */
+static double round_per_R5RS(double x) {
+ double fl=floor(x);
+ double ce=ceil(x);
+ double dfl=x-fl;
+ double dce=ce-x;
+ if(dfl>dce) {
+ return ce;
+ } else if(dfl<dce) {
+ return fl;
+ } else {
+ if(fmod(fl,2.0)==0.0) { /* I imagine this holds */
+ return fl;
+ } else {
+ return ce;
+ }
+ }
+}
+#endif
+
+static int is_zero_double(double x) {
+ return x<DBL_MIN && x>-DBL_MIN;
+}
+
+static long binary_decode(const char *s) {
+ long x=0;
+
+ while(*s!=0 && (*s=='1' || *s=='0')) {
+ x<<=1;
+ x+=*s-'0';
+ s++;
+ }
+
+ return x;
+}
+
+/* allocate new cell segment */
+static int alloc_cellseg(scheme *sc, int n) {
+ pointer newp;
+ pointer last;
+ pointer p;
+ char *cp;
+ long i;
+ int k;
+ int adj=ADJ;
+
+ if(adj<sizeof(struct cell)) {
+ adj=sizeof(struct cell);
+ }
+
+ for (k = 0; k < n; k++) {
+ if (sc->last_cell_seg >= CELL_NSEGMENT - 1)
+ return k;
+ cp = (char*) sc->malloc(CELL_SEGSIZE * sizeof(struct cell)+adj);
+ if (cp == 0)
+ return k;
+ i = ++sc->last_cell_seg ;
+ sc->alloc_seg[i] = cp;
+ /* adjust in TYPE_BITS-bit boundary */
+ if(((uintptr_t)cp)%adj!=0) {
+ cp=(char*)(adj*((uintptr_t)cp/adj+1));
+ }
+ /* insert new segment in address order */
+ newp=(pointer)cp;
+ sc->cell_seg[i] = newp;
+ while (i > 0 && sc->cell_seg[i - 1] > sc->cell_seg[i]) {
+ p = sc->cell_seg[i];
+ sc->cell_seg[i] = sc->cell_seg[i - 1];
+ sc->cell_seg[--i] = p;
+ }
+ sc->fcells += CELL_SEGSIZE;
+ last = newp + CELL_SEGSIZE - 1;
+ for (p = newp; p <= last; p++) {
+ typeflag(p) = 0;
+ cdr(p) = p + 1;
+ car(p) = sc->NIL;
+ }
+ /* insert new cells in address order on free list */
+ if (sc->free_cell == sc->NIL || p < sc->free_cell) {
+ cdr(last) = sc->free_cell;
+ sc->free_cell = newp;
+ } else {
+ p = sc->free_cell;
+ while (cdr(p) != sc->NIL && newp > cdr(p))
+ p = cdr(p);
+ cdr(last) = cdr(p);
+ cdr(p) = newp;
+ }
+ }
+ return n;
+}
+
+static INLINE pointer get_cell_x(scheme *sc, pointer a, pointer b) {
+ if (sc->free_cell != sc->NIL) {
+ pointer x = sc->free_cell;
+ sc->free_cell = cdr(x);
+ --sc->fcells;
+ return (x);
+ }
+ return _get_cell (sc, a, b);
+}
+
+
+/* get new cell. parameter a, b is marked by gc. */
+static pointer _get_cell(scheme *sc, pointer a, pointer b) {
+ pointer x;
+
+ if(sc->no_memory) {
+ return sc->sink;
+ }
+
+ if (sc->free_cell == sc->NIL) {
+ const int min_to_be_recovered = sc->last_cell_seg*8;
+ gc(sc,a, b);
+ if (sc->fcells < min_to_be_recovered
+ || sc->free_cell == sc->NIL) {
+ /* if only a few recovered, get more to avoid fruitless gc's */
+ if (!alloc_cellseg(sc,1) && sc->free_cell == sc->NIL) {
+ sc->no_memory=1;
+ return sc->sink;
+ }
+ }
+ }
+ x = sc->free_cell;
+ sc->free_cell = cdr(x);
+ --sc->fcells;
+ return (x);
+}
+
+/* make sure that there is a given number of cells free */
+static pointer reserve_cells(scheme *sc, int n) {
+ if(sc->no_memory) {
+ return sc->NIL;
+ }
+
+ /* Are there enough cells available? */
+ if (sc->fcells < n) {
+ /* If not, try gc'ing some */
+ gc(sc, sc->NIL, sc->NIL);
+ if (sc->fcells < n) {
+ /* If there still aren't, try getting more heap */
+ if (!alloc_cellseg(sc,1)) {
+ sc->no_memory=1;
+ return sc->NIL;
+ }
+ }
+ if (sc->fcells < n) {
+ /* If all fail, report failure */
+ sc->no_memory=1;
+ return sc->NIL;
+ }
+ }
+ return (sc->T);
+}
+
+static pointer get_consecutive_cells(scheme *sc, int n) {
+ pointer x;
+
+ if(sc->no_memory) { return sc->sink; }
+
+ /* Are there any cells available? */
+ x=find_consecutive_cells(sc,n);
+ if (x != sc->NIL) { return x; }
+
+ /* If not, try gc'ing some */
+ gc(sc, sc->NIL, sc->NIL);
+ x=find_consecutive_cells(sc,n);
+ if (x != sc->NIL) { return x; }
+
+ /* If there still aren't, try getting more heap */
+ if (!alloc_cellseg(sc,1))
+ {
+ sc->no_memory=1;
+ return sc->sink;
+ }
+
+ x=find_consecutive_cells(sc,n);
+ if (x != sc->NIL) { return x; }
+
+ /* If all fail, report failure */
+ sc->no_memory=1;
+ return sc->sink;
+}
+
+static int count_consecutive_cells(pointer x, int needed) {
+ int n=1;
+ while(cdr(x)==x+1) {
+ x=cdr(x);
+ n++;
+ if(n>needed) return n;
+ }
+ return n;
+}
+
+static pointer find_consecutive_cells(scheme *sc, int n) {
+ pointer *pp;
+ int cnt;
+
+ pp=&sc->free_cell;
+ while(*pp!=sc->NIL) {
+ cnt=count_consecutive_cells(*pp,n);
+ if(cnt>=n) {
+ pointer x=*pp;
+ *pp=cdr(*pp+n-1);
+ sc->fcells -= n;
+ return x;
+ }
+ pp=&cdr(*pp+cnt-1);
+ }
+ return sc->NIL;
+}
+
+/* To retain recent allocs before interpreter knows about them -
+ Tehom */
+
+static void push_recent_alloc(scheme *sc, pointer recent, pointer extra)
+{
+ pointer holder = get_cell_x(sc, recent, extra);
+ typeflag(holder) = T_PAIR | T_IMMUTABLE;
+ car(holder) = recent;
+ cdr(holder) = car(sc->sink);
+ car(sc->sink) = holder;
+}
+
+
+static pointer get_cell(scheme *sc, pointer a, pointer b)
+{
+ pointer cell = get_cell_x(sc, a, b);
+ /* For right now, include "a" and "b" in "cell" so that gc doesn't
+ think they are garbage. */
+ /* Tentatively record it as a pair so gc understands it. */
+ typeflag(cell) = T_PAIR;
+ car(cell) = a;
+ cdr(cell) = b;
+ push_recent_alloc(sc, cell, sc->NIL);
+ return cell;
+}
+
+static pointer get_vector_object(scheme *sc, int len, pointer init)
+{
+ pointer cells = get_consecutive_cells(sc,len/2+len%2+1);
+ if(sc->no_memory) { return sc->sink; }
+ /* Record it as a vector so that gc understands it. */
+ typeflag(cells) = (T_VECTOR | T_ATOM);
+ ivalue_unchecked(cells)=len;
+ set_num_integer(cells);
+ fill_vector(cells,init);
+ push_recent_alloc(sc, cells, sc->NIL);
+ return cells;
+}
+
+static INLINE void ok_to_freely_gc(scheme *sc)
+{
+ car(sc->sink) = sc->NIL;
+}
+
+
+#if defined TSGRIND
+static void check_cell_alloced(pointer p, int expect_alloced)
+{
+ /* Can't use putstr(sc,str) because callers have no access to
+ sc. */
+ if(typeflag(p) & !expect_alloced)
+ {
+ fprintf(stderr,"Cell is already allocated!\n");
+ }
+ if(!(typeflag(p)) & expect_alloced)
+ {
+ fprintf(stderr,"Cell is not allocated!\n");
+ }
+}
+static void check_range_alloced(pointer p, int n, int expect_alloced)
+{
+ int i;
+ for(i = 0;i<n;i++)
+ { (void)check_cell_alloced(p+i,expect_alloced); }
+}
+
+#endif
+
+/* Medium level cell allocation */
+
+/* get new cons cell */
+pointer _cons(scheme *sc, pointer a, pointer b, int immutable) {
+ pointer x = get_cell(sc,a, b);
+
+ typeflag(x) = T_PAIR;
+ if(immutable) {
+ setimmutable(x);
+ }
+ car(x) = a;
+ cdr(x) = b;
+ return (x);
+}
+
+/* ========== oblist implementation ========== */
+
+#ifndef USE_OBJECT_LIST
+
+static int hash_fn(const char *key, int table_size);
+
+static pointer oblist_initial_value(scheme *sc)
+{
+ return mk_vector(sc, 461); /* probably should be bigger */
+}
+
+/* returns the new symbol */
+static pointer oblist_add_by_name(scheme *sc, const char *name)
+{
+ pointer x;
+ int location;
+
+ x = immutable_cons(sc, mk_string(sc, name), sc->NIL);
+ typeflag(x) = T_SYMBOL;
+ setimmutable(car(x));
+
+ location = hash_fn(name, ivalue_unchecked(sc->oblist));
+ set_vector_elem(sc->oblist, location,
+ immutable_cons(sc, x, vector_elem(sc->oblist, location)));
+ return x;
+}
+
+static INLINE pointer oblist_find_by_name(scheme *sc, const char *name)
+{
+ int location;
+ pointer x;
+ char *s;
+
+ location = hash_fn(name, ivalue_unchecked(sc->oblist));
+ for (x = vector_elem(sc->oblist, location); x != sc->NIL; x = cdr(x)) {
+ s = symname(car(x));
+ /* case-insensitive, per R5RS section 2. */
+ if(stricmp(name, s) == 0) {
+ return car(x);
+ }
+ }
+ return sc->NIL;
+}
+
+static pointer oblist_all_symbols(scheme *sc)
+{
+ int i;
+ pointer x;
+ pointer ob_list = sc->NIL;
+
+ for (i = 0; i < ivalue_unchecked(sc->oblist); i++) {
+ for (x = vector_elem(sc->oblist, i); x != sc->NIL; x = cdr(x)) {
+ ob_list = cons(sc, x, ob_list);
+ }
+ }
+ return ob_list;
+}
+
+#else
+
+static pointer oblist_initial_value(scheme *sc)
+{
+ return sc->NIL;
+}
+
+static INLINE pointer oblist_find_by_name(scheme *sc, const char *name)
+{
+ pointer x;
+ char *s;
+
+ for (x = sc->oblist; x != sc->NIL; x = cdr(x)) {
+ s = symname(car(x));
+ /* case-insensitive, per R5RS section 2. */
+ if(stricmp(name, s) == 0) {
+ return car(x);
+ }
+ }
+ return sc->NIL;
+}
+
+/* returns the new symbol */
+static pointer oblist_add_by_name(scheme *sc, const char *name)
+{
+ pointer x;
+
+ x = immutable_cons(sc, mk_string(sc, name), sc->NIL);
+ typeflag(x) = T_SYMBOL;
+ setimmutable(car(x));
+ sc->oblist = immutable_cons(sc, x, sc->oblist);
+ return x;
+}
+
+static pointer oblist_all_symbols(scheme *sc)
+{
+ return sc->oblist;
+}
+
+#endif
+
+static pointer mk_port(scheme *sc, port *p) {
+ pointer x = get_cell(sc, sc->NIL, sc->NIL);
+
+ typeflag(x) = T_PORT|T_ATOM;
+ x->_object._port=p;
+ return (x);
+}
+
+pointer mk_foreign_func(scheme *sc, foreign_func f) {
+ pointer x = get_cell(sc, sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_FOREIGN | T_ATOM);
+ x->_object._ff=f;
+ return (x);
+}
+
+INTERFACE pointer mk_character(scheme *sc, gunichar c) {
+ pointer x = get_cell(sc,sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_CHARACTER | T_ATOM);
+ ivalue_unchecked(x)= c;
+ set_num_integer(x);
+ return (x);
+}
+
+/* get number atom (integer) */
+INTERFACE pointer mk_integer(scheme *sc, long num) {
+ pointer x = get_cell(sc,sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_NUMBER | T_ATOM);
+ ivalue_unchecked(x)= num;
+ set_num_integer(x);
+ return (x);
+}
+
+INTERFACE pointer mk_real(scheme *sc, double n) {
+ pointer x = get_cell(sc,sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_NUMBER | T_ATOM);
+ rvalue_unchecked(x)= n;
+ set_num_real(x);
+ return (x);
+}
+
+static pointer mk_number(scheme *sc, num n) {
+ if(n.is_fixnum) {
+ return mk_integer(sc,n.value.ivalue);
+ } else {
+ return mk_real(sc,n.value.rvalue);
+ }
+}
+
+pointer foreign_error (scheme *sc, const char *s, pointer a) {
+ sc->foreign_error = cons (sc, mk_string (sc, s), a);
+ return sc->T;
+}
+
+/* char_cnt is length of string in chars. */
+/* str points to a NUL terminated string. */
+/* Only uses fill_char if str is NULL. */
+/* This routine automatically adds 1 byte */
+/* to allow space for terminating NUL. */
+static char *store_string(scheme *sc, int char_cnt,
+ const char *str, gunichar fill) {
+ int len;
+ int i;
+ gchar utf8[7];
+ gchar *q;
+ gchar *q2;
+
+ if(str!=0) {
+ q2 = g_utf8_offset_to_pointer(str, (long)char_cnt);
+ (void)g_utf8_validate(str, -1, (const gchar **)&q);
+ if (q <= q2)
+ len = q - str;
+ else
+ len = q2 - str;
+ q=(gchar*)sc->malloc(len+1);
+ } else {
+ len = g_unichar_to_utf8(fill, utf8);
+ q=(gchar*)sc->malloc(char_cnt*len+1);
+ }
+
+ if(q==0) {
+ sc->no_memory=1;
+ return sc->strbuff;
+ }
+ if(str!=0) {
+ memcpy(q, str, len);
+ q[len]=0;
+ } else {
+ q2 = q;
+ for (i = 0; i < char_cnt; ++i)
+ {
+ memcpy(q2, utf8, len);
+ q2 += len;
+ }
+ *q2=0;
+ }
+ return (q);
+}
+
+/* get new string */
+INTERFACE pointer mk_string(scheme *sc, const char *str) {
+ return mk_counted_string(sc,str,g_utf8_strlen(str, -1));
+}
+
+/* str points to a NUL terminated string. */
+/* len is the length of str in characters */
+INTERFACE pointer mk_counted_string(scheme *sc, const char *str, int len) {
+ pointer x = get_cell(sc, sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_STRING | T_ATOM);
+ strvalue(x) = store_string(sc,len,str,0);
+ strlength(x) = len;
+ return (x);
+}
+
+/* len is the length for the empty string in characters */
+INTERFACE pointer mk_empty_string(scheme *sc, int len, gunichar fill) {
+ pointer x = get_cell(sc, sc->NIL, sc->NIL);
+
+ typeflag(x) = (T_STRING | T_ATOM);
+ strvalue(x) = store_string(sc,len,0,fill);
+ strlength(x) = len;
+ return (x);
+}
+
+INTERFACE static pointer mk_vector(scheme *sc, int len)
+{ return get_vector_object(sc,len,sc->NIL); }
+
+INTERFACE static void fill_vector(pointer vec, pointer obj) {
+ int i;
+ int num=ivalue(vec)/2+ivalue(vec)%2;
+ for(i=0; i<num; i++) {
+ typeflag(vec+1+i) = T_PAIR;
+ setimmutable(vec+1+i);
+ car(vec+1+i)=obj;
+ cdr(vec+1+i)=obj;
+ }
+}
+
+INTERFACE static pointer vector_elem(pointer vec, int ielem) {
+ int n=ielem/2;
+ if(ielem%2==0) {
+ return car(vec+1+n);
+ } else {
+ return cdr(vec+1+n);
+ }
+}
+
+INTERFACE static pointer set_vector_elem(pointer vec, int ielem, pointer a) {
+ int n=ielem/2;
+ if(ielem%2==0) {
+ return car(vec+1+n)=a;
+ } else {
+ return cdr(vec+1+n)=a;
+ }
+}
+
+/* get new symbol */
+INTERFACE pointer mk_symbol(scheme *sc, const char *name) {
+ pointer x;
+
+ /* first check oblist */
+ x = oblist_find_by_name(sc, name);
+ if (x != sc->NIL) {
+ return (x);
+ } else {
+ x = oblist_add_by_name(sc, name);
+ return (x);
+ }
+}
+
+INTERFACE pointer gensym(scheme *sc) {
+ pointer x;
+ char name[40];
+
+ for(; sc->gensym_cnt<LONG_MAX; sc->gensym_cnt++) {
+ snprintf(name,40,"gensym-%ld",sc->gensym_cnt);
+
+ /* first check oblist */
+ x = oblist_find_by_name(sc, name);
+
+ if (x != sc->NIL) {
+ continue;
+ } else {
+ x = oblist_add_by_name(sc, name);
+ return (x);
+ }
+ }
+
+ return sc->NIL;
+}
+
+/* make symbol or number atom from string */
+static pointer mk_atom(scheme *sc, char *q) {
+ char c, *p;
+ int has_dec_point=0;
+ int has_fp_exp = 0;
+
+#if USE_COLON_HOOK
+ if((p=strstr(q,"::"))!=0) {
+ *p=0;
+ return cons(sc, sc->COLON_HOOK,
+ cons(sc,
+ cons(sc,
+ sc->QUOTE,
+ cons(sc, mk_atom(sc,p+2), sc->NIL)),
+ cons(sc, mk_symbol(sc,strlwr(q)), sc->NIL)));
+ }
+#endif
+
+ p = q;
+ c = *p++;
+ if ((c == '+') || (c == '-')) {
+ c = *p++;
+ if (c == '.') {
+ has_dec_point=1;
+ c = *p++;
+ }
+ if (!isdigit(c)) {
+ return (mk_symbol(sc, strlwr(q)));
+ }
+ } else if (c == '.') {
+ has_dec_point=1;
+ c = *p++;
+ if (!isdigit(c)) {
+ return (mk_symbol(sc, strlwr(q)));
+ }
+ } else if (!isdigit(c)) {
+ return (mk_symbol(sc, strlwr(q)));
+ }
+
+ for ( ; (c = *p) != 0; ++p) {
+ if (!isdigit(c)) {
+ if(c=='.') {
+ if(!has_dec_point) {
+ has_dec_point=1;
+ continue;
+ }
+ }
+ else if ((c == 'e') || (c == 'E')) {
+ if(!has_fp_exp) {
+ has_dec_point = 1; /* decimal point illegal
+ from now on */
+ p++;
+ if ((*p == '-') || (*p == '+') || isdigit(*p)) {
+ continue;
+ }
+ }
+ }
+ return (mk_symbol(sc, strlwr(q)));
+ }
+ }
+ if(has_dec_point) {
+ return mk_real(sc,g_ascii_strtod(q,NULL));
+ }
+ return (mk_integer(sc, atol(q)));
+}
+
+/* make constant */
+static pointer mk_sharp_const(scheme *sc, char *name) {
+ long x;
+ char tmp[STRBUFFSIZE];
+
+ if (!strcmp(name, "t"))
+ return (sc->T);
+ else if (!strcmp(name, "f"))
+ return (sc->F);
+ else if (*name == 'o') {/* #o (octal) */
+ snprintf(tmp, STRBUFFSIZE, "0%s", name+1);
+ sscanf(tmp, "%lo", (long unsigned *)&x);
+ return (mk_integer(sc, x));
+ } else if (*name == 'd') { /* #d (decimal) */
+ sscanf(name+1, "%ld", (long int *)&x);
+ return (mk_integer(sc, x));
+ } else if (*name == 'x') { /* #x (hex) */
+ snprintf(tmp, STRBUFFSIZE, "0x%s", name+1);
+ sscanf(tmp, "%lx", (long unsigned *)&x);
+ return (mk_integer(sc, x));
+ } else if (*name == 'b') { /* #b (binary) */
+ x = binary_decode(name+1);
+ return (mk_integer(sc, x));
+ } else if (*name == '\\') { /* #\w (character) */
+ gunichar c=0;
+ if(stricmp(name+1,"space")==0) {
+ c=' ';
+ } else if(stricmp(name+1,"newline")==0) {
+ c='\n';
+ } else if(stricmp(name+1,"return")==0) {
+ c='\r';
+ } else if(stricmp(name+1,"tab")==0) {
+ c='\t';
+ } else if(name[1]=='x' && name[2]!=0) {
+ int c1=0;
+ if(sscanf(name+2,"%x",(unsigned int *)&c1)==1 && c1 < UCHAR_MAX) {
+ c=c1;
+ } else {
+ return sc->NIL;
+ }
+#if USE_ASCII_NAMES
+ } else if(is_ascii_name(name+1,&c)) {
+ /* nothing */
+#endif
+ } else if(name[2]==0) {
+ c=name[1];
+ } else {
+ return sc->NIL;
+ }
+ return mk_character(sc,c);
+ } else
+ return (sc->NIL);
+}
+
+/* ========== garbage collector ========== */
+
+/*--
+ * We use algorithm E (Knuth, The Art of Computer Programming Vol.1,
+ * sec. 2.3.5), the Schorr-Deutsch-Waite link-inversion algorithm,
+ * for marking.
+ */
+static void mark(pointer a) {
+ pointer t, q, p;
+
+ t = (pointer) 0;
+ p = a;
+E2: setmark(p);
+ if(is_vector(p)) {
+ int i;
+ int num=ivalue_unchecked(p)/2+ivalue_unchecked(p)%2;
+ for(i=0; i<num; i++) {
+ /* Vector cells will be treated like ordinary cells */
+ mark(p+1+i);
+ }
+ }
+ if (is_atom(p))
+ goto E6;
+ /* E4: down car */
+ q = car(p);
+ if (q && !is_mark(q)) {
+ setatom(p); /* a note that we have moved car */
+ car(p) = t;
+ t = p;
+ p = q;
+ goto E2;
+ }
+ E5: q = cdr(p); /* down cdr */
+ if (q && !is_mark(q)) {
+ cdr(p) = t;
+ t = p;
+ p = q;
+ goto E2;
+ }
+E6: /* up. Undo the link switching from steps E4 and E5. */
+ if (!t)
+ return;
+ q = t;
+ if (is_atom(q)) {
+ clratom(q);
+ t = car(q);
+ car(q) = p;
+ p = q;
+ goto E5;
+ } else {
+ t = cdr(q);
+ cdr(q) = p;
+ p = q;
+ goto E6;
+ }
+}
+
+/* garbage collection. parameter a, b is marked. */
+static void gc(scheme *sc, pointer a, pointer b) {
+ pointer p;
+ int i;
+
+ if(sc->gc_verbose) {
+ putstr(sc, "gc...");
+ }
+
+ /* mark system globals */
+ mark(sc->oblist);
+ mark(sc->global_env);
+
+ /* mark current registers */
+ mark(sc->args);
+ mark(sc->envir);
+ mark(sc->code);
+ dump_stack_mark(sc);
+ mark(sc->value);
+ mark(sc->inport);
+ mark(sc->save_inport);
+ mark(sc->outport);
+ mark(sc->loadport);
+
+ /* Mark recent objects the interpreter doesn't know about yet. */
+ mark(car(sc->sink));
+ /* Mark any older stuff above nested C calls */
+ mark(sc->c_nest);
+
+ /* mark variables a, b */
+ mark(a);
+ mark(b);
+
+ /* garbage collect */
+ clrmark(sc->NIL);
+ sc->fcells = 0;
+ sc->free_cell = sc->NIL;
+ /* free-list is kept sorted by address so as to maintain consecutive
+ ranges, if possible, for use with vectors. Here we scan the cells
+ (which are also kept sorted by address) downwards to build the
+ free-list in sorted order.
+ */
+ for (i = sc->last_cell_seg; i >= 0; i--) {
+ p = sc->cell_seg[i] + CELL_SEGSIZE;
+ while (--p >= sc->cell_seg[i]) {
+ if (is_mark(p)) {
+ clrmark(p);
+ } else {
+ /* reclaim cell */
+ if (typeflag(p) != 0) {
+ finalize_cell(sc, p);
+ typeflag(p) = 0;
+ car(p) = sc->NIL;
+ }
+ ++sc->fcells;
+ cdr(p) = sc->free_cell;
+ sc->free_cell = p;
+ }
+ }
+ }
+
+ if (sc->gc_verbose) {
+ char msg[80];
+ snprintf(msg,80,"done: %ld cells were recovered.\n", sc->fcells);
+ putstr(sc,msg);
+ }
+}
+
+static void finalize_cell(scheme *sc, pointer a) {
+ if(is_string(a)) {
+ sc->free(strvalue(a));
+ } else if(is_port(a)) {
+ if(a->_object._port->kind&port_file
+ && a->_object._port->rep.stdio.closeit) {
+ port_close(sc,a,port_input|port_output);
+ }
+ sc->free(a->_object._port);
+ }
+}
+
+/* ========== Routines for Reading ========== */
+
+static int file_push(scheme *sc, const char *fname) {
+ FILE *fin = NULL;
+ if (sc->file_i == MAXFIL-1)
+ return 0;
+
+ fin=g_fopen(fname,"rb");
+ if(fin!=0) {
+ sc->file_i++;
+ sc->load_stack[sc->file_i].kind=port_file|port_input;
+ sc->load_stack[sc->file_i].rep.stdio.file=fin;
+ sc->load_stack[sc->file_i].rep.stdio.closeit=1;
+ sc->nesting_stack[sc->file_i]=0;
+ sc->loadport->_object._port=sc->load_stack+sc->file_i;
+
+#if SHOW_ERROR_LINE
+ sc->load_stack[sc->file_i].rep.stdio.curr_line = 0;
+ if(fname)
+ sc->load_stack[sc->file_i].rep.stdio.filename = store_string(sc, strlen(fname), fname, 0);
+#endif
+ }
+ return fin!=0;
+}
+
+static void file_pop(scheme *sc) {
+ if(sc->file_i != 0) {
+ sc->nesting=sc->nesting_stack[sc->file_i];
+ port_close(sc,sc->loadport,port_input);
+ sc->file_i--;
+ sc->loadport->_object._port=sc->load_stack+sc->file_i;
+ }
+}
+
+static int file_interactive(scheme *sc) {
+ return sc->file_i==0 && sc->load_stack[0].rep.stdio.file==stdin
+ && sc->inport->_object._port->kind&port_file;
+}
+
+static port *port_rep_from_filename(scheme *sc, const char *fn, int prop) {
+ FILE *f;
+ char *rw;
+ port *pt;
+ if(prop==(port_input|port_output)) {
+ rw="a+b";
+ } else if(prop==port_output) {
+ rw="wb";
+ } else {
+ rw="rb";
+ }
+ f=g_fopen(fn,rw);
+ if(f==0) {
+ return 0;
+ }
+ pt=port_rep_from_file(sc,f,prop);
+ pt->rep.stdio.closeit=1;
+
+#if SHOW_ERROR_LINE
+ if(fn)
+ pt->rep.stdio.filename = store_string(sc, strlen(fn), fn, 0);
+
+ pt->rep.stdio.curr_line = 0;
+#endif
+ return pt;
+}
+
+static pointer port_from_filename(scheme *sc, const char *fn, int prop) {
+ port *pt;
+ pt=port_rep_from_filename(sc,fn,prop);
+ if(pt==0) {
+ return sc->NIL;
+ }
+ return mk_port(sc,pt);
+}
+
+static port *port_rep_from_file(scheme *sc, FILE *f, int prop)
+{
+ port *pt;
+
+ pt = (port *)sc->malloc(sizeof *pt);
+ if (pt == NULL) {
+ return NULL;
+ }
+ pt->kind = port_file | prop;
+ pt->rep.stdio.file = f;
+ pt->rep.stdio.closeit = 0;
+ return pt;
+}
+
+static pointer port_from_file(scheme *sc, FILE *f, int prop) {
+ port *pt;
+ pt=port_rep_from_file(sc,f,prop);
+ if(pt==0) {
+ return sc->NIL;
+ }
+ return mk_port(sc,pt);
+}
+
+static port *port_rep_from_string(scheme *sc, char *start, char *past_the_end, int prop) {
+ port *pt;
+ pt=(port*)sc->malloc(sizeof(port));
+ if(pt==0) {
+ return 0;
+ }
+ pt->kind=port_string|prop;
+ pt->rep.string.start=start;
+ pt->rep.string.curr=start;
+ pt->rep.string.past_the_end=past_the_end;
+ return pt;
+}
+
+static pointer port_from_string(scheme *sc, char *start, char *past_the_end, int prop) {
+ port *pt;
+ pt=port_rep_from_string(sc,start,past_the_end,prop);
+ if(pt==0) {
+ return sc->NIL;
+ }
+ return mk_port(sc,pt);
+}
+
+#define BLOCK_SIZE 256
+
+static port *port_rep_from_scratch(scheme *sc) {
+ port *pt;
+ char *start;
+ pt=(port*)sc->malloc(sizeof(port));
+ if(pt==0) {
+ return 0;
+ }
+ start=sc->malloc(BLOCK_SIZE);
+ if(start==0) {
+ return 0;
+ }
+ memset(start,' ',BLOCK_SIZE-1);
+ start[BLOCK_SIZE-1]='\0';
+ pt->kind=port_string|port_output|port_srfi6;
+ pt->rep.string.start=start;
+ pt->rep.string.curr=start;
+ pt->rep.string.past_the_end=start+BLOCK_SIZE-1;
+ return pt;
+}
+
+static pointer port_from_scratch(scheme *sc) {
+ port *pt;
+ pt=port_rep_from_scratch(sc);
+ if(pt==0) {
+ return sc->NIL;
+ }
+ return mk_port(sc,pt);
+}
+
+static void port_close(scheme *sc, pointer p, int flag) {
+ port *pt=p->_object._port;
+ pt->kind&=~flag;
+ if((pt->kind & (port_input|port_output))==0) {
+ if(pt->kind&port_file) {
+
+#if SHOW_ERROR_LINE
+ /* Cleanup is here so (close-*-port) functions could work too */
+ pt->rep.stdio.curr_line = 0;
+
+ if(pt->rep.stdio.filename)
+ sc->free(pt->rep.stdio.filename);
+#endif
+
+ fclose(pt->rep.stdio.file);
+ }
+ pt->kind=port_free;
+ }
+}
+
+/* This routine will ignore byte sequences that are not valid UTF-8 */
+static gunichar basic_inchar(port *pt) {
+ if(pt->kind & port_file) {
+ int c;
+
+ c = fgetc(pt->rep.stdio.file);
+
+ while (TRUE)
+ {
+ if (c == EOF) return EOF;
+
+ if (c <= 0x7f)
+ return (gunichar) c;
+
+ /* Is this byte an invalid lead per RFC-3629? */
+ if (c < 0xc2 || c > 0xf4)
+ {
+ /* Ignore invalid lead byte and get the next character */
+ c = fgetc(pt->rep.stdio.file);
+ }
+ else /* Byte is valid lead */
+ {
+ unsigned char utf8[7];
+ int len;
+ int i;
+
+ utf8[0] = c; /* Save the lead byte */
+
+ len = utf8_length[c & 0x3F];
+ for (i = 1; i <= len; i++)
+ {
+ c = fgetc(pt->rep.stdio.file);
+
+ /* Stop reading if this is not a continuation character */
+ if ((c & 0xc0) != 0x80)
+ break;
+
+ utf8[i] = c;
+ }
+
+ if (i > len) /* Read the expected number of bytes? */
+ {
+ return g_utf8_get_char_validated ((char *) utf8,
+ sizeof(utf8));
+ }
+
+ /* Not enough continuation characters so ignore and restart */
+ }
+ } /* end of while (TRUE) */
+ } else {
+ gunichar c;
+ int len;
+
+ while (TRUE)
+ {
+ /* Found NUL or at end of input buffer? */
+ if (*pt->rep.string.curr == 0 ||
+ pt->rep.string.curr == pt->rep.string.past_the_end) {
+ return EOF;
+ }
+
+ len = pt->rep.string.past_the_end - pt->rep.string.curr;
+ c = g_utf8_get_char_validated(pt->rep.string.curr, len);
+
+ if (c != (gunichar) -1 &&
+ c != (gunichar) -2) /* Valid UTF-8 character? */
+ {
+ len = g_unichar_to_utf8(c, NULL); /* Length of UTF-8 sequence */
+ pt->rep.string.curr += len;
+ return c;
+ }
+
+ /* Look for next valid UTF-8 character in buffer */
+ pt->rep.string.curr = g_utf8_find_next_char(pt->rep.string.curr,
+ pt->rep.string.past_the_end);
+ } /* end of while (TRUE) */
+ }
+}
+
+/* get new character from input file */
+static gunichar inchar(scheme *sc) {
+ gunichar c;
+ port *pt;
+
+ pt = sc->inport->_object._port;
+ if(pt->kind & port_saw_EOF)
+ { return(EOF); }
+ if(pt->kind&port_file)
+ {
+ if (sc->bc_flag)
+ c = sc->backchar[--sc->bc_flag];
+ else
+ c = basic_inchar(pt);
+ }
+ else
+ c = basic_inchar(pt);
+ if(c == EOF && sc->inport == sc->loadport) {
+ /* Instead, set port_saw_EOF */
+ pt->kind |= port_saw_EOF;
+
+ /* file_pop(sc); */
+ return EOF;
+ /* NOTREACHED */
+ }
+ return c;
+}
+
+/* back character to input buffer */
+static void backchar(scheme *sc, gunichar c) {
+ port *pt;
+ gint charlen;
+
+ if(c==EOF) return;
+ charlen = g_unichar_to_utf8(c, NULL);
+ pt=sc->inport->_object._port;
+ if(pt->kind&port_file) {
+ if (sc->bc_flag < 2)
+ sc->backchar[sc->bc_flag++] = c;
+ } else {
+ if(pt->rep.string.curr!=pt->rep.string.start) {
+ if(pt->rep.string.curr-pt->rep.string.start >= charlen)
+ pt->rep.string.curr -= charlen;
+ else
+ pt->rep.string.curr = pt->rep.string.start;
+ }
+ }
+}
+
+static int realloc_port_string(scheme *sc, port *p)
+{
+ char *start=p->rep.string.start;
+ size_t new_size=p->rep.string.past_the_end-start+1+BLOCK_SIZE;
+ char *str=sc->malloc(new_size);
+ if(str) {
+ memset(str,' ',new_size-1);
+ str[new_size-1]='\0';
+ strcpy(str,start);
+ p->rep.string.start=str;
+ p->rep.string.past_the_end=str+new_size-1;
+ p->rep.string.curr-=start-str;
+ sc->free(start);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* len is number of UTF-8 characters in string pointed to by chars */
+static void putchars(scheme *sc, const char *chars, int char_cnt) {
+ int free_bytes; /* Space remaining in buffer (in bytes) */
+ int l;
+ port *pt=sc->outport->_object._port;
+
+ if (char_cnt <= 0)
+ return;
+
+ /* Get length of 'chars' in bytes */
+ char_cnt = g_utf8_offset_to_pointer(chars, (long)char_cnt) - chars;
+
+ if(pt->kind&port_file) {
+#if STANDALONE
+ fwrite(chars,1,char_cnt,pt->rep.stdio.file);
+ fflush(pt->rep.stdio.file);
+#else
+ /* If output is still directed to stdout (the default) it should be */
+ /* safe to redirect it to the registered output routine. */
+ if (pt->rep.stdio.file == stdout)
+ ts_output_string (TS_OUTPUT_NORMAL, chars, char_cnt);
+ else {
+ fwrite(chars,1,char_cnt,pt->rep.stdio.file);
+ fflush(pt->rep.stdio.file);
+ }
+#endif
+ } else {
+ if (pt->rep.string.past_the_end != pt->rep.string.curr)
+ {
+ free_bytes = pt->rep.string.past_the_end - pt->rep.string.curr;
+ l = min(char_cnt, free_bytes);
+ memcpy(pt->rep.string.curr, chars, l);
+ pt->rep.string.curr += l;
+ }
+ else if(pt->kind&port_srfi6&&realloc_port_string(sc,pt))
+ {
+ free_bytes = pt->rep.string.past_the_end - pt->rep.string.curr;
+ l = min(char_cnt, free_bytes);
+ memcpy(pt->rep.string.curr, chars, char_cnt);
+ pt->rep.string.curr += l;
+ }
+ }
+}
+
+INTERFACE void putcharacter(scheme *sc, gunichar c) {
+ char utf8[7];
+
+ (void)g_unichar_to_utf8(c, utf8);
+ putchars(sc, utf8, 1);
+}
+
+INTERFACE void putstr(scheme *sc, const char *s) {
+ putchars(sc, s, g_utf8_strlen(s, -1));
+}
+
+/* read characters up to delimiter, but cater to character constants */
+static char *readstr_upto(scheme *sc, char *delim) {
+ char *p = sc->strbuff;
+ gunichar c = 0;
+ gunichar c_prev = 0;
+ int len = 0;
+
+ do {
+ c_prev = c;
+ c = inchar(sc);
+ len = g_unichar_to_utf8(c, p);
+ p += len;
+ } while ((p - sc->strbuff < sizeof(sc->strbuff)) &&
+ (c && !is_one_of(delim, c)));
+
+ if(p == sc->strbuff+2 && c_prev == '\\')
+ *p = '\0';
+ else
+ {
+ backchar(sc,c); /* put back the delimiter */
+ p[-len] = '\0';
+ }
+ return sc->strbuff;
+}
+
+/* read string expression "xxx...xxx" */
+static pointer readstrexp(scheme *sc) {
+ char *p = sc->strbuff;
+ gunichar c;
+ int c1=0;
+ int len;
+ enum { st_ok, st_bsl, st_x1, st_x2, st_oct1, st_oct2 } state=st_ok;
+
+ for (;;) {
+ c=inchar(sc);
+ if(c == EOF || p-sc->strbuff > sizeof(sc->strbuff)-1) {
+ return sc->F;
+ }
+ switch(state) {
+ case st_ok:
+ switch(c) {
+ case '\\':
+ state=st_bsl;
+ break;
+ case '"':
+ *p=0;
+ return mk_counted_string(sc,sc->strbuff,
+ g_utf8_strlen(sc->strbuff, sizeof(sc->strbuff)));
+ default:
+ len = g_unichar_to_utf8(c, p);
+ p += len;
+ break;
+ }
+ break;
+ case st_bsl:
+ switch(c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state=st_oct1;
+ c1=g_unichar_digit_value(c);
+ break;
+ case 'x':
+ case 'X':
+ state=st_x1;
+ c1=0;
+ break;
+ case 'n':
+ *p++='\n';
+ state=st_ok;
+ break;
+ case 't':
+ *p++='\t';
+ state=st_ok;
+ break;
+ case 'r':
+ *p++='\r';
+ state=st_ok;
+ break;
+ case '"':
+ *p++='"';
+ state=st_ok;
+ break;
+ default:
+ len = g_unichar_to_utf8(c, p);
+ p += len;
+ state=st_ok;
+ break;
+ }
+ break;
+ case st_x1:
+ case st_x2:
+ if (!g_unichar_isxdigit(c))
+ return sc->F;
+ c1=(c1<<4)+g_unichar_xdigit_value(c);
+ if(state==st_x1)
+ state=st_x2;
+ else {
+ *p++=c1;
+ state=st_ok;
+ }
+ break;
+ case st_oct1: /* State when handling second octal digit */
+ case st_oct2: /* State when handling third octal digit */
+ if (!g_unichar_isdigit(c) || g_unichar_digit_value(c) > 7)
+ {
+ *p++=c1;
+ backchar(sc, c);
+ state=st_ok;
+ }
+ else
+ {
+ /* Is value of three character octal too big for a byte? */
+ if (state==st_oct2 && c1 >= 32)
+ return sc->F;
+
+ c1=(c1<<3)+g_unichar_digit_value(c);
+
+ if (state == st_oct1)
+ state=st_oct2;
+ else
+ {
+ *p++=c1;
+ state=st_ok;
+ }
+ }
+ break;
+ }
+ }
+}
+
+/* check c is in chars */
+static INLINE int is_one_of(char *s, gunichar c) {
+ if (c==EOF)
+ return 1;
+
+ if (g_utf8_strchr(s, -1, c) != NULL)
+ return (1);
+
+ return (0);
+}
+
+/* skip white characters */
+static INLINE int skipspace(scheme *sc) {
+ gunichar c;
+ int curr_line = 0;
+ do {
+ c=inchar(sc);
+#if SHOW_ERROR_LINE
+ if(c=='\n')
+ curr_line++;
+#endif
+ } while (g_unichar_isspace(c));
+
+/* record it */
+#if SHOW_ERROR_LINE
+ if (sc->load_stack[sc->file_i].kind & port_file)
+ sc->load_stack[sc->file_i].rep.stdio.curr_line += curr_line;
+#endif
+
+ if(c!=EOF) {
+ backchar(sc,c);
+ return 1;
+ }
+ else
+ { return EOF; }
+}
+
+/* get token */
+static int token(scheme *sc) {
+ gunichar c;
+ c = skipspace(sc);
+ if(c == EOF) { return (TOK_EOF); }
+ switch (c=inchar(sc)) {
+ case EOF:
+ return (TOK_EOF);
+ case '(':
+ return (TOK_LPAREN);
+ case ')':
+ return (TOK_RPAREN);
+ case '.':
+ c=inchar(sc);
+ if(is_one_of(" \n\t",c)) {
+ return (TOK_DOT);
+ } else {
+ backchar(sc,c);
+ backchar(sc,'.');
+ return TOK_ATOM;
+ }
+ case '\'':
+ return (TOK_QUOTE);
+ case ';':
+ while ((c=inchar(sc)) != '\n' && c!=EOF)
+ ;
+
+#if SHOW_ERROR_LINE
+ if(c == '\n' && sc->load_stack[sc->file_i].kind & port_file)
+ sc->load_stack[sc->file_i].rep.stdio.curr_line++;
+#endif
+
+ if(c == EOF)
+ { return (TOK_EOF); }
+ else
+ { return (token(sc));}
+ case '"':
+ return (TOK_DQUOTE);
+ case '_':
+ if ((c=inchar(sc)) == '"')
+ return (TOK_USCORE);
+ backchar(sc,c);
+ return (TOK_ATOM);
+ case BACKQUOTE:
+ return (TOK_BQUOTE);
+ case ',':
+ if ((c=inchar(sc)) == '@') {
+ return (TOK_ATMARK);
+ } else {
+ backchar(sc,c);
+ return (TOK_COMMA);
+ }
+ case '#':
+ c=inchar(sc);
+ if (c == '(') {
+ return (TOK_VEC);
+ } else if(c == '!') {
+ while ((c=inchar(sc)) != '\n' && c!=EOF)
+ ;
+
+#if SHOW_ERROR_LINE
+ if(c == '\n' && sc->load_stack[sc->file_i].kind & port_file)
+ sc->load_stack[sc->file_i].rep.stdio.curr_line++;
+#endif
+
+ if(c == EOF)
+ { return (TOK_EOF); }
+ else
+ { return (token(sc));}
+ } else {
+ backchar(sc,c);
+ if(is_one_of(" tfodxb\\",c)) {
+ return TOK_SHARP_CONST;
+ } else {
+ return (TOK_SHARP);
+ }
+ }
+ default:
+ backchar(sc,c);
+ return (TOK_ATOM);
+ }
+}
+
+/* ========== Routines for Printing ========== */
+#define ok_abbrev(x) (is_pair(x) && cdr(x) == sc->NIL)
+
+static void printslashstring(scheme *sc, char *p, int len) {
+ int i;
+ gunichar c;
+ char *s=(char*)p;
+
+ putcharacter(sc,'"');
+ for (i=0; i<len; i++) {
+ c = g_utf8_get_char(s);
+ /* Is a check for a value of 0xff still valid in UTF8?? ~~~~~ */
+ if(c==0xff || c=='"' || c<' ' || c=='\\') {
+ putcharacter(sc,'\\');
+ switch(c) {
+ case '"':
+ putcharacter(sc,'"');
+ break;
+ case '\n':
+ putcharacter(sc,'n');
+ break;
+ case '\t':
+ putcharacter(sc,'t');
+ break;
+ case '\r':
+ putcharacter(sc,'r');
+ break;
+ case '\\':
+ putcharacter(sc,'\\');
+ break;
+ default: {
+ /* This still needs work ~~~~~ */
+ int d=c/16;
+ putcharacter(sc,'x');
+ if(d<10) {
+ putcharacter(sc,d+'0');
+ } else {
+ putcharacter(sc,d-10+'A');
+ }
+ d=c%16;
+ if(d<10) {
+ putcharacter(sc,d+'0');
+ } else {
+ putcharacter(sc,d-10+'A');
+ }
+ }
+ }
+ } else {
+ putcharacter(sc,c);
+ }
+ s = g_utf8_next_char(s);
+ }
+ putcharacter(sc,'"');
+}
+
+
+/* print atoms */
+static void printatom(scheme *sc, pointer l, int f) {
+ char *p;
+ int len;
+ atom2str(sc,l,f,&p,&len);
+ putchars(sc,p,len);
+}
+
+
+/* Uses internal buffer unless string pointer is already available */
+static void atom2str(scheme *sc, pointer l, int f, char **pp, int *plen) {
+ char *p;
+
+ if (l == sc->NIL) {
+ p = "()";
+ } else if (l == sc->T) {
+ p = "#t";
+ } else if (l == sc->F) {
+ p = "#f";
+ } else if (l == sc->EOF_OBJ) {
+ p = "#<EOF>";
+ } else if (is_port(l)) {
+ p = "#<PORT>";
+ } else if (is_number(l)) {
+ p = sc->strbuff;
+ if (f <= 1 || f == 10) /* f is the base for numbers if > 1 */ {
+ if(num_is_integer(l)) {
+ snprintf(p, STRBUFFSIZE, "%ld", ivalue_unchecked(l));
+ } else {
+ snprintf(p, STRBUFFSIZE, "%.10g", rvalue_unchecked(l));
+ /* r5rs says there must be a '.' (unless 'e'?) */
+ f = strcspn(p, ".e");
+ if (p[f] == 0) {
+ p[f] = '.'; /* not found, so add '.0' at the end */
+ p[f+1] = '0';
+ p[f+2] = 0;
+ }
+ }
+ } else {
+ long v = ivalue(l);
+ if (f == 16) {
+ if (v >= 0)
+ snprintf(p, STRBUFFSIZE, "%lx", v);
+ else
+ snprintf(p, STRBUFFSIZE, "-%lx", -v);
+ } else if (f == 8) {
+ if (v >= 0)
+ snprintf(p, STRBUFFSIZE, "%lo", v);
+ else
+ snprintf(p, STRBUFFSIZE, "-%lo", -v);
+ } else if (f == 2) {
+ unsigned long b = (v < 0) ? -v : v;
+ p = &p[STRBUFFSIZE-1];
+ *p = 0;
+ do { *--p = (b&1) ? '1' : '0'; b >>= 1; } while (b != 0);
+ if (v < 0) *--p = '-';
+ }
+ }
+ } else if (is_string(l)) {
+ if (!f) {
+ p = strvalue(l);
+ } else { /* Hack, uses the fact that printing is needed */
+ *pp=sc->strbuff;
+ *plen=0;
+ printslashstring(sc, strvalue(l),
+ g_utf8_strlen(strvalue(l), -1));
+ return;
+ }
+ } else if (is_character(l)) {
+ gunichar c=charvalue(l);
+ p = sc->strbuff;
+ if (!f) {
+ int len = g_unichar_to_utf8(c, p);
+ p[len]=0;
+ } else {
+ switch(c) {
+ case ' ':
+ p = "#\\space";
+ break;
+ case '\n':
+ p = "#\\newline";
+ break;
+ case '\r':
+ p = "#\\return";
+ break;
+ case '\t':
+ p = "#\\tab";
+ break;
+ default:
+#if USE_ASCII_NAMES
+ if(c==127) {
+ p = "#\\del";
+ break;
+ } else if(c<32) {
+ snprintf(p,STRBUFFSIZE, "#\\%s", charnames[c]);
+ break;
+ }
+#else
+ if(c<32) {
+ snprintf(p,STRBUFFSIZE,"#\\x%x",c);
+ break;
+ }
+#endif
+ snprintf(p,STRBUFFSIZE,"#\\%c",c);
+ break;
+ }
+ }
+ } else if (is_symbol(l)) {
+ p = symname(l);
+ } else if (is_proc(l)) {
+ p = sc->strbuff;
+ snprintf(p,STRBUFFSIZE,"#<%s PROCEDURE %ld>",
+ procname(l),procnum(l));
+ } else if (is_macro(l)) {
+ p = "#<MACRO>";
+ } else if (is_closure(l)) {
+ p = "#<CLOSURE>";
+ } else if (is_promise(l)) {
+ p = "#<PROMISE>";
+ } else if (is_foreign(l)) {
+ p = sc->strbuff;
+ snprintf(p,STRBUFFSIZE,"#<FOREIGN PROCEDURE %ld>", procnum(l));
+ } else if (is_continuation(l)) {
+ p = "#<CONTINUATION>";
+ } else {
+ p = "#<ERROR>";
+ }
+ *pp=p;
+ *plen=g_utf8_strlen(p, -1);
+}
+/* ========== Routines for Evaluation Cycle ========== */
+
+/* make closure. c is code. e is environment */
+static pointer mk_closure(scheme *sc, pointer c, pointer e) {
+ pointer x = get_cell(sc, c, e);
+
+ typeflag(x) = T_CLOSURE;
+ car(x) = c;
+ cdr(x) = e;
+ return (x);
+}
+
+/* make continuation. */
+static pointer mk_continuation(scheme *sc, pointer d) {
+ pointer x = get_cell(sc, sc->NIL, d);
+
+ typeflag(x) = T_CONTINUATION;
+ cont_dump(x) = d;
+ return (x);
+}
+
+static pointer list_star(scheme *sc, pointer d) {
+ pointer p, q;
+ if(cdr(d)==sc->NIL) {
+ return car(d);
+ }
+ p=cons(sc,car(d),cdr(d));
+ q=p;
+ while(cdr(cdr(p))!=sc->NIL) {
+ d=cons(sc,car(p),cdr(p));
+ if(cdr(cdr(p))!=sc->NIL) {
+ p=cdr(d);
+ }
+ }
+ cdr(p)=car(cdr(p));
+ return q;
+}
+
+/* reverse list -- produce new list */
+static pointer reverse(scheme *sc, pointer a) {
+/* a must be checked by gc */
+ pointer p = sc->NIL;
+
+ for ( ; is_pair(a); a = cdr(a)) {
+ p = cons(sc, car(a), p);
+ }
+ return (p);
+}
+
+/* reverse list --- in-place */
+static pointer reverse_in_place(scheme *sc, pointer term, pointer list) {
+ pointer p = list, result = term, q;
+
+ while (p != sc->NIL) {
+ q = cdr(p);
+ cdr(p) = result;
+ result = p;
+ p = q;
+ }
+ return (result);
+}
+
+/* append list -- produce new list */
+static pointer revappend(scheme *sc, pointer a, pointer b) {
+ pointer result = a;
+ pointer p = b;
+
+ while (is_pair(p)) {
+ result = cons(sc, car(p), result);
+ p = cdr(p);
+ }
+
+ if (p == sc->NIL) {
+ return result;
+ }
+
+ return sc->F; /* signal an error */
+}
+
+/* equivalence of atoms */
+int eqv(pointer a, pointer b) {
+ if (is_string(a)) {
+ if (is_string(b))
+ return (strvalue(a) == strvalue(b));
+ else
+ return (0);
+ } else if (is_number(a)) {
+ if (is_number(b)) {
+ if (num_is_integer(a) == num_is_integer(b))
+ return num_eq(nvalue(a),nvalue(b));
+ }
+ return (0);
+ } else if (is_character(a)) {
+ if (is_character(b))
+ return charvalue(a)==charvalue(b);
+ else
+ return (0);
+ } else if (is_port(a)) {
+ if (is_port(b))
+ return a==b;
+ else
+ return (0);
+ } else if (is_proc(a)) {
+ if (is_proc(b))
+ return procnum(a)==procnum(b);
+ else
+ return (0);
+ } else {
+ return (a == b);
+ }
+}
+
+/* true or false value macro */
+/* () is #t in R5RS */
+#define is_true(p) ((p) != sc->F)
+#define is_false(p) ((p) == sc->F)
+
+/* ========== Environment implementation ========== */
+
+#if !defined(USE_ALIST_ENV) || !defined(USE_OBJECT_LIST)
+
+static int hash_fn(const char *key, int table_size)
+{
+ unsigned int hashed = 0;
+ const char *c;
+ int bits_per_int = sizeof(unsigned int)*8;
+
+ for (c = key; *c; c++) {
+ /* letters have about 5 bits in them */
+ hashed = (hashed<<5) | (hashed>>(bits_per_int-5));
+ hashed ^= *c;
+ }
+ return hashed % table_size;
+}
+#endif
+
+#ifndef USE_ALIST_ENV
+
+/*
+ * In this implementation, each frame of the environment may be
+ * a hash table: a vector of alists hashed by variable name.
+ * In practice, we use a vector only for the initial frame;
+ * subsequent frames are too small and transient for the lookup
+ * speed to out-weigh the cost of making a new vector.
+ */
+
+static void new_frame_in_env(scheme *sc, pointer old_env)
+{
+ pointer new_frame;
+
+ /* The interaction-environment has about 300 variables in it. */
+ if (old_env == sc->NIL) {
+ new_frame = mk_vector(sc, 461);
+ } else {
+ new_frame = sc->NIL;
+ }
+
+ sc->envir = immutable_cons(sc, new_frame, old_env);
+ setenvironment(sc->envir);
+}
+
+static INLINE void new_slot_spec_in_env(scheme *sc, pointer env,
+ pointer variable, pointer value)
+{
+ pointer slot = immutable_cons(sc, variable, value);
+
+ if (is_vector(car(env))) {
+ int location = hash_fn(symname(variable), ivalue_unchecked(car(env)));
+
+ set_vector_elem(car(env), location,
+ immutable_cons(sc, slot, vector_elem(car(env), location)));
+ } else {
+ car(env) = immutable_cons(sc, slot, car(env));
+ }
+}
+
+static pointer find_slot_in_env(scheme *sc, pointer env, pointer hdl, int all)
+{
+ pointer x,y;
+ int location;
+
+ for (x = env; x != sc->NIL; x = cdr(x)) {
+ if (is_vector(car(x))) {
+ location = hash_fn(symname(hdl), ivalue_unchecked(car(x)));
+ y = vector_elem(car(x), location);
+ } else {
+ y = car(x);
+ }
+ for ( ; y != sc->NIL; y = cdr(y)) {
+ if (caar(y) == hdl) {
+ break;
+ }
+ }
+ if (y != sc->NIL) {
+ break;
+ }
+ if(!all) {
+ return sc->NIL;
+ }
+ }
+ if (x != sc->NIL) {
+ return car(y);
+ }
+ return sc->NIL;
+}
+
+#else /* USE_ALIST_ENV */
+
+static INLINE void new_frame_in_env(scheme *sc, pointer old_env)
+{
+ sc->envir = immutable_cons(sc, sc->NIL, old_env);
+ setenvironment(sc->envir);
+}
+
+static INLINE void new_slot_spec_in_env(scheme *sc, pointer env,
+ pointer variable, pointer value)
+{
+ car(env) = immutable_cons(sc, immutable_cons(sc, variable, value), car(env));
+}
+
+static pointer find_slot_in_env(scheme *sc, pointer env, pointer hdl, int all)
+{
+ pointer x,y;
+ for (x = env; x != sc->NIL; x = cdr(x)) {
+ for (y = car(x); y != sc->NIL; y = cdr(y)) {
+ if (caar(y) == hdl) {
+ break;
+ }
+ }
+ if (y != sc->NIL) {
+ break;
+ }
+ if(!all) {
+ return sc->NIL;
+ }
+ }
+ if (x != sc->NIL) {
+ return car(y);
+ }
+ return sc->NIL;
+}
+
+#endif /* USE_ALIST_ENV else */
+
+static INLINE void new_slot_in_env(scheme *sc, pointer variable, pointer value)
+{
+ new_slot_spec_in_env(sc, sc->envir, variable, value);
+}
+
+static INLINE void set_slot_in_env(scheme *sc, pointer slot, pointer value)
+{
+ cdr(slot) = value;
+}
+
+static INLINE pointer slot_value_in_env(pointer slot)
+{
+ return cdr(slot);
+}
+
+/* ========== Evaluation Cycle ========== */
+
+
+static pointer _Error_1(scheme *sc, const char *s, pointer a) {
+ const char *str = s;
+#if USE_ERROR_HOOK
+ pointer x;
+ pointer hdl=sc->ERROR_HOOK;
+#endif
+
+#if SHOW_ERROR_LINE
+ char sbuf[STRBUFFSIZE];
+
+ /* make sure error is not in REPL */
+ if (sc->load_stack[sc->file_i].kind & port_file &&
+ sc->load_stack[sc->file_i].rep.stdio.file != stdin) {
+ int ln = sc->load_stack[sc->file_i].rep.stdio.curr_line;
+ const char *fname = sc->load_stack[sc->file_i].rep.stdio.filename;
+
+ /* should never happen */
+ if(!fname) fname = "<unknown>";
+
+ /* we started from 0 */
+ ln++;
+ snprintf(sbuf, STRBUFFSIZE, "(%s : %i) %s", fname, ln, s);
+
+ str = (const char*)sbuf;
+ }
+#endif
+
+#if USE_ERROR_HOOK
+ x=find_slot_in_env(sc,sc->envir,hdl,1);
+ if (x != sc->NIL) {
+ if(a!=0) {
+ sc->code = cons(sc, cons(sc, sc->QUOTE, cons(sc,(a), sc->NIL)), sc->NIL);
+ } else {
+ sc->code = sc->NIL;
+ }
+ sc->code = cons(sc, mk_string(sc, str), sc->code);
+ setimmutable(car(sc->code));
+ sc->code = cons(sc, slot_value_in_env(x), sc->code);
+ sc->op = (int)OP_EVAL;
+ return sc->T;
+ }
+#endif
+
+ if(a!=0) {
+ sc->args = cons(sc, (a), sc->NIL);
+ } else {
+ sc->args = sc->NIL;
+ }
+ sc->args = cons(sc, mk_string(sc, str), sc->args);
+ setimmutable(car(sc->args));
+ sc->op = (int)OP_ERR0;
+ return sc->T;
+}
+#define Error_1(sc,s,a) return _Error_1(sc,s,a)
+#define Error_0(sc,s) return _Error_1(sc,s,0)
+
+/* Too small to turn into function */
+# define BEGIN do {
+# define END } while (0)
+#define s_goto(sc,a) BEGIN \
+ sc->op = (int)(a); \
+ return sc->T; END
+
+#define s_return(sc,a) return _s_return(sc,a)
+
+#ifndef USE_SCHEME_STACK
+
+/* this structure holds all the interpreter's registers */
+struct dump_stack_frame {
+ enum scheme_opcodes op;
+ pointer args;
+ pointer envir;
+ pointer code;
+};
+
+#define STACK_GROWTH 3
+
+static void s_save(scheme *sc, enum scheme_opcodes op, pointer args, pointer code)
+{
+ int nframes = (int)sc->dump;
+ struct dump_stack_frame *next_frame;
+
+ /* enough room for the next frame? */
+ if (nframes >= sc->dump_size) {
+ sc->dump_size += STACK_GROWTH;
+ /* alas there is no sc->realloc */
+ sc->dump_base = realloc(sc->dump_base,
+ sizeof(struct dump_stack_frame) * sc->dump_size);
+ }
+ next_frame = (struct dump_stack_frame *)sc->dump_base + nframes;
+ next_frame->op = op;
+ next_frame->args = args;
+ next_frame->envir = sc->envir;
+ next_frame->code = code;
+ sc->dump = (pointer)(nframes+1);
+}
+
+static pointer _s_return(scheme *sc, pointer a)
+{
+ int nframes = (int)sc->dump;
+ struct dump_stack_frame *frame;
+
+ sc->value = (a);
+ if (nframes <= 0) {
+ return sc->NIL;
+ }
+ nframes--;
+ frame = (struct dump_stack_frame *)sc->dump_base + nframes;
+ sc->op = frame->op;
+ sc->args = frame->args;
+ sc->envir = frame->envir;
+ sc->code = frame->code;
+ sc->dump = (pointer)nframes;
+ return sc->T;
+}
+
+static INLINE void dump_stack_reset(scheme *sc)
+{
+ /* in this implementation, sc->dump is the number of frames on the stack */
+ sc->dump = (pointer)0;
+}
+
+static INLINE void dump_stack_initialize(scheme *sc)
+{
+ sc->dump_size = 0;
+ sc->dump_base = NULL;
+ dump_stack_reset(sc);
+}
+
+static void dump_stack_free(scheme *sc)
+{
+ free(sc->dump_base);
+ sc->dump_base = NULL;
+ sc->dump = (pointer)0;
+ sc->dump_size = 0;
+}
+
+static INLINE void dump_stack_mark(scheme *sc)
+{
+ int nframes = (int)sc->dump;
+ int i;
+ for(i=0; i<nframes; i++) {
+ struct dump_stack_frame *frame;
+ frame = (struct dump_stack_frame *)sc->dump_base + i;
+ mark(frame->args);
+ mark(frame->envir);
+ mark(frame->code);
+ }
+}
+
+#else
+
+static INLINE void dump_stack_reset(scheme *sc)
+{
+ sc->dump = sc->NIL;
+}
+
+static INLINE void dump_stack_initialize(scheme *sc)
+{
+ dump_stack_reset(sc);
+}
+
+static void dump_stack_free(scheme *sc)
+{
+ sc->dump = sc->NIL;
+}
+
+static pointer _s_return(scheme *sc, pointer a) {
+ sc->value = (a);
+ if(sc->dump==sc->NIL) return sc->NIL;
+ sc->op = ivalue(car(sc->dump));
+ sc->args = cadr(sc->dump);
+ sc->envir = caddr(sc->dump);
+ sc->code = cadddr(sc->dump);
+ sc->dump = cddddr(sc->dump);
+ return sc->T;
+}
+
+static void s_save(scheme *sc, enum scheme_opcodes op, pointer args, pointer code) {
+ sc->dump = cons(sc, sc->envir, cons(sc, (code), sc->dump));
+ sc->dump = cons(sc, (args), sc->dump);
+ sc->dump = cons(sc, mk_integer(sc, (long)(op)), sc->dump);
+}
+
+static INLINE void dump_stack_mark(scheme *sc)
+{
+ mark(sc->dump);
+}
+#endif
+
+#define s_retbool(tf) s_return(sc,(tf) ? sc->T : sc->F)
+
+static pointer opexe_0(scheme *sc, enum scheme_opcodes op) {
+ pointer x, y;
+
+ switch (op) {
+ case OP_LOAD: /* load */
+ if(file_interactive(sc)) {
+ fprintf(sc->outport->_object._port->rep.stdio.file,
+ "Loading %s\n", strvalue(car(sc->args)));
+ }
+ if (!file_push(sc,strvalue(car(sc->args)))) {
+ Error_1(sc,"unable to open", car(sc->args));
+ }
+ else
+ {
+ sc->args = mk_integer(sc,sc->file_i);
+ s_goto(sc,OP_T0LVL);
+ }
+
+ case OP_T0LVL: /* top level */
+ /* If we reached the end of file, this loop is done. */
+ if(sc->loadport->_object._port->kind & port_saw_EOF)
+ {
+ if(sc->file_i == 0)
+ {
+ sc->args=sc->NIL;
+ s_goto(sc,OP_QUIT);
+ }
+ else
+ {
+ file_pop(sc);
+ s_return(sc,sc->value);
+ }
+ /* NOTREACHED */
+ }
+
+ /* If interactive, be nice to user. */
+ if(file_interactive(sc))
+ {
+ sc->envir = sc->global_env;
+ dump_stack_reset(sc);
+ putstr(sc,"\n");
+ putstr(sc,prompt);
+ }
+
+ /* Set up another iteration of REPL */
+ sc->nesting=0;
+ sc->save_inport=sc->inport;
+ sc->inport = sc->loadport;
+ s_save(sc,OP_T0LVL, sc->NIL, sc->NIL);
+ s_save(sc,OP_VALUEPRINT, sc->NIL, sc->NIL);
+ s_save(sc,OP_T1LVL, sc->NIL, sc->NIL);
+ s_goto(sc,OP_READ_INTERNAL);
+
+ case OP_T1LVL: /* top level */
+ sc->code = sc->value;
+ sc->inport=sc->save_inport;
+ s_goto(sc,OP_EVAL);
+
+ case OP_READ_INTERNAL: /* internal read */
+ sc->tok = token(sc);
+ if(sc->tok==TOK_EOF)
+ { s_return(sc,sc->EOF_OBJ); }
+ s_goto(sc,OP_RDSEXPR);
+
+ case OP_GENSYM:
+ s_return(sc, gensym(sc));
+
+ case OP_VALUEPRINT: /* print evaluation result */
+ /* OP_VALUEPRINT is always pushed, because when changing from
+ non-interactive to interactive mode, it needs to be
+ already on the stack */
+ if(sc->tracing) {
+ putstr(sc,"\nGives: ");
+ }
+ if(file_interactive(sc) || sc->print_output) {
+ sc->print_flag = 1;
+ sc->args = sc->value;
+ s_goto(sc,OP_P0LIST);
+ } else {
+ s_return(sc,sc->value);
+ }
+
+ case OP_EVAL: /* main part of evaluation */
+#if USE_TRACING
+ if(sc->tracing) {
+ /*s_save(sc,OP_VALUEPRINT,sc->NIL,sc->NIL);*/
+ s_save(sc,OP_REAL_EVAL,sc->args,sc->code);
+ sc->args=sc->code;
+ putstr(sc,"\nEval: ");
+ s_goto(sc,OP_P0LIST);
+ }
+ /* fall through */
+ case OP_REAL_EVAL:
+#endif
+ if (is_symbol(sc->code)) { /* symbol */
+ x=find_slot_in_env(sc,sc->envir,sc->code,1);
+ if (x != sc->NIL) {
+ s_return(sc,slot_value_in_env(x));
+ } else {
+ Error_1(sc,"eval: unbound variable:", sc->code);
+ }
+ } else if (is_pair(sc->code)) {
+ if (is_syntax(x = car(sc->code))) { /* SYNTAX */
+ sc->code = cdr(sc->code);
+ s_goto(sc,syntaxnum(x));
+ } else {/* first, eval top element and eval arguments */
+ s_save(sc,OP_E0ARGS, sc->NIL, sc->code);
+ /* If no macros => s_save(sc,OP_E1ARGS, sc->NIL, cdr(sc->code));*/
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+ }
+ } else {
+ s_return(sc,sc->code);
+ }
+
+ case OP_E0ARGS: /* eval arguments */
+ if (is_macro(sc->value)) { /* macro expansion */
+ s_save(sc,OP_DOMACRO, sc->NIL, sc->NIL);
+ sc->args = cons(sc,sc->code, sc->NIL);
+ sc->code = sc->value;
+ s_goto(sc,OP_APPLY);
+ } else {
+ sc->code = cdr(sc->code);
+ s_goto(sc,OP_E1ARGS);
+ }
+
+ case OP_E1ARGS: /* eval arguments */
+ sc->args = cons(sc, sc->value, sc->args);
+ if (is_pair(sc->code)) { /* continue */
+ s_save(sc,OP_E1ARGS, sc->args, cdr(sc->code));
+ sc->code = car(sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_EVAL);
+ } else { /* end */
+ sc->args = reverse_in_place(sc, sc->NIL, sc->args);
+ sc->code = car(sc->args);
+ sc->args = cdr(sc->args);
+ s_goto(sc,OP_APPLY);
+ }
+
+#if USE_TRACING
+ case OP_TRACING: {
+ int tr=sc->tracing;
+ sc->tracing=ivalue(car(sc->args));
+ s_return(sc,mk_integer(sc,tr));
+ }
+#endif
+
+ case OP_APPLY: /* apply 'code' to 'args' */
+#if USE_TRACING
+ if(sc->tracing) {
+ s_save(sc,OP_REAL_APPLY,sc->args,sc->code);
+ sc->print_flag = 1;
+ /* sc->args=cons(sc,sc->code,sc->args);*/
+ putstr(sc,"\nApply to: ");
+ s_goto(sc,OP_P0LIST);
+ }
+ /* fall through */
+ case OP_REAL_APPLY:
+#endif
+ if (is_proc(sc->code)) {
+ s_goto(sc,procnum(sc->code)); /* PROCEDURE */
+ } else if (is_foreign(sc->code))
+ {
+ /* Keep nested calls from GC'ing the arglist */
+ push_recent_alloc(sc,sc->args,sc->NIL);
+ sc->foreign_error = sc->NIL;
+ x=sc->code->_object._ff(sc,sc->args);
+ if (sc->foreign_error == sc->NIL) {
+ s_return(sc,x);
+ } else {
+ x = sc->foreign_error;
+ sc->foreign_error = sc->NIL;
+ Error_1 (sc, string_value (car (x)), cdr (x));
+ }
+ } else if (is_closure(sc->code) || is_macro(sc->code)
+ || is_promise(sc->code)) { /* CLOSURE */
+ /* Should not accept promise */
+ /* make environment */
+ new_frame_in_env(sc, closure_env(sc->code));
+ for (x = car(closure_code(sc->code)), y = sc->args;
+ is_pair(x); x = cdr(x), y = cdr(y)) {
+ if (y == sc->NIL) {
+ Error_0(sc,"not enough arguments");
+ } else {
+ new_slot_in_env(sc, car(x), car(y));
+ }
+ }
+ if (x == sc->NIL) {
+ /*--
+ * if (y != sc->NIL) {
+ * Error_0(sc,"too many arguments");
+ * }
+ */
+ } else if (is_symbol(x))
+ new_slot_in_env(sc, x, y);
+ else {
+ Error_1(sc,"syntax error in closure: not a symbol:", x);
+ }
+ sc->code = cdr(closure_code(sc->code));
+ sc->args = sc->NIL;
+ s_goto(sc,OP_BEGIN);
+ } else if (is_continuation(sc->code)) { /* CONTINUATION */
+ sc->dump = cont_dump(sc->code);
+ s_return(sc,sc->args != sc->NIL ? car(sc->args) : sc->NIL);
+ } else {
+ Error_0(sc,"illegal function");
+ }
+
+ case OP_DOMACRO: /* do macro */
+ sc->code = sc->value;
+ s_goto(sc,OP_EVAL);
+
+#if 1
+ case OP_LAMBDA: /* lambda */
+ /* If the hook is defined, apply it to sc->code, otherwise
+ set sc->value fall thru */
+ {
+ pointer f=find_slot_in_env(sc,sc->envir,sc->COMPILE_HOOK,1);
+ if(f==sc->NIL) {
+ sc->value = sc->code;
+ /* Fallthru */
+ } else {
+ s_save(sc,OP_LAMBDA1,sc->args,sc->code);
+ sc->args=cons(sc,sc->code,sc->NIL);
+ sc->code=slot_value_in_env(f);
+ s_goto(sc,OP_APPLY);
+ }
+ }
+
+ case OP_LAMBDA1:
+ s_return(sc,mk_closure(sc, sc->value, sc->envir));
+
+#else
+ case OP_LAMBDA: /* lambda */
+ s_return(sc,mk_closure(sc, sc->code, sc->envir));
+
+#endif
+
+ case OP_MKCLOSURE: /* make-closure */
+ x=car(sc->args);
+ if(car(x)==sc->LAMBDA) {
+ x=cdr(x);
+ }
+ if(cdr(sc->args)==sc->NIL) {
+ y=sc->envir;
+ } else {
+ y=cadr(sc->args);
+ }
+ s_return(sc,mk_closure(sc, x, y));
+
+ case OP_QUOTE: /* quote */
+ s_return(sc,car(sc->code));
+
+ case OP_DEF0: /* define */
+ if(is_immutable(car(sc->code)))
+ Error_1(sc,"define: unable to alter immutable", car(sc->code));
+
+ if (is_pair(car(sc->code))) {
+ x = caar(sc->code);
+ sc->code = cons(sc, sc->LAMBDA, cons(sc, cdar(sc->code), cdr(sc->code)));
+ } else {
+ x = car(sc->code);
+ sc->code = cadr(sc->code);
+ }
+ if (!is_symbol(x)) {
+ Error_0(sc,"variable is not a symbol");
+ }
+ s_save(sc,OP_DEF1, sc->NIL, x);
+ s_goto(sc,OP_EVAL);
+
+ case OP_DEF1: /* define */
+ x=find_slot_in_env(sc,sc->envir,sc->code,0);
+ if (x != sc->NIL) {
+ set_slot_in_env(sc, x, sc->value);
+ } else {
+ new_slot_in_env(sc, sc->code, sc->value);
+ }
+ s_return(sc,sc->code);
+
+
+ case OP_DEFP: /* defined? */
+ x=sc->envir;
+ if(cdr(sc->args)!=sc->NIL) {
+ x=cadr(sc->args);
+ }
+ s_retbool(find_slot_in_env(sc,x,car(sc->args),1)!=sc->NIL);
+
+ case OP_SET0: /* set! */
+ if(is_immutable(car(sc->code)))
+ Error_1(sc,"set!: unable to alter immutable variable",car(sc->code));
+ s_save(sc,OP_SET1, sc->NIL, car(sc->code));
+ sc->code = cadr(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_SET1: /* set! */
+ y=find_slot_in_env(sc,sc->envir,sc->code,1);
+ if (y != sc->NIL) {
+ set_slot_in_env(sc, y, sc->value);
+ s_return(sc,sc->value);
+ } else {
+ Error_1(sc,"set!: unbound variable:", sc->code);
+ }
+
+ case OP_BEGIN: /* begin */
+ if (!is_pair(sc->code)) {
+ s_return(sc,sc->code);
+ }
+ if (cdr(sc->code) != sc->NIL) {
+ s_save(sc,OP_BEGIN, sc->NIL, cdr(sc->code));
+ }
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_IF0: /* if */
+ s_save(sc,OP_IF1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_IF1: /* if */
+ if (is_true(sc->value))
+ sc->code = car(sc->code);
+ else
+ sc->code = cadr(sc->code); /* (if #f 1) ==> () because
+ * car(sc->NIL) = sc->NIL */
+ s_goto(sc,OP_EVAL);
+
+ case OP_LET0: /* let */
+ sc->args = sc->NIL;
+ sc->value = sc->code;
+ sc->code = is_symbol(car(sc->code)) ? cadr(sc->code) : car(sc->code);
+ s_goto(sc,OP_LET1);
+
+ case OP_LET1: /* let (calculate parameters) */
+ sc->args = cons(sc, sc->value, sc->args);
+ if (is_pair(sc->code)) { /* continue */
+ if (!is_pair(car(sc->code)) || !is_pair(cdar(sc->code))) {
+ Error_1(sc, "Bad syntax of binding spec in let :", car(sc->code));
+ }
+ s_save(sc,OP_LET1, sc->args, cdr(sc->code));
+ sc->code = cadar(sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_EVAL);
+ } else { /* end */
+ sc->args = reverse_in_place(sc, sc->NIL, sc->args);
+ sc->code = car(sc->args);
+ sc->args = cdr(sc->args);
+ s_goto(sc,OP_LET2);
+ }
+
+ case OP_LET2: /* let */
+ new_frame_in_env(sc, sc->envir);
+ for (x = is_symbol(car(sc->code)) ? cadr(sc->code) : car(sc->code), y = sc->args;
+ y != sc->NIL; x = cdr(x), y = cdr(y)) {
+ new_slot_in_env(sc, caar(x), car(y));
+ }
+ if (is_symbol(car(sc->code))) { /* named let */
+ for (x = cadr(sc->code), sc->args = sc->NIL; x != sc->NIL; x = cdr(x)) {
+ if (!is_pair(x))
+ Error_1(sc, "Bad syntax of binding in let :", x);
+ if (!is_list(sc, car(x)))
+ Error_1(sc, "Bad syntax of binding in let :", car(x));
+ sc->args = cons(sc, caar(x), sc->args);
+ }
+ x = mk_closure(sc, cons(sc, reverse_in_place(sc, sc->NIL, sc->args), cddr(sc->code)), sc->envir);
+ new_slot_in_env(sc, car(sc->code), x);
+ sc->code = cddr(sc->code);
+ sc->args = sc->NIL;
+ } else {
+ sc->code = cdr(sc->code);
+ sc->args = sc->NIL;
+ }
+ s_goto(sc,OP_BEGIN);
+
+ case OP_LET0AST: /* let* */
+ if (car(sc->code) == sc->NIL) {
+ new_frame_in_env(sc, sc->envir);
+ sc->code = cdr(sc->code);
+ s_goto(sc,OP_BEGIN);
+ }
+ if(!is_pair(car(sc->code)) || !is_pair(caar(sc->code)) || !is_pair(cdaar(sc->code))) {
+ Error_1(sc,"Bad syntax of binding spec in let* :",car(sc->code));
+ }
+ s_save(sc,OP_LET1AST, cdr(sc->code), car(sc->code));
+ sc->code = cadaar(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_LET1AST: /* let* (make new frame) */
+ new_frame_in_env(sc, sc->envir);
+ s_goto(sc,OP_LET2AST);
+
+ case OP_LET2AST: /* let* (calculate parameters) */
+ new_slot_in_env(sc, caar(sc->code), sc->value);
+ sc->code = cdr(sc->code);
+ if (is_pair(sc->code)) { /* continue */
+ s_save(sc,OP_LET2AST, sc->args, sc->code);
+ sc->code = cadar(sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_EVAL);
+ } else { /* end */
+ sc->code = sc->args;
+ sc->args = sc->NIL;
+ s_goto(sc,OP_BEGIN);
+ }
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T;
+}
+
+static pointer opexe_1(scheme *sc, enum scheme_opcodes op) {
+ pointer x, y;
+
+ switch (op) {
+ case OP_LET0REC: /* letrec */
+ new_frame_in_env(sc, sc->envir);
+ sc->args = sc->NIL;
+ sc->value = sc->code;
+ sc->code = car(sc->code);
+ s_goto(sc,OP_LET1REC);
+
+ case OP_LET1REC: /* letrec (calculate parameters) */
+ sc->args = cons(sc, sc->value, sc->args);
+ if (is_pair(sc->code)) { /* continue */
+ if (!is_pair(car(sc->code)) || !is_pair(cdar(sc->code))) {
+ Error_1(sc,"Bad syntax of binding spec in letrec :",car(sc->code));
+ }
+ s_save(sc,OP_LET1REC, sc->args, cdr(sc->code));
+ sc->code = cadar(sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_EVAL);
+ } else { /* end */
+ sc->args = reverse_in_place(sc, sc->NIL, sc->args);
+ sc->code = car(sc->args);
+ sc->args = cdr(sc->args);
+ s_goto(sc,OP_LET2REC);
+ }
+
+ case OP_LET2REC: /* letrec */
+ for (x = car(sc->code), y = sc->args; y != sc->NIL; x = cdr(x), y = cdr(y)) {
+ new_slot_in_env(sc, caar(x), car(y));
+ }
+ sc->code = cdr(sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_BEGIN);
+
+ case OP_COND0: /* cond */
+ if (!is_pair(sc->code)) {
+ Error_0(sc,"syntax error in cond");
+ }
+ s_save(sc,OP_COND1, sc->NIL, sc->code);
+ sc->code = caar(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_COND1: /* cond */
+ if (is_true(sc->value)) {
+ if ((sc->code = cdar(sc->code)) == sc->NIL) {
+ s_return(sc,sc->value);
+ }
+ if(!sc->code) {
+ Error_0(sc,"syntax error in cond");
+ }
+ if(car(sc->code)==sc->FEED_TO) {
+ if(!is_pair(cdr(sc->code))) {
+ Error_0(sc,"syntax error in cond");
+ }
+ x=cons(sc, sc->QUOTE, cons(sc, sc->value, sc->NIL));
+ sc->code=cons(sc,cadr(sc->code),cons(sc,x,sc->NIL));
+ s_goto(sc,OP_EVAL);
+ }
+ s_goto(sc,OP_BEGIN);
+ } else {
+ if ((sc->code = cdr(sc->code)) == sc->NIL) {
+ s_return(sc,sc->NIL);
+ } else {
+ s_save(sc,OP_COND1, sc->NIL, sc->code);
+ sc->code = caar(sc->code);
+ s_goto(sc,OP_EVAL);
+ }
+ }
+
+ case OP_DELAY: /* delay */
+ x = mk_closure(sc, cons(sc, sc->NIL, sc->code), sc->envir);
+ typeflag(x)=T_PROMISE;
+ s_return(sc,x);
+
+ case OP_AND0: /* and */
+ if (sc->code == sc->NIL) {
+ s_return(sc,sc->T);
+ }
+ s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_AND1: /* and */
+ if (is_false(sc->value)) {
+ s_return(sc,sc->value);
+ } else if (sc->code == sc->NIL) {
+ s_return(sc,sc->value);
+ } else {
+ s_save(sc,OP_AND1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+ }
+
+ case OP_OR0: /* or */
+ if (sc->code == sc->NIL) {
+ s_return(sc,sc->F);
+ }
+ s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_OR1: /* or */
+ if (is_true(sc->value)) {
+ s_return(sc,sc->value);
+ } else if (sc->code == sc->NIL) {
+ s_return(sc,sc->value);
+ } else {
+ s_save(sc,OP_OR1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+ }
+
+ case OP_C0STREAM: /* cons-stream */
+ s_save(sc,OP_C1STREAM, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_C1STREAM: /* cons-stream */
+ sc->args = sc->value; /* save sc->value to register sc->args for gc */
+ x = mk_closure(sc, cons(sc, sc->NIL, sc->code), sc->envir);
+ typeflag(x)=T_PROMISE;
+ s_return(sc,cons(sc, sc->args, x));
+
+ case OP_MACRO0: /* macro */
+ if (is_pair(car(sc->code))) {
+ x = caar(sc->code);
+ sc->code = cons(sc, sc->LAMBDA, cons(sc, cdar(sc->code), cdr(sc->code)));
+ } else {
+ x = car(sc->code);
+ sc->code = cadr(sc->code);
+ }
+ if (!is_symbol(x)) {
+ Error_0(sc,"variable is not a symbol");
+ }
+ s_save(sc,OP_MACRO1, sc->NIL, x);
+ s_goto(sc,OP_EVAL);
+
+ case OP_MACRO1: /* macro */
+ typeflag(sc->value) = T_MACRO;
+ x = find_slot_in_env(sc, sc->envir, sc->code, 0);
+ if (x != sc->NIL) {
+ set_slot_in_env(sc, x, sc->value);
+ } else {
+ new_slot_in_env(sc, sc->code, sc->value);
+ }
+ s_return(sc,sc->code);
+
+ case OP_CASE0: /* case */
+ s_save(sc,OP_CASE1, sc->NIL, cdr(sc->code));
+ sc->code = car(sc->code);
+ s_goto(sc,OP_EVAL);
+
+ case OP_CASE1: /* case */
+ for (x = sc->code; x != sc->NIL; x = cdr(x)) {
+ if (!is_pair(y = caar(x))) {
+ break;
+ }
+ for ( ; y != sc->NIL; y = cdr(y)) {
+ if (eqv(car(y), sc->value)) {
+ break;
+ }
+ }
+ if (y != sc->NIL) {
+ break;
+ }
+ }
+ if (x != sc->NIL) {
+ if (is_pair(caar(x))) {
+ sc->code = cdar(x);
+ s_goto(sc,OP_BEGIN);
+ } else {/* else */
+ s_save(sc,OP_CASE2, sc->NIL, cdar(x));
+ sc->code = caar(x);
+ s_goto(sc,OP_EVAL);
+ }
+ } else {
+ s_return(sc,sc->NIL);
+ }
+
+ case OP_CASE2: /* case */
+ if (is_true(sc->value)) {
+ s_goto(sc,OP_BEGIN);
+ } else {
+ s_return(sc,sc->NIL);
+ }
+
+ case OP_PAPPLY: /* apply */
+ sc->code = car(sc->args);
+ sc->args = list_star(sc,cdr(sc->args));
+ /*sc->args = cadr(sc->args);*/
+ s_goto(sc,OP_APPLY);
+
+ case OP_PEVAL: /* eval */
+ if(cdr(sc->args)!=sc->NIL) {
+ sc->envir=cadr(sc->args);
+ }
+ sc->code = car(sc->args);
+ s_goto(sc,OP_EVAL);
+
+ case OP_CONTINUATION: /* call-with-current-continuation */
+ sc->code = car(sc->args);
+ sc->args = cons(sc, mk_continuation(sc, sc->dump), sc->NIL);
+ s_goto(sc,OP_APPLY);
+
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T;
+}
+
+static pointer opexe_2(scheme *sc, enum scheme_opcodes op) {
+ pointer x;
+ num v;
+#if USE_MATH
+ double dd;
+#endif
+
+ switch (op) {
+#if USE_MATH
+ case OP_INEX2EX: /* inexact->exact */
+ x=car(sc->args);
+ if(num_is_integer(x)) {
+ s_return(sc,x);
+ } else if(modf(rvalue_unchecked(x),&dd)==0.0) {
+ s_return(sc,mk_integer(sc,ivalue(x)));
+ } else {
+ Error_1(sc,"inexact->exact: not integral:",x);
+ }
+
+ case OP_EXP:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, exp(rvalue(x))));
+
+ case OP_LOG:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, log(rvalue(x))));
+
+ case OP_SIN:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, sin(rvalue(x))));
+
+ case OP_COS:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, cos(rvalue(x))));
+
+ case OP_TAN:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, tan(rvalue(x))));
+
+ case OP_ASIN:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, asin(rvalue(x))));
+
+ case OP_ACOS:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, acos(rvalue(x))));
+
+ case OP_ATAN:
+ x=car(sc->args);
+ if(cdr(sc->args)==sc->NIL) {
+ s_return(sc, mk_real(sc, atan(rvalue(x))));
+ } else {
+ pointer y=cadr(sc->args);
+ s_return(sc, mk_real(sc, atan2(rvalue(x),rvalue(y))));
+ }
+
+ case OP_SQRT:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, sqrt(rvalue(x))));
+
+ case OP_EXPT: {
+ double result;
+ int real_result=1;
+ pointer y=cadr(sc->args);
+ x=car(sc->args);
+ if (num_is_integer(x) && num_is_integer(y))
+ real_result=0;
+ /* This 'if' is an R5RS compatibility fix. */
+ /* NOTE: Remove this 'if' fix for R6RS. */
+ if (rvalue(x) == 0 && rvalue(y) < 0) {
+ result = 0.0;
+ } else {
+ result = pow(rvalue(x),rvalue(y));
+ }
+ /* Before returning integer result make sure we can. */
+ /* If the test fails, result is too big for integer. */
+ if (!real_result)
+ {
+ long result_as_long = (long)result;
+ if (result != (double)result_as_long)
+ real_result = 1;
+ }
+ if (real_result) {
+ s_return(sc, mk_real(sc, result));
+ } else {
+ s_return(sc, mk_integer(sc, result));
+ }
+ }
+
+ case OP_FLOOR:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, floor(rvalue(x))));
+
+ case OP_CEILING:
+ x=car(sc->args);
+ s_return(sc, mk_real(sc, ceil(rvalue(x))));
+
+ case OP_TRUNCATE : {
+ double rvalue_of_x ;
+ x=car(sc->args);
+ rvalue_of_x = rvalue(x) ;
+ if (rvalue_of_x > 0) {
+ s_return(sc, mk_real(sc, floor(rvalue_of_x)));
+ } else {
+ s_return(sc, mk_real(sc, ceil(rvalue_of_x)));
+ }
+ }
+
+ case OP_ROUND:
+ x=car(sc->args);
+ if (num_is_integer(x))
+ s_return(sc, x);
+ s_return(sc, mk_real(sc, round_per_R5RS(rvalue(x))));
+#endif
+
+ case OP_ADD: /* + */
+ v=num_zero;
+ for (x = sc->args; x != sc->NIL; x = cdr(x)) {
+ v=num_add(v,nvalue(car(x)));
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_MUL: /* * */
+ v=num_one;
+ for (x = sc->args; x != sc->NIL; x = cdr(x)) {
+ v=num_mul(v,nvalue(car(x)));
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_SUB: /* - */
+ if(cdr(sc->args)==sc->NIL) {
+ x=sc->args;
+ v=num_zero;
+ } else {
+ x = cdr(sc->args);
+ v = nvalue(car(sc->args));
+ }
+ for (; x != sc->NIL; x = cdr(x)) {
+ v=num_sub(v,nvalue(car(x)));
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_DIV: /* / */
+ if(cdr(sc->args)==sc->NIL) {
+ x=sc->args;
+ v=num_one;
+ } else {
+ x = cdr(sc->args);
+ v = nvalue(car(sc->args));
+ }
+ for (; x != sc->NIL; x = cdr(x)) {
+ if (!is_zero_double(rvalue(car(x))))
+ v=num_div(v,nvalue(car(x)));
+ else {
+ Error_0(sc,"/: division by zero");
+ }
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_INTDIV: /* quotient */
+ if(cdr(sc->args)==sc->NIL) {
+ x=sc->args;
+ v=num_one;
+ } else {
+ x = cdr(sc->args);
+ v = nvalue(car(sc->args));
+ }
+ for (; x != sc->NIL; x = cdr(x)) {
+ if (ivalue(car(x)) != 0)
+ v=num_intdiv(v,nvalue(car(x)));
+ else {
+ Error_0(sc,"quotient: division by zero");
+ }
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_REM: /* remainder */
+ v = nvalue(car(sc->args));
+ if (ivalue(cadr(sc->args)) != 0)
+ v=num_rem(v,nvalue(cadr(sc->args)));
+ else {
+ Error_0(sc,"remainder: division by zero");
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_MOD: /* modulo */
+ v = nvalue(car(sc->args));
+ if (ivalue(cadr(sc->args)) != 0)
+ v=num_mod(v,nvalue(cadr(sc->args)));
+ else {
+ Error_0(sc,"modulo: division by zero");
+ }
+ s_return(sc,mk_number(sc, v));
+
+ case OP_CAR: /* car */
+ s_return(sc,caar(sc->args));
+
+ case OP_CDR: /* cdr */
+ s_return(sc,cdar(sc->args));
+
+ case OP_CONS: /* cons */
+ cdr(sc->args) = cadr(sc->args);
+ s_return(sc,sc->args);
+
+ case OP_SETCAR: /* set-car! */
+ if(!is_immutable(car(sc->args))) {
+ caar(sc->args) = cadr(sc->args);
+ s_return(sc,car(sc->args));
+ } else {
+ Error_0(sc,"set-car!: unable to alter immutable pair");
+ }
+
+ case OP_SETCDR: /* set-cdr! */
+ if(!is_immutable(car(sc->args))) {
+ cdar(sc->args) = cadr(sc->args);
+ s_return(sc,car(sc->args));
+ } else {
+ Error_0(sc,"set-cdr!: unable to alter immutable pair");
+ }
+
+ case OP_CHAR2INT: { /* char->integer */
+ gunichar c;
+ c=ivalue(car(sc->args));
+ s_return(sc,mk_integer(sc,c));
+ }
+
+ case OP_INT2CHAR: { /* integer->char */
+ gunichar c;
+ c=(gunichar)ivalue(car(sc->args));
+ s_return(sc,mk_character(sc,c));
+ }
+
+ case OP_CHARUPCASE: {
+ gunichar c;
+ c=(gunichar)ivalue(car(sc->args));
+ c=g_unichar_toupper(c);
+ s_return(sc,mk_character(sc,c));
+ }
+
+ case OP_CHARDNCASE: {
+ gunichar c;
+ c=(gunichar)ivalue(car(sc->args));
+ c=g_unichar_tolower(c);
+ s_return(sc,mk_character(sc,c));
+ }
+
+ case OP_STR2SYM: /* string->symbol */
+ s_return(sc,mk_symbol(sc,strvalue(car(sc->args))));
+
+ case OP_STR2ATOM: /* string->atom */ {
+ char *s=strvalue(car(sc->args));
+ long pf = 0;
+ if(cdr(sc->args)!=sc->NIL) {
+ /* we know cadr(sc->args) is a natural number */
+ /* see if it is 2, 8, 10, or 16, or error */
+ pf = ivalue_unchecked(cadr(sc->args));
+ if(pf == 16 || pf == 10 || pf == 8 || pf == 2) {
+ /* base is OK */
+ }
+ else {
+ pf = -1;
+ }
+ }
+ if (pf < 0) {
+ Error_1(sc, "string->atom: bad base:", cadr(sc->args));
+ } else if(*s=='#') /* no use of base! */ {
+ s_return(sc, mk_sharp_const(sc, s+1));
+ } else {
+ if (pf == 0 || pf == 10) {
+ s_return(sc, mk_atom(sc, s));
+ }
+ else {
+ char *ep;
+ long iv = strtol(s,&ep,(int )pf);
+ if (*ep == 0) {
+ s_return(sc, mk_integer(sc, iv));
+ }
+ else {
+ s_return(sc, sc->F);
+ }
+ }
+ }
+ }
+
+ case OP_SYM2STR: /* symbol->string */
+ x=mk_string(sc,symname(car(sc->args)));
+ setimmutable(x);
+ s_return(sc,x);
+
+ case OP_ATOM2STR: /* atom->string */ {
+ long pf = 0;
+ x=car(sc->args);
+ if(cdr(sc->args)!=sc->NIL) {
+ /* we know cadr(sc->args) is a natural number */
+ /* see if it is 2, 8, 10, or 16, or error */
+ pf = ivalue_unchecked(cadr(sc->args));
+ if(is_number(x) && (pf == 16 || pf == 10 || pf == 8 || pf == 2)) {
+ /* base is OK */
+ }
+ else {
+ pf = -1;
+ }
+ }
+ if (pf < 0) {
+ Error_1(sc, "atom->string: bad base:", cadr(sc->args));
+ } else if(is_number(x) || is_character(x) || is_string(x) || is_symbol(x)) {
+ char *p;
+ int len;
+ atom2str(sc,x,(int )pf,&p,&len);
+ s_return(sc,mk_counted_string(sc,p,len));
+ } else {
+ Error_1(sc, "atom->string: not an atom:", x);
+ }
+ }
+
+ case OP_MKSTRING: { /* make-string */
+ gunichar fill=' ';
+ int len;
+
+ len=ivalue(car(sc->args));
+
+ if(cdr(sc->args)!=sc->NIL) {
+ fill=charvalue(cadr(sc->args));
+ }
+ s_return(sc,mk_empty_string(sc,len,fill));
+ }
+
+ case OP_STRLEN: /* string-length */
+ s_return(sc,mk_integer(sc,g_utf8_strlen(strvalue(car(sc->args)), -1)));
+
+ case OP_STRREF: { /* string-ref */
+ char *str;
+ int index;
+
+ str=strvalue(car(sc->args));
+
+ index=ivalue(cadr(sc->args));
+
+ if(index>=g_utf8_strlen(strvalue(car(sc->args)), -1)) {
+ Error_1(sc,"string-ref: out of bounds:",cadr(sc->args));
+ }
+
+ str = g_utf8_offset_to_pointer(str, (long)index);
+ s_return(sc,mk_character(sc, g_utf8_get_char(str)));
+ }
+
+ case OP_STRSET: { /* string-set! */
+ pointer a;
+ char *str;
+ int index;
+ gunichar c;
+ char utf8[7];
+ int utf8_len;
+ int newlen;
+ char *p1, *p2;
+ int p1_len;
+ int p2_len;
+ char *newstr;
+
+ a=car(sc->args);
+ if(is_immutable(a)) {
+ Error_1(sc,"string-set!: unable to alter immutable string:",a);
+ }
+
+ str=strvalue(a);
+ index=ivalue(cadr(sc->args));
+ if(index>=g_utf8_strlen(str, -1)) {
+ Error_1(sc,"string-set!: out of bounds:",cadr(sc->args));
+ }
+
+ c=charvalue(caddr(sc->args));
+ utf8_len = g_unichar_to_utf8(c, utf8);
+
+ p1 = g_utf8_offset_to_pointer(str, (long)index);
+ p2 = g_utf8_offset_to_pointer(str, (long)index+1);
+ p1_len = p1-str;
+ p2_len = strlen(p2);
+
+ newlen = p1_len+utf8_len+p2_len;
+ newstr = (char *)sc->malloc(newlen+1);
+ if (newstr == NULL) {
+ sc->no_memory=1;
+ Error_1(sc,"string-set!: No memory to alter string:",car(sc->args));
+ }
+
+ if (p1_len > 0)
+ memcpy(newstr, str, p1_len);
+ memcpy(newstr+p1_len, utf8, utf8_len);
+ if (p2_len > 0)
+ memcpy(newstr+p1_len+utf8_len, p2, p2_len);
+ newstr[newlen] = '\0';
+
+ free(strvalue(a));
+ strvalue(a)=newstr;
+ strlength(a)=g_utf8_strlen(newstr, -1);
+
+ s_return(sc,a);
+ }
+
+ case OP_STRAPPEND: { /* string-append */
+ /* in 1.29 string-append was in Scheme in init.scm but was too slow */
+ int len = 0;
+ pointer car_x;
+ char *newstr;
+ char *pos;
+ char *end;
+
+ /* compute needed length for new string */
+ for (x = sc->args; x != sc->NIL; x = cdr(x)) {
+ car_x = car(x);
+ end = g_utf8_offset_to_pointer(strvalue(car_x), (long)strlength(car_x));
+ len += end - strvalue(car_x);
+ }
+
+ newstr = (char *)sc->malloc(len+1);
+ if (newstr == NULL) {
+ sc->no_memory=1;
+ Error_1(sc,"string-set!: No memory to append strings:",car(sc->args));
+ }
+
+ /* store the contents of the argument strings into the new string */
+ pos = newstr;
+ for (x = sc->args; x != sc->NIL; x = cdr(x)) {
+ car_x = car(x);
+ end = g_utf8_offset_to_pointer(strvalue(car_x), (long)strlength(car_x));
+ len = end - strvalue(car_x);
+ memcpy(pos, strvalue(car_x), len);
+ pos += len;
+ }
+ *pos = '\0';
+
+ car_x = mk_string(sc, newstr);
+ g_free(newstr);
+
+ s_return(sc, car_x);
+ }
+
+ case OP_SUBSTR: { /* substring */
+ char *str;
+ char *beg;
+ char *end;
+ int index0;
+ int index1;
+ int len;
+ pointer x;
+
+ str=strvalue(car(sc->args));
+
+ index0=ivalue(cadr(sc->args));
+
+ if(index0>g_utf8_strlen(str, -1)) {
+ Error_1(sc,"substring: start out of bounds:",cadr(sc->args));
+ }
+
+ if(cddr(sc->args)!=sc->NIL) {
+ index1=ivalue(caddr(sc->args));
+ if(index1>g_utf8_strlen(str, -1) || index1<index0) {
+ Error_1(sc,"substring: end out of bounds:",caddr(sc->args));
+ }
+ } else {
+ index1=g_utf8_strlen(str, -1);
+ }
+
+ /* store the contents of the argument strings into the new string */
+ beg = g_utf8_offset_to_pointer(str, (long)index0);
+ end = g_utf8_offset_to_pointer(str, (long)index1);
+ len=end-beg;
+
+ str = (char *)sc->malloc(len+1);
+ if (str == NULL) {
+ sc->no_memory=1;
+ Error_1(sc,"string-set!: No memory to extract substring:",car(sc->args));
+ }
+
+ memcpy(str, beg, len);
+ str[len] = '\0';
+
+ x = mk_string(sc, str);
+ g_free(str);
+
+ s_return(sc,x);
+ }
+
+ case OP_VECTOR: { /* vector */
+ int i;
+ pointer vec;
+ int len=list_length(sc,sc->args);
+ if(len<0) {
+ Error_1(sc,"vector: not a proper list:",sc->args);
+ }
+ vec=mk_vector(sc,len);
+ if(sc->no_memory) { s_return(sc, sc->sink); }
+ for (x = sc->args, i = 0; is_pair(x); x = cdr(x), i++) {
+ set_vector_elem(vec,i,car(x));
+ }
+ s_return(sc,vec);
+ }
+
+ case OP_MKVECTOR: { /* make-vector */
+ pointer fill=sc->NIL;
+ int len;
+ pointer vec;
+
+ len=ivalue(car(sc->args));
+
+ if(cdr(sc->args)!=sc->NIL) {
+ fill=cadr(sc->args);
+ }
+ vec=mk_vector(sc,len);
+ if(sc->no_memory) { s_return(sc, sc->sink); }
+ if(fill!=sc->NIL) {
+ fill_vector(vec,fill);
+ }
+ s_return(sc,vec);
+ }
+
+ case OP_VECLEN: /* vector-length */
+ s_return(sc,mk_integer(sc,ivalue(car(sc->args))));
+
+ case OP_VECREF: { /* vector-ref */
+ int index;
+
+ index=ivalue(cadr(sc->args));
+
+ if(index>=ivalue(car(sc->args))) {
+ Error_1(sc,"vector-ref: out of bounds:",cadr(sc->args));
+ }
+
+ s_return(sc,vector_elem(car(sc->args),index));
+ }
+
+ case OP_VECSET: { /* vector-set! */
+ int index;
+
+ if(is_immutable(car(sc->args))) {
+ Error_1(sc,"vector-set!: unable to alter immutable vector:",car(sc->args));
+ }
+
+ index=ivalue(cadr(sc->args));
+ if(index>=ivalue(car(sc->args))) {
+ Error_1(sc,"vector-set!: out of bounds:",cadr(sc->args));
+ }
+
+ set_vector_elem(car(sc->args),index,caddr(sc->args));
+ s_return(sc,car(sc->args));
+ }
+
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T;
+}
+
+static int is_list(scheme *sc, pointer a)
+{ return list_length(sc,a) >= 0; }
+
+/* Result is:
+ proper list: length
+ circular list: -1
+ not even a pair: -2
+ dotted list: -2 minus length before dot
+*/
+int list_length(scheme *sc, pointer p) {
+ int i=0;
+ pointer slow, fast;
+
+ slow = fast = p;
+ while (1)
+ {
+ if (fast == sc->NIL)
+ return i;
+ if (!is_pair(fast))
+ return -2 - i;
+ fast = cdr(fast);
+ ++i;
+ if (fast == sc->NIL)
+ return i;
+ if (!is_pair(fast))
+ return -2 - i;
+ ++i;
+ fast = cdr(fast);
+
+ /* Safe because we would have already returned if `fast'
+ encountered a non-pair. */
+ slow = cdr(slow);
+ if (fast == slow)
+ {
+ /* the fast pointer has looped back around and caught up
+ with the slow pointer, hence the structure is circular,
+ not of finite length, and therefore not a list */
+ return -1;
+ }
+ }
+}
+
+static pointer opexe_3(scheme *sc, enum scheme_opcodes op) {
+ pointer x;
+ num v;
+ int (*comp_func)(num,num)=0;
+
+ switch (op) {
+ case OP_NOT: /* not */
+ s_retbool(is_false(car(sc->args)));
+ case OP_BOOLP: /* boolean? */
+ s_retbool(car(sc->args) == sc->F || car(sc->args) == sc->T);
+ case OP_EOFOBJP: /* boolean? */
+ s_retbool(car(sc->args) == sc->EOF_OBJ);
+ case OP_NULLP: /* null? */
+ s_retbool(car(sc->args) == sc->NIL);
+ case OP_NUMEQ: /* = */
+ case OP_LESS: /* < */
+ case OP_GRE: /* > */
+ case OP_LEQ: /* <= */
+ case OP_GEQ: /* >= */
+ switch(op) {
+ case OP_NUMEQ: comp_func=num_eq; break;
+ case OP_LESS: comp_func=num_lt; break;
+ case OP_GRE: comp_func=num_gt; break;
+ case OP_LEQ: comp_func=num_le; break;
+ case OP_GEQ: comp_func=num_ge; break;
+ default: break; /* Quiet the compiler */
+ }
+ x=sc->args;
+ v=nvalue(car(x));
+ x=cdr(x);
+
+ for (; x != sc->NIL; x = cdr(x)) {
+ if(!comp_func(v,nvalue(car(x)))) {
+ s_retbool(0);
+ }
+ v=nvalue(car(x));
+ }
+ s_retbool(1);
+ case OP_SYMBOLP: /* symbol? */
+ s_retbool(is_symbol(car(sc->args)));
+ case OP_NUMBERP: /* number? */
+ s_retbool(is_number(car(sc->args)));
+ case OP_STRINGP: /* string? */
+ s_retbool(is_string(car(sc->args)));
+ case OP_INTEGERP: /* integer? */
+ s_retbool(is_integer(car(sc->args)));
+ case OP_REALP: /* real? */
+ s_retbool(is_number(car(sc->args))); /* All numbers are real */
+ case OP_CHARP: /* char? */
+ s_retbool(is_character(car(sc->args)));
+#if USE_CHAR_CLASSIFIERS
+ case OP_CHARAP: /* char-alphabetic? */
+ s_retbool(Cisalpha(ivalue(car(sc->args))));
+ case OP_CHARNP: /* char-numeric? */
+ s_retbool(Cisdigit(ivalue(car(sc->args))));
+ case OP_CHARWP: /* char-whitespace? */
+ s_retbool(Cisspace(ivalue(car(sc->args))));
+ case OP_CHARUP: /* char-upper-case? */
+ s_retbool(Cisupper(ivalue(car(sc->args))));
+ case OP_CHARLP: /* char-lower-case? */
+ s_retbool(Cislower(ivalue(car(sc->args))));
+#endif
+ case OP_PORTP: /* port? */
+ s_retbool(is_port(car(sc->args)));
+ case OP_INPORTP: /* input-port? */
+ s_retbool(is_inport(car(sc->args)));
+ case OP_OUTPORTP: /* output-port? */
+ s_retbool(is_outport(car(sc->args)));
+ case OP_PROCP: /* procedure? */
+ /*--
+ * continuation should be procedure by the example
+ * (call-with-current-continuation procedure?) ==> #t
+ * in R^3 report sec. 6.9
+ */
+ s_retbool(is_proc(car(sc->args)) || is_closure(car(sc->args))
+ || is_continuation(car(sc->args)) || is_foreign(car(sc->args)));
+ case OP_PAIRP: /* pair? */
+ s_retbool(is_pair(car(sc->args)));
+ case OP_LISTP: /* list? */
+ s_retbool(list_length(sc,car(sc->args)) >= 0);
+ case OP_ENVP: /* environment? */
+ s_retbool(is_environment(car(sc->args)));
+ case OP_VECTORP: /* vector? */
+ s_retbool(is_vector(car(sc->args)));
+ case OP_EQ: /* eq? */
+ s_retbool(car(sc->args) == cadr(sc->args));
+ case OP_EQV: /* eqv? */
+ s_retbool(eqv(car(sc->args), cadr(sc->args)));
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T;
+}
+
+static pointer opexe_4(scheme *sc, enum scheme_opcodes op) {
+ pointer x, y;
+
+ switch (op) {
+ case OP_FORCE: /* force */
+ sc->code = car(sc->args);
+ if (is_promise(sc->code)) {
+ /* Should change type to closure here */
+ s_save(sc, OP_SAVE_FORCED, sc->NIL, sc->code);
+ sc->args = sc->NIL;
+ s_goto(sc,OP_APPLY);
+ } else {
+ s_return(sc,sc->code);
+ }
+
+ case OP_SAVE_FORCED: /* Save forced value replacing promise */
+ memcpy(sc->code,sc->value,sizeof(struct cell));
+ s_return(sc,sc->value);
+
+ case OP_WRITE: /* write */
+ case OP_DISPLAY: /* display */
+ case OP_WRITE_CHAR: /* write-char */
+ if(is_pair(cdr(sc->args))) {
+ if(cadr(sc->args)!=sc->outport) {
+ x=cons(sc,sc->outport,sc->NIL);
+ s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
+ sc->outport=cadr(sc->args);
+ }
+ }
+ sc->args = car(sc->args);
+ if(op==OP_WRITE) {
+ sc->print_flag = 1;
+ } else {
+ sc->print_flag = 0;
+ }
+ s_goto(sc,OP_P0LIST);
+
+ case OP_NEWLINE: /* newline */
+ if(is_pair(sc->args)) {
+ if(car(sc->args)!=sc->outport) {
+ x=cons(sc,sc->outport,sc->NIL);
+ s_save(sc,OP_SET_OUTPORT, x, sc->NIL);
+ sc->outport=car(sc->args);
+ }
+ }
+ putstr(sc, "\n");
+ s_return(sc,sc->T);
+
+ case OP_ERR0: /* error */
+ sc->retcode=-1;
+ if (!is_string(car(sc->args))) {
+ sc->args=cons(sc,mk_string(sc," -- "),sc->args);
+ setimmutable(car(sc->args));
+ }
+ putstr(sc, "Error: ");
+ putstr(sc, strvalue(car(sc->args)));
+ sc->args = cdr(sc->args);
+ s_goto(sc,OP_ERR1);
+
+ case OP_ERR1: /* error */
+ putstr(sc, " ");
+ if (sc->args != sc->NIL) {
+ s_save(sc,OP_ERR1, cdr(sc->args), sc->NIL);
+ sc->args = car(sc->args);
+ sc->print_flag = 1;
+ s_goto(sc,OP_P0LIST);
+ } else {
+ putstr(sc, "\n");
+ if(sc->interactive_repl) {
+ s_goto(sc,OP_T0LVL);
+ } else {
+ return sc->NIL;
+ }
+ }
+
+ case OP_REVERSE: /* reverse */
+ s_return(sc,reverse(sc, car(sc->args)));
+
+ case OP_LIST_STAR: /* list* */
+ s_return(sc,list_star(sc,sc->args));
+
+ case OP_APPEND: /* append */
+ x = sc->NIL;
+ y = sc->args;
+ if (y == x) {
+ s_return(sc, x);
+ }
+
+ /* cdr() in the while condition is not a typo. If car() */
+ /* is used (append '() 'a) will return the wrong result.*/
+ while (cdr(y) != sc->NIL) {
+ x = revappend(sc, x, car(y));
+ y = cdr(y);
+ if (x == sc->F) {
+ Error_0(sc, "non-list argument to append");
+ }
+ }
+
+ s_return(sc, reverse_in_place(sc, car(y), x));
+
+#if USE_PLIST
+ case OP_PUT: /* put */
+ if (!hasprop(car(sc->args)) || !hasprop(cadr(sc->args))) {
+ Error_0(sc,"illegal use of put");
+ }
+ for (x = symprop(car(sc->args)), y = cadr(sc->args); x != sc->NIL; x = cdr(x)) {
+ if (caar(x) == y) {
+ break;
+ }
+ }
+ if (x != sc->NIL)
+ cdar(x) = caddr(sc->args);
+ else
+ symprop(car(sc->args)) = cons(sc, cons(sc, y, caddr(sc->args)),
+ symprop(car(sc->args)));
+ s_return(sc,sc->T);
+
+ case OP_GET: /* get */
+ if (!hasprop(car(sc->args)) || !hasprop(cadr(sc->args))) {
+ Error_0(sc,"illegal use of get");
+ }
+ for (x = symprop(car(sc->args)), y = cadr(sc->args); x != sc->NIL; x = cdr(x)) {
+ if (caar(x) == y) {
+ break;
+ }
+ }
+ if (x != sc->NIL) {
+ s_return(sc,cdar(x));
+ } else {
+ s_return(sc,sc->NIL);
+ }
+#endif /* USE_PLIST */
+ case OP_QUIT: /* quit */
+ if(is_pair(sc->args)) {
+ sc->retcode=ivalue(car(sc->args));
+ }
+ return (sc->NIL);
+
+ case OP_GC: /* gc */
+ gc(sc, sc->NIL, sc->NIL);
+ s_return(sc,sc->T);
+
+ case OP_GCVERB: /* gc-verbose */
+ { int was = sc->gc_verbose;
+
+ sc->gc_verbose = (car(sc->args) != sc->F);
+ s_retbool(was);
+ }
+
+ case OP_NEWSEGMENT: /* new-segment */
+ if (!is_pair(sc->args) || !is_number(car(sc->args))) {
+ Error_0(sc,"new-segment: argument must be a number");
+ }
+ alloc_cellseg(sc, (int) ivalue(car(sc->args)));
+ s_return(sc,sc->T);
+
+ case OP_OBLIST: /* oblist */
+ s_return(sc, oblist_all_symbols(sc));
+
+ case OP_CURR_INPORT: /* current-input-port */
+ s_return(sc,sc->inport);
+
+ case OP_CURR_OUTPORT: /* current-output-port */
+ s_return(sc,sc->outport);
+
+ case OP_OPEN_INFILE: /* open-input-file */
+ case OP_OPEN_OUTFILE: /* open-output-file */
+ case OP_OPEN_INOUTFILE: /* open-input-output-file */ {
+ int prop=0;
+ pointer p;
+ switch(op) {
+ case OP_OPEN_INFILE: prop=port_input; break;
+ case OP_OPEN_OUTFILE: prop=port_output; break;
+ case OP_OPEN_INOUTFILE: prop=port_input|port_output; break;
+ default: break; /* Quiet the compiler */
+ }
+ p=port_from_filename(sc,strvalue(car(sc->args)),prop);
+ if(p==sc->NIL) {
+ s_return(sc,sc->F);
+ }
+ s_return(sc,p);
+ }
+
+#if USE_STRING_PORTS
+ case OP_OPEN_INSTRING: /* open-input-string */
+ case OP_OPEN_INOUTSTRING: /* open-input-output-string */ {
+ int prop=0;
+ pointer p;
+ switch(op) {
+ case OP_OPEN_INSTRING: prop=port_input; break;
+ case OP_OPEN_INOUTSTRING: prop=port_input|port_output; break;
+ default: break; /* Quiet the compiler */
+ }
+ p=port_from_string(sc, strvalue(car(sc->args)),
+ g_utf8_offset_to_pointer(strvalue(car(sc->args)),
+ strlength(car(sc->args))), prop);
+ if(p==sc->NIL) {
+ s_return(sc,sc->F);
+ }
+ s_return(sc,p);
+ }
+ case OP_OPEN_OUTSTRING: /* open-output-string */ {
+ pointer p;
+ if(car(sc->args)==sc->NIL) {
+ p=port_from_scratch(sc);
+ if(p==sc->NIL) {
+ s_return(sc,sc->F);
+ }
+ } else {
+ p=port_from_string(sc, strvalue(car(sc->args)),
+ strvalue(car(sc->args))+strlength(car(sc->args)),
+ port_output);
+ if(p==sc->NIL) {
+ s_return(sc,sc->F);
+ }
+ }
+ s_return(sc,p);
+ }
+ case OP_GET_OUTSTRING: /* get-output-string */ {
+ port *p;
+
+ if ((p=car(sc->args)->_object._port)->kind&port_string) {
+ off_t size;
+ char *str;
+
+ size=p->rep.string.curr-p->rep.string.start+1;
+ str=sc->malloc(size);
+ if(str != NULL) {
+ pointer s;
+
+ memcpy(str,p->rep.string.start,size-1);
+ str[size-1]='\0';
+ s=mk_string(sc,str);
+ sc->free(str);
+ s_return(sc,s);
+ }
+ }
+ s_return(sc,sc->F);
+ }
+#endif
+
+ case OP_CLOSE_INPORT: /* close-input-port */
+ port_close(sc,car(sc->args),port_input);
+ s_return(sc,sc->T);
+
+ case OP_CLOSE_OUTPORT: /* close-output-port */
+ port_close(sc,car(sc->args),port_output);
+ s_return(sc,sc->T);
+
+ case OP_INT_ENV: /* interaction-environment */
+ s_return(sc,sc->global_env);
+
+ case OP_CURR_ENV: /* current-environment */
+ s_return(sc,sc->envir);
+
+ default:
+ sprintf(sc->strbuff, "%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T;
+}
+
+static pointer opexe_5(scheme *sc, enum scheme_opcodes op) {
+ pointer x;
+ char *trans_str;
+
+ if(sc->nesting!=0) {
+ int n=sc->nesting;
+ sc->nesting=0;
+ sc->retcode=-1;
+ Error_1(sc,"unmatched parentheses:",mk_integer(sc,n));
+ }
+
+ switch (op) {
+ /* ========== reading part ========== */
+ case OP_READ:
+ if(!is_pair(sc->args)) {
+ s_goto(sc,OP_READ_INTERNAL);
+ }
+ if(!is_inport(car(sc->args))) {
+ Error_1(sc,"read: not an input port:",car(sc->args));
+ }
+ if(car(sc->args)==sc->inport) {
+ s_goto(sc,OP_READ_INTERNAL);
+ }
+ x=sc->inport;
+ sc->inport=car(sc->args);
+ x=cons(sc,x,sc->NIL);
+ s_save(sc,OP_SET_INPORT, x, sc->NIL);
+ s_goto(sc,OP_READ_INTERNAL);
+
+ case OP_READ_CHAR: /* read-char */
+ case OP_PEEK_CHAR: /* peek-char */ {
+ gunichar c;
+ if(is_pair(sc->args)) {
+ if(car(sc->args)!=sc->inport) {
+ x=sc->inport;
+ x=cons(sc,x,sc->NIL);
+ s_save(sc,OP_SET_INPORT, x, sc->NIL);
+ sc->inport=car(sc->args);
+ }
+ }
+ c=inchar(sc);
+ if(c==EOF) {
+ s_return(sc,sc->EOF_OBJ);
+ }
+ if(sc->op==OP_PEEK_CHAR) {
+ backchar(sc,c);
+ }
+ s_return(sc,mk_character(sc,c));
+ }
+
+ case OP_CHAR_READY: /* char-ready? */ {
+ pointer p=sc->inport;
+ int res;
+ if(is_pair(sc->args)) {
+ p=car(sc->args);
+ }
+ res=p->_object._port->kind&port_string;
+ s_retbool(res);
+ }
+
+ case OP_SET_INPORT: /* set-input-port */
+ sc->inport=car(sc->args);
+ s_return(sc,sc->value);
+
+ case OP_SET_OUTPORT: /* set-output-port */
+ sc->outport=car(sc->args);
+ s_return(sc,sc->value);
+
+ case OP_RDSEXPR:
+ switch (sc->tok) {
+ case TOK_EOF:
+ s_return(sc,sc->EOF_OBJ);
+ /* NOTREACHED */
+/*
+ * Commented out because we now skip comments in the scanner
+ *
+ case TOK_COMMENT: {
+ gunichar c;
+ while ((c=inchar(sc)) != '\n' && c!=EOF)
+ ;
+ sc->tok = token(sc);
+ s_goto(sc,OP_RDSEXPR);
+ }
+*/
+ case TOK_VEC:
+ s_save(sc,OP_RDVEC,sc->NIL,sc->NIL);
+ /* fall through */
+ case TOK_LPAREN:
+ sc->tok = token(sc);
+ if (sc->tok == TOK_RPAREN) {
+ s_return(sc,sc->NIL);
+ } else if (sc->tok == TOK_DOT) {
+ Error_0(sc,"syntax error: illegal dot expression");
+ } else {
+ sc->nesting_stack[sc->file_i]++;
+ s_save(sc,OP_RDLIST, sc->NIL, sc->NIL);
+ s_goto(sc,OP_RDSEXPR);
+ }
+ case TOK_QUOTE:
+ s_save(sc,OP_RDQUOTE, sc->NIL, sc->NIL);
+ sc->tok = token(sc);
+ s_goto(sc,OP_RDSEXPR);
+ case TOK_BQUOTE:
+ sc->tok = token(sc);
+ if(sc->tok==TOK_VEC) {
+ s_save(sc,OP_RDQQUOTEVEC, sc->NIL, sc->NIL);
+ sc->tok=TOK_LPAREN;
+ s_goto(sc,OP_RDSEXPR);
+ } else {
+ s_save(sc,OP_RDQQUOTE, sc->NIL, sc->NIL);
+ }
+ s_goto(sc,OP_RDSEXPR);
+ case TOK_COMMA:
+ s_save(sc,OP_RDUNQUOTE, sc->NIL, sc->NIL);
+ sc->tok = token(sc);
+ s_goto(sc,OP_RDSEXPR);
+ case TOK_ATMARK:
+ s_save(sc,OP_RDUQTSP, sc->NIL, sc->NIL);
+ sc->tok = token(sc);
+ s_goto(sc,OP_RDSEXPR);
+ case TOK_ATOM:
+ s_return(sc,mk_atom(sc, readstr_upto(sc, DELIMITERS)));
+ case TOK_DQUOTE:
+ x=readstrexp(sc);
+ if(x==sc->F) {
+ Error_0(sc,"Error reading string");
+ }
+ setimmutable(x);
+ s_return(sc,x);
+ case TOK_USCORE:
+ x=readstrexp(sc);
+ if(x==sc->F) {
+ Error_0(sc,"Error reading string");
+ }
+ trans_str = gettext (strvalue (x));
+ if (trans_str != strvalue(x)) {
+ sc->free(strvalue(x));
+ strlength(x) = g_utf8_strlen(trans_str, -1);
+ strvalue(x) = store_string(sc, strlength(x), trans_str, 0);
+ }
+ setimmutable(x);
+ s_return(sc,x);
+ case TOK_SHARP: {
+ pointer f=find_slot_in_env(sc,sc->envir,sc->SHARP_HOOK,1);
+ if(f==sc->NIL) {
+ Error_0(sc,"undefined sharp expression");
+ } else {
+ sc->code=cons(sc,slot_value_in_env(f),sc->NIL);
+ s_goto(sc,OP_EVAL);
+ }
+ }
+ case TOK_SHARP_CONST:
+ if ((x = mk_sharp_const(sc, readstr_upto(sc, DELIMITERS))) == sc->NIL) {
+ Error_0(sc,"undefined sharp expression");
+ } else {
+ s_return(sc,x);
+ }
+ default:
+ Error_1(sc, "syntax error: illegal token", mk_integer (sc, sc->tok));
+ }
+ break;
+
+ case OP_RDLIST: {
+ sc->args = cons(sc, sc->value, sc->args);
+ sc->tok = token(sc);
+/* We now skip comments in the scanner
+ while (sc->tok == TOK_COMMENT) {
+ gunichar c;
+ while ((c=inchar(sc)) != '\n' && c!=EOF)
+ ;
+ sc->tok = token(sc);
+ }
+*/
+ if (sc->tok == TOK_EOF)
+ { s_return(sc,sc->EOF_OBJ); }
+ else if (sc->tok == TOK_RPAREN) {
+ gunichar c = inchar(sc);
+ if (c != '\n')
+ backchar(sc,c);
+#if SHOW_ERROR_LINE
+ else if (sc->load_stack[sc->file_i].kind & port_file)
+ sc->load_stack[sc->file_i].rep.stdio.curr_line++;
+#endif
+ sc->nesting_stack[sc->file_i]--;
+ s_return(sc,reverse_in_place(sc, sc->NIL, sc->args));
+ } else if (sc->tok == TOK_DOT) {
+ s_save(sc,OP_RDDOT, sc->args, sc->NIL);
+ sc->tok = token(sc);
+ s_goto(sc,OP_RDSEXPR);
+ } else {
+ s_save(sc,OP_RDLIST, sc->args, sc->NIL);
+ s_goto(sc,OP_RDSEXPR);
+ }
+ }
+
+ case OP_RDDOT:
+ if (token(sc) != TOK_RPAREN) {
+ Error_0(sc,"syntax error: illegal dot expression");
+ } else {
+ sc->nesting_stack[sc->file_i]--;
+ s_return(sc,reverse_in_place(sc, sc->value, sc->args));
+ }
+
+ case OP_RDQUOTE:
+ s_return(sc,cons(sc, sc->QUOTE, cons(sc, sc->value, sc->NIL)));
+
+ case OP_RDQQUOTE:
+ s_return(sc,cons(sc, sc->QQUOTE, cons(sc, sc->value, sc->NIL)));
+
+ case OP_RDQQUOTEVEC:
+ s_return(sc,cons(sc, mk_symbol(sc,"apply"),
+ cons(sc, mk_symbol(sc,"vector"),
+ cons(sc,cons(sc, sc->QQUOTE,
+ cons(sc,sc->value,sc->NIL)),
+ sc->NIL))));
+
+ case OP_RDUNQUOTE:
+ s_return(sc,cons(sc, sc->UNQUOTE, cons(sc, sc->value, sc->NIL)));
+
+ case OP_RDUQTSP:
+ s_return(sc,cons(sc, sc->UNQUOTESP, cons(sc, sc->value, sc->NIL)));
+
+ case OP_RDVEC:
+ /*sc->code=cons(sc,mk_proc(sc,OP_VECTOR),sc->value);
+ s_goto(sc,OP_EVAL); Cannot be quoted*/
+ /*x=cons(sc,mk_proc(sc,OP_VECTOR),sc->value);
+ s_return(sc,x); Cannot be part of pairs*/
+ /*sc->code=mk_proc(sc,OP_VECTOR);
+ sc->args=sc->value;
+ s_goto(sc,OP_APPLY);*/
+ sc->args=sc->value;
+ s_goto(sc,OP_VECTOR);
+
+ /* ========== printing part ========== */
+ case OP_P0LIST:
+ if(is_vector(sc->args)) {
+ putstr(sc,"#(");
+ sc->args=cons(sc,sc->args,mk_integer(sc,0));
+ s_goto(sc,OP_PVECFROM);
+ } else if(is_environment(sc->args)) {
+ putstr(sc,"#<ENVIRONMENT>");
+ s_return(sc,sc->T);
+ } else if (!is_pair(sc->args)) {
+ printatom(sc, sc->args, sc->print_flag);
+ s_return(sc,sc->T);
+ } else if (car(sc->args) == sc->QUOTE && ok_abbrev(cdr(sc->args))) {
+ putstr(sc, "'");
+ sc->args = cadr(sc->args);
+ s_goto(sc,OP_P0LIST);
+ } else if (car(sc->args) == sc->QQUOTE && ok_abbrev(cdr(sc->args))) {
+ putstr(sc, "`");
+ sc->args = cadr(sc->args);
+ s_goto(sc,OP_P0LIST);
+ } else if (car(sc->args) == sc->UNQUOTE && ok_abbrev(cdr(sc->args))) {
+ putstr(sc, ",");
+ sc->args = cadr(sc->args);
+ s_goto(sc,OP_P0LIST);
+ } else if (car(sc->args) == sc->UNQUOTESP && ok_abbrev(cdr(sc->args))) {
+ putstr(sc, ",@");
+ sc->args = cadr(sc->args);
+ s_goto(sc,OP_P0LIST);
+ } else {
+ putstr(sc, "(");
+ s_save(sc,OP_P1LIST, cdr(sc->args), sc->NIL);
+ sc->args = car(sc->args);
+ s_goto(sc,OP_P0LIST);
+ }
+
+ case OP_P1LIST:
+ if (is_pair(sc->args)) {
+ s_save(sc,OP_P1LIST, cdr(sc->args), sc->NIL);
+ putstr(sc, " ");
+ sc->args = car(sc->args);
+ s_goto(sc,OP_P0LIST);
+ } else if(is_vector(sc->args)) {
+ s_save(sc,OP_P1LIST,sc->NIL,sc->NIL);
+ putstr(sc, " . ");
+ s_goto(sc,OP_P0LIST);
+ } else {
+ if (sc->args != sc->NIL) {
+ putstr(sc, " . ");
+ printatom(sc, sc->args, sc->print_flag);
+ }
+ putstr(sc, ")");
+ s_return(sc,sc->T);
+ }
+ case OP_PVECFROM: {
+ int i=ivalue_unchecked(cdr(sc->args));
+ pointer vec=car(sc->args);
+ int len=ivalue_unchecked(vec);
+ if(i==len) {
+ putstr(sc,")");
+ s_return(sc,sc->T);
+ } else {
+ pointer elem=vector_elem(vec,i);
+ ivalue_unchecked(cdr(sc->args))=i+1;
+ s_save(sc,OP_PVECFROM, sc->args, sc->NIL);
+ sc->args=elem;
+ if (i > 0)
+ putstr(sc," ");
+ s_goto(sc,OP_P0LIST);
+ }
+ }
+
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+
+ }
+ return sc->T;
+}
+
+static pointer opexe_6(scheme *sc, enum scheme_opcodes op) {
+ pointer x, y;
+ long v;
+
+ switch (op) {
+ case OP_LIST_LENGTH: /* length */ /* a.k */
+ v=list_length(sc,car(sc->args));
+ if(v<0) {
+ Error_1(sc,"length: not a list:",car(sc->args));
+ }
+ s_return(sc,mk_integer(sc, v));
+
+ case OP_ASSQ: /* assq */ /* a.k */
+ x = car(sc->args);
+ for (y = cadr(sc->args); is_pair(y); y = cdr(y)) {
+ if (!is_pair(car(y))) {
+ Error_0(sc,"unable to handle non pair element");
+ }
+ if (x == caar(y))
+ break;
+ }
+ if (is_pair(y)) {
+ s_return(sc,car(y));
+ } else {
+ s_return(sc,sc->F);
+ }
+
+
+ case OP_GET_CLOSURE: /* get-closure-code */ /* a.k */
+ sc->args = car(sc->args);
+ if (sc->args == sc->NIL) {
+ s_return(sc,sc->F);
+ } else if (is_closure(sc->args)) {
+ s_return(sc,cons(sc, sc->LAMBDA, closure_code(sc->value)));
+ } else if (is_macro(sc->args)) {
+ s_return(sc,cons(sc, sc->LAMBDA, closure_code(sc->value)));
+ } else {
+ s_return(sc,sc->F);
+ }
+ case OP_CLOSUREP: /* closure? */
+ /*
+ * Note, macro object is also a closure.
+ * Therefore, (closure? <#MACRO>) ==> #t
+ */
+ s_retbool(is_closure(car(sc->args)));
+ case OP_MACROP: /* macro? */
+ s_retbool(is_macro(car(sc->args)));
+ default:
+ snprintf(sc->strbuff,STRBUFFSIZE,"%d: illegal operator", sc->op);
+ Error_0(sc,sc->strbuff);
+ }
+ return sc->T; /* NOTREACHED */
+}
+
+typedef pointer (*dispatch_func)(scheme *, enum scheme_opcodes);
+
+typedef int (*test_predicate)(pointer);
+static int is_any(pointer p) { return 1;}
+
+static int is_nonneg(pointer p) {
+ return ivalue(p)>=0 && is_integer(p);
+}
+
+/* Correspond carefully with following defines! */
+static struct {
+ test_predicate fct;
+ const char *kind;
+} tests[]={
+ {0,0}, /* unused */
+ {is_any, 0},
+ {is_string, "string"},
+ {is_symbol, "symbol"},
+ {is_port, "port"},
+ {is_inport,"input port"},
+ {is_outport,"output port"},
+ {is_environment, "environment"},
+ {is_pair, "pair"},
+ {0, "pair or '()"},
+ {is_character, "character"},
+ {is_vector, "vector"},
+ {is_number, "number"},
+ {is_integer, "integer"},
+ {is_nonneg, "non-negative integer"}
+};
+
+#define TST_NONE 0
+#define TST_ANY "\001"
+#define TST_STRING "\002"
+#define TST_SYMBOL "\003"
+#define TST_PORT "\004"
+#define TST_INPORT "\005"
+#define TST_OUTPORT "\006"
+#define TST_ENVIRONMENT "\007"
+#define TST_PAIR "\010"
+#define TST_LIST "\011"
+#define TST_CHAR "\012"
+#define TST_VECTOR "\013"
+#define TST_NUMBER "\014"
+#define TST_INTEGER "\015"
+#define TST_NATURAL "\016"
+
+typedef struct {
+ dispatch_func func;
+ char *name;
+ int min_arity;
+ int max_arity;
+ char *arg_tests_encoding;
+} op_code_info;
+
+#define INF_ARG 0xffff
+
+static op_code_info dispatch_table[]= {
+#define _OP_DEF(A,B,C,D,E,OP) {A,B,C,D,E},
+#include "opdefines.h"
+ { 0 }
+};
+
+static const char *procname(pointer x) {
+ int n=procnum(x);
+ const char *name=dispatch_table[n].name;
+ if(name==0) {
+ name="ILLEGAL!";
+ }
+ return name;
+}
+
+/* kernel of this interpreter */
+static void Eval_Cycle(scheme *sc, enum scheme_opcodes op) {
+ sc->op = op;
+ for (;;) {
+ op_code_info *pcd=dispatch_table+sc->op;
+ if (pcd->name!=0) { /* if built-in function, check arguments */
+ char msg[STRBUFFSIZE];
+ int ok=1;
+ int n=list_length(sc,sc->args);
+
+ /* Check number of arguments */
+ if(n<pcd->min_arity) {
+ ok=0;
+ snprintf(msg, STRBUFFSIZE, "%s: needs%s %d argument(s)",
+ pcd->name,
+ pcd->min_arity==pcd->max_arity?"":" at least",
+ pcd->min_arity);
+ }
+ if(ok && n>pcd->max_arity) {
+ ok=0;
+ snprintf(msg, STRBUFFSIZE, "%s: needs%s %d argument(s)",
+ pcd->name,
+ pcd->min_arity==pcd->max_arity?"":" at most",
+ pcd->max_arity);
+ }
+ if(ok) {
+ if(pcd->arg_tests_encoding!=0) {
+ int i=0;
+ int j;
+ const char *t=pcd->arg_tests_encoding;
+ pointer arglist=sc->args;
+ do {
+ pointer arg=car(arglist);
+ j=(int)t[0];
+ if(j==TST_LIST[0]) {
+ if(arg!=sc->NIL && !is_pair(arg)) break;
+ } else {
+ if(!tests[j].fct(arg)) break;
+ }
+
+ if(t[1]!=0) {/* last test is replicated as necessary */
+ t++;
+ }
+ arglist=cdr(arglist);
+ i++;
+ } while(i<n);
+ if(i<n) {
+ ok=0;
+ snprintf(msg, STRBUFFSIZE, "%s: argument %d must be: %s",
+ pcd->name,
+ i+1,
+ tests[j].kind);
+ }
+ }
+ }
+ if(!ok) {
+ if(_Error_1(sc,msg,0)==sc->NIL) {
+ return;
+ }
+ pcd=dispatch_table+sc->op;
+ }
+ }
+ ok_to_freely_gc(sc);
+ if (pcd->func(sc, (enum scheme_opcodes)sc->op) == sc->NIL) {
+ return;
+ }
+ if(sc->no_memory) {
+ fprintf(stderr,"No memory!\n");
+ return;
+ }
+ }
+}
+
+/* ========== Initialization of internal keywords ========== */
+
+static void assign_syntax(scheme *sc, char *name) {
+ pointer x;
+
+ x = oblist_add_by_name(sc, name);
+ typeflag(x) |= T_SYNTAX;
+}
+
+static void assign_proc(scheme *sc, enum scheme_opcodes op, char *name) {
+ pointer x, y;
+
+ x = mk_symbol(sc, name);
+ y = mk_proc(sc,op);
+ new_slot_in_env(sc, x, y);
+}
+
+static pointer mk_proc(scheme *sc, enum scheme_opcodes op) {
+ pointer y;
+
+ y = get_cell(sc, sc->NIL, sc->NIL);
+ typeflag(y) = (T_PROC | T_ATOM);
+ ivalue_unchecked(y) = (long) op;
+ set_num_integer(y);
+ return y;
+}
+
+/* Hard-coded for the given keywords. Remember to rewrite if more are added! */
+static int syntaxnum(pointer p) {
+ const char *s=strvalue(car(p));
+ switch(strlength(car(p))) {
+ case 2:
+ if(s[0]=='i') return OP_IF0; /* if */
+ else return OP_OR0; /* or */
+ case 3:
+ if(s[0]=='a') return OP_AND0; /* and */
+ else return OP_LET0; /* let */
+ case 4:
+ switch(s[3]) {
+ case 'e': return OP_CASE0; /* case */
+ case 'd': return OP_COND0; /* cond */
+ case '*': return OP_LET0AST; /* let* */
+ default: return OP_SET0; /* set! */
+ }
+ case 5:
+ switch(s[2]) {
+ case 'g': return OP_BEGIN; /* begin */
+ case 'l': return OP_DELAY; /* delay */
+ case 'c': return OP_MACRO0; /* macro */
+ default: return OP_QUOTE; /* quote */
+ }
+ case 6:
+ switch(s[2]) {
+ case 'm': return OP_LAMBDA; /* lambda */
+ case 'f': return OP_DEF0; /* define */
+ default: return OP_LET0REC; /* letrec */
+ }
+ default:
+ return OP_C0STREAM; /* cons-stream */
+ }
+}
+
+/* initialization of TinyScheme */
+#if USE_INTERFACE
+INTERFACE static pointer s_cons(scheme *sc, pointer a, pointer b) {
+ return cons(sc,a,b);
+}
+INTERFACE static pointer s_immutable_cons(scheme *sc, pointer a, pointer b) {
+ return immutable_cons(sc,a,b);
+}
+
+static struct scheme_interface vtbl ={
+ scheme_define,
+ s_cons,
+ s_immutable_cons,
+ reserve_cells,
+ mk_integer,
+ mk_real,
+ mk_symbol,
+ gensym,
+ mk_string,
+ mk_counted_string,
+ mk_character,
+ mk_vector,
+ mk_foreign_func,
+ mk_closure,
+ putstr,
+ putcharacter,
+
+ is_string,
+ string_length,
+ string_value,
+ is_number,
+ nvalue,
+ ivalue,
+ rvalue,
+ is_integer,
+ is_real,
+ is_character,
+ charvalue,
+ is_list,
+ is_vector,
+ list_length,
+ ivalue,
+ fill_vector,
+ vector_elem,
+ set_vector_elem,
+ is_port,
+ is_pair,
+ pair_car,
+ pair_cdr,
+ set_car,
+ set_cdr,
+
+ is_symbol,
+ symname,
+
+ is_syntax,
+ is_proc,
+ is_foreign,
+ syntaxname,
+ is_closure,
+ is_macro,
+ closure_code,
+ closure_env,
+
+ is_continuation,
+ is_promise,
+ is_environment,
+ is_immutable,
+ setimmutable,
+
+ scheme_load_file,
+ scheme_load_string
+};
+#endif
+
+scheme *scheme_init_new(void) {
+ scheme *sc=(scheme*)malloc(sizeof(scheme));
+ if(!scheme_init(sc)) {
+ free(sc);
+ return 0;
+ } else {
+ return sc;
+ }
+}
+
+scheme *scheme_init_new_custom_alloc(func_alloc malloc, func_dealloc free) {
+ scheme *sc=(scheme*)malloc(sizeof(scheme));
+ if(!scheme_init_custom_alloc(sc,malloc,free)) {
+ free(sc);
+ return 0;
+ } else {
+ return sc;
+ }
+}
+
+
+int scheme_init(scheme *sc) {
+ return scheme_init_custom_alloc(sc,malloc,free);
+}
+
+int scheme_init_custom_alloc(scheme *sc, func_alloc malloc, func_dealloc free) {
+ int i, n=sizeof(dispatch_table)/sizeof(dispatch_table[0]);
+ pointer x;
+
+ num_zero.is_fixnum=1;
+ num_zero.value.ivalue=0;
+ num_one.is_fixnum=1;
+ num_one.value.ivalue=1;
+
+#if USE_INTERFACE
+ sc->vptr=&vtbl;
+#endif
+ sc->gensym_cnt=0;
+ sc->malloc=malloc;
+ sc->free=free;
+ sc->last_cell_seg = -1;
+ sc->sink = &sc->_sink;
+ sc->NIL = &sc->_NIL;
+ sc->T = &sc->_HASHT;
+ sc->F = &sc->_HASHF;
+ sc->EOF_OBJ=&sc->_EOF_OBJ;
+ sc->free_cell = &sc->_NIL;
+ sc->fcells = 0;
+ sc->no_memory=0;
+ sc->inport=sc->NIL;
+ sc->outport=sc->NIL;
+ sc->save_inport=sc->NIL;
+ sc->loadport=sc->NIL;
+ sc->nesting=0;
+ sc->interactive_repl=0;
+ sc->print_output=0;
+
+ if (alloc_cellseg(sc,FIRST_CELLSEGS) != FIRST_CELLSEGS) {
+ sc->no_memory=1;
+ return 0;
+ }
+ sc->gc_verbose = 0;
+ dump_stack_initialize(sc);
+ sc->code = sc->NIL;
+ sc->tracing=0;
+ sc->bc_flag = 0;
+
+ /* init sc->NIL */
+ typeflag(sc->NIL) = (T_ATOM | MARK);
+ car(sc->NIL) = cdr(sc->NIL) = sc->NIL;
+ /* init T */
+ typeflag(sc->T) = (T_ATOM | MARK);
+ car(sc->T) = cdr(sc->T) = sc->T;
+ /* init F */
+ typeflag(sc->F) = (T_ATOM | MARK);
+ car(sc->F) = cdr(sc->F) = sc->F;
+ /* init sink */
+ typeflag(sc->sink) = (T_PAIR | MARK);
+ car(sc->sink) = sc->NIL;
+ /* init c_nest */
+ sc->c_nest = sc->NIL;
+
+ sc->oblist = oblist_initial_value(sc);
+ /* init global_env */
+ new_frame_in_env(sc, sc->NIL);
+ sc->global_env = sc->envir;
+ /* init else */
+ x = mk_symbol(sc,"else");
+ new_slot_in_env(sc, x, sc->T);
+
+ assign_syntax(sc, "lambda");
+ assign_syntax(sc, "quote");
+ assign_syntax(sc, "define");
+ assign_syntax(sc, "if");
+ assign_syntax(sc, "begin");
+ assign_syntax(sc, "set!");
+ assign_syntax(sc, "let");
+ assign_syntax(sc, "let*");
+ assign_syntax(sc, "letrec");
+ assign_syntax(sc, "cond");
+ assign_syntax(sc, "delay");
+ assign_syntax(sc, "and");
+ assign_syntax(sc, "or");
+ assign_syntax(sc, "cons-stream");
+ assign_syntax(sc, "macro");
+ assign_syntax(sc, "case");
+
+ for(i=0; i<n; i++) {
+ if(dispatch_table[i].name!=0) {
+ assign_proc(sc, (enum scheme_opcodes)i, dispatch_table[i].name);
+ }
+ }
+
+ /* initialization of global pointers to special symbols */
+ sc->LAMBDA = mk_symbol(sc, "lambda");
+ sc->QUOTE = mk_symbol(sc, "quote");
+ sc->QQUOTE = mk_symbol(sc, "quasiquote");
+ sc->UNQUOTE = mk_symbol(sc, "unquote");
+ sc->UNQUOTESP = mk_symbol(sc, "unquote-splicing");
+ sc->FEED_TO = mk_symbol(sc, "=>");
+ sc->COLON_HOOK = mk_symbol(sc,"*colon-hook*");
+ sc->ERROR_HOOK = mk_symbol(sc, "*error-hook*");
+ sc->SHARP_HOOK = mk_symbol(sc, "*sharp-hook*");
+ sc->COMPILE_HOOK = mk_symbol(sc, "*compile-hook*");
+
+ return !sc->no_memory;
+}
+
+SCHEME_EXPORT void scheme_set_input_port_file(scheme *sc, FILE *fin) {
+ sc->inport=port_from_file(sc,fin,port_input);
+}
+
+void scheme_set_input_port_string(scheme *sc, char *start, char *past_the_end) {
+ sc->inport=port_from_string(sc,start,past_the_end,port_input);
+}
+
+SCHEME_EXPORT void scheme_set_output_port_file(scheme *sc, FILE *fout) {
+ sc->outport=port_from_file(sc,fout,port_output);
+}
+
+void scheme_set_output_port_string(scheme *sc, char *start, char *past_the_end) {
+ sc->outport=port_from_string(sc,start,past_the_end,port_output);
+}
+
+void scheme_set_external_data(scheme *sc, void *p) {
+ sc->ext_data=p;
+}
+
+void scheme_deinit(scheme *sc) {
+ int i;
+
+#if SHOW_ERROR_LINE
+ char *fname;
+#endif
+
+ sc->oblist=sc->NIL;
+ sc->global_env=sc->NIL;
+ dump_stack_free(sc);
+ sc->envir=sc->NIL;
+ sc->code=sc->NIL;
+ sc->args=sc->NIL;
+ sc->value=sc->NIL;
+ if(is_port(sc->inport)) {
+ typeflag(sc->inport) = T_ATOM;
+ }
+ sc->inport=sc->NIL;
+ sc->outport=sc->NIL;
+ if(is_port(sc->save_inport)) {
+ typeflag(sc->save_inport) = T_ATOM;
+ }
+ sc->save_inport=sc->NIL;
+ if(is_port(sc->loadport)) {
+ typeflag(sc->loadport) = T_ATOM;
+ }
+ sc->loadport=sc->NIL;
+ sc->gc_verbose=0;
+ gc(sc,sc->NIL,sc->NIL);
+
+ for(i=0; i<=sc->last_cell_seg; i++) {
+ sc->free(sc->alloc_seg[i]);
+ }
+
+#if SHOW_ERROR_LINE
+ for(i=0; i<sc->file_i; i++) {
+ if (sc->load_stack[sc->file_i].kind & port_file) {
+ fname = sc->load_stack[i].rep.stdio.filename;
+ if(fname)
+ sc->free(fname);
+ }
+ }
+#endif
+}
+
+void scheme_load_file(scheme *sc, FILE *fin)
+{ scheme_load_named_file(sc,fin,0); }
+
+void scheme_load_named_file(scheme *sc, FILE *fin, const char *filename) {
+ dump_stack_reset(sc);
+ sc->envir = sc->global_env;
+ sc->file_i=0;
+ sc->load_stack[0].kind=port_input|port_file;
+ sc->load_stack[0].rep.stdio.file=fin;
+ sc->loadport=mk_port(sc,sc->load_stack);
+ sc->retcode=0;
+ if(fin==stdin) {
+ sc->interactive_repl=1;
+ }
+
+#if SHOW_ERROR_LINE
+ sc->load_stack[0].rep.stdio.curr_line = 0;
+ if(fin!=stdin && filename)
+ sc->load_stack[0].rep.stdio.filename = store_string(sc, strlen(filename), filename, 0);
+ else
+ sc->load_stack[0].rep.stdio.filename = NULL;
+#endif
+
+ sc->inport=sc->loadport;
+ sc->args = mk_integer(sc,sc->file_i);
+ Eval_Cycle(sc, OP_T0LVL);
+ typeflag(sc->loadport)=T_ATOM;
+ if(sc->retcode==0) {
+ sc->retcode=sc->nesting!=0;
+ }
+}
+
+void scheme_load_string(scheme *sc, const char *cmd) {
+ dump_stack_reset(sc);
+ sc->envir = sc->global_env;
+ sc->file_i=0;
+ sc->load_stack[0].kind=port_input|port_string;
+ sc->load_stack[0].rep.string.start=(char*)cmd; /* This func respects const */
+ sc->load_stack[0].rep.string.past_the_end=(char*)cmd+strlen(cmd);
+ sc->load_stack[0].rep.string.curr=(char*)cmd;
+ sc->loadport=mk_port(sc,sc->load_stack);
+ sc->retcode=0;
+ sc->interactive_repl=0;
+ sc->inport=sc->loadport;
+ sc->args = mk_integer(sc,sc->file_i);
+ Eval_Cycle(sc, OP_T0LVL);
+ typeflag(sc->loadport)=T_ATOM;
+ if(sc->retcode==0) {
+ sc->retcode=sc->nesting!=0;
+ }
+}
+
+void scheme_define(scheme *sc, pointer envir, pointer symbol, pointer value) {
+ pointer x;
+
+ x=find_slot_in_env(sc,envir,symbol,0);
+ if (x != sc->NIL) {
+ set_slot_in_env(sc, x, value);
+ } else {
+ new_slot_spec_in_env(sc, envir, symbol, value);
+ }
+}
+
+#if !STANDALONE
+void scheme_register_foreign_func(scheme * sc, scheme_registerable * sr)
+{
+ scheme_define(sc,
+ sc->global_env,
+ mk_symbol(sc,sr->name),
+ mk_foreign_func(sc, sr->f));
+}
+
+void scheme_register_foreign_func_list(scheme * sc,
+ scheme_registerable * list,
+ int count)
+{
+ int i;
+ for(i = 0; i < count; i++)
+ {
+ scheme_register_foreign_func(sc, list + i);
+ }
+}
+
+pointer scheme_apply0(scheme *sc, const char *procname)
+{ return scheme_eval(sc, cons(sc,mk_symbol(sc,procname),sc->NIL)); }
+
+static void save_from_C_call(scheme *sc)
+{
+ pointer saved_data =
+ cons(sc,
+ car(sc->sink),
+ cons(sc,
+ sc->envir,
+ sc->dump));
+ /* Push */
+ sc->c_nest = cons(sc, saved_data, sc->c_nest);
+ /* Truncate the dump stack so TS will return here when done, not
+ directly resume pre-C-call operations. */
+ dump_stack_reset(sc);
+}
+
+static void restore_from_C_call(scheme *sc)
+{
+ car(sc->sink) = caar(sc->c_nest);
+ sc->envir = cadar(sc->c_nest);
+ sc->dump = cdr(cdar(sc->c_nest));
+ /* Pop */
+ sc->c_nest = cdr(sc->c_nest);
+}
+
+/* "func" and "args" are assumed to be already eval'ed. */
+pointer scheme_call(scheme *sc, pointer func, pointer args)
+{
+ int old_repl = sc->interactive_repl;
+ sc->interactive_repl = 0;
+ save_from_C_call(sc);
+ sc->envir = sc->global_env;
+ sc->args = args;
+ sc->code = func;
+ sc->retcode = 0;
+ Eval_Cycle(sc, OP_APPLY);
+ sc->interactive_repl = old_repl;
+ restore_from_C_call(sc);
+ return sc->value;
+}
+
+pointer scheme_eval(scheme *sc, pointer obj)
+{
+ int old_repl = sc->interactive_repl;
+ sc->interactive_repl = 0;
+ save_from_C_call(sc);
+ sc->args = sc->NIL;
+ sc->code = obj;
+ sc->retcode = 0;
+ Eval_Cycle(sc, OP_EVAL);
+ sc->interactive_repl = old_repl;
+ restore_from_C_call(sc);
+ return sc->value;
+}
+
+
+#endif
+
+/* ========== Main ========== */
+
+#if STANDALONE
+
+#if defined(__APPLE__) && !defined (OSX)
+int main(int argc, char **argv)
+{
+ extern MacTS_main(int argc, char **argv);
+ char** argv;
+ int argc = ccommand(&argv);
+ MacTS_main(argc,argv);
+ return 0;
+}
+int MacTS_main(int argc, char **argv) {
+#else
+int main(int argc, char **argv) {
+#endif
+ scheme sc;
+ FILE *fin;
+ char *file_name=InitFile;
+ int retcode;
+ int isfile=1;
+
+ if(argc==1) {
+ printf(banner);
+ }
+ if(argc==2 && strcmp(argv[1],"-?")==0) {
+ printf("Usage: tinyscheme -?\n");
+ printf("or: tinyscheme [<file1> <file2> ...]\n");
+ printf("followed by\n");
+ printf(" -1 <file> [<arg1> <arg2> ...]\n");
+ printf(" -c <Scheme commands> [<arg1> <arg2> ...]\n");
+ printf("assuming that the executable is named tinyscheme.\n");
+ printf("Use - as filename for stdin.\n");
+ return 1;
+ }
+ if(!scheme_init(&sc)) {
+ fprintf(stderr,"Could not initialize!\n");
+ return 2;
+ }
+ scheme_set_input_port_file(&sc, stdin);
+ scheme_set_output_port_file(&sc, stdout);
+#if USE_DL
+ scheme_define(&sc,sc.global_env,mk_symbol(&sc,"load-extension"),mk_foreign_func(&sc, scm_load_ext));
+#endif
+ argv++;
+ if(g_access(file_name,0)!=0) {
+ char *p=getenv("TINYSCHEMEINIT");
+ if(p!=0) {
+ file_name=p;
+ }
+ }
+ do {
+ if(strcmp(file_name,"-")==0) {
+ fin=stdin;
+ } else if(strcmp(file_name,"-1")==0 || strcmp(file_name,"-c")==0) {
+ pointer args=sc.NIL;
+ isfile=file_name[1]=='1';
+ file_name=*argv++;
+ if(strcmp(file_name,"-")==0) {
+ fin=stdin;
+ } else if(isfile) {
+ fin=g_fopen(file_name,"r");
+ }
+ for(;*argv;argv++) {
+ pointer value=mk_string(&sc,*argv);
+ args=cons(&sc,value,args);
+ }
+ args=reverse_in_place(&sc,sc.NIL,args);
+ scheme_define(&sc,sc.global_env,mk_symbol(&sc,"*args*"),args);
+
+ } else {
+ fin=g_fopen(file_name,"r");
+ }
+ if(isfile && fin==0) {
+ fprintf(stderr,"Could not open file %s\n",file_name);
+ } else {
+ if(isfile) {
+ scheme_load_named_file(&sc,fin,file_name);
+ } else {
+ scheme_load_string(&sc,file_name);
+ }
+ if(!isfile || fin!=stdin) {
+ if(sc.retcode!=0) {
+ fprintf(stderr,"Errors encountered reading %s\n",file_name);
+ }
+ if(isfile) {
+ fclose(fin);
+ }
+ }
+ }
+ file_name=*argv++;
+ } while(file_name!=0);
+ if(argc==1) {
+ scheme_load_named_file(&sc,stdin,0);
+ }
+ retcode=sc.retcode;
+ scheme_deinit(&sc);
+
+ return retcode;
+}
+
+#endif
+
+/*
+Local variables:
+c-file-style: "k&r"
+End:
+*/
diff --git a/plug-ins/script-fu/tinyscheme/scheme.h b/plug-ins/script-fu/tinyscheme/scheme.h
new file mode 100644
index 0000000..198182c
--- /dev/null
+++ b/plug-ins/script-fu/tinyscheme/scheme.h
@@ -0,0 +1,273 @@
+/* SCHEME.H */
+
+#ifndef _SCHEME_H
+#define _SCHEME_H
+
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Default values for #define'd symbols
+ */
+#ifndef STANDALONE /* If used as standalone interpreter */
+# define STANDALONE 1
+#endif
+
+#ifndef _MSC_VER
+# ifndef USE_STRLWR
+# define USE_STRLWR 1
+# endif
+# define SCHEME_EXPORT extern
+#else
+# define USE_STRLWR 0
+# ifdef _SCHEME_SOURCE
+# define SCHEME_EXPORT __declspec(dllexport)
+# else
+# define SCHEME_EXPORT __declspec(dllimport)
+# endif
+#endif
+
+#if USE_NO_FEATURES
+# define USE_MATH 0
+# define USE_CHAR_CLASSIFIERS 0
+# define USE_ASCII_NAMES 0
+# define USE_STRING_PORTS 0
+# define USE_ERROR_HOOK 0
+# define USE_TRACING 0
+# define USE_COLON_HOOK 0
+# define USE_DL 0
+# define USE_PLIST 0
+#endif
+
+/*
+ * Leave it defined if you want continuations, and also for the Sharp Zaurus.
+ * Undefine it if you only care about faster speed and not strict Scheme compatibility.
+ */
+#define USE_SCHEME_STACK
+
+#if USE_DL
+# define USE_INTERFACE 1
+#endif
+
+
+#ifndef USE_MATH /* If math support is needed */
+# define USE_MATH 1
+#endif
+
+#ifndef USE_CHAR_CLASSIFIERS /* If char classifiers are needed */
+# define USE_CHAR_CLASSIFIERS 1
+#endif
+
+#ifndef USE_ASCII_NAMES /* If extended escaped characters are needed */
+# define USE_ASCII_NAMES 1
+#endif
+
+#ifndef USE_STRING_PORTS /* Enable string ports */
+# define USE_STRING_PORTS 1
+#endif
+
+#ifndef USE_TRACING
+# define USE_TRACING 1
+#endif
+
+#ifndef USE_PLIST
+# define USE_PLIST 0
+#endif
+
+/* To force system errors through user-defined error handling (see *error-hook*) */
+#ifndef USE_ERROR_HOOK
+# define USE_ERROR_HOOK 1
+#endif
+
+#ifndef USE_COLON_HOOK /* Enable qualified qualifier */
+# define USE_COLON_HOOK 1
+#endif
+
+#ifndef USE_STRLWR
+# define USE_STRLWR 1
+#endif
+
+#ifndef STDIO_ADDS_CR /* Define if DOS/Windows */
+# define STDIO_ADDS_CR 0
+#endif
+
+#ifndef INLINE
+# define INLINE
+#endif
+
+#ifndef USE_INTERFACE
+# define USE_INTERFACE 0
+#endif
+
+#ifndef SHOW_ERROR_LINE /* Show error line in file */
+# define SHOW_ERROR_LINE 1
+#endif
+
+typedef struct scheme scheme;
+typedef struct cell *pointer;
+
+typedef void * (*func_alloc)(size_t);
+typedef void (*func_dealloc)(void *);
+
+/* num, for generic arithmetic */
+typedef struct num {
+ char is_fixnum;
+ union {
+ long ivalue;
+ double rvalue;
+ } value;
+} num;
+
+#if !STANDALONE
+
+typedef enum { TS_OUTPUT_NORMAL, TS_OUTPUT_ERROR } TsOutputType;
+
+typedef void (* ts_output_func) (TsOutputType type,
+ const char *string,
+ int len,
+ gpointer data);
+
+SCHEME_EXPORT void ts_register_output_func (ts_output_func func,
+ gpointer user_data);
+SCHEME_EXPORT void ts_output_string (TsOutputType type,
+ const char *string,
+ int len);
+#endif
+
+SCHEME_EXPORT scheme *scheme_init_new(void);
+SCHEME_EXPORT scheme *scheme_init_new_custom_alloc(func_alloc malloc, func_dealloc free);
+SCHEME_EXPORT int scheme_init(scheme *sc);
+SCHEME_EXPORT int scheme_init_custom_alloc(scheme *sc, func_alloc, func_dealloc);
+SCHEME_EXPORT void scheme_deinit(scheme *sc);
+SCHEME_EXPORT void scheme_set_input_port_file(scheme *sc, FILE *fin);
+void scheme_set_input_port_string(scheme *sc, char *start, char *past_the_end);
+SCHEME_EXPORT void scheme_set_output_port_file(scheme *sc, FILE *fin);
+void scheme_set_output_port_string(scheme *sc, char *start, char *past_the_end);
+SCHEME_EXPORT void scheme_load_file(scheme *sc, FILE *fin);
+SCHEME_EXPORT void scheme_load_named_file(scheme *sc, FILE *fin, const char *filename);
+SCHEME_EXPORT void scheme_load_string(scheme *sc, const char *cmd);
+SCHEME_EXPORT pointer scheme_apply0(scheme *sc, const char *procname);
+SCHEME_EXPORT pointer scheme_call(scheme *sc, pointer func, pointer args);
+SCHEME_EXPORT pointer scheme_eval(scheme *sc, pointer obj);
+void scheme_set_external_data(scheme *sc, void *p);
+SCHEME_EXPORT void scheme_define(scheme *sc, pointer env, pointer symbol, pointer value);
+
+typedef pointer (*foreign_func)(scheme *, pointer);
+
+pointer _cons(scheme *sc, pointer a, pointer b, int immutable);
+pointer mk_integer(scheme *sc, long num);
+pointer mk_real(scheme *sc, double num);
+pointer mk_symbol(scheme *sc, const char *name);
+pointer gensym(scheme *sc);
+pointer mk_string(scheme *sc, const char *str);
+pointer mk_counted_string(scheme *sc, const char *str, int len);
+pointer mk_empty_string(scheme *sc, int len, gunichar fill);
+pointer mk_character(scheme *sc, gunichar c);
+pointer mk_foreign_func(scheme *sc, foreign_func f);
+void putcharacter(scheme *sc, gunichar c);
+void putstr(scheme *sc, const char *s);
+int list_length(scheme *sc, pointer a);
+int eqv(pointer a, pointer b);
+
+SCHEME_EXPORT pointer foreign_error (scheme *sc, const char *s, pointer a);
+
+#if USE_INTERFACE
+struct scheme_interface {
+ void (*scheme_define)(scheme *sc, pointer env, pointer symbol, pointer value);
+ pointer (*cons)(scheme *sc, pointer a, pointer b);
+ pointer (*immutable_cons)(scheme *sc, pointer a, pointer b);
+ pointer (*reserve_cells)(scheme *sc, int n);
+ pointer (*mk_integer)(scheme *sc, long num);
+ pointer (*mk_real)(scheme *sc, double num);
+ pointer (*mk_symbol)(scheme *sc, const char *name);
+ pointer (*gensym)(scheme *sc);
+ pointer (*mk_string)(scheme *sc, const char *str);
+ pointer (*mk_counted_string)(scheme *sc, const char *str, int len);
+ pointer (*mk_character)(scheme *sc, gunichar c);
+ pointer (*mk_vector)(scheme *sc, int len);
+ pointer (*mk_foreign_func)(scheme *sc, foreign_func f);
+ pointer (*mk_closure)(scheme *sc, pointer c, pointer e);
+ void (*putstr)(scheme *sc, const char *s);
+ void (*putcharacter)(scheme *sc, gunichar c);
+
+ int (*is_string)(pointer p);
+ int (*string_length)(pointer p);
+ char *(*string_value)(pointer p);
+ int (*is_number)(pointer p);
+ num (*nvalue)(pointer p);
+ long (*ivalue)(pointer p);
+ double (*rvalue)(pointer p);
+ int (*is_integer)(pointer p);
+ int (*is_real)(pointer p);
+ int (*is_character)(pointer p);
+ gunichar (*charvalue)(pointer p);
+ int (*is_list)(scheme *sc, pointer p);
+ int (*is_vector)(pointer p);
+ int (*list_length)(scheme *sc, pointer p);
+ long (*vector_length)(pointer vec);
+ void (*fill_vector)(pointer vec, pointer elem);
+ pointer (*vector_elem)(pointer vec, int ielem);
+ pointer (*set_vector_elem)(pointer vec, int ielem, pointer newel);
+
+ int (*is_port)(pointer p);
+
+ int (*is_pair)(pointer p);
+ pointer (*pair_car)(pointer p);
+ pointer (*pair_cdr)(pointer p);
+ pointer (*set_car)(pointer p, pointer q);
+ pointer (*set_cdr)(pointer p, pointer q);
+
+ int (*is_symbol)(pointer p);
+ char *(*symname)(pointer p);
+
+ int (*is_syntax)(pointer p);
+ int (*is_proc)(pointer p);
+ int (*is_foreign)(pointer p);
+ char *(*syntaxname)(pointer p);
+ int (*is_closure)(pointer p);
+ int (*is_macro)(pointer p);
+ pointer (*closure_code)(pointer p);
+ pointer (*closure_env)(pointer p);
+
+ int (*is_continuation)(pointer p);
+ int (*is_promise)(pointer p);
+ int (*is_environment)(pointer p);
+ int (*is_immutable)(pointer p);
+ void (*setimmutable)(pointer p);
+
+ void (*load_file)(scheme *sc, FILE *fin);
+ void (*load_string)(scheme *sc, const char *input);
+};
+#endif
+
+#if !STANDALONE
+typedef struct scheme_registerable
+{
+ foreign_func f;
+ const char * name;
+}
+scheme_registerable;
+
+void scheme_register_foreign_func(scheme * sc, scheme_registerable * sr);
+void scheme_register_foreign_func_list(scheme * sc,
+ scheme_registerable * list,
+ int n);
+
+#endif /* !STANDALONE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*
+Local variables:
+c-file-style: "k&r"
+End:
+*/
diff --git a/plug-ins/selection-to-path/Makefile.am b/plug-ins/selection-to-path/Makefile.am
new file mode 100644
index 0000000..e27d85e
--- /dev/null
+++ b/plug-ins/selection-to-path/Makefile.am
@@ -0,0 +1,73 @@
+## Process this file with automake to produce Makefile.in
+
+if OS_WIN32
+mwindows = -mwindows
+else
+libm = -lm
+endif
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+selection_to_path_RC = selection-to-path.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/selection-to-path
+
+libexec_PROGRAMS = selection-to-path
+
+selection_to_path_SOURCES = \
+ bitmap.h \
+ bounding-box.h \
+ curve.c \
+ curve.h \
+ edge.c \
+ edge.h \
+ fit.c \
+ fit.h \
+ global.h \
+ math.c \
+ pxl-outline.c \
+ pxl-outline.h \
+ selection-to-path.c \
+ selection-to-path.h \
+ selection-to-path-dialog.c \
+ spline.c \
+ spline.h \
+ types.h \
+ vector.c \
+ vector.h
+
+EXTRA_DIST = \
+ README \
+ README.limn
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(selection_to_path_RC)
diff --git a/plug-ins/selection-to-path/Makefile.in b/plug-ins/selection-to-path/Makefile.in
new file mode 100644
index 0000000..2ac5eba
--- /dev/null
+++ b/plug-ins/selection-to-path/Makefile.in
@@ -0,0 +1,1057 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = selection-to-path$(EXEEXT)
+subdir = plug-ins/selection-to-path
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_selection_to_path_OBJECTS = curve.$(OBJEXT) edge.$(OBJEXT) \
+ fit.$(OBJEXT) math.$(OBJEXT) pxl-outline.$(OBJEXT) \
+ selection-to-path.$(OBJEXT) selection-to-path-dialog.$(OBJEXT) \
+ spline.$(OBJEXT) vector.$(OBJEXT)
+selection_to_path_OBJECTS = $(am_selection_to_path_OBJECTS)
+selection_to_path_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+selection_to_path_DEPENDENCIES = $(am__DEPENDENCIES_1) $(libgimpui) \
+ $(libgimpwidgets) $(libgimpconfig) $(libgimpmath) $(libgimp) \
+ $(libgimpcolor) $(libgimpbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(selection_to_path_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/curve.Po ./$(DEPDIR)/edge.Po \
+ ./$(DEPDIR)/fit.Po ./$(DEPDIR)/math.Po \
+ ./$(DEPDIR)/pxl-outline.Po \
+ ./$(DEPDIR)/selection-to-path-dialog.Po \
+ ./$(DEPDIR)/selection-to-path.Po ./$(DEPDIR)/spline.Po \
+ ./$(DEPDIR)/vector.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(selection_to_path_SOURCES)
+DIST_SOURCES = $(selection_to_path_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/selection-to-path
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+@OS_WIN32_TRUE@mwindows = -mwindows
+@OS_WIN32_FALSE@libm = -lm
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@selection_to_path_RC = selection-to-path.rc.o
+AM_LDFLAGS = $(mwindows)
+selection_to_path_SOURCES = \
+ bitmap.h \
+ bounding-box.h \
+ curve.c \
+ curve.h \
+ edge.c \
+ edge.h \
+ fit.c \
+ fit.h \
+ global.h \
+ math.c \
+ pxl-outline.c \
+ pxl-outline.h \
+ selection-to-path.c \
+ selection-to-path.h \
+ selection-to-path-dialog.c \
+ spline.c \
+ spline.h \
+ types.h \
+ vector.c \
+ vector.h
+
+EXTRA_DIST = \
+ README \
+ README.limn
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libm) \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimpmath) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(selection_to_path_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/selection-to-path/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/selection-to-path/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+selection-to-path$(EXEEXT): $(selection_to_path_OBJECTS) $(selection_to_path_DEPENDENCIES) $(EXTRA_selection_to_path_DEPENDENCIES)
+ @rm -f selection-to-path$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(selection_to_path_OBJECTS) $(selection_to_path_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/curve.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edge.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/math.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pxl-outline.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selection-to-path-dialog.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/selection-to-path.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spline.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vector.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/curve.Po
+ -rm -f ./$(DEPDIR)/edge.Po
+ -rm -f ./$(DEPDIR)/fit.Po
+ -rm -f ./$(DEPDIR)/math.Po
+ -rm -f ./$(DEPDIR)/pxl-outline.Po
+ -rm -f ./$(DEPDIR)/selection-to-path-dialog.Po
+ -rm -f ./$(DEPDIR)/selection-to-path.Po
+ -rm -f ./$(DEPDIR)/spline.Po
+ -rm -f ./$(DEPDIR)/vector.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/curve.Po
+ -rm -f ./$(DEPDIR)/edge.Po
+ -rm -f ./$(DEPDIR)/fit.Po
+ -rm -f ./$(DEPDIR)/math.Po
+ -rm -f ./$(DEPDIR)/pxl-outline.Po
+ -rm -f ./$(DEPDIR)/selection-to-path-dialog.Po
+ -rm -f ./$(DEPDIR)/selection-to-path.Po
+ -rm -f ./$(DEPDIR)/spline.Po
+ -rm -f ./$(DEPDIR)/vector.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/selection-to-path/README b/plug-ins/selection-to-path/README
new file mode 100644
index 0000000..80fa1c0
--- /dev/null
+++ b/plug-ins/selection-to-path/README
@@ -0,0 +1,51 @@
+
+
+Andy Thomas (alt@gimp.org) 9th July 1999
+
+
+This plug-in will take a selection and convert it into a path.
+For the purpose of the plug-in the selection boundary is defined
+in a similar manner to that worked out for the "marching ants" markers
+of the selection. I think this gives the best user feel/feedback since
+the created path "follows" the "marching ants".
+
+I cannot claim responsibility for the underlying algorithms. These
+were taken directly from the GNU font utilities (the "limn" program
+in particular) written by Karl Berry and Kathryn Hargreaves.
+
+Their email addresses quoted in the README are:-
+
+Karl Berry karl@cs.umb.edu
+Kathryn Hargreaves letters@cs.umb.edu
+
+Please see fontutils-0.6 package for more details. I have included the
+README from the limn part of the package.
+
+I thank Karl & Kathryn for producing such a well written set of utilities.
+
+I have just added a gimp front-end onto them.
+
+
+How to use it.
+~~~~~~~~~~~~~~
+
+Simply select an area and then select either "<Image>/Selection/To Path"
+menu item or the "Selection To Image" button in the paths dialog. The new
+path will be created. Currently if the LCP dialog has not been activated
+then the path will not be visible... A bug I have just found - simply
+bring up the LCP dialog and select the Paths tab to see the newly created
+path.
+
+An additional function can be obtained by having the "Shift" modifier pressed
+while using the button in the paths dialog. This will pop-up a "power-users"
+menu where the parameters to the underlying algorithms can be modified.
+WARNING:- Some values may cause the plugin to enter extremely long operations.
+You have been warned.
+
+Have fun!
+
+
+Andy.
+
+PS. Please direct any bugs etc found in this plugin to either
+myself or the gimp-developer mailing list. Thank.
diff --git a/plug-ins/selection-to-path/README.limn b/plug-ins/selection-to-path/README.limn
new file mode 100644
index 0000000..3e0e568
--- /dev/null
+++ b/plug-ins/selection-to-path/README.limn
@@ -0,0 +1,56 @@
+This program converts bitmap fonts to a homegrown outline format, bezier
+(BZR). The program `bzrto' converts that format to something usable for
+typesetting.
+
+We used two main sources in writing the program:
+
+@mastersthesis{Schneider:PIC-88,
+ author = "Philip J. Schneider",
+ title = "Phoenix: An Interactive Curve Design System Based on the
+ Automatic Fitting of Hand-Sketched Curves",
+ school = inst-u-wash,
+ year = 1988,
+}
+
+@article{Plass:CG-17-229,
+ author = "Michael Plass and Maureen Stone",
+ title = "Curve-fitting with Piecewise Parametric Cubics",
+ journal = j-comp-graphics,
+ year = 1983,
+ volume = 17,
+ number = 3,
+ month = jul,
+ pages = "229-239",
+}
+
+We had access to the code for Phoenix, thanks to Philip, but none of our
+code is based on his (mostly because his task was allow interactive
+sketching, and ours to fit bitmap characters, and the two require
+different data structures). The general outline of the fitting
+algorithm does come from Phoenix.
+
+We also found this article helpful:
+
+@Inproceedings{Gonczarowski:RIDT91-1,
+ author = "Jakob Gonczarowski",
+ title = "A Fast Approach to Auto-tracing (with Parametric
+ Cubics)",
+ pages = "1--15",
+ crossref = "Morris:RIDT91",
+ acknowledgement = ack-kb,
+}
+
+@String{proc-RIDT91 = "Raster Imaging and Digital Typography II"}
+
+@Proceedings{Morris:RIDT91,
+ title = proc-RIDT91,
+ booktitle = proc-RIDT91,
+ year = "1991",
+ editor = "Robert A. Morris and Jacques Andr{\'e}",
+ publisher = pub-CUP,
+ address = pub-CUP:adr,
+ acknowledgement = ack-kb,
+}
+
+(These BibTeX entries are from the type.bib and ep.bib files on
+math.utah.edu:pub/tex/bib.)
diff --git a/plug-ins/selection-to-path/bitmap.h b/plug-ins/selection-to-path/bitmap.h
new file mode 100644
index 0000000..98c89ac
--- /dev/null
+++ b/plug-ins/selection-to-path/bitmap.h
@@ -0,0 +1,112 @@
+/* bitmap.h: definition for a bitmap type. No packing is done by
+ * default; each pixel is represented by an entire byte. Among other
+ * things, this means the type can be used for both grayscale and binary
+ * images.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include <stdio.h>
+#include "bounding-box.h"
+#include "types.h"
+
+
+/* If the bitmap holds 8-bit values, rather than one-bit, the
+ definition of BLACK here is wrong. So don't use it in that case! */
+#define WHITE 0
+#define BLACK 1
+
+
+/* The basic structure and macros to access it. */
+typedef struct
+{
+ dimensions_type dimensions;
+ one_byte *bitmap;
+} bitmap_type;
+
+/* The dimensions of the bitmap, in pixels. */
+#define BITMAP_DIMENSIONS(b) ((b).dimensions)
+
+/* The pixels, represented as an array of bytes (in contiguous storage).
+ Each pixel is a single byte, even for binary fonts. */
+#define BITMAP_BITS(b) ((b).bitmap)
+
+/* These are convenient abbreviations for getting inside the members. */
+#define BITMAP_WIDTH(b) DIMENSIONS_WIDTH (BITMAP_DIMENSIONS (b))
+#define BITMAP_HEIGHT(b) DIMENSIONS_HEIGHT (BITMAP_DIMENSIONS (b))
+
+/* This is the address of the first pixel in the row ROW. */
+#define BITMAP_ROW(b, row) (BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b))
+
+/* This is the pixel at [ROW,COL]. */
+#define BITMAP_PIXEL(b, row, col) \
+ (*(BITMAP_BITS (b) + (row) * BITMAP_WIDTH (b) + (col)))
+
+#define BITMAP_VALID_PIXEL(b, row, col) \
+ (0 <= (row) && (row) < BITMAP_HEIGHT (b) \
+ && 0 <= (col) && (col) < BITMAP_WIDTH (b))
+
+/* Assume that the pixel at [ROW,COL] itself is black. */
+
+#define BITMAP_INTERIOR_PIXEL(b, row, col) \
+ (0 != (row) && (row) != BITMAP_HEIGHT (b) - 1 \
+ && 0 != (col) && (col) != BITMAP_WIDTH (b) - 1 \
+ && BITMAP_PIXEL (b, row - 1, col - 1) == BLACK \
+ && BITMAP_PIXEL (b, row - 1, col) == BLACK \
+ && BITMAP_PIXEL (b, row - 1, col + 1) == BLACK \
+ && BITMAP_PIXEL (b, row, col - 1) == BLACK \
+ && BITMAP_PIXEL (b, row, col + 1) == BLACK \
+ && BITMAP_PIXEL (b, row + 1, col - 1) == BLACK \
+ && BITMAP_PIXEL (b, row + 1, col) == BLACK \
+ && BITMAP_PIXEL (b, row + 1, col + 1) == BLACK)
+
+/* Allocate storage for the bits, set them all to white, and return an
+ initialized structure. */
+extern bitmap_type new_bitmap (dimensions_type);
+
+/* Free that storage. */
+extern void free_bitmap (bitmap_type *);
+
+/* Make a fresh copy of BITMAP in a new structure, and return it. */
+extern bitmap_type copy_bitmap (bitmap_type bitmap);
+
+/* Return the pixels in the bitmap B enclosed by the bounding box BB.
+ The result is put in newly allocated storage. */
+extern bitmap_type extract_subbitmap (bitmap_type b, bounding_box_type bb);
+
+/* Consider the dimensions of a bitmap as a bounding box. The bounding
+ box returned is in bitmap coordinates, rather than Cartesian, and
+ refers to pixels, rather than edges. Specifically, this means that
+ the maximum column is one less than results from `dimensions_to_bb
+ (BITMAP_DIMENSIONS ())'. */
+extern bounding_box_type bitmap_to_bb (const bitmap_type);
+
+/* Return a vector of zero-based column numbers marking transitions from
+ black to white or white to black in ROW, which is of length WIDTH.
+ The end of the vector is marked with an element of length WIDTH + 1.
+ The first element always marks a white-to-black transition (or it's
+ 0, if the first pixel in ROW is black). */
+extern unsigned *bitmap_find_transitions (const one_byte *row, unsigned width);
+
+/* Print part of or all of a bitmap. */
+extern void print_bounded_bitmap (FILE *, bitmap_type, bounding_box_type);
+extern void print_bitmap (FILE *, bitmap_type);
+
+#endif /* not BITMAP_H */
+
diff --git a/plug-ins/selection-to-path/bounding-box.h b/plug-ins/selection-to-path/bounding-box.h
new file mode 100644
index 0000000..54f1e71
--- /dev/null
+++ b/plug-ins/selection-to-path/bounding-box.h
@@ -0,0 +1,63 @@
+/* bounding-box.h: operations on both real- and integer-valued bounding boxes.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef BOUNDING_BOX_H
+#define BOUNDING_BOX_H
+
+#include "types.h"
+
+
+/* The bounding box's numbers are usually in Cartesian/Metafont
+ coordinates: (0,0) is towards the lower left. */
+typedef struct
+{
+ signed_4_bytes min_row, max_row;
+ signed_4_bytes min_col, max_col;
+} bounding_box_type;
+
+typedef struct
+{
+ real min_row, max_row;
+ real min_col, max_col;
+} real_bounding_box_type;
+
+/* These accessing macros work for both types of bounding boxes, since
+ the member names are the same. */
+#define MIN_ROW(bb) ((bb).min_row)
+#define MAX_ROW(bb) ((bb).max_row)
+#define MIN_COL(bb) ((bb).min_col)
+#define MAX_COL(bb) ((bb).max_col)
+
+/* See the comments at `get_character_bitmap' in gf_input.c for why the
+ width and height are treated asymmetrically. */
+#define BB_WIDTH(bb) (MAX_COL (bb) - MIN_COL (bb))
+#define BB_HEIGHT(bb) (MAX_ROW (bb) - MIN_ROW (bb) + 1)
+
+
+/* Convert a dimensions structure to an integer bounding box, and vice
+ versa. */
+extern bounding_box_type dimensions_to_bb (dimensions_type);
+extern dimensions_type bb_to_dimensions (bounding_box_type);
+
+
+/* Update the bounding box BB from the point P. */
+extern void update_real_bounding_box (real_bounding_box_type *bb,
+ real_coordinate_type p);
+extern void update_bounding_box (bounding_box_type *bb, coordinate_type p);
+
+#endif /* not BOUNDING_BOX_H */
diff --git a/plug-ins/selection-to-path/curve.c b/plug-ins/selection-to-path/curve.c
new file mode 100644
index 0000000..afb157c
--- /dev/null
+++ b/plug-ins/selection-to-path/curve.c
@@ -0,0 +1,184 @@
+/* curve.c: operations on the lists of pixels and lists of curves.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "global.h"
+#include "curve.h"
+
+
+/* Return an entirely empty curve. */
+
+curve_type
+new_curve (void)
+{
+ curve_type curve = g_new (struct curve, 1);
+
+ curve->point_list = NULL;
+ CURVE_LENGTH (curve) = 0;
+ CURVE_CYCLIC (curve) = false;
+ CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
+ PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
+
+ return curve;
+}
+
+
+/* Start the returned curve off with COORD as the first point. */
+
+curve_type
+init_curve (coordinate_type coord)
+{
+ curve_type curve = new_curve ();
+
+ curve->point_list = g_new (point_type, 1);
+ CURVE_LENGTH (curve) = 1;
+
+ CURVE_POINT (curve, 0) = int_to_real_coord (coord);
+
+ return curve;
+}
+
+
+/* Don't copy the points or tangents, but copy everything else. */
+
+curve_type
+copy_most_of_curve (curve_type old_curve)
+{
+ curve_type curve = new_curve ();
+
+ CURVE_CYCLIC (curve) = CURVE_CYCLIC (old_curve);
+ PREVIOUS_CURVE (curve) = PREVIOUS_CURVE (old_curve);
+ NEXT_CURVE (curve) = NEXT_CURVE (old_curve);
+
+ return curve;
+}
+
+
+/* The length of CURVE will be zero if we ended up not being able to fit
+ it (which in turn implies a problem elsewhere in the program, but at
+ any rate, we shouldn't try here to free the nonexistent curve). */
+
+void
+free_curve (curve_type curve)
+{
+ if (CURVE_LENGTH (curve) > 0)
+ safe_free ((address *) &(curve->point_list));
+}
+
+
+void
+append_pixel (curve_type curve, coordinate_type coord)
+{
+ append_point (curve, int_to_real_coord (coord));
+}
+
+
+void
+append_point (curve_type curve, real_coordinate_type coord)
+{
+ CURVE_LENGTH (curve)++;
+ curve->point_list = g_realloc (curve->point_list,CURVE_LENGTH (curve) * sizeof(point_type));
+ LAST_CURVE_POINT (curve) = coord;
+ /* The t value does not need to be set. */
+}
+
+/* Return an initialized but empty curve list. */
+
+curve_list_type
+new_curve_list (void)
+{
+ curve_list_type curve_list;
+
+ curve_list.length = 0;
+ curve_list.data = NULL;
+ curve_list.clockwise = FALSE;
+
+ return curve_list;
+}
+
+
+/* Free a curve list and all the curves it contains. */
+
+void
+free_curve_list (curve_list_type *curve_list)
+{
+ unsigned this_curve;
+
+ for (this_curve = 0; this_curve < curve_list->length; this_curve++)
+ free_curve (curve_list->data[this_curve]);
+
+ /* If the character was empty, it won't have any curves. */
+ if (curve_list->data != NULL)
+ safe_free ((address *) &(curve_list->data));
+}
+
+
+/* Add an element to a curve list. */
+
+void
+append_curve (curve_list_type *curve_list, curve_type curve)
+{
+ curve_list->length++;
+ curve_list->data = g_realloc (curve_list->data,curve_list->length*sizeof(curve_type));
+ curve_list->data[curve_list->length - 1] = curve;
+}
+
+
+/* Return an initialized but empty curve list array. */
+
+curve_list_array_type
+new_curve_list_array (void)
+{
+ curve_list_array_type curve_list_array;
+
+ CURVE_LIST_ARRAY_LENGTH (curve_list_array) = 0;
+ curve_list_array.data = NULL;
+
+ return curve_list_array;
+}
+
+
+/* Free a curve list array and all the curve lists it contains. */
+
+void
+free_curve_list_array (curve_list_array_type *curve_list_array)
+{
+ unsigned this_list;
+
+ for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH (*curve_list_array);
+ this_list++)
+ free_curve_list (&CURVE_LIST_ARRAY_ELT (*curve_list_array, this_list));
+
+ /* If the character was empty, it won't have any curves. */
+ if (curve_list_array->data != NULL)
+ safe_free ((address *) &(curve_list_array->data));
+}
+
+
+/* Add an element to a curve list array. */
+
+void
+append_curve_list (curve_list_array_type *l, curve_list_type curve_list)
+{
+ CURVE_LIST_ARRAY_LENGTH (*l)++;
+ l->data = g_realloc (l->data,( CURVE_LIST_ARRAY_LENGTH (*l))*sizeof(curve_list_type));
+ LAST_CURVE_LIST_ARRAY_ELT (*l) = curve_list;
+}
diff --git a/plug-ins/selection-to-path/curve.h b/plug-ins/selection-to-path/curve.h
new file mode 100644
index 0000000..b0bd374
--- /dev/null
+++ b/plug-ins/selection-to-path/curve.h
@@ -0,0 +1,157 @@
+/* curve.h: data structures for the conversion from pixels to splines.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef CURVE_H
+#define CURVE_H
+
+#include "types.h"
+#include "vector.h"
+
+
+/* We are simultaneously manipulating two different representations of
+ the same outline: one based on (x,y) positions in the plane, and one
+ based on parametric splines. (We are trying to match the latter to
+ the former.) Although the original (x,y)'s are pixel positions,
+ i.e., integers, after filtering they are reals. */
+
+typedef struct
+{
+ real_coordinate_type coord;
+ real t;
+} point_type;
+
+
+/* It turns out to be convenient to break the list of all the pixels in
+ the outline into sublists, divided at ``corners''. Then each of the
+ sublists is treated independently. Each of these sublists is a `curve'. */
+
+struct curve
+{
+ point_type *point_list;
+ int length;
+ boolean cyclic;
+ vector_type *start_tangent;
+ vector_type *end_tangent;
+ struct curve *previous;
+ struct curve *next;
+};
+
+typedef struct curve *curve_type;
+
+/* Get at the coordinates and the t values. */
+#define CURVE_POINT(c, n) ((c)->point_list[n].coord)
+#define LAST_CURVE_POINT(c) ((c)->point_list[(c)->length-1].coord)
+#define CURVE_T(c, n) ((c)->point_list[n].t)
+#define LAST_CURVE_T(c) ((c)->point_list[(c)->length-1].t)
+
+/* This is the length of `point_list'. */
+#define CURVE_LENGTH(c) ((c)->length)
+
+/* A curve is ``cyclic'' if it didn't have any corners, after all, so
+ the last point is adjacent to the first. */
+#define CURVE_CYCLIC(c) ((c)->cyclic)
+
+/* If the curve is cyclic, the next and previous points should wrap
+ around; otherwise, if we get to the end, we return CURVE_LENGTH and
+ -1, respectively. */
+#define CURVE_NEXT(c, n) \
+ ((n) + 1 >= CURVE_LENGTH (c) \
+ ? CURVE_CYCLIC (c) ? ((n) + 1) % CURVE_LENGTH (c) : CURVE_LENGTH (c) \
+ : (n) + 1)
+#define CURVE_PREV(c, n) \
+ ((int) (n) - 1 < 0 \
+ ? CURVE_CYCLIC (c) ? CURVE_LENGTH (c) + (int) (n) - 1 : -1 \
+ : (int) (n) - 1)
+
+/* The tangents at the endpoints are computed using the neighboring curves. */
+#define CURVE_START_TANGENT(c) ((c)->start_tangent)
+#define CURVE_END_TANGENT(c) ((c)->end_tangent)
+#define PREVIOUS_CURVE(c) ((c)->previous)
+#define NEXT_CURVE(c) ((c)->next)
+
+
+/* Return an entirely empty curve. */
+extern curve_type new_curve (void);
+
+/* Return a curve with the point P as its first element. */
+extern curve_type init_curve (coordinate_type p);
+
+/* Return a curve the same as C, except without any points. */
+extern curve_type copy_most_of_curve (curve_type c);
+
+/* Free the memory C uses. */
+extern void free_curve (curve_type c);
+
+/* Append the point P to the end of C's list. */
+extern void append_pixel (curve_type c, coordinate_type p);
+
+/* Like `append_pixel', for a point in real coordinates. */
+extern void append_point (curve_type c, real_coordinate_type p);
+
+/* Write some or all, respectively, of the curve C in human-readable
+ form to the log file, if logging is enabled. */
+extern void log_curve (curve_type c, boolean print_t);
+extern void log_entire_curve (curve_type c);
+
+/* Display the curve C online, if displaying is enabled. */
+extern void display_curve (curve_type);
+
+/* So, an outline is a list of curves. */
+typedef struct
+{
+ curve_type *data;
+ unsigned length;
+ boolean clockwise;
+} curve_list_type;
+
+/* Number of curves in the list. */
+#define CURVE_LIST_LENGTH(c_l) ((c_l).length)
+
+/* Access the individual curves. */
+#define CURVE_LIST_ELT(c_l, n) ((c_l).data[n])
+#define LAST_CURVE_LIST_ELT(c_l) ((c_l).data[CURVE_LIST_LENGTH (c_l) - 1])
+
+/* Says whether the outline that this curve list represents moves
+ clockwise or counterclockwise. */
+#define CURVE_LIST_CLOCKWISE(c_l) ((c_l).clockwise)
+
+
+extern curve_list_type new_curve_list (void);
+extern void free_curve_list (curve_list_type *);
+extern void append_curve (curve_list_type *, curve_type);
+
+/* And a character is a list of outlines. I named this
+ `curve_list_array_type' because `curve_list_list_type' seemed pretty
+ monstrous. */
+typedef struct
+{
+ curve_list_type *data;
+ unsigned length;
+} curve_list_array_type;
+
+/* Turns out we can use the same definitions for lists of lists as for
+ just lists. But we define the usual names, just in case. */
+#define CURVE_LIST_ARRAY_LENGTH CURVE_LIST_LENGTH
+#define CURVE_LIST_ARRAY_ELT CURVE_LIST_ELT
+#define LAST_CURVE_LIST_ARRAY_ELT LAST_CURVE_LIST_ELT
+
+extern curve_list_array_type new_curve_list_array (void);
+extern void free_curve_list_array (curve_list_array_type *);
+extern void append_curve_list (curve_list_array_type *, curve_list_type);
+
+#endif /* not CURVE_H */
diff --git a/plug-ins/selection-to-path/edge.c b/plug-ins/selection-to-path/edge.c
new file mode 100644
index 0000000..8aa20be
--- /dev/null
+++ b/plug-ins/selection-to-path/edge.c
@@ -0,0 +1,268 @@
+/* edge.c: operations on edges in bitmaps.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "types.h"
+#include "selection-to-path.h"
+#include "edge.h"
+
+/* We can move in any of eight directions as we are traversing
+ the outline. These numbers are not arbitrary; TRY_PIXEL depends on
+ them. */
+
+typedef enum
+{
+ north = 0, northwest = 1, west = 2, southwest = 3, south = 4,
+ southeast = 5, east = 6, northeast = 7
+} direction_type;
+
+
+static boolean is_marked_edge (edge_type, unsigned, unsigned, bitmap_type);
+static boolean is_outline_edge (edge_type, unsigned, unsigned);
+static edge_type next_edge (edge_type);
+
+/* The following macros are used (directly or indirectly) by the
+ `next_outline_edge' routine. */
+
+/* Given the direction DIR of the pixel to test, decide which edge on
+ that pixel we are supposed to test. Because we've chosen the mapping
+ from directions to numbers carefully, we don't have to do much. */
+
+#define FIND_TEST_EDGE(dir) ((dir) / 2)
+
+
+/* Find how to move in direction DIR on the axis AXIS (either `ROW' or
+ `COL'). We are in the ``display'' coordinate system, with y
+ increasing downward and x increasing to the right. Therefore, we are
+ implementing the following table:
+
+ direction row delta col delta
+ north -1 0
+ south +1 0
+ east 0 +1
+ west 0 +1
+
+ with the other four directions (e.g., northwest) being the sum of
+ their components (e.g., north + west).
+
+ The first macro, `COMPUTE_DELTA', handles splitting up the latter
+ cases, all of which have been assigned odd numbers. */
+
+#define COMPUTE_DELTA(axis, dir) \
+ ((dir) % 2 != 0 \
+ ? COMPUTE_##axis##_DELTA ((dir) - 1) \
+ + COMPUTE_##axis##_DELTA (((dir) + 1) % 8) \
+ : COMPUTE_##axis##_DELTA (dir) \
+ )
+
+/* Now it is trivial to implement the four cardinal directions. */
+#define COMPUTE_ROW_DELTA(dir) \
+ ((dir) == north ? -1 : (dir) == south ? +1 : 0)
+
+#define COMPUTE_COL_DELTA(dir) \
+ ((dir) == west ? -1 : (dir) == east ? +1 : 0)
+
+
+/* See if the appropriate edge on the pixel from (row,col) in direction
+ DIR is on the outline. If so, update `row', `col', and `edge', and
+ break. We also use the variable `character' as the bitmap in which
+ to look. */
+
+#define TRY_PIXEL(dir) \
+ { \
+ int delta_r = COMPUTE_DELTA (ROW, dir); \
+ int delta_c = COMPUTE_DELTA (COL, dir); \
+ int test_row = *row + delta_r; \
+ int test_col = *col + delta_c; \
+ edge_type test_edge = FIND_TEST_EDGE (dir); \
+ \
+ if (sel_valid_pixel(test_row, test_col) \
+ && is_outline_edge (test_edge, test_row, test_col)) \
+ { \
+ *row = test_row; \
+ *col = test_col; \
+ *edge = test_edge; \
+ break; \
+ } \
+ }
+
+/* Finally, we are ready to implement the routine that finds the next
+ edge on the outline. We look first for an adjacent edge that is not
+ on the current pixel. We want to go around outside outlines
+ counterclockwise, and inside outlines clockwise (because that is how
+ both Metafont and Adobe Type 1 format want their curves to be drawn).
+
+ The very first outline (an outside one) on each character starts on a
+ top edge (STARTING_EDGE in edge.h defines this); so, if we're at a
+ top edge, we want to go only to the left (on the pixel to the west)
+ or down (on the same pixel), to begin with. Then, when we're on a
+ left edge, we want to go to the top edge (on the southwest pixel) or
+ to the left edge (on the south pixel).
+
+ All well and good. But if you draw a rasterized circle (or whatever),
+ eventually we have to come back around to the beginning; at that
+ point, we'll be on a top edge, and we'll have to go to the right edge
+ on the northwest pixel. Draw pictures.
+
+ The upshot is, if we find an edge on another pixel, we return (in ROW
+ and COL) the position of the new pixel, and (in EDGE) the kind of
+ edge it is. If we don't find such an edge, we return (in EDGE) the
+ next (in a counterclockwise direction) edge on the current pixel. */
+
+void
+next_outline_edge (edge_type *edge,
+ unsigned *row, unsigned *col)
+{
+ unsigned original_row = *row;
+ unsigned original_col = *col;
+
+ switch (*edge)
+ {
+ case right:
+ TRY_PIXEL (north);
+ TRY_PIXEL (northeast);
+ break;
+
+ case top:
+ TRY_PIXEL (west);
+ TRY_PIXEL (northwest);
+ break;
+
+ case left:
+ TRY_PIXEL (south);
+ TRY_PIXEL (southwest);
+ break;
+
+ case bottom:
+ TRY_PIXEL (east);
+ TRY_PIXEL (southeast);
+ break;
+
+ default:
+ printf ("next_outline_edge: Bad edge value (%d)", *edge);
+
+ }
+
+ /* If we didn't find an adjacent edge on another pixel, return the
+ next edge on the current pixel. */
+ if (*row == original_row && *col == original_col)
+ *edge = next_edge (*edge);
+}
+
+/* We return the next edge on the pixel at position ROW and COL which is
+ an unmarked outline edge. By ``next'' we mean either the one sent in
+ in STARTING_EDGE, if it qualifies, or the next such returned by
+ `next_edge'. */
+
+edge_type
+next_unmarked_outline_edge (unsigned row, unsigned col,
+ edge_type starting_edge,
+ bitmap_type marked)
+{
+ edge_type edge = starting_edge;
+
+ assert (edge != no_edge);
+
+ while (is_marked_edge (edge, row, col, marked)
+ || !is_outline_edge (edge, row, col))
+ {
+ edge = next_edge (edge);
+ if (edge == starting_edge)
+ return no_edge;
+ }
+
+ return edge;
+}
+
+
+/* We check to see if the edge EDGE of the pixel at position ROW and COL
+ is an outline edge; i.e., that it is a black pixel which shares that
+ edge with a white pixel. The position ROW and COL should be inside
+ the bitmap CHARACTER. */
+
+static boolean
+is_outline_edge (edge_type edge,
+ unsigned row, unsigned col)
+{
+ /* If this pixel isn't black, it's not part of the outline. */
+ if (sel_pixel_is_white(row, col))
+ return false;
+
+ switch (edge)
+ {
+ case left:
+ return col == 0 || sel_pixel_is_white(row, col - 1);
+
+ case top:
+ return row == 0 || sel_pixel_is_white(row - 1, col);
+
+ case right:
+ return (col == sel_get_width() - 1)
+ || sel_pixel_is_white(row, col + 1);
+
+ case bottom:
+ return (row == sel_get_height() - 1)
+ || sel_pixel_is_white(row + 1, col);
+
+ case no_edge:
+ default:
+ printf ("is_outline_edge: Bad edge value(%d)", edge);
+ }
+
+ return 0; /* NOTREACHED */
+}
+
+/* If EDGE is not already marked, we mark it; otherwise, it's a fatal error.
+ The position ROW and COL should be inside the bitmap MARKED. EDGE can
+ be `no_edge'; we just return false. */
+
+void
+mark_edge (edge_type edge, unsigned row, unsigned col, bitmap_type *marked)
+{
+ /* printf("row = %d, col = %d \n",row,col); */
+ assert (!is_marked_edge (edge, row, col, *marked));
+
+ if (edge != no_edge)
+ BITMAP_PIXEL (*marked, row, col) |= 1 << edge;
+}
+
+
+/* Test if the edge EDGE at ROW/COL in MARKED is marked. */
+
+static boolean
+is_marked_edge (edge_type edge, unsigned row, unsigned col, bitmap_type marked)
+{
+ return
+ edge == no_edge ? false : BITMAP_PIXEL (marked, row, col) & (1 << edge);
+}
+
+
+/* Return the edge which is counterclockwise-adjacent to EDGE. This
+ code makes use of the ``numericness'' of C enumeration constants;
+ sorry about that. */
+
+#define NUM_EDGES no_edge
+
+static edge_type
+next_edge (edge_type edge)
+{
+ return edge == no_edge ? edge : (edge + 1) % NUM_EDGES;
+}
diff --git a/plug-ins/selection-to-path/edge.h b/plug-ins/selection-to-path/edge.h
new file mode 100644
index 0000000..1d9d347
--- /dev/null
+++ b/plug-ins/selection-to-path/edge.h
@@ -0,0 +1,59 @@
+/* edge.h: declarations for edge traversing.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef EDGE_H
+#define EDGE_H
+
+#include "bitmap.h"
+
+/* We consider each pixel to consist of four edges, and we travel along
+ edges, instead of through pixel centers. This is necessary for those
+ unfortunate times when a single pixel is on both an inside and an
+ outside outline.
+
+ The numbers chosen here are not arbitrary; the code that figures out
+ which edge to move to depends on particular values. See the
+ `TRY_PIXEL' macro in `edge.c'. To emphasize this, I've written in the
+ numbers we need for each edge value. */
+
+typedef enum
+{
+ top = 1, left = 2, bottom = 3, right = 0, no_edge = 4
+} edge_type;
+
+/* This choice is also not arbitrary: starting at the top edge makes the
+ code find outside outlines before inside ones, which is certainly
+ what we want. */
+#define START_EDGE top
+
+
+/* Return the next outline edge on B in EDGE, ROW, and COL. */
+extern void next_outline_edge (edge_type *edge,
+ unsigned *row, unsigned *col);
+
+/* Return the next edge after START on the pixel ROW/COL in B that is
+ unmarked, according to the MARKED array. */
+extern edge_type next_unmarked_outline_edge (unsigned row, unsigned col,
+ edge_type start,
+ bitmap_type marked);
+
+/* Mark the edge E at the pixel ROW/COL in MARKED. */
+extern void mark_edge (edge_type e, unsigned row, unsigned col,
+ bitmap_type *marked);
+
+#endif /* not EDGE_H */
diff --git a/plug-ins/selection-to-path/fit.c b/plug-ins/selection-to-path/fit.c
new file mode 100644
index 0000000..8ee57ae
--- /dev/null
+++ b/plug-ins/selection-to-path/fit.c
@@ -0,0 +1,1967 @@
+/* fit.c: turn a bitmap representation of a curve into a list of splines.
+ * Some of the ideas, but not the code, comes from the Phoenix thesis.
+ * See README for the reference.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <float.h>
+#include <math.h>
+#include <assert.h>
+
+#include <glib.h>
+
+#include "global.h"
+#include "spline.h"
+#include "vector.h"
+
+#include "curve.h"
+#include "fit.h"
+#include "pxl-outline.h"
+
+/* If two endpoints are closer than this, they are made to be equal.
+ (-align-threshold) */
+real align_threshold = 0.5;
+
+/* If the angle defined by a point and its predecessors and successors
+ is smaller than this, it's a corner, even if it's within
+ `corner_surround' pixels of a point with a smaller angle.
+ (-corner-always-threshold) */
+real corner_always_threshold = 60.0;
+
+/* Number of points to consider when determining if a point is a corner
+ or not. (-corner-surround) */
+unsigned corner_surround = 4;
+
+/* If a point, its predecessors, and its successors define an angle
+ smaller than this, it's a corner. Should be in range 0..180.
+ (-corner-threshold) */
+real corner_threshold = 100.0;
+
+/* Amount of error at which a fitted spline is unacceptable. If any
+ pixel is further away than this from the fitted curve, we try again.
+ (-error-threshold) */
+/* real error_threshold = .8; ALT */
+real error_threshold = .4;
+
+/* A second number of adjacent points to consider when filtering.
+ (-filter-alternative-surround) */
+unsigned filter_alternative_surround = 1;
+
+/* If the angles between the vectors produced by filter_surround and
+ filter_alternative_surround points differ by more than this, use
+ the one from filter_alternative_surround. (-filter-epsilon) */
+real filter_epsilon = 10.0;
+
+/* Number of times to smooth original data points. Increasing this
+ number dramatically---to 50 or so---can produce vastly better
+ results. But if any points that ``should'' be corners aren't found,
+ the curve goes to hell around that point. (-filter-iterations) */
+/* unsigned filter_iteration_count = 4; ALT */
+unsigned filter_iteration_count = 4;
+
+/* To produce the new point, use the old point plus this times the
+ neighbors. (-filter-percent) */
+real filter_percent = .33;
+
+/* Number of adjacent points to consider if `filter_surround' points
+ defines a straight line. (-filter-secondary-surround) */
+static unsigned filter_secondary_surround = 3;
+
+/* Number of adjacent points to consider when filtering.
+ (-filter-surround) */
+unsigned filter_surround = 2;
+
+/* Says whether or not to remove ``knee'' points after finding the outline.
+ (See the comments at `remove_knee_points'.) (-remove-knees). */
+boolean keep_knees = false;
+
+/* If a spline is closer to a straight line than this, it remains a
+ straight line, even if it would otherwise be changed back to a curve.
+ This is weighted by the square of the curve length, to make shorter
+ curves more likely to be reverted. (-line-reversion-threshold) */
+real line_reversion_threshold = .01;
+
+/* How many pixels (on the average) a spline can diverge from the line
+ determined by its endpoints before it is changed to a straight line.
+ (-line-threshold) */
+/* real line_threshold = 1.0; ALT */
+real line_threshold = 0.5;
+
+/* If reparameterization doesn't improve the fit by this much percent,
+ stop doing it. (-reparameterize-improve) */
+/* real reparameterize_improvement = .10; ALT */
+real reparameterize_improvement = .01;
+
+/* Amount of error at which it is pointless to reparameterize. This
+ happens, for example, when we are trying to fit the outline of the
+ outside of an `O' with a single spline. The initial fit is not good
+ enough for the Newton-Raphson iteration to improve it. It may be
+ that it would be better to detect the cases where we didn't find any
+ corners. (-reparameterize-threshold) */
+/* real reparameterize_threshold = 30.0; ALT */
+real reparameterize_threshold = 1.0;
+
+/* Percentage of the curve away from the worst point to look for a
+ better place to subdivide. (-subdivide-search) */
+real subdivide_search = .1;
+
+/* Number of points to consider when deciding whether a given point is a
+ better place to subdivide. (-subdivide-surround) */
+unsigned subdivide_surround = 4;
+
+/* How many pixels a point can diverge from a straight line and still be
+ considered a better place to subdivide. (-subdivide-threshold) */
+real subdivide_threshold = .03;
+
+/* Number of points to look at on either side of a point when computing
+ the approximation to the tangent at that point. (-tangent-surround) */
+unsigned tangent_surround = 3;
+
+
+/* We need to manipulate lists of array indices. */
+
+typedef struct index_list
+{
+ unsigned *data;
+ unsigned length;
+} index_list_type;
+
+/* The usual accessor macros. */
+#define GET_INDEX(i_l, n) ((i_l).data[n])
+#define INDEX_LIST_LENGTH(i_l) ((i_l).length)
+#define GET_LAST_INDEX(i_l) ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1])
+
+static void append_index (index_list_type *, unsigned);
+static void free_index_list (index_list_type *);
+static index_list_type new_index_list (void);
+static void remove_adjacent_corners (index_list_type *, unsigned);
+
+static void align (spline_list_type *);
+static void change_bad_lines (spline_list_type *);
+static void filter (curve_type);
+static real filter_angle (vector_type, vector_type);
+static void find_curve_vectors
+ (unsigned, curve_type, unsigned, vector_type *, vector_type *, unsigned *);
+static unsigned find_subdivision (curve_type, unsigned initial);
+static void find_vectors
+ (unsigned, pixel_outline_type, vector_type *, vector_type *);
+static index_list_type find_corners (pixel_outline_type);
+static real find_error (curve_type, spline_type, unsigned *);
+static vector_type find_half_tangent (curve_type, boolean start, unsigned *);
+static void find_tangent (curve_type, boolean, boolean);
+static spline_type fit_one_spline (curve_type);
+static spline_list_type *fit_curve (curve_type);
+static spline_list_type fit_curve_list (curve_list_type);
+static spline_list_type *fit_with_least_squares (curve_type);
+static spline_list_type *fit_with_line (curve_type);
+static void remove_knee_points (curve_type, boolean);
+static boolean reparameterize (curve_type, spline_type);
+static void set_initial_parameter_values (curve_type);
+static boolean spline_linear_enough (spline_type *, curve_type);
+static curve_list_array_type split_at_corners (pixel_outline_list_type);
+static boolean test_subdivision_point (curve_type, unsigned, vector_type *);
+
+/* The top-level call that transforms the list of pixels in the outlines
+ of the original character to a list of spline lists fitted to those
+ pixels. */
+
+spline_list_array_type
+fitted_splines (pixel_outline_list_type pixel_outline_list)
+{
+ unsigned this_list;
+ unsigned total = 0;
+ spline_list_array_type char_splines = new_spline_list_array ();
+ curve_list_array_type curve_array = split_at_corners (pixel_outline_list);
+
+ for (this_list = 0; this_list < CURVE_LIST_ARRAY_LENGTH (curve_array);
+ this_list++)
+ {
+ spline_list_type curve_list_splines;
+ curve_list_type curves = CURVE_LIST_ARRAY_ELT (curve_array, this_list);
+
+ curve_list_splines = fit_curve_list (curves);
+ append_spline_list (&char_splines, curve_list_splines);
+
+/* REPORT ("* "); */
+ }
+
+ free_curve_list_array (&curve_array);
+
+ for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH (char_splines);
+ this_list++)
+ total
+ += SPLINE_LIST_LENGTH (SPLINE_LIST_ARRAY_ELT (char_splines, this_list));
+
+/* REPORT1 ("=%u", total); */
+
+ return char_splines;
+}
+
+/* Set up the internal parameters from the external ones */
+
+void
+fit_set_params(SELVALS *selVals)
+{
+ align_threshold = selVals->align_threshold;
+ corner_always_threshold = selVals->corner_always_threshold;
+ corner_surround = selVals->corner_surround;
+ corner_threshold = selVals->corner_threshold;
+ error_threshold = selVals->error_threshold;
+ filter_alternative_surround = selVals->filter_alternative_surround;
+ filter_epsilon = selVals->filter_epsilon;
+ filter_iteration_count = selVals->filter_iteration_count;
+ filter_percent = selVals->filter_percent;
+ filter_secondary_surround = selVals->filter_secondary_surround;
+ filter_surround = selVals->filter_surround;
+ keep_knees = selVals->keep_knees;
+ line_reversion_threshold = selVals->line_reversion_threshold;
+ line_threshold = selVals->line_threshold;
+ reparameterize_improvement = selVals->reparameterize_improvement;
+ reparameterize_threshold = selVals->reparameterize_threshold;
+ subdivide_search = selVals->subdivide_search;
+ subdivide_surround = selVals->subdivide_surround;
+ subdivide_threshold = selVals->subdivide_threshold;
+ tangent_surround = selVals->tangent_surround;
+}
+
+void
+fit_set_default_params(SELVALS *selVals)
+{
+ selVals->align_threshold = align_threshold;
+ selVals->corner_always_threshold = corner_always_threshold;
+ selVals->corner_surround = corner_surround;
+ selVals->corner_threshold = corner_threshold;
+ selVals->error_threshold = error_threshold;
+ selVals->filter_alternative_surround = filter_alternative_surround;
+ selVals->filter_epsilon = filter_epsilon;
+ selVals->filter_iteration_count = filter_iteration_count;
+ selVals->filter_percent = filter_percent;
+ selVals->filter_secondary_surround = filter_secondary_surround;
+ selVals->filter_surround = filter_surround;
+ selVals->keep_knees = keep_knees;
+ selVals->line_reversion_threshold = line_reversion_threshold;
+ selVals->line_threshold = line_threshold;
+ selVals->reparameterize_improvement = reparameterize_improvement;
+ selVals->reparameterize_threshold = reparameterize_threshold;
+ selVals->subdivide_search = subdivide_search;
+ selVals->subdivide_surround = subdivide_surround;
+ selVals->subdivide_threshold = subdivide_threshold;
+ selVals->tangent_surround = tangent_surround;
+}
+
+/* Fit the list of curves CURVE_LIST to a list of splines, and return
+ it. CURVE_LIST represents a single closed paths, e.g., either the
+ inside or outside outline of an `o'. */
+
+static spline_list_type
+fit_curve_list (curve_list_type curve_list)
+{
+ curve_type curve;
+ unsigned this_curve, this_spline;
+ unsigned curve_list_length = CURVE_LIST_LENGTH (curve_list);
+ spline_list_type curve_list_splines = *new_spline_list ();
+
+ /* Remove the extraneous ``knee'' points before filtering. Since the
+ corners have already been found, we don't need to worry about
+ removing a point that should be a corner. */
+ if (!keep_knees)
+ {
+/* LOG ("\nRemoving knees:\n"); */
+ for (this_curve = 0; this_curve < curve_list_length; this_curve++)
+ {
+/* LOG1 ("#%u:", this_curve); */
+ remove_knee_points (CURVE_LIST_ELT (curve_list, this_curve),
+ CURVE_LIST_CLOCKWISE (curve_list));
+ }
+ }
+
+ /* We filter all the curves in CURVE_LIST at once; otherwise, we would
+ look at an unfiltered curve when computing tangents. */
+/* LOG ("\nFiltering curves:\n"); */
+ for (this_curve = 0; this_curve < curve_list.length; this_curve++)
+ {
+/* LOG1 ("#%u: ", this_curve); */
+ filter (CURVE_LIST_ELT (curve_list, this_curve));
+/* REPORT ("f"); */
+ }
+
+ /* Make the first point in the first curve also be the last point in
+ the last curve, so the fit to the whole curve list will begin and
+ end at the same point. This may cause slight errors in computing
+ the tangents and t values, but it's worth it for the continuity.
+ Of course we don't want to do this if the two points are already
+ the same, as they are if the curve is cyclic. (We don't append it
+ earlier, in `split_at_corners', because that confuses the
+ filtering.) Finally, we can't append the point if the curve is
+ exactly three points long, because we aren't adding any more data,
+ and three points isn't enough to determine a spline. Therefore,
+ the fitting will fail. */
+ curve = CURVE_LIST_ELT (curve_list, 0);
+ if (CURVE_CYCLIC (curve) && CURVE_LENGTH (curve) != 3)
+ append_point (curve, CURVE_POINT (curve, 0));
+
+ /* Finally, fit each curve in the list to a list of splines. */
+ for (this_curve = 0; this_curve < curve_list_length; this_curve++)
+ {
+ spline_list_type *curve_splines;
+ curve_type current_curve = CURVE_LIST_ELT (curve_list, this_curve);
+
+/* REPORT1 (" %u", this_curve); */
+/* LOG1 ("\nFitting curve #%u:\n", this_curve); */
+
+ curve_splines = fit_curve (current_curve);
+ if (curve_splines == NULL)
+ printf("Could not fit curve #%u", this_curve);
+ else
+ {
+/* LOG1 ("Fitted splines for curve #%u:\n", this_curve); */
+ for (this_spline = 0;
+ this_spline < SPLINE_LIST_LENGTH (*curve_splines);
+ this_spline++)
+ {
+/* LOG1 (" %u: ", this_spline); */
+/* if (logging) */
+/* print_spline (log_
+file, */
+/* SPLINE_LIST_ELT (*curve_splines, this_spline)); */
+ }
+
+ /* After fitting, we may need to change some would-be lines
+ back to curves, because they are in a list with other
+ curves. */
+ change_bad_lines (curve_splines);
+
+ concat_spline_lists (&curve_list_splines, *curve_splines);
+/* REPORT1 ("(%u)", SPLINE_LIST_LENGTH (*curve_splines)); */
+ }
+ }
+
+
+ /* We do this for each outline's spline list because when a point
+ is changed, it needs to be changed in both segments in which it
+ appears---and the segments might be in different curves. */
+ align (&curve_list_splines);
+
+ return curve_list_splines;
+}
+
+
+/* Transform a set of locations to a list of splines (the fewer the
+ better). We are guaranteed that CURVE does not contain any corners.
+ We return NULL if we cannot fit the points at all. */
+
+static spline_list_type *
+fit_curve (curve_type curve)
+{
+ spline_list_type *fitted_splines;
+
+ if (CURVE_LENGTH (curve) < 2)
+ {
+ printf ("Tried to fit curve with less than two points");
+ return NULL;
+ }
+
+ /* Do we have enough points to fit with a spline? */
+ fitted_splines = CURVE_LENGTH (curve) < 4
+ ? fit_with_line (curve)
+ : fit_with_least_squares (curve);
+
+ return fitted_splines;
+}
+
+/* As mentioned above, the first step is to find the corners in
+ PIXEL_LIST, the list of points. (Presumably we can't fit a single
+ spline around a corner.) The general strategy is to look through all
+ the points, remembering which we want to consider corners. Then go
+ through that list, producing the curve_list. This is dictated by the
+ fact that PIXEL_LIST does not necessarily start on a corner---it just
+ starts at the character's first outline pixel, going left-to-right,
+ top-to-bottom. But we want all our splines to start and end on real
+ corners.
+
+ For example, consider the top of a capital `C' (this is in cmss20):
+ x
+ ***********
+ ******************
+
+ PIXEL_LIST will start at the pixel below the `x'. If we considered
+ this pixel a corner, we would wind up matching a very small segment
+ from there to the end of the line, probably as a straight line, which
+ is certainly not what we want.
+
+ PIXEL_LIST has one element for each closed outline on the character.
+ To preserve this information, we return an array of curve_lists, one
+ element (which in turn consists of several curves, one between each
+ pair of corners) for each element in PIXEL_LIST. */
+
+static curve_list_array_type
+split_at_corners (pixel_outline_list_type pixel_list)
+{
+ unsigned this_pixel_o;
+ curve_list_array_type curve_array = new_curve_list_array ();
+
+/* LOG ("\nFinding corners:\n"); */
+
+ for (this_pixel_o = 0; this_pixel_o < O_LIST_LENGTH (pixel_list);
+ this_pixel_o++)
+ {
+ curve_type curve, first_curve;
+ index_list_type corner_list;
+ unsigned p, this_corner;
+ curve_list_type curve_list = new_curve_list ();
+ pixel_outline_type pixel_o = O_LIST_OUTLINE (pixel_list, this_pixel_o);
+
+ CURVE_LIST_CLOCKWISE (curve_list) = O_CLOCKWISE (pixel_o);
+
+/* LOG1 ("#%u:", this_pixel_o); */
+
+ /* If the outline does not have enough points, we can't do
+ anything. The endpoints of the outlines are automatically
+ corners. We need at least `corner_surround' more pixels on
+ either side of a point before it is conceivable that we might
+ want another corner. */
+ if (O_LENGTH (pixel_o) > corner_surround * 2 + 2)
+ {
+ corner_list = find_corners (pixel_o);
+ }
+ else
+ {
+ corner_list.data = NULL;
+ corner_list.length = 0;
+ }
+
+ /* Remember the first curve so we can make it be the `next' of the
+ last one. (And vice versa.) */
+ first_curve = new_curve ();
+
+ curve = first_curve;
+
+ if (corner_list.length == 0)
+ { /* No corners. Use all of the pixel outline as the curve. */
+ for (p = 0; p < O_LENGTH (pixel_o); p++)
+ append_pixel (curve, O_COORDINATE (pixel_o, p));
+
+ /* This curve is cyclic. */
+ CURVE_CYCLIC (curve) = true;
+ }
+ else
+ { /* Each curve consists of the points between (inclusive) each pair
+ of corners. */
+ for (this_corner = 0; this_corner < corner_list.length - 1;
+ this_corner++)
+ {
+ curve_type previous_curve = curve;
+ unsigned corner = GET_INDEX (corner_list, this_corner);
+ unsigned next_corner = GET_INDEX (corner_list, this_corner + 1);
+
+ for (p = corner; p <= next_corner; p++)
+ append_pixel (curve, O_COORDINATE (pixel_o, p));
+
+ append_curve (&curve_list, curve);
+ curve = new_curve ();
+ NEXT_CURVE (previous_curve) = curve;
+ PREVIOUS_CURVE (curve) = previous_curve;
+ }
+
+ /* The last curve is different. It consists of the points
+ (inclusive) between the last corner and the end of the list,
+ and the beginning of the list and the first corner. */
+ for (p = GET_LAST_INDEX (corner_list); p < O_LENGTH (pixel_o);
+ p++)
+ append_pixel (curve, O_COORDINATE (pixel_o, p));
+
+ for (p = 0; p <= GET_INDEX (corner_list, 0); p++)
+ append_pixel (curve, O_COORDINATE (pixel_o, p));
+ }
+
+/* LOG1 (" [%u].\n", corner_list.length); */
+
+ /* Add `curve' to the end of the list, updating the pointers in
+ the chain. */
+ append_curve (&curve_list, curve);
+ NEXT_CURVE (curve) = first_curve;
+ PREVIOUS_CURVE (first_curve) = curve;
+
+ /* And now add the just-completed curve list to the array. */
+ append_curve_list (&curve_array, curve_list);
+ } /* End of considering each pixel outline. */
+
+ return curve_array;
+}
+
+
+/* We consider a point to be a corner if (1) the angle defined by the
+ `corner_surround' points coming into it and going out from it is less
+ than `corner_threshold' degrees, and no point within
+ `corner_surround' points has a smaller angle; or (2) the angle is less
+ than `corner_always_threshold' degrees.
+
+ Because of the different cases, it is convenient to have the
+ following macro to append a corner on to the list we return. The
+ character argument C is simply so that the different cases can be
+ distinguished in the log file. */
+
+#define APPEND_CORNER(index, angle, c) \
+ do \
+ { \
+ append_index (&corner_list, index); \
+ /*LOG4 (" (%d,%d)%c%.3f", */ \
+ /* O_COORDINATE (pixel_outline, index).x,*/ \
+ /* O_COORDINATE (pixel_outline, index).y,*/ \
+ /* c, angle);*/ \
+ } \
+ while (0)
+
+static index_list_type
+find_corners (pixel_outline_type pixel_outline)
+{
+ unsigned p;
+ index_list_type corner_list = new_index_list ();
+
+ /* Consider each pixel on the outline in turn. */
+ for (p = 0; p < O_LENGTH (pixel_outline); p++)
+ {
+ real corner_angle;
+ vector_type in_vector, out_vector;
+
+ /* Check if the angle is small enough. */
+ find_vectors (p, pixel_outline, &in_vector, &out_vector);
+ corner_angle = Vangle (in_vector, out_vector);
+
+ if (fabs (corner_angle) <= corner_threshold)
+ {
+ /* We want to keep looking, instead of just appending the
+ first pixel we find with a small enough angle, since there
+ might be another corner within `corner_surround' pixels, with
+ a smaller angle. If that is the case, we want that one. */
+ real best_corner_angle = corner_angle;
+ unsigned best_corner_index = p;
+ index_list_type equally_good_list = new_index_list ();
+ /* As we come into the loop, `p' is the index of the point
+ that has an angle less than `corner_angle'. We use `i' to
+ move through the pixels next to that, and `q' for moving
+ through the adjacent pixels to each `p'. */
+ unsigned q = p;
+ unsigned i = p + 1;
+
+ while (true)
+ {
+ /* Perhaps the angle is sufficiently small that we want to
+ consider this a corner, even if it's not the best
+ (unless we've already wrapped around in the search,
+ i.e., `q<i', in which case we have already added the
+ corner, and we don't want to add it again). We want to
+ do this check on the first candidate we find, as well
+ as the others in the loop, hence this comes before the
+ stopping condition. */
+ if (corner_angle <= corner_always_threshold && q >= p)
+ APPEND_CORNER (q, corner_angle, '\\');
+
+ /* Exit the loop if we've looked at `corner_surround'
+ pixels past the best one we found, or if we've looked
+ at all the pixels. */
+ if (i >= best_corner_index + corner_surround
+ || i >= O_LENGTH (pixel_outline))
+ break;
+
+ /* Check the angle. */
+ q = i % O_LENGTH (pixel_outline);
+ find_vectors (q, pixel_outline, &in_vector, &out_vector);
+ corner_angle = Vangle (in_vector, out_vector);
+
+ /* If we come across a corner that is just as good as the
+ best one, we should make it a corner, too. This
+ happens, for example, at the points on the `W' in some
+ typefaces, where the ``points'' are flat. */
+ if (epsilon_equal (corner_angle, best_corner_angle))
+ append_index (&equally_good_list, q);
+
+ else if (corner_angle < best_corner_angle)
+ {
+ best_corner_angle = corner_angle;
+ /* We want to check `corner_surround' pixels beyond the
+ new best corner. */
+ i = best_corner_index = q;
+ free_index_list (&equally_good_list);
+ equally_good_list = new_index_list ();
+ }
+
+ i++;
+ }
+
+ /* After we exit the loop, `q' is the index of the last point
+ we checked. We have already added the corner if
+ `best_corner_angle' is less than `corner_always_threshold'.
+ Again, if we've already wrapped around, we don't want to
+ add the corner again. */
+ if (best_corner_angle > corner_always_threshold
+ && best_corner_index >= p)
+ {
+ unsigned i;
+
+ APPEND_CORNER (best_corner_index, best_corner_angle, '/');
+
+ for (i = 0; i < INDEX_LIST_LENGTH (equally_good_list); i++)
+ APPEND_CORNER (GET_INDEX (equally_good_list, i),
+ best_corner_angle, '@');
+ free_index_list (&equally_good_list);
+ }
+
+ /* If we wrapped around in our search, we're done; otherwise,
+ we don't want the outer loop to look at the pixels that we
+ already looked at in searching for the best corner. */
+ p = (q < p) ? O_LENGTH (pixel_outline) : q;
+ } /* End of searching for the best corner. */
+ } /* End of considering each pixel. */
+
+ if (INDEX_LIST_LENGTH (corner_list) > 0)
+ /* We never want two corners next to each other, since the
+ only way to fit such a ``curve'' would be with a straight
+ line, which usually interrupts the continuity dreadfully. */
+ remove_adjacent_corners (&corner_list, O_LENGTH (pixel_outline) - 1);
+
+ return corner_list;
+}
+
+
+/* Return the difference vectors coming in and going out of the outline
+ OUTLINE at the point whose index is TEST_INDEX. In Phoenix,
+ Schneider looks at a single point on either side of the point we're
+ considering. That works for him because his points are not touching.
+ But our points *are* touching, and so we have to look at
+ `corner_surround' points on either side, to get a better picture of
+ the outline's shape. */
+
+static void
+find_vectors (unsigned test_index, pixel_outline_type outline,
+ vector_type *in, vector_type *out)
+{
+ int i;
+ unsigned n_done;
+ coordinate_type candidate = O_COORDINATE (outline, test_index);
+
+ in->dx = 0.0;
+ in->dy = 0.0;
+ out->dx = 0.0;
+ out->dy = 0.0;
+
+ /* Add up the differences from p of the `corner_surround' points
+ before p. */
+ for (i = O_PREV (outline, test_index), n_done = 0; n_done < corner_surround;
+ i = O_PREV (outline, i), n_done++)
+ *in = Vadd (*in, IPsubtract (O_COORDINATE (outline, i), candidate));
+
+#if 0
+ /* We don't need this code any more, because now we create the pixel
+ outline from the corners of the pixels, rather than the edges. */
+
+ /* To see why we need this test, consider the following
+ case: four pixels stacked vertically with no other adjacent pixels,
+ i.e., *
+ *x
+ *
+ *
+ *** (etc.) We are considering the pixel marked `x' for cornerhood.
+ The out vector at this point is going to be the zero vector (if
+ `corner_surround' is 3), because the first
+ pixel on the outline is the one above the x, the second pixel x
+ itself, and the third the one below x. (Remember that we go
+ around the edges of the pixels to find the outlines, not the
+ pixels themselves.) */
+ if (magnitude (*in) == 0.0)
+ {
+ WARNING ("Zero magnitude in");
+ return corner_threshold + 1.0;
+ }
+#endif /* 0 */
+
+ /* And the points after p. */
+ for (i = O_NEXT (outline, test_index), n_done = 0; n_done < corner_surround;
+ i = O_NEXT (outline, i), n_done++)
+ *out = Vadd (*out, IPsubtract (O_COORDINATE (outline, i), candidate));
+
+#if 0
+ /* As with the test for the in vector, we don't need this any more. */
+ if (magnitude (*out) == 0.0)
+ {
+ WARNING ("Zero magnitude out");
+ return corner_threshold + 1.0;
+ }
+#endif /* 0 */
+}
+
+
+/* Remove adjacent points from the index list LIST. We do this by first
+ sorting the list and then running through it. Since these lists are
+ quite short, a straight selection sort (e.g., p.139 of the Art of
+ Computer Programming, vol.3) is good enough. LAST_INDEX is the index
+ of the last pixel on the outline, i.e., the next one is the first
+ pixel. We need this for checking the adjacency of the last corner.
+
+ We need to do this because the adjacent corners turn into
+ two-pixel-long curves, which can only be fit by straight lines. */
+
+static void
+remove_adjacent_corners (index_list_type *list, unsigned last_index)
+{
+ unsigned j;
+ unsigned last;
+ index_list_type new = new_index_list ();
+
+ for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
+ {
+ unsigned search;
+ unsigned temp;
+ /* Find maximal element below `j'. */
+ unsigned max_index = j;
+
+ for (search = 0; search < j; search++)
+ if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
+ max_index = search;
+
+ if (max_index != j)
+ {
+ temp = GET_INDEX (*list, j);
+ GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
+ GET_INDEX (*list, max_index) = temp;
+ printf ("needed exchange"); /* xx -- really have to sort? */
+ }
+ }
+
+ /* The list is sorted. Now look for adjacent entries. Each time
+ through the loop we insert the current entry and, if appropriate,
+ the next entry. */
+ for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
+ {
+ unsigned current = GET_INDEX (*list, j);
+ unsigned next = GET_INDEX (*list, j + 1);
+
+ /* We should never have inserted the same element twice. */
+ assert (current != next);
+
+ append_index (&new, current);
+ if (next == current + 1)
+ j++;
+ }
+
+ /* Don't append the last element if it is 1) adjacent to the previous
+ one; or 2) adjacent to the very first one. */
+ last = GET_LAST_INDEX (*list);
+ if (INDEX_LIST_LENGTH (new) == 0
+ || !(last == GET_LAST_INDEX (new) + 1
+ || (last == last_index && GET_INDEX (*list, 0) == 0)))
+ append_index (&new, last);
+
+ free_index_list (list);
+ *list = new;
+}
+
+/* A ``knee'' is a point which forms a ``right angle'' with its
+ predecessor and successor. See the documentation (the `Removing
+ knees' section) for an example and more details.
+
+ The argument CLOCKWISE tells us which direction we're moving. (We
+ can't figure that information out from just the single segment with
+ which we are given to work.)
+
+ We should never find two consecutive knees.
+
+ Since the first and last points are corners (unless the curve is
+ cyclic), it doesn't make sense to remove those. */
+
+/* This evaluates to true if the vector V is zero in one direction and
+ nonzero in the other. */
+#define ONLY_ONE_ZERO(v) \
+ (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
+
+
+/* There are four possible cases for knees, one for each of the four
+ corners of a rectangle; and then the cases differ depending on which
+ direction we are going around the curve. The tests are listed here
+ in the order of upper left, upper right, lower right, lower left.
+ Perhaps there is some simple pattern to the
+ clockwise/counterclockwise differences, but I don't see one. */
+#define CLOCKWISE_KNEE(prev_delta, next_delta) \
+ ((prev_delta.dx == -1.0 && next_delta.dy == 1.0) \
+ || (prev_delta.dy == 1.0 && next_delta.dx == 1.0) \
+ || (prev_delta.dx == 1.0 && next_delta.dy == -1.0) \
+ || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
+
+#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta) \
+ ((prev_delta.dy == 1.0 && next_delta.dx == -1.0) \
+ || (prev_delta.dx == 1.0 && next_delta.dy == 1.0) \
+ || (prev_delta.dy == -1.0 && next_delta.dx == 1.0) \
+ || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
+
+static void
+remove_knee_points (curve_type curve, boolean clockwise)
+{
+ int i;
+ unsigned offset = CURVE_CYCLIC (curve) ? 0 : 1;
+ coordinate_type previous
+ = real_to_int_coord (CURVE_POINT (curve, CURVE_PREV (curve, offset)));
+ curve_type trimmed_curve = copy_most_of_curve (curve);
+
+ if (!CURVE_CYCLIC (curve))
+ append_pixel (trimmed_curve, real_to_int_coord (CURVE_POINT (curve, 0)));
+
+ for (i = offset; i < CURVE_LENGTH (curve) - offset; i++)
+ {
+ coordinate_type current
+ = real_to_int_coord (CURVE_POINT (curve, i));
+ coordinate_type next
+ = real_to_int_coord (CURVE_POINT (curve, CURVE_NEXT (curve, i)));
+ vector_type prev_delta = IPsubtract (previous, current);
+ vector_type next_delta = IPsubtract (next, current);
+
+ if (ONLY_ONE_ZERO (prev_delta) && ONLY_ONE_ZERO (next_delta)
+ && ((clockwise && CLOCKWISE_KNEE (prev_delta, next_delta))
+ || (!clockwise
+ && COUNTERCLOCKWISE_KNEE (prev_delta, next_delta))))
+ {
+ /* LOG2 (" (%d,%d)", current.x, current.y); */
+ }
+ else
+ {
+ previous = current;
+ append_pixel (trimmed_curve, current);
+ }
+ }
+
+ if (!CURVE_CYCLIC (curve))
+ append_pixel (trimmed_curve, real_to_int_coord (LAST_CURVE_POINT (curve)));
+
+/* if (CURVE_LENGTH (trimmed_curve) == CURVE_LENGTH (curve)) */
+/* LOG (" (none)"); */
+
+/* LOG (".\n"); */
+
+ free_curve (curve);
+ *curve = *trimmed_curve;
+}
+
+/* Smooth the curve by adding in neighboring points. Do this
+ `filter_iteration_count' times. But don't change the corners. */
+
+#if 0
+/* Computing the new point based on a single neighboring point and with
+ constant percentages, as the `SMOOTH' macro did, isn't quite good
+ enough. For example, at the top of an `o' the curve might well have
+ three consecutive horizontal pixels, even though there isn't really a
+ straight there. With this code, the middle point would remain
+ unfiltered. */
+
+#define SMOOTH(axis) \
+ CURVE_POINT (curve, this_point).axis \
+ = ((1.0 - center_percent) / 2) \
+ * CURVE_POINT (curve, CURVE_PREV (curve, this_point)).axis \
+ + center_percent * CURVE_POINT (curve, this_point).axis \
+ + ((1.0 - center_percent) / 2) \
+ * CURVE_POINT (curve, CURVE_NEXT (curve, this_point)).axis
+#endif /* 0 */
+
+/* We sometimes need to change the information about the filtered point.
+ This macro assigns to the relevant variables. */
+#define FILTER_ASSIGN(new) \
+ do \
+ { \
+ in = in_##new; \
+ out = out_##new; \
+ count = new##_count; \
+ angle = angle_##new; \
+ } \
+ while (0)
+
+static void
+filter (curve_type curve)
+{
+ unsigned iteration, this_point;
+ unsigned offset = CURVE_CYCLIC (curve) ? 0 : 1;
+
+ /* We must have at least three points---the previous one, the current
+ one, and the next one. But if we don't have at least five, we will
+ probably collapse the curve down onto a single point, which means
+ we won't be able to fit it with a spline. */
+ if (CURVE_LENGTH (curve) < 5)
+ {
+/* LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve)); */
+ return;
+ }
+
+ for (iteration = 0; iteration < filter_iteration_count; iteration++)
+ {
+ curve_type new_curve = copy_most_of_curve (curve);
+
+ /* Keep the first point on the curve. */
+ if (offset)
+ append_point (new_curve, CURVE_POINT (curve, 0));
+
+ for (this_point = offset; this_point < CURVE_LENGTH (curve) - offset;
+ this_point++)
+ {
+ real angle, angle_alt;
+ vector_type in, in_alt, out, out_alt, sum;
+ unsigned count, alt_count;
+ real_coordinate_type new_point;
+
+ /* Find the angle using the usual number of surrounding points
+ on the curve. */
+ find_curve_vectors (this_point, curve, filter_surround,
+ &in, &out, &count);
+ angle = filter_angle (in, out);
+
+ /* Find the angle using the alternative (presumably smaller)
+ number. */
+ find_curve_vectors (this_point, curve, filter_alternative_surround,
+ &in_alt, &out_alt, &alt_count);
+ angle_alt = filter_angle (in_alt, out_alt);
+
+ /* If the alternate angle is enough larger than the usual one
+ and neither of the components of the sum are zero, use it.
+ (We don't use absolute value here, since if the alternate
+ angle is smaller, we don't particularly care, since that
+ means the curve is pretty flat around the current point,
+ anyway.) This helps keep small features from disappearing
+ into the surrounding curve. */
+ sum = Vadd (in_alt, out_alt);
+ if (angle_alt - angle >= filter_epsilon
+ && sum.dx != 0 && sum.dy != 0)
+ FILTER_ASSIGN (alt);
+
+#if 0
+/* This code isn't needed anymore, since we do the filtering in a
+ somewhat more general way. */
+ /* If we're left with an angle of zero, don't stop yet; we
+ might be at a straight which really isn't one (as in the `o'
+ discussed above). */
+ if (epsilon_equal (angle, 0.0))
+ {
+ real angle_secondary;
+ vector_type in_secondary, out_secondary;
+ unsigned in_secondary_count, out_secondary_count;
+
+ find_curve_vectors (this_point, curve, filter_secondary_surround,
+ &in_secondary, &out_secondary,
+ &in_secondary_count, &out_secondary_count);
+ angle_secondary = filter_angle (in_secondary, out_secondary);
+ if (!epsilon_equal (angle_secondary, 0.0))
+ FILTER_ASSIGN (secondary);
+ }
+#endif /* 0 */
+
+ /* Start with the old point. */
+ new_point = CURVE_POINT (curve, this_point);
+ sum = Vadd (in, out);
+ new_point.x += sum.dx * filter_percent / count;
+ new_point.y += sum.dy * filter_percent / count;
+
+ /* Put the newly computed point into a separate curve, so it
+ doesn't affect future computation (on this iteration). */
+ append_point (new_curve, new_point);
+ }
+
+ /* Just as with the first point, we have to keep the last point. */
+ if (offset)
+ append_point (new_curve, LAST_CURVE_POINT (curve));
+
+ /* Set the original curve to the newly filtered one, and go again. */
+ free_curve (curve);
+ *curve = *new_curve;
+ }
+
+/* log_curve (curve, false); */
+/* display_curve (curve); */
+}
+
+
+/* Return the vectors IN and OUT, computed by looking at SURROUND points
+ on either side of TEST_INDEX. Also return the number of points in
+ the vectors in COUNT (we make sure they are the same). */
+
+static void
+find_curve_vectors (unsigned test_index, curve_type curve,
+ unsigned surround,
+ vector_type *in, vector_type *out, unsigned *count)
+{
+ int i;
+ unsigned in_count, out_count;
+ unsigned n_done;
+ real_coordinate_type candidate = CURVE_POINT (curve, test_index);
+
+ /* Add up the differences from p of the `surround' points
+ before p. */
+
+ in->dx = 0.0;
+ in->dy = 0.0;
+
+ for (i = CURVE_PREV (curve, test_index), n_done = 0;
+ i >= 0 && n_done < surround; /* Do not wrap around. */
+ i = CURVE_PREV (curve, i), n_done++)
+ *in = Vadd (*in, Psubtract (CURVE_POINT (curve, i), candidate));
+ in_count = n_done;
+
+ /* And the points after p. Don't use more points after p than we
+ ended up with before it. */
+ out->dx = 0.0;
+ out->dy = 0.0;
+
+ for (i = CURVE_NEXT (curve, test_index), n_done = 0;
+ i < CURVE_LENGTH (curve) && n_done < surround && n_done < in_count;
+ i = CURVE_NEXT (curve, i), n_done++)
+ *out = Vadd (*out, Psubtract (CURVE_POINT (curve, i), candidate));
+ out_count = n_done;
+
+ /* If we used more points before p than after p, we have to go back
+ and redo it. (We could just subtract the ones that were missing,
+ but for this few of points, efficiency doesn't matter.) */
+ if (out_count < in_count)
+ {
+ in->dx = 0.0;
+ in->dy = 0.0;
+
+ for (i = CURVE_PREV (curve, test_index), n_done = 0;
+ i >= 0 && n_done < out_count;
+ i = CURVE_PREV (curve, i), n_done++)
+ *in = Vadd (*in, Psubtract (CURVE_POINT (curve, i), candidate));
+ in_count = n_done;
+ }
+
+ assert (in_count == out_count);
+ *count = in_count;
+}
+
+
+/* Find the angle between the vectors IN and OUT, and bring it into the
+ range [0,45]. */
+
+static real
+filter_angle (vector_type in, vector_type out)
+{
+ real angle = Vangle (in, out);
+
+ /* What we want to do between 90 and 180 is the same as what we
+ want to do between 0 and 90. */
+ angle = fmod (angle, 1990.0);
+
+ /* And what we want to do between 45 and 90 is the same as
+ between 0 and 45, only reversed. */
+ if (angle > 45.0) angle = 90.0 - angle;
+
+ return angle;
+}
+
+/* This routine returns the curve fitted to a straight line in a very
+ simple way: make the first and last points on the curve be the
+ endpoints of the line. This simplicity is justified because we are
+ called only on very short curves. */
+
+static spline_list_type *
+fit_with_line (curve_type curve)
+{
+ spline_type line = new_spline ();
+
+/* LOG ("Fitting with straight line:\n"); */
+/* REPORT ("l"); */
+
+ SPLINE_DEGREE (line) = LINEAR;
+ START_POINT (line) = CONTROL1 (line) = CURVE_POINT (curve, 0);
+ END_POINT (line) = CONTROL2 (line) = LAST_CURVE_POINT (curve);
+
+ /* Make sure that this line is never changed to a cubic. */
+ SPLINE_LINEARITY (line) = 0;
+
+/* if (logging) */
+/* { */
+/* LOG (" "); */
+/* print_spline (log_file, line); */
+/* } */
+
+ return init_spline_list (line);
+}
+
+/* The least squares method is well described in Schneider's thesis.
+ Briefly, we try to fit the entire curve with one spline. If that fails,
+ we try reparameterizing, i.e., finding new, and supposedly better,
+ t values. If that still fails, we subdivide the curve. */
+
+static spline_list_type *
+fit_with_least_squares (curve_type curve)
+{
+ real error, best_error = FLT_MAX;
+ spline_type spline, best_spline;
+ spline_list_type *spline_list;
+ unsigned worst_point;
+ unsigned iteration = 0;
+ real previous_error = FLT_MAX;
+ real improvement = FLT_MAX;
+
+ /* FIXME: Initialize best_spline to zeroes. This is strictly not
+ necessary as best_spline is always set in the loop below. But the
+ compiler thinks it isn't and warns. Ideally, the code should be
+ rewritten such that best_spline and best_error are initialized with
+ the first values before the loop begins. */
+ memset (&best_spline, 0, sizeof best_spline);
+
+/* LOG ("\nFitting with least squares:\n"); */
+
+ /* Phoenix reduces the number of points with a ``linear spline
+ technique''. But for fitting letterforms, that is
+ inappropriate. We want all the points we can get. */
+
+ /* It makes no difference whether we first set the `t' values or
+ find the tangents. This order makes the documentation a little
+ more coherent. */
+
+/* LOG ("Finding tangents:\n"); */
+ find_tangent (curve, /* to_start */ true, /* cross_curve */ false);
+ find_tangent (curve, /* to_start */ false, /* cross_curve */ false);
+
+ set_initial_parameter_values (curve);
+
+ /* Now we loop, reparameterizing and/or subdividing, until CURVE has
+ been fit. */
+ while (true)
+ {
+/* LOG (" fitted to spline:\n"); */
+
+ spline = fit_one_spline (curve);
+
+/* if (logging) */
+/* { */
+/* LOG (" "); */
+/* print_spline (log_file, spline); */
+/* } */
+
+ error = find_error (curve, spline, &worst_point);
+ if (error > previous_error)
+ {
+/* LOG ("Reparameterization made it worse.\n"); */
+ /* Just fall through; we will subdivide. */
+ }
+ else
+ {
+ best_error = error;
+ best_spline = spline;
+ }
+
+ improvement = 1.0 - error / previous_error;
+
+ /* Don't exit, even if the error is less than `error_threshold',
+ since we might be able to improve the fit with further
+ reparameterization. If the reparameterization made it worse,
+ we will exit here, since `improvement' will be negative. */
+ if (improvement < reparameterize_improvement
+ || error > reparameterize_threshold)
+ break;
+
+ iteration++;
+/* LOG3 ("Reparameterization #%u (error was %.3f, a %u%% improvement):\n", */
+/* iteration, error, ((unsigned) (improvement * 100.0))); */
+
+ /* The reparameterization might fail, if the initial fit was
+ better than `reparameterize_threshold', yet worse than the
+ Newton-Raphson algorithm could handle. */
+ if (!reparameterize (curve, spline))
+ break;
+
+ previous_error = error;
+ }
+
+ /* Go back to the best fit. */
+ spline = best_spline;
+ error = best_error;
+
+ if (error < error_threshold)
+ {
+ /* The points were fitted with a (possibly reparameterized)
+ spline. We end up here whenever a fit is accepted. We have
+ one more job: see if the ``curve'' that was fit should really
+ be a straight line. */
+ if (spline_linear_enough (&spline, curve))
+ {
+ SPLINE_DEGREE (spline) = LINEAR;
+/* LOG ("Changed to line.\n"); */
+ }
+ spline_list = init_spline_list (spline);
+/* LOG1 ("Accepted error of %.3f.\n", error); */
+ }
+ else
+ {
+ /* We couldn't fit the curve acceptably, so subdivide. */
+ unsigned subdivision_index;
+ spline_list_type *left_spline_list;
+ spline_list_type *right_spline_list;
+ curve_type left_curve = new_curve ();
+ curve_type right_curve = new_curve ();
+
+ /* Keep the linked list of curves intact. */
+ NEXT_CURVE (right_curve) = NEXT_CURVE (curve);
+ PREVIOUS_CURVE (right_curve) = left_curve;
+ NEXT_CURVE (left_curve) = right_curve;
+ PREVIOUS_CURVE (left_curve) = curve;
+ NEXT_CURVE (curve) = left_curve;
+
+/* REPORT ("s"); */
+/* LOG1 ("\nSubdividing (error %.3f):\n", error); */
+/* LOG3 (" Original point: (%.3f,%.3f), #%u.\n", */
+/* CURVE_POINT (curve, worst_point).x, */
+/* CURVE_POINT (curve, worst_point).y, worst_point); */
+ subdivision_index = find_subdivision (curve, worst_point);
+/* LOG3 (" Final point: (%.3f,%.3f), #%u.\n", */
+/* CURVE_POINT (curve, subdivision_index).x, */
+/* CURVE_POINT (curve, subdivision_index).y, subdivision_index); */
+/* display_subdivision (CURVE_POINT (curve, subdivision_index)); */
+
+ /* The last point of the left-hand curve will also be the first
+ point of the right-hand curve. */
+ CURVE_LENGTH (left_curve) = subdivision_index + 1;
+ CURVE_LENGTH (right_curve) = CURVE_LENGTH (curve) - subdivision_index;
+ left_curve->point_list = curve->point_list;
+ right_curve->point_list = curve->point_list + subdivision_index;
+
+ /* We want to use the tangents of the curve which we are
+ subdividing for the start tangent for left_curve and the
+ end tangent for right_curve. */
+ CURVE_START_TANGENT (left_curve) = CURVE_START_TANGENT (curve);
+ CURVE_END_TANGENT (right_curve) = CURVE_END_TANGENT (curve);
+
+ /* We have to set up the two curves before finding the tangent at
+ the subdivision point. The tangent at that point must be the
+ same for both curves, or noticeable bumps will occur in the
+ character. But we want to use information on both sides of the
+ point to compute the tangent, hence cross_curve = true. */
+ find_tangent (left_curve, /* to_start_point: */ false,
+ /* cross_curve: */ true);
+ CURVE_START_TANGENT (right_curve) = CURVE_END_TANGENT (left_curve);
+
+ /* Now that we've set up the curves, we can fit them. */
+ left_spline_list = fit_curve (left_curve);
+ right_spline_list = fit_curve (right_curve);
+
+ /* Neither of the subdivided curves could be fit, so fail. */
+ if (left_spline_list == NULL && right_spline_list == NULL)
+ return NULL;
+
+ /* Put the two together (or whichever of them exist). */
+ spline_list = new_spline_list ();
+
+ if (left_spline_list == NULL)
+ {
+ WARNING ("could not fit left spline list");
+/* LOG1 ("Could not fit spline to left curve (%x).\n", */
+/* (unsigned) left_curve); */
+ }
+ else
+ concat_spline_lists (spline_list, *left_spline_list);
+
+ if (right_spline_list == NULL)
+ {
+ WARNING ("could not fit right spline list");
+/* LOG1 ("Could not fit spline to right curve (%x).\n", */
+/* (unsigned) right_curve); */
+ }
+ else
+ concat_spline_lists (spline_list, *right_spline_list);
+ }
+
+ return spline_list;
+}
+
+
+/* Our job here is to find alpha1 (and alpha2), where t1_hat (t2_hat) is
+ the tangent to CURVE at the starting (ending) point, such that:
+
+ control1 = alpha1*t1_hat + starting point
+ control2 = alpha2*t2_hat + ending_point
+
+ and the resulting spline (starting_point .. control1 and control2 ..
+ ending_point) minimizes the least-square error from CURVE.
+
+ See pp.57--59 of the Phoenix thesis.
+
+ The B?(t) here corresponds to B_i^3(U_i) there.
+ The Bernshte\u in polynomials of degree n are defined by
+ B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n */
+
+#define B0(t) CUBE (1 - (t))
+#define B1(t) (3.0 * (t) * SQUARE (1 - (t)))
+#define B2(t) (3.0 * SQUARE (t) * (1 - (t)))
+#define B3(t) CUBE (t)
+
+#define U(i) CURVE_T (curve, i)
+
+static spline_type
+fit_one_spline (curve_type curve)
+{
+ /* Since our arrays are zero-based, the `C0' and `C1' here correspond
+ to `C1' and `C2' in the paper. */
+ real X_C1_det, C0_X_det, C0_C1_det;
+ real alpha1, alpha2;
+ spline_type spline;
+ vector_type start_vector, end_vector;
+ unsigned i;
+ vector_type t1_hat = *CURVE_START_TANGENT (curve);
+ vector_type t2_hat = *CURVE_END_TANGENT (curve);
+ real C[2][2] = { { 0.0, 0.0 }, { 0.0, 0.0 } };
+ real X[2] = { 0.0, 0.0 };
+ int Alen = CURVE_LENGTH (curve);
+ vector_type *A;
+
+ A = g_new0 (vector_type, Alen * 2);
+
+ START_POINT (spline) = CURVE_POINT (curve, 0);
+ END_POINT (spline) = LAST_CURVE_POINT (curve);
+ SPLINE_LINEARITY (spline) = 0;
+ start_vector = make_vector (START_POINT (spline));
+ end_vector = make_vector (END_POINT (spline));
+
+ for (i = 0; i < CURVE_LENGTH (curve); i++)
+ {
+ A[i*2+0] = Vmult_scalar (t1_hat, B1 (U (i)));
+ A[i*2+1] = Vmult_scalar (t2_hat, B2 (U (i)));
+ }
+
+ for (i = 0; i < CURVE_LENGTH (curve); i++)
+ {
+ vector_type temp, temp0, temp1, temp2, temp3;
+ vector_type *Ai = &A[i*2];
+
+ C[0][0] += Vdot (Ai[0], Ai[0]);
+ C[0][1] += Vdot (Ai[0], Ai[1]);
+ /* C[1][0] = C[0][1] (this is assigned outside the loop) */
+ C[1][1] += Vdot (Ai[1], Ai[1]);
+
+ /* Now the right-hand side of the equation in the paper. */
+ temp0 = Vmult_scalar (start_vector, B0 (U (i)));
+ temp1 = Vmult_scalar (start_vector, B1 (U (i)));
+ temp2 = Vmult_scalar (end_vector, B2 (U (i)));
+ temp3 = Vmult_scalar (end_vector, B3 (U (i)));
+
+ temp = make_vector (Vsubtract_point (CURVE_POINT (curve, i),
+ Vadd (temp0, Vadd (temp1, Vadd (temp2, temp3)))));
+
+ X[0] += Vdot (temp, Ai[0]);
+ X[1] += Vdot (temp, Ai[1]);
+ }
+
+ C[1][0] = C[0][1];
+
+ X_C1_det = X[0] * C[1][1] - X[1] * C[0][1];
+ C0_X_det = C[0][0] * X[1] - C[0][1] * X[0];
+ C0_C1_det = C[0][0] * C[1][1] - C[1][0] * C[0][1];
+ if (C0_C1_det == 0.0)
+ FATAL ("zero determinant of C0*C1");
+
+ alpha1 = X_C1_det / C0_C1_det;
+ alpha2 = C0_X_det / C0_C1_det;
+
+ CONTROL1 (spline) = Vadd_point (START_POINT (spline),
+ Vmult_scalar (t1_hat, alpha1));
+ CONTROL2 (spline) = Vadd_point (END_POINT (spline),
+ Vmult_scalar (t2_hat, alpha2));
+ SPLINE_DEGREE (spline) = CUBIC;
+
+ g_free (A);
+
+ return spline;
+}
+
+/* Find closer-to-optimal t values for the given x,y's and control
+ points, using Newton-Raphson iteration. A good description of this
+ is in Plass & Stone. This routine performs one step in the
+ iteration, not the whole thing. */
+
+static boolean
+reparameterize (curve_type curve, spline_type S)
+{
+ unsigned p, i;
+ spline_type S1, S2; /* S' and S''. */
+
+/* REPORT ("r"); */
+
+ /* Find the first and second derivatives of S. To make
+ `evaluate_spline' work, we fill the beginning points (i.e., the first
+ two for a linear spline and the first three for a quadratic one),
+ even though this is at odds with the rest of the program. */
+ for (i = 0; i < 3; i++)
+ {
+ S1.v[i].x = 3.0 * (S.v[i + 1].x - S.v[i].x);
+ S1.v[i].y = 3.0 * (S.v[i + 1].y - S.v[i].y);
+ }
+ S1.v[i].x = S1.v[i].y = -1.0; /* These will never be accessed. */
+ SPLINE_DEGREE (S1) = QUADRATIC;
+
+ for (i = 0; i < 2; i++)
+ {
+ S2.v[i].x = 2.0 * (S1.v[i + 1].x - S1.v[i].x);
+ S2.v[i].y = 2.0 * (S1.v[i + 1].y - S1.v[i].y);
+ }
+ S2.v[2].x = S2.v[2].y = S2.v[3].x = S2.v[3].y = -1.0;
+ SPLINE_DEGREE (S2) = LINEAR;
+
+ for (p = 0; p < CURVE_LENGTH (curve); p++)
+ {
+ real new_distance, old_distance;
+ real_coordinate_type new_point;
+ real_coordinate_type point = CURVE_POINT (curve, p);
+ real t = CURVE_T (curve, p);
+
+ /* Find the points at this t on S, S', and S''. */
+ real_coordinate_type S_t = evaluate_spline (S, t);
+ real_coordinate_type S1_t = evaluate_spline (S1, t);
+ real_coordinate_type S2_t = evaluate_spline (S2, t);
+
+ /* The differences in x and y (Q1(t) on p.62 of Schneider's thesis). */
+ real_coordinate_type d;
+ real numerator;
+ real denominator;
+
+ d.x = S_t.x - point.x;
+ d.y = S_t.y - point.y;
+
+ /* The step size, f(t)/f'(t). */
+ numerator = d.x * S1_t.x + d.y * S1_t.y;
+ denominator = (SQUARE (S1_t.x) + d.x * S2_t.x
+ + SQUARE (S1_t.y) + d.y * S2_t.y);
+
+ /* We compute the distances only to be able to check that we
+ really are moving closer. I don't know how this condition can
+ be reliably checked for in advance, but I know what it means in
+ practice: the original fit was not good enough for the
+ Newton-Raphson iteration to converge. Therefore, we need to
+ abort the reparameterization, and subdivide. */
+ old_distance = distance (S_t, point);
+ CURVE_T (curve, p) -= numerator / denominator;
+ new_point = evaluate_spline (S, CURVE_T (curve, p));
+ new_distance = distance (new_point, point);
+
+ if (new_distance > old_distance)
+ {
+/* REPORT ("!"); */
+/* LOG3 (" Stopped reparameterizing; %.3f > %.3f at point %u.\n", */
+/* new_distance, old_distance, p); */
+ return false;
+ }
+
+ /* The t value might be negative or > 1, if the choice of control
+ points wasn't so great. We have no difficulty in evaluating
+ a spline at a t outside the range zero to one, though, so it
+ doesn't matter. (Although it is a little unconventional.) */
+ }
+/* LOG (" reparameterized curve:\n "); */
+/* log_curve (curve, true); */
+
+ return true;
+}
+
+/* This routine finds the best place to subdivide the curve CURVE,
+ somewhere near to the point whose index is INITIAL. Originally,
+ curves were always subdivided at the point of worst error, which is
+ intuitively appealing, but doesn't always give the best results. For
+ example, at the end of a serif that tapers into the stem, the best
+ subdivision point is at the point where they join, even if the worst
+ point is a little ways into the serif.
+
+ We return the index of the point at which to subdivide. */
+
+static unsigned
+find_subdivision (curve_type curve, unsigned initial)
+{
+ unsigned i, n_done;
+ int best_point = -1;
+ vector_type best = { FLT_MAX, FLT_MAX };
+ unsigned search = subdivide_search * CURVE_LENGTH (curve);
+
+/* LOG1 (" Number of points to search: %u.\n", search); */
+
+ /* We don't want to find the first (or last) point in the curve. */
+ for (i = initial, n_done = 0; i > 0 && n_done < search;
+ i = CURVE_PREV (curve, i), n_done++)
+ {
+ if (test_subdivision_point (curve, i, &best))
+ {
+ best_point = i;
+/* LOG3 (" Better point: (%.3f,%.3f), #%u.\n", */
+/* CURVE_POINT (curve, i).x, CURVE_POINT (curve, i).y, i); */
+ }
+ }
+
+ /* If we found a good one, let's take it. */
+ if (best_point != -1)
+ return best_point;
+
+ for (i = CURVE_NEXT (curve, initial), n_done = 0;
+ i < CURVE_LENGTH (curve) - 1 && n_done < search;
+ i = CURVE_NEXT (curve, i), n_done++)
+ {
+ if (test_subdivision_point (curve, i, &best))
+ {
+ best_point = i;
+/* LOG3 (" Better point at (%.3f,%.3f), #%u.\n", */
+/* CURVE_POINT (curve, i).x, CURVE_POINT (curve, i).y, i); */
+ }
+ }
+
+ /* If we didn't find any better point, return the original. */
+ return best_point == -1 ? initial : best_point;
+}
+
+
+/* Here are some macros that decide whether or not we're at a
+ ``join point'', as described above. */
+#define ONLY_ONE_LESS(v) \
+ (((v).dx < subdivide_threshold && (v).dy > subdivide_threshold) \
+ || ((v).dy < subdivide_threshold && (v).dx > subdivide_threshold))
+
+#define BOTH_GREATER(v) \
+ ((v).dx > subdivide_threshold && (v).dy > subdivide_threshold)
+
+/* We assume that the vectors V1 and V2 are nonnegative. */
+#define JOIN(v1, v2) \
+ ((ONLY_ONE_LESS (v1) && BOTH_GREATER (v2)) \
+ || (ONLY_ONE_LESS (v2) && BOTH_GREATER (v1)))
+
+/* If the component D of the vector V is smaller than the best so far,
+ update the best point. */
+#define UPDATE_BEST(v, d) \
+ do \
+ { \
+ if ((v).d < subdivide_threshold && (v).d < best->d) \
+ best->d = (v).d; \
+ } \
+ while (0)
+
+
+/* If the point INDEX in the curve CURVE is the best subdivision point
+ we've found so far, update the vector BEST. */
+
+static boolean
+test_subdivision_point (curve_type curve, unsigned index, vector_type *best)
+{
+ unsigned count;
+ vector_type in, out;
+ boolean join = false;
+
+ find_curve_vectors (index, curve, subdivide_surround, &in, &out, &count);
+
+ /* We don't want to subdivide at points which are very close to the
+ endpoints, so if the vectors aren't computed from as many points as
+ we asked for, don't bother checking this point. */
+ if (count == subdivide_surround)
+ {
+ in = Vabs (in);
+ out = Vabs (out);
+
+ join = JOIN (in, out);
+ if (join)
+ {
+ UPDATE_BEST (in, dx);
+ UPDATE_BEST (in, dy);
+ UPDATE_BEST (out, dx);
+ UPDATE_BEST (out, dy);
+ }
+ }
+
+ return join;
+}
+
+/* Find reasonable values for t for each point on CURVE. The method is
+ called chord-length parameterization, which is described in Plass &
+ Stone. The basic idea is just to use the distance from one point to
+ the next as the t value, normalized to produce values that increase
+ from zero for the first point to one for the last point. */
+
+static void
+set_initial_parameter_values (curve_type curve)
+{
+ unsigned p;
+
+/* LOG ("\nAssigning initial t values:\n "); */
+
+ CURVE_T (curve, 0) = 0.0;
+
+ for (p = 1; p < CURVE_LENGTH (curve); p++)
+ {
+ real_coordinate_type point = CURVE_POINT (curve, p),
+ previous_p = CURVE_POINT (curve, p - 1);
+ real d = distance (point, previous_p);
+ CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d;
+ }
+
+ assert (LAST_CURVE_T (curve) != 0.0);
+
+ for (p = 1; p < CURVE_LENGTH (curve); p++)
+ CURVE_T (curve, p) = CURVE_T (curve, p) / LAST_CURVE_T (curve);
+
+/* log_entire_curve (curve); */
+}
+
+/* Find an approximation to the tangent to an endpoint of CURVE (to the
+ first point if TO_START_POINT is true, else the last). If
+ CROSS_CURVE is true, consider points on the adjacent curve to CURVE.
+
+ It is important to compute an accurate approximation, because the
+ control points that we eventually decide upon to fit the curve will
+ be placed on the half-lines defined by the tangents and
+ endpoints...and we never recompute the tangent after this. */
+
+static void
+find_tangent (curve_type curve, boolean to_start_point, boolean cross_curve)
+{
+ vector_type tangent;
+ vector_type **curve_tangent = to_start_point ? &(CURVE_START_TANGENT (curve))
+ : &(CURVE_END_TANGENT (curve));
+ unsigned n_points = 0;
+
+/* LOG1 (" tangent to %s: ", to_start_point ? "start" : "end"); */
+
+ if (*curve_tangent == NULL)
+ {
+ *curve_tangent = g_new (vector_type, 1);
+ tangent = find_half_tangent (curve, to_start_point, &n_points);
+
+ if (cross_curve)
+ {
+ curve_type adjacent_curve
+ = to_start_point ? PREVIOUS_CURVE (curve) : NEXT_CURVE (curve);
+ vector_type tangent2
+ = find_half_tangent (adjacent_curve, !to_start_point, &n_points);
+
+/* LOG2 ("(adjacent curve half tangent (%.3f,%.3f)) ", */
+/* tangent2.dx, tangent2.dy); */
+ tangent = Vadd (tangent, tangent2);
+ }
+
+ assert (n_points > 0);
+ **curve_tangent = Vmult_scalar (tangent, 1.0 / n_points);
+ }
+ else
+ {
+/* LOG ("(already computed) "); */
+ }
+
+/* LOG2 ("(%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy); */
+}
+
+
+/* Find the change in y and change in x for `tangent_surround' (a global)
+ points along CURVE. Increment N_POINTS by the number of points we
+ actually look at. */
+
+static vector_type
+find_half_tangent (curve_type c, boolean to_start_point, unsigned *n_points)
+{
+ unsigned p;
+ int factor = to_start_point ? 1 : -1;
+ unsigned tangent_index = to_start_point ? 0 : c->length - 1;
+ real_coordinate_type tangent_point = CURVE_POINT (c, tangent_index);
+ vector_type tangent;
+
+ tangent.dx = 0.0;
+ tangent.dy = 0.0;
+
+ for (p = 1; p <= tangent_surround; p++)
+ {
+ int this_index = p * factor + tangent_index;
+ real_coordinate_type this_point;
+
+ if (this_index < 0 || this_index >= c->length)
+ break;
+
+ this_point = CURVE_POINT (c, p * factor + tangent_index);
+
+ /* Perhaps we should weight the tangent from `this_point' by some
+ factor dependent on the distance from the tangent point. */
+ tangent = Vadd (tangent,
+ Vmult_scalar (Psubtract (this_point, tangent_point),
+ factor));
+ (*n_points)++;
+ }
+
+ return tangent;
+}
+
+/* When this routine is called, we have computed a spline representation
+ for the digitized curve. The question is, how good is it? If the
+ fit is very good indeed, we might have an error of zero on each
+ point, and then WORST_POINT becomes irrelevant. But normally, we
+ return the error at the worst point, and the index of that point in
+ WORST_POINT. The error computation itself is the Euclidean distance
+ from the original curve CURVE to the fitted spline SPLINE. */
+
+static real
+find_error (curve_type curve, spline_type spline, unsigned *worst_point)
+{
+ unsigned this_point;
+ real total_error = 0.0;
+ real worst_error = FLT_MIN;
+
+ *worst_point = CURVE_LENGTH (curve) + 1; /* A sentinel value. */
+
+ for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+ {
+ real_coordinate_type curve_point = CURVE_POINT (curve, this_point);
+ real t = CURVE_T (curve, this_point);
+ real_coordinate_type spline_point = evaluate_spline (spline, t);
+ real this_error = distance (curve_point, spline_point);
+
+ if (this_error > worst_error)
+ {
+ *worst_point = this_point;
+ worst_error = this_error;
+ }
+ total_error += this_error;
+ }
+
+ if (*worst_point == CURVE_LENGTH (curve) + 1)
+ { /* Didn't have any ``worst point''; the error should be zero. */
+ if (epsilon_equal (total_error, 0.0))
+ {
+/* LOG (" Every point fit perfectly.\n"); */
+ }
+ else
+ printf ("No worst point found; something is wrong");
+ }
+ else
+ {
+/* LOG4 (" Worst error (at (%.3f,%.3f), point #%u) was %.3f.\n", */
+/* CURVE_POINT (curve, *worst_point).x, */
+/* CURVE_POINT (curve, *worst_point).y, *worst_point, worst_error); */
+/* LOG1 (" Total error was %.3f.\n", total_error); */
+/* LOG2 (" Average error (over %u points) was %.3f.\n", */
+/* CURVE_LENGTH (curve), total_error / CURVE_LENGTH (curve)); */
+ }
+
+ return worst_error;
+}
+
+/* Supposing that we have accepted the error, another question arises:
+ would we be better off just using a straight line? */
+
+static boolean
+spline_linear_enough (spline_type *spline, curve_type curve)
+{
+ real A, B, C, slope;
+ unsigned this_point;
+ real distance = 0.0;
+
+/* LOG ("Checking linearity:\n"); */
+
+ /* For a line described by Ax + By + C = 0, the distance d from a
+ point (x0,y0) to that line is:
+
+ d = | Ax0 + By0 + C | / sqrt (A^2 + B^2).
+
+ We can find A, B, and C from the starting and ending points,
+ unless the line defined by those two points is vertical. */
+
+ if (epsilon_equal (START_POINT (*spline).x, END_POINT (*spline).x))
+ {
+ A = 1;
+ B = 0;
+ C = -START_POINT (*spline).x;
+ }
+ else
+ {
+ /* Plug the spline's starting and ending points into the two-point
+ equation for a line, that is,
+
+ (y - y1) = ((y2 - y1)/(x2 - x1)) * (x - x1)
+
+ to get the values for A, B, and C. */
+
+ slope = ((END_POINT (*spline).y - START_POINT (*spline).y)
+ / (END_POINT (*spline).x - START_POINT (*spline).x));
+ A = -slope;
+ B = 1;
+ C = slope * START_POINT (*spline).x - START_POINT (*spline).y;
+ }
+/* LOG3 (" Line is %.3fx + %.3fy + %.3f = 0.\n", A, B, C); */
+
+ for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
+ {
+ real t = CURVE_T (curve, this_point);
+ real_coordinate_type spline_point = evaluate_spline (*spline, t);
+
+ distance += fabs (A * spline_point.x + B * spline_point.y + C)
+ / sqrt (A * A + B * B);
+ }
+/* LOG1 (" Total distance is %.3f, ", distance); */
+
+ distance /= CURVE_LENGTH (curve);
+/* LOG1 ("which is %.3f normalized.\n", distance); */
+
+ /* We want reversion of short curves to splines to be more likely than
+ reversion of long curves, hence the second division by the curve
+ length, for use in `change_bad_lines'. */
+ SPLINE_LINEARITY (*spline) = distance / CURVE_LENGTH (curve);
+/* LOG1 (" Final linearity: %.3f.\n", SPLINE_LINEARITY (*spline)); */
+
+ return distance < line_threshold;
+}
+
+
+/* Unfortunately, we cannot tell in isolation whether a given spline
+ should be changed to a line or not. That can only be known after the
+ entire curve has been fit to a list of splines. (The curve is the
+ pixel outline between two corners.) After subdividing the curve, a
+ line may very well fit a portion of the curve just as well as the
+ spline---but unless a spline is truly close to being a line, it
+ should not be combined with other lines. */
+
+static void
+change_bad_lines (spline_list_type *spline_list)
+{
+ unsigned this_spline;
+ boolean found_cubic = false;
+ unsigned length = SPLINE_LIST_LENGTH (*spline_list);
+
+/* LOG1 ("\nChecking for bad lines (length %u):\n", length); */
+
+ /* First see if there are any splines in the fitted shape. */
+ for (this_spline = 0; this_spline < length; this_spline++)
+ {
+ if (SPLINE_DEGREE (SPLINE_LIST_ELT (*spline_list, this_spline)) == CUBIC)
+ {
+ found_cubic = true;
+ break;
+ }
+ }
+
+ /* If so, change lines back to splines (we haven't done anything to
+ their control points, so we only have to change the degree) unless
+ the spline is close enough to being a line. */
+ if (found_cubic)
+ for (this_spline = 0; this_spline < length; this_spline++)
+ {
+ spline_type s = SPLINE_LIST_ELT (*spline_list, this_spline);
+
+ if (SPLINE_DEGREE (s) == LINEAR)
+ {
+/* LOG1 (" #%u: ", this_spline); */
+ if (SPLINE_LINEARITY (s) > line_reversion_threshold)
+ {
+/* LOG ("reverted, "); */
+ SPLINE_DEGREE (SPLINE_LIST_ELT (*spline_list, this_spline))
+ = CUBIC;
+ }
+/* LOG1 ("linearity %.3f.\n", SPLINE_LINEARITY (s)); */
+ }
+ }
+ else
+ {
+/* LOG (" No lines.\n"); */
+ }
+}
+
+/* When we have finished fitting an entire pixel outline to a spline
+ list L, we go through L to ensure that any endpoints that are ``close
+ enough'' (i.e., within `align_threshold') to being the same really
+ are the same. */
+
+/* This macro adjusts the AXIS axis on the starting and ending points on
+ a particular spline if necessary. */
+#define TRY_AXIS(axis) \
+ do \
+ { \
+ real delta = fabs (end.axis - start.axis); \
+ \
+ if (!epsilon_equal (delta, 0.0) && delta <= align_threshold) \
+ { \
+ spline_type *next = &NEXT_SPLINE_LIST_ELT (*l, this_spline); \
+ spline_type *prev = &PREV_SPLINE_LIST_ELT (*l, this_spline); \
+ \
+ START_POINT (*s).axis = END_POINT (*s).axis \
+ = END_POINT (*prev).axis = START_POINT (*next).axis \
+ = (start.axis + end.axis) / 2; \
+ spline_change = true; \
+ } \
+ } \
+ while (0)
+
+static void
+align (spline_list_type *l)
+{
+ boolean change;
+ unsigned this_spline;
+ unsigned length = SPLINE_LIST_LENGTH (*l);
+
+/* LOG1 ("\nAligning spline list (length %u):\n", length); */
+
+ do
+ {
+ change = false;
+
+/* LOG (" "); */
+
+ for (this_spline = 0; this_spline < length; this_spline++)
+ {
+ boolean spline_change = false;
+ spline_type *s = &SPLINE_LIST_ELT (*l, this_spline);
+ real_coordinate_type start = START_POINT (*s);
+ real_coordinate_type end = END_POINT (*s);
+
+ TRY_AXIS (x);
+ TRY_AXIS (y);
+ if (spline_change)
+ {
+/* LOG1 ("%u ", this_spline); */
+ change |= spline_change;
+ }
+ }
+/* LOG ("\n"); */
+ }
+ while (change);
+}
+
+/* Lists of array indices (well, that is what we use it for). */
+
+static index_list_type
+new_index_list (void)
+{
+ index_list_type index_list;
+
+ index_list.data = NULL;
+ INDEX_LIST_LENGTH (index_list) = 0;
+
+ return index_list;
+}
+
+
+static void
+free_index_list (index_list_type *index_list)
+{
+ if (INDEX_LIST_LENGTH (*index_list) > 0)
+ {
+ g_free (index_list->data);
+ index_list->data = NULL;
+ INDEX_LIST_LENGTH (*index_list) = 0;
+ }
+}
+
+
+static void
+append_index (index_list_type *list, unsigned new_index)
+{
+ INDEX_LIST_LENGTH (*list)++;
+ list->data = (unsigned *)g_realloc(list->data,(INDEX_LIST_LENGTH (*list)) * sizeof(unsigned));
+/* XRETALLOC (list->data, INDEX_LIST_LENGTH (*list), unsigned); */
+ list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
+}
diff --git a/plug-ins/selection-to-path/fit.h b/plug-ins/selection-to-path/fit.h
new file mode 100644
index 0000000..f009c9a
--- /dev/null
+++ b/plug-ins/selection-to-path/fit.h
@@ -0,0 +1,54 @@
+/* fit.h: convert the pixel representation to splines.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef FIT_H
+#define FIT_H
+
+#include "pxl-outline.h"
+#include "spline.h"
+
+
+/* See fit.c for descriptions of these variables, all of which can be
+ set using options. */
+extern real align_threshold;
+extern real corner_always_threshold;
+extern unsigned corner_surround;
+extern real corner_threshold;
+extern real error_threshold;
+extern unsigned filter_alternative_surround;
+extern real filter_epsilon;
+extern unsigned filter_iteration_count;
+extern real filter_percent;
+extern unsigned filter_surround;
+extern boolean keep_knees;
+extern real line_reversion_threshold;
+extern real line_threshold;
+extern real reparameterize_improvement;
+extern real reparameterize_threshold;
+extern real subdivide_search;
+extern unsigned subdivide_surround;
+extern real subdivide_threshold;
+extern unsigned tangent_surround;
+
+
+/* Fit splines and lines to LIST. */
+extern spline_list_array_type fitted_splines (pixel_outline_list_type list);
+void fit_set_params(SELVALS *);
+void fit_set_default_params(SELVALS *);
+
+#endif /* not FIT_H */
diff --git a/plug-ins/selection-to-path/global.h b/plug-ins/selection-to-path/global.h
new file mode 100644
index 0000000..1ee5962
--- /dev/null
+++ b/plug-ins/selection-to-path/global.h
@@ -0,0 +1,213 @@
+/* global.h: extend the standard programming environment a little. This
+ * is included from config.h, which everyone includes.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include <stdlib.h>
+
+#include "types.h"
+
+/* Define common sorts of messages. */
+
+/* This should be called only after a system call fails. */
+#define FATAL_PERROR(s) do { perror (s); exit (errno); } while (0)
+
+
+#define START_FATAL() do { fputs ("fatal: ", stderr)
+#define END_FATAL() fputs (".\n", stderr); exit (1); } while (0)
+
+#define FATAL(x) \
+ START_FATAL (); fprintf (stderr, "%s", x); END_FATAL ()
+#define FATAL1(s, e1) \
+ START_FATAL (); fprintf (stderr, s, e1); END_FATAL ()
+#define FATAL2(s, e1, e2) \
+ START_FATAL (); fprintf (stderr, s, e1, e2); END_FATAL ()
+#define FATAL3(s, e1, e2, e3) \
+ START_FATAL (); fprintf (stderr, s, e1, e2, e3); END_FATAL ()
+#define FATAL4(s, e1, e2, e3, e4) \
+ START_FATAL (); fprintf (stderr, s, e1, e2, e3, e4); END_FATAL ()
+
+
+#define START_WARNING() do { fputs ("warning: ", stderr)
+#define END_WARNING() fputs (".\n", stderr); fflush (stderr); } while (0)
+
+#define WARNING(x) \
+ START_WARNING (); fprintf (stderr, "%s", x); END_WARNING ()
+#define WARNING1(s, e1) \
+ START_WARNING (); fprintf (stderr, s, e1); END_WARNING ()
+#define WARNING2(s, e1, e2) \
+ START_WARNING (); fprintf (stderr, s, e1, e2); END_WARNING ()
+#define WARNING3(s, e1, e2, e3) \
+ START_WARNING (); fprintf (stderr, s, e1, e2, e3); END_WARNING ()
+#define WARNING4(s, e1, e2, e3, e4) \
+ START_WARNING (); fprintf (stderr, s, e1, e2, e3, e4); END_WARNING ()
+
+/* Define useful abbreviations. */
+
+/* This is the maximum number of numerals that result when a 64-bit
+ integer is converted to a string, plus one for a trailing null byte,
+ plus one for a sign. */
+#define MAX_INT_LENGTH 21
+
+/* Printer's points, as defined by TeX (and good typesetters everywhere). */
+#define POINTS_PER_INCH 72.27
+
+/* Convert a number V in pixels to printer's points, and vice versa,
+ assuming a resolution of DPI pixels per inch. */
+#define PIXELS_TO_POINTS(v, dpi) (POINTS_PER_INCH * (v) / (dpi))
+#define POINTS_TO_REAL_PIXELS(v, dpi) ((v) * (dpi) / POINTS_PER_INCH)
+#define POINTS_TO_PIXELS(v, dpi) ((int) (POINTS_TO_REAL_PIXELS (v, dpi) + .5))
+
+/* Some simple numeric operations. It is possible to define these much
+ more cleanly in GNU C, but we haven't done that (yet). */
+#define SQUARE(x) ((x) * (x))
+#define CUBE(x) ((x) * (x) * (x))
+#define SAME_SIGN(u,v) ((u) >= 0 && (v) >= 0 || (u) < 0 && (v) < 0)
+#define SIGN(x) ((x) > 0 ? 1 : (x) < 0 ? -1 : 0)
+#define SROUND(x) ((int) ((int) (x) + .5 * SIGN (x)))
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Too bad C doesn't define operators for these. */
+#define MAX_EQUALS(var, expr) if ((expr) > (var)) (var) = (expr)
+#define MIN_EQUALS(var, expr) if ((expr) < (var)) (var) = (expr)
+
+#define STREQ(s1, s2) (strcmp (s1, s2) == 0)
+
+/* Declarations for commonly-used routines we provide ourselves. The
+ ones here are only needed by us, so we do not provide them in
+ unprototyped form. Others are declared both ways in lib.h. */
+
+#if 0 /* These aren't actually defined anywhere */
+/* Return the current date and time a la date(1). */
+extern string now (void);
+
+/* Check if a string is a valid floating-point or decimal integer.
+ Returns false if passed NULL. */
+extern boolean float_ok (string);
+extern boolean integer_ok (string);
+
+/* Like `atoi', but disallow negative numbers. */
+extern const unsigned atou (string);
+
+/* The converses of atoi, atou, and atof. These all return dynamically
+ allocated strings. `dtoa' is so-named because `ftoa' is a library
+ function on some systems (the IBM RT), and the loader complains that
+ is defined twice, for reasons I don't understand. */
+extern string itoa (int);
+extern string utoa (unsigned);
+extern string dtoa (double);
+
+#endif
+
+/* Like their stdio counterparts, but abort on error, after calling
+ perror(3) with FILENAME as its argument. */
+/* extern FILE *xfopen (string filename, string mode); */
+/* extern void xfclose (FILE *, string filename); */
+/* extern void xfseek (FILE *, long, int, string filename); */
+/* extern four_bytes xftell (FILE *, string filename); */
+
+/* Copies the file FROM to the file TO, then unlinks FROM. */
+extern void xrename (string from, string to);
+
+/* Return NAME with any leading path stripped off. This returns a
+ pointer into NAME. */
+/* ALT extern string basename (string name); */
+
+
+
+/* If P or *P is null, abort. Otherwise, call free(3) on P,
+ and then set *P to NULL. */
+extern void safe_free (address *p);
+
+
+/* Math functions. */
+
+/* Says whether V1 and V2 are within REAL_EPSILON of each other.
+ Fixed-point arithmetic would be better, to guarantee machine
+ independence, but it's so much more painful to work with. The value
+ here is smaller than can be represented in either a `fix_word' or a
+ `scaled_num', so more precision than this will be lost when we
+ output, anyway. */
+#define REAL_EPSILON 0.00001
+extern boolean epsilon_equal (real v1, real v2);
+
+/* Arc cosine, in degrees. */
+extern real my_acosd (real);
+
+/* Return the Euclidean distance between the two points. */
+extern real distance (real_coordinate_type, real_coordinate_type);
+extern real int_distance (coordinate_type, coordinate_type);
+
+/* Slope between two points (delta y per unit x). */
+extern real slope (real_coordinate_type, real_coordinate_type);
+
+/* Make a real coordinate from an integer one, and vice versa. */
+extern real_coordinate_type int_to_real_coord (coordinate_type);
+extern coordinate_type real_to_int_coord (real_coordinate_type);
+
+/* Test if two integer points are adjacent. */
+extern boolean points_adjacent_p (int row1, int col1, int r2, int c2);
+
+/* Find the largest and smallest elements of an array. */
+extern void find_bounds (real values[], unsigned value_count,
+ /* returned: */ real *the_min, real *the_max);
+
+/* Make all the elements in the array between zero and one. */
+extern real *map_to_unit (real * values, unsigned value_count);
+
+
+/* String functions. */
+
+/* Return (a fresh copy of) SOURCE beginning at START and ending at
+ LIMIT. (Or NULL if LIMIT < START.) */
+extern string substring (string source, const unsigned start,
+ const unsigned limit);
+
+/* Change all uppercase letters in S to lowercase. */
+extern string lowercasify (string s);
+
+
+/* Character code parsing. */
+
+/* If the string S parses as a character code, this sets *VALID to
+ `true' and returns the number. If it doesn't, it sets *VALID to
+ `false' and the return value is garbage.
+
+ We allow any of the following possibilities: a single character, as in
+ `a' or `0'; a decimal number, as in `21'; an octal number, as in `03'
+ or `0177'; a hexadecimal number, as in `0x3' or `0xff'. */
+extern charcode_type parse_charcode (string s, boolean *valid);
+
+/* Like `parse_charcode', but gives a fatal error if the string isn't a
+ valid character code. */
+extern charcode_type xparse_charcode (string s);
+
+/* The environment variable name with which to look up auxiliary files. */
+#ifndef LIB_ENVVAR
+#define LIB_ENVVAR "FONTUTIL_LIB"
+#endif
+
+#endif /* not GLOBAL_H */
diff --git a/plug-ins/selection-to-path/math.c b/plug-ins/selection-to-path/math.c
new file mode 100644
index 0000000..58f3b3f
--- /dev/null
+++ b/plug-ins/selection-to-path/math.c
@@ -0,0 +1,177 @@
+/* math.c: define some simple array operations, and other functions.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+
+#include "libgimp/gimp.h"
+
+#include "types.h"
+#include "global.h"
+
+/* Numerical errors sometimes make a floating point number just slightly
+ larger or smaller than its true value. When it matters, we need to
+ compare with some tolerance, REAL_EPSILON, defined in kbase.h. */
+
+boolean
+epsilon_equal (real v1, real v2)
+{
+ return
+ v1 == v2 /* Usually they'll be exactly equal, anyway. */
+ || fabs (v1 - v2) <= REAL_EPSILON;
+}
+
+
+/* Return the Euclidean distance between P1 and P2. */
+
+real
+distance (real_coordinate_type p1, real_coordinate_type p2)
+{
+ return hypot (p1.x - p2.x, p1.y - p2.y);
+}
+
+
+/* Same thing, for integer points. */
+real
+int_distance (coordinate_type p1, coordinate_type p2)
+{
+ return hypot ((double) p1.x - p2.x, (double) p1.y - p2.y);
+}
+
+
+/* Return the arc cosine of V, in degrees in the range zero to 180. V
+ is taken to be in radians. */
+
+real
+my_acosd (real v)
+{
+ real a;
+
+ if (epsilon_equal (v, 1.0))
+ v = 1.0;
+ else if (epsilon_equal (v, -1.0))
+ v = -1.0;
+
+ errno = 0;
+ a = acos (v);
+ if (errno == ERANGE || errno == EDOM)
+ FATAL_PERROR ("acosd");
+
+ return a * 180.0 / G_PI;
+}
+
+
+/* The slope of the line defined by COORD1 and COORD2. */
+
+real
+slope (real_coordinate_type coord1, real_coordinate_type coord2)
+{
+ g_assert (coord2.x - coord1.x != 0);
+
+ return (coord2.y - coord1.y) / (coord2.x - coord1.x);
+}
+
+
+/* Turn an integer point into a real one, and vice versa. */
+
+real_coordinate_type
+int_to_real_coord (coordinate_type int_coord)
+{
+ real_coordinate_type real_coord;
+
+ real_coord.x = int_coord.x;
+ real_coord.y = int_coord.y;
+
+ return real_coord;
+}
+
+
+coordinate_type
+real_to_int_coord (real_coordinate_type real_coord)
+{
+ coordinate_type int_coord;
+
+ int_coord.x = SROUND (real_coord.x);
+ int_coord.y = SROUND (real_coord.y);
+
+ return int_coord;
+}
+
+
+/* See if two points (described by their row and column) are adjacent. */
+
+boolean
+points_adjacent_p (int row1, int col1, int row2, int col2)
+{
+ int row_diff = abs (row1 - row2);
+ int col_diff = abs (col1 - col2);
+
+ return
+ (row_diff == 1 && col_diff == 1)
+ || (row_diff == 0 && col_diff == 1)
+ || (row_diff == 1 && col_diff == 0);
+}
+
+
+/* Find the largest and smallest elements in an array of reals. */
+
+void
+find_bounds (real *values, unsigned value_count, real *min, real *max)
+{
+ unsigned this_value;
+
+ /* We must use FLT_MAX and FLT_MIN, instead of the corresponding
+ values for double, because gcc uses the native atof to parse
+ floating point constants, and many atof's choke on the extremes. */
+ *min = FLT_MAX;
+ *max = FLT_MIN;
+
+ for (this_value = 0; this_value < value_count; this_value++)
+ {
+ if (values[this_value] < *min)
+ *min = values[this_value];
+
+ if (values[this_value] > *max)
+ *max = values[this_value];
+ }
+}
+
+/* Map a range of numbers, some positive and some negative, into all
+ positive, with the greatest being at one and the least at zero.
+
+ This allocates new memory. */
+
+real *
+map_to_unit (real *values, unsigned value_count)
+{
+ real smallest, largest;
+ int this_value;
+ real *mapped_values = g_new (real, value_count);
+
+ find_bounds (values, value_count, &smallest, &largest);
+
+ largest -= smallest; /* We never care about largest itself. */
+
+ for (this_value = 0; this_value < value_count; this_value++)
+ mapped_values[this_value] = (values[this_value] - smallest) / largest;
+
+ return mapped_values;
+}
diff --git a/plug-ins/selection-to-path/pxl-outline.c b/plug-ins/selection-to-path/pxl-outline.c
new file mode 100644
index 0000000..a339ff3
--- /dev/null
+++ b/plug-ins/selection-to-path/pxl-outline.c
@@ -0,0 +1,254 @@
+/* pxl-outline.c: find the edges of the bitmap image; we call each such
+ * edge an ``outline''; each outline is made up of one or more pixels;
+ * and each pixel participates via one or more edges.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "global.h"
+#include "selection-to-path.h"
+#include "bitmap.h"
+#include "edge.h"
+#include "pxl-outline.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+static pixel_outline_type find_one_outline (edge_type,
+ unsigned, unsigned, bitmap_type *);
+static void append_pixel_outline (pixel_outline_list_type *,
+ pixel_outline_type);
+static pixel_outline_type new_pixel_outline (void);
+static void append_outline_pixel (pixel_outline_type *, coordinate_type);
+static void append_coordinate (pixel_outline_type *, int, int, edge_type);
+
+
+static bitmap_type
+local_new_bitmap (unsigned width,unsigned height)
+{
+ bitmap_type answer;
+ unsigned size = width * height;
+
+
+ BITMAP_HEIGHT(answer) = height;
+ BITMAP_WIDTH(answer) = width;
+
+ BITMAP_BITS (answer) = g_new0 (one_byte, size); /* g_new returns NULL if size == 0 */
+
+/* printf("local_new_bitmap size = %d @[%p]\n",size,BITMAP_BITS (answer)); */
+
+ return answer;
+}
+
+static void
+local_free_bitmap (bitmap_type *b)
+{
+ if (BITMAP_BITS (*b) != NULL)
+ safe_free ((address *) &BITMAP_BITS (*b));
+}
+
+/* A character is made up of a list of one or more outlines. Here, we
+ go through a character's bitmap top to bottom, left to right, looking
+ for the next pixel with an unmarked edge also on the character's outline.
+ Each one of these we find is the starting place for one outline. We
+ find these outlines and put them in a list to return. */
+
+pixel_outline_list_type
+find_outline_pixels (void)
+{
+ pixel_outline_list_type outline_list;
+ unsigned row, col;
+ gint height;
+ gint width;
+ bitmap_type marked = local_new_bitmap (sel_get_width(),sel_get_height());
+
+/* printf("width = %d, height = %d\n",BITMAP_WIDTH(marked),BITMAP_HEIGHT(marked)); */
+
+ gimp_progress_init (_("Selection to Path"));
+
+ O_LIST_LENGTH (outline_list) = 0;
+ outline_list.data = NULL;
+
+ height = sel_get_height ();
+ width = sel_get_width ();
+
+ for (row = 0; row < height; row++)
+ {
+ for (col = 0; col < width; col++)
+ {
+ edge_type edge;
+
+ if (sel_pixel_is_white(row, col))
+ continue;
+
+ edge = next_unmarked_outline_edge (row, col, START_EDGE,marked);
+
+ if (edge != no_edge)
+ {
+ pixel_outline_type outline;
+ boolean clockwise = edge == bottom;
+
+ outline = find_one_outline (edge, row, col, &marked);
+
+ /* Outside outlines will start at a top edge, and move
+ counterclockwise, and inside outlines will start at a
+ bottom edge, and move clockwise. This happens because of
+ the order in which we look at the edges. */
+ O_CLOCKWISE (outline) = clockwise;
+ append_pixel_outline (&outline_list, outline);
+
+ }
+ }
+
+ if ((row & 0xf) == 0)
+ gimp_progress_update (((gdouble)row) / height);
+ }
+
+ gimp_progress_update (1.0);
+
+ local_free_bitmap (&marked);
+
+ return outline_list;
+}
+
+
+/* Here we find one of a character C's outlines. We're passed the
+ position (ORIGINAL_ROW and ORIGINAL_COL) of a starting pixel and one
+ of its unmarked edges, ORIGINAL_EDGE. We traverse the adjacent edges
+ of the outline pixels, appending to the coordinate list. We keep
+ track of the marked edges in MARKED, so it should be initialized to
+ zeros when we first get it. */
+
+static pixel_outline_type
+find_one_outline (edge_type original_edge,
+ unsigned original_row, unsigned original_col,
+ bitmap_type *marked)
+{
+ pixel_outline_type outline = new_pixel_outline ();
+ unsigned row = original_row, col = original_col;
+ edge_type edge = original_edge;
+
+ do
+ {
+ /* Put this edge on to the output list, changing to Cartesian, and
+ taking account of the side bearings. */
+ append_coordinate (&outline, col,
+ sel_get_height() - row, edge);
+
+ mark_edge (edge, row, col, marked);
+ next_outline_edge (&edge, &row, &col);
+ }
+ while (row != original_row || col != original_col || edge != original_edge);
+
+ return outline;
+}
+
+
+/* Append an outline to an outline list. This is called when we have
+ completed an entire pixel outline. */
+
+static void
+append_pixel_outline (pixel_outline_list_type *outline_list,
+ pixel_outline_type outline)
+{
+ O_LIST_LENGTH (*outline_list)++;
+ outline_list->data = (pixel_outline_type *)g_realloc(outline_list->data,outline_list->length *sizeof(pixel_outline_type));
+ O_LIST_OUTLINE (*outline_list, O_LIST_LENGTH (*outline_list) - 1) = outline;
+}
+
+
+/* Here is a routine that frees a list of such lists. */
+
+void
+free_pixel_outline_list (pixel_outline_list_type *outline_list)
+{
+ unsigned this_outline;
+
+ for (this_outline = 0; this_outline < outline_list->length; this_outline++)
+ {
+ pixel_outline_type o = outline_list->data[this_outline];
+ safe_free ((address *) &(o.data));
+ }
+
+ if (outline_list->data != NULL)
+ safe_free ((address *) &(outline_list->data));
+}
+
+
+/* Return an empty list of pixels. */
+
+
+static pixel_outline_type
+new_pixel_outline (void)
+{
+ pixel_outline_type pixel_outline;
+
+ O_LENGTH (pixel_outline) = 0;
+ pixel_outline.data = NULL;
+
+ return pixel_outline;
+}
+
+
+/* Add the coordinate C to the pixel list O. */
+
+static void
+append_outline_pixel (pixel_outline_type *o, coordinate_type c)
+{
+ O_LENGTH (*o)++;
+ o->data = (coordinate_type *)g_realloc(o->data, O_LENGTH (*o)*sizeof(coordinate_type));
+ O_COORDINATE (*o, O_LENGTH (*o) - 1) = c;
+}
+
+
+/* We are given an (X,Y) in Cartesian coordinates, and the edge of the pixel
+ we're on. We append a corner of that pixel as our coordinate.
+ If we're on a top edge, we use the upper-left hand corner; right edge
+ => upper right; bottom edge => lower right; left edge => lower left. */
+
+static void
+append_coordinate (pixel_outline_type *o, int x, int y, edge_type edge)
+{
+ coordinate_type c;
+
+ c.x = x;
+ c.y = y;
+
+ switch (edge)
+ {
+ case top:
+ c.y++;
+ break;
+
+ case right:
+ c.x++;
+ c.y++;
+ break;
+
+ case bottom:
+ c.x++;
+ break;
+
+ case left:
+ break;
+
+ default:
+ g_printerr ("append_coordinate: Bad edge (%d)", edge);
+ }
+
+ append_outline_pixel (o, c);
+}
diff --git a/plug-ins/selection-to-path/pxl-outline.h b/plug-ins/selection-to-path/pxl-outline.h
new file mode 100644
index 0000000..896722f
--- /dev/null
+++ b/plug-ins/selection-to-path/pxl-outline.h
@@ -0,0 +1,68 @@
+/* pxl-outline.h: find a list of outlines which make up one character.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef PXL_OUTLINE_H
+#define PXL_OUTLINE_H
+
+#include "types.h"
+
+
+/* This is a list of contiguous points on the bitmap. */
+typedef struct
+{
+ coordinate_type *data;
+ unsigned length;
+ boolean clockwise;
+} pixel_outline_type;
+
+/* The Nth coordinate in the list. */
+#define O_COORDINATE(p_o, n) ((p_o).data[n])
+
+/* The length of the list. */
+#define O_LENGTH(p_o) ((p_o).length)
+
+/* Whether the outline moves clockwise or counterclockwise. */
+#define O_CLOCKWISE(p_o) ((p_o).clockwise)
+
+/* Since a pixel outline is cyclic, the index of the next coordinate
+ after the last is the first, and the previous coordinate before the
+ first is the last. */
+#define O_NEXT(p_o, n) (((n) + 1) % O_LENGTH (p_o))
+#define O_PREV(p_o, n) ((n) == 0 ? O_LENGTH (p_o) - 1 : (n) - 1)
+
+/* And the character turns into a list of such lists. */
+typedef struct
+{
+ pixel_outline_type *data;
+ unsigned length;
+} pixel_outline_list_type;
+
+/* The Nth list in the list of lists. */
+#define O_LIST_OUTLINE(p_o_l, n) ((p_o_l).data[n])
+
+/* The length of the list of lists. */
+#define O_LIST_LENGTH(p_o_l) ((p_o_l).length)
+
+
+/* Find all pixels on the outline in the character C. */
+extern pixel_outline_list_type find_outline_pixels (void);
+
+/* Free the memory in the list. */
+extern void free_pixel_outline_list (pixel_outline_list_type *);
+
+#endif /* not PXL_OUTLINE_H */
diff --git a/plug-ins/selection-to-path/selection-to-path-dialog.c b/plug-ins/selection-to-path/selection-to-path-dialog.c
new file mode 100644
index 0000000..f6f3f18
--- /dev/null
+++ b/plug-ins/selection-to-path/selection-to-path-dialog.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Plugin to convert a selection to a path.
+ *
+ * Copyright (C) 1999 Andy Thomas alt@gimp.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Change log:-
+ * 0.1 First version.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "types.h"
+
+#include "selection-to-path.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+#define SCALE_WIDTH 100
+#define SCALE_DIGITS 8
+
+
+static GSList * adjust_widgets = NULL;
+
+
+/* Reset to recommended defaults */
+void
+reset_adv_dialog (void)
+{
+ GSList *list;
+ GtkObject *widget;
+ gdouble *value;
+
+ for (list = adjust_widgets; list; list = g_slist_next (list))
+ {
+ widget = GTK_OBJECT (list->data);
+ value = (gdouble *) g_object_get_data (G_OBJECT (widget),
+ "default_value");
+
+ if (GTK_IS_ADJUSTMENT (widget))
+ {
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (widget),
+ *value);
+ }
+ else if (GTK_IS_TOGGLE_BUTTON (widget))
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
+ (gboolean)(*value));
+ }
+ else
+ g_warning ("Internal widget list error");
+ }
+}
+
+static gpointer
+def_val (gdouble default_value)
+{
+ gdouble *value = g_new0 (gdouble, 1);
+ *value = default_value;
+ return (value);
+}
+
+GtkWidget *
+dialog_create_selection_area (SELVALS *sels)
+{
+ GtkWidget *scrolled_win;
+ GtkWidget *viewport;
+ GtkWidget *table;
+ GtkWidget *check;
+ GtkObject *adj;
+ gint row;
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request (scrolled_win, -1, 400);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_ALWAYS);
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
+ gtk_container_add (GTK_CONTAINER (scrolled_win), viewport);
+ gtk_widget_show (viewport);
+
+ table = gtk_table_new (20, 3, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (viewport), table);
+ gtk_widget_show (table);
+
+ row = 0;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Align Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->align_threshold,
+ 0.2, 2.0, 0.1, 0.1, 2,
+ TRUE, 0, 0,
+ _("If two endpoints are closer than this, "
+ "they are made to be equal."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->align_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.5));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Corner Always Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->corner_always_threshold,
+ 30, 180, 1, 1, 2,
+ TRUE, 0, 0,
+ _("If the angle defined by a point and its predecessors "
+ "and successors is smaller than this, it's a corner, "
+ "even if it's within 'corner_surround' pixels of a "
+ "point with a smaller angle."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->corner_always_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (60.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Corner Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->corner_surround,
+ 3, 8, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of points to consider when determining if a "
+ "point is a corner or not."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->corner_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Corner Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->corner_threshold,
+ 0, 180, 1, 1, 2,
+ TRUE, 0, 0,
+ _("If a point, its predecessors, and its successors "
+ "define an angle smaller than this, it's a corner."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->corner_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (100.0));
+
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Error Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->error_threshold,
+ 0.2, 10, 0.1, 0.1, 2,
+ TRUE, 0, 0,
+ _("Amount of error at which a fitted spline is "
+ "unacceptable. If any pixel is further away "
+ "than this from the fitted curve, we try again."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->error_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.40));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Alternative Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_alternative_surround,
+ 1, 10, 1, 1, 0,
+ TRUE, 0, 0,
+ _("A second number of adjacent points to consider "
+ "when filtering."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_alternative_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (1.0));
+
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Epsilon:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_epsilon,
+ 5, 40, 1, 1, 2,
+ TRUE, 0, 0,
+ _("If the angles between the vectors produced by "
+ "filter_surround and filter_alternative_surround "
+ "points differ by more than this, use the one from "
+ "filter_alternative_surround."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_epsilon);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (10.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Iteration Count:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_iteration_count,
+ 4, 70, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of times to smooth original data points. "
+ "Increasing this number dramatically --- to 50 or "
+ "so --- can produce vastly better results. But if "
+ "any points that 'should' be corners aren't found, "
+ "the curve goes to hell around that point."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_iteration_count);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Percent:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_percent,
+ 0, 1, 0.05, 0.01, 2,
+ TRUE, 0, 0,
+ _("To produce the new point, use the old point plus "
+ "this times the neighbors."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_percent);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.33));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Secondary Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_secondary_surround,
+ 3, 10, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of adjacent points to consider if "
+ "'filter_surround' points defines a straight line."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_secondary_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (3.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Filter Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->filter_surround,
+ 2, 10, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of adjacent points to consider when filtering."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->filter_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (2.0));
+
+ check = gtk_check_button_new_with_label (_("Keep Knees"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check), sels->keep_knees);
+ gtk_table_attach (GTK_TABLE (table), check, 1, 3, row, row + 1,
+ GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gimp_help_set_help_data (GTK_WIDGET (check),
+ _("Says whether or not to remove 'knee' "
+ "points after finding the outline."), NULL);
+ g_signal_connect (check, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &sels->keep_knees);
+ gtk_widget_show (check);
+ adjust_widgets = g_slist_append (adjust_widgets, check);
+ g_object_set_data (G_OBJECT (check), "default_value", def_val ((gdouble)FALSE));
+ row++;
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Line Reversion Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->line_reversion_threshold,
+ 0.01, 0.2, 0.01, 0.01, 3,
+ TRUE, 0, 0,
+ _("If a spline is closer to a straight line than this, "
+ "it remains a straight line, even if it would otherwise "
+ "be changed back to a curve. This is weighted by the "
+ "square of the curve length, to make shorter curves "
+ "more likely to be reverted."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->line_reversion_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.01));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Line Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->line_threshold,
+ 0.2, 4, 0.1, 0.01, 2,
+ TRUE, 0, 0,
+ _("How many pixels (on the average) a spline can "
+ "diverge from the line determined by its endpoints "
+ "before it is changed to a straight line."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->line_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.5));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Reparametrize Improvement:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->reparameterize_improvement,
+ 0, 1, 0.05, 0.01, 2,
+ TRUE, 0, 0,
+ _("If reparameterization doesn't improve the fit by this "
+ "much percent, stop doing it. ""Amount of error at which "
+ "it is pointless to reparameterize."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->reparameterize_improvement);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.01));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Reparametrize Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->reparameterize_threshold,
+ 1, 50, 0.5, 0.5, 2,
+ TRUE, 0, 0,
+ _("Amount of error at which it is pointless to reparameterize. "
+ "This happens, for example, when we are trying to fit the "
+ "outline of the outside of an 'O' with a single spline. "
+ "The initial fit is not good enough for the Newton-Raphson "
+ "iteration to improve it. It may be that it would be better "
+ "to detect the cases where we didn't find any corners."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->reparameterize_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (1.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Subdivide Search:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->subdivide_search,
+ 0.05, 1, 0.05, 0.01, 2,
+ TRUE, 0, 0,
+ _("Percentage of the curve away from the worst point "
+ "to look for a better place to subdivide."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->subdivide_search);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.1));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Subdivide Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->subdivide_surround,
+ 2, 10, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of points to consider when deciding whether "
+ "a given point is a better place to subdivide."),
+ NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->subdivide_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (4.0));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Subdivide Threshold:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->subdivide_threshold,
+ 0.01, 1, 0.01, 0.01, 2,
+ TRUE, 0, 0,
+ _("How many pixels a point can diverge from a straight "
+ "line and still be considered a better place to "
+ "subdivide."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->subdivide_threshold);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (0.03));
+
+ adj = gimp_scale_entry_new (GTK_TABLE (table), 0, row++,
+ _("Tangent Surround:"), SCALE_WIDTH, SCALE_DIGITS,
+ sels->tangent_surround,
+ 2, 10, 1, 1, 0,
+ TRUE, 0, 0,
+ _("Number of points to look at on either side of a "
+ "point when computing the approximation to the "
+ "tangent at that point."), NULL);
+ g_signal_connect (adj, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &sels->tangent_surround);
+ adjust_widgets = g_slist_append (adjust_widgets, adj);
+ g_object_set_data (G_OBJECT (adj), "default_value", def_val (3.0));
+
+ return scrolled_win;
+}
diff --git a/plug-ins/selection-to-path/selection-to-path.c b/plug-ins/selection-to-path/selection-to-path.c
new file mode 100644
index 0000000..52fc849
--- /dev/null
+++ b/plug-ins/selection-to-path/selection-to-path.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Plugin to convert a selection to a path.
+ *
+ * Copyright (C) 1999 Andy Thomas alt@gimp.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Change log:-
+ * 0.1 First version.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "libgimp/gimp.h"
+#include "libgimp/gimpui.h"
+
+#include "libgimpmath/gimpmath.h"
+
+#include "global.h"
+#include "types.h"
+#include "pxl-outline.h"
+#include "fit.h"
+#include "spline.h"
+#include "selection-to-path.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_BINARY "selection-to-path"
+#define PLUG_IN_ROLE "gimp-selection-to-path"
+
+#define RESPONSE_RESET 1
+#define MID_POINT 127
+
+/***** Magic numbers *****/
+
+/* Variables set in dialog box */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+static gint sel2path_dialog (SELVALS *sels);
+static void sel2path_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+static void dialog_print_selVals (SELVALS *sels);
+static gboolean sel2path (gint32 image_ID);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static gint sel_x1, sel_y1, sel_x2, sel_y2;
+static gint has_sel, sel_width, sel_height;
+static SELVALS selVals;
+static GeglSampler *sel_sampler;
+static gboolean retVal = TRUE; /* Toggle if cancel button clicked */
+
+MAIN ()
+
+static void
+query (void)
+{
+ static const GimpParamDef args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" },
+ };
+
+ static const GimpParamDef advanced_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_IMAGE, "image", "Input image" },
+ { GIMP_PDB_DRAWABLE, "drawable", "Input drawable (unused)" },
+ { GIMP_PDB_FLOAT, "align-threshold", "align_threshold"},
+ { GIMP_PDB_FLOAT, "corner-always-threshold", "corner_always_threshold"},
+ { GIMP_PDB_INT8, "corner-surround", "corner_surround"},
+ { GIMP_PDB_FLOAT, "corner-threshold", "corner_threshold"},
+ { GIMP_PDB_FLOAT, "error-threshold", "error_threshold"},
+ { GIMP_PDB_INT8, "filter-alternative-surround", "filter_alternative_surround"},
+ { GIMP_PDB_FLOAT, "filter-epsilon", "filter_epsilon"},
+ { GIMP_PDB_INT8, "filter-iteration-count", "filter_iteration_count"},
+ { GIMP_PDB_FLOAT, "filter-percent", "filter_percent"},
+ { GIMP_PDB_INT8, "filter-secondary-surround", "filter_secondary_surround"},
+ { GIMP_PDB_INT8, "filter-surround", "filter_surround"},
+ { GIMP_PDB_INT8, "keep-knees", "{1-Yes, 0-No}"},
+ { GIMP_PDB_FLOAT, "line-reversion-threshold", "line_reversion_threshold"},
+ { GIMP_PDB_FLOAT, "line-threshold", "line_threshold"},
+ { GIMP_PDB_FLOAT, "reparameterize-improvement", "reparameterize_improvement"},
+ { GIMP_PDB_FLOAT, "reparameterize-threshold", "reparameterize_threshold"},
+ { GIMP_PDB_FLOAT, "subdivide-search", "subdivide_search"},
+ { GIMP_PDB_INT8, "subdivide-surround", "subdivide_surround"},
+ { GIMP_PDB_FLOAT, "subdivide-threshold", "subdivide_threshold"},
+ { GIMP_PDB_INT8, "tangent-surround", "tangent_surround"},
+ };
+
+ gimp_install_procedure ("plug-in-sel2path",
+ "Converts a selection to a path",
+ "Converts a selection to a path",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1999",
+ NULL,
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (args), 0,
+ args, NULL);
+
+ gimp_install_procedure ("plug-in-sel2path-advanced",
+ "Converts a selection to a path (with advanced user menu)",
+ "Converts a selection to a path (with advanced user menu)",
+ "Andy Thomas",
+ "Andy Thomas",
+ "1999",
+ NULL,
+ "RGB*, INDEXED*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (advanced_args), 0,
+ advanced_args, NULL);
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[1];
+ gint32 image_ID;
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gboolean no_dialog;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ no_dialog = (strcmp (name, "plug-in-sel2path") == 0);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = status;
+
+ image_ID = param[1].data.d_image;
+ if (image_ID < 0)
+ {
+ g_warning ("plug-in-sel2path needs a valid image ID");
+ return;
+ }
+
+ if (gimp_selection_is_empty (image_ID))
+ {
+ g_message (_("No selection to convert"));
+ return;
+ }
+
+ fit_set_default_params (&selVals);
+
+ if (!no_dialog)
+ {
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ if (gimp_get_data_size ("plug-in-sel2path-advanced") > 0)
+ {
+ gimp_get_data ("plug-in-sel2path-advanced", &selVals);
+ }
+
+ if (!sel2path_dialog (&selVals))
+ return;
+
+ /* Get the current settings */
+ fit_set_params (&selVals);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ if (nparams != 23)
+ status = GIMP_PDB_CALLING_ERROR;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ selVals.align_threshold = param[3].data.d_float;
+ selVals.corner_always_threshold = param[4].data.d_float;
+ selVals.corner_surround = param[5].data.d_int8;
+ selVals.corner_threshold = param[6].data.d_float;
+ selVals.error_threshold = param[7].data.d_float;
+ selVals.filter_alternative_surround = param[8].data.d_int8;
+ selVals.filter_epsilon = param[9].data.d_float;
+ selVals.filter_iteration_count = param[10].data.d_int8;
+ selVals.filter_percent = param[11].data.d_float;
+ selVals.filter_secondary_surround = param[12].data.d_int8;
+ selVals.filter_surround = param[13].data.d_int8;
+ selVals.keep_knees = param[14].data.d_int8;
+ selVals.line_reversion_threshold = param[15].data.d_float;
+ selVals.line_threshold = param[16].data.d_float;
+ selVals.reparameterize_improvement = param[17].data.d_float;
+ selVals.reparameterize_threshold = param[18].data.d_float;
+ selVals.subdivide_search = param[19].data.d_float;
+ selVals.subdivide_surround = param[20].data.d_int8;
+ selVals.subdivide_threshold = param[21].data.d_float;
+ selVals.tangent_surround = param[22].data.d_int8;
+ fit_set_params (&selVals);
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ if(gimp_get_data_size ("plug-in-sel2path-advanced") > 0)
+ {
+ gimp_get_data ("plug-in-sel2path-advanced", &selVals);
+
+ /* Set up the last values */
+ fit_set_params (&selVals);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ sel2path (image_ID);
+ values[0].data.d_status = status;
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ dialog_print_selVals(&selVals);
+ if (run_mode == GIMP_RUN_INTERACTIVE && !no_dialog)
+ gimp_set_data ("plug-in-sel2path-advanced", &selVals, sizeof(SELVALS));
+ }
+}
+
+static void
+dialog_print_selVals (SELVALS *sels)
+{
+#if 0
+ printf ("selVals.align_threshold %g\n", selVals.align_threshold);
+ printf ("selVals.corner_always_threshol %g\n", selVals.corner_always_threshold);
+ printf ("selVals.corner_surround %g\n", selVals.corner_surround);
+ printf ("selVals.corner_threshold %g\n", selVals.corner_threshold);
+ printf ("selVals.error_threshold %g\n", selVals.error_threshold);
+ printf ("selVals.filter_alternative_surround %g\n", selVals.filter_alternative_surround);
+ printf ("selVals.filter_epsilon %g\n", selVals.filter_epsilon);
+ printf ("selVals.filter_iteration_count %g\n", selVals.filter_iteration_count);
+ printf ("selVals.filter_percent %g\n", selVals.filter_percent);
+ printf ("selVals.filter_secondary_surround %g\n", selVals.filter_secondary_surround);
+ printf ("selVals.filter_surround %g\n", selVals.filter_surround);
+ printf ("selVals.keep_knees %d\n", selVals.keep_knees);
+ printf ("selVals.line_reversion_threshold %g\n", selVals.line_reversion_threshold);
+ printf ("selVals.line_threshold %g\n", selVals.line_threshold);
+ printf ("selVals.reparameterize_improvement %g\n", selVals.reparameterize_improvement);
+ printf ("selVals.reparameterize_threshold %g\n", selVals.reparameterize_threshold);
+ printf ("selVals.subdivide_search %g\n" selVals.subdivide_search);
+ printf ("selVals.subdivide_surround %g\n", selVals.subdivide_surround);
+ printf ("selVals.subdivide_threshold %g\n", selVals.subdivide_threshold);
+ printf ("selVals.tangent_surround %g\n", selVals.tangent_surround);
+#endif /* 0 */
+}
+
+/* Build the dialog up. This was the hard part! */
+static gint
+sel2path_dialog (SELVALS *sels)
+{
+ GtkWidget *dlg;
+ GtkWidget *table;
+
+ retVal = FALSE;
+
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ dlg = gimp_dialog_new (_("Selection to Path Advanced Settings"),
+ PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, "plug-in-sel2path-advanced",
+
+ _("_Reset"), RESPONSE_RESET,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dlg),
+ RESPONSE_RESET,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dlg));
+
+ g_signal_connect (dlg, "response",
+ G_CALLBACK (sel2path_response),
+ NULL);
+ g_signal_connect (dlg, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ table = dialog_create_selection_area (sels);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
+ table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ gtk_widget_show (dlg);
+
+ gtk_main ();
+
+ return retVal;
+}
+
+static void
+sel2path_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ switch (response_id)
+ {
+ case RESPONSE_RESET:
+ reset_adv_dialog ();
+ fit_set_params (&selVals);
+ break;
+
+ case GTK_RESPONSE_OK:
+ retVal = TRUE;
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+guchar
+sel_pixel_value (gint row,
+ gint col)
+{
+ guchar ret;
+
+ if (col > sel_width || row > sel_height)
+ {
+ g_warning ("sel_pixel_value [%d,%d] out of bounds", col, row);
+ return 0;
+ }
+
+ gegl_sampler_get (sel_sampler,
+ col + sel_x1, row + sel_y1, NULL, &ret, GEGL_ABYSS_NONE);
+
+ return ret;
+}
+
+gboolean
+sel_pixel_is_white (gint row,
+ gint col)
+{
+ if (sel_pixel_value (row, col) < MID_POINT)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gint
+sel_get_width (void)
+{
+ return sel_width;
+}
+
+gint
+sel_get_height (void)
+{
+ return sel_height;
+}
+
+gboolean
+sel_valid_pixel (gint row,
+ gint col)
+{
+ return (0 <= (row) && (row) < sel_get_height ()
+ && 0 <= (col) && (col) < sel_get_width ());
+}
+
+
+static void
+do_points (spline_list_array_type in_splines,
+ gint32 image_ID)
+{
+ gint32 vectors;
+ gint32 stroke;
+ gint i, j;
+ gboolean have_points = FALSE;
+ spline_list_type spline_list;
+
+ /* check if there really is something to do... */
+ for (i = 0; i < SPLINE_LIST_ARRAY_LENGTH (in_splines); i++)
+ {
+ spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, i);
+ /* Ignore single points that are on their own */
+ if (SPLINE_LIST_LENGTH (spline_list) < 2)
+ continue;
+ have_points = TRUE;
+ break;
+ }
+
+ if (!have_points)
+ return;
+
+ vectors = gimp_vectors_new (image_ID, _("Selection"));
+
+ for (j = 0; j < SPLINE_LIST_ARRAY_LENGTH (in_splines); j++)
+ {
+ spline_type seg;
+
+ spline_list = SPLINE_LIST_ARRAY_ELT (in_splines, j);
+
+ /* Ignore single points that are on their own */
+ if (SPLINE_LIST_LENGTH (spline_list) < 2)
+ continue;
+
+ /*
+ * we're constructing the path backwards
+ * to have the result of least surprise for "Text along Path".
+ */
+ seg = SPLINE_LIST_ELT (spline_list, SPLINE_LIST_LENGTH (spline_list) - 1);
+ stroke = gimp_vectors_bezier_stroke_new_moveto (vectors,
+ END_POINT (seg).x,
+ END_POINT (seg).y);
+
+ for (i = SPLINE_LIST_LENGTH (spline_list); i > 0; i--)
+ {
+ seg = SPLINE_LIST_ELT (spline_list, i-1);
+
+ if (SPLINE_DEGREE (seg) == LINEAR)
+ gimp_vectors_bezier_stroke_lineto (vectors, stroke,
+ START_POINT (seg).x,
+ START_POINT (seg).y);
+ else if (SPLINE_DEGREE (seg) == CUBIC)
+ gimp_vectors_bezier_stroke_cubicto (vectors, stroke,
+ CONTROL2 (seg).x,
+ CONTROL2 (seg).y,
+ CONTROL1 (seg).x,
+ CONTROL1 (seg).y,
+ START_POINT (seg).x,
+ START_POINT (seg).y);
+ else
+ g_warning ("print_spline: strange degree (%d)",
+ SPLINE_DEGREE (seg));
+ }
+
+ gimp_vectors_stroke_close (vectors, stroke);
+
+ /* transform to GIMPs coordinate system, taking the selections
+ * bounding box into account */
+ gimp_vectors_stroke_scale (vectors, stroke, 1.0, -1.0);
+ gimp_vectors_stroke_translate (vectors, stroke,
+ sel_x1, sel_y1 + sel_height + 1);
+ }
+
+ gimp_image_insert_vectors (image_ID, vectors, -1, -1);
+}
+
+
+static gboolean
+sel2path (gint32 image_ID)
+{
+ gint32 selection_ID;
+ GeglBuffer *sel_buffer;
+ pixel_outline_list_type olt;
+ spline_list_array_type splines;
+
+ gimp_selection_bounds (image_ID, &has_sel,
+ &sel_x1, &sel_y1, &sel_x2, &sel_y2);
+
+ sel_width = sel_x2 - sel_x1;
+ sel_height = sel_y2 - sel_y1;
+
+ /* Now get the selection channel */
+
+ selection_ID = gimp_image_get_selection (image_ID);
+
+ if (selection_ID < 0)
+ return FALSE;
+
+ sel_buffer = gimp_drawable_get_buffer (selection_ID);
+ sel_sampler = gegl_buffer_sampler_new (sel_buffer,
+ babl_format ("Y u8"),
+ GEGL_SAMPLER_NEAREST);
+
+ olt = find_outline_pixels ();
+
+ splines = fitted_splines (olt);
+
+ do_points (splines, image_ID);
+
+ g_object_unref (sel_sampler);
+ g_object_unref (sel_buffer);
+
+ gimp_displays_flush ();
+
+ return TRUE;
+}
+
+void
+safe_free (address *item)
+{
+ g_free (*item);
+ *item = NULL;
+}
diff --git a/plug-ins/selection-to-path/selection-to-path.h b/plug-ins/selection-to-path/selection-to-path.h
new file mode 100644
index 0000000..d798492
--- /dev/null
+++ b/plug-ins/selection-to-path/selection-to-path.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This is a plug-in for GIMP.
+ *
+ * Plugin to convert a selection to a path.
+ *
+ * Copyright (C) 1999 Andy Thomas alt@gimp.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "gtk/gtk.h"
+#include "libgimp/gimp.h"
+
+guchar sel_pixel_value (gint, gint);
+gint sel_pixel_is_white (gint, gint);
+gint sel_get_width (void);
+gint sel_get_height (void);
+gboolean sel_valid_pixel (gint, gint);
+void reset_adv_dialog (void);
+
+
+GtkWidget * dialog_create_selection_area(SELVALS *);
+
diff --git a/plug-ins/selection-to-path/spline.c b/plug-ins/selection-to-path/spline.c
new file mode 100644
index 0000000..1e8c2e7
--- /dev/null
+++ b/plug-ins/selection-to-path/spline.c
@@ -0,0 +1,233 @@
+/* spline.c: spline and spline list (represented as arrays) manipulation.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+
+#include <glib.h>
+
+#include "global.h"
+#include "bounding-box.h"
+#include "spline.h"
+#include "vector.h"
+
+/* Return a new spline structure, initialized with (recognizable)
+ garbage. */
+
+spline_type
+new_spline (void)
+{
+ real_coordinate_type coord = { -100.0, -100.0 };
+ spline_type spline;
+
+ START_POINT (spline)
+ = CONTROL1 (spline)
+ = CONTROL2 (spline)
+ = END_POINT (spline)
+ = coord;
+ SPLINE_DEGREE (spline) = -1;
+ SPLINE_LINEARITY (spline) = 0;
+
+ return spline;
+}
+
+
+/* Print a spline in human-readable form. */
+
+void
+print_spline (FILE *f, spline_type s)
+{
+ if (SPLINE_DEGREE (s) == LINEAR)
+ fprintf (f, "(%.3f,%.3f)--(%.3f,%.3f).\n",
+ START_POINT (s).x, START_POINT (s).y,
+ END_POINT (s).x, END_POINT (s).y);
+
+ else if (SPLINE_DEGREE (s) == CUBIC)
+ fprintf (f, "(%.3f,%.3f)..ctrls(%.3f,%.3f)&(%.3f,%.3f)..(%.3f,%.3f).\n",
+ START_POINT (s).x, START_POINT (s).y,
+ CONTROL1 (s).x, CONTROL1 (s).y,
+ CONTROL2 (s).x, CONTROL2 (s).y,
+ END_POINT (s).x, END_POINT (s).y);
+
+ else
+ {
+/* FATAL1 ("print_spline: strange degree (%d)", SPLINE_DEGREE (s)); */
+ }
+}
+
+/* Evaluate the spline S at a given T value. This is an implementation
+ of de Casteljau's algorithm. See Schneider's thesis (reference in
+ ../limn/README), p.37. The variable names are taken from there. */
+
+real_coordinate_type
+evaluate_spline (spline_type s, real t)
+{
+ spline_type V[4]; /* We need degree+1 splines, but assert degree <= 3. */
+ unsigned i, j;
+ real one_minus_t = 1.0 - t;
+ polynomial_degree degree = SPLINE_DEGREE (s);
+
+ for (i = 0; i <= degree; i++)
+ V[0].v[i] = s.v[i];
+
+ for (j = 1; j <= degree; j++)
+ for (i = 0; i <= degree - j; i++)
+ {
+#if defined (__GNUC__)
+ real_coordinate_type t1 = Pmult_scalar (V[j - 1].v[i], one_minus_t);
+ real_coordinate_type t2 = Pmult_scalar (V[j - 1].v[i + 1], t);
+ V[j].v[i] = Padd (t1, t2);
+#else
+ /* HB: the above is really nice, but is there any other compiler
+ * supporting this ??
+ */
+ real_coordinate_type t1;
+ real_coordinate_type t2;
+ t1.x = V[j - 1].v[i].x * one_minus_t;
+ t1.y = V[j - 1].v[i].y * one_minus_t;
+ t2.x = V[j - 1].v[i + 1].x * t;
+ t2.y = V[j - 1].v[i + 1].y * t;
+ V[j].v[i].x = t1.x + t2.x;
+ V[j].v[i].y = t1.y + t2.y;
+#endif
+ }
+
+ return V[degree].v[0];
+}
+
+
+
+/* Return a new, empty, spline list. */
+
+spline_list_type *
+new_spline_list (void)
+{
+ spline_list_type *answer = g_new (spline_list_type, 1);
+
+ SPLINE_LIST_DATA (*answer) = NULL;
+ SPLINE_LIST_LENGTH (*answer) = 0;
+
+ return answer;
+}
+
+
+/* Return a new spline list with SPLINE as the first element. */
+
+spline_list_type *
+init_spline_list (spline_type spline)
+{
+ spline_list_type *answer = g_new (spline_list_type, 1);
+
+ SPLINE_LIST_DATA (*answer) = g_new (spline_type, 1);
+ SPLINE_LIST_ELT (*answer, 0) = spline;
+ SPLINE_LIST_LENGTH (*answer) = 1;
+
+ return answer;
+}
+
+
+/* Free the storage in a spline list. We don't have to free the
+ elements, since they are arrays in automatic storage. And we don't
+ want to free the list if it was empty. */
+
+void
+free_spline_list (spline_list_type *spline_list)
+{
+ if (SPLINE_LIST_DATA (*spline_list) != NULL)
+ safe_free ((address *) &(SPLINE_LIST_DATA (*spline_list)));
+}
+
+
+/* Append the spline S to the list SPLINE_LIST. */
+
+void
+append_spline (spline_list_type *l, spline_type s)
+{
+ assert (l != NULL);
+
+ SPLINE_LIST_LENGTH (*l)++;
+ SPLINE_LIST_DATA (*l) = g_realloc (SPLINE_LIST_DATA (*l),
+ SPLINE_LIST_LENGTH (*l) * sizeof (spline_type));
+ LAST_SPLINE_LIST_ELT (*l) = s;
+}
+
+
+/* Tack the elements in the list S2 onto the end of S1.
+ S2 is not changed. */
+
+void
+concat_spline_lists (spline_list_type *s1, spline_list_type s2)
+{
+ unsigned this_spline;
+ unsigned new_length;
+
+ assert (s1 != NULL);
+
+ new_length = SPLINE_LIST_LENGTH (*s1) + SPLINE_LIST_LENGTH (s2);
+
+ SPLINE_LIST_DATA (*s1) = g_realloc(SPLINE_LIST_DATA (*s1),new_length * sizeof(spline_type));
+
+ for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH (s2); this_spline++)
+ SPLINE_LIST_ELT (*s1, SPLINE_LIST_LENGTH (*s1)++)
+ = SPLINE_LIST_ELT (s2, this_spline);
+}
+
+
+/* Return a new, empty, spline list array. */
+
+spline_list_array_type
+new_spline_list_array (void)
+{
+ spline_list_array_type answer;
+
+ SPLINE_LIST_ARRAY_DATA (answer) = NULL;
+ SPLINE_LIST_ARRAY_LENGTH (answer) = 0;
+
+ return answer;
+}
+
+
+/* Free the storage in a spline list array. We don't
+ want to free the list if it is empty. */
+
+void
+free_spline_list_array (spline_list_array_type *spline_list_array)
+{
+ unsigned this_list;
+
+ for (this_list = 0;
+ this_list < SPLINE_LIST_ARRAY_LENGTH (*spline_list_array);
+ this_list++)
+ free_spline_list (&SPLINE_LIST_ARRAY_ELT (*spline_list_array, this_list));
+
+ if (SPLINE_LIST_ARRAY_DATA (*spline_list_array) != NULL)
+ safe_free ((address *) &(SPLINE_LIST_ARRAY_DATA (*spline_list_array)));
+}
+
+
+/* Append the spline S to the list SPLINE_LIST_ARRAY. */
+
+void
+append_spline_list (spline_list_array_type *l, spline_list_type s)
+{
+ SPLINE_LIST_ARRAY_LENGTH (*l)++;
+
+ SPLINE_LIST_ARRAY_DATA (*l) = g_realloc(SPLINE_LIST_ARRAY_DATA (*l),(SPLINE_LIST_ARRAY_LENGTH (*l))*sizeof(spline_list_type));
+ LAST_SPLINE_LIST_ARRAY_ELT (*l) = s;
+}
diff --git a/plug-ins/selection-to-path/spline.h b/plug-ins/selection-to-path/spline.h
new file mode 100644
index 0000000..3daa9cc
--- /dev/null
+++ b/plug-ins/selection-to-path/spline.h
@@ -0,0 +1,124 @@
+/* spline.h: manipulate the spline representation.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPLINE_H
+#define SPLINE_H
+
+#include <stdio.h>
+#include "bounding-box.h"
+#include "types.h"
+
+
+/* Third degree is the highest we deal with. */
+typedef enum
+{
+ LINEAR = 1, QUADRATIC = 2, CUBIC = 3
+} polynomial_degree;
+
+
+/* A Bezier spline can be represented as four points in the real plane:
+ a starting point, ending point, and two control points. The
+ curve always lies in the convex hull defined by the four points. It
+ is also convenient to save the divergence of the spline from the
+ straight line defined by the endpoints. */
+typedef struct
+{
+ real_coordinate_type v[4]; /* The control points. */
+ polynomial_degree degree;
+ real linearity;
+} spline_type;
+
+#define START_POINT(spl) ((spl).v[0])
+#define CONTROL1(spl) ((spl).v[1])
+#define CONTROL2(spl) ((spl).v[2])
+#define END_POINT(spl) ((spl).v[3])
+#define SPLINE_DEGREE(spl) ((spl).degree)
+#define SPLINE_LINEARITY(spl) ((spl).linearity)
+
+
+/* Return a spline structure. */
+extern spline_type new_spline (void);
+
+/* Print a spline on the given file. */
+extern void print_spline (FILE *, spline_type);
+
+/* Evaluate SPLINE at the given T value. */
+extern real_coordinate_type evaluate_spline (spline_type spline, real t);
+
+/* Each outline in a character is typically represented by many
+ splines. So, here is a list structure for that: */
+typedef struct
+{
+ spline_type *data;
+ unsigned length;
+} spline_list_type;
+
+/* An empty list will have length zero (and null data). */
+#define SPLINE_LIST_LENGTH(s_l) ((s_l).length)
+
+/* The address of the beginning of the array of data. */
+#define SPLINE_LIST_DATA(s_l) ((s_l).data)
+
+/* The element INDEX in S_L. */
+#define SPLINE_LIST_ELT(s_l, index) (SPLINE_LIST_DATA (s_l)[index])
+
+/* The last element in S_L. */
+#define LAST_SPLINE_LIST_ELT(s_l) \
+ (SPLINE_LIST_DATA (s_l)[SPLINE_LIST_LENGTH (s_l) - 1])
+
+/* The previous and next elements to INDEX in S_L. */
+#define NEXT_SPLINE_LIST_ELT(s_l, index) \
+ SPLINE_LIST_ELT (s_l, ((index) + 1) % SPLINE_LIST_LENGTH (s_l))
+#define PREV_SPLINE_LIST_ELT(s_l, index) \
+ SPLINE_LIST_ELT (s_l, index == 0 \
+ ? SPLINE_LIST_LENGTH (s_l) - 1 \
+ : index - 1)
+
+/* Construct and destroy new `spline_list_type' objects. */
+extern spline_list_type *new_spline_list (void);
+extern spline_list_type *init_spline_list (spline_type);
+extern void free_spline_list (spline_list_type *);
+
+/* Append the spline S to the list S_LIST. */
+extern void append_spline (spline_list_type *s_list, spline_type s);
+
+/* Append the elements in list S2 to S1, changing S1. */
+extern void concat_spline_lists (spline_list_type *s1, spline_list_type s2);
+
+
+/* Each character is in general made up of many outlines. So here is one
+ more list structure. */
+typedef struct
+{
+ spline_list_type *data;
+ unsigned length;
+} spline_list_array_type;
+
+/* Turns out we can use the same definitions for lists of lists as for
+ just lists. But we define the usual names, just in case. */
+#define SPLINE_LIST_ARRAY_LENGTH SPLINE_LIST_LENGTH
+#define SPLINE_LIST_ARRAY_DATA SPLINE_LIST_DATA
+#define SPLINE_LIST_ARRAY_ELT SPLINE_LIST_ELT
+#define LAST_SPLINE_LIST_ARRAY_ELT LAST_SPLINE_LIST_ELT
+
+/* The usual routines. */
+extern spline_list_array_type new_spline_list_array (void);
+extern void free_spline_list_array (spline_list_array_type *);
+extern void append_spline_list (spline_list_array_type *, spline_list_type);
+
+#endif /* not SPLINE_H */
diff --git a/plug-ins/selection-to-path/types.h b/plug-ins/selection-to-path/types.h
new file mode 100644
index 0000000..9b040fa
--- /dev/null
+++ b/plug-ins/selection-to-path/types.h
@@ -0,0 +1,147 @@
+/* types.h: general types.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef TYPES_H
+#define TYPES_H
+
+/* Booleans. */
+typedef enum { false = 0, true = 1 } boolean;
+
+/* The X11 library defines `FALSE' and `TRUE', and so we only want to
+ define them if necessary. */
+#ifndef FALSE
+#define FALSE false
+#define TRUE true
+#endif /* FALSE */
+
+/* The usual null-terminated string. */
+typedef char *string;
+
+/* A generic pointer in ANSI C. */
+typedef void *address;
+
+/* We use `real' for our floating-point variables. */
+typedef double real;
+
+/* A character code. Perhaps someday we will allow for 16-bit
+ character codes, but for now we are restricted to 256 characters per
+ font (like TeX and PostScript). */
+typedef unsigned char charcode_type;
+
+
+/* Used in file formats. */
+typedef unsigned char one_byte;
+typedef signed char signed_byte;
+typedef unsigned short two_bytes;
+typedef short signed_2_bytes;
+typedef unsigned int four_bytes;
+typedef int signed_4_bytes;
+typedef int byte_count_type;
+
+/* These are intended to be used for output in file formats where a
+ ``byte'' is defined to be eight bits, regardless of the hardware. */
+#define ONE_BYTE_BIG (1 << 8)
+#define TWO_BYTES_BIG (1 << 16)
+#define THREE_BYTES_BIG (1 << 24)
+
+
+/* Complex numbers. */
+typedef struct
+{
+ real real;
+ real imag;
+} complex;
+typedef enum { first_complex_part, second_complex_part} complex_part_type;
+typedef enum { polar_rep, rectangular_rep} complex_rep_type;
+
+
+/* Dimensions of a rectangle. */
+typedef struct
+{
+ unsigned height, width;
+} dimensions_type;
+
+#define DIMENSIONS_HEIGHT(d) ((d).height)
+#define DIMENSIONS_WIDTH(d) ((d).width)
+
+
+/* Cartesian points. */
+typedef struct
+{
+ int x, y;
+} coordinate_type;
+
+typedef struct
+{
+ double x, y;
+} real_coordinate_type;
+
+#if 0
+typedef struct
+{
+ double align_threshold;
+ double corner_always_threshold;
+ unsigned int corner_surround;
+ double corner_threshold;
+ double error_threshold;
+ unsigned int filter_alternative_surround;
+ double filter_epsilon;
+ unsigned int filter_iteration_count;
+ double filter_percent;
+ unsigned int filter_secondary_surround;
+ unsigned int filter_surround;
+ boolean keep_knees;
+ double line_reversion_threshold;
+ double line_threshold;
+ double reparameterize_improvement;
+ double reparameterize_threshold;
+ double subdivide_search;
+ unsigned int subdivide_surround;
+ double subdivide_threshold;
+ unsigned int tangent_surround;
+} SELVALS;
+
+#else
+
+typedef struct
+{
+ double align_threshold;
+ double corner_always_threshold;
+ double corner_surround;
+ double corner_threshold;
+ double error_threshold;
+ double filter_alternative_surround;
+ double filter_epsilon;
+ double filter_iteration_count;
+ double filter_percent;
+ double filter_secondary_surround;
+ double filter_surround;
+ boolean keep_knees;
+ double line_reversion_threshold;
+ double line_threshold;
+ double reparameterize_improvement;
+ double reparameterize_threshold;
+ double subdivide_search;
+ double subdivide_surround;
+ double subdivide_threshold;
+ double tangent_surround;
+} SELVALS;
+
+#endif /* 1 */
+
+#endif /* not TYPES_H */
diff --git a/plug-ins/selection-to-path/vector.c b/plug-ins/selection-to-path/vector.c
new file mode 100644
index 0000000..6c16700
--- /dev/null
+++ b/plug-ins/selection-to-path/vector.c
@@ -0,0 +1,249 @@
+/* vector.c: vector/point operations.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <math.h>
+#include <assert.h>
+
+#include "global.h"
+#include "config.h"
+
+#include "vector.h"
+
+
+/* Given the point COORD, return the corresponding vector. */
+
+vector_type
+make_vector (const real_coordinate_type c)
+{
+ vector_type v;
+
+ v.dx = c.x;
+ v.dy = c.y;
+
+ return v;
+}
+
+
+/* And the converse: given a vector, return the corresponding point. */
+
+real_coordinate_type
+vector_to_point (const vector_type v)
+{
+ real_coordinate_type coord;
+
+ coord.x = v.dx;
+ coord.y = v.dy;
+
+ return coord;
+}
+
+
+real
+magnitude (const vector_type v)
+{
+ return hypot (v.dx, v.dy);
+}
+
+
+vector_type
+normalize (const vector_type v)
+{
+ vector_type new_v;
+ real m = magnitude (v);
+
+ assert (m > 0.0);
+
+ new_v.dx = v.dx / m;
+ new_v.dy = v.dy / m;
+
+ return new_v;
+}
+
+
+vector_type
+Vadd (const vector_type v1, const vector_type v2)
+{
+ vector_type new_v;
+
+ new_v.dx = v1.dx + v2.dx;
+ new_v.dy = v1.dy + v2.dy;
+
+ return new_v;
+}
+
+
+real
+Vdot (const vector_type v1, const vector_type v2)
+{
+ return v1.dx * v2.dx + v1.dy * v2.dy;
+}
+
+
+vector_type
+Vmult_scalar (const vector_type v, const real r)
+{
+ vector_type new_v;
+
+ new_v.dx = v.dx * r;
+ new_v.dy = v.dy * r;
+
+ return new_v;
+}
+
+
+/* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
+ degrees, in the range zero to 180. */
+
+real
+Vangle (const vector_type in_vector, const vector_type out_vector)
+{
+ vector_type v1 = normalize (in_vector);
+ vector_type v2 = normalize (out_vector);
+
+ return my_acosd (Vdot (v2, v1));
+}
+
+
+real_coordinate_type
+Vadd_point (const real_coordinate_type c, const vector_type v)
+{
+ real_coordinate_type new_c;
+
+ new_c.x = c.x + v.dx;
+ new_c.y = c.y + v.dy;
+ return new_c;
+}
+
+
+real_coordinate_type
+Vsubtract_point (const real_coordinate_type c, const vector_type v)
+{
+ real_coordinate_type new_c;
+
+ new_c.x = c.x - v.dx;
+ new_c.y = c.y - v.dy;
+ return new_c;
+}
+
+
+coordinate_type
+Vadd_int_point (const coordinate_type c, const vector_type v)
+{
+ coordinate_type a;
+
+ a.x = SROUND ((real) c.x + v.dx);
+ a.y = SROUND ((real) c.y + v.dy);
+ return a;
+}
+
+
+vector_type
+Vabs (const vector_type v)
+{
+ vector_type new_v;
+
+ new_v.dx = fabs (v.dx);
+ new_v.dy = fabs (v.dy);
+ return new_v;
+}
+
+
+/* Operations on points. */
+
+vector_type
+Psubtract (const real_coordinate_type c1, const real_coordinate_type c2)
+{
+ vector_type v;
+
+ v.dx = c1.x - c2.x;
+ v.dy = c1.y - c2.y;
+
+ return v;
+}
+
+/* Operations on integer points. */
+
+vector_type
+IPsubtract (const coordinate_type coord1, const coordinate_type coord2)
+{
+ vector_type v;
+
+ v.dx = coord1.x - coord2.x;
+ v.dy = coord1.y - coord2.y;
+
+ return v;
+}
+
+
+coordinate_type
+IPsubtractP (const coordinate_type c1, const coordinate_type c2)
+{
+ coordinate_type c;
+
+ c.x = c1.x - c2.x;
+ c.y = c1.y - c2.y;
+
+ return c;
+}
+
+
+coordinate_type
+IPadd (const coordinate_type c1, const coordinate_type c2)
+{
+ coordinate_type c;
+
+ c.x = c1.x + c2.x;
+ c.y = c1.y + c2.y;
+
+ return c;
+}
+
+
+coordinate_type
+IPmult_scalar (const coordinate_type c, const int i)
+{
+ coordinate_type a;
+
+ a.x = c.x * i;
+ a.y = c.y * i;
+
+ return a;
+}
+
+
+real_coordinate_type
+IPmult_real (const coordinate_type c, const real r)
+{
+ real_coordinate_type a;
+
+ a.x = c.x * r;
+ a.y = c.y * r;
+
+ return a;
+}
+
+
+boolean
+IPequal (const coordinate_type c1, const coordinate_type c2)
+{
+ return c1.x == c2.x && c1.y == c2.y;
+}
diff --git a/plug-ins/selection-to-path/vector.h b/plug-ins/selection-to-path/vector.h
new file mode 100644
index 0000000..bc6c213
--- /dev/null
+++ b/plug-ins/selection-to-path/vector.h
@@ -0,0 +1,95 @@
+/* vector.h: operations on vectors and points.
+ *
+ * Copyright (C) 1992 Free Software Foundation, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+#include "types.h"
+
+/* Our vectors are represented as displacements along the x and y axes. */
+
+typedef struct
+{
+ real dx, dy;
+} vector_type;
+
+
+/* Consider a point as a vector from the origin. */
+extern vector_type make_vector (const real_coordinate_type);
+
+/* And a vector as a point, i.e., a displacement from the origin. */
+extern real_coordinate_type vector_to_point (const vector_type);
+
+
+/* Definitions for these common operations can be found in any decent
+ linear algebra book, and most calculus books. */
+
+extern real magnitude (const vector_type);
+extern vector_type normalize (const vector_type);
+
+extern vector_type Vadd (const vector_type, const vector_type);
+extern real Vdot (const vector_type, const vector_type);
+extern vector_type Vmult_scalar (const vector_type, const real);
+extern real Vangle (const vector_type in, const vector_type out);
+
+/* These operations could have been named `P..._vector' just as well as
+ V..._point, so we may as well allow both names. */
+#define Padd_vector Vadd_point
+extern real_coordinate_type Vadd_point
+ (const real_coordinate_type, const vector_type);
+
+#define Psubtract_vector Vsubtract_point
+extern real_coordinate_type Vsubtract_point
+ (const real_coordinate_type, const vector_type);
+
+/* This returns the rounded sum. */
+#define IPadd_vector Vadd_int_point
+extern coordinate_type Vadd_int_point
+ (const coordinate_type, const vector_type);
+
+/* Take the absolute value of both components. */
+extern vector_type Vabs (const vector_type);
+
+
+/* Operations on points with real coordinates. It is not orthogonal,
+ but more convenient, to have the subtraction operator return a
+ vector, and the addition operator return a point. */
+extern vector_type Psubtract
+ (const real_coordinate_type, const real_coordinate_type);
+
+/* These are heavily used in spline fitting, so we define them as macros
+ instead of functions. */
+#define Padd(rc1, rc2) \
+ ((real_coordinate_type) { (rc1).x + (rc2).x, (rc1).y + (rc2).y })
+#define Pmult_scalar(rc, r) \
+ ((real_coordinate_type) { (rc).x * (r), (rc).y * (r) })
+
+/* Similarly, for points with integer coordinates; here, a subtraction
+ operator that does return another point is useful. */
+extern vector_type IPsubtract
+ (const coordinate_type, const coordinate_type);
+extern coordinate_type IPsubtractP
+ (const coordinate_type, const coordinate_type);
+extern coordinate_type IPadd
+ (const coordinate_type, const coordinate_type);
+extern coordinate_type IPmult_scalar (const coordinate_type, const int);
+extern real_coordinate_type IPmult_real
+ (const coordinate_type, const real);
+extern boolean IPequal (const coordinate_type, const coordinate_type);
+
+#endif /* not VECTOR_H */
diff --git a/plug-ins/twain/Makefile.am b/plug-ins/twain/Makefile.am
new file mode 100644
index 0000000..4e094a5
--- /dev/null
+++ b/plug-ins/twain/Makefile.am
@@ -0,0 +1,61 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+libexecdir = $(gimpplugindir)/plug-ins/twain
+
+libexec_PROGRAMS = twain
+
+
+if OS_WIN32
+twain_LDFLAGS = -mwindows -luser32
+
+twainplatform = tw_win.c
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+twain_RC = twain.rc.o
+endif
+
+
+twain_SOURCES = \
+ tw_func.c \
+ tw_func.h \
+ tw_util.c \
+ tw_util.h \
+ twain.c \
+ twain.h \
+ tw_local.h \
+ tw_platform.h \
+ $(twainplatform)
+
+EXTRA_DIST = \
+ README \
+ gimp-twain.png \
+ tw_dump.c \
+ tw_dump.h \
+ tw_win.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(twain_LIBS) \
+ $(twain_RC)
diff --git a/plug-ins/twain/Makefile.in b/plug-ins/twain/Makefile.in
new file mode 100644
index 0000000..a52e0c7
--- /dev/null
+++ b/plug-ins/twain/Makefile.in
@@ -0,0 +1,1027 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = twain$(EXEEXT)
+subdir = plug-ins/twain
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am__twain_SOURCES_DIST = tw_func.c tw_func.h tw_util.c tw_util.h \
+ twain.c twain.h tw_local.h tw_platform.h tw_win.c
+@OS_WIN32_TRUE@am__objects_1 = tw_win.$(OBJEXT)
+am_twain_OBJECTS = tw_func.$(OBJEXT) tw_util.$(OBJEXT) twain.$(OBJEXT) \
+ $(am__objects_1)
+twain_OBJECTS = $(am_twain_OBJECTS)
+twain_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+twain_DEPENDENCIES = $(libgimp) $(libgimpcolor) $(libgimpbase) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(twain_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+twain_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(twain_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/tw_func.Po ./$(DEPDIR)/tw_util.Po \
+ ./$(DEPDIR)/tw_win.Po ./$(DEPDIR)/twain.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(twain_SOURCES)
+DIST_SOURCES = $(am__twain_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp README
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/twain
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@twain_LDFLAGS = -mwindows -luser32
+@OS_WIN32_TRUE@twainplatform = tw_win.c
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@twain_RC = twain.rc.o
+twain_SOURCES = \
+ tw_func.c \
+ tw_func.h \
+ tw_util.c \
+ tw_util.h \
+ twain.c \
+ twain.h \
+ tw_local.h \
+ tw_platform.h \
+ $(twainplatform)
+
+EXTRA_DIST = \
+ README \
+ gimp-twain.png \
+ tw_dump.c \
+ tw_dump.h \
+ tw_win.c
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ -I$(includedir)
+
+LDADD = \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpbase) \
+ $(GLIB_LIBS) \
+ $(GEGL_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(twain_LIBS) \
+ $(twain_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/twain/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/twain/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+twain$(EXEEXT): $(twain_OBJECTS) $(twain_DEPENDENCIES) $(EXTRA_twain_DEPENDENCIES)
+ @rm -f twain$(EXEEXT)
+ $(AM_V_CCLD)$(twain_LINK) $(twain_OBJECTS) $(twain_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tw_func.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tw_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tw_win.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twain.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/tw_func.Po
+ -rm -f ./$(DEPDIR)/tw_util.Po
+ -rm -f ./$(DEPDIR)/tw_win.Po
+ -rm -f ./$(DEPDIR)/twain.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/tw_func.Po
+ -rm -f ./$(DEPDIR)/tw_util.Po
+ -rm -f ./$(DEPDIR)/tw_win.Po
+ -rm -f ./$(DEPDIR)/twain.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/twain/README b/plug-ins/twain/README
new file mode 100644
index 0000000..87c1492
--- /dev/null
+++ b/plug-ins/twain/README
@@ -0,0 +1,92 @@
+TWAIN Plug-in
+Copyright (C) 1999 Craig Setera
+Craig Setera <setera@home.com>
+03/31/1999
+
+Updated for Mac OS X support
+Brion Vibber <brion@pobox.com>
+07/22/2004
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Based on (at least) the following plug-ins:
+Screenshot
+GIF
+Randomize
+
+Any suggestions, bug-reports or patches are welcome.
+
+This plug-in interfaces to the TWAIN support library in order
+to capture images from TWAIN devices directly into GIMP images.
+The plug-in is capable of acquiring the following type of
+images:
+ - B/W (1 bit images translated to grayscale B/W)
+ - Grayscale up to 16 bits per pixel
+ - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ - Paletted images (both Gray and RGB)
+
+Prerequisites:
+ Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ also on 10.2).
+
+Known problems:
+
+ - Multiple image transfers will hang the plug-in. The current
+ configuration compiles with a maximum of single image transfers.
+ - On Mac OS X, canceling doesn't always close things out fully.
+ - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+
+Debugging:
+ There are two different versions of the program included, a standard
+ version and a version built for debugging. The debugging version is
+ a special version capable of capturing the TWAIN datasource data to
+ a file for later processing. This feature can be used to create a data
+ dump that can be sent to me if for some reason the plug-in fails for
+ your TWAIN datasource. The function of the plug-in is controlled by
+ the name of the executable file. The plug-in should be placed in your
+ plug-ins directory and named as follows:
+
+twain.exe - This is the standard (non-debugging) version. This file will
+ not behave differently if renamed. This version is intended to be used
+ unless your TWAIN datasource has some problem.
+
+gtwain.exe - This is the debugging version of the program shipped along.
+ If this program is used as named, it will behave the same as "twain.exe"
+ except that the file C:\twain.log will be written. This file is a text
+ file with very little information available for debugging.
+
+dtwain.exe - Renaming "gtwain.exe" to "dtwain.exe" in your plug-ins directory
+ will change the behavior of the plug-in. The menu option will be registered
+ as "TWAIN Acquire (Dump)..." and will cause the TWAIN acquire operation to
+ be dumped to the file "C:\twaincap.bin". This file contains all of the data
+ necessary to recreate the capture on another system (such as my machine). The
+ image will not be displayed, as it might cause the plug-in to crash making
+ the data useless. Instead, a message will be displayed that the image
+ information was dumped to a file.
+
+rtwain.exe - Renaming "gtwain.exe" to "rtwain.exe" in your plug-ins directory
+ will change the behavior of the plug-in. The menu option will be registered
+ as "TWAIN Acquire (Read)..." and will cause the TWAIN acquire operation to
+ read a previously dumped (using dtwain.exe) TWAIN acquire from
+ "C:\twaincap.bin".
+
+If you find that you are unable to capture data from you datasource (the plug-in
+crashes or the image is wrong), please capture data using the following steps:
+
+1) Copy gtwain.exe to your plug-ins directory as "dtwain.exe".
+2) Run GIMP.
+3) Choose Xtns->TWAIN Acquire (Dump)...
+4) Choose the datasource settings that are causing problems.
+5) Choose a *SMALL* sample image.
+6) Zip up "C:\twaincap.bin" and "C:\twain.log" and send them to me.
diff --git a/plug-ins/twain/gimp-twain.png b/plug-ins/twain/gimp-twain.png
new file mode 100644
index 0000000..25ff3f3
--- /dev/null
+++ b/plug-ins/twain/gimp-twain.png
Binary files differ
diff --git a/plug-ins/twain/tw_dump.c b/plug-ins/twain/tw_dump.c
new file mode 100644
index 0000000..b75bc8c
--- /dev/null
+++ b/plug-ins/twain/tw_dump.c
@@ -0,0 +1,273 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gstdio.h>
+
+#include "libgimp/gimp.h"
+
+#include "tw_dump.h"
+#include "tw_func.h"
+#include "tw_util.h"
+
+/* File variables */
+static FILE *outputFile = NULL;
+extern pTW_SESSION twSession;
+
+/*
+ * dumpPreTransferCallback
+ *
+ * This callback function is called before any images
+ * are transferred. Set up the one time only stuff.
+ */
+void
+dumpPreTransferCallback(void *clientData)
+{
+ /* Open our output file... Not settable... Always
+ * write to the root directory. Simplistic, but
+ * gets the job done.
+ */
+ outputFile = g_fopen(DUMP_FILE, "wb");
+}
+
+/*
+ * dumpBeginTransferCallback
+ *
+ * The following function is called at the beginning
+ * of each image transfer.
+ */
+int
+dumpBeginTransferCallback(pTW_IMAGEINFO imageInfo, void *clientData)
+{
+ logBegin(imageInfo, clientData);
+
+ /* Dump the image information */
+ fwrite((void *) imageInfo, sizeof(TW_IMAGEINFO), 1, outputFile);
+
+ /* Keep going */
+ return TRUE;
+}
+
+/*
+ * dumpDataTransferCallback
+ *
+ * The following function is called for each memory
+ * block that is transferred from the data source.
+ */
+int
+dumpDataTransferCallback(pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+ int flag = 1;
+
+ logData(imageInfo, imageMemXfer, clientData);
+
+ /* Write a flag that says that this is a data packet */
+ fwrite((void *) &flag, sizeof(int), 1, outputFile);
+
+ /* Dump the memory information */
+ fwrite((void *) imageMemXfer, sizeof(TW_IMAGEMEMXFER), 1, outputFile);
+ fwrite((void *) imageMemXfer->Memory.TheMem,
+ 1, imageMemXfer->BytesWritten, outputFile);
+
+ /* Keep going */
+ return TRUE;
+}
+
+/*
+ * dumpEndTransferCallback
+ *
+ * The following function is called at the end of the
+ * image transfer. The caller will be handed
+ * the image transfer completion state. The
+ * following values (defined in twain.h) are
+ * possible:
+ *
+ * TWRC_XFERDONE
+ * The transfer completed successfully
+ * TWRC_CANCEL
+ * The transfer was completed by the user
+ * TWRC_FAILURE
+ * The transfer failed.
+ */
+int
+dumpEndTransferCallback(int completionState, int pendingCount, void *clientData)
+{
+ int flag = 0;
+
+ /* Write a flag that says that this is a data packet */
+ fwrite((void *) &flag, sizeof(int), 1, outputFile);
+
+ /* Write the necessary data */
+ fwrite(&completionState, sizeof(int), 1, outputFile);
+ fwrite(&pendingCount, sizeof(int), 1, outputFile);
+
+ /* Only ever transfer a single image */
+ return FALSE;
+}
+
+/*
+ * dumpPostTransferCallback
+ *
+ * This callback function will be called
+ * after all possible images have been
+ * transferred.
+ */
+void
+dumpPostTransferCallback(int pendingCount, void *clientData)
+{
+ char buffer[128];
+
+ /* Shut things down. */
+ if (pendingCount != 0)
+ cancelPendingTransfers(twSession);
+
+ /* This will close the datasource and datasource
+ * manager. Then the message queue will be shut
+ * down and the run() procedure will finally be
+ * able to finish.
+ */
+ disableDS(twSession);
+ closeDS(twSession);
+ closeDSM(twSession);
+
+ /* Close the dump file */
+ fclose(outputFile);
+
+ /* Tell the user */
+ sprintf(buffer, "Image dumped to file %s\n", DUMP_FILE);
+ gimp_message(buffer);
+
+ /* Post a message to close up the application */
+ twainQuitApplication ();
+}
+
+/*
+ * readDumpedImage
+ *
+ * Get a previously dumped image.
+ */
+void readDumpedImage(pTW_SESSION twSession)
+{
+ int moreData;
+ int completionState;
+ int pendingCount;
+
+ TW_IMAGEINFO imageInfo;
+ TW_IMAGEMEMXFER imageMemXfer;
+
+ /* Open our output file... Not settable... Always
+ * write to the root directory. Simplistic, but
+ * gets the job done.
+ */
+ FILE *inputFile = g_fopen(DUMP_FILE, "rb");
+
+ /*
+ * Inform our application that we are getting ready
+ * to transfer images.
+ */
+ (*twSession->transferFunctions->preTxfrCb)(NULL);
+
+ /* Read the image information */
+ fread((void *) &imageInfo, sizeof(TW_IMAGEINFO), 1, inputFile);
+
+ /* Call the begin transfer callback */
+ if (!(*twSession->transferFunctions->txfrBeginCb)(&imageInfo, twSession->clientData))
+ return;
+
+ /* Read all of the data packets */
+ fread((void *) &moreData, sizeof(int), 1, inputFile);
+ while (moreData) {
+ /* Read the memory information */
+ fread((void *) &imageMemXfer, sizeof(TW_IMAGEMEMXFER), 1, inputFile);
+ imageMemXfer.Memory.TheMem = (TW_MEMREF) g_malloc (imageMemXfer.BytesWritten);
+ fread((void *) imageMemXfer.Memory.TheMem,
+ 1, imageMemXfer.BytesWritten, inputFile);
+
+ /* Call the data transfer callback function */
+ if (!(*twSession->transferFunctions->txfrDataCb)
+ (&imageInfo,
+ &imageMemXfer,
+ twSession->clientData))
+ return;
+
+ /* Clean up the memory */
+ g_free (imageMemXfer.Memory.TheMem);
+
+ /* Check for continuation */
+ fread((void *) &moreData, sizeof(int), 1, inputFile);
+ }
+
+ /* Grab the final information */
+ fread(&completionState, sizeof(int), 1, inputFile);
+ fread(&pendingCount, sizeof(int), 1, inputFile);
+
+ if (twSession->transferFunctions->txfrEndCb)
+ (*twSession->transferFunctions->txfrEndCb)(completionState, 0,
+ twSession->clientData);
+
+ /* Post a message to close up the application */
+ twainQuitApplication ();
+}
diff --git a/plug-ins/twain/tw_dump.h b/plug-ins/twain/tw_dump.h
new file mode 100644
index 0000000..5386af7
--- /dev/null
+++ b/plug-ins/twain/tw_dump.h
@@ -0,0 +1,70 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * This plug-in will not compile on anything other than a Win32
+ * platform. Although the TWAIN documentation implies that there
+ * is TWAIN support available on Macintosh, I neither have a
+ * Macintosh nor the interest in porting this. If anyone else
+ * has an interest, consult www.twain.org for more information on
+ * interfacing to TWAIN.
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ */
+
+#include "tw_platform.h"
+#include "tw_func.h"
+
+void dumpPreTransferCallback(void *clientData);
+int dumpBeginTransferCallback(pTW_IMAGEINFO imageInfo, void *clientData);
+int dumpDataTransferCallback(pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData);
+int dumpEndTransferCallback(int completionState, int pendingCount, void *clientData);
+void dumpPostTransferCallback(int pendingCount, void *clientData);
+void readDumpedImage(pTW_SESSION twSession);
+
diff --git a/plug-ins/twain/tw_func.c b/plug-ins/twain/tw_func.c
new file mode 100644
index 0000000..8baa7b3
--- /dev/null
+++ b/plug-ins/twain/tw_func.c
@@ -0,0 +1,874 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+
+#include "config.h"
+
+#include <glib.h> /* Needed when compiling with gcc */
+
+#include "tw_func.h"
+#include "tw_util.h"
+#include "tw_local.h"
+
+/*
+ * Twain error code to string mappings
+ */
+static int twainErrorCount = 0;
+static char *twainErrors[] = {
+ "No error",
+ "Failure due to unknown causes",
+ "Not enough memory to perform operation",
+ "No Data Source",
+ "DS is connected to max possible apps",
+ "DS or DSM reported error, application shouldn't",
+ "Unknown capability",
+ "Unrecognized MSG DG DAT combination",
+ "Data parameter out of range",
+ "DG DAT MSG out of expected sequence",
+ "Unknown destination App/Src in DSM_Entry",
+ "Capability not supported by source",
+ "Operation not supported by capability",
+ "Capability has dependency on other capability",
+ "File System operation is denied (file is protected)",
+ "Operation failed because file already exists.",
+ "File not found",
+ "Operation failed because directory is not empty",
+ "The feeder is jammed",
+ "The feeder detected multiple pages",
+ "Error writing the file (disk full?)",
+ "The device went offline prior to or during this operation",
+ NULL
+};
+
+/*
+ * FloatToFix32
+ *
+ * Convert a floating point value into a FIX32.
+ */
+TW_FIX32 FloatToFIX32(float floater)
+{
+ TW_FIX32 Fix32_value;
+ TW_INT32 value = (TW_INT32) (floater * 65536.0 + 0.5);
+ Fix32_value.Whole = value >> 16;
+ Fix32_value.Frac = value & 0x0000ffffL;
+ return (Fix32_value);
+}
+
+/*
+ * Fix32ToFloat
+ *
+ * Convert a FIX32 value into a floating point value.
+ */
+float FIX32ToFloat(TW_FIX32 fix32)
+{
+ float floater;
+ floater = (float) fix32.Whole + (float) fix32.Frac / 65536.0;
+ return floater;
+}
+
+/*
+ * twainError
+ *
+ * Return the TWAIN error message associated
+ * with the specified error code.
+ */
+char *
+twainError(int errorCode)
+{
+ /* Check whether we've counted */
+ if (twainErrorCount == 0)
+ while (twainErrors[twainErrorCount++]) {}
+
+ /* Check out of bounds */
+ if (errorCode >= twainErrorCount)
+ return "Unknown TWAIN Error Code";
+ else
+ return twainErrors[errorCode];
+}
+
+/*
+ * currentTwainError
+ *
+ * Return the current TWAIN error message.
+ */
+char *
+currentTwainError(pTW_SESSION twSession)
+{
+ TW_STATUS twStatus;
+
+ /* Get the current status code from the DSM */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_STATUS, MSG_GET,
+ (TW_MEMREF) &twStatus);
+
+ /* Return the mapped error code */
+ return twainError(twStatus.ConditionCode);
+}
+
+
+/*
+ * getImage
+ *
+ * This is a "high-level" function that can be called in order
+ * to take all of the steps necessary to kick off an image-transfer
+ * from a user-specified TWAIN datasource. The data will be passed
+ * back to the callback function specified in the session structure.
+ */
+int
+getImage(pTW_SESSION twSession)
+{
+ /* Do some sanity checking first and bail
+ * if necessary.
+ */
+
+ if (!twainIsAvailable()) {
+ LogMessage("TWAIN is not available for image capture\n");
+ return FALSE;
+ }
+
+ /* One step at a time */
+ if (!openDSM(twSession)) {
+ LogMessage("Unable to open data source manager\n");
+ return FALSE;
+ }
+
+ if (!selectDS(twSession)) {
+ LogMessage("Data source not selected\n");
+ return FALSE;
+ }
+
+ if (!openDS(twSession)) {
+ LogMessage("Unable to open datasource\n");
+ return FALSE;
+ }
+
+ requestImageAcquire(twSession, TRUE);
+
+ return TRUE;
+}
+
+/*
+ * openDSM
+ *
+ * Open the data source manager
+ */
+int
+openDSM(pTW_SESSION twSession)
+{
+ /* Make sure that we aren't already open */
+ if (DSM_IS_OPEN(twSession))
+ return TRUE;
+
+ /* Open the data source manager */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_PARENT, MSG_OPENDSM,
+ (TW_MEMREF) &(twSession->hwnd));
+
+ /* Check the return code */
+ switch (twSession->twRC) {
+ case TWRC_SUCCESS:
+ /* We are now at state 3 */
+ twSession->twainState = 3;
+ return TRUE;
+ break;
+
+ case TWRC_FAILURE:
+ default:
+ LogMessage("OpenDSM failure\n");
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * selectDS
+ *
+ * Select a datasource using the TWAIN user
+ * interface.
+ */
+int
+selectDS(pTW_SESSION twSession)
+{
+ /* The datasource manager must be open */
+ if (DSM_IS_CLOSED(twSession)) {
+ LogMessage("Can't select data source with closed source manager\n");
+ return FALSE;
+ }
+
+ /* Ask TWAIN to present the source select dialog */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT,
+ (TW_MEMREF) DS_IDENTITY(twSession));
+
+ /* Check the return to determine what the user decided
+ * to do.
+ */
+ switch (twSession->twRC) {
+ case TWRC_SUCCESS:
+ LogMessage("Data source %s selected\n", DS_IDENTITY(twSession)->ProductName);
+ return TRUE;
+ break;
+
+ case TWRC_CANCEL:
+ LogMessage("User cancelled TWAIN source selection\n");
+ break;
+
+ case TWRC_FAILURE:
+ default:
+ LogMessage("Error \"%s\" during TWAIN source selection\n",
+ currentTwainError(twSession));
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * selectDefaultDS
+ *
+ * Select the default datasource.
+ */
+int
+selectDefaultDS(pTW_SESSION twSession)
+{
+ /* The datasource manager must be open */
+ if (DSM_IS_CLOSED(twSession)) {
+ LogMessage("Can't select data source with closed source manager\n");
+ return FALSE;
+ }
+
+ /* Ask TWAIN to present the source select dialog */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT,
+ (TW_MEMREF) DS_IDENTITY(twSession));
+
+ /* Check the return code */
+ return (twSession->twRC == TWRC_SUCCESS);
+}
+
+/*
+ * openDS
+ *
+ * Open a data source using the TWAIN user interface.
+ */
+int
+openDS(pTW_SESSION twSession)
+{
+ TW_IDENTITY *dsIdentity;
+
+ /* The datasource manager must be open */
+ if (DSM_IS_CLOSED(twSession)) {
+ LogMessage("openDS: Cannot open data source... manager closed\n");
+ return FALSE;
+ }
+
+ /* Is the data source already open? */
+ if (DS_IS_OPEN(twSession)) {
+ LogMessage("openDS: Data source already open\n");
+ return TRUE;
+ }
+
+ /* Open the TWAIN datasource */
+ dsIdentity = DS_IDENTITY(twSession);
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_IDENTITY, MSG_OPENDS,
+ (TW_MEMREF) dsIdentity);
+
+ /* Check the return to determine what the user decided
+ * to do.
+ */
+ switch (twSession->twRC) {
+ case TWRC_SUCCESS:
+ /* We are now in TWAIN state 4 */
+ twSession->twainState = 4;
+ LogMessage("Data source %s opened\n", DS_IDENTITY(twSession)->ProductName);
+ LogMessage("\tVersion.MajorNum = %d\n", dsIdentity->Version.MajorNum);
+ LogMessage("\tVersion.MinorNum = %d\n", dsIdentity->Version.MinorNum);
+ LogMessage("\tVersion.Info = %s\n", dsIdentity->Version.Info);
+ LogMessage("\tProtocolMajor = %d\n", dsIdentity->ProtocolMajor);
+ LogMessage("\tProtocolMinor = %d\n", dsIdentity->ProtocolMinor);
+ LogMessage("\tManufacturer = %s\n", dsIdentity->Manufacturer);
+ LogMessage("\tProductFamily = %s\n", dsIdentity->ProductFamily);
+ return TRUE;
+ break;
+
+ default:
+ LogMessage("Error \"%s\" opening data source\n", currentTwainError(twSession));
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * setBufferedXfer
+ */
+static int
+setBufferedXfer(pTW_SESSION twSession)
+{
+ TW_CAPABILITY bufXfer;
+ pTW_ONEVALUE pvalOneValue;
+
+ /* Make sure the data source is open first */
+ if (DS_IS_CLOSED(twSession))
+ return FALSE;
+
+ /* Create the capability information */
+ bufXfer.Cap = ICAP_XFERMECH;
+ bufXfer.ConType = TWON_ONEVALUE;
+ bufXfer.hContainer = twainAllocHandle(sizeof(TW_ONEVALUE));
+
+ pvalOneValue = (pTW_ONEVALUE) twainLockHandle(bufXfer.hContainer);
+ pvalOneValue->ItemType = TWTY_UINT16;
+ pvalOneValue->Item = TWSX_MEMORY;
+ twainUnlockHandle(bufXfer.hContainer);
+
+ /* Make the call to the source manager */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_CAPABILITY, MSG_SET,
+ (TW_MEMREF) &bufXfer);
+
+ /* Free the container */
+ twainFreeHandle(bufXfer.hContainer);
+
+ /* Let the caller know what happened */
+ return (twSession->twRC==TWRC_SUCCESS);
+}
+
+/*
+ * requestImageAcquire
+ *
+ * Request that the acquire user interface
+ * be displayed. This may or may not cause
+ * an image to actually be transferred.
+ */
+int
+requestImageAcquire(pTW_SESSION twSession, gboolean showUI)
+{
+ /* Make sure in the correct state */
+ if (DS_IS_CLOSED(twSession)) {
+ LogMessage("Can't acquire image with closed datasource\n");
+ return FALSE;
+ }
+
+ twainSetupCallback(twSession);
+
+ /* Set the transfer mode */
+ if (setBufferedXfer(twSession)) {
+ TW_USERINTERFACE ui;
+
+ /* Set the UI information */
+ ui.ShowUI = TRUE;
+ ui.ModalUI = TRUE;
+ /* In Windows, the callbacks are sent to the window message handler */
+ ui.hParent = twSession->hwnd;
+
+ /* Make the call to the source manager */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_USERINTERFACE, MSG_ENABLEDS,
+ (TW_MEMREF) &ui);
+
+ if (twSession->twRC == TWRC_SUCCESS) {
+ /* We are now at a new twain state */
+ twSession->twainState = 5;
+
+ return TRUE;
+ } else {
+ LogMessage("Error during data source enable\n");
+ return FALSE;
+ }
+ } else {
+ LogMessage("Unable to set buffered transfer mode: %s\n",
+ currentTwainError(twSession));
+ return FALSE;
+ }
+}
+
+/*
+ * disableDS
+ *
+ * Disable the datasource associated with twSession.
+ */
+int
+disableDS(pTW_SESSION twSession)
+{
+ TW_USERINTERFACE ui;
+
+ /* Verify the datasource is enabled */
+ if (DS_IS_DISABLED(twSession)) {
+ LogMessage("disableDS: Data source not enabled\n");
+ return TRUE;
+ }
+
+ /* Set the UI information */
+ ui.ShowUI = TRUE;
+ ui.ModalUI = TRUE;
+ ui.hParent = twSession->hwnd;
+
+ /* Make the call to the source manager */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_USERINTERFACE, MSG_DISABLEDS,
+ (TW_MEMREF) &ui);
+
+ if (twSession->twRC == TWRC_SUCCESS) {
+ /* We are now at a new twain state */
+ twSession->twainState = 4;
+
+ return TRUE;
+ } else {
+ LogMessage("Error during data source disable\n");
+ return FALSE;
+ }
+}
+
+/*
+ * closeDS
+ *
+ * Close the datasource associated with the
+ * specified session.
+ */
+int
+closeDS(pTW_SESSION twSession)
+{
+ /* Can't close a closed data source */
+ if (DS_IS_CLOSED(twSession)) {
+ LogMessage("closeDS: Data source already closed\n");
+ return TRUE;
+ }
+
+ /* Open the TWAIN datasource */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS,
+ (TW_MEMREF) DS_IDENTITY(twSession));
+
+ /* Check the return to determine what the user decided
+ * to do.
+ */
+ switch (twSession->twRC) {
+ case TWRC_SUCCESS:
+ /* We are now in TWAIN state 3 */
+ twSession->twainState = 3;
+ LogMessage("Data source %s closed\n", DS_IDENTITY(twSession)->ProductName);
+ return TRUE;
+ break;
+
+ default:
+ LogMessage("Error \"%s\" closing data source\n", currentTwainError(twSession));
+ break;
+ }
+
+ return FALSE;
+}
+
+/*
+ * closeDSM
+ *
+ * Close the data source manager
+ */
+int
+closeDSM(pTW_SESSION twSession)
+{
+ if (DSM_IS_CLOSED(twSession)) {
+ LogMessage("closeDSM: Source Manager not open\n");
+ return FALSE;
+ } else {
+ if (DS_IS_OPEN(twSession)) {
+ LogMessage("closeDSM: Can't close source manager with open source\n");
+ return FALSE;
+ } else {
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), NULL,
+ DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM,
+ (TW_MEMREF)&(twSession->hwnd));
+
+ if (twSession->twRC != TWRC_SUCCESS) {
+ LogMessage("CloseDSM failure -- %s\n", currentTwainError(twSession));
+ }
+ else {
+
+ /* We are now in state 2 */
+ twSession->twainState = 2;
+ }
+ }
+ }
+
+ /* Let the caller know what happened */
+ return (twSession->twRC==TWRC_SUCCESS);
+}
+
+
+/*
+ * beginImageTransfer
+ *
+ * Begin an image transfer.
+ */
+static int
+beginImageTransfer(pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
+{
+ /* Clear our structures */
+ memset(imageInfo, 0, sizeof(TW_IMAGEINFO));
+
+ /* Query the image information */
+ twSession->twRC = callDSM(
+ APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_IMAGE, DAT_IMAGEINFO, MSG_GET,
+ (TW_MEMREF) imageInfo);
+
+ /* Check the return code */
+ if (twSession->twRC != TWRC_SUCCESS) {
+ LogMessage("Get Image Info failure - %s\n", currentTwainError(twSession));
+
+ return FALSE;
+ }
+
+ /* Call the begin transfer callback if registered */
+ if (twSession->transferFunctions->txfrBeginCb)
+ if (!(*twSession->transferFunctions->txfrBeginCb)(imageInfo, twSession->clientData))
+ return FALSE;
+
+ /* We should continue */
+ return TRUE;
+}
+
+/*
+ * transferImage
+ *
+ * The Source indicated it is ready to transfer data. It is
+ * waiting for the application to inquire about the image details,
+ * initiate the actual transfer, and, hence, transition the session
+ * from State 6 to 7. Return the reason for exiting the transfer.
+ */
+static void
+transferImage(pTW_SESSION twSession, pTW_IMAGEINFO imageInfo)
+{
+ TW_SETUPMEMXFER setupMemXfer;
+ TW_IMAGEMEMXFER imageMemXfer;
+ char *buffer;
+
+ /* Clear our structures */
+ memset(&setupMemXfer, 0, sizeof(TW_SETUPMEMXFER));
+ memset(&imageMemXfer, 0, sizeof(TW_IMAGEMEMXFER));
+
+ /* Find out how the source would like to transfer... */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_SETUPMEMXFER, MSG_GET,
+ (TW_MEMREF) &setupMemXfer);
+
+ /* Allocate the buffer for the transfer */
+ buffer = g_new (char, setupMemXfer.Preferred);
+ imageMemXfer.Memory.Flags = TWMF_APPOWNS | TWMF_POINTER;
+ imageMemXfer.Memory.Length = setupMemXfer.Preferred;
+ imageMemXfer.Memory.TheMem = (TW_MEMREF) buffer;
+
+ /* Get the data */
+ do {
+ /* Setup for the memory transfer */
+ imageMemXfer.Compression = TWON_DONTCARE16;
+ imageMemXfer.BytesPerRow = TWON_DONTCARE32;
+ imageMemXfer.Columns = TWON_DONTCARE32;
+ imageMemXfer.Rows = TWON_DONTCARE32;
+ imageMemXfer.XOffset = TWON_DONTCARE32;
+ imageMemXfer.YOffset = TWON_DONTCARE32;
+ imageMemXfer.BytesWritten = TWON_DONTCARE32;
+
+ /* Get the next block of memory */
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_IMAGE, DAT_IMAGEMEMXFER, MSG_GET,
+ (TW_MEMREF) &imageMemXfer);
+
+ if ((twSession->twRC == TWRC_SUCCESS) ||
+ (twSession->twRC == TWRC_XFERDONE)) {
+ /* Call the callback function */
+ if (!(*twSession->transferFunctions->txfrDataCb) (
+ imageInfo,
+ &imageMemXfer,
+ twSession->clientData)) {
+ /* Callback function requested to cancel */
+ twSession->twRC = TWRC_CANCEL;
+ break;
+ }
+ }
+ } while (twSession->twRC == TWRC_SUCCESS);
+
+ /* Free the memory buffer */
+ g_free (imageMemXfer.Memory.TheMem);
+}
+
+/*
+ * endPendingTransfer
+ *
+ * Cancel the currently pending transfer.
+ * Return the count of pending transfers.
+ */
+static int
+endPendingTransfer(pTW_SESSION twSession)
+{
+ TW_PENDINGXFERS pendingXfers;
+
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_PENDINGXFERS, MSG_ENDXFER,
+ (TW_MEMREF) &pendingXfers);
+
+ if (!pendingXfers.Count)
+ twSession->twainState = 5;
+
+ return pendingXfers.Count;
+}
+
+/*
+ * cancelPendingTransfers
+ *
+ * Cancel all pending image transfers.
+ */
+void
+cancelPendingTransfers(pTW_SESSION twSession)
+{
+ TW_PENDINGXFERS pendingXfers;
+
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET,
+ (TW_MEMREF) &pendingXfers);
+}
+
+/*
+ * endImageTransfer
+ *
+ * Finish transferring an image. Return the count
+ * of pending images.
+ */
+static int
+endImageTransfer(pTW_SESSION twSession, int *pendingCount)
+{
+ gboolean continueTransfers;
+ int exitCode = twSession->twRC;
+
+ /* Have now exited the transfer for some reason... Figure out
+ * why and what to do about it
+ */
+ switch (twSession->twRC) {
+ case TWRC_XFERDONE:
+ case TWRC_CANCEL:
+ LogMessage("Xfer done received\n");
+ *pendingCount = endPendingTransfer(twSession);
+ break;
+
+ case TWRC_FAILURE:
+ LogMessage("Failure received\n");
+ *pendingCount = endPendingTransfer(twSession);
+ break;
+ }
+
+ /* Call the end transfer callback */
+ if (twSession->transferFunctions->txfrEndCb)
+ continueTransfers =
+ (*twSession->transferFunctions->txfrEndCb)(exitCode,
+ *pendingCount,
+ twSession->clientData);
+
+ return (*pendingCount && continueTransfers);
+}
+
+/*
+ * transferImages
+ *
+ * Transfer all of the images that are available from the
+ * datasource.
+ */
+static void
+transferImages(pTW_SESSION twSession)
+{
+ TW_IMAGEINFO imageInfo;
+ int pendingCount;
+
+ /* Check the image transfer callback function
+ * before even attempting to do the transfer
+ */
+ if (!twSession->transferFunctions || !twSession->transferFunctions->txfrDataCb) {
+ LogMessage("Attempting image transfer without callback function\n");
+ return;
+ }
+
+ /*
+ * Inform our application that we are getting ready
+ * to transfer images.
+ */
+ if (twSession->transferFunctions->preTxfrCb)
+ (*twSession->transferFunctions->preTxfrCb)(twSession->clientData);
+
+ /* Loop through the available images */
+ do {
+ /* Move to the new state */
+ twSession->twainState = 6;
+
+ /* Begin the image transfer */
+ if (!beginImageTransfer(twSession, &imageInfo))
+ continue;
+
+ /* Call the image transfer function */
+ transferImage(twSession, &imageInfo);
+
+ } while (endImageTransfer(twSession, &pendingCount));
+
+ /*
+ * Inform our application that we are done
+ * transferring images.
+ */
+ if (twSession->transferFunctions->postTxfrCb)
+ (*twSession->transferFunctions->postTxfrCb)(pendingCount,
+ twSession->clientData);
+}
+
+void
+processTwainMessage(TW_UINT16 message, pTW_SESSION twSession)
+{
+ switch (message) {
+ case MSG_XFERREADY:
+ LogMessage("Source says that data is ready\n");
+ transferImages(twSession);
+ break;
+
+ case MSG_CLOSEDSREQ:
+ /* Disable the datasource, Close the Data source
+ * and close the data source manager
+ */
+ LogMessage("CloseDSReq\n");
+ disableDS(twSession);
+ closeDS(twSession);
+ closeDSM(twSession);
+ break;
+
+ /* No message from the Source to the App break;
+ * possible new message
+ */
+ case MSG_NULL:
+ default:
+ break;
+ }
+}
+
+/**********************************************************************
+ * Session related functions
+ **********************************************************************/
+
+/*
+ * newSession
+ *
+ * Create a new TWAIN session.
+ */
+pTW_SESSION
+newSession(pTW_IDENTITY appIdentity) {
+ /* Create the structure */
+ pTW_SESSION session = g_new (TW_SESSION, 1);
+
+ /* Set the structure fields */
+ session->hwnd = 0;
+ session->twRC = TWRC_SUCCESS;
+ session->appIdentity = appIdentity;
+ session->dsIdentity = g_new (TW_IDENTITY, 1);
+ session->dsIdentity->Id = 0;
+ session->dsIdentity->ProductName[0] = '\0';
+ session->transferFunctions = NULL;
+
+ if (twainIsAvailable())
+ session->twainState = 2;
+ else
+ session->twainState = 0;
+
+ return session;
+}
+
+/*
+ * registerWindowHandle
+ *
+ * Register the window handle to be used for this
+ * session.
+ */
+void
+registerWindowHandle(pTW_SESSION session, TW_HANDLE hwnd)
+{
+ session->hwnd = hwnd;
+}
+
+/*
+ * registerTransferCallback
+ *
+ * Register the callback to use when transferring
+ * image data.
+ */
+void
+registerTransferCallbacks(pTW_SESSION session,
+ pTXFR_CB_FUNCS txfrFuncs,
+ void *clientData)
+{
+ session->transferFunctions = txfrFuncs;
+ session->clientData = clientData;
+}
+
+/*
+ * setClientData
+ *
+ * Set the client data associated with the specified
+ * TWAIN session.
+ */
+void
+setClientData(pTW_SESSION session, void *clientData)
+{
+ session->clientData = clientData;
+}
diff --git a/plug-ins/twain/tw_func.h b/plug-ins/twain/tw_func.h
new file mode 100644
index 0000000..69d305c
--- /dev/null
+++ b/plug-ins/twain/tw_func.h
@@ -0,0 +1,243 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+
+#ifndef _TW_FUNC_H
+#define _TW_FUNC_H
+
+#include "tw_platform.h"
+
+/*
+ * Pre-image transfer function type
+ *
+ * Sent to the caller before any of the
+ * images are transferred to the application.
+ */
+typedef void (* TW_PRE_TXFR_CB)(void *);
+
+/*
+ * Image transfer begin function type
+ *
+ * Sent to the caller when an image transfer
+ * is about to begin. The caller may return
+ * FALSE if the transfer should not continue.
+ * Otherwise, the function should return a
+ * TRUE value.
+ */
+typedef int (* TW_TXFR_BEGIN_CB)(pTW_IMAGEINFO, void *);
+
+/*
+ * Image transfer callback function type
+ *
+ * Expected to return true if the image transfer
+ * should continue. False if the transfer should
+ * be cancelled.
+ */
+typedef int (* TW_TXFR_DATA_CB)(pTW_IMAGEINFO, pTW_IMAGEMEMXFER, void *);
+
+/*
+ * Image transfer end function type
+ *
+ * Sent to the caller when an image transfer
+ * is completed. The caller will be handed
+ * the image transfer completion state. The
+ * following values (defined in twain.h) are
+ * possible:
+ *
+ * TWRC_XFERDONE
+ * The transfer completed successfully
+ * TWRC_CANCEL
+ * The transfer was completed by the user
+ * TWRC_FAILURE
+ * The transfer failed.
+ */
+typedef int (* TW_TXFR_END_CB)(int, int, void *);
+
+/*
+ * Post-image transfer callback
+ *
+ * This callback function is called after all
+ * of the possible images have been transferred
+ * from the datasource.
+ */
+typedef void (* TW_POST_TXFR_CB)(int, void *);
+
+/*
+ * The following structure defines the
+ * three callback functions that are called
+ * while an image is being transferred.
+ * The types of these functions are defined
+ * above. Any function that is NULL will just
+ * not be called.
+ */
+typedef struct _TXFR_CB_FUNCS {
+ /* Pre-transfer function */
+ TW_PRE_TXFR_CB preTxfrCb;
+
+ /* Begin function */
+ TW_TXFR_BEGIN_CB txfrBeginCb;
+
+ /* Data transfer */
+ TW_TXFR_DATA_CB txfrDataCb;
+
+ /* End function */
+ TW_TXFR_END_CB txfrEndCb;
+
+ /* Post-transfer function */
+ TW_POST_TXFR_CB postTxfrCb;
+} TXFR_CB_FUNCS, *pTXFR_CB_FUNCS;
+
+/*
+ * Data representing a TWAIN
+ * application to data source
+ * session.
+ */
+typedef struct _TWAIN_SESSION {
+ /* The window handle related to the TWAIN application on Win32 */
+ TW_HANDLE hwnd;
+
+ /* The current TWAIN return code */
+ TW_UINT16 twRC;
+
+ /* The application's TWAIN identity */
+ pTW_IDENTITY appIdentity;
+
+ /* The datasource's TWAIN identity */
+ pTW_IDENTITY dsIdentity;
+
+ /* The image data transfer functions */
+ pTXFR_CB_FUNCS transferFunctions;
+
+ /* Client data that is associated with the image
+ * transfer callback
+ */
+ void *clientData;
+
+ /*
+ * The following variable tracks the current state
+ * as related to the TWAIN engine. The states are:
+ *
+ * 1) Pre-session: The DSM has not been loaded
+ * 2) Source Manager Loaded (not opened)
+ * 3) Source Manager Opened
+ * 4) Source Open
+ * 5) Source Enabled
+ * 6) Transfer ready
+ * 7) Transferring
+ */
+ int twainState;
+
+} TW_SESSION, *pTW_SESSION;
+
+/* Session structure access
+ * macros
+ */
+/* #define pAPP_IDENTITY(tw_session) (&(tw_session->appIdentity)) */
+#define APP_IDENTITY(tw_session) (tw_session->appIdentity)
+/* #define pDS_IDENTITY(tw_session) (&(tw_session->dsIdentity)) */
+#define DS_IDENTITY(tw_session) (tw_session->dsIdentity)
+
+/* State macros... Each expects
+ * a Twain Session pointer
+ */
+#define TWAIN_LOADED(tw_session) (tw_session->twainState >= 2)
+#define TWAIN_UNLOADED(tw_session) (tw_session->twainState < 2)
+#define DSM_IS_OPEN(tw_session) (tw_session->twainState >= 3)
+#define DSM_IS_CLOSED(tw_session) (tw_session->twainState < 3)
+#define DS_IS_OPEN(tw_session) (tw_session->twainState >= 4)
+#define DS_IS_CLOSED(tw_session) (tw_session->twainState < 4)
+#define DS_IS_ENABLED(tw_session) (tw_session->twainState >= 5)
+#define DS_IS_DISABLED(tw_session) (tw_session->twainState < 5)
+
+/* Function declarations */
+char *twainError(int);
+char *currentTwainError(pTW_SESSION);
+int getImage(pTW_SESSION);
+int loadTwainLibrary(pTW_SESSION);
+int unloadTwainLibrary(pTW_SESSION twSession);
+int openDSM(pTW_SESSION);
+int selectDS(pTW_SESSION);
+int selectDefaultDS(pTW_SESSION);
+int openDS(pTW_SESSION);
+int requestImageAcquire(pTW_SESSION, gboolean);
+int disableDS(pTW_SESSION);
+int closeDS(pTW_SESSION);
+int closeDSM(pTW_SESSION);
+void cancelPendingTransfers(pTW_SESSION);
+int scanImage (void);
+
+TW_FIX32 FloatToFIX32(float);
+float FIX32ToFloat(TW_FIX32);
+
+void processTwainMessage(TW_UINT16 message, pTW_SESSION twSession);
+
+pTW_SESSION newSession(pTW_IDENTITY);
+void registerWindowHandle(pTW_SESSION, TW_HANDLE);
+void registerTransferCallbacks(pTW_SESSION, pTXFR_CB_FUNCS, void *);
+void setClientData(pTW_SESSION session, void *clientData);
+pTW_SESSION initializeTwain(void);
+
+#ifdef G_OS_WIN32
+void LogLastWinError(void);
+BOOL InitApplication(HINSTANCE hInstance);
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow, pTW_SESSION twSession);
+#endif
+
+#endif /* _TW_FUNC_H */
diff --git a/plug-ins/twain/tw_local.h b/plug-ins/twain/tw_local.h
new file mode 100644
index 0000000..88348ab
--- /dev/null
+++ b/plug-ins/twain/tw_local.h
@@ -0,0 +1,48 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TW_LOCAL_H
+#define _TW_LOCAL_H
+
+#include "tw_func.h"
+
+/* Functions which the platform-independent code will call */
+
+TW_UINT16 callDSM(pTW_IDENTITY, pTW_IDENTITY,
+ TW_UINT32, TW_UINT16,
+ TW_UINT16, TW_MEMREF);
+
+int twainIsAvailable(void);
+void twainQuitApplication (void);
+gboolean twainSetupCallback (pTW_SESSION twSession);
+
+TW_HANDLE twainAllocHandle(size_t size);
+TW_MEMREF twainLockHandle (TW_HANDLE handle);
+void twainUnlockHandle (TW_HANDLE handle);
+void twainFreeHandle (TW_HANDLE handle);
+
+int twainMain (void);
+int scanImage (void);
+
+#endif
diff --git a/plug-ins/twain/tw_platform.h b/plug-ins/twain/tw_platform.h
new file mode 100644
index 0000000..de0263b
--- /dev/null
+++ b/plug-ins/twain/tw_platform.h
@@ -0,0 +1,37 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef _TW_PLATFORM_H
+#define _TW_PLATFORM_H
+
+#include <windows.h>
+#include "twain.h"
+
+/* The DLL to be loaded for TWAIN support */
+#define TWAIN_DLL_NAME "TWAIN_32.DLL"
+#define DEBUG_LOGFILE "c:\\twain.log"
+#define DUMP_FILE "C:\\TWAINCAP.BIN"
+#define DUMP_NAME "DTWAIN.EXE"
+#define READDUMP_NAME "RTWAIN.EXE"
+
+/* Windows uses separate entry point */
+#define TWAIN_ALTERNATE_MAIN
+
+#endif
diff --git a/plug-ins/twain/tw_util.c b/plug-ins/twain/tw_util.c
new file mode 100644
index 0000000..f02109b
--- /dev/null
+++ b/plug-ins/twain/tw_util.c
@@ -0,0 +1,170 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+
+#include "config.h"
+
+#include <glib.h> /* Needed when compiling with gcc */
+
+#include <glib/gstdio.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "tw_util.h"
+#ifdef _DEBUG
+#include "tw_func.h"
+FILE *logFile = NULL;
+#endif
+
+#ifdef _DEBUG
+
+/*
+ * LogMessage
+ */
+void
+LogMessage(char *format, ...)
+{
+ va_list args;
+ time_t time_of_day;
+ char *ctime_string;
+
+ /* Open the log file as necessary */
+ if (!logFile)
+ logFile = g_fopen(DEBUG_LOGFILE, "w");
+
+ time_of_day = time(NULL);
+ ctime_string = ctime(&time_of_day);
+ ctime_string[19] = '\0';
+
+ fprintf(logFile, "[%s] ", (ctime_string + 11));
+ va_start(args, format);
+ vfprintf(logFile, format, args);
+ fflush(logFile);
+ va_end(args);
+}
+
+void
+logBegin(pTW_IMAGEINFO imageInfo, void *clientData)
+{
+ int i;
+ char buffer[256];
+
+ LogMessage("\n");
+ LogMessage("*************************************\n");
+ LogMessage("\n");
+ LogMessage("Image transfer begin:\n");
+ /* LogMessage("\tClient data: %s\n", (char *) clientData); */
+
+ /* Log the image information */
+ LogMessage("Image information:\n");
+ LogMessage("\tXResolution: %f\n", FIX32ToFloat(imageInfo->XResolution));
+ LogMessage("\tYResolution: %f\n", FIX32ToFloat(imageInfo->YResolution));
+ LogMessage("\tImageWidth: %d\n", imageInfo->ImageWidth);
+ LogMessage("\tImageLength: %d\n", imageInfo->ImageLength);
+ LogMessage("\tSamplesPerPixel: %d\n", imageInfo->SamplesPerPixel);
+ sprintf(buffer, "\tBitsPerSample: {");
+ for (i = 0; i < 8; i++) {
+ if (imageInfo->BitsPerSample[i])
+ strcat(buffer, "1");
+ else
+ strcat(buffer, "0");
+
+ if (i != 7)
+ strcat(buffer, ",");
+ }
+ LogMessage("%s}\n", buffer);
+
+ LogMessage("\tBitsPerPixel: %d\n", imageInfo->BitsPerPixel);
+ LogMessage("\tPlanar: %s\n", (imageInfo->Planar ? "TRUE" : "FALSE"));
+ LogMessage("\tPixelType: %d\n", imageInfo->PixelType);
+ /* Compression */
+
+}
+
+void
+logData(pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+ LogMessage("Image transfer callback called:\n");
+ LogMessage("\tClient data: %s\n", (char *) clientData);
+ LogMessage("Memory block transferred:\n");
+ LogMessage("\tBytesPerRow = %d\n", imageMemXfer->BytesPerRow);
+ LogMessage("\tColumns = %d\n", imageMemXfer->Columns);
+ LogMessage("\tRows = %d\n", imageMemXfer->Rows);
+ LogMessage("\tXOffset = %d\n", imageMemXfer->XOffset);
+ LogMessage("\tYOffset = %d\n", imageMemXfer->YOffset);
+ LogMessage("\tBytesWritten = %d\n", imageMemXfer->BytesWritten);
+}
+
+#else
+
+/*
+ * LogMessage
+ */
+void
+LogMessage(char *format, ...)
+{
+}
+
+#endif /* DEBUG */
diff --git a/plug-ins/twain/tw_util.h b/plug-ins/twain/tw_util.h
new file mode 100644
index 0000000..8fac459
--- /dev/null
+++ b/plug-ins/twain/tw_util.h
@@ -0,0 +1,73 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+#ifndef __TW_UTIL_H
+#define __TW_UTIL_H
+
+#include "tw_platform.h"
+
+void LogMessage(char *, ...);
+
+#ifdef _DEBUG
+void logBegin(pTW_IMAGEINFO, void *);
+void logData(pTW_IMAGEINFO, pTW_IMAGEMEMXFER, void *);
+#endif
+
+#endif /* __TW_UTIL_H */
diff --git a/plug-ins/twain/tw_win.c b/plug-ins/twain/tw_win.c
new file mode 100644
index 0000000..726a45f
--- /dev/null
+++ b/plug-ins/twain/tw_win.c
@@ -0,0 +1,452 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Windows platform-specific code
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "tw_platform.h"
+#include "tw_func.h"
+#include "tw_util.h"
+#include "tw_local.h"
+
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+int twainMessageLoop(pTW_SESSION);
+int TwainProcessMessage(LPMSG lpMsg, pTW_SESSION twSession);
+
+extern GimpPlugInInfo PLUG_IN_INFO;
+extern pTW_SESSION initializeTwain ();
+#ifdef _DEBUG
+extern void setRunMode(char *argv[]);
+#endif
+
+
+#define APP_NAME "TWAIN"
+#define SHOW_WINDOW 0
+#define WM_TRANSFER_IMAGE (WM_USER + 100)
+
+/* main bits */
+static HWND hwnd = NULL;
+static HINSTANCE hInst = NULL;
+
+/* Storage for the DLL handle */
+static HINSTANCE hDLL = NULL;
+
+/* Storage for the entry point into the DSM */
+static DSMENTRYPROC dsmEntryPoint = NULL;
+
+
+/*
+ * callDSM
+ *
+ * Call the specified function on the data source manager.
+ */
+TW_UINT16
+callDSM(pTW_IDENTITY pOrigin,
+ pTW_IDENTITY pDest,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData)
+{
+ /* Call the function */
+ return (*dsmEntryPoint) (pOrigin, pDest, DG, DAT, MSG, pData);
+}
+
+/*
+ * twainIsAvailable
+ *
+ * Return boolean indicating whether TWAIN is available
+ */
+int
+twainIsAvailable(void)
+{
+ /* Already loaded? */
+ if (dsmEntryPoint) {
+ return TRUE;
+ }
+
+ /* Attempt to load the library */
+ hDLL = LoadLibrary(TWAIN_DLL_NAME);
+ if (hDLL == NULL)
+ return FALSE;
+
+ /* Look up the entry point for use */
+ dsmEntryPoint = (DSMENTRYPROC) GetProcAddress(hDLL, "DSM_Entry");
+ if (dsmEntryPoint == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+TW_HANDLE
+twainAllocHandle (size_t size)
+{
+ return GlobalAlloc(GHND, size);
+}
+
+TW_MEMREF
+twainLockHandle (TW_HANDLE handle)
+{
+ return GlobalLock (handle);
+}
+
+void
+twainUnlockHandle (TW_HANDLE handle)
+{
+ GlobalUnlock (handle);
+}
+
+void
+twainFreeHandle (TW_HANDLE handle)
+{
+ GlobalFree (handle);
+}
+
+gboolean
+twainSetupCallback (pTW_SESSION twSession)
+{
+ /* Callbacks go through the window messaging system */
+ return TRUE;
+}
+
+/*
+ * unloadTwainLibrary
+ *
+ * Unload the TWAIN dynamic link library
+ */
+int
+unloadTwainLibrary(pTW_SESSION twSession)
+{
+ /* Explicitly free the SM library */
+ if (hDLL) {
+ FreeLibrary(hDLL);
+ hDLL=NULL;
+ }
+
+ /* the data source id will no longer be valid after
+ * twain is killed. If the id is left around the
+ * data source can not be found or opened
+ */
+ DS_IDENTITY(twSession)->Id = 0;
+
+ /* We are now back at state 1 */
+ twSession->twainState = 1;
+ LogMessage("Source Manager successfully closed\n");
+
+ return TRUE;
+}
+
+/*
+ * TwainProcessMessage
+ *
+ * Returns TRUE if the application should process message as usual.
+ * Returns FALSE if the application should skip processing of this message
+ */
+int
+TwainProcessMessage(LPMSG lpMsg, pTW_SESSION twSession)
+{
+ TW_EVENT twEvent;
+
+ twSession->twRC = TWRC_NOTDSEVENT;
+
+ /* Only ask Source Manager to process event if there is a Source connected. */
+ if (DSM_IS_OPEN(twSession) && DS_IS_OPEN(twSession)) {
+ /*
+ * A Source provides a modeless dialog box as its user interface.
+ * The following call relays Windows messages down to the Source's
+ * UI that were intended for its dialog box. It also retrieves TWAIN
+ * messages sent from the Source to our Application.
+ */
+ twEvent.pEvent = (TW_MEMREF) lpMsg;
+ twSession->twRC = callDSM(APP_IDENTITY(twSession), DS_IDENTITY(twSession),
+ DG_CONTROL, DAT_EVENT, MSG_PROCESSEVENT,
+ (TW_MEMREF) &twEvent);
+
+ /* Check the return code */
+ if (twSession->twRC == TWRC_NOTDSEVENT) {
+ return FALSE;
+ }
+
+ /* Process the message as necessary */
+ processTwainMessage(twEvent.TWMessage, twSession);
+ }
+
+ /* tell the caller what happened */
+ return (twSession->twRC == TWRC_DSEVENT);
+}
+
+/*
+ * twainMessageLoop
+ *
+ * Process Win32 window messages and provide special handling
+ * of TWAIN specific messages. This loop will not exit until
+ * the application exits.
+ */
+int
+twainMessageLoop(pTW_SESSION twSession)
+{
+ MSG msg;
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if (DS_IS_CLOSED(twSession) || !TwainProcessMessage(&msg, twSession)) {
+ TranslateMessage((LPMSG)&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ return msg.wParam;
+}
+
+/*
+ * LogLastWinError
+ *
+ * Log the last Windows error as returned by
+ * GetLastError.
+ */
+void
+LogLastWinError(void)
+{
+ LPVOID lpMsgBuf;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ LogMessage("%s\n", lpMsgBuf);
+
+ /* Free the buffer. */
+ LocalFree( lpMsgBuf );
+}
+
+void twainQuitApplication ()
+{
+ PostQuitMessage (0);
+}
+
+
+/******************************************************************
+ * Win32 entry point and setup...
+ ******************************************************************/
+
+/*
+ * WinMain
+ *
+ * The standard gimp entry point won't quite cut it for
+ * this plug-in. This plug-in requires creation of a
+ * standard Win32 window (hidden) in order to receive
+ * and process window messages on behalf of the TWAIN
+ * datasource.
+ */
+int APIENTRY
+WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+
+ /*
+ * Normally, we would do all of the Windows-ish set up of
+ * the window classes and stuff here in WinMain. But,
+ * the only time we really need the window and message
+ * queues is during the plug-in run. So, all of that will
+ * be done during run(). This avoids all of the Windows
+ * setup stuff for the query(). Stash the instance handle now
+ * so it is available from the run() procedure.
+ */
+ hInst = hInstance;
+
+#ifdef _DEBUG
+ /* When in debug version, we allow different run modes...
+ * make sure that it is correctly set.
+ */
+ setRunMode(__argv);
+#endif /* _DEBUG */
+
+ /*
+ * Now, call gimp_main... This is what the MAIN() macro
+ * would usually do.
+ */
+ return gimp_main(&PLUG_IN_INFO, __argc, __argv);
+}
+
+/*
+ * main
+ *
+ * allow to build as console app as well
+ */
+int main (int argc, char *argv[])
+{
+#ifdef _DEBUG
+ /* When in debug version, we allow different run modes...
+ * make sure that it is correctly set.
+ */
+ setRunMode(__argv);
+#endif /* _DEBUG */
+
+ /*
+ * Now, call gimp_main... This is what the MAIN() macro
+ * would usually do.
+ */
+ return gimp_main(&PLUG_IN_INFO, __argc, __argv);
+}
+
+/*
+ * InitApplication
+ *
+ * Initialize window data and register the window class
+ */
+BOOL
+InitApplication(HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ BOOL retValue;
+
+ /*
+ * Fill in window class structure with parameters to describe
+ * the main window.
+ */
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wc.lpszClassName = APP_NAME;
+ wc.lpszMenuName = NULL;
+
+ /* Register the window class and stash success/failure code. */
+ retValue = RegisterClass(&wc);
+
+ /* Log error */
+ if (!retValue)
+ LogLastWinError();
+
+ return retValue;
+}
+
+/*
+ * InitInstance
+ *
+ * Create the main window for the application. Used to
+ * interface with the TWAIN datasource.
+ */
+BOOL
+InitInstance(HINSTANCE hInstance, int nCmdShow, pTW_SESSION twSession)
+{
+ /* Create our window */
+ hwnd = CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
+ NULL, NULL, hInstance, NULL);
+
+ if (!hwnd) {
+ return (FALSE);
+ }
+
+ /* Register our window handle with the TWAIN
+ * support.
+ */
+ registerWindowHandle(twSession, hwnd);
+
+ /* Schedule the image transfer by posting a message */
+ PostMessage(hwnd, WM_TRANSFER_IMAGE, 0, 0);
+
+ ShowWindow(hwnd, nCmdShow);
+ UpdateWindow(hwnd);
+
+ return TRUE;
+}
+
+/*
+ * twainWinMain
+ *
+ * This is the function that represents the code that
+ * would normally reside in WinMain (see above). This
+ * function will get called during run() in order to set
+ * up the windowing environment necessary for TWAIN to
+ * operate.
+ */
+int
+twainMain()
+{
+ /* Initialize the twain information */
+ pTW_SESSION twSession = initializeTwain();
+
+ /* Perform instance initialization */
+ if (!InitApplication(hInst))
+ return (FALSE);
+
+ /* Perform application initialization */
+ if (!InitInstance(hInst, SHOW_WINDOW, twSession))
+ return (FALSE);
+
+ /*
+ * Call the main message processing loop...
+ * This call will not return until the application
+ * exits.
+ */
+ return twainMessageLoop(twSession);
+}
+
+/*
+ * WndProc
+ *
+ * Process window message for the main window.
+ */
+LRESULT CALLBACK
+WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+
+ case WM_TRANSFER_IMAGE:
+ /* Get an image */
+ scanImage ();
+ break;
+
+ case WM_DESTROY:
+ LogMessage("Exiting application\n");
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return (DefWindowProc(hWnd, message, wParam, lParam));
+ }
+ return 0;
+}
+
diff --git a/plug-ins/twain/twain.c b/plug-ins/twain/twain.c
new file mode 100644
index 0000000..dafb215
--- /dev/null
+++ b/plug-ins/twain/twain.c
@@ -0,0 +1,962 @@
+/*
+ * TWAIN Plug-in
+ * Copyright (C) 1999 Craig Setera
+ * Craig Setera <setera@home.com>
+ * 03/31/1999
+ *
+ * Updated for Mac OS X support
+ * Brion Vibber <brion@pobox.com>
+ * 07/22/2004
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ *
+ * Based on (at least) the following plug-ins:
+ * Screenshot
+ * GIF
+ * Randomize
+ *
+ * Any suggestions, bug-reports or patches are welcome.
+ *
+ * This plug-in interfaces to the TWAIN support library in order
+ * to capture images from TWAIN devices directly into GIMP images.
+ * The plug-in is capable of acquiring the following type of
+ * images:
+ * - B/W (1 bit images translated to grayscale B/W)
+ * - Grayscale up to 16 bits per pixel
+ * - RGB up to 16 bits per sample (24, 30, 36, etc.)
+ * - Paletted images (both Gray and RGB)
+ *
+ * Prerequisites:
+ * Should compile and run on both Win32 and Mac OS X 10.3 (possibly
+ * also on 10.2).
+ *
+ * Known problems:
+ * - Multiple image transfers will hang the plug-in. The current
+ * configuration compiles with a maximum of single image transfers.
+ * - On Mac OS X, canceling doesn't always close things out fully.
+ * - Epson TWAIN driver on Mac OS X crashes the plugin when scanning.
+ */
+
+/*
+ * Revision history
+ * (02/07/99) v0.1 First working version (internal)
+ * (02/09/99) v0.2 First release to anyone other than myself
+ * (02/15/99) v0.3 Added image dump and read support for debugging
+ * (03/31/99) v0.5 Added support for multi-byte samples and paletted
+ * images.
+ * (07/23/04) v0.6 Added Mac OS X support.
+ */
+#include "config.h"
+
+#include <glib.h> /* Needed when compiling with gcc */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tw_platform.h"
+#include "tw_local.h"
+
+#include "libgimp/gimp.h"
+#include "libgimp/stdplugins-intl.h"
+
+#include "tw_func.h"
+#include "tw_util.h"
+
+#ifdef _DEBUG
+#include "tw_dump.h"
+#endif /* _DEBUG */
+
+/*
+ * Plug-in Definitions
+ */
+#define PLUG_IN_NAME "twain-acquire"
+#define PLUG_IN_DESCRIPTION N_("Capture an image from a TWAIN datasource")
+#define PLUG_IN_HELP "This plug-in will capture an image from a TWAIN datasource"
+#define PLUG_IN_AUTHOR "Craig Setera (setera@home.com)"
+#define PLUG_IN_COPYRIGHT "Craig Setera"
+#define PLUG_IN_VERSION "v0.6 (07/22/2004)"
+
+#ifdef _DEBUG
+#define PLUG_IN_D_NAME "twain-acquire-dump"
+#define PLUG_IN_R_NAME "twain-acquire-read"
+#endif /* _DEBUG */
+
+/*
+ * Application definitions
+ */
+#define MAX_IMAGES 1
+
+/*
+ * Definition of the run states
+ */
+#define RUN_STANDARD 0
+#define RUN_DUMP 1
+#define RUN_READDUMP 2
+
+/* Global variables */
+pTW_SESSION twSession = NULL;
+
+#ifdef _DEBUG
+static int twain_run_mode = RUN_STANDARD;
+#endif
+
+/* Forward declarations */
+void preTransferCallback (void *clientData);
+int beginTransferCallback (pTW_IMAGEINFO imageInfo,
+ void *clientData);
+int dataTransferCallback (pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData);
+int endTransferCallback (int completionState,
+ int pendingCount,
+ void *clientData);
+void postTransferCallback (int pendingCount,
+ void *clientData);
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+extern void set_gimp_PLUG_IN_INFO_PTR (GimpPlugInInfo *);
+
+/* Data structure holding data between runs */
+/* Currently unused... Eventually may be used
+ * to track dialog data.
+ */
+typedef struct
+{
+ gchar sourceName[34];
+ gfloat xResolution;
+ gfloat yResolution;
+ gint xOffset;
+ gint yOffset;
+ gint width;
+ gint height;
+ gint imageType;
+} TwainValues;
+
+/* Default Twain values */
+static TwainValues twainvals =
+{
+ "",
+ 100.0, 100.0,
+ 0, 0,
+ 0, 0,
+ TWPT_RGB
+};
+
+/* The standard callback functions */
+TXFR_CB_FUNCS standardCbFuncs =
+{
+ preTransferCallback,
+ beginTransferCallback,
+ dataTransferCallback,
+ endTransferCallback,
+ postTransferCallback
+};
+
+/******************************************************************
+ * Dump handling
+ ******************************************************************/
+
+#ifdef _DEBUG
+/* The dumper callback functions */
+TXFR_CB_FUNCS dumperCbFuncs =
+{
+ dumpPreTransferCallback,
+ dumpBeginTransferCallback,
+ dumpDataTransferCallback,
+ dumpEndTransferCallback,
+ dumpPostTransferCallback
+};
+
+void
+setRunMode (char *argv[])
+{
+ char *exeName = strrchr (argv[0], '\\') + 1;
+
+ LogMessage ("Executable name: %s\n", exeName);
+
+ if (!_stricmp (exeName, DUMP_NAME))
+ twain_run_mode = RUN_DUMP;
+
+ if (!_stricmp (exeName, RUNDUMP_NAME))
+ twain_run_mode = RUN_READDUMP;
+}
+#endif /* _DEBUG */
+
+#ifndef TWAIN_ALTERNATE_MAIN
+MAIN ()
+#endif
+
+int
+scanImage (void)
+{
+#ifdef _DEBUG
+ if (twain_run_mode == RUN_READDUMP)
+ return readDumpedImage (twSession);
+ else
+#endif /* _DEBUG */
+ return getImage (twSession);
+}
+
+/*
+ * initTwainAppIdentity
+ *
+ * Initialize and return our application's identity for
+ * the TWAIN runtime.
+ */
+static pTW_IDENTITY
+getAppIdentity (void)
+{
+ pTW_IDENTITY appIdentity = g_new (TW_IDENTITY, 1);
+
+ /* Set up the application identity */
+ appIdentity->Id = 0;
+ appIdentity->Version.MajorNum = 0;
+ appIdentity->Version.MinorNum = 1;
+ appIdentity->Version.Language = TWLG_USA;
+ appIdentity->Version.Country = TWCY_USA;
+ strcpy(appIdentity->Version.Info, "GIMP TWAIN 0.6");
+ appIdentity->ProtocolMajor = TWON_PROTOCOLMAJOR;
+ appIdentity->ProtocolMinor = TWON_PROTOCOLMINOR;
+ appIdentity->SupportedGroups = DG_IMAGE;
+ strcpy(appIdentity->Manufacturer, "Craig Setera");
+ strcpy(appIdentity->ProductFamily, "GIMP");
+ strcpy(appIdentity->ProductName, "GIMP");
+
+ return appIdentity;
+}
+
+/*
+ * initializeTwain
+ *
+ * Do the necessary TWAIN initialization. This sets up
+ * our TWAIN session information. The session stuff is
+ * something built by me on top of the standard TWAIN
+ * datasource manager calls.
+ */
+pTW_SESSION
+initializeTwain (void)
+{
+ pTW_IDENTITY appIdentity;
+
+ /* Get our application's identity */
+ appIdentity = getAppIdentity ();
+
+ /* Create a new session object */
+ twSession = newSession (appIdentity);
+
+ /* Register our image transfer callback functions */
+#ifdef _DEBUG
+ if (twain_run_mode == RUN_DUMP)
+ registerTransferCallbacks (twSession, &dumperCbFuncs, NULL);
+ else
+#endif /* _DEBUG */
+ registerTransferCallbacks (twSession, &standardCbFuncs, NULL);
+
+ return twSession;
+}
+
+/******************************************************************
+ * GIMP Plug-in entry points
+ ******************************************************************/
+
+/*
+ * Plug-in Parameter definitions
+ */
+#define NUMBER_IN_ARGS 1
+#define IN_ARGS { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }
+#define NUMBER_OUT_ARGS 2
+#define OUT_ARGS \
+ { GIMP_PDB_INT32, "image-count", "Number of acquired images" }, \
+ { GIMP_PDB_INT32ARRAY, "image-ids", "Array of acquired image identifiers" }
+
+
+/*
+ * query
+ *
+ * The plug-in is being queried. Install our procedure for
+ * acquiring.
+ */
+static void
+query (void)
+{
+ static const GimpParamDef args[] = { IN_ARGS };
+ static const GimpParamDef return_vals[] = { OUT_ARGS };
+
+#ifdef _DEBUG
+ if (twain_run_mode == RUN_DUMP)
+ {
+ /* the installation of the plugin */
+ gimp_install_procedure (PLUG_IN_D_NAME,
+ PLUG_IN_DESCRIPTION,
+ PLUG_IN_HELP,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ "TWAIN (Dump)...",
+ NULL,
+ GIMP_PLUGIN,
+ NUMBER_IN_ARGS,
+ NUMBER_OUT_ARGS,
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_D_NAME, "<Image>/File/Create/Acquire");
+ }
+ else if (twain_run_mode == RUN_READDUMP)
+ {
+ /* the installation of the plugin */
+ gimp_install_procedure (PLUG_IN_R_NAME,
+ PLUG_IN_DESCRIPTION,
+ PLUG_IN_HELP,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ "TWAIN (Read)...",
+ NULL,
+ GIMP_PLUGIN,
+ NUMBER_IN_ARGS,
+ NUMBER_OUT_ARGS,
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_R_NAME, "<Image>/File/Create/Acquire");
+ }
+ else
+#endif /* _DEBUG */
+ {
+ /* the installation of the plugin */
+ gimp_install_procedure (PLUG_IN_NAME,
+ PLUG_IN_DESCRIPTION,
+ PLUG_IN_HELP,
+ PLUG_IN_AUTHOR,
+ PLUG_IN_COPYRIGHT,
+ PLUG_IN_VERSION,
+ N_("_Scanner/Camera..."),
+ NULL,
+ GIMP_PLUGIN,
+ NUMBER_IN_ARGS,
+ NUMBER_OUT_ARGS,
+ args,
+ return_vals);
+
+ gimp_plugin_menu_register (PLUG_IN_NAME, "<Image>/File/Create/Acquire");
+ }
+}
+
+
+/* Return values storage */
+static GimpParam values[3];
+
+/*
+ * run
+ *
+ * The plug-in is being requested to run.
+ * Capture an image from a TWAIN datasource
+ */
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ GimpRunMode run_mode = param[0].data.d_int32;
+
+ /* Initialize the return values
+ * Always return at least the status to the caller.
+ */
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ /* Before we get any further, verify that we have
+ * TWAIN and that there is actually a datasource
+ * to be used in doing the acquire.
+ */
+ if (! twainIsAvailable ())
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ return;
+ }
+
+ /* Set up the rest of the return parameters */
+ values[1].type = GIMP_PDB_INT32;
+ values[1].data.d_int32 = 0;
+ values[2].type = GIMP_PDB_INT32ARRAY;
+ values[2].data.d_int32array = g_new (gint32, MAX_IMAGES);
+
+ /* How are we running today? */
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Retrieve values from the last run...
+ * Currently ignored
+ */
+ gimp_get_data (PLUG_IN_NAME, &twainvals);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Currently, we don't do non-interactive calls.
+ * Bail if someone tries to call us non-interactively
+ */
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+ return;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Retrieve values from the last run...
+ * Currently ignored
+ */
+ gimp_get_data (PLUG_IN_NAME, &twainvals);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Have we succeeded so far? */
+ if (values[0].data.d_status == GIMP_PDB_SUCCESS)
+ twainMain ();
+
+ /* Check to make sure we got at least one valid
+ * image.
+ */
+ if (values[1].data.d_int32 > 0)
+ {
+ /* An image was captured from the TWAIN
+ * datasource. Do final Interactive
+ * steps.
+ */
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ /* Store variable states for next run */
+ gimp_set_data (PLUG_IN_NAME, &twainvals, sizeof (TwainValues));
+ }
+
+ /* Set return values */
+ *nreturn_vals = 3;
+ }
+ else
+ {
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+ }
+}
+
+/***********************************************************************
+ * Image transfer callback functions
+ ***********************************************************************/
+
+/* Data used to carry data between each of
+ * the callback function calls.
+ */
+typedef struct
+{
+ gint32 image_id;
+ gint32 layer_id;
+ GeglBuffer *buffer;
+ const Babl *format;
+ pTW_PALETTE8 paletteData;
+ int totalPixels;
+ int completedPixels;
+} ClientDataStruct, *pClientDataStruct;
+
+/*
+ * preTransferCallback
+ *
+ * This callback function is called before any images
+ * are transferred. Set up the one time only stuff.
+ */
+void
+preTransferCallback (void *clientData)
+{
+ /* Initialize our progress dialog */
+ gimp_progress_init (_("Transferring data from scanner/camera"));
+}
+
+/*
+ * beginTransferCallback
+ *
+ * The following function is called at the beginning
+ * of each image transfer.
+ */
+int
+beginTransferCallback (pTW_IMAGEINFO imageInfo,
+ void *clientData)
+{
+ pClientDataStruct theClientData = g_new (ClientDataStruct, 1);
+
+ const Babl *format;
+ GimpImageBaseType imageType;
+ GimpImageType layerType;
+ GimpPrecision precision;
+
+ gint bpc = imageInfo->BitsPerPixel /
+ imageInfo->SamplesPerPixel;
+
+
+#ifdef _DEBUG
+ logBegin (imageInfo, clientData);
+#endif
+
+ /* Decide on the image type */
+ switch (imageInfo->PixelType)
+ {
+ case TWPT_BW:
+ /* Set up the image and layer types */
+ imageType = GIMP_GRAY;
+ layerType = GIMP_GRAY_IMAGE;
+ precision = GIMP_PRECISION_U8_GAMMA;
+ format = babl_format ("Y' u8");
+ break;
+
+ case TWPT_GRAY:
+ /* Set up the image and layer types */
+ imageType = GIMP_GRAY;
+ layerType = GIMP_GRAY_IMAGE;
+
+ switch (bpc)
+ {
+ case 8:
+ precision = GIMP_PRECISION_U8_GAMMA;
+ format = babl_format ("Y' u8");
+ break;
+
+ case 16:
+ precision = GIMP_PRECISION_U16_GAMMA;
+ format = babl_format ("Y' u16");
+ break;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ case TWPT_RGB:
+ /* Set up the image and layer types */
+ imageType = GIMP_RGB;
+ layerType = GIMP_RGB_IMAGE;
+
+ switch (bpc)
+ {
+ case 8:
+ precision = GIMP_PRECISION_U8_GAMMA;
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case 16:
+ precision = GIMP_PRECISION_U16_GAMMA;
+ format = babl_format ("R'G'B' u16");
+ break;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ case TWPT_PALETTE:
+ /* Get the palette data */
+ theClientData->paletteData = g_new (TW_PALETTE8, 1);
+ twSession->twRC = callDSM (APP_IDENTITY (twSession),
+ DS_IDENTITY (twSession),
+ DG_IMAGE, DAT_PALETTE8, MSG_GET,
+ (TW_MEMREF) theClientData->paletteData);
+ if (twSession->twRC != TWRC_SUCCESS)
+ return FALSE;
+
+ switch (theClientData->paletteData->PaletteType)
+ {
+ case TWPA_RGB:
+ /* Set up the image and layer types */
+ imageType = GIMP_RGB;
+ layerType = GIMP_RGB_IMAGE;
+ precision = GIMP_PRECISION_U8_GAMMA;
+
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case TWPA_GRAY:
+ /* Set up the image and layer types */
+ imageType = GIMP_GRAY;
+ layerType = GIMP_GRAY_IMAGE;
+ precision = GIMP_PRECISION_U8_GAMMA;
+
+ format = babl_format ("Y' u8");
+ break;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ default:
+ /* We don't know how to deal with anything other than
+ * the types listed above. Bail for any other image
+ * type.
+ */
+ return FALSE;
+ }
+
+ /* Create the GIMP image */
+ theClientData->image_id = gimp_image_new_with_precision (
+ imageInfo->ImageWidth,
+ imageInfo->ImageLength,
+ imageType,
+ precision);
+
+ /* Set the actual resolution */
+ gimp_image_set_resolution (theClientData->image_id,
+ FIX32ToFloat (imageInfo->XResolution),
+ FIX32ToFloat (imageInfo->YResolution));
+ gimp_image_set_unit (theClientData->image_id, GIMP_UNIT_INCH);
+
+ /* Create a layer */
+ theClientData->layer_id = gimp_layer_new (theClientData->image_id,
+ _("Background"),
+ imageInfo->ImageWidth,
+ imageInfo->ImageLength,
+ layerType, 100,
+ GIMP_LAYER_MODE_NORMAL);
+
+ /* Add the layer to the image */
+ gimp_image_insert_layer (theClientData->image_id,
+ theClientData->layer_id, -1, 0);
+
+ /* Update the progress dialog */
+ theClientData->totalPixels = imageInfo->ImageWidth * imageInfo->ImageLength;
+ theClientData->completedPixels = 0;
+
+ gimp_progress_update (0.0);
+
+ theClientData->buffer = gimp_drawable_get_buffer (theClientData->layer_id);
+ theClientData->format = format;
+
+ /* Store our client data for the data transfer callbacks */
+ if (clientData)
+ g_free (clientData);
+
+ setClientData (twSession, (void *) theClientData);
+
+ /* Make sure to return TRUE to continue the image
+ * transfer
+ */
+ return TRUE;
+}
+
+/*
+ * bitTransferCallback
+ *
+ * The following function is called for each memory
+ * block that is transferred from the data source if
+ * the image type is Black/White.
+ *
+ * Black and white data is unpacked from bit data
+ * into byte data and written into a gray scale GIMP
+ * image.
+ */
+static char bitMasks[] = { 128, 64, 32, 16, 8, 4, 2, 1 };
+static int
+bitTransferCallback (pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+ int row, col, offset;
+ char *srcBuf;
+ char *destBuf;
+ int rows = imageMemXfer->Rows;
+ int cols = imageMemXfer->Columns;
+ pClientDataStruct theClientData = (pClientDataStruct) clientData;
+
+ /* Allocate a buffer as necessary */
+ destBuf = gegl_scratch_new (char, rows * cols);
+
+ /* Unpack the image data from bits into bytes */
+ srcBuf = (char *) imageMemXfer->Memory.TheMem;
+ offset = 0;
+ for (row = 0; row < rows; row++)
+ {
+ for (col = 0; col < cols; col++)
+ {
+ char byte = srcBuf[(row * imageMemXfer->BytesPerRow) + (col / 8)];
+ destBuf[offset++] = ((byte & bitMasks[col % 8]) != 0) ? 255 : 0;
+ }
+ }
+
+ /* Update the complete chunk */
+ gegl_buffer_set (theClientData->buffer,
+ GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset,
+ cols, rows), 0,
+ theClientData->format, destBuf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* Free the buffer */
+ gegl_scratch_free (destBuf);
+
+ /* Update the user on our progress */
+ theClientData->completedPixels += (cols * rows);
+ gimp_progress_update ((double) theClientData->completedPixels /
+ (double) theClientData->totalPixels);
+
+ return TRUE;
+}
+
+/*
+ * directTransferCallback
+ *
+ * The following function is called for each memory
+ * block that is transferred from the data source if
+ * the image type is Grayscale or RGB.
+ */
+static int
+directTransferCallback (pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+ int rows = imageMemXfer->Rows;
+ int cols = imageMemXfer->Columns;
+ pClientDataStruct theClientData = (pClientDataStruct) clientData;
+
+ /* Update the complete chunk */
+ gegl_buffer_set (theClientData->buffer,
+ GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset,
+ cols, rows), 0,
+ theClientData->format, imageMemXfer->Memory.TheMem,
+ imageMemXfer->BytesPerRow);
+
+ /* Update the user on our progress */
+ theClientData->completedPixels += (cols * rows);
+ gimp_progress_update ((double) theClientData->completedPixels /
+ (double) theClientData->totalPixels);
+
+ return TRUE;
+}
+
+/*
+ * palettedTransferCallback
+ *
+ * The following function is called for each memory
+ * block that is transferred from the data source if
+ * the image type is paletted. This does not create
+ * an indexed image type in GIMP because for some
+ * reason it does not allow creation of a specific
+ * palette. This function will create an RGB or Gray
+ * image and use the palette to set the details of
+ * the pixels.
+ */
+static int
+palettedTransferCallback (pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+ int channelsPerEntry;
+ int row, col;
+ int rows = imageMemXfer->Rows;
+ int cols = imageMemXfer->Columns;
+ char *destBuf;
+ char *destPtr = NULL;
+ char *srcPtr = NULL;
+
+ /* Get the client data */
+ pClientDataStruct theClientData = (pClientDataStruct) clientData;
+
+ /* Look up the palette entry size */
+ channelsPerEntry =
+ (theClientData->paletteData->PaletteType == TWPA_RGB) ? 3 : 1;
+
+ /* Allocate a buffer as necessary */
+ destBuf = gegl_scratch_new (char, rows * cols * channelsPerEntry);
+
+ /* Work through the rows */
+ destPtr = destBuf;
+ for (row = 0; row < rows; row++)
+ {
+ srcPtr = (char *) ((char *) imageMemXfer->Memory.TheMem +
+ (row * imageMemXfer->BytesPerRow));
+
+ /* Work through the columns */
+ for (col = 0; col < cols; col++)
+ {
+ /* Get the palette index */
+ int index = (unsigned char) *srcPtr;
+
+ srcPtr++;
+
+ switch (theClientData->paletteData->PaletteType)
+ {
+ case TWPA_GRAY:
+ *destPtr = theClientData->paletteData->Colors[index].Channel1;
+ destPtr++;
+ break;
+
+ case TWPA_RGB:
+ *destPtr = theClientData->paletteData->Colors[index].Channel1;
+ destPtr++;
+ *destPtr = theClientData->paletteData->Colors[index].Channel2;
+ destPtr++;
+ *destPtr = theClientData->paletteData->Colors[index].Channel3;
+ destPtr++;
+ }
+ }
+ }
+
+ /* Send the complete chunk */
+ gegl_buffer_set (theClientData->buffer,
+ GEGL_RECTANGLE (imageMemXfer->XOffset, imageMemXfer->YOffset,
+ cols, rows), 0,
+ theClientData->format, destBuf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ /* Free the buffer */
+ gegl_scratch_free (destBuf);
+
+ /* Update the user on our progress */
+ theClientData->completedPixels += (cols * rows);
+ gimp_progress_update ((double) theClientData->completedPixels /
+ (double) theClientData->totalPixels);
+
+ return TRUE;
+}
+
+/*
+ * dataTransferCallback
+ *
+ * The following function is called for each memory
+ * block that is transferred from the data source.
+ */
+int
+dataTransferCallback (pTW_IMAGEINFO imageInfo,
+ pTW_IMAGEMEMXFER imageMemXfer,
+ void *clientData)
+{
+#ifdef _DEBUG
+ logData (imageInfo, imageMemXfer, clientData);
+#endif
+
+ /* Choose the appropriate transfer handler */
+ switch (imageInfo->PixelType)
+ {
+ case TWPT_PALETTE:
+ return palettedTransferCallback (imageInfo, imageMemXfer, clientData);
+
+ case TWPT_BW:
+ return bitTransferCallback (imageInfo, imageMemXfer, clientData);
+
+ case TWPT_GRAY:
+ case TWPT_RGB:
+ return directTransferCallback (imageInfo, imageMemXfer, clientData);
+
+ default:
+ return FALSE;
+ }
+}
+
+/*
+ * endTransferCallback
+ *
+ * The following function is called at the end of the
+ * image transfer. The caller will be handed
+ * the image transfer completion state. The
+ * following values (defined in twain.h) are
+ * possible:
+ *
+ * TWRC_XFERDONE
+ * The transfer completed successfully
+ * TWRC_CANCEL
+ * The transfer was completed by the user
+ * TWRC_FAILURE
+ * The transfer failed.
+ */
+int
+endTransferCallback (int completionState,
+ int pendingCount,
+ void *clientData)
+{
+ pClientDataStruct theClientData = (pClientDataStruct) clientData;
+
+ LogMessage ("endTransferCallback: CompState = %d, pending = %d\n",
+ completionState, pendingCount);
+
+ /* Clean up and detach from the drawable */
+ g_object_unref (theClientData->buffer);
+
+ /* Make sure to check our return code */
+ if (completionState == TWRC_XFERDONE)
+ {
+ /* We have a completed image transfer */
+ values[2].type = GIMP_PDB_INT32ARRAY;
+ values[2].data.d_int32array[values[1].data.d_int32++] =
+ theClientData->image_id;
+
+ /* Display the image */
+ LogMessage ("Displaying image %d\n", theClientData->image_id);
+ gimp_display_new (theClientData->image_id);
+ }
+ else
+ {
+ /* The transfer did not complete successfully */
+ LogMessage ("Deleting image\n");
+ gimp_image_delete (theClientData->image_id);
+ }
+
+ /* Shut down if we have received all of the possible images */
+ return (values[1].data.d_int32 < MAX_IMAGES);
+}
+
+/*
+ * postTransferCallback
+ *
+ * This callback function will be called
+ * after all possible images have been
+ * transferred.
+ */
+void
+postTransferCallback (int pendingCount,
+ void *clientData)
+{
+ /* Shut things down. */
+ if (pendingCount != 0)
+ cancelPendingTransfers(twSession);
+
+ /* This will close the datasource and datasource
+ * manager. Then the message queue will be shut
+ * down and the run() procedure will finally be
+ * able to finish.
+ */
+ disableDS (twSession);
+ closeDS (twSession);
+ closeDSM (twSession);
+
+ /* Post a message to close up the application */
+ twainQuitApplication ();
+}
diff --git a/plug-ins/twain/twain.h b/plug-ins/twain/twain.h
new file mode 100644
index 0000000..af00295
--- /dev/null
+++ b/plug-ins/twain/twain.h
@@ -0,0 +1,1819 @@
+/* ======================================================================== *\
+
+ Copyright (C) 1991, 1992 TWAIN Working Group: Aldus, Caere, Eastman-Kodak,
+ Hewlett-Packard and Logitech Corporations. All rights reserved.
+
+ Copyright (C) 1997 TWAIN Working Group: Bell+Howell, Canon, DocuMagix,
+ Fujitsu, Genoa Technology, Hewlett-Packard, Kofax Imaging Products, and
+ Ricoh Corporation. All rights reserved.
+
+ Copyright © 1998 TWAIN Working Group: Adobe Systems Incorporated,
+ Canon Information Systems, Eastman Kodak Company,
+ Fujitsu Computer Products of America, Genoa Technology,
+ Hewlett-Packard Company, Intel Corporation, Kofax Image Products,
+ JFL Peripheral Solutions Inc., Ricoh Corporation, and Xerox Corporation.
+ All rights reserved.
+
+ TWAIN.h - This is the definitive include file for applications and
+ data sources written to the TWAIN specification.
+ It defines constants, data structures, messages etc.
+ for the public interface to TWAIN.
+
+ Revision History:
+ version 1.0, March 6, 1992. TWAIN 1.0.
+ version 1.1, January 1993. Tech Notes 1.1
+ version 1.5, June 1993. Specification Update 1.5
+ Change DC to TW
+ Change filename from DC.H to TWAIN.H
+ version 1.5, July 1993. Remove spaces from country identifiers
+
+ version 1.7, July 1997 Added Capabilities and data structure for
+ document imaging and digital cameras.
+ KHL.
+ version 1.7, July 1997 Inserted Borland compatibile structure packing
+ directives provided by Mentor. JMH
+ version 1.7, Aug 1997 Expanded file tabs to spaces.
+ NOTE: future authors should be sure to have
+ their editors set to automatically expand tabs
+ to spaces (original tab setting was 4 spaces).
+ version 1.7, Sept 1997 Added job control values
+ Added return codes
+ version 1.7, Sept 1997 changed definition of pRGBRESPONSE to
+ pTW_RGBRESPONSE
+ version 1.7 Aug 1998 Added missing TWEI_BARCODEROTATION values
+ TWBCOR_ types JMH
+ version 1.8 August 1998 Added new types and definitions required
+ for 1.8 Specification JMH
+\* ======================================================================== */
+
+#ifndef TWAIN
+#define TWAIN
+
+/* SDH - 02/08/95 - TWUNK */
+/* Force 32-bit twain to use same packing of twain structures as existing */
+/* 16-bit twain. This allows 16/32-bit thunking. */
+#ifdef WIN32
+ #ifdef __BORLANDC__ /* (Mentor June 13, 1996) if using a Borland compiler */
+ #pragma option -a2 /*(Mentor June 13, 1996) switch to word alignment*/
+ #else /*(Mentor June 13, 1996) if we're using some other compiler*/
+ #pragma pack (push, before_twain)
+ #pragma pack (2)
+ #endif /*(Mentor June 13, 1996)*/
+#else /* WIN32 */
+#endif /* WIN32 */
+
+/****************************************************************************
+ * TWAIN Version *
+ ****************************************************************************/
+#define TWON_PROTOCOLMINOR 8 /* Changed for Version 1.8 */
+#define TWON_PROTOCOLMAJOR 1
+
+/****************************************************************************
+ * Platform Dependent Definitions and Typedefs *
+ ****************************************************************************/
+
+/* Define one of the following, depending on the platform */
+/* #define _MAC_ */
+/* #define _UNIX_ */
+#define _MSWIN_
+
+#ifdef _MSWIN_
+ typedef HANDLE TW_HANDLE;
+ typedef LPVOID TW_MEMREF;
+
+ /* SDH - 05/05/95 - TWUNK */
+ /* For common code between 16 and 32 bits. */
+ #ifdef WIN32
+ #define TW_HUGE
+ #else /* WIN32 */
+ #define TW_HUGE huge
+ #endif /* WIN32 */
+ typedef BYTE TW_HUGE * HPBYTE;
+ typedef void TW_HUGE * HPVOID;
+#endif /* _MSWIN_ */
+
+#ifdef _MAC_
+ #define PASCAL pascal
+ #define FAR
+ typedef Handle TW_HANDLE;
+ typedef char *TW_MEMREF;
+#endif /* _MAC_ */
+
+#ifdef _UNIX_
+ #define PASCAL pascal
+ typedef unsigned char *TW_HANDLE;
+ typedef unsigned char *TW_MEMREF;
+#endif /* _UNIX_ */
+
+/****************************************************************************
+ * Type Definitions *
+ ****************************************************************************/
+
+/* String types. These include room for the strings and a NULL char, *
+ * or, on the Mac, a length byte followed by the string. *
+ * TW_STR255 must hold less than 256 chars so length fits in first byte. */
+typedef char TW_STR32[34], FAR *pTW_STR32;
+typedef char TW_STR64[66], FAR *pTW_STR64;
+typedef char TW_STR128[130], FAR *pTW_STR128;
+typedef char TW_STR255[256], FAR *pTW_STR255;
+
+/* Numeric types. */
+typedef char TW_INT8, FAR *pTW_INT8;
+typedef short TW_INT16, FAR *pTW_INT16;
+typedef long TW_INT32, FAR *pTW_INT32;
+typedef unsigned char TW_UINT8, FAR *pTW_UINT8;
+typedef unsigned short TW_UINT16, FAR *pTW_UINT16;
+typedef unsigned long TW_UINT32, FAR *pTW_UINT32;
+typedef unsigned short TW_BOOL, FAR *pTW_BOOL;
+
+/* Fixed point structure type. */
+typedef struct {
+ TW_INT16 Whole; /* maintains the sign */
+ TW_UINT16 Frac;
+} TW_FIX32, FAR *pTW_FIX32;
+
+/****************************************************************************
+ * Structure Definitions *
+ ****************************************************************************/
+
+/* No DAT needed. */
+typedef struct {
+ TW_FIX32 X;
+ TW_FIX32 Y;
+ TW_FIX32 Z;
+} TW_CIEPOINT, FAR * pTW_CIEPOINT;
+
+/* No DAT needed. */
+typedef struct {
+ TW_FIX32 StartIn;
+ TW_FIX32 BreakIn;
+ TW_FIX32 EndIn;
+ TW_FIX32 StartOut;
+ TW_FIX32 BreakOut;
+ TW_FIX32 EndOut;
+ TW_FIX32 Gamma;
+ TW_FIX32 SampleCount; /* if =0 use the gamma */
+} TW_DECODEFUNCTION, FAR * pTW_DECODEFUNCTION;
+
+/* No DAT needed. */
+typedef struct {
+ TW_UINT8 Index; /* Value used to index into the color table. */
+ TW_UINT8 Channel1; /* First tri-stimulus value (e.g Red) */
+ TW_UINT8 Channel2; /* Second tri-stimulus value (e.g Green) */
+ TW_UINT8 Channel3; /* Third tri-stimulus value (e.g Blue) */
+} TW_ELEMENT8, FAR * pTW_ELEMENT8;
+
+/* No DAT. Defines a frame rectangle in ICAP_UNITS coordinates. */
+typedef struct {
+ TW_FIX32 Left;
+ TW_FIX32 Top;
+ TW_FIX32 Right;
+ TW_FIX32 Bottom;
+} TW_FRAME, FAR * pTW_FRAME;
+
+/* No DAT needed. Used to manage memory buffers. */
+typedef struct {
+ TW_UINT32 Flags; /* Any combination of the TWMF_ constants. */
+ TW_UINT32 Length; /* Number of bytes stored in buffer TheMem. */
+ TW_MEMREF TheMem; /* Pointer or handle to the allocated memory buffer. */
+} TW_MEMORY, FAR * pTW_MEMORY;
+
+/* No DAT needed. */
+typedef struct {
+ TW_DECODEFUNCTION Decode[3];
+ TW_FIX32 Mix[3][3];
+} TW_TRANSFORMSTAGE, FAR * pTW_TRANSFORMSTAGE;
+
+/* No DAT needed. Describes version of software currently running. */
+typedef struct {
+ TW_UINT16 MajorNum; /* Major revision number of the software. */
+ TW_UINT16 MinorNum; /* Incremental revision number of the software. */
+ TW_UINT16 Language; /* e.g. TWLG_SWISSFRENCH */
+ TW_UINT16 Country; /* e.g. TWCY_SWITZERLAND */
+ TW_STR32 Info; /* e.g. "1.0b3 Beta release" */
+} TW_VERSION, FAR * pTW_VERSION;
+
+/* TWON_ARRAY. Container for array of values (a simplified TW_ENUMERATION) */
+typedef struct {
+ TW_UINT16 ItemType;
+ TW_UINT32 NumItems; /* How many items in ItemList */
+ TW_UINT8 ItemList[1]; /* Array of ItemType values starts here */
+} TW_ARRAY, FAR * pTW_ARRAY;
+
+/* TWON_ENUMERATION. Container for a collection of values. */
+typedef struct {
+ TW_UINT16 ItemType;
+ TW_UINT32 NumItems; /* How many items in ItemList */
+ TW_UINT32 CurrentIndex; /* Current value is in ItemList[CurrentIndex] */
+ TW_UINT32 DefaultIndex; /* Powerup value is in ItemList[DefaultIndex] */
+ TW_UINT8 ItemList[1]; /* Array of ItemType values starts here */
+} TW_ENUMERATION, FAR * pTW_ENUMERATION;
+
+/* TWON_ONEVALUE. Container for one value. */
+typedef struct {
+ TW_UINT16 ItemType;
+ TW_UINT32 Item;
+} TW_ONEVALUE, FAR * pTW_ONEVALUE;
+
+/* TWON_RANGE. Container for a range of values. */
+typedef struct {
+ TW_UINT16 ItemType;
+ TW_UINT32 MinValue; /* Starting value in the range. */
+ TW_UINT32 MaxValue; /* Final value in the range. */
+ TW_UINT32 StepSize; /* Increment from MinValue to MaxValue. */
+ TW_UINT32 DefaultValue; /* Power-up value. */
+ TW_UINT32 CurrentValue; /* The value that is currently in effect. */
+} TW_RANGE, FAR * pTW_RANGE;
+
+/* DAT_CAPABILITY. Used by application to get/set capability from/in a data source. */
+typedef struct {
+ TW_UINT16 Cap; /* id of capability to set or get, e.g. CAP_BRIGHTNESS */
+ TW_UINT16 ConType; /* TWON_ONEVALUE, _RANGE, _ENUMERATION or _ARRAY */
+ TW_HANDLE hContainer; /* Handle to container of type Dat */
+} TW_CAPABILITY, FAR * pTW_CAPABILITY;
+
+/* DAT_CIECOLOR. */
+typedef struct {
+ TW_UINT16 ColorSpace;
+ TW_INT16 LowEndian;
+ TW_INT16 DeviceDependent;
+ TW_INT32 VersionNumber;
+ TW_TRANSFORMSTAGE StageABC;
+ TW_TRANSFORMSTAGE StageLMN;
+ TW_CIEPOINT WhitePoint;
+ TW_CIEPOINT BlackPoint;
+ TW_CIEPOINT WhitePaper;
+ TW_CIEPOINT BlackInk;
+ TW_FIX32 Samples[1];
+} TW_CIECOLOR, FAR * pTW_CIECOLOR;
+
+/* DAT_EVENT. For passing events down from the application to the DS. */
+typedef struct {
+ TW_MEMREF pEvent; /* Windows pMSG or Mac pEvent. */
+ TW_UINT16 TWMessage; /* TW msg from data source, e.g. MSG_XFERREADY */
+} TW_EVENT, FAR * pTW_EVENT;
+
+/* DAT_GRAYRESPONSE */
+typedef struct {
+ TW_ELEMENT8 Response[1];
+} TW_GRAYRESPONSE, FAR * pTW_GRAYRESPONSE;
+
+/* DAT_IDENTITY. Identifies the program/library/code resource. */
+typedef struct {
+ TW_UINT32 Id; /* Unique number. In Windows, application hWnd */
+ TW_VERSION Version; /* Identifies the piece of code */
+ TW_UINT16 ProtocolMajor; /* Application and DS must set to TWON_PROTOCOLMAJOR */
+ TW_UINT16 ProtocolMinor; /* Application and DS must set to TWON_PROTOCOLMINOR */
+ TW_UINT32 SupportedGroups; /* Bit field OR combination of DG_ constants */
+ TW_STR32 Manufacturer; /* Manufacturer name, e.g. "Hewlett-Packard" */
+ TW_STR32 ProductFamily; /* Product family name, e.g. "ScanJet" */
+ TW_STR32 ProductName; /* Product name, e.g. "ScanJet Plus" */
+} TW_IDENTITY, FAR * pTW_IDENTITY;
+
+/* DAT_IMAGEINFO. Application gets detailed image info from DS with this. */
+typedef struct {
+ TW_FIX32 XResolution; /* Resolution in the horizontal */
+ TW_FIX32 YResolution; /* Resolution in the vertical */
+ TW_INT32 ImageWidth; /* Columns in the image, -1 if unknown by DS*/
+ TW_INT32 ImageLength; /* Rows in the image, -1 if unknown by DS */
+ TW_INT16 SamplesPerPixel; /* Number of samples per pixel, 3 for RGB */
+ TW_INT16 BitsPerSample[8]; /* Number of bits for each sample */
+ TW_INT16 BitsPerPixel; /* Number of bits for each padded pixel */
+ TW_BOOL Planar; /* True if Planar, False if chunky */
+ TW_INT16 PixelType; /* How to interp data; photo interp (TWPT_) */
+ TW_UINT16 Compression; /* How the data is compressed (TWCP_xxxx) */
+} TW_IMAGEINFO, FAR * pTW_IMAGEINFO;
+
+/* DAT_IMAGELAYOUT. Provides image layout information in current units. */
+typedef struct {
+ TW_FRAME Frame; /* Frame coords within larger document */
+ TW_UINT32 DocumentNumber;
+ TW_UINT32 PageNumber; /* Reset when you go to next document */
+ TW_UINT32 FrameNumber; /* Reset when you go to next page */
+} TW_IMAGELAYOUT, FAR * pTW_IMAGELAYOUT;
+
+/* DAT_IMAGEMEMXFER. Used to pass image data (e.g. in strips) from DS to application.*/
+typedef struct {
+ TW_UINT16 Compression; /* How the data is compressed */
+ TW_UINT32 BytesPerRow; /* Number of bytes in a row of data */
+ TW_UINT32 Columns; /* How many columns */
+ TW_UINT32 Rows; /* How many rows */
+ TW_UINT32 XOffset; /* How far from the side of the image */
+ TW_UINT32 YOffset; /* How far from the top of the image */
+ TW_UINT32 BytesWritten; /* How many bytes written in Memory */
+ TW_MEMORY Memory; /* Mem struct used to pass actual image data */
+} TW_IMAGEMEMXFER, FAR * pTW_IMAGEMEMXFER;
+
+/* Changed in 1.1: QuantTable, HuffmanDC, HuffmanAC TW_MEMREF -> TW_MEMORY */
+/* DAT_JPEGCOMPRESSION. Based on JPEG Draft International Std, ver 10918-1. */
+typedef struct {
+ TW_UINT16 ColorSpace; /* One of the TWPT_xxxx values */
+ TW_UINT32 SubSampling; /* Two word "array" for subsampling values */
+ TW_UINT16 NumComponents; /* Number of color components in image */
+ TW_UINT16 RestartFrequency; /* Frequency of restart marker codes in MDU's */
+ TW_UINT16 QuantMap[4]; /* Mapping of components to QuantTables */
+ TW_MEMORY QuantTable[4]; /* Quantization tables */
+ TW_UINT16 HuffmanMap[4]; /* Mapping of components to Huffman tables */
+ TW_MEMORY HuffmanDC[2]; /* DC Huffman tables */
+ TW_MEMORY HuffmanAC[2]; /* AC Huffman tables */
+} TW_JPEGCOMPRESSION, FAR * pTW_JPEGCOMPRESSION;
+
+/* DAT_PALETTE8. Color palette when TWPT_PALETTE pixels xfer'd in mem buf. */
+typedef struct {
+ TW_UINT16 NumColors; /* Number of colors in the color table. */
+ TW_UINT16 PaletteType; /* TWPA_xxxx, specifies type of palette. */
+ TW_ELEMENT8 Colors[256]; /* Array of palette values starts here. */
+} TW_PALETTE8, FAR * pTW_PALETTE8;
+
+/* DAT_PENDINGXFERS. Used with MSG_ENDXFER to indicate additional data. */
+typedef struct {
+ TW_UINT16 Count;
+ union {
+ TW_UINT32 EOJ;
+ TW_UINT32 Reserved;
+ };
+} TW_PENDINGXFERS, FAR *pTW_PENDINGXFERS;
+
+/* DAT_RGBRESPONSE */
+typedef struct {
+ TW_ELEMENT8 Response[1];
+} TW_RGBRESPONSE, FAR * pTW_RGBRESPONSE;
+
+/* DAT_SETUPFILEXFER. Sets up DS to application data transfer via a file. */
+typedef struct {
+ TW_STR255 FileName;
+ TW_UINT16 Format; /* Any TWFF_ constant */
+ TW_INT16 VRefNum; /* Used for Mac only */
+} TW_SETUPFILEXFER, FAR * pTW_SETUPFILEXFER;
+
+/* DAT_SETUPMEMXFER. Sets up DS to application data transfer via a memory buffer. */
+typedef struct {
+ TW_UINT32 MinBufSize;
+ TW_UINT32 MaxBufSize;
+ TW_UINT32 Preferred;
+} TW_SETUPMEMXFER, FAR * pTW_SETUPMEMXFER;
+
+/* DAT_STATUS. Application gets detailed status info from a data source with this. */
+typedef struct {
+ TW_UINT16 ConditionCode; /* Any TWCC_ constant */
+ TW_UINT16 Reserved; /* Future expansion space */
+} TW_STATUS, FAR * pTW_STATUS;
+
+/* DAT_USERINTERFACE. Coordinates UI between application and data source. */
+typedef struct {
+ TW_BOOL ShowUI; /* TRUE if DS should bring up its UI */
+ TW_BOOL ModalUI; /* For Mac only - true if the DS's UI is modal */
+ TW_HANDLE hParent; /* For windows only - Application window handle */
+} TW_USERINTERFACE, FAR * pTW_USERINTERFACE;
+
+/* SDH - 03/21/95 - TWUNK */
+/* DAT_TWUNKIDENTITY. Provides DS identity and 'other' information necessary */
+/* across thunk link. */
+typedef struct {
+ TW_IDENTITY identity; /* Identity of data source. */
+ TW_STR255 dsPath; /* Full path and file name of data source. */
+} TW_TWUNKIDENTITY, FAR * pTW_TWUNKIDENTITY;
+
+/* SDH - 03/21/95 - TWUNK */
+/* Provides DS_Entry parameters over thunk link. */
+typedef struct
+{
+ TW_INT8 destFlag; /* TRUE if dest is not NULL */
+ TW_IDENTITY dest; /* Identity of data source (if used) */
+ TW_INT32 dataGroup; /* DSM_Entry dataGroup parameter */
+ TW_INT16 dataArgType; /* DSM_Entry dataArgType parameter */
+ TW_INT16 message; /* DSM_Entry message parameter */
+ TW_INT32 pDataSize; /* Size of pData (0 if NULL) */
+ /* TW_MEMREF pData; Based on implementation specifics, a */
+ /* pData parameter makes no sense in this */
+ /* structure, but data (if provided) will be*/
+ /* appended in the data block. */
+ } TW_TWUNKDSENTRYPARAMS, FAR * pTW_TWUNKDSENTRYPARAMS;
+
+/* SDH - 03/21/95 - TWUNK */
+/* Provides DS_Entry results over thunk link. */
+typedef struct
+{
+ TW_UINT16 returnCode; /* Thunker DsEntry return code. */
+ TW_UINT16 conditionCode; /* Thunker DsEntry condition code. */
+ TW_INT32 pDataSize; /* Size of pData (0 if NULL) */
+ /* TW_MEMREF pData; Based on implementation specifics, a */
+ /* pData parameter makes no sense in this */
+ /* structure, but data (if provided) will be*/
+ /* appended in the data block. */
+} TW_TWUNKDSENTRYRETURN, FAR * pTW_TWUNKDSENTRYRETURN;
+
+/* WJD - 950818 */
+/* Added for 1.6 Specification */
+/* TWAIN 1.6 CAP_SUPPORTEDCAPSEXT structure */
+typedef struct
+{
+ TW_UINT16 Cap; /* Which CAP/ICAP info is relevant to */
+ TW_UINT16 Properties; /* Messages this CAP/ICAP supports */
+} TW_CAPEXT, FAR * pTW_CAPEXT;
+
+/* ----------------------------------------------------------------------- *\
+
+ Version 1.7: Added Following data structure for Document Imaging
+ July 1997 Enhancement.
+ KHL TW_CUSTOMDSDATA -- For Saving and Restoring Source's
+ state.
+ TW_INFO -- Each attribute for extended image
+ information.
+ TW_EXTIMAGEINFO -- Extended image information structure.
+
+\* ----------------------------------------------------------------------- */
+
+typedef struct {
+ TW_UINT32 InfoLength; /* Length of Information in bytes. */
+ TW_HANDLE hData; /* Place holder for data, DS Allocates */
+}TW_CUSTOMDSDATA, FAR *pTW_CUSTOMDSDATA;
+
+typedef struct {
+ TW_UINT16 InfoID;
+ TW_UINT16 ItemType;
+ TW_UINT16 NumItems;
+ TW_UINT16 CondCode;
+ TW_UINT32 Item;
+}TW_INFO, FAR* pTW_INFO;
+
+typedef struct {
+ TW_UINT32 NumInfos;
+ TW_INFO Info[1];
+}TW_EXTIMAGEINFO, FAR* pTW_EXTIMAGEINFO;
+
+/* Added 1.8 */
+
+/* DAT_AUDIOINFO, information about audio data */
+typedef struct {
+ TW_STR255 Name; /* name of audio data */
+ TW_UINT32 Reserved; /* reserved space */
+} TW_AUDIOINFO, FAR * pTW_AUDIOINFO;
+
+/* DAT_DEVICEEVENT, information about events */
+typedef struct {
+ TW_UINT32 Event; /* One of the TWDE_xxxx values. */
+ TW_STR255 DeviceName; /* The name of the device that generated the event */
+ TW_UINT32 BatteryMinutes; /* Battery Minutes Remaining */
+ TW_INT16 BatteryPercentage; /* Battery Percentage Remaining */
+ TW_INT32 PowerSupply; /* Power Supply */
+ TW_FIX32 XResolution; /* Resolution */
+ TW_FIX32 YResolution; /* Resolution */
+ TW_UINT32 FlashUsed2; /* Flash Used2 */
+ TW_UINT32 AutomaticCapture; /* Automatic Capture */
+ TW_UINT32 TimeBeforeFirstCapture; /* Automatic Capture */
+ TW_UINT32 TimeBetweenCaptures; /* Automatic Capture */
+} TW_DEVICEEVENT, FAR * pTW_DEVICEEVENT;
+
+/* DAT_FILESYSTEM, information about TWAIN file system */
+typedef struct {
+ /* DG_CONTROL / DAT_FILESYSTEM / MSG_xxxx fields */
+ TW_STR255 InputName; /* The name of the input or source file */
+ TW_STR255 OutputName; /* The result of an operation or the name of a destination file */
+ TW_MEMREF Context; /* Source specific data used to remember state information */
+ /* DG_CONTROL / DAT_FILESYSTEM / MSG_DELETE field */
+ int Recursive; /* recursively delete all sub-directories */
+ /* DG_CONTROL / DAT_FILESYSTEM / MSG_GETINFO fields */
+ TW_INT32 FileType; /* One of the TWFT_xxxx values */
+ TW_UINT32 Size; /* Size of current FileType */
+ TW_STR32 CreateTimeDate; /* creation date of the file */
+ TW_STR32 ModifiedTimeDate; /* last date the file was modified */
+ TW_UINT32 FreeSpace; /* bytes of free space on the current device */
+ TW_INT32 NewImageSize; /* estimate of the amount of space a new image would take up */
+ TW_UINT32 NumberOfFiles; /* number of files, depends on FileType */
+ TW_UINT32 NumberOfSnippets; /**/
+ char Reserved[512]; /**/
+} TW_FILESYSTEM, FAR * pTW_FILESYSTEM;
+
+/* DAT_PASSTHRU, device dependent data to pass through Data Source */
+typedef struct {
+ TW_MEMREF pCommand; /* Pointer to Command buffer */
+ TW_UINT32 CommandBytes; /* Number of bytes in Command buffer */
+ TW_INT32 Direction; /* One of the TWDR_xxxx values. Defines the direction of data flow */
+ TW_MEMREF pData; /* Pointer to Data buffer */
+ TW_UINT32 DataBytes; /* Number of bytes in Data buffer */
+ TW_UINT32 DataBytesXfered; /* Number of bytes successfully transferred */
+} TW_PASSTHRU, FAR * pTW_PASSTHRU;
+
+/* DAT_SETUPAUDIOFILEXFER, information required to setup an audio file transfer */
+typedef struct {
+ TW_STR255 FileName; /* full path target file */
+ TW_UINT16 Format; /* one of TWAF_xxxx */
+ TW_INT16 VRefNum;
+} TW_SETUPAUDIOFILEXFER, FAR * pTW_SETUPAUDIOFILEXFER;
+
+/****************************************************************************
+ * Generic Constants *
+ ****************************************************************************/
+
+#define TWON_ARRAY 3 /* indicates TW_ARRAY container */
+#define TWON_ENUMERATION 4 /* indicates TW_ENUMERATION container */
+#define TWON_ONEVALUE 5 /* indicates TW_ONEVALUE container */
+#define TWON_RANGE 6 /* indicates TW_RANGE container */
+
+#define TWON_ICONID 962 /* res Id of icon used in USERSELECT lbox */
+#define TWON_DSMID 461 /* res Id of the DSM version num resource */
+#define TWON_DSMCODEID 63 /* res Id of the Mac SM Code resource */
+
+#define TWON_DONTCARE8 0xff
+#define TWON_DONTCARE16 0xffff
+#define TWON_DONTCARE32 0xffffffff
+
+/* Flags used in TW_MEMORY structure. */
+#define TWMF_APPOWNS 0x1
+#define TWMF_DSMOWNS 0x2
+#define TWMF_DSOWNS 0x4
+#define TWMF_POINTER 0x8
+#define TWMF_HANDLE 0x10
+
+/* Palette types for TW_PALETTE8 */
+#define TWPA_RGB 0
+#define TWPA_GRAY 1
+#define TWPA_CMY 2
+
+/* There are four containers used for capabilities negotiation:
+ * TWON_ONEVALUE, TWON_RANGE, TWON_ENUMERATION, TWON_ARRAY
+ * In each container structure ItemType can be TWTY_INT8, TWTY_INT16, etc.
+ * The kind of data stored in the container can be determined by doing
+ * DCItemSize[ItemType] where the following is defined in TWAIN glue code:
+ * DCItemSize[]= { sizeof(TW_INT8),
+ * sizeof(TW_INT16),
+ * etc.
+ * sizeof(TW_UINT32) };
+ *
+ */
+
+#define TWTY_INT8 0x0000 /* Means Item is a TW_INT8 */
+#define TWTY_INT16 0x0001 /* Means Item is a TW_INT16 */
+#define TWTY_INT32 0x0002 /* Means Item is a TW_INT32 */
+
+#define TWTY_UINT8 0x0003 /* Means Item is a TW_UINT8 */
+#define TWTY_UINT16 0x0004 /* Means Item is a TW_UINT16 */
+#define TWTY_UINT32 0x0005 /* Means Item is a TW_UINT32 */
+
+#define TWTY_BOOL 0x0006 /* Means Item is a TW_BOOL */
+
+#define TWTY_FIX32 0x0007 /* Means Item is a TW_FIX32 */
+
+#define TWTY_FRAME 0x0008 /* Means Item is a TW_FRAME */
+
+#define TWTY_STR32 0x0009 /* Means Item is a TW_STR32 */
+#define TWTY_STR64 0x000a /* Means Item is a TW_STR64 */
+#define TWTY_STR128 0x000b /* Means Item is a TW_STR128 */
+#define TWTY_STR255 0x000c /* Means Item is a TW_STR255 */
+
+/****************************************************************************
+ * Capability Constants *
+ ****************************************************************************/
+
+/* ICAP_BITORDER values (BO_ means Bit Order) */
+#define TWBO_LSBFIRST 0
+#define TWBO_MSBFIRST 1
+
+/* ICAP_COMPRESSION values (CP_ means ComPression ) */
+#define TWCP_NONE 0
+#define TWCP_PACKBITS 1
+#define TWCP_GROUP31D 2 /* Follows CCITT spec (no End Of Line) */
+#define TWCP_GROUP31DEOL 3 /* Follows CCITT spec (has End Of Line) */
+#define TWCP_GROUP32D 4 /* Follows CCITT spec (use cap for K Factor) */
+#define TWCP_GROUP4 5 /* Follows CCITT spec */
+#define TWCP_JPEG 6 /* Use capability for more info */
+#define TWCP_LZW 7 /* Must license from Unisys and IBM to use */
+#define TWCP_JBIG 8 /* For Bitonal images -- Added 1.7 KHL */
+/* Added 1.8 */
+#define TWCP_PNG 9
+#define TWCP_RLE4 10
+#define TWCP_RLE8 11
+#define TWCP_BITFIELDS 12
+
+
+/* ICAP_IMAGEFILEFORMAT values (FF_means File Format) */
+#define TWFF_TIFF 0 /* Tagged Image File Format */
+#define TWFF_PICT 1 /* Macintosh PICT */
+#define TWFF_BMP 2 /* Windows Bitmap */
+#define TWFF_XBM 3 /* X-Windows Bitmap */
+#define TWFF_JFIF 4 /* JPEG File Interchange Format */
+#define TWFF_FPX 5 /* Flash Pix */
+#define TWFF_TIFFMULTI 6 /* Multi-page tiff file */
+#define TWFF_PNG 7
+#define TWFF_SPIFF 8
+#define TWFF_EXIF 9
+
+
+/* ICAP_FILTER values (FT_ means Filter Type) */
+#define TWFT_RED 0
+#define TWFT_GREEN 1
+#define TWFT_BLUE 2
+#define TWFT_NONE 3
+#define TWFT_WHITE 4
+#define TWFT_CYAN 5
+#define TWFT_MAGENTA 6
+#define TWFT_YELLOW 7
+#define TWFT_BLACK 8
+
+/* ICAP_LIGHTPATH values (LP_ means Light Path) */
+#define TWLP_REFLECTIVE 0
+#define TWLP_TRANSMISSIVE 1
+
+/* ICAP_LIGHTSOURCE values (LS_ means Light Source) */
+#define TWLS_RED 0
+#define TWLS_GREEN 1
+#define TWLS_BLUE 2
+#define TWLS_NONE 3
+#define TWLS_WHITE 4
+#define TWLS_UV 5
+#define TWLS_IR 6
+
+/* ICAP_ORIENTATION values (OR_ means ORientation) */
+#define TWOR_ROT0 0
+#define TWOR_ROT90 1
+#define TWOR_ROT180 2
+#define TWOR_ROT270 3
+#define TWOR_PORTRAIT TWOR_ROT0
+#define TWOR_LANDSCAPE TWOR_ROT270
+
+/* ICAP_PLANARCHUNKY values (PC_ means Planar/Chunky ) */
+#define TWPC_CHUNKY 0
+#define TWPC_PLANAR 1
+
+/* ICAP_PIXELFLAVOR values (PF_ means Pixel Flavor) */
+#define TWPF_CHOCOLATE 0 /* zero pixel represents darkest shade */
+#define TWPF_VANILLA 1 /* zero pixel represents lightest shade */
+
+/* ICAP_PIXELTYPE values (PT_ means Pixel Type) */
+#define TWPT_BW 0 /* Black and White */
+#define TWPT_GRAY 1
+#define TWPT_RGB 2
+#define TWPT_PALETTE 3
+#define TWPT_CMY 4
+#define TWPT_CMYK 5
+#define TWPT_YUV 6
+#define TWPT_YUVK 7
+#define TWPT_CIEXYZ 8
+
+/* ICAP_SUPPORTEDSIZES values (SS_ means Supported Sizes) */
+#define TWSS_NONE 0
+#define TWSS_A4LETTER 1
+#define TWSS_B5LETTER 2
+#define TWSS_USLETTER 3
+#define TWSS_USLEGAL 4
+/* Added 1.5 */
+#define TWSS_A5 5
+#define TWSS_B4 6
+#define TWSS_B6 7
+/*#define TWSS_B 8 */
+/* Added 1.7 */
+#define TWSS_USLEDGER 9
+#define TWSS_USEXECUTIVE 10
+#define TWSS_A3 11
+#define TWSS_B3 12
+#define TWSS_A6 13
+#define TWSS_C4 14
+#define TWSS_C5 15
+#define TWSS_C6 16
+/* Added 1.8 */
+#define TWSS_4A0 17
+#define TWSS_2A0 18
+#define TWSS_A0 19
+#define TWSS_A1 20
+#define TWSS_A2 21
+#define TWSS_A4 TWSS_A4LETTER
+#define TWSS_A7 22
+#define TWSS_A8 23
+#define TWSS_A9 24
+#define TWSS_A10 25
+#define TWSS_ISOB0 26
+#define TWSS_ISOB1 27
+#define TWSS_ISOB2 28
+#define TWSS_ISOB3 TWSS_B3
+#define TWSS_ISOB4 TWSS_B4
+#define TWSS_ISOB5 29
+#define TWSS_ISOB6 TWSS_B6
+#define TWSS_ISOB7 30
+#define TWSS_ISOB8 31
+#define TWSS_ISOB9 32
+#define TWSS_ISOB10 33
+#define TWSS_JISB0 34
+#define TWSS_JISB1 35
+#define TWSS_JISB2 36
+#define TWSS_JISB3 37
+#define TWSS_JISB4 38
+#define TWSS_JISB5 TWSS_B5LETTER
+#define TWSS_JISB6 39
+#define TWSS_JISB7 40
+#define TWSS_JISB8 41
+#define TWSS_JISB9 42
+#define TWSS_JISB10 43
+#define TWSS_C0 44
+#define TWSS_C1 45
+#define TWSS_C2 46
+#define TWSS_C3 47
+#define TWSS_C7 48
+#define TWSS_C8 49
+#define TWSS_C9 50
+#define TWSS_C10 51
+#define TWSS_USSTATEMENT 52
+#define TWSS_BUSINESSCARD 53
+
+/* ICAP_XFERMECH values (SX_ means Setup XFer) */
+#define TWSX_NATIVE 0
+#define TWSX_FILE 1
+#define TWSX_MEMORY 2
+
+/* ICAP_UNITS values (UN_ means UNits) */
+#define TWUN_INCHES 0
+#define TWUN_CENTIMETERS 1
+#define TWUN_PICAS 2
+#define TWUN_POINTS 3
+#define TWUN_TWIPS 4
+#define TWUN_PIXELS 5
+
+/* Added 1.5 */
+/* ICAP_BITDEPTHREDUCTION values (BR_ means Bitdepth Reduction) */
+#define TWBR_THRESHOLD 0
+#define TWBR_HALFTONE 1
+#define TWBR_CUSTHALFTONE 2
+#define TWBR_DIFFUSION 3
+
+/* Added 1.7 */
+/* ICAP_DUPLEX values */
+#define TWDX_NONE 0
+#define TWDX_1PASSDUPLEX 1
+#define TWDX_2PASSDUPLEX 2
+
+/* Added 1.7 */
+/* TWEI_BARCODETYPE values */
+#define TWBT_3OF9 0
+#define TWBT_2OF5INTERLEAVED 1
+#define TWBT_2OF5NONINTERLEAVED 2
+#define TWBT_CODE93 3
+#define TWBT_CODE128 4
+#define TWBT_UCC128 5
+#define TWBT_CODABAR 6
+#define TWBT_UPCA 7
+#define TWBT_UPCE 8
+#define TWBT_EAN8 9
+#define TWBT_EAN13 10
+#define TWBT_POSTNET 11
+#define TWBT_PDF417 12
+/* Added 1.8 */
+#define TWBT_2OF5INDUSTRIAL 13
+#define TWBT_2OF5MATRIX 14
+#define TWBT_2OF5DATALOGIC 15
+#define TWBT_2OF5IATA 16
+#define TWBT_3OF9FULLASCII 17
+#define TWBT_CODABARWITHSTARTSTOP 18
+
+
+/* Added 1.7 */
+/* TWEI_DESKEWSTATUS values */
+#define TWDSK_SUCCESS 0
+#define TWDSK_REPORTONLY 1
+#define TWDSK_FAIL 2
+#define TWDSK_DISABLED 3
+
+/* Added 1.7 */
+/* TWEI_PATCHCODE values */
+#define TWPCH_PATCH1 0
+#define TWPCH_PATCH2 1
+#define TWPCH_PATCH3 2
+#define TWPCH_PATCH4 3
+#define TWPCH_PATCH6 4
+#define TWPCH_PATCHT 5
+
+/* Added 1.7 */
+/* CAP_JOBCONTROL values */
+#define TWJC_NONE 0
+#define TWJC_JSIC 1
+#define TWJC_JSIS 2
+#define TWJC_JSXC 3
+#define TWJC_JSXS 4
+
+/* Added 1.7 */
+/* TWEI_BARCODEROTATION values (BCOR_ means barcode rotation) */
+#define TWBCOR_ROT0 0
+#define TWBCOR_ROT90 1
+#define TWBCOR_ROT180 2
+#define TWBCOR_ROT270 3
+#define TWBCOR_ROTX 4
+
+/* Added 1.8 */
+/* ACAP_AUDIOFILEFORMAT values (AF_ means audio format) */
+#define TWAF_WAV 0
+#define TWAF_AIFF 1
+#define TWAF_AU 3
+#define TWAF_SND 4
+
+/* CAP_ALARMS values (AL_ means alarms) */
+#define TWAL_ALARM 0
+#define TWAL_FEEDERERROR 1
+#define TWAL_FEEDERWARNING 2
+#define TWAL_BARCODE 3
+#define TWAL_DOUBLEFEED 4
+#define TWAL_JAM 5
+#define TWAL_PATCHCODE 6
+#define TWAL_POWER 7
+#define TWAL_SKEW 8
+
+/* CAP_CLEARBUFFERS values (CB_ means clear buffers) */
+#define TWCB_AUTO 0
+#define TWCB_CLEAR 1
+#define TWCB_NOCLEAR 2
+
+/* CAP_DEVICEEVENT values (DE_ means device event) */
+#define TWDE_CUSTOMEVENTS 0x8000
+#define TWDE_CHECKAUTOMATICCAPTURE 0
+#define TWDE_CHECKBATTERY 1
+#define TWDE_CHECKDEVICEONLINE 2
+#define TWDE_CHECKFLASH 3
+#define TWDE_CHECKPOWERSUPPLY 4
+#define TWDE_CHECKRESOLUTION 5
+#define TWDE_DEVICEADDED 6
+#define TWDE_DEVICEOFFLINE 7
+#define TWDE_DEVICEREADY 8
+#define TWDE_DEVICEREMOVED 9
+#define TWDE_IMAGECAPTURED 10
+#define TWDE_IMAGEDELETED 11
+#define TWDE_PAPERDOUBLEFEED 12
+#define TWDE_PAPERJAM 13
+#define TWDE_LAMPFAILURE 14
+#define TWDE_POWERSAVE 15
+#define TWDE_POWERSAVENOTIFY 16
+
+/* CAP_FEEDERALIGNMENT values (FA_ means feeder alignment) */
+#define TWFA_NONE 0
+#define TWFA_LEFT 1
+#define TWFA_CENTER 2
+#define TWFA_RIGHT 3
+
+/* CAP_FEEDERORDER values (FO_ means feeder order) */
+#define TWFO_FIRSTPAGEFIRST 0
+#define TWFO_LASTPAGEFIRST 1
+
+/* CAP_FILESYSTEM values (FS_ means file system) */
+#define TWFS_FILESYSTEM 0
+#define TWFS_RECURSIVEDELETE 1
+
+/* CAP_POWERSUPPLY values (PS_ means power supply) */
+#define TWPS_EXTERNAL 0
+#define TWPS_BATTERY 1
+
+/* CAP_PRINTER values (PR_ means printer) */
+#define TWPR_IMPRINTERTOPBEFORE 0
+#define TWPR_IMPRINTERTOPAFTER 1
+#define TWPR_IMPRINTERBOTTOMBEFORE 2
+#define TWPR_IMPRINTERBOTTOMAFTER 3
+#define TWPR_ENDORSERTOPBEFORE 4
+#define TWPR_ENDORSERTOPAFTER 5
+#define TWPR_ENDORSERBOTTOMBEFORE 6
+#define TWPR_ENDORSERBOTTOMAFTER 7
+
+/* CAP_PRINTERMODE values (PM_ means printer mode) */
+#define TWPM_SINGLESTRING 0
+#define TWPM_MULTISTRING 1
+#define TWPM_COMPOUNDSTRING 2
+
+/* ICAP_BARCODESEARCHMODE values (SRCH_ means search) */
+#define SRCH_HORZ 0
+#define SRCH_VERT 1
+#define SRCH_HORZVERT 2
+#define SRCH_VERTHORZ 3
+
+/* ICAP_FLASHUSED2 values (FL_ means flash) */
+#define TWFL_NONE 0
+#define TWFL_OFF 1
+#define TWFL_ON 2
+#define TWFL_AUTO 3
+#define TWFL_REDEYE 4
+
+/* ICAP_FLIPROTATION values (FR_ means flip rotation) */
+#define TWFR_BOOK 0
+#define TWFR_FANFOLD 1
+
+/* ICAP_IMAGEFILTER values (IF_ means image filter) */
+#define TWIF_NONE 0
+#define TWIF_AUTO 1
+#define TWIF_LOWPASS 2
+#define TWIF_BANDPASS 3
+#define TWIF_HIGHPASS 4
+#define TWIF_TEXT TWIF_BANDPASS
+#define TWIF_FINELINE TWIF_HIGHPASS
+
+/* ICAP_NOISEFILTER values (NF_ means noise filter) */
+#define TWNF_NONE 0
+#define TWNF_AUTO 1
+#define TWNF_LONEPIXEL 2
+#define TWNF_MAJORITYRULE 3
+
+/* ICAP_OVERSCAN values (OV_ means overscan) */
+#define TWOV_NONE 0
+#define TWOV_AUTO 1
+#define TWOV_TOPBOTTOM 2
+#define TWOV_LEFTRIGHT 3
+#define TWOV_ALL 4
+
+/* TW_FILESYSTEM.FileType values (FT_ means file type) */
+#define TWFY_CAMERA 0
+#define TWFY_CAMERATOP 1
+#define TWFY_CAMERABOTTOM 2
+#define TWFY_CAMERAPREVIEW 3
+#define TWFY_DOMAIN 4
+#define TWFY_HOST 5
+#define TWFY_DIRECTORY 6
+#define TWFY_IMAGE 7
+#define TWFY_UNKNOWN 8
+
+/****************************************************************************
+ * Country Constants *
+ ****************************************************************************/
+
+#define TWCY_AFGHANISTAN 1001
+#define TWCY_ALGERIA 213
+#define TWCY_AMERICANSAMOA 684
+#define TWCY_ANDORRA 033
+#define TWCY_ANGOLA 1002
+#define TWCY_ANGUILLA 8090
+#define TWCY_ANTIGUA 8091
+#define TWCY_ARGENTINA 54
+#define TWCY_ARUBA 297
+#define TWCY_ASCENSIONI 247
+#define TWCY_AUSTRALIA 61
+#define TWCY_AUSTRIA 43
+#define TWCY_BAHAMAS 8092
+#define TWCY_BAHRAIN 973
+#define TWCY_BANGLADESH 880
+#define TWCY_BARBADOS 8093
+#define TWCY_BELGIUM 32
+#define TWCY_BELIZE 501
+#define TWCY_BENIN 229
+#define TWCY_BERMUDA 8094
+#define TWCY_BHUTAN 1003
+#define TWCY_BOLIVIA 591
+#define TWCY_BOTSWANA 267
+#define TWCY_BRITAIN 6
+#define TWCY_BRITVIRGINIS 8095
+#define TWCY_BRAZIL 55
+#define TWCY_BRUNEI 673
+#define TWCY_BULGARIA 359
+#define TWCY_BURKINAFASO 1004
+#define TWCY_BURMA 1005
+#define TWCY_BURUNDI 1006
+#define TWCY_CAMAROON 237
+#define TWCY_CANADA 2
+#define TWCY_CAPEVERDEIS 238
+#define TWCY_CAYMANIS 8096
+#define TWCY_CENTRALAFREP 1007
+#define TWCY_CHAD 1008
+#define TWCY_CHILE 56
+#define TWCY_CHINA 86
+#define TWCY_CHRISTMASIS 1009
+#define TWCY_COCOSIS 1009
+#define TWCY_COLOMBIA 57
+#define TWCY_COMOROS 1010
+#define TWCY_CONGO 1011
+#define TWCY_COOKIS 1012
+#define TWCY_COSTARICA 506
+#define TWCY_CUBA 005
+#define TWCY_CYPRUS 357
+#define TWCY_CZECHOSLOVAKIA 42
+#define TWCY_DENMARK 45
+#define TWCY_DJIBOUTI 1013
+#define TWCY_DOMINICA 8097
+#define TWCY_DOMINCANREP 8098
+#define TWCY_EASTERIS 1014
+#define TWCY_ECUADOR 593
+#define TWCY_EGYPT 20
+#define TWCY_ELSALVADOR 503
+#define TWCY_EQGUINEA 1015
+#define TWCY_ETHIOPIA 251
+#define TWCY_FALKLANDIS 1016
+#define TWCY_FAEROEIS 298
+#define TWCY_FIJIISLANDS 679
+#define TWCY_FINLAND 358
+#define TWCY_FRANCE 33
+#define TWCY_FRANTILLES 596
+#define TWCY_FRGUIANA 594
+#define TWCY_FRPOLYNEISA 689
+#define TWCY_FUTANAIS 1043
+#define TWCY_GABON 241
+#define TWCY_GAMBIA 220
+#define TWCY_GERMANY 49
+#define TWCY_GHANA 233
+#define TWCY_GIBRALTER 350
+#define TWCY_GREECE 30
+#define TWCY_GREENLAND 299
+#define TWCY_GRENADA 8099
+#define TWCY_GRENEDINES 8015
+#define TWCY_GUADELOUPE 590
+#define TWCY_GUAM 671
+#define TWCY_GUANTANAMOBAY 5399
+#define TWCY_GUATEMALA 502
+#define TWCY_GUINEA 224
+#define TWCY_GUINEABISSAU 1017
+#define TWCY_GUYANA 592
+#define TWCY_HAITI 509
+#define TWCY_HONDURAS 504
+#define TWCY_HONGKONG 852
+#define TWCY_HUNGARY 36
+#define TWCY_ICELAND 354
+#define TWCY_INDIA 91
+#define TWCY_INDONESIA 62
+#define TWCY_IRAN 98
+#define TWCY_IRAQ 964
+#define TWCY_IRELAND 353
+#define TWCY_ISRAEL 972
+#define TWCY_ITALY 39
+#define TWCY_IVORYCOAST 225
+#define TWCY_JAMAICA 8010
+#define TWCY_JAPAN 81
+#define TWCY_JORDAN 962
+#define TWCY_KENYA 254
+#define TWCY_KIRIBATI 1018
+#define TWCY_KOREA 82
+#define TWCY_KUWAIT 965
+#define TWCY_LAOS 1019
+#define TWCY_LEBANON 1020
+#define TWCY_LIBERIA 231
+#define TWCY_LIBYA 218
+#define TWCY_LIECHTENSTEIN 41
+#define TWCY_LUXENBOURG 352
+#define TWCY_MACAO 853
+#define TWCY_MADAGASCAR 1021
+#define TWCY_MALAWI 265
+#define TWCY_MALAYSIA 60
+#define TWCY_MALDIVES 960
+#define TWCY_MALI 1022
+#define TWCY_MALTA 356
+#define TWCY_MARSHALLIS 692
+#define TWCY_MAURITANIA 1023
+#define TWCY_MAURITIUS 230
+#define TWCY_MEXICO 3
+#define TWCY_MICRONESIA 691
+#define TWCY_MIQUELON 508
+#define TWCY_MONACO 33
+#define TWCY_MONGOLIA 1024
+#define TWCY_MONTSERRAT 8011
+#define TWCY_MOROCCO 212
+#define TWCY_MOZAMBIQUE 1025
+#define TWCY_NAMIBIA 264
+#define TWCY_NAURU 1026
+#define TWCY_NEPAL 977
+#define TWCY_NETHERLANDS 31
+#define TWCY_NETHANTILLES 599
+#define TWCY_NEVIS 8012
+#define TWCY_NEWCALEDONIA 687
+#define TWCY_NEWZEALAND 64
+#define TWCY_NICARAGUA 505
+#define TWCY_NIGER 227
+#define TWCY_NIGERIA 234
+#define TWCY_NIUE 1027
+#define TWCY_NORFOLKI 1028
+#define TWCY_NORWAY 47
+#define TWCY_OMAN 968
+#define TWCY_PAKISTAN 92
+#define TWCY_PALAU 1029
+#define TWCY_PANAMA 507
+#define TWCY_PARAGUAY 595
+#define TWCY_PERU 51
+#define TWCY_PHILLIPPINES 63
+#define TWCY_PITCAIRNIS 1030
+#define TWCY_PNEWGUINEA 675
+#define TWCY_POLAND 48
+#define TWCY_PORTUGAL 351
+#define TWCY_QATAR 974
+#define TWCY_REUNIONI 1031
+#define TWCY_ROMANIA 40
+#define TWCY_RWANDA 250
+#define TWCY_SAIPAN 670
+#define TWCY_SANMARINO 39
+#define TWCY_SAOTOME 1033
+#define TWCY_SAUDIARABIA 966
+#define TWCY_SENEGAL 221
+#define TWCY_SEYCHELLESIS 1034
+#define TWCY_SIERRALEONE 1035
+#define TWCY_SINGAPORE 65
+#define TWCY_SOLOMONIS 1036
+#define TWCY_SOMALI 1037
+#define TWCY_SOUTHAFRICA 27
+#define TWCY_SPAIN 34
+#define TWCY_SRILANKA 94
+#define TWCY_STHELENA 1032
+#define TWCY_STKITTS 8013
+#define TWCY_STLUCIA 8014
+#define TWCY_STPIERRE 508
+#define TWCY_STVINCENT 8015
+#define TWCY_SUDAN 1038
+#define TWCY_SURINAME 597
+#define TWCY_SWAZILAND 268
+#define TWCY_SWEDEN 46
+#define TWCY_SWITZERLAND 41
+#define TWCY_SYRIA 1039
+#define TWCY_TAIWAN 886
+#define TWCY_TANZANIA 255
+#define TWCY_THAILAND 66
+#define TWCY_TOBAGO 8016
+#define TWCY_TOGO 228
+#define TWCY_TONGAIS 676
+#define TWCY_TRINIDAD 8016
+#define TWCY_TUNISIA 216
+#define TWCY_TURKEY 90
+#define TWCY_TURKSCAICOS 8017
+#define TWCY_TUVALU 1040
+#define TWCY_UGANDA 256
+#define TWCY_USSR 7
+#define TWCY_UAEMIRATES 971
+#define TWCY_UNITEDKINGDOM 44
+#define TWCY_USA 1
+#define TWCY_URUGUAY 598
+#define TWCY_VANUATU 1041
+#define TWCY_VATICANCITY 39
+#define TWCY_VENEZUELA 58
+#define TWCY_WAKE 1042
+#define TWCY_WALLISIS 1043
+#define TWCY_WESTERNSAHARA 1044
+#define TWCY_WESTERNSAMOA 1045
+#define TWCY_YEMEN 1046
+#define TWCY_YUGOSLAVIA 38
+#define TWCY_ZAIRE 243
+#define TWCY_ZAMBIA 260
+#define TWCY_ZIMBABWE 263
+
+/****************************************************************************
+ * Language Constants *
+ ****************************************************************************/
+
+#define TWLG_DAN 0 /* Danish */
+#define TWLG_DUT 1 /* Dutch */
+#define TWLG_ENG 2 /* International English */
+#define TWLG_FCF 3 /* French Canadian */
+#define TWLG_FIN 4 /* Finnish */
+#define TWLG_FRN 5 /* French */
+#define TWLG_GER 6 /* German */
+#define TWLG_ICE 7 /* Icelandic */
+#define TWLG_ITN 8 /* Italian */
+#define TWLG_NOR 9 /* Norwegian */
+#define TWLG_POR 10 /* Portuguese */
+#define TWLG_SPA 11 /* Spanish */
+#define TWLG_SWE 12 /* Swedish */
+#define TWLG_USA 13 /* U.S. English */
+/* Added for 1.8 */
+#define TWLG_USERLOCALE -1
+#define TWLG_AFRIKAANS 14
+#define TWLG_ALBANIA 15
+#define TWLG_ARABIC 16
+#define TWLG_ARABIC_ALGERIA 17
+#define TWLG_ARABIC_BAHRAIN 18
+#define TWLG_ARABIC_EGYPT 19
+#define TWLG_ARABIC_IRAQ 20
+#define TWLG_ARABIC_JORDAN 21
+#define TWLG_ARABIC_KUWAIT 22
+#define TWLG_ARABIC_LEBANON 23
+#define TWLG_ARABIC_LIBYA 24
+#define TWLG_ARABIC_MOROCCO 25
+#define TWLG_ARABIC_OMAN 26
+#define TWLG_ARABIC_QATAR 27
+#define TWLG_ARABIC_SAUDIARABIA 28
+#define TWLG_ARABIC_SYRIA 29
+#define TWLG_ARABIC_TUNISIA 30
+#define TWLG_ARABIC_UAE 31 /* United Arabic Emirates */
+#define TWLG_ARABIC_YEMEN 32
+#define TWLG_BASQUE 33
+#define TWLG_BYELORUSSIAN 34
+#define TWLG_BULGARIAN 35
+#define TWLG_CATALAN 36
+#define TWLG_CHINESE 37
+#define TWLG_CHINESE_HONGKONG 38
+#define TWLG_CHINESE_PRC 39 /* People's Republic of China */
+#define TWLG_CHINESE_SINGAPORE 40
+#define TWLG_CHINESE_SIMPLIFIED 41
+#define TWLG_CHINESE_TAIWAN 42
+#define TWLG_CHINESE_TRADITIONAL 43
+#define TWLG_CROATIA 44
+#define TWLG_CZECH 45
+#define TWLG_DANISH TWLG_DAN
+#define TWLG_DUTCH TWLG_DUT
+#define TWLG_DUTCH_BELGIAN 46
+#define TWLG_ENGLISH TWLG_ENG
+#define TWLG_ENGLISH_AUSTRALIAN 47
+#define TWLG_ENGLISH_CANADIAN 48
+#define TWLG_ENGLISH_IRELAND 49
+#define TWLG_ENGLISH_NEWZEALAND 50
+#define TWLG_ENGLISH_SOUTHAFRICA 51
+#define TWLG_ENGLISH_UK 52
+#define TWLG_ENGLISH_USA TWLG_USA
+#define TWLG_ESTONIAN 53
+#define TWLG_FAEROESE 54
+#define TWLG_FARSI 55
+#define TWLG_FINNISH TWLG_FIN
+#define TWLG_FRENCH TWLG_FRN
+#define TWLG_FRENCH_BELGIAN 56
+#define TWLG_FRENCH_CANADIAN TWLG_FCF
+#define TWLG_FRENCH_LUXEMBOURG 57
+#define TWLG_FRENCH_SWISS 58
+#define TWLG_GERMAN TWLG_GER
+#define TWLG_GERMAN_AUSTRIAN 59
+#define TWLG_GERMAN_LUXEMBOURG 60
+#define TWLG_GERMAN_LIECHTENSTEIN 61
+#define TWLG_GERMAN_SWISS 62
+#define TWLG_GREEK 63
+#define TWLG_HEBREW 64
+#define TWLG_HUNGARIAN 65
+#define TWLG_ICELANDIC TWLG_ICE
+#define TWLG_INDONESIAN 66
+#define TWLG_ITALIAN TWLG_ITN
+#define TWLG_ITALIAN_SWISS 67
+#define TWLG_JAPANESE 68
+#define TWLG_KOREAN 69
+#define TWLG_KOREAN_JOHAB 70
+#define TWLG_LATVIAN 71
+#define TWLG_LITHUANIAN 72
+#define TWLG_NORWEGIAN TWLG_NOR
+#define TWLG_NORWEGIAN_BOKMAL 73
+#define TWLG_NORWEGIAN_NYNORSK 74
+#define TWLG_POLISH 75
+#define TWLG_PORTUGUESE TWLG_POR
+#define TWLG_PORTUGUESE_BRAZIL 76
+#define TWLG_ROMANIAN 77
+#define TWLG_RUSSIAN 78
+#define TWLG_SERBIAN_LATIN 79
+#define TWLG_SLOVAK 80
+#define TWLG_SLOVENIAN 81
+#define TWLG_SPANISH TWLG_SPA
+#define TWLG_SPANISH_MEXICAN 82
+#define TWLG_SPANISH_MODERN 83
+#define TWLG_SWEDISH TWLG_SWE
+#define TWLG_THAI 84
+#define TWLG_TURKISH 85
+#define TWLG_UKRANIAN 86
+
+/****************************************************************************
+ * Data Groups *
+ ****************************************************************************/
+
+/* More Data Groups may be added in the future.
+ * Possible candidates include text, vector graphics, sound, etc.
+ * NOTE: Data Group constants must be powers of 2 as they are used
+ * as bitflags when Application asks DSM to present a list of DSs.
+ */
+
+#define DG_CONTROL 0x0001L /* data pertaining to control */
+#define DG_IMAGE 0x0002L /* data pertaining to raster images */
+/* Added 1.8 */
+#define DG_AUDIO 0x0004L /* data pertaining to audio */
+
+/****************************************************************************
+ * Data Argument Types *
+ ****************************************************************************/
+
+/* SDH - 03/23/95 - WATCH */
+/* The thunker requires knowledge about size of data being passed in the */
+/* lpData parameter to DS_Entry (which is not readily available due to */
+/* type LPVOID. Thus, we key off the DAT_ argument to determine the size. */
+/* This has a couple implications: */
+/* 1) Any additional DAT_ features require modifications to the thunk code */
+/* for thunker support. */
+/* 2) Any applications which use the custom capabilities are not supported */
+/* under thunking since we have no way of knowing what size data (if */
+/* any) is being passed. */
+
+#define DAT_NULL 0x0000 /* No data or structure. */
+#define DAT_CUSTOMBASE 0x8000 /* Base of custom DATs. */
+
+/* Data Argument Types for the DG_CONTROL Data Group. */
+#define DAT_CAPABILITY 0x0001 /* TW_CAPABILITY */
+#define DAT_EVENT 0x0002 /* TW_EVENT */
+#define DAT_IDENTITY 0x0003 /* TW_IDENTITY */
+#define DAT_PARENT 0x0004 /* TW_HANDLE, application win handle in Windows */
+#define DAT_PENDINGXFERS 0x0005 /* TW_PENDINGXFERS */
+#define DAT_SETUPMEMXFER 0x0006 /* TW_SETUPMEMXFER */
+#define DAT_SETUPFILEXFER 0x0007 /* TW_SETUPFILEXFER */
+#define DAT_STATUS 0x0008 /* TW_STATUS */
+#define DAT_USERINTERFACE 0x0009 /* TW_USERINTERFACE */
+#define DAT_XFERGROUP 0x000a /* TW_UINT32 */
+/* SDH - 03/21/95 - TWUNK */
+/* Additional message required for thunker to request the special */
+/* identity information. */
+#define DAT_TWUNKIDENTITY 0x000b /* TW_TWUNKIDENTITY */
+#define DAT_CUSTOMDSDATA 0x000c /* TW_CUSTOMDSDATA. */
+/* Added 1.8 */
+#define DAT_DEVICEEVENT 0x000d /* TW_DEVICEEVENT */
+#define DAT_FILESYSTEM 0x000e /* TW_FILESYSTEM */
+#define DAT_PASSTHRU 0x000f /* TW_PASSTHRU */
+
+/* Data Argument Types for the DG_IMAGE Data Group. */
+#define DAT_IMAGEINFO 0x0101 /* TW_IMAGEINFO */
+#define DAT_IMAGELAYOUT 0x0102 /* TW_IMAGELAYOUT */
+#define DAT_IMAGEMEMXFER 0x0103 /* TW_IMAGEMEMXFER */
+#define DAT_IMAGENATIVEXFER 0x0104 /* TW_UINT32 loword is hDIB, PICHandle */
+#define DAT_IMAGEFILEXFER 0x0105 /* Null data */
+#define DAT_CIECOLOR 0x0106 /* TW_CIECOLOR */
+#define DAT_GRAYRESPONSE 0x0107 /* TW_GRAYRESPONSE */
+#define DAT_RGBRESPONSE 0x0108 /* TW_RGBRESPONSE */
+#define DAT_JPEGCOMPRESSION 0x0109 /* TW_JPEGCOMPRESSION */
+#define DAT_PALETTE8 0x010a /* TW_PALETTE8 */
+#define DAT_EXTIMAGEINFO 0x010b /* TW_EXTIMAGEINFO -- for 1.7 Spec. */
+
+/* Added 1.8 */
+/* Data Argument Types for the DG_AUDIO Data Group. */
+#define DAT_AUDIOFILEXFER 0x0201 /* Null data */
+#define DAT_AUDIOINFO 0x0202 /* TW_AUDIOINFO */
+#define DAT_AUDIONATIVEXFER 0x0203 /* TW_UINT32 handle to WAV, (AIFF Mac) */
+
+/****************************************************************************
+ * Messages *
+ ****************************************************************************/
+
+/* All message constants are unique.
+ * Messages are grouped according to which DATs they are used with.*/
+
+#define MSG_NULL 0x0000 /* Used in TW_EVENT structure */
+#define MSG_CUSTOMBASE 0x8000 /* Base of custom messages */
+
+/* Generic messages may be used with any of several DATs. */
+#define MSG_GET 0x0001 /* Get one or more values */
+#define MSG_GETCURRENT 0x0002 /* Get current value */
+#define MSG_GETDEFAULT 0x0003 /* Get default (e.g. power up) value */
+#define MSG_GETFIRST 0x0004 /* Get first of a series of items, e.g. DSs */
+#define MSG_GETNEXT 0x0005 /* Iterate through a series of items. */
+#define MSG_SET 0x0006 /* Set one or more values */
+#define MSG_RESET 0x0007 /* Set current value to default value */
+#define MSG_QUERYSUPPORT 0x0008 /* Get supported operations on the cap. */
+
+/* Messages used with DAT_NULL */
+#define MSG_XFERREADY 0x0101 /* The data source has data ready */
+#define MSG_CLOSEDSREQ 0x0102 /* Request for Application. to close DS */
+#define MSG_CLOSEDSOK 0x0103 /* Tell the Application. to save the state. */
+/* Added 1.8 */
+#define MSG_DEVICEEVENT 0X0104 /* Some event has taken place */
+
+/* Messages used with a pointer to a DAT_STATUS structure */
+#define MSG_CHECKSTATUS 0x0201 /* Get status information */
+
+/* Messages used with a pointer to DAT_PARENT data */
+#define MSG_OPENDSM 0x0301 /* Open the DSM */
+#define MSG_CLOSEDSM 0x0302 /* Close the DSM */
+
+/* Messages used with a pointer to a DAT_IDENTITY structure */
+#define MSG_OPENDS 0x0401 /* Open a data source */
+#define MSG_CLOSEDS 0x0402 /* Close a data source */
+#define MSG_USERSELECT 0x0403 /* Put up a dialog of all DS */
+
+/* Messages used with a pointer to a DAT_USERINTERFACE structure */
+#define MSG_DISABLEDS 0x0501 /* Disable data transfer in the DS */
+#define MSG_ENABLEDS 0x0502 /* Enable data transfer in the DS */
+#define MSG_ENABLEDSUIONLY 0x0503 /* Enable for saving DS state only. */
+
+/* Messages used with a pointer to a DAT_EVENT structure */
+#define MSG_PROCESSEVENT 0x0601
+
+/* Messages used with a pointer to a DAT_PENDINGXFERS structure */
+#define MSG_ENDXFER 0x0701
+
+/* Added 1.8 */
+/* Messages used with a pointer to a DAT_FILESYSTEM structure */
+#define MSG_CHANGEDIRECTORY 0x0801
+#define MSG_CREATEDIRECTORY 0x0802
+#define MSG_DELETE 0x0803
+#define MSG_FORMATMEDIA 0x0804
+#define MSG_GETCLOSE 0x0805
+#define MSG_GETFIRSTFILE 0x0806
+#define MSG_GETINFO 0x0807
+#define MSG_GETNEXTFILE 0x0808
+#define MSG_RENAME 0x0809
+
+/* Messages used with a pointer to a DAT_PASSTHRU structure */
+#define MSG_PASSTHRU 0x0901
+
+/****************************************************************************
+ * Capabilities *
+ ****************************************************************************/
+
+#define CAP_CUSTOMBASE 0x8000 /* Base of custom capabilities */
+
+/* all data sources are REQUIRED to support these caps */
+#define CAP_XFERCOUNT 0x0001
+
+/* image data sources are REQUIRED to support these caps */
+#define ICAP_COMPRESSION 0x0100
+#define ICAP_PIXELTYPE 0x0101
+#define ICAP_UNITS 0x0102 /* default is TWUN_INCHES */
+#define ICAP_XFERMECH 0x0103
+
+/* all data sources MAY support these caps */
+#define CAP_AUTHOR 0x1000
+#define CAP_CAPTION 0x1001
+#define CAP_FEEDERENABLED 0x1002
+#define CAP_FEEDERLOADED 0x1003
+#define CAP_TIMEDATE 0x1004
+#define CAP_SUPPORTEDCAPS 0x1005
+#define CAP_EXTENDEDCAPS 0x1006
+#define CAP_AUTOFEED 0x1007
+#define CAP_CLEARPAGE 0x1008
+#define CAP_FEEDPAGE 0x1009
+#define CAP_REWINDPAGE 0x100a
+#define CAP_INDICATORS 0x100b /* Added 1.1 */
+#define CAP_SUPPORTEDCAPSEXT 0x100c /* Added 1.6 */
+#define CAP_PAPERDETECTABLE 0x100d /* Added 1.6 */
+#define CAP_UICONTROLLABLE 0x100e /* Added 1.6 */
+#define CAP_DEVICEONLINE 0x100f /* Added 1.6 */
+#define CAP_AUTOSCAN 0x1010 /* Added 1.6 */
+#define CAP_THUMBNAILSENABLED 0x1011 /* Added 1.7 */
+#define CAP_DUPLEX 0x1012 /* Added 1.7 */
+#define CAP_DUPLEXENABLED 0x1013 /* Added 1.7 */
+#define CAP_ENABLEDSUIONLY 0x1014 /* Added 1.7 */
+#define CAP_CUSTOMDSDATA 0x1015 /* Added 1.7 */
+#define CAP_ENDORSER 0x1016 /* Added 1.7 */
+#define CAP_JOBCONTROL 0x1017 /* Added 1.7 */
+#define CAP_ALARMS 0x1018 /* Added 1.8 */
+#define CAP_ALARMVOLUME 0x1019 /* Added 1.8 */
+#define CAP_AUTOMATICCAPTURE 0x101a /* Added 1.8 */
+#define CAP_TIMEBEFOREFIRSTCAPTURE 0x101b /* Added 1.8 */
+#define CAP_TIMEBETWEENCAPTURES 0x101c /* Added 1.8 */
+#define CAP_CLEARBUFFERS 0x101d /* Added 1.8 */
+#define CAP_MAXBATCHBUFFERS 0x101e /* Added 1.8 */
+#define CAP_DEVICETIMEDATE 0x101f /* Added 1.8 */
+#define CAP_POWERSUPPLY 0x1020 /* Added 1.8 */
+#define CAP_CAMERAPREVIEWUI 0x1021 /* Added 1.8 */
+#define CAP_DEVICEEVENT 0x1022 /* Added 1.8 */
+#define CAP_PAGEMULTIPLEACQUIRE 0x1023 /* Added 1.8 */
+#define CAP_SERIALNUMBER 0x1024 /* Added 1.8 */
+#define CAP_FILESYSTEM 0x1025 /* Added 1.8 */
+#define CAP_PRINTER 0x1026 /* Added 1.8 */
+#define CAP_PRINTERENABLED 0x1027 /* Added 1.8 */
+#define CAP_PRINTERINDEX 0x1028 /* Added 1.8 */
+#define CAP_PRINTERMODE 0x1029 /* Added 1.8 */
+#define CAP_PRINTERSTRING 0x102a /* Added 1.8 */
+#define CAP_PRINTERSUFFIX 0x102b /* Added 1.8 */
+#define CAP_LANGUAGE 0x102c /* Added 1.8 */
+#define CAP_FEEDERALIGNMENT 0x102d /* Added 1.8 */
+#define CAP_FEEDERORDER 0x102e /* Added 1.8 */
+#define CAP_PAPERBINDING 0x102f /* Added 1.8 */
+#define CAP_REACQUIREALLOWED 0x1030 /* Added 1.8 */
+#define CAP_PASSTHRU 0x1031 /* Added 1.8 */
+#define CAP_BATTERYMINUTES 0x1032 /* Added 1.8 */
+#define CAP_BATTERYPERCENTAGE 0x1033 /* Added 1.8 */
+#define CAP_POWERDOWNTIME 0x1034 /* Added 1.8 */
+
+/* image data sources MAY support these caps */
+#define ICAP_AUTOBRIGHT 0x1100
+#define ICAP_BRIGHTNESS 0x1101
+#define ICAP_CONTRAST 0x1103
+#define ICAP_CUSTHALFTONE 0x1104
+#define ICAP_EXPOSURETIME 0x1105
+#define ICAP_FILTER 0x1106
+#define ICAP_FLASHUSED 0x1107
+#define ICAP_GAMMA 0x1108
+#define ICAP_HALFTONES 0x1109
+#define ICAP_HIGHLIGHT 0x110a
+#define ICAP_IMAGEFILEFORMAT 0x110c
+#define ICAP_LAMPSTATE 0x110d
+#define ICAP_LIGHTSOURCE 0x110e
+#define ICAP_ORIENTATION 0x1110
+#define ICAP_PHYSICALWIDTH 0x1111
+#define ICAP_PHYSICALHEIGHT 0x1112
+#define ICAP_SHADOW 0x1113
+#define ICAP_FRAMES 0x1114
+#define ICAP_XNATIVERESOLUTION 0x1116
+#define ICAP_YNATIVERESOLUTION 0x1117
+#define ICAP_XRESOLUTION 0x1118
+#define ICAP_YRESOLUTION 0x1119
+#define ICAP_MAXFRAMES 0x111a
+#define ICAP_TILES 0x111b
+#define ICAP_BITORDER 0x111c
+#define ICAP_CCITTKFACTOR 0x111d
+#define ICAP_LIGHTPATH 0x111e
+#define ICAP_PIXELFLAVOR 0x111f
+#define ICAP_PLANARCHUNKY 0x1120
+#define ICAP_ROTATION 0x1121
+#define ICAP_SUPPORTEDSIZES 0x1122
+#define ICAP_THRESHOLD 0x1123
+#define ICAP_XSCALING 0x1124
+#define ICAP_YSCALING 0x1125
+#define ICAP_BITORDERCODES 0x1126
+#define ICAP_PIXELFLAVORCODES 0x1127
+#define ICAP_JPEGPIXELTYPE 0x1128
+#define ICAP_TIMEFILL 0x112a
+#define ICAP_BITDEPTH 0x112b
+#define ICAP_BITDEPTHREDUCTION 0x112c /* Added 1.5 */
+#define ICAP_UNDEFINEDIMAGESIZE 0x112d /* Added 1.6 */
+#define ICAP_IMAGEDATASET 0x112e /* Added 1.7 */
+#define ICAP_EXTIMAGEINFO 0x112f /* Added 1.7 */
+#define ICAP_MINIMUMHEIGHT 0x1130 /* Added 1.7 */
+#define ICAP_MINIMUMWIDTH 0x1131 /* Added 1.7 */
+#define ICAP_AUTOBORDERDETECTION 0x1132 /* Added 1.8 */
+#define ICAP_AUTODESKEW 0x1133 /* Added 1.8 */
+#define ICAP_AUTODISCARDBLANKPAGES 0x1134 /* Added 1.8 */
+#define ICAP_AUTOROTATE 0x1135 /* Added 1.8 */
+#define ICAP_FLIPROTATION 0x1136 /* Added 1.8 */
+#define ICAP_BARCODEDETECTIONENABLED 0x1137 /* Added 1.8 */
+#define ICAP_SUPPORTEDBARCODETYPES 0x1138 /* Added 1.8 */
+#define ICAP_BARCODEMAXSEARCHPRIORITIES 0x1139 /* Added 1.8 */
+#define ICAP_BARCODESEARCHPRIORITIES 0x113a /* Added 1.8 */
+#define ICAP_BARCODESEARCHMODE 0x113b /* Added 1.8 */
+#define ICAP_BARCODEMAXRETRIES 0x113c /* Added 1.8 */
+#define ICAP_BARCODETIMEOUT 0x113d /* Added 1.8 */
+#define ICAP_ZOOMFACTOR 0x113e /* Added 1.8 */
+#define ICAP_PATCHCODEDETECTIONENABLED 0x113f /* Added 1.8 */
+#define ICAP_SUPPORTEDPATCHCODETYPES 0x1140 /* Added 1.8 */
+#define ICAP_PATCHCODEMAXSEARCHPRIORITIES 0x1141 /* Added 1.8 */
+#define ICAP_PATCHCODESEARCHPRIORITIES 0x1142 /* Added 1.8 */
+#define ICAP_PATCHCODESEARCHMODE 0x1143 /* Added 1.8 */
+#define ICAP_PATCHCODEMAXRETRIES 0x1144 /* Added 1.8 */
+#define ICAP_PATCHCODETIMEOUT 0x1145 /* Added 1.8 */
+#define ICAP_FLASHUSED2 0x1146 /* Added 1.8 */
+#define ICAP_IMAGEFILTER 0x1147 /* Added 1.8 */
+#define ICAP_NOISEFILTER 0x1148 /* Added 1.8 */
+#define ICAP_OVERSCAN 0x1149 /* Added 1.8 */
+#define ICAP_AUTOMATICBORDERDETECTION 0x1150 /* Added 1.8 */
+#define ICAP_AUTOMATICDESKEW 0x1151 /* Added 1.8 */
+#define ICAP_AUTOMATICROTATE 0x1152 /* Added 1.8 */
+
+/* image data sources MAY support these audio caps */
+#define ACAP_AUDIOFILEFORMAT 0x1201 /* Added 1.8 */
+#define ACAP_XFERMECH 0x1202 /* Added 1.8 */
+
+/* ----------------------------------------------------------------------- *\
+
+ Version 1.7: Following is Extended Image Info Attributes.
+ July 1997
+ KHL
+
+\* ----------------------------------------------------------------------- */
+
+#define TWEI_BARCODEX 0x1200
+#define TWEI_BARCODEY 0x1201
+#define TWEI_BARCODETEXT 0x1202
+#define TWEI_BARCODETYPE 0x1203
+#define TWEI_DESHADETOP 0x1204
+#define TWEI_DESHADELEFT 0x1205
+#define TWEI_DESHADEHEIGHT 0x1206
+#define TWEI_DESHADEWIDTH 0x1207
+#define TWEI_DESHADESIZE 0x1208
+#define TWEI_SPECKLESREMOVED 0x1209
+#define TWEI_HORZLINEXCOORD 0x120A
+#define TWEI_HORZLINEYCOORD 0x120B
+#define TWEI_HORZLINELENGTH 0x120C
+#define TWEI_HORZLINETHICKNESS 0x120D
+#define TWEI_VERTLINEXCOORD 0x120E
+#define TWEI_VERTLINEYCOORD 0x120F
+#define TWEI_VERTLINELENGTH 0x1210
+#define TWEI_VERTLINETHICKNESS 0x1211
+#define TWEI_PATCHCODE 0x1212
+#define TWEI_ENDORSEDTEXT 0x1213
+#define TWEI_FORMCONFIDENCE 0x1214
+#define TWEI_FORMTEMPLATEMATCH 0x1215
+#define TWEI_FORMTEMPLATEPAGEMATCH 0x1216
+#define TWEI_FORMHORZDOCOFFSET 0x1217
+#define TWEI_FORMVERTDOCOFFSET 0x1218
+#define TWEI_BARCODECOUNT 0x1219
+#define TWEI_BARCODECONFIDENCE 0x121A
+#define TWEI_BARCODEROTATION 0x121B
+#define TWEI_BARCODETEXTLENGTH 0x121C
+#define TWEI_DESHADECOUNT 0x121D
+#define TWEI_DESHADEBLACKCOUNTOLD 0x121E
+#define TWEI_DESHADEBLACKCOUNTNEW 0x121F
+#define TWEI_DESHADEBLACKRLMIN 0x1220
+#define TWEI_DESHADEBLACKRLMAX 0x1221
+#define TWEI_DESHADEWHITECOUNTOLD 0x1222
+#define TWEI_DESHADEWHITECOUNTNEW 0x1223
+#define TWEI_DESHADEWHITERLMIN 0x1224
+#define TWEI_DESHADEWHITERLAVE 0x1225
+#define TWEI_DESHADEWHITERLMAX 0x1226
+#define TWEI_BLACKSPECKLESREMOVED 0x1227
+#define TWEI_WHITESPECKLESREMOVED 0x1228
+#define TWEI_HORZLINECOUNT 0x1229
+#define TWEI_VERTLINECOUNT 0x122A
+#define TWEI_DESKEWSTATUS 0x122B
+#define TWEI_SKEWORIGINALANGLE 0x122C
+#define TWEI_SKEWFINALANGLE 0x122D
+#define TWEI_SKEWCONFIDENCE 0x122E
+#define TWEI_SKEWWINDOWX1 0x122F
+#define TWEI_SKEWWINDOWY1 0x1230
+#define TWEI_SKEWWINDOWX2 0x1231
+#define TWEI_SKEWWINDOWY2 0x1232
+#define TWEI_SKEWWINDOWX3 0x1233
+#define TWEI_SKEWWINDOWY3 0x1234
+#define TWEI_SKEWWINDOWX4 0x1235
+#define TWEI_SKEWWINDOWY4 0x1236
+
+#define TWEJ_NONE 0x0000
+#define TWEJ_MIDSEPARATOR 0x0001
+#define TWEJ_PATCH1 0x0002
+#define TWEJ_PATCH2 0x0003
+#define TWEJ_PATCH3 0x0004
+#define TWEJ_PATCH4 0x0005
+#define TWEJ_PATCH6 0x0006
+#define TWEJ_PATCHT 0x0007
+
+/***************************************************************************
+ * Return Codes and Condition Codes section *
+ ***************************************************************************/
+
+/* Return Codes: DSM_Entry and DS_Entry may return any one of these values. */
+#define TWRC_CUSTOMBASE 0x8000
+
+#define TWRC_SUCCESS 0
+#define TWRC_FAILURE 1 /* Application may get TW_STATUS for info on failure */
+#define TWRC_CHECKSTATUS 2 /* "tried hard"; get status */
+#define TWRC_CANCEL 3
+#define TWRC_DSEVENT 4
+#define TWRC_NOTDSEVENT 5
+#define TWRC_XFERDONE 6
+#define TWRC_ENDOFLIST 7 /* After MSG_GETNEXT if nothing left */
+#define TWRC_INFONOTSUPPORTED 8
+#define TWRC_DATANOTAVAILABLE 9
+
+/* Condition Codes: Application gets these by doing DG_CONTROL DAT_STATUS MSG_GET. */
+#define TWCC_CUSTOMBASE 0x8000
+
+#define TWCC_SUCCESS 0 /* It worked! */
+#define TWCC_BUMMER 1 /* Failure due to unknown causes */
+#define TWCC_LOWMEMORY 2 /* Not enough memory to perform operation */
+#define TWCC_NODS 3 /* No Data Source */
+#define TWCC_MAXCONNECTIONS 4 /* DS is connected to max possible applications */
+#define TWCC_OPERATIONERROR 5 /* DS or DSM reported error, application shouldn't */
+#define TWCC_BADCAP 6 /* Unknown capability */
+#define TWCC_BADPROTOCOL 9 /* Unrecognized MSG DG DAT combination */
+#define TWCC_BADVALUE 10 /* Data parameter out of range */
+#define TWCC_SEQERROR 11 /* DG DAT MSG out of expected sequence */
+#define TWCC_BADDEST 12 /* Unknown destination Application/Source in DSM_Entry */
+#define TWCC_CAPUNSUPPORTED 13 /* Capability not supported by source */
+#define TWCC_CAPBADOPERATION 14 /* Operation not supported by capability */
+#define TWCC_CAPSEQERROR 15 /* Capability has dependency on other capability */
+/* Added 1.8 */
+#define TWCC_DENIED 16 /* File System operation is denied (file is protected) */
+#define TWCC_FILEEXISTS 17 /* Operation failed because file already exists. */
+#define TWCC_FILENOTFOUND 18 /* File not found */
+#define TWCC_NOTEMPTY 19 /* Operation failed because directory is not empty */
+#define TWCC_PAPERJAM 20 /* The feeder is jammed */
+#define TWCC_PAPERDOUBLEFEED 21 /* The feeder detected multiple pages */
+#define TWCC_FILEWRITEERROR 22 /* Error writing the file (meant for things like disk full conditions) */
+#define TWCC_CHECKDEVICEONLINE 23 /* The device went offline prior to or during this operation */
+
+
+/* bit patterns: for query the operation that are supported by the data source on a capability */
+/* Application gets these through DG_CONTROL/DAT_CAPABILITY/MSG_QUERYSUPPORT */
+/* Added 1.6 */
+#define TWQC_GET 0x0001
+#define TWQC_SET 0x0002
+#define TWQC_GETDEFAULT 0x0004
+#define TWQC_GETCURRENT 0x0008
+#define TWQC_RESET 0x0010
+
+
+/****************************************************************************
+ * Entry Points *
+ ****************************************************************************/
+
+/**********************************************************************
+ * Function: DSM_Entry, the only entry point into the Data Source Manager.
+ *
+ * Parameters:
+ * pOrigin Identifies the source module of the message. This could
+ * identify an Application, a Source, or the Source Manager.
+ *
+ * pDest Identifies the destination module for the message.
+ * This could identify an application or a data source.
+ * If this is NULL, the message goes to the Source Manager.
+ *
+ * DG The Data Group.
+ * Example: DG_IMAGE.
+ *
+ * DAT The Data Attribute Type.
+ * Example: DAT_IMAGEMEMXFER.
+ *
+ * MSG The message. Messages are interpreted by the destination module
+ * with respect to the Data Group and the Data Attribute Type.
+ * Example: MSG_GET.
+ *
+ * pData A pointer to the data structure or variable identified
+ * by the Data Attribute Type.
+ * Example: (TW_MEMREF)&ImageMemXfer
+ * where ImageMemXfer is a TW_IMAGEMEMXFER structure.
+ *
+ * Returns:
+ * ReturnCode
+ * Example: TWRC_SUCCESS.
+ *
+ ********************************************************************/
+
+/* Don't mangle the name "DSM_Entry" if we're compiling in C++! */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef _MSWIN_
+TW_UINT16 FAR PASCAL DSM_Entry( pTW_IDENTITY pOrigin,
+ pTW_IDENTITY pDest,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+
+typedef TW_UINT16 (FAR PASCAL *DSMENTRYPROC)(pTW_IDENTITY, pTW_IDENTITY,
+ TW_UINT32, TW_UINT16,
+ TW_UINT16, TW_MEMREF);
+#else /* _MSWIN_ */
+
+FAR PASCAL TW_UINT16 DSM_Entry( pTW_IDENTITY pOrigin,
+ pTW_IDENTITY pDest,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+
+typedef TW_UINT16 (*DSMENTRYPROC)(pTW_IDENTITY, pTW_IDENTITY,
+ TW_UINT32, TW_UINT16,
+ TW_UINT16, TW_MEMREF);
+#endif /* _MSWIN_ */
+
+#ifdef __cplusplus
+}
+#endif /* cplusplus */
+
+
+/**********************************************************************
+ * Function: DS_Entry, the entry point provided by a Data Source.
+ *
+ * Parameters:
+ * pOrigin Identifies the source module of the message. This could
+ * identify an application or the Data Source Manager.
+ *
+ * DG The Data Group.
+ * Example: DG_IMAGE.
+ *
+ * DAT The Data Attribute Type.
+ * Example: DAT_IMAGEMEMXFER.
+ *
+ * MSG The message. Messages are interpreted by the data source
+ * with respect to the Data Group and the Data Attribute Type.
+ * Example: MSG_GET.
+ *
+ * pData A pointer to the data structure or variable identified
+ * by the Data Attribute Type.
+ * Example: (TW_MEMREF)&ImageMemXfer
+ * where ImageMemXfer is a TW_IMAGEMEMXFER structure.
+ *
+ * Returns:
+ * ReturnCode
+ * Example: TWRC_SUCCESS.
+ *
+ * Note:
+ * The DSPROC type is only used by an application when it calls
+ * a Data Source directly, bypassing the Data Source Manager.
+ *
+ ********************************************************************/
+/* Don't mangle the name "DS_Entry" if we're compiling in C++! */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef _MSWIN_
+ #ifdef _WIN32
+ __declspec(dllexport) TW_UINT16 FAR PASCAL DS_Entry (pTW_IDENTITY pOrigin,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+ #else /* _WIN32 */
+ TW_UINT16 FAR PASCAL DS_Entry (pTW_IDENTITY pOrigin,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+ #endif /* _WIN32 */
+
+ typedef TW_UINT16 (FAR PASCAL *DSENTRYPROC) (pTW_IDENTITY pOrigin,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+#else /* _MSWIN_ */
+FAR PASCAL TW_UINT16 DS_Entry( pTW_IDENTITY pOrigin,
+ TW_UINT32 DG,
+ TW_UINT16 DAT,
+ TW_UINT16 MSG,
+ TW_MEMREF pData);
+
+typedef TW_UINT16 (*DSENTRYPROC)(pTW_IDENTITY,
+ TW_UINT32, TW_UINT16,
+ TW_UINT16, TW_MEMREF);
+#endif /* _MSWIN_ */
+
+#ifdef __cplusplus
+}
+#endif /* cplusplus */
+
+/* SDH - 02/08/95 - TWUNK */
+/* Force 32-bit twain to use same packing of twain structures as existing */
+/* 16-bit twain. This allows 16/32-bit thunking. */
+#ifdef WIN32
+ #ifdef __BORLANDC__ /*(Mentor June 13, 1996) if we're using a Borland compiler */
+ #pragma option -a. /*(Mentor October 30, 1996) switch back to original alignment */
+ #else /*(Mentor June 13, 1996) if NOT using a Borland compiler */
+ #pragma pack (pop, before_twain)
+ #endif /*(Mentor June 13, 1996) */
+#else /* WIN32 */
+#endif /* WIN32 */
+
+#endif /* TWAIN */
diff --git a/plug-ins/ui/Makefile.am b/plug-ins/ui/Makefile.am
new file mode 100644
index 0000000..058aaf8
--- /dev/null
+++ b/plug-ins/ui/Makefile.am
@@ -0,0 +1,12 @@
+uidatadir = $(gimpdatadir)/ui/plug-ins
+
+uidata_DATA = \
+ plug-in-file-gif.ui \
+ plug-in-file-png.ui \
+ plug-in-file-raw.ui \
+ plug-in-file-tiff.ui \
+ plug-in-metadata-editor.ui \
+ plug-in-metadata-editor-calendar.ui \
+ plug-in-metadata-viewer.ui
+
+EXTRA_DIST = $(uidata_DATA)
diff --git a/plug-ins/ui/Makefile.in b/plug-ins/ui/Makefile.in
new file mode 100644
index 0000000..2f5fd6f
--- /dev/null
+++ b/plug-ins/ui/Makefile.in
@@ -0,0 +1,811 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = plug-ins/ui
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(uidatadir)"
+DATA = $(uidata_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+uidatadir = $(gimpdatadir)/ui/plug-ins
+uidata_DATA = \
+ plug-in-file-gif.ui \
+ plug-in-file-png.ui \
+ plug-in-file-raw.ui \
+ plug-in-file-tiff.ui \
+ plug-in-metadata-editor.ui \
+ plug-in-metadata-editor-calendar.ui \
+ plug-in-metadata-viewer.ui
+
+EXTRA_DIST = $(uidata_DATA)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/ui/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/ui/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-uidataDATA: $(uidata_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(uidata_DATA)'; test -n "$(uidatadir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(uidatadir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(uidatadir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(uidatadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(uidatadir)" || exit $$?; \
+ done
+
+uninstall-uidataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(uidata_DATA)'; test -n "$(uidatadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(uidatadir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(uidatadir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-uidataDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-uidataDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip install-uidataDATA installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags-am uninstall uninstall-am uninstall-uidataDATA
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/ui/plug-in-file-gif.ui b/plug-ins/ui/plug-in-file-gif.ui
new file mode 100644
index 0000000..8d4f841
--- /dev/null
+++ b/plug-ins/ui/plug-in-file-gif.ui
@@ -0,0 +1,306 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="main-vbox">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="gif-options-frame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="gif-options-frame-alignment">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="gif-options-frame-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="interlace">
+ <property name="label" translatable="yes">I_nterlace</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="comment-hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="save-comment">
+ <property name="label" translatable="yes">_GIF comment:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="comment-scrolled-window">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="comment">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_below_lines">1</property>
+ <property name="wrap_mode">word</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="as-animation">
+ <property name="label" translatable="yes">As _animation</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="frame">
+ <property name="visible">True</property>
+ <!-- Don't use markup, because it complicates the string to translate -->
+ <property name="label" translatable="yes">GIF Options</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="animation-frame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="animation-frame-alignment">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="animation-options-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="loop-forever">
+ <property name="label" translatable="yes">_Loop forever</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="delay-hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="delay-label-begin">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Delay between frames where unspecified:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">delay-spin</property>
+ <property name="tooltip-text" translatable="yes">GIF supports hundredths of a second precision.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GimpSpinButton" id="delay-spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="shadow_type">none</property>
+ <property name="adjustment">delay</property>
+ <property name="climb_rate">1</property>
+ <property name="snap-to-ticks">True</property>
+ <property name="tooltip-text" translatable="yes">GIF supports hundredths of a second precision.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="delay-label-end">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">milliseconds</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="frame-disposal-hbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="disposal-label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Frame disposal where unspecified:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">dispose-combo</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="dispose-combo">
+ <property name="visible">True</property>
+ <property name="model">dispose-store</property>
+ <child>
+ <object class="GtkCellRendererText" id="text-renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="use-default-delay">
+ <property name="label" translatable="yes">_Use delay entered above for all frames</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="use-default-dispose">
+ <property name="label" translatable="yes">U_se disposal entered above for all frames</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="animated-options-frame-label">
+ <property name="visible">True</property>
+ <property name="yalign">0.49000000953674316</property>
+ <property name="label" translatable="yes">Animated GIF Options</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="delay">
+ <property name="value">100</property>
+ <property name="lower">10</property>
+ <property name="upper">65000</property>
+ <property name="step_increment">10</property>
+ <property name="page_increment">100</property>
+ </object>
+ <object class="GtkListStore" id="dispose-store">
+ <columns>
+ <!-- column-name dispose-mode -->
+ <column type="gint"/>
+ <!-- column-name dispose-mode-label -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-file-png.ui b/plug-ins/ui/plug-in-file-png.ui
new file mode 100644
index 0000000..9925d51
--- /dev/null
+++ b/plug-ins/ui/plug-in-file-png.ui
@@ -0,0 +1,350 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkAdjustment" id="compression-level">
+ <property name="upper">9</property>
+ <property name="value">9</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkTable" id="table">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="n_rows">12</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="interlace">
+ <property name="label" translatable="yes">_Interlacing (Adam7)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-background-color">
+ <property name="label" translatable="yes">Save _background color</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-gamma">
+ <property name="label" translatable="yes">Save gamma</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-layer-offset">
+ <property name="label" translatable="yes">Save layer o_ffset</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-resolution">
+ <property name="label" translatable="yes">Save _resolution</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-creation-time">
+ <property name="label" translatable="yes">Save creation _time</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-comment">
+ <property name="label" translatable="yes">Save comme_nt</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-transparent-pixels">
+ <property name="label" translatable="yes">Save color _values from transparent pixels</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScale" id="compression-level-scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">compression-level</property>
+ <property name="draw_value">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GimpSpinButton" id="compression-level-spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">Choose a high compression level for small file size</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="adjustment">compression-level</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="load-defaults">
+ <property name="label" translatable="yes">_Load Defaults</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="save-defaults">
+ <property name="label" translatable="yes">S_ave Defaults</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">11</property>
+ <property name="bottom_attach">12</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkCheckButton" id="save-exif">
+ <property name="label" translatable="yes">Save Exif data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-xmp">
+ <property name="label" translatable="yes">Save XMP data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-iptc">
+ <property name="label" translatable="yes">Save IPTC data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-thumbnail">
+ <property name="label" translatable="yes">Save thumbnail</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-color-profile">
+ <property name="label" translatable="yes">Save color profile</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="right_attach">3</property>
+ <property name="top_attach">10</property>
+ <property name="bottom_attach">11</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="compression-level-label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Co_mpression level:</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">compression-level-spin</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="pixelformat-combo">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <items>
+ <item translatable="yes">automatic pixelformat</item>
+ <item translatable="yes">8bpc RGB</item>
+ <item translatable="yes">8bpc GRAY</item>
+ <item translatable="yes">8bpc RGBA</item>
+ <item translatable="yes">8bpc GRAYA</item>
+ <item translatable="yes">16bpc RGB</item>
+ <item translatable="yes">16bpc GRAY</item>
+ <item translatable="yes">16bpc RGBA</item>
+ <item translatable="yes">16bpc GRAYA</item>
+ </items>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-file-raw.ui b/plug-ins/ui/plug-in-file-raw.ui
new file mode 100644
index 0000000..fec1315
--- /dev/null
+++ b/plug-ins/ui/plug-in-file-raw.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="vbox">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GimpFrame" id="image-type-frame">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">RGB Save Type</property>
+ <child>
+ <object class="GtkVBox" id="image-type-vbox">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkRadioButton" id="image-type-standard">
+ <property name="label" translatable="yes">_Standard (R,G,B)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="image-type-planar">
+ <property name="label" translatable="yes">_Planar (RRR,GGG,BBB)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">image-type-standard</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GimpFrame" id="palette-type-frame">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Palette Type</property>
+ <child>
+ <object class="GtkVBox" id="palette-type-vbox">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkRadioButton" id="palette-type-normal">
+ <property name="label" translatable="yes">_R, G, B (normal)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="palette-type-bmp">
+ <property name="label" translatable="yes">_B, G, R, X (BMP style)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">palette-type-normal</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">start</property>
+ <child>
+ <object class="GtkButton" id="load-defaults">
+ <property name="label" translatable="yes">_Load Defaults</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="save-defaults">
+ <property name="label" translatable="yes">S_ave Defaults</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-file-tiff.ui b/plug-ins/ui/plug-in-file-tiff.ui
new file mode 100644
index 0000000..28e2822
--- /dev/null
+++ b/plug-ins/ui/plug-in-file-tiff.ui
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="tiff_export_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="radio_button_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">3</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="bigtiff-warning">
+ <property name="label" translatable="yes">Warning: maximum TIFF file size exceeded. Retry as BigTIFF or with a different compression algorithm, or cancel.</property>
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="bigtiff">
+ <property name="label" translatable="yes">Export in _BigTIFF variant file format</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-layers">
+ <property name="label" translatable="yes">Save _layers</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GimpFrame" id="layers-frame">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="crop-layers">
+ <property name="label" translatable="yes">Cr_op layers to image bounds</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-alpha">
+ <property name="label" translatable="yes">Save color _values from transparent pixels</property>
+ <property name="tooltip_text" translatable="yes">Colors are not stored premultiplied by the associated alpha</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="commentfield">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="commentlabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">&lt;b&gt;Comment&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkCheckButton" id="save-exif">
+ <property name="label" translatable="yes">S_ave Exif data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-xmp">
+ <property name="label" translatable="yes">Save _XMP data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-iptc">
+ <property name="label" translatable="yes">Save _IPTC data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkCheckButton" id="save-thumbnail">
+ <property name="label" translatable="yes">Save _thumbnail</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-color-profile">
+ <property name="label" translatable="yes">Save color _profile</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="save-geotiff">
+ <property name="label" translatable="yes">Save _GeoTIFF data</property>
+ <property name="tooltip_text" translatable="yes">Keep the GeoTIFF metadata if it was present on import</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-metadata-editor-calendar.ui b/plug-ins/ui/plug-in-metadata-editor-calendar.ui
new file mode 100644
index 0000000..e066a84
--- /dev/null
+++ b/plug-ins/ui/plug-in-metadata-editor-calendar.ui
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkVBox" id="calendar-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCalendar" id="calendar">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="year">2016</property>
+ <property name="month">1</property>
+ <property name="day">1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-metadata-editor.ui b/plug-ins/ui/plug-in-metadata-editor.ui
new file mode 100644
index 0000000..ff3a008
--- /dev/null
+++ b/plug-ins/ui/plug-in-metadata-editor.ui
@@ -0,0 +1,4953 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.24"/>
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkListStore" id="artwork_or_object">
+ <columns>
+ <!-- column-name Title -->
+ <column type="gchararray"/>
+ <!-- column-name Date -->
+ <column type="gchararray"/>
+ <!-- column-name Creator -->
+ <column type="gchararray"/>
+ <!-- column-name Source -->
+ <column type="gchararray"/>
+ <!-- column-name SourceInventoryID -->
+ <column type="gchararray"/>
+ <!-- column-name CopyrightNotice -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="copyright_owner">
+ <columns>
+ <!-- column-name Name -->
+ <column type="gchararray"/>
+ <!-- column-name Identifier -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="feature_organization_code">
+ <columns>
+ <!-- column-name Code -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="feature_organization_name">
+ <columns>
+ <!-- column-name Name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="image_creator">
+ <columns>
+ <!-- column-name Name -->
+ <column type="gchararray"/>
+ <!-- column-name Identifier -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="licensor">
+ <columns>
+ <!-- column-name Name -->
+ <column type="gchararray"/>
+ <!-- column-name Identifier -->
+ <column type="gchararray"/>
+ <!-- column-name Phone1 -->
+ <column type="gchararray"/>
+ <!-- column-name PhoneType1 -->
+ <column type="gchararray"/>
+ <!-- column-name Phone2 -->
+ <column type="gchararray"/>
+ <!-- column-name PhoneType2 -->
+ <column type="gchararray"/>
+ <!-- column-name Email -->
+ <column type="gchararray"/>
+ <!-- column-name Web -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="location_shown">
+ <columns>
+ <!-- column-name Sublocation -->
+ <column type="gchararray"/>
+ <!-- column-name City -->
+ <column type="gchararray"/>
+ <!-- column-name ProvinceState -->
+ <column type="gchararray"/>
+ <!-- column-name Country -->
+ <column type="gchararray"/>
+ <!-- column-name CountryCode -->
+ <column type="gchararray"/>
+ <!-- column-name WorldRegion -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="model_release_identifier">
+ <columns>
+ <!-- column-name Model -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="property_release_identifier">
+ <columns>
+ <!-- column-name PropertyReleaseID -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="registry_entry">
+ <columns>
+ <!-- column-name OrganisationID -->
+ <column type="gchararray"/>
+ <!-- column-name ItemID -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="metadata-vbox">
+ <property name="width_request">644</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkNotebook" id="iptc-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="description_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="description_table">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">9</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Document Title</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_author">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Author</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_authortitle">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Author Title</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Description</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_descriptionwriter">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Description Writer </property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.dc.title">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.dc.creator">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.AuthorsPosition">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.CaptionWriter">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="caption_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.dc.description">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_rating">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Rating</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.dc.subject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_keywords2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Keywords</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.xmp.Rating">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="copyright_table">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">29</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_copyright_status">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Copyright Status</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Copyright Notice</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright_url">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Copyright URL</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.dc.rights">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.xmpRights.WebStatement">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.xmpRights.Marked">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="description_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Description</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="iptc_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="description_table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">37</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_address">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Address</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l__city">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_postal_code">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Postal Code </property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.CiAdrPcode">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_state_province">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">State / Province</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_country">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Country</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="address_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.CiAdrExtadr">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.CiAdrRegion">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.CiAdrCtry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.CiAdrCity">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="copyright_table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">68</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_phones">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Phone(s)</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Email(s)</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_copyright_url1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Website(s)</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="address_scroll1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.CiTelWork">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="address_scroll2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.CiEmailWork">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="address_scroll3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.CiUrlWork">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="copyright_table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">23</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_creation_date1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Creation Date</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_intellectual_genre1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Intellectual Genre</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_scene_code1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">IPTC Scene Code</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="address_scroll5">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.Scene">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.IntellectualGenre">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.DateCreated">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="create_date_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="create_date_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">16</property>
+ <property name="icon_name">gtk-add</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="iptc_location_table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">23</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_sublocation1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Sublocation </property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_city1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.Location">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.City">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.Country">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_state_province1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">State / Province</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_country_code1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">ISO Country Code</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.State">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptc.CountryCode">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_country1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Country</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="iptc_location_table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">20</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_subject_code1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">IPTC Subject Code</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_keywords1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Headline</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_headline_scroll1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.photoshop.Headline">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_subject_code_scroll4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptc.SubjectCode">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_urgency">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Urgency</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.photoshop.Urgency">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">12</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="iptc_location_table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">42</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_job_identifier">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Job Identifier </property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_instructions">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Instructions</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_credit_line">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Credit Line</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_source">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Source</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.Source">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.TransmissionReference">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.Credit">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_subject_code_scroll1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.photoshop.Instructions">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_usage_terms">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Usage Terms</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_usage_terms_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.xmpRights.UsageTerms">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">17</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptc_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">IPTC</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="iptc_ext_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="image_description_table">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">10</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">43</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_sublocation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Sublocation</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_city">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">City</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_prov_state">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">State / Province</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_person_in_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Person Shown</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_country_name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Country Name</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.CountryName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.Sublocation">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.ProvinceState">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_country_code">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Country ISO-Code</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_world_region">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">World Region</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_ext_person_shown_scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptcExt.PersonInImage">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.City">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.CountryCode">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.WorldRegion">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_location_shown">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Location Shown</property>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_featured_org">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Featured Organization</property>
+ </object>
+ <packing>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_event">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Event</property>
+ </object>
+ <packing>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.iptcExt.OrganisationInImageName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">feature_organization_name</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Name">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Name</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_feat_org_name_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_feat_org_name_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_feat_org_name_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_feat_org_name_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.iptcExt.OrganisationInImageCode">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">feature_organization_code</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Code">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Code</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkButton" id="rem_feat_org_code_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_feat_org_code_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add_feat_org_code_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_feat_org_code_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">8</property>
+ <property name="bottom_attach">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.iptcExt.LocationShown">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">location_shown</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Sublocation">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Sublocation</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="City">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">City</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="State">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Province / State</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Country">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Country</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="CountryCode">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Country ISO Code</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="WorldRegion">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">World Region</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext6">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_shown_location_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="add_shown_location_image">
+ <property name="width_request">0</property>
+ <property name="height_request">0</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_shown_location_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="rem_shown_location_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.Event">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">9</property>
+ <property name="bottom_attach">10</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="image_description_table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">73</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_artwork_object">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Artwork or Object</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.iptcExt.ArtworkOrObject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">artwork_or_object</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Title">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Title</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext9">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="DateCreated">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Date Created</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Creator">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Creator</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext11">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Source">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Source</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext12">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="SourceInventoryID">
+ <property name="title" translatable="yes">Source Inventory ID</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext13">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Copyright">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Copyright Notice</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext14">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="rem_artwork_object_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_artwork_object_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add_artwork_object_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_artwork_object_image">
+ <property name="width_request">0</property>
+ <property name="height_request">0</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="image_description_table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">8</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_model_age">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Model Age</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_city1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Minor Model Age Disclosure</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_model_release_status">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Model Release Status</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_additional_model_info">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Additional Model Info</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.ModelAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_ext_person_shown_scroll1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.iptcExt.AddlModelInfo">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.plus.MinorModelAgeDisclosure">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkFixed" id="fixed2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.plus.ModelReleaseStatus">
+ <property name="width_request">140</property>
+ <property name="height_request">32</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.plus.ModelReleaseID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">model_release_identifier</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="ModelReleaseID">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Model Release Identifier</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext15">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_model_rel_id_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_model_rel_id_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_model_rel_id_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_model_rel_id_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="image_description_table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">51</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_image_supplier_id">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Image Supplier ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_iptc_ext_city2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Supplier's Image ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_registry_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Registry Entry</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_image_supplier">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Image Supplier Name</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_max_width">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Max. Available Width</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.MaxAvailWidth">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.plus.ImageSupplierID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_max_height">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Max. Available Height</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_digital_source_type">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.019999999552965164</property>
+ <property name="xpad">4</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Digital Source Type</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_ext_person_shown_scroll2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.plus.ImageSupplierName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.plus.ImageSupplierImageID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.iptcExt.MaxAvailHeight">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.iptcExt.DigitalSourceType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.iptcExt.RegistryId">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">registry_entry</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="OrganizationID">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Organization Identifier</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext16">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="ItemID">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Item Identifier</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext17">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_reg_entry_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_reg_entry_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_reg_entry_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_reg_entry_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">8</property>
+ <property name="position">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="image_description_table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">36</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_copyright_owner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Copyright Owner</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_licensor">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Licensor</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_property_release_status">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Property Release Status</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_image_creator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Image Creator</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.plus.ImageCreator">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">image_creator</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Name1">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">Name</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext18">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Identifier">
+ <property name="title" translatable="yes">Identifier</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext19">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_image_creator_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_image_creator_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_image_creator_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_image_creator_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.plus.CopyrightOwner">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">copyright_owner</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Name2">
+ <property name="title" translatable="yes">Name</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext20">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Identifier1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Identifier</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext21">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_copyright_own_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_copyright_own_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_copyright_own_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_copyright_own_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.plus.Licensor">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">licensor</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="Name3">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Name</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext22">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="Identifier2">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Identifier</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext23">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="PhoneNumber1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Phone Number 1</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext24">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="PhoneNumberCombo1">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Phone Type 1</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererCombo" id="cellrenderercombo1"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="PhoneNumber2">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Phone Number 2</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext25">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">4</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="PhoneNumberCombo2">
+ <property name="title" translatable="yes">Phone Type 2</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererCombo" id="cellrenderercombo2"/>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="EmailAddress">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Email Address</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext26">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">6</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="WebAddress">
+ <property name="title" translatable="yes">Web Address</property>
+ <property name="expand">True</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext27">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">7</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_licensor_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_licensor_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_licensor_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_licensor_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkFixed" id="fixed1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.plus.PropertyReleaseStatus">
+ <property name="width_request">140</property>
+ <property name="height_request">32</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="Xmp.plus.PropertyReleaseID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">property_release_identifier</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <property name="enable_grid_lines">both</property>
+ <child>
+ <object class="GtkTreeViewColumn" id="PropertyReleaseID">
+ <property name="resizable">True</property>
+ <property name="title" translatable="yes">Property Release Identifier</property>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext28">
+ <property name="editable">True</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="add_prop_rel_id_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <child>
+ <object class="GtkImage" id="add_prop_rel_id_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Add an entry</property>
+ <property name="stock">gtk-add</property>
+ <property name="pixel_size">16</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="rem_prop_rel_id_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="rem_prop_rel_id_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Remove an entry</property>
+ <property name="stock">gtk-remove</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">2</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">9</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptc_extension_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">IPTC Extension</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="categories_box_page">
+ <property name="height_request">380</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="image_description_table5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">16</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_supplemental_category">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Supplemental Category</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_category">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Category</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_ext_person_shown_scroll4">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.photoshop.SupplementalCategories">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.photoshop.Category">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">3</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="categories_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">2</property>
+ <property name="ypad">2</property>
+ <property name="label" translatable="yes">Categories</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="gps_box_page">
+ <property name="height_request">380</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow6">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="image_description_table6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">6</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">43</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_longitude_ref">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Longitude Reference</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_longitude">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Longitude</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Exif.GPSInfo.GPSLongitude">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_altitude">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Altitude</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_latitude">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Latitude</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Exif.GPSInfo.GPSLatitude">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_latitude_ref">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Latitude Reference</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_altitude_ref">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Altitude Reference</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Exif.GPSInfo.GPSLongitudeRef">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Exif.GPSInfo.GPSLatitudeRef">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Exif.GPSInfo.GPSAltitudeRef">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="Exif.GPSInfo.GPSAltitude">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="GPSAltitudeSystem">
+ <property name="width_request">60</property>
+ <property name="height_request">30</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="gps_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">GPS</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="dicom_box_page">
+ <property name="height_request">380</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow7">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="viewport6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkTable" id="description_table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">100</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_patient_name">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Patient</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_patient_id">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Patient ID</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_dob">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Date of Birth</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_patient_sex">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Patient Sex</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.PatientName">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.PatientID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="Xmp.DICOM.PatientSex">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_padding">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.PatientDOB">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="dob_date_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="dob_date_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">16</property>
+ <property name="icon_name">gtk-add</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="description_table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">8</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">59</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_study_id">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Study ID</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_referring_physician">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Referring Physician</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_study_date">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Study Date</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.StudyID">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.StudyPhysician">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_study_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Study Description</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_headline_scroll2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.DICOM.StudyDescription">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_series_number">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Series Number</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_modality">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Modality</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_series_date">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Series Date</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_series_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Series Description</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc_headline_scroll3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTextView" id="Xmp.DICOM.SeriesDescription">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">7</property>
+ <property name="bottom_attach">8</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.SeriesNumber">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.SeriesModality">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.StudyDateTime">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="study_date_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="study_date_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">16</property>
+ <property name="icon_name">gtk-add</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.SeriesDateTime">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="series_date_button">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage" id="series_date_image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">16</property>
+ <property name="icon_name">gtk-add</property>
+ <property name="icon-size">2</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="description_table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">23</property>
+ <property name="row_spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="l_equipment_institution">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Equipment Institution</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="l_equipment_manufacturer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">3</property>
+ <property name="ypad">3</property>
+ <property name="label" translatable="yes">Equipment Manufacturer</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_SHRINK | GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.EquipmentInstitution">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="Xmp.DICOM.EquipmentManufacturer">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="invisible_char_set">True</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="dicom_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">DICOM</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkComboBoxText" id="impex_combo">
+ <property name="width_request">160</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/plug-ins/ui/plug-in-metadata-viewer.ui b/plug-ins/ui/plug-in-metadata-viewer.ui
new file mode 100644
index 0000000..706550c
--- /dev/null
+++ b/plug-ins/ui/plug-in-metadata-viewer.ui
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkListStore" id="exif-liststore">
+ <columns>
+ <!-- column-name c_exif_tag -->
+ <column type="gchararray"/>
+ <!-- column-name c_exif_value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="xmp-liststore">
+ <columns>
+ <!-- column-name c_xmp_tag -->
+ <column type="gchararray"/>
+ <!-- column-name c_xmp_value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="iptc-liststore">
+ <columns>
+ <!-- column-name c_iptc_tag -->
+ <column type="gchararray"/>
+ <!-- column-name c_iptc_value -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="metadata-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkNotebook" id="metadata-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="scrollable">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="exif-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="exif-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">exif-liststore</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="exif-treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_tag_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">Exif Tag</property>
+ <child>
+ <object class="GtkCellRendererText" id="exif_tag_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="exif_value_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="exif_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="exif">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">Exif</property>
+ </object>
+ <packing>
+ <property name="tab_expand">True</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="xmp-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="xmp-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">xmp-liststore</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="xmp-treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_tag_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">XMP Tag</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_tag_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="xmp_value_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="xmp_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="xmp">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">XMP</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="iptc-scroll">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="border_width">6</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="iptc-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">iptc-liststore</property>
+ <property name="headers_clickable">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="iptc-treeview-selection"/>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="iptc_tag_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">IPTC Tag</property>
+ <child>
+ <object class="GtkCellRendererText" id="iptc_tag_cell_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="iptc_value_column">
+ <property name="resizable">True</property>
+ <property name="spacing">3</property>
+ <property name="title" translatable="yes">Value</property>
+ <child>
+ <object class="GtkCellRendererText" id="iptc_value_cell_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="iptc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="ypad">4</property>
+ <property name="label" translatable="yes">IPTC</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+</interface>